1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
|
.. _hoc_ste:
StateTransitionEvent
--------------------
.. hoc:class:: StateTransitionEvent
Syntax:
``ste = new StateTransitionEvent(nstate, [pointprocess])``
Description:
A StateTransitionEvent describes a finite state machine which is computed during a simulation run and moves
instantaneously from one state to another as trigger threshold conditions become true according to
transitions defined by the set of m then specifications. Generally, it is the
case that when a transition occurs, a callback is executed
`nstate` is the number of states available to the machine and must be > 0 (1 is valid). If a state index, ``istate``,
is not the destination of a :hoc:meth:`StateTransitionEvent.transition`, then the only way to reach
it is via an interpreter call to :hoc:meth:`StateTransitionEvent.state` with arg ``istate``. If ``istate`` is not
the source for a transition, then the only exit from it is when a transition enters it an the consequent callback
executes a :hoc:meth:`StateTransitionEvent.state` with arg different from ``istate``.
``The pointprocess`` arg is needed only if the simulation uses multiple threads or the local variable time
step method. (an admittedly grotesque requirement to give a hint as to which thread and cell is appropriate for
all the trigger variables specified by the transitions)
Example:
.. code-block::
python
from neuron import h
h.load_file("stdrun.hoc") # use h.run(), h.cvode, etc
soma = h.Section() # empty model not allowed.
ste = h.StateTransitionEvent(1)
tnext = h.ref(1)
def fteinit():
tnext[0] = 1.0 # first transition at 1.0
ste.state(0) # initial state
fih = h.FInitializeHandler(1, fteinit)
def foo(src): # current state is the destination. arg gives the source
print h.t, " transition ", src, int(ste.state()), " t-tnext =", h.t-tnext[0]
tnext[0] += 1.0 # update for next transition
ste.transition(0, 0, h._ref_t, tnext, (foo, 0))
print "default dt=0.025 fixed step run"
h.run()
h.steps_per_ms = 64
h.dt = 1.0/h.steps_per_ms
print "dt=1/64 fixed step run ", h.dt
h.run()
for i in [1,2]:
h.cvode.condition_order(i)
print "cvode.condition_order() = %d" % int(h.cvode.condition_order())
h.cvode_active(1)
h.run()
The results of a run are:
.. code-block::
none
$ nrniv -python temp.py
NEURON -- VERSION 7.4 (1353:fa0eeb93b0fb) 2015-07-22
Duke, Yale, and the BlueBrain Project -- Copyright 1984-2015
See http://www.neuron.yale.edu/neuron/credits
default dt=0.025 fixed step run
1.025 transition 0 0 t-tnext = 0.025
2.025 transition 0 0 t-tnext = 0.025
3.0 transition 0 0 t-tnext = 8.881784197e-15
4.0 transition 0 0 t-tnext = 2.30926389122e-14
dt=1/64 fixed step run 0.015625
1.015625 transition 0 0 t-tnext = 0.015625
2.015625 transition 0 0 t-tnext = 0.015625
3.015625 transition 0 0 t-tnext = 0.015625
4.015625 transition 0 0 t-tnext = 0.015625
cvode.condition_order() = 1
3.43225906488 transition 0 0 t-tnext = 2.43225906488
cvode.condition_order() = 2
1.0 transition 0 0 t-tnext = -1.11022302463e-16
2.0 transition 0 0 t-tnext = 0.0
3.0 transition 0 0 t-tnext = 0.0
4.0 transition 0 0 t-tnext = 0.0
5.0 transition 0 0 t-tnext = 0.0
>>>
Note that the dt=0.025 fixed step run exhibits round off errors with respect to repeated addition of dt to t
when dt is not an exact binary fraction.
Note that when dt is an exact binary fraction (1/64) and the trigger variable exactly equals the trigger
threshold, that does not constitute (triggervar - triggerthreash > 0) == true and so the transition occurs at
the end of the next step.
Note that cvode with condition order 1 uses very large time steps with this trivial model. This is not necessarily
a problem in practice as time steps are generally quite small when states are changing rapidly. However, one
should consider the benefits of condition order 2.
----
.. hoc:method:: StateTransitionEvent.state
Syntax:
``istate = ste.state()``
``ste.state(istate)``
Description:
With no args, returns the index of the current state. With an arg, sets the current state to the ``istate`` index.
When setting a state, the transitions from the previous state are deactivated and all the transitions leaving the
``istate`` index become possible during future time steps.
The user should supply a type 1 :hoc:class:`FInitializeHandler` callback to set the initial state index (and perhaps set
state dependent transition trigger threshold values)
when a new simulation run begins.
----
.. hoc:method:: StateTransitionEvent.transition
Syntax:
``ste.transition(isrcstate, ideststate, &triggervar, &triggerthresh, "statement")``
``ste.transition(isrcstate, ideststate, hocref, hocref, pycallable)``
Description:
Adds a transition from the ``isrcstate`` of the StateTransitionEvent instance to the ``ideststate``.
``Isrcstate`` and ``ideststate`` must be >= 0 and < ``nstate`` (number of states specified in the constructor).
``Isrcstate`` == ``ideststate`` is allowed.
A transition occurs when ``triggervar`` becomes greater than ``triggerthresh``. Note: a transition does NOT
occur when it merely becomes equal. Note: a transition does not occur if the isrcstate is entered and triggervar
is greater than triggerthresh. ie. triggervar must first become not greater than triggervar and then become greater
for the transition to occur.
On each time step, the transitions from a source state are checked in the order in which they are created
and the first true condition
specifies the transition to be taken. But note a subtlety with regard to the variable step methods
with cvode.condition_order(2). Since that
involves interpolation back to the time at which the threshold crossing actually occurred, the transition with
the earliest crossing will be the one actually taken.
The ``triggervar`` may be the hoc time variable t. This will work properly with threads and local variable time steps
as the system will point to the correct thread/cvode instance time. Hoc time as a ``triggerthresh``
will work correctly
only for single thread fixed and global variable step methods and otherwise allow a race condition. Note that
with multiple threads or the local variable time step method. All ``triggervar`` for a given ``ste`` need to be
in the same thread or cell as was specified by the StateTransitionEvent constructor.
In python, the syntax for a triggervar reference is, for example, h._ref_t or sec(.5)._ref_v . A reference to a
hoc variable is also allowed for a triggerthreash, but if the triggerthresh is a constant, one can declare a python
reference with triggerthresh = h.ref(value) and pass that for the ``triggerthresh`` arg.
One changes its value via the
triggerthreash[0] = ... syntax. Since the ste object keeps pointers to these values, it is very important that
triggerthresh not be destroyed unless the ste instance is also destroyed.
``statement`` or ``pycallable`` are optional arguments. They are executed when the transition takes place.
Bugs:
A time ``triggervar`` is handled the same way as any other range variable such as membrane potential. That is,
it is compared every time step to its corresponding ``triggerthresh``.
It would be more efficient in most cases to handle it as a normal time event. Perhaps a time event method will
be eventually integrated into the StateTransitionEvent class. Note that cvode.event(tevent, callback) is almost
ok as it is easy to activate the transition when entering the source state. However, one must remember to logically
deactivate it if a different transition leaving the source state takes place.
Internal pointers to ``Triggervar`` and ``triggerthresh`` do not know if those variables have been destroyed.
To avoid using freed memory, it is up to the user to avoid this possibility.
That a transition requires a threshold crossing can be occasionally limiting when one wished to check a condition
and immediately leave a state on entering it. However, the callback can change the current state and that will
become the activated state on return from the callback.
|