A while back, I solved this problem, Performance and Allocation with Arrays of Functions. More recently, I wanted a multithreaded loop inside of this, so that the code now reads:
function update(x, Δt)
return x + 0.5 * Δt * x
end
@generated function compute_threaded(x0_vals, Δt, nΔt, f::Tuple{Vararg{<:Any,K}}) where {K}
quote
x_vals = copy(x0_vals);
nx = length(x0_vals);
f_vals = zeros($K, nx);
f_avg_vals = zeros($K, nΔt)
for n in 1:nΔt
# @. x_vals = update(x_vals, Δt); switch to this, and it works fine
Threads.@threads for j in 1:nx
x_vals[j] = update(x_vals[j], Δt)
end
Base.Cartesian.@nexprs $K k -> f_vals[k,:] .= (f[k]).(x_vals);
for j in 1:nx
Base.Cartesian.@nexprs $K k -> f_avg_vals[k,n] += f_vals[k,j]/nx
end
end
return f_avg_vals
end
end
Random.seed!(100);
x0 = randn(10);
x₀ = [1.0];
Δt = 0.5;
nΔt = 10^2;
f1(x) = sin(x)
f2(x) = x^2
compute_threaded(x0, Δt, nΔt, (f1,))
I get the error:
The function body AST defined by this @generated function is not pure. This likely means it contains a closure or comprehension.
The motivation here is that the real update
function will be very expensive and will benefit from multithreading. I am open to moving away from @generated
code, but the motivation from the earlier post still remains: I want to evaluate some collection of observable functions, on the fly, on a time series, as it is generated, and I may have a variable number of such observables for different problems.