arbor-sim / arbor

The Arbor multi-compartment neural network simulation library.
https://arbor-sim.org
BSD 3-Clause "New" or "Revised" License
108 stars 60 forks source link

Change mechanism parameters during simulation #1705

Open llandsmeer opened 3 years ago

llandsmeer commented 3 years ago

Describe the feature you need Change global and/or normal parameters of point/density mechanisms and gap junction mechanisms during simulation from the simulation script.

Explain what it is supposed to enable The easiest path would be:

Another option would be being able to send/receive custom events in mechanisms, receivable by both point/density and gap junction mechanisms

Additional context The use case is mostly in experimental procedures: Apply a certain mechanism for a certain amount of time, disable it then enable it some time later on. Then disable gap junctions halfway the simulation. Sweep through gap junction conductance in small stept to search for a bifurcation point.

# let controller do it's thing, discard transient
sim.run(1*10000)
save_probe_data(filename='transient.npz', sim)
sim.clear_samplers()

# how do the cells work without controller
sim.send_mechanism_event(cells='allcells', mechanism='cal', event='toggle_controller_off')
sim.run(2*10000)
save_probe_data(filename='steady_state.npz', sim)
sim.clear_samplers()

# now switch half the population to a different regime and record
sim.set_mechanism_parameter(cells=[...,], mechanism='cal', variable='gmax_scale', value=0.5)
# or sim.set_mechanism_global(mechanism='cal', variable='gmax_scale_nonosc', value=0.5)
sim.run(3*10000)
save_probe_data(filename='two_populations.npz', sim)
sim.clear_samplers()

# now delete the gap junctions and record behaviour
for cellid, gj_id in gap_junction_list:
    sim.set_gap_junction_weight(cellid, gj_id, weight=0)
sim.run(4*10000)
save_probe_data(filename='unconnected.npz', sim)
llandsmeer commented 3 years ago

For the moment this is solvable by encoding switch timepoints in the mechanisms itself, but that requires editing and recompiling the mechanisms everytime the experimental procedure changes. And those timepoints are also not guaranteed to be at the same time as sim.run(tfinal) calls

schmitts commented 3 years ago

This feature would also come in handy for FIPPA, for, e.g., diffusion.

llandsmeer commented 3 years ago

Ok this issue became more important to me as I just realized that I need to do things that are impossible via switchtime parameters in NMODL files. I need a loop that runs an unspecified amount of times, alternating between different mechanisms and mechanism parameters until the entire network behaves in a certain way.

noraabiakar commented 3 years ago

@llandsmeer I have a couple of questions:

Another option would be being able to send/receive custom events in mechanisms, receivable by both point/density and gap junction mechanisms.

Is this an alternative to changing mechanism parameters during a simulation? Allowing events on gap-junction is not something we have considered.

I see that send_mechanism_event is part of the proposed API. What is an event in this case?

I need a loop that runs an unspecified amount of times, alternating between different mechanisms and mechanism parameters until the entire network behaves in a certain way.

This loop is at the level of the simulation? using the proposed API?

llandsmeer commented 3 years ago

The key is that I need some method to change mechanism parameters from outside the mechanism - that could either be via direct parameter editing of by sending 'events' on the mechanisms s.t. it can update itself. What the 'event' is doens't matter - it could just be calling a function on the mechanism.

This loop is at the level of the simulation? using the proposed API?

Yes. So update a few parameters - measure network response - repeat until network behaves in the right way

thorstenhater commented 3 years ago

For the usecase you describe I would expect that you also require a full reset to avoid contamination by residual state. So basically:

  1. Set up simulation
  2. Run burn-in phase until equilibrium is reached
  3. Save snapshot
  4. Tweak parameters
  5. Observe response
  6. Restore snapshot.
  7. Go to 3.

Is that what you aim for?

llandsmeer commented 3 years ago

The full reset is not needed and will actually slow down the process but this is indeed the way I'd implement this right now

noraabiakar commented 2 years ago

See related issue: https://github.com/arbor-sim/arbor/issues/632

thorstenhater commented 2 years ago

Just checking in:

llandsmeer commented 2 years ago

Luckily I haven't had the need to touch that part of the model for a while, but this change would still very much improve the methods. Among others, I could discard the custom cuda code I'm using to update model parameters using information from different cells. And it would allow for control in compartments that exist of more than a single CV (that's something my code definitely doesn't handle). In general this would be a nice improvement.

ie what's the expected behaviour here?

Good question. It think does show a flaw in my ideas w.r.t the set_mechanism_parameter function. Maybe a better way (that also solves the current problem that changing params is impossible because not enough information is retained to do this externally) would be the following:

# ...
decor.paint('(tag 1)', density('hh', g=42))
hh_param_array_pos = decor.paint_and_remember_parameter_array_positions('(tag 2)', density('hh', g=23))
# later on
hh_param_array_pos.set_parameter(parameter='g', value=3.141)

E.g. ask explicitely for remembering where the parameters is stored (either via another function or a flag passed to paint), and later use that information to change parameter values.

Although that would probably break in different ways. Maybe a similar api to the probes (which can ask specific states vars from specific mechanisms) would be a better idea?

thorstenhater commented 2 years ago
hh_param_array_pos = decor.paint_and_remember_parameter_array_positions('(tag 2)', density('hh', g=23))

That is a bit more tricky to implement than I would like, the 'positions' only appear after the cell has been converted into its discrete representation. Doable sure and maybe better than my idea, see below. If we go down this route, I'd prefer this though

# ...
decor.paint('(tag 1)', density('hh', g=42))
decor.paint('(tag 2)', density('hh', g=23), label='my-hh-region') # optional tags allow setting
# later on
sim.set_parameter(label='my-hh-region', parameter='g', value=3.141)

mainly due to the reason that the cell/decor description might be inside the recipe and it might be hard to extract inyour proposal.

I was thinking about this originally

# ...
decor.paint('(tag 1)', density('hh', g=42))
decor.paint('(tag 2)', density('hh', g=23))
# later on
sim.set_parameter(gid=123, region='(tag 2)', mechanism='hh', parameter='g', value=3.141)

which has its own issues, ie having to retain the recipe and re-reify the cell and potentially the whole cell group (to figure out the indices). However, it's a bit more flexible, since it allows to tweak sub-regions of a mechanisms.

llandsmeer commented 2 years ago

Yes the save-on-paint method definitely has a lot of downsides w.r.t. bookkeeping. I'm happy with either

decor.paint('(tag 2)', density('hh', g=23), some_kind_of_label='my-hh-region')
sim.set_parameter(gid=123, that_label='my-hh-region', parameter='g', value=3.141)

or

sim.set_parameter(gid=123, region='(tag 2)', mechanism='hh', parameter='g', value=3.141)

It's nice that the latter allows for more flexibility but it also sounds harder to implement