Proper way to identify variables/models with functions

Recently I revisited some old Julia code of mine and wondered how to implement it properly.

The setting is: I have a model struct which holds some model definition. Let’s say, each model just describes some system of equations which we want to solve for all variables x[i]:

struct model
    equations::String # just to simplify (no strings in reality)
end

a = model("x[1] = x[2]     and x[2] = 0")
b = model("x[1] = x[2] + 1 and x[2] = 0")

Therefore, I want to have a function solve(foo::model) which returns [0, 0] for a and [1, 0] in the case of b. For that reason, I have some macro which autogenerates the functions

function f_a!(F, x)
    F[1] = x[2] - x[1]
    F[2] = -x[2]
end

and

function f_b!(F, x)
    F[1] = x[2] + 1 - x[1]
    F[2] = -x[2]
end

These allow us to formulate the model problem as F(x) = 0 and we can plug the respective function into NLsolve.jl.

My simple question is now: How do we properly identify the model foo with the function f_foo! inside the function solve? After all, all models have the same type. My first intention is to just add some field id or name which can then be used inside solve. However, assume we have 1000 models loaded, then this is associated with runtime costs.

We could also have a new field f!::Function in every model which holds the correct function. This seems kinda odd.

Furthermore, if I define a new type for every model instead, it is not a good user experience. Of course I could hide them with some kind of composition:

struct model
    equations <: AbstractEquations
end

and then have the structs Equations1, Equations2 etc. for each new model.
But I guess I am overthinking this way too much. Any suggestions?

Seems like a reasonable approach if you want to create many independent objects, so not rely on dispatch with lots of types.

It’s also what, e.g., SciML does to define their problem types, so I don’t think it’s that odd :slight_smile:

But (as in the example) it’s probably better to use the concrete type of the model function as a parameter, instead of f!::Function (otherwise it will be an abstract type for the field, which is usually worse than a concretely typed field).

1 Like