Function to determine number of parameters in a differential equation model function

I want a function to determine the number of parameters in a differential equation model but I’ve been unable to figure out what function I’m looking for.

Here’s a MWE (differential equation from this example) where count_ODE_parameters is the function I’m looking for.

function lotka_volterra(du, u, p, t)
    # Model parameters.
    α, β, γ, δ = p
    # Current state.
    x, y = u

    # Evaluate differential equations.
    du[1] = (α - β * y) * x # prey
    du[2] = (δ * x - γ) * y # predator

    return nothing
end

count_ODE_parameters(lokta_volterra) # Gives 4, as there are 4 parameters in the Lokta-Volterra model

The greater context is that I want to compare ODE systems using Akaite’s Information Criterion (AIC) which depends on the number of parameters in an ODE system, so a generic function that allows me to input separately defined differential equations is preferred here as I’ll be comparing more than one ODE model.

I know a work-around is to count the number of parameters from initial estimates while fitting experimental data to the ODE models.

If such a function is not possible, I’d welcome some reading on why e.g. information about a function’s properties and how to access them, or how the design came to be this way.

Welcome to the Julia discourse! Since you are new here, I’ll assume you are newish to Julia as well.

Indeed, taking literally what you wrote, such a function is not possible. I think there might be two misconceptions at play here and I’ll try to clarify them one-by-one.

Language-level Misconception: What is a ‘function’ in Julia?

Considering the code

What is now lotka_volterra referring to?

The short answer is: it is a function. In Julia, all functions are so-called generic functions, which means they use multiple dispatch to determine the actual method to execute. In this example, the generic function called lotka_volterra has 1 method which takes 4 arguments of type Any. You can check this in the REPL with methods(lotka_volterra).

Now you can call the function with any 4 arguments and Julia dispatches the call to the method corresponding to the code you wrote above. To actually execute something, Julia compiles a so-called MethodInstance which is a variant of the method for the concrete types you called it with. Each time you use arguments with different types, you get a new MethodInstance.

Side Note: This function specialization is the reason Julia can be so performant.

So does it make sense to ask the generic function what arguments it takes? No because that depends upon it’s methods.

Does it make sense to ask a method what arguments it takes? Maybe but most likely not because the argument types of methods are just used for dispatch and thus will be too wide (like in the example all 4 arguments are of type Any).

So from a language-level perspective count_ODE_parameters(lokta_volterra) is just ill-posed because the generic function lotka_volterra may contain many methods with different signatures each of which might have different MethodInstances for different sets of argument types. For your question to have a meaningful answer, you need the concrete types of the input arguments.

DiffEq.jl misconception: What is an ODE system?

Ok so I think your post was made with DifferentialEquations.jl in mind since the function looks exactly like DiffEq.jl requires.
I’d like to point out that so far the definition of lotka_volterra is just a usual Julia function and there is nothing that indicates that this is in any way related to ODEs. So perhaps it is not the best object to use for ODE-specific questions.

When using DifferentialEquations.jl, you’d use the function lotka_volterra to construct an ODEProblem and this gives it the meaning of an ODE system. The ODEProblem is constructed like ODEProblem(f, u0, t, p) which means it contains concrete parameters! I think this looks quite promising because given an ODEProblem you can access it’s parameters via problem.p.

Proposal

Your current approach is ill-posed in its generality. I suggest that you could define a function like count_ODE_parameters that takes an ODEProblem instead and then looks at the parameter object of that problem. The only issue is that this can be an arbitrary object containing arbitrary data but you can probably make a best effort solution by impleting ways to extract parameter counts for the most common ways people use the parameters (probably Tuple, NamedTuple, AbstractVector, structs).

3 Likes

Just to add to @abraemer’s excellent answer: There is also the package ModelingToolkit.jl (MTK) which provides another layer on top of bare ODEProblems. You can define a problem with its variables and parameters there in terms of symbols and then transform it to a “bare” ODEProblem to be solved.

If the additional overhead of MTK is not an issue, maybe that’s what you are looking for in the end.

1 Like