I am wondering how to loop over the elements of a structure by their names?
Big-picture, I have a structure which contains the parameters of an economic model, some of which are functions of other parameters. I would like to see how the model output changes when I vary a subset of parameters one at a time. Importantly, I want to make sure the parameter dependencies remain. For instance, in the MWE below, I’d like to have c and d also update when I update a and b. I believe this precludes me using something like SetField. I think it’d be easy to do this if I put the parameters in an array, but I’d like to be able to access them by name.
Concretely, I’m trying to see how particular simulated moments change and how the difference between the data moments and simulated moments changes.I haven’t figured out a way to do this which doesn’t involve copying the same block of code for each parameter, which is roughly what I’m doing in the testfunc() example below.
What I’d like is to have something like testfunc2(), which errors out because I cannot get the iterator to be recognized as a valid input to the myparams() structure. Per some previous threads in the discord I’ve tried experimenting with using metaprogramming in testfunc3(). This works in the REPL, but not otherwise, and I’m not sure if something like this is overkill for this type of problem.
@with_kw struct myparams
a::Float64 = 0.03
b::Float64 = 0.95
c::Float64 = 1 / (1 + a)
d::Float64 = a * b
end
function myobjfunc(p::myparams)
@unpack a, b, c, d = p
return (a + b) * (c + d)
end
function testfunc()
p0 = myparams()
steps = collect(0.5:0.1:1.5)
vals = Array{Float64}(undef, 2, length(steps))
for i = eachindex(steps)
p = myparams(a=p0.a * steps[i])
vals[1, i] = myobjfunc(p)
p = myparams(b=p0.b * steps[i])
vals[2, i] = myobjfunc(p)
end
return vals
end
function testfunc2()
p0 = myparams()
steps = collect(0.5:0.1:1.5)
vals = Array{Float64}(undef, 2, length(steps))
iter = 0
for symb in (:a, :b)
iter += 1
for i = eachindex(steps)
p = myparams(symb=p0.symb * steps[i])
vals[iter, i] = myobjfunc(p)
end
end
return vals
end
function testfunc3()
p0 = myparams()
steps = collect(0.5:0.1:1.5)
vals = Array{Float64}(undef, 2, length(steps))
iter = 0
for symb in (:a, :b)
iter += 1
for i = eachindex(steps)
ex = :(myparams($symb=p0.$symb * steps[i]))
p = eval(ex)
vals[iter, i] = myobjfunc(p)
end
end
return vals
end
testfunc()
2×11 Matrix{Float64}:
0.96449 0.967437 0.9704 0.973379 0.976375 0.979386 0.982414 0.985459 0.988519 0.991596 0.994689
0.497488 0.592784 0.688623 0.785002 0.881924 0.979386 1.07739 1.17594 1.27502 1.37465 1.47482
testfunc2()
ERROR: type myparams has no field symb
testfunc3()
ERROR: UndefVarError: p0 not defined
Any advice would be greatly appreciated! This seems it should be simple to implement, so I’ve been a bit frustrated by how it has stumped me…