I’m using a parameter structure for use in an economic model in Julia. I’d like to know what are the best practices here, including the best way to include restrictions when generating and modifying a parameter structure. Let me give you an MWE of the structure I’m using:
using Parameters
@with_kw mutable struct Param
β::Float64 = 0.95 # discount factor
ϕ::Float64 = 0.12 # relative taste for housing
γ::Float64 = 0.8 # ES parameter between consumption and housing
βsq::Float64 = β^2 # squared discount factor
end
I then do p=Param(). Let’s say I wish to change β to 0.9. Two issues come up, that I’d like to overcome:
I want βsq to update accordingly, if possible.
I want to introduce restrictions on these variables. Say 0<β<1.
What are the best practices to do this (including abandoning the use of structures)?
Regarding you final question, I’ve found that if I am just dealing with individual parameters, I am often better off using named tuples. I only reach for structs when I am also passing around more complicated structures.
Given how lightweight your struct is, I’d guess the performance you might get with mutability might not be worth the hassle. An immutable approach (using simpler letters bc I’m on a phone) might look like this:
struct Params
a::Real
b::Real
bsq::Real
# mask the default constructor
Params(a::Real = 0.5, b::Real = 0.5) = new(a, b, b^2)
end
# an "update" constructor, you can also call this `update` instead of making it a constructor
Params(old::Params; a=nothing, b=nothing) =
Params(isnothing(a) ? old.a : a, isnothing(b) ? old.b : b)
Here is what I would do that addresses both concerns
function paramsfun(a,b)
@assert 0 < b < 1 "b is must be between 0 and 1"
return (; a, b, bsq=b^2)
end
Because you are only working with a few scalar values, creating named tuples is going to be lightweight and easy. You don’t have to worry about working “in place”. This thing will also place nicely with AD and lots of other neat tools for free.
I believe the docs would suggest not to use the types as you’ve put them.
Ah, yes, for better performance it should be typed like this:
struct Params {T<:Real}
a::T
b::T
bsq::T
end
I’m interested in this lightweight approach of using a tuple instead of an immutable struct, but I can’t seem to figure out how I’d write and update function to make it easy to get a new parameters tuple with just one or two values changed.
@tbeason can you show an example of updating parameters in the NamedTuple approach?
You can use packages for that if you want an easy way to do it. In truth I never use these but now that you’ve forced me to think about it I will probably do this going forward…
using BangBang
julia> p = paramsfun(0.8,0.95)
(a = 0.8, b = 0.95, bsq = 0.9025)
julia> p = setproperties!!(p;a=0.5)
(a = 0.5, b = 0.95, bsq = 0.9025)