myokit icon indicating copy to clipboard operation
myokit copied to clipboard

Automatically set default state sensitivities for initial values that depend on parameters [edited]

Open MichaelClerx opened this issue 3 years ago • 9 comments

  • In #898, #899, #918 initial values are changed from floats to myokit.Expression objects, which are allowed to reference model variables.
  • As a simplification, states are turned into floats when simulations are created.
  • In this ticket, we ~should investigate keeping an "initial state" with expressions.~ will auto-set some of the state sensitivities so that dependencies of initial states on parameters are implemented - after that it's up to the user
  • The main sim that could benefit from this is the Simulation with sensitivities enabled. See @frankiepe 's use case with a logistic model where one parameter affects both ODEs and an initial state

To do

  • [x] Write up the test case as a technical note in https://github.com/myokit/myokit-examples
  • [x] Work out what a good (intuitive, understandable, backwards compatible) simulation API would do
  • [ ] Add tests based on above
  • [ ] Implement

MichaelClerx avatar Feb 20 '23 16:02 MichaelClerx

Proposal and draft test case at https://github.com/myokit/examples/blob/main/technical-notes/2-4-test-case-logistic-model.ipynb

MichaelClerx avatar Jun 26 '23 17:06 MichaelClerx

@frankiepe @martinjrobins comments on the questions in the proposal section would be very welcome

MichaelClerx avatar Jun 26 '23 17:06 MichaelClerx

Question 1: Alternatively, we could remove the ability to calculate dy/dy0 altogether, only allowing it via y0=f(p). This would make Myokit's language a bit simpler (remove the init keyword, remove the InitialValue expression that we introduced explicitly for this purpose). Would it make it harder to use though? Or would it make it conceptually more clear?

I'm always in favor of simplicity, so remove dy/dy0, there is no point if you can have parameters in initial conditions

Methods state() and set_state() use floats. Question 2: Should these methods set the s_state too? Or do we maintain separate methods for that?

s_state is separate from state so feels like it should have its own setter

Question 3: This means that the explicit dependence of s_state on initial states is lost. I think this is OK because you wouldn't pre-pace if part of your initial state was a parameter?

Agree this is OK, the user is explicitly setting new initial conditions

Question 4: It also means that you still have an implicit dependence on any initial state parameters from before you pre-paced. That makes less sense to me, because running a simulation now answers the question "how do my states/variables depend on the initial values at some long ago time now forgotten?" I.e. they are no longer the sensitivities w.r.t. the set initial_state. Maybe they should all just be zeroed instead? Or should we maintain the "pre paced" sensitivities w.r.t. parameters, but zero the ones for initial states?

I think sensititvies should be zeroed after the pre, by using pre you are constructing a new model (new initial conditions), so I think it makes sense that sensitivities are zeroed out.

Question 5: When I (1) Create a sim, (2) Set a parameter, (3) Run. Should I expect the evaluation of the initial_state to have been updated? With the mechanism above the answer would be no (state was set to evaluated-initial_state at creation). So a user would have to (1) Create a sim, (2) Set a constant, (3) Reset (thereby re-evaluating the initial state), (4) Run?

Perhaps you need a new way of setting initial parameters, because currently (2) seems to be aimed at altering parameters mid-simulation. Perhaps allow users to set initial parameters in the simulation constructor (and the reset method so you don't have to re-compile the sim when doing a parameter sweep)?

martinjrobins avatar Jun 26 '23 19:06 martinjrobins

Thanks @martinjrobins ! I think I agree with your first 4 answers. Would like something simpler for the 5th but will have to wait and see what we come up with :D

MichaelClerx avatar Jun 29 '23 09:06 MichaelClerx

@martinjrobins @frankiepe I've updated the test case doc again, and am interested to hear your thoughts (the important bit starts at "Requirements" and ends after "Use case 1": https://github.com/myokit/examples/blob/main/technical-notes/2-4-test-case-logistic-model.ipynb

In short, being clever in the simulation object causes problems:

  1. The default_state and default_s_state are related, i.e. if x0 = p then this implies something about dx0/dp. This means that we need to make decisions about when the user is in charge of maintaining this relationship, and when the simulation class can take over
  2. The simulation class can be clever about some, but not all initial state sensitivities. I.E. the user can specify the initial state using expressions, and this can be used to derive initial state sensitivities that depend on parameters used as independents. However, the remaining initial state sensitivities are non-zero in realistic contexts, and the user would have to do either (A) clever things or (B) a bit of pre-pacing to a steady or periodic state to set those. Both mean the benefits of having the simulation work stuff out symbolically are lost
  3. The rules for set_constant become more complicated. E.g. if affects the initial state but only after calling reset but not if the user has explicitly set a different initial state or things like that

In light of those 3, I'm thinking we do clever stuff only when a simulation is created, resulting in a simulation that only has floats (no Expressions), and leaving the clever stuff to the user?

MichaelClerx avatar Nov 13 '23 16:11 MichaelClerx

I definitely think “leaving clever stuff to the user” is the way to go here. I think my use case was quite particular and probably not something that would come up much for myokit users (?).

frankiepe avatar Nov 14 '23 12:11 frankiepe

I agree with you both :) Lets have the Simulation be clever in the __init__, but after that the user is in charge of keeping everything consistent.

martinjrobins avatar Nov 14 '23 12:11 martinjrobins

I didn't quite follow all that to be honest, but still holding out on being a proper myokit user! I agree that Frankie's use case was somewhat pathological in terms of setting weird times to get the state variable IC we wanted.

I think the only time I might expect the initial sensitivities to be set non-zero for me is if there is something like set_initial_state_variables_to_steady_state_for(-80mV) (or some other more explicit function of parameters) which I have in old matlab code. In which case you can do the sensitivity_ICs as well (at least, easy with autodiff).

mirams avatar Nov 14 '23 22:11 mirams

Bit more clarification here: https://github.com/myokit/examples/blob/main/technical-notes/1-3b-cvodes-initial-values.ipynb

MichaelClerx avatar Nov 20 '23 13:11 MichaelClerx