diff --git a/doc/nestml_language/nestml_language_concepts.rst b/doc/nestml_language/nestml_language_concepts.rst
index a02d86b74..acf1c852b 100644
--- a/doc/nestml_language/nestml_language_concepts.rst
+++ b/doc/nestml_language/nestml_language_concepts.rst
@@ -758,7 +758,7 @@ Expressions in NESTML can be specified in a recursive fashion.
Terms
~~~~~
-All variables, literals, and function calls are valid terms. Variables are names of user-defined or predefined variables (``t``, ``e``).
+All variables, literals, and function calls are valid terms.
List of operators
~~~~~~~~~~~~~~~~~
@@ -818,107 +818,6 @@ Block types
- ``onCondition`` - Contains statements that are executed when a particular condition holds. The condition is expressed as a (boolean typed) expression. The advantage of having conditions separate from the ``update`` block is that a root-finding algorithm can be used to find the precise time at which a condition holds (with a higher resolution than the simulation timestep). This makes the model more generic with respect to the simulator that is used.
-Input
------
-
-A model written in NESTML can be configured to receive two distinct types of input: spikes and continuous-time values.
-
-
-Continuous-time input ports
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Continuous-time input ports receive a time-varying signal :math:`f(t)` (possibly, a vector :math:`\mathbf{f}(t)`) that is defined for all :math:`t` (but that could, in practice, be implemented as a stepwise-continuous function of time).
-
-
-Spiking input ports
-~~~~~~~~~~~~~~~~~~~
-
-The incoming spikes at the spiking input port are modelled as Dirac delta functions. The Dirac Delta function :math:`\delta(x)` is an impulsive function defined as zero at every value of :math:`x`, except for :math:`x=u`, and whose integral is equal to 1:
-
-.. math::
-
- \int \delta(x - u) dx = 1
-
-The unit of the Dirac delta function follows from its definition:
-
-.. math::
-
- f(0) = \int \delta(x) f(x) dx
-
-Here :math:`f(x)` is a continuous function of x. As the unit of the :math:`f()` is the same on both left- and right-hand side, the unit of :math:`dx \delta(x)` must be equal to 1.
-Therefore, the unit of :math:`\delta(x)` must be equal to the inverse of the unit of :math:`x`.
-
-In the context of neuroscience, the spikes are represented as events in time with a unit of :math:`\text{s}`. Consequently, the delta pulses will have a unit of inverse of time, :math:`\text{1/s}`.
-Therefore, all the incoming spikes defined in the input block will have an implicit unit of :math:`\text{1/s}`.
-
-Physical units such as millivolts (:math:`\text{mV}`) and nanoamperes (:math:`\text{nA}`) can be directly combined with the Dirac delta function to model an impulse with a physical quantity such as voltage or current.
-In such cases, the Dirac delta function is multiplied by the appropriate unit of the physical quantity, such as :math:`\text{mV}` or :math:`\text{nA}`, to obtain a quantity with units of volts or amperes, respectively.
-For example, the product of a Dirac delta function and millivolt (:math:`\text{mV}`) unit can be written as :math:`\delta(t) \text{mV}`. This can be interpreted as an impulse in voltage with a magnitude of one millivolt.
-
-
-Handling spiking input
-~~~~~~~~~~~~~~~~~~~~~~
-
-Spiking input can be handled by convolutions with kernels (see :ref:`Integrating spiking input`) or by means of ``onReceive`` event handler blocks. An ``onReceive`` block can be defined for every spiking input port, for example, if a port named ``pre_spikes`` is defined, the corresponding event handler has the general structure:
-
-.. code-block:: nestml
-
- onReceive(pre_spikes):
- println("Info: processing a presynaptic spike at time t = {t}")
- # ... further statements go here ...
-
-The statements in the event handler will be executed when the event occurs.
-
-To specify in which sequence the event handlers should be called in case multiple events are received at the exact same time, the ``priority`` parameter can be used, which can be given an integer value, where a larger value means higher priority. For example:
-
-.. code-block:: nestml
-
- onReceive(pre_spikes, priority=1):
- println("Info: processing a presynaptic spike at time t = {t}")
-
- onReceive(post_spikes, priority=2):
- println("Info: processing a postsynaptic spike at time t = {t}")
-
-In this case, if a pre- and postsynaptic spike are received at the exact same time, the higher-priority ``post_spikes`` handler will be invoked first.
-
-
-Output
-------
-
-Each model can only send a single type of event. The type of the event has to be given in the `output` block. Currently, however, only spike output is supported.
-
-.. code-block:: nestml
-
- output:
- spike
-
-Calling the ``emit_spike()`` function in the ``update`` block results in firing a spike to all target neurons and devices time stamped with the simulation time at the end of the time interval ``t + timestep()``.
-
-Event attributes
-~~~~~~~~~~~~~~~~
-
-Each spiking output event can be parameterised by one or more attributes. For example, a synapse could assign a weight (as a real number) and delay (in milliseconds) to its spike events by including these values in the call to ``emit_spike()``:
-
-.. code-block:: nestml
-
- parameters:
- weight real = 10.
-
- update:
- emit_spike(weight, 1 ms)
-
-If spike event attributes are used, their names and types must be given as part of the output port specification, for example:
-
-.. code-block:: nestml
-
- output:
- spike(weight real, delay ms)
-
-The names are only used externally, so that other models can refer to the correct attribute (such as a downstream neuron that is receiving the spike through its input port). It is thus allowed to have a state variable called ``weight`` and an output port attribute by the same name; the output port attribute name does not refer to names declared inside the model.
-
-Specific code generators may support a specific set of attributes; please check the documentation of each individual code generator for more details.
-
-
Equations
---------
@@ -1095,20 +994,268 @@ A Dirac delta impulse kernel can be defined by using the predefined function ``d
kernel g = delta(t)
+Output
+------
+
+Each model can only produce a single output. The type of the event has to be given in the `output` block. Currently, only spike output is supported.
+
+.. code-block:: nestml
+
+ output:
+ spike
+
+Calling the ``emit_spike()`` function in the ``update`` block results in firing a spike to all target neurons and devices time stamped with the simulation time at the end of the time interval ``t + timestep()``.
+
+Each spiking output event can optionally be parameterised by one or more attributes. For example, a synapse could assign a weight (as a real number) and delay (in milliseconds) to its spike events by including these values in the call to ``emit_spike()``:
+
+.. code-block:: nestml
+
+ parameters:
+ weight real = 10.
+
+ update:
+ emit_spike(weight, 1 ms)
+
+If spike event attributes are used, their names and types must be given as part of the output port specification, for example:
+
+.. code-block:: nestml
+
+ output:
+ spike(weight real, delay ms)
+
+The names are only used externally, so that other models can refer to the correct attribute (such as a downstream neuron that is receiving the spike through its input port). It is thus allowed to have a state variable called ``weight`` and an output port attribute by the same name; the output port attribute name does not refer to names declared inside the model.
+
+Specific code generators may support a specific set of attributes; please check the documentation of each individual code generator for more details.
+
+
+Input
+-----
+
+A model written in NESTML can be configured to receive two distinct types of input: spikes and continuous-time values.
+
+
+Continuous-time input ports
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Continuous-time input ports receive a time-varying signal :math:`f(t)` (possibly, a vector :math:`\mathbf{f}(t)`) that is defined for all :math:`t` (but that could, in practice, be implemented as a stepwise-continuous function of time).
+
+For example, the following will add an external signal :math:`f(t)` with units of 1/s to a dynamical variable named :math:`x`.
+
+.. code-block:: nestml
+
+ state:
+ x real = 0
+
+ parameters:
+ tau ms = 20 ms
+
+ equations:
+ x' = -x / tau + f
+
+ input:
+ f 1/s <- continuous
+
+
+Spiking input ports
+~~~~~~~~~~~~~~~~~~~
+
+The incoming spikes at the spiking input port are modelled as Dirac delta functions. The Dirac delta function :math:`\delta(x)` is an impulsive function defined as zero at every value of :math:`x`, except for :math:`x=0`, and whose integral is equal to 1:
+
+.. math::
+
+ \int \delta(t) dt = 1
+
+The unit of the Dirac delta function follows from its definition:
+
+.. math::
+
+ f(0) = \int \delta(t) f(t) dt
+
+Here :math:`f(t)` is a continuous function of :math:`t`. As the unit of the :math:`f()` is the same on both left-and right-hand side, the unit of :math:`dt \delta(t)` must be equal to 1. Therefore, the unit of :math:`\delta(t)` must be equal to the inverse of the unit of :math:`t`, that is :math:`s^{-1}`. Therefore, all the incoming spikes defined in the input block will have an implicit unit of :math:`\text{1/s}`.
+
+Given an input port named ``spikes_in``, the semantics of using this name in expressions and ODEs is that it should be understood as a train of delta pulses:
+
+.. math::
+
+ \mathrm{spikes\_in}(t) = \sum_k \delta(t - t_k)
+
+The units are the same as for a single delta function.
+
+Each spike event can optionally contain one or more attributes, such as weight or delay. These are given numerical values by the sending side when calling ``emit_spike()``, and are read out by the receiving side, by appending a dot (fullstop) to the name of the spiking input port and then writing the name of the attribute.
+
+For example, say there is a train of weighted spike events, with each event :math:`k` having weight :math:`w_k`:
+
+.. math::
+
+ \mathrm{spikes\_in}(t) = \sum_k w_k \delta(t - t_k)
+
+A spiking input port that is suitable for handling these events could be defined as such:
+
+.. code-block:: nestml
+
+ input:
+ spikes_in <- spike(w real)
+
+Note that the units of ``spikes_in.w`` are again in 1/s, as ``w`` has been defined as a dimensionless real number. If a physical unit is specified (such as pA or mV), the numeric value of the attribute is interpreted as having the units given in the definition of the input port. For example, if :math:`w_k` is assumed to be in units of mV, then in combination with the 1/s unit of the delta train, the units of ``spikes_in.w`` would be in mV/s, and the input port can be defined as follows:
+
+.. code-block:: nestml
+
+ input:
+ spikes_in <- spike(w mV)
+
+In general, spiking input can be processed by referencing the input port in the right-hand side of an equation (see :ref:`Handling spiking input in equations`) or by means of ``onReceive`` event handlers (see :ref:`Handling spiking input by event handlers`).
+
+
+Handling spiking input in equations
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The spiking input port name ``spikes_in`` can be used directly in the right-hand side of ODEs:
+
+.. math::
+
+ \frac{dx}{dt} = -\frac{x}{\tau} + \mathrm{spikes\_in}(t)
+
+If ``x`` is a real number, then the units here are consistent (in 1/s) on left- and right-hand side of the equation. This can be written in NESTML as:
+
+.. code-block:: nestml
+
+ x' = -x / tau + spikes_in
+
+``spikes_in`` can also be used inside a convolution; for instance, if ``K`` is a kernel, then:
+
+.. math::
+
+ \frac{dx}{dt} = -\frac{x}{\tau} + \frac{1}{C} \left(K \ast \mathrm{spikes\_in}\right)
+
+Note that applying the convolution means integrating over time, hence dropping the [1/s] unit, leaving a unitless quantity (the function of time (:math:`K \ast \mathrm{spikes\_in}`). To make the units consistent in this case, an explicit division by time (such as by a constant :math:`C` with units [s]) is required.
+
+This can be written in NESTML as:
+
+.. code-block:: nestml
+
+ x' = -x / tau + convolve(K, spikes_in) / C
+
+Physical units such as millivolts (:math:`\text{mV}`) and picoamperes (:math:`\text{pA}`) can be directly combined with the Dirac delta function to model an impulse with a physical quantity such as voltage or current. In such cases, the Dirac delta function is multiplied by the appropriate unit of the physical quantity to obtain a quantity with units of volts or amperes, for instance, if ``I`` is in ``pA``, then we can write:
+
+.. code-block:: nestml
+
+ I' = -I / tau + spikes_in * (1 pA)
+
+However, note that this does not account for different spikes carrying different weight (which typically results in different postsynaptic currents or potentials). In this example, each spike will result in a change in :math:`I` of 1 pA.
+
+To read out the attributes from events, for example the weight of the spike, the dot notation can be used, for example:
+
+.. code-block:: nestml
+
+ equations:
+ x' = -x / tau + spikes_in.w
+
+If ``spikes_in.w`` is defined as a real number, the units here are consistent (in 1/s). In case the weight is defined as having a unit in mV, it could be used for instance as follows:
+
+.. code-block:: nestml
+
+ state:
+ V mV = 0 mV
+
+ input:
+ spikes_in <- spike(w mV)
+
+ equations:
+ V' = -V / tau + spikes_in.w
+
+Note that again, the units are consistent if :math:`w_k` is assumed to be in units of mV; in combination with the 1/s unit of the delta train, the units of ``spikes_in.w`` are in mV/s.
+
+
+Handling spiking input by event handlers
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+An ``onReceive`` block can be defined for every spiking input port. For example, if a port named ``pre_spikes`` is defined, the corresponding event handler has the general structure:
+
+.. code-block:: nestml
+
+ onReceive(pre_spikes):
+ println("Info: processing a presynaptic spike at time t = {t}")
+ # ... further statements go here ...
+
+The statements in the event handler will be executed when the event occurs and integrate the state of the system from "just before" the event (at :math:`t-\epsilon`, for :math:`\epsilon\rightarrow 0`) to "just after" the event (at :math:`t=t+\epsilon`). Analogous to the ``update`` block, the predefined variable ``t`` indicates the time :math:`t-\epsilon` at the start of the interval, whereas the predefined function ``timestep()`` yields the duration of the interval :math:`2\epsilon` for :math:`\epsilon\rightarrow 0`. As the timestep() function would typically yield a numerical value equal to zero, its use inside an ``onReceive`` block only makes sense to integrate over (trains of) delta pulses to obtain the area under the curve.
+
+Typically, the statements in the ``onReceive`` block integrate the delta function across time, which yields the surface area under the curve, which typically corresponds to the weight of the spike, or to another spike event attribute. Integration across time causes the 1/s unit of the spike train to drop out, so that what remains are the units of the spike attribute itself. For instance, when a port is defined with an attribute "psp" in units of mV:
+
+.. code-block:: nestml
+
+ input:
+ in_spikes(psp mV) <- spike
+
+then the following has consistent units: ``in_spikes.psp`` is in mV/s as it consists of the unit given in the spiking input port definition, multiplied with the 1/s from the delta pulses), and after integration the 1/s drops out leaving a unit of mV.
+
+.. code-block:: nestml
+
+ state:
+ V_m mV = 0 mV
+
+ onReceive(in_spikes):
+ V_m += integrate(in_spikes.psp, t, t + timestep()) # lhs and rhs both in [mV]
+
+In ``onReceive`` blocks, a spiking input port (or any of its attributes) may not appear outside of a ``integrate()`` call, because the units will be inconsistent; for example:
+
+.. code-block:: nestml
+
+ onReceive(in_spikes):
+ V_m += in_spikes.psp # error! lhs in [mV], rhs in [mV/s]
+
+A spiking input port may appear without an attribute present; this refers to the unweighted train of delta pulses (with surface area 1):
+
+.. code-block:: nestml
+
+ state:
+ x real = 0
+
+ input:
+ in_spikes <- spike(foo pA, bar mmol)
+
+ onReceive(in_spikes):
+ x += integrate(in_spikes, t, t + timestep()) # increments x by 1
+
+To specify in which sequence the event handlers should be called in case multiple events are received at the exact same time, the ``priority`` parameter can be used, which can be given an integer value, where a larger value means higher priority (handled earlier). For example:
+
+.. code-block:: nestml
+
+ onReceive(pre_spikes, priority=1):
+ println("Info: processing a presynaptic spike at time t = {t}")
+
+ onReceive(post_spikes, priority=2):
+ println("Info: processing a postsynaptic spike at time t = {t}")
+
+In this case, if a pre- and postsynaptic spike are received at the exact same time, the higher-priority ``post_spikes`` handler will be invoked first.
+
+Vector input ports of constant size and with a constant numerical value for the index can be used:
+
+.. code-block:: nestml
+
+ input:
+ foo[2] <- spike
+
+ onReceive(foo[0]):
+ # ... handle foo[0] spikes...
+
+ onReceive(foo[1]):
+ # ... handle foo[1] spikes...
+
+
Handling of time
----------------
-Inside the ``update`` block, the current time can be retrieved via the predefined, global variable ``t``. The statements executed in the block are responsible for updating the state of the model between timesteps or events. The statements in this block update the state of the model from the "current" time ``t``, to the next simulation timestep or time of next event ``t + timestep()``. The update step involves integration of the ODEs and corresponds to the "free-flight" or "subthreshold" integration; the events themselves are handled elsewhere, namely as a convolution with a kernel, or as an ``onReceive`` block.
+Inside the ``update`` block, the current time can be retrieved via the predefined, global variable ``t``. The statements executed in the block are responsible for updating the state of the model between events. The statements in this block update the state of the model from the "current" time ``t``, to the next simulation timestep or time of next event ``t + timestep()``. The update step involves integration of the ODEs, corresponding to the "free-flight" or "subthreshold" integration; the events themselves are handled elsewhere, namely as a convolution with a kernel, or as an ``onReceive`` block.
Integrating the ODEs
~~~~~~~~~~~~~~~~~~~~
-Integrating the ODEs needs to be triggered explicitly inside the ``update`` block by calling the ``integrate_odes()`` function. Making this call explicit forces the model to be precise about the sequence of steps that needs to be carried out to step the model state forward in time.
+Numerical integration of the ODEs needs to be triggered explicitly inside the ``update`` block by calling the ``integrate_odes()`` function. Making this call explicit allows subtle differences in integration sequence to be expressed, as well as making it explicit that some variables but not others are integrated; for example, if a neuron is in an absolute refractory state, we might want to skip integrating the differential equation for the membrane potential.
-The ``integrate_odes()`` function numerically integrates the differential equations defined in the ``equations`` block. Integrating the ODEs from one timestep to the next has to be explicitly carried out in the model by calling the ``integrate_odes()`` function. If no parameters are given, all ODEs in the model are integrated. Integration can be limited to a given set of ODEs by giving their left-hand side state variables as parameters to the function, for example ``integrate_odes(V_m, I_ahp)`` if ODEs exist for the variables ``V_m`` and ``I_ahp``. In this example, these variables are integrated simultaneously (as one single system of equations). This is different from calling ``integrate_odes(V_m)`` and then ``integrate_odes(I_ahp)`` in that the second call would use the already-updated values from the first call. Variables not included in the call to ``integrate_odes()`` are assumed to remain constant (both inside the numeric solver stepping function as well as from before to after the call).
+If ``integrate_odes()`` is called without parameters, all ODEs defined in the model are integrated. Integration can be limited to a given set of ODEs by giving their left-hand side state variables as parameters to the function, for example, ``integrate_odes(V_m, I_ahp)`` if ODEs exist for the variables ``V_m`` and ``I_ahp``. In this example, these variables are integrated simultaneously (as one single system of equations). This is different from calling ``integrate_odes(V_m)`` and then ``integrate_odes(I_ahp)``, in that the second call would use the already-updated state value from the first call. Variables not included in the call to ``integrate_odes()`` are assumed to remain constant (both inside the numeric solver stepping function as well as from before to after the call).
-In case of higher-order ODEs of the form ``F(x'', x', x) = 0``, the solution ``x(t)`` is obtained by just providing the variable ``x`` to the ``integrate_odes`` function. For example,
+In case of higher-order ODEs, calling ``integrate_odes()`` integrates variables of all order. For example, in case an ODE :math:`d^2x/dt^2` is defined, then calling ``integrate_odes(x)`` will integrate all variable orders related to ``x``:
.. code-block:: nestml
@@ -1122,9 +1269,7 @@ In case of higher-order ODEs of the form ``F(x'', x', x) = 0``, the solution ``x
update:
integrate_odes(x)
-Here, ``integrate_odes(x)`` integrates the entire dynamics of ``x(t)``, in this case, ``x`` and ``x'``.
-
-Note that the dynamical equations that correspond to convolutions are always updated, regardless of whether ``integrate_odes()`` is called. The state variables affected by incoming events are updated at the end of each timestep, that is, within one timestep, the state as observed by statements in the ``update`` block will be those at :math:`t^-`, i.e. "just before" it has been updated due to the events. See also :ref:`Integrating spiking input` and :ref:`Integration order`.
+Here, ``integrate_odes(x)`` integrates both ``x`` and ``x'``.
ODEs that can be solved analytically are integrated to machine precision from one timestep to the next using the propagators obtained from `ODE-toolbox `_. In case a numerical solver is used (such as Runge-Kutta or forward Euler), the same ODEs are also evaluated numerically by the numerical solver to allow more precise values for analytically solvable ODEs *within* a timestep. In this way, the long-term dynamics obeys the analytic (more exact) equations, while the short-term (within one timestep) dynamics is evaluated to the precision of the numerical integrator.
@@ -1132,13 +1277,13 @@ ODEs that can be solved analytically are integrated to machine precision from on
Retrieving simulation timing parameters
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-To retrieve timing parameters from the simulator kernel, two special functions are built into NESTML:
+To retrieve timing parameters from the simulator kernel, three predefined functions are built into NESTML:
-- ``resolution`` returns the current timestep taken. Can be used only inside the ``update`` block and in intialising expressions. The use of this function assumes that the simulator uses fixed resolution steps, therefore it is recommended to use ``timestep()`` instead in order to make the models more generic.
-- ``timestep`` returns the current timestep taken. Can be used only inside the ``update`` block.
-- ``steps`` takes one parameter of type ``ms`` and returns the number of simulation steps in the current simulation resolution. This only makes sense in case of a fixed simulation resolution (such as in NEST); hence, use of this function is not recommended, because it precludes the models from being compatible with other simulation platforms where a non-constant simulation timestep is used.
+- ``resolution()`` can only be used in the context of fixed-timestep simulation. It returns the time resolution (duration of each timestep) taken by the simulator. It is only allowed to be called inside the ``update`` block and in intialising expressions. This only makes sense in case of a fixed-timestep simulation; hence, use of this function is not recommended, because it precludes the models from being compatible with other simulation platforms where a non-constant simulation timestep is used. Instead, ``timestep()`` should be preferred to make models more generic.
+- ``timestep()`` returns the current timestep taken. It is only allowed inside the ``update`` block.
+- ``steps()`` takes one parameter of type ``ms`` and returns the number of simulation steps in the current simulation resolution. This only makes sense in case of a fixed-timestep simulation; hence, use of this function is not recommended, because it precludes the models from being compatible with other simulation platforms where a non-constant simulation timestep is used.
-When using ``resolution()``, it is recommended to use the function call directly in the code, rather than defining it as a parameter. This makes the model more robust in case the resolution is changed during the simulation. In some cases, as in the synapse ``update`` block, a step is made between spike events, unconstrained by the simulation resolution. For example:
+When using ``resolution()``, it is recommended to use the function call directly in the code, rather than assigning it to a parameter. This makes the model more robust in case the resolution is changed during the simulation. In some cases, as in the ``update`` block, a step may be made between spike events, unconstrained by the simulation resolution. For example:
.. code-block:: nestml
@@ -1156,7 +1301,7 @@ When using ``resolution()``, it is recommended to use the function call directly
Integration order
~~~~~~~~~~~~~~~~~
-During simulation, the simulation kernel (for example, NEST Simulator) is responsible for invoking the model functions that update its state: those in ``update``, ``onReceive``, integrating the ODEs, etc. Different simulators may invoke these functions in a different sequence and with different steps of time, leading to different numerical results even though the same model was used. For example, "time-based" simulators take discrete steps of time of fixed duration (for example, 1 millisecond), whereas "event-based" simulators process events at their exact time of occurrence, without having to round off the time of occurrence of the event to the nearest timestep interval. The following section describes some of the variants of integration sequences that can be encountered and what this means for the outcome of a simulation.
+During simulation, the simulation kernel (for example, NEST Simulator) is responsible for invoking the model functions that update its state: those in ``update``, ``onReceive``, and ``onCondition`` blocks. Different simulators may invoke these functions in a different sequence and with different steps of time, leading to different numerical results even though the same model was used. For example, "time-based" simulators take discrete steps of time of fixed duration (for example, 1 millisecond), whereas "event-based" simulators process events at their exact time of occurrence, without having to round off the time of occurrence of the event to the nearest timestep interval. The following section describes some of the variants of integration sequences that can be encountered and what this means for the outcome of a simulation.
The recommended update sequence for a spiking neuron model is shown below (panel A), which is optimal ("gives the fewest surprises") in the case the simulator uses a minimum synaptic transmission delay (this includes NEST). In this sequence, first the subthreshold dynamics are evaluated (that is, ``integrate_odes()`` is called; in the simplest case, all equations are solved simultaneously) and only afterwards, incoming spikes are processed.
@@ -1164,12 +1309,12 @@ The recommended update sequence for a spiking neuron model is shown below (panel
.. figure:: https://raw.githubusercontent.com/nest/nestml/master/doc/fig/integration_order.png
:alt: Different conventions for the integration sequence. Modified after [1]_, their Fig. 10.2. The precise sequence of operations depends on whether the simulation is considered to have synaptic propagation delays (A) or not (B).
-The numeric results of a typical simulation run are shown below. Consider a leaky integrate-and-fire neuron with exponentially decaying postsynaptic currents :math:`I_\text{syn}`. The neuron is integrated using a fixed timestep of :math:`1~\text{ms}` (left) and using an event-based method (right):
+The numeric results of a typical simulation run are shown below. Consider a leaky integrate-and-fire neuron with exponentially decaying postsynaptic currents :math:`I_\text{syn}`. The same neuron is integrated using a fixed timestep of :math:`1~\text{ms}` (left) and using an event-based method (right):
.. figure:: https://raw.githubusercontent.com/nest/nestml/master/doc/fig/integration_order_example.png
:alt: Numerical example for two different integration sequences.
-On the left, both pre-synaptic spikes are only processed at the end of the interval in which they occur. The statements in the ``update`` block are run every timestep for a fixed timestep of :math:`1~\text{ms}`, alternating with the statements in the ``onReceive`` handler for the spiking input port. Note that this means that the effect of the spikes becomes visible at the end of the timestep in :math:`I_\text{syn}`, but it takes another timestep before ``integrate_odes()`` is called again and consequently for the effect of the spikes to become visible in the membrane potential. This results in a threshold crossing and the neuron firing a spike. On the right half of the figure, the same presynaptic spike timing is used, but events are processed at their exact time of occurrence. In this case, the ``update`` statements are called once to update the neuron from time 0 to :math:`1~\text{ms}`, then again to update from :math:`1~\text{ms}` to the time of the first spike, then the spike is processed by running the statements in its ``onReceive`` block, then ``update`` is called to update from the time of the first spike to the second spike, and so on. The time courses of :math:`I_\text{syn}` and :math:`V_\text{m}` are such that the threshold is not reached and the neuron does not fire, illustrating the numerical differences that can occur when the same model is simulated using different strategies.
+On the left, both pre-synaptic spikes are only processed at the end of the interval in which they occur. The statements in the ``update`` block are run every timestep for a fixed timestep of :math:`1~\text{ms}`, alternating with the statements in the ``onReceive`` handler for the spiking input port. Note that this means that the effect of the spikes becomes visible at the end of the timestep in :math:`I_\text{syn}`, but it takes another timestep before ``integrate_odes()`` is called again and consequently for the effect of the spikes to become visible in the membrane potential. This results in a threshold crossing and the neuron firing a spike. In the right panel in the figure, the same presynaptic spike timing is used, but events are processed at their exact time of occurrence. In this case, the ``update`` statements are called once to update the neuron from time 0 to :math:`1~\text{ms}`, then again to update from :math:`1~\text{ms}` to the time of the first spike, then the spike is processed by running the statements in its ``onReceive`` block, then ``update`` is called to update from the time of the first spike to the second spike, and so on. The time courses of :math:`I_\text{syn}` and :math:`V_\text{m}` are such that the threshold is not reached and the neuron does not fire, illustrating the numerical differences that can occur when the same model is simulated using different strategies.
Guards
diff --git a/doc/nestml_language/neurons_in_nestml.rst b/doc/nestml_language/neurons_in_nestml.rst
index 3a7fd2208..e8a486a5c 100644
--- a/doc/nestml_language/neurons_in_nestml.rst
+++ b/doc/nestml_language/neurons_in_nestml.rst
@@ -31,48 +31,7 @@ A neuron model written in NESTML can be configured to receive two distinct types
AMPA_spikes <- spike
I_stim pA <- continuous
-The general syntax is:
-
-::
-
- port_name <- inputQualifier spike
- port_name dataType <- continuous
-
The spiking input ports are declared without a data type, whereas the continuous input ports must have a data type.
-For spiking input ports, the qualifier keywords decide whether inhibitory and excitatory inputs are lumped together into a single named input port, or if they are separated into differently named input ports based on their sign. When processing a spike event, some simulators (including NEST) use the sign of the amplitude (or weight) property in the spike event to indicate whether it should be considered an excitatory or inhibitory spike. By using the qualifier keywords, a single spike handler can route each incoming spike event to the correct input buffer (excitatory or inhibitory). Compare:
-
-.. code-block:: nestml
-
- input:
- # [...]
- all_spikes <- spike
-
-In this case, all spike events will be processed through the ``all_spikes`` input port. A spike weight could be positive or negative, and the occurrences of ``all_spikes`` in the model should be considered a signed quantity.
-
-.. code-block:: nestml
-
- input:
- # [...]
- AMPA_spikes <- excitatory spike
- GABA_spikes <- inhibitory spike
-
-In this case, spike events that have a negative weight are routed to the ``GABA_spikes`` input port, and those that have a positive weight to the ``AMPA_spikes`` port.
-
-It is equivalent if either both `inhibitory` and `excitatory` are given, or neither: an unmarked port will by default handle all incoming presynaptic spikes.
-
-.. list-table::
- :header-rows: 1
- :widths: 10 60
-
- * - Keyword
- - The incoming weight :math:`w`...
- * - none, or ``excitatory`` and ``inhibitory``
- - ... may be positive or negative. It is added to the buffer with signed value :math:`w` (positive or negative).
- * - ``excitatory``
- - ... should not be negative. It is added to the buffer with non-negative magnitude :math:`w`.
- * - ``inhibitory``
- - ... should be negative. It is added to the buffer with non-negative magnitude :math:`-w`.
-
Integrating current input
@@ -92,14 +51,6 @@ The current port symbol (here, ``I_stim``) is available as a variable and can be
Integrating spiking input
^^^^^^^^^^^^^^^^^^^^^^^^^
-Spikes arriving at the input port of a neuron can be written as a spike train :math:`s(t)`:
-
-.. math::
-
- \large s(t) = \sum_{i=1}^N w_i \cdot \delta(t - t_i)
-
-where :math:`w_i` is the weight of spike :math:`i`.
-
To model the effect that an arriving spike has on the state of the neuron, a convolution with a kernel can be used. The kernel defines the postsynaptic response kernel, for example, an alpha (bi-exponential) function, decaying exponential, or a delta function. (See :ref:`Kernel functions` for how to define a kernel.) The convolution of the kernel with the spike train is defined as follows:
.. math::
@@ -110,16 +61,20 @@ To model the effect that an arriving spike has on the state of the neuron, a con
&= \sum_{i=1}^N w_i \cdot f(t - t_i)
\end{align*}
-For example, say there is a spiking input port defined named ``spikes``. A decaying exponential with time constant ``tau_syn`` is defined as postsynaptic kernel ``G``. Their convolution is expressed using the ``convolve()`` function, which takes a kernel and input port, respectively, as its arguments:
+For example, say there is a spiking input port defined named ``spikes``, which receives weighted spike events:
+
+.. code-block:: nestml
+
+ input:
+ spikes <- spike(weight pA)
+
+A decaying exponential with time constant ``tau_syn`` is defined as postsynaptic kernel ``G``. Their convolution is expressed using the ``convolve()`` function, which takes a kernel and input port, respectively, as its arguments:
.. code-block:: nestml
equations:
kernel G = exp(-t / tau_syn)
- inline I_syn pA = convolve(G, spikes) * pA
- V_m' = -V_m / tau_m + I_syn / C_m
-
-Note that in this example, the intended physical unit (pA) was assigned by multiplying the scalar convolution result with the unit literal. By the definition of convolution, ``convolve(G, spikes)`` will have the unit of kernel ``G`` multiplied by the unit of ``spikes`` and unit of time, i.e., ``[G] * [spikes] * s``. Kernel functions in NESTML are always untyped and the unit of spikes is :math:`1/s` as discussed above. As a result, the unit of convolution is :math:`(1/s) * s`, a scalar quantity without a unit.
+ inline I_syn pA = convolve(G, spikes.weight)
The incoming spikes could have been equivalently handled with an ``onReceive`` event handler block:
@@ -130,12 +85,9 @@ The incoming spikes could have been equivalently handled with an ``onReceive`` e
equations:
I_syn' = -I_syn / tau_syn
- V_m' = -V_m / tau_m + I_syn / C_m
onReceive(spikes):
- I_syn += spikes * pA * s
-
-Note that in this example, the intended physical unit (pA) was assigned by multiplying the type of the input port ``spikes`` (which is 1/s) by pA·s, resulting in a unit of pA for ``I_syn``.
+ I_syn += integrate(spikes.weight, t, t + timestep())
(Re)setting synaptic integration state
@@ -217,12 +169,12 @@ The input ports can also be defined as vectors. For example,
neuron multi_synapse_vectors:
input:
- AMPA_spikes <- excitatory spike
- GABA_spikes <- inhibitory spike
+ AMPA_spikes <- spike
+ GABA_spikes <- spike
NMDA_spikes <- spike
foo[2] <- spike
- exc_spikes[3] <- excitatory spike
- inh_spikes[3] <- inhibitory spike
+ exc_spikes[3] <- spike
+ inh_spikes[3] <- spike
equations:
kernel I_kernel_exc = exp(-1 / tau_syn_exc * t)
diff --git a/doc/pynestml_toolchain/front.rst b/doc/pynestml_toolchain/front.rst
index b6fd2ec04..407498ef8 100644
--- a/doc/pynestml_toolchain/front.rst
+++ b/doc/pynestml_toolchain/front.rst
@@ -267,8 +267,6 @@ Given the fact that context conditions have the commonality of checking the cont
- *CoCoConvolveHasCorrectParameter*: Checks that *convolve* calls are not provided with complex expressions, but only variables.
-- *CoCoTypeOfBufferUnique*: Checks that no keyword is stated twice in an input buffer declaration, e.g., *inhibitory inhibitory spike*.
-
- *CoCoUserDeclaredFunctionCorrectlyDefined*: Checks that user-defined functions are correctly defined, i.e., only parameters of the function are used, and the return type is correctly stated.
- *CoCoVariableOncePerScope*: Checks that each variable is defined at most once per scope, i.e., no variable is redefined.
diff --git a/doc/running/running_nest.rst b/doc/running/running_nest.rst
index 2edbee3d3..99c452fd0 100644
--- a/doc/running/running_nest.rst
+++ b/doc/running/running_nest.rst
@@ -12,6 +12,12 @@ After NESTML completes, the NEST extension module (by default called ``"nestmlmo
Several code generator options are available; for an overview see :class:`pynestml.codegeneration.nest_code_generator.NESTCodeGenerator`.
+Data types
+----------
+
+- The NESTML data type ``real`` will be rendered as ``double``.
+- The NESTML data type ``integer`` will be rendered as ``long``.
+
Simulation loop
---------------
@@ -135,7 +141,7 @@ Multiple spiking input ports with vectors in NEST
See :ref:`Multiple input ports with vectors` for an example with input ports defined as vectors.
-Each connection in NEST is denoted by a receiver port or ``rport`` number which is an integer that starts with 0. All default connections in NEST have the ``rport`` 0. NESTML routes the spikes with ``excitatory`` and ``inhibitory`` qualifiers into separate input buffers, whereas NEST identifies them with the same ``rport`` number.
+Each connection in NEST is denoted by a receiver port or ``rport`` number which is an integer that starts with 0. All default connections in NEST have the ``rport`` 0.
During the code generation for NEST, NESTML maintains an internal mapping between NEST ``rports`` and NESTML input ports. A list of port names defined in a model and their corresponding ``rport`` numbers can be queried from the status dictionary using the NEST API. For neurons with multiple input ports, the ``receptor_type`` values in the ``nest.Connect()`` call start from 1 as the default ``receptor_type`` 0 is excluded to avoid any accidental connections.
@@ -161,7 +167,7 @@ The above code querying for ``receptor_types`` gives a list of port names and NE
- 1
* - NMDA_spikes
- 2
- * - FOO_0
+ * - FOO_0 XXXXX _VEC_IDX_
- 3
* - FOO_1
- 4
@@ -263,8 +269,6 @@ By default, the "continuous-time" based buffer is selected. This covers the most
As a computationally more efficient alternative, a spike-based buffer can be selected. In this case, the third factor is not stored every timestep, but only upon the occurrence of postsynaptic (somatic) spikes. Because of the existence of a nonzero dendritic delay, the time at which the somatic spike is observed at the synapse is delayed, and the time at which the third factor is sampled should match the time of the spike at the synapse, rather than the soma. When the spike-based buffering method is used, the dendritic delay is therefore ignored, because the third factor is sampled instead at the time of the somatic spike.
-
-
Dendritic delay and synaptic weight
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -316,6 +320,44 @@ Random numbers
In case random numbers are needed inside the synapse, the random number generator belonging to the postsynaptic target is used.
+Performance optimisations
+-------------------------
+
+In neuron models, incoming spikes are by default buffered into a queue (a ``std::vector``) before being processed. This implementation is the most generic, allowing, for example, spikes with both positive and negative weights arriving at one and the same input port to be handled differently according to the sign. However, the queue can cause a degradation in runtime performance on the order of 10%. If no conditional processing of the incoming spikes is necessary, and all spikes are treated in the same, linear, time-invariant (LTI) manner, then no queue is necessary as all spike weights can be simply added together into a single floating-point variable. The code generator option ``linear_time_invariant_spiking_input_ports`` can be used to indicate for which ports the spikes can be treated in an LTI-manner.
+
+For instance, if spikes arriving at the same port are handled differently according to sign of the weight:
+
+.. code:: nestml
+
+ input:
+ spike_in_port <- spike(weight pA)
+
+ onReceive(spike_in_port):
+ # route the incoming spike on the basis of the weight: less than zero means an inhibitory spike; greater than zero means an excitatory spike
+ if spike_in_port.weight > 0:
+ I_syn_exc += spike_in_port.weight
+ else:
+ I_syn_inh -= spike_in_port.weight
+
+then the system is not LTI and a queue is necessary.
+
+However, if two separate ports are used (and weights are subsequently processed in an LTI manner), the model can be formulated in a mathematically equivalent way:
+
+.. code:: nestml
+
+ input:
+ spike_in_port_exc <- spike(weight pA)
+ spike_in_port_inh <- spike(weight pA)
+
+ onReceive(spike_in_port_exc):
+ I_syn_exc += spike_in_port.weight
+
+ onReceive(spike_in_port_exc):
+ I_syn_inh += spike_in_port.weight
+
+In this case, the ``linear_time_invariant_spiking_input_ports`` option can be used to specify that both ``spike_in_port_exc`` and ``spike_in_port_inh`` are LTI ports, for better runtime performance.
+
+
References
----------
diff --git a/doc/tutorials/active_dendrite/nestml_active_dendrite_tutorial.ipynb b/doc/tutorials/active_dendrite/nestml_active_dendrite_tutorial.ipynb
index 5b3a63ec6..1821fc41d 100644
--- a/doc/tutorials/active_dendrite/nestml_active_dendrite_tutorial.ipynb
+++ b/doc/tutorials/active_dendrite/nestml_active_dendrite_tutorial.ipynb
@@ -124,7 +124,7 @@
" equations:\n",
" # alpha shaped postsynaptic current kernel\n",
" kernel syn_kernel = (e / tau_syn) * t * exp(-t / tau_syn)\n",
- " recordable inline I_syn pA = convolve(syn_kernel, spikes_in) * pA\n",
+ " recordable inline I_syn pA = convolve(syn_kernel, spikes_in.weight)\n",
" V_m' = -(V_m - E_L) / tau_m + (I_syn + I_dAP + I_e) / C_m\n",
"\n",
" parameters:\n",
@@ -142,7 +142,7 @@
" T_dAP ms = 10 ms # time window over which the dendritic current clamp is active\n",
"\n",
" input:\n",
- " spikes_in <- spike\n",
+ " spikes_in <- spike(weight pA)\n",
"\n",
" output: \n",
" spike\n",
@@ -503,7 +503,7 @@
" equations:\n",
" # alpha shaped postsynaptic current kernel\n",
" kernel syn_kernel = (e / tau_syn) * t * exp(-t / tau_syn)\n",
- " recordable inline I_syn pA = convolve(syn_kernel, spikes_in) * pA\n",
+ " recordable inline I_syn pA = convolve(syn_kernel, spikes_in.weight)\n",
" V_m' = -(V_m - E_L) / tau_m + (enable_I_syn * I_syn + I_dAP + I_e) / C_m\n",
"\n",
" parameters:\n",
@@ -521,7 +521,7 @@
" T_dAP ms = 10 ms # time window over which the dendritic current clamp is active\n",
"\n",
" input:\n",
- " spikes_in <- spike\n",
+ " spikes_in <- spike(weight pA)\n",
"\n",
" output:\n",
" spike\n",
diff --git a/doc/tutorials/izhikevich/izhikevich_solution.nestml b/doc/tutorials/izhikevich/izhikevich_solution.nestml
index 834b2e3ad..e12eb4739 100644
--- a/doc/tutorials/izhikevich/izhikevich_solution.nestml
+++ b/doc/tutorials/izhikevich/izhikevich_solution.nestml
@@ -25,8 +25,8 @@
model izhikevich_tutorial_neuron:
state:
- v mV = -65 mV # Membrane potential in mV
- u real = 0 # Membrane potential recovery variable
+ v mV = -65 mV # Membrane potential in mV
+ u real = 0 # Membrane potential recovery variable
equations:
v' = (.04 * v * v / mV + 5 * v + (140 - u) * mV + (I_e * GOhm)) / ms
@@ -34,12 +34,12 @@ model izhikevich_tutorial_neuron:
parameters:
a real = .02 # describes time scale of recovery variable
- b real = .2 # sensitivity of recovery variable
- c mV = -65 mV # after-spike reset value of v
- d real = 8. # after-spike reset value of u
+ b real = .2 # sensitivity of recovery variable
+ c mV = -65 mV # after-spike reset value of v
+ d real = 8. # after-spike reset value of u
input:
- spikes <- spike
+ spikes <- spike(weight mV)
I_e pA <- continuous
output:
@@ -50,7 +50,7 @@ model izhikevich_tutorial_neuron:
onReceive(spikes):
# add synaptic current
- v += spikes * mV * s
+ v += spikes.weight
onCondition(v >= 30mV):
# threshold crossing
diff --git a/doc/tutorials/izhikevich/izhikevich_task.nestml b/doc/tutorials/izhikevich/izhikevich_task.nestml
index 0d3270e22..46896ee36 100644
--- a/doc/tutorials/izhikevich/izhikevich_task.nestml
+++ b/doc/tutorials/izhikevich/izhikevich_task.nestml
@@ -39,7 +39,7 @@ model izhikevich_tutorial_neuron:
# TODO: add remaining variables
input:
- spikes <- spike
+ spikes <- spike(weight mV)
I_e pA <- continuous
output:
@@ -50,7 +50,7 @@ model izhikevich_tutorial_neuron:
onReceive(spikes):
# add synaptic current
- v += spikes * mV * s
+ v += spikes.weight
onCondition(v >= 30mV):
# TODO: implement threshold crossing check
diff --git a/doc/tutorials/sequence_learning/iaf_psc_exp_nonlineardendrite_neuron.nestml b/doc/tutorials/sequence_learning/iaf_psc_exp_nonlineardendrite_neuron.nestml
index a01bdadd9..a3ab1e242 100644
--- a/doc/tutorials/sequence_learning/iaf_psc_exp_nonlineardendrite_neuron.nestml
+++ b/doc/tutorials/sequence_learning/iaf_psc_exp_nonlineardendrite_neuron.nestml
@@ -46,8 +46,8 @@ model iaf_psc_exp_nonlineardendrite_neuron:
kernel I_kernel3 = exp(-1/tau_syn3*t)
# diff. eq. for membrane potential
- inline I_syn pA = convolve(I_kernel1, I_1) * pA - convolve(I_kernel3, I_3) * pA + I_e
- V_m' = -(V_m - E_L)/tau_m + (I_syn + I_dend) / C_m
+ inline I_syn pA = convolve(I_kernel1, I_1.weight) - convolve(I_kernel3, I_3.weight) + I_e
+ V_m' = -(V_m - E_L) / tau_m + (I_syn + I_dend) / C_m
# diff. eq. for dAP trace
dAP_trace' = -evolve_dAP_trace * dAP_trace / tau_h
@@ -77,17 +77,17 @@ model iaf_psc_exp_nonlineardendrite_neuron:
I_dend_incr pA/ms = pA * exp(1) / tau_syn2
-
input:
- I_1 <- spike
- I_2 <- spike
- I_3 <- spike
+ I_1 <- spike(weight pA)
+ I_2 <- spike(weight real)
+ I_3 <- spike(weight pA)
output:
spike
onReceive(I_2):
- I_dend$ += I_2 * s * I_dend_incr
+ spike_weight pA = integral(I_2.weight, t, t + timestep())
+ I_dend$ += spike_weight * I_dend_incr
update:
# solve ODEs
@@ -136,4 +136,3 @@ model iaf_psc_exp_nonlineardendrite_neuron:
active_dendrite_readout = 0.
dAP_counts = 0
I_dend = 0 pA
-
diff --git a/doc/tutorials/spike_frequency_adaptation/models/iaf_psc_alpha.nestml b/doc/tutorials/spike_frequency_adaptation/models/iaf_psc_alpha.nestml
index 5da652ad0..0b3c4f1a7 100644
--- a/doc/tutorials/spike_frequency_adaptation/models/iaf_psc_alpha.nestml
+++ b/doc/tutorials/spike_frequency_adaptation/models/iaf_psc_alpha.nestml
@@ -65,7 +65,7 @@ model iaf_psc_alpha_neuron:
equations:
kernel I_kernel_inh = (e / tau_syn_inh) * t * exp(-t / tau_syn_inh)
kernel I_kernel_exc = (e / tau_syn_exc) * t * exp(-t / tau_syn_exc)
- inline I pA = (convolve(I_kernel_exc, exc_spikes) - convolve(I_kernel_inh, inh_spikes)) * pA + I_e + I_stim
+ inline I pA = convolve(I_kernel_exc, exc_spikes.weight) - convolve(I_kernel_inh, inh_spikes.weight) + I_e + I_stim
V_m' = -(V_m - E_L) / tau_m + I / C_m
parameters:
@@ -82,8 +82,8 @@ model iaf_psc_alpha_neuron:
I_e pA = 0 pA
input:
- exc_spikes <- excitatory spike
- inh_spikes <- inhibitory spike
+ exc_spikes <- spike(weight pA)
+ inh_spikes <- spike(weight pA)
I_stim pA <- continuous
output:
diff --git a/doc/tutorials/spike_frequency_adaptation/models/iaf_psc_alpha_adapt_curr.nestml b/doc/tutorials/spike_frequency_adaptation/models/iaf_psc_alpha_adapt_curr.nestml
index 7b7f572cb..59ac280d0 100644
--- a/doc/tutorials/spike_frequency_adaptation/models/iaf_psc_alpha_adapt_curr.nestml
+++ b/doc/tutorials/spike_frequency_adaptation/models/iaf_psc_alpha_adapt_curr.nestml
@@ -68,7 +68,7 @@ model iaf_psc_alpha_adapt_curr_neuron:
equations:
kernel I_kernel_inh = (e / tau_syn_inh) * t * exp(-t / tau_syn_inh)
kernel I_kernel_exc = (e / tau_syn_exc) * t * exp(-t / tau_syn_exc)
- inline I pA = (convolve(I_kernel_exc, exc_spikes) - convolve(I_kernel_inh, inh_spikes)) * pA - I_sfa + I_e + I_stim
+ inline I pA = convolve(I_kernel_exc, exc_spikes.weight) - convolve(I_kernel_inh, inh_spikes.weight) - I_sfa + I_e + I_stim
V_m' = -(V_m - E_L) / tau_m + I / C_m
I_sfa' = -I_sfa / tau_sfa
@@ -89,8 +89,8 @@ model iaf_psc_alpha_adapt_curr_neuron:
I_e pA = 0 pA
input:
- exc_spikes <- excitatory spike
- inh_spikes <- inhibitory spike
+ exc_spikes <- spike(weight pA)
+ inh_spikes <- spike(weight pA)
I_stim pA <- continuous
output:
diff --git a/doc/tutorials/spike_frequency_adaptation/models/iaf_psc_alpha_adapt_thresh.nestml b/doc/tutorials/spike_frequency_adaptation/models/iaf_psc_alpha_adapt_thresh.nestml
index 89c1344a9..243a60ef2 100644
--- a/doc/tutorials/spike_frequency_adaptation/models/iaf_psc_alpha_adapt_thresh.nestml
+++ b/doc/tutorials/spike_frequency_adaptation/models/iaf_psc_alpha_adapt_thresh.nestml
@@ -68,7 +68,7 @@ model iaf_psc_alpha_adapt_thresh_neuron:
equations:
kernel I_kernel_inh = (e / tau_syn_inh) * t * exp(-t / tau_syn_inh)
kernel I_kernel_exc = (e / tau_syn_exc) * t * exp(-t / tau_syn_exc)
- inline I pA = (convolve(I_kernel_exc, exc_spikes) - convolve(I_kernel_inh, inh_spikes)) * pA + I_e + I_stim
+ inline I pA = convolve(I_kernel_exc, exc_spikes.weight) - convolve(I_kernel_inh, inh_spikes.weight) + I_e + I_stim
V_m' = -(V_m - E_L) / tau_m + I / C_m
Theta' = -(Theta - Theta_init) / tau_Theta
@@ -89,8 +89,8 @@ model iaf_psc_alpha_adapt_thresh_neuron:
I_e pA = 0 pA
input:
- exc_spikes <- excitatory spike
- inh_spikes <- inhibitory spike
+ exc_spikes <- spike(weight pA)
+ inh_spikes <- spike(weight pA)
I_stim pA <- continuous
output:
diff --git a/doc/tutorials/spike_frequency_adaptation/models/iaf_psc_alpha_adapt_thresh_OU.nestml b/doc/tutorials/spike_frequency_adaptation/models/iaf_psc_alpha_adapt_thresh_OU.nestml
index 67fa0d04d..7657fb782 100644
--- a/doc/tutorials/spike_frequency_adaptation/models/iaf_psc_alpha_adapt_thresh_OU.nestml
+++ b/doc/tutorials/spike_frequency_adaptation/models/iaf_psc_alpha_adapt_thresh_OU.nestml
@@ -70,7 +70,7 @@ model iaf_psc_alpha_adapt_thresh_OU_neuron:
equations:
kernel I_kernel_inh = (e / tau_syn_inh) * t * exp(-t / tau_syn_inh)
kernel I_kernel_exc = (e / tau_syn_exc) * t * exp(-t / tau_syn_exc)
- inline I pA = (convolve(I_kernel_exc, exc_spikes) - convolve(I_kernel_inh, inh_spikes)) * pA + I_e + I_stim + I_noise
+ inline I pA = convolve(I_kernel_exc, exc_spikes.weight) - convolve(I_kernel_inh, inh_spikes.weight) + I_e + I_stim + I_noise
V_m' = -(V_m - E_L) / tau_m + I / C_m
Theta' = -(Theta - Theta_init) / tau_Theta
@@ -98,8 +98,8 @@ model iaf_psc_alpha_adapt_thresh_OU_neuron:
A_noise pA = ((D_noise * tau_syn_exc / 2) * (1 - exp(-2 * resolution() / tau_syn_exc )))**.5
input:
- exc_spikes <- excitatory spike
- inh_spikes <- inhibitory spike
+ exc_spikes <- spike(weight pA)
+ inh_spikes <- spike(weight pA)
I_stim pA <- continuous
output:
diff --git a/doc/tutorials/stdp_third_factor_active_dendrite/stdp_third_factor_active_dendrite.ipynb b/doc/tutorials/stdp_third_factor_active_dendrite/stdp_third_factor_active_dendrite.ipynb
index 4e758fc4d..900ec4527 100644
--- a/doc/tutorials/stdp_third_factor_active_dendrite/stdp_third_factor_active_dendrite.ipynb
+++ b/doc/tutorials/stdp_third_factor_active_dendrite/stdp_third_factor_active_dendrite.ipynb
@@ -335,8 +335,8 @@
" # alpha shaped postsynaptic current kernel\n",
" kernel syn_kernel = (e / tau_syn) * t * exp(-t / tau_syn)\n",
" kernel sg_kernel = delta(t)\n",
- " recordable inline I_syn pA = convolve(syn_kernel, synaptic_spikes) * pA\n",
- " V_m' = -(V_m - E_L) / tau_m + (I_syn + I_dAP + I_e) / C_m + convolve(sg_kernel, spike_generator_spikes) * mV / s\n",
+ " recordable inline I_syn pA = convolve(syn_kernel, synaptic_spikes.weight)\n",
+ " V_m' = -(V_m - E_L) / tau_m + (I_syn + I_dAP + I_e) / C_m + convolve(sg_kernel, spike_generator_spikes.weight) / s\n",
" I_dAP' = -I_dAP / tau_dAP\n",
"\n",
" parameters:\n",
@@ -356,10 +356,10 @@
" reset_I_dAP_after_AP boolean = true\n",
"\n",
" input:\n",
- " synaptic_spikes <- spike\n",
- " spike_generator_spikes <- spike\n",
+ " synaptic_spikes <- spike(weight pA)\n",
+ " spike_generator_spikes <- spike(weight mV)\n",
"\n",
- " output: \n",
+ " output:\n",
" spike\n",
"\n",
" update:\n",
diff --git a/models/neurons/aeif_cond_alpha_neuron.nestml b/models/neurons/aeif_cond_alpha_neuron.nestml
index 2b3763e68..9a65a3d41 100644
--- a/models/neurons/aeif_cond_alpha_neuron.nestml
+++ b/models/neurons/aeif_cond_alpha_neuron.nestml
@@ -1,48 +1,48 @@
# aeif_cond_alpha - Conductance based exponential integrate-and-fire neuron model
# ###############################################################################
-#
+#
# Description
# +++++++++++
-#
+#
# aeif_cond_alpha is the adaptive exponential integrate and fire neuron according to Brette and Gerstner (2005), with post-synaptic conductances in the form of a bi-exponential ("alpha") function.
-#
+#
# The membrane potential is given by the following differential equation:
-#
+#
# .. math::
-#
+#
# C_m \frac{dV_m}{dt} =
# -g_L(V_m-E_L)+g_L\Delta_T\exp\left(\frac{V_m-V_{th}}{\Delta_T}\right) -
# g_e(t)(V_m-E_e) \\
# -g_i(t)(V_m-E_i)-w + I_e
-#
+#
# and
-#
+#
# .. math::
-#
+#
# \tau_w \frac{dw}{dt} = a(V_m-E_L) - w
-#
+#
# Note that the membrane potential can diverge to positive infinity due to the exponential term. To avoid numerical instabilities, instead of :math:`V_m`, the value :math:`\min(V_m,V_{peak})` is used in the dynamical equations.
-#
+#
# .. note::
-#
+#
# The default refractory period for ``aeif`` models is zero, consistent with the model definition in
# Brette & Gerstner [1]_. Thus, an ``aeif`` neuron with default parameters can fire multiple spikes in a single
# time step, which can lead to exploding spike numbers and extreme slow-down of simulations.
# To avoid such unphysiological behavior, you should set a refractory time ``refr_t > 0``.
-#
-#
+#
+#
# References
# ++++++++++
-#
+#
# .. [1] Brette R and Gerstner W (2005). Adaptive exponential
# integrate-and-fire model as an effective description of neuronal
# activity. Journal of Neurophysiology. 943637-3642
# DOI: https://doi.org/10.1152/jn.00686.2005
-#
-#
+#
+#
# See also
# ++++++++
-#
+#
# iaf_cond_alpha, aeif_cond_exp
#
#
@@ -50,7 +50,7 @@
# +++++++++++++++++++
#
# This file is part of NEST.
-#
+#
# Copyright (C) 2004 The NEST Initiative
#
# NEST is free software: you can redistribute it and/or modify
@@ -75,15 +75,15 @@ model aeif_cond_alpha_neuron:
refr_t ms = 0 ms # Refractory period timer
equations:
- inline V_bounded mV = min(V_m, V_peak) # prevent exponential divergence
kernel g_inh = (e / tau_syn_inh) * t * exp(-t / tau_syn_inh)
kernel g_exc = (e / tau_syn_exc) * t * exp(-t / tau_syn_exc)
# Add inlines to simplify the equation definition of V_m
+ inline V_bounded mV = min(V_m, V_peak) # prevent exponential divergence
inline exp_arg real = (V_bounded - V_th) / Delta_T
inline I_spike pA = g_L * Delta_T * exp(exp_arg)
- inline I_syn_exc pA = convolve(g_exc, exc_spikes) * nS * (V_bounded - E_exc)
- inline I_syn_inh pA = convolve(g_inh, inh_spikes) * nS * (V_bounded - E_inh)
+ inline I_syn_exc pA = convolve(g_exc, exc_spikes.weight) * (V_bounded - E_exc)
+ inline I_syn_inh pA = convolve(g_inh, inh_spikes.weight) * (V_bounded - E_inh)
V_m' = (-g_L * (V_bounded - E_L) + I_spike - I_syn_exc - I_syn_inh - w + I_e + I_stim) / C_m
w' = (a * (V_bounded - E_L) - w) / tau_w
@@ -118,16 +118,9 @@ model aeif_cond_alpha_neuron:
# Constant external input current
I_e pA = 0 pA
- internals:
- # Impulse to add to DG_EXC on spike arrival to evoke unit-amplitude conductance excursion
- PSConInit_E nS/ms = nS * e / tau_syn_exc
-
- # Impulse to add to DG_INH on spike arrival to evoke unit-amplitude conductance excursion
- PSConInit_I nS/ms = nS * e / tau_syn_inh
-
input:
- exc_spikes <- excitatory spike
- inh_spikes <- inhibitory spike
+ exc_spikes <- spike(weight nS/s)
+ inh_spikes <- spike(weight nS/s)
I_stim pA <- continuous
output:
diff --git a/models/neurons/aeif_cond_exp_neuron.nestml b/models/neurons/aeif_cond_exp_neuron.nestml
index f7927871b..a3b0445bc 100644
--- a/models/neurons/aeif_cond_exp_neuron.nestml
+++ b/models/neurons/aeif_cond_exp_neuron.nestml
@@ -1,49 +1,49 @@
# aeif_cond_exp - Conductance based exponential integrate-and-fire neuron model
# #############################################################################
-#
+#
# Description
# +++++++++++
-#
+#
# aeif_cond_exp is the adaptive exponential integrate and fire neuron
# according to Brette and Gerstner (2005), with post-synaptic
# conductances in the form of truncated exponentials.
-#
+#
# The membrane potential is given by the following differential equation:
-#
+#
# .. math::
-#
+#
# C_m \frac{dV_m}{dt} =
# -g_L(V_m-E_L)+g_L\Delta_T\exp\left(\frac{V_m-V_{th}}{\Delta_T}\right) - g_e(t)(V_m-E_e) \\
# -g_i(t)(V_m-E_i)-w +I_e
-#
+#
# and
-#
+#
# .. math::
-#
+#
# \tau_w \frac{dw}{dt} = a(V_m-E_L) - w
-#
+#
# Note that the membrane potential can diverge to positive infinity due to the exponential term. To avoid numerical instabilities, instead of :math:`V_m`, the value :math:`\min(V_m,V_{peak})` is used in the dynamical equations.
-#
+#
# .. note::
-#
+#
# The default refractory period for ``aeif`` models is zero, consistent with the model definition in
# Brette & Gerstner [1]_. Thus, an ``aeif`` neuron with default parameters can fire multiple spikes in a single
# time step, which can lead to exploding spike numbers and extreme slow-down of simulations.
# To avoid such unphysiological behavior, you should set a refractory time ``refr_t > 0``.
-#
-#
+#
+#
# References
# ++++++++++
-#
+#
# .. [1] Brette R and Gerstner W (2005). Adaptive exponential
# integrate-and-fire model as an effective description of neuronal
# activity. Journal of Neurophysiology. 943637-3642
# DOI: https://doi.org/10.1152/jn.00686.2005
-#
-#
+#
+#
# See also
# ++++++++
-#
+#
# iaf_cond_exp, aeif_cond_alpha
#
#
@@ -51,7 +51,7 @@
# +++++++++++++++++++
#
# This file is part of NEST.
-#
+#
# Copyright (C) 2004 The NEST Initiative
#
# NEST is free software: you can redistribute it and/or modify
@@ -74,17 +74,19 @@ model aeif_cond_exp_neuron:
V_m mV = E_L # Membrane potential
w pA = 0 pA # Spike-adaptation current
refr_t ms = 0 ms # Refractory period timer
+ g_syn_exc nS = 0 nS
+ g_syn_inh nS = 0 nS
equations:
- inline V_bounded mV = min(V_m, V_peak) # prevent exponential divergence
- kernel g_inh = exp(-t / tau_syn_inh)
- kernel g_exc = exp(-t / tau_syn_exc)
+ g_syn_exc' = -g_syn_exc / tau_syn_exc
+ g_syn_inh' = -g_syn_inh / tau_syn_inh
# Add inlines to simplify the equation definition of V_m
+ inline V_bounded mV = min(V_m, V_peak) # prevent exponential divergence
inline exp_arg real = (V_bounded - V_th) / Delta_T
inline I_spike pA = g_L * Delta_T * exp(exp_arg)
- inline I_syn_exc pA = convolve(g_exc, exc_spikes) * nS * (V_bounded - E_exc)
- inline I_syn_inh pA = convolve(g_inh, inh_spikes) * nS * (V_bounded - E_inh)
+ inline I_syn_exc pA = g_syn_exc * (V_bounded - E_exc)
+ inline I_syn_inh pA = g_syn_inh * (V_bounded - E_inh)
V_m' = (-g_L * (V_bounded - E_L) + I_spike - I_syn_exc - I_syn_inh - w + I_e + I_stim) / C_m
w' = (a * (V_bounded - E_L) - w) / tau_w
@@ -117,20 +119,27 @@ model aeif_cond_exp_neuron:
I_e pA = 0 pA
input:
- inh_spikes <- inhibitory spike
- exc_spikes <- excitatory spike
+ spike_in_port <- spike(weight nS)
I_stim pA <- continuous
output:
spike
+ onReceive(spike_in_port):
+ # route the incoming spike on the basis of the weight: less than zero means an inhibitory spike; greater than zero means an excitatory spike
+ spike_weight nS = integral(spike_in_port.weight, t, t + timestep())
+ if spike_weight > 0 nS:
+ g_syn_exc += spike_weight
+ else:
+ g_syn_inh -= spike_weight
+
update:
if refr_t > 0 ms:
# neuron is absolute refractory, do not evolve V_m
- integrate_odes(w, refr_t)
+ integrate_odes(g_syn_exc, g_syn_inh, w, refr_t)
else:
# neuron not refractory
- integrate_odes(w, V_m)
+ integrate_odes(g_syn_exc, g_syn_inh, w, V_m)
onCondition(refr_t <= 0 ms and V_m >= V_peak):
# threshold crossing
diff --git a/models/neurons/aeif_psc_alpha_neuron.nestml b/models/neurons/aeif_psc_alpha_neuron.nestml
deleted file mode 100644
index 8d313e445..000000000
--- a/models/neurons/aeif_psc_alpha_neuron.nestml
+++ /dev/null
@@ -1,117 +0,0 @@
-# aeif_psc_alpha - Conductance based exponential integrate-and-fire neuron model
-# ##############################################################################
-#
-# Description
-# +++++++++++
-#
-# aeif_psc_alpha is the adaptive exponential integrate and fire neuron according to Brette and Gerstner (2005), with post-synaptic conductances in the form of a bi-exponential ("alpha") function.
-#
-# The membrane potential is given by the following differential equation:
-#
-# .. math::
-#
-# C_m \frac{dV_m}{dt} =
-# -g_L(V_m-E_L)+g_L\Delta_T\exp\left(\frac{V_m-V_{th}}{\Delta_T}\right) -
-# g_e(t)(V_m-E_e) \\
-# -g_i(t)(V_m-E_i)-w + I_e
-#
-# and
-#
-# .. math::
-#
-# \tau_w \frac{dw}{dt} = a(V_m-E_L) - w
-#
-# Note that the membrane potential can diverge to positive infinity due to the exponential term. To avoid numerical instabilities, instead of :math:`V_m`, the value :math:`\min(V_m,V_{peak})` is used in the dynamical equations.
-#
-#
-# References
-# ++++++++++
-#
-# .. [1] Brette R and Gerstner W (2005). Adaptive exponential
-# integrate-and-fire model as an effective description of neuronal
-# activity. Journal of Neurophysiology. 943637-3642
-# DOI: https://doi.org/10.1152/jn.00686.2005
-#
-#
-# See also
-# ++++++++
-#
-# iaf_psc_alpha, aeif_psc_exp
-#
-model aeif_psc_alpha_neuron:
-
- state:
- V_m mV = E_L # Membrane potential
- w pA = 0 pA # Spike-adaptation current
- refr_t ms = 0 ms # Refractory period timer
- I_syn_exc pA = 0 pA # AHP conductance
- I_syn_exc' pA/ms = 0 pA/ms # AHP conductance
- I_syn_inh pA = 0 pA # AHP conductance
- I_syn_inh' pA/ms = 0 pA/ms # AHP conductance
-
- equations:
- inline V_bounded mV = min(V_m, V_peak) # prevent exponential divergence
-
- I_syn_exc'' = -2 * I_syn_exc' / tau_exc - I_syn_exc / tau_exc**2
- I_syn_inh'' = -2 * I_syn_inh' / tau_inh - I_syn_inh / tau_inh**2
-
- # Add inlines to simplify the equation definition of V_m
- inline exp_arg real = (V_bounded - V_th) / Delta_T
- inline I_spike pA = g_L * Delta_T * exp(exp_arg)
-
- V_m' = (-g_L * (V_bounded - E_L) + I_spike + I_syn_exc - I_syn_inh - w + I_e + I_stim) / C_m
- w' = (a * (V_bounded - E_L) - w) / tau_w
-
- refr_t' = -1e3 * ms/s # refractoriness is implemented as an ODE, representing a timer counting back down to zero. XXX: TODO: This should simply read ``refr_t' = -1 / s`` (see https://github.com/nest/nestml/issues/984)
-
- parameters:
- # membrane parameters
- C_m pF = 281.0 pF # Membrane Capacitance
- refr_T ms = 2 ms # Duration of refractory period
- V_reset mV = -60.0 mV # Reset Potential
- g_L nS = 30.0 nS # Leak Conductance
- E_L mV = -70.6 mV # Leak reversal Potential (aka resting potential)
-
- # spike adaptation parameters
- a nS = 4 nS # Subthreshold adaptation
- b pA = 80.5 pA # Spike-triggered adaptation
- Delta_T mV = 2.0 mV # Slope factor
- tau_w ms = 144.0 ms # Adaptation time constant
- V_th mV = -50.4 mV # Threshold Potential
- V_peak mV = 0 mV # Spike detection threshold
-
- # synaptic parameters
- tau_exc ms = 0.2 ms # Synaptic Time Constant Excitatory Synapse
- tau_inh ms = 2.0 ms # Synaptic Time Constant for Inhibitory Synapse
-
- # constant external input current
- I_e pA = 0 pA
-
- input:
- exc_spikes <- excitatory spike
- inh_spikes <- inhibitory spike
- I_stim pA <- continuous
-
- output:
- spike
-
- update:
- if refr_t > 0 ms:
- # neuron is absolute refractory, do not evolve V_m
- integrate_odes(I_syn_exc, I_syn_inh, w, refr_t)
- else:
- # always evolve all ODEs; V_m
- integrate_odes(I_syn_exc, I_syn_inh, w, V_m)
-
- onCondition(V_m >= V_th):
- # threshold crossing
- refr_t = refr_T # start of the refractory period
- V_m = V_reset
- w += b
- emit_spike()
-
- onReceive(exc_spikes): # Spike input
- I_syn_exc' += exc_spikes * (e / tau_exc) * pA * s
-
- onReceive(inh_spikes): # Spike input
- I_syn_inh' += inh_spikes * (e / tau_inh) * pA * s
diff --git a/models/neurons/hh_cond_exp_destexhe_neuron.nestml b/models/neurons/hh_cond_exp_destexhe_neuron.nestml
index 96dc8902f..a609dbac4 100644
--- a/models/neurons/hh_cond_exp_destexhe_neuron.nestml
+++ b/models/neurons/hh_cond_exp_destexhe_neuron.nestml
@@ -80,8 +80,8 @@ model hh_cond_exp_destexhe_neuron:
inline I_M pA = g_M * Noninact_p * (V_m - E_K)
inline I_noise pA = (g_noise_exc * (V_m - E_exc) + g_noise_inh * (V_m - E_inh))
- inline I_syn_exc pA = convolve(g_exc, exc_spikes) * nS * ( V_m - E_exc )
- inline I_syn_inh pA = convolve(g_inh, inh_spikes) * nS * ( V_m - E_inh )
+ inline I_syn_exc pA = convolve(g_exc, exc_spikes.weight) * ( V_m - E_exc )
+ inline I_syn_inh pA = convolve(g_inh, inh_spikes.weight) * ( V_m - E_inh )
V_m' =( -I_Na - I_K - I_M - I_L - I_syn_exc - I_syn_inh + I_e + I_stim - I_noise) / C_m
refr_t' = -1e3 * ms/s # refractoriness is implemented as an ODE, representing a timer counting back down to zero. XXX: TODO: This should simply read ``refr_t' = -1 / s`` (see https://github.com/nest/nestml/issues/984)
@@ -147,8 +147,8 @@ model hh_cond_exp_destexhe_neuron:
D_inh uS**2/ms = 2 * sigma_noise_inh**2 / tau_syn_inh
input:
- inh_spikes <- inhibitory spike
- exc_spikes <- excitatory spike
+ inh_spikes <- spike(weight nS)
+ exc_spikes <- spike(weight nS)
I_stim pA <- continuous
output:
diff --git a/models/neurons/hh_cond_exp_traub_neuron.nestml b/models/neurons/hh_cond_exp_traub_neuron.nestml
index 29c939f1e..d02e2c904 100644
--- a/models/neurons/hh_cond_exp_traub_neuron.nestml
+++ b/models/neurons/hh_cond_exp_traub_neuron.nestml
@@ -88,8 +88,8 @@ model hh_cond_exp_traub_neuron:
inline I_Na pA = g_Na * Act_m * Act_m * Act_m * Act_h * ( V_m - E_Na )
inline I_K pA = g_K * Inact_n * Inact_n * Inact_n * Inact_n * ( V_m - E_K )
inline I_L pA = g_L * ( V_m - E_L )
- inline I_syn_exc pA = convolve(g_exc, exc_spikes) * nS * ( V_m - E_exc )
- inline I_syn_inh pA = convolve(g_inh, inh_spikes) * nS * ( V_m - E_inh )
+ inline I_syn_exc pA = convolve(g_exc, exc_spikes.weight) * ( V_m - E_exc )
+ inline I_syn_inh pA = convolve(g_inh, inh_spikes.weight) * ( V_m - E_inh )
V_m' = ( -I_Na - I_K - I_L - I_syn_exc - I_syn_inh + I_e + I_stim ) / C_m
refr_t' = -1e3 * ms/s # refractoriness is implemented as an ODE, representing a timer counting back down to zero. XXX: TODO: This should simply read ``refr_t' = -1 / s`` (see https://github.com/nest/nestml/issues/984)
@@ -134,8 +134,8 @@ model hh_cond_exp_traub_neuron:
I_e pA = 0 pA
input:
- inh_spikes <- inhibitory spike
- exc_spikes <- excitatory spike
+ inh_spikes <- spike(weight nS)
+ exc_spikes <- spike(weight nS)
I_stim pA <- continuous
output:
diff --git a/models/neurons/hh_moto_5ht_neuron.nestml b/models/neurons/hh_moto_5ht_neuron.nestml
index a53ce44cb..d51cdf716 100644
--- a/models/neurons/hh_moto_5ht_neuron.nestml
+++ b/models/neurons/hh_moto_5ht_neuron.nestml
@@ -68,8 +68,8 @@ model hh_moto_5ht_neuron:
# synapses: alpha functions
kernel I_syn_in = (e/tau_syn_in) * t * exp(-t/tau_syn_in)
kernel I_syn_ex = (e/tau_syn_ex) * t * exp(-t/tau_syn_ex)
- inline I_syn_exc pA = convolve(I_syn_ex, exc_spikes) * pA
- inline I_syn_inh pA = convolve(I_syn_in, inh_spikes) * pA
+ inline I_syn_exc pA = convolve(I_syn_ex, exc_spikes.weight)
+ inline I_syn_inh pA = convolve(I_syn_in, inh_spikes.weight)
inline E_Ca mV = ((1000.0 * R_const * T_current) / (2. * F_const)) * log10(Ca_out / Ca_in) * mV
@@ -129,8 +129,8 @@ model hh_moto_5ht_neuron:
alpha mmol/pA = 1E-5 mmol/pA
input:
- inh_spikes <- inhibitory spike
- exc_spikes <- excitatory spike
+ inh_spikes <- spike(weight pA)
+ exc_spikes <- spike(weight pA)
I_stim pA <- continuous
output:
diff --git a/models/neurons/hh_psc_alpha_neuron.nestml b/models/neurons/hh_psc_alpha_neuron.nestml
index ee393bd7c..dd272d686 100644
--- a/models/neurons/hh_psc_alpha_neuron.nestml
+++ b/models/neurons/hh_psc_alpha_neuron.nestml
@@ -79,8 +79,8 @@ model hh_psc_alpha_neuron:
kernel K_syn_inh = (e/tau_syn_inh) * t * exp(-t/tau_syn_inh)
kernel K_syn_exc = (e/tau_syn_exc) * t * exp(-t/tau_syn_exc)
- inline I_syn_exc pA = convolve(K_syn_exc, exc_spikes) * pA
- inline I_syn_inh pA = convolve(K_syn_inh, inh_spikes) * pA
+ inline I_syn_exc pA = convolve(K_syn_exc, exc_spikes.weight)
+ inline I_syn_inh pA = convolve(K_syn_inh, inh_spikes.weight)
inline I_Na pA = g_Na * Act_m * Act_m * Act_m * Inact_h * ( V_m - E_Na )
inline I_K pA = g_K * Act_n * Act_n * Act_n * Act_n * ( V_m - E_K )
inline I_L pA = g_L * ( V_m - E_L )
@@ -129,8 +129,8 @@ model hh_psc_alpha_neuron:
beta_h_init real = 1. / ( 1. + exp( -( V_m_init / mV + 35. ) / 10. ) )
input:
- exc_spikes <- excitatory spike
- inh_spikes <- inhibitory spike
+ exc_spikes <- spike(weight pA)
+ inh_spikes <- spike(weight pA)
I_stim pA <- continuous
output:
diff --git a/models/neurons/hill_tononi_neuron.nestml b/models/neurons/hill_tononi_neuron.nestml
index 77ac79176..092d025ed 100644
--- a/models/neurons/hill_tononi_neuron.nestml
+++ b/models/neurons/hill_tononi_neuron.nestml
@@ -78,10 +78,10 @@ model hill_tononi_neuron:
# V_m
#############
- inline I_syn_ampa pA = -convolve(g_AMPA, AMPA) * nS * ( V_m - AMPA_E_rev )
- inline I_syn_nmda pA = -convolve(g_NMDA, NMDA) * nS * ( V_m - NMDA_E_rev ) / ( 1 + exp( ( NMDA_Vact - V_m ) / NMDA_Sact ) )
- inline I_syn_gaba_a pA = -convolve(g_GABAA, GABA_A) * nS * ( V_m - GABA_A_E_rev )
- inline I_syn_gaba_b pA = -convolve(g_GABAB, GABA_B) * nS * ( V_m - GABA_B_E_rev )
+ inline I_syn_ampa pA = -convolve(g_AMPA, AMPA.weight) * ( V_m - AMPA_E_rev )
+ inline I_syn_nmda pA = -convolve(g_NMDA, NMDA.weight) * ( V_m - NMDA_E_rev ) / ( 1 + exp( ( NMDA_Vact - V_m ) / NMDA_Sact ) )
+ inline I_syn_gaba_a pA = -convolve(g_GABAA, GABA_A.weight) * ( V_m - GABA_A_E_rev )
+ inline I_syn_gaba_b pA = -convolve(g_GABAB, GABA_B.weight) * ( V_m - GABA_B_E_rev )
inline I_syn pA = I_syn_ampa + I_syn_nmda + I_syn_gaba_a + I_syn_gaba_b
inline I_Na pA = -g_NaL * ( V_m - E_Na )
@@ -203,10 +203,10 @@ model hill_tononi_neuron:
GABA_BInitialValue real = compute_synapse_constant( GABA_B_Tau_1, GABA_B_Tau_2, GABA_B_g_peak )
input:
- AMPA <- spike
- NMDA <- spike
- GABA_A <- spike
- GABA_B <- spike
+ AMPA <- spike(weight nS)
+ NMDA <- spike(weight nS)
+ GABA_A <- spike(weight nS)
+ GABA_B <- spike(weight nS)
I_stim pA <- continuous
output:
diff --git a/models/neurons/iaf_chxk_2008_neuron.nestml b/models/neurons/iaf_chxk_2008_neuron.nestml
index e9033104d..aa59f945d 100644
--- a/models/neurons/iaf_chxk_2008_neuron.nestml
+++ b/models/neurons/iaf_chxk_2008_neuron.nestml
@@ -68,8 +68,8 @@ model iaf_chxk_2008_neuron:
kernel g_exc = (e/tau_syn_exc) * t * exp(-t/tau_syn_exc)
g_ahp'' = -2 * g_ahp' / tau_ahp - g_ahp / tau_ahp**2
- inline I_syn_exc pA = convolve(g_exc, exc_spikes) * nS * ( V_m - E_exc )
- inline I_syn_inh pA = convolve(g_inh, inh_spikes) * nS * ( V_m - E_inh )
+ inline I_syn_exc pA = convolve(g_exc, exc_spikes.weight) * ( V_m - E_exc )
+ inline I_syn_inh pA = convolve(g_inh, inh_spikes.weight) * ( V_m - E_inh )
inline I_ahp pA = g_ahp * ( V_m - E_ahp )
inline I_leak pA = g_L * ( V_m - E_L )
@@ -102,8 +102,8 @@ model iaf_chxk_2008_neuron:
PSConInit_AHP real = G_ahp * e / tau_ahp * (ms/nS)
input:
- inh_spikes <- inhibitory spike
- exc_spikes <- excitatory spike
+ inh_spikes <- spike(weight nS)
+ exc_spikes <- spike(weight nS)
I_stim pA <- continuous
output:
diff --git a/models/neurons/iaf_cond_alpha_neuron.nestml b/models/neurons/iaf_cond_alpha_neuron.nestml
index 986cac306..6ff142f65 100644
--- a/models/neurons/iaf_cond_alpha_neuron.nestml
+++ b/models/neurons/iaf_cond_alpha_neuron.nestml
@@ -62,14 +62,14 @@ model iaf_cond_alpha_neuron:
refr_t ms = 0 ms # Refractory period timer
equations:
- kernel g_inh = (e/tau_syn_inh) * t * exp(-t/tau_syn_inh)
- kernel g_exc = (e/tau_syn_exc) * t * exp(-t/tau_syn_exc)
+ kernel g_inh = (e / tau_syn_inh) * t * exp(-t / tau_syn_inh)
+ kernel g_exc = (e / tau_syn_exc) * t * exp(-t / tau_syn_exc)
- inline I_syn_exc pA = convolve(g_exc, exc_spikes) * nS * ( V_m - E_exc )
- inline I_syn_inh pA = convolve(g_inh, inh_spikes) * nS * ( V_m - E_inh )
- inline I_leak pA = g_L * ( V_m - E_L )
+ inline I_syn_exc pA = convolve(g_exc, exc_spikes.weight) * (V_m - E_exc)
+ inline I_syn_inh pA = convolve(g_inh, inh_spikes.weight) * (V_m - E_inh)
+ inline I_leak pA = g_L * (V_m - E_L)
- V_m' = ( -I_leak - I_syn_exc - I_syn_inh + I_e + I_stim ) / C_m
+ V_m' = (-I_leak - I_syn_exc - I_syn_inh + I_e + I_stim) / C_m
refr_t' = -1e3 * ms/s # refractoriness is implemented as an ODE, representing a timer counting back down to zero. XXX: TODO: This should simply read ``refr_t' = -1 / s`` (see https://github.com/nest/nestml/issues/984)
parameters:
@@ -89,8 +89,8 @@ model iaf_cond_alpha_neuron:
I_e pA = 0 pA
input:
- exc_spikes <- excitatory spike
- inh_spikes <- inhibitory spike
+ exc_spikes <- spike(weight nS)
+ inh_spikes <- spike(weight nS)
I_stim pA <- continuous
output:
diff --git a/models/neurons/iaf_cond_beta_neuron.nestml b/models/neurons/iaf_cond_beta_neuron.nestml
index 59e995748..bd396df0f 100644
--- a/models/neurons/iaf_cond_beta_neuron.nestml
+++ b/models/neurons/iaf_cond_beta_neuron.nestml
@@ -84,8 +84,8 @@ model iaf_cond_beta_neuron:
kernel g_ex' = g_ex$ - g_ex / tau_syn_rise_E,
g_ex$' = -g_ex$ / tau_syn_decay_E
- inline I_syn_exc pA = (F_E + convolve(g_ex, exc_spikes) * nS) * (V_m - E_ex)
- inline I_syn_inh pA = (F_I + convolve(g_in, inh_spikes)* nS) * (V_m - E_in)
+ inline I_syn_exc pA = (F_E + convolve(g_ex, exc_spikes.weight)) * (V_m - E_ex)
+ inline I_syn_inh pA = (F_I + convolve(g_in, inh_spikes.weight)) * (V_m - E_in)
inline I_leak pA = g_L * (V_m - E_L) # pA = nS * mV
V_m' = (-I_leak - I_syn_exc - I_syn_inh + I_e + I_stim ) / C_m
@@ -121,8 +121,8 @@ model iaf_cond_beta_neuron:
g_I_const real = 1 / (exp(-t_peak_I / tau_syn_decay_I) - exp(-t_peak_I / tau_syn_rise_I))
input:
- exc_spikes <- excitatory spike
- inh_spikes <- inhibitory spike
+ exc_spikes <- spike(weight nS)
+ inh_spikes <- spike(weight nS)
I_stim pA <- continuous
output:
diff --git a/models/neurons/iaf_cond_exp_neuron.nestml b/models/neurons/iaf_cond_exp_neuron.nestml
index aeeec2c28..19a051358 100644
--- a/models/neurons/iaf_cond_exp_neuron.nestml
+++ b/models/neurons/iaf_cond_exp_neuron.nestml
@@ -56,8 +56,8 @@ model iaf_cond_exp_neuron:
kernel g_inh = exp(-t/tau_syn_inh) # inputs from the inh conductance
kernel g_exc = exp(-t/tau_syn_exc) # inputs from the exc conductance
- inline I_syn_exc pA = convolve(g_exc, exc_spikes) * nS * ( V_m - E_exc )
- inline I_syn_inh pA = convolve(g_inh, inh_spikes) * nS * ( V_m - E_inh )
+ inline I_syn_exc pA = convolve(g_exc, exc_spikes.weight) * ( V_m - E_exc )
+ inline I_syn_inh pA = convolve(g_inh, inh_spikes.weight) * ( V_m - E_inh )
inline I_leak pA = g_L * ( V_m - E_L )
V_m' = ( -I_leak - I_syn_exc - I_syn_inh + I_e + I_stim ) / C_m
@@ -80,8 +80,8 @@ model iaf_cond_exp_neuron:
I_e pA = 0 pA
input:
- exc_spikes <- excitatory spike
- inh_spikes <- inhibitory spike
+ exc_spikes <- spike(weight nS)
+ inh_spikes <- spike(weight nS)
I_stim pA <- continuous
output:
diff --git a/models/neurons/iaf_cond_exp_sfa_rr_neuron.nestml b/models/neurons/iaf_cond_exp_sfa_rr_neuron.nestml
index 636a2fc51..636ae83fa 100644
--- a/models/neurons/iaf_cond_exp_sfa_rr_neuron.nestml
+++ b/models/neurons/iaf_cond_exp_sfa_rr_neuron.nestml
@@ -69,8 +69,8 @@ model iaf_cond_exp_sfa_rr_neuron:
g_sfa' = -g_sfa / tau_sfa
g_rr' = -g_rr / tau_rr
- inline I_syn_exc pA = convolve(g_exc, exc_spikes) * nS * ( V_m - E_exc )
- inline I_syn_inh pA = convolve(g_inh, inh_spikes) * nS * ( V_m - E_inh )
+ inline I_syn_exc pA = convolve(g_exc, exc_spikes.weight) * ( V_m - E_exc )
+ inline I_syn_inh pA = convolve(g_inh, inh_spikes.weight) * ( V_m - E_inh )
inline I_L pA = g_L * ( V_m - E_L )
inline I_sfa pA = g_sfa * ( V_m - E_sfa )
inline I_rr pA = g_rr * ( V_m - E_rr )
@@ -100,8 +100,8 @@ model iaf_cond_exp_sfa_rr_neuron:
I_e pA = 0 pA
input:
- inh_spikes <- inhibitory spike
- exc_spikes <- excitatory spike
+ inh_spikes <- spike(weight nS)
+ exc_spikes <- spike(weight nS)
I_stim pA <- continuous
output:
diff --git a/models/neurons/iaf_psc_alpha_neuron.nestml b/models/neurons/iaf_psc_alpha_neuron.nestml
index dabd0b993..80bfbd5ed 100644
--- a/models/neurons/iaf_psc_alpha_neuron.nestml
+++ b/models/neurons/iaf_psc_alpha_neuron.nestml
@@ -87,7 +87,7 @@ model iaf_psc_alpha_neuron:
equations:
kernel I_kernel_inh = (e / tau_syn_inh) * t * exp(-t / tau_syn_inh)
kernel I_kernel_exc = (e / tau_syn_exc) * t * exp(-t / tau_syn_exc)
- inline I pA = convolve(I_kernel_exc, exc_spikes) * pA - convolve(I_kernel_inh, inh_spikes) * pA + I_e + I_stim
+ inline I pA = convolve(I_kernel_exc, exc_spikes.weight) - convolve(I_kernel_inh, inh_spikes.weight) + I_e + I_stim
V_m' = -(V_m - E_L) / tau_m + I / C_m
refr_t' = -1e3 * ms/s # refractoriness is implemented as an ODE, representing a timer counting back down to zero. XXX: TODO: This should simply read ``refr_t' = -1 / s`` (see https://github.com/nest/nestml/issues/984)
@@ -105,8 +105,8 @@ model iaf_psc_alpha_neuron:
I_e pA = 0 pA
input:
- exc_spikes <- excitatory spike
- inh_spikes <- inhibitory spike
+ exc_spikes <- spike(weight pA)
+ inh_spikes <- spike(weight pA)
I_stim pA <- continuous
output:
diff --git a/models/neurons/iaf_psc_delta_fixed_timestep_neuron.nestml b/models/neurons/iaf_psc_delta_fixed_timestep_neuron.nestml
index 38b00b099..e1c5975fb 100644
--- a/models/neurons/iaf_psc_delta_fixed_timestep_neuron.nestml
+++ b/models/neurons/iaf_psc_delta_fixed_timestep_neuron.nestml
@@ -1,19 +1,19 @@
# iaf_psc_delta_fixed_timestep - Current-based leaky integrate-and-fire neuron model with delta-kernel post-synaptic currents
# ###########################################################################################################################
-#
+#
# Description
# +++++++++++
-#
+#
# An implementation of a leaky integrate-and-fire model where the potential jumps on each spike arrival. The threshold crossing is followed by an absolute refractory period during which the membrane potential is clamped to the resting potential. Spikes arriving while the neuron is refractory are discarded.
-#
+#
# The general framework for the consistent formulation of systems with neuron-like dynamics interacting by point events is described in [1]_. A flow chart can be found in [2]_.
-#
+#
# This model differs from ``iaf_psc_delta`` in that it assumes a fixed-timestep simulator, so the functions ``resolution()`` and ``steps()`` can be used.
-#
-#
+#
+#
# References
# ++++++++++
-#
+#
# .. [1] Rotter S, Diesmann M (1999). Exact simulation of
# time-invariant linear systems with applications to neuronal
# modeling. Biologial Cybernetics 81:381-402.
@@ -22,11 +22,11 @@
# space analysis of synchronous spiking in cortical neural
# networks. Neurocomputing 38-40:565-571.
# DOI: https://doi.org/10.1016/S0925-2312(01)00409-X
-#
-#
+#
+#
# See also
# ++++++++
-#
+#
# iaf_psc_alpha, iaf_psc_exp
#
#
@@ -34,7 +34,7 @@
# +++++++++++++++++++
#
# This file is part of NEST.
-#
+#
# Copyright (C) 2004 The NEST Initiative
#
# NEST is free software: you can redistribute it and/or modify
@@ -73,16 +73,17 @@ model iaf_psc_delta_fixed_timestep_neuron:
refr_counts integer = steps(refr_T)
input:
- spikes <- spike
+ spike_in_port <- spike(weight mV)
I_stim pA <- continuous
output:
spike
- onReceive(spikes):
- # discard spikes if neuron is refractory
+ onReceive(spike_in_port):
+ # process spike only if neuron is not refractory
if refr_counter == 0:
- V_m += spikes * mV * s
+ spike_weight mV = integrate(spike_in_port.weight, t, t + timestep())
+ V_m += spike_weight
update:
if refr_counter > 0:
diff --git a/models/neurons/iaf_psc_delta_neuron.nestml b/models/neurons/iaf_psc_delta_neuron.nestml
index 392d76f4a..9361f8e02 100644
--- a/models/neurons/iaf_psc_delta_neuron.nestml
+++ b/models/neurons/iaf_psc_delta_neuron.nestml
@@ -1,29 +1,29 @@
# iaf_psc_delta - Current-based leaky integrate-and-fire neuron model with delta-kernel post-synaptic currents
# ############################################################################################################
-#
+#
# Description
# +++++++++++
-#
+#
# iaf_psc_delta is an implementation of a leaky integrate-and-fire model
# where the potential jumps on each spike arrival.
-#
+#
# The threshold crossing is followed by an absolute refractory period
# during which the membrane potential is clamped to the resting potential.
-#
+#
# Spikes arriving while the neuron is refractory, are discarded by
# default. If the property ``with_refr_input`` is set to true, such
# spikes are added to the membrane potential at the end of the
# refractory period, dampened according to the interval between
# arrival and end of refractoriness.
-#
+#
# The general framework for the consistent formulation of systems with
# neuron like dynamics interacting by point events is described in
# [1]_. A flow chart can be found in [2]_.
-#
-#
+#
+#
# References
# ++++++++++
-#
+#
# .. [1] Rotter S, Diesmann M (1999). Exact simulation of
# time-invariant linear systems with applications to neuronal
# modeling. Biologial Cybernetics 81:381-402.
@@ -32,11 +32,11 @@
# space analysis of synchronous spiking in cortical neural
# networks. Neurocomputing 38-40:565-571.
# DOI: https://doi.org/10.1016/S0925-2312(01)00409-X
-#
-#
+#
+#
# See also
# ++++++++
-#
+#
# iaf_psc_alpha, iaf_psc_exp
#
#
@@ -44,7 +44,7 @@
# +++++++++++++++++++
#
# This file is part of NEST.
-#
+#
# Copyright (C) 2004 The NEST Initiative
#
# NEST is free software: you can redistribute it and/or modify
@@ -66,14 +66,13 @@ model iaf_psc_delta_neuron:
refr_t ms = 0 ms # Refractory period timer
equations:
- kernel K_delta = delta(t)
- V_m' = -(V_m - E_L) / tau_m + convolve(K_delta, spikes) * (mV / ms) + (I_e + I_stim) / C_m
+ V_m' = -(V_m - E_L) / tau_m + (I_e + I_stim) / C_m + spike_in_port.psp
refr_t' = -1e3 * ms/s # refractoriness is implemented as an ODE, representing a timer counting back down to zero. XXX: TODO: This should simply read ``refr_t' = -1 / s`` (see https://github.com/nest/nestml/issues/984)
parameters:
tau_m ms = 10 ms # Membrane time constant
C_m pF = 250 pF # Capacity of the membrane
- refr_T ms = 2 ms # Duration of refractory period
+ refr_T ms = 2 ms # Duration of refractory period
E_L mV = -70 mV # Resting membrane potential
V_reset mV = -70 mV # Reset potential of the membrane
V_th mV = -55 mV # Spike threshold
@@ -82,7 +81,7 @@ model iaf_psc_delta_neuron:
I_e pA = 0 pA
input:
- spikes <- spike
+ spike_in_port <- spike(psp mV)
I_stim pA <- continuous
output:
diff --git a/models/neurons/iaf_psc_exp_dend_neuron.nestml b/models/neurons/iaf_psc_exp_dend_neuron.nestml
new file mode 100644
index 000000000..3f7bd5384
--- /dev/null
+++ b/models/neurons/iaf_psc_exp_dend_neuron.nestml
@@ -0,0 +1,86 @@
+# iaf_psc_exp_dend - Leaky integrate-and-fire neuron model with exponential PSCs
+# #########################################################################
+#
+# Description
+# +++++++++++
+#
+# iaf_psc_exp is an implementation of a leaky integrate-and-fire model
+# with exponential-kernel postsynaptic currents (PSCs) according to [1]_.
+# Thus, postsynaptic currents have an infinitely short rise time.
+#
+# The threshold crossing is followed by an absolute refractory period (t_ref)
+# during which the membrane potential is clamped to the resting potential
+# and spiking is prohibited.
+#
+# .. note::
+# If tau_m is very close to tau_syn_ex or tau_syn_in, numerical problems
+# may arise due to singularities in the propagator matrics. If this is
+# the case, replace equal-valued parameters by a single parameter.
+#
+# For details, please see ``IAF_neurons_singularity.ipynb`` in
+# the NEST source code (``docs/model_details``).
+#
+#
+# References
+# ++++++++++
+#
+# .. [1] Tsodyks M, Uziel A, Markram H (2000). Synchrony generation in recurrent
+# networks with frequency-dependent synapses. The Journal of Neuroscience,
+# 20,RC50:1-5. URL: https://infoscience.epfl.ch/record/183402
+#
+#
+# See also
+# ++++++++
+#
+# iaf_cond_exp
+#
+model iaf_psc_exp_dend_neuron:
+
+ state:
+ V_m mV = E_L # Membrane potential
+ I_dend pA = 0 pA # Third factor, to be read out by synapse during weight update
+ refr_t ms = 0 ms # Refractory period timer
+
+ equations:
+ kernel I_kernel_inh = exp(-t/tau_syn_inh)
+ kernel I_kernel_exc = exp(-t/tau_syn_exc)
+ inline I_syn pA = convolve(I_kernel_exc, exc_spikes.weight) - convolve(I_kernel_inh, inh_spikes.weight)
+ V_m' = -(V_m - E_L) / tau_m + (I_syn + I_e + I_stim) / C_m
+ refr_t' = -1e3 * ms/s # refractoriness is implemented as an ODE, representing a timer counting back down to zero. XXX: TODO: This should simply read ``refr_t' = -1 / s`` (see https://github.com/nest/nestml/issues/984)
+
+ parameters:
+ C_m pF = 250 pF # Capacity of the membrane
+ tau_m ms = 10 ms # Membrane time constant
+ tau_syn_inh ms = 2 ms # Time constant of inhibitory synaptic current
+ tau_syn_exc ms = 2 ms # Time constant of excitatory synaptic current
+ refr_T ms = 2 ms # Duration of refractory period
+ E_L mV = -70 mV # Resting potential
+ V_reset mV = -70 mV # Reset potential of the membrane
+ V_th mV = -55 mV # Spike threshold potential
+
+ # constant external input current
+ I_e pA = 0 pA
+
+ input:
+ exc_spikes <- spike(weight pA)
+ inh_spikes <- spike(weight pA)
+ I_stim pA <- continuous
+
+ output:
+ spike
+
+ update:
+ I_dend *= .95 # I_dend is always evolved, even when refractory
+
+ if refr_t > 0 ms:
+ # neuron is absolute refractory, do not evolve V_m
+ integrate_odes(refr_t)
+ else:
+ # neuron not refractory
+ integrate_odes(V_m)
+
+ onCondition(refr_t <= 0 ms and V_m >= V_th): # threshold crossing
+ # threshold crossing
+ refr_t = refr_T # start of the refractory period
+ V_m = V_reset
+ emit_spike()
diff --git a/models/neurons/iaf_psc_exp_htum_neuron.nestml b/models/neurons/iaf_psc_exp_htum_neuron.nestml
index f42984338..9760dcb8a 100644
--- a/models/neurons/iaf_psc_exp_htum_neuron.nestml
+++ b/models/neurons/iaf_psc_exp_htum_neuron.nestml
@@ -83,7 +83,7 @@ model iaf_psc_exp_htum_neuron:
equations:
kernel I_kernel_inh = exp(-t / tau_syn_inh)
kernel I_kernel_exc = exp(-t / tau_syn_exc)
- inline I_syn pA = convolve(I_kernel_exc, exc_spikes) * pA - convolve(I_kernel_inh, inh_spikes) * pA
+ inline I_syn pA = convolve(I_kernel_exc, exc_spikes.weight) - convolve(I_kernel_inh, inh_spikes.weight)
V_m' = -V_m / tau_m + (I_syn + I_e + I_stim) / C_m
parameters:
@@ -121,8 +121,8 @@ model iaf_psc_exp_htum_neuron:
RefractoryCountsTot integer = steps(t_ref_tot) [[RefractoryCountsTot > 0]]
input:
- exc_spikes <- excitatory spike
- inh_spikes <- inhibitory spike
+ exc_spikes <- spike(weight pA)
+ inh_spikes <- spike(weight pA)
I_stim pA <- continuous
output:
diff --git a/models/neurons/iaf_psc_exp_neuron.nestml b/models/neurons/iaf_psc_exp_neuron.nestml
index 1134f20bb..3c4ceecbb 100644
--- a/models/neurons/iaf_psc_exp_neuron.nestml
+++ b/models/neurons/iaf_psc_exp_neuron.nestml
@@ -102,13 +102,20 @@ model iaf_psc_exp_neuron:
I_e pA = 0 pA
input:
- exc_spikes <- excitatory spike
- inh_spikes <- inhibitory spike
+ spike_in_port <- spike(weight pA)
I_stim pA <- continuous
output:
spike
+ onReceive(spike_in_port):
+ # route the incoming spike on the basis of the weight: less than zero means an inhibitory spike; greater than zero means an excitatory spike
+ weight pA = integral(spike_in_port.weight, t, t + timestep()) # integrate the incoming spike train (in pA/s) from "just before" the spike to "just after" the spike to obtain weight in pA
+ if weight > 0 pA:
+ I_syn_exc += weight
+ else:
+ I_syn_inh -= weight
+
update:
if refr_t > 0 ms:
# neuron is absolute refractory, do not evolve V_m
@@ -117,12 +124,6 @@ model iaf_psc_exp_neuron:
# neuron not refractory
integrate_odes(I_syn_exc, I_syn_inh, V_m)
- onReceive(exc_spikes):
- I_syn_exc += exc_spikes * pA * s
-
- onReceive(inh_spikes):
- I_syn_inh += inh_spikes * pA * s
-
onCondition(refr_t <= 0 ms and V_m >= V_th):
# threshold crossing
refr_t = refr_T # start of the refractory period
diff --git a/models/neurons/izhikevich_neuron.nestml b/models/neurons/izhikevich_neuron.nestml
index cdb8c1282..ac8688ac4 100644
--- a/models/neurons/izhikevich_neuron.nestml
+++ b/models/neurons/izhikevich_neuron.nestml
@@ -1,38 +1,38 @@
# izhikevich - Izhikevich neuron model
# ####################################
-#
+#
# Description
# +++++++++++
-#
+#
# Implementation of the simple spiking neuron model introduced by Izhikevich [1]_. The dynamics are given by:
-#
+#
# .. math::
-#
+#
# dV_{m}/dt &= 0.04 V_{m}^2 + 5 V_{m} + 140 - U_{m} + I\\
# dU_{m}/dt &= a (b V_{m} - U_{m})
-#
-#
+#
+#
# .. math::
-#
+#
# &\text{if}\;\; V_{m} \geq V_{th}:\\
# &\;\;\;\; V_{m} \text{ is set to } c\\
# &\;\;\;\; U_{m} \text{ is incremented by } d\\
# & \, \\
# &V_{m} \text{ jumps on each spike arrival by the weight of the spike}
-#
+#
# Incoming spikes cause an instantaneous jump in the membrane potential proportional to the strength of the synapse.
-#
+#
# As published in [1]_, the numerics differs from the standard forward Euler technique in two ways:
-#
+#
# 1) the new value of :math:`U_{m}` is calculated based on the new value of :math:`V_{m}`, rather than the previous value
# 2) the variable :math:`V_{m}` is updated using a time step half the size of that used to update variable :math:`U_{m}`.
-#
+#
# This model will instead be simulated using the numerical solver that is recommended by ODE-toolbox during code generation.
-#
-#
+#
+#
# References
# ++++++++++
-#
+#
# .. [1] Izhikevich, Simple Model of Spiking Neurons, IEEE Transactions on Neural Networks (2003) 14:1569-1572
#
#
@@ -40,7 +40,7 @@
# +++++++++++++++++++
#
# This file is part of NEST.
-#
+#
# Copyright (C) 2004 The NEST Initiative
#
# NEST is free software: you can redistribute it and/or modify
@@ -63,8 +63,8 @@ model izhikevich_neuron:
U_m real = b * V_m_init # Membrane potential recovery variable
equations:
- V_m' = ( 0.04 * V_m * V_m / mV + 5.0 * V_m + ( 140 - U_m ) * mV + ( (I_e + I_stim) * GOhm ) ) / ms
- U_m' = a*(b*V_m-U_m * mV) / (mV*ms)
+ V_m' = (0.04 * V_m * V_m / mV + 5 * V_m + (140 - U_m) * mV + ((I_e + I_stim) * GOhm)) / ms
+ U_m' = a * (b * V_m - U_m * mV) / (mV * ms)
parameters:
a real = 0.02 # describes time scale of recovery variable
@@ -79,21 +79,23 @@ model izhikevich_neuron:
I_e pA = 0 pA
input:
- spikes <- spike
+ spike_in_port <- spike(weight mV)
I_stim pA <- continuous
output:
spike
- update:
- integrate_odes()
-
- # Add synaptic current
- V_m += spikes * mV * s
+ onReceive(spike_in_port):
+ # Add synaptic contribution
+ spike_weight real = integrate(spike_in_port.weight, t, t + timestep())
+ V_m += spike_weight
# lower bound of membrane potential
V_m = max(V_min, V_m)
+ update:
+ integrate_odes()
+
onCondition(V_m >= V_th):
# threshold crossing
V_m = c
diff --git a/models/neurons/izhikevich_psc_alpha_neuron.nestml b/models/neurons/izhikevich_psc_alpha_neuron.nestml
index fc6ccc72d..b29b50e79 100644
--- a/models/neurons/izhikevich_psc_alpha_neuron.nestml
+++ b/models/neurons/izhikevich_psc_alpha_neuron.nestml
@@ -68,8 +68,8 @@ model izhikevich_psc_alpha_neuron:
kernel K_syn_inh = (e/tau_syn_inh) * t * exp(-t/tau_syn_inh)
kernel K_syn_exc = (e/tau_syn_exc) * t * exp(-t/tau_syn_exc)
- inline I_syn_exc pA = convolve(K_syn_exc, exc_spikes) * pA
- inline I_syn_inh pA = convolve(K_syn_inh, inh_spikes) * pA
+ inline I_syn_exc pA = convolve(K_syn_exc, exc_spikes.weight)
+ inline I_syn_inh pA = convolve(K_syn_inh, inh_spikes.weight)
V_m' = (k * (V_m - V_r) * (V_m - V_t) - U_m + I_e + I_stim + I_syn_exc - I_syn_inh) / C_m
U_m' = a * (b * (V_m - V_r) - U_m)
@@ -93,8 +93,8 @@ model izhikevich_psc_alpha_neuron:
I_e pA = 0 pA
input:
- inh_spikes <- inhibitory spike
- exc_spikes <- excitatory spike
+ inh_spikes <- spike(weight pA)
+ exc_spikes <- spike(weight pA)
I_stim pA <- continuous
output:
diff --git a/models/neurons/mat2_psc_exp_neuron.nestml b/models/neurons/mat2_psc_exp_neuron.nestml
index edb592042..c350296de 100644
--- a/models/neurons/mat2_psc_exp_neuron.nestml
+++ b/models/neurons/mat2_psc_exp_neuron.nestml
@@ -76,7 +76,7 @@ model mat2_psc_exp_neuron:
kernel I_kernel_inh = exp(-t/tau_syn_inh)
kernel I_kernel_exc = exp(-t/tau_syn_exc)
- inline I_syn pA = convolve(I_kernel_exc, exc_spikes) * pA - convolve(I_kernel_inh, inh_spikes) * pA
+ inline I_syn pA = convolve(I_kernel_exc, exc_spikes.weight) - convolve(I_kernel_inh, inh_spikes.weight)
V_m' = -(V_m - E_L) / tau_m + (I_syn + I_e + I_stim) / C_m
refr_t' = -1e3 * ms/s # refractoriness is implemented as an ODE, representing a timer counting back down to zero. XXX: TODO: This should simply read ``refr_t' = -1 / s`` (see https://github.com/nest/nestml/issues/984)
@@ -102,8 +102,8 @@ model mat2_psc_exp_neuron:
P22th real = exp(-h / tau_2)
input:
- exc_spikes <- excitatory spike
- inh_spikes <- inhibitory spike
+ exc_spikes <- spike(weight pA)
+ inh_spikes <- spike(weight pA)
I_stim pA <- continuous
output:
diff --git a/models/neurons/terub_gpe_neuron.nestml b/models/neurons/terub_gpe_neuron.nestml
index c09700021..d11772e7b 100644
--- a/models/neurons/terub_gpe_neuron.nestml
+++ b/models/neurons/terub_gpe_neuron.nestml
@@ -99,8 +99,8 @@ model terub_gpe_neuron:
inline g_k_Ca real = 15.0 #Report:15, Terman Rubin 2002: 20.0
inline g_k1 real = 30.0
- inline I_exc_mod real = -convolve(g_exc, exc_spikes) * nS * V_m
- inline I_inh_mod real = convolve(g_inh, inh_spikes) * nS * (V_m-E_gg)
+ inline I_exc_mod real = -convolve(g_exc, exc_spikes.weight) * V_m
+ inline I_inh_mod real = convolve(g_inh, inh_spikes.weight) * (V_m - E_gg)
inline tau_n real = g_tau_n_0 + g_tau_n_1 / (1. + exp(-(V_m-g_theta_n_tau)/g_sigma_n_tau))
inline tau_h real = g_tau_h_0 + g_tau_h_1 / (1. + exp(-(V_m-g_theta_h_tau)/g_sigma_h_tau))
@@ -159,8 +159,8 @@ model terub_gpe_neuron:
I_e pA = 0 pA
input:
- exc_spikes <- excitatory spike
- inh_spikes <- inhibitory spike
+ exc_spikes <- spike(weight nS)
+ inh_spikes <- spike(weight nS)
I_stim pA <- continuous
output:
diff --git a/models/neurons/terub_stn_neuron.nestml b/models/neurons/terub_stn_neuron.nestml
index 856351d3a..ddcba9356 100644
--- a/models/neurons/terub_stn_neuron.nestml
+++ b/models/neurons/terub_stn_neuron.nestml
@@ -104,8 +104,8 @@ model terub_stn_neuron:
inline k_Ca real = 22.5
inline k1 real = 15.0
- inline I_exc_mod pA = -convolve(g_exc, exc_spikes) * nS * V_m
- inline I_inh_mod pA = convolve(g_inh, inh_spikes) * nS * (V_m - E_gs)
+ inline I_exc_mod pA = -convolve(g_exc, exc_spikes.weight) * V_m
+ inline I_inh_mod pA = convolve(g_inh, inh_spikes.weight) * (V_m - E_gs)
inline tau_n ms = tau_n_0 + tau_n_1 / (1. + exp(-(V_m-theta_n_tau)/sigma_n_tau))
inline tau_h ms = tau_h_0 + tau_h_1 / (1. + exp(-(V_m-theta_h_tau)/sigma_h_tau))
@@ -165,8 +165,8 @@ model terub_stn_neuron:
I_e pA = 0 pA
input:
- exc_spikes <- excitatory spike
- inh_spikes <- inhibitory spike
+ exc_spikes <- spike(weight nS)
+ inh_spikes <- spike(weight nS)
I_stim pA <- continuous
output:
diff --git a/models/neurons/traub_cond_multisyn_neuron.nestml b/models/neurons/traub_cond_multisyn_neuron.nestml
index 9c9aa3f4d..d11d3f63a 100644
--- a/models/neurons/traub_cond_multisyn_neuron.nestml
+++ b/models/neurons/traub_cond_multisyn_neuron.nestml
@@ -66,10 +66,10 @@ model traub_cond_multisyn_neuron:
g_GABAB$ real = GABA_BInitialValue
equations:
- recordable inline I_syn_ampa pA = -convolve(g_AMPA, AMPA) * nS * ( V_m - AMPA_E_rev )
- recordable inline I_syn_nmda pA = -convolve(g_NMDA, NMDA) * nS * ( V_m - NMDA_E_rev ) / ( 1 + exp( ( NMDA_Vact - V_m ) / NMDA_Sact ) )
- recordable inline I_syn_gaba_a pA = -convolve(g_GABAA, GABA_A) * nS * ( V_m - GABA_A_E_rev )
- recordable inline I_syn_gaba_b pA = -convolve(g_GABAB, GABA_B) * nS * ( V_m - GABA_B_E_rev )
+ recordable inline I_syn_ampa pA = -convolve(g_AMPA, AMPA.weight) * ( V_m - AMPA_E_rev )
+ recordable inline I_syn_nmda pA = -convolve(g_NMDA, NMDA.weight) * ( V_m - NMDA_E_rev ) / ( 1 + exp( ( NMDA_Vact - V_m ) / NMDA_Sact ) )
+ recordable inline I_syn_gaba_a pA = -convolve(g_GABAA, GABA_A.weight) * ( V_m - GABA_A_E_rev )
+ recordable inline I_syn_gaba_b pA = -convolve(g_GABAB, GABA_B.weight) * ( V_m - GABA_B_E_rev )
recordable inline I_syn pA = I_syn_ampa + I_syn_nmda + I_syn_gaba_a + I_syn_gaba_b
inline I_Na pA = g_Na * Act_m * Act_m * Act_m * Inact_h * ( V_m - E_Na )
@@ -161,10 +161,10 @@ model traub_cond_multisyn_neuron:
beta_h_init real = 4.0 / (1.0 + exp(-(V_m / mV + 27.) / 5.))
input:
- AMPA <- spike
- NMDA <- spike
- GABA_A <- spike
- GABA_B <- spike
+ AMPA <- spike(weight nS)
+ NMDA <- spike(weight nS)
+ GABA_A <- spike(weight nS)
+ GABA_B <- spike(weight nS)
I_stim pA <- continuous
output:
diff --git a/models/neurons/traub_psc_alpha_neuron.nestml b/models/neurons/traub_psc_alpha_neuron.nestml
index 2bf4f1f1f..4f06e0258 100644
--- a/models/neurons/traub_psc_alpha_neuron.nestml
+++ b/models/neurons/traub_psc_alpha_neuron.nestml
@@ -56,8 +56,8 @@ model traub_psc_alpha_neuron:
kernel K_syn_inh = (e/tau_syn_inh) * t * exp(-t/tau_syn_inh)
kernel K_syn_exc = (e/tau_syn_exc) * t * exp(-t/tau_syn_exc)
- inline I_syn_exc pA = convolve(K_syn_exc, exc_spikes) * pA
- inline I_syn_inh pA = convolve(K_syn_inh, inh_spikes) * pA
+ inline I_syn_exc pA = convolve(K_syn_exc, exc_spikes.weight)
+ inline I_syn_inh pA = convolve(K_syn_inh, inh_spikes.weight)
inline I_Na pA = g_Na * Act_m * Act_m * Act_m * Inact_h * ( V_m - E_Na )
inline I_K pA = g_K * Act_n * Act_n * Act_n * Act_n * ( V_m - E_K )
inline I_L pA = g_L * ( V_m - E_L )
@@ -107,8 +107,8 @@ model traub_psc_alpha_neuron:
beta_h_init real = 4.0 / (1.0 + exp(-(V_m / mV + 27.) / 5.))
input:
- exc_spikes <- excitatory spike
- inh_spikes <- inhibitory spike
+ exc_spikes <- spike(weight pA)
+ inh_spikes <- spike(weight pA)
I_stim pA <- continuous
output:
diff --git a/models/neurons/wb_cond_exp_neuron.nestml b/models/neurons/wb_cond_exp_neuron.nestml
index 62ab2a5c4..6ba80526d 100644
--- a/models/neurons/wb_cond_exp_neuron.nestml
+++ b/models/neurons/wb_cond_exp_neuron.nestml
@@ -61,8 +61,8 @@ model wb_cond_exp_neuron:
kernel g_inh = exp(-t / tau_syn_inh)
kernel g_exc = exp(-t / tau_syn_exc)
- recordable inline I_syn_exc pA = convolve(g_exc, exc_spikes) * nS * ( V_m - E_exc )
- recordable inline I_syn_inh pA = convolve(g_inh, inh_spikes) * nS * ( V_m - E_inh )
+ recordable inline I_syn_exc pA = convolve(g_exc, exc_spikes.weight) * ( V_m - E_exc )
+ recordable inline I_syn_inh pA = convolve(g_inh, inh_spikes.weight) * ( V_m - E_inh )
inline I_Na pA = g_Na * _subexpr(V_m) * Inact_h * ( V_m - E_Na )
inline I_K pA = g_K * Act_n**4 * ( V_m - E_K )
@@ -99,8 +99,8 @@ model wb_cond_exp_neuron:
beta_h_init 1/ms = 5.0 / (exp(-0.1 / mV * (E_L + 28.0 mV)) + 1.0) /ms
input:
- exc_spikes <- excitatory spike
- inh_spikes <- inhibitory spike
+ exc_spikes <- spike(weight nS)
+ inh_spikes <- spike(weight nS)
I_stim pA <- continuous
output:
diff --git a/models/neurons/wb_cond_multisyn_neuron.nestml b/models/neurons/wb_cond_multisyn_neuron.nestml
index 6dd763c1b..2e8e3eb9f 100644
--- a/models/neurons/wb_cond_multisyn_neuron.nestml
+++ b/models/neurons/wb_cond_multisyn_neuron.nestml
@@ -66,10 +66,10 @@ model wb_cond_multisyn_neuron:
g_GABAB$ real = GABA_BInitialValue
equations:
- recordable inline I_syn_ampa pA = -convolve(g_AMPA, AMPA) * nS * ( V_m - AMPA_E_rev )
- recordable inline I_syn_nmda pA = -convolve(g_NMDA, NMDA) * nS * ( V_m - NMDA_E_rev ) / ( 1 + exp( ( NMDA_Vact - V_m ) / NMDA_Sact ) )
- recordable inline I_syn_gaba_a pA = -convolve(g_GABAA, GABA_A) * nS * ( V_m - GABA_A_E_rev )
- recordable inline I_syn_gaba_b pA = -convolve(g_GABAB, GABA_B) * nS * ( V_m - GABA_B_E_rev )
+ recordable inline I_syn_ampa pA = -convolve(g_AMPA, AMPA.weight) * ( V_m - AMPA_E_rev )
+ recordable inline I_syn_nmda pA = -convolve(g_NMDA, NMDA.weight) * ( V_m - NMDA_E_rev ) / ( 1 + exp( ( NMDA_Vact - V_m ) / NMDA_Sact ) )
+ recordable inline I_syn_gaba_a pA = -convolve(g_GABAA, GABA_A.weight) * ( V_m - GABA_A_E_rev )
+ recordable inline I_syn_gaba_b pA = -convolve(g_GABAB, GABA_B.weight) * ( V_m - GABA_B_E_rev )
recordable inline I_syn pA = I_syn_ampa + I_syn_nmda + I_syn_gaba_a + I_syn_gaba_b
inline I_Na pA = g_Na * Act_m_inf(V_m)**3 * Inact_h * ( V_m - E_Na )
@@ -149,10 +149,10 @@ model wb_cond_multisyn_neuron:
beta_h_init real = 5.0 / (exp(-0.1 * (V_m / mV + 28.0)) + 1.0)
input:
- AMPA <- spike
- NMDA <- spike
- GABA_A <- spike
- GABA_B <- spike
+ AMPA <- spike(weight nS)
+ NMDA <- spike(weight nS)
+ GABA_A <- spike(weight nS)
+ GABA_B <- spike(weight nS)
I_stim pA <- continuous
output:
diff --git a/pynestml/cocos/__init__.py b/pynestml/cocos/__init__.py
index ddfc395d4..635e455a4 100644
--- a/pynestml/cocos/__init__.py
+++ b/pynestml/cocos/__init__.py
@@ -41,7 +41,6 @@
'co_co_parameters_assigned_only_in_parameter_block.py',
'co_cos_manager.py',
'co_co_convolve_has_correct_parameter.py',
- 'co_co_input_port_qualifier_unique.py',
'co_co_user_defined_function_correctly_defined.py',
'co_co_variable_once_per_scope.py',
'co_co_vector_variable_in_non_vector_declaration.py'
diff --git a/pynestml/cocos/co_co_convolve_has_correct_parameter.py b/pynestml/cocos/co_co_convolve_has_correct_parameter.py
index 8e3661a2d..38d0ee3d4 100644
--- a/pynestml/cocos/co_co_convolve_has_correct_parameter.py
+++ b/pynestml/cocos/co_co_convolve_has_correct_parameter.py
@@ -19,9 +19,9 @@
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see .
+from pynestml.cocos.co_co import CoCo
from pynestml.meta_model.ast_function_call import ASTFunctionCall
from pynestml.meta_model.ast_simple_expression import ASTSimpleExpression
-from pynestml.cocos.co_co import CoCo
from pynestml.symbols.predefined_functions import PredefinedFunctions
from pynestml.utils.logger import LoggingLevel, Logger
from pynestml.utils.messages import Messages
diff --git a/pynestml/cocos/co_co_input_port_qualifier_unique.py b/pynestml/cocos/co_co_input_port_qualifier_unique.py
deleted file mode 100644
index 92cc5a4ca..000000000
--- a/pynestml/cocos/co_co_input_port_qualifier_unique.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# co_co_input_port_qualifier_unique.py
-#
-# This file is part of NEST.
-#
-# Copyright (C) 2004 The NEST Initiative
-#
-# NEST is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-#
-# NEST is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with NEST. If not, see .
-
-from pynestml.cocos.co_co import CoCo
-from pynestml.meta_model.ast_model import ASTModel
-from pynestml.utils.logger import LoggingLevel, Logger
-from pynestml.utils.messages import Messages
-from pynestml.visitors.ast_visitor import ASTVisitor
-
-
-class CoCoInputPortQualifierUnique(CoCo):
- """
- This coco ensures that each spike input port has at most one type of modifier inhibitory and excitatory.
-
- Allowed:
-
- .. code-block:: nestml
-
- spike pA <- inhibitory spike
-
- Not allowed:
-
- .. code-block:: nestml
-
- spike pA <- inhibitory inhibitory spike
-
- """
-
- @classmethod
- def check_co_co(cls, model: ASTModel):
- """
- Ensures the coco for the handed over model.
- :param node: a single model instance.
- """
- cls.neuronName = model.get_name()
- model.accept(InputPortQualifierUniqueVisitor())
-
-
-class InputPortQualifierUniqueVisitor(ASTVisitor):
- """
- This visitor ensures that all input ports are qualified uniquely by keywords.
- """
-
- def visit_input_port(self, node):
- """
- Checks the coco on the current node.
- :param node: a single input port.
- :type node: ASTInputPort
- """
- if node.is_spike():
- if node.has_input_qualifiers() and len(node.get_input_qualifiers()) > 1:
- code, message = Messages.get_multiple_keywords(", ".join([str(q) for q in node.get_input_qualifiers()]))
- Logger.log_message(error_position=node.get_source_position(), code=code, message=message,
- log_level=LoggingLevel.ERROR)
diff --git a/pynestml/cocos/co_co_no_attributes_on_continuous_port.py b/pynestml/cocos/co_co_no_attributes_on_continuous_port.py
new file mode 100644
index 000000000..26de84276
--- /dev/null
+++ b/pynestml/cocos/co_co_no_attributes_on_continuous_port.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+#
+# co_co_no_attributes_on_continuous_port.py
+#
+# This file is part of NEST.
+#
+# Copyright (C) 2004 The NEST Initiative
+#
+# NEST is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# NEST is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with NEST. If not, see .
+
+from pynestml.cocos.co_co import CoCo
+from pynestml.meta_model.ast_model import ASTModel
+from pynestml.utils.logger import Logger, LoggingLevel
+from pynestml.utils.messages import Messages
+
+
+class CoCoNoAttributesOnContinuousPort(CoCo):
+ """
+ This context condition checker ensures that no attributes are defined on continuous time output ports.
+ """
+
+ @classmethod
+ def check_co_co(cls, neuron: ASTModel):
+ """
+ Checks the coco for the handed over neuron.
+ :param neuron: a single neuron instance.
+ """
+ output_blocks = neuron.get_output_blocks()
+ if not len(output_blocks) == 1:
+ # too few or too many output blocks; this will be checked elsewhere
+ return
+
+ output_block = output_blocks[0]
+
+ if output_block.is_continuous() and output_block.get_attributes():
+ code, message = Messages.get_continuous_output_port_cannot_have_attributes()
+ Logger.log_message(code=code, message=message, log_level=LoggingLevel.ERROR,
+ error_position=output_block.get_source_position())
+ return
diff --git a/pynestml/cocos/co_co_on_receive_vectors_should_be_constant_size.py b/pynestml/cocos/co_co_on_receive_vectors_should_be_constant_size.py
new file mode 100644
index 000000000..89de214a8
--- /dev/null
+++ b/pynestml/cocos/co_co_on_receive_vectors_should_be_constant_size.py
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+#
+# co_co_on_receive_vectors_should_be_constant_size.py
+#
+# This file is part of NEST.
+#
+# Copyright (C) 2004 The NEST Initiative
+#
+# NEST is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# NEST is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with NEST. If not, see .
+
+from pynestml.cocos.co_co import CoCo
+from pynestml.meta_model.ast_input_port import ASTInputPort
+from pynestml.meta_model.ast_model import ASTModel
+from pynestml.utils.logger import LoggingLevel, Logger
+from pynestml.utils.messages import Messages
+from pynestml.visitors.ast_visitor import ASTVisitor
+
+
+class CoCoOnReceiveVectorsShouldBeConstantSize(CoCo):
+ r"""
+ This CoCo is used to test the usage of onReceive blocks for vector ports of variable length.
+ """
+
+ @classmethod
+ def check_co_co(cls, node: ASTModel):
+ visitor = CoCoOnReceiveVectorsShouldBeConstantSizeVisitor()
+ node.accept(visitor)
+
+
+class CoCoOnReceiveVectorsShouldBeConstantSizeVisitor(ASTVisitor):
+ def visit_input_port(self, node: ASTInputPort):
+ if node.has_size_parameter():
+ try:
+ int(str(node.get_size_parameter()))
+ except ValueError:
+ # exception converting size parameter to int; hence, not allowed
+ code, message = Messages.get_vector_input_ports_should_be_of_constant_size()
+ Logger.log_message(error_position=node.get_source_position(), log_level=LoggingLevel.ERROR, code=code, message=message)
diff --git a/pynestml/cocos/co_co_priorities_correctly_specified.py b/pynestml/cocos/co_co_priorities_correctly_specified.py
index a36cdc631..5b97272c1 100644
--- a/pynestml/cocos/co_co_priorities_correctly_specified.py
+++ b/pynestml/cocos/co_co_priorities_correctly_specified.py
@@ -42,7 +42,7 @@ def check_co_co(cls, node: ASTModel):
priorities = {} # type: Dict[str, int]
for on_receive_block in node.get_on_receive_blocks():
if "priority" in on_receive_block.get_const_parameters():
- priorities[on_receive_block.get_port_name()] = int(on_receive_block.get_const_parameters()["priority"])
+ priorities[on_receive_block.get_input_port_variable().get_name()] = int(on_receive_block.get_const_parameters()["priority"])
if len(priorities) == 1:
on_receive_block_name = list(priorities.keys())[0]
diff --git a/pynestml/cocos/co_co_spike_input_ports_appear_only_in_equation_rhs_and_event_handlers.py b/pynestml/cocos/co_co_spike_input_ports_appear_only_in_equation_rhs_and_event_handlers.py
new file mode 100644
index 000000000..176ca16e5
--- /dev/null
+++ b/pynestml/cocos/co_co_spike_input_ports_appear_only_in_equation_rhs_and_event_handlers.py
@@ -0,0 +1,106 @@
+# -*- coding: utf-8 -*-
+#
+# co_co_spike_input_ports_appear_only_in_equation_rhs_and_event_handlers.py
+#
+# This file is part of NEST.
+#
+# Copyright (C) 2004 The NEST Initiative
+#
+# NEST is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# NEST is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with NEST. If not, see .
+
+from typing import Optional
+
+from pynestml.cocos.co_co import CoCo
+from pynestml.meta_model.ast_inline_expression import ASTInlineExpression
+from pynestml.meta_model.ast_input_port import ASTInputPort
+from pynestml.meta_model.ast_model import ASTModel
+from pynestml.meta_model.ast_ode_equation import ASTOdeEquation
+from pynestml.meta_model.ast_on_receive_block import ASTOnReceiveBlock
+from pynestml.meta_model.ast_variable import ASTVariable
+from pynestml.utils.ast_utils import ASTUtils
+from pynestml.utils.logger import Logger, LoggingLevel
+from pynestml.utils.messages import Messages
+from pynestml.visitors.ast_visitor import ASTVisitor
+
+
+class CoCoSpikeInputPortsAppearOnlyInEquationRHSAndEventHandlers(CoCo):
+ """
+ This coco ensures that spiking input port names appear only in the right-hand side of equations and in the onReceive block declaration.
+ """
+
+ @classmethod
+ def check_co_co(cls, node):
+ """
+ Ensures the coco for the handed over node.
+ """
+ assert node is not None and (isinstance(node, ASTModel)), "No or wrong type provided (%s): expecting neuron or synapse!" % type(node)
+
+ visitor = SpikeInputPortsAppearOnlyInEquationRHSAndEventHandlersVisitor()
+ visitor.model_ = node
+ node.accept(visitor)
+
+
+class SpikeInputPortsAppearOnlyInEquationRHSAndEventHandlersVisitor(ASTVisitor):
+
+ def visit_variable(self, node: ASTVariable):
+ in_port: Optional[ASTInputPort] = ASTUtils.get_input_port_by_name(self.model_.get_input_blocks(), node.get_name())
+
+ # only check spiking input ports
+ if in_port is not None and in_port.is_spike():
+ if isinstance(node.get_parent(), ASTOnReceiveBlock) and node.get_parent().get_input_port_variable() == node:
+ # input port appears inside the declaration of an onReceive block; everything is OK
+ return
+
+ # this should only be checked in event handler statements
+ if in_port.parameters and not node.attribute:
+ # input port has parameters (for instance, ``x`` in ``foo <- spike(x real)`` but the variable reference is missing an attribute (``foo`` instead of ``foo.x``)
+ code, message = Messages.get_spike_input_port_attribute_missing(node.get_name())
+ Logger.log_message(code=code, message=message, error_position=node.get_source_position(),
+ log_level=LoggingLevel.ERROR)
+
+ _node = node
+ while _node:
+ _node = _node.get_parent()
+
+ if isinstance(_node, ASTOnReceiveBlock) and _node.input_port_variable.name == in_port.name:
+ if not node.get_vector_parameter():
+ # non-vector spike input port was used inside an ``onReceive`` block for this spike port; everything is OK
+ return
+
+ try:
+ if int(str(node.get_vector_parameter())) == int(str(_node.get_input_port_variable().get_vector_parameter())):
+ # vector spike input port was used inside an ``onReceive`` block for this spike port and numerical index is correct; everything is OK
+ return
+ except ValueError:
+ # in case vector parameter was not an integer numeral
+ return # XXX: DO MORE CHECKS!
+
+ if isinstance(_node, ASTOdeEquation):
+ # spike input port was used inside the rhs of an equation; everything is OK
+ return
+
+ if isinstance(_node, ASTInlineExpression):
+ # spike input port was used inside the rhs of an inline expression; everything is OK
+ return
+
+ if isinstance(_node, ASTModel):
+ # we reached the top-level block without running into an ``update`` block on the way --> incorrect usage of the function
+
+ node_name = node.get_name()
+ if node.get_vector_parameter():
+ node_name += "[" + str(node.get_vector_parameter()) + "]"
+
+ code, message = Messages.get_spike_input_port_appears_outside_equation_rhs_and_event_handler(node_name)
+ Logger.log_message(code=code, message=message, error_position=node.get_source_position(),
+ log_level=LoggingLevel.ERROR)
diff --git a/pynestml/cocos/co_co_vector_declaration_right_size.py b/pynestml/cocos/co_co_vector_declaration_right_size.py
index 6597f481f..fcb8536eb 100644
--- a/pynestml/cocos/co_co_vector_declaration_right_size.py
+++ b/pynestml/cocos/co_co_vector_declaration_right_size.py
@@ -18,6 +18,7 @@
#
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see .
+
from pynestml.cocos.co_co import CoCo
from pynestml.meta_model.ast_declaration import ASTDeclaration
from pynestml.meta_model.ast_expression import ASTExpression
diff --git a/pynestml/cocos/co_cos_manager.py b/pynestml/cocos/co_cos_manager.py
index 6858151f0..908030af6 100644
--- a/pynestml/cocos/co_cos_manager.py
+++ b/pynestml/cocos/co_cos_manager.py
@@ -42,24 +42,26 @@
from pynestml.cocos.co_co_inline_expression_not_assigned_to import CoCoInlineExpressionNotAssignedTo
from pynestml.cocos.co_co_inline_max_one_lhs import CoCoInlineMaxOneLhs
from pynestml.cocos.co_co_input_port_not_assigned_to import CoCoInputPortNotAssignedTo
-from pynestml.cocos.co_co_input_port_qualifier_unique import CoCoInputPortQualifierUnique
from pynestml.cocos.co_co_internals_assigned_only_in_internals_block import CoCoInternalsAssignedOnlyInInternalsBlock
from pynestml.cocos.co_co_integrate_odes_called_if_equations_defined import CoCoIntegrateOdesCalledIfEquationsDefined
from pynestml.cocos.co_co_invariant_is_boolean import CoCoInvariantIsBoolean
from pynestml.cocos.co_co_kernel_type import CoCoKernelType
from pynestml.cocos.co_co_model_name_unique import CoCoModelNameUnique
from pynestml.cocos.co_co_nest_random_functions_legally_used import CoCoNestRandomFunctionsLegallyUsed
+from pynestml.cocos.co_co_no_attributes_on_continuous_port import CoCoNoAttributesOnContinuousPort
from pynestml.cocos.co_co_no_kernels_except_in_convolve import CoCoNoKernelsExceptInConvolve
from pynestml.cocos.co_co_no_nest_name_space_collision import CoCoNoNestNameSpaceCollision
from pynestml.cocos.co_co_no_duplicate_compilation_unit_names import CoCoNoDuplicateCompilationUnitNames
from pynestml.cocos.co_co_odes_have_consistent_units import CoCoOdesHaveConsistentUnits
from pynestml.cocos.co_co_ode_functions_have_consistent_units import CoCoOdeFunctionsHaveConsistentUnits
+from pynestml.cocos.co_co_on_receive_vectors_should_be_constant_size import CoCoOnReceiveVectorsShouldBeConstantSize
from pynestml.cocos.co_co_output_port_defined_if_emit_call import CoCoOutputPortDefinedIfEmitCall
from pynestml.cocos.co_co_parameters_assigned_only_in_parameter_block import CoCoParametersAssignedOnlyInParameterBlock
from pynestml.cocos.co_co_priorities_correctly_specified import CoCoPrioritiesCorrectlySpecified
from pynestml.cocos.co_co_resolution_func_legally_used import CoCoResolutionFuncLegallyUsed
from pynestml.cocos.co_co_resolution_func_used import CoCoResolutionOrStepsFuncUsed
from pynestml.cocos.co_co_simple_delta_function import CoCoSimpleDeltaFunction
+from pynestml.cocos.co_co_spike_input_ports_appear_only_in_equation_rhs_and_event_handlers import CoCoSpikeInputPortsAppearOnlyInEquationRHSAndEventHandlers
from pynestml.cocos.co_co_state_variables_initialized import CoCoStateVariablesInitialized
from pynestml.cocos.co_co_timestep_function_legally_used import CoCoTimestepFuncLegallyUsed
from pynestml.cocos.co_co_user_defined_function_correctly_defined import CoCoUserDefinedFunctionCorrectlyDefined
@@ -100,6 +102,14 @@ def check_each_block_defined_at_most_once(cls, node: ASTModel):
"""
CoCoEachBlockDefinedAtMostOnce.check_co_co(node)
+ @classmethod
+ def check_input_ports_appear_only_in_equation_rhs_and_event_handlers(cls, node: ASTModel):
+ """
+ Checks if in the handed over model, each block is defined at most once and mandatory blocks are defined.
+ :param node: a single model instance
+ """
+ CoCoSpikeInputPortsAppearOnlyInEquationRHSAndEventHandlers.check_co_co(node)
+
@classmethod
def check_function_declared_and_correctly_typed(cls, model: ASTModel):
"""
@@ -211,14 +221,6 @@ def check_no_nest_namespace_collisions(cls, model: ASTModel):
"""
CoCoNoNestNameSpaceCollision.check_co_co(model)
- @classmethod
- def check_input_port_qualifier_unique(cls, model: ASTModel):
- """
- Checks that no spiking input ports are defined with redundant qualifiers.
- :param model: a single model object.
- """
- CoCoInputPortQualifierUnique.check_co_co(model)
-
@classmethod
def check_kernel_type(cls, model: ASTModel) -> None:
"""
@@ -419,6 +421,13 @@ def check_input_port_size_type(cls, model: ASTModel):
"""
CoCoVectorInputPortsCorrectSizeType.check_co_co(model)
+ @classmethod
+ def check_on_receive_vectors_should_be_constant_size(cls, model: ASTModel):
+ """
+ :param model: a single model object
+ """
+ CoCoOnReceiveVectorsShouldBeConstantSize.check_co_co(model)
+
@classmethod
def check_co_co_nest_random_functions_legally_used(cls, model: ASTModel):
"""
@@ -427,6 +436,14 @@ def check_co_co_nest_random_functions_legally_used(cls, model: ASTModel):
"""
CoCoNestRandomFunctionsLegallyUsed.check_co_co(model)
+ @classmethod
+ def check_co_co_no_attributes_on_continuous_port(cls, model: ASTModel):
+ """
+ Checks that no attributes are defined on continuous time output ports.
+ :param model: a single model object.
+ """
+ CoCoNoAttributesOnContinuousPort.check_co_co(model)
+
@classmethod
def check_cocos(cls, model: ASTModel, after_ast_rewrite: bool = False):
"""
@@ -451,7 +468,6 @@ def check_cocos(cls, model: ASTModel, after_ast_rewrite: bool = False):
cls.check_order_of_equations_correct(model)
cls.check_numerator_of_unit_is_one_if_numeric(model)
cls.check_no_nest_namespace_collisions(model)
- cls.check_input_port_qualifier_unique(model)
cls.check_parameters_not_assigned_outside_parameters_block(model)
cls.check_internals_not_assigned_outside_internals_block(model)
cls.check_user_defined_function_correctly_built(model)
@@ -468,12 +484,12 @@ def check_cocos(cls, model: ASTModel, after_ast_rewrite: bool = False):
cls.check_ode_functions_have_consistent_units(model)
cls.check_correct_usage_of_kernels(model)
cls.check_resolution_func_used(model) # ``__h = resolution()`` is added after transformations; put this check inside the ``if`` to make sure it's not always triggered
+ cls.check_expression_correct(model)
if FrontendConfiguration.get_target_platform().upper() != 'NEST_COMPARTMENTAL':
cls.check_integrate_odes_called_if_equations_defined(model)
cls.check_invariant_type_correct(model)
cls.check_vector_in_non_vector_declaration_detected(model)
cls.check_convolve_has_correct_parameter(model)
- cls.check_expression_correct(model)
cls.check_simple_delta_function(model)
cls.check_function_argument_template_types_consistent(model)
cls.check_vector_parameter_declaration(model)
@@ -482,5 +498,7 @@ def check_cocos(cls, model: ASTModel, after_ast_rewrite: bool = False):
cls.check_resolution_func_legally_used(model)
cls.check_input_port_size_type(model)
cls.check_timestep_func_legally_used(model)
+ cls.check_co_co_no_attributes_on_continuous_port(model)
+ cls.check_input_ports_appear_only_in_equation_rhs_and_event_handlers(model)
Logger.set_current_node(None)
diff --git a/pynestml/codegeneration/autodoc_builder.py b/pynestml/codegeneration/autodoc_builder.py
index f1ccf7124..2dd6e7156 100644
--- a/pynestml/codegeneration/autodoc_builder.py
+++ b/pynestml/codegeneration/autodoc_builder.py
@@ -234,6 +234,24 @@ def _test_model_psp(self, model_name, max_weight: float = 10., model_opts=None,
spikegenerator = nest.Create("spike_generator",
params={"spike_times": spike_times, "spike_weights": spike_weights})
+ nest.Connect(spikegenerator, neuron1, syn_spec=syn_spec)
+ if len(neuron2.get("receptor_types")) > 1:
+ # this NESTML neuron is written as having separate input ports for excitatory and inhibitory spikes
+ spikegenerator_exc = nest.Create("spike_generator",
+ params={"spike_times": spike_times,
+ "spike_weights": spike_weights})
+ spikegenerator_inh = nest.Create("spike_generator",
+ params={"spike_times": spike_times,
+ "spike_weights": spike_weights})
+ nest.Connect(spikegenerator_exc, neuron2, syn_spec=syn_spec | {"receptor_type": neuron2.get("receptor_type")["EXC_SPIKES"]})
+ spikegenerator_inh = nest.Create("spike_generator",
+ params={"spike_times": spike_times,
+ "spike_weights": spike_weights})
+ nest.Connect(spikegenerator_inh, neuron2, syn_spec=syn_spec | {"receptor_type": neuron2.get("receptor_type")["INH_SPIKES"]})
+ else:
+ # this NESTML neuron is written as having one input port for excitatory and inhibitory spikes (with sign of the weight telling the difference)
+ nest.Connect(spikegenerator, neuron2, syn_spec=syn_spec)
+
nest.Connect(spikegenerator, neuron)
spike_recorder = nest.Create("spike_recorder")
diff --git a/pynestml/codegeneration/code_generator.py b/pynestml/codegeneration/code_generator.py
index d69784542..939130980 100644
--- a/pynestml/codegeneration/code_generator.py
+++ b/pynestml/codegeneration/code_generator.py
@@ -170,8 +170,9 @@ def generate_synapses(self, synapses: Sequence[ASTModel]) -> None:
for synapse in synapses:
self.generate_synapse_code(synapse)
- code, message = Messages.get_code_generated(synapse.get_name(), FrontendConfiguration.get_target_path())
- Logger.log_message(synapse, code, message, synapse.get_source_position(), LoggingLevel.INFO)
+ if not Logger.has_errors(synapse):
+ code, message = Messages.get_code_generated(synapse.get_name(), FrontendConfiguration.get_target_path())
+ Logger.log_message(synapse, code, message, synapse.get_source_position(), LoggingLevel.INFO)
def generate_model_code(self,
model_name: str,
diff --git a/pynestml/codegeneration/nest_code_generator.py b/pynestml/codegeneration/nest_code_generator.py
index 066109e3d..ea8135999 100644
--- a/pynestml/codegeneration/nest_code_generator.py
+++ b/pynestml/codegeneration/nest_code_generator.py
@@ -52,11 +52,17 @@
from pynestml.codegeneration.printers.sympy_simple_expression_printer import SympySimpleExpressionPrinter
from pynestml.frontend.frontend_configuration import FrontendConfiguration
from pynestml.meta_model.ast_assignment import ASTAssignment
+from pynestml.meta_model.ast_equations_block import ASTEquationsBlock
+from pynestml.meta_model.ast_input_block import ASTInputBlock
from pynestml.meta_model.ast_input_port import ASTInputPort
from pynestml.meta_model.ast_kernel import ASTKernel
from pynestml.meta_model.ast_model import ASTModel
from pynestml.meta_model.ast_node_factory import ASTNodeFactory
from pynestml.meta_model.ast_ode_equation import ASTOdeEquation
+from pynestml.meta_model.ast_on_receive_block import ASTOnReceiveBlock
+from pynestml.meta_model.ast_simple_expression import ASTSimpleExpression
+from pynestml.meta_model.ast_stmts_body import ASTStmtsBody
+from pynestml.meta_model.ast_variable import ASTVariable
from pynestml.symbol_table.symbol_table import SymbolTable
from pynestml.symbols.real_type_symbol import RealTypeSymbol
from pynestml.symbols.unit_type_symbol import UnitTypeSymbol
@@ -73,10 +79,11 @@
from pynestml.visitors.ast_equations_with_delay_vars_visitor import ASTEquationsWithDelayVarsVisitor
from pynestml.visitors.ast_equations_with_vector_variables import ASTEquationsWithVectorVariablesVisitor
from pynestml.visitors.ast_mark_delay_vars_visitor import ASTMarkDelayVarsVisitor
-from pynestml.visitors.ast_set_vector_parameter_in_update_expressions import \
- ASTSetVectorParameterInUpdateExpressionVisitor
+from pynestml.visitors.ast_parent_visitor import ASTParentVisitor
+from pynestml.visitors.ast_set_vector_parameter_in_update_expressions import ASTSetVectorParameterInUpdateExpressionVisitor
from pynestml.visitors.ast_symbol_table_visitor import ASTSymbolTableVisitor
from pynestml.visitors.ast_random_number_generator_visitor import ASTRandomNumberGeneratorVisitor
+from pynestml.visitors.ast_visitor import ASTVisitor
def find_spiking_post_port(synapse, namespace):
@@ -87,6 +94,45 @@ def find_spiking_post_port(synapse, namespace):
return None
+class NoAttributesSpikingInputPortNecessaryVisitor(ASTVisitor):
+ r"""This visitor checks whether any references occur in the model to a spiking input port without attributes.
+
+ For instance, for a spiking input port:
+
+ .. code:: nestml
+
+ input:
+ spikes_in_port <- spike(weight pA)
+
+ A reference to the port with attribute would be ``spikes_in_port.weight`` and a reference without attributes would be ``spikes_in_port``.
+
+ If no references to the port without an attribute are present, then no code needs to be generated for the buffer, saving on runtime performance.
+ """
+ def __init__(self, model: ASTModel, enable_on_receive_check: bool = True):
+ super().__init__()
+
+ self._model = model
+ self._attributes_spiking_input_port_necessary = False
+ self._spike_input_ports = model.get_spike_input_port_names()
+ self.enable_on_receive_check = enable_on_receive_check
+
+ def visit_variable(self, node: ASTVariable):
+ if node.name in self._spike_input_ports \
+ and node.get_attribute() is None \
+ and not ASTUtils.find_parent_node_by_type(node, ASTInputBlock):
+
+ if self.enable_on_receive_check and ASTUtils.find_parent_node_by_type(node, ASTOnReceiveBlock):
+ # parent is an onReceive block: ignore mentions in the onReceive block input_port_variable, check instead for occurrences in the body (statements) of the block
+ on_receive_block_stmts = ASTUtils.find_parent_node_by_type(node, ASTOnReceiveBlock).get_stmts_body()
+ v = NoAttributesSpikingInputPortNecessaryVisitor(self._model, enable_on_receive_check=False)
+ on_receive_block_stmts.accept(v)
+ self._attributes_spiking_input_port_necessary = v._attributes_spiking_input_port_necessary
+
+ return
+
+ self._attributes_spiking_input_port_necessary = True
+
+
class NESTCodeGenerator(CodeGenerator):
r"""
Code generator for a NEST Simulator C++ extension module.
@@ -115,6 +161,25 @@ class NESTCodeGenerator(CodeGenerator):
- **continuous_state_buffering_method**: Which method to use for buffering state variables between neuron and synapse pairs. When a synapse has a "continuous" input port, connected to a postsynaptic neuron, either the value is obtained taking the synaptic (dendritic, that is, synapse-soma) delay into account, requiring a buffer to store the value at each timepoint (``continuous_state_buffering_method = "continuous_time_buffer"``); or the value is obtained at the times of the somatic spikes of the postsynaptic neuron, ignoring the synaptic delay (``continuous_state_buffering_method == "post_spike_based"``). The former is more physically accurate but requires a large buffer and can require a long time to simulate. The latter ignores the dendritic delay but is much more computationally efficient.
- **delay_variable**: A mapping identifying, for each synapse (the name of which is given as a key), the variable or parameter in the model that corresponds with the NEST ``Connection`` class delay property.
- **weight_variable**: Like ``delay_variable``, but for synaptic weight.
+ - **linear_time_invariant_spiking_input_ports**: A list of spiking input ports which can be treated as linear and time-invariant; this implies that, for the given port(s), the weight of all spikes received within a timestep can be added together, improving memory consumption and runtime performance. Use with caution; for example, this is not compatible with using a single input port for, depending on the sign of the weight of the spike event, processing both inhibitory vs. excitatory spikes.
+ - **excitatory_inhibitory_combined_port**: A tuple containing the name of two spiking input ports. Without this option set, each input port would be assigned its own unique rport for connecting to in NEST, for instance:
+
+ .. code-block:: python
+
+ receptor_types = nest.GetStatus(neuron, "receptor_types")[0]
+ nest.Connect(sg_exc, neuron, syn_spec={"receptor_type": receptor_types["EXC_SPIKES"], "weight": 1.})
+ nest.Connect(sg_inh, neuron, syn_spec={"receptor_type": receptor_types["INH_SPIKES"], "weight": 1.})
+
+ For compatibility with legacy NEST Simulator models, these ports can instead be externally represented as a single port, that interprets spikes as either "excitatory" or "inhibitory" depending on the sign of the weight. In this case, connections to the neuron are made while omitting an explicit rport; inhibitory connections are the indicated by weights with a negative sign:
+
+ .. code-block:: python
+
+ receptor_types = nest.GetStatus(neuron, "receptor_types")[0]
+ nest.Connect(sg_exc, neuron, syn_spec={"weight": 1.})
+ nest.Connect(sg_inh, neuron, syn_spec={"weight": -1.})
+
+ This flag can only be used if there are exactly two spiking input ports in the model.
+
- **redirect_build_output**: An optional boolean key for redirecting the build output. Setting the key to ``True``, two files will be created for redirecting the ``stdout`` and the ``stderr`. The ``target_path`` will be used as the default location for creating the two files.
- **build_output_dir**: An optional string key representing the new path where the files corresponding to the output of the build phase will be created. This key requires that the ``redirect_build_output`` is set to ``True``.
@@ -146,7 +211,9 @@ class NESTCodeGenerator(CodeGenerator):
"numeric_solver": "rk45",
"continuous_state_buffering_method": "continuous_time_buffer",
"delay_variable": {},
- "weight_variable": {}
+ "weight_variable": {},
+ "linear_time_invariant_spiking_input_ports": [],
+ "excitatory_inhibitory_combined_port": ()
}
def __init__(self, options: Optional[Mapping[str, Any]] = None):
@@ -174,6 +241,7 @@ def run_nest_target_specific_cocos(self, neurons: Sequence[ASTModel], synapses:
for model in neurons + synapses:
# Check if the random number functions are used in the right blocks
CoCosManager.check_co_co_nest_random_functions_legally_used(model)
+ CoCosManager.check_on_receive_vectors_should_be_constant_size(model)
if Logger.has_errors(model.name):
raise Exception("Error(s) occurred during code generation")
@@ -339,6 +407,10 @@ def analyse_neuron(self, neuron: ASTModel) -> Tuple[Dict[str, ASTAssignment], Di
code, message = Messages.get_start_processing_model(neuron.get_name())
Logger.log_message(neuron, code, message, neuron.get_source_position(), LoggingLevel.INFO)
+ v = NoAttributesSpikingInputPortNecessaryVisitor(neuron)
+ neuron.accept(v)
+ neuron._attributes_spiking_input_port_necessary = v._attributes_spiking_input_port_necessary
+
if not neuron.get_equations_blocks():
# add all declared state variables as none of them are used in equations block
self.non_equations_state_variables[neuron.get_name()] = []
@@ -354,7 +426,8 @@ def analyse_neuron(self, neuron: ASTModel) -> Tuple[Dict[str, ASTAssignment], Di
kernel_buffers = ASTUtils.generate_kernel_buffers(neuron, equations_block)
InlineExpressionExpansionTransformer().transform(neuron)
- delta_factors = ASTUtils.get_delta_factors_(neuron, equations_block)
+ delta_factors = ASTUtils.get_delta_factors_from_input_port_references(neuron)
+ delta_factors |= ASTUtils.get_delta_factors_from_convolutions(neuron)
ASTUtils.replace_convolve_calls_with_buffers_(neuron, equations_block)
# Collect all equations with delay variables and replace ASTFunctionCall to ASTVariable wherever necessary
@@ -367,6 +440,8 @@ def analyse_neuron(self, neuron: ASTModel) -> Tuple[Dict[str, ASTAssignment], Di
neuron.accept(eqns_with_vector_vars_visitor)
equations_with_vector_vars = eqns_with_vector_vars_visitor.equations
+ neuron.accept(ASTParentVisitor())
+
analytic_solver, numeric_solver = self.ode_toolbox_analysis(neuron, kernel_buffers)
self.analytic_solver[neuron.get_name()] = analytic_solver
self.numeric_solver[neuron.get_name()] = numeric_solver
@@ -423,6 +498,10 @@ def analyse_synapse(self, synapse: ASTModel) -> Dict[str, ASTAssignment]:
code, message = Messages.get_start_processing_model(synapse.get_name())
Logger.log_message(synapse, code, message, synapse.get_source_position(), LoggingLevel.INFO)
+ v = NoAttributesSpikingInputPortNecessaryVisitor(synapse)
+ synapse.accept(v)
+ synapse._attributes_spiking_input_port_necessary = v._attributes_spiking_input_port_necessary
+
spike_updates = {}
if synapse.get_equations_blocks():
if len(synapse.get_equations_blocks()) > 1:
@@ -432,7 +511,8 @@ def analyse_synapse(self, synapse: ASTModel) -> Dict[str, ASTAssignment]:
kernel_buffers = ASTUtils.generate_kernel_buffers(synapse, equations_block)
InlineExpressionExpansionTransformer().transform(synapse)
- delta_factors = ASTUtils.get_delta_factors_(synapse, equations_block)
+ delta_factors = ASTUtils.get_delta_factors_from_input_port_references(synapse)
+ delta_factors |= ASTUtils.get_delta_factors_from_convolutions(synapse)
ASTUtils.replace_convolve_calls_with_buffers_(synapse, equations_block)
analytic_solver, numeric_solver = self.ode_toolbox_analysis(synapse, kernel_buffers)
@@ -553,6 +633,11 @@ def _get_model_namespace(self, astnode: ASTModel) -> Dict:
if "continuous_post_ports" in dir(astnode):
namespace["continuous_post_ports"] = astnode.continuous_post_ports
+ # input port/event handling options
+ namespace["linear_time_invariant_spiking_input_ports"] = self.get_option("linear_time_invariant_spiking_input_ports")
+
+ namespace["attributes_spiking_input_port_necessary"] = astnode._attributes_spiking_input_port_necessary
+
return namespace
def _get_synapse_model_namespace(self, synapse: ASTModel) -> Dict:
@@ -928,6 +1013,16 @@ def ode_toolbox_analysis(self, neuron: ASTModel, kernel_buffers: Mapping[ASTKern
disable_analytic_solver=disable_analytic_solver,
preserve_expressions=self.get_option("preserve_expressions"),
log_level=FrontendConfiguration.logging_level)
+
+ for solver in solver_result:
+ if "propagators" in solver.keys():
+ for k, v in solver["propagators"].items():
+ solver["propagators"][k] = v.replace("__DOT__", ".")
+
+ if "update_expressions" in solver.keys():
+ for k, v in solver["update_expressions"].items():
+ solver["update_expressions"][k] = v.replace("__DOT__", ".")
+
analytic_solver = None
analytic_solvers = [x for x in solver_result if x["solver"] == "analytical"]
assert len(analytic_solvers) <= 1, "More than one analytic solver not presently supported"
@@ -986,7 +1081,7 @@ def get_spike_update_expressions(self, neuron: ASTModel, kernel_buffers, solver_
spike_input_port_name = spike_input_port.get_variable().get_name()
if not spike_input_port_name in spike_updates.keys():
- spike_updates[str(spike_input_port)] = []
+ spike_updates[spike_input_port_name] = []
if "_is_post_port" in dir(spike_input_port.get_variable()) \
and spike_input_port.get_variable()._is_post_port:
@@ -994,25 +1089,32 @@ def get_spike_update_expressions(self, neuron: ASTModel, kernel_buffers, solver_
orig_port_name = spike_input_port_name[:spike_input_port_name.index("__for_")]
buffer_type = neuron.paired_synapse.get_scope().resolve_to_symbol(orig_port_name, SymbolKind.VARIABLE).get_type_symbol()
else:
- buffer_type = neuron.get_scope().resolve_to_symbol(spike_input_port_name, SymbolKind.VARIABLE).get_type_symbol()
+ # not a post port
+ if spike_input_port.get_variable().get_attribute():
+ variable_name = spike_input_port_name + "." + str(spike_input_port.get_variable().get_attribute())
+ else:
+ variable_name = spike_input_port_name
+
+ buffer_type = neuron.get_scope().resolve_to_symbol(variable_name, SymbolKind.VARIABLE).get_type_symbol()
assert not buffer_type is None
for kernel_var in kernel.get_variables():
for var_order in range(ASTUtils.get_kernel_var_order_from_ode_toolbox_result(kernel_var.get_name(), solver_dicts)):
- kernel_spike_buf_name = ASTUtils.construct_kernel_X_spike_buf_name(kernel_var.get_name(), spike_input_port, var_order)
+ kernel_spike_buf_name = ASTUtils.construct_kernel_X_spike_buf_name(kernel_var.get_name(), spike_input_port, var_order, attribute=spike_input_port.get_variable().get_attribute())
expr = ASTUtils.get_initial_value_from_ode_toolbox_result(kernel_spike_buf_name, solver_dicts)
assert expr is not None, "Initial value not found for kernel " + kernel_var
expr = str(expr)
if expr in ["0", "0.", "0.0"]:
continue # skip adding the statement if we are only adding zero
-
assignment_str = kernel_spike_buf_name + " += "
if "_is_post_port" in dir(spike_input_port.get_variable()) \
and spike_input_port.get_variable()._is_post_port:
assignment_str += "1."
else:
- assignment_str += "(" + str(spike_input_port) + ")"
+ var_name = str(spike_input_port)
+ assignment_str += "(" + var_name + ")"
+
if not expr in ["1.", "1.0", "1"]:
assignment_str += " * (" + expr + ")"
@@ -1023,14 +1125,18 @@ def get_spike_update_expressions(self, neuron: ASTModel, kernel_buffers, solver_
ast_assignment.update_scope(neuron.get_scope())
ast_assignment.accept(ASTSymbolTableVisitor())
- if neuron.get_scope().resolve_to_symbol(spike_input_port_name, SymbolKind.VARIABLE) is None:
+ spike_input_port_name_with_attr = spike_input_port_name
+ if spike_input_port.get_variable().get_attribute():
+ spike_input_port_name_with_attr += "." + str(spike_input_port.get_variable().get_attribute())
+
+ if neuron.get_scope().resolve_to_symbol(spike_input_port_name_with_attr, SymbolKind.VARIABLE) is None:
# this case covers variables that were moved from synapse to the neuron
post_spike_updates[kernel_var.get_name()] = ast_assignment
elif "_is_post_port" in dir(spike_input_port.get_variable()) and spike_input_port.get_variable()._is_post_port:
Logger.log_message(None, None, "Adding post assignment string: " + str(ast_assignment), None, LoggingLevel.INFO)
- spike_updates[str(spike_input_port)].append(ast_assignment)
+ spike_updates[spike_input_port_name].append(ast_assignment)
else:
- spike_updates[str(spike_input_port)].append(ast_assignment)
+ spike_updates[spike_input_port_name].append(ast_assignment)
for k, factor in delta_factors.items():
var = k[0]
diff --git a/pynestml/codegeneration/nest_compartmental_code_generator.py b/pynestml/codegeneration/nest_compartmental_code_generator.py
index 82be5734f..9cc3ad1bc 100644
--- a/pynestml/codegeneration/nest_compartmental_code_generator.py
+++ b/pynestml/codegeneration/nest_compartmental_code_generator.py
@@ -74,6 +74,7 @@
from pynestml.utils.model_parser import ModelParser
from pynestml.utils.syns_info_enricher import SynsInfoEnricher
from pynestml.utils.synapse_processing import SynapseProcessing
+from pynestml.visitors.ast_parent_visitor import ASTParentVisitor
from pynestml.visitors.ast_random_number_generator_visitor import ASTRandomNumberGeneratorVisitor
from pynestml.visitors.ast_symbol_table_visitor import ASTSymbolTableVisitor
@@ -417,6 +418,7 @@ def analyse_neuron(self, neuron: ASTModel) -> List[ASTAssignment]:
assert len(neuron.get_equations_blocks()) == 1, "Only one equations block supported for now"
assert len(neuron.get_state_blocks()) == 1, "Only one state block supported for now"
+ neuron.accept(ASTParentVisitor())
equations_block = neuron.get_equations_blocks()[0]
@@ -433,7 +435,8 @@ def analyse_neuron(self, neuron: ASTModel) -> List[ASTAssignment]:
# if they have delta kernels, use sympy to expand the expression, then
# find the convolve calls and replace them with constant value 1
# then return every subexpression that had that convolve() replaced
- delta_factors = ASTUtils.get_delta_factors_(neuron, equations_block)
+ delta_factors = ASTUtils.get_delta_factors_from_input_port_references(neuron)
+ delta_factors |= ASTUtils.get_delta_factors_from_convolutions(neuron)
# goes through all convolve() inside equations block
# extracts what kernel is paired with what spike buffer
@@ -526,6 +529,8 @@ def analyse_neuron(self, neuron: ASTModel) -> List[ASTAssignment]:
spike_updates = self.get_spike_update_expressions(
neuron, kernel_buffers, [analytic_solver, numeric_solver], delta_factors)
+ neuron.accept(ASTParentVisitor())
+
return spike_updates
def compute_name_of_generated_file(self, jinja_file_name, neuron):
diff --git a/pynestml/codegeneration/nest_declarations_helper.py b/pynestml/codegeneration/nest_declarations_helper.py
index 981c05b95..827ad6dcc 100644
--- a/pynestml/codegeneration/nest_declarations_helper.py
+++ b/pynestml/codegeneration/nest_declarations_helper.py
@@ -56,7 +56,6 @@ def print_variable_type(self, variable_symbol) -> str:
:param variable_symbol: a single variable symbol
:type variable_symbol: variable_symbol
:return: a string presentation of the variable symbol's type
- :rtype: str
"""
if variable_symbol.has_vector_parameter():
return 'std::vector< ' + self.type_symbol_printer.print(variable_symbol.get_type_symbol()) + \
diff --git a/pynestml/codegeneration/printers/cpp_function_call_printer.py b/pynestml/codegeneration/printers/cpp_function_call_printer.py
index 11beba1bd..3c31d5660 100644
--- a/pynestml/codegeneration/printers/cpp_function_call_printer.py
+++ b/pynestml/codegeneration/printers/cpp_function_call_printer.py
@@ -23,15 +23,14 @@
import re
-from pynestml.symbols.symbol import SymbolKind
-
from pynestml.codegeneration.printers.function_call_printer import FunctionCallPrinter
from pynestml.meta_model.ast_function_call import ASTFunctionCall
+from pynestml.meta_model.ast_node import ASTNode
+from pynestml.meta_model.ast_variable import ASTVariable
from pynestml.symbol_table.scope import Scope
from pynestml.symbols.predefined_functions import PredefinedFunctions
+from pynestml.symbols.symbol import SymbolKind
from pynestml.utils.ast_utils import ASTUtils
-from pynestml.meta_model.ast_node import ASTNode
-from pynestml.meta_model.ast_variable import ASTVariable
class CppFunctionCallPrinter(FunctionCallPrinter):
diff --git a/pynestml/codegeneration/printers/cpp_variable_printer.py b/pynestml/codegeneration/printers/cpp_variable_printer.py
index 3756aff55..940776ce3 100644
--- a/pynestml/codegeneration/printers/cpp_variable_printer.py
+++ b/pynestml/codegeneration/printers/cpp_variable_printer.py
@@ -36,9 +36,9 @@ def _print_cpp_name(cls, variable_name: str) -> str:
"""
differential_order = variable_name.count("'")
if differential_order > 0:
- return variable_name.replace("'", "").replace("$", "__DOLLAR") + "__" + "d" * differential_order
+ return variable_name.replace(".", "__DOT__").replace("'", "").replace("$", "__DOLLAR") + "__" + "d" * differential_order
- return variable_name.replace("$", "__DOLLAR")
+ return variable_name.replace(".", "__DOT__").replace("$", "__DOLLAR")
def print_variable(self, node: ASTVariable) -> str:
"""
diff --git a/pynestml/codegeneration/printers/gsl_variable_printer.py b/pynestml/codegeneration/printers/gsl_variable_printer.py
index c9cfbc46f..9340f4a16 100644
--- a/pynestml/codegeneration/printers/gsl_variable_printer.py
+++ b/pynestml/codegeneration/printers/gsl_variable_printer.py
@@ -101,7 +101,13 @@ def _print_buffer_value(self, variable: ASTVariable) -> str:
var_name += "_0 + " + variable.get_vector_parameter().get_variable().get_name()
else:
var_name += "_" + str(variable.get_vector_parameter())
- return "spike_inputs_grid_sum_[node." + var_name + " - node.MIN_SPIKE_RECEPTOR]"
+ # add variable attribute if it exists
+ if variable.attribute:
+ return "spike_input_" + str(variable.name) + "__DOT__" + variable.attribute + "_grid_sum_"
+
+ return "spike_input_" + str(variable) + "_grid_sum_"
+
+ # case of continuous-type input port
assert variable_symbol.is_continuous_input_port()
- return "continuous_inputs_grid_sum_[" + variable.get_name().upper() + "]"
+ return variable_symbol.get_symbol_name() + '_grid_sum_'
diff --git a/pynestml/codegeneration/printers/model_printer.py b/pynestml/codegeneration/printers/model_printer.py
index b8b257dff..d48c6464c 100644
--- a/pynestml/codegeneration/printers/model_printer.py
+++ b/pynestml/codegeneration/printers/model_printer.py
@@ -36,7 +36,6 @@
from pynestml.meta_model.ast_if_clause import ASTIfClause
from pynestml.meta_model.ast_if_stmt import ASTIfStmt
from pynestml.meta_model.ast_input_block import ASTInputBlock
-from pynestml.meta_model.ast_input_qualifier import ASTInputQualifier
from pynestml.meta_model.ast_kernel import ASTKernel
from pynestml.meta_model.ast_logical_operator import ASTLogicalOperator
from pynestml.meta_model.ast_model import ASTModel
@@ -123,9 +122,6 @@ def print_input_block(self, node: ASTInputBlock) -> str:
def print_input_port(self, node: ASTInputPort) -> str:
raise Exception("Printer does not support printing this node type")
- def print_input_qualifier(self, node: ASTInputQualifier) -> str:
- raise Exception("Printer does not support printing this node type")
-
def print_logical_operator(self, node: ASTLogicalOperator) -> str:
raise Exception("Printer does not support printing this node type")
@@ -250,9 +246,6 @@ def print(self, node: ASTNode) -> str:
if isinstance(node, ASTInputPort):
return self.print_input_port(node)
- if isinstance(node, ASTInputQualifier):
- return self.print_input_qualifier(node)
-
if isinstance(node, ASTKernel):
return self.print_kernel(node)
diff --git a/pynestml/codegeneration/printers/nest_variable_printer.py b/pynestml/codegeneration/printers/nest_variable_printer.py
index 5f7e9fbfa..5d7a9d92f 100644
--- a/pynestml/codegeneration/printers/nest_variable_printer.py
+++ b/pynestml/codegeneration/printers/nest_variable_printer.py
@@ -53,11 +53,22 @@ def __init__(self, expression_printer: ExpressionPrinter, with_origin: bool = Tr
# self.postsynaptic_getter_string_ = "start->get_%s()" # XXX: TODO: see https://github.com/nest/nestml/issues/1163
# self.postsynaptic_getter_string_ = "((post_neuron_t*)(__target))->get_%s(t_hist_entry_ms)"
self.postsynaptic_getter_string_ = "((post_neuron_t*)(__target))->get_%s(get_t())"
+ self.buffers_are_zero = True
+
+ def set_buffers_to_zero(self, buffers_are_zero: bool):
+ self.buffers_are_zero = buffers_are_zero
+ return ""
def set_getter_string(self, s):
+ r"""Returns the empty string, because this method can be called from inside the Jinja template"""
self.postsynaptic_getter_string_ = s
return ""
+ def set_cpp_variable_suffix(self, s):
+ r"""Returns the empty string, because this method can be called from inside the Jinja template"""
+ self.cpp_variable_suffix = s
+ return ""
+
def print_variable(self, variable: ASTVariable) -> str:
"""
Converts a single variable to nest processable format.
@@ -94,7 +105,10 @@ def print_variable(self, variable: ASTVariable) -> str:
if variable.get_name() == PredefinedVariables.TIME_CONSTANT:
return "get_t()"
- symbol = variable.get_scope().resolve_to_symbol(variable.get_complete_name(), SymbolKind.VARIABLE)
+ symbol = variable.get_scope().resolve_to_symbol(variable.get_complete_name().replace("__DOT__", "."), SymbolKind.VARIABLE)
+
+ if symbol is None:
+ symbol = variable.get_scope().resolve_to_symbol(variable.get_complete_name(), SymbolKind.VARIABLE)
if symbol is None:
# test if variable name can be resolved to a type
@@ -119,6 +133,10 @@ def print_variable(self, variable: ASTVariable) -> str:
if not units_conversion_factor == 1:
s += "(" + str(units_conversion_factor) + " * "
if self.cpp_variable_suffix == "":
+ if self.buffers_are_zero and symbol.is_spike_input_port():
+ # XXX do this in a derived class
+ return "0.0"
+
s += "B_."
s += self._print_buffer_value(variable)
if not units_conversion_factor == 1:
@@ -152,14 +170,13 @@ def _print_delay_variable(self, variable: ASTVariable) -> str:
return ""
- def _print_buffer_value(self, variable: ASTVariable) -> str:
- """
- Converts for a handed over symbol the corresponding name of the buffer to a nest processable format.
- :param variable: a single variable symbol.
- :return: the corresponding representation as a string
- """
- variable_symbol = variable.get_scope().resolve_to_symbol(variable.get_complete_name(), SymbolKind.VARIABLE)
+ def __print_buffer_value(self, variable: ASTVariable) -> str:
+
+ variable_symbol = variable.get_scope().resolve_to_symbol(variable.get_complete_name().replace("__DOT__", "."), SymbolKind.VARIABLE)
if variable_symbol.is_spike_input_port():
+ if self.buffers_are_zero:
+ return "0.0" # XXX this should be spun off to a NESTVariablePrinterWithFactorsAsZeros
+
var_name = variable_symbol.get_symbol_name().upper()
if variable.has_vector_parameter():
if variable.get_vector_parameter().is_variable():
@@ -167,14 +184,37 @@ def _print_buffer_value(self, variable: ASTVariable) -> str:
var_name += "_0 + " + variable.get_vector_parameter().get_variable().get_name()
else:
var_name += "_" + str(variable.get_vector_parameter())
- return "spike_inputs_grid_sum_[" + var_name + " - MIN_SPIKE_RECEPTOR]"
+
+ # add variable attribute if it exists
+ if variable.attribute:
+ return "__spike_input_" + str(variable.name) + "_VEC_IDX_" + str(variable.get_vector_parameter()) + "__DOT__" + variable.attribute
+
+ else:
+ # add variable attribute if it exists
+ if variable.attribute:
+ return "__spike_input_" + str(variable.name) + "__DOT__" + variable.attribute
+
+ # no vector indices, no attributes
+ return "__spike_input_" + str(variable)
if self.cpp_variable_suffix:
return variable_symbol.get_symbol_name() + self.cpp_variable_suffix
+ # case of continuous-time input port
assert variable_symbol.is_continuous_input_port()
return "continuous_inputs_grid_sum_[" + variable.get_name().upper() + "]"
+ def _print_buffer_value(self, variable: ASTVariable) -> str:
+ """
+ Converts for a handed over symbol the corresponding name of the buffer to a nest processable format.
+ :param variable: a single variable symbol.
+ :return: the corresponding representation as a string
+ """
+ if variable.get_implicit_conversion_factor() and not variable.get_implicit_conversion_factor() == 1:
+ return "(" + str(variable.get_implicit_conversion_factor()) + " * (" + self.__print_buffer_value(variable) + "))"
+
+ return self.__print_buffer_value(variable)
+
def _print(self, variable: ASTVariable, symbol, with_origin: bool = True) -> str:
variable_name = CppVariablePrinter._print_cpp_name(variable.get_complete_name())
@@ -184,7 +224,11 @@ def _print(self, variable: ASTVariable, symbol, with_origin: bool = True) -> str
if variable.is_delay_variable():
return self._print_delay_variable(variable)
- if with_origin:
+ with_origin_ = with_origin
+ if symbol.is_spike_input_port():
+ with_origin_ = False
+
+ if with_origin_ and NESTCodeGeneratorUtils.print_symbol_origin(symbol, variable):
return NESTCodeGeneratorUtils.print_symbol_origin(symbol, variable) % variable_name
return variable_name
diff --git a/pynestml/codegeneration/printers/nestml_printer.py b/pynestml/codegeneration/printers/nestml_printer.py
index fb0081e7c..6f6768ac9 100644
--- a/pynestml/codegeneration/printers/nestml_printer.py
+++ b/pynestml/codegeneration/printers/nestml_printer.py
@@ -50,7 +50,6 @@
from pynestml.meta_model.ast_inline_expression import ASTInlineExpression
from pynestml.meta_model.ast_input_block import ASTInputBlock
from pynestml.meta_model.ast_input_port import ASTInputPort
-from pynestml.meta_model.ast_input_qualifier import ASTInputQualifier
from pynestml.meta_model.ast_kernel import ASTKernel
from pynestml.meta_model.ast_logical_operator import ASTLogicalOperator
from pynestml.meta_model.ast_model import ASTModel
@@ -348,23 +347,18 @@ def print_input_port(self, node: ASTInputPort) -> str:
if node.has_size_parameter():
ret += "[" + self.print(node.get_size_parameter()) + "]"
ret += " <- "
- if node.has_input_qualifiers():
- for qual in node.get_input_qualifiers():
- ret += self.print(qual) + " "
if node.is_spike():
ret += "spike"
+ if node.get_parameters():
+ ret += "("
+ for parameter in node.get_parameters():
+ ret += self.print_parameter(parameter)
+ ret += ")"
else:
ret += "continuous"
ret += print_sl_comment(node.in_comment) + "\n"
return ret
- def print_input_qualifier(self, node: ASTInputQualifier) -> str:
- if node.is_inhibitory:
- return "inhibitory"
- if node.is_excitatory:
- return "excitatory"
- return ""
-
def print_compilation_unit(self, node: ASTNestMLCompilationUnit) -> str:
ret = ""
if node.get_model_list() is not None:
@@ -477,7 +471,7 @@ def print_unit_type(self, node: ASTUnitType) -> str:
def print_on_receive_block(self, node: ASTOnReceiveBlock) -> str:
ret = print_ml_comments(node.pre_comments, self.indent, False)
- ret += print_n_spaces(self.indent) + "onReceive(" + node.port_name + "):" + print_sl_comment(node.in_comment) + "\n"
+ ret += print_n_spaces(self.indent) + "onReceive(" + self.print(node.get_input_port_variable()) + "):" + print_sl_comment(node.in_comment) + "\n"
self.inc_indent()
ret += self.print(node.get_stmts_body())
self.dec_indent()
diff --git a/pynestml/codegeneration/printers/nestml_simple_expression_printer_units_as_factors.py b/pynestml/codegeneration/printers/nestml_simple_expression_printer_units_as_factors.py
new file mode 100644
index 000000000..ce9710585
--- /dev/null
+++ b/pynestml/codegeneration/printers/nestml_simple_expression_printer_units_as_factors.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+#
+# nestml_simple_expression_printer_units_as_factors.py
+#
+# This file is part of NEST.
+#
+# Copyright (C) 2004 The NEST Initiative
+#
+# NEST is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# NEST is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with NEST. If not, see .
+
+from pynestml.codegeneration.printers.nestml_simple_expression_printer import NESTMLSimpleExpressionPrinter
+from pynestml.meta_model.ast_simple_expression import ASTSimpleExpression
+
+
+class NESTMLSimpleExpressionPrinterUnitsAsFactors(NESTMLSimpleExpressionPrinter):
+ r"""
+ Same as the NESTMLPrinter, except print unit literals with a multiplication operator between (for example "42 * ms" instead of "42 ms").
+ """
+
+ def print_simple_expression(self, node: ASTSimpleExpression) -> str:
+ if node.is_numeric_literal():
+ if node.variable is not None:
+ # numeric literal + physical unit
+ return str(node.numeric_literal) + " * " + self.print(node.variable)
+
+ return str(node.numeric_literal)
+
+ return super().print_simple_expression(node)
diff --git a/pynestml/codegeneration/printers/nestml_variable_printer.py b/pynestml/codegeneration/printers/nestml_variable_printer.py
index 834df2918..3ee6d5210 100644
--- a/pynestml/codegeneration/printers/nestml_variable_printer.py
+++ b/pynestml/codegeneration/printers/nestml_variable_printer.py
@@ -36,6 +36,9 @@ def print_variable(self, node: ASTVariable):
if node.get_vector_parameter():
ret += "[" + self._expression_printer.print(node.get_vector_parameter()) + "]"
+ if node.get_attribute():
+ ret += "." + node.get_attribute()
+
for i in range(1, node.differential_order + 1):
ret += "'"
diff --git a/pynestml/codegeneration/printers/ode_toolbox_variable_printer.py b/pynestml/codegeneration/printers/ode_toolbox_variable_printer.py
index 4b6a9b952..3d51f2731 100644
--- a/pynestml/codegeneration/printers/ode_toolbox_variable_printer.py
+++ b/pynestml/codegeneration/printers/ode_toolbox_variable_printer.py
@@ -20,7 +20,9 @@
# along with NEST. If not, see .
from pynestml.codegeneration.printers.variable_printer import VariablePrinter
+from pynestml.meta_model.ast_model import ASTModel
from pynestml.meta_model.ast_variable import ASTVariable
+from pynestml.utils.ast_utils import ASTUtils
class ODEToolboxVariablePrinter(VariablePrinter):
@@ -34,4 +36,14 @@ def print_variable(self, node: ASTVariable) -> str:
:param node: the node to print
:return: string representation
"""
- return node.get_name().replace("$", "__DOLLAR") + "__d" * node.get_differential_order()
+ s = node.get_name().replace("$", "__DOLLAR")
+
+ if node.get_vector_parameter():
+ s += "_VEC_IDX_" + self._expression_printer.print(node.get_vector_parameter())
+
+ if node.get_attribute():
+ s += "__DOT__" + node.get_attribute()
+
+ s += "__d" * node.get_differential_order()
+
+ return s
diff --git a/pynestml/codegeneration/printers/python_variable_printer.py b/pynestml/codegeneration/printers/python_variable_printer.py
index d03bdadd0..acdc8d44a 100644
--- a/pynestml/codegeneration/printers/python_variable_printer.py
+++ b/pynestml/codegeneration/printers/python_variable_printer.py
@@ -56,9 +56,9 @@ def _print_python_name(cls, variable_name: str) -> str:
"""
differential_order = variable_name.count("\"")
if differential_order > 0:
- return variable_name.replace("\"", "").replace("$", "__DOLLAR") + "__" + "d" * differential_order
+ return variable_name.replace(".", "__DOT__").replace("\"", "").replace("$", "__DOLLAR") + "__" + "d" * differential_order
- return variable_name.replace("$", "__DOLLAR")
+ return variable_name.replace(".", "__DOT__").replace("$", "__DOLLAR")
def print_variable(self, variable: ASTVariable) -> str:
"""
@@ -112,10 +112,18 @@ def print_variable(self, variable: ASTVariable) -> str:
s = ""
if not units_conversion_factor == 1:
s += "(" + str(units_conversion_factor) + " * "
- s += self._print(variable, symbol, with_origin=self.with_origin) + vector_param
+
+ if symbol.is_spike_input_port():
+ # spike buffer variables are prefixed with "B__" as the values are grabbed from the buffers one-by-one
+ s += "B__" + self._print(variable, symbol, with_origin=False) + vector_param
+ else:
+ assert symbol.is_continuous_input_port()
+ s += self._print(variable, symbol, with_origin=self.with_origin) + vector_param
+
s += vector_param
if not units_conversion_factor == 1:
s += ")"
+
return s
if symbol.is_inline_expression:
diff --git a/pynestml/codegeneration/resources_nest/point_neuron/common/NeuronClass.jinja2 b/pynestml/codegeneration/resources_nest/point_neuron/common/NeuronClass.jinja2
index 9aee4c9cc..7209b002e 100644
--- a/pynestml/codegeneration/resources_nest/point_neuron/common/NeuronClass.jinja2
+++ b/pynestml/codegeneration/resources_nest/point_neuron/common/NeuronClass.jinja2
@@ -72,7 +72,7 @@ along with NEST. If not, see .
// uncomment the next line to enable printing of detailed debug information
// #define DEBUG
-{%- if state_vars_that_need_continuous_buffering | length > 0 %}
+{% if state_vars_that_need_continuous_buffering | length > 0 %}
{%- if continuous_state_buffering_method == "continuous_time_buffer" %}
continuous_variable_histentry_{{ neuronName }}::continuous_variable_histentry_{{ neuronName }}( double t,
{%- for state_var in state_vars_that_need_continuous_buffering %}
@@ -106,12 +106,11 @@ nest::RecordablesMap<{{ neuronName }}> {{ neuronName }}::recordablesMap_;
namespace nest
{
- // Override the create() method with one call to RecordablesMap::insert_()
- // for each quantity to be recorded.
+ // Override the create() method with one call to RecordablesMap::insert_() for each quantity to be recorded.
{%- if has_state_vectors %}
-template <> void DynamicRecordablesMap<{{ neuronName }}>::create({{ neuronName }}& host)
+ template <> void DynamicRecordablesMap<{{ neuronName }}>::create({{ neuronName }}& host)
{%- else %}
-template <> void RecordablesMap<{{ neuronName }}>::create()
+ template <> void RecordablesMap<{{ neuronName }}>::create()
{%- endif %}
{
@@ -125,7 +124,7 @@ template <> void RecordablesMap<{{ neuronName }}>::create()
{%- else %}
// add state variables to recordables map
{%- for variable in recordable_state_variables %}
- insert_({{names_namespace}}::_{{ variable.get_complete_name() }}, &{{ neuronName }}::get_{{ printer_no_origin.print(variable) }});
+ insert_({{names_namespace}}::_{{ variable.get_complete_name() }}, &{{ neuronName }}::get_{{ printer_no_origin.print(variable) }});
{%- endfor %}
{%- endif %}
{%- endif %}
@@ -134,7 +133,7 @@ template <> void RecordablesMap<{{ neuronName }}>::create()
// add recordable inline expressions to recordables map
{%- for variable_symbol in recordable_inline_expressions %}
{%- set variable = utils.get_variable_by_name(astnode, variable_symbol.get_symbol_name()) %}
- insert_({{ names_namespace }}::_{{ variable_symbol.get_symbol_name() }}, &{{ neuronName }}::{{ printer_no_origin.print(variable)[:-2] }});
+ insert_({{ names_namespace }}::_{{ variable_symbol.get_symbol_name() }}, &{{ neuronName }}::{{ printer_no_origin.print(variable)[:-2] }});
{%- endfor %}
{%- endif %}
@@ -147,24 +146,6 @@ template <> void RecordablesMap<{{ neuronName }}>::create()
}
}
-{%- if neuron.get_spike_input_ports()|length > 1 or neuron.is_multisynapse_spikes() %}
-std::vector< std::tuple< int, int > > {{ neuronName }}::rport_to_nestml_buffer_idx =
-{
-{%- for key, ports in utils.get_spike_input_ports_in_pairs(neuron).items() %}
-{%- set ns = namespace(rport=key) %}
-{%- if ports[0].has_vector_parameter() %}
-{%- set size = utils.get_numeric_vector_size(ports[0]) %}
-{%- for i in range(size) %}
- {{ rport_to_port_map_entry.RportToBufferIndexEntry(ports, ns.rport, index=i) }}
-{%- set ns.rport = ns.rport + 1 %}
-{%- endfor %}
-{%- else %}
- {{ rport_to_port_map_entry.RportToBufferIndexEntry(ports, ns.rport) }}
-{%- endif %}
-{%- endfor %}
-};
-{%- endif %}
-
{%- if has_state_vectors %}
std::string {{ neuronName }}::get_var_name(size_t elem, std::string var_name)
{
@@ -212,12 +193,58 @@ std::vector< std::tuple< int, int > > {{ neuronName }}::rport_to_nestml_buffer_i
{{ neuronName }}::Buffers_::Buffers_({{ neuronName }} &n):
logger_(n)
-{%- if neuron.get_spike_input_ports()|length > 0 %}
- , spike_inputs_( std::vector< nest::RingBuffer >( NUM_SPIKE_RECEPTORS ) )
- , spike_inputs_grid_sum_( std::vector< double >( NUM_SPIKE_RECEPTORS ) )
- , spike_input_received_( std::vector< nest::RingBuffer >( NUM_SPIKE_RECEPTORS ) )
- , spike_input_received_grid_sum_( std::vector< double >( NUM_SPIKE_RECEPTORS ) )
-{%- endif %}
+{%- for inputPortSymbol in neuron.get_spike_input_ports() %}
+{%- set inputPort = utils.get_input_port_by_name(astnode.get_input_blocks(), inputPortSymbol.name.split(".")[0]) %}
+{%- if inputPortSymbol.has_vector_parameter() %}
+{%- set size = utils.get_numeric_vector_size(inputPortSymbol) %}
+{%- for i in range(size) %}
+{%- if inputPort.get_parameters() %}
+{%- if inputPort.get_parameters() | length > 1 %}
+ {{ raise('Neuron models in NEST Simulator only support one spike event attribute ("weight")') }}
+{%- endif %}
+{%- for parameter in inputPort.get_parameters() %}
+{%- if inputPortSymbol.name in linear_time_invariant_spiking_input_ports %}
+{#- linear, time-invariant input port: all spike events for a specific buffer slot can be added together into a single number #}
+ , spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_( nest::RingBuffer() )
+ , spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_grid_sum_( 0. )
+{%- else %}
+{%- if attributes_spiking_input_port_necessary %}
+{#- generic input port: use lists of spike events for each buffer slot #}
+ , spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_( nest::VectorRingBuffer() )
+{%- endif %}
+{%- endif %}
+{%- endfor %}
+{%- endif %}
+{%- endfor %}
+{%- else %}
+{%- if inputPort.get_parameters() | length > 1 %}
+ {{ raise('Neuron models in NEST Simulator only support one spike event attribute ("weight")') }}
+{%- endif %}
+{%- for parameter in inputPort.get_parameters() %}
+{%- if inputPortSymbol.name in linear_time_invariant_spiking_input_ports %}
+{#- linear, time-invariant input port: all spike events for a specific buffer slot can be added together into a single number #}
+ , spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_( nest::RingBuffer() )
+ , spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_grid_sum_( 0. )
+{%- else %}
+{%- if attributes_spiking_input_port_necessary %}
+{#- generic input port: use lists of spike events for each buffer slot #}
+ , spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_( nest::VectorRingBuffer() )
+{%- endif %}
+{%- endif %}
+{%- endfor %}
+{%- if inputPortSymbol.name in linear_time_invariant_spiking_input_ports %}
+{#- linear, time-invariant input port: all spike events for a specific buffer slot can be added together into a single number #}
+ , spike_input_{{ inputPort.name }}_( nest::RingBuffer() )
+ , spike_input_{{ inputPort.name }}_grid_sum_( 0. )
+{%- else %}
+{#- generic input port: use lists of spike events for each buffer slot #}
+{%- if attributes_spiking_input_port_necessary %}
+ , spike_input_{{ inputPort.name }}_( nest::VectorRingBuffer() )
+{%- endif %}
+{%- endif %}
+ , spike_input_{{ inputPort.name }}_n_spikes_received_( nest::RingBuffer() )
+{%- endif %}
+{%- endfor %}
{%- if has_continuous_input %}
, continuous_inputs_( std::vector< nest::RingBuffer >( NUM_CONTINUOUS_INPUT_PORTS ) )
, continuous_inputs_grid_sum_( std::vector< double >( NUM_CONTINUOUS_INPUT_PORTS ) )
@@ -233,11 +260,59 @@ std::vector< std::tuple< int, int > > {{ neuronName }}::rport_to_nestml_buffer_i
{{ neuronName }}::Buffers_::Buffers_(const Buffers_ &, {{ neuronName }} &n):
logger_(n)
-{%- if neuron.get_spike_input_ports()|length > 0 %}
- , spike_inputs_( std::vector< nest::RingBuffer >( NUM_SPIKE_RECEPTORS ) )
- , spike_inputs_grid_sum_( std::vector< double >( NUM_SPIKE_RECEPTORS ) )
- , spike_input_received_( std::vector< nest::RingBuffer >( NUM_SPIKE_RECEPTORS ) )
- , spike_input_received_grid_sum_( std::vector< double >( NUM_SPIKE_RECEPTORS ) )
+{%- if neuron.get_spike_input_ports() | length > 0 %}
+{%- for inputPortSymbol in neuron.get_spike_input_ports() %}
+{%- set inputPort = utils.get_input_port_by_name(astnode.get_input_blocks(), inputPortSymbol.name.split(".")[0]) %}
+{%- if inputPortSymbol.has_vector_parameter() %}
+{%- set size = utils.get_numeric_vector_size(inputPortSymbol) %}
+{%- for i in range(size) %}
+{%- if inputPort.get_parameters() %}
+{%- if inputPort.get_parameters() | length > 1 %}
+ {{ raise('Neuron models in NEST Simulator only support one spike event attribute ("weight")') }}
+{%- endif %}
+{%- for parameter in inputPort.get_parameters() %}
+{%- if inputPortSymbol.name in linear_time_invariant_spiking_input_ports %}
+{#- linear, time-invariant input port: all spike events for a specific buffer slot can be added together into a single number #}
+ , spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_( nest::RingBuffer() )
+ , spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_grid_sum_( 0. )
+{%- else %}
+{#- generic input port: use lists of spike events for each buffer slot #}
+{%- if attributes_spiking_input_port_necessary %}
+ , spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_( nest::VectorRingBuffer() )
+{%- endif %}
+{%- endif %}
+{%- endfor %}
+{%- endif %}
+{%- endfor %}
+{%- else %}
+{%- if inputPort.get_parameters() | length > 1 %}
+ {{ raise('Neuron models in NEST Simulator only support one spike event attribute ("weight")') }}
+{%- endif %}
+{%- for parameter in inputPort.get_parameters() %}
+{%- if inputPortSymbol.name in linear_time_invariant_spiking_input_ports %}
+{#- linear, time-invariant input port: all spike events for a specific buffer slot can be added together into a single number #}
+ , spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_( nest::RingBuffer() )
+ , spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_grid_sum_( 0. )
+{%- else %}
+{#- generic input port: use lists of spike events for each buffer slot #}
+{%- if attributes_spiking_input_port_necessary %}
+ , spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_( nest::VectorRingBuffer() )
+{%- endif %}
+{%- endif %}
+{%- endfor %}
+{%- if inputPortSymbol.name in linear_time_invariant_spiking_input_ports %}
+{#- linear, time-invariant input port: all spike events for a specific buffer slot can be added together into a single number #}
+ , spike_input_{{ inputPort.name }}_( nest::RingBuffer() )
+ , spike_input_{{ inputPort.name }}_grid_sum_( 0. )
+{%- else %}
+{#- generic input port: use lists of spike events for each buffer slot #}
+{%- if attributes_spiking_input_port_necessary %}
+ , spike_input_{{ inputPort.name }}_( nest::VectorRingBuffer() )
+{%- endif %}
+{%- endif %}
+ , spike_input_{{ inputPort.name }}_n_spikes_received_( nest::RingBuffer() )
+{%- endif %}
+{%- endfor %}
{%- endif %}
{%- if has_continuous_input %}
, continuous_inputs_( std::vector< nest::RingBuffer >( NUM_CONTINUOUS_INPUT_PORTS ) )
@@ -445,11 +520,40 @@ void {{ neuronName }}::init_buffers_()
{%- endif %}
{%- if neuron.get_spike_input_ports() | length > 0 %}
- // spike input buffers
- get_spike_inputs_().clear();
- get_spike_inputs_grid_sum_().clear();
- get_spike_input_received_().clear();
- get_spike_input_received_grid_sum_().clear();
+ // spike input buffers -- note that .clear() includes a resize
+
+{%- for inputPortSymbol in neuron.get_spike_input_ports() %}
+{%- set inputPort = utils.get_input_port_by_name(astnode.get_input_blocks(), inputPortSymbol.name.split(".")[0]) %}
+{%- if inputPortSymbol.has_vector_parameter() %}
+{%- set size = utils.get_numeric_vector_size(inputPortSymbol) %}
+{%- for i in range(size) %}
+{%- if inputPort.get_parameters() %}
+{%- if inputPort.get_parameters() | length > 1 %}
+ {{ raise('Neuron models in NEST Simulator only support one spike event attribute ("weight")') }}
+{%- endif %}
+{%- for parameter in inputPort.get_parameters() %}
+ B_.spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_.clear();
+{%- endfor %}
+{%- endif %}
+{%- if attributes_spiking_input_port_necessary %}
+ B_.spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}_.clear();
+{%- endif %}
+ B_.spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}_n_spikes_received_.clear();
+{%- endfor %}
+{%- else %}
+{%- if inputPort.get_parameters() | length > 1 %}
+ {{ raise('Neuron models in NEST Simulator only support one spike event attribute ("weight")') }}
+{%- endif %}
+{%- for parameter in inputPort.get_parameters() %}
+ B_.spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_.clear();
+{%- endfor %}
+{%- if attributes_spiking_input_port_necessary %}
+ B_.spike_input_{{ inputPort.name }}_.clear();
+{%- endif %}
+ B_.spike_input_{{ inputPort.name }}_n_spikes_received_.clear();
+{%- endif %}
+{%- endfor %}
+
{%- endif %}
{%- if has_continuous_input %}
get_continuous_inputs_().clear();
@@ -572,14 +676,6 @@ void {{ neuronName }}::pre_run_hook()
// parameters might have changed -- recompute internals
V_.__h = nest::Time::get_resolution().get_ms();
recompute_internal_variables();
-
- // buffers B_
-{%- if ((neuron.get_spike_input_ports())|length > 0) %}
- B_.spike_inputs_.resize(NUM_SPIKE_RECEPTORS);
- B_.spike_inputs_grid_sum_.resize(NUM_SPIKE_RECEPTORS);
- B_.spike_input_received_.resize(NUM_SPIKE_RECEPTORS);
- B_.spike_input_received_grid_sum_.resize(NUM_SPIKE_RECEPTORS);
-{%- endif %}
{%- if has_continuous_input %}
B_.continuous_inputs_.resize(NUM_CONTINUOUS_INPUT_PORTS);
B_.continuous_inputs_grid_sum_.resize(NUM_CONTINUOUS_INPUT_PORTS);
@@ -671,9 +767,9 @@ void {{ neuronName }}::update(nest::Time const & origin, const long from, const
for ( long lag = from ; lag < to ; ++lag )
{
{% if propagators_are_state_dependent %}
- // the propagators are state dependent; update them!
- V_.__h = nest::Time::get_resolution().get_ms();
- recompute_internal_variables();
+ // the propagators are state dependent; update them!
+ V_.__h = nest::Time::get_resolution().get_ms();
+ recompute_internal_variables();
{% endif %}
@@ -689,32 +785,32 @@ void {{ neuronName }}::update(nest::Time const & origin, const long from, const
{%- if not gap_junction_membrane_potential_variable_is_numeric %}
{# in case V_m is solved analytically, need to compute __I_gap here so dV_m/dt can be computed below #}
- // set I_gap depending on interpolation order
- double __I_gap = 0.0;
+ // set I_gap depending on interpolation order
+ double __I_gap = 0.0;
- const double __t_gap = gap_junction_step / nest::Time::get_resolution().get_ms();
+ const double __t_gap = gap_junction_step / nest::Time::get_resolution().get_ms();
- switch ( nest::kernel().simulation_manager.get_wfr_interpolation_order() )
- {
- case 0:
- __I_gap = -B_.sumj_g_ij_ * {{ gap_junction_membrane_potential_variable_cpp }} + B_.interpolation_coefficients[ B_.lag_ ];
- break;
-
- case 1:
- __I_gap = -B_.sumj_g_ij_ * {{ gap_junction_membrane_potential_variable_cpp }} + B_.interpolation_coefficients[ B_.lag_ * 2 + 0 ]
- + B_.interpolation_coefficients[ B_.lag_ * 2 + 1 ] * __t_gap;
- break;
-
- case 3:
- __I_gap = -B_.sumj_g_ij_ * {{ gap_junction_membrane_potential_variable_cpp }} + B_.interpolation_coefficients[ B_.lag_ * 4 + 0 ]
- + B_.interpolation_coefficients[ B_.lag_ * 4 + 1 ] * __t_gap
- + B_.interpolation_coefficients[ B_.lag_ * 4 + 2 ] * __t_gap * __t_gap
- + B_.interpolation_coefficients[ B_.lag_ * 4 + 3 ] * __t_gap * __t_gap * __t_gap;
- break;
-
- default:
- throw nest::BadProperty( "Interpolation order must be 0, 1, or 3." );
- }
+ switch ( nest::kernel().simulation_manager.get_wfr_interpolation_order() )
+ {
+ case 0:
+ __I_gap = -B_.sumj_g_ij_ * {{ gap_junction_membrane_potential_variable_cpp }} + B_.interpolation_coefficients[ B_.lag_ ];
+ break;
+
+ case 1:
+ __I_gap = -B_.sumj_g_ij_ * {{ gap_junction_membrane_potential_variable_cpp }} + B_.interpolation_coefficients[ B_.lag_ * 2 + 0 ]
+ + B_.interpolation_coefficients[ B_.lag_ * 2 + 1 ] * __t_gap;
+ break;
+
+ case 3:
+ __I_gap = -B_.sumj_g_ij_ * {{ gap_junction_membrane_potential_variable_cpp }} + B_.interpolation_coefficients[ B_.lag_ * 4 + 0 ]
+ + B_.interpolation_coefficients[ B_.lag_ * 4 + 1 ] * __t_gap
+ + B_.interpolation_coefficients[ B_.lag_ * 4 + 2 ] * __t_gap * __t_gap
+ + B_.interpolation_coefficients[ B_.lag_ * 4 + 3 ] * __t_gap * __t_gap * __t_gap;
+ break;
+
+ default:
+ throw nest::BadProperty( "Interpolation order must be 0, 1, or 3." );
+ }
{%- endif %}
if ( called_from_wfr_update )
@@ -745,14 +841,56 @@ void {{ neuronName }}::update(nest::Time const & origin, const long from, const
{%- endif %}
/**
- * buffer spikes from spiking input ports
+ * buffer spikes from spiking input ports -- in case of convolutions with spiking input ports
**/
- for (long i = 0; i < NUM_SPIKE_RECEPTORS; ++i)
- {
- get_spike_inputs_grid_sum_()[i] = get_spike_inputs_()[i].get_value(lag);
- get_spike_input_received_grid_sum_()[i] = get_spike_input_received_()[i].get_value(lag);
- }
+{%- for inputPortSymbol in neuron.get_spike_input_ports() %}
+{%- set inputPort = utils.get_input_port_by_name(astnode.get_input_blocks(), inputPortSymbol.name.split(".")[0]) %}
+{%- if inputPortSymbol.has_vector_parameter() %}
+{%- set size = utils.get_numeric_vector_size(inputPortSymbol) %}
+{%- for i in range(size) %}
+{%- if inputPort.get_parameters() %}
+{%- if inputPort.get_parameters() | length > 1 %}
+ {{ raise('Neuron models in NEST Simulator only support one spike event attribute ("weight")') }}
+{%- endif %}
+{%- for parameter in inputPort.get_parameters() %}
+{%- if inputPortSymbol.name in linear_time_invariant_spiking_input_ports %}
+{#- linear, time-invariant input port: all spike events for a specific buffer slot can be added together into a single number #}
+ B_.spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_grid_sum_ = B_.spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_.get_value(lag);
+ const double __spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }} = B_.spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_grid_sum_;
+{%- else %}
+{#- generic input port: use lists of spike events for each buffer slot #}
+ const double __spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }} = std::accumulate(B_.spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_.get_list(lag).begin(), B_.spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_.get_list(lag).end(), 0.0);
+{%- endif %}
+{%- endfor %}
+{%- endif %}
+{%- endfor %}
+{%- else %}
+{%- if inputPort.get_parameters() | length > 1 %}
+ {{ raise('Neuron models in NEST Simulator only support one spike event attribute ("weight")') }}
+{%- endif %}
+{%- for parameter in inputPort.get_parameters() %}
+{%- if inputPortSymbol.name in linear_time_invariant_spiking_input_ports %}
+{#- linear, time-invariant input port: all spike events for a specific buffer slot can be added together into a single number #}
+ B_.spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_grid_sum_ = B_.spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_.get_value(lag);
+ const double __spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }} = B_.spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_grid_sum_;
+{%- else %}
+{#- generic input port: use lists of spike events for each buffer slot #}
+ const double __spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }} = std::accumulate(B_.spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_.get_list(lag).begin(), B_.spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_.get_list(lag).end(), 0.0);
+{%- endif %}
+{%- endfor %}
+{%- if attributes_spiking_input_port_necessary %}
+{%- if inputPortSymbol.name in linear_time_invariant_spiking_input_ports %}
+{#- linear, time-invariant input port: all spike events for a specific buffer slot can be added together into a single number #}
+ B_.spike_input_{{ inputPort.name }}_grid_sum_ = B_.spike_input_{{ inputPort.name }}_.get_value(lag);
+ const double __spike_input_{{ inputPort.name }} = B_.spike_input_{{ inputPort.name }}_grid_sum_;
+{%- else %}
+{#- generic input port: use lists of spike events for each buffer slot #}
+ const double __spike_input_{{ inputPort.name }} = std::accumulate(B_.spike_input_{{ inputPort.name }}_.get_list(lag).begin(), B_.spike_input_{{ inputPort.name }}_.get_list(lag).end(), 0.0);
+{%- endif %}
+{%- endif %}
+{%- endif %}
+{%- endfor %}
{%- if has_delay_variables %}
/**
@@ -787,7 +925,7 @@ void {{ neuronName }}::update(nest::Time const & origin, const long from, const
* Begin NESTML generated code for the update block(s)
**/
{% if neuron.get_update_blocks() %}
-{%- filter indent(2) %}
+{%- filter indent(4) %}
{%- for block in neuron.get_update_blocks() %}
{%- set ast = block.get_stmts_body() %}
{%- if ast.print_comment('*')|length > 1 %}
@@ -804,11 +942,14 @@ void {{ neuronName }}::update(nest::Time const & origin, const long from, const
* Begin NESTML generated code for the onReceive block(s)
**/
{% for blk in neuron.get_on_receive_blocks() %}
-{%- set inport = blk.get_port_name() %}
- if (B_.spike_input_received_grid_sum_[{{ inport.upper() }} - MIN_SPIKE_RECEPTOR])
+{%- set inport = utils.port_name_printer(blk.get_input_port_variable()) %}
{
- // B_.spike_input_received_[{{ inport.upper() }} - MIN_SPIKE_RECEPTOR] = false; // no need to reset the flag -- reading from the RingBuffer into the "grid_sum" variables resets the RingBuffer entries
- on_receive_block_{{ blk.get_port_name() }}(origin, lag);
+ const size_t n_spikes_received = B_.spike_input_{{ inport }}_n_spikes_received_.get_value(lag);
+ if (n_spikes_received)
+ {
+ // B_.spike_input_{{ inport }} = false; // no need to reset the flag -- reading from the RingBuffer resets the RingBuffer entries
+ on_receive_block_{{ utils.port_name_printer(blk.get_input_port_variable()) }}(origin, lag, n_spikes_received);
+ }
}
{%- endfor %}
@@ -849,9 +990,14 @@ void {{ neuronName }}::update(nest::Time const & origin, const long from, const
/**
* spike updates due to convolutions
**/
+
+{{ printer._expression_printer._simple_expression_printer._variable_printer.set_buffers_to_zero(False) }}
+{{ printer._expression_printer._simple_expression_printer._variable_printer.set_cpp_variable_suffix(" ") }} {# prevent printing origin #}
{% filter indent(4) %}
{%- include "directives_cpp/ApplySpikesFromBuffers.jinja2" %}
{%- endfilter %}
+{{ printer._expression_printer._simple_expression_printer._variable_printer.set_cpp_variable_suffix("") }}
+{{ printer._expression_printer._simple_expression_printer._variable_printer.set_buffers_to_zero(True) }}
/**
* Begin NESTML generated code for the onCondition block(s)
@@ -899,6 +1045,48 @@ void {{ neuronName }}::update(nest::Time const & origin, const long from, const
{%- endif %}
{%- endif %}
+ /**
+ * clear spike buffers at end of timestep (all spikes have been processed at this point)
+ **/
+
+{%- if neuron.get_spike_input_ports() | length > 0 %}
+{%- for inputPortSymbol in neuron.get_spike_input_ports() %}
+{%- set inputPort = utils.get_input_port_by_name(astnode.get_input_blocks(), inputPortSymbol.name.split(".")[0]) %}
+
+{%- if inputPortSymbol.name not in linear_time_invariant_spiking_input_ports %}
+
+{%- if inputPortSymbol.has_vector_parameter() %}
+{%- set size = utils.get_numeric_vector_size(inputPortSymbol) %}
+{%- for i in range(size) %}
+{%- if inputPort.get_parameters() %}
+{%- if inputPort.get_parameters() | length > 1 %}
+ {{ raise('Neuron models in NEST Simulator only support one spike event attribute ("weight")') }}
+{%- endif %}
+{%- for parameter in inputPort.get_parameters() %}
+ std::vector< double >& __spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_list = B_.spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_.get_list(lag);
+ __spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_list.clear();
+{%- endfor %}
+{%- endif %}
+{%- endfor %}
+{%- else %}
+{%- if inputPort.get_parameters() | length > 1 %}
+ {{ raise('Neuron models in NEST Simulator only support one spike event attribute ("weight")') }}
+{%- endif %}
+{%- for parameter in inputPort.get_parameters() %}
+ std::vector< double >& __spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_list = B_.spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_.get_list(lag);
+ __spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_list.clear();
+{%- endfor %}
+{%- if attributes_spiking_input_port_necessary %}
+ std::vector< double >& __spike_input_{{ inputPort.name }}_list = B_.spike_input_{{ inputPort.name }}_.get_list(lag);
+ __spike_input_{{ inputPort.name }}_list.clear();
+{%- endif %}
+{%- endif %}
+{%- endif %}
+
+{%- endfor %}
+{%- endif %}
+
+
{%- if use_gap_junctions %}
if ( called_from_wfr_update )
{
@@ -1099,35 +1287,57 @@ void {{ neuronName }}::handle(nest::DataLoggingRequest& e)
void {{ neuronName }}::handle(nest::SpikeEvent &e)
{
#ifdef DEBUG
- std::cout << "[neuron " << this << "] {{ neuronName }}::handle(SpikeEvent)" << std::endl;
+ std::cout << "[neuron " << this << "] {{ neuronName }}::handle(SpikeEvent) on rport " << e.get_rport() << std::endl;
#endif
assert(e.get_delay_steps() > 0);
- assert( e.get_rport() < B_.spike_inputs_.size() );
+ assert(e.get_rport() <= {{ utils.nestml_spiking_input_port_to_nest_rport_dict(astnode) | length }});
- double weight = e.get_weight();
- size_t nestml_buffer_idx = 0;
-{%- if neuron.get_spike_input_ports()|length > 1 or neuron.is_multisynapse_spikes() %}
- if ( weight >= 0.0 )
- {
- nestml_buffer_idx = std::get<0>(rport_to_nestml_buffer_idx[e.get_rport()]);
- }
- else
+{%- for spike_in_port_name, rport in utils.nestml_spiking_input_port_to_nest_rport_dict(astnode).items() %}
+{%- set spike_in_port = utils.get_input_port_by_name(astnode.get_input_blocks(), spike_in_port_name.split("_VEC_IDX_")[0]) %}
+{%- if astnode.get_body().get_spike_input_ports() | length > 1 or astnode.is_multisynapse_spikes() %}
+ if (e.get_rport() == {{ rport }})
+{%- endif %}
{
- nestml_buffer_idx = std::get<1>(rport_to_nestml_buffer_idx[e.get_rport()]);
- if ( nestml_buffer_idx == {{ neuronName }}::PORT_NOT_AVAILABLE )
- {
- nestml_buffer_idx = std::get<0>(rport_to_nestml_buffer_idx[e.get_rport()]);
- }
- weight = -weight;
+{%- if spike_in_port.get_parameters() %}
+{%- if spike_in_port.get_parameters() | length > 1 %}
+ {{ raise('Neuron models in NEST Simulator only support one spike event attribute ("weight")') }}
+{%- endif %}
+{%- for attribute in spike_in_port.get_parameters() %}
+{%- if spike_in_port_name in linear_time_invariant_spiking_input_ports %}
+{#- linear, time-invariant input port: all spike events for a specific buffer slot can be added together into a single number #}
+ B_.spike_input_{{ spike_in_port_name }}__DOT__{{ attribute.name }}_.add_value(
+ e.get_rel_delivery_steps( nest::kernel().simulation_manager.get_slice_origin() ),
+ e.get_weight() * e.get_multiplicity() );
+{%- else %}
+{#- generic input port: use lists of spike events for each buffer slot #}
+ B_.spike_input_{{ spike_in_port_name }}__DOT__{{ attribute.name }}_.append_value(
+ e.get_rel_delivery_steps( nest::kernel().simulation_manager.get_slice_origin() ),
+ e.get_weight() * e.get_multiplicity() );
+{%- endif %}
+{%- endfor %}
+{%- endif %}
+
+{%- if spike_in_port_name in linear_time_invariant_spiking_input_ports %}
+{#- linear, time-invariant input port: all spike events for a specific buffer slot can be added together into a single number #}
+ B_.spike_input_{{ spike_in_port_name }}_.add_value(
+ e.get_rel_delivery_steps( nest::kernel().simulation_manager.get_slice_origin() ),
+ e.get_multiplicity() );
+{%- else %}
+{%- if attributes_spiking_input_port_necessary %}
+{#- generic input port: use lists of spike events for each buffer slot #}
+ B_.spike_input_{{ spike_in_port_name }}_.append_value(
+ e.get_rel_delivery_steps( nest::kernel().simulation_manager.get_slice_origin() ),
+ e.get_multiplicity() );
+{%- endif %}
+{%- endif %}
+
+ // increment the number of spikes received
+ B_.spike_input_{{ spike_in_port_name }}_n_spikes_received_.add_value(
+ e.get_rel_delivery_steps( nest::kernel().simulation_manager.get_slice_origin() ),
+ 1. );
}
-{%- endif %}
- B_.spike_inputs_[ nestml_buffer_idx - MIN_SPIKE_RECEPTOR ].add_value(
- e.get_rel_delivery_steps( nest::kernel().simulation_manager.get_slice_origin() ),
- weight * e.get_multiplicity() );
- B_.spike_input_received_[ nestml_buffer_idx - MIN_SPIKE_RECEPTOR ].add_value(
- e.get_rel_delivery_steps( nest::kernel().simulation_manager.get_slice_origin() ),
- 1. );
+{%- endfor %}
}
{%- endif %}
@@ -1151,28 +1361,193 @@ void {{ neuronName }}::handle(nest::CurrentEvent& e)
{%- endif %}
// -------------------------------------------------------------------------
-// Methods corresponding to event handlers
+// Methods corresponding to onReceive blocks
// -------------------------------------------------------------------------
{%- for blk in neuron.get_on_receive_blocks() %}
+{%- set inport = utils.port_name_printer(blk.get_input_port_variable()) %}
{%- set ast = blk.get_stmts_body() %}
+{%- set inputPort = utils.get_input_port_by_name(astnode.get_input_blocks(), inport.split(".")[0]) %}
+{%- set inputPortSymbol = astnode.get_scope().resolve_to_symbol(inputPort.name, SymbolKind.VARIABLE) %}
void
-{{ neuronName }}::on_receive_block_{{ blk.get_port_name() }}(nest::Time const &origin, const long lag)
+{{ neuronName }}::on_receive_block_{{ utils.port_name_printer(blk.get_input_port_variable()) }}(nest::Time const &origin, const long lag, const size_t n_spikes_received)
{
#ifdef DEBUG
- std::cout << "[neuron " << this << "] {{ neuronName }}::on_receive_block_{{ blk.get_port_name() }}()" << std::endl;
+ std::cout << "[neuron " << this << "] {{ neuronName }}::on_receive_block_{{ utils.port_name_printer(blk.get_input_port_variable()) }}(lag=" << lag << ", n_spikes_received = " << n_spikes_received << ")" << std::endl;
#endif
const double __timestep = nest::Time::get_resolution().get_ms(); // do not remove, this is necessary for the timestep() function
auto get_t = [origin, lag](){ return nest::Time( nest::Time::step( origin.get_steps() + lag + 1) ).get_ms(); };
+{{ printer._expression_printer._simple_expression_printer._variable_printer.set_buffers_to_zero(False) }}
+{%- if inputPortSymbol.name in linear_time_invariant_spiking_input_ports %}
+{#- linear, time-invariant input port: all spike events for a specific buffer slot can be added together into a single number #}
+ /**
+ * Grab the actual spike event data from the buffers (for the current timepoint ``origin + lag``)
+ **/
+{%- if inputPortSymbol.has_vector_parameter() %}
+{%- set size = utils.get_numeric_vector_size(inputPortSymbol) %}
+{%- for i in range(size) %}
+{%- if inputPort.get_parameters() %}
+{%- if inputPort.get_parameters() | length > 1 %}
+ {{ raise('Neuron models in NEST Simulator only support one spike event attribute ("weight")') }}
+{%- endif %}
+{%- for parameter in inputPort.get_parameters() %}
+ const double __spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }} = B_.spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_grid_sum_;
+{%- endfor %}
+{%- endif %}
+{%- endfor %}
+{%- else %}
+{%- if inputPort.get_parameters() | length > 1 %}
+ {{ raise('Neuron models in NEST Simulator only support one spike event attribute ("weight")') }}
+{%- endif %}
+{%- for parameter in inputPort.get_parameters() %}
+ const double __spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }} = B_.spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_grid_sum_;
+{%- endfor %}
+{%- endif %}
+ const double __spike_input_{{ inputPort.name }} = B_.spike_input_{{ inputPort.name }}_grid_sum_;
+{{ printer._expression_printer._simple_expression_printer._variable_printer.set_buffers_to_zero(True) }}
+
/**
* Begin NESTML generated code for the onReceive() block statements
**/
-{%- filter indent(2, True) -%}
+{{ printer._expression_printer._simple_expression_printer._variable_printer.set_buffers_to_zero(False) }}
+{{ printer._expression_printer._simple_expression_printer._variable_printer.set_cpp_variable_suffix(" ") }} {# prevent printing origin #}
+
+{% filter indent(4, True) -%}
{%- include "directives_cpp/StmtsBody.jinja2" %}
{%- endfilter %}
+{{ printer._expression_printer._simple_expression_printer._variable_printer.set_buffers_to_zero(True) }}
+{{ printer._expression_printer._simple_expression_printer._variable_printer.set_cpp_variable_suffix("") }}
+{%- else %}
+{#- generic input port: use lists of spike events for each buffer slot #}
+ // grab the lists of spike events from the buffers for the current timepoint
+{%- if inputPortSymbol.has_vector_parameter() %}
+{%- set size = utils.get_numeric_vector_size(inputPortSymbol) %}
+{%- for i in range(size) %}
+{%- if inputPort.get_parameters() %}
+{%- if inputPort.get_parameters() | length > 1 %}
+ {{ raise('Neuron models in NEST Simulator only support one spike event attribute ("weight")') }}
+{%- endif %}
+{%- for parameter in inputPort.get_parameters() %}
+ std::vector< double >& __spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_list = B_.spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_.get_list(lag);
+ std::vector< double >::iterator __spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_list_iterator = __spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_list.begin();
+{%- endfor %}
+{%- endif %}
+{%- endfor %}
+{%- else %}
+{%- for parameter in inputPort.get_parameters() %}
+{%- if inputPort.get_parameters() | length > 1 %}
+ {{ raise('Neuron models in NEST Simulator only support one spike event attribute ("weight")') }}
+{%- endif %}
+ std::vector< double >& __spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_list = B_.spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_.get_list(lag);
+ std::vector< double >::iterator __spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_list_iterator = __spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_list.begin();
+{%- endfor %}
+{%- endif %}
+{%- if attributes_spiking_input_port_necessary %}
+ std::vector< double >& __spike_input_{{ inputPort.name }}_list = B_.spike_input_{{ inputPort.name }}_.get_list(lag);
+ std::vector< double >::iterator __spike_input_{{ inputPort.name }}_list_iterator = __spike_input_{{ inputPort.name }}_list.begin();
+{%- endif %}
+
+ for (size_t spike_idx = 0; spike_idx < n_spikes_received; ++spike_idx)
+ {
+ /**
+ * Grab the actual spike event data from the buffers (for the current timepoint ``origin + lag``)
+ **/
+{%- if inputPortSymbol.has_vector_parameter() %}
+{%- set size = utils.get_numeric_vector_size(inputPortSymbol) %}
+{%- for i in range(size) %}
+{%- if inputPort.get_parameters() %}
+{%- if inputPort.get_parameters() | length > 1 %}
+ {{ raise('Neuron models in NEST Simulator only support one spike event attribute ("weight")') }}
+{%- endif %}
+{%- for parameter in inputPort.get_parameters() %}
+ const double __spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }} = *__spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_list_iterator;
+{%- endfor %}
+{%- endif %}
+{%- endfor %}
+{%- else %}
+{%- if inputPort.get_parameters() | length > 1 %}
+ {{ raise('Neuron models in NEST Simulator only support one spike event attribute ("weight")') }}
+{%- endif %}
+{%- for parameter in inputPort.get_parameters() %}
+ const double __spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }} = *__spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_list_iterator;
+{%- endfor %}
+{%- endif %}
+{%- if attributes_spiking_input_port_necessary %}
+ const double __spike_input_{{ inputPort.name }} = *__spike_input_{{ inputPort.name }}_list_iterator;
+{%- endif %}
+
+ /**
+ * Begin NESTML generated code for the onReceive() block statements
+ **/
+
+{{ printer._expression_printer._simple_expression_printer._variable_printer.set_buffers_to_zero(False) }}
+{{ printer._expression_printer._simple_expression_printer._variable_printer.set_cpp_variable_suffix(" ") }} {# prevent printing origin #}
+{% filter indent(4, True) -%}
+{%- include "directives_cpp/StmtsBody.jinja2" %}
+{%- endfilter %}
+{{ printer._expression_printer._simple_expression_printer._variable_printer.set_cpp_variable_suffix("") }}
+{{ printer._expression_printer._simple_expression_printer._variable_printer.set_buffers_to_zero(True) }}
+
+ /**
+ * Advance the iterators
+ **/
+{%- if inputPortSymbol.has_vector_parameter() %}
+{%- set size = utils.get_numeric_vector_size(inputPortSymbol) %}
+{%- for i in range(size) %}
+{%- if inputPort.get_parameters() %}
+{%- if inputPort.get_parameters() | length > 1 %}
+ {{ raise('Neuron models in NEST Simulator only support one spike event attribute ("weight")') }}
+{%- endif %}
+{%- for parameter in inputPort.get_parameters() %}
+ ++__spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_list_iterator;
+{%- endfor %}
+{%- endif %}
+{%- endfor %}
+{%- else %}
+{%- if inputPort.get_parameters() | length > 1 %}
+ {{ raise('Neuron models in NEST Simulator only support one spike event attribute ("weight")') }}
+{%- endif %}
+{%- for parameter in inputPort.get_parameters() %}
+ ++__spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_list_iterator;
+{%- endfor %}
+{%- endif %}
+{%- if attributes_spiking_input_port_necessary %}
+ ++__spike_input_{{ inputPort.name }}_list_iterator;
+{%- endif %}
+ }
+/*
+ // clear the processed spike events from the list
+ // this can be skipped as reading the spikes into the buffer (using ``get_value(lag)`` automatically clears the list)
+
+{%- if inputPortSymbol.has_vector_parameter() %}
+{%- set size = utils.get_numeric_vector_size(inputPortSymbol) %}
+{%- for i in range(size) %}
+{%- if inputPort.get_parameters() %}
+{%- if inputPort.get_parameters() | length > 1 %}
+ {{ raise('Neuron models in NEST Simulator only support one spike event attribute ("weight")') }}
+{%- endif %}
+{%- for parameter in inputPort.get_parameters() %}
+ __spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_list.clear();
+{%- endfor %}
+{%- endif %}
+{%- endfor %}
+{%- else %}
+{%- if inputPort.get_parameters() | length > 1 %}
+ {{ raise('Neuron models in NEST Simulator only support one spike event attribute ("weight")') }}
+{%- endif %}
+{%- for parameter in inputPort.get_parameters() %}
+ __spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_list.clear();
+{%- endfor %}
+{%- endif %}
+
+{%- if attributes_spiking_input_port_necessary %}
+ __spike_input_{{ inputPort.name }}_list.clear();
+{%- endif %}
+*/
+{%- endif %}
}
{% endfor %}
diff --git a/pynestml/codegeneration/resources_nest/point_neuron/common/NeuronHeader.jinja2 b/pynestml/codegeneration/resources_nest/point_neuron/common/NeuronHeader.jinja2
index c19871483..3375fed86 100644
--- a/pynestml/codegeneration/resources_nest/point_neuron/common/NeuronHeader.jinja2
+++ b/pynestml/codegeneration/resources_nest/point_neuron/common/NeuronHeader.jinja2
@@ -54,6 +54,7 @@ along with NEST. If not, see .
// C++ includes:
#include
+#include
#include "config.h"
{%- if norm_rng %}
@@ -90,6 +91,7 @@ along with NEST. If not, see .
#include "event.h"
#include "nest_types.h"
#include "ring_buffer.h"
+#include "vector_ring_buffer.h"
#include "universal_data_logger.h"
// Includes from sli:
@@ -473,7 +475,7 @@ public:
{% filter indent(2, True) -%}
{%- for blk in neuron.get_on_receive_blocks() %}
- void on_receive_block_{{ blk.get_port_name() }}(nest::Time const &origin, const long lag);
+ void on_receive_block_{{ utils.port_name_printer(blk.get_input_port_variable()) }}(nest::Time const &origin, const long lag, const size_t n_spikes_received);
{%- endfor %}
{%- endfilter %}
@@ -533,60 +535,15 @@ private:
{%- endif %}
private:
-{% if has_multiple_synapses -%}
/**
* Synapse types to connect to
* @note Excluded lower and upper bounds are defined as MIN_, MAX_.
* Excluding port 0 avoids accidental connections.
**/
- static const nest_port_t MIN_SPIKE_RECEPTOR = 1;
-{%- set ns = namespace(count=1) %}
-{%- else %}
- static const nest_port_t MIN_SPIKE_RECEPTOR = 0;
-{%- set ns = namespace(count=0) %}
-{%- endif %}
- static const nest_port_t PORT_NOT_AVAILABLE = -1;
-
- enum SynapseTypes
- {
-{%- for port in neuron.get_spike_input_ports() %}
-{%- if port.has_vector_parameter() -%}
-{% set size = utils.get_numeric_vector_size(port) | int %}
-{%- for i in range(size) %}
- {{port.get_symbol_name().upper()}}_{{i}} = {{ns.count}},
-{%- set ns.count = ns.count + 1 -%}
-{%- endfor %}
-{%- else %}
- {{port.get_symbol_name().upper()}} = {{ns.count}},
-{%- set ns.count = ns.count + 1 -%}
-{%- endif -%}
-{%- endfor %}
- MAX_SPIKE_RECEPTOR = {{ns.count}}
- };
-
- enum ContinuousInput
- {
-{%- set ns.count = 0 -%}
-{%- for port in neuron.get_continuous_input_ports() %}
-{%- if port.has_vector_parameter() -%}
-{% set size = utils.get_numeric_vector_size(port) | int %}
-{%- for i in range(size) %}
- {{port.get_symbol_name().upper()}}_{{i}} = {{ns.count}},
-{%- set ns.count = ns.count + 1 -%}
-{%- endfor %}
-{%- else %}
- {{port.get_symbol_name().upper()}} = {{ns.count}},
-{%- set ns.count = ns.count + 1 -%}
-{%- endif -%}
-{%- endfor %}
- NUM_CONTINUOUS_INPUT_PORTS = {{ ns.count }}
- };
+ const nest_port_t MAX_SPIKE_RECEPTOR = {{ utils.nestml_spiking_input_port_to_nest_rport_dict(astnode) | length }};
- static const size_t NUM_SPIKE_RECEPTORS = MAX_SPIKE_RECEPTOR - MIN_SPIKE_RECEPTOR;
+ const nest_port_t NUM_CONTINUOUS_INPUT_PORTS = {{ utils.nestml_continuous_input_port_to_nest_rport_dict(astnode) | length }};
-{% if neuron.get_spike_input_ports()|length > 1 or neuron.is_multisynapse_spikes() -%}
- static std::vector< std::tuple< int, int > > rport_to_nestml_buffer_idx;
-{%- endif %}
/**
* Reset state of neuron.
@@ -827,9 +784,69 @@ private:
// Spike buffers and sums of incoming spikes/currents per timestep
// -----------------------------------------------------------------------
-{%- filter indent(4, True) -%}
-{{ buffer_getter.SpikeBufferGetter(true) }}
-{%- endfilter %}
+{%- for inputPortSymbol in neuron.get_spike_input_ports() %}
+{%- set inputPort = utils.get_input_port_by_name(astnode.get_input_blocks(), inputPortSymbol.name.split(".")[0]) %}
+
+ // input port: {{ inputPort.name }}
+{%- if inputPortSymbol.name in linear_time_invariant_spiking_input_ports %}
+{#- linear, time-invariant input port: all spike events for a specific buffer slot can be added together into a single number #}
+
+{%- if inputPortSymbol.has_vector_parameter() %}
+{%- set size = utils.get_numeric_vector_size(inputPortSymbol) %}
+{%- for i in range(size) %}
+{%- if inputPort.get_parameters() %}
+{%- for parameter in inputPort.get_parameters() %}
+ nest::RingBuffer spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_;
+ double spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_grid_sum_;
+{%- endfor %}
+{%- endif %}
+ nest::RingBuffer spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}_;
+ double spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}_grid_sum_;
+ nest::RingBuffer spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}_n_spikes_received_;
+ double spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}_n_spikes_received_grid_sum_;
+{%- endfor %}
+{%- else %}
+{%- for parameter in inputPort.get_parameters() %}
+ nest::RingBuffer spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_;
+ double spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_grid_sum_;
+{%- endfor %}
+ nest::RingBuffer spike_input_{{ inputPort.name }}_; // buffer for unweighted spikes
+ double spike_input_{{ inputPort.name }}_grid_sum_; // buffer for unweighted spikes
+ nest::RingBuffer spike_input_{{ inputPort.name }}_n_spikes_received_; // buffer for the "spike received" boolean flag
+ double spike_input_{{ inputPort.name }}_n_spikes_received_grid_sum_; // buffer for the "spike received" boolean flag
+{%- endif %}
+{%- else %}
+{#- generic input port: use lists of spike events for each buffer slot #}
+
+{%- if inputPortSymbol.has_vector_parameter() %}
+{%- set size = utils.get_numeric_vector_size(inputPortSymbol) %}
+{%- for i in range(size) %}
+{%- if inputPort.get_parameters() %}
+{%- for parameter in inputPort.get_parameters() %}
+ nest::VectorRingBuffer spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_;
+ double spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}__DOT__{{ parameter.get_name() }}_grid_sum_;
+{%- endfor %}
+{%- endif %}
+{%- if attributes_spiking_input_port_necessary %}
+ nest::VectorRingBuffer spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}_;
+ double spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}_grid_sum_;
+{%- endif %}
+ nest::RingBuffer spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}_n_spikes_received_;
+ double spike_input_{{ inputPort.name }}_VEC_IDX_{{ i }}_n_spikes_received_grid_sum_;
+{%- endfor %}
+{%- else %}
+{%- for parameter in inputPort.get_parameters() %}
+ nest::VectorRingBuffer spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_;
+ double spike_input_{{ inputPort.name }}__DOT__{{ parameter.get_name() }}_grid_sum_;
+{%- endfor %}
+{%- if attributes_spiking_input_port_necessary %}
+ nest::VectorRingBuffer spike_input_{{ inputPort.name }}_; // buffer for unweighted spikes
+{%- endif %}
+ nest::RingBuffer spike_input_{{ inputPort.name }}_n_spikes_received_; // buffer for the "spike received" boolean flag
+ double spike_input_{{ inputPort.name }}_n_spikes_received_grid_sum_; // buffer for the "spike received" boolean flag
+{%- endif %}
+{%- endif %}
+{%- endfor %}
// -----------------------------------------------------------------------
// Continuous-input buffers
@@ -892,14 +909,12 @@ private:
{%- endfilter %}
// -------------------------------------------------------------------------
- // Getters/setters for input buffers
+ // Getters/setters for continuous-time input buffers
// -------------------------------------------------------------------------
-{%- filter indent(2, True) %}
-{{ buffer_getter.SpikeBufferGetter(false) }}
+{% filter indent(2, True) %}
{{ continuous_buffer_getter.ContinuousInputBufferGetter(false) }}
{%- endfilter %}
-
{%- if neuron.get_functions()|length > 0 %}
// -------------------------------------------------------------------------
// Function declarations
@@ -986,7 +1001,7 @@ inline nest_port_t {{neuronName}}::send_test_event(nest::Node& target, nest_rpor
{
// You should usually not change the code in this function.
// It confirms that the target of connection @c c accepts @c {{ output_event.OutputEvent() }} on
- // the given @c receptor_type.
+ // the given receptor_type.
{{ output_event.OutputEvent() }} e;
e.set_sender(*this);
return target.handles_test_event(e, receptor_type);
@@ -995,17 +1010,16 @@ inline nest_port_t {{neuronName}}::send_test_event(nest::Node& target, nest_rpor
inline nest_port_t {{neuronName}}::handles_test_event(nest::SpikeEvent&, nest_port_t receptor_type)
{
-{%- if (neuron.get_multiple_receptors())|length > 1 or neuron.is_multisynapse_spikes() %}
- assert( B_.spike_inputs_.size() == NUM_SPIKE_RECEPTORS );
- if ( receptor_type < MIN_SPIKE_RECEPTOR or receptor_type >= MAX_SPIKE_RECEPTOR )
+{%- if (neuron.get_multiple_receptors()) | length > 1 or neuron.is_multisynapse_spikes() %}
+ if ( receptor_type < 1 or receptor_type > MAX_SPIKE_RECEPTOR )
{
throw nest::UnknownReceptorType( receptor_type, get_name() );
}
- return receptor_type - MIN_SPIKE_RECEPTOR;
+ return receptor_type;
{%- else %}
// You should usually not change the code in this function.
// It confirms to the connection management system that we are able
- // to handle @c SpikeEvent on port 0. You need to extend the function
+ // to handle a SpikeEvent on port 0. You need to extend the function
// if you want to differentiate between input ports.
if (receptor_type != 0)
{
@@ -1021,7 +1035,7 @@ inline nest_port_t {{neuronName}}::handles_test_event(nest::CurrentEvent&, nest_
{
// You should usually not change the code in this function.
// It confirms to the connection management system that we are able
- // to handle @c CurrentEvent on port 0. You need to extend the function
+ // to handle a CurrentEvent on port 0. You need to extend the function
// if you want to differentiate between input ports.
if (receptor_type >= NUM_CONTINUOUS_INPUT_PORTS)
{
@@ -1035,7 +1049,7 @@ inline nest_port_t {{neuronName}}::handles_test_event(nest::DataLoggingRequest&
{
// You should usually not change the code in this function.
// It confirms to the connection management system that we are able
- // to handle @c DataLoggingRequest on port 0.
+ // to handle a DataLoggingRequest on port 0.
// The function also tells the built-in UniversalDataLogger that this node
// is recorded from and that it thus needs to collect data during simulation.
if (receptor_type != 0)
@@ -1068,30 +1082,20 @@ inline void {{neuronName}}::get_status(DictionaryDatum &__d) const
{{neuron_parent_class}}::get_status( __d );
-{%- if (neuron.get_multiple_receptors())|length > 1 or neuron.is_multisynapse_spikes() %}
+ // input ports to NEST rport number mapping
DictionaryDatum __receptor_type = new Dictionary();
-{%- for key, ports in utils.get_spike_input_ports_in_pairs(neuron).items() %}
-{%- set ns = namespace(rport=key) %}
-{%- for port in ports %}
-{%- if not port.has_vector_parameter() %}
- ( *__receptor_type )[ "{{port.get_symbol_name().upper()}}" ] = {{ns.rport + 1}};
-{%- else %}
-{%- set size = utils.get_numeric_vector_size(port) %}
-{%- for i in range(size) %}
- ( *__receptor_type )[ "{{port.get_symbol_name().upper()}}_{{i}}" ] = {{ns.rport + i + 1}},
-{%- endfor %}
-{%- endif %}
-{%- endfor %}
-{%- endfor %}
- ( *__d )[ "receptor_types" ] = __receptor_type;
-{%- endif %}
+{%- for spike_in_port_name, rport in utils.nestml_spiking_input_port_to_nest_rport_dict(astnode).items() %}
+{%- set spike_in_port = utils.get_input_port_by_name(astnode.get_input_blocks(), spike_in_port_name.split("_VEC_IDX_")[0]) %}
+ ( *__receptor_type )[ "{{spike_in_port_name.upper()}}" ] = {{ rport }};
+{%- endfor %}
+ (*__d)["receptor_types"] = __receptor_type;
{%- if (neuron.get_continuous_input_ports())|length > 1 %}
DictionaryDatum __continuous_input_type = new Dictionary();
-{%- for port in neuron.get_continuous_input_ports() %}
- ( *__continuous_input_type )[ "{{port.get_symbol_name().upper()}}" ] = {{port.get_symbol_name().upper()}};
-{%- endfor %}
- ( *__d )[ "continuous_inputs" ] = __continuous_input_type;
+{%- for port in neuron.get_continuous_input_ports() %}
+ ( *__continuous_input_type )[ "{{port.get_symbol_name().upper()}}" ] = {{port.get_symbol_name().upper()}};
+{%- endfor %}
+ ( *__d )[ "continuous_inputs" ] = __continuous_input_type;
{%- endif %}
(*__d)[nest::names::recordables] = recordablesMap_.get_list();
diff --git a/pynestml/codegeneration/resources_nest/point_neuron/directives_cpp/ApplySpikesFromBuffers.jinja2 b/pynestml/codegeneration/resources_nest/point_neuron/directives_cpp/ApplySpikesFromBuffers.jinja2
index 881257451..2ef3aa91a 100644
--- a/pynestml/codegeneration/resources_nest/point_neuron/directives_cpp/ApplySpikesFromBuffers.jinja2
+++ b/pynestml/codegeneration/resources_nest/point_neuron/directives_cpp/ApplySpikesFromBuffers.jinja2
@@ -1,6 +1,6 @@
{% if tracing %}/* generated by {{self._TemplateReference__context.name}} */ {% endif %}
{%- for spike_updates_for_port in spike_updates.values() %}
-{%- for ast in spike_updates_for_port -%}
+{%- for ast in spike_updates_for_port -%}
{%- include "directives_cpp/Assignment.jinja2" %}
-{%- endfor %}
+{%- endfor %}
{%- endfor %}
diff --git a/pynestml/codegeneration/resources_nest/point_neuron/directives_cpp/RportToBufferIndexEntry.jinja2 b/pynestml/codegeneration/resources_nest/point_neuron/directives_cpp/RportToBufferIndexEntry.jinja2
index 0e6a13c28..e3251a576 100644
--- a/pynestml/codegeneration/resources_nest/point_neuron/directives_cpp/RportToBufferIndexEntry.jinja2
+++ b/pynestml/codegeneration/resources_nest/point_neuron/directives_cpp/RportToBufferIndexEntry.jinja2
@@ -4,17 +4,5 @@
{%- else -%}
{%- set name = "{}" %}
{%- endif -%}
-
-{%- if ports|length > 1 -%}
-{%- if ports[0].is_excitatory() %}
-{%- set exc_port = ports[0] %}
-{%- set inh_port = ports[1] %}
-{%- else %}
-{%- set exc_port = ports[1] %}
-{%- set inh_port = ports[0] %}
-{%- endif %}
- { {{neuronName}}::{{ name.format(exc_port.get_symbol_name().upper()) }}, {{neuronName}}::{{ name.format(inh_port.get_symbol_name().upper()) }} },
-{%- else -%}
- { {{neuronName}}::{{ name.format(ports[0].get_symbol_name().upper()) }}, {{neuronName}}::PORT_NOT_AVAILABLE },
-{%- endif -%}
+ {{ neuronName }}::{{ name.format(ports[0].get_symbol_name().upper()) }}
{%- endmacro -%}
diff --git a/pynestml/codegeneration/resources_nest/point_neuron/directives_cpp/SpikeBufferGetter.jinja2 b/pynestml/codegeneration/resources_nest/point_neuron/directives_cpp/SpikeBufferGetter.jinja2
index a151590ef..c96ba217d 100644
--- a/pynestml/codegeneration/resources_nest/point_neuron/directives_cpp/SpikeBufferGetter.jinja2
+++ b/pynestml/codegeneration/resources_nest/point_neuron/directives_cpp/SpikeBufferGetter.jinja2
@@ -4,24 +4,24 @@
{% set _tuples = [
("nest::RingBuffer", "spike_inputs_", "Buffer containing the incoming spikes"),
("double", "spike_inputs_grid_sum_", "Buffer containing the sum of all the incoming spikes"),
- ("nest::RingBuffer", "spike_input_received_", "Buffer containing a flag whether incoming spikes have been received on a given port"),
- ("double", "spike_input_received_grid_sum_", "Buffer containing a flag whether incoming spikes have been received on a given port")
+ ("nest::RingBuffer", "n_spikes_received_", "Buffer containing a flag whether incoming spikes have been received on a given port"),
+ ("double", "n_spikes_received_grid_sum_", "Buffer containing a flag whether incoming spikes have been received on a given port")
] %}
{%- for data_type, variable_name, comment_string in _tuples %}
/**
* {{ comment_string }}
**/
-inline std::vector< {{data_type}} >& get_{{variable_name}}()
+inline {{ data_type }}& get_{{ variable_name }}_{{ suffix }}()
{
{%- if is_in_struct %}
- return {{variable_name}};
+ return {{ variable_name }}_{{ suffix }};
{%- else %}
- return B_.get_{{variable_name}}();
+ return B_.get_{{ variable_name }}()_{{ suffix }};
{%- endif %}
}
{%- if is_in_struct %}
-std::vector< {{data_type}} > {{variable_name}};
+{{ data_type }} {{ variable_name }}_{{ suffix }};
{%- endif %}
{%- endfor %}
diff --git a/pynestml/codegeneration/resources_nest/point_neuron/setup/CMakeLists.txt.jinja2 b/pynestml/codegeneration/resources_nest/point_neuron/setup/CMakeLists.txt.jinja2
index 002ff05de..29ef9cee9 100644
--- a/pynestml/codegeneration/resources_nest/point_neuron/setup/CMakeLists.txt.jinja2
+++ b/pynestml/codegeneration/resources_nest/point_neuron/setup/CMakeLists.txt.jinja2
@@ -59,10 +59,11 @@ set( MODULE_NAME ${SHORT_NAME} )
# 2) Add all your sources here
set( MODULE_SOURCES
- {{moduleName}}.h {{moduleName}}.cpp
- {% for neuron in neurons %}
- {{neuron.get_name()}}.cpp {{neuron.get_name()}}.h
- {% endfor %}
+ {{ moduleName }}.h {{ moduleName }}.cpp
+{%- for neuron in neurons %}
+ {{ neuron.get_name() }}.cpp {{ neuron.get_name() }}.h
+{%- endfor %}
+ vector_ring_buffer.h vector_ring_buffer.cpp
)
# 3) We require a header name like this:
diff --git a/pynestml/codegeneration/resources_nest/point_neuron/setup/vector_ring_buffer.cpp.jinja2 b/pynestml/codegeneration/resources_nest/point_neuron/setup/vector_ring_buffer.cpp.jinja2
new file mode 100644
index 000000000..91b4694cf
--- /dev/null
+++ b/pynestml/codegeneration/resources_nest/point_neuron/setup/vector_ring_buffer.cpp.jinja2
@@ -0,0 +1,71 @@
+{#
+ * vector_ring_buffer.cpp.jinja2
+ *
+ * This file is part of NEST.
+ *
+ * Copyright (C) 2004 The NEST Initiative
+ *
+ * NEST is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * NEST is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with NEST. If not, see .
+ *
+#}
+/*
+ * vector_ring_buffer.cpp
+ *
+ * This file is part of NEST.
+ *
+ * Copyright (C) 2004 The NEST Initiative
+ *
+ * NEST is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * NEST is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with NEST. If not, see .
+ *
+ */
+
+#include "vector_ring_buffer.h"
+
+
+nest::VectorRingBuffer::VectorRingBuffer()
+ : buffer_( kernel().connection_manager.get_min_delay() + kernel().connection_manager.get_max_delay() )
+{
+}
+
+void
+nest::VectorRingBuffer::resize()
+{
+ size_t size = kernel().connection_manager.get_min_delay() + kernel().connection_manager.get_max_delay();
+ if ( buffer_.size() != size )
+ {
+ buffer_.resize( size );
+ }
+}
+
+void
+nest::VectorRingBuffer::clear()
+{
+ resize(); // does nothing if size is fine
+ // clear all elements
+ for ( unsigned int i = 0; i < buffer_.size(); i++ )
+ {
+ buffer_[ i ].clear();
+ }
+}
diff --git a/pynestml/codegeneration/resources_nest/point_neuron/setup/vector_ring_buffer.h.jinja2 b/pynestml/codegeneration/resources_nest/point_neuron/setup/vector_ring_buffer.h.jinja2
new file mode 100644
index 000000000..609681cc9
--- /dev/null
+++ b/pynestml/codegeneration/resources_nest/point_neuron/setup/vector_ring_buffer.h.jinja2
@@ -0,0 +1,142 @@
+{#
+ * vector_ring_buffer.h.jinja2
+ *
+ * This file is part of NEST.
+ *
+ * Copyright (C) 2004 The NEST Initiative
+ *
+ * NEST is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * NEST is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with NEST. If not, see .
+ *
+#}
+/*
+ * vector_ring_buffer.h
+ *
+ * This file is part of NEST.
+ *
+ * Copyright (C) 2004 The NEST Initiative
+ *
+ * NEST is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * NEST is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with NEST. If not, see .
+ *
+ */
+
+#ifndef VECTOR_RING_BUFFER_H
+#define VECTOR_RING_BUFFER_H
+
+// C++ includes:
+#include
+#include
+#include
+
+// Includes from nestkernel:
+#include "kernel_manager.h"
+#include "nest_time.h"
+#include "nest_types.h"
+
+namespace nest
+{
+
+class VectorRingBuffer
+{
+public:
+ VectorRingBuffer();
+
+ /**
+ * Append a value to the ring buffer list.
+ *
+ * @param offs Arrival time relative to beginning of slice.
+ * @param double Value to append.
+ */
+ void append_value( const long offs, const double );
+
+ std::vector< double >& get_list( const long offs );
+
+ /**
+ * Initialize the buffer with empty lists.
+ * Also resizes the buffer if necessary.
+ */
+ void clear();
+
+ /**
+ * Resize the buffer according to max_thread and max_delay.
+ *
+ * New elements are filled with empty lists.
+ * @note resize() has no effect if the buffer has the correct size.
+ */
+ void resize();
+
+ /**
+ * Returns buffer size, for memory measurement.
+ */
+ size_t
+ size() const
+ {
+ return buffer_.size();
+ }
+
+private:
+ //! Buffered data
+ std::vector< std::vector< double > > buffer_;
+
+ /**
+ * Obtain buffer index.
+ *
+ * @param delay delivery delay for event
+ * @returns index to buffer element into which event should be
+ * recorded.
+ */
+ size_t get_index_( const long d ) const;
+};
+
+inline void
+VectorRingBuffer::append_value( const long offs, const double v )
+{
+ buffer_[ get_index_( offs ) ].push_back( v );
+}
+
+inline std::vector< double >&
+VectorRingBuffer::get_list( const long offs )
+{
+ assert( 0 <= offs and static_cast< size_t >( offs ) < buffer_.size() );
+ assert( offs < kernel().connection_manager.get_min_delay() );
+
+ // offs == 0 is beginning of slice, but we have to
+ // take modulo into account when indexing
+ long idx = get_index_( offs );
+ return buffer_[ idx ];
+}
+
+inline size_t
+VectorRingBuffer::get_index_( const long d ) const
+{
+ const long idx = kernel().event_delivery_manager.get_modulo( d );
+ assert( 0 <= idx );
+ assert( static_cast< size_t >( idx ) < buffer_.size() );
+ return idx;
+}
+
+} // namespace nest
+
+
+#endif /* #ifndef VECTOR_RING_BUFFER_H */
diff --git a/pynestml/codegeneration/resources_python_standalone/point_neuron/@NEURON_NAME@.py.jinja2 b/pynestml/codegeneration/resources_python_standalone/point_neuron/@NEURON_NAME@.py.jinja2
index 122ccb033..72d72d4c4 100644
--- a/pynestml/codegeneration/resources_python_standalone/point_neuron/@NEURON_NAME@.py.jinja2
+++ b/pynestml/codegeneration/resources_python_standalone/point_neuron/@NEURON_NAME@.py.jinja2
@@ -103,7 +103,11 @@ class Neuron_{{neuronName}}(Neuron):
{{ port.get_symbol_name() }}: List[float] = []
spike_received_{{ port.get_symbol_name() }}: List[bool] = []
{%- else %}
- {{ port.get_symbol_name() }}: float = 0.
+{% set ast_input_port = utils.get_input_port_by_name(astnode.get_input_blocks(), port.name) %}
+{%- for attribute in ast_input_port.get_parameters() %}
+ {{ port.get_symbol_name() }}__DOT__{{ attribute.name }}: List[float] = []
+{%- endfor %}
+ {{ port.get_symbol_name() }}: List[float] = [] # buffer for the port name by itself (train of unweighted delta pulses)
spike_received_{{ port.get_symbol_name() }}: bool = False
{%- endif %}
{%- endfor %}
@@ -266,9 +270,11 @@ class Neuron_{{neuronName}}(Neuron):
# integrate variables related to convolutions
# -------------------------------------------------------------------------
-{%- with analytic_state_variables_ = analytic_state_variables_from_convolutions %}
-{%- include "directives_py/AnalyticIntegrationStep_begin.jinja2" %}
-{%- endwith %}
+{%- filter indent(4, True) -%}
+{%- with analytic_state_variables_ = analytic_state_variables_from_convolutions %}
+{%- include "directives_py/AnalyticIntegrationStep_begin.jinja2" %}
+{%- endwith %}
+{%- endfilter %}
# -------------------------------------------------------------------------
# NESTML generated code for the update block
@@ -287,9 +293,11 @@ class Neuron_{{neuronName}}(Neuron):
# integrate variables related to convolutions
# -------------------------------------------------------------------------
-{%- with analytic_state_variables_ = analytic_state_variables_from_convolutions %}
-{%- include "directives_py/AnalyticIntegrationStep_end.jinja2" %}
-{%- endwith %}
+{%- filter indent(4, True) -%}
+{%- with analytic_state_variables_ = analytic_state_variables_from_convolutions %}
+{%- include "directives_py/AnalyticIntegrationStep_end.jinja2" %}
+{%- endwith %}
+{%- endfilter %}
# -------------------------------------------------------------------------
# process spikes from buffers
@@ -303,9 +311,11 @@ class Neuron_{{neuronName}}(Neuron):
# -------------------------------------------------------------------------
{% for blk in neuron.get_on_receive_blocks() %}
-{%- set inport = blk.get_port_name() %}
- if self.B_.spike_received_{{ inport }}:
- self.on_receive_block_{{ blk.get_port_name() }}()
+ if self.B_.spike_received_{{ utils.port_name_printer(blk.get_input_port_variable()) }}:
+ # loop over all spikes received since last timestep
+ n_spikes_received: int = len(self.B_.{{ utils.port_name_printer(blk.get_input_port_variable()) }})
+ for i in range(n_spikes_received):
+ self.on_receive_block_{{ utils.port_name_printer(blk.get_input_port_variable()) }}()
{%- endfor %}
# -------------------------------------------------------------------------
@@ -313,7 +323,11 @@ class Neuron_{{neuronName}}(Neuron):
# -------------------------------------------------------------------------
{%- for port in neuron.get_spike_input_ports() %}
- self.B_.{{port.get_symbol_name()}} = 0.
+ self.B_.{{port.get_symbol_name()}} = []
+{%- set ast_input_port = utils.get_input_port_by_name(astnode.get_input_blocks(), port.name) %}
+{%- for attribute in ast_input_port.get_parameters() %}
+ self.B_.{{ port.get_symbol_name() }}__DOT__{{ attribute.name }} = []
+{%- endfor %}
self.B_.spike_received_{{port.get_symbol_name()}} = False
{%- endfor %}
@@ -340,9 +354,17 @@ class Neuron_{{neuronName}}(Neuron):
def handle(self, t_spike: float, w: float, port_name: str) -> None:
{%- for port in neuron.get_spike_input_ports() %}
- if port_name == "{{port.name}}":
- self.B_.{{port.get_symbol_name()}} += abs(w)
- self.B_.spike_received_{{port.get_symbol_name()}} = True
+ if port_name == "{{ port.name }}":
+ self.B_.{{ port.get_symbol_name() }}.append(1.) # unweighted spike port
+{%- set ast_input_port = utils.get_input_port_by_name(astnode.get_input_blocks(), port.name) %}
+{%- for attribute in ast_input_port.get_parameters() %}
+{%- if attribute.name == "weight" %}
+ self.B_.{{ port.get_symbol_name() }}__DOT__{{ attribute.name }}.append(w) # weighted spike port
+{%- else %}
+{{ raise('The Python-standalone code generator only supports \'weight\' spike input port attribute for now') }}
+{%- endif %}
+{%- endfor %}
+ self.B_.spike_received_{{ port.get_symbol_name() }} = True
return
{%- endfor %}
raise Exception("Received a spike on unknown input port \"" + port_name + "\" at t = " + "{0:E}".format(t_spike))
@@ -361,7 +383,16 @@ class Neuron_{{neuronName}}(Neuron):
{%- for blk in neuron.get_on_receive_blocks() %}
{%- set ast = blk.get_stmts_body() %}
- def on_receive_block_{{ blk.get_port_name() }}(self):
+{%- set port = blk.get_input_port_variable() %}
+{% set ast_input_port = utils.get_input_port_by_name(astnode.get_input_blocks(), port.name) %}
+ def on_receive_block_{{ utils.port_name_printer(blk.get_input_port_variable()) }}(self):
+ # first, grab all the buffer values from the buffers
+ B__{{ utils.port_name_printer(blk.get_input_port_variable()) }} = self.B_.{{ utils.port_name_printer(blk.get_input_port_variable()) }}.pop()
+{%- for attribute in ast_input_port.get_parameters() %}
+ B__{{ ast_input_port.get_name() }}__DOT__{{ attribute.name }} = self.B_.{{ ast_input_port.get_name() }}__DOT__{{ attribute.name }}.pop()
+{%- endfor %}
+ # XXX: TODO: check that NESTML models are only alowed to have on onreceive block per input port
+
{%- filter indent(4, True) -%}
{%- include "directives_py/StmtsBody.jinja2" %}
{%- endfilter %}
diff --git a/pynestml/codegeneration/resources_python_standalone/point_neuron/@SYNAPSE_NAME@.py.jinja2 b/pynestml/codegeneration/resources_python_standalone/point_neuron/@SYNAPSE_NAME@.py.jinja2
index 402dd9834..f17e0db8b 100644
--- a/pynestml/codegeneration/resources_python_standalone/point_neuron/@SYNAPSE_NAME@.py.jinja2
+++ b/pynestml/codegeneration/resources_python_standalone/point_neuron/@SYNAPSE_NAME@.py.jinja2
@@ -342,6 +342,10 @@ class Synapse_{{ astnode.name }}(Synapse):
{%- for port in astnode.get_spike_input_ports() %}
self.B_.{{port.get_symbol_name()}} = 0.
+{%- set ast_input_port = utils.get_input_port_by_name(astnode.get_input_blocks(), port.name) %}
+{%- for attribute in ast_input_port.get_parameters() %}
+ self.B_.{{ port.get_symbol_name() }}__DOT__{{ attribute.name }} = 0.
+{%- endfor %}
self.B_.spike_received_{{port.get_symbol_name()}} = False
{%- endfor %}
@@ -370,7 +374,7 @@ class Synapse_{{ astnode.name }}(Synapse):
{%- for port in astnode.get_spike_input_ports() %}
if port_name == "{{port.name}}":
- self.B_.{{port.get_symbol_name()}} += abs(w)
+ self.B_.{{port.get_symbol_name()}} += w
self.B_.spike_received_{{port.get_symbol_name()}} = True
return
{%- endfor %}
diff --git a/pynestml/codegeneration/resources_python_standalone/point_neuron/directives_py/ApplySpikesFromBuffers.jinja2 b/pynestml/codegeneration/resources_python_standalone/point_neuron/directives_py/ApplySpikesFromBuffers.jinja2
index c0952b2f5..bb74e623b 100644
--- a/pynestml/codegeneration/resources_python_standalone/point_neuron/directives_py/ApplySpikesFromBuffers.jinja2
+++ b/pynestml/codegeneration/resources_python_standalone/point_neuron/directives_py/ApplySpikesFromBuffers.jinja2
@@ -1,6 +1,6 @@
{%- if tracing %}# generated by {{self._TemplateReference__context.name}}{% endif %}
{%- for spike_updates_for_port in spike_updates.values() %}
-{%- for ast in spike_updates_for_port -%}
-{%- include "directives_py/Assignment.jinja2" %}
-{%- endfor %}
+{%- for ast in spike_updates_for_port -%}
+{%- include "directives_py/Assignment.jinja2" %}
+{%- endfor %}
{%- endfor %}
diff --git a/pynestml/codegeneration/resources_python_standalone/point_neuron/test_python_standalone_module.py.jinja2 b/pynestml/codegeneration/resources_python_standalone/point_neuron/test_python_standalone_module.py.jinja2
index 2454c6a1f..a11e6ed5a 100644
--- a/pynestml/codegeneration/resources_python_standalone/point_neuron/test_python_standalone_module.py.jinja2
+++ b/pynestml/codegeneration/resources_python_standalone/point_neuron/test_python_standalone_module.py.jinja2
@@ -42,12 +42,15 @@ class TestSimulator:
sg_exc = simulator.add_neuron(SpikeGenerator(interval=10.))
sg_inh = simulator.add_neuron(SpikeGenerator(interval=50.))
{% for neuron in neurons %}
- neuron = simulator.add_neuron(Neuron_{{neuron.get_name()}}(timestep=simulator.timestep))
+ neuron = simulator.add_neuron(Neuron_{{ neuron.get_name() }}(timestep=simulator.timestep))
if "exc_spikes" in simulator.neurons[neuron].get_spiking_input_ports():
simulator.connect(sg_exc, neuron, "exc_spikes", w=1000.)
simulator.connect(sg_inh, neuron, "inh_spikes", w=4000.)
else:
- simulator.connect(sg_exc, neuron, "spikes", w=1000.)
+ simulator.connect(sg_exc, neuron, "spike_in_port", w=1000.)
+{%- if not "izhikevich" in neuron.get_name() %}
+ simulator.connect(sg_inh, neuron, "spike_in_port", w=-4000.)
+{%- endif %}
{% endfor %}
simulator.run(t_stop)
diff --git a/pynestml/codegeneration/resources_spinnaker/@NEURON_NAME@_impl.h.jinja2 b/pynestml/codegeneration/resources_spinnaker/@NEURON_NAME@_impl.h.jinja2
index 268aa61ac..2acad5a22 100644
--- a/pynestml/codegeneration/resources_spinnaker/@NEURON_NAME@_impl.h.jinja2
+++ b/pynestml/codegeneration/resources_spinnaker/@NEURON_NAME@_impl.h.jinja2
@@ -68,21 +68,6 @@
extern "C" inline int {{neuronName}}_dynamics( double, const double ode_state[], double f[], void* pnode );
{% endif %}
-// Missing paired_synapse stuff here
-
-{% if has_multiple_synapses -%}
- /**
- * Synapse types to connect to
- * @note Excluded lower and upper bounds are defined as MIN_, MAX_.
- * Excluding port 0 avoids accidental connections.
- **/
-const long MIN_SPIKE_RECEPTOR = 1;
-{%- set ns = namespace(count=1) %}
-{%- else %}
-const long MIN_SPIKE_RECEPTOR = 0;
-{%- set ns = namespace(count=0) %}
-{%- endif %}
-
enum input_indices
{
{%- set ns = namespace(count=0) %}
@@ -98,7 +83,7 @@ enum input_indices
};
typedef struct {
-{# Make shure count is |spike_input_ports| + |continous_input_ports| #}
+{# Make sure count is |spike_input_ports| + |continous_input_ports| #}
// 0: exc, 1: inh, 2: Istim
{%- if ns.count > 0 %}
REAL inputs[{{ ns.count }}];
diff --git a/pynestml/generated/PyNestMLLexer.py b/pynestml/generated/PyNestMLLexer.py
index 9dcd9588e..bbc234488 100644
--- a/pynestml/generated/PyNestMLLexer.py
+++ b/pynestml/generated/PyNestMLLexer.py
@@ -1,4 +1,4 @@
-# Generated from PyNestMLLexer.g4 by ANTLR 4.13.2
+# Generated from PyNestMLLexer.g4 by ANTLR 4.13.1
from antlr4 import *
from io import StringIO
import sys
@@ -15,7 +15,7 @@
def serializedATN():
return [
- 4,0,89,684,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,
+ 4,0,87,658,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,
2,6,7,6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,
13,7,13,2,14,7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7,
19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,2,
@@ -28,237 +28,227 @@ def serializedATN():
65,7,65,2,66,7,66,2,67,7,67,2,68,7,68,2,69,7,69,2,70,7,70,2,71,7,
71,2,72,7,72,2,73,7,73,2,74,7,74,2,75,7,75,2,76,7,76,2,77,7,77,2,
78,7,78,2,79,7,79,2,80,7,80,2,81,7,81,2,82,7,82,2,83,7,83,2,84,7,
- 84,2,85,7,85,2,86,7,86,2,87,7,87,2,88,7,88,2,89,7,89,2,90,7,90,1,
- 0,3,0,185,8,0,1,0,1,0,1,1,1,1,1,1,3,1,192,8,1,1,2,4,2,195,8,2,11,
- 2,12,2,196,1,2,1,2,1,3,1,3,1,3,1,3,1,3,1,4,1,4,5,4,208,8,4,10,4,
- 12,4,211,9,4,1,4,1,4,1,5,1,5,1,5,3,5,218,8,5,1,5,1,5,1,5,3,5,223,
- 8,5,3,5,225,8,5,1,5,1,5,1,6,1,6,1,6,1,6,1,6,1,6,1,6,1,6,1,7,1,7,
- 1,7,1,7,1,7,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,9,1,9,1,9,1,9,1,9,1,9,
- 1,9,1,9,1,10,1,10,1,10,1,10,1,10,1,11,1,11,1,11,1,11,1,11,1,11,1,
- 11,1,11,1,11,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,13,1,13,1,13,1,
- 13,1,13,1,13,1,13,1,14,1,14,1,14,1,15,1,15,1,15,1,15,1,15,1,16,1,
- 16,1,16,1,16,1,16,1,17,1,17,1,17,1,17,1,18,1,18,1,18,1,18,1,18,1,
- 18,1,19,1,19,1,19,1,20,1,20,1,20,1,20,1,20,1,21,1,21,1,21,1,21,1,
- 22,1,22,1,22,1,22,1,23,1,23,1,23,1,24,1,24,1,24,1,24,1,25,1,25,1,
- 25,1,25,1,25,1,25,1,25,1,25,1,25,1,25,1,25,1,26,1,26,1,26,1,26,1,
- 26,1,26,1,26,1,27,1,27,1,27,1,27,1,27,1,27,1,28,1,28,1,28,1,28,1,
- 28,1,28,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,
- 30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,31,1,31,1,31,1,
- 31,1,31,1,31,1,31,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,
- 32,1,33,1,33,1,33,1,33,1,33,1,33,1,34,1,34,1,34,1,34,1,34,1,34,1,
- 34,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,36,1,
- 36,1,36,1,36,1,36,1,36,1,36,1,36,1,36,1,36,1,37,1,37,1,37,1,37,1,
- 37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,38,1,38,1,38,1,38,1,38,1,
- 38,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,40,1,
- 40,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,41,1,41,1,41,1,
- 41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,42,1,42,1,42,1,
- 42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,43,1,
- 43,1,44,1,44,1,44,1,44,1,45,1,45,1,46,1,46,1,47,1,47,1,48,1,48,1,
- 49,1,49,1,50,1,50,1,51,1,51,1,52,1,52,1,53,1,53,1,53,1,54,1,54,1,
- 55,1,55,1,55,1,56,1,56,1,56,1,57,1,57,1,57,1,58,1,58,1,58,1,59,1,
- 59,1,60,1,60,1,61,1,61,1,61,1,62,1,62,1,62,1,63,1,63,1,63,1,64,1,
- 64,1,64,1,65,1,65,1,65,1,66,1,66,1,66,1,67,1,67,1,67,1,68,1,68,1,
- 68,1,69,1,69,1,69,1,70,1,70,1,71,1,71,1,72,1,72,1,73,1,73,1,74,1,
- 74,1,74,1,75,1,75,1,76,1,76,1,77,1,77,1,78,1,78,1,79,1,79,1,79,1,
- 80,1,80,1,81,1,81,1,82,1,82,1,83,1,83,1,83,1,83,1,83,1,83,1,83,1,
- 83,1,83,1,83,1,83,1,83,1,83,1,83,1,83,1,83,1,83,1,83,3,83,617,8,
- 83,1,84,1,84,1,84,4,84,622,8,84,11,84,12,84,623,1,84,3,84,627,8,
- 84,1,84,3,84,630,8,84,1,84,3,84,633,8,84,1,84,5,84,636,8,84,10,84,
- 12,84,639,9,84,1,84,1,84,1,85,3,85,644,8,85,1,85,5,85,647,8,85,10,
- 85,12,85,650,9,85,1,86,4,86,653,8,86,11,86,12,86,654,1,87,1,87,3,
- 87,659,8,87,1,88,3,88,662,8,88,1,88,1,88,1,88,1,88,1,88,1,88,3,88,
- 670,8,88,1,89,1,89,3,89,674,8,89,1,89,1,89,1,89,1,90,1,90,3,90,681,
- 8,90,1,90,1,90,0,0,91,1,0,3,3,5,4,7,5,9,6,11,7,13,8,15,9,17,10,19,
- 11,21,12,23,13,25,14,27,15,29,16,31,17,33,18,35,19,37,20,39,21,41,
- 22,43,23,45,24,47,25,49,26,51,27,53,28,55,29,57,30,59,31,61,32,63,
- 33,65,34,67,35,69,36,71,37,73,38,75,39,77,40,79,41,81,42,83,43,85,
- 44,87,45,89,46,91,47,93,48,95,49,97,50,99,51,101,52,103,53,105,54,
- 107,55,109,56,111,57,113,58,115,59,117,60,119,61,121,62,123,63,125,
- 64,127,65,129,66,131,67,133,68,135,69,137,70,139,71,141,72,143,73,
- 145,74,147,75,149,76,151,77,153,78,155,79,157,80,159,81,161,82,163,
- 83,165,84,167,85,169,86,171,87,173,88,175,89,177,0,179,0,181,0,1,
- 0,7,2,0,9,9,32,32,2,0,10,10,13,13,4,0,10,10,13,13,34,34,92,92,4,
- 0,36,36,65,90,95,95,97,122,5,0,36,36,48,57,65,90,95,95,97,122,1,
- 0,48,57,2,0,69,69,101,101,703,0,3,1,0,0,0,0,5,1,0,0,0,0,7,1,0,0,
- 0,0,9,1,0,0,0,0,11,1,0,0,0,0,13,1,0,0,0,0,15,1,0,0,0,0,17,1,0,0,
- 0,0,19,1,0,0,0,0,21,1,0,0,0,0,23,1,0,0,0,0,25,1,0,0,0,0,27,1,0,0,
- 0,0,29,1,0,0,0,0,31,1,0,0,0,0,33,1,0,0,0,0,35,1,0,0,0,0,37,1,0,0,
- 0,0,39,1,0,0,0,0,41,1,0,0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,47,1,0,0,
- 0,0,49,1,0,0,0,0,51,1,0,0,0,0,53,1,0,0,0,0,55,1,0,0,0,0,57,1,0,0,
- 0,0,59,1,0,0,0,0,61,1,0,0,0,0,63,1,0,0,0,0,65,1,0,0,0,0,67,1,0,0,
- 0,0,69,1,0,0,0,0,71,1,0,0,0,0,73,1,0,0,0,0,75,1,0,0,0,0,77,1,0,0,
- 0,0,79,1,0,0,0,0,81,1,0,0,0,0,83,1,0,0,0,0,85,1,0,0,0,0,87,1,0,0,
- 0,0,89,1,0,0,0,0,91,1,0,0,0,0,93,1,0,0,0,0,95,1,0,0,0,0,97,1,0,0,
- 0,0,99,1,0,0,0,0,101,1,0,0,0,0,103,1,0,0,0,0,105,1,0,0,0,0,107,1,
- 0,0,0,0,109,1,0,0,0,0,111,1,0,0,0,0,113,1,0,0,0,0,115,1,0,0,0,0,
- 117,1,0,0,0,0,119,1,0,0,0,0,121,1,0,0,0,0,123,1,0,0,0,0,125,1,0,
- 0,0,0,127,1,0,0,0,0,129,1,0,0,0,0,131,1,0,0,0,0,133,1,0,0,0,0,135,
- 1,0,0,0,0,137,1,0,0,0,0,139,1,0,0,0,0,141,1,0,0,0,0,143,1,0,0,0,
- 0,145,1,0,0,0,0,147,1,0,0,0,0,149,1,0,0,0,0,151,1,0,0,0,0,153,1,
- 0,0,0,0,155,1,0,0,0,0,157,1,0,0,0,0,159,1,0,0,0,0,161,1,0,0,0,0,
- 163,1,0,0,0,0,165,1,0,0,0,0,167,1,0,0,0,0,169,1,0,0,0,0,171,1,0,
- 0,0,0,173,1,0,0,0,0,175,1,0,0,0,1,184,1,0,0,0,3,188,1,0,0,0,5,194,
- 1,0,0,0,7,200,1,0,0,0,9,205,1,0,0,0,11,224,1,0,0,0,13,228,1,0,0,
- 0,15,236,1,0,0,0,17,241,1,0,0,0,19,248,1,0,0,0,21,256,1,0,0,0,23,
- 261,1,0,0,0,25,270,1,0,0,0,27,277,1,0,0,0,29,284,1,0,0,0,31,287,
- 1,0,0,0,33,292,1,0,0,0,35,297,1,0,0,0,37,301,1,0,0,0,39,307,1,0,
- 0,0,41,310,1,0,0,0,43,315,1,0,0,0,45,319,1,0,0,0,47,323,1,0,0,0,
- 49,326,1,0,0,0,51,330,1,0,0,0,53,341,1,0,0,0,55,348,1,0,0,0,57,354,
- 1,0,0,0,59,360,1,0,0,0,61,371,1,0,0,0,63,381,1,0,0,0,65,388,1,0,
- 0,0,67,398,1,0,0,0,69,404,1,0,0,0,71,411,1,0,0,0,73,422,1,0,0,0,
- 75,432,1,0,0,0,77,444,1,0,0,0,79,450,1,0,0,0,81,461,1,0,0,0,83,472,
- 1,0,0,0,85,485,1,0,0,0,87,500,1,0,0,0,89,502,1,0,0,0,91,506,1,0,
- 0,0,93,508,1,0,0,0,95,510,1,0,0,0,97,512,1,0,0,0,99,514,1,0,0,0,
- 101,516,1,0,0,0,103,518,1,0,0,0,105,520,1,0,0,0,107,522,1,0,0,0,
- 109,525,1,0,0,0,111,527,1,0,0,0,113,530,1,0,0,0,115,533,1,0,0,0,
- 117,536,1,0,0,0,119,539,1,0,0,0,121,541,1,0,0,0,123,543,1,0,0,0,
- 125,546,1,0,0,0,127,549,1,0,0,0,129,552,1,0,0,0,131,555,1,0,0,0,
- 133,558,1,0,0,0,135,561,1,0,0,0,137,564,1,0,0,0,139,567,1,0,0,0,
- 141,570,1,0,0,0,143,572,1,0,0,0,145,574,1,0,0,0,147,576,1,0,0,0,
- 149,578,1,0,0,0,151,581,1,0,0,0,153,583,1,0,0,0,155,585,1,0,0,0,
- 157,587,1,0,0,0,159,589,1,0,0,0,161,592,1,0,0,0,163,594,1,0,0,0,
- 165,596,1,0,0,0,167,616,1,0,0,0,169,618,1,0,0,0,171,643,1,0,0,0,
- 173,652,1,0,0,0,175,658,1,0,0,0,177,669,1,0,0,0,179,673,1,0,0,0,
- 181,680,1,0,0,0,183,185,5,13,0,0,184,183,1,0,0,0,184,185,1,0,0,0,
- 185,186,1,0,0,0,186,187,5,10,0,0,187,2,1,0,0,0,188,189,3,141,70,
- 0,189,191,3,1,0,0,190,192,3,5,2,0,191,190,1,0,0,0,191,192,1,0,0,
- 0,192,4,1,0,0,0,193,195,7,0,0,0,194,193,1,0,0,0,195,196,1,0,0,0,
- 196,194,1,0,0,0,196,197,1,0,0,0,197,198,1,0,0,0,198,199,6,2,0,0,
- 199,6,1,0,0,0,200,201,5,92,0,0,201,202,3,1,0,0,202,203,1,0,0,0,203,
- 204,6,3,0,0,204,8,1,0,0,0,205,209,5,35,0,0,206,208,8,1,0,0,207,206,
- 1,0,0,0,208,211,1,0,0,0,209,207,1,0,0,0,209,210,1,0,0,0,210,212,
- 1,0,0,0,211,209,1,0,0,0,212,213,6,4,1,0,213,10,1,0,0,0,214,215,4,
- 5,0,0,215,225,3,5,2,0,216,218,5,13,0,0,217,216,1,0,0,0,217,218,1,
- 0,0,0,218,219,1,0,0,0,219,220,5,10,0,0,220,222,1,0,0,0,221,223,3,
- 5,2,0,222,221,1,0,0,0,222,223,1,0,0,0,223,225,1,0,0,0,224,214,1,
- 0,0,0,224,217,1,0,0,0,225,226,1,0,0,0,226,227,6,5,2,0,227,12,1,0,
- 0,0,228,229,5,105,0,0,229,230,5,110,0,0,230,231,5,116,0,0,231,232,
- 5,101,0,0,232,233,5,103,0,0,233,234,5,101,0,0,234,235,5,114,0,0,
- 235,14,1,0,0,0,236,237,5,114,0,0,237,238,5,101,0,0,238,239,5,97,
- 0,0,239,240,5,108,0,0,240,16,1,0,0,0,241,242,5,115,0,0,242,243,5,
- 116,0,0,243,244,5,114,0,0,244,245,5,105,0,0,245,246,5,110,0,0,246,
- 247,5,103,0,0,247,18,1,0,0,0,248,249,5,98,0,0,249,250,5,111,0,0,
- 250,251,5,111,0,0,251,252,5,108,0,0,252,253,5,101,0,0,253,254,5,
- 97,0,0,254,255,5,110,0,0,255,20,1,0,0,0,256,257,5,118,0,0,257,258,
- 5,111,0,0,258,259,5,105,0,0,259,260,5,100,0,0,260,22,1,0,0,0,261,
- 262,5,102,0,0,262,263,5,117,0,0,263,264,5,110,0,0,264,265,5,99,0,
- 0,265,266,5,116,0,0,266,267,5,105,0,0,267,268,5,111,0,0,268,269,
- 5,110,0,0,269,24,1,0,0,0,270,271,5,105,0,0,271,272,5,110,0,0,272,
- 273,5,108,0,0,273,274,5,105,0,0,274,275,5,110,0,0,275,276,5,101,
- 0,0,276,26,1,0,0,0,277,278,5,114,0,0,278,279,5,101,0,0,279,280,5,
- 116,0,0,280,281,5,117,0,0,281,282,5,114,0,0,282,283,5,110,0,0,283,
- 28,1,0,0,0,284,285,5,105,0,0,285,286,5,102,0,0,286,30,1,0,0,0,287,
- 288,5,101,0,0,288,289,5,108,0,0,289,290,5,105,0,0,290,291,5,102,
- 0,0,291,32,1,0,0,0,292,293,5,101,0,0,293,294,5,108,0,0,294,295,5,
- 115,0,0,295,296,5,101,0,0,296,34,1,0,0,0,297,298,5,102,0,0,298,299,
- 5,111,0,0,299,300,5,114,0,0,300,36,1,0,0,0,301,302,5,119,0,0,302,
- 303,5,104,0,0,303,304,5,105,0,0,304,305,5,108,0,0,305,306,5,101,
- 0,0,306,38,1,0,0,0,307,308,5,105,0,0,308,309,5,110,0,0,309,40,1,
- 0,0,0,310,311,5,115,0,0,311,312,5,116,0,0,312,313,5,101,0,0,313,
- 314,5,112,0,0,314,42,1,0,0,0,315,316,5,105,0,0,316,317,5,110,0,0,
- 317,318,5,102,0,0,318,44,1,0,0,0,319,320,5,97,0,0,320,321,5,110,
- 0,0,321,322,5,100,0,0,322,46,1,0,0,0,323,324,5,111,0,0,324,325,5,
- 114,0,0,325,48,1,0,0,0,326,327,5,110,0,0,327,328,5,111,0,0,328,329,
- 5,116,0,0,329,50,1,0,0,0,330,331,5,114,0,0,331,332,5,101,0,0,332,
- 333,5,99,0,0,333,334,5,111,0,0,334,335,5,114,0,0,335,336,5,100,0,
- 0,336,337,5,97,0,0,337,338,5,98,0,0,338,339,5,108,0,0,339,340,5,
- 101,0,0,340,52,1,0,0,0,341,342,5,107,0,0,342,343,5,101,0,0,343,344,
- 5,114,0,0,344,345,5,110,0,0,345,346,5,101,0,0,346,347,5,108,0,0,
- 347,54,1,0,0,0,348,349,5,109,0,0,349,350,5,111,0,0,350,351,5,100,
- 0,0,351,352,5,101,0,0,352,353,5,108,0,0,353,56,1,0,0,0,354,355,5,
- 115,0,0,355,356,5,116,0,0,356,357,5,97,0,0,357,358,5,116,0,0,358,
- 359,5,101,0,0,359,58,1,0,0,0,360,361,5,112,0,0,361,362,5,97,0,0,
- 362,363,5,114,0,0,363,364,5,97,0,0,364,365,5,109,0,0,365,366,5,101,
- 0,0,366,367,5,116,0,0,367,368,5,101,0,0,368,369,5,114,0,0,369,370,
- 5,115,0,0,370,60,1,0,0,0,371,372,5,105,0,0,372,373,5,110,0,0,373,
- 374,5,116,0,0,374,375,5,101,0,0,375,376,5,114,0,0,376,377,5,110,
- 0,0,377,378,5,97,0,0,378,379,5,108,0,0,379,380,5,115,0,0,380,62,
- 1,0,0,0,381,382,5,117,0,0,382,383,5,112,0,0,383,384,5,100,0,0,384,
- 385,5,97,0,0,385,386,5,116,0,0,386,387,5,101,0,0,387,64,1,0,0,0,
- 388,389,5,101,0,0,389,390,5,113,0,0,390,391,5,117,0,0,391,392,5,
- 97,0,0,392,393,5,116,0,0,393,394,5,105,0,0,394,395,5,111,0,0,395,
- 396,5,110,0,0,396,397,5,115,0,0,397,66,1,0,0,0,398,399,5,105,0,0,
- 399,400,5,110,0,0,400,401,5,112,0,0,401,402,5,117,0,0,402,403,5,
- 116,0,0,403,68,1,0,0,0,404,405,5,111,0,0,405,406,5,117,0,0,406,407,
- 5,116,0,0,407,408,5,112,0,0,408,409,5,117,0,0,409,410,5,116,0,0,
- 410,70,1,0,0,0,411,412,5,99,0,0,412,413,5,111,0,0,413,414,5,110,
- 0,0,414,415,5,116,0,0,415,416,5,105,0,0,416,417,5,110,0,0,417,418,
- 5,117,0,0,418,419,5,111,0,0,419,420,5,117,0,0,420,421,5,115,0,0,
- 421,72,1,0,0,0,422,423,5,111,0,0,423,424,5,110,0,0,424,425,5,82,
- 0,0,425,426,5,101,0,0,426,427,5,99,0,0,427,428,5,101,0,0,428,429,
- 5,105,0,0,429,430,5,118,0,0,430,431,5,101,0,0,431,74,1,0,0,0,432,
- 433,5,111,0,0,433,434,5,110,0,0,434,435,5,67,0,0,435,436,5,111,0,
- 0,436,437,5,110,0,0,437,438,5,100,0,0,438,439,5,105,0,0,439,440,
- 5,116,0,0,440,441,5,105,0,0,441,442,5,111,0,0,442,443,5,110,0,0,
- 443,76,1,0,0,0,444,445,5,115,0,0,445,446,5,112,0,0,446,447,5,105,
- 0,0,447,448,5,107,0,0,448,449,5,101,0,0,449,78,1,0,0,0,450,451,5,
- 105,0,0,451,452,5,110,0,0,452,453,5,104,0,0,453,454,5,105,0,0,454,
- 455,5,98,0,0,455,456,5,105,0,0,456,457,5,116,0,0,457,458,5,111,0,
- 0,458,459,5,114,0,0,459,460,5,121,0,0,460,80,1,0,0,0,461,462,5,101,
- 0,0,462,463,5,120,0,0,463,464,5,99,0,0,464,465,5,105,0,0,465,466,
- 5,116,0,0,466,467,5,97,0,0,467,468,5,116,0,0,468,469,5,111,0,0,469,
- 470,5,114,0,0,470,471,5,121,0,0,471,82,1,0,0,0,472,473,5,64,0,0,
- 473,474,5,104,0,0,474,475,5,111,0,0,475,476,5,109,0,0,476,477,5,
- 111,0,0,477,478,5,103,0,0,478,479,5,101,0,0,479,480,5,110,0,0,480,
- 481,5,101,0,0,481,482,5,111,0,0,482,483,5,117,0,0,483,484,5,115,
- 0,0,484,84,1,0,0,0,485,486,5,64,0,0,486,487,5,104,0,0,487,488,5,
- 101,0,0,488,489,5,116,0,0,489,490,5,101,0,0,490,491,5,114,0,0,491,
- 492,5,111,0,0,492,493,5,103,0,0,493,494,5,101,0,0,494,495,5,110,
- 0,0,495,496,5,101,0,0,496,497,5,111,0,0,497,498,5,117,0,0,498,499,
- 5,115,0,0,499,86,1,0,0,0,500,501,5,64,0,0,501,88,1,0,0,0,502,503,
- 5,46,0,0,503,504,5,46,0,0,504,505,5,46,0,0,505,90,1,0,0,0,506,507,
- 5,40,0,0,507,92,1,0,0,0,508,509,5,41,0,0,509,94,1,0,0,0,510,511,
- 5,43,0,0,511,96,1,0,0,0,512,513,5,126,0,0,513,98,1,0,0,0,514,515,
- 5,124,0,0,515,100,1,0,0,0,516,517,5,94,0,0,517,102,1,0,0,0,518,519,
- 5,38,0,0,519,104,1,0,0,0,520,521,5,91,0,0,521,106,1,0,0,0,522,523,
- 5,60,0,0,523,524,5,45,0,0,524,108,1,0,0,0,525,526,5,93,0,0,526,110,
- 1,0,0,0,527,528,5,91,0,0,528,529,5,91,0,0,529,112,1,0,0,0,530,531,
- 5,93,0,0,531,532,5,93,0,0,532,114,1,0,0,0,533,534,5,60,0,0,534,535,
- 5,60,0,0,535,116,1,0,0,0,536,537,5,62,0,0,537,538,5,62,0,0,538,118,
- 1,0,0,0,539,540,5,60,0,0,540,120,1,0,0,0,541,542,5,62,0,0,542,122,
- 1,0,0,0,543,544,5,60,0,0,544,545,5,61,0,0,545,124,1,0,0,0,546,547,
- 5,43,0,0,547,548,5,61,0,0,548,126,1,0,0,0,549,550,5,45,0,0,550,551,
- 5,61,0,0,551,128,1,0,0,0,552,553,5,42,0,0,553,554,5,61,0,0,554,130,
- 1,0,0,0,555,556,5,47,0,0,556,557,5,61,0,0,557,132,1,0,0,0,558,559,
- 5,61,0,0,559,560,5,61,0,0,560,134,1,0,0,0,561,562,5,33,0,0,562,563,
- 5,61,0,0,563,136,1,0,0,0,564,565,5,60,0,0,565,566,5,62,0,0,566,138,
- 1,0,0,0,567,568,5,62,0,0,568,569,5,61,0,0,569,140,1,0,0,0,570,571,
- 5,44,0,0,571,142,1,0,0,0,572,573,5,45,0,0,573,144,1,0,0,0,574,575,
- 5,61,0,0,575,146,1,0,0,0,576,577,5,42,0,0,577,148,1,0,0,0,578,579,
- 5,42,0,0,579,580,5,42,0,0,580,150,1,0,0,0,581,582,5,47,0,0,582,152,
- 1,0,0,0,583,584,5,37,0,0,584,154,1,0,0,0,585,586,5,63,0,0,586,156,
- 1,0,0,0,587,588,5,58,0,0,588,158,1,0,0,0,589,590,5,58,0,0,590,591,
- 5,58,0,0,591,160,1,0,0,0,592,593,5,59,0,0,593,162,1,0,0,0,594,595,
- 5,39,0,0,595,164,1,0,0,0,596,597,5,46,0,0,597,166,1,0,0,0,598,599,
- 5,116,0,0,599,600,5,114,0,0,600,601,5,117,0,0,601,617,5,101,0,0,
- 602,603,5,84,0,0,603,604,5,114,0,0,604,605,5,117,0,0,605,617,5,101,
- 0,0,606,607,5,102,0,0,607,608,5,97,0,0,608,609,5,108,0,0,609,610,
- 5,115,0,0,610,617,5,101,0,0,611,612,5,70,0,0,612,613,5,97,0,0,613,
- 614,5,108,0,0,614,615,5,115,0,0,615,617,5,101,0,0,616,598,1,0,0,
- 0,616,602,1,0,0,0,616,606,1,0,0,0,616,611,1,0,0,0,617,168,1,0,0,
- 0,618,637,5,34,0,0,619,632,5,92,0,0,620,622,7,0,0,0,621,620,1,0,
- 0,0,622,623,1,0,0,0,623,621,1,0,0,0,623,624,1,0,0,0,624,629,1,0,
- 0,0,625,627,5,13,0,0,626,625,1,0,0,0,626,627,1,0,0,0,627,628,1,0,
- 0,0,628,630,5,10,0,0,629,626,1,0,0,0,629,630,1,0,0,0,630,633,1,0,
- 0,0,631,633,9,0,0,0,632,621,1,0,0,0,632,631,1,0,0,0,633,636,1,0,
- 0,0,634,636,8,2,0,0,635,619,1,0,0,0,635,634,1,0,0,0,636,639,1,0,
- 0,0,637,635,1,0,0,0,637,638,1,0,0,0,638,640,1,0,0,0,639,637,1,0,
- 0,0,640,641,5,34,0,0,641,170,1,0,0,0,642,644,7,3,0,0,643,642,1,0,
- 0,0,644,648,1,0,0,0,645,647,7,4,0,0,646,645,1,0,0,0,647,650,1,0,
- 0,0,648,646,1,0,0,0,648,649,1,0,0,0,649,172,1,0,0,0,650,648,1,0,
- 0,0,651,653,7,5,0,0,652,651,1,0,0,0,653,654,1,0,0,0,654,652,1,0,
- 0,0,654,655,1,0,0,0,655,174,1,0,0,0,656,659,3,177,88,0,657,659,3,
- 179,89,0,658,656,1,0,0,0,658,657,1,0,0,0,659,176,1,0,0,0,660,662,
- 3,173,86,0,661,660,1,0,0,0,661,662,1,0,0,0,662,663,1,0,0,0,663,664,
- 3,165,82,0,664,665,3,173,86,0,665,670,1,0,0,0,666,667,3,173,86,0,
- 667,668,3,165,82,0,668,670,1,0,0,0,669,661,1,0,0,0,669,666,1,0,0,
- 0,670,178,1,0,0,0,671,674,3,173,86,0,672,674,3,177,88,0,673,671,
- 1,0,0,0,673,672,1,0,0,0,674,675,1,0,0,0,675,676,7,6,0,0,676,677,
- 3,181,90,0,677,180,1,0,0,0,678,681,3,95,47,0,679,681,3,143,71,0,
- 680,678,1,0,0,0,680,679,1,0,0,0,680,681,1,0,0,0,681,682,1,0,0,0,
- 682,683,3,173,86,0,683,182,1,0,0,0,24,0,184,191,196,209,217,222,
- 224,616,623,626,629,632,635,637,643,646,648,654,658,661,669,673,
- 680,3,0,1,0,0,2,0,1,5,0
+ 84,2,85,7,85,2,86,7,86,2,87,7,87,2,88,7,88,1,0,3,0,181,8,0,1,0,1,
+ 0,1,1,1,1,1,1,3,1,188,8,1,1,2,4,2,191,8,2,11,2,12,2,192,1,2,1,2,
+ 1,3,1,3,1,3,1,3,1,3,1,4,1,4,5,4,204,8,4,10,4,12,4,207,9,4,1,4,1,
+ 4,1,5,1,5,1,5,3,5,214,8,5,1,5,1,5,1,5,3,5,219,8,5,3,5,221,8,5,1,
+ 5,1,5,1,6,1,6,1,6,1,6,1,6,1,6,1,6,1,6,1,7,1,7,1,7,1,7,1,7,1,8,1,
+ 8,1,8,1,8,1,8,1,8,1,8,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,10,1,10,
+ 1,10,1,10,1,10,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1,12,
+ 1,12,1,12,1,12,1,12,1,12,1,12,1,13,1,13,1,13,1,13,1,13,1,13,1,13,
+ 1,14,1,14,1,14,1,15,1,15,1,15,1,15,1,15,1,16,1,16,1,16,1,16,1,16,
+ 1,17,1,17,1,17,1,17,1,18,1,18,1,18,1,18,1,18,1,18,1,19,1,19,1,19,
+ 1,20,1,20,1,20,1,20,1,20,1,21,1,21,1,21,1,21,1,22,1,22,1,22,1,22,
+ 1,23,1,23,1,23,1,24,1,24,1,24,1,24,1,25,1,25,1,25,1,25,1,25,1,25,
+ 1,25,1,25,1,25,1,25,1,25,1,26,1,26,1,26,1,26,1,26,1,26,1,26,1,27,
+ 1,27,1,27,1,27,1,27,1,27,1,28,1,28,1,28,1,28,1,28,1,28,1,29,1,29,
+ 1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,30,1,30,1,30,1,30,
+ 1,30,1,30,1,30,1,30,1,30,1,30,1,31,1,31,1,31,1,31,1,31,1,31,1,31,
+ 1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,33,1,33,1,33,
+ 1,33,1,33,1,33,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,35,1,35,1,35,
+ 1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,36,1,36,1,36,1,36,1,36,
+ 1,36,1,36,1,36,1,36,1,36,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,
+ 1,37,1,37,1,37,1,37,1,38,1,38,1,38,1,38,1,38,1,38,1,39,1,39,1,39,
+ 1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,40,1,40,1,40,
+ 1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,41,
+ 1,41,1,42,1,42,1,42,1,42,1,43,1,43,1,44,1,44,1,45,1,45,1,46,1,46,
+ 1,47,1,47,1,48,1,48,1,49,1,49,1,50,1,50,1,51,1,51,1,51,1,52,1,52,
+ 1,53,1,53,1,53,1,54,1,54,1,54,1,55,1,55,1,55,1,56,1,56,1,56,1,57,
+ 1,57,1,58,1,58,1,59,1,59,1,59,1,60,1,60,1,60,1,61,1,61,1,61,1,62,
+ 1,62,1,62,1,63,1,63,1,63,1,64,1,64,1,64,1,65,1,65,1,65,1,66,1,66,
+ 1,66,1,67,1,67,1,67,1,68,1,68,1,69,1,69,1,70,1,70,1,71,1,71,1,72,
+ 1,72,1,72,1,73,1,73,1,74,1,74,1,75,1,75,1,76,1,76,1,77,1,77,1,77,
+ 1,78,1,78,1,79,1,79,1,80,1,80,1,81,1,81,1,81,1,81,1,81,1,81,1,81,
+ 1,81,1,81,1,81,1,81,1,81,1,81,1,81,1,81,1,81,1,81,1,81,3,81,591,
+ 8,81,1,82,1,82,1,82,4,82,596,8,82,11,82,12,82,597,1,82,3,82,601,
+ 8,82,1,82,3,82,604,8,82,1,82,3,82,607,8,82,1,82,5,82,610,8,82,10,
+ 82,12,82,613,9,82,1,82,1,82,1,83,3,83,618,8,83,1,83,5,83,621,8,83,
+ 10,83,12,83,624,9,83,1,84,4,84,627,8,84,11,84,12,84,628,1,85,1,85,
+ 3,85,633,8,85,1,86,3,86,636,8,86,1,86,1,86,1,86,1,86,1,86,1,86,3,
+ 86,644,8,86,1,87,1,87,3,87,648,8,87,1,87,1,87,1,87,1,88,1,88,3,88,
+ 655,8,88,1,88,1,88,0,0,89,1,0,3,3,5,4,7,5,9,6,11,7,13,8,15,9,17,
+ 10,19,11,21,12,23,13,25,14,27,15,29,16,31,17,33,18,35,19,37,20,39,
+ 21,41,22,43,23,45,24,47,25,49,26,51,27,53,28,55,29,57,30,59,31,61,
+ 32,63,33,65,34,67,35,69,36,71,37,73,38,75,39,77,40,79,41,81,42,83,
+ 43,85,44,87,45,89,46,91,47,93,48,95,49,97,50,99,51,101,52,103,53,
+ 105,54,107,55,109,56,111,57,113,58,115,59,117,60,119,61,121,62,123,
+ 63,125,64,127,65,129,66,131,67,133,68,135,69,137,70,139,71,141,72,
+ 143,73,145,74,147,75,149,76,151,77,153,78,155,79,157,80,159,81,161,
+ 82,163,83,165,84,167,85,169,86,171,87,173,0,175,0,177,0,1,0,7,2,
+ 0,9,9,32,32,2,0,10,10,13,13,4,0,10,10,13,13,34,34,92,92,4,0,36,36,
+ 65,90,95,95,97,122,5,0,36,36,48,57,65,90,95,95,97,122,1,0,48,57,
+ 2,0,69,69,101,101,677,0,3,1,0,0,0,0,5,1,0,0,0,0,7,1,0,0,0,0,9,1,
+ 0,0,0,0,11,1,0,0,0,0,13,1,0,0,0,0,15,1,0,0,0,0,17,1,0,0,0,0,19,1,
+ 0,0,0,0,21,1,0,0,0,0,23,1,0,0,0,0,25,1,0,0,0,0,27,1,0,0,0,0,29,1,
+ 0,0,0,0,31,1,0,0,0,0,33,1,0,0,0,0,35,1,0,0,0,0,37,1,0,0,0,0,39,1,
+ 0,0,0,0,41,1,0,0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,47,1,0,0,0,0,49,1,
+ 0,0,0,0,51,1,0,0,0,0,53,1,0,0,0,0,55,1,0,0,0,0,57,1,0,0,0,0,59,1,
+ 0,0,0,0,61,1,0,0,0,0,63,1,0,0,0,0,65,1,0,0,0,0,67,1,0,0,0,0,69,1,
+ 0,0,0,0,71,1,0,0,0,0,73,1,0,0,0,0,75,1,0,0,0,0,77,1,0,0,0,0,79,1,
+ 0,0,0,0,81,1,0,0,0,0,83,1,0,0,0,0,85,1,0,0,0,0,87,1,0,0,0,0,89,1,
+ 0,0,0,0,91,1,0,0,0,0,93,1,0,0,0,0,95,1,0,0,0,0,97,1,0,0,0,0,99,1,
+ 0,0,0,0,101,1,0,0,0,0,103,1,0,0,0,0,105,1,0,0,0,0,107,1,0,0,0,0,
+ 109,1,0,0,0,0,111,1,0,0,0,0,113,1,0,0,0,0,115,1,0,0,0,0,117,1,0,
+ 0,0,0,119,1,0,0,0,0,121,1,0,0,0,0,123,1,0,0,0,0,125,1,0,0,0,0,127,
+ 1,0,0,0,0,129,1,0,0,0,0,131,1,0,0,0,0,133,1,0,0,0,0,135,1,0,0,0,
+ 0,137,1,0,0,0,0,139,1,0,0,0,0,141,1,0,0,0,0,143,1,0,0,0,0,145,1,
+ 0,0,0,0,147,1,0,0,0,0,149,1,0,0,0,0,151,1,0,0,0,0,153,1,0,0,0,0,
+ 155,1,0,0,0,0,157,1,0,0,0,0,159,1,0,0,0,0,161,1,0,0,0,0,163,1,0,
+ 0,0,0,165,1,0,0,0,0,167,1,0,0,0,0,169,1,0,0,0,0,171,1,0,0,0,1,180,
+ 1,0,0,0,3,184,1,0,0,0,5,190,1,0,0,0,7,196,1,0,0,0,9,201,1,0,0,0,
+ 11,220,1,0,0,0,13,224,1,0,0,0,15,232,1,0,0,0,17,237,1,0,0,0,19,244,
+ 1,0,0,0,21,252,1,0,0,0,23,257,1,0,0,0,25,266,1,0,0,0,27,273,1,0,
+ 0,0,29,280,1,0,0,0,31,283,1,0,0,0,33,288,1,0,0,0,35,293,1,0,0,0,
+ 37,297,1,0,0,0,39,303,1,0,0,0,41,306,1,0,0,0,43,311,1,0,0,0,45,315,
+ 1,0,0,0,47,319,1,0,0,0,49,322,1,0,0,0,51,326,1,0,0,0,53,337,1,0,
+ 0,0,55,344,1,0,0,0,57,350,1,0,0,0,59,356,1,0,0,0,61,367,1,0,0,0,
+ 63,377,1,0,0,0,65,384,1,0,0,0,67,394,1,0,0,0,69,400,1,0,0,0,71,407,
+ 1,0,0,0,73,418,1,0,0,0,75,428,1,0,0,0,77,440,1,0,0,0,79,446,1,0,
+ 0,0,81,459,1,0,0,0,83,474,1,0,0,0,85,476,1,0,0,0,87,480,1,0,0,0,
+ 89,482,1,0,0,0,91,484,1,0,0,0,93,486,1,0,0,0,95,488,1,0,0,0,97,490,
+ 1,0,0,0,99,492,1,0,0,0,101,494,1,0,0,0,103,496,1,0,0,0,105,499,1,
+ 0,0,0,107,501,1,0,0,0,109,504,1,0,0,0,111,507,1,0,0,0,113,510,1,
+ 0,0,0,115,513,1,0,0,0,117,515,1,0,0,0,119,517,1,0,0,0,121,520,1,
+ 0,0,0,123,523,1,0,0,0,125,526,1,0,0,0,127,529,1,0,0,0,129,532,1,
+ 0,0,0,131,535,1,0,0,0,133,538,1,0,0,0,135,541,1,0,0,0,137,544,1,
+ 0,0,0,139,546,1,0,0,0,141,548,1,0,0,0,143,550,1,0,0,0,145,552,1,
+ 0,0,0,147,555,1,0,0,0,149,557,1,0,0,0,151,559,1,0,0,0,153,561,1,
+ 0,0,0,155,563,1,0,0,0,157,566,1,0,0,0,159,568,1,0,0,0,161,570,1,
+ 0,0,0,163,590,1,0,0,0,165,592,1,0,0,0,167,617,1,0,0,0,169,626,1,
+ 0,0,0,171,632,1,0,0,0,173,643,1,0,0,0,175,647,1,0,0,0,177,654,1,
+ 0,0,0,179,181,5,13,0,0,180,179,1,0,0,0,180,181,1,0,0,0,181,182,1,
+ 0,0,0,182,183,5,10,0,0,183,2,1,0,0,0,184,185,3,137,68,0,185,187,
+ 3,1,0,0,186,188,3,5,2,0,187,186,1,0,0,0,187,188,1,0,0,0,188,4,1,
+ 0,0,0,189,191,7,0,0,0,190,189,1,0,0,0,191,192,1,0,0,0,192,190,1,
+ 0,0,0,192,193,1,0,0,0,193,194,1,0,0,0,194,195,6,2,0,0,195,6,1,0,
+ 0,0,196,197,5,92,0,0,197,198,3,1,0,0,198,199,1,0,0,0,199,200,6,3,
+ 0,0,200,8,1,0,0,0,201,205,5,35,0,0,202,204,8,1,0,0,203,202,1,0,0,
+ 0,204,207,1,0,0,0,205,203,1,0,0,0,205,206,1,0,0,0,206,208,1,0,0,
+ 0,207,205,1,0,0,0,208,209,6,4,1,0,209,10,1,0,0,0,210,211,4,5,0,0,
+ 211,221,3,5,2,0,212,214,5,13,0,0,213,212,1,0,0,0,213,214,1,0,0,0,
+ 214,215,1,0,0,0,215,216,5,10,0,0,216,218,1,0,0,0,217,219,3,5,2,0,
+ 218,217,1,0,0,0,218,219,1,0,0,0,219,221,1,0,0,0,220,210,1,0,0,0,
+ 220,213,1,0,0,0,221,222,1,0,0,0,222,223,6,5,2,0,223,12,1,0,0,0,224,
+ 225,5,105,0,0,225,226,5,110,0,0,226,227,5,116,0,0,227,228,5,101,
+ 0,0,228,229,5,103,0,0,229,230,5,101,0,0,230,231,5,114,0,0,231,14,
+ 1,0,0,0,232,233,5,114,0,0,233,234,5,101,0,0,234,235,5,97,0,0,235,
+ 236,5,108,0,0,236,16,1,0,0,0,237,238,5,115,0,0,238,239,5,116,0,0,
+ 239,240,5,114,0,0,240,241,5,105,0,0,241,242,5,110,0,0,242,243,5,
+ 103,0,0,243,18,1,0,0,0,244,245,5,98,0,0,245,246,5,111,0,0,246,247,
+ 5,111,0,0,247,248,5,108,0,0,248,249,5,101,0,0,249,250,5,97,0,0,250,
+ 251,5,110,0,0,251,20,1,0,0,0,252,253,5,118,0,0,253,254,5,111,0,0,
+ 254,255,5,105,0,0,255,256,5,100,0,0,256,22,1,0,0,0,257,258,5,102,
+ 0,0,258,259,5,117,0,0,259,260,5,110,0,0,260,261,5,99,0,0,261,262,
+ 5,116,0,0,262,263,5,105,0,0,263,264,5,111,0,0,264,265,5,110,0,0,
+ 265,24,1,0,0,0,266,267,5,105,0,0,267,268,5,110,0,0,268,269,5,108,
+ 0,0,269,270,5,105,0,0,270,271,5,110,0,0,271,272,5,101,0,0,272,26,
+ 1,0,0,0,273,274,5,114,0,0,274,275,5,101,0,0,275,276,5,116,0,0,276,
+ 277,5,117,0,0,277,278,5,114,0,0,278,279,5,110,0,0,279,28,1,0,0,0,
+ 280,281,5,105,0,0,281,282,5,102,0,0,282,30,1,0,0,0,283,284,5,101,
+ 0,0,284,285,5,108,0,0,285,286,5,105,0,0,286,287,5,102,0,0,287,32,
+ 1,0,0,0,288,289,5,101,0,0,289,290,5,108,0,0,290,291,5,115,0,0,291,
+ 292,5,101,0,0,292,34,1,0,0,0,293,294,5,102,0,0,294,295,5,111,0,0,
+ 295,296,5,114,0,0,296,36,1,0,0,0,297,298,5,119,0,0,298,299,5,104,
+ 0,0,299,300,5,105,0,0,300,301,5,108,0,0,301,302,5,101,0,0,302,38,
+ 1,0,0,0,303,304,5,105,0,0,304,305,5,110,0,0,305,40,1,0,0,0,306,307,
+ 5,115,0,0,307,308,5,116,0,0,308,309,5,101,0,0,309,310,5,112,0,0,
+ 310,42,1,0,0,0,311,312,5,105,0,0,312,313,5,110,0,0,313,314,5,102,
+ 0,0,314,44,1,0,0,0,315,316,5,97,0,0,316,317,5,110,0,0,317,318,5,
+ 100,0,0,318,46,1,0,0,0,319,320,5,111,0,0,320,321,5,114,0,0,321,48,
+ 1,0,0,0,322,323,5,110,0,0,323,324,5,111,0,0,324,325,5,116,0,0,325,
+ 50,1,0,0,0,326,327,5,114,0,0,327,328,5,101,0,0,328,329,5,99,0,0,
+ 329,330,5,111,0,0,330,331,5,114,0,0,331,332,5,100,0,0,332,333,5,
+ 97,0,0,333,334,5,98,0,0,334,335,5,108,0,0,335,336,5,101,0,0,336,
+ 52,1,0,0,0,337,338,5,107,0,0,338,339,5,101,0,0,339,340,5,114,0,0,
+ 340,341,5,110,0,0,341,342,5,101,0,0,342,343,5,108,0,0,343,54,1,0,
+ 0,0,344,345,5,109,0,0,345,346,5,111,0,0,346,347,5,100,0,0,347,348,
+ 5,101,0,0,348,349,5,108,0,0,349,56,1,0,0,0,350,351,5,115,0,0,351,
+ 352,5,116,0,0,352,353,5,97,0,0,353,354,5,116,0,0,354,355,5,101,0,
+ 0,355,58,1,0,0,0,356,357,5,112,0,0,357,358,5,97,0,0,358,359,5,114,
+ 0,0,359,360,5,97,0,0,360,361,5,109,0,0,361,362,5,101,0,0,362,363,
+ 5,116,0,0,363,364,5,101,0,0,364,365,5,114,0,0,365,366,5,115,0,0,
+ 366,60,1,0,0,0,367,368,5,105,0,0,368,369,5,110,0,0,369,370,5,116,
+ 0,0,370,371,5,101,0,0,371,372,5,114,0,0,372,373,5,110,0,0,373,374,
+ 5,97,0,0,374,375,5,108,0,0,375,376,5,115,0,0,376,62,1,0,0,0,377,
+ 378,5,117,0,0,378,379,5,112,0,0,379,380,5,100,0,0,380,381,5,97,0,
+ 0,381,382,5,116,0,0,382,383,5,101,0,0,383,64,1,0,0,0,384,385,5,101,
+ 0,0,385,386,5,113,0,0,386,387,5,117,0,0,387,388,5,97,0,0,388,389,
+ 5,116,0,0,389,390,5,105,0,0,390,391,5,111,0,0,391,392,5,110,0,0,
+ 392,393,5,115,0,0,393,66,1,0,0,0,394,395,5,105,0,0,395,396,5,110,
+ 0,0,396,397,5,112,0,0,397,398,5,117,0,0,398,399,5,116,0,0,399,68,
+ 1,0,0,0,400,401,5,111,0,0,401,402,5,117,0,0,402,403,5,116,0,0,403,
+ 404,5,112,0,0,404,405,5,117,0,0,405,406,5,116,0,0,406,70,1,0,0,0,
+ 407,408,5,99,0,0,408,409,5,111,0,0,409,410,5,110,0,0,410,411,5,116,
+ 0,0,411,412,5,105,0,0,412,413,5,110,0,0,413,414,5,117,0,0,414,415,
+ 5,111,0,0,415,416,5,117,0,0,416,417,5,115,0,0,417,72,1,0,0,0,418,
+ 419,5,111,0,0,419,420,5,110,0,0,420,421,5,82,0,0,421,422,5,101,0,
+ 0,422,423,5,99,0,0,423,424,5,101,0,0,424,425,5,105,0,0,425,426,5,
+ 118,0,0,426,427,5,101,0,0,427,74,1,0,0,0,428,429,5,111,0,0,429,430,
+ 5,110,0,0,430,431,5,67,0,0,431,432,5,111,0,0,432,433,5,110,0,0,433,
+ 434,5,100,0,0,434,435,5,105,0,0,435,436,5,116,0,0,436,437,5,105,
+ 0,0,437,438,5,111,0,0,438,439,5,110,0,0,439,76,1,0,0,0,440,441,5,
+ 115,0,0,441,442,5,112,0,0,442,443,5,105,0,0,443,444,5,107,0,0,444,
+ 445,5,101,0,0,445,78,1,0,0,0,446,447,5,64,0,0,447,448,5,104,0,0,
+ 448,449,5,111,0,0,449,450,5,109,0,0,450,451,5,111,0,0,451,452,5,
+ 103,0,0,452,453,5,101,0,0,453,454,5,110,0,0,454,455,5,101,0,0,455,
+ 456,5,111,0,0,456,457,5,117,0,0,457,458,5,115,0,0,458,80,1,0,0,0,
+ 459,460,5,64,0,0,460,461,5,104,0,0,461,462,5,101,0,0,462,463,5,116,
+ 0,0,463,464,5,101,0,0,464,465,5,114,0,0,465,466,5,111,0,0,466,467,
+ 5,103,0,0,467,468,5,101,0,0,468,469,5,110,0,0,469,470,5,101,0,0,
+ 470,471,5,111,0,0,471,472,5,117,0,0,472,473,5,115,0,0,473,82,1,0,
+ 0,0,474,475,5,64,0,0,475,84,1,0,0,0,476,477,5,46,0,0,477,478,5,46,
+ 0,0,478,479,5,46,0,0,479,86,1,0,0,0,480,481,5,40,0,0,481,88,1,0,
+ 0,0,482,483,5,41,0,0,483,90,1,0,0,0,484,485,5,43,0,0,485,92,1,0,
+ 0,0,486,487,5,126,0,0,487,94,1,0,0,0,488,489,5,124,0,0,489,96,1,
+ 0,0,0,490,491,5,94,0,0,491,98,1,0,0,0,492,493,5,38,0,0,493,100,1,
+ 0,0,0,494,495,5,91,0,0,495,102,1,0,0,0,496,497,5,60,0,0,497,498,
+ 5,45,0,0,498,104,1,0,0,0,499,500,5,93,0,0,500,106,1,0,0,0,501,502,
+ 5,91,0,0,502,503,5,91,0,0,503,108,1,0,0,0,504,505,5,93,0,0,505,506,
+ 5,93,0,0,506,110,1,0,0,0,507,508,5,60,0,0,508,509,5,60,0,0,509,112,
+ 1,0,0,0,510,511,5,62,0,0,511,512,5,62,0,0,512,114,1,0,0,0,513,514,
+ 5,60,0,0,514,116,1,0,0,0,515,516,5,62,0,0,516,118,1,0,0,0,517,518,
+ 5,60,0,0,518,519,5,61,0,0,519,120,1,0,0,0,520,521,5,43,0,0,521,522,
+ 5,61,0,0,522,122,1,0,0,0,523,524,5,45,0,0,524,525,5,61,0,0,525,124,
+ 1,0,0,0,526,527,5,42,0,0,527,528,5,61,0,0,528,126,1,0,0,0,529,530,
+ 5,47,0,0,530,531,5,61,0,0,531,128,1,0,0,0,532,533,5,61,0,0,533,534,
+ 5,61,0,0,534,130,1,0,0,0,535,536,5,33,0,0,536,537,5,61,0,0,537,132,
+ 1,0,0,0,538,539,5,60,0,0,539,540,5,62,0,0,540,134,1,0,0,0,541,542,
+ 5,62,0,0,542,543,5,61,0,0,543,136,1,0,0,0,544,545,5,44,0,0,545,138,
+ 1,0,0,0,546,547,5,45,0,0,547,140,1,0,0,0,548,549,5,61,0,0,549,142,
+ 1,0,0,0,550,551,5,42,0,0,551,144,1,0,0,0,552,553,5,42,0,0,553,554,
+ 5,42,0,0,554,146,1,0,0,0,555,556,5,47,0,0,556,148,1,0,0,0,557,558,
+ 5,37,0,0,558,150,1,0,0,0,559,560,5,63,0,0,560,152,1,0,0,0,561,562,
+ 5,58,0,0,562,154,1,0,0,0,563,564,5,58,0,0,564,565,5,58,0,0,565,156,
+ 1,0,0,0,566,567,5,59,0,0,567,158,1,0,0,0,568,569,5,39,0,0,569,160,
+ 1,0,0,0,570,571,5,46,0,0,571,162,1,0,0,0,572,573,5,116,0,0,573,574,
+ 5,114,0,0,574,575,5,117,0,0,575,591,5,101,0,0,576,577,5,84,0,0,577,
+ 578,5,114,0,0,578,579,5,117,0,0,579,591,5,101,0,0,580,581,5,102,
+ 0,0,581,582,5,97,0,0,582,583,5,108,0,0,583,584,5,115,0,0,584,591,
+ 5,101,0,0,585,586,5,70,0,0,586,587,5,97,0,0,587,588,5,108,0,0,588,
+ 589,5,115,0,0,589,591,5,101,0,0,590,572,1,0,0,0,590,576,1,0,0,0,
+ 590,580,1,0,0,0,590,585,1,0,0,0,591,164,1,0,0,0,592,611,5,34,0,0,
+ 593,606,5,92,0,0,594,596,7,0,0,0,595,594,1,0,0,0,596,597,1,0,0,0,
+ 597,595,1,0,0,0,597,598,1,0,0,0,598,603,1,0,0,0,599,601,5,13,0,0,
+ 600,599,1,0,0,0,600,601,1,0,0,0,601,602,1,0,0,0,602,604,5,10,0,0,
+ 603,600,1,0,0,0,603,604,1,0,0,0,604,607,1,0,0,0,605,607,9,0,0,0,
+ 606,595,1,0,0,0,606,605,1,0,0,0,607,610,1,0,0,0,608,610,8,2,0,0,
+ 609,593,1,0,0,0,609,608,1,0,0,0,610,613,1,0,0,0,611,609,1,0,0,0,
+ 611,612,1,0,0,0,612,614,1,0,0,0,613,611,1,0,0,0,614,615,5,34,0,0,
+ 615,166,1,0,0,0,616,618,7,3,0,0,617,616,1,0,0,0,618,622,1,0,0,0,
+ 619,621,7,4,0,0,620,619,1,0,0,0,621,624,1,0,0,0,622,620,1,0,0,0,
+ 622,623,1,0,0,0,623,168,1,0,0,0,624,622,1,0,0,0,625,627,7,5,0,0,
+ 626,625,1,0,0,0,627,628,1,0,0,0,628,626,1,0,0,0,628,629,1,0,0,0,
+ 629,170,1,0,0,0,630,633,3,173,86,0,631,633,3,175,87,0,632,630,1,
+ 0,0,0,632,631,1,0,0,0,633,172,1,0,0,0,634,636,3,169,84,0,635,634,
+ 1,0,0,0,635,636,1,0,0,0,636,637,1,0,0,0,637,638,3,161,80,0,638,639,
+ 3,169,84,0,639,644,1,0,0,0,640,641,3,169,84,0,641,642,3,161,80,0,
+ 642,644,1,0,0,0,643,635,1,0,0,0,643,640,1,0,0,0,644,174,1,0,0,0,
+ 645,648,3,169,84,0,646,648,3,173,86,0,647,645,1,0,0,0,647,646,1,
+ 0,0,0,648,649,1,0,0,0,649,650,7,6,0,0,650,651,3,177,88,0,651,176,
+ 1,0,0,0,652,655,3,91,45,0,653,655,3,139,69,0,654,652,1,0,0,0,654,
+ 653,1,0,0,0,654,655,1,0,0,0,655,656,1,0,0,0,656,657,3,169,84,0,657,
+ 178,1,0,0,0,24,0,180,187,192,205,213,218,220,590,597,600,603,606,
+ 609,611,617,620,622,628,632,635,643,647,654,3,0,1,0,0,2,0,1,5,0
]
class PyNestMLLexer(PyNestMLLexerBase):
@@ -309,55 +299,53 @@ class PyNestMLLexer(PyNestMLLexerBase):
ON_RECEIVE_KEYWORD = 38
ON_CONDITION_KEYWORD = 39
SPIKE_KEYWORD = 40
- INHIBITORY_KEYWORD = 41
- EXCITATORY_KEYWORD = 42
- DECORATOR_HOMOGENEOUS = 43
- DECORATOR_HETEROGENEOUS = 44
- AT = 45
- ELLIPSIS = 46
- LEFT_PAREN = 47
- RIGHT_PAREN = 48
- PLUS = 49
- TILDE = 50
- PIPE = 51
- CARET = 52
- AMPERSAND = 53
- LEFT_SQUARE_BRACKET = 54
- LEFT_ANGLE_MINUS = 55
- RIGHT_SQUARE_BRACKET = 56
- LEFT_LEFT_SQUARE = 57
- RIGHT_RIGHT_SQUARE = 58
- LEFT_LEFT_ANGLE = 59
- RIGHT_RIGHT_ANGLE = 60
- LEFT_ANGLE = 61
- RIGHT_ANGLE = 62
- LEFT_ANGLE_EQUALS = 63
- PLUS_EQUALS = 64
- MINUS_EQUALS = 65
- STAR_EQUALS = 66
- FORWARD_SLASH_EQUALS = 67
- EQUALS_EQUALS = 68
- EXCLAMATION_EQUALS = 69
- LEFT_ANGLE_RIGHT_ANGLE = 70
- RIGHT_ANGLE_EQUALS = 71
- COMMA = 72
- MINUS = 73
- EQUALS = 74
- STAR = 75
- STAR_STAR = 76
- FORWARD_SLASH = 77
- PERCENT = 78
- QUESTION = 79
- COLON = 80
- DOUBLE_COLON = 81
- SEMICOLON = 82
- DIFFERENTIAL_ORDER = 83
- FULLSTOP = 84
- BOOLEAN_LITERAL = 85
- STRING_LITERAL = 86
- NAME = 87
- UNSIGNED_INTEGER = 88
- FLOAT = 89
+ DECORATOR_HOMOGENEOUS = 41
+ DECORATOR_HETEROGENEOUS = 42
+ AT = 43
+ ELLIPSIS = 44
+ LEFT_PAREN = 45
+ RIGHT_PAREN = 46
+ PLUS = 47
+ TILDE = 48
+ PIPE = 49
+ CARET = 50
+ AMPERSAND = 51
+ LEFT_SQUARE_BRACKET = 52
+ LEFT_ANGLE_MINUS = 53
+ RIGHT_SQUARE_BRACKET = 54
+ LEFT_LEFT_SQUARE = 55
+ RIGHT_RIGHT_SQUARE = 56
+ LEFT_LEFT_ANGLE = 57
+ RIGHT_RIGHT_ANGLE = 58
+ LEFT_ANGLE = 59
+ RIGHT_ANGLE = 60
+ LEFT_ANGLE_EQUALS = 61
+ PLUS_EQUALS = 62
+ MINUS_EQUALS = 63
+ STAR_EQUALS = 64
+ FORWARD_SLASH_EQUALS = 65
+ EQUALS_EQUALS = 66
+ EXCLAMATION_EQUALS = 67
+ LEFT_ANGLE_RIGHT_ANGLE = 68
+ RIGHT_ANGLE_EQUALS = 69
+ COMMA = 70
+ MINUS = 71
+ EQUALS = 72
+ STAR = 73
+ STAR_STAR = 74
+ FORWARD_SLASH = 75
+ PERCENT = 76
+ QUESTION = 77
+ COLON = 78
+ DOUBLE_COLON = 79
+ SEMICOLON = 80
+ DIFFERENTIAL_ORDER = 81
+ FULLSTOP = 82
+ BOOLEAN_LITERAL = 83
+ STRING_LITERAL = 84
+ NAME = 85
+ UNSIGNED_INTEGER = 86
+ FLOAT = 87
channelNames = [ u"DEFAULT_TOKEN_CHANNEL", u"HIDDEN", u"COMMENT" ]
@@ -369,13 +357,12 @@ class PyNestMLLexer(PyNestMLLexerBase):
"'while'", "'in'", "'step'", "'inf'", "'and'", "'or'", "'not'",
"'recordable'", "'kernel'", "'model'", "'state'", "'parameters'",
"'internals'", "'update'", "'equations'", "'input'", "'output'",
- "'continuous'", "'onReceive'", "'onCondition'", "'spike'", "'inhibitory'",
- "'excitatory'", "'@homogeneous'", "'@heterogeneous'", "'@'",
- "'...'", "'('", "')'", "'+'", "'~'", "'|'", "'^'", "'&'", "'['",
- "'<-'", "']'", "'[['", "']]'", "'<<'", "'>>'", "'<'", "'>'",
- "'<='", "'+='", "'-='", "'*='", "'/='", "'=='", "'!='", "'<>'",
- "'>='", "','", "'-'", "'='", "'*'", "'**'", "'/'", "'%'", "'?'",
- "':'", "'::'", "';'", "'''", "'.'" ]
+ "'continuous'", "'onReceive'", "'onCondition'", "'spike'", "'@homogeneous'",
+ "'@heterogeneous'", "'@'", "'...'", "'('", "')'", "'+'", "'~'",
+ "'|'", "'^'", "'&'", "'['", "'<-'", "']'", "'[['", "']]'", "'<<'",
+ "'>>'", "'<'", "'>'", "'<='", "'+='", "'-='", "'*='", "'/='",
+ "'=='", "'!='", "'<>'", "'>='", "','", "'-'", "'='", "'*'",
+ "'**'", "'/'", "'%'", "'?'", "':'", "'::'", "';'", "'''", "'.'" ]
symbolicNames = [ "",
"INDENT", "DEDENT", "KERNEL_JOINING", "WS", "LINE_ESCAPE", "SL_COMMENT",
@@ -387,19 +374,18 @@ class PyNestMLLexer(PyNestMLLexerBase):
"KERNEL_KEYWORD", "MODEL_KEYWORD", "STATE_KEYWORD", "PARAMETERS_KEYWORD",
"INTERNALS_KEYWORD", "UPDATE_KEYWORD", "EQUATIONS_KEYWORD",
"INPUT_KEYWORD", "OUTPUT_KEYWORD", "CONTINUOUS_KEYWORD", "ON_RECEIVE_KEYWORD",
- "ON_CONDITION_KEYWORD", "SPIKE_KEYWORD", "INHIBITORY_KEYWORD",
- "EXCITATORY_KEYWORD", "DECORATOR_HOMOGENEOUS", "DECORATOR_HETEROGENEOUS",
- "AT", "ELLIPSIS", "LEFT_PAREN", "RIGHT_PAREN", "PLUS", "TILDE",
- "PIPE", "CARET", "AMPERSAND", "LEFT_SQUARE_BRACKET", "LEFT_ANGLE_MINUS",
- "RIGHT_SQUARE_BRACKET", "LEFT_LEFT_SQUARE", "RIGHT_RIGHT_SQUARE",
- "LEFT_LEFT_ANGLE", "RIGHT_RIGHT_ANGLE", "LEFT_ANGLE", "RIGHT_ANGLE",
- "LEFT_ANGLE_EQUALS", "PLUS_EQUALS", "MINUS_EQUALS", "STAR_EQUALS",
- "FORWARD_SLASH_EQUALS", "EQUALS_EQUALS", "EXCLAMATION_EQUALS",
- "LEFT_ANGLE_RIGHT_ANGLE", "RIGHT_ANGLE_EQUALS", "COMMA", "MINUS",
- "EQUALS", "STAR", "STAR_STAR", "FORWARD_SLASH", "PERCENT", "QUESTION",
- "COLON", "DOUBLE_COLON", "SEMICOLON", "DIFFERENTIAL_ORDER",
- "FULLSTOP", "BOOLEAN_LITERAL", "STRING_LITERAL", "NAME", "UNSIGNED_INTEGER",
- "FLOAT" ]
+ "ON_CONDITION_KEYWORD", "SPIKE_KEYWORD", "DECORATOR_HOMOGENEOUS",
+ "DECORATOR_HETEROGENEOUS", "AT", "ELLIPSIS", "LEFT_PAREN", "RIGHT_PAREN",
+ "PLUS", "TILDE", "PIPE", "CARET", "AMPERSAND", "LEFT_SQUARE_BRACKET",
+ "LEFT_ANGLE_MINUS", "RIGHT_SQUARE_BRACKET", "LEFT_LEFT_SQUARE",
+ "RIGHT_RIGHT_SQUARE", "LEFT_LEFT_ANGLE", "RIGHT_RIGHT_ANGLE",
+ "LEFT_ANGLE", "RIGHT_ANGLE", "LEFT_ANGLE_EQUALS", "PLUS_EQUALS",
+ "MINUS_EQUALS", "STAR_EQUALS", "FORWARD_SLASH_EQUALS", "EQUALS_EQUALS",
+ "EXCLAMATION_EQUALS", "LEFT_ANGLE_RIGHT_ANGLE", "RIGHT_ANGLE_EQUALS",
+ "COMMA", "MINUS", "EQUALS", "STAR", "STAR_STAR", "FORWARD_SLASH",
+ "PERCENT", "QUESTION", "COLON", "DOUBLE_COLON", "SEMICOLON",
+ "DIFFERENTIAL_ORDER", "FULLSTOP", "BOOLEAN_LITERAL", "STRING_LITERAL",
+ "NAME", "UNSIGNED_INTEGER", "FLOAT" ]
ruleNames = [ "NEWLINE_FRAG", "KERNEL_JOINING", "WS", "LINE_ESCAPE",
"SL_COMMENT", "NEWLINE", "INTEGER_KEYWORD", "REAL_KEYWORD",
@@ -412,25 +398,25 @@ class PyNestMLLexer(PyNestMLLexerBase):
"INTERNALS_KEYWORD", "UPDATE_KEYWORD", "EQUATIONS_KEYWORD",
"INPUT_KEYWORD", "OUTPUT_KEYWORD", "CONTINUOUS_KEYWORD",
"ON_RECEIVE_KEYWORD", "ON_CONDITION_KEYWORD", "SPIKE_KEYWORD",
- "INHIBITORY_KEYWORD", "EXCITATORY_KEYWORD", "DECORATOR_HOMOGENEOUS",
- "DECORATOR_HETEROGENEOUS", "AT", "ELLIPSIS", "LEFT_PAREN",
- "RIGHT_PAREN", "PLUS", "TILDE", "PIPE", "CARET", "AMPERSAND",
- "LEFT_SQUARE_BRACKET", "LEFT_ANGLE_MINUS", "RIGHT_SQUARE_BRACKET",
- "LEFT_LEFT_SQUARE", "RIGHT_RIGHT_SQUARE", "LEFT_LEFT_ANGLE",
- "RIGHT_RIGHT_ANGLE", "LEFT_ANGLE", "RIGHT_ANGLE", "LEFT_ANGLE_EQUALS",
- "PLUS_EQUALS", "MINUS_EQUALS", "STAR_EQUALS", "FORWARD_SLASH_EQUALS",
- "EQUALS_EQUALS", "EXCLAMATION_EQUALS", "LEFT_ANGLE_RIGHT_ANGLE",
- "RIGHT_ANGLE_EQUALS", "COMMA", "MINUS", "EQUALS", "STAR",
- "STAR_STAR", "FORWARD_SLASH", "PERCENT", "QUESTION", "COLON",
- "DOUBLE_COLON", "SEMICOLON", "DIFFERENTIAL_ORDER", "FULLSTOP",
- "BOOLEAN_LITERAL", "STRING_LITERAL", "NAME", "UNSIGNED_INTEGER",
- "FLOAT", "POINT_FLOAT", "EXPONENT_FLOAT", "EXPONENT" ]
+ "DECORATOR_HOMOGENEOUS", "DECORATOR_HETEROGENEOUS", "AT",
+ "ELLIPSIS", "LEFT_PAREN", "RIGHT_PAREN", "PLUS", "TILDE",
+ "PIPE", "CARET", "AMPERSAND", "LEFT_SQUARE_BRACKET", "LEFT_ANGLE_MINUS",
+ "RIGHT_SQUARE_BRACKET", "LEFT_LEFT_SQUARE", "RIGHT_RIGHT_SQUARE",
+ "LEFT_LEFT_ANGLE", "RIGHT_RIGHT_ANGLE", "LEFT_ANGLE",
+ "RIGHT_ANGLE", "LEFT_ANGLE_EQUALS", "PLUS_EQUALS", "MINUS_EQUALS",
+ "STAR_EQUALS", "FORWARD_SLASH_EQUALS", "EQUALS_EQUALS",
+ "EXCLAMATION_EQUALS", "LEFT_ANGLE_RIGHT_ANGLE", "RIGHT_ANGLE_EQUALS",
+ "COMMA", "MINUS", "EQUALS", "STAR", "STAR_STAR", "FORWARD_SLASH",
+ "PERCENT", "QUESTION", "COLON", "DOUBLE_COLON", "SEMICOLON",
+ "DIFFERENTIAL_ORDER", "FULLSTOP", "BOOLEAN_LITERAL", "STRING_LITERAL",
+ "NAME", "UNSIGNED_INTEGER", "FLOAT", "POINT_FLOAT", "EXPONENT_FLOAT",
+ "EXPONENT" ]
grammarFileName = "PyNestMLLexer.g4"
def __init__(self, input=None, output:TextIO = sys.stdout):
super().__init__(input, output)
- self.checkVersion("4.13.2")
+ self.checkVersion("4.13.1")
self._interp = LexerATNSimulator(self, self.atn, self.decisionsToDFA, PredictionContextCache())
self._actions = None
self._predicates = None
diff --git a/pynestml/generated/PyNestMLParser.py b/pynestml/generated/PyNestMLParser.py
index 3d226e3fe..4ce33aed3 100644
--- a/pynestml/generated/PyNestMLParser.py
+++ b/pynestml/generated/PyNestMLParser.py
@@ -1,4 +1,4 @@
-# Generated from PyNestMLParser.g4 by ANTLR 4.13.2
+# Generated from PyNestMLParser.g4 by ANTLR 4.13.1
# encoding: utf-8
from antlr4 import *
from io import StringIO
@@ -10,7 +10,7 @@
def serializedATN():
return [
- 4,1,89,666,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,
+ 4,1,87,676,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,
6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,
2,14,7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7,19,2,20,
7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,2,26,7,26,
@@ -29,234 +29,238 @@ def serializedATN():
3,8,221,8,8,1,9,1,9,1,9,1,9,1,9,3,9,228,8,9,1,10,1,10,1,10,1,10,
1,10,1,10,1,10,3,10,237,8,10,1,11,1,11,3,11,241,8,11,1,12,1,12,1,
12,1,12,1,12,3,12,248,8,12,1,12,5,12,251,8,12,10,12,12,12,254,9,
- 12,1,13,1,13,1,13,1,13,1,13,5,13,261,8,13,10,13,12,13,264,9,13,3,
- 13,266,8,13,1,13,1,13,1,14,3,14,271,8,14,1,14,1,14,1,14,1,14,1,14,
- 1,14,3,14,279,8,14,1,14,5,14,282,8,14,10,14,12,14,285,9,14,1,14,
- 1,14,1,15,1,15,1,15,1,15,3,15,293,8,15,1,15,5,15,296,8,15,10,15,
- 12,15,299,9,15,1,15,1,15,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,
- 1,16,5,16,312,8,16,10,16,12,16,315,9,16,1,16,3,16,318,8,16,1,16,
- 1,16,1,17,1,17,3,17,324,8,17,1,18,1,18,1,18,3,18,329,8,18,1,19,1,
- 19,1,19,1,19,3,19,335,8,19,1,19,1,19,1,20,1,20,1,20,1,20,1,20,1,
- 20,3,20,345,8,20,1,20,1,20,1,21,3,21,350,8,21,1,21,3,21,353,8,21,
- 1,21,1,21,1,21,5,21,358,8,21,10,21,12,21,361,9,21,1,21,1,21,1,21,
- 3,21,366,8,21,1,21,1,21,1,21,1,21,3,21,372,8,21,1,21,5,21,375,8,
- 21,10,21,12,21,378,9,21,1,22,1,22,1,22,1,23,3,23,384,8,23,1,23,1,
- 23,1,23,5,23,389,8,23,10,23,12,23,392,9,23,1,24,1,24,3,24,396,8,
- 24,1,25,1,25,5,25,400,8,25,10,25,12,25,403,9,25,1,25,3,25,406,8,
- 25,1,26,1,26,1,26,1,26,1,26,1,26,1,26,1,26,1,27,1,27,1,27,1,27,1,
- 27,1,27,1,27,1,27,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,29,1,29,1,
- 29,1,29,1,29,1,29,1,29,1,29,3,29,439,8,29,1,29,1,29,1,29,1,29,1,
- 29,1,29,1,29,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,31,1,31,4,
- 31,458,8,31,11,31,12,31,459,1,31,1,31,1,32,1,32,1,32,1,32,1,32,1,
- 33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,4,33,479,8,33,11,
- 33,12,33,480,1,33,1,33,1,34,1,34,1,34,1,34,1,34,5,34,490,8,34,10,
- 34,12,34,493,9,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,35,1,35,1,
- 35,1,35,1,35,5,35,507,8,35,10,35,12,35,510,9,35,1,35,1,35,1,35,1,
- 35,1,35,1,35,1,35,1,36,1,36,1,36,1,36,1,36,4,36,524,8,36,11,36,12,
- 36,525,1,36,1,36,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,38,1,38,1,
- 38,1,38,1,38,1,38,1,38,4,38,544,8,38,11,38,12,38,545,1,38,1,38,1,
- 39,1,39,1,39,1,39,1,39,1,39,3,39,556,8,39,1,39,1,39,1,39,1,39,5,
- 39,562,8,39,10,39,12,39,565,9,39,3,39,567,8,39,1,39,3,39,570,8,39,
- 4,39,572,8,39,11,39,12,39,573,1,39,1,39,1,40,1,40,1,40,1,40,1,40,
- 3,40,583,8,40,1,40,1,40,5,40,587,8,40,10,40,12,40,590,9,40,1,40,
- 1,40,1,40,1,41,1,41,1,41,1,41,1,41,3,41,600,8,41,1,41,1,41,1,41,
- 1,41,1,41,1,42,1,42,3,42,609,8,42,1,43,1,43,1,43,1,43,1,43,1,43,
- 1,43,1,43,1,43,5,43,620,8,43,10,43,12,43,623,9,43,3,43,625,8,43,
- 1,43,3,43,628,8,43,1,43,3,43,631,8,43,1,43,1,43,1,43,1,44,1,44,1,
- 44,1,44,1,44,1,44,5,44,642,8,44,10,44,12,44,645,9,44,3,44,647,8,
- 44,1,44,1,44,3,44,651,8,44,1,44,1,44,1,44,1,44,1,44,1,44,1,45,1,
- 45,1,45,1,46,1,46,1,46,1,46,1,46,0,2,4,12,47,0,2,4,6,8,10,12,14,
- 16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,
- 60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,0,4,2,0,49,49,
- 73,73,1,0,88,89,1,0,30,32,3,0,23,23,85,86,88,89,731,0,100,1,0,0,
- 0,2,114,1,0,0,0,4,125,1,0,0,0,6,148,1,0,0,0,8,150,1,0,0,0,10,152,
- 1,0,0,0,12,165,1,0,0,0,14,215,1,0,0,0,16,220,1,0,0,0,18,227,1,0,
- 0,0,20,236,1,0,0,0,22,240,1,0,0,0,24,242,1,0,0,0,26,255,1,0,0,0,
- 28,270,1,0,0,0,30,288,1,0,0,0,32,302,1,0,0,0,34,323,1,0,0,0,36,328,
- 1,0,0,0,38,334,1,0,0,0,40,338,1,0,0,0,42,349,1,0,0,0,44,379,1,0,
- 0,0,46,383,1,0,0,0,48,393,1,0,0,0,50,397,1,0,0,0,52,407,1,0,0,0,
- 54,415,1,0,0,0,56,423,1,0,0,0,58,430,1,0,0,0,60,447,1,0,0,0,62,457,
- 1,0,0,0,64,463,1,0,0,0,66,468,1,0,0,0,68,484,1,0,0,0,70,501,1,0,
- 0,0,72,518,1,0,0,0,74,529,1,0,0,0,76,536,1,0,0,0,78,549,1,0,0,0,
- 80,577,1,0,0,0,82,594,1,0,0,0,84,608,1,0,0,0,86,610,1,0,0,0,88,635,
- 1,0,0,0,90,658,1,0,0,0,92,661,1,0,0,0,94,101,5,8,0,0,95,101,5,9,
- 0,0,96,101,5,10,0,0,97,101,5,11,0,0,98,101,5,12,0,0,99,101,3,4,2,
- 0,100,94,1,0,0,0,100,95,1,0,0,0,100,96,1,0,0,0,100,97,1,0,0,0,100,
- 98,1,0,0,0,100,99,1,0,0,0,101,1,1,0,0,0,102,104,5,73,0,0,103,102,
- 1,0,0,0,103,104,1,0,0,0,104,105,1,0,0,0,105,106,5,47,0,0,106,107,
- 5,88,0,0,107,108,5,77,0,0,108,109,5,88,0,0,109,115,5,48,0,0,110,
- 112,7,0,0,0,111,110,1,0,0,0,111,112,1,0,0,0,112,113,1,0,0,0,113,
- 115,7,1,0,0,114,103,1,0,0,0,114,111,1,0,0,0,115,3,1,0,0,0,116,117,
- 6,2,-1,0,117,118,5,47,0,0,118,119,3,4,2,0,119,120,5,48,0,0,120,126,
- 1,0,0,0,121,122,5,88,0,0,122,123,5,77,0,0,123,126,3,4,2,2,124,126,
- 5,87,0,0,125,116,1,0,0,0,125,121,1,0,0,0,125,124,1,0,0,0,126,138,
- 1,0,0,0,127,130,10,3,0,0,128,131,5,75,0,0,129,131,5,77,0,0,130,128,
- 1,0,0,0,130,129,1,0,0,0,131,132,1,0,0,0,132,137,3,4,2,4,133,134,
- 10,4,0,0,134,135,5,76,0,0,135,137,3,2,1,0,136,127,1,0,0,0,136,133,
- 1,0,0,0,137,140,1,0,0,0,138,136,1,0,0,0,138,139,1,0,0,0,139,5,1,
- 0,0,0,140,138,1,0,0,0,141,149,5,43,0,0,142,149,5,44,0,0,143,144,
- 5,45,0,0,144,145,3,8,4,0,145,146,5,81,0,0,146,147,3,10,5,0,147,149,
- 1,0,0,0,148,141,1,0,0,0,148,142,1,0,0,0,148,143,1,0,0,0,149,7,1,
- 0,0,0,150,151,5,87,0,0,151,9,1,0,0,0,152,153,5,87,0,0,153,11,1,0,
- 0,0,154,155,6,6,-1,0,155,156,5,47,0,0,156,157,3,12,6,0,157,158,5,
- 48,0,0,158,166,1,0,0,0,159,160,3,16,8,0,160,161,3,12,6,9,161,166,
- 1,0,0,0,162,163,5,26,0,0,163,166,3,12,6,4,164,166,3,14,7,0,165,154,
- 1,0,0,0,165,159,1,0,0,0,165,162,1,0,0,0,165,164,1,0,0,0,166,203,
- 1,0,0,0,167,168,10,10,0,0,168,169,5,76,0,0,169,202,3,12,6,10,170,
- 174,10,8,0,0,171,175,5,75,0,0,172,175,5,77,0,0,173,175,5,78,0,0,
- 174,171,1,0,0,0,174,172,1,0,0,0,174,173,1,0,0,0,175,176,1,0,0,0,
- 176,202,3,12,6,9,177,180,10,7,0,0,178,181,5,49,0,0,179,181,5,73,
- 0,0,180,178,1,0,0,0,180,179,1,0,0,0,181,182,1,0,0,0,182,202,3,12,
- 6,8,183,184,10,6,0,0,184,185,3,18,9,0,185,186,3,12,6,7,186,202,1,
- 0,0,0,187,188,10,5,0,0,188,189,3,20,10,0,189,190,3,12,6,6,190,202,
- 1,0,0,0,191,192,10,3,0,0,192,193,3,22,11,0,193,194,3,12,6,4,194,
- 202,1,0,0,0,195,196,10,2,0,0,196,197,5,79,0,0,197,198,3,12,6,0,198,
- 199,5,80,0,0,199,200,3,12,6,3,200,202,1,0,0,0,201,167,1,0,0,0,201,
- 170,1,0,0,0,201,177,1,0,0,0,201,183,1,0,0,0,201,187,1,0,0,0,201,
- 191,1,0,0,0,201,195,1,0,0,0,202,205,1,0,0,0,203,201,1,0,0,0,203,
- 204,1,0,0,0,204,13,1,0,0,0,205,203,1,0,0,0,206,216,3,26,13,0,207,
- 216,5,85,0,0,208,210,7,1,0,0,209,211,3,24,12,0,210,209,1,0,0,0,210,
- 211,1,0,0,0,211,216,1,0,0,0,212,216,5,86,0,0,213,216,5,23,0,0,214,
- 216,3,24,12,0,215,206,1,0,0,0,215,207,1,0,0,0,215,208,1,0,0,0,215,
- 212,1,0,0,0,215,213,1,0,0,0,215,214,1,0,0,0,216,15,1,0,0,0,217,221,
- 5,49,0,0,218,221,5,73,0,0,219,221,5,50,0,0,220,217,1,0,0,0,220,218,
- 1,0,0,0,220,219,1,0,0,0,221,17,1,0,0,0,222,228,5,53,0,0,223,228,
- 5,52,0,0,224,228,5,51,0,0,225,228,5,59,0,0,226,228,5,60,0,0,227,
- 222,1,0,0,0,227,223,1,0,0,0,227,224,1,0,0,0,227,225,1,0,0,0,227,
- 226,1,0,0,0,228,19,1,0,0,0,229,237,5,61,0,0,230,237,5,63,0,0,231,
- 237,5,68,0,0,232,237,5,69,0,0,233,237,5,70,0,0,234,237,5,71,0,0,
- 235,237,5,62,0,0,236,229,1,0,0,0,236,230,1,0,0,0,236,231,1,0,0,0,
- 236,232,1,0,0,0,236,233,1,0,0,0,236,234,1,0,0,0,236,235,1,0,0,0,
- 237,21,1,0,0,0,238,241,5,24,0,0,239,241,5,25,0,0,240,238,1,0,0,0,
- 240,239,1,0,0,0,241,23,1,0,0,0,242,247,5,87,0,0,243,244,5,54,0,0,
- 244,245,3,12,6,0,245,246,5,56,0,0,246,248,1,0,0,0,247,243,1,0,0,
- 0,247,248,1,0,0,0,248,252,1,0,0,0,249,251,5,83,0,0,250,249,1,0,0,
- 0,251,254,1,0,0,0,252,250,1,0,0,0,252,253,1,0,0,0,253,25,1,0,0,0,
- 254,252,1,0,0,0,255,256,5,87,0,0,256,265,5,47,0,0,257,262,3,12,6,
- 0,258,259,5,72,0,0,259,261,3,12,6,0,260,258,1,0,0,0,261,264,1,0,
- 0,0,262,260,1,0,0,0,262,263,1,0,0,0,263,266,1,0,0,0,264,262,1,0,
- 0,0,265,257,1,0,0,0,265,266,1,0,0,0,266,267,1,0,0,0,267,268,5,48,
- 0,0,268,27,1,0,0,0,269,271,5,27,0,0,270,269,1,0,0,0,270,271,1,0,
- 0,0,271,272,1,0,0,0,272,273,5,14,0,0,273,274,5,87,0,0,274,275,3,
- 0,0,0,275,276,5,74,0,0,276,278,3,12,6,0,277,279,5,82,0,0,278,277,
- 1,0,0,0,278,279,1,0,0,0,279,283,1,0,0,0,280,282,3,6,3,0,281,280,
- 1,0,0,0,282,285,1,0,0,0,283,281,1,0,0,0,283,284,1,0,0,0,284,286,
- 1,0,0,0,285,283,1,0,0,0,286,287,5,7,0,0,287,29,1,0,0,0,288,289,3,
- 24,12,0,289,290,5,74,0,0,290,292,3,12,6,0,291,293,5,82,0,0,292,291,
- 1,0,0,0,292,293,1,0,0,0,293,297,1,0,0,0,294,296,3,6,3,0,295,294,
- 1,0,0,0,296,299,1,0,0,0,297,295,1,0,0,0,297,298,1,0,0,0,298,300,
- 1,0,0,0,299,297,1,0,0,0,300,301,5,7,0,0,301,31,1,0,0,0,302,303,5,
- 28,0,0,303,304,3,24,12,0,304,305,5,74,0,0,305,313,3,12,6,0,306,307,
- 5,3,0,0,307,308,3,24,12,0,308,309,5,74,0,0,309,310,3,12,6,0,310,
- 312,1,0,0,0,311,306,1,0,0,0,312,315,1,0,0,0,313,311,1,0,0,0,313,
- 314,1,0,0,0,314,317,1,0,0,0,315,313,1,0,0,0,316,318,5,82,0,0,317,
- 316,1,0,0,0,317,318,1,0,0,0,318,319,1,0,0,0,319,320,5,7,0,0,320,
- 33,1,0,0,0,321,324,3,38,19,0,322,324,3,36,18,0,323,321,1,0,0,0,323,
- 322,1,0,0,0,324,35,1,0,0,0,325,329,3,50,25,0,326,329,3,58,29,0,327,
- 329,3,60,30,0,328,325,1,0,0,0,328,326,1,0,0,0,328,327,1,0,0,0,329,
- 37,1,0,0,0,330,335,3,40,20,0,331,335,3,26,13,0,332,335,3,42,21,0,
- 333,335,3,48,24,0,334,330,1,0,0,0,334,331,1,0,0,0,334,332,1,0,0,
- 0,334,333,1,0,0,0,335,336,1,0,0,0,336,337,5,7,0,0,337,39,1,0,0,0,
- 338,344,3,24,12,0,339,345,5,74,0,0,340,345,5,64,0,0,341,345,5,65,
- 0,0,342,345,5,66,0,0,343,345,5,67,0,0,344,339,1,0,0,0,344,340,1,
- 0,0,0,344,341,1,0,0,0,344,342,1,0,0,0,344,343,1,0,0,0,345,346,1,
- 0,0,0,346,347,3,12,6,0,347,41,1,0,0,0,348,350,5,27,0,0,349,348,1,
- 0,0,0,349,350,1,0,0,0,350,352,1,0,0,0,351,353,5,14,0,0,352,351,1,
- 0,0,0,352,353,1,0,0,0,353,354,1,0,0,0,354,359,3,24,12,0,355,356,
- 5,72,0,0,356,358,3,24,12,0,357,355,1,0,0,0,358,361,1,0,0,0,359,357,
- 1,0,0,0,359,360,1,0,0,0,360,362,1,0,0,0,361,359,1,0,0,0,362,365,
- 3,0,0,0,363,364,5,74,0,0,364,366,3,12,6,0,365,363,1,0,0,0,365,366,
- 1,0,0,0,366,371,1,0,0,0,367,368,5,57,0,0,368,369,3,12,6,0,369,370,
- 5,58,0,0,370,372,1,0,0,0,371,367,1,0,0,0,371,372,1,0,0,0,372,376,
- 1,0,0,0,373,375,3,6,3,0,374,373,1,0,0,0,375,378,1,0,0,0,376,374,
- 1,0,0,0,376,377,1,0,0,0,377,43,1,0,0,0,378,376,1,0,0,0,379,380,3,
- 42,21,0,380,381,5,7,0,0,381,45,1,0,0,0,382,384,5,7,0,0,383,382,1,
- 0,0,0,383,384,1,0,0,0,384,385,1,0,0,0,385,390,3,34,17,0,386,389,
- 5,7,0,0,387,389,3,34,17,0,388,386,1,0,0,0,388,387,1,0,0,0,389,392,
- 1,0,0,0,390,388,1,0,0,0,390,391,1,0,0,0,391,47,1,0,0,0,392,390,1,
- 0,0,0,393,395,5,15,0,0,394,396,3,12,6,0,395,394,1,0,0,0,395,396,
- 1,0,0,0,396,49,1,0,0,0,397,401,3,52,26,0,398,400,3,54,27,0,399,398,
- 1,0,0,0,400,403,1,0,0,0,401,399,1,0,0,0,401,402,1,0,0,0,402,405,
- 1,0,0,0,403,401,1,0,0,0,404,406,3,56,28,0,405,404,1,0,0,0,405,406,
- 1,0,0,0,406,51,1,0,0,0,407,408,5,16,0,0,408,409,3,12,6,0,409,410,
- 5,80,0,0,410,411,5,7,0,0,411,412,5,1,0,0,412,413,3,46,23,0,413,414,
- 5,2,0,0,414,53,1,0,0,0,415,416,5,17,0,0,416,417,3,12,6,0,417,418,
- 5,80,0,0,418,419,5,7,0,0,419,420,5,1,0,0,420,421,3,46,23,0,421,422,
- 5,2,0,0,422,55,1,0,0,0,423,424,5,18,0,0,424,425,5,80,0,0,425,426,
- 5,7,0,0,426,427,5,1,0,0,427,428,3,46,23,0,428,429,5,2,0,0,429,57,
- 1,0,0,0,430,431,5,19,0,0,431,432,5,87,0,0,432,433,5,21,0,0,433,434,
- 3,12,6,0,434,435,5,46,0,0,435,436,3,12,6,0,436,438,5,22,0,0,437,
- 439,5,73,0,0,438,437,1,0,0,0,438,439,1,0,0,0,439,440,1,0,0,0,440,
- 441,7,1,0,0,441,442,5,80,0,0,442,443,5,7,0,0,443,444,5,1,0,0,444,
- 445,3,46,23,0,445,446,5,2,0,0,446,59,1,0,0,0,447,448,5,20,0,0,448,
- 449,3,12,6,0,449,450,5,80,0,0,450,451,5,7,0,0,451,452,5,1,0,0,452,
- 453,3,46,23,0,453,454,5,2,0,0,454,61,1,0,0,0,455,458,3,64,32,0,456,
- 458,5,7,0,0,457,455,1,0,0,0,457,456,1,0,0,0,458,459,1,0,0,0,459,
- 457,1,0,0,0,459,460,1,0,0,0,460,461,1,0,0,0,461,462,5,0,0,1,462,
- 63,1,0,0,0,463,464,5,29,0,0,464,465,5,87,0,0,465,466,5,80,0,0,466,
- 467,3,66,33,0,467,65,1,0,0,0,468,469,5,7,0,0,469,478,5,1,0,0,470,
- 479,3,72,36,0,471,479,3,76,38,0,472,479,3,78,39,0,473,479,3,86,43,
- 0,474,479,3,88,44,0,475,479,3,68,34,0,476,479,3,70,35,0,477,479,
- 3,74,37,0,478,470,1,0,0,0,478,471,1,0,0,0,478,472,1,0,0,0,478,473,
- 1,0,0,0,478,474,1,0,0,0,478,475,1,0,0,0,478,476,1,0,0,0,478,477,
- 1,0,0,0,479,480,1,0,0,0,480,478,1,0,0,0,480,481,1,0,0,0,481,482,
- 1,0,0,0,482,483,5,2,0,0,483,67,1,0,0,0,484,485,5,38,0,0,485,486,
- 5,47,0,0,486,491,5,87,0,0,487,488,5,72,0,0,488,490,3,92,46,0,489,
- 487,1,0,0,0,490,493,1,0,0,0,491,489,1,0,0,0,491,492,1,0,0,0,492,
- 494,1,0,0,0,493,491,1,0,0,0,494,495,5,48,0,0,495,496,5,80,0,0,496,
- 497,5,7,0,0,497,498,5,1,0,0,498,499,3,46,23,0,499,500,5,2,0,0,500,
- 69,1,0,0,0,501,502,5,39,0,0,502,503,5,47,0,0,503,508,3,12,6,0,504,
- 505,5,72,0,0,505,507,3,92,46,0,506,504,1,0,0,0,507,510,1,0,0,0,508,
- 506,1,0,0,0,508,509,1,0,0,0,509,511,1,0,0,0,510,508,1,0,0,0,511,
- 512,5,48,0,0,512,513,5,80,0,0,513,514,5,7,0,0,514,515,5,1,0,0,515,
- 516,3,46,23,0,516,517,5,2,0,0,517,71,1,0,0,0,518,519,7,2,0,0,519,
- 520,5,80,0,0,520,521,5,7,0,0,521,523,5,1,0,0,522,524,3,44,22,0,523,
- 522,1,0,0,0,524,525,1,0,0,0,525,523,1,0,0,0,525,526,1,0,0,0,526,
- 527,1,0,0,0,527,528,5,2,0,0,528,73,1,0,0,0,529,530,5,33,0,0,530,
- 531,5,80,0,0,531,532,5,7,0,0,532,533,5,1,0,0,533,534,3,46,23,0,534,
- 535,5,2,0,0,535,75,1,0,0,0,536,537,5,34,0,0,537,538,5,80,0,0,538,
- 539,5,7,0,0,539,543,5,1,0,0,540,544,3,28,14,0,541,544,3,30,15,0,
- 542,544,3,32,16,0,543,540,1,0,0,0,543,541,1,0,0,0,543,542,1,0,0,
- 0,544,545,1,0,0,0,545,543,1,0,0,0,545,546,1,0,0,0,546,547,1,0,0,
- 0,547,548,5,2,0,0,548,77,1,0,0,0,549,550,5,35,0,0,550,551,5,80,0,
- 0,551,552,5,7,0,0,552,571,5,1,0,0,553,556,3,80,40,0,554,556,3,82,
- 41,0,555,553,1,0,0,0,555,554,1,0,0,0,556,569,1,0,0,0,557,566,5,47,
- 0,0,558,563,3,90,45,0,559,560,5,72,0,0,560,562,3,90,45,0,561,559,
- 1,0,0,0,562,565,1,0,0,0,563,561,1,0,0,0,563,564,1,0,0,0,564,567,
- 1,0,0,0,565,563,1,0,0,0,566,558,1,0,0,0,566,567,1,0,0,0,567,568,
- 1,0,0,0,568,570,5,48,0,0,569,557,1,0,0,0,569,570,1,0,0,0,570,572,
- 1,0,0,0,571,555,1,0,0,0,572,573,1,0,0,0,573,571,1,0,0,0,573,574,
- 1,0,0,0,574,575,1,0,0,0,575,576,5,2,0,0,576,79,1,0,0,0,577,582,5,
- 87,0,0,578,579,5,54,0,0,579,580,3,12,6,0,580,581,5,56,0,0,581,583,
- 1,0,0,0,582,578,1,0,0,0,582,583,1,0,0,0,583,584,1,0,0,0,584,588,
- 5,55,0,0,585,587,3,84,42,0,586,585,1,0,0,0,587,590,1,0,0,0,588,586,
- 1,0,0,0,588,589,1,0,0,0,589,591,1,0,0,0,590,588,1,0,0,0,591,592,
- 5,40,0,0,592,593,5,7,0,0,593,81,1,0,0,0,594,599,5,87,0,0,595,596,
- 5,54,0,0,596,597,3,12,6,0,597,598,5,56,0,0,598,600,1,0,0,0,599,595,
- 1,0,0,0,599,600,1,0,0,0,600,601,1,0,0,0,601,602,3,0,0,0,602,603,
- 5,55,0,0,603,604,5,37,0,0,604,605,5,7,0,0,605,83,1,0,0,0,606,609,
- 5,41,0,0,607,609,5,42,0,0,608,606,1,0,0,0,608,607,1,0,0,0,609,85,
- 1,0,0,0,610,611,5,36,0,0,611,612,5,80,0,0,612,613,5,7,0,0,613,630,
- 5,1,0,0,614,627,5,40,0,0,615,624,5,47,0,0,616,621,3,90,45,0,617,
- 618,5,72,0,0,618,620,3,90,45,0,619,617,1,0,0,0,620,623,1,0,0,0,621,
- 619,1,0,0,0,621,622,1,0,0,0,622,625,1,0,0,0,623,621,1,0,0,0,624,
- 616,1,0,0,0,624,625,1,0,0,0,625,626,1,0,0,0,626,628,5,48,0,0,627,
- 615,1,0,0,0,627,628,1,0,0,0,628,631,1,0,0,0,629,631,5,37,0,0,630,
- 614,1,0,0,0,630,629,1,0,0,0,631,632,1,0,0,0,632,633,5,7,0,0,633,
- 634,5,2,0,0,634,87,1,0,0,0,635,636,5,13,0,0,636,637,5,87,0,0,637,
- 646,5,47,0,0,638,643,3,90,45,0,639,640,5,72,0,0,640,642,3,90,45,
- 0,641,639,1,0,0,0,642,645,1,0,0,0,643,641,1,0,0,0,643,644,1,0,0,
- 0,644,647,1,0,0,0,645,643,1,0,0,0,646,638,1,0,0,0,646,647,1,0,0,
- 0,647,648,1,0,0,0,648,650,5,48,0,0,649,651,3,0,0,0,650,649,1,0,0,
- 0,650,651,1,0,0,0,651,652,1,0,0,0,652,653,5,80,0,0,653,654,5,7,0,
- 0,654,655,5,1,0,0,655,656,3,46,23,0,656,657,5,2,0,0,657,89,1,0,0,
- 0,658,659,5,87,0,0,659,660,3,0,0,0,660,91,1,0,0,0,661,662,5,87,0,
- 0,662,663,5,74,0,0,663,664,7,3,0,0,664,93,1,0,0,0,73,100,103,111,
+ 12,1,12,1,12,3,12,258,8,12,1,13,1,13,1,13,1,13,1,13,5,13,265,8,13,
+ 10,13,12,13,268,9,13,3,13,270,8,13,1,13,1,13,1,14,3,14,275,8,14,
+ 1,14,1,14,1,14,1,14,1,14,1,14,3,14,283,8,14,1,14,5,14,286,8,14,10,
+ 14,12,14,289,9,14,1,14,1,14,1,15,1,15,1,15,1,15,3,15,297,8,15,1,
+ 15,5,15,300,8,15,10,15,12,15,303,9,15,1,15,1,15,1,16,1,16,1,16,1,
+ 16,1,16,1,16,1,16,1,16,1,16,5,16,316,8,16,10,16,12,16,319,9,16,1,
+ 16,3,16,322,8,16,1,16,1,16,1,17,1,17,3,17,328,8,17,1,18,1,18,1,18,
+ 3,18,333,8,18,1,19,1,19,1,19,1,19,3,19,339,8,19,1,19,1,19,1,20,1,
+ 20,1,20,1,20,1,20,1,20,3,20,349,8,20,1,20,1,20,1,21,3,21,354,8,21,
+ 1,21,3,21,357,8,21,1,21,1,21,1,21,5,21,362,8,21,10,21,12,21,365,
+ 9,21,1,21,1,21,1,21,3,21,370,8,21,1,21,1,21,1,21,1,21,3,21,376,8,
+ 21,1,21,5,21,379,8,21,10,21,12,21,382,9,21,1,22,1,22,1,22,1,23,3,
+ 23,388,8,23,1,23,1,23,1,23,5,23,393,8,23,10,23,12,23,396,9,23,1,
+ 24,1,24,3,24,400,8,24,1,25,1,25,5,25,404,8,25,10,25,12,25,407,9,
+ 25,1,25,3,25,410,8,25,1,26,1,26,1,26,1,26,1,26,1,26,1,26,1,26,1,
+ 27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,28,1,28,1,28,1,28,1,28,1,
+ 28,1,28,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,3,29,443,8,29,1,
+ 29,1,29,1,29,1,29,1,29,1,29,1,29,1,30,1,30,1,30,1,30,1,30,1,30,1,
+ 30,1,30,1,31,1,31,4,31,462,8,31,11,31,12,31,463,1,31,1,31,1,32,1,
+ 32,1,32,1,32,1,32,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,
+ 33,4,33,483,8,33,11,33,12,33,484,1,33,1,33,1,34,1,34,1,34,1,34,1,
+ 34,5,34,494,8,34,10,34,12,34,497,9,34,1,34,1,34,1,34,1,34,1,34,1,
+ 34,1,34,1,35,1,35,1,35,1,35,1,35,5,35,511,8,35,10,35,12,35,514,9,
+ 35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,36,1,36,1,36,1,36,1,36,4,
+ 36,528,8,36,11,36,12,36,529,1,36,1,36,1,37,1,37,1,37,1,37,1,37,1,
+ 37,1,37,1,38,1,38,1,38,1,38,1,38,1,38,1,38,4,38,548,8,38,11,38,12,
+ 38,549,1,38,1,38,1,39,1,39,1,39,1,39,1,39,1,39,4,39,560,8,39,11,
+ 39,12,39,561,1,39,1,39,1,40,1,40,1,40,1,40,1,40,3,40,571,8,40,1,
+ 40,1,40,1,40,1,40,1,40,1,40,5,40,579,8,40,10,40,12,40,582,9,40,3,
+ 40,584,8,40,1,40,3,40,587,8,40,1,40,1,40,1,41,1,41,1,41,1,41,1,41,
+ 3,41,596,8,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,5,41,605,8,41,10,
+ 41,12,41,608,9,41,3,41,610,8,41,1,41,3,41,613,8,41,1,41,1,41,1,42,
+ 1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,5,42,626,8,42,10,42,12,42,
+ 629,9,42,3,42,631,8,42,1,42,3,42,634,8,42,1,42,3,42,637,8,42,1,42,
+ 1,42,1,42,1,43,1,43,1,43,1,43,1,43,1,43,5,43,648,8,43,10,43,12,43,
+ 651,9,43,3,43,653,8,43,1,43,1,43,3,43,657,8,43,1,43,1,43,1,43,1,
+ 43,1,43,1,43,1,44,1,44,1,44,1,45,1,45,3,45,670,8,45,1,46,1,46,1,
+ 46,1,46,1,46,0,2,4,12,47,0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,
+ 30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,
+ 74,76,78,80,82,84,86,88,90,92,0,4,2,0,47,47,71,71,1,0,86,87,1,0,
+ 30,32,3,0,23,23,83,84,86,87,744,0,100,1,0,0,0,2,114,1,0,0,0,4,125,
+ 1,0,0,0,6,148,1,0,0,0,8,150,1,0,0,0,10,152,1,0,0,0,12,165,1,0,0,
+ 0,14,215,1,0,0,0,16,220,1,0,0,0,18,227,1,0,0,0,20,236,1,0,0,0,22,
+ 240,1,0,0,0,24,242,1,0,0,0,26,259,1,0,0,0,28,274,1,0,0,0,30,292,
+ 1,0,0,0,32,306,1,0,0,0,34,327,1,0,0,0,36,332,1,0,0,0,38,338,1,0,
+ 0,0,40,342,1,0,0,0,42,353,1,0,0,0,44,383,1,0,0,0,46,387,1,0,0,0,
+ 48,397,1,0,0,0,50,401,1,0,0,0,52,411,1,0,0,0,54,419,1,0,0,0,56,427,
+ 1,0,0,0,58,434,1,0,0,0,60,451,1,0,0,0,62,461,1,0,0,0,64,467,1,0,
+ 0,0,66,472,1,0,0,0,68,488,1,0,0,0,70,505,1,0,0,0,72,522,1,0,0,0,
+ 74,533,1,0,0,0,76,540,1,0,0,0,78,553,1,0,0,0,80,565,1,0,0,0,82,590,
+ 1,0,0,0,84,616,1,0,0,0,86,641,1,0,0,0,88,664,1,0,0,0,90,669,1,0,
+ 0,0,92,671,1,0,0,0,94,101,5,8,0,0,95,101,5,9,0,0,96,101,5,10,0,0,
+ 97,101,5,11,0,0,98,101,5,12,0,0,99,101,3,4,2,0,100,94,1,0,0,0,100,
+ 95,1,0,0,0,100,96,1,0,0,0,100,97,1,0,0,0,100,98,1,0,0,0,100,99,1,
+ 0,0,0,101,1,1,0,0,0,102,104,5,71,0,0,103,102,1,0,0,0,103,104,1,0,
+ 0,0,104,105,1,0,0,0,105,106,5,45,0,0,106,107,5,86,0,0,107,108,5,
+ 75,0,0,108,109,5,86,0,0,109,115,5,46,0,0,110,112,7,0,0,0,111,110,
+ 1,0,0,0,111,112,1,0,0,0,112,113,1,0,0,0,113,115,7,1,0,0,114,103,
+ 1,0,0,0,114,111,1,0,0,0,115,3,1,0,0,0,116,117,6,2,-1,0,117,118,5,
+ 45,0,0,118,119,3,4,2,0,119,120,5,46,0,0,120,126,1,0,0,0,121,122,
+ 5,86,0,0,122,123,5,75,0,0,123,126,3,4,2,2,124,126,5,85,0,0,125,116,
+ 1,0,0,0,125,121,1,0,0,0,125,124,1,0,0,0,126,138,1,0,0,0,127,130,
+ 10,3,0,0,128,131,5,73,0,0,129,131,5,75,0,0,130,128,1,0,0,0,130,129,
+ 1,0,0,0,131,132,1,0,0,0,132,137,3,4,2,4,133,134,10,4,0,0,134,135,
+ 5,74,0,0,135,137,3,2,1,0,136,127,1,0,0,0,136,133,1,0,0,0,137,140,
+ 1,0,0,0,138,136,1,0,0,0,138,139,1,0,0,0,139,5,1,0,0,0,140,138,1,
+ 0,0,0,141,149,5,41,0,0,142,149,5,42,0,0,143,144,5,43,0,0,144,145,
+ 3,8,4,0,145,146,5,79,0,0,146,147,3,10,5,0,147,149,1,0,0,0,148,141,
+ 1,0,0,0,148,142,1,0,0,0,148,143,1,0,0,0,149,7,1,0,0,0,150,151,5,
+ 85,0,0,151,9,1,0,0,0,152,153,5,85,0,0,153,11,1,0,0,0,154,155,6,6,
+ -1,0,155,156,5,45,0,0,156,157,3,12,6,0,157,158,5,46,0,0,158,166,
+ 1,0,0,0,159,160,3,16,8,0,160,161,3,12,6,9,161,166,1,0,0,0,162,163,
+ 5,26,0,0,163,166,3,12,6,4,164,166,3,14,7,0,165,154,1,0,0,0,165,159,
+ 1,0,0,0,165,162,1,0,0,0,165,164,1,0,0,0,166,203,1,0,0,0,167,168,
+ 10,10,0,0,168,169,5,74,0,0,169,202,3,12,6,10,170,174,10,8,0,0,171,
+ 175,5,73,0,0,172,175,5,75,0,0,173,175,5,76,0,0,174,171,1,0,0,0,174,
+ 172,1,0,0,0,174,173,1,0,0,0,175,176,1,0,0,0,176,202,3,12,6,9,177,
+ 180,10,7,0,0,178,181,5,47,0,0,179,181,5,71,0,0,180,178,1,0,0,0,180,
+ 179,1,0,0,0,181,182,1,0,0,0,182,202,3,12,6,8,183,184,10,6,0,0,184,
+ 185,3,18,9,0,185,186,3,12,6,7,186,202,1,0,0,0,187,188,10,5,0,0,188,
+ 189,3,20,10,0,189,190,3,12,6,6,190,202,1,0,0,0,191,192,10,3,0,0,
+ 192,193,3,22,11,0,193,194,3,12,6,4,194,202,1,0,0,0,195,196,10,2,
+ 0,0,196,197,5,77,0,0,197,198,3,12,6,0,198,199,5,78,0,0,199,200,3,
+ 12,6,3,200,202,1,0,0,0,201,167,1,0,0,0,201,170,1,0,0,0,201,177,1,
+ 0,0,0,201,183,1,0,0,0,201,187,1,0,0,0,201,191,1,0,0,0,201,195,1,
+ 0,0,0,202,205,1,0,0,0,203,201,1,0,0,0,203,204,1,0,0,0,204,13,1,0,
+ 0,0,205,203,1,0,0,0,206,216,3,26,13,0,207,216,5,83,0,0,208,210,7,
+ 1,0,0,209,211,3,24,12,0,210,209,1,0,0,0,210,211,1,0,0,0,211,216,
+ 1,0,0,0,212,216,5,84,0,0,213,216,5,23,0,0,214,216,3,24,12,0,215,
+ 206,1,0,0,0,215,207,1,0,0,0,215,208,1,0,0,0,215,212,1,0,0,0,215,
+ 213,1,0,0,0,215,214,1,0,0,0,216,15,1,0,0,0,217,221,5,47,0,0,218,
+ 221,5,71,0,0,219,221,5,48,0,0,220,217,1,0,0,0,220,218,1,0,0,0,220,
+ 219,1,0,0,0,221,17,1,0,0,0,222,228,5,51,0,0,223,228,5,50,0,0,224,
+ 228,5,49,0,0,225,228,5,57,0,0,226,228,5,58,0,0,227,222,1,0,0,0,227,
+ 223,1,0,0,0,227,224,1,0,0,0,227,225,1,0,0,0,227,226,1,0,0,0,228,
+ 19,1,0,0,0,229,237,5,59,0,0,230,237,5,61,0,0,231,237,5,66,0,0,232,
+ 237,5,67,0,0,233,237,5,68,0,0,234,237,5,69,0,0,235,237,5,60,0,0,
+ 236,229,1,0,0,0,236,230,1,0,0,0,236,231,1,0,0,0,236,232,1,0,0,0,
+ 236,233,1,0,0,0,236,234,1,0,0,0,236,235,1,0,0,0,237,21,1,0,0,0,238,
+ 241,5,24,0,0,239,241,5,25,0,0,240,238,1,0,0,0,240,239,1,0,0,0,241,
+ 23,1,0,0,0,242,247,5,85,0,0,243,244,5,52,0,0,244,245,3,90,45,0,245,
+ 246,5,54,0,0,246,248,1,0,0,0,247,243,1,0,0,0,247,248,1,0,0,0,248,
+ 252,1,0,0,0,249,251,5,81,0,0,250,249,1,0,0,0,251,254,1,0,0,0,252,
+ 250,1,0,0,0,252,253,1,0,0,0,253,257,1,0,0,0,254,252,1,0,0,0,255,
+ 256,5,82,0,0,256,258,3,24,12,0,257,255,1,0,0,0,257,258,1,0,0,0,258,
+ 25,1,0,0,0,259,260,5,85,0,0,260,269,5,45,0,0,261,266,3,12,6,0,262,
+ 263,5,70,0,0,263,265,3,12,6,0,264,262,1,0,0,0,265,268,1,0,0,0,266,
+ 264,1,0,0,0,266,267,1,0,0,0,267,270,1,0,0,0,268,266,1,0,0,0,269,
+ 261,1,0,0,0,269,270,1,0,0,0,270,271,1,0,0,0,271,272,5,46,0,0,272,
+ 27,1,0,0,0,273,275,5,27,0,0,274,273,1,0,0,0,274,275,1,0,0,0,275,
+ 276,1,0,0,0,276,277,5,14,0,0,277,278,5,85,0,0,278,279,3,0,0,0,279,
+ 280,5,72,0,0,280,282,3,12,6,0,281,283,5,80,0,0,282,281,1,0,0,0,282,
+ 283,1,0,0,0,283,287,1,0,0,0,284,286,3,6,3,0,285,284,1,0,0,0,286,
+ 289,1,0,0,0,287,285,1,0,0,0,287,288,1,0,0,0,288,290,1,0,0,0,289,
+ 287,1,0,0,0,290,291,5,7,0,0,291,29,1,0,0,0,292,293,3,24,12,0,293,
+ 294,5,72,0,0,294,296,3,12,6,0,295,297,5,80,0,0,296,295,1,0,0,0,296,
+ 297,1,0,0,0,297,301,1,0,0,0,298,300,3,6,3,0,299,298,1,0,0,0,300,
+ 303,1,0,0,0,301,299,1,0,0,0,301,302,1,0,0,0,302,304,1,0,0,0,303,
+ 301,1,0,0,0,304,305,5,7,0,0,305,31,1,0,0,0,306,307,5,28,0,0,307,
+ 308,3,24,12,0,308,309,5,72,0,0,309,317,3,12,6,0,310,311,5,3,0,0,
+ 311,312,3,24,12,0,312,313,5,72,0,0,313,314,3,12,6,0,314,316,1,0,
+ 0,0,315,310,1,0,0,0,316,319,1,0,0,0,317,315,1,0,0,0,317,318,1,0,
+ 0,0,318,321,1,0,0,0,319,317,1,0,0,0,320,322,5,80,0,0,321,320,1,0,
+ 0,0,321,322,1,0,0,0,322,323,1,0,0,0,323,324,5,7,0,0,324,33,1,0,0,
+ 0,325,328,3,38,19,0,326,328,3,36,18,0,327,325,1,0,0,0,327,326,1,
+ 0,0,0,328,35,1,0,0,0,329,333,3,50,25,0,330,333,3,58,29,0,331,333,
+ 3,60,30,0,332,329,1,0,0,0,332,330,1,0,0,0,332,331,1,0,0,0,333,37,
+ 1,0,0,0,334,339,3,40,20,0,335,339,3,26,13,0,336,339,3,42,21,0,337,
+ 339,3,48,24,0,338,334,1,0,0,0,338,335,1,0,0,0,338,336,1,0,0,0,338,
+ 337,1,0,0,0,339,340,1,0,0,0,340,341,5,7,0,0,341,39,1,0,0,0,342,348,
+ 3,24,12,0,343,349,5,72,0,0,344,349,5,62,0,0,345,349,5,63,0,0,346,
+ 349,5,64,0,0,347,349,5,65,0,0,348,343,1,0,0,0,348,344,1,0,0,0,348,
+ 345,1,0,0,0,348,346,1,0,0,0,348,347,1,0,0,0,349,350,1,0,0,0,350,
+ 351,3,12,6,0,351,41,1,0,0,0,352,354,5,27,0,0,353,352,1,0,0,0,353,
+ 354,1,0,0,0,354,356,1,0,0,0,355,357,5,14,0,0,356,355,1,0,0,0,356,
+ 357,1,0,0,0,357,358,1,0,0,0,358,363,3,24,12,0,359,360,5,70,0,0,360,
+ 362,3,24,12,0,361,359,1,0,0,0,362,365,1,0,0,0,363,361,1,0,0,0,363,
+ 364,1,0,0,0,364,366,1,0,0,0,365,363,1,0,0,0,366,369,3,0,0,0,367,
+ 368,5,72,0,0,368,370,3,12,6,0,369,367,1,0,0,0,369,370,1,0,0,0,370,
+ 375,1,0,0,0,371,372,5,55,0,0,372,373,3,12,6,0,373,374,5,56,0,0,374,
+ 376,1,0,0,0,375,371,1,0,0,0,375,376,1,0,0,0,376,380,1,0,0,0,377,
+ 379,3,6,3,0,378,377,1,0,0,0,379,382,1,0,0,0,380,378,1,0,0,0,380,
+ 381,1,0,0,0,381,43,1,0,0,0,382,380,1,0,0,0,383,384,3,42,21,0,384,
+ 385,5,7,0,0,385,45,1,0,0,0,386,388,5,7,0,0,387,386,1,0,0,0,387,388,
+ 1,0,0,0,388,389,1,0,0,0,389,394,3,34,17,0,390,393,5,7,0,0,391,393,
+ 3,34,17,0,392,390,1,0,0,0,392,391,1,0,0,0,393,396,1,0,0,0,394,392,
+ 1,0,0,0,394,395,1,0,0,0,395,47,1,0,0,0,396,394,1,0,0,0,397,399,5,
+ 15,0,0,398,400,3,12,6,0,399,398,1,0,0,0,399,400,1,0,0,0,400,49,1,
+ 0,0,0,401,405,3,52,26,0,402,404,3,54,27,0,403,402,1,0,0,0,404,407,
+ 1,0,0,0,405,403,1,0,0,0,405,406,1,0,0,0,406,409,1,0,0,0,407,405,
+ 1,0,0,0,408,410,3,56,28,0,409,408,1,0,0,0,409,410,1,0,0,0,410,51,
+ 1,0,0,0,411,412,5,16,0,0,412,413,3,12,6,0,413,414,5,78,0,0,414,415,
+ 5,7,0,0,415,416,5,1,0,0,416,417,3,46,23,0,417,418,5,2,0,0,418,53,
+ 1,0,0,0,419,420,5,17,0,0,420,421,3,12,6,0,421,422,5,78,0,0,422,423,
+ 5,7,0,0,423,424,5,1,0,0,424,425,3,46,23,0,425,426,5,2,0,0,426,55,
+ 1,0,0,0,427,428,5,18,0,0,428,429,5,78,0,0,429,430,5,7,0,0,430,431,
+ 5,1,0,0,431,432,3,46,23,0,432,433,5,2,0,0,433,57,1,0,0,0,434,435,
+ 5,19,0,0,435,436,5,85,0,0,436,437,5,21,0,0,437,438,3,12,6,0,438,
+ 439,5,44,0,0,439,440,3,12,6,0,440,442,5,22,0,0,441,443,5,71,0,0,
+ 442,441,1,0,0,0,442,443,1,0,0,0,443,444,1,0,0,0,444,445,7,1,0,0,
+ 445,446,5,78,0,0,446,447,5,7,0,0,447,448,5,1,0,0,448,449,3,46,23,
+ 0,449,450,5,2,0,0,450,59,1,0,0,0,451,452,5,20,0,0,452,453,3,12,6,
+ 0,453,454,5,78,0,0,454,455,5,7,0,0,455,456,5,1,0,0,456,457,3,46,
+ 23,0,457,458,5,2,0,0,458,61,1,0,0,0,459,462,3,64,32,0,460,462,5,
+ 7,0,0,461,459,1,0,0,0,461,460,1,0,0,0,462,463,1,0,0,0,463,461,1,
+ 0,0,0,463,464,1,0,0,0,464,465,1,0,0,0,465,466,5,0,0,1,466,63,1,0,
+ 0,0,467,468,5,29,0,0,468,469,5,85,0,0,469,470,5,78,0,0,470,471,3,
+ 66,33,0,471,65,1,0,0,0,472,473,5,7,0,0,473,482,5,1,0,0,474,483,3,
+ 72,36,0,475,483,3,76,38,0,476,483,3,78,39,0,477,483,3,84,42,0,478,
+ 483,3,86,43,0,479,483,3,68,34,0,480,483,3,70,35,0,481,483,3,74,37,
+ 0,482,474,1,0,0,0,482,475,1,0,0,0,482,476,1,0,0,0,482,477,1,0,0,
+ 0,482,478,1,0,0,0,482,479,1,0,0,0,482,480,1,0,0,0,482,481,1,0,0,
+ 0,483,484,1,0,0,0,484,482,1,0,0,0,484,485,1,0,0,0,485,486,1,0,0,
+ 0,486,487,5,2,0,0,487,67,1,0,0,0,488,489,5,38,0,0,489,490,5,45,0,
+ 0,490,495,3,24,12,0,491,492,5,70,0,0,492,494,3,92,46,0,493,491,1,
+ 0,0,0,494,497,1,0,0,0,495,493,1,0,0,0,495,496,1,0,0,0,496,498,1,
+ 0,0,0,497,495,1,0,0,0,498,499,5,46,0,0,499,500,5,78,0,0,500,501,
+ 5,7,0,0,501,502,5,1,0,0,502,503,3,46,23,0,503,504,5,2,0,0,504,69,
+ 1,0,0,0,505,506,5,39,0,0,506,507,5,45,0,0,507,512,3,12,6,0,508,509,
+ 5,70,0,0,509,511,3,92,46,0,510,508,1,0,0,0,511,514,1,0,0,0,512,510,
+ 1,0,0,0,512,513,1,0,0,0,513,515,1,0,0,0,514,512,1,0,0,0,515,516,
+ 5,46,0,0,516,517,5,78,0,0,517,518,5,7,0,0,518,519,5,1,0,0,519,520,
+ 3,46,23,0,520,521,5,2,0,0,521,71,1,0,0,0,522,523,7,2,0,0,523,524,
+ 5,78,0,0,524,525,5,7,0,0,525,527,5,1,0,0,526,528,3,44,22,0,527,526,
+ 1,0,0,0,528,529,1,0,0,0,529,527,1,0,0,0,529,530,1,0,0,0,530,531,
+ 1,0,0,0,531,532,5,2,0,0,532,73,1,0,0,0,533,534,5,33,0,0,534,535,
+ 5,78,0,0,535,536,5,7,0,0,536,537,5,1,0,0,537,538,3,46,23,0,538,539,
+ 5,2,0,0,539,75,1,0,0,0,540,541,5,34,0,0,541,542,5,78,0,0,542,543,
+ 5,7,0,0,543,547,5,1,0,0,544,548,3,28,14,0,545,548,3,30,15,0,546,
+ 548,3,32,16,0,547,544,1,0,0,0,547,545,1,0,0,0,547,546,1,0,0,0,548,
+ 549,1,0,0,0,549,547,1,0,0,0,549,550,1,0,0,0,550,551,1,0,0,0,551,
+ 552,5,2,0,0,552,77,1,0,0,0,553,554,5,35,0,0,554,555,5,78,0,0,555,
+ 556,5,7,0,0,556,559,5,1,0,0,557,560,3,80,40,0,558,560,3,82,41,0,
+ 559,557,1,0,0,0,559,558,1,0,0,0,560,561,1,0,0,0,561,559,1,0,0,0,
+ 561,562,1,0,0,0,562,563,1,0,0,0,563,564,5,2,0,0,564,79,1,0,0,0,565,
+ 570,5,85,0,0,566,567,5,52,0,0,567,568,3,12,6,0,568,569,5,54,0,0,
+ 569,571,1,0,0,0,570,566,1,0,0,0,570,571,1,0,0,0,571,572,1,0,0,0,
+ 572,573,5,53,0,0,573,586,5,40,0,0,574,583,5,45,0,0,575,580,3,88,
+ 44,0,576,577,5,70,0,0,577,579,3,88,44,0,578,576,1,0,0,0,579,582,
+ 1,0,0,0,580,578,1,0,0,0,580,581,1,0,0,0,581,584,1,0,0,0,582,580,
+ 1,0,0,0,583,575,1,0,0,0,583,584,1,0,0,0,584,585,1,0,0,0,585,587,
+ 5,46,0,0,586,574,1,0,0,0,586,587,1,0,0,0,587,588,1,0,0,0,588,589,
+ 5,7,0,0,589,81,1,0,0,0,590,595,5,85,0,0,591,592,5,52,0,0,592,593,
+ 3,12,6,0,593,594,5,54,0,0,594,596,1,0,0,0,595,591,1,0,0,0,595,596,
+ 1,0,0,0,596,597,1,0,0,0,597,598,3,0,0,0,598,599,5,53,0,0,599,612,
+ 5,37,0,0,600,609,5,45,0,0,601,606,3,88,44,0,602,603,5,70,0,0,603,
+ 605,3,88,44,0,604,602,1,0,0,0,605,608,1,0,0,0,606,604,1,0,0,0,606,
+ 607,1,0,0,0,607,610,1,0,0,0,608,606,1,0,0,0,609,601,1,0,0,0,609,
+ 610,1,0,0,0,610,611,1,0,0,0,611,613,5,46,0,0,612,600,1,0,0,0,612,
+ 613,1,0,0,0,613,614,1,0,0,0,614,615,5,7,0,0,615,83,1,0,0,0,616,617,
+ 5,36,0,0,617,618,5,78,0,0,618,619,5,7,0,0,619,636,5,1,0,0,620,633,
+ 5,40,0,0,621,630,5,45,0,0,622,627,3,88,44,0,623,624,5,70,0,0,624,
+ 626,3,88,44,0,625,623,1,0,0,0,626,629,1,0,0,0,627,625,1,0,0,0,627,
+ 628,1,0,0,0,628,631,1,0,0,0,629,627,1,0,0,0,630,622,1,0,0,0,630,
+ 631,1,0,0,0,631,632,1,0,0,0,632,634,5,46,0,0,633,621,1,0,0,0,633,
+ 634,1,0,0,0,634,637,1,0,0,0,635,637,5,37,0,0,636,620,1,0,0,0,636,
+ 635,1,0,0,0,637,638,1,0,0,0,638,639,5,7,0,0,639,640,5,2,0,0,640,
+ 85,1,0,0,0,641,642,5,13,0,0,642,643,5,85,0,0,643,652,5,45,0,0,644,
+ 649,3,88,44,0,645,646,5,70,0,0,646,648,3,88,44,0,647,645,1,0,0,0,
+ 648,651,1,0,0,0,649,647,1,0,0,0,649,650,1,0,0,0,650,653,1,0,0,0,
+ 651,649,1,0,0,0,652,644,1,0,0,0,652,653,1,0,0,0,653,654,1,0,0,0,
+ 654,656,5,46,0,0,655,657,3,0,0,0,656,655,1,0,0,0,656,657,1,0,0,0,
+ 657,658,1,0,0,0,658,659,5,78,0,0,659,660,5,7,0,0,660,661,5,1,0,0,
+ 661,662,3,46,23,0,662,663,5,2,0,0,663,87,1,0,0,0,664,665,5,85,0,
+ 0,665,666,3,0,0,0,666,89,1,0,0,0,667,670,3,88,44,0,668,670,3,12,
+ 6,0,669,667,1,0,0,0,669,668,1,0,0,0,670,91,1,0,0,0,671,672,5,85,
+ 0,0,672,673,5,72,0,0,673,674,7,3,0,0,674,93,1,0,0,0,76,100,103,111,
114,125,130,136,138,148,165,174,180,201,203,210,215,220,227,236,
- 240,247,252,262,265,270,278,283,292,297,313,317,323,328,334,344,
- 349,352,359,365,371,376,383,388,390,395,401,405,438,457,459,478,
- 480,491,508,525,543,545,555,563,566,569,573,582,588,599,608,621,
- 624,627,630,643,646,650
+ 240,247,252,257,266,269,274,282,287,296,301,317,321,327,332,338,
+ 348,353,356,363,369,375,380,387,392,394,399,405,409,442,461,463,
+ 482,484,495,512,529,547,549,559,561,570,580,583,586,595,606,609,
+ 612,627,630,633,636,649,652,656,669
]
class PyNestMLParser ( Parser ):
@@ -277,14 +281,13 @@ class PyNestMLParser ( Parser ):
"'and'", "'or'", "'not'", "'recordable'", "'kernel'",
"'model'", "'state'", "'parameters'", "'internals'",
"'update'", "'equations'", "'input'", "'output'", "'continuous'",
- "'onReceive'", "'onCondition'", "'spike'", "'inhibitory'",
- "'excitatory'", "'@homogeneous'", "'@heterogeneous'",
- "'@'", "'...'", "'('", "')'", "'+'", "'~'", "'|'",
- "'^'", "'&'", "'['", "'<-'", "']'", "'[['", "']]'",
- "'<<'", "'>>'", "'<'", "'>'", "'<='", "'+='", "'-='",
- "'*='", "'/='", "'=='", "'!='", "'<>'", "'>='", "','",
- "'-'", "'='", "'*'", "'**'", "'/'", "'%'", "'?'", "':'",
- "'::'", "';'", "'''", "'.'" ]
+ "'onReceive'", "'onCondition'", "'spike'", "'@homogeneous'",
+ "'@heterogeneous'", "'@'", "'...'", "'('", "')'", "'+'",
+ "'~'", "'|'", "'^'", "'&'", "'['", "'<-'", "']'", "'[['",
+ "']]'", "'<<'", "'>>'", "'<'", "'>'", "'<='", "'+='",
+ "'-='", "'*='", "'/='", "'=='", "'!='", "'<>'", "'>='",
+ "','", "'-'", "'='", "'*'", "'**'", "'/'", "'%'", "'?'",
+ "':'", "'::'", "';'", "'''", "'.'" ]
symbolicNames = [ "", "INDENT", "DEDENT", "KERNEL_JOINING",
"WS", "LINE_ESCAPE", "SL_COMMENT", "NEWLINE", "INTEGER_KEYWORD",
@@ -297,20 +300,19 @@ class PyNestMLParser ( Parser ):
"STATE_KEYWORD", "PARAMETERS_KEYWORD", "INTERNALS_KEYWORD",
"UPDATE_KEYWORD", "EQUATIONS_KEYWORD", "INPUT_KEYWORD",
"OUTPUT_KEYWORD", "CONTINUOUS_KEYWORD", "ON_RECEIVE_KEYWORD",
- "ON_CONDITION_KEYWORD", "SPIKE_KEYWORD", "INHIBITORY_KEYWORD",
- "EXCITATORY_KEYWORD", "DECORATOR_HOMOGENEOUS", "DECORATOR_HETEROGENEOUS",
- "AT", "ELLIPSIS", "LEFT_PAREN", "RIGHT_PAREN", "PLUS",
- "TILDE", "PIPE", "CARET", "AMPERSAND", "LEFT_SQUARE_BRACKET",
- "LEFT_ANGLE_MINUS", "RIGHT_SQUARE_BRACKET", "LEFT_LEFT_SQUARE",
- "RIGHT_RIGHT_SQUARE", "LEFT_LEFT_ANGLE", "RIGHT_RIGHT_ANGLE",
- "LEFT_ANGLE", "RIGHT_ANGLE", "LEFT_ANGLE_EQUALS",
- "PLUS_EQUALS", "MINUS_EQUALS", "STAR_EQUALS", "FORWARD_SLASH_EQUALS",
- "EQUALS_EQUALS", "EXCLAMATION_EQUALS", "LEFT_ANGLE_RIGHT_ANGLE",
- "RIGHT_ANGLE_EQUALS", "COMMA", "MINUS", "EQUALS",
- "STAR", "STAR_STAR", "FORWARD_SLASH", "PERCENT", "QUESTION",
- "COLON", "DOUBLE_COLON", "SEMICOLON", "DIFFERENTIAL_ORDER",
- "FULLSTOP", "BOOLEAN_LITERAL", "STRING_LITERAL", "NAME",
- "UNSIGNED_INTEGER", "FLOAT" ]
+ "ON_CONDITION_KEYWORD", "SPIKE_KEYWORD", "DECORATOR_HOMOGENEOUS",
+ "DECORATOR_HETEROGENEOUS", "AT", "ELLIPSIS", "LEFT_PAREN",
+ "RIGHT_PAREN", "PLUS", "TILDE", "PIPE", "CARET", "AMPERSAND",
+ "LEFT_SQUARE_BRACKET", "LEFT_ANGLE_MINUS", "RIGHT_SQUARE_BRACKET",
+ "LEFT_LEFT_SQUARE", "RIGHT_RIGHT_SQUARE", "LEFT_LEFT_ANGLE",
+ "RIGHT_RIGHT_ANGLE", "LEFT_ANGLE", "RIGHT_ANGLE",
+ "LEFT_ANGLE_EQUALS", "PLUS_EQUALS", "MINUS_EQUALS",
+ "STAR_EQUALS", "FORWARD_SLASH_EQUALS", "EQUALS_EQUALS",
+ "EXCLAMATION_EQUALS", "LEFT_ANGLE_RIGHT_ANGLE", "RIGHT_ANGLE_EQUALS",
+ "COMMA", "MINUS", "EQUALS", "STAR", "STAR_STAR", "FORWARD_SLASH",
+ "PERCENT", "QUESTION", "COLON", "DOUBLE_COLON", "SEMICOLON",
+ "DIFFERENTIAL_ORDER", "FULLSTOP", "BOOLEAN_LITERAL",
+ "STRING_LITERAL", "NAME", "UNSIGNED_INTEGER", "FLOAT" ]
RULE_dataType = 0
RULE_unitTypeExponent = 1
@@ -354,10 +356,10 @@ class PyNestMLParser ( Parser ):
RULE_inputBlock = 39
RULE_spikeInputPort = 40
RULE_continuousInputPort = 41
- RULE_inputQualifier = 42
- RULE_outputBlock = 43
- RULE_function = 44
- RULE_parameter = 45
+ RULE_outputBlock = 42
+ RULE_function = 43
+ RULE_parameter = 44
+ RULE_expressionOrParameter = 45
RULE_constParameter = 46
ruleNames = [ "dataType", "unitTypeExponent", "unitType", "anyDecorator",
@@ -371,7 +373,7 @@ class PyNestMLParser ( Parser ):
"nestMLCompilationUnit", "model", "modelBody", "onReceiveBlock",
"onConditionBlock", "blockWithVariables", "updateBlock",
"equationsBlock", "inputBlock", "spikeInputPort", "continuousInputPort",
- "inputQualifier", "outputBlock", "function", "parameter",
+ "outputBlock", "function", "parameter", "expressionOrParameter",
"constParameter" ]
EOF = Token.EOF
@@ -415,59 +417,57 @@ class PyNestMLParser ( Parser ):
ON_RECEIVE_KEYWORD=38
ON_CONDITION_KEYWORD=39
SPIKE_KEYWORD=40
- INHIBITORY_KEYWORD=41
- EXCITATORY_KEYWORD=42
- DECORATOR_HOMOGENEOUS=43
- DECORATOR_HETEROGENEOUS=44
- AT=45
- ELLIPSIS=46
- LEFT_PAREN=47
- RIGHT_PAREN=48
- PLUS=49
- TILDE=50
- PIPE=51
- CARET=52
- AMPERSAND=53
- LEFT_SQUARE_BRACKET=54
- LEFT_ANGLE_MINUS=55
- RIGHT_SQUARE_BRACKET=56
- LEFT_LEFT_SQUARE=57
- RIGHT_RIGHT_SQUARE=58
- LEFT_LEFT_ANGLE=59
- RIGHT_RIGHT_ANGLE=60
- LEFT_ANGLE=61
- RIGHT_ANGLE=62
- LEFT_ANGLE_EQUALS=63
- PLUS_EQUALS=64
- MINUS_EQUALS=65
- STAR_EQUALS=66
- FORWARD_SLASH_EQUALS=67
- EQUALS_EQUALS=68
- EXCLAMATION_EQUALS=69
- LEFT_ANGLE_RIGHT_ANGLE=70
- RIGHT_ANGLE_EQUALS=71
- COMMA=72
- MINUS=73
- EQUALS=74
- STAR=75
- STAR_STAR=76
- FORWARD_SLASH=77
- PERCENT=78
- QUESTION=79
- COLON=80
- DOUBLE_COLON=81
- SEMICOLON=82
- DIFFERENTIAL_ORDER=83
- FULLSTOP=84
- BOOLEAN_LITERAL=85
- STRING_LITERAL=86
- NAME=87
- UNSIGNED_INTEGER=88
- FLOAT=89
+ DECORATOR_HOMOGENEOUS=41
+ DECORATOR_HETEROGENEOUS=42
+ AT=43
+ ELLIPSIS=44
+ LEFT_PAREN=45
+ RIGHT_PAREN=46
+ PLUS=47
+ TILDE=48
+ PIPE=49
+ CARET=50
+ AMPERSAND=51
+ LEFT_SQUARE_BRACKET=52
+ LEFT_ANGLE_MINUS=53
+ RIGHT_SQUARE_BRACKET=54
+ LEFT_LEFT_SQUARE=55
+ RIGHT_RIGHT_SQUARE=56
+ LEFT_LEFT_ANGLE=57
+ RIGHT_RIGHT_ANGLE=58
+ LEFT_ANGLE=59
+ RIGHT_ANGLE=60
+ LEFT_ANGLE_EQUALS=61
+ PLUS_EQUALS=62
+ MINUS_EQUALS=63
+ STAR_EQUALS=64
+ FORWARD_SLASH_EQUALS=65
+ EQUALS_EQUALS=66
+ EXCLAMATION_EQUALS=67
+ LEFT_ANGLE_RIGHT_ANGLE=68
+ RIGHT_ANGLE_EQUALS=69
+ COMMA=70
+ MINUS=71
+ EQUALS=72
+ STAR=73
+ STAR_STAR=74
+ FORWARD_SLASH=75
+ PERCENT=76
+ QUESTION=77
+ COLON=78
+ DOUBLE_COLON=79
+ SEMICOLON=80
+ DIFFERENTIAL_ORDER=81
+ FULLSTOP=82
+ BOOLEAN_LITERAL=83
+ STRING_LITERAL=84
+ NAME=85
+ UNSIGNED_INTEGER=86
+ FLOAT=87
def __init__(self, input:TokenStream, output:TextIO = sys.stdout):
super().__init__(input, output)
- self.checkVersion("4.13.2")
+ self.checkVersion("4.13.1")
self._interp = ParserATNSimulator(self, self.atn, self.decisionsToDFA, self.sharedContextCache)
self._predicates = None
@@ -551,7 +551,7 @@ def dataType(self):
self.state = 98
localctx.isVoid = self.match(PyNestMLParser.VOID_KEYWORD)
pass
- elif token in [47, 87, 88]:
+ elif token in [45, 85, 86]:
self.enterOuterAlt(localctx, 6)
self.state = 99
localctx.unit = self.unitType(0)
@@ -631,7 +631,7 @@ def unitTypeExponent(self):
self.state = 103
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==73:
+ if _la==71:
self.state = 102
localctx.negative = self.match(PyNestMLParser.MINUS)
@@ -653,10 +653,10 @@ def unitTypeExponent(self):
self.state = 111
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==49 or _la==73:
+ if _la==47 or _la==71:
self.state = 110
_la = self._input.LA(1)
- if not(_la==49 or _la==73):
+ if not(_la==47 or _la==71):
self._errHandler.recoverInline(self)
else:
self._errHandler.reportMatch(self)
@@ -665,7 +665,7 @@ def unitTypeExponent(self):
self.state = 113
_la = self._input.LA(1)
- if not(_la==88 or _la==89):
+ if not(_la==86 or _la==87):
self._errHandler.recoverInline(self)
else:
self._errHandler.reportMatch(self)
@@ -756,7 +756,7 @@ def unitType(self, _p:int=0):
self.state = 125
self._errHandler.sync(self)
token = self._input.LA(1)
- if token in [47]:
+ if token in [45]:
self.state = 117
localctx.leftParentheses = self.match(PyNestMLParser.LEFT_PAREN)
self.state = 118
@@ -764,7 +764,7 @@ def unitType(self, _p:int=0):
self.state = 119
localctx.rightParentheses = self.match(PyNestMLParser.RIGHT_PAREN)
pass
- elif token in [88]:
+ elif token in [86]:
self.state = 121
localctx.unitlessLiteral = self.match(PyNestMLParser.UNSIGNED_INTEGER)
self.state = 122
@@ -772,7 +772,7 @@ def unitType(self, _p:int=0):
self.state = 123
localctx.right = self.unitType(2)
pass
- elif token in [87]:
+ elif token in [85]:
self.state = 124
localctx.unit = self.match(PyNestMLParser.NAME)
pass
@@ -802,11 +802,11 @@ def unitType(self, _p:int=0):
self.state = 130
self._errHandler.sync(self)
token = self._input.LA(1)
- if token in [75]:
+ if token in [73]:
self.state = 128
localctx.timesOp = self.match(PyNestMLParser.STAR)
pass
- elif token in [77]:
+ elif token in [75]:
self.state = 129
localctx.divOp = self.match(PyNestMLParser.FORWARD_SLASH)
pass
@@ -892,17 +892,17 @@ def anyDecorator(self):
self.state = 148
self._errHandler.sync(self)
token = self._input.LA(1)
- if token in [43]:
+ if token in [41]:
self.enterOuterAlt(localctx, 1)
self.state = 141
self.match(PyNestMLParser.DECORATOR_HOMOGENEOUS)
pass
- elif token in [44]:
+ elif token in [42]:
self.enterOuterAlt(localctx, 2)
self.state = 142
self.match(PyNestMLParser.DECORATOR_HETEROGENEOUS)
pass
- elif token in [45]:
+ elif token in [43]:
self.enterOuterAlt(localctx, 3)
self.state = 143
self.match(PyNestMLParser.AT)
@@ -1110,7 +1110,7 @@ def expression(self, _p:int=0):
self.state = 165
self._errHandler.sync(self)
token = self._input.LA(1)
- if token in [47]:
+ if token in [45]:
self.state = 155
localctx.leftParentheses = self.match(PyNestMLParser.LEFT_PAREN)
self.state = 156
@@ -1118,7 +1118,7 @@ def expression(self, _p:int=0):
self.state = 157
localctx.rightParentheses = self.match(PyNestMLParser.RIGHT_PAREN)
pass
- elif token in [49, 50, 73]:
+ elif token in [47, 48, 71]:
self.state = 159
self.unaryOperator()
self.state = 160
@@ -1130,7 +1130,7 @@ def expression(self, _p:int=0):
self.state = 163
localctx.term = self.expression(4)
pass
- elif token in [23, 85, 86, 87, 88, 89]:
+ elif token in [23, 83, 84, 85, 86, 87]:
self.state = 164
self.simpleExpression()
pass
@@ -1174,15 +1174,15 @@ def expression(self, _p:int=0):
self.state = 174
self._errHandler.sync(self)
token = self._input.LA(1)
- if token in [75]:
+ if token in [73]:
self.state = 171
localctx.timesOp = self.match(PyNestMLParser.STAR)
pass
- elif token in [77]:
+ elif token in [75]:
self.state = 172
localctx.divOp = self.match(PyNestMLParser.FORWARD_SLASH)
pass
- elif token in [78]:
+ elif token in [76]:
self.state = 173
localctx.moduloOp = self.match(PyNestMLParser.PERCENT)
pass
@@ -1204,11 +1204,11 @@ def expression(self, _p:int=0):
self.state = 180
self._errHandler.sync(self)
token = self._input.LA(1)
- if token in [49]:
+ if token in [47]:
self.state = 178
localctx.plusOp = self.match(PyNestMLParser.PLUS)
pass
- elif token in [73]:
+ elif token in [71]:
self.state = 179
localctx.minusOp = self.match(PyNestMLParser.MINUS)
pass
@@ -1362,7 +1362,7 @@ def simpleExpression(self):
self.enterOuterAlt(localctx, 3)
self.state = 208
_la = self._input.LA(1)
- if not(_la==88 or _la==89):
+ if not(_la==86 or _la==87):
self._errHandler.recoverInline(self)
else:
self._errHandler.reportMatch(self)
@@ -1444,17 +1444,17 @@ def unaryOperator(self):
self.state = 220
self._errHandler.sync(self)
token = self._input.LA(1)
- if token in [49]:
+ if token in [47]:
self.enterOuterAlt(localctx, 1)
self.state = 217
localctx.unaryPlus = self.match(PyNestMLParser.PLUS)
pass
- elif token in [73]:
+ elif token in [71]:
self.enterOuterAlt(localctx, 2)
self.state = 218
localctx.unaryMinus = self.match(PyNestMLParser.MINUS)
pass
- elif token in [50]:
+ elif token in [48]:
self.enterOuterAlt(localctx, 3)
self.state = 219
localctx.unaryTilde = self.match(PyNestMLParser.TILDE)
@@ -1518,27 +1518,27 @@ def bitOperator(self):
self.state = 227
self._errHandler.sync(self)
token = self._input.LA(1)
- if token in [53]:
+ if token in [51]:
self.enterOuterAlt(localctx, 1)
self.state = 222
localctx.bitAnd = self.match(PyNestMLParser.AMPERSAND)
pass
- elif token in [52]:
+ elif token in [50]:
self.enterOuterAlt(localctx, 2)
self.state = 223
localctx.bitXor = self.match(PyNestMLParser.CARET)
pass
- elif token in [51]:
+ elif token in [49]:
self.enterOuterAlt(localctx, 3)
self.state = 224
localctx.bitOr = self.match(PyNestMLParser.PIPE)
pass
- elif token in [59]:
+ elif token in [57]:
self.enterOuterAlt(localctx, 4)
self.state = 225
localctx.bitShiftLeft = self.match(PyNestMLParser.LEFT_LEFT_ANGLE)
pass
- elif token in [60]:
+ elif token in [58]:
self.enterOuterAlt(localctx, 5)
self.state = 226
localctx.bitShiftRight = self.match(PyNestMLParser.RIGHT_RIGHT_ANGLE)
@@ -1610,37 +1610,37 @@ def comparisonOperator(self):
self.state = 236
self._errHandler.sync(self)
token = self._input.LA(1)
- if token in [61]:
+ if token in [59]:
self.enterOuterAlt(localctx, 1)
self.state = 229
localctx.lt = self.match(PyNestMLParser.LEFT_ANGLE)
pass
- elif token in [63]:
+ elif token in [61]:
self.enterOuterAlt(localctx, 2)
self.state = 230
localctx.le = self.match(PyNestMLParser.LEFT_ANGLE_EQUALS)
pass
- elif token in [68]:
+ elif token in [66]:
self.enterOuterAlt(localctx, 3)
self.state = 231
localctx.eq = self.match(PyNestMLParser.EQUALS_EQUALS)
pass
- elif token in [69]:
+ elif token in [67]:
self.enterOuterAlt(localctx, 4)
self.state = 232
localctx.ne = self.match(PyNestMLParser.EXCLAMATION_EQUALS)
pass
- elif token in [70]:
+ elif token in [68]:
self.enterOuterAlt(localctx, 5)
self.state = 233
localctx.ne2 = self.match(PyNestMLParser.LEFT_ANGLE_RIGHT_ANGLE)
pass
- elif token in [71]:
+ elif token in [69]:
self.enterOuterAlt(localctx, 6)
self.state = 234
localctx.ge = self.match(PyNestMLParser.RIGHT_ANGLE_EQUALS)
pass
- elif token in [62]:
+ elif token in [60]:
self.enterOuterAlt(localctx, 7)
self.state = 235
localctx.gt = self.match(PyNestMLParser.RIGHT_ANGLE)
@@ -1721,7 +1721,8 @@ def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
super().__init__(parent, invokingState)
self.parser = parser
self.name = None # Token
- self.vectorParameter = None # ExpressionContext
+ self.vectorParameter = None # ExpressionOrParameterContext
+ self.attribute = None # VariableContext
def NAME(self):
return self.getToken(PyNestMLParser.NAME, 0)
@@ -1738,8 +1739,15 @@ def DIFFERENTIAL_ORDER(self, i:int=None):
else:
return self.getToken(PyNestMLParser.DIFFERENTIAL_ORDER, i)
- def expression(self):
- return self.getTypedRuleContext(PyNestMLParser.ExpressionContext,0)
+ def FULLSTOP(self):
+ return self.getToken(PyNestMLParser.FULLSTOP, 0)
+
+ def expressionOrParameter(self):
+ return self.getTypedRuleContext(PyNestMLParser.ExpressionOrParameterContext,0)
+
+
+ def variable(self):
+ return self.getTypedRuleContext(PyNestMLParser.VariableContext,0)
def getRuleIndex(self):
@@ -1769,7 +1777,7 @@ def variable(self):
self.state = 243
self.match(PyNestMLParser.LEFT_SQUARE_BRACKET)
self.state = 244
- localctx.vectorParameter = self.expression(0)
+ localctx.vectorParameter = self.expressionOrParameter()
self.state = 245
self.match(PyNestMLParser.RIGHT_SQUARE_BRACKET)
@@ -1785,6 +1793,16 @@ def variable(self):
self._errHandler.sync(self)
_alt = self._interp.adaptivePredict(self._input,21,self._ctx)
+ self.state = 257
+ self._errHandler.sync(self)
+ la_ = self._interp.adaptivePredict(self._input,22,self._ctx)
+ if la_ == 1:
+ self.state = 255
+ self.match(PyNestMLParser.FULLSTOP)
+ self.state = 256
+ localctx.attribute = self.variable()
+
+
except RecognitionException as re:
localctx.exception = re
self._errHandler.reportError(self, re)
@@ -1843,31 +1861,31 @@ def functionCall(self):
self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
- self.state = 255
+ self.state = 259
localctx.calleeName = self.match(PyNestMLParser.NAME)
- self.state = 256
+ self.state = 260
self.match(PyNestMLParser.LEFT_PAREN)
- self.state = 265
+ self.state = 269
self._errHandler.sync(self)
_la = self._input.LA(1)
- if (((_la) & ~0x3f) == 0 and ((1 << _la) & 1829587424116736) != 0) or ((((_la - 73)) & ~0x3f) == 0 and ((1 << (_la - 73)) & 126977) != 0):
- self.state = 257
+ if (((_la) & ~0x3f) == 0 and ((1 << _la) & 457396912652288) != 0) or ((((_la - 71)) & ~0x3f) == 0 and ((1 << (_la - 71)) & 126977) != 0):
+ self.state = 261
self.expression(0)
- self.state = 262
+ self.state = 266
self._errHandler.sync(self)
_la = self._input.LA(1)
- while _la==72:
- self.state = 258
+ while _la==70:
+ self.state = 262
self.match(PyNestMLParser.COMMA)
- self.state = 259
+ self.state = 263
self.expression(0)
- self.state = 264
+ self.state = 268
self._errHandler.sync(self)
_la = self._input.LA(1)
- self.state = 267
+ self.state = 271
self.match(PyNestMLParser.RIGHT_PAREN)
except RecognitionException as re:
localctx.exception = re
@@ -1940,43 +1958,43 @@ def inlineExpression(self):
self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
- self.state = 270
+ self.state = 274
self._errHandler.sync(self)
_la = self._input.LA(1)
if _la==27:
- self.state = 269
+ self.state = 273
localctx.recordable = self.match(PyNestMLParser.RECORDABLE_KEYWORD)
- self.state = 272
+ self.state = 276
self.match(PyNestMLParser.INLINE_KEYWORD)
- self.state = 273
+ self.state = 277
localctx.variableName = self.match(PyNestMLParser.NAME)
- self.state = 274
+ self.state = 278
self.dataType()
- self.state = 275
+ self.state = 279
self.match(PyNestMLParser.EQUALS)
- self.state = 276
+ self.state = 280
self.expression(0)
- self.state = 278
+ self.state = 282
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==82:
- self.state = 277
+ if _la==80:
+ self.state = 281
self.match(PyNestMLParser.SEMICOLON)
- self.state = 283
+ self.state = 287
self._errHandler.sync(self)
_la = self._input.LA(1)
- while (((_la) & ~0x3f) == 0 and ((1 << _la) & 61572651155456) != 0):
- self.state = 280
+ while (((_la) & ~0x3f) == 0 and ((1 << _la) & 15393162788864) != 0):
+ self.state = 284
localctx.decorator = self.anyDecorator()
- self.state = 285
+ self.state = 289
self._errHandler.sync(self)
_la = self._input.LA(1)
- self.state = 286
+ self.state = 290
self.match(PyNestMLParser.NEWLINE)
except RecognitionException as re:
localctx.exception = re
@@ -2040,31 +2058,31 @@ def odeEquation(self):
self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
- self.state = 288
+ self.state = 292
localctx.lhs = self.variable()
- self.state = 289
+ self.state = 293
self.match(PyNestMLParser.EQUALS)
- self.state = 290
+ self.state = 294
localctx.rhs = self.expression(0)
- self.state = 292
+ self.state = 296
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==82:
- self.state = 291
+ if _la==80:
+ self.state = 295
self.match(PyNestMLParser.SEMICOLON)
- self.state = 297
+ self.state = 301
self._errHandler.sync(self)
_la = self._input.LA(1)
- while (((_la) & ~0x3f) == 0 and ((1 << _la) & 61572651155456) != 0):
- self.state = 294
+ while (((_la) & ~0x3f) == 0 and ((1 << _la) & 15393162788864) != 0):
+ self.state = 298
localctx.decorator = self.anyDecorator()
- self.state = 299
+ self.state = 303
self._errHandler.sync(self)
_la = self._input.LA(1)
- self.state = 300
+ self.state = 304
self.match(PyNestMLParser.NEWLINE)
except RecognitionException as re:
localctx.exception = re
@@ -2136,39 +2154,39 @@ def kernel(self):
self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
- self.state = 302
+ self.state = 306
self.match(PyNestMLParser.KERNEL_KEYWORD)
- self.state = 303
+ self.state = 307
self.variable()
- self.state = 304
+ self.state = 308
self.match(PyNestMLParser.EQUALS)
- self.state = 305
+ self.state = 309
self.expression(0)
- self.state = 313
+ self.state = 317
self._errHandler.sync(self)
_la = self._input.LA(1)
while _la==3:
- self.state = 306
+ self.state = 310
self.match(PyNestMLParser.KERNEL_JOINING)
- self.state = 307
+ self.state = 311
self.variable()
- self.state = 308
+ self.state = 312
self.match(PyNestMLParser.EQUALS)
- self.state = 309
+ self.state = 313
self.expression(0)
- self.state = 315
+ self.state = 319
self._errHandler.sync(self)
_la = self._input.LA(1)
- self.state = 317
+ self.state = 321
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==82:
- self.state = 316
+ if _la==80:
+ self.state = 320
self.match(PyNestMLParser.SEMICOLON)
- self.state = 319
+ self.state = 323
self.match(PyNestMLParser.NEWLINE)
except RecognitionException as re:
localctx.exception = re
@@ -2211,17 +2229,17 @@ def stmt(self):
localctx = PyNestMLParser.StmtContext(self, self._ctx, self.state)
self.enterRule(localctx, 34, self.RULE_stmt)
try:
- self.state = 323
+ self.state = 327
self._errHandler.sync(self)
token = self._input.LA(1)
- if token in [14, 15, 27, 87]:
+ if token in [14, 15, 27, 85]:
self.enterOuterAlt(localctx, 1)
- self.state = 321
+ self.state = 325
self.smallStmt()
pass
elif token in [16, 19, 20]:
self.enterOuterAlt(localctx, 2)
- self.state = 322
+ self.state = 326
self.compoundStmt()
pass
else:
@@ -2272,22 +2290,22 @@ def compoundStmt(self):
localctx = PyNestMLParser.CompoundStmtContext(self, self._ctx, self.state)
self.enterRule(localctx, 36, self.RULE_compoundStmt)
try:
- self.state = 328
+ self.state = 332
self._errHandler.sync(self)
token = self._input.LA(1)
if token in [16]:
self.enterOuterAlt(localctx, 1)
- self.state = 325
+ self.state = 329
self.ifStmt()
pass
elif token in [19]:
self.enterOuterAlt(localctx, 2)
- self.state = 326
+ self.state = 330
self.forStmt()
pass
elif token in [20]:
self.enterOuterAlt(localctx, 3)
- self.state = 327
+ self.state = 331
self.whileStmt()
pass
else:
@@ -2346,31 +2364,31 @@ def smallStmt(self):
self.enterRule(localctx, 38, self.RULE_smallStmt)
try:
self.enterOuterAlt(localctx, 1)
- self.state = 334
+ self.state = 338
self._errHandler.sync(self)
- la_ = self._interp.adaptivePredict(self._input,33,self._ctx)
+ la_ = self._interp.adaptivePredict(self._input,34,self._ctx)
if la_ == 1:
- self.state = 330
+ self.state = 334
self.assignment()
pass
elif la_ == 2:
- self.state = 331
+ self.state = 335
self.functionCall()
pass
elif la_ == 3:
- self.state = 332
+ self.state = 336
self.declaration()
pass
elif la_ == 4:
- self.state = 333
+ self.state = 337
self.returnStmt()
pass
- self.state = 336
+ self.state = 340
self.match(PyNestMLParser.NEWLINE)
except RecognitionException as re:
localctx.exception = re
@@ -2435,35 +2453,35 @@ def assignment(self):
self.enterRule(localctx, 40, self.RULE_assignment)
try:
self.enterOuterAlt(localctx, 1)
- self.state = 338
+ self.state = 342
localctx.lhs_variable = self.variable()
- self.state = 344
+ self.state = 348
self._errHandler.sync(self)
token = self._input.LA(1)
- if token in [74]:
- self.state = 339
+ if token in [72]:
+ self.state = 343
localctx.directAssignment = self.match(PyNestMLParser.EQUALS)
pass
- elif token in [64]:
- self.state = 340
+ elif token in [62]:
+ self.state = 344
localctx.compoundSum = self.match(PyNestMLParser.PLUS_EQUALS)
pass
- elif token in [65]:
- self.state = 341
+ elif token in [63]:
+ self.state = 345
localctx.compoundMinus = self.match(PyNestMLParser.MINUS_EQUALS)
pass
- elif token in [66]:
- self.state = 342
+ elif token in [64]:
+ self.state = 346
localctx.compoundProduct = self.match(PyNestMLParser.STAR_EQUALS)
pass
- elif token in [67]:
- self.state = 343
+ elif token in [65]:
+ self.state = 347
localctx.compoundQuotient = self.match(PyNestMLParser.FORWARD_SLASH_EQUALS)
pass
else:
raise NoViableAltException(self)
- self.state = 346
+ self.state = 350
self.expression(0)
except RecognitionException as re:
localctx.exception = re
@@ -2551,67 +2569,67 @@ def declaration(self):
self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
- self.state = 349
+ self.state = 353
self._errHandler.sync(self)
_la = self._input.LA(1)
if _la==27:
- self.state = 348
+ self.state = 352
localctx.isRecordable = self.match(PyNestMLParser.RECORDABLE_KEYWORD)
- self.state = 352
+ self.state = 356
self._errHandler.sync(self)
_la = self._input.LA(1)
if _la==14:
- self.state = 351
+ self.state = 355
localctx.isInlineExpression = self.match(PyNestMLParser.INLINE_KEYWORD)
- self.state = 354
+ self.state = 358
self.variable()
- self.state = 359
+ self.state = 363
self._errHandler.sync(self)
_la = self._input.LA(1)
- while _la==72:
- self.state = 355
+ while _la==70:
+ self.state = 359
self.match(PyNestMLParser.COMMA)
- self.state = 356
+ self.state = 360
self.variable()
- self.state = 361
+ self.state = 365
self._errHandler.sync(self)
_la = self._input.LA(1)
- self.state = 362
+ self.state = 366
self.dataType()
- self.state = 365
+ self.state = 369
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==74:
- self.state = 363
+ if _la==72:
+ self.state = 367
self.match(PyNestMLParser.EQUALS)
- self.state = 364
+ self.state = 368
localctx.rhs = self.expression(0)
- self.state = 371
+ self.state = 375
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==57:
- self.state = 367
+ if _la==55:
+ self.state = 371
self.match(PyNestMLParser.LEFT_LEFT_SQUARE)
- self.state = 368
+ self.state = 372
localctx.invariant = self.expression(0)
- self.state = 369
+ self.state = 373
self.match(PyNestMLParser.RIGHT_RIGHT_SQUARE)
- self.state = 376
+ self.state = 380
self._errHandler.sync(self)
_la = self._input.LA(1)
- while (((_la) & ~0x3f) == 0 and ((1 << _la) & 61572651155456) != 0):
- self.state = 373
+ while (((_la) & ~0x3f) == 0 and ((1 << _la) & 15393162788864) != 0):
+ self.state = 377
localctx.decorator = self.anyDecorator()
- self.state = 378
+ self.state = 382
self._errHandler.sync(self)
_la = self._input.LA(1)
@@ -2656,9 +2674,9 @@ def declaration_newline(self):
self.enterRule(localctx, 44, self.RULE_declaration_newline)
try:
self.enterOuterAlt(localctx, 1)
- self.state = 379
+ self.state = 383
self.declaration()
- self.state = 380
+ self.state = 384
self.match(PyNestMLParser.NEWLINE)
except RecognitionException as re:
localctx.exception = re
@@ -2708,35 +2726,35 @@ def stmtsBody(self):
self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
- self.state = 383
+ self.state = 387
self._errHandler.sync(self)
_la = self._input.LA(1)
if _la==7:
- self.state = 382
+ self.state = 386
self.match(PyNestMLParser.NEWLINE)
- self.state = 385
+ self.state = 389
self.stmt()
- self.state = 390
+ self.state = 394
self._errHandler.sync(self)
_la = self._input.LA(1)
- while (((_la) & ~0x3f) == 0 and ((1 << _la) & 135905408) != 0) or _la==87:
- self.state = 388
+ while (((_la) & ~0x3f) == 0 and ((1 << _la) & 135905408) != 0) or _la==85:
+ self.state = 392
self._errHandler.sync(self)
token = self._input.LA(1)
if token in [7]:
- self.state = 386
+ self.state = 390
self.match(PyNestMLParser.NEWLINE)
pass
- elif token in [14, 15, 16, 19, 20, 27, 87]:
- self.state = 387
+ elif token in [14, 15, 16, 19, 20, 27, 85]:
+ self.state = 391
self.stmt()
pass
else:
raise NoViableAltException(self)
- self.state = 392
+ self.state = 396
self._errHandler.sync(self)
_la = self._input.LA(1)
@@ -2782,13 +2800,13 @@ def returnStmt(self):
self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
- self.state = 393
+ self.state = 397
self.match(PyNestMLParser.RETURN_KEYWORD)
- self.state = 395
+ self.state = 399
self._errHandler.sync(self)
_la = self._input.LA(1)
- if (((_la) & ~0x3f) == 0 and ((1 << _la) & 1829587424116736) != 0) or ((((_la - 73)) & ~0x3f) == 0 and ((1 << (_la - 73)) & 126977) != 0):
- self.state = 394
+ if (((_la) & ~0x3f) == 0 and ((1 << _la) & 457396912652288) != 0) or ((((_la - 71)) & ~0x3f) == 0 and ((1 << (_la - 71)) & 126977) != 0):
+ self.state = 398
self.expression(0)
@@ -2842,23 +2860,23 @@ def ifStmt(self):
self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
- self.state = 397
- self.ifClause()
self.state = 401
+ self.ifClause()
+ self.state = 405
self._errHandler.sync(self)
_la = self._input.LA(1)
while _la==17:
- self.state = 398
+ self.state = 402
self.elifClause()
- self.state = 403
+ self.state = 407
self._errHandler.sync(self)
_la = self._input.LA(1)
- self.state = 405
+ self.state = 409
self._errHandler.sync(self)
_la = self._input.LA(1)
if _la==18:
- self.state = 404
+ self.state = 408
self.elseClause()
@@ -2919,19 +2937,19 @@ def ifClause(self):
self.enterRule(localctx, 52, self.RULE_ifClause)
try:
self.enterOuterAlt(localctx, 1)
- self.state = 407
+ self.state = 411
self.match(PyNestMLParser.IF_KEYWORD)
- self.state = 408
+ self.state = 412
self.expression(0)
- self.state = 409
+ self.state = 413
self.match(PyNestMLParser.COLON)
- self.state = 410
+ self.state = 414
self.match(PyNestMLParser.NEWLINE)
- self.state = 411
+ self.state = 415
self.match(PyNestMLParser.INDENT)
- self.state = 412
+ self.state = 416
self.stmtsBody()
- self.state = 413
+ self.state = 417
self.match(PyNestMLParser.DEDENT)
except RecognitionException as re:
localctx.exception = re
@@ -2990,19 +3008,19 @@ def elifClause(self):
self.enterRule(localctx, 54, self.RULE_elifClause)
try:
self.enterOuterAlt(localctx, 1)
- self.state = 415
+ self.state = 419
self.match(PyNestMLParser.ELIF_KEYWORD)
- self.state = 416
+ self.state = 420
self.expression(0)
- self.state = 417
+ self.state = 421
self.match(PyNestMLParser.COLON)
- self.state = 418
+ self.state = 422
self.match(PyNestMLParser.NEWLINE)
- self.state = 419
+ self.state = 423
self.match(PyNestMLParser.INDENT)
- self.state = 420
+ self.state = 424
self.stmtsBody()
- self.state = 421
+ self.state = 425
self.match(PyNestMLParser.DEDENT)
except RecognitionException as re:
localctx.exception = re
@@ -3057,17 +3075,17 @@ def elseClause(self):
self.enterRule(localctx, 56, self.RULE_elseClause)
try:
self.enterOuterAlt(localctx, 1)
- self.state = 423
+ self.state = 427
self.match(PyNestMLParser.ELSE_KEYWORD)
- self.state = 424
+ self.state = 428
self.match(PyNestMLParser.COLON)
- self.state = 425
+ self.state = 429
self.match(PyNestMLParser.NEWLINE)
- self.state = 426
+ self.state = 430
self.match(PyNestMLParser.INDENT)
- self.state = 427
+ self.state = 431
self.stmtsBody()
- self.state = 428
+ self.state = 432
self.match(PyNestMLParser.DEDENT)
except RecognitionException as re:
localctx.exception = re
@@ -3155,45 +3173,45 @@ def forStmt(self):
self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
- self.state = 430
+ self.state = 434
self.match(PyNestMLParser.FOR_KEYWORD)
- self.state = 431
+ self.state = 435
localctx.var = self.match(PyNestMLParser.NAME)
- self.state = 432
+ self.state = 436
self.match(PyNestMLParser.IN_KEYWORD)
- self.state = 433
+ self.state = 437
localctx.start_from = self.expression(0)
- self.state = 434
+ self.state = 438
self.match(PyNestMLParser.ELLIPSIS)
- self.state = 435
+ self.state = 439
localctx.end_at = self.expression(0)
- self.state = 436
+ self.state = 440
self.match(PyNestMLParser.STEP_KEYWORD)
- self.state = 438
+ self.state = 442
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==73:
- self.state = 437
+ if _la==71:
+ self.state = 441
localctx.negative = self.match(PyNestMLParser.MINUS)
- self.state = 440
+ self.state = 444
_la = self._input.LA(1)
- if not(_la==88 or _la==89):
+ if not(_la==86 or _la==87):
self._errHandler.recoverInline(self)
else:
self._errHandler.reportMatch(self)
self.consume()
- self.state = 441
+ self.state = 445
self.match(PyNestMLParser.COLON)
- self.state = 442
+ self.state = 446
self.match(PyNestMLParser.NEWLINE)
- self.state = 443
+ self.state = 447
self.match(PyNestMLParser.INDENT)
- self.state = 444
+ self.state = 448
self.stmtsBody()
- self.state = 445
+ self.state = 449
self.match(PyNestMLParser.DEDENT)
except RecognitionException as re:
localctx.exception = re
@@ -3252,19 +3270,19 @@ def whileStmt(self):
self.enterRule(localctx, 60, self.RULE_whileStmt)
try:
self.enterOuterAlt(localctx, 1)
- self.state = 447
+ self.state = 451
self.match(PyNestMLParser.WHILE_KEYWORD)
- self.state = 448
+ self.state = 452
self.expression(0)
- self.state = 449
+ self.state = 453
self.match(PyNestMLParser.COLON)
- self.state = 450
+ self.state = 454
self.match(PyNestMLParser.NEWLINE)
- self.state = 451
+ self.state = 455
self.match(PyNestMLParser.INDENT)
- self.state = 452
+ self.state = 456
self.stmtsBody()
- self.state = 453
+ self.state = 457
self.match(PyNestMLParser.DEDENT)
except RecognitionException as re:
localctx.exception = re
@@ -3317,31 +3335,31 @@ def nestMLCompilationUnit(self):
self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
- self.state = 457
+ self.state = 461
self._errHandler.sync(self)
_la = self._input.LA(1)
while True:
- self.state = 457
+ self.state = 461
self._errHandler.sync(self)
token = self._input.LA(1)
if token in [29]:
- self.state = 455
+ self.state = 459
self.model()
pass
elif token in [7]:
- self.state = 456
+ self.state = 460
self.match(PyNestMLParser.NEWLINE)
pass
else:
raise NoViableAltException(self)
- self.state = 459
+ self.state = 463
self._errHandler.sync(self)
_la = self._input.LA(1)
if not (_la==7 or _la==29):
break
- self.state = 461
+ self.state = 465
self.match(PyNestMLParser.EOF)
except RecognitionException as re:
localctx.exception = re
@@ -3390,13 +3408,13 @@ def model(self):
self.enterRule(localctx, 64, self.RULE_model)
try:
self.enterOuterAlt(localctx, 1)
- self.state = 463
+ self.state = 467
self.match(PyNestMLParser.MODEL_KEYWORD)
- self.state = 464
+ self.state = 468
self.match(PyNestMLParser.NAME)
- self.state = 465
+ self.state = 469
self.match(PyNestMLParser.COLON)
- self.state = 466
+ self.state = 470
self.modelBody()
except RecognitionException as re:
localctx.exception = re
@@ -3498,59 +3516,59 @@ def modelBody(self):
self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
- self.state = 468
+ self.state = 472
self.match(PyNestMLParser.NEWLINE)
- self.state = 469
+ self.state = 473
self.match(PyNestMLParser.INDENT)
- self.state = 478
+ self.state = 482
self._errHandler.sync(self)
_la = self._input.LA(1)
while True:
- self.state = 478
+ self.state = 482
self._errHandler.sync(self)
token = self._input.LA(1)
if token in [30, 31, 32]:
- self.state = 470
+ self.state = 474
self.blockWithVariables()
pass
elif token in [34]:
- self.state = 471
+ self.state = 475
self.equationsBlock()
pass
elif token in [35]:
- self.state = 472
+ self.state = 476
self.inputBlock()
pass
elif token in [36]:
- self.state = 473
+ self.state = 477
self.outputBlock()
pass
elif token in [13]:
- self.state = 474
+ self.state = 478
self.function()
pass
elif token in [38]:
- self.state = 475
+ self.state = 479
self.onReceiveBlock()
pass
elif token in [39]:
- self.state = 476
+ self.state = 480
self.onConditionBlock()
pass
elif token in [33]:
- self.state = 477
+ self.state = 481
self.updateBlock()
pass
else:
raise NoViableAltException(self)
- self.state = 480
+ self.state = 484
self._errHandler.sync(self)
_la = self._input.LA(1)
if not ((((_la) & ~0x3f) == 0 and ((1 << _la) & 960998940672) != 0)):
break
- self.state = 482
+ self.state = 486
self.match(PyNestMLParser.DEDENT)
except RecognitionException as re:
localctx.exception = re
@@ -3567,7 +3585,7 @@ class OnReceiveBlockContext(ParserRuleContext):
def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
super().__init__(parent, invokingState)
self.parser = parser
- self.inputPortName = None # Token
+ self.inputPortVariable = None # VariableContext
def ON_RECEIVE_KEYWORD(self):
return self.getToken(PyNestMLParser.ON_RECEIVE_KEYWORD, 0)
@@ -3594,8 +3612,9 @@ def stmtsBody(self):
def DEDENT(self):
return self.getToken(PyNestMLParser.DEDENT, 0)
- def NAME(self):
- return self.getToken(PyNestMLParser.NAME, 0)
+ def variable(self):
+ return self.getTypedRuleContext(PyNestMLParser.VariableContext,0)
+
def COMMA(self, i:int=None):
if i is None:
@@ -3629,35 +3648,35 @@ def onReceiveBlock(self):
self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
- self.state = 484
+ self.state = 488
self.match(PyNestMLParser.ON_RECEIVE_KEYWORD)
- self.state = 485
+ self.state = 489
self.match(PyNestMLParser.LEFT_PAREN)
- self.state = 486
- localctx.inputPortName = self.match(PyNestMLParser.NAME)
- self.state = 491
+ self.state = 490
+ localctx.inputPortVariable = self.variable()
+ self.state = 495
self._errHandler.sync(self)
_la = self._input.LA(1)
- while _la==72:
- self.state = 487
+ while _la==70:
+ self.state = 491
self.match(PyNestMLParser.COMMA)
- self.state = 488
+ self.state = 492
self.constParameter()
- self.state = 493
+ self.state = 497
self._errHandler.sync(self)
_la = self._input.LA(1)
- self.state = 494
+ self.state = 498
self.match(PyNestMLParser.RIGHT_PAREN)
- self.state = 495
+ self.state = 499
self.match(PyNestMLParser.COLON)
- self.state = 496
+ self.state = 500
self.match(PyNestMLParser.NEWLINE)
- self.state = 497
+ self.state = 501
self.match(PyNestMLParser.INDENT)
- self.state = 498
+ self.state = 502
self.stmtsBody()
- self.state = 499
+ self.state = 503
self.match(PyNestMLParser.DEDENT)
except RecognitionException as re:
localctx.exception = re
@@ -3737,35 +3756,35 @@ def onConditionBlock(self):
self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
- self.state = 501
+ self.state = 505
self.match(PyNestMLParser.ON_CONDITION_KEYWORD)
- self.state = 502
+ self.state = 506
self.match(PyNestMLParser.LEFT_PAREN)
- self.state = 503
+ self.state = 507
localctx.condition = self.expression(0)
- self.state = 508
+ self.state = 512
self._errHandler.sync(self)
_la = self._input.LA(1)
- while _la==72:
- self.state = 504
+ while _la==70:
+ self.state = 508
self.match(PyNestMLParser.COMMA)
- self.state = 505
+ self.state = 509
self.constParameter()
- self.state = 510
+ self.state = 514
self._errHandler.sync(self)
_la = self._input.LA(1)
- self.state = 511
+ self.state = 515
self.match(PyNestMLParser.RIGHT_PAREN)
- self.state = 512
+ self.state = 516
self.match(PyNestMLParser.COLON)
- self.state = 513
+ self.state = 517
self.match(PyNestMLParser.NEWLINE)
- self.state = 514
+ self.state = 518
self.match(PyNestMLParser.INDENT)
- self.state = 515
+ self.state = 519
self.stmtsBody()
- self.state = 516
+ self.state = 520
self.match(PyNestMLParser.DEDENT)
except RecognitionException as re:
localctx.exception = re
@@ -3831,7 +3850,7 @@ def blockWithVariables(self):
self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
- self.state = 518
+ self.state = 522
localctx.blockType = self._input.LT(1)
_la = self._input.LA(1)
if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 7516192768) != 0)):
@@ -3839,25 +3858,25 @@ def blockWithVariables(self):
else:
self._errHandler.reportMatch(self)
self.consume()
- self.state = 519
+ self.state = 523
self.match(PyNestMLParser.COLON)
- self.state = 520
+ self.state = 524
self.match(PyNestMLParser.NEWLINE)
- self.state = 521
+ self.state = 525
self.match(PyNestMLParser.INDENT)
- self.state = 523
+ self.state = 527
self._errHandler.sync(self)
_la = self._input.LA(1)
while True:
- self.state = 522
+ self.state = 526
self.declaration_newline()
- self.state = 525
+ self.state = 529
self._errHandler.sync(self)
_la = self._input.LA(1)
- if not (_la==14 or _la==27 or _la==87):
+ if not (_la==14 or _la==27 or _la==85):
break
- self.state = 527
+ self.state = 531
self.match(PyNestMLParser.DEDENT)
except RecognitionException as re:
localctx.exception = re
@@ -3912,17 +3931,17 @@ def updateBlock(self):
self.enterRule(localctx, 74, self.RULE_updateBlock)
try:
self.enterOuterAlt(localctx, 1)
- self.state = 529
+ self.state = 533
self.match(PyNestMLParser.UPDATE_KEYWORD)
- self.state = 530
+ self.state = 534
self.match(PyNestMLParser.COLON)
- self.state = 531
+ self.state = 535
self.match(PyNestMLParser.NEWLINE)
- self.state = 532
+ self.state = 536
self.match(PyNestMLParser.INDENT)
- self.state = 533
+ self.state = 537
self.stmtsBody()
- self.state = 534
+ self.state = 538
self.match(PyNestMLParser.DEDENT)
except RecognitionException as re:
localctx.exception = re
@@ -3995,43 +4014,43 @@ def equationsBlock(self):
self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
- self.state = 536
+ self.state = 540
self.match(PyNestMLParser.EQUATIONS_KEYWORD)
- self.state = 537
+ self.state = 541
self.match(PyNestMLParser.COLON)
- self.state = 538
+ self.state = 542
self.match(PyNestMLParser.NEWLINE)
- self.state = 539
+ self.state = 543
self.match(PyNestMLParser.INDENT)
- self.state = 543
+ self.state = 547
self._errHandler.sync(self)
_la = self._input.LA(1)
while True:
- self.state = 543
+ self.state = 547
self._errHandler.sync(self)
token = self._input.LA(1)
if token in [14, 27]:
- self.state = 540
+ self.state = 544
self.inlineExpression()
pass
- elif token in [87]:
- self.state = 541
+ elif token in [85]:
+ self.state = 545
self.odeEquation()
pass
elif token in [28]:
- self.state = 542
+ self.state = 546
self.kernel()
pass
else:
raise NoViableAltException(self)
- self.state = 545
+ self.state = 549
self._errHandler.sync(self)
_la = self._input.LA(1)
- if not ((((_la) & ~0x3f) == 0 and ((1 << _la) & 402669568) != 0) or _la==87):
+ if not ((((_la) & ~0x3f) == 0 and ((1 << _la) & 402669568) != 0) or _la==85):
break
- self.state = 547
+ self.state = 551
self.match(PyNestMLParser.DEDENT)
except RecognitionException as re:
localctx.exception = re
@@ -4078,31 +4097,6 @@ def continuousInputPort(self, i:int=None):
return self.getTypedRuleContext(PyNestMLParser.ContinuousInputPortContext,i)
- def LEFT_PAREN(self, i:int=None):
- if i is None:
- return self.getTokens(PyNestMLParser.LEFT_PAREN)
- else:
- return self.getToken(PyNestMLParser.LEFT_PAREN, i)
-
- def RIGHT_PAREN(self, i:int=None):
- if i is None:
- return self.getTokens(PyNestMLParser.RIGHT_PAREN)
- else:
- return self.getToken(PyNestMLParser.RIGHT_PAREN, i)
-
- def parameter(self, i:int=None):
- if i is None:
- return self.getTypedRuleContexts(PyNestMLParser.ParameterContext)
- else:
- return self.getTypedRuleContext(PyNestMLParser.ParameterContext,i)
-
-
- def COMMA(self, i:int=None):
- if i is None:
- return self.getTokens(PyNestMLParser.COMMA)
- else:
- return self.getToken(PyNestMLParser.COMMA, i)
-
def getRuleIndex(self):
return PyNestMLParser.RULE_inputBlock
@@ -4122,69 +4116,39 @@ def inputBlock(self):
self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
- self.state = 549
+ self.state = 553
self.match(PyNestMLParser.INPUT_KEYWORD)
- self.state = 550
+ self.state = 554
self.match(PyNestMLParser.COLON)
- self.state = 551
+ self.state = 555
self.match(PyNestMLParser.NEWLINE)
- self.state = 552
+ self.state = 556
self.match(PyNestMLParser.INDENT)
- self.state = 571
+ self.state = 559
self._errHandler.sync(self)
_la = self._input.LA(1)
while True:
- self.state = 555
+ self.state = 559
self._errHandler.sync(self)
- la_ = self._interp.adaptivePredict(self._input,57,self._ctx)
+ la_ = self._interp.adaptivePredict(self._input,58,self._ctx)
if la_ == 1:
- self.state = 553
+ self.state = 557
self.spikeInputPort()
pass
elif la_ == 2:
- self.state = 554
+ self.state = 558
self.continuousInputPort()
pass
- self.state = 569
+ self.state = 561
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==47:
- self.state = 557
- self.match(PyNestMLParser.LEFT_PAREN)
- self.state = 566
- self._errHandler.sync(self)
- _la = self._input.LA(1)
- if _la==87:
- self.state = 558
- self.parameter()
- self.state = 563
- self._errHandler.sync(self)
- _la = self._input.LA(1)
- while _la==72:
- self.state = 559
- self.match(PyNestMLParser.COMMA)
- self.state = 560
- self.parameter()
- self.state = 565
- self._errHandler.sync(self)
- _la = self._input.LA(1)
-
-
-
- self.state = 568
- self.match(PyNestMLParser.RIGHT_PAREN)
-
-
- self.state = 573
- self._errHandler.sync(self)
- _la = self._input.LA(1)
- if not (_la==87):
+ if not (_la==85):
break
- self.state = 575
+ self.state = 563
self.match(PyNestMLParser.DEDENT)
except RecognitionException as re:
localctx.exception = re
@@ -4222,17 +4186,29 @@ def LEFT_SQUARE_BRACKET(self):
def RIGHT_SQUARE_BRACKET(self):
return self.getToken(PyNestMLParser.RIGHT_SQUARE_BRACKET, 0)
- def inputQualifier(self, i:int=None):
- if i is None:
- return self.getTypedRuleContexts(PyNestMLParser.InputQualifierContext)
- else:
- return self.getTypedRuleContext(PyNestMLParser.InputQualifierContext,i)
+ def LEFT_PAREN(self):
+ return self.getToken(PyNestMLParser.LEFT_PAREN, 0)
+ def RIGHT_PAREN(self):
+ return self.getToken(PyNestMLParser.RIGHT_PAREN, 0)
def expression(self):
return self.getTypedRuleContext(PyNestMLParser.ExpressionContext,0)
+ def parameter(self, i:int=None):
+ if i is None:
+ return self.getTypedRuleContexts(PyNestMLParser.ParameterContext)
+ else:
+ return self.getTypedRuleContext(PyNestMLParser.ParameterContext,i)
+
+
+ def COMMA(self, i:int=None):
+ if i is None:
+ return self.getTokens(PyNestMLParser.COMMA)
+ else:
+ return self.getToken(PyNestMLParser.COMMA, i)
+
def getRuleIndex(self):
return PyNestMLParser.RULE_spikeInputPort
@@ -4252,35 +4228,55 @@ def spikeInputPort(self):
self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
- self.state = 577
+ self.state = 565
localctx.name = self.match(PyNestMLParser.NAME)
- self.state = 582
+ self.state = 570
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==54:
- self.state = 578
+ if _la==52:
+ self.state = 566
self.match(PyNestMLParser.LEFT_SQUARE_BRACKET)
- self.state = 579
+ self.state = 567
localctx.sizeParameter = self.expression(0)
- self.state = 580
+ self.state = 568
self.match(PyNestMLParser.RIGHT_SQUARE_BRACKET)
- self.state = 584
+ self.state = 572
self.match(PyNestMLParser.LEFT_ANGLE_MINUS)
- self.state = 588
+ self.state = 573
+ self.match(PyNestMLParser.SPIKE_KEYWORD)
+ self.state = 586
self._errHandler.sync(self)
_la = self._input.LA(1)
- while _la==41 or _la==42:
- self.state = 585
- self.inputQualifier()
- self.state = 590
+ if _la==45:
+ self.state = 574
+ self.match(PyNestMLParser.LEFT_PAREN)
+ self.state = 583
self._errHandler.sync(self)
_la = self._input.LA(1)
+ if _la==85:
+ self.state = 575
+ self.parameter()
+ self.state = 580
+ self._errHandler.sync(self)
+ _la = self._input.LA(1)
+ while _la==70:
+ self.state = 576
+ self.match(PyNestMLParser.COMMA)
+ self.state = 577
+ self.parameter()
+ self.state = 582
+ self._errHandler.sync(self)
+ _la = self._input.LA(1)
- self.state = 591
- self.match(PyNestMLParser.SPIKE_KEYWORD)
- self.state = 592
+
+
+ self.state = 585
+ self.match(PyNestMLParser.RIGHT_PAREN)
+
+
+ self.state = 588
self.match(PyNestMLParser.NEWLINE)
except RecognitionException as re:
localctx.exception = re
@@ -4322,10 +4318,29 @@ def LEFT_SQUARE_BRACKET(self):
def RIGHT_SQUARE_BRACKET(self):
return self.getToken(PyNestMLParser.RIGHT_SQUARE_BRACKET, 0)
+ def LEFT_PAREN(self):
+ return self.getToken(PyNestMLParser.LEFT_PAREN, 0)
+
+ def RIGHT_PAREN(self):
+ return self.getToken(PyNestMLParser.RIGHT_PAREN, 0)
+
def expression(self):
return self.getTypedRuleContext(PyNestMLParser.ExpressionContext,0)
+ def parameter(self, i:int=None):
+ if i is None:
+ return self.getTypedRuleContexts(PyNestMLParser.ParameterContext)
+ else:
+ return self.getTypedRuleContext(PyNestMLParser.ParameterContext,i)
+
+
+ def COMMA(self, i:int=None):
+ if i is None:
+ return self.getTokens(PyNestMLParser.COMMA)
+ else:
+ return self.getToken(PyNestMLParser.COMMA, i)
+
def getRuleIndex(self):
return PyNestMLParser.RULE_continuousInputPort
@@ -4345,85 +4360,58 @@ def continuousInputPort(self):
self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
- self.state = 594
+ self.state = 590
localctx.name = self.match(PyNestMLParser.NAME)
- self.state = 599
+ self.state = 595
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==54:
- self.state = 595
+ if _la==52:
+ self.state = 591
self.match(PyNestMLParser.LEFT_SQUARE_BRACKET)
- self.state = 596
+ self.state = 592
localctx.sizeParameter = self.expression(0)
- self.state = 597
+ self.state = 593
self.match(PyNestMLParser.RIGHT_SQUARE_BRACKET)
- self.state = 601
+ self.state = 597
self.dataType()
- self.state = 602
+ self.state = 598
self.match(PyNestMLParser.LEFT_ANGLE_MINUS)
- self.state = 603
+ self.state = 599
self.match(PyNestMLParser.CONTINUOUS_KEYWORD)
- self.state = 604
- self.match(PyNestMLParser.NEWLINE)
- except RecognitionException as re:
- localctx.exception = re
- self._errHandler.reportError(self, re)
- self._errHandler.recover(self, re)
- finally:
- self.exitRule()
- return localctx
-
-
- class InputQualifierContext(ParserRuleContext):
- __slots__ = 'parser'
-
- def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
- super().__init__(parent, invokingState)
- self.parser = parser
- self.isInhibitory = None # Token
- self.isExcitatory = None # Token
-
- def INHIBITORY_KEYWORD(self):
- return self.getToken(PyNestMLParser.INHIBITORY_KEYWORD, 0)
-
- def EXCITATORY_KEYWORD(self):
- return self.getToken(PyNestMLParser.EXCITATORY_KEYWORD, 0)
-
- def getRuleIndex(self):
- return PyNestMLParser.RULE_inputQualifier
-
- def accept(self, visitor:ParseTreeVisitor):
- if hasattr( visitor, "visitInputQualifier" ):
- return visitor.visitInputQualifier(self)
- else:
- return visitor.visitChildren(self)
+ self.state = 612
+ self._errHandler.sync(self)
+ _la = self._input.LA(1)
+ if _la==45:
+ self.state = 600
+ self.match(PyNestMLParser.LEFT_PAREN)
+ self.state = 609
+ self._errHandler.sync(self)
+ _la = self._input.LA(1)
+ if _la==85:
+ self.state = 601
+ self.parameter()
+ self.state = 606
+ self._errHandler.sync(self)
+ _la = self._input.LA(1)
+ while _la==70:
+ self.state = 602
+ self.match(PyNestMLParser.COMMA)
+ self.state = 603
+ self.parameter()
+ self.state = 608
+ self._errHandler.sync(self)
+ _la = self._input.LA(1)
+ self.state = 611
+ self.match(PyNestMLParser.RIGHT_PAREN)
- def inputQualifier(self):
-
- localctx = PyNestMLParser.InputQualifierContext(self, self._ctx, self.state)
- self.enterRule(localctx, 84, self.RULE_inputQualifier)
- try:
- self.state = 608
- self._errHandler.sync(self)
- token = self._input.LA(1)
- if token in [41]:
- self.enterOuterAlt(localctx, 1)
- self.state = 606
- localctx.isInhibitory = self.match(PyNestMLParser.INHIBITORY_KEYWORD)
- pass
- elif token in [42]:
- self.enterOuterAlt(localctx, 2)
- self.state = 607
- localctx.isExcitatory = self.match(PyNestMLParser.EXCITATORY_KEYWORD)
- pass
- else:
- raise NoViableAltException(self)
+ self.state = 614
+ self.match(PyNestMLParser.NEWLINE)
except RecognitionException as re:
localctx.exception = re
self._errHandler.reportError(self, re)
@@ -4501,65 +4489,65 @@ def accept(self, visitor:ParseTreeVisitor):
def outputBlock(self):
localctx = PyNestMLParser.OutputBlockContext(self, self._ctx, self.state)
- self.enterRule(localctx, 86, self.RULE_outputBlock)
+ self.enterRule(localctx, 84, self.RULE_outputBlock)
self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
- self.state = 610
+ self.state = 616
self.match(PyNestMLParser.OUTPUT_KEYWORD)
- self.state = 611
+ self.state = 617
self.match(PyNestMLParser.COLON)
- self.state = 612
+ self.state = 618
self.match(PyNestMLParser.NEWLINE)
- self.state = 613
+ self.state = 619
self.match(PyNestMLParser.INDENT)
- self.state = 630
+ self.state = 636
self._errHandler.sync(self)
token = self._input.LA(1)
if token in [40]:
- self.state = 614
+ self.state = 620
localctx.isSpike = self.match(PyNestMLParser.SPIKE_KEYWORD)
- self.state = 627
+ self.state = 633
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==47:
- self.state = 615
+ if _la==45:
+ self.state = 621
self.match(PyNestMLParser.LEFT_PAREN)
- self.state = 624
+ self.state = 630
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==87:
- self.state = 616
+ if _la==85:
+ self.state = 622
localctx.attribute = self.parameter()
- self.state = 621
+ self.state = 627
self._errHandler.sync(self)
_la = self._input.LA(1)
- while _la==72:
- self.state = 617
+ while _la==70:
+ self.state = 623
self.match(PyNestMLParser.COMMA)
- self.state = 618
+ self.state = 624
localctx.attribute = self.parameter()
- self.state = 623
+ self.state = 629
self._errHandler.sync(self)
_la = self._input.LA(1)
- self.state = 626
+ self.state = 632
self.match(PyNestMLParser.RIGHT_PAREN)
pass
elif token in [37]:
- self.state = 629
+ self.state = 635
localctx.isContinuous = self.match(PyNestMLParser.CONTINUOUS_KEYWORD)
pass
else:
raise NoViableAltException(self)
- self.state = 632
+ self.state = 638
self.match(PyNestMLParser.NEWLINE)
- self.state = 633
+ self.state = 639
self.match(PyNestMLParser.DEDENT)
except RecognitionException as re:
localctx.exception = re
@@ -4638,55 +4626,55 @@ def accept(self, visitor:ParseTreeVisitor):
def function(self):
localctx = PyNestMLParser.FunctionContext(self, self._ctx, self.state)
- self.enterRule(localctx, 88, self.RULE_function)
+ self.enterRule(localctx, 86, self.RULE_function)
self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
- self.state = 635
+ self.state = 641
self.match(PyNestMLParser.FUNCTION_KEYWORD)
- self.state = 636
+ self.state = 642
self.match(PyNestMLParser.NAME)
- self.state = 637
+ self.state = 643
self.match(PyNestMLParser.LEFT_PAREN)
- self.state = 646
+ self.state = 652
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==87:
- self.state = 638
+ if _la==85:
+ self.state = 644
self.parameter()
- self.state = 643
+ self.state = 649
self._errHandler.sync(self)
_la = self._input.LA(1)
- while _la==72:
- self.state = 639
+ while _la==70:
+ self.state = 645
self.match(PyNestMLParser.COMMA)
- self.state = 640
+ self.state = 646
self.parameter()
- self.state = 645
+ self.state = 651
self._errHandler.sync(self)
_la = self._input.LA(1)
- self.state = 648
+ self.state = 654
self.match(PyNestMLParser.RIGHT_PAREN)
- self.state = 650
+ self.state = 656
self._errHandler.sync(self)
_la = self._input.LA(1)
- if (((_la) & ~0x3f) == 0 and ((1 << _la) & 140737488363264) != 0) or _la==87 or _la==88:
- self.state = 649
+ if (((_la) & ~0x3f) == 0 and ((1 << _la) & 35184372096768) != 0) or _la==85 or _la==86:
+ self.state = 655
localctx.returnType = self.dataType()
- self.state = 652
+ self.state = 658
self.match(PyNestMLParser.COLON)
- self.state = 653
+ self.state = 659
self.match(PyNestMLParser.NEWLINE)
- self.state = 654
+ self.state = 660
self.match(PyNestMLParser.INDENT)
- self.state = 655
+ self.state = 661
self.stmtsBody()
- self.state = 656
+ self.state = 662
self.match(PyNestMLParser.DEDENT)
except RecognitionException as re:
localctx.exception = re
@@ -4726,12 +4714,12 @@ def accept(self, visitor:ParseTreeVisitor):
def parameter(self):
localctx = PyNestMLParser.ParameterContext(self, self._ctx, self.state)
- self.enterRule(localctx, 90, self.RULE_parameter)
+ self.enterRule(localctx, 88, self.RULE_parameter)
try:
self.enterOuterAlt(localctx, 1)
- self.state = 658
+ self.state = 664
self.match(PyNestMLParser.NAME)
- self.state = 659
+ self.state = 665
self.dataType()
except RecognitionException as re:
localctx.exception = re
@@ -4742,6 +4730,63 @@ def parameter(self):
return localctx
+ class ExpressionOrParameterContext(ParserRuleContext):
+ __slots__ = 'parser'
+
+ def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
+ super().__init__(parent, invokingState)
+ self.parser = parser
+
+ def parameter(self):
+ return self.getTypedRuleContext(PyNestMLParser.ParameterContext,0)
+
+
+ def expression(self):
+ return self.getTypedRuleContext(PyNestMLParser.ExpressionContext,0)
+
+
+ def getRuleIndex(self):
+ return PyNestMLParser.RULE_expressionOrParameter
+
+ def accept(self, visitor:ParseTreeVisitor):
+ if hasattr( visitor, "visitExpressionOrParameter" ):
+ return visitor.visitExpressionOrParameter(self)
+ else:
+ return visitor.visitChildren(self)
+
+
+
+
+ def expressionOrParameter(self):
+
+ localctx = PyNestMLParser.ExpressionOrParameterContext(self, self._ctx, self.state)
+ self.enterRule(localctx, 90, self.RULE_expressionOrParameter)
+ try:
+ self.state = 669
+ self._errHandler.sync(self)
+ la_ = self._interp.adaptivePredict(self._input,75,self._ctx)
+ if la_ == 1:
+ self.enterOuterAlt(localctx, 1)
+ self.state = 667
+ self.parameter()
+ pass
+
+ elif la_ == 2:
+ self.enterOuterAlt(localctx, 2)
+ self.state = 668
+ self.expression(0)
+ pass
+
+
+ except RecognitionException as re:
+ localctx.exception = re
+ self._errHandler.reportError(self, re)
+ self._errHandler.recover(self, re)
+ finally:
+ self.exitRule()
+ return localctx
+
+
class ConstParameterContext(ParserRuleContext):
__slots__ = 'parser'
@@ -4791,14 +4836,14 @@ def constParameter(self):
self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
- self.state = 661
+ self.state = 671
localctx.name = self.match(PyNestMLParser.NAME)
- self.state = 662
+ self.state = 672
self.match(PyNestMLParser.EQUALS)
- self.state = 663
+ self.state = 673
localctx.value = self._input.LT(1)
_la = self._input.LA(1)
- if not(_la==23 or ((((_la - 85)) & ~0x3f) == 0 and ((1 << (_la - 85)) & 27) != 0)):
+ if not(_la==23 or ((((_la - 83)) & ~0x3f) == 0 and ((1 << (_la - 83)) & 27) != 0)):
localctx.value = self._errHandler.recoverInline(self)
else:
self._errHandler.reportMatch(self)
diff --git a/pynestml/generated/PyNestMLParserVisitor.py b/pynestml/generated/PyNestMLParserVisitor.py
index bd8b48073..d021a3f89 100644
--- a/pynestml/generated/PyNestMLParserVisitor.py
+++ b/pynestml/generated/PyNestMLParserVisitor.py
@@ -1,4 +1,4 @@
-# Generated from PyNestMLParser.g4 by ANTLR 4.13.2
+# Generated from PyNestMLParser.g4 by ANTLR 4.13.1
from antlr4 import *
if "." in __name__:
from .PyNestMLParser import PyNestMLParser
@@ -219,11 +219,6 @@ def visitContinuousInputPort(self, ctx:PyNestMLParser.ContinuousInputPortContext
return self.visitChildren(ctx)
- # Visit a parse tree produced by PyNestMLParser#inputQualifier.
- def visitInputQualifier(self, ctx:PyNestMLParser.InputQualifierContext):
- return self.visitChildren(ctx)
-
-
# Visit a parse tree produced by PyNestMLParser#outputBlock.
def visitOutputBlock(self, ctx:PyNestMLParser.OutputBlockContext):
return self.visitChildren(ctx)
@@ -239,6 +234,11 @@ def visitParameter(self, ctx:PyNestMLParser.ParameterContext):
return self.visitChildren(ctx)
+ # Visit a parse tree produced by PyNestMLParser#expressionOrParameter.
+ def visitExpressionOrParameter(self, ctx:PyNestMLParser.ExpressionOrParameterContext):
+ return self.visitChildren(ctx)
+
+
# Visit a parse tree produced by PyNestMLParser#constParameter.
def visitConstParameter(self, ctx:PyNestMLParser.ConstParameterContext):
return self.visitChildren(ctx)
diff --git a/pynestml/grammars/PyNestMLLexer.g4 b/pynestml/grammars/PyNestMLLexer.g4
index 591fc56ab..47a57fdc1 100644
--- a/pynestml/grammars/PyNestMLLexer.g4
+++ b/pynestml/grammars/PyNestMLLexer.g4
@@ -86,8 +86,6 @@ lexer grammar PyNestMLLexer;
ON_RECEIVE_KEYWORD : 'onReceive';
ON_CONDITION_KEYWORD : 'onCondition';
SPIKE_KEYWORD : 'spike';
- INHIBITORY_KEYWORD : 'inhibitory';
- EXCITATORY_KEYWORD : 'excitatory';
DECORATOR_HOMOGENEOUS : '@homogeneous';
DECORATOR_HETEROGENEOUS : '@heterogeneous';
diff --git a/pynestml/grammars/PyNestMLParser.g4 b/pynestml/grammars/PyNestMLParser.g4
index 18267d26a..5398c4802 100644
--- a/pynestml/grammars/PyNestMLParser.g4
+++ b/pynestml/grammars/PyNestMLParser.g4
@@ -121,14 +121,16 @@ parser grammar PyNestMLParser;
| logicalOr=OR_KEYWORD;
/**
- * ASTVariable Provides a 'marker' AST node to identify variables used in expressions.
+ * ASTVariable: A variable used in expressions. Can optionally be a vector, have a differential order, and attributes.
+ * If it is a vector, the square brackets contain a variable declaration (used for example as in ``onReceive(my_vector_input_port[i integer])``).
* @attribute name: The name of the variable without the differential order, e.g. V_m
* @attribute vectorParameter: An optional array parameter, e.g., 'tau_syn ms[n_receptors]'.
* @attribute differentialOrder: The corresponding differential order, e.g. 2
**/
variable : name=NAME
- (LEFT_SQUARE_BRACKET vectorParameter=expression RIGHT_SQUARE_BRACKET)?
- (DIFFERENTIAL_ORDER)*;
+ (LEFT_SQUARE_BRACKET vectorParameter=expressionOrParameter RIGHT_SQUARE_BRACKET)?
+ (DIFFERENTIAL_ORDER)*
+ (FULLSTOP attribute=variable)?;
/**
* ASTFunctionCall Represents a function call, e.g. myFun("a", "b").
@@ -255,7 +257,7 @@ parser grammar PyNestMLParser;
* ASTOnReceiveBlock
* @attribute stmtsBody implementation of the dynamics
**/
- onReceiveBlock : ON_RECEIVE_KEYWORD LEFT_PAREN inputPortName=NAME (COMMA constParameter)* RIGHT_PAREN COLON
+ onReceiveBlock : ON_RECEIVE_KEYWORD LEFT_PAREN inputPortVariable=variable (COMMA constParameter)* RIGHT_PAREN COLON
NEWLINE INDENT stmtsBody DEDENT;
/**
@@ -297,7 +299,7 @@ parser grammar PyNestMLParser;
* @attribute continuousInputPort: A list of continous-time input ports.
**/
inputBlock : INPUT_KEYWORD COLON
- NEWLINE INDENT ((spikeInputPort | continuousInputPort) (LEFT_PAREN (parameter (COMMA parameter)*)? RIGHT_PAREN)?)+ DEDENT;
+ NEWLINE INDENT (spikeInputPort | continuousInputPort)+ DEDENT;
/**
* ASTInputPort represents a single input port.
@@ -308,20 +310,16 @@ parser grammar PyNestMLParser;
**/
spikeInputPort : name=NAME
(LEFT_SQUARE_BRACKET sizeParameter=expression RIGHT_SQUARE_BRACKET)?
- LEFT_ANGLE_MINUS inputQualifier*
- SPIKE_KEYWORD NEWLINE;
+ LEFT_ANGLE_MINUS SPIKE_KEYWORD
+ (LEFT_PAREN (parameter (COMMA parameter)*)? RIGHT_PAREN)?
+ NEWLINE;
continuousInputPort : name = NAME
(LEFT_SQUARE_BRACKET sizeParameter=expression RIGHT_SQUARE_BRACKET)?
dataType
- LEFT_ANGLE_MINUS CONTINUOUS_KEYWORD NEWLINE;
-
- /**
- * ASTInputQualifier represents the qualifier of an inputPort. Only valid for spiking inputs.
- * @attribute isInhibitory: Indicates that this spiking input port is inhibitory.
- * @attribute isExcitatory: Indicates that this spiking input port is excitatory.
- **/
- inputQualifier : isInhibitory=INHIBITORY_KEYWORD | isExcitatory=EXCITATORY_KEYWORD;
+ LEFT_ANGLE_MINUS CONTINUOUS_KEYWORD
+ (LEFT_PAREN (parameter (COMMA parameter)*)? RIGHT_PAREN)?
+ NEWLINE;
/**
* ASTOutputBlock Represents the output block of the model, i.e., declarations of output ports.
@@ -349,6 +347,8 @@ parser grammar PyNestMLParser;
**/
parameter : NAME dataType;
+ expressionOrParameter : parameter | expression;
+
/**
* ASTConstParameter represents a single parameter consisting of a name and a literal default value, e.g. ``foo=42``.
* @attribute name: The name of the parameter.
diff --git a/pynestml/meta_model/__init__.py b/pynestml/meta_model/__init__.py
index 4af860d75..08a312ae3 100644
--- a/pynestml/meta_model/__init__.py
+++ b/pynestml/meta_model/__init__.py
@@ -41,7 +41,6 @@
'ast_inline_expression',
'ast_input_block',
'ast_input_port',
- 'ast_input_qualifier',
'ast_kernel',
'ast_logical_operator',
'ast_model',
diff --git a/pynestml/meta_model/ast_input_block.py b/pynestml/meta_model/ast_input_block.py
index d74dd8c36..9aa875ac5 100644
--- a/pynestml/meta_model/ast_input_block.py
+++ b/pynestml/meta_model/ast_input_block.py
@@ -32,7 +32,7 @@ class ASTInputBlock(ASTNode):
.. code-block:: nestml
input:
- spike_in <- excitatory spike
+ spike_in <- spike
current_in pA <- continuous
Attributes:
@@ -77,11 +77,10 @@ def clone(self):
return dup
- def get_input_ports(self):
+ def get_input_ports(self) -> List[ASTInputPort]:
"""
Returns the list of input ports.
:return: a list of input ports
- :rtype: list(ASTInputPort)
"""
return self.input_definitions
diff --git a/pynestml/meta_model/ast_input_port.py b/pynestml/meta_model/ast_input_port.py
index 45bc87dbb..8861d0ed7 100644
--- a/pynestml/meta_model/ast_input_port.py
+++ b/pynestml/meta_model/ast_input_port.py
@@ -25,8 +25,8 @@
from pynestml.meta_model.ast_data_type import ASTDataType
from pynestml.meta_model.ast_expression import ASTExpression
-from pynestml.meta_model.ast_input_qualifier import ASTInputQualifier
from pynestml.meta_model.ast_node import ASTNode
+from pynestml.meta_model.ast_parameter import ASTParameter
from pynestml.meta_model.ast_simple_expression import ASTSimpleExpression
from pynestml.utils.port_signal_type import PortSignalType
@@ -38,31 +38,21 @@ class ASTInputPort(ASTNode):
.. code-block:: nestml
- spike_in pA <- excitatory spike
+ spike_in <- spike(weight real)
@attribute name: The name of the input port.
@attribute sizeParameter: Optional size parameter for multisynapse neuron.
@attribute datatype: Optional data type of the port.
- @attribute inputQualifier: The qualifier keyword of the input port, to indicate e.g. inhibitory-only or excitatory-only spiking inputs on this port.
@attribute isSpike: Indicates that this input port accepts spikes.
@attribute isContinuous: Indicates that this input port accepts continuous time input.
-
- Grammar:
- inputPort:
- name=NAME
- (LEFT_SQUARE_BRACKET sizeParameter=NAME RIGHT_SQUARE_BRACKET)?
- (dataType)?
- LEFT_ANGLE_MINUS inputQualifier*
- (isContinuous = CONTINUOUS_KEYWORD | isSpike = SPIKE_KEYWORD);
-
"""
def __init__(self,
name: str,
signal_type: PortSignalType,
+ parameters: Optional[List[ASTParameter]] = None,
size_parameter: Optional[Union[ASTSimpleExpression, ASTExpression]] = None,
data_type: Optional[ASTDataType] = None,
- input_qualifiers: Optional[List[ASTInputQualifier]] = None,
*args, **kwargs):
r"""
Standard constructor.
@@ -70,19 +60,17 @@ def __init__(self,
Parameters for superclass (ASTNode) can be passed through :python:`*args` and :python:`**kwargs`.
:param name: the name of the port
+ :param signal_type: type of signal received, i.e., spikes or continuous
+ :param parameters: spike event parameters (for instance, ``foo ms`` in ``spike_in_port <- spike(foo ms)``)
:param size_parameter: a parameter indicating the index in an array.
:param data_type: the data type of this input port
- :param input_qualifiers: a list of input qualifiers for this port.
- :param signal_type: type of signal received, i.e., spikes or continuous
"""
super(ASTInputPort, self).__init__(*args, **kwargs)
- if input_qualifiers is None:
- input_qualifiers = []
self.name = name
self.signal_type = signal_type
self.size_parameter = size_parameter
self.data_type = data_type
- self.input_qualifiers = input_qualifiers
+ self.parameters = parameters
def clone(self) -> ASTInputPort:
r"""
@@ -93,11 +81,14 @@ def clone(self) -> ASTInputPort:
data_type_dup = None
if self.data_type:
data_type_dup = self.data_type.clone()
+ parameters_dup = None
+ if self.parameters:
+ parameters_dup = [parameter.clone() for parameter in self.parameters]
dup = ASTInputPort(name=self.name,
signal_type=self.signal_type,
+ parameters=parameters_dup,
size_parameter=self.size_parameter,
data_type=data_type_dup,
- input_qualifiers=[input_qualifier.clone() for input_qualifier in self.input_qualifiers],
# ASTNode common attributes:
source_position=self.source_position,
scope=self.scope,
@@ -115,6 +106,16 @@ def get_name(self) -> str:
"""
return self.name
+ def get_parameters(self) -> List[ASTParameter]:
+ r"""
+ Returns the parameters of the declared input port.
+ :return: the parameters.
+ """
+ if self.parameters is not None:
+ return self.parameters
+
+ return []
+
def has_size_parameter(self) -> bool:
r"""
Returns whether a size parameter has been defined.
@@ -129,20 +130,6 @@ def get_size_parameter(self) -> Optional[Union[ASTSimpleExpression, ASTExpressio
"""
return self.size_parameter
- def has_input_qualifiers(self) -> bool:
- r"""
- Returns whether input qualifiers have been defined.
- :return: True, if at least one input qualifier has been defined.
- """
- return len(self.input_qualifiers) > 0
-
- def get_input_qualifiers(self) -> List[ASTInputQualifier]:
- r"""
- Returns the list of input qualifiers.
- :return: a list of input qualifiers.
- """
- return self.input_qualifiers
-
def is_spike(self) -> bool:
r"""
Returns whether this is a spiking input port or not.
@@ -157,32 +144,6 @@ def is_continuous(self) -> bool:
"""
return self.signal_type is PortSignalType.CONTINUOUS
- def is_excitatory(self) -> bool:
- r"""
- Returns whether this port is excitatory or not. For this, it has to be marked explicitly by the
- excitatory keyword or no keywords at all shall occur (implicitly all types).
- :return: True if excitatory, False otherwise.
- """
- if self.get_input_qualifiers() is not None and len(self.get_input_qualifiers()) == 0:
- return True
- for in_type in self.get_input_qualifiers():
- if in_type.is_excitatory:
- return True
- return False
-
- def is_inhibitory(self) -> bool:
- r"""
- Returns whether this port is inhibitory or not. For this, it has to be marked explicitly by the
- inhibitory keyword or no keywords at all shall occur (implicitly all types).
- :return: True if inhibitory, False otherwise.
- """
- if self.get_input_qualifiers() is not None and len(self.get_input_qualifiers()) == 0:
- return True
- for in_type in self.get_input_qualifiers():
- if in_type.is_inhibitory:
- return True
- return False
-
def has_datatype(self):
r"""
Returns whether this port has a defined data type or not.
@@ -206,9 +167,6 @@ def get_children(self) -> List[ASTNode]:
if self.has_datatype():
children.append(self.get_datatype())
- for qual in self.get_input_qualifiers():
- children.append(qual)
-
if self.get_size_parameter():
children.append(self.get_size_parameter())
@@ -228,7 +186,7 @@ def equals(self, other: ASTNode) -> bool:
return False
if (self.has_size_parameter() and other.has_size_parameter()
- and self.get_input_qualifiers() != other.get_size_parameter()):
+ and self.get_size_parameter() != other.get_size_parameter()):
return False
if self.has_datatype() + other.has_datatype() == 1:
@@ -237,13 +195,4 @@ def equals(self, other: ASTNode) -> bool:
if self.has_datatype() and other.has_datatype() and not self.get_datatype().equals(other.get_datatype()):
return False
- if len(self.get_input_qualifiers()) != len(other.get_input_qualifiers()):
- return False
-
- my_input_qualifiers = self.get_input_qualifiers()
- your_input_qualifiers = other.get_input_qualifiers()
- for i in range(0, len(my_input_qualifiers)):
- if not my_input_qualifiers[i].equals(your_input_qualifiers[i]):
- return False
-
return self.is_spike() == other.is_spike() and self.is_continuous() == other.is_continuous()
diff --git a/pynestml/meta_model/ast_input_qualifier.py b/pynestml/meta_model/ast_input_qualifier.py
deleted file mode 100644
index 6c34c33ec..000000000
--- a/pynestml/meta_model/ast_input_qualifier.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# ast_input_qualifier.py
-#
-# This file is part of NEST.
-#
-# Copyright (C) 2004 The NEST Initiative
-#
-# NEST is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-#
-# NEST is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with NEST. If not, see .
-
-from typing import List
-
-from pynestml.meta_model.ast_node import ASTNode
-
-
-class ASTInputQualifier(ASTNode):
- """
- This class is used to store the qualifier of a buffer.
- ASTInputQualifier represents the qualifier of the input port. Only valid for spiking inputs.
- @attribute inhibitory true Indicates that this spiking input port is inhibitory.
- @attribute excitatory true Indicates that this spiking input port is excitatory.
-
- Grammar:
- inputQualifier : ('inhibitory' | 'excitatory');
-
- Attributes:
- is_inhibitory = False
- is_excitatory = False
- """
-
- def __init__(self, is_inhibitory=False, is_excitatory=False, *args, **kwargs):
- """
- Standard constructor.
-
- Parameters for superclass (ASTNode) can be passed through :python:`*args` and :python:`**kwargs`.
-
- :param is_inhibitory: is inhibitory buffer.
- :type is_inhibitory: bool
- :param is_excitatory: is excitatory buffer.
- :type is_excitatory: bool
- """
- super(ASTInputQualifier, self).__init__(*args, **kwargs)
- self.is_excitatory = is_excitatory
- self.is_inhibitory = is_inhibitory
-
- def clone(self):
- """
- Return a clone ("deep copy") of this node.
-
- :return: new AST node instance
- :rtype: ASTInputQualifier
- """
- dup = ASTInputQualifier(is_excitatory=self.is_excitatory,
- is_inhibitory=self.is_inhibitory,
- # ASTNode common attributes:
- source_position=self.source_position,
- scope=self.scope,
- comment=self.comment,
- pre_comments=[s for s in self.pre_comments],
- in_comment=self.in_comment,
- implicit_conversion_factor=self.implicit_conversion_factor)
-
- return dup
-
- def get_children(self) -> List[ASTNode]:
- r"""
- Returns the children of this node, if any.
- :return: List of children of this node.
- """
- return []
-
- def equals(self, other: ASTNode) -> bool:
- r"""
- The equality method.
- """
- if not isinstance(other, ASTInputQualifier):
- return False
-
- return self.is_excitatory == other.is_excitatory and self.is_inhibitory == other.is_inhibitory
diff --git a/pynestml/meta_model/ast_model.py b/pynestml/meta_model/ast_model.py
index f14259c86..f4d97d63b 100644
--- a/pynestml/meta_model/ast_model.py
+++ b/pynestml/meta_model/ast_model.py
@@ -308,16 +308,15 @@ def get_multiple_receptors(self) -> List[VariableSymbol]:
"""
ret = list()
for port in self.get_spike_input_ports():
- if port.is_excitatory() and port.is_inhibitory():
- if port is not None:
- ret.append(port)
- else:
- code, message = Messages.get_could_not_resolve(port.get_symbol_name())
- Logger.log_message(
- message=message,
- code=code,
- error_position=port.get_source_position(),
- log_level=LoggingLevel.ERROR)
+ if port is not None:
+ ret.append(port)
+ else:
+ code, message = Messages.get_could_not_resolve(port.get_symbol_name())
+ Logger.log_message(
+ message=message,
+ code=code,
+ error_position=port.get_source_position(),
+ log_level=LoggingLevel.ERROR)
return ret
def get_kernel_by_name(self, kernel_name: str) -> Optional[ASTKernel]:
@@ -554,7 +553,7 @@ def get_input_ports(self) -> List[VariableSymbol]:
symbols = self.get_scope().get_symbols_in_this_scope()
ret = list()
for symbol in symbols:
- if isinstance(symbol, VariableSymbol) and symbol.block_type == BlockType.INPUT:
+ if isinstance(symbol, VariableSymbol) and symbol.block_type == BlockType.INPUT and not "." in symbol.name:
ret.append(symbol)
return ret
diff --git a/pynestml/meta_model/ast_model_body.py b/pynestml/meta_model/ast_model_body.py
index 630d24b77..7bf2503f1 100644
--- a/pynestml/meta_model/ast_model_body.py
+++ b/pynestml/meta_model/ast_model_body.py
@@ -151,8 +151,9 @@ def get_internals_blocks(self) -> List[ASTBlockWithVariables]:
def get_on_receive_block(self, port_name) -> Optional[ASTOnReceiveBlock]:
for elem in self.get_body_elements():
- if isinstance(elem, ASTOnReceiveBlock) and elem.port_name == port_name:
+ if isinstance(elem, ASTOnReceiveBlock) and elem.input_port_variable.name == port_name:
return elem
+
return None
def get_on_receive_blocks(self) -> List[ASTOnReceiveBlock]:
@@ -175,6 +176,14 @@ def get_on_receive_blocks(self) -> List[ASTOnReceiveBlock]:
return on_receive_blocks
+ def get_on_condition_block(self, port_name) -> Optional[ASTOnConditionBlock]:
+ for elem in self.get_body_elements():
+ assert not "." in elem.input_port_variable.name
+ if isinstance(elem, ASTOnConditionBlock) and elem.input_port_variable.name == port_name:
+ return elem
+
+ return None
+
def get_on_condition_blocks(self) -> List[ASTOnConditionBlock]:
on_condition_blocks = []
for elem in self.get_body_elements():
diff --git a/pynestml/meta_model/ast_node_factory.py b/pynestml/meta_model/ast_node_factory.py
index 3c47acf9b..979e7e696 100644
--- a/pynestml/meta_model/ast_node_factory.py
+++ b/pynestml/meta_model/ast_node_factory.py
@@ -40,7 +40,6 @@
from pynestml.meta_model.ast_inline_expression import ASTInlineExpression
from pynestml.meta_model.ast_input_block import ASTInputBlock
from pynestml.meta_model.ast_input_port import ASTInputPort
-from pynestml.meta_model.ast_input_qualifier import ASTInputQualifier
from pynestml.meta_model.ast_if_clause import ASTIfClause
from pynestml.meta_model.ast_if_stmt import ASTIfStmt
from pynestml.meta_model.ast_kernel import ASTKernel
@@ -116,8 +115,8 @@ def create_ast_namespace_decorator(cls, namespace=None, name=None, source_positi
return ASTNamespaceDecorator(namespace, name, source_position=source_position)
@classmethod
- def create_ast_on_receive_block(cls, block=None, port_name=None, const_parameters=None, source_position=None):
- return ASTOnReceiveBlock(block, port_name, const_parameters, source_position=source_position)
+ def create_ast_on_receive_block(cls, input_port_variable: ASTInputPort, block=None, const_parameters=None, source_position=None):
+ return ASTOnReceiveBlock(input_port_variable, block, const_parameters, source_position=source_position)
@classmethod
def create_ast_on_condition_block(cls, block=None, cond_expr=None, const_parameters=None, source_position=None):
@@ -253,15 +252,9 @@ def create_ast_input_block(cls, input_definitions, source_position):
return ASTInputBlock(input_definitions, source_position=source_position)
@classmethod
- def create_ast_input_port(cls, name, size_parameter, data_type, input_qualifiers, signal_type, source_position):
- # type:(str,str,(None|ASTDataType),list(ASTInputQualifier),PortSignalType,ASTSourceLocation) -> ASTInputPort
- return ASTInputPort(name=name, size_parameter=size_parameter, data_type=data_type, input_qualifiers=input_qualifiers,
- signal_type=signal_type, source_position=source_position)
-
- @classmethod
- def create_ast_input_qualifier(cls, is_inhibitory=False, is_excitatory=False, source_position=None):
- # type: (bool,bool,ASTSourceLocation) -> ASTInputQualifier
- return ASTInputQualifier(is_inhibitory, is_excitatory, source_position=source_position)
+ def create_ast_input_port(cls, name: str, size_parameter: str, data_type: Optional[ASTDataType], signal_type: Optional[PortSignalType], parameters: Optional[List[ASTParameter]], source_position: ASTSourceLocation) -> ASTInputPort:
+ return ASTInputPort(name=name, size_parameter=size_parameter, data_type=data_type,
+ signal_type=signal_type, parameters=parameters, source_position=source_position)
@classmethod
def create_ast_logical_operator(cls, is_logical_and=False, is_logical_or=False, source_position=None):
@@ -360,8 +353,9 @@ def create_ast_update_block(cls, block, source_position):
return ASTUpdateBlock(block, source_position=source_position)
@classmethod
- def create_ast_variable(cls, name: str, differential_order: int = 0, vector_parameter=None, is_homogeneous=False, source_position: Optional[ASTSourceLocation] = None, scope: Optional[Scope] = None) -> ASTVariable:
- var = ASTVariable(name, differential_order, vector_parameter=vector_parameter, is_homogeneous=is_homogeneous, source_position=source_position)
+ def create_ast_variable(cls, name: str, differential_order: int = 0, vector_parameter=None, is_homogeneous=False, attribute: Optional[str] = None, source_position: Optional[ASTSourceLocation] = None, scope: Optional[Scope] = None) -> ASTVariable:
+ var = ASTVariable(name, differential_order, vector_parameter=vector_parameter, is_homogeneous=is_homogeneous, attribute=attribute, source_position=source_position)
+
if scope:
var.scope = scope
diff --git a/pynestml/meta_model/ast_on_receive_block.py b/pynestml/meta_model/ast_on_receive_block.py
index 9118eea4c..26619d28a 100644
--- a/pynestml/meta_model/ast_on_receive_block.py
+++ b/pynestml/meta_model/ast_on_receive_block.py
@@ -21,32 +21,29 @@
from __future__ import annotations
-from typing import Any, List, Optional, Mapping
+from typing import List, Optional, Mapping
from pynestml.meta_model.ast_stmts_body import ASTStmtsBody
from pynestml.meta_model.ast_node import ASTNode
+from pynestml.meta_model.ast_variable import ASTVariable
class ASTOnReceiveBlock(ASTNode):
r"""
- This class is used to store a declaration of an onReceive block, for example:
-
- .. code-block:: nestml
-
- onReceive(pre_spikes):
- pre_tr += 1
-
+ This class is used to store a declaration of an onReceive block.
"""
- def __init__(self, stmts_body: ASTStmtsBody, port_name: str, const_parameters: Optional[Mapping] = None, *args, **kwargs):
+ def __init__(self, input_port_variable: ASTVariable, stmts_body: ASTStmtsBody, const_parameters: Optional[Mapping] = None, *args, **kwargs):
r"""
Standard constructor.
:param stmts_body: a body of statements.
+ :param input_port_variable: the variable referencing the corresponding input port.
+ :param const_parameters: constant parameters like priority.
:param source_position: the position of this element in the source file.
"""
super(ASTOnReceiveBlock, self).__init__(*args, **kwargs)
self.stmts_body = stmts_body
- self.port_name = port_name
+ self.input_port_variable = input_port_variable
self.const_parameters = const_parameters
if self.const_parameters is None:
self.const_parameters = {}
@@ -58,7 +55,7 @@ def clone(self) -> ASTOnReceiveBlock:
:return: new AST node instance
"""
dup = ASTOnReceiveBlock(stmts_body=self.stmts_body.clone(),
- port_name=self.port_name,
+ input_port_variable=self.input_port_variable,
const_parameters=self.const_parameters,
# ASTNode common attributes:
source_position=self.source_position,
@@ -80,19 +77,19 @@ def get_stmts_body(self) -> ASTStmtsBody:
"""
return self.stmts_body
- def get_port_name(self) -> str:
+ def get_input_port_variable(self) -> ASTVariable:
r"""
- Returns the port name.
- :return: the port name
+ Returns the port.
+ :return: the port
"""
- return self.port_name
+ return self.input_port_variable
def get_children(self) -> List[ASTNode]:
r"""
Returns the children of this node, if any.
:return: List of children of this node.
"""
- return [self.get_stmts_body()]
+ return [self.get_input_port_variable(), self.get_stmts_body(), self.get_stmts_body()]
def equals(self, other: ASTNode) -> bool:
r"""
@@ -101,4 +98,4 @@ def equals(self, other: ASTNode) -> bool:
if not isinstance(other, ASTOnReceiveBlock):
return False
- return self.get_stmts_body().equals(other.get_stmts_body()) and self.port_name == other.port_name
+ return self.get_stmts_body().equals(other.get_stmts_body()) and self.input_port.equals(other.input_port)
diff --git a/pynestml/meta_model/ast_variable.py b/pynestml/meta_model/ast_variable.py
index c0645be25..ca2895960 100644
--- a/pynestml/meta_model/ast_variable.py
+++ b/pynestml/meta_model/ast_variable.py
@@ -19,11 +19,12 @@
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see .
-from typing import Any, List, Optional
+from typing import Any, List, Optional, Union
from copy import copy
from pynestml.meta_model.ast_node import ASTNode
+from pynestml.meta_model.ast_parameter import ASTParameter
from pynestml.symbol_table.scope import Scope
from pynestml.symbols.type_symbol import TypeSymbol
@@ -43,7 +44,7 @@ class ASTVariable(ASTNode):
"""
def __init__(self, name, differential_order=0, type_symbol: Optional[str] = None,
- vector_parameter: Optional[str] = None, is_homogeneous: bool = False, delay_parameter: Optional[str] = None, *args, **kwargs):
+ vector_parameter: Optional[Union[str, ASTParameter]] = None, is_homogeneous: bool = False, delay_parameter: Optional[str] = None, attribute: Optional[str] = None, *args, **kwargs):
r"""
Standard constructor.
:param name: the name of the variable
@@ -53,6 +54,7 @@ def __init__(self, name, differential_order=0, type_symbol: Optional[str] = None
:param type_symbol: the type of the variable
:param vector_parameter: the vector parameter of the variable
:param delay_parameter: the delay value to be used in the differential equation
+ :param attribute: the attribute (using the dot notation, for example, ``variable.attribute``)
"""
super(ASTVariable, self).__init__(*args, **kwargs)
assert isinstance(differential_order, int), \
@@ -67,6 +69,7 @@ def __init__(self, name, differential_order=0, type_symbol: Optional[str] = None
self.vector_parameter = vector_parameter
self.is_homogeneous = is_homogeneous
self.delay_parameter = delay_parameter
+ self.attribute = attribute
def clone(self):
r"""
@@ -77,6 +80,7 @@ def clone(self):
type_symbol=self.type_symbol,
vector_parameter=self.vector_parameter,
delay_parameter=self.delay_parameter,
+ attribute=self.attribute,
# ASTNode common attriutes:
source_position=self.get_source_position(),
scope=self.scope,
@@ -97,6 +101,13 @@ def get_name(self) -> str:
"""
return self.name
+ def get_attribute(self) -> str:
+ r"""
+ Returns the attribute of the variable.
+ :return: the attribute of the variable.
+ """
+ return self.attribute
+
def set_name(self, name: str) -> None:
"""
Sets the name of the variable.
@@ -125,6 +136,9 @@ def get_complete_name(self) -> str:
Returns the complete name, consisting of the name and the differential order.
:return: the complete name.
"""
+ if self.attribute:
+ return self.get_name() + "." + self.attribute + '\'' * self.get_differential_order()
+
return self.get_name() + '\'' * self.get_differential_order()
def get_name_of_lhs(self) -> str:
@@ -134,6 +148,7 @@ def get_name_of_lhs(self) -> str:
"""
if self.get_differential_order() > 0:
return self.get_name() + '\'' * (self.get_differential_order() - 1)
+
return self.get_name()
def get_type_symbol(self) -> TypeSymbol:
@@ -220,4 +235,4 @@ def equals(self, other: ASTNode) -> bool:
if not isinstance(other, ASTVariable):
return False
- return self.get_name() == other.get_name() and self.get_differential_order() == other.get_differential_order()
+ return self.get_name() == other.get_name() and self.get_differential_order() == other.get_differential_order() and self.attribute == other.attribute
diff --git a/pynestml/symbols/predefined_functions.py b/pynestml/symbols/predefined_functions.py
index efdf483a3..f2a8e6785 100644
--- a/pynestml/symbols/predefined_functions.py
+++ b/pynestml/symbols/predefined_functions.py
@@ -64,6 +64,7 @@ class PredefinedFunctions:
DELTA = "delta"
INTEGRATE_ODES = "integrate_odes"
CONVOLVE = "convolve"
+ INTEGRAL = "integral"
name2function = {} # type: Mapping[str, FunctionSymbol]
@classmethod
@@ -106,6 +107,7 @@ def register_functions(cls):
cls.__register_floor_function()
cls.__register_round_function()
cls.__register_convolve()
+ cls.__register_integral()
@classmethod
def register_function(cls, name, params, return_type, element_reference):
@@ -512,13 +514,27 @@ def __register_convolve(cls):
Registers the convolve function into the system.
"""
params = list()
- params.append(PredefinedTypes.get_real_type())
- params.append(PredefinedTypes.get_real_type())
+ params.append(PredefinedTypes.get_real_type()) # kernel
+ params.append(PredefinedTypes.get_template_type(0)) # spike input buffer
symbol = FunctionSymbol(name=cls.CONVOLVE, param_types=params,
- return_type=PredefinedTypes.get_real_type(),
+ return_type=PredefinedTypes.get_template_type(0),
element_reference=None, is_predefined=True)
cls.name2function[cls.CONVOLVE] = symbol
+ @classmethod
+ def __register_integral(cls):
+ """
+ Registers the integral function into the system.
+ """
+ params = list()
+ params.append(PredefinedTypes.get_real_type()) # function to integrate
+ params.append(PredefinedTypes.get_type("ms")) # from time
+ params.append(PredefinedTypes.get_type("ms")) # to time
+ symbol = FunctionSymbol(name=cls.INTEGRAL, param_types=params,
+ return_type=PredefinedTypes.get_template_type(0),
+ element_reference=None, is_predefined=True)
+ cls.name2function[cls.INTEGRAL] = symbol
+
@classmethod
def get_function_symbols(cls):
"""
diff --git a/pynestml/symbols/variable_symbol.py b/pynestml/symbols/variable_symbol.py
index f1ba24975..12ffeedda 100644
--- a/pynestml/symbols/variable_symbol.py
+++ b/pynestml/symbols/variable_symbol.py
@@ -19,6 +19,8 @@
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see .
+from typing import Optional
+
from copy import copy
from enum import Enum
@@ -61,6 +63,7 @@ class BlockType(Enum):
INPUT = 7
OUTPUT = 8
PREDEFINED = 9
+ ON_RECEIVE = 10
class VariableSymbol(Symbol):
@@ -85,7 +88,7 @@ def __init__(self, element_reference=None, scope: Scope=None, name: str=None, bl
vector_parameter: str=None, delay_parameter: str=None, declaring_expression: ASTExpression=None,
is_predefined: bool=False, is_inline_expression: bool=False, is_recordable: bool=False,
type_symbol: TypeSymbol=None, initial_value: ASTExpression=None, variable_type: VariableType=None,
- decorators=None, namespace_decorators=None):
+ decorators=None, namespace_decorators=None, attribute: Optional[str] = None):
"""
Standard constructor.
:param element_reference: a reference to the first element where this type has been used/defined
@@ -118,6 +121,7 @@ def __init__(self, element_reference=None, scope: Scope=None, name: str=None, bl
self.initial_value = initial_value
self.variable_type = variable_type
self.ode_or_kernel = None
+ self.attribute = attribute
if decorators is None:
decorators = []
if namespace_decorators is None:
@@ -220,20 +224,6 @@ def is_continuous_input_port(self) -> bool:
"""
return isinstance(self.get_referenced_object(), ASTInputPort) and self.get_referenced_object().is_continuous()
- def is_excitatory(self) -> bool:
- """
- Returns whether this symbol represents an input port with qualifier excitatory.
- :return: True if is excitatory, otherwise False.
- """
- return isinstance(self.get_referenced_object(), ASTInputPort) and self.get_referenced_object().is_excitatory()
-
- def is_inhibitory(self) -> bool:
- """
- Returns whether this symbol represents an input port with qualifier inhibitory.
- :return: True if is inhibitory, otherwise False.
- """
- return isinstance(self.get_referenced_object(), ASTInputPort) and self.get_referenced_object().is_inhibitory()
-
def is_state(self) -> bool:
"""
Returns whether this variable symbol has been declared in a state block.
diff --git a/pynestml/utils/ast_utils.py b/pynestml/utils/ast_utils.py
index ef51e0812..e783db248 100644
--- a/pynestml/utils/ast_utils.py
+++ b/pynestml/utils/ast_utils.py
@@ -28,6 +28,7 @@
from pynestml.codegeneration.printers.ast_printer import ASTPrinter
from pynestml.codegeneration.printers.cpp_variable_printer import CppVariablePrinter
+from pynestml.codegeneration.printers.nestml_simple_expression_printer_units_as_factors import NESTMLSimpleExpressionPrinterUnitsAsFactors
from pynestml.frontend.frontend_configuration import FrontendConfiguration
from pynestml.generated.PyNestMLLexer import PyNestMLLexer
from pynestml.meta_model.ast_assignment import ASTAssignment
@@ -50,6 +51,7 @@
from pynestml.meta_model.ast_node_factory import ASTNodeFactory
from pynestml.meta_model.ast_ode_equation import ASTOdeEquation
from pynestml.meta_model.ast_on_receive_block import ASTOnReceiveBlock
+from pynestml.meta_model.ast_parameter import ASTParameter
from pynestml.meta_model.ast_return_stmt import ASTReturnStmt
from pynestml.meta_model.ast_simple_expression import ASTSimpleExpression
from pynestml.meta_model.ast_small_stmt import ASTSmallStmt
@@ -359,12 +361,16 @@ def get_vectorized_variable(cls, ast, scope):
return None
@classmethod
- def get_numeric_vector_size(cls, variable: ASTVariable) -> int:
+ def get_numeric_vector_size(cls, variable: VariableSymbol) -> int:
"""
Returns the numerical size of the vector by resolving any variable used as a size parameter in declaration
:param variable: vector variable
:return: the size of the vector as a numerical value
"""
+
+ if isinstance(variable, ASTVariable):
+ variable = variable.get_scope().resolve_to_symbol(variable.get_complete_name(), SymbolKind.VARIABLE)
+
vector_parameter = variable.get_vector_parameter()
if vector_parameter.is_variable():
symbol = vector_parameter.get_scope().resolve_to_symbol(vector_parameter.get_variable().get_complete_name(), SymbolKind.VARIABLE)
@@ -1417,8 +1423,8 @@ def all_convolution_variable_names(cls, model: ASTModel) -> List[str]:
@classmethod
def construct_kernel_X_spike_buf_name(cls, kernel_var_name: str, spike_input_port: ASTInputPort, order: int,
- diff_order_symbol="__d", suffix=""):
- """
+ diff_order_symbol="__d", suffix="", attribute: Optional[str] = None):
+ r"""
Construct a kernel-buffer name as
For example, if the kernel is
@@ -1427,7 +1433,7 @@ def construct_kernel_X_spike_buf_name(cls, kernel_var_name: str, spike_input_por
and the input port is
.. code-block::
- pre_spikes nS <- spike
+ pre_spikes <- spike
then the constructed variable will be 'I_kernel__X__pre_pikes'
"""
@@ -1445,9 +1451,14 @@ def construct_kernel_X_spike_buf_name(cls, kernel_var_name: str, spike_input_por
if isinstance(spike_input_port, ASTVariable):
if spike_input_port.has_vector_parameter():
- spike_input_port_name += "_" + str(cls.get_numeric_vector_size(spike_input_port))
+ spike_input_port_name += "_" + str(spike_input_port.get_vector_parameter())
+
+ if attribute is not None:
+ attribute = "__DOT__" + attribute
+ else:
+ attribute = ""
- return kernel_var_name.replace("$", "__DOLLAR") + suffix + "__X__" + spike_input_port_name + diff_order_symbol * order + suffix
+ return kernel_var_name.replace("$", "__DOLLAR") + suffix + "__X__" + spike_input_port_name + attribute + diff_order_symbol * order + suffix
@classmethod
def replace_rhs_variable(cls, expr: ASTExpression, variable_name_to_replace: str, kernel_var: ASTVariable,
@@ -1466,7 +1477,7 @@ def replace_kernel_var(node):
and node.get_variable().get_name() == variable_name_to_replace:
var_order = node.get_variable().get_differential_order()
new_variable_name = cls.construct_kernel_X_spike_buf_name(
- kernel_var.get_name(), spike_buf, var_order - 1, diff_order_symbol="'")
+ kernel_var.get_name(), spike_buf, var_order - 1, diff_order_symbol="'", attribute=spike_buf.get_variable().get_attribute())
new_variable = ASTVariable(new_variable_name, var_order)
new_variable.set_source_position(node.get_variable().get_source_position())
node.set_variable(new_variable)
@@ -1529,7 +1540,18 @@ def is_delta_kernel(cls, kernel: ASTKernel) -> bool:
return rhs_is_delta_kernel or rhs_is_multiplied_delta_kernel
@classmethod
- def get_input_port_by_name(cls, input_blocks: List[ASTInputBlock], port_name: str) -> ASTInputPort:
+ def find_parent_node_by_type(cls, node: ASTNode, type_to_find):
+ _node = node.get_parent()
+ while _node:
+ if isinstance(_node, type_to_find):
+ return _node
+
+ _node = _node.get_parent()
+
+ return None
+
+ @classmethod
+ def get_input_port_by_name(cls, input_blocks: List[ASTInputBlock], port_name: str) -> Optional[ASTInputPort]:
"""
Get the input port given the port name
:param input_block: block to be searched
@@ -1538,15 +1560,9 @@ def get_input_port_by_name(cls, input_blocks: List[ASTInputBlock], port_name: st
"""
for input_block in input_blocks:
for input_port in input_block.get_input_ports():
- if input_port.has_size_parameter():
- size_parameter = input_port.get_size_parameter()
- if isinstance(size_parameter, ASTSimpleExpression):
- size_parameter = size_parameter.get_numeric_literal()
- port_name, port_index = port_name.split("_")
- assert int(port_index) >= 0
- assert int(port_index) <= size_parameter
if input_port.name == port_name:
return input_port
+
return None
@classmethod
@@ -2178,8 +2194,9 @@ def transform_ode_and_kernels_to_json(cls, model: ASTModel, parameters_blocks: S
for kernel_var in kernel.get_variables():
expr = cls.get_expr_from_kernel_var(kernel, kernel_var.get_complete_name())
kernel_order = kernel_var.get_differential_order()
+ attribute = spike_input_port.get_variable().get_attribute()
kernel_X_spike_buf_name_ticks = cls.construct_kernel_X_spike_buf_name(
- kernel_var.get_name(), spike_input_port, kernel_order, diff_order_symbol="'")
+ kernel_var.get_name(), spike_input_port, kernel_order, diff_order_symbol="'", attribute=attribute)
cls.replace_rhs_variables(expr, kernel_buffers)
@@ -2189,7 +2206,7 @@ def transform_ode_and_kernels_to_json(cls, model: ASTModel, parameters_blocks: S
# f(t) = ...; 1 for kernel ODE f'(t) = ...; 2 for f''(t) = ... and so on)
for order in range(kernel_order):
iv_sym_name_ode_toolbox = cls.construct_kernel_X_spike_buf_name(
- kernel_var.get_name(), spike_input_port, order, diff_order_symbol="'")
+ kernel_var.get_name(), spike_input_port, order, diff_order_symbol="'", attribute=attribute)
symbol_name_ = kernel_var.get_name() + "'" * order
symbol = equations_block.get_scope().resolve_to_symbol(symbol_name_, SymbolKind.VARIABLE)
assert symbol is not None, "Could not find initial value for variable " + symbol_name_
@@ -2221,32 +2238,136 @@ def remove_ode_definitions_from_equations_block(cls, model: ASTModel) -> None:
equations_block.get_declarations().remove(decl)
@classmethod
- def get_delta_factors_(cls, neuron: ASTModel, equations_block: ASTEquationsBlock) -> dict:
+ def get_delta_factors_from_convolutions(cls, model: ASTModel) -> dict:
r"""
For every occurrence of a convolution of the form `x^(n) = a * convolve(kernel, inport) + ...` where `kernel` is a delta function, add the element `(x^(n), inport) --> a` to the set.
"""
delta_factors = {}
- for ode_eq in equations_block.get_ode_equations():
- var = ode_eq.get_lhs()
- expr = ode_eq.get_rhs()
- conv_calls = ASTUtils.get_convolve_function_calls(expr)
- for conv_call in conv_calls:
- assert len(
- conv_call.args) == 2, "convolve() function call should have precisely two arguments: kernel and spike input port"
- kernel = conv_call.args[0]
- if cls.is_delta_kernel(neuron.get_kernel_by_name(kernel.get_variable().get_name())):
- inport = conv_call.args[1].get_variable()
- expr_str = str(expr)
- sympy_expr = sympy.parsing.sympy_parser.parse_expr(expr_str, global_dict=odetoolbox.Shape._sympy_globals)
- sympy_expr = sympy.expand(sympy_expr)
- sympy_conv_expr = sympy.parsing.sympy_parser.parse_expr(str(conv_call), global_dict=odetoolbox.Shape._sympy_globals)
- factor_str = []
- for term in sympy.Add.make_args(sympy_expr):
- if term.find(sympy_conv_expr):
- factor_str.append(str(term.replace(sympy_conv_expr, 1)))
- factor_str = " + ".join(factor_str)
- delta_factors[(var, inport)] = factor_str
+ for equations_block in model.get_equations_blocks():
+ for ode_eq in equations_block.get_ode_equations():
+ var = ode_eq.get_lhs()
+ expr = ode_eq.get_rhs()
+ conv_calls = ASTUtils.get_convolve_function_calls(expr)
+ for conv_call in conv_calls:
+ assert len(conv_call.args) == 2, "convolve() function call should have precisely two arguments: kernel and spike input port"
+ kernel = conv_call.args[0]
+ if ASTUtils.is_delta_kernel(model.get_kernel_by_name(kernel.get_variable().get_name())):
+ inport = conv_call.args[1].get_variable()
+ factor_str = ASTUtils.get_factor_str_from_expr_and_inport(expr, str(conv_call))
+ assert factor_str
+ delta_factors[(var, inport)] = factor_str
+
+ return delta_factors
+
+ @classmethod
+ def get_factor_str_from_expr_and_inport(cls, expr, sub_expr, skip_if_in_convolve_call: bool = False):
+ from sympy.physics.units import Quantity, Unit, siemens, milli, micro, nano, pico, femto, kilo, mega, volt, ampere, ohm, farad, second, meter, hertz
+ from sympy import sympify
+
+ units = {
+ 'V': volt, # Volt
+ 'mV': milli * volt, # Millivolt (10^-3 V)
+ 'uV': micro * volt, # Microvolt (10^-6 V)
+ 'nV': nano * volt, # Nanovolt (10^-9 V)
+
+ 'S': siemens, # Ampere
+ 'nS': nano * siemens, # Ampere
+
+ 'A': ampere, # Ampere
+ 'mA': milli * ampere, # Milliampere (10^-3 A)
+ 'uA': micro * ampere, # Microampere (10^-6 A)
+ 'nA': nano * ampere, # Nanoampere (10^-9 A)
+
+ 'Ohm': ohm, # Ohm
+ 'kOhm': kilo * ohm, # Kiloohm (10^3 Ohm)
+ 'MOhm': mega * ohm, # Megaohm (10^6 Ohm)
+
+ 'F': farad, # Farad
+ 'uF': micro * farad, # Microfarad (10^-6 F)
+ 'nF': nano * farad, # Nanofarad (10^-9 F)
+ 'pF': pico * farad, # Picofarad (10^-12 F)
+ 'fF': femto * farad, # Femtofarad (10^-15 F)
+
+ 's': second, # Second
+ 'ms': milli * second, # Millisecond (10^-3 s)
+ 'us': micro * second, # Microsecond (10^-6 s)
+ 'ns': nano * second, # Nanosecond (10^-9 s)
+
+ 'Hz': hertz, # Hertz (1/s)
+ 'kHz': kilo * hertz, # Kilohertz (10^3 Hz)
+ 'MHz': mega * hertz, # Megahertz (10^6 Hz)
+
+ 'm': meter, # Meter
+ 'mm': milli * meter, # Millimeter (10^-3 m)
+ 'um': micro * meter, # Micrometer (10^-6 m)
+ 'nm': nano * meter, # Nanometer (10^-9 m)
+ }
+
+ from pynestml.codegeneration.printers.constant_printer import ConstantPrinter
+ from pynestml.codegeneration.printers.nestml_function_call_printer import NESTMLFunctionCallPrinter
+ from pynestml.codegeneration.printers.nestml_printer import NESTMLPrinter
+ from pynestml.codegeneration.printers.ode_toolbox_expression_printer import ODEToolboxExpressionPrinter
+ from pynestml.codegeneration.printers.ode_toolbox_variable_printer import ODEToolboxVariablePrinter
+
+ printer = NESTMLPrinter()
+ printer._expression_printer = ODEToolboxExpressionPrinter(simple_expression_printer=None)
+ printer._constant_printer = ConstantPrinter()
+ printer._function_call_printer = NESTMLFunctionCallPrinter(expression_printer=printer._expression_printer)
+ printer._variable_printer = ODEToolboxVariablePrinter(expression_printer=printer._expression_printer)
+ printer._simple_expression_printer = NESTMLSimpleExpressionPrinterUnitsAsFactors(variable_printer=printer._variable_printer, function_call_printer=printer._function_call_printer, constant_printer=printer._constant_printer)
+ printer._expression_printer._simple_expression_printer = printer._simple_expression_printer
+
+ expr_str = printer.print(expr)
+
+ sympy_expr = sympify(expr_str, locals=units)
+ sympy_expr = sympy.expand(sympy_expr)
+ sympy_conv_expr = sympy.parsing.sympy_parser.parse_expr(sub_expr.replace(".", "__DOT__"))
+ factor_str = []
+ for term in sympy.Add.make_args(sympy_expr):
+ coeff = term.coeff(sympy_conv_expr)
+ if coeff:
+ factor_str.append(str(coeff))
+
+ factor_str = " + ".join(factor_str)
+
+ return factor_str
+
+ @classmethod
+ def get_delta_factors_from_input_port_references(cls, model: ASTModel) -> dict:
+ r"""
+ For every occurrence of a convolution of the form ``x^(n) = a * inport + ...``, add the element `(x^(n), inport) --> a` to the set.
+ """
+ delta_factors = {}
+
+ spike_inports = model.get_spike_input_ports()
+ for equations_block in model.get_equations_blocks():
+ for ode_eq in equations_block.get_ode_equations():
+ var = ode_eq.get_lhs()
+ expr = ode_eq.get_rhs()
+
+ for inport_sym in spike_inports:
+ inport_ = ASTUtils.get_input_port_by_name(model.get_input_blocks(), inport_sym.name)
+
+ inport_var = ASTNodeFactory.create_ast_variable(inport_sym.name)
+ inport_var.update_scope(equations_block.get_scope())
+
+ factor_str = ASTUtils.get_factor_str_from_expr_and_inport(expr, inport_var.name, skip_if_in_convolve_call=True)
+
+ if factor_str:
+ delta_factors[(var, inport_var)] = factor_str
+
+ for param in inport_.get_parameters():
+ inport_var = inport_var.clone()
+
+ inport_var.attribute = param.get_name()
+
+ factor_str = ASTUtils.get_factor_str_from_expr_and_inport(expr, inport_var.name + "__DOT__" + inport_var.attribute, skip_if_in_convolve_call=True)
+
+ if factor_str:
+ delta_factors[(var, inport_var)] = factor_str
+
+ # XXX: what about vectors?????
return delta_factors
@@ -2380,7 +2501,7 @@ def replace_function_call_through_var(_expr=None):
_expr.set_function_call(None)
buffer_var = cls.construct_kernel_X_spike_buf_name(
- var.get_name(), spike_input_port, var.get_differential_order() - 1)
+ var.get_name(), spike_input_port, var.get_differential_order() - 1, attribute=spike_input_port.get_attribute())
if cls.is_delta_kernel(kernel):
# delta kernels are treated separately, and should be kept out of the dynamics (computing derivates etc.) --> set to zero
_expr.set_variable(None)
@@ -2505,55 +2626,6 @@ def get_unit_name(cls, variable: ASTVariable) -> str:
return ''
- @classmethod
- def _find_port_in_dict(cls, rport_to_port_map: Dict[int, List[VariableSymbol]], port: VariableSymbol) -> int:
- """
- Finds the corresponding "inhibitory" port for a given "excitatory" port and vice versa in the handed over map.
- :param rport_to_port_map: map containing NESTML port names for the rport
- :param port: port to be searched
- :return: key value in the map if the port is found, else None
- """
- for key, value in rport_to_port_map.items():
- if len(value) == 1:
- if (port.is_excitatory() and value[0].is_inhibitory() and not value[0].is_excitatory()) \
- or (port.is_inhibitory() and value[0].is_excitatory() and not value[0].is_inhibitory()):
- if port.has_vector_parameter():
- if cls.get_numeric_vector_size(port) == cls.get_numeric_vector_size(value[0]):
- return key
- else:
- return key
- return None
-
- @classmethod
- def get_spike_input_ports_in_pairs(cls, neuron: ASTModel) -> Dict[int, List[VariableSymbol]]:
- """
- Returns a list of spike input ports in pairs in case of input port qualifiers.
- The result of this function is used to construct a vector that provides a mapping to the NESTML spike buffer index. The vector looks like below:
- .. code-block::
- [ {AMPA_SPIKES, GABA_SPIKES}, {NMDA_SPIKES, -1} ]
-
- where the vector index is the NEST rport number. The value is a tuple containing the NESTML index(es) to the spike buffer.
- In case if the rport is shared between two NESTML buffers, the vector element contains the tuple of the form (excitatory_port_index, inhibitory_port_index). Otherwise, the tuple is of the form (spike_port_index, -1).
- """
- rport_to_port_map = {}
- rport = 0
- for port in neuron.get_spike_input_ports():
- if port.is_excitatory() and port.is_inhibitory():
- rport_to_port_map[rport] = [port]
- rport += cls.get_numeric_vector_size(port) if port.has_vector_parameter() else 1
- else:
- key = cls._find_port_in_dict(rport_to_port_map, port)
- if key is not None:
- # The corresponding spiking input pair is found.
- # Add the port to the list and update rport
- rport_to_port_map[key].append(port)
- rport += cls.get_numeric_vector_size(port) if port.has_vector_parameter() else 1
- else:
- # New input port. Retain the same rport number until the corresponding input port pair is found.
- rport_to_port_map[rport] = [port]
-
- return rport_to_port_map
-
@classmethod
def assign_numeric_non_numeric_state_variables(cls, neuron, numeric_state_variable_names, numeric_update_expressions, update_expressions):
r"""For each ASTVariable, set the ``node._is_numeric`` member to True or False based on whether this variable will be solved with the analytic or numeric solver.
@@ -2625,7 +2697,7 @@ def get_on_receive_blocks_by_input_port_name(cls, model: ASTModel, port_name: st
r"""Get the onReceive blocks in the model associated with a given input port."""
blks = []
for blk in model.get_on_receive_blocks():
- if blk.get_port_name() == port_name:
+ if blk.get_input_port_variable().get_name() == port_name:
blks.append(blk)
return blks
@@ -2637,6 +2709,44 @@ def initial_value_or_zero(cls, astnode: ASTModel, var):
return "0"
+ @classmethod
+ def nestml_spiking_input_port_to_nest_rport_dict(cls, astnode: ASTModel) -> Dict[str, int]:
+ input_port_to_rport = {}
+ rport = 1 # if there is more than one spiking input port, count begins at 1
+ for input_block in astnode.get_input_blocks():
+ for input_port in input_block.get_input_ports():
+ if not input_port.is_spike():
+ continue
+
+ if input_port.get_size_parameter():
+ for i in range(int(str(input_port.size_parameter))): # XXX: should be able to convert size_parameter expression to an integer more generically (allowing for e.g. parameters)
+ input_port_to_rport[input_port.name + "_VEC_IDX_" + str(i)] = rport
+ rport += 1
+ else:
+ input_port_to_rport[input_port.name] = rport
+ rport += 1
+
+ return input_port_to_rport
+
+ @classmethod
+ def nestml_continuous_input_port_to_nest_rport_dict(cls, astnode: ASTModel) -> Dict[str, int]:
+ input_port_to_rport = {}
+ rport = 1 # if there is more than one spiking input port, count begins at 1
+ for input_block in astnode.get_input_blocks():
+ for input_port in input_block.get_input_ports():
+ if not input_port.is_continuous():
+ continue
+
+ if input_port.get_size_parameter():
+ for i in range(int(str(input_port.size_parameter))): # XXX: should be able to convert size_parameter expression to an integer more generically (allowing for e.g. parameters)
+ input_port_to_rport[input_port.name + "_VEC_IDX_" + str(i)] = rport
+ rport += 1
+ else:
+ input_port_to_rport[input_port.name] = rport
+ rport += 1
+
+ return input_port_to_rport
+
@classmethod
def find_parent_node_by_type(cls, node: ASTNode, type_to_find: Any) -> Optional[Any]:
r"""Find the first parent of the given node that has the type ``type_to_find``. Return None if no parent with that type could be found."""
@@ -2648,3 +2758,33 @@ def find_parent_node_by_type(cls, node: ASTNode, type_to_find: Any) -> Optional[
_node = _node.get_parent()
return None
+
+ @classmethod
+ def nestml_input_port_to_nest_rport(cls, astnode: ASTModel, spike_in_port: ASTInputPort):
+ return ASTUtils.nestml_spiking_input_port_to_nest_rport_dict(astnode)[spike_in_port]
+
+ @classmethod
+ def port_name_printer(cls, variable: ASTVariable) -> str:
+ s = variable.get_name()
+ if variable.has_vector_parameter():
+ s += "_VEC_IDX_"
+ s += str(variable.get_vector_parameter())
+
+ return s
+
+ @classmethod
+ def is_parameter(cls, variable) -> str:
+ return isinstance(variable, ASTParameter)
+
+ def get_spiking_input_port_terms(model: ASTModel, expr):
+ r"""Collect all terms that refer to a spiking input inside ``expr``"""
+
+ spiking_input_port_terms = []
+ spike_inports = model.get_spike_input_ports()
+ spike_inport_names = [inport.name for inport in spike_inports]
+
+ for var in expr.get_variables():
+ if str(var).split(".")[0] in spike_inport_names:
+ spiking_input_port_terms.append(var)
+
+ return spiking_input_port_terms
diff --git a/pynestml/utils/messages.py b/pynestml/utils/messages.py
index 1930d91e0..3d421abb1 100644
--- a/pynestml/utils/messages.py
+++ b/pynestml/utils/messages.py
@@ -54,7 +54,6 @@ class MessageCode(Enum):
ARG_NOT_SPIKE_INPUT = 20
NUMERATOR_NOT_ONE = 21
ORDER_NOT_DECLARED = 22
- CONTINUOUS_INPUT_PORT_WITH_QUALIFIERS = 23
BLOCK_NOT_CORRECT = 24
VARIABLE_NOT_IN_STATE_BLOCK = 25
WRONG_NUMBER_OF_ARGS = 26
@@ -84,7 +83,6 @@ class MessageCode(Enum):
TYPE_MISMATCH = 50
NEURON_SOLVED_BY_GSL = 52
NO_UNIT = 53
- NOT_NEUROSCIENCE_UNIT = 54
INTERNAL_WARNING = 55
OPERATION_NOT_DEFINED = 56
INPUT_PATH_NOT_FOUND = 58
@@ -138,12 +136,15 @@ class MessageCode(Enum):
RANDOM_FUNCTIONS_LEGALLY_USED = 113
EXPONENT_MUST_BE_INTEGER = 114
EMIT_SPIKE_OUTPUT_PORT_TYPE_DIFFERS = 115
- CONTINUOUS_OUTPUT_PORT_MAY_NOT_HAVE_ATTRIBUTES = 116
+ SPIKING_INPUT_PORT_NAME_ILLEGALLY_USED = 116
+ CONTINUOUS_OUTPUT_PORT_MAY_NOT_HAVE_ATTRIBUTES = 117
INTEGRATE_ODES_ARG_HIGHER_ORDER = 117
- DELAY_VARIABLE_NOT_SPECIFIED = 118
- WEIGHT_VARIABLE_NOT_SPECIFIED = 119
- DELAY_VARIABLE_NOT_FOUND = 120
- WEIGHT_VARIABLE_NOT_FOUND = 121
+ SPIKING_INPUT_PORT_REFERENCE_MISSING_ATTRIBUTE = 118
+ CONVOLVE_NEEDS_BUFFER_PARAMETER = 119
+ SPIKE_INPUT_PORT_IN_EQUATION_RHS_OUTSIDE_CONVOLVE = 120
+ DELAY_VARIABLE_NOT_SPECIFIED = 121
+ WEIGHT_VARIABLE_NOT_SPECIFIED = 122
+ DELAY_VARIABLE_NOT_FOUND = 123
class Messages:
@@ -514,23 +515,6 @@ def get_order_not_declared(cls, lhs):
message = 'Order of differential equation for %s is not declared!' % lhs
return MessageCode.ORDER_NOT_DECLARED, message
- @classmethod
- def get_continuous_input_port_specified(cls, name, keyword):
- """
- Indicates that the continuous time input port has been specified with an `inputQualifier` keyword.
- :param name: the name of the buffer
- :type name: str
- :param keyword: the keyword
- :type keyword: list(str)
- :return: a message
- :rtype: (MessageCode,str)
- """
- assert (name is not None and isinstance(name, str)), \
- '(PyNestML.Utils.Message) Not a string provided (%s)!' % name
- message = 'Continuous time input port \'%s\' specified with type keywords (%s)!' % (
- name, keyword)
- return MessageCode.CONTINUOUS_INPUT_PORT_WITH_QUALIFIERS, message
-
@classmethod
def get_block_not_defined_correctly(cls, block, missing):
"""
@@ -907,21 +891,6 @@ def get_unit_does_not_exist(cls, name):
message = 'Unit does not exist (%s).' % name
return MessageCode.NO_UNIT, message
- @classmethod
- def get_not_neuroscience_unit_used(cls, name):
- """
- Indicates that a non-neuroscientific unit, e.g., kg, has been used. Those units can not be converted to
- a corresponding representation in the simulation and are therefore represented by the factor 1.
- :param name: the name of the variable
- :type name: str
- :return: a nes code,message tuple
- :rtype: (MessageCode,str)
- """
- assert (name is not None and isinstance(name, str)), \
- '(PyNestML.Utils.Message) Not a string provided (%s)!' % type(name)
- message = 'Not convertible unit \'%s\' used, 1 assumed as factor!' % name
- return MessageCode.NOT_NEUROSCIENCE_UNIT, message
-
@classmethod
def get_ode_needs_consistent_units(cls, name, differential_order, lhs_type, rhs_type):
assert (name is not None and isinstance(name, str)), \
@@ -1377,11 +1346,36 @@ def get_random_functions_legally_used(cls, name):
message = "The function '" + name + "' can only be used in the update, onReceive, or onCondition blocks."
return MessageCode.RANDOM_FUNCTIONS_LEGALLY_USED, message
+ @classmethod
+ def get_spike_input_port_appears_outside_equation_rhs_and_event_handler(cls, name):
+ message = "Spiking input port names (in this case '" + name + "') can only be used in the right-hand side of equations or in an onReceive block!"
+ return MessageCode.SPIKING_INPUT_PORT_NAME_ILLEGALLY_USED, message
+
@classmethod
def get_continuous_output_port_cannot_have_attributes(cls):
message = "continuous time output port may not have attributes."
return MessageCode.CONTINUOUS_OUTPUT_PORT_MAY_NOT_HAVE_ATTRIBUTES, message
+ @classmethod
+ def get_continuous_output_port_cannot_have_attributes(cls):
+ message = "Continuous time output port may not have attributes."
+ return MessageCode.CONTINUOUS_OUTPUT_PORT_MAY_NOT_HAVE_ATTRIBUTES, message
+
+ @classmethod
+ def get_spike_input_port_attribute_missing(cls, name: str):
+ message = "Spiking input port '" + name + "' reference is missing attribute."
+ return MessageCode.SPIKING_INPUT_PORT_REFERENCE_MISSING_ATTRIBUTE, message
+
+ @classmethod
+ def get_vector_input_ports_should_be_of_constant_size(cls):
+ message = "Vector input ports should be of constant size (this is a limitation of NEST Simulator)"
+ return MessageCode.VECTOR_INPUT_PORTS_SHOULD_BE_OF_CONSTANT_SIZE, message
+
+ @classmethod
+ def get_spike_input_port_in_equation_rhs_outside_convolve(cls):
+ message = "Spike input port appears in right-hand side of equation outside of convolve(). This is a known issue (see https://github.com/nest/nestml/pull/1050)."
+ return MessageCode.SPIKE_INPUT_PORT_IN_EQUATION_RHS_OUTSIDE_CONVOLVE, message
+
@classmethod
def get_delay_variable_not_specified(cls) -> Tuple[MessageCode, str]:
message = "Delay variable is not specified for synapse model. Please see https://nestml.readthedocs.io/en/latest/running/running_nest.html#dendritic-delay-and-synaptic-weight"
diff --git a/pynestml/utils/model_parser.py b/pynestml/utils/model_parser.py
index f05cdf0e1..ce5f34bea 100644
--- a/pynestml/utils/model_parser.py
+++ b/pynestml/utils/model_parser.py
@@ -49,7 +49,6 @@
from pynestml.meta_model.ast_inline_expression import ASTInlineExpression
from pynestml.meta_model.ast_input_block import ASTInputBlock
from pynestml.meta_model.ast_input_port import ASTInputPort
-from pynestml.meta_model.ast_input_qualifier import ASTInputQualifier
from pynestml.meta_model.ast_logical_operator import ASTLogicalOperator
from pynestml.meta_model.ast_nestml_compilation_unit import ASTNestMLCompilationUnit
from pynestml.meta_model.ast_model import ASTModel
@@ -323,14 +322,6 @@ def parse_input_port(cls, string):
ret.accept(ASTHigherOrderVisitor(log_set_added_source_position))
return ret
- @classmethod
- def parse_input_qualifier(cls, string):
- # type: (str) -> ASTInputQualifier
- (builder, parser) = tokenize(string)
- ret = builder.visit(parser.inputQualifier())
- ret.accept(ASTHigherOrderVisitor(log_set_added_source_position))
- return ret
-
@classmethod
def parse_logic_operator(cls, string):
# type: (str) -> ASTLogicalOperator
diff --git a/pynestml/utils/ode_toolbox_utils.py b/pynestml/utils/ode_toolbox_utils.py
index a4162a4d0..fd2200a5b 100644
--- a/pynestml/utils/ode_toolbox_utils.py
+++ b/pynestml/utils/ode_toolbox_utils.py
@@ -38,10 +38,13 @@ def _rewrite_piecewise_into_ternary(cls, s: str) -> str:
"Float": sympy.Float,
"Function": sympy.Function}
- sympy_expr = sympy.parsing.sympy_parser.parse_expr(s, global_dict=_sympy_globals_no_functions)
+ pattern = r'\.(?!\d)' # pattern matches dots in variable names but not in numbers
+
+ import re
+ sympy_expr = sympy.parsing.sympy_parser.parse_expr(re.sub(pattern, '__DOT__', s), global_dict=_sympy_globals_no_functions)
class MySympyPrinter(StrPrinter):
- """Resulting expressions will be parsed by NESTML parser. R
+ """Resulting expressions will be parsed by NESTML parser.
"""
def _print_Function(self, expr):
if expr.func.__name__ == "Piecewise":
diff --git a/pynestml/utils/type_caster.py b/pynestml/utils/type_caster.py
index 2f7827bad..64f23373f 100644
--- a/pynestml/utils/type_caster.py
+++ b/pynestml/utils/type_caster.py
@@ -19,6 +19,7 @@
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see .
+from pynestml.symbols.error_type_symbol import ErrorTypeSymbol
from pynestml.symbols.unit_type_symbol import UnitTypeSymbol
from pynestml.utils.logger import Logger, LoggingLevel
from pynestml.utils.messages import Messages
diff --git a/pynestml/visitors/ast_builder_visitor.py b/pynestml/visitors/ast_builder_visitor.py
index fcc24d0ed..423f29ff3 100644
--- a/pynestml/visitors/ast_builder_visitor.py
+++ b/pynestml/visitors/ast_builder_visitor.py
@@ -292,9 +292,15 @@ def visitVariable(self, ctx):
vector_parameter = self.visit(ctx.vectorParameter)
differential_order = (len(ctx.DIFFERENTIAL_ORDER()) if ctx.DIFFERENTIAL_ORDER() is not None else 0)
+ if ctx.attribute:
+ attribute = ctx.attribute.getText()
+ else:
+ attribute = None
+
return ASTNodeFactory.create_ast_variable(name=str(ctx.NAME()),
differential_order=differential_order,
vector_parameter=vector_parameter,
+ attribute=attribute,
source_position=create_source_pos(ctx))
# Visit a parse tree produced by PyNESTMLParser#functionCall.
@@ -645,13 +651,13 @@ def visitSpikeInputPort(self, ctx):
size_parameter = None
if ctx.sizeParameter is not None:
size_parameter = self.visit(ctx.sizeParameter)
- input_qualifiers = []
- if ctx.inputQualifier() is not None:
- for qual in ctx.inputQualifier():
- input_qualifiers.append(self.visit(qual))
+ parameters_ast = None
+ if ctx.parameter:
+ parameters_ast = [self.visit(parameter) for parameter in ctx.parameter()]
signal_type = PortSignalType.SPIKE
ret = ASTNodeFactory.create_ast_input_port(name=name, size_parameter=size_parameter, data_type=None,
- input_qualifiers=input_qualifiers, signal_type=signal_type,
+ signal_type=signal_type,
+ parameters=parameters_ast,
source_position=create_source_pos(ctx))
update_node_comments(ret, self.__comments.visit(ctx))
return ret
@@ -664,18 +670,12 @@ def visitContinuousInputPort(self, ctx):
data_type = self.visit(ctx.dataType()) if ctx.dataType() is not None else None
signal_type = PortSignalType.CONTINUOUS
ret = ASTNodeFactory.create_ast_input_port(name=name, size_parameter=size_parameter, data_type=data_type,
- input_qualifiers=None, signal_type=signal_type,
+ signal_type=signal_type,
+ parameters=None,
source_position=create_source_pos(ctx))
update_node_comments(ret, self.__comments.visit(ctx))
return ret
- # Visit a parse tree produced by PyNESTMLParser#inputQualifier.
- def visitInputQualifier(self, ctx):
- is_inhibitory = True if ctx.isInhibitory is not None else False
- is_excitatory = True if ctx.isExcitatory is not None else False
- return ASTNodeFactory.create_ast_input_qualifier(is_inhibitory=is_inhibitory, is_excitatory=is_excitatory,
- source_position=create_source_pos(ctx))
-
# Visit a parse tree produced by PyNESTMLParser#outputBuffer.
def visitOutputBlock(self, ctx):
source_pos = create_source_pos(ctx)
@@ -729,12 +729,12 @@ def visitStmt(self, ctx):
return ASTNodeFactory.create_ast_stmt(small, compound, create_source_pos(ctx))
def visitOnReceiveBlock(self, ctx):
+ input_port_variable = self.visit(ctx.inputPortVariable)
block = self.visit(ctx.stmtsBody()) if ctx.stmtsBody() is not None else None
- port_name = ctx.inputPortName.text
const_parameters = {}
for el in ctx.constParameter():
const_parameters[el.name.text] = el.value.text
- ret = ASTNodeFactory.create_ast_on_receive_block(block=block, port_name=port_name, const_parameters=const_parameters, source_position=create_source_pos(ctx))
+ ret = ASTNodeFactory.create_ast_on_receive_block(block=block, input_port_variable=input_port_variable, const_parameters=const_parameters, source_position=create_source_pos(ctx))
update_node_comments(ret, self.__comments.visit(ctx))
return ret
diff --git a/pynestml/visitors/ast_function_call_visitor.py b/pynestml/visitors/ast_function_call_visitor.py
index e4ec8650e..2e4eb714e 100644
--- a/pynestml/visitors/ast_function_call_visitor.py
+++ b/pynestml/visitors/ast_function_call_visitor.py
@@ -18,15 +18,18 @@
#
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see .
-from pynestml.symbols.unit_type_symbol import UnitTypeSymbol
-
-from pynestml.symbols.predefined_units import PredefinedUnits
+from pynestml.meta_model.ast_equations_block import ASTEquationsBlock
+from pynestml.meta_model.ast_input_port import ASTInputPort
+from pynestml.meta_model.ast_model import ASTModel
from pynestml.meta_model.ast_simple_expression import ASTSimpleExpression
from pynestml.symbols.error_type_symbol import ErrorTypeSymbol
+from pynestml.symbols.predefined_units import PredefinedUnits
+from pynestml.symbols.real_type_symbol import RealTypeSymbol
from pynestml.symbols.template_type_symbol import TemplateTypeSymbol
from pynestml.symbols.predefined_functions import PredefinedFunctions
from pynestml.symbols.symbol import SymbolKind
+from pynestml.symbols.unit_type_symbol import UnitTypeSymbol
from pynestml.symbols.void_type_symbol import VoidTypeSymbol
from pynestml.utils.ast_utils import ASTUtils
from pynestml.utils.logger import LoggingLevel, Logger
@@ -46,13 +49,29 @@ def visit_simple_expression(self, node: ASTSimpleExpression) -> None:
:param node: a simple expression
"""
assert isinstance(node, ASTSimpleExpression), \
- '(PyNestML.Visitor.FunctionCallVisitor) No or wrong type of simple expression provided (%s)!' % tuple(node)
+ "(PyNestML.Visitor.FunctionCallVisitor) No or wrong type of simple expression provided (%s)!" % tuple(node)
assert (node.get_scope() is not None), \
"(PyNestML.Visitor.FunctionCallVisitor) No scope found, run symboltable creator!"
scope = node.get_scope()
function_name = node.get_function_call().get_name()
method_symbol = scope.resolve_to_symbol(function_name, SymbolKind.FUNCTION)
+ # return type of the convolve function is the type of the second parameter (the spike input buffer)
+ if function_name == PredefinedFunctions.CONVOLVE:
+ buffer_parameter = node.get_function_call().get_args()[1]
+
+ assert buffer_parameter.get_variable() is not None
+
+ if "." in str(buffer_parameter):
+ # the type of the convolve call is [the type of the attribute] * [s]
+ input_port = ASTUtils.get_input_port_by_name(ASTUtils.find_parent_node_by_type(node, ASTModel).get_input_blocks(), buffer_parameter.get_variable().get_name())
+ node.type = input_port.get_parameters()[0].get_data_type().get_type_symbol()
+ return
+ else:
+ # convolve with a train of delta pulses --> the type of the convolve call is [1]
+ node.type = RealTypeSymbol()
+ return
+
# check if this is a delay variable
symbol = ASTUtils.get_delay_variable_symbol(node.get_function_call())
if method_symbol is None and symbol is not None:
@@ -92,24 +111,6 @@ def visit_simple_expression(self, node: ASTSimpleExpression) -> None:
return_type.referenced_object = node
- # return type of the convolve function is the type of the second parameter multiplied by the unit of time (s)
- if function_name == PredefinedFunctions.CONVOLVE:
- buffer_parameter = node.get_function_call().get_args()[1]
-
- if buffer_parameter.get_variable() is not None:
- buffer_name = buffer_parameter.get_variable().get_name()
- buffer_symbol_resolve = scope.resolve_to_symbol(buffer_name, SymbolKind.VARIABLE)
- if buffer_symbol_resolve is not None:
- node.type = buffer_symbol_resolve.get_type_symbol() * UnitTypeSymbol(PredefinedUnits.get_unit("s"))
- return
-
- # getting here means there is an error with the parameters to convolve
- code, message = Messages.get_convolve_needs_buffer_parameter()
- Logger.log_message(code=code, message=message, error_position=node.get_source_position(),
- log_level=LoggingLevel.ERROR)
- node.type = ErrorTypeSymbol()
- return
-
if isinstance(method_symbol.get_return_type(), VoidTypeSymbol):
code, message = Messages.get_void_function_on_rhs(function_name)
Logger.log_message(code=code, message=message, error_position=node.get_source_position(),
diff --git a/pynestml/visitors/ast_symbol_table_visitor.py b/pynestml/visitors/ast_symbol_table_visitor.py
index a0f01e9fa..50cbd759f 100644
--- a/pynestml/visitors/ast_symbol_table_visitor.py
+++ b/pynestml/visitors/ast_symbol_table_visitor.py
@@ -19,15 +19,20 @@
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see .
+from pynestml.meta_model.ast_input_port import ASTInputPort
from pynestml.meta_model.ast_model import ASTModel
from pynestml.meta_model.ast_model_body import ASTModelBody
from pynestml.meta_model.ast_namespace_decorator import ASTNamespaceDecorator
from pynestml.meta_model.ast_declaration import ASTDeclaration
from pynestml.meta_model.ast_inline_expression import ASTInlineExpression
+from pynestml.meta_model.ast_node import ASTNode
+from pynestml.meta_model.ast_on_receive_block import ASTOnReceiveBlock
+from pynestml.meta_model.ast_parameter import ASTParameter
from pynestml.meta_model.ast_simple_expression import ASTSimpleExpression
from pynestml.meta_model.ast_stmt import ASTStmt
from pynestml.meta_model.ast_variable import ASTVariable
from pynestml.symbol_table.scope import Scope, ScopeType
+from pynestml.symbols.error_type_symbol import ErrorTypeSymbol
from pynestml.symbols.function_symbol import FunctionSymbol
from pynestml.symbols.predefined_functions import PredefinedFunctions
from pynestml.symbols.predefined_types import PredefinedTypes
@@ -159,25 +164,23 @@ def visit_update_block(self, node):
source_position=node.get_source_position())
node.get_scope().add_scope(scope)
node.get_stmts_body().update_scope(scope)
- return
def endvisit_update_block(self, node=None):
self.block_type_stack.pop()
- return
- def visit_on_receive_block(self, node):
+ def visit_on_receive_block(self, node: ASTOnReceiveBlock) -> None:
"""
Private method: Used to visit a single onReceive block and create the corresponding scope.
:param node: an onReceive block object.
- :type node: ASTOnReceiveBlock
"""
self.block_type_stack.push(BlockType.LOCAL)
scope = Scope(scope_type=ScopeType.ON_RECEIVE, enclosing_scope=node.get_scope(),
source_position=node.get_source_position())
node.get_scope().add_scope(scope)
node.get_stmts_body().update_scope(scope)
+ node.get_input_port_variable().update_scope(scope)
- def endvisit_on_receive_block(self, node=None):
+ def endvisit_on_receive_block(self, node: ASTOnReceiveBlock):
self.block_type_stack.pop()
def visit_on_condition_block(self, node):
@@ -463,10 +466,30 @@ def visit_simple_expression(self, node):
node.get_variable().get_vector_parameter().update_scope(node.get_scope())
def visit_variable(self, node: ASTVariable):
+
if node.has_vector_parameter():
node.get_vector_parameter().update_scope(node.get_scope())
node.get_vector_parameter().accept(self)
+ if isinstance(node.get_vector_parameter(), ASTParameter):
+ # vector parameter is a declaration
+ symbol = VariableSymbol(element_reference=node,
+ scope=node.get_scope(),
+ name=node.get_vector_parameter().get_name(),
+ block_type=BlockType.ON_RECEIVE,
+ declaring_expression=None,
+ is_predefined=False,
+ is_inline_expression=False,
+ is_recordable=False,
+ type_symbol=node.get_vector_parameter().get_data_type(),
+ initial_value=None,
+ vector_parameter=None,
+ variable_type=VariableType.VARIABLE,
+ decorators=None,
+ namespace_decorators=None)
+ symbol.set_comment(node.get_vector_parameter().get_comment())
+ node.get_parent().get_scope().add_symbol(symbol)
+
def visit_inline_expression(self, node: ASTInlineExpression):
"""
Private method: Used to visit a single inline expression, create the corresponding symbol and update the scope.
@@ -582,23 +605,53 @@ def visit_input_port(self, node):
else:
node.get_datatype().update_scope(node.get_scope())
- for qual in node.get_input_qualifiers():
- qual.update_scope(node.get_scope())
-
- def endvisit_input_port(self, node):
- type_symbol = PredefinedTypes.get_type("s")**-1
- if node.is_continuous() and node.has_datatype():
+ def endvisit_input_port(self, node: ASTInputPort):
+ if node.is_continuous():
+ assert node.has_datatype()
type_symbol = node.get_datatype().get_type_symbol()
- type_symbol.is_buffer = True # set it as a buffer
+ type_symbol.is_buffer = True # set it as a buffer
+ if node.has_size_parameter():
+ if isinstance(node.get_size_parameter(), ASTSimpleExpression) and node.get_size_parameter().is_variable():
+ node.get_size_parameter().update_scope(node.get_scope())
+ symbol = VariableSymbol(element_reference=node, scope=node.get_scope(), name=node.get_name(),
+ block_type=BlockType.INPUT, vector_parameter=node.get_size_parameter(),
+ is_predefined=False, is_inline_expression=False, is_recordable=False,
+ type_symbol=type_symbol, variable_type=VariableType.BUFFER)
+ symbol.set_comment(node.get_comment())
+ node.get_scope().add_symbol(symbol)
+ return
+
+ assert node.is_spike()
+
+ if node.parameters:
+ for parameter in node.parameters:
+ type_symbol = parameter.get_data_type().type_symbol
+ type_symbol.is_buffer = True # set it as a buffer
+ symbol = VariableSymbol(element_reference=node, scope=node.get_scope(), name=node.get_name() + "." + parameter.get_name(),
+ block_type=BlockType.INPUT, vector_parameter=node.get_size_parameter(),
+ is_predefined=False, is_inline_expression=False, is_recordable=False,
+ type_symbol=type_symbol, variable_type=VariableType.BUFFER,
+ attribute=parameter.get_name())
+ node.get_scope().add_symbol(symbol)
+
+ # add a symbol for the bare input port (without any attributes)
+ symbol = VariableSymbol(element_reference=node,
+ scope=node.get_scope(),
+ name=node.get_name(),
+ block_type=BlockType.INPUT,
+ declaring_expression=None,
+ is_predefined=False,
+ is_inline_expression=False,
+ is_recordable=False,
+ type_symbol=PredefinedTypes.get_type("s")**-1,
+ vector_parameter=node.get_size_parameter(),
+ variable_type=VariableType.BUFFER)
+ symbol.set_comment(node.get_comment())
+ node.get_scope().add_symbol(symbol)
+
if node.has_size_parameter():
if isinstance(node.get_size_parameter(), ASTSimpleExpression) and node.get_size_parameter().is_variable():
node.get_size_parameter().update_scope(node.get_scope())
- symbol = VariableSymbol(element_reference=node, scope=node.get_scope(), name=node.get_name(),
- block_type=BlockType.INPUT, vector_parameter=node.get_size_parameter(),
- is_predefined=False, is_inline_expression=False, is_recordable=False,
- type_symbol=type_symbol, variable_type=VariableType.BUFFER)
- symbol.set_comment(node.get_comment())
- node.get_scope().add_symbol(symbol)
def visit_stmt(self, node: ASTStmt):
"""
diff --git a/pynestml/visitors/ast_variable_visitor.py b/pynestml/visitors/ast_variable_visitor.py
index 07860acd7..241d672c2 100644
--- a/pynestml/visitors/ast_variable_visitor.py
+++ b/pynestml/visitors/ast_variable_visitor.py
@@ -19,11 +19,16 @@
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see .
+from pynestml.meta_model.ast_equations_block import ASTEquationsBlock
+from pynestml.meta_model.ast_model import ASTModel
from pynestml.meta_model.ast_simple_expression import ASTSimpleExpression
from pynestml.symbols.error_type_symbol import ErrorTypeSymbol
from pynestml.symbols.symbol import SymbolKind
+from pynestml.symbols.unit_type_symbol import UnitTypeSymbol
+from pynestml.utils.ast_utils import ASTUtils
from pynestml.utils.logger import LoggingLevel, Logger
from pynestml.utils.messages import MessageCode
+from pynestml.utils.unit_type import UnitType
from pynestml.visitors.ast_visitor import ASTVisitor
@@ -32,11 +37,10 @@ class ASTVariableVisitor(ASTVisitor):
This visitor visits a single variable and updates its type.
"""
- def visit_simple_expression(self, node):
+ def visit_simple_expression(self, node: ASTSimpleExpression):
"""
Visits a single variable as contained in a simple expression and derives its type.
:param node: a single simple expression
- :type node: ASTSimpleExpression
"""
assert isinstance(node, ASTSimpleExpression), \
'(PyNestML.Visitor.VariableVisitor) No or wrong type of simple expression provided (%s)!' % type(node)
@@ -49,7 +53,23 @@ def visit_simple_expression(self, node):
# update the type of the variable according to its symbol type.
if var_resolve is not None:
- node.type = var_resolve.get_type_symbol()
+ inport = ASTUtils.get_input_port_by_name(ASTUtils.find_parent_node_by_type(node, ASTModel).get_input_blocks(), node.get_variable().get_name())
+ if inport and inport.is_spike():
+ # this variable represents a spiking input port
+ if ASTUtils.find_parent_node_by_type(node, ASTEquationsBlock):
+ # it appears in an equations block; units are [units of attribute / s]
+ from astropy import units as u
+ if inport.get_parameters():
+ node.type = var_resolve.get_type_symbol() * UnitTypeSymbol(UnitType(name=str("1/s"), unit=1 / u.si.s))
+ else:
+ node.type = var_resolve.get_type_symbol() # the type of the base port is [1/s]
+ else:
+ # it appears in an equations block; units are [units of attribute]
+ node.type = var_resolve.get_type_symbol()
+ else:
+ # variable does not represent a spiking input port
+ node.type = var_resolve.get_type_symbol()
+
node.type.referenced_object = node
return
diff --git a/pynestml/visitors/ast_visitor.py b/pynestml/visitors/ast_visitor.py
index 630759f6d..72b442e87 100644
--- a/pynestml/visitors/ast_visitor.py
+++ b/pynestml/visitors/ast_visitor.py
@@ -39,7 +39,6 @@
from pynestml.meta_model.ast_if_stmt import ASTIfStmt
from pynestml.meta_model.ast_input_block import ASTInputBlock
from pynestml.meta_model.ast_input_port import ASTInputPort
-from pynestml.meta_model.ast_input_qualifier import ASTInputQualifier
from pynestml.meta_model.ast_kernel import ASTKernel
from pynestml.meta_model.ast_logical_operator import ASTLogicalOperator
from pynestml.meta_model.ast_nestml_compilation_unit import ASTNestMLCompilationUnit
@@ -371,14 +370,6 @@ def visit_input_port(self, node):
"""
return
- def visit_input_qualifier(self, node):
- """
- Used to visit a single input port qualifier.
- :param node: a single input port qualifier node.
- :type node: ASTInputQualifier
- """
- return
-
def visit_arithmetic_operator(self, node):
"""
Used to visit a single arithmetic operator.
@@ -679,7 +670,7 @@ def endvisit_output_block(self, node):
"""
return
- def endvisit_input_port(self, node):
+ def endvisit_input_port(self, node) -> None:
"""
Used to endvisit a single input port.
:param node: a single input port.
@@ -687,15 +678,7 @@ def endvisit_input_port(self, node):
"""
return
- def endvisit_input_qualifier(self, node):
- """
- Used to endvisit a single input port qualifier.
- :param node: a single input port qualifier node.
- :type node: ASTInputQualifer
- """
- return
-
- def endvisit_arithmetic_operator(self, node):
+ def endvisit_arithmetic_operator(self, node) -> None:
"""
Used to endvisit a single arithmetic operator.
:param node: a single arithmetic operator.
@@ -703,7 +686,7 @@ def endvisit_arithmetic_operator(self, node):
"""
return
- def endvisit_parameter(self, node):
+ def endvisit_parameter(self, node) -> None:
"""
Used to endvisit a single parameter.
:param node: a single parameter.
@@ -711,11 +694,10 @@ def endvisit_parameter(self, node):
"""
return
- def endvisit_stmt(self, node):
+ def endvisit_stmt(self, node) -> None:
"""
Used to endvisit a single stmt.
:param node: a single stmt
- :return: ASTStmt
"""
return
@@ -726,15 +708,15 @@ def set_real_self(self, _visitor):
def get_real_self(self):
return self.real_self
- def handle(self, _node):
+ def handle(self, _node: ASTNode) -> None:
self.get_real_self().visit(_node)
self.get_real_self().traverse(_node)
self.get_real_self().endvisit(_node)
- def visit(self, node: ASTNode):
+ def visit(self, node: ASTNode) -> None:
"""
Dispatcher for visitor pattern.
- :param node: The ASTNode to visit
+ :param node: the node to visit
"""
if isinstance(node, ASTArithmeticOperator):
self.visit_arithmetic_operator(node)
@@ -799,9 +781,6 @@ def visit(self, node: ASTNode):
if isinstance(node, ASTInputPort):
self.visit_input_port(node)
return
- if isinstance(node, ASTInputQualifier):
- self.visit_input_qualifier(node)
- return
if isinstance(node, ASTLogicalOperator):
self.visit_logical_operator(node)
return
@@ -861,11 +840,10 @@ def visit(self, node: ASTNode):
return
return
- def traverse(self, node):
+ def traverse(self, node: ASTNode) -> None:
"""
Dispatcher for traverse method.
- :param node: The ASTElement to visit
- :type node: Inherited from ASTElement
+ :param node: the node to traverse
"""
if isinstance(node, ASTArithmeticOperator):
self.traverse_arithmetic_operator(node)
@@ -930,9 +908,6 @@ def traverse(self, node):
if isinstance(node, ASTInputPort):
self.traverse_input_port(node)
return
- if isinstance(node, ASTInputQualifier):
- self.traverse_input_qualifier(node)
- return
if isinstance(node, ASTLogicalOperator):
self.traverse_logical_operator(node)
return
@@ -992,11 +967,10 @@ def traverse(self, node):
return
return
- def endvisit(self, node):
+ def endvisit(self, node: ASTNode) -> None:
"""
Dispatcher for endvisit.
- :param node: The ASTElement to endvisit
- :type node: ASTElement or inherited
+ :param node: the node to end-visit
"""
if isinstance(node, ASTArithmeticOperator):
self.endvisit_arithmetic_operator(node)
@@ -1061,9 +1035,6 @@ def endvisit(self, node):
if isinstance(node, ASTInputPort):
self.endvisit_input_port(node)
return
- if isinstance(node, ASTInputQualifier):
- self.endvisit_input_qualifier(node)
- return
if isinstance(node, ASTLogicalOperator):
self.endvisit_logical_operator(node)
return
@@ -1248,14 +1219,6 @@ def traverse_input_block(self, node):
for sub_node in node.get_input_ports():
sub_node.accept(self.get_real_self())
- def traverse_input_port(self, node):
- if node.get_input_qualifiers() is not None:
- for sub_node in node.get_input_qualifiers():
- sub_node.accept(self.get_real_self())
-
- def traverse_input_qualifier(self, node):
- return
-
def traverse_logical_operator(self, node):
return
@@ -1280,6 +1243,9 @@ def traverse_inline_expression(self, node):
if node.get_expression() is not None:
node.get_expression().accept(self.get_real_self())
+ def traverse_input_port(self, node):
+ return
+
def traverse_kernel(self, node):
for var, expr in zip(node.get_variables(), node.get_expressions()):
var.accept(self.get_real_self())
@@ -1333,6 +1299,8 @@ def traverse_update_block(self, node):
def traverse_on_receive_block(self, node):
if node.get_stmts_body() is not None:
node.get_stmts_body().accept(self.get_real_self())
+ if node.input_port_variable is not None:
+ node.input_port_variable.accept(self.get_real_self())
def traverse_on_condition_block(self, node):
if node.get_cond_expr() is not None:
diff --git a/tests/invalid/CoCoAssignmentToInlineExpression.nestml b/tests/invalid/CoCoAssignmentToInlineExpression.nestml
index 3484b87f2..03cef2a94 100644
--- a/tests/invalid/CoCoAssignmentToInlineExpression.nestml
+++ b/tests/invalid/CoCoAssignmentToInlineExpression.nestml
@@ -28,10 +28,10 @@ model CoCoAssignmentToInlineExpression:
equations:
kernel alpha_kernel = (e / tau_syn) * t * exp(-t / tau_syn)
- inline foo real = convolve(alpha_kernel, spikes_in)
+ inline foo real = convolve(alpha_kernel, spikes_in.weight)
input:
- spikes_in <- spike
+ spikes_in <- spike(weight real)
update:
foo = 42.
diff --git a/tests/invalid/CoCoConvolveNotCorrectlyParametrized.nestml b/tests/invalid/CoCoConvolveNotCorrectlyParametrized.nestml
index 1ca5cf78a..af08cb917 100644
--- a/tests/invalid/CoCoConvolveNotCorrectlyParametrized.nestml
+++ b/tests/invalid/CoCoConvolveNotCorrectlyParametrized.nestml
@@ -38,4 +38,4 @@ model CoCoConvolveNotCorrectlyParametrized:
inline testB pA = convolve(V_m+V_m, spikeExc)
input:
- spikeExc <- excitatory spike
+ spikeExc <- spike
diff --git a/tests/invalid/CoCoConvolveNotCorrectlyProvided.nestml b/tests/invalid/CoCoConvolveNotCorrectlyProvided.nestml
index 7e44fdaec..094b9948b 100644
--- a/tests/invalid/CoCoConvolveNotCorrectlyProvided.nestml
+++ b/tests/invalid/CoCoConvolveNotCorrectlyProvided.nestml
@@ -36,12 +36,12 @@ model CoCoConvolveNotCorrectlyProvided:
g_ex mV = 10mV
equations:
- kernel test = 10
- inline testB pA = convolve(g_ex, g_ex) + test
+ kernel test = delta(t)
+ inline testB pA = convolve(g_ex, g_ex)
V_m' = 20 mV/ms
input:
- spikeExc <- excitatory spike
+ spikeExc <- spike
update:
integrate_odes()
diff --git a/tests/nest_tests/resources/input_ports_in_loop.nestml b/tests/invalid/CoCoInputPortsIllegal.nestml
similarity index 53%
rename from tests/nest_tests/resources/input_ports_in_loop.nestml
rename to tests/invalid/CoCoInputPortsIllegal.nestml
index ec8a253a5..0825d3b73 100644
--- a/tests/nest_tests/resources/input_ports_in_loop.nestml
+++ b/tests/invalid/CoCoInputPortsIllegal.nestml
@@ -1,12 +1,10 @@
-# input_ports_in_loop.nestml
-# ##########################
-#
+# CoCoInputPortsIllegal.nestml
+# ############################
#
# Description
# +++++++++++
#
-# This test is used to test the usage of both vectorized and non-vectorized input ports in loops
-#
+# This test is used to test the declaration of both vectorized and non-vectorized input ports.
#
# Copyright statement
# +++++++++++++++++++
@@ -28,33 +26,22 @@
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see .
#
-model input_ports_loop:
+model input_ports_illegal_neuron:
state:
- bar real = 0
- foo_spikes real = 0
- my_spikes_ip[N_spikes] real = 0
-
- parameters:
- N_buf integer = 5
- N_spikes integer = 10
+ bar pA = 0 pA
+ foo_spikes pA = 0 pA
+ my_spikes_ip pA = 0 pA
input:
+ AMPA_spikes <- spike
+ GABA_spikes <- spike
NMDA_spikes <- spike
foo[2] <- spike
- spike_buf[N_buf] <- spike
+ my_spikes[3] <- spike
+ my_spikes2[3] <- spike
+ I_stim pA <- continuous
update:
- bar += NMDA_spikes * s
-
- # foo spikes
- i integer = 0
- for i in 0 ... (N_buf) step 1:
- foo_spikes += 2.5 * foo[i] * s
-
- # spike_buf spikes
- j integer = 0
- k integer = 0
- while j < N_buf and k < N_spikes:
- my_spikes_ip[k+2] += spike_buf[j] * s
- j += 1
- k += 1
+ bar += (NMDA_spikes + 2 * AMPA_spikes - 3 * GABA_spikes) * (pA * s)
+ foo_spikes += (foo[0] + 5.5 * foo[1]) * (pA * s)
+ my_spikes_ip += (my_spikes[0] + my_spikes[1] - my_spikes2[1]) * (pA * s)
diff --git a/tests/invalid/CoCoInputPortWithRedundantTypes.nestml b/tests/invalid/CoCoInputPortsIllegalMissingAttribute.nestml
similarity index 65%
rename from tests/invalid/CoCoInputPortWithRedundantTypes.nestml
rename to tests/invalid/CoCoInputPortsIllegalMissingAttribute.nestml
index 811cd63c4..fa5df5536 100644
--- a/tests/invalid/CoCoInputPortWithRedundantTypes.nestml
+++ b/tests/invalid/CoCoInputPortsIllegalMissingAttribute.nestml
@@ -1,35 +1,41 @@
-# CoCoInputPortWithRedundantTypes.nestml
-# ######################################
-#
-#
+# CoCoInputPortsIllegal.nestml
+# ############################
+#
# Description
# +++++++++++
-#
-# This model is used to test if broken CoCos are identified correctly. Here, if each input port is defined uniquely, i.e., no redundant keywords are used.
-#
-# Negative case.
-#
-#
+#
+# This test is used to test the declaration of both vectorized and non-vectorized input ports.
+#
+#
+# Positive case.
+#
+#
# Copyright statement
# +++++++++++++++++++
-#
+#
# This file is part of NEST.
-#
+#
# Copyright (C) 2004 The NEST Initiative
-#
+#
# NEST is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
-#
+#
# NEST is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see .
#
-model CoCoInputPortWithRedundantTypes:
+model input_ports_illegal_neuron:
+ state:
+ foo real = 0.
+
input:
- spikeInhX2 <- inhibitory inhibitory spike # spike redundant keywords used
+ spike_in_port <- spike(foo real)
+
+ onReceive(spike_in_port):
+ foo += spike_in_port
diff --git a/tests/invalid/CoCoValueAssignedToInputPort.nestml b/tests/invalid/CoCoValueAssignedToInputPort.nestml
index 5444cf65c..598293b02 100644
--- a/tests/invalid/CoCoValueAssignedToInputPort.nestml
+++ b/tests/invalid/CoCoValueAssignedToInputPort.nestml
@@ -32,7 +32,7 @@
#
model CoCoValueAssignedToInputPort:
input:
- spikeInh <- inhibitory spike
+ spike_in_port <- spike
update:
- spikeInh = 10 / s
+ spike_in_port = 10 / s
diff --git a/tests/lexer_parser_test.py b/tests/lexer_parser_test.py
index b5d35696e..92e872b82 100644
--- a/tests/lexer_parser_test.py
+++ b/tests/lexer_parser_test.py
@@ -55,8 +55,6 @@ def test(self):
stream.fill()
# parse the file
parser = PyNestMLParser(stream)
- parser._errHandler = BailErrorStrategy()
- parser._errHandler.reset(parser)
compilation_unit = parser.nestMLCompilationUnit()
assert compilation_unit is not None
diff --git a/tests/nest_tests/fir_filter_test.py b/tests/nest_tests/fir_filter_test.py
index 220aceaa9..a7ad1bad8 100644
--- a/tests/nest_tests/fir_filter_test.py
+++ b/tests/nest_tests/fir_filter_test.py
@@ -111,7 +111,7 @@ def test_fir_filter(self):
spike_times = nest.GetStatus(sr, keys="events")[0]["times"]
# Scipy filtering
- spikes, bin_edges = np.histogram(spike_times, np.arange(0, t_sim, resolution))
+ spikes, bin_edges = np.histogram(spike_times + resolution * 1.5, np.arange(0, t_sim, resolution))
output = scipy.signal.lfilter(h, 1, spikes)
# Plots
diff --git a/tests/nest_tests/input_ports_test.py b/tests/nest_tests/input_ports_test.py
deleted file mode 100644
index 0d26c79ad..000000000
--- a/tests/nest_tests/input_ports_test.py
+++ /dev/null
@@ -1,165 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# input_ports_test.py
-#
-# This file is part of NEST.
-#
-# Copyright (C) 2004 The NEST Initiative
-#
-# NEST is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-#
-# NEST is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with NEST. If not, see .
-import os
-import pytest
-
-import nest
-
-from pynestml.frontend.pynestml_frontend import generate_nest_target
-from pynestml.codegeneration.nest_tools import NESTTools
-
-
-class TestInputPorts:
- """
- Tests the different kind of input ports supported in NESTML.
- """
-
- @pytest.mark.skipif(NESTTools.detect_nest_version().startswith("v2"),
- reason="This test does not support NEST 2")
- def test_input_ports(self):
- input_path = os.path.join(os.path.realpath(os.path.join(
- os.path.dirname(__file__), "resources", "input_ports.nestml")))
- target_path = "target"
- logging_level = "INFO"
- module_name = "nestmlmodule"
- suffix = "_nestml"
-
- generate_nest_target(input_path,
- target_path=target_path,
- logging_level=logging_level,
- module_name=module_name,
- suffix=suffix)
- nest.ResetKernel()
- nest.Install(module_name)
-
- neuron = nest.Create("input_ports_nestml")
-
- # List of receptor types for the spiking input ports
- receptor_types = nest.GetStatus(neuron, "receptor_types")[0]
-
- spike_times = [
- [10., 44.], # NMDA_SPIKES
- [12., 42.], # AMPA_SPIKES
- [14., 40.], # GABA_SPIKES
- [16., 38.], # FOO_0
- [18., 36.], # FOO_1
- [20., 34.], # MY_SPIKES_0
- [22., 32.], # MY_SPIKES_1
- [24., 30.], # MY_SPIKES2_1
- ]
- sgs = nest.Create('spike_generator', len(spike_times))
- for i, sg in enumerate(sgs):
- sg.spike_times = spike_times[i]
-
- nest.Connect(sgs[0], neuron, syn_spec={'receptor_type': receptor_types["NMDA_SPIKES"], 'weight': -1.0, 'delay': 1.0})
- nest.Connect(sgs[1], neuron, syn_spec={'receptor_type': receptor_types["AMPA_SPIKES"], 'weight': 1.0, 'delay': 1.0})
- nest.Connect(sgs[2], neuron, syn_spec={'receptor_type': receptor_types["GABA_SPIKES"], 'weight': -1.0, 'delay': 1.0})
- nest.Connect(sgs[3], neuron, syn_spec={'receptor_type': receptor_types["FOO_0"], 'weight': 1.0, 'delay': 1.0})
- nest.Connect(sgs[4], neuron, syn_spec={'receptor_type': receptor_types["FOO_1"], 'weight': 1.0, 'delay': 1.0})
- nest.Connect(sgs[5], neuron, syn_spec={'receptor_type': receptor_types["MY_SPIKES_0"], 'weight': 1.0, 'delay': 1.0})
- nest.Connect(sgs[6], neuron, syn_spec={'receptor_type': receptor_types["MY_SPIKES_1"], 'weight': 2.0, 'delay': 1.0})
- nest.Connect(sgs[7], neuron, syn_spec={'receptor_type': receptor_types["MY_SPIKES2_1"], 'weight': -3.0, 'delay': 1.0})
-
- mm = nest.Create("multimeter", {"record_from": ["bar", "foo_spikes", "my_spikes_ip"]})
- nest.Connect(mm, neuron)
-
- nest.Simulate(50.)
-
- events = mm.get("events")
- connections = nest.GetConnections(target=neuron)
-
- # corresponds to ``bar += NMDA_spikes + 2 * AMPA_spikes - 3 * GABA_spikes`` in the update block
- assert events["bar"][-1] == len(spike_times[0]) * abs(connections.get("weight")[0]) \
- + 2 * len(spike_times[1]) * abs(connections.get("weight")[1]) \
- - 3 * len(spike_times[2]) * abs(connections.get("weight")[2])
-
- # corresponds to ``foo_spikes += foo[0] + 5.5 * foo[1]`` in the update block
- assert events["foo_spikes"][-1] == len(spike_times[3]) * abs(connections.get("weight")[3]) \
- + 5.5 * len(spike_times[4]) * abs(connections.get("weight")[4])
-
- # corresponds to ``my_spikes_ip += my_spikes[0] + my_spikes[1] - my_spikes2[1]`` in the update block
- assert events["my_spikes_ip"][-1] == len(spike_times[5]) * abs(connections.get("weight")[5]) \
- + len(spike_times[6]) * abs(connections.get("weight")[6]) \
- - len(spike_times[7]) * abs(connections.get("weight")[7])
-
- @pytest.mark.skipif(NESTTools.detect_nest_version().startswith("v2"),
- reason="This test does not support NEST 2")
- def test_input_ports_in_loop(self):
- input_path = os.path.join(os.path.realpath(os.path.join(
- os.path.dirname(__file__), "resources", "input_ports_in_loop.nestml")))
- target_path = "target"
- logging_level = "INFO"
- module_name = "nestmlmodule"
- suffix = "_nestml"
-
- generate_nest_target(input_path,
- target_path=target_path,
- logging_level=logging_level,
- module_name=module_name,
- suffix=suffix)
- nest.ResetKernel()
- nest.Install(module_name)
-
- neuron = nest.Create("input_ports_loop_nestml")
-
- # List of receptor types for the spiking input ports
- receptor_types = nest.GetStatus(neuron, "receptor_types")[0]
-
- spike_times = [
- [10., 39.], # NMDA_SPIKES
- [12., 37.], # FOO_0
- [14., 35.], # FOO_1
- [16., 33.], # SPIKE_BUF_0
- [18., 31.], # SPIKE_BUF_1
- [20., 29.], # SPIKE_BUF_2
- [22., 27.], # SPIKE_BUF_3
- [24., 25.], # SPIKE_BUF_4
- ]
- sgs = nest.Create('spike_generator', len(spike_times))
- for i, sg in enumerate(sgs):
- sg.spike_times = spike_times[i]
-
- nest.Connect(sgs[0], neuron,
- syn_spec={'receptor_type': receptor_types["NMDA_SPIKES"], 'weight': 1.0, 'delay': 1.0})
- nest.Connect(sgs[1], neuron,
- syn_spec={'receptor_type': receptor_types["FOO_0"], 'weight': 1.0, 'delay': 1.0})
- nest.Connect(sgs[2], neuron,
- syn_spec={'receptor_type': receptor_types["FOO_1"], 'weight': 1.0, 'delay': 1.0})
- nest.Connect(sgs[3], neuron, syn_spec={'receptor_type': receptor_types["SPIKE_BUF_0"], 'weight': 1.0, 'delay': 1.0})
- nest.Connect(sgs[4], neuron, syn_spec={'receptor_type': receptor_types["SPIKE_BUF_1"], 'weight': 1.0, 'delay': 1.0})
- nest.Connect(sgs[5], neuron,
- syn_spec={'receptor_type': receptor_types["SPIKE_BUF_2"], 'weight': 1.0, 'delay': 1.0})
- nest.Connect(sgs[6], neuron,
- syn_spec={'receptor_type': receptor_types["SPIKE_BUF_3"], 'weight': 2.0, 'delay': 1.0})
- nest.Connect(sgs[7], neuron,
- syn_spec={'receptor_type': receptor_types["SPIKE_BUF_4"], 'weight': 3.0, 'delay': 1.0})
-
- mm = nest.Create("multimeter", {"record_from": ["bar", "foo_spikes", "MY_SPIKES_IP_2", "MY_SPIKES_IP_3", "MY_SPIKES_IP_4", "MY_SPIKES_IP_5", "MY_SPIKES_IP_6"]})
- nest.Connect(mm, neuron)
-
- nest.Simulate(41.)
-
- events = mm.get("events")
- assert events["bar"][-1] == 2.0
- assert events["foo_spikes"][-1] == 25.0
- assert events["MY_SPIKES_IP_2"][-1] == 2.0
- assert events["MY_SPIKES_IP_5"][-1] == 4.0
- assert events["MY_SPIKES_IP_6"][-1] == 6.0
diff --git a/tests/nest_tests/nest_custom_templates_test.py b/tests/nest_tests/nest_custom_templates_test.py
index c1a130dfc..8f13653e3 100644
--- a/tests/nest_tests/nest_custom_templates_test.py
+++ b/tests/nest_tests/nest_custom_templates_test.py
@@ -51,7 +51,10 @@ def test_custom_templates(self):
"model_templates": {"neuron": ["@NEURON_NAME@.cpp.jinja2", "@NEURON_NAME@.h.jinja2"],
"synapse": ["@SYNAPSE_NAME@.h.jinja2"]},
"module_templates": ["setup/CMakeLists.txt.jinja2",
- "setup/@MODULE_NAME@.h.jinja2", "setup/@MODULE_NAME@.cpp.jinja2"]}}
+ "setup/@MODULE_NAME@.h.jinja2",
+ "setup/@MODULE_NAME@.cpp.jinja2",
+ "setup/vector_ring_buffer.h.jinja2",
+ "setup/vector_ring_buffer.cpp.jinja2"]}}
generate_target(input_path, target_platform, target_path,
logging_level=logging_level,
diff --git a/tests/nest_tests/nest_integration_test.py b/tests/nest_tests/nest_integration_test.py
index 441e4916e..04fd1e9ae 100644
--- a/tests/nest_tests/nest_integration_test.py
+++ b/tests/nest_tests/nest_integration_test.py
@@ -179,19 +179,19 @@ def _test_model_equivalence_curr_inj(self, nest_model_name, nestml_model_name, g
# ResetKernel() does not unload modules for NEST Simulator < v3.7; ignore exception if module is already loaded on earlier versions
pass
- neuron1 = nest.Create(nest_model_name, params=nest_model_parameters)
- neuron2 = nest.Create(nestml_model_name, params=nestml_model_parameters)
+ nest_neuron = nest.Create(nest_model_name, params=nest_model_parameters)
+ nestml_neuron = nest.Create(nestml_model_name, params=nestml_model_parameters)
if model_initial_state is not None:
- nest.SetStatus(neuron1, model_initial_state)
- nest.SetStatus(neuron2, model_initial_state)
+ nest.SetStatus(nest_neuron, model_initial_state)
+ nest.SetStatus(nestml_neuron, model_initial_state)
# if gsl_error_tol is not None:
- # nest.SetStatus(neuron2, {"gsl_error_tol": gsl_error_tol})
+ # nest.SetStatus(nestml_neuron, {"gsl_error_tol": gsl_error_tol})
dc = nest.Create("dc_generator", params={"amplitude": 0.})
- nest.Connect(dc, neuron1)
- nest.Connect(dc, neuron2)
+ nest.Connect(dc, nest_neuron)
+ nest.Connect(dc, nestml_neuron)
multimeter1 = nest.Create("multimeter")
multimeter2 = nest.Create("multimeter")
@@ -200,8 +200,8 @@ def _test_model_equivalence_curr_inj(self, nest_model_name, nestml_model_name, g
nest.SetStatus(multimeter1, {"record_from": [V_m_specifier]})
nest.SetStatus(multimeter2, {"record_from": [V_m_specifier]})
- nest.Connect(multimeter1, neuron1)
- nest.Connect(multimeter2, neuron2)
+ nest.Connect(multimeter1, nest_neuron)
+ nest.Connect(multimeter2, nestml_neuron)
if NESTTools.detect_nest_version().startswith("v2"):
sd_reference = nest.Create("spike_detector")
@@ -210,8 +210,8 @@ def _test_model_equivalence_curr_inj(self, nest_model_name, nestml_model_name, g
sd_reference = nest.Create("spike_recorder")
sd_testant = nest.Create("spike_recorder")
- nest.Connect(neuron1, sd_reference)
- nest.Connect(neuron2, sd_testant)
+ nest.Connect(nest_neuron, sd_reference)
+ nest.Connect(nestml_neuron, sd_testant)
nest.Simulate(t_pulse_start)
dc.amplitude = I_stim * 1E12 # 1E12: convert A to pA
@@ -262,19 +262,19 @@ def _test_model_equivalence_fI_curve(self, nest_model_name, nestml_model_name, g
# ResetKernel() does not unload modules for NEST Simulator < v3.7; ignore exception if module is already loaded on earlier versions
pass
- neuron1 = nest.Create(nest_model_name, params=nest_model_parameters)
- neuron2 = nest.Create(nestml_model_name, params=nestml_model_parameters)
+ nest_neuron = nest.Create(nest_model_name, params=nest_model_parameters)
+ nestml_neuron = nest.Create(nestml_model_name, params=nestml_model_parameters)
if model_initial_state is not None:
- nest.SetStatus(neuron1, model_initial_state)
- nest.SetStatus(neuron2, model_initial_state)
+ nest.SetStatus(nest_neuron, model_initial_state)
+ nest.SetStatus(nestml_neuron, model_initial_state)
# if gsl_error_tol is not None:
- # nest.SetStatus(neuron2, {"gsl_error_tol": gsl_error_tol})
+ # nest.SetStatus(nestml_neuron, {"gsl_error_tol": gsl_error_tol})
dc = nest.Create("dc_generator", params={"amplitude": 1E12 * I_stim}) # 1E12: convert A to pA
- nest.Connect(dc, neuron1)
- nest.Connect(dc, neuron2)
+ nest.Connect(dc, nest_neuron)
+ nest.Connect(dc, nestml_neuron)
multimeter1 = nest.Create("multimeter")
multimeter2 = nest.Create("multimeter")
@@ -283,8 +283,8 @@ def _test_model_equivalence_fI_curve(self, nest_model_name, nestml_model_name, g
nest.SetStatus(multimeter1, {"record_from": [V_m_specifier]})
nest.SetStatus(multimeter2, {"record_from": [V_m_specifier]})
- nest.Connect(multimeter1, neuron1)
- nest.Connect(multimeter2, neuron2)
+ nest.Connect(multimeter1, nest_neuron)
+ nest.Connect(multimeter2, nestml_neuron)
if NESTTools.detect_nest_version().startswith("v2"):
sd_reference = nest.Create("spike_detector")
@@ -293,8 +293,8 @@ def _test_model_equivalence_fI_curve(self, nest_model_name, nestml_model_name, g
sd_reference = nest.Create("spike_recorder")
sd_testant = nest.Create("spike_recorder")
- nest.Connect(neuron1, sd_reference)
- nest.Connect(neuron2, sd_testant)
+ nest.Connect(nest_neuron, sd_reference)
+ nest.Connect(nestml_neuron, sd_testant)
nest.Simulate(t_stop)
@@ -365,26 +365,35 @@ def _test_model_equivalence_psc(self, nest_model_name, nestml_model_name, gsl_er
# ResetKernel() does not unload modules for NEST Simulator < v3.7; ignore exception if module is already loaded on earlier versions
pass
- neuron1 = nest.Create(nest_model_name, params=nest_model_parameters)
- neuron2 = nest.Create(nestml_model_name, params=nestml_model_parameters)
+ nest_neuron = nest.Create(nest_model_name, params=nest_model_parameters)
+ nestml_neuron = nest.Create(nestml_model_name, params=nestml_model_parameters)
if model_initial_state is not None:
- nest.SetStatus(neuron1, model_initial_state)
- nest.SetStatus(neuron2, model_initial_state)
+ nest.SetStatus(nest_neuron, model_initial_state)
+ nest.SetStatus(nestml_neuron, model_initial_state)
# if gsl_error_tol is not None:
- # nest.SetStatus(neuron2, {"gsl_error_tol": gsl_error_tol})
+ # nest.SetStatus(nestml_neuron, {"gsl_error_tol": gsl_error_tol})
spikegenerator = nest.Create("spike_generator",
params={"spike_times": spike_times, "spike_weights": spike_weights})
-
- nest.Connect(spikegenerator, neuron1, syn_spec=syn_spec)
- nest.Connect(spikegenerator, neuron2, syn_spec=syn_spec)
+ nest.Connect(spikegenerator, nest_neuron, syn_spec=syn_spec)
+
+ if len(nestml_neuron.get("receptor_types")) > 1:
+ # this NESTML neuron is written as having separate input ports for excitatory and inhibitory spikes
+ syn_spec_nestml = syn_spec
+ if syn_spec_nestml is None:
+ syn_spec_nestml = {}
+ syn_spec_nestml.update({"receptor_type": nestml_neuron.get("receptor_types")["EXC_SPIKES"]})
+ nest.Connect(spikegenerator, nestml_neuron, syn_spec=syn_spec_nestml)
+ else:
+ # this NESTML neuron is written as having one input port for excitatory and inhibitory spikes (with sign of the weight telling the difference)
+ nest.Connect(spikegenerator, nestml_neuron, syn_spec=syn_spec)
spike_recorder1 = nest.Create("spike_recorder")
spike_recorder2 = nest.Create("spike_recorder")
- nest.Connect(neuron1, spike_recorder1)
- nest.Connect(neuron2, spike_recorder2)
+ nest.Connect(nest_neuron, spike_recorder1)
+ nest.Connect(nestml_neuron, spike_recorder2)
multimeter1 = nest.Create("multimeter")
multimeter2 = nest.Create("multimeter")
@@ -393,8 +402,8 @@ def _test_model_equivalence_psc(self, nest_model_name, nestml_model_name, gsl_er
nest.SetStatus(multimeter1, {"record_from": [V_m_specifier]})
nest.SetStatus(multimeter2, {"record_from": [V_m_specifier]})
- nest.Connect(multimeter1, neuron1)
- nest.Connect(multimeter2, neuron2)
+ nest.Connect(multimeter1, nest_neuron)
+ nest.Connect(multimeter2, nestml_neuron)
nest.Simulate(400.)
diff --git a/tests/nest_tests/non_linear_dendrite_test.py b/tests/nest_tests/non_linear_dendrite_test.py
index 42ff2e7e4..6c97a9bd9 100644
--- a/tests/nest_tests/non_linear_dendrite_test.py
+++ b/tests/nest_tests/non_linear_dendrite_test.py
@@ -48,7 +48,7 @@ class NestNonLinearDendriteTest(unittest.TestCase):
reason="This test does not support NEST 2")
def test_non_linear_dendrite(self):
I_dend_alias_name = "I_dend" # synaptic current
- I_dend_internal_name = "I_kernel2__X__I_2" # alias for the synaptic current
+ I_dend_internal_name = "I_kernel2__X__I_2__DOT__weight" # alias for the synaptic current
input_path = os.path.join(os.path.realpath(os.path.join(os.path.dirname(__file__), "resources")), "iaf_psc_exp_nonlineardendrite.nestml")
target_path = "target"
diff --git a/tests/nest_tests/recordable_variables_test.py b/tests/nest_tests/recordable_variables_test.py
index b8c126d23..9b6f59966 100644
--- a/tests/nest_tests/recordable_variables_test.py
+++ b/tests/nest_tests/recordable_variables_test.py
@@ -62,7 +62,7 @@ def test_recordable_variables(self):
sg = nest.Create("spike_generator", params={"spike_times": [20., 80.]})
nest.Connect(sg, neuron)
- mm = nest.Create('multimeter', params={'record_from': ['V_ex', 'V_rel', 'V_m', 'I_kernel__X__spikes'],
+ mm = nest.Create('multimeter', params={'record_from': ['V_ex', 'V_rel', 'V_m', 'I_kernel__X__spike_in_port__DOT__weight'],
'interval': 0.1})
nest.Connect(mm, neuron)
diff --git a/tests/nest_tests/resources/BiexponentialPostSynapticResponse.nestml b/tests/nest_tests/resources/BiexponentialPostSynapticResponse.nestml
index 388a1246b..2e2be1564 100644
--- a/tests/nest_tests/resources/BiexponentialPostSynapticResponse.nestml
+++ b/tests/nest_tests/resources/BiexponentialPostSynapticResponse.nestml
@@ -56,10 +56,10 @@ model biexp_postsynaptic_response_neuron:
kernel g_gap = g_gap_const * (exp(-t/tau_syn_decay_gap) - exp(-t/tau_syn_rise_gap))
kernel g_GABA'' = -(g_GABA + g_GABA' * (tau_syn_decay_E + tau_syn_rise_E)) / (tau_syn_decay_E * tau_syn_rise_E)
- inline I_syn_exc pA = (F_E + convolve(g_ex, spikeExc) * nS) * (V_m - E_ex)
- inline I_syn_inh pA = (F_I + convolve(g_in, spikeInh) * nS) * (V_m - E_in)
- inline I_syn_gap pA = (F_I + convolve(g_gap, spikeGap) * nS) * (V_m - E_gap)
- inline I_syn_GABA pA = (F_I + convolve(g_GABA, spikeGABA) * nS) * (V_m - E_gap)
+ inline I_syn_exc pA = (F_E + convolve(g_ex, spikeExc.weight)) * (V_m - E_ex)
+ inline I_syn_inh pA = (F_I + convolve(g_in, spikeInh.weight)) * (V_m - E_in)
+ inline I_syn_gap pA = (F_I + convolve(g_gap, spikeGap.weight)) * (V_m - E_gap)
+ inline I_syn_GABA pA = (F_I + convolve(g_GABA, spikeGABA.weight)) * (V_m - E_gap)
inline I_leak pA = g_L * (V_m - E_L)
V_m' = (-I_leak - I_syn_exc - I_syn_inh - I_syn_gap - I_syn_GABA + I_e + I_stim) / C_m
@@ -107,10 +107,10 @@ model biexp_postsynaptic_response_neuron:
g_gap_const real = 1 / (exp(-t_peak_gap / tau_syn_decay_gap) - exp(-t_peak_gap / tau_syn_rise_gap))
input:
- spikeInh <- spike
- spikeExc <- spike
- spikeGap <- spike
- spikeGABA <- spike
+ spikeInh <- spike(weight nS)
+ spikeExc <- spike(weight nS)
+ spikeGap <- spike(weight nS)
+ spikeGABA <- spike(weight nS)
I_stim pA <- continuous
output:
diff --git a/tests/nest_tests/resources/ConvolveSpikingNoAttributes.nestml b/tests/nest_tests/resources/ConvolveSpikingNoAttributes.nestml
new file mode 100644
index 000000000..78f3b0bc7
--- /dev/null
+++ b/tests/nest_tests/resources/ConvolveSpikingNoAttributes.nestml
@@ -0,0 +1,41 @@
+# ConvolveSpikingNoAttributes - Test convolution with spiking input ports without attributes
+# ##########################################################################################
+#
+#
+# Copyright statement
+# +++++++++++++++++++
+#
+# This file is part of NEST.
+#
+# Copyright (C) 2004 The NEST Initiative
+#
+# NEST is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# NEST is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with NEST. If not, see .
+#
+model convolve_spiking_no_attributes_neuron:
+ state:
+ x real = 0.
+
+ equations:
+ #kernel K = (e / tau_syn) * t * exp(-t / tau_syn)
+ kernel K = delta(t)
+ x' = convolve(K, spikes_in) / s
+
+ parameters:
+ tau_syn ms = 2 ms
+
+ input:
+ spikes_in <- spike
+
+ update:
+ integrate_odes()
diff --git a/tests/nest_tests/resources/FIR_filter.nestml b/tests/nest_tests/resources/FIR_filter.nestml
index d116b43fb..e192ace86 100644
--- a/tests/nest_tests/resources/FIR_filter.nestml
+++ b/tests/nest_tests/resources/FIR_filter.nestml
@@ -30,6 +30,8 @@
#
model fir_filter:
state:
+ spike_in_buffer real = 0.
+
# FIR filter output (to be recorded by NEST multimeter)
y real = 0.
@@ -42,11 +44,15 @@ model fir_filter:
h[N] real = 1. # filter coefficients
input:
- spike_in <- spike
+ spike_in <- spike(weight real)
+
+ onReceive(spike_in):
+ spike_in_buffer += spike_in.weight
update:
# circular buffer for input spike count per timestep
- x[i] = spike_in * s
+ x[i] = spike_in_buffer
+ spike_in_buffer = 0.
# compute the new value of y
j integer = 0
diff --git a/tests/nest_tests/resources/RecordableVariables.nestml b/tests/nest_tests/resources/RecordableVariables.nestml
index 2339a1d60..99b7ab7cc 100644
--- a/tests/nest_tests/resources/RecordableVariables.nestml
+++ b/tests/nest_tests/resources/RecordableVariables.nestml
@@ -33,8 +33,8 @@ model recordable_variables:
V_rel mV = 0 mV # Membrane potential relative to the reset potential
equations:
- kernel I_kernel = exp(-1/tau_syn*t)
- inline I_syn pA = convolve(I_kernel, spikes) * pA
+ kernel I_kernel = exp(-t / tau_syn)
+ inline I_syn pA = convolve(I_kernel, spike_in_port.weight)
recordable inline V_m mV = V_rel + V_reset
V_rel' = -V_rel / tau_m + (I_syn + I_e + I_stim) / C_m
@@ -47,7 +47,7 @@ model recordable_variables:
V_thr mV = -55 mV
input:
- spikes <- spike
+ spike_in_port <- spike(weight pA)
I_stim pA <- continuous
update:
diff --git a/tests/nest_tests/resources/add_spikes_to_ode.nestml b/tests/nest_tests/resources/add_spikes_to_ode.nestml
index 3dc74fd56..d8f6b30d0 100644
--- a/tests/nest_tests/resources/add_spikes_to_ode.nestml
+++ b/tests/nest_tests/resources/add_spikes_to_ode.nestml
@@ -1,25 +1,25 @@
# add_spikes_to_ode.nestml
# ########################
-#
+#
# Test that spikes from an input port can be directly added to a linear (propagator-solved) and nonlinear (numeric solver) ODE.
-#
+#
# Copyright statement
# +++++++++++++++++++
-#
+#
# This file is part of NEST.
-#
+#
# Copyright (C) 2004 The NEST Initiative
-#
+#
# NEST is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
-#
+#
# NEST is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see .
#
@@ -29,8 +29,9 @@ model add_spikes_to_ode:
y real = 0
equations:
- x' = -x + 42 * spikes # linear eq
- y' = -y**2 + 123 * spikes # nonlinear eq
+ kernel K = delta(t)
+ x' = -x + 42 * convolve(K, spikes) / s # linear eq
+ y' = -y**2 + 123 * convolve(K, spikes) / s # nonlinear eq
input:
spikes <- spike
@@ -40,6 +41,3 @@ model add_spikes_to_ode:
update:
integrate_odes()
-
- x += spikes / 5
- y += spikes / 42
diff --git a/tests/nest_tests/resources/aeif_cond_alpha_alt_neuron.nestml b/tests/nest_tests/resources/aeif_cond_alpha_alt_neuron.nestml
index 7428601ab..b1e7f1ad5 100644
--- a/tests/nest_tests/resources/aeif_cond_alpha_alt_neuron.nestml
+++ b/tests/nest_tests/resources/aeif_cond_alpha_alt_neuron.nestml
@@ -1,41 +1,41 @@
# aeif_cond_alpha - Conductance based exponential integrate-and-fire neuron model
# ###############################################################################
-#
+#
# Description
# +++++++++++
-#
+#
# aeif_psc_alpha is the adaptive exponential integrate and fire neuron according to Brette and Gerstner (2005), with post-synaptic conductances in the form of a bi-exponential ("alpha") function.
-#
+#
# The membrane potential is given by the following differential equation:
-#
+#
# .. math::
-#
+#
# C_m \frac{dV_m}{dt} =
# -g_L(V_m-E_L)+g_L\Delta_T\exp\left(\frac{V_m-V_{th}}{\Delta_T}\right) -
# g_e(t)(V_m-E_e) \\
# -g_i(t)(V_m-E_i)-w + I_e
-#
+#
# and
-#
+#
# .. math::
-#
+#
# \tau_w \frac{dw}{dt} = a(V_m-E_L) - w
-#
+#
# Note that the membrane potential can diverge to positive infinity due to the exponential term. To avoid numerical instabilities, instead of :math:`V_m`, the value :math:`\min(V_m,V_{peak})` is used in the dynamical equations.
-#
-#
+#
+#
# References
# ++++++++++
-#
+#
# .. [1] Brette R and Gerstner W (2005). Adaptive exponential
# integrate-and-fire model as an effective description of neuronal
# activity. Journal of Neurophysiology. 943637-3642
# DOI: https://doi.org/10.1152/jn.00686.2005
-#
-#
+#
+#
# See also
# ++++++++
-#
+#
# iaf_psc_alpha, aeif_psc_exp
#
model aeif_cond_alpha_alt_neuron:
@@ -90,8 +90,7 @@ model aeif_cond_alpha_alt_neuron:
I_e pA = 0 pA
input:
- exc_spikes <- excitatory spike
- inh_spikes <- inhibitory spike
+ spike_in_port <- spike(weight nS)
I_stim pA <- continuous
output:
@@ -105,11 +104,12 @@ model aeif_cond_alpha_alt_neuron:
# neuron not refractory
integrate_odes(g_exc, g_inh, V_m, w)
- onReceive(exc_spikes):
- g_exc' += exc_spikes * (e / tau_syn_exc) * nS * s
-
- onReceive(inh_spikes):
- g_inh' += inh_spikes * (e / tau_syn_inh) * nS * s
+ onReceive(spike_in_port):
+ spike_weight nS = integrate(spike_in_port.weight, t, t + timestep())
+ if spike_weight > 0:
+ g_exc' += spike_weight * (e / tau_syn_exc)
+ else:
+ g_inh' -= spike_weight * (e / tau_syn_inh)
onCondition(refr_t <= 0 ms and V_m >= V_th):
# threshold crossing
diff --git a/tests/nest_tests/resources/alpha_function_2nd_order_ode_neuron.nestml b/tests/nest_tests/resources/alpha_function_2nd_order_ode_neuron.nestml
index 82111c9cc..af9991d7c 100644
--- a/tests/nest_tests/resources/alpha_function_2nd_order_ode_neuron.nestml
+++ b/tests/nest_tests/resources/alpha_function_2nd_order_ode_neuron.nestml
@@ -1,43 +1,44 @@
# alpha_function_2nd_order_ode_neuron.nestml
# ##########################################
-#
+#
# Tests that for a system of higher-oder ODEs of the form F(x'',x',x)=0, integrate_odes(x) includes the integration of all the higher-order variables involved of the system.
-#
+#
# Copyright statement
# +++++++++++++++++++
-#
+#
# This file is part of NEST.
-#
+#
# Copyright (C) 2004 The NEST Initiative
-#
+#
# NEST is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
-#
+#
# NEST is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see .
#
model alpha_function_2nd_order_ode_neuron:
state:
- x real = 0
- x' ms**-1 = 0 * ms**-1
+ x real = 0
+ x' ms**-1 = 0 * ms**-1
y real = 0
input:
- fX <- spike
+ fX <- spike(weight real)
equations:
- x'' = - 2 * x' / ms - x / ms**2
+ x'' = -2 * x' / ms - x / ms**2
y' = (-y + 42) / s
update:
integrate_odes(x, y)
onReceive(fX):
- x' += e*fX * s / ms
+ spike_weight real = integrate(fX.weight, t, t + timestep())
+ x' += e * spike_weight / ms
diff --git a/tests/nest_tests/resources/beta_function_with_inline_expression_neuron.nestml b/tests/nest_tests/resources/beta_function_with_inline_expression_neuron.nestml
index 51af5512b..16cd12c2c 100644
--- a/tests/nest_tests/resources/beta_function_with_inline_expression_neuron.nestml
+++ b/tests/nest_tests/resources/beta_function_with_inline_expression_neuron.nestml
@@ -1,24 +1,24 @@
# beta_function_with_inline_expression_neuron
# ###########################################
-#
+#
# Description
# +++++++++++
-#
+#
# Used for testing processing of inline expressions.
-#
-#
+#
+#
# Copyright
# +++++++++
-#
+#
# This file is part of NEST.
-#
+#
# Copyright (C) 2004 The NEST Initiative
-#
+#
# NEST is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
-#
+#
# NEST is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
@@ -29,8 +29,8 @@
model beta_function_with_inline_expression_neuron:
parameters:
- tau1 ms = 20 ms ## decay time
- tau2 ms = 10 ms ## rise time
+ tau1 ms = 20 ms # decay time
+ tau2 ms = 10 ms # rise time
state:
x_ pA/ms = 0 pA/ms
@@ -41,12 +41,12 @@ model beta_function_with_inline_expression_neuron:
equations:
x' = x_ - x / tau2
- x_' = - x_ / tau1
+ x_' = -x_ / tau1
recordable inline z pA = x
input:
- weighted_input_spikes <- spike
+ spike_in_port <- spike(weight pA)
output:
spike
@@ -55,4 +55,4 @@ model beta_function_with_inline_expression_neuron:
integrate_odes()
onReceive(weighted_input_spikes):
- x_ += alpha * (1 / tau2 - 1 / tau1) * pA * weighted_input_spikes * s
+ x_ += alpha * (1 / tau2 - 1 / tau1) * integrate(spike_in_port.weight, t, t + timestep())
diff --git a/tests/nest_tests/resources/iaf_cond_exp_Istep_neuron.nestml b/tests/nest_tests/resources/iaf_cond_exp_Istep_neuron.nestml
index e4fdc2b3f..9492d0c6b 100644
--- a/tests/nest_tests/resources/iaf_cond_exp_Istep_neuron.nestml
+++ b/tests/nest_tests/resources/iaf_cond_exp_Istep_neuron.nestml
@@ -57,8 +57,8 @@ model iaf_cond_exp_Istep_neuron:
t_step[n_step] ms = 0. ms # times of step current changes
input:
- inh_spikes <- inhibitory spike
- exc_spikes <- excitatory spike
+ inh_spikes <- spike
+ exc_spikes <- spike
I_stim pA <- continuous
output:
diff --git a/tests/nest_tests/resources/iaf_psc_exp_multisynapse.nestml b/tests/nest_tests/resources/iaf_psc_exp_multisynapse.nestml
index 2c73778ba..57df2e0f3 100644
--- a/tests/nest_tests/resources/iaf_psc_exp_multisynapse.nestml
+++ b/tests/nest_tests/resources/iaf_psc_exp_multisynapse.nestml
@@ -21,7 +21,7 @@ model iaf_psc_exp_multisynapse_neuron:
kernel I_kernel2 = (e / tau_syn2) * t * exp(-t / tau_syn2)
kernel I_kernel3 = -exp(-t / tau_syn3)
- recordable inline I_syn pA = (convolve(I_kernel1, spikes1) - convolve(I_kernel2, spikes2) + convolve(I_kernel3, spikes3)) * pA
+ recordable inline I_syn pA = convolve(I_kernel1, spikes1.weight) - convolve(I_kernel2, spikes2.weight) + convolve(I_kernel3, spikes3.weight)
V_m' = -(V_m - E_L) / tau_m + (I_syn + I_e + I_stim) / C_m
@@ -37,9 +37,9 @@ model iaf_psc_exp_multisynapse_neuron:
I_e pA = 0 pA # External current.
input:
- spikes1 <- spike
- spikes2 <- spike
- spikes3 <- spike
+ spikes1 <- spike(weight pA)
+ spikes2 <- spike(weight pA)
+ spikes3 <- spike(weight pA)
I_stim pA <- continuous
output:
diff --git a/tests/nest_tests/resources/iaf_psc_exp_multisynapse_vectors.nestml b/tests/nest_tests/resources/iaf_psc_exp_multisynapse_vectors.nestml
index 305fcf53e..af96f9766 100644
--- a/tests/nest_tests/resources/iaf_psc_exp_multisynapse_vectors.nestml
+++ b/tests/nest_tests/resources/iaf_psc_exp_multisynapse_vectors.nestml
@@ -1,27 +1,27 @@
# iaf_psc_exp_multisynapse - Leaky integrate-and-fire neuron model with multiple ports
# ####################################################################################
-#
+#
# Description
# +++++++++++
-#
+#
# Used in NESTML unit testing.
-#
+#
# For more information about the model, see iaf_psc_exp in the ``models`` directory.
-#
+#
# For more information about "multisynapse" models, please refer to the NESTML documentation.
#
model iaf_psc_exp_multisynapse_vectors_neuron:
state:
- V_m mV = E_L # membrane potential
- refr_t ms = 0 ms # Refractory period timer
+ V_m mV = E_L # membrane potential
+ refr_t ms = 0 ms # Refractory period timer
is_refractory boolean = false
equations:
- kernel I_kernel1 = exp(-1/tau_syn1*t)
- kernel I_kernel2 = exp(-1/tau_syn2*t)
- kernel I_kernel3 = -exp(-1/tau_syn3*t)
+ kernel I_kernel1 = exp(-t / tau_syn1)
+ kernel I_kernel2 = exp(-t / tau_syn2)
+ kernel I_kernel3 = -exp(-t / tau_syn3)
- inline I_syn pA = (convolve(I_kernel1, spikes[0]) - convolve(I_kernel2, spikes[1]) + convolve(I_kernel3, spikes[2])) * pA
+ inline I_syn pA = convolve(I_kernel1, spikes[0].weight) - convolve(I_kernel2, spikes[1].weight) + convolve(I_kernel3, spikes[2].weight)
V_m' = -(V_m - E_L) / tau_m + (I_syn + I_e + I_stim) / C_m
@@ -38,7 +38,7 @@ model iaf_psc_exp_multisynapse_vectors_neuron:
I_e pA = 0pA # External current.
input:
- spikes[3] <- spike
+ spikes[3] <- spike(weight pA)
I_stim pA <- continuous
output:
diff --git a/tests/nest_tests/resources/iaf_psc_exp_nonlineardendrite.nestml b/tests/nest_tests/resources/iaf_psc_exp_nonlineardendrite.nestml
index cb29de804..5a7065f4a 100644
--- a/tests/nest_tests/resources/iaf_psc_exp_nonlineardendrite.nestml
+++ b/tests/nest_tests/resources/iaf_psc_exp_nonlineardendrite.nestml
@@ -42,9 +42,9 @@ model iaf_psc_exp_nonlineardendrite:
kernel I_kernel2 = (e / tau_syn2) * t * exp(-t / tau_syn2)
kernel I_kernel3 = exp(-t / tau_syn3)
- recordable inline I_dend pA = convolve(I_kernel2, I_2) * pA
+ recordable inline I_dend pA = convolve(I_kernel2, I_2.weight)
- inline I_syn pA = convolve(I_kernel1, I_1) * pA + dend_curr_enabled * I_dend + I_dend_ap + convolve(I_kernel3, I_3) * pA + I_e
+ inline I_syn pA = convolve(I_kernel1, I_1.weight) + dend_curr_enabled * I_dend + I_dend_ap + convolve(I_kernel3, I_3.weight) + I_e
V_m' = -(V_m - E_L) / tau_m + I_syn / C_m
@@ -65,9 +65,9 @@ model iaf_psc_exp_nonlineardendrite:
T_dend_ap ms = 10 ms # time window over which the dendritic current clamp is active
input:
- I_1 <- spike
- I_2 <- spike
- I_3 <- spike
+ I_1 <- spike(weight pA)
+ I_2 <- spike(weight pA)
+ I_3 <- spike(weight pA)
output:
spike
diff --git a/tests/nest_tests/resources/iaf_psc_exp_resolution_test.nestml b/tests/nest_tests/resources/iaf_psc_exp_resolution_test.nestml
index d659d2e78..dfc1b7a7a 100644
--- a/tests/nest_tests/resources/iaf_psc_exp_resolution_test.nestml
+++ b/tests/nest_tests/resources/iaf_psc_exp_resolution_test.nestml
@@ -1,23 +1,24 @@
# iaf_psc_exp_resolution_test
# ###########################
-#
+#
# Description
# +++++++++++
-#
+#
# Used to test resolution() function.
#
model iaf_psc_exp_resolution_test_neuron:
state:
+ I_syn_exc pA = 0 pA
+ I_syn_inh pA = 0 pA
V_m mV = E_L
refr_t ms = 0 ms # Refractory period timer
is_refractory boolean = false
a ms = resolution()
equations:
- kernel I_kernel_inh = exp(-t/tau_syn_inh)
- kernel I_kernel_exc = exp(-t/tau_syn_exc)
- inline I_syn pA = convolve(I_kernel_inh, inh_spikes) * pA + convolve(I_kernel_exc, exc_spikes) * pA + I_e + I_stim
- V_m' = -(V_m - E_L) / tau_m + I_syn / C_m
+ I_syn_exc' = -I_syn_exc / tau_syn_exc
+ I_syn_inh' = -I_syn_inh / tau_syn_inh
+ V_m' = -(V_m - E_L) / tau_m + (I_syn_exc - I_syn_inh + I_e + I_stim) / C_m
parameters:
C_m pF = 250 pF # Capacitance of the membrane
@@ -37,13 +38,19 @@ model iaf_psc_exp_resolution_test_neuron:
c ms = resolution()
input:
- exc_spikes <- excitatory spike
- inh_spikes <- inhibitory spike
+ spike_in_port <- spike(weight pA)
I_stim pA <- continuous
output:
spike
+ onReceive(spike_in_port):
+ # route the incoming spike on the basis of the weight: less than zero means an inhibitory spike; greater than zero means an excitatory spike
+ if spike_in_port.weight < 0:
+ I_syn_inh += spike_in_port.weight
+ else:
+ I_syn_exc += spike_in_port.weight
+
update:
d ms = resolution()
if is_refractory:
diff --git a/tests/nest_tests/resources/input_ports.nestml b/tests/nest_tests/resources/input_ports.nestml
index 057039a89..7d20c4545 100644
--- a/tests/nest_tests/resources/input_ports.nestml
+++ b/tests/nest_tests/resources/input_ports.nestml
@@ -35,15 +35,35 @@ model input_ports:
my_spikes_ip pA = 0 pA
input:
- AMPA_spikes <- excitatory spike
- GABA_spikes <- inhibitory spike
- NMDA_spikes <- spike
- foo[2] <- spike
- my_spikes[3] <- excitatory spike
- my_spikes2[3] <- inhibitory spike
+ AMPA_spikes <- spike(weight pA)
+ GABA_spikes <- spike(weight pA)
+ NMDA_spikes <- spike(weight pA)
+ foo[2] <- spike(weight pA)
+ my_spikes[3] <- spike(weight pA)
+ my_spikes2[3] <- spike(weight pA)
I_stim pA <- continuous
- update:
- bar += (NMDA_spikes + 2 * AMPA_spikes - 3 * GABA_spikes) * (pA * s)
- foo_spikes += (foo[0] + 5.5 * foo[1]) * (pA * s)
- my_spikes_ip += (my_spikes[0] + my_spikes[1] - my_spikes2[1]) * (pA * s)
+ onReceive(NMDA_spikes):
+ bar += NMDA_spikes.weight
+
+ onReceive(AMPA_spikes):
+ bar += 2 * AMPA_spikes.weight
+
+ onReceive(GABA_spikes):
+ bar += 3 * GABA_spikes.weight
+
+ onReceive(foo[0]):
+ foo_spikes += foo[0].weight
+
+ onReceive(foo[1]):
+ foo_spikes += 5.5 * foo[1].weight
+
+ onReceive(my_spikes[0]):
+ my_spikes_ip += my_spikes[0].weight
+
+ onReceive(my_spikes[1]):
+ my_spikes_ip += my_spikes[1].weight
+
+ onReceive(my_spikes2[1]):
+ print("SPIKE RECEIVED 3")
+ my_spikes_ip -= my_spikes2[1].weight
diff --git a/tests/nest_tests/resources/test_plasticity_dynamics_neuron.nestml b/tests/nest_tests/resources/test_plasticity_dynamics_neuron.nestml
index ac18960a4..9fd37e5c6 100644
--- a/tests/nest_tests/resources/test_plasticity_dynamics_neuron.nestml
+++ b/tests/nest_tests/resources/test_plasticity_dynamics_neuron.nestml
@@ -45,7 +45,8 @@ model test_plasticity_dynamics_neuron:
output:
spike
+ onReceive(spikes_parrot):
+ emit_spike()
+
update:
integrate_odes()
- if spikes_parrot * 1 s != 0:
- emit_spike()
diff --git a/tests/nest_tests/test_add_spikes_to_ode.py b/tests/nest_tests/test_add_spikes_to_ode.py
index 9f91e7d13..8d4743a43 100644
--- a/tests/nest_tests/test_add_spikes_to_ode.py
+++ b/tests/nest_tests/test_add_spikes_to_ode.py
@@ -19,9 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see .
-import nest
import os
+import nest
+
from pynestml.frontend.pynestml_frontend import generate_nest_target
diff --git a/tests/nest_tests/test_biexponential_synapse_kernel.py b/tests/nest_tests/test_biexponential_synapse_kernel.py
index 0ed0954d8..eb9b106bb 100644
--- a/tests/nest_tests/test_biexponential_synapse_kernel.py
+++ b/tests/nest_tests/test_biexponential_synapse_kernel.py
@@ -19,11 +19,12 @@
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see .
-import nest
import numpy as np
import os
import pytest
+import nest
+
from pynestml.codegeneration.nest_tools import NESTTools
from pynestml.frontend.pynestml_frontend import generate_nest_target
@@ -75,7 +76,7 @@ def test_biexp_synapse(self):
nest.Connect(sg4, neuron, syn_spec={"receptor_type": 4, "weight": 100.})
i_1 = nest.Create("multimeter", params={"record_from": [
- "g_gap__X__spikeGap", "g_ex__X__spikeExc", "g_in__X__spikeInh", "g_GABA__X__spikeGABA"], "interval": .1})
+ "g_gap__X__spikeGap__DOT__weight", "g_ex__X__spikeExc__DOT__weight", "g_in__X__spikeInh__DOT__weight", "g_GABA__X__spikeGABA__DOT__weight"], "interval": .1})
nest.Connect(i_1, neuron)
vm_1 = nest.Create("voltmeter")
@@ -105,16 +106,16 @@ def plot(self, vm_1, i_1, sd):
ax[0].scatter(sd.events["times"], np.mean(vm_1["V_m"]) * np.ones_like(sd.events["times"]))
- ax[1].plot(i_1["times"], i_1["g_gap__X__spikeGap"], label="g_gap__X__spikeGap")
+ ax[1].plot(i_1["times"], i_1["g_gap__X__spikeGap__DOT__weight"], label="g_gap__X__spikeGap")
ax[1].set_ylabel("current")
- ax[2].plot(i_1["times"], i_1["g_ex__X__spikeExc"], label="g_ex__X__spikeExc")
+ ax[2].plot(i_1["times"], i_1["g_ex__X__spikeExc__DOT__weight"], label="g_ex__X__spikeExc")
ax[2].set_ylabel("current")
- ax[3].plot(i_1["times"], i_1["g_in__X__spikeInh"], label="g_in__X__spikeInh")
+ ax[3].plot(i_1["times"], i_1["g_in__X__spikeInh__DOT__weight"], label="g_in__X__spikeInh")
ax[3].set_ylabel("current")
- ax[4].plot(i_1["times"], i_1["g_GABA__X__spikeGABA"], label="g_GABA__X__spikeGABA")
+ ax[4].plot(i_1["times"], i_1["g_GABA__X__spikeGABA__DOT__weight"], label="g_GABA__X__spikeGABA")
ax[4].set_ylabel("current")
for _ax in ax:
diff --git a/tests/nest_tests/test_convolve.py b/tests/nest_tests/test_convolve.py
new file mode 100644
index 000000000..7c9a9a981
--- /dev/null
+++ b/tests/nest_tests/test_convolve.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+#
+# test_convolve.py
+#
+# This file is part of NEST.
+#
+# Copyright (C) 2004 The NEST Initiative
+#
+# NEST is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# NEST is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with NEST. If not, see .
+
+import os
+import pytest
+
+import nest
+
+from pynestml.frontend.pynestml_frontend import generate_nest_target
+from pynestml.codegeneration.nest_tools import NESTTools
+
+
+class TestConvolve:
+ """
+ """
+
+ @pytest.mark.skipif(NESTTools.detect_nest_version().startswith("v2"),
+ reason="This test does not support NEST 2")
+ def test_convolve(self):
+ input_path = os.path.join(os.path.realpath(os.path.join(
+ os.path.dirname(__file__), "resources", "ConvolveSpikingNoAttributes.nestml")))
+ target_path = "target"
+ logging_level = "DEBUG"
+ module_name = "nestmlmodule"
+ suffix = "_nestml"
+
+ generate_nest_target(input_path,
+ target_path=target_path,
+ logging_level=logging_level,
+ module_name=module_name,
+ suffix=suffix)
+ nest.ResetKernel()
+ nest.Install(module_name)
+
+ neuron = nest.Create("convolve_spiking_no_attributes_neuron_nestml")
+ sg = nest.Create("spike_generator")
+ sg.spike_times = [10., 50.]
+
+ nest.Connect(sg, neuron)
+
+ mm = nest.Create("multimeter", {"record_from": ["x"]})
+ nest.Connect(mm, neuron)
+
+ nest.Simulate(100.)
+
+ events = mm.get("events")
+
+ import matplotlib.pyplot as plt
+ plt.subplots()
+ plt.plot(events["times"], events["x"])
+ plt.savefig("/tmp/test_convolve.png")
+
+ assert events["x"][-1] == 2E-3
diff --git a/tests/nest_tests/test_linear_time_invariant_input_port_optimisation.py b/tests/nest_tests/test_linear_time_invariant_input_port_optimisation.py
new file mode 100644
index 000000000..be65cc7e6
--- /dev/null
+++ b/tests/nest_tests/test_linear_time_invariant_input_port_optimisation.py
@@ -0,0 +1,133 @@
+# -*- coding: utf-8 -*-
+#
+# test_linear_time_invariant_input_port_optimisation.py
+#
+# This file is part of NEST.
+#
+# Copyright (C) 2004 The NEST Initiative
+#
+# NEST is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# NEST is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with NEST. If not, see .
+
+from typing import Optional
+
+import matplotlib.pyplot as plt
+import numpy as np
+import os
+import pytest
+
+import nest
+
+from pynestml.codegeneration.nest_tools import NESTTools
+from pynestml.frontend.pynestml_frontend import generate_nest_target
+
+TestLinearTimeInvariantInputPortOptimisation_neuron_types = ["iaf_psc_delta", "iaf_psc_exp"]
+
+
+@pytest.mark.skipif(NESTTools.detect_nest_version().startswith("v2"),
+ reason="This test does not support NEST 2")
+class TestLinearTimeInvariantInputPortOptimisation:
+ """
+ Test that the optimisations with the ``linear_time_invariant_spiking_input_ports`` NEST code generator option are working correctly.
+ """
+
+ module_name: Optional[str] = None
+
+ @pytest.fixture(scope="module", autouse=True)
+ def generate_code(self):
+ TestLinearTimeInvariantInputPortOptimisation.module_name = "nestmlmodule" # unfortunately, pytest only allows us to set static attributes on the class
+ input_path = [os.path.join(os.path.realpath(os.path.join(os.path.dirname(__file__), os.path.join(os.pardir, os.pardir, "models", "neurons", neuron_name + "_neuron.nestml")))) for neuron_name in TestLinearTimeInvariantInputPortOptimisation_neuron_types]
+ target_path = "nestmlmodule"
+ logging_level = "DEBUG"
+ suffix = "_nestml"
+ codegen_opts = {"linear_time_invariant_spiking_input_ports": ["spike_in_port"]}
+
+ generate_nest_target(input_path, target_path,
+ module_name=TestLinearTimeInvariantInputPortOptimisation.module_name,
+ logging_level=logging_level,
+ suffix=suffix,
+ codegen_opts=codegen_opts)
+
+ @pytest.mark.xfail
+ @pytest.mark.parametrize("neuron_name", TestLinearTimeInvariantInputPortOptimisation_neuron_types)
+ def test_simultaneous_spikes_different_ports(self, neuron_name: str):
+ r"""This is known to not work if there are simultaneous spikes!"""
+ spike_times_sg_exc = [10., 20., 30., 40., 50.]
+ spike_times_sg_exc2 = [40.]
+ spike_times_sg_inh = [20., 40.]
+ self.run_experiment(neuron_name,
+ spike_times_sg_exc,
+ spike_times_sg_exc2,
+ spike_times_sg_inh)
+
+ @pytest.mark.xfail
+ @pytest.mark.parametrize("neuron_name", TestLinearTimeInvariantInputPortOptimisation_neuron_types)
+ def test_simultaneous_spikes_different_ports2(self, neuron_name: str):
+ r"""This is known to not work if there are simultaneous spikes!"""
+ spike_times_sg_exc = [10., 20., 30., 40., 50.]
+ spike_times_sg_exc2 = [0.]
+ spike_times_sg_inh = [20., 40.]
+ self.run_experiment(neuron_name,
+ spike_times_sg_exc,
+ spike_times_sg_exc2,
+ spike_times_sg_inh)
+
+ @pytest.mark.parametrize("neuron_name", TestLinearTimeInvariantInputPortOptimisation_neuron_types)
+ def test_non_simultaneous_spikes_different_ports(self, neuron_name: str):
+ spike_times_sg_exc = [10., 20., 30., 40., 50.]
+ spike_times_sg_exc2 = [45.]
+ spike_times_sg_inh = [25., 55.]
+ self.run_experiment(neuron_name,
+ spike_times_sg_exc,
+ spike_times_sg_exc2,
+ spike_times_sg_inh)
+
+ def run_experiment(self, neuron_name, spike_times_sg_exc, spike_times_sg_exc2, spike_times_sg_inh):
+ nest.ResetKernel()
+ nest.Install(TestLinearTimeInvariantInputPortOptimisation.module_name)
+
+ sg_exc = nest.Create("spike_generator", {"spike_times": spike_times_sg_exc})
+ sg_exc2 = nest.Create("spike_generator", {"spike_times": spike_times_sg_exc2})
+ sg_inh = nest.Create("spike_generator", {"spike_times": spike_times_sg_inh})
+
+ neuron_nest = nest.Create(neuron_name)
+ neuron_nestml = nest.Create(neuron_name + "_neuron_nestml")
+ mm_nest = nest.Create("voltmeter")
+ mm_nestml = nest.Create("voltmeter")
+
+ nest.Connect(sg_exc, neuron_nest)
+ nest.Connect(sg_exc, neuron_nestml)
+ nest.Connect(sg_exc2, neuron_nest)
+ nest.Connect(sg_exc2, neuron_nestml)
+ nest.Connect(sg_inh, neuron_nest, syn_spec={"weight": -1})
+ nest.Connect(sg_inh, neuron_nestml, syn_spec={"weight": -1})
+
+ nest.Connect(mm_nest, neuron_nest)
+ nest.Connect(mm_nestml, neuron_nestml)
+
+ nest.Simulate(60.)
+
+ # plot the results
+
+ fig, ax = plt.subplots(nrows=1)
+
+ ax.plot(mm_nest.events["times"], mm_nest.events["V_m"], label="NEST")
+ ax.plot(mm_nestml.events["times"], mm_nestml.events["V_m"], label="NESTML")
+ ax.legend()
+
+ fig.savefig("/tmp/test_simultaneous_spikes_different_ports_[neuron=" + neuron_name + "].png")
+
+ # test that membrane potential is the same at the end of the simulation
+
+ assert neuron_nestml.V_m != neuron_nestml.E_L
+ np.testing.assert_allclose(neuron_nest.V_m, neuron_nestml.V_m)
diff --git a/tests/nest_tests/test_multisynapse.py b/tests/nest_tests/test_multisynapse.py
index 60fc50abe..add2b5fee 100644
--- a/tests/nest_tests/test_multisynapse.py
+++ b/tests/nest_tests/test_multisynapse.py
@@ -19,11 +19,12 @@
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see .
-import nest
import numpy as np
import os
import pytest
+import nest
+
from pynestml.codegeneration.nest_tools import NESTTools
from pynestml.frontend.pynestml_frontend import generate_nest_target
@@ -75,7 +76,7 @@ def test_multisynapse(self):
nest.Connect(sg3, neuron, syn_spec={"receptor_type": receptor_types["SPIKES3"], "weight": 500., "delay": 0.1})
mm = nest.Create("multimeter", params={"record_from": [
- "I_syn", "I_kernel2__X__spikes2", "I_kernel3__X__spikes3"], "interval": nest.resolution})
+ "I_syn", "I_kernel2__X__spikes2__DOT__weight", "I_kernel3__X__spikes3__DOT__weight"], "interval": nest.resolution})
nest.Connect(mm, neuron)
vm_1 = nest.Create("voltmeter", params={"interval": nest.resolution})
@@ -98,10 +99,10 @@ def test_multisynapse(self):
ax[1].plot(mm["times"], mm["I_syn"], label="I_syn")
ax[1].set_ylabel("current")
- ax[2].plot(mm["times"], mm["I_kernel2__X__spikes2"], label="I_kernel2")
+ ax[2].plot(mm["times"], mm["I_kernel2__X__spikes2__DOT__weight"], label="I_kernel2")
ax[2].set_ylabel("current")
- ax[3].plot(mm["times"], mm["I_kernel3__X__spikes3"], label="I_kernel3")
+ ax[3].plot(mm["times"], mm["I_kernel3__X__spikes3__DOT__weight"], label="I_kernel3")
ax[3].set_ylabel("current")
for _ax in ax:
@@ -117,7 +118,7 @@ def test_multisynapse(self):
fig.savefig("/tmp/test_multisynapse.png")
# testing
- np.testing.assert_almost_equal(V_m[-1], -72.77625579314515)
+ np.testing.assert_almost_equal(V_m[-1], -72.58743039242219)
def test_multisynapse_with_vector_input_ports(self):
input_path = os.path.join(os.path.realpath(os.path.join(
@@ -147,16 +148,16 @@ def test_multisynapse_with_vector_input_ports(self):
receptor_types = nest.GetStatus(neuron, "receptor_types")[0]
sg = nest.Create("spike_generator", params={"spike_times": [20., 80.]})
- nest.Connect(sg, neuron, syn_spec={"receptor_type": receptor_types["SPIKES_0"], "weight": 1000., "delay": 0.1})
+ nest.Connect(sg, neuron, syn_spec={"receptor_type": receptor_types["SPIKES_VEC_IDX_0"], "weight": 1000., "delay": 0.1})
sg2 = nest.Create("spike_generator", params={"spike_times": [40., 60.]})
- nest.Connect(sg2, neuron, syn_spec={"receptor_type": receptor_types["SPIKES_1"], "weight": 1000., "delay": 0.1})
+ nest.Connect(sg2, neuron, syn_spec={"receptor_type": receptor_types["SPIKES_VEC_IDX_1"], "weight": 1000., "delay": 0.1})
sg3 = nest.Create("spike_generator", params={"spike_times": [30., 70.]})
- nest.Connect(sg3, neuron, syn_spec={"receptor_type": receptor_types["SPIKES_2"], "weight": 500., "delay": 0.1})
+ nest.Connect(sg3, neuron, syn_spec={"receptor_type": receptor_types["SPIKES_VEC_IDX_2"], "weight": 500., "delay": 0.1})
mm = nest.Create("multimeter", params={"record_from": [
- "I_kernel1__X__spikes_0", "I_kernel2__X__spikes_1", "I_kernel3__X__spikes_2"], "interval": nest.resolution})
+ "I_kernel1__X__spikes_0__DOT__weight", "I_kernel2__X__spikes_1__DOT__weight", "I_kernel3__X__spikes_2__DOT__weight"], "interval": nest.resolution})
nest.Connect(mm, neuron)
vm_1 = nest.Create("voltmeter", params={"interval": nest.resolution})
@@ -175,13 +176,13 @@ def test_multisynapse_with_vector_input_ports(self):
ax[0].plot(V_m_timevec, V_m, label="V_m")
ax[0].set_ylabel("voltage")
- ax[1].plot(mm.events["times"], mm.events["I_kernel1__X__spikes_0"], label="I_kernel0")
+ ax[1].plot(mm.events["times"], mm.events["I_kernel1__X__spikes_0__DOT__weight"], label="I_kernel0")
ax[1].set_ylabel("current")
- ax[2].plot(mm.events["times"], mm.events["I_kernel2__X__spikes_1"], label="I_kernel1")
+ ax[2].plot(mm.events["times"], mm.events["I_kernel2__X__spikes_1__DOT__weight"], label="I_kernel1")
ax[2].set_ylabel("current")
- ax[3].plot(mm.events["times"], mm.events["I_kernel3__X__spikes_2"], label="I_kernel2")
+ ax[3].plot(mm.events["times"], mm.events["I_kernel3__X__spikes_2__DOT__weight"], label="I_kernel2")
ax[3].set_ylabel("current")
for _ax in ax:
diff --git a/tests/nest_tests/test_simultaneous_spikes_different_ports.py b/tests/nest_tests/test_simultaneous_spikes_different_ports.py
new file mode 100644
index 000000000..c22f14ca1
--- /dev/null
+++ b/tests/nest_tests/test_simultaneous_spikes_different_ports.py
@@ -0,0 +1,93 @@
+# -*- coding: utf-8 -*-
+#
+# test_simultaneous_spikes_different_ports.py
+#
+# This file is part of NEST.
+#
+# Copyright (C) 2004 The NEST Initiative
+#
+# NEST is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# NEST is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with NEST. If not, see .
+
+import matplotlib.pyplot as plt
+import numpy as np
+import os
+import pytest
+
+import nest
+
+from pynestml.codegeneration.nest_tools import NESTTools
+from pynestml.frontend.pynestml_frontend import generate_nest_target
+
+
+@pytest.mark.skipif(NESTTools.detect_nest_version().startswith("v2"),
+ reason="This test does not support NEST 2")
+class TestSimultaneousSpikesDifferentPorts:
+ """
+ Tests the code generation and running a little simulation. Check that the numerical membrane voltage at the end of the simulation is close to a hard-coded numeric value.
+ """
+
+ @pytest.mark.parametrize("neuron_name", ["aeif_cond_exp", "iaf_psc_delta"])
+ def test_simultaneous_spikes_different_ports(self, neuron_name: str):
+ input_path = os.path.join(os.path.realpath(os.path.join(os.path.dirname(__file__), os.path.join(os.pardir, os.pardir, "models", "neurons", neuron_name + "_neuron.nestml"))))
+ target_path = "nestmlmodule"
+ logging_level = "DEBUG"
+ suffix = "_nestml"
+ module_name = "nestmlmodule"
+ codegen_opts = {}
+
+ generate_nest_target(input_path, target_path,
+ module_name=module_name,
+ logging_level=logging_level,
+ suffix=suffix,
+ codegen_opts=codegen_opts)
+
+ nest.ResetKernel()
+ nest.Install(module_name)
+
+ sg_exc = nest.Create("spike_generator", {"spike_times": [10., 20., 30., 40., 50.]})
+ sg_exc2 = nest.Create("spike_generator", {"spike_times": [40.]})
+ sg_inh = nest.Create("spike_generator", {"spike_times": [20., 40.]})
+ # sg_inh = nest.Create("spike_generator", {"spike_times": []})
+
+ neuron_nest = nest.Create(neuron_name)
+ neuron_nestml = nest.Create(neuron_name + "_neuron_nestml")
+ mm_nest = nest.Create("voltmeter")
+ mm_nestml = nest.Create("voltmeter")
+
+ nest.Connect(sg_exc, neuron_nest)
+ nest.Connect(sg_exc, neuron_nestml)
+ nest.Connect(sg_exc2, neuron_nest)
+ nest.Connect(sg_exc2, neuron_nestml)
+ nest.Connect(sg_inh, neuron_nest, syn_spec={"weight": -1})
+ nest.Connect(sg_inh, neuron_nestml, syn_spec={"weight": -1})
+
+ nest.Connect(mm_nest, neuron_nest)
+ nest.Connect(mm_nestml, neuron_nestml)
+
+ nest.Simulate(60.)
+
+ # plot the results
+
+ fig, ax = plt.subplots(nrows=1)
+
+ ax.plot(mm_nest.events["times"], mm_nest.events["V_m"], label="NEST")
+ ax.plot(mm_nestml.events["times"], mm_nestml.events["V_m"], label="NESTML")
+ ax.legend()
+
+ fig.savefig("/tmp/test_simultaneous_spikes_different_ports.png")
+
+ # test that membrane potential is the same at the end of the simulation
+
+ assert neuron_nestml.V_m != neuron_nestml.E_L
+ np.testing.assert_allclose(neuron_nest.V_m, neuron_nestml.V_m)
diff --git a/tests/resources/NestMLPrinterTest.nestml b/tests/resources/NestMLPrinterTest.nestml
index 087fe983b..0356f23b4 100644
--- a/tests/resources/NestMLPrinterTest.nestml
+++ b/tests/resources/NestMLPrinterTest.nestml
@@ -61,7 +61,7 @@ model aeif_cond_alpha_implicit:
# input pre
input:
# input decl pre
- inh_spikes <- inhibitory spike # input decl in
+ inh_spikes <- spike # input decl in
# input decl post
# output pre
diff --git a/tests/resources/random_number_generators_test.nestml b/tests/resources/random_number_generators_test.nestml
index b65210e62..2cf61a32b 100644
--- a/tests/resources/random_number_generators_test.nestml
+++ b/tests/resources/random_number_generators_test.nestml
@@ -33,10 +33,5 @@ model test_random:
q' = random_normal(500, 25) / s
r' = random_uniform(42, 123) / s
- input:
- exc_spikes <- excitatory spike
- inh_spikes <- inhibitory spike
- currents pA <- continuous
-
update:
integrate_odes()
diff --git a/tests/resources/spiking_input_port_on_equation_rhs_outside_convolve.nestml b/tests/resources/spiking_input_port_on_equation_rhs_outside_convolve.nestml
new file mode 100644
index 000000000..d9638e5f9
--- /dev/null
+++ b/tests/resources/spiking_input_port_on_equation_rhs_outside_convolve.nestml
@@ -0,0 +1,36 @@
+# spiking_input_port_on_equation_rhs_outside_convolve
+# ##########################################################################################
+#
+#
+# Copyright statement
+# +++++++++++++++++++
+#
+# This file is part of NEST.
+#
+# Copyright (C) 2004 The NEST Initiative
+#
+# NEST is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# NEST is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with NEST. If not, see .
+#
+model spiking_input_port_on_equation_rhs_outside_convolve_neuron:
+ state:
+ x real = 0.
+
+ equations:
+ x' = spikes_in_port
+
+ input:
+ spikes_in_port <- spike
+
+ update:
+ integrate_odes()
diff --git a/tests/resources/spiking_input_port_on_equation_rhs_outside_convolve2.nestml b/tests/resources/spiking_input_port_on_equation_rhs_outside_convolve2.nestml
new file mode 100644
index 000000000..4d40bb6c8
--- /dev/null
+++ b/tests/resources/spiking_input_port_on_equation_rhs_outside_convolve2.nestml
@@ -0,0 +1,36 @@
+# spiking_input_port_on_equation_rhs_outside_convolve
+# ###################################################
+#
+#
+# Copyright statement
+# +++++++++++++++++++
+#
+# This file is part of NEST.
+#
+# Copyright (C) 2004 The NEST Initiative
+#
+# NEST is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# NEST is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with NEST. If not, see .
+#
+model spiking_input_port_on_equation_rhs_outside_convolve2_neuron:
+ state:
+ x real = 0.
+
+ equations:
+ x' = spikes_in_port.weight
+
+ input:
+ spikes_in_port <- spike(weight real)
+
+ update:
+ integrate_odes()
diff --git a/tests/test_cocos.py b/tests/test_cocos.py
index 969e92b1d..eb78138fa 100644
--- a/tests/test_cocos.py
+++ b/tests/test_cocos.py
@@ -132,11 +132,7 @@ def test_valid_inline_expression_has_several_lhs(self):
def test_invalid_no_values_assigned_to_input_ports(self):
model = self._parse_and_validate_model(os.path.join(os.path.realpath(os.path.join(os.path.dirname(__file__), 'invalid')), 'CoCoValueAssignedToInputPort.nestml'))
- assert len(Logger.get_messages(model, LoggingLevel.ERROR)) == 1
-
- def test_valid_no_values_assigned_to_input_ports(self):
- model = self._parse_and_validate_model(os.path.join(os.path.realpath(os.path.join(os.path.dirname(__file__), 'valid')), 'CoCoValueAssignedToInputPort.nestml'))
- assert len(Logger.get_messages(model, LoggingLevel.ERROR)) == 0
+ assert len(Logger.get_messages(model, LoggingLevel.ERROR)) == 2
def test_invalid_order_of_equations_correct(self):
model = self._parse_and_validate_model(os.path.join(os.path.realpath(os.path.join(os.path.dirname(__file__), 'invalid')), 'CoCoNoOrderOfEquations.nestml'))
@@ -202,14 +198,10 @@ def test_valid_no_nest_collision(self):
model = self._parse_and_validate_model(os.path.join(os.path.realpath(os.path.join(os.path.dirname(__file__), 'valid')), 'CoCoNestNamespaceCollision.nestml'))
assert len(Logger.get_messages(model, LoggingLevel.ERROR)) == 0
- def test_invalid_redundant_input_port_keywords_detected(self):
- model = self._parse_and_validate_model(os.path.join(os.path.realpath(os.path.join(os.path.dirname(__file__), 'invalid')), 'CoCoInputPortWithRedundantTypes.nestml'))
+ def test_invalid_co_co_spike_input_ports_illegal_missing_attribute(self):
+ model = self._parse_and_validate_model(os.path.join(os.path.realpath(os.path.join(os.path.dirname(__file__), 'invalid')), 'CoCoInputPortsIllegalMissingAttribute.nestml'))
assert len(Logger.get_messages(model, LoggingLevel.ERROR)) == 1
- def test_valid_redundant_input_port_keywords_detected(self):
- model = self._parse_and_validate_model(os.path.join(os.path.realpath(os.path.join(os.path.dirname(__file__), 'valid')), 'CoCoInputPortWithRedundantTypes.nestml'))
- assert len(Logger.get_messages(model, LoggingLevel.ERROR)) == 0
-
def test_invalid_parameters_assigned_only_in_parameters_block(self):
model = self._parse_and_validate_model(os.path.join(os.path.realpath(os.path.join(os.path.dirname(__file__), 'invalid')), 'CoCoParameterAssignedOutsideBlock.nestml'))
assert len(Logger.get_messages(model, LoggingLevel.ERROR)) == 1
diff --git a/tests/valid/CoCoConvolveNotCorrectlyParametrized.nestml b/tests/valid/CoCoConvolveNotCorrectlyParametrized.nestml
index 63f67cbf5..e513769c5 100644
--- a/tests/valid/CoCoConvolveNotCorrectlyParametrized.nestml
+++ b/tests/valid/CoCoConvolveNotCorrectlyParametrized.nestml
@@ -42,4 +42,4 @@ model CoCoConvolveNotCorrectlyParametrized:
inline testB pA = convolve(G, spikeExc) * pA # convolve is now correctly parametrized
input:
- spikeExc <- excitatory spike
+ spikeExc <- spike
diff --git a/tests/valid/CoCoConvolveNotCorrectlyProvided.nestml b/tests/valid/CoCoConvolveNotCorrectlyProvided.nestml
index 39b505b31..e8160c731 100644
--- a/tests/valid/CoCoConvolveNotCorrectlyProvided.nestml
+++ b/tests/valid/CoCoConvolveNotCorrectlyProvided.nestml
@@ -32,11 +32,11 @@
#
model CoCoConvolveNotCorrectlyProvided:
equations:
- kernel test = 10
+ kernel test = delta(t)
inline testB pA = convolve(test, spikeExc) * pA # convolve provided with a kernel and a spike input port, thus correct
input:
- spikeExc <- excitatory spike
+ spikeExc <- spike
update:
integrate_odes()
diff --git a/tests/valid/CoCoInputPortWithRedundantTypes.nestml b/tests/valid/CoCoInputPortsIllegalMissingAttribute.nestml
similarity index 67%
rename from tests/valid/CoCoInputPortWithRedundantTypes.nestml
rename to tests/valid/CoCoInputPortsIllegalMissingAttribute.nestml
index 2e4bea075..3cde8b10f 100644
--- a/tests/valid/CoCoInputPortWithRedundantTypes.nestml
+++ b/tests/valid/CoCoInputPortsIllegalMissingAttribute.nestml
@@ -1,14 +1,10 @@
-# CoCoInputPortWithRedundantTypes.nestml
-# ######################################
-#
+# CoCoInputPortsIllegal.nestml
+# ############################
#
# Description
# +++++++++++
#
-# This model is used to test if broken CoCos are identified correctly. Here, if each input port is defined uniquely, i.e., no redundant keywords are used.
-#
-# Positive case.
-#
+# This test is used to test the declaration of both vectorized and non-vectorized input ports.
#
# Copyright statement
# +++++++++++++++++++
@@ -30,6 +26,12 @@
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see .
#
-model CoCoInputPortWithRedundantTypes:
+model input_ports_illegal_neuron:
+ state:
+ foo real = 0.
+
input:
- spikeInh <- inhibitory spike # no redundant keywords used, thus correct
+ spike_in_port <- spike(foo real)
+
+ onReceive(spike_in_port):
+ foo += spike_in_port.foo
diff --git a/tests/valid/CoCoInputPortsLegal.nestml b/tests/valid/CoCoInputPortsLegal.nestml
new file mode 100644
index 000000000..15d964e37
--- /dev/null
+++ b/tests/valid/CoCoInputPortsLegal.nestml
@@ -0,0 +1,49 @@
+# CoCoInputPortsIllegal.nestml
+# ############################
+#
+#
+# Description
+# +++++++++++
+#
+# This test is used to test the declaration of both vectorized and non-vectorized input ports.
+#
+#
+# Copyright statement
+# +++++++++++++++++++
+#
+# This file is part of NEST.
+#
+# Copyright (C) 2004 The NEST Initiative
+#
+# NEST is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# NEST is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with NEST. If not, see .
+#
+model input_ports_legal_neuron:
+ state:
+ bar pA = 0 pA
+ foo_spikes pA = 0 pA
+ my_spikes_ip pA = 0 pA
+
+ input:
+ AMPA_spikes <- spike
+ GABA_spikes <- spike
+ NMDA_spikes <- spike
+ foo[2] <- spike
+ my_spikes[3] <- spike
+ my_spikes2[3] <- spike
+ I_stim pA <- continuous
+
+ onReceive(AMPA_spikes):
+ bar += (NMDA_spikes + 2 * AMPA_spikes - 3 * GABA_spikes) * (pA * s)
+ foo_spikes += (foo[0] + 5.5 * foo[1]) * (pA * s)
+ my_spikes_ip += (my_spikes[0] + my_spikes[1] - my_spikes2[1]) * (pA * s)
diff --git a/tests/valid/CoCoValueAssignedToInputPort.nestml b/tests/valid/CoCoOnReceiveVectorsShouldBeConstantSize.nestml
similarity index 64%
rename from tests/valid/CoCoValueAssignedToInputPort.nestml
rename to tests/valid/CoCoOnReceiveVectorsShouldBeConstantSize.nestml
index 92d23869d..bfca05009 100644
--- a/tests/valid/CoCoValueAssignedToInputPort.nestml
+++ b/tests/valid/CoCoOnReceiveVectorsShouldBeConstantSize.nestml
@@ -1,14 +1,10 @@
-# CoCoValueAssignedToInputPort.nestml
-# ###################################
-#
-#
+# CoCoOnReceiveVectorsShouldBeConstantSize.nestml
+# ###############################################
+#
# Description
# +++++++++++
-#
-# This model is used to test if broken CoCos are identified correctly. Here, if assignment of values to input ports is detected.
-#
-# Positive case.
-#
+#
+# This test is used to test the usage of onReceive blocks for vector ports of variable length.
#
# Copyright statement
# +++++++++++++++++++
@@ -30,9 +26,12 @@
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see .
#
-model CoCoValueAssignedToInputPort:
+model CoCoOnReceiveVectorsShouldBeConstantSize:
+ state:
+ GABA_spikes_sum real = 0
+
input:
- spikeInh <- inhibitory spike
+ GABA_spikes[5] <- spike(weight real)
- update: # input port not assigned to, thus everything is correct
- test integer = spikeInh * s + 10
+ onReceive(GABA_spikes[i integer]):
+ GABA_spikes_sum += GABA_spikes[i].weight
diff --git a/tests/valid/CoCoVectorInputPortSizeAndType.nestml b/tests/valid/CoCoVectorInputPortSizeAndType.nestml
index bf0f5fa37..c54dff121 100644
--- a/tests/valid/CoCoVectorInputPortSizeAndType.nestml
+++ b/tests/valid/CoCoVectorInputPortSizeAndType.nestml
@@ -34,5 +34,5 @@
model CoCoVectorInputPortSizeAndType:
input:
foo[2] <- spike
- bar1[3] <- excitatory spike
- bar2[3] <- inhibitory spike
+ bar1[3] <- spike
+ bar2[3] <- spike