-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pre-defined parametrization of the model #69
Comments
Hm, would this not only require What exactly would be the differences between |
I think no duplication are needed, because there are already a duplication made (with multiple dispatch) for some functions used in
struct YodzisParameters <: ModelParameters
network <: EcologicalNetwork
biorates <: YodzisBioRates
...
end
I think the big pro of this, is that for ecologist knowing the model, it will be way clearer this way, to do the distinction between both models on the object that stores all the parameters and not only the functional response. Because currently we are mixing some parameters of both models. Typically, |
Okay, the nature of the need is becoming clearer. There are still quite a few options to extend
The concern I'm having right now is not only that the amount of code changes required to distinguish the two flavours sounds big, but that the amount of code changes required to add a third flavour would be as big, and as big again for a fourth flavour etc. until the sources become a mess. Also, the solution you are thinking of duplicates fields like Here is one other approach to achieve the same results, but without exposing too much internals. The idea is to flag the core struct ModelParameters
flavour::String # "Brose" or "Yodzis" or "Furnob" etc.
network::EcologicalNetwork
biorates::Biorates
environment::Environment
end
struct BroseBioRates
a::Float64
b::Float64
c::Float64 # not used with the "Yodzis" flavour.
end .. this is easy to understand and it keeps all possible models flavour under one single type from the user POV. But it does not leverage Julia's type system, and there is this unsatisfactory function package_function(p::ModelParameters, ...)
if (p.flavour == "Yodzis")
...
elseif (p.flavour == "Brose")
...
end
end One interesting pattern to improve the above design revolves around the idea of tagged unions. They are not native in Julia, but you could craft them with something along: # The official list of flavours.
@enum ModelFlavour Brose Yodzis Furnob
# The exact data needed for Brose.
struct BroseBiorates
a::Float64
b::Float64
end
# The exact data needed for Yodzis.
struct YodzisBiorates
a::Float64
b::Float64
c::Float64
end
# The only type exported to user.
struct ModelParameters
flavour::ModelFlavour
network::EcologicalNetwork
biorates::Union{BroseBiorates, YodzisBiorates} # Runtime type always corresponds to `flavour`.
environment::Environment
end
@export ModelParameters This way, you can write package functions with type-based dispatching like: function package_function(br::BroseBiorates) ... end
function package_function(br::YodzisBiorates) ... end
function simulate(p::ModelParameters, ...) # <- no change needed
package_function(p.biorates) # <- correctly dispatched
# ...
end Pros: code changes do not propagate to functions needing Cons: developers need to guarantee that |
First, here are the answers to your question.
No, this seems very improbable to me.
No, or at least no in the framework I'm thinking about. But may be @alaindanet could argue against that? Don't know.
For this I have to dive back into the models, because I don't remember if there is a 1 to 1 mapping between these two models (I think so but no sure). If so, it could totally make sense to convert one to another, otherwise it would ambiguous.
No.
No. Secondly, I really like your suggestion and think it's better than what I had in mind. Just a small technical question I have, I don't understand why you need this line / what this line is doing
|
Great, well let's go for tagged unions then :) This line: @enum ModelFlavour Brose Yodzis Creates an enum type. It's new type named julia> typeof(ModelFlavour) # This is actually a type, like `ModelParameters`.
DataType
julia> ModelFlavour # Summary of the enum type.
Enum ModelFlavour:
Brose = 0
Yodzis = 1
julia> Brose # First possible value. Under the hood it's just a `0` integer, but it's *named*.
Brose::ModelFlavour = 0
julia> typeof(Brose) == ModelFlavour
true
julia> Yodzis # Second possible value.
Yodzis::ModelFlavour = 1
julia> ModelFlavour(0) === Brose # same value.
true
julia> ModelFlavour(1) === Yodzis # same value.
true
julia> ModelFlavour(2) # Error: This enum has only 2 possible variants.
ERROR: ArgumentError: invalid value for Enum ModelFlavour: 2 To compare them, instead of: if p.flavour == "Yodnis" # wops, mistake you'd just if p.flavour == Yodzis # mistake is impossible Same, instead of creating Yodzis models with: p = ModelParameters(foodweb, flavour = "Yodziz") # wops! the user would p = ModelParameters(foodweb, flavour = Yodzis) So:
.. yeah, enums are really cool :) |
I didn't know about enums, it is really nice! Thanks for the tip! |
I suppose there is a slight shift in the issue now that #131 is about to land @ismael-lajaaiti? Does it become something like "Add Yodzis as an alternate set of defaults" instead? |
Mmmh, I am not how to transpose this issue to the new API, I have to admit... |
Okay. I'll consider this closed by #132, then. Feel free to reopen if |
Disentangle the parametrisation for the Yodzis model and the Brose model by creating two sub-type of the
ModelParameters
abstract type (e.g.YodzisParameters
andBroseParameters
), these types should be coupled with differentdBdt!
which can be achieved with multiple dispatch or by defining different type of ODEProblem as done here.The text was updated successfully, but these errors were encountered: