I asked a question at stackoverflow about how to properly design a model. I’m still unsure how to do it, partly because I have this old design that no longer works and that I’m trying to rescue, and partly because I’m struggling with the basics: struct, method, functor, abstract types, concrete types. I’ve read relevant portions of the docs, but as I’m no programmer I’m still far from grasping the basics.
The way I had things set up back in Julia 0.5 times, was something like this:
- an immutable struct to declare types for parameter values and functional forms (that depend on the parameters).
- a method with concrete types and default values
- This
Model
was then manipulated to conduct experiments to see the effect of changing parameter values and functional forms. I had these stored in separate files and would load them in a loop and produce tables of results for each (each combination of parameters and functional forms corresponded to some reasonably well-known paper in the field). - I was able to write something like
sol = solve(Model(p = p1))
, wherep1
was one particular combination of parameters and functional forms.
That code broke and I’m in the process of rewriting it. I would like to adopt the most appropriate design. But after discussing the issue over at stackoverflow, it seems that fixing the old design is difficult and possibly not advisable. Below is where I’m currently at: a model with one parameter and one function, which I want to be able to change (idiomatically and efficiently of course).
I’m not tied to this particular piece of code. What I really want is to be able to do something like Model(p = p1)
or Model(p = p1, f = f1)
, as I don’t mind keeping my model functional forms f1
separate from my model parameters p1
, if that’s recommended.
# Define an abstract type for the model's functions
abstract type ModelParameter end
abstract type ModelFunction end
# Define a struct for functional form F
Base.@kwdef struct F{ModelParameter} <: ModelFunction
p::ModelParameter = 1.11
end
# Define a method for functional form F
(ff::F)(x) = x + ff.p # I read a hint about using functors, but this looks ugly.
# Define a struct for functional form G
Base.@kwdef struct G{ModelParameter} <: ModelFunction
p::ModelParameter = 2.22
end
# Define a method for functional form G
(gg::G)(x) = x - gg.p
# Wrap one function in a struct, where the function is defined inline (the design I used to have)
Base.@kwdef struct Model1{ModelParameter, ModelFunction}
p::ModelParameter = 2.0
f::ModelFunction = x -> x + p
end
julia> Model1(p=1.11).f(1.0)
2.11
julia> Model1(p=2.22).f(1.0)
3.22 # "WORKING!"
# Wrap one function in a struct, where the function is defined outside (as was suggested to me at stackoverflow).
Base.@kwdef struct Model2{ModelParameter, ModelFunction}
p::ModelParameter = 2.0
f::ModelFunction = F()
end
julia> Model2(p=1.11).f(1.0)
2.11
julia> Model2(p=2.22).f(1.0)
2.11 # NOT WORKING!
Ultimately I want to be able to manipulate the parameters and functional forms,
e.g. changing the parameter p and the function f to G instead of the default F
julia> Model2(p=2.22, f=G()).f(1.0) # HOW CAN I GET TO DO THIS OR SIMILAR?