Is there a way to change a single parameter in am ODEproblem and rerun it?

I do define a model with a set of parameters that comes from the default settings for the components.

I then want to change a parameter and rerun the problem.

I could of course set up a parameter input map

p = [
p1 => 1
p2 => 2
p3 => 3
.
.
.
p156 => 156
]

and then use varmap_to_vars and remake to get the new model. And that is usually how I do it.

But in some cases I want to avoid setting up the whole parameter map, only to change one parameter, and in those cases, I’d like to have something like the CellMLToolkit method:

p = list_params(ml)
update_list!(p, :stimulus_protocol₊IstimPeriod, 250.0)
prob = ODEProblem(ml, (0, 10000.0); p=p)

Is such a thing possible for an MTK model?

What exactly is the problem with varmap_to_vars? It is runtime concerns or
is the code not nice enough for you? Using the defaults keyword, is seems pretty close to your suggested variant and you could simply define a function update_params! if you need it often.

using ModelingToolkit: varmap_to_vars

p_new = varmap_to_vars([p156 => 157.0], parameters(sys), defaults=Dict(p))
prob_new = remark(prob, p = p_new)
2 Likes

I did not know that keyword (I looked for the documentation on varmap_to_vars and could not find that in what I found). In the cases were I have the parameter map p as described. That will be really helpful. Thanks a lot.

However, in this case p does not exist yet. So nothing to put into defaults (if I understand your example correctly).

So I guess the question is can I get p (the input map) from parameters(sys) and prob.p, which contain the two vectors that could create that?

This:

prob.p[156] = 157

does work, but requires me to now the order of the parameter vector (which I don’t do all the time, since the equations change between variation of models. Most of the time the parameter list stays in the same order, but not when I replace a simple model for a more complex one.

Something like:

prob.p[:p156] = 157

or (in my case):

prob.p[:AV.c1] = 0.75

where AV is the component, and c1 is the parameter to be changed, would be what I’m after.

Edit:
I just realised that my (seemingly obvious) idea to switch from a parameter file, which has all parameters as global parameters that are then used in the creation of the model (so no globals within the model functions themselves) to a parameter map of the model parameters, wouldn’t be practical, since I use the different parameters (with the same valuse, so only defined once) in different models.

So I’d need separate maps for different models, since MTK/DifferentialEquations doesn’t like unused parameters in the maps.

My models have between 80 and several hundred parameters. But

  • most of them will stay at their sane defaults, which are set in the functions that create the equations
  • others will be set once at the @named component creation (from those global parameter variables) and then stay the same
  • a varying subset will be defined as the optimisation/tuning parameters.

I want to avoid having to create p with all of these hundreds of lines (error prone) and have a p that only contains the last set.

So I was wondering if there is a function like that already (like the default parameter I didn’t know about) or if I need to hack one together myself.

If there isn’t I’d put a feature request on github, and possibly have a go myself to implement it properly.

I already have a reparameterise(prob, sys, p) which takes the whole parameter map p, will add the default option to that, so it deals with partial maps as well.

That’s very helpful.

Note that sys now exists in the ODEFunction, so in theory we can setup remake to use it correctly. Just no one has done it.

1 Like

Interesting. How do I get that? I only have ODESystem and ODEProblem in my case?

so in theory we can setup remake to use it correctly. Just no one has done it.

Would that mean one could use

remake(prob, p = [p156 => 157])

in order to change only the value of p156 and leave the others as they are?

yes, if someone improves the remake dispatch. It’s in prob.f.sys.

1 Like

Thanks.

I’ll put that on github as a suggestion, then. Not a high priority, but I guess it would be a neat feature. Teaching is looming, so I don’t think I can dive that deep into the code (and I’m a high-level user, so the internals are all black magic to me).

1 Like

I have played around with this a bit and found that:

Dict(parameters(prob.f.sys)[n] => prob.p[n] for n in 1:length(prob.p))

allows me to create a dict of all the parameters that are in the current problem.

So a simple function like:

function update_params(prob, pmap)
    pdict=Dict(parameters(prob.f.sys)[n] => prob.p[n] for n in 1:length(prob.p))

    remake(prob; p = varmap_to_vars(pmap, parameters(prob.f.sys), defaults=pdict))
end

does work, and allows me to do things like:

new_prob = update_params(prob, [p156 => 157])

where that vector of parameters can contain as many as we need. We can also pass a Dict instead of the vector parameter map.

Btw, at the moment we can use vectors or Dicts as parameter maps. Will MTK keep both, or/and is there a preference? The defaults parameter only takes a Dict.

Btw2, is there a way to make that a mutating function?

function update_params!(prob, pmap)
    pdict=Dict(parameters(prob.f.sys)[n] => prob.p[n] for n in 1:length(prob.p))

    prob = remake(prob; p = varmap_to_vars(pmap, parameters(prob.f.sys), defaults=pdict))
end

doesn’t seem to work.

Why not just do new_prob = remake(prob, [p156 => 157]) on the latest version? This works now.

1 Like

Great. When was that introduced? Will update and test.

Maybe a month or two ago?

Indeed, it does work. I really need to try to keep up with your release notes :slight_smile:

Ok, I had Chris’s post set as the solution, but there’s a typo in that.

It needs to be p = [ <parameter map>], so:

new_prob = remake(prob, p = [p156 => 157])

Edit: according to the changelogs, I guess this was brought in in v8.29

1 Like

I might be (very) late to the party, but if I try to use this syntax I get missing from variable map errors, while the above syntax does work. In other words, I need a function like this to update only part of the parameters:

function update_params(prob, pmap)
    params = ModelingToolkit.parameters(prob.f.sys)
    pdict = Dict(
        params[n] => prob.p[n] for n in 1:length(prob.p)
    )
    p = ModelingToolkit.varmap_to_vars(
        pmap, params, defaults=pdict
    )
    updated_prob = remake(prob; p=p)
    return updated_prob
end

as just calling remake(prob, p=pmap) does not work when pmap does not contain all parameters.

What version are you using? Can you make an MWE for MTK v9?