Ejemplo n.º 1
0
class WilsonCowan(TVBWilsonCowan):
    r"""
    **References**:

    .. [WC_1972] Wilson, H.R. and Cowan, J.D. *Excitatory and inhibitory
        interactions in localized populations of model neurons*, Biophysical
        journal, 12: 1-24, 1972.
    .. [WC_1973] Wilson, H.R. and Cowan, J.D  *A Mathematical Theory of the
        Functional Dynamics of Cortical and Thalamic Nervous Tissue*

    .. [D_2011] Daffertshofer, A. and van Wijk, B. *On the influence of
        amplitude on the connectivity between phases*
        Frontiers in Neuroinformatics, July, 2011

    Used Eqns 11 and 12 from [WC_1972]_ in ``dfun``.  P and Q represent external
    inputs, which when exploring the phase portrait of the local model are set
    to constant values. However in the case of a full network, P and Q are the
    entry point to our long range and local couplings, that is, the  activity
    from all other nodes is the external input to the local population.

    The default parameters are taken from figure 4 of [WC_1972]_, pag. 10

    In [WC_1973]_ they present a model of neural tissue on the pial surface is.
    See Fig. 1 in page 58. The following local couplings (lateral interactions)
    occur given a region i and a region j:

      E_i-> E_j
      E_i-> I_j
      I_i-> I_j
      I_i-> E_j


    +---------------------------+
    |          Table 1          |
    +--------------+------------+
    |                           |
    |  SanzLeonetAl,   2014     |
    +--------------+------------+
    |Parameter     |  Value     |
    +==============+============+
    | k_e, k_i     |    1.00    |
    +--------------+------------+
    | r_e, r_i     |    0.00    |
    +--------------+------------+
    | tau_e, tau_i |    10.0    |
    +--------------+------------+
    | c_1          |    10.0    |
    +--------------+------------+
    | c_2          |    6.0     |
    +--------------+------------+
    | c_3          |    1.0     |
    +--------------+------------+
    | c_4          |    1.0     |
    +--------------+------------+
    | a_e, a_i     |    1.0     |
    +--------------+------------+
    | b_e, b_i     |    0.0     |
    +--------------+------------+
    | theta_e      |    2.0     |
    +--------------+------------+
    | theta_i      |    3.5     |
    +--------------+------------+
    | alpha_e      |    1.2     |
    +--------------+------------+
    | alpha_i      |    2.0     |
    +--------------+------------+
    | P            |    0.5     |
    +--------------+------------+
    | Q            |    0       |
    +--------------+------------+
    | c_e, c_i     |    1.0     |
    +--------------+------------+
    | alpha_e      |    1.2     |
    +--------------+------------+
    | alpha_i      |    2.0     |
    +--------------+------------+
    |                           |
    |  frequency peak at 20  Hz |
    |                           |
    +---------------------------+


    The parameters in Table 1 reproduce Figure A1 in  [D_2011]_
    but set the limit cycle frequency to a sensible value (eg, 20Hz).

    Model bifurcation parameters:
        * :math:`c_1`
        * :math:`P`



    The builders (:math:`E`, :math:`I`) phase-plane, including a representation of
    the vector field as well as its nullclines, using default parameters, can be
    seen below:

        .. _phase-plane-WC:
        .. figure :: img/WilsonCowan_01_mode_0_pplane.svg
            :alt: Wilson-Cowan phase plane (E, I)

            The (:math:`E`, :math:`I`) phase-plane for the Wilson-Cowan model.


    The general formulation for the \textit{\textbf{Wilson-Cowan}} model as a
    dynamical unit at a node $k$ in a BNM with $l$ nodes reads:

    .. math::
            \dot{E}_k &= \dfrac{1}{\tau_e} (-E_k  + (k_e - r_e E_k) \mathcal{S}_e (\alpha_e \left( c_{ee} E_k - c_{ei} I_k  + P_k - \theta_e + \mathbf{\Gamma}(E_k, E_j, u_{kj}) + W_{\zeta}\cdot E_j + W_{\zeta}\cdot I_j\right) ))\\
            \dot{I}_k &= \dfrac{1}{\tau_i} (-I_k  + (k_i - r_i I_k) \mathcal{S}_i (\alpha_i \left( c_{ie} E_k - c_{ee} I_k  + Q_k - \theta_i + \mathbf{\Gamma}(E_k, E_j, u_{kj}) + W_{\zeta}\cdot E_j + W_{\zeta}\cdot I_j\right) )),

    """

    # Define traited attributes for this model, these represent possible kwargs.

    tau_Ein = NArray(
        label=r":math:`\tau_Ein`",
        default=numpy.array([
            50.,
        ]),
        domain=Range(lo=1., hi=100., step=1.0),
        doc=
        """[ms]. Excitatory population instant spiking rate time constant.""")

    tau_Iin = NArray(
        label=r":math:`\tau_Iin`",
        default=numpy.array([
            50.,
        ]),
        domain=Range(lo=1., hi=100., step=1.0),
        doc=
        """[ms]. Inhibitory population instant spiking rate time constant.""")

    # Used for phase-plane axis ranges and to bound random initial() conditions.
    state_variable_boundaries = Final(
        label="State Variable boundaries [lo, hi]",
        default={
            "E": numpy.array([0.0, 1.0]),
            "I": numpy.array([0.0, 1.0]),
            "Ein": numpy.array([0.0, 1.0]),
            "Iin": numpy.array([0.0, 1.0])
        },
        doc="""The values for each state-variable should be set to encompass
                the boundaries of the dynamic range of that state-variable. 
                Set None for one-sided boundaries.""")

    # Used for phase-plane axis ranges and to bound random initial() conditions.
    state_variable_range = Final(
        label="State Variable ranges [lo, hi]",
        default={
            "E": numpy.array([0.0, 0.5]),
            "I": numpy.array([0.0, 0.5]),
            "Ein": numpy.array([0.0, 0.5]),
            "Iin": numpy.array([0.0, 0.5])
        },
        doc="""The values for each state-variable should be set to encompass
        the expected dynamic range of that state-variable for the current
        parameters, it is used as a mechanism for bounding random inital
        conditions when the simulation isn't started from an explicit history,
        it is also provides the default range of phase-plane plots.""")

    variables_of_interest = List(
        of=str,
        label="Variables watched by Monitors",
        choices=('E', 'I', 'Ein', 'Iin'),
        default=('E', 'I', 'Ein', 'Iin'),
        doc="""default state variables to be monitored""")

    state_variables = ['E', 'I', 'Ein', 'Iin']
    _nvar = 4

    def update_derived_parameters(self):
        """
        When needed, this should be a method for calculating parameters that are
        calculated based on paramaters directly set by the caller. For example,
        see, ReducedSetFitzHughNagumo. When not needed, this pass simplifies
        code that updates an arbitrary models parameters -- ie, this can be
        safely called on any model, whether it's used or not.
        """
        for var in ["Ein", "Iin"]:
            if hasattr(self, var):
                setattr(self, "_" + var, getattr(self, var) > 0)
            else:
                setattr(self, "_" + var, numpy.array([
                    False,
                ]))

    def dfun(self, state_variables, coupling, local_coupling=0.0):
        r"""

        .. math::
            \tau \dot{x}(t) &= -z(t) + \phi(z(t)) \\
            \phi(x) &= \frac{c}{1-exp(-a (x-b))}

        """

        E = state_variables[0, :]
        I = state_variables[1, :]
        Ein = state_variables[2, :]  # Input from Spiking Network
        Iin = state_variables[3, :]  # Input from Spiking Network
        derivative = numpy.zeros(state_variables.shape)

        # long-range coupling
        c_0 = coupling[0, :]

        # short-range (local) coupling
        lc_0 = local_coupling * E
        lc_1 = local_coupling * I

        x_e = self.alpha_e * (self.c_ee * E - self.c_ei * I + self.P -
                              self.theta_e + c_0 + lc_0 + lc_1)
        x_i = self.alpha_i * (self.c_ie * E - self.c_ii * I + self.Q -
                              self.theta_i + lc_0 + lc_1)

        s_e = self.c_e / (1.0 + numpy.exp(-self.a_e * (x_e - self.b_e)))
        s_i = self.c_i / (1.0 + numpy.exp(-self.a_i * (x_i - self.b_i)))

        derivative[0] = numpy.where(
            self._Ein,
            (-E + Ein) / self.tau_Ein,  # Update from Spiking Network
            (-E + (self.k_e - self.r_e * E) * s_e) / self.tau_e)
        derivative[1] = numpy.where(
            self._Iin,
            (-I + Iin) / self.tau_Iin,  # Update from Spiking Network
            (-I + (self.k_i - self.r_i * I) * s_i) / self.tau_i)

        return derivative
Ejemplo n.º 2
0
class PreSigmoidal(Coupling):
    r"""
    Provides a pre-summation sigmoidal coupling function with a static or dynamic
    and local or global threshold.

    .. math::
        H * (Q + \tanh(G * (P*x - \theta)))

    The dynamic threshold as state variable given by the second state variable.
    With the coupling term, returns the direct node output for the dynamic threshold.

    """

    H = NArray(
        label="H",
        default=numpy.array([
            0.5,
        ]),
        domain=Range(lo=-100.0, hi=100.0, step=1.0),
        doc="Global Factor.",
    )

    Q = NArray(
        label="Q",
        default=numpy.array([
            1.,
        ]),
        domain=Range(lo=-100.0, hi=100.0, step=1.0),
        doc="Average.",
    )

    G = NArray(
        label="G",
        default=numpy.array([
            60.,
        ]),
        domain=Range(lo=-1000.0, hi=1000.0, step=1.),
        doc="Gain.",
    )

    P = NArray(
        label="P",
        default=numpy.array([
            1.,
        ]),
        domain=Range(lo=-100.0, hi=100.0, step=0.01),
        doc="Excitation-Inhibition ratio.",
    )

    theta = NArray(
        label=":math:`\\theta`",
        default=numpy.array([
            0.5,
        ]),
        domain=Range(lo=-100.0, hi=100.0, step=0.01),
        doc="Threshold.",
    )

    dynamic = Attr(
        field_type=bool,
        label="Dynamic",
        default=True,
        doc="Use dynamic threshold (otherwise static).",
    )

    globalT = Attr(
        field_type=bool,
        label=":math:`global_{\\theta}`",
        default=False,
        doc="Use global threshold (otherwise local).",
    )

    def __str__(self):
        return simple_gen_astr(self, 'H Q G P theta dynamic globalT')

    def configure(self):
        """Set the right indirect call."""
        super(PreSigmoidal, self).configure()
        self.sliceT = 0 if self.globalT else slice(None)

    # override __call__ directly simpler than pre/post form
    # TODO check use of arrays dims here
    def __call__(self, step, history, na=numpy.newaxis):
        g_ij = history.es_weights
        x_i, x_j = history.query(step)
        if self.dynamic:
            _ = (self.P * x_j[:, 0] - x_j[:, 1, self.sliceT])[:, na]
        else:
            _ = self.P * x_j - self.theta[self.sliceT, na]
        A_j = self.H * (self.Q + numpy.tanh(self.G * _))
        if self.dynamic:
            c_0 = (g_ij[:, 0] * A_j[:, 0]).sum(axis=0)
            c_1 = numpy.diag(A_j[:, 0, :, 0])[:, na]
            if self.globalT:
                c_1[:] = c_1.mean()
            return numpy.array([c_0, c_1])
        else:  # static threshold
            return (g_ij.transpose((2, 1, 0, 3)) * A_j).sum(axis=0)
Ejemplo n.º 3
0
class Linear(Linear):
    I_o = NArray(
        label=r":math:`I_o`",
        default=numpy.array([0.0]),
        domain=Range(lo=-100.0, hi=100.0, step=1.0),
        doc="External stimulus")

    G = NArray(
        label=r":math:`G`",
        default=numpy.array([0.0]),
        domain=Range(lo=-0.0, hi=100.0, step=1.0),
        doc="Global coupling scaling")

    tau = NArray(
        label=r":math:`\tau`",
        default=numpy.array([1.0]),
        domain=Range(lo=-0.1, hi=100.0, step=0.1),
        doc="Time constant")

    tau_rin = NArray(
        label=r":math:`\tau_rin_e`",
        default=numpy.array([10., ]),
        domain=Range(lo=1., hi=100., step=1.0),
        doc="""[ms]. Excitatory population instant spiking rate time constant.""")

    # Used for phase-plane axis ranges and to bound random initial() conditions.
    state_variable_boundaries = Final(
        default={"R": numpy.array([0.0, None]),
                 "Rin": numpy.array([0.0, None])},
        label="State Variable boundaries [lo, hi]",
        doc="""The values for each state-variable should be set to encompass
                the boundaries of the dynamic range of that state-variable. 
                Set None for one-sided boundaries""")

    state_variable_range = Final(
        label="State Variable ranges [lo, hi]",
        default={"R": numpy.array([0, 100]),
                 "Rin": numpy.array([0, 100])},
        doc="Range used for state variable initialization and visualization.")

    variables_of_interest = List(
        of=str,
        label="Variables watched by Monitors",
        choices=("R", 'Rin'),
        default=("R", 'Rin'), )

    state_variables = ('R', 'Rin')
    integration_variables = ('R',)
    _nvar = 2
    cvar = numpy.array([0], dtype=numpy.int32)

    def update_derived_parameters(self):
        """
        When needed, this should be a method for calculating parameters that are
        calculated based on paramaters directly set by the caller. For example,
        see, ReducedSetFitzHughNagumo. When not needed, this pass simplifies
        code that updates an arbitrary models parameters -- ie, this can be
        safely called on any model, whether it's used or not.
        """
        if hasattr(self, "Rin"):
            setattr(self, "_Rin", getattr(self, "Rin") > 0)
        else:
            setattr(self, "Rin", numpy.array([0.0, ]))
            setattr(self, "_Rin", numpy.array([False, ]))

    def update_non_state_variables_after_integration(self, state_variables):
        # Reset to 0 the Rin for nodes not updated by Spiking Network
        state_variables[1] = numpy.where(self._Rin, state_variables[1], 0.0)
        return state_variables

    def dfun(self, state, coupling, local_coupling=0.0):
        """
        .. math::
            dR/dt = (-R + G * coupling) / {\tau} + I_o
        """
        dR = numpy.where(self._Rin,
                         (- state[0] + state[1]) / self.tau_rin,
                         (-state[0] + self.G * coupling[0] + local_coupling * state[0] ) / self.tau + self.I_o)
        return numpy.array([dR, 0.0*dR])
Ejemplo n.º 4
0
class EpileptorRestingState(ModelNumbaDfun):
    r"""
        EpileptorRestingState is an extension of the phenomenological neural mass model of partial seizures 
        Epileptor [Jirsaetal_2014], tuned to express regionally specific physiological oscillations in addition
        to the epileptiform discharges. This extension was made using the Generic 2-dimensional Oscillator model
        (parametrized close to a supercritical Hopf Bifurcation) [SanzLeonetal_2013] to reproduce the spontaneous
        local field potential-like signal.
        
        This model, its motivation and derivation can be found in the published article [Courtioletal_2020].
        
        .. Tutorial: Modeling_Resting-State_in_Epilepsy.ipynb
        
        .. References:
            [Jirsaetal_2014] Jirsa, V. K.; Stacey, W. C.; Quilichini, P. P.; Ivanov, A. I.; Bernard, 
            C. *On the nature of seizure dynamics.* Brain, 2014.
            [SanzLeonetal_2013] Sanz Leon, P.; Knock, S. A.; Woodman, M. M.; Domide, L.; Mersmann, 
            J.; McIntosh, A. R.; Jirsa, V. K. *The Virtual Brain: a simulator of primate brain 
            network dynamics.* Front.Neuroinf., 2013.
            [Courtioletal_2020] Courtiol, J.; Guye, M.; Bartolomei, F.; Petkoski, S.; Jirsa, V. K.
            *Dynamical Mechanisms of Interictal Resting-State Functional Connectivity in Epilepsy.*
            J.Neurosci., 2020.
        
        Variables of interest to be used by monitors: p * (-x_{1} + x_{2}) + (1 - p) * x_{rs}
        
            .. math::
                \dot{x_{1}} &=& y_{1} - f_{1}(x_{1}, x_{2}) - z + I_{ext1} \\
                \dot{y_{1}} &=& c - d x_{1}^{2} - y_{1} \\
                \dot{z} &=&
                    \begin{cases}
                        r(4 (x_{1} - x_{0}) - z -0.1 z^{7}) & \text{if} x<0 \\
                        r(4 (x_{1} - x_{0}) - z) & \text{if} x \geq 0
                    \end{cases} \\
                \dot{x_{2}} &=& -y_{2} + x_{2} - x_{2}^{3} + I_{ext2} + b_{2} g(x_{1}) - 0.3 (z-3.5) \\
                \dot{y_{2}} &=& 1 / \tau (-y_{2} + f_{2}(x_{2}))\\
                \dot{g} &=& -0.01 (g - 0.1 x_{1})\\
                \dot{x_{rs}} &=& d_{rs} \tau_{rs} (-f_{rs} x_{rs}^3 + e_{rs} x_{rs}^2 + \alpha_{rs} y_{rs} +
                \gamma_{rs} I_{rs}) \\
                \dot{y_{rs}} &=& d_{rs} (b_{rs}  x_{rs} - \beta_{rs} y_{rs} + a_{rs}) / \tau_{rs}
        
        where:
            .. math::
                f_{1}(x_{1}, x_{2}) =
                    \begin{cases}
                        a x_{1}^{3} - b x_{1}^2 & \text{if } x_{1} <0\\
                        -(slope - x_{2} + 0.6(z-4)^2) x_{1} &\text{if }x_{1} \geq 0
                    \end{cases}
            
        and:
            .. math::
                f_{2}(x_{2}) =
                    \begin{cases}
                        0 & \text{if } x_{2} <-0.25\\
                        a_{2}(x_{2} + 0.25) & \text{if } x_{2} \geq -0.25
                    \end{cases}


    """

    a = NArray(
        label=":math:`a`",
        default=numpy.array([1.0]),
        doc="Coefficient of the cubic term in the first state-variable x1.")

    b = NArray(
        label=":math:`b`",
        default=numpy.array([3.0]),
        doc="Coefficient of the squared term in the first state-variable x1.")

    c = NArray(label=":math:`c`",
               default=numpy.array([1.0]),
               doc="Additive coefficient for the second state-variable y1, \
        called :math:'y_{0}' in Jirsa et al. (2014).")

    d = NArray(
        label=":math:`d`",
        default=numpy.array([5.0]),
        doc="Coefficient of the squared term in the second state-variable y1.")

    r = NArray(label=":math:`r`",
               domain=Range(lo=0.0, hi=0.001, step=0.00005),
               default=numpy.array([0.00035]),
               doc="Temporal scaling in the third state-variable z, \
        called :math:'1/\tau_{0}' in Jirsa et al. (2014).")

    s = NArray(label=":math:`s`",
               default=numpy.array([4.0]),
               doc="Linear coefficient in the third state-variable z.")

    x0 = NArray(label=":math:`x_0`",
                domain=Range(lo=-3.0, hi=-1.0, step=0.1),
                default=numpy.array([-1.6]),
                doc="Epileptogenicity parameter.")

    Iext = NArray(
        label=":math:`I_{ext}`",
        domain=Range(lo=1.5, hi=5.0, step=0.1),
        default=numpy.array([3.1]),
        doc="External input current to the first population (x1, y1).")

    slope = NArray(label=":math:`slope`",
                   domain=Range(lo=-16.0, hi=6.0, step=0.1),
                   default=numpy.array([0.]),
                   doc="Linear coefficient in the first state-variable x1.")

    Iext2 = NArray(
        label=":math:`I_{ext2}`",
        domain=Range(lo=0.0, hi=1.0, step=0.05),
        default=numpy.array([0.45]),
        doc="External input current to the second population (x2, y2).")

    tau = NArray(
        label=":math:`/tau`",
        default=numpy.array([10.0]),
        doc="Temporal scaling coefficient in the fifth state-variable y2.")

    aa = NArray(label=":math:`aa`",
                default=numpy.array([6.0]),
                doc="Linear coefficient in the fifth state-variable y2.")

    bb = NArray(
        label=":math:`bb`",
        default=numpy.array([2.0]),
        doc="Linear coefficient of lowpass excitatory coupling in the fourth \
        state-variable x2.")

    Kvf = NArray(label=":math:`K_{vf}`",
                 default=numpy.array([0.0]),
                 domain=Range(lo=0.0, hi=4.0, step=0.5),
                 doc="Coupling scaling on a very fast time scale.")

    Kf = NArray(label=":math:`K_f`",
                default=numpy.array([0.0]),
                domain=Range(lo=0.0, hi=4.0, step=0.5),
                doc="Coupling scaling on a fast time scale.")

    Ks = NArray(
        label=":math:`K_s`",
        default=numpy.array([0.0]),
        domain=Range(lo=-4.0, hi=4.0, step=0.1),
        doc="Permittivity coupling, that is from the very fast time scale \
        toward the slow time scale.")

    tt = NArray(label=":math:`tt`",
                default=numpy.array([1.0]),
                domain=Range(lo=0.001, hi=10.0, step=0.001),
                doc="Time scaling of the Epileptor.")

    # Generic-2D's parameters
    tau_rs = NArray(
        label=r":math:`\tau_{rs}`",
        default=numpy.array([1.0]),
        domain=Range(lo=1.0, hi=5.0, step=0.01),
        doc="Temporal scaling coefficient in the third population (x_rs, y_rs)."
    )

    I_rs = NArray(
        label=":math:`I_{rs}`",
        default=numpy.array([0.0]),
        domain=Range(lo=-5.0, hi=5.0, step=0.01),
        doc="External input current to the third population (x_rs, y_rs).")

    a_rs = NArray(label=":math:`a_{rs}`",
                  default=numpy.array([-2.0]),
                  domain=Range(lo=-5.0, hi=5.0, step=0.01),
                  doc="Vertical shift of the configurable nullcline \
        in the state-variable y_rs.")

    b_rs = NArray(label=":math:`b_{rs}`",
                  default=numpy.array([-10.0]),
                  domain=Range(lo=-20.0, hi=15.0, step=0.01),
                  doc="Linear coefficient of the state-variable y_rs.")

    d_rs = NArray(
        label=":math:`d_{rs}`",
        default=numpy.array([0.02]),
        domain=Range(lo=0.0001, hi=1.0, step=0.0001),
        doc="Temporal scaling of the whole third system (x_rs, y_rs).")

    e_rs = NArray(
        label=":math:`e_{rs}`",
        default=numpy.array([3.0]),
        domain=Range(lo=-5.0, hi=5.0, step=0.0001),
        doc="Coefficient of the squared term in the sixth state-variable x_rs."
    )

    f_rs = NArray(
        label=":math:`f_{rs}`",
        default=numpy.array([1.0]),
        domain=Range(lo=-5.0, hi=5.0, step=0.0001),
        doc="Coefficient of the cubic term in the sixth state-variable x_rs.")

    alpha_rs = NArray(
        label=r":math:`\alpha_{rs}`",
        default=numpy.array([1.0]),
        domain=Range(lo=-5.0, hi=5.0, step=0.0001),
        doc="Constant parameter to scale the rate of feedback from the \
        slow variable y_rs to the fast variable x_rs.")

    beta_rs = NArray(
        label=r":math:`\beta_{rs}`",
        default=numpy.array([1.0]),
        domain=Range(lo=-5.0, hi=5.0, step=0.0001),
        doc="Constant parameter to scale the rate of feedback from the \
        slow variable y_rs to itself.")

    gamma_rs = NArray(label=r":math:`\gamma_{rs}`",
                      default=numpy.array([1.0]),
                      domain=Range(lo=-1.0, hi=1.0, step=0.1),
                      doc="Constant parameter to reproduce FHN dynamics where \
        excitatory input currents are negative.\
        Note: It scales both I_rs and the long-range coupling term.")

    K_rs = NArray(label=r":math:`K_{rs}`",
                  default=numpy.array([1.0]),
                  domain=Range(lo=0.0, hi=10.0, step=0.001),
                  doc="Coupling scaling on a fast time scale.")

    # Combination 2 models
    p = NArray(label=r":math:`p`",
               default=numpy.array([0.]),
               domain=Range(lo=-1.0, hi=1.0, step=0.1),
               doc="Linear coefficient.")

    # Initialization.
    # Epileptor model is set in a fixed point by default.
    state_variable_range = Final(
        label="State variable ranges [lo, hi]",
        default={
            "x1": numpy.array([-1.8, -1.4]),
            "y1": numpy.array([-15, -10]),
            "z": numpy.array([3.6, 4.0]),
            "x2": numpy.array([-1.1, -0.9]),
            "y2": numpy.array([0.001, 0.01]),
            "g": numpy.array([-1., 1.]),
            "x_rs": numpy.array([-2.0, 4.0]),
            "y_rs": numpy.array([-6.0, 6.0])
        },
        doc="Typical bounds on state-variables in EpileptorRestingState model."
    )

    variables_of_interest = List(
        of=str,
        label="Variables watched by Monitors",
        choices=("x1", "y1", "z", "x2", "y2", "g", "x_rs", "y_rs", "x2 - x1"),
        default=("x2 - x1", "z", "x_rs"),
        doc="Quantities of EpileptorRestingState available to monitor.")

    state_variables = ("x1", "y1", "z", "x2", "y2", "g", "x_rs", "y_rs")

    _nvar = 8  # number of state-variables
    cvar = numpy.array([0, 3, 6], dtype=numpy.int32)  # coupling variables

    def _numpy_dfun(self,
                    state_variables,
                    coupling,
                    local_coupling=0.0,
                    array=numpy.array,
                    where=numpy.where,
                    concat=numpy.concatenate):

        y = state_variables
        ydot = numpy.empty_like(state_variables)

        # long-range coupling
        c_pop1 = coupling[0]
        c_pop2 = coupling[1]
        c_pop3 = coupling[2]

        # short-range (local) coupling
        Iext = self.Iext + local_coupling * y[0]
        lc_1 = local_coupling * y[6]

        # Epileptor's equations:
        # population 1
        if_ydot0 = -self.a * y[0]**2 + self.b * y[0]
        else_ydot0 = self.slope - y[3] + 0.6 * (y[2] - 4.0)**2
        ydot[0] = self.tt * (y[1] - y[2] + Iext + self.Kvf * c_pop1 +
                             where(y[0] < 0., if_ydot0, else_ydot0) * y[0])
        ydot[1] = self.tt * (self.c - self.d * y[0]**2 - y[1])

        # energy
        if_ydot2 = -0.1 * y[2]**7
        else_ydot2 = 0
        ydot[2] = self.tt * (self.r * (4 * (y[0] - self.x0) - y[2] + where(
            y[2] < 0., if_ydot2, else_ydot2) + self.Ks * c_pop1))

        # population 2
        ydot[3] = self.tt * (-y[4] + y[3] - y[3]**3 + self.Iext2 +
                             self.bb * y[5] - 0.3 *
                             (y[2] - 3.5) + self.Kf * c_pop2)
        if_ydot4 = 0
        else_ydot4 = self.aa * (y[3] + 0.25)
        ydot[4] = self.tt * (
            (-y[4] + where(y[3] < -0.25, if_ydot4, else_ydot4)) / self.tau)

        # filter
        ydot[5] = self.tt * (-0.01 * (y[5] - 0.1 * y[0]))  # 0.01 = \gamma

        # G2D's equations:
        ydot[6] = self.d_rs * self.tau_rs * (
            self.alpha_rs * y[7] - self.f_rs * y[6]**3 + self.e_rs * y[6]**2 +
            self.gamma_rs * self.I_rs + self.gamma_rs * self.K_rs * c_pop3 +
            lc_1)
        ydot[7] = self.d_rs * (self.a_rs + self.b_rs * y[6] -
                               self.beta_rs * y[7]) / self.tau_rs

        # output: LFP
        self.output = self.p * (-y[0] + y[3]) + (1 - self.p) * y[6]

        return ydot

    def dfun(self, x, c, local_coupling=0.0):
        r"""
            Computes the derivatives of the state-variables of EpileptorRestingState
            with respect to time.
        """

        x_ = x.reshape(x.shape[:-1]).T
        c_ = c.reshape(c.shape[:-1]).T
        Iext = self.Iext + local_coupling * x[0, :, 0]
        lc_1 = local_coupling * x[6, :, 0]
        deriv = _numba_dfun(x_, c_, self.x0, Iext, self.Iext2, self.a, self.b,
                            self.slope, self.tt, self.Kvf, self.c, self.d,
                            self.r, self.Ks, self.Kf, self.aa, self.bb,
                            self.tau, self.tau_rs, self.I_rs, self.a_rs,
                            self.b_rs, self.d_rs, self.e_rs, self.f_rs,
                            self.beta_rs, self.alpha_rs, self.gamma_rs,
                            self.K_rs, lc_1)
        return deriv.T[..., numpy.newaxis]
Ejemplo n.º 5
0
class Sigmoidal(Coupling):
    r"""
    Provides a sigmoidal coupling function of the form

    .. math::
        c_{min} + (c_{max} - c_{min}) / (1.0 + \exp(-a(x-midpoint)/\sigma))

    NB: using a = numpy.pi / numpy.sqrt(3.0) and the default parameter 
        produces something close to the current default for
        Linear (a=0.00390625, b=0) over the linear portion of the sigmoid,
        with saturation at -1 and 1.

    """

    cmin = NArray(
        label=":math:`c_{min}`",
        default=numpy.array([
            -1.0,
        ]),
        domain=Range(lo=-1000.0, hi=1000.0, step=10.0),
        doc="""Minimum of the sigmoid function""",
    )

    cmax = NArray(
        label=":math:`c_{max}`",
        default=numpy.array([
            1.0,
        ]),
        domain=Range(lo=-1000.0, hi=1000.0, step=10.0),
        doc="""Maximum of the sigmoid function""",
    )

    midpoint = NArray(
        label="midpoint",
        default=numpy.array([
            0.0,
        ]),
        domain=Range(lo=-1000.0, hi=1000.0, step=10.0),
        doc="Midpoint of the linear portion of the sigmoid",
    )

    a = NArray(
        label=r":math:`a`",
        default=numpy.array([
            1.0,
        ]),
        domain=Range(lo=0.01, hi=1000.0, step=10.0),
        doc="Scaling of sigmoidal",
    )

    sigma = NArray(
        label=r":math:`\sigma`",
        default=numpy.array([
            230.0,
        ]),
        domain=Range(lo=0.01, hi=1000.0, step=10.0),
        doc="Standard deviation of the sigmoidal",
    )

    parameter_names = 'cmin cmax midpoint a sigma'.split()
    pre_expr = 'x_j'
    post_expr = 'cmin + ((cmax - cmin) / (1.0 + exp(-a *((gx - midpoint) / sigma))))'

    def __str__(self):
        return simple_gen_astr(self, 'cmin cmax midpoint a sigma')

    def post(self, gx):
        return self.cmin + ((self.cmax - self.cmin) /
                            (1.0 + numpy.exp(-self.a * (
                                (gx - self.midpoint) / self.sigma))))
Ejemplo n.º 6
0
class LarterBreakspear(models.Model):
    """
    A modified Morris-Lecar model that includes a third equation which simulates
    the effect of a population of inhibitory interneurons synapsing on
    the pyramidal cells.
    
    .. [Larteretal_1999] Larter et.al. *A coupled ordinary differential equation
        lattice model for the simulation of epileptic seizures.* Chaos. 9(3):
        795, 1999.
    

    .. [Breaksetal_2003_a] Breakspear, M.; Terry, J. R. & Friston, K. J.  *Modulation of excitatory
        synaptic coupling facilitates synchronization and complex dynamics in an
        onlinear model of neuronal dynamics*. Neurocomputing 52–54 (2003).151–158

    .. [Breaksetal_2003_b] M. J. Breakspear et.al. *Modulation of excitatory 
        synaptic coupling facilitates synchronization and complex dynamics in a
        biophysical model of neuronal dynamics.* Network: Computation in Neural
        Systems 14: 703-732, 2003.
    
    Equations and default parameters are taken from [Breaksetal_2003_b]_. 
    All equations and parameters are non-dimensional and normalized.
    For values of d_v  < 0.55, the dynamics of a single column settles onto a 
    solitary fixed point attractor.


    Parameters used for simulations in [Breaksetal_2003_a]_ Table 1. Page 153.
    Two nodes were coupled.

    +---------------------------+
    |          Table 1          | 
    +--------------+------------+
    |Parameter     |  Value     |
    +--------------+------------+
    | I            |      0.3   |
    | a_ee         |      0.4   |
    | a_ei         |      0.1   |
    | a_ie         |      1.0   |
    | a_ne         |      1.0   |
    | a_ni         |      0.4   |
    | r_NMDA       |      0.2   |
    | delta        |      0.001 |
    +---------------------------+



    +---------------------------+
    |          Table 2          | 
    +--------------+------------+
    |Parameter     |  Value     |
    +--------------+------------+
    | gK           |      2.0   |
    | gL           |      0.5   |
    | gNa          |      6.7   |
    | gCa          |      1.0   |
    | a_ne         |      1.0   |
    | a_ni         |      0.4   |
    | a_ee         |      0.36  |
    | a_ei         |      2.0   |
    | a_ie         |      2.0   |
    | VK           |     -0.7   |
    | VL           |     -0.5   |
    | VNa          |      0.53  |
    | VCa          |      1.0   |
    | phi          |      0.7   | 
    | b            |      0.1   |
    | I            |      0.3   |
    | r_NMDA       |      0.25  |
    | C            |      0.1   |
    | TCa          |     -0.01  |
    | d_Ca         |      0.15  |
    | TK           |      0.0   |
    | d_K          |      0.3   |
    | VT           |      0.0   |
    | ZT           |      0.0   |
    | TNa          |      0.3   |
    | d_Na         |      0.15  |
    | d_V          |      0.65  |
    | d_Z          |      d_V   |  # note, this parameter might be spatialized: ones(N,1).*0.65 + modn*(rand(N,1)-0.5);
    | QV_max       |      1.0   |
    | QZ_max       |      1.0   |
    +---------------------------+
    |   Alstott et al. 2009     |
    +---------------------------+


    NOTES about parameters

    d_V
    For d_V < 0.55, uncoupled network, the system exhibits fixed point dynamics; 
    for 55 < lb.d_V < 0.59, limit cycle atractors; 
    and for d_V > 0.59 chaotic attractors (eg, d_V=0.6,aee=0.5,aie=0.5, 
                                               gNa=0, Iext=0.165)

    C
    The long-range coupling 'C' is ‘weak’ in the sense that 
    they investigated parameter values for which C < a_ee and C << a_ie.


    
    .. figure :: img/LarterBreakspear_01_mode_0_pplane.svg
            :alt: Larter-Breaskpear phase plane (V, W)
            
            The (:math:`V`, :math:`W`) phase-plane for the Larter-Breakspear model.
    
    """

    # Define traited attributes for this model, these represent possible kwargs.
    gCa = NArray(label=":math:`g_{Ca}`",
                 default=numpy.array([1.1]),
                 domain=Range(lo=0.9, hi=1.5, step=0.1),
                 doc="""Conductance of population of Ca++ channels.""")

    gK = NArray(label=":math:`g_{K}`",
                default=numpy.array([2.0]),
                domain=Range(lo=1.95, hi=2.05, step=0.025),
                doc="""Conductance of population of K channels.""")

    gL = NArray(label=":math:`g_{L}`",
                default=numpy.array([0.5]),
                domain=Range(lo=0.45, hi=0.55, step=0.05),
                doc="""Conductance of population of leak channels.""")

    phi = NArray(label=":math:`\\phi`",
                 default=numpy.array([0.7]),
                 domain=Range(lo=0.3, hi=0.9, step=0.1),
                 doc="""Temperature scaling factor.""")

    gNa = NArray(label=":math:`g_{Na}`",
                 default=numpy.array([6.7]),
                 domain=Range(lo=0.0, hi=10.0, step=0.1),
                 doc="""Conductance of population of Na channels.""")

    TK = NArray(label=":math:`T_{K}`",
                default=numpy.array([0.0]),
                domain=Range(lo=0.0, hi=0.0001, step=0.00001),
                doc="""Threshold value for K channels.""")

    TCa = NArray(label=":math:`T_{Ca}`",
                 default=numpy.array([-0.01]),
                 domain=Range(lo=-0.02, hi=-0.01, step=0.0025),
                 doc="Threshold value for Ca channels.")

    TNa = NArray(label=":math:`T_{Na}`",
                 default=numpy.array([0.3]),
                 domain=Range(lo=0.25, hi=0.3, step=0.025),
                 doc="Threshold value for Na channels.")

    VCa = NArray(label=":math:`V_{Ca}`",
                 default=numpy.array([1.0]),
                 domain=Range(lo=0.9, hi=1.1, step=0.05),
                 doc="""Ca Nernst potential.""")

    VK = NArray(label=":math:`V_{K}`",
                default=numpy.array([-0.7]),
                domain=Range(lo=-0.8, hi=1., step=0.1),
                doc="""K Nernst potential.""")

    VL = NArray(label=":math:`V_{L}`",
                default=numpy.array([-0.5]),
                domain=Range(lo=-0.7, hi=-0.4, step=0.1),
                doc="""Nernst potential leak channels.""")

    VNa = NArray(label=":math:`V_{Na}`",
                 default=numpy.array([0.53]),
                 domain=Range(lo=0.51, hi=0.55, step=0.01),
                 doc="""Na Nernst potential.""")

    d_K = NArray(label=":math:`\\delta_{K}`",
                 default=numpy.array([0.3]),
                 domain=Range(lo=0.1, hi=0.4, step=0.1),
                 doc="""Variance of K channel threshold.""")

    tau_K = NArray(label=":math:`\\tau_{K}`",
                   default=numpy.array([1.0]),
                   domain=Range(lo=0.01, hi=0.0, step=0.1),
                   doc="""Time constant for K relaxation time (ms)""")

    d_Na = NArray(label=":math:`\\delta_{Na}`",
                  default=numpy.array([0.15]),
                  domain=Range(lo=0.1, hi=0.2, step=0.05),
                  doc="Variance of Na channel threshold.")

    d_Ca = NArray(label=":math:`\\delta_{Ca}`",
                  default=numpy.array([0.15]),
                  domain=Range(lo=0.1, hi=0.2, step=0.05),
                  doc="Variance of Ca channel threshold.")

    aei = NArray(label=":math:`a_{ei}`",
                 default=numpy.array([2.0]),
                 domain=Range(lo=0.1, hi=2.0, step=0.1),
                 doc="""Excitatory-to-inhibitory synaptic strength.""")

    aie = NArray(label=":math:`a_{ie}`",
                 default=numpy.array([2.0]),
                 domain=Range(lo=0.5, hi=2.0, step=0.1),
                 doc="""Inhibitory-to-excitatory synaptic strength.""")

    b = NArray(
        label=":math:`b`",
        default=numpy.array([0.1]),
        domain=Range(lo=0.0001, hi=1.0, step=0.0001),
        doc="""Time constant scaling factor. The original value is 0.1""")

    C = NArray(
        label=":math:`c`",
        default=numpy.array([0.0]),
        domain=Range(lo=0.0, hi=0.2, step=0.05),
        doc="""Strength of excitatory coupling. Balance between internal and
        local (and global) coupling strength. C > 0 introduces interdependences between 
        consecutive columns/nodes. C=1 corresponds to maximum coupling.
        This strenght should be set to sensible values when a whole network is connected. """
    )

    ane = NArray(label=":math:`a_{ne}`",
                 default=numpy.array([1.0]),
                 domain=Range(lo=0.4, hi=1.0, step=0.05),
                 doc="""Non-specific-to-excitatory synaptic strength.""")

    ani = NArray(label=":math:`a_{ni}`",
                 default=numpy.array([0.4]),
                 domain=Range(lo=0.3, hi=0.5, step=0.05),
                 doc="""Non-specific-to-inhibitory synaptic strength.""")

    aee = NArray(label=":math:`a_{ee}`",
                 default=numpy.array([0.4]),
                 domain=Range(lo=0.4, hi=0.6, step=0.05),
                 doc="""Excitatory-to-excitatory synaptic strength.""")

    Iext = NArray(
        label=":math:`I_{ext}`",
        default=numpy.array([0.3]),
        domain=Range(lo=0.165, hi=0.3, step=0.005),
        doc="""Subcortical input strength. It represents a non-specific
       excitation or thalamic inputs.""")

    rNMDA = NArray(label=":math:`r_{NMDA}`",
                   default=numpy.array([0.25]),
                   domain=Range(lo=0.2, hi=0.3, step=0.05),
                   doc="""Ratio of NMDA to AMPA receptors.""")

    VT = NArray(label=":math:`V_{T}`",
                default=numpy.array([0.0]),
                domain=Range(lo=0.0, hi=0.7, step=0.01),
                doc="""Threshold potential (mean) for excitatory neurons. 
        In [Breaksetal_2003_b]_ this values is 0.""")

    d_V = NArray(
        label=":math:`\\delta_{V}`",
        default=numpy.array([0.65]),
        domain=Range(lo=0.49, hi=0.7, step=0.01),
        doc="""Variance of the excitatory threshold. It is one of the main
        parameters explored in [Breaksetal_2003_b]_.""")

    ZT = NArray(label=":math:`Z_{T}`",
                default=numpy.array([0.0]),
                domain=Range(lo=0.0, hi=0.1, step=0.005),
                doc="""Threshold potential (mean) for inihibtory neurons.""")

    d_Z = NArray(label=":math:`\\delta_{Z}`",
                 default=numpy.array([0.7]),
                 domain=Range(lo=0.001, hi=0.75, step=0.05),
                 doc="""Variance of the inhibitory threshold.""")

    # NOTE: the values were not in the article.
    # I took these ones from DESTEXHE 2001
    QV_max = NArray(
        label=":math:`Q_{max}`",
        default=numpy.array([1.0]),
        domain=Range(lo=0.1, hi=1., step=0.001),
        doc="""Maximal firing rate for excitatory populations (kHz)""")

    QZ_max = NArray(
        label=":math:`Q_{max}`",
        default=numpy.array([1.0]),
        domain=Range(lo=0.1, hi=1., step=0.001),
        doc="""Maximal firing rate for excitatory populations (kHz)""")

    variables_of_interest = List(
        of=str,
        label="Variables watched by Monitors",
        choices=("V", "W", "Z"),
        default=("V", "W", "Z"),
        doc="""This represents the default state-variables of this Model to be
        monitored. It can be overridden for each Monitor if desired.""")

    # Informational attribute, used for phase-plane and initial()
    state_variable_range = Final(
        {
            "V": numpy.array([-1.5, 1.5]),
            "W": numpy.array([-1.0, 1.0]),
            "Z": numpy.array([-1.5, 1.5])
        },
        label="State Variable ranges [lo, hi]",
        doc="""The values for each state-variable should be set to encompass
            the expected dynamic range of that state-variable for the current 
            parameters, it is used as a mechanism for bounding random inital 
            conditions when the simulation isn't started from an explicit
            history, it is also provides the default range of phase-plane plots."""
    )

    state_variables = ["V", "W", "Z"]
    _nvar = 3

    def __init__(self, **kwargs):
        """
        .. May need to put kwargs back if we can't get them from trait...
        
        """
        super(LarterBreakspear, self).__init__(**kwargs)

        LOG.info('%s: initing...' % str(self))

        self.cvar = numpy.array([0], dtype=numpy.int32)

        LOG.debug('%s: inited.' % repr(self))

    def dfun(self, state_variables, coupling, local_coupling=0.0):
        """
        .. math::
             \\dot{V} &= - (g_{Ca} + (1 - C) \\, r_{NMDA} \\, a_{ee} Q_V^i +
            C \\, r_{NMDA} \\, a_{ee} \\langle Q_V \\rangle) \\, m_{Ca} \\,(V - V_{Ca})
            - g_K\\, W\\, (V - V_K) - g_L\\, (V - V_L)
            - (g_{Na} m_{Na} + (1 - C) \\, a_{ee} Q_V^i + 
            C \\, a_{ee} \\langle Q_V \\rangle) \\, (V - V_{Na})
            - a_{ie}\\, Z \\, Q_Z^i + a_{ne} \\, I_{\\delta}
            
            \\dot{W} &= \\frac{\\phi \\, (m_K - W)}{\\tau_K} \\\\
            \\dot{Z} &= b \\, (a_{ni} \\, I_{\\delta} + a_{ei} \\, V \\, Q_V)\\\\
            
            m_{ion}(X) &= 0.5 \\, (1 + tanh(\\frac{V-T_{ion}}{\\delta_{ion}})
            
        See Equations (7), (3), (6) and (2) respectively in [Breaksetal_2003]_.
        Pag: 705-706

        NOTE: Equation (8) has an error the sign before the term :math:`a_{ie}\\, Z \\, Q_Z^i`
        should be a minus (-) and not a plus (+).
        
        """
        V = state_variables[0, :]
        W = state_variables[1, :]
        Z = state_variables[2, :]

        c_0 = coupling[0, :]
        lc_0 = local_coupling

        # relationship between membrane voltage and channel conductance
        m_Ca = 0.5 * (1 + numpy.tanh((V - self.TCa) / self.d_Ca))
        m_Na = 0.5 * (1 + numpy.tanh((V - self.TNa) / self.d_Na))
        m_K = 0.5 * (1 + numpy.tanh((V - self.TK) / self.d_K))

        # voltage to firing rate
        QV = 0.5 * self.QV_max * (1 + numpy.tanh((V - self.VT) / self.d_V))
        QZ = 0.5 * self.QZ_max * (1 + numpy.tanh((Z - self.ZT) / self.d_Z))

        dV = (-(self.gCa + (1.0 - self.C) * self.rNMDA * self.aee * QV +
                self.C * self.rNMDA * self.aee * c_0) * m_Ca * (V - self.VCa) -
              self.gK * W * (V - self.VK) - self.gL * (V - self.VL) -
              (self.gNa * m_Na +
               (1.0 - self.C) * self.aee * QV + self.C * self.aee * c_0) *
              (V - self.VNa) - self.aei * Z * QZ + self.ane * self.Iext)

        dW = (self.phi * (m_K - W) / self.tau_K)

        dZ = (self.b * (self.ani * self.Iext + self.aei * V * QV))

        derivative = numpy.array([dV, dW, dZ])

        return derivative
Ejemplo n.º 7
0
class ReducedSetHindmarshRose(ReducedSetBase):
    r"""
    .. [SJ_2008] Stefanescu and Jirsa, PLoS Computational Biology, *A Low
        Dimensional Description of Globally Coupled Heterogeneous Neural
        Networks of Excitatory and Inhibitory*  4, 11, 26--36, 2008.

    The models (:math:`\xi`, :math:`\eta`) phase-plane, including a
    representation of the vector field as well as its nullclines, using default
    parameters, can be seen below:

        .. _phase-plane-rHR_0:
        .. figure :: img/ReducedSetHindmarshRose_01_mode_0_pplane.svg
            :alt: Reduced set of FitzHughNagumo phase plane (xi, eta), 1st mode.

            The (:math:`\xi`, :math:`\eta`) phase-plane for the first mode of
            a reduced set of Hindmarsh-Rose oscillators.

        .. _phase-plane-rHR_1:
        .. figure :: img/ReducedSetHindmarshRose_01_mode_1_pplane.svg
            :alt: Reduced set of FitzHughNagumo phase plane (xi, eta), 2nd mode.

            The (:math:`\xi`, :math:`\eta`) phase-plane for the second mode of
            a reduced set of Hindmarsh-Rose oscillators.

        .. _phase-plane-rHR_2:
        .. figure :: img/ReducedSetHindmarshRose_01_mode_2_pplane.svg
            :alt: Reduced set of FitzHughNagumo phase plane (xi, eta), 3rd mode.

            The (:math:`\xi`, :math:`\eta`) phase-plane for the third mode of
            a reduced set of Hindmarsh-Rose oscillators.


    The dynamic equations were orginally taken from [SJ_2008]_.

    The equations of the population model for i-th mode at node q are:

    .. math::
                \dot{\xi}_i     &=  \eta_i-a_i\xi_i^3 + b_i\xi_i^2- \tau_i
                                 + K_{11} \left[\sum_{k=1}^{o} A_{ik} \xi_k - \xi_i \right]
                                 - K_{12} \left[\sum_{k=1}^{o} B_{ik} \alpha_k - \xi_i\right] + IE_i \\
                                &\, + \left[\sum_{k=1}^{o} \mathbf{\Gamma}(\xi_{kq}, \xi_{kr}, u_{qr})\right]
                                 + \left[\sum_{k=1}^{o} W_{\zeta}\cdot\xi_{kr} \right] \\
                & \\
                \dot{\eta}_i    &=  c_i-d_i\xi_i^2 -\tau_i \\
                & \\
                \dot{\tau}_i    &=  rs\xi_i - r\tau_i -m_i \\
                & \\
                \dot{\alpha}_i  &=  \beta_i - e_i \alpha_i^3 + f_i \alpha_i^2 - \gamma_i
                                 + K_{21} \left[\sum_{k=1}^{o} C_{ik} \xi_k - \alpha_i \right] + II_i \\
                                &\, +\left[\sum_{k=1}^{o}\mathbf{\Gamma}(\xi_{kq}, \xi_{kr}, u_{qr})\right]
                                 + \left[\sum_{k=1}^{o}W_{\zeta}\cdot\xi_{kr}\right] \\
                & \\
                \dot{\beta}_i   &= h_i - p_i \alpha_i^2 - \beta_i \\
                \dot{\gamma}_i  &= rs \alpha_i - r \gamma_i - n_i

    .. automethod:: ReducedSetHindmarshRose.update_derived_parameters

    #NOTE: In the Article this modelis called StefanescuJirsa3D

    """

    # Define traited attributes for this model, these represent possible kwargs.
    r = NArray(
        label=":math:`r`",
        default=numpy.array([0.006]),
        domain=Range(lo=0.0, hi=0.1, step=0.0005),
        doc="""Adaptation parameter""")

    a = NArray(
        label=":math:`a`",
        default=numpy.array([1.0]),
        domain=Range(lo=0.0, hi=1.0, step=0.01),
        doc="""Dimensionless parameter as in the Hindmarsh-Rose model""")

    b = NArray(
        label=":math:`b`",
        default=numpy.array([3.0]),
        domain=Range(lo=0.0, hi=3.0, step=0.01),
        doc="""Dimensionless parameter as in the Hindmarsh-Rose model""")

    c = NArray(
        label=":math:`c`",
        default=numpy.array([1.0]),
        domain=Range(lo=0.0, hi=1.0, step=0.01),
        doc="""Dimensionless parameter as in the Hindmarsh-Rose model""")

    d = NArray(
        label=":math:`d`",
        default=numpy.array([5.0]),
        domain=Range(lo=2.5, hi=7.5, step=0.01),
        doc="""Dimensionless parameter as in the Hindmarsh-Rose model""")

    s = NArray(
        label=":math:`s`",
        default=numpy.array([4.0]),
        domain=Range(lo=2.0, hi=6.0, step=0.01),
        doc="""Adaptation paramters, governs feedback""")

    xo = NArray(
        label=":math:`x_{o}`",
        default=numpy.array([-1.6]),
        domain=Range(lo=-2.4, hi=-0.8, step=0.01),
        doc="""Leftmost equilibrium point of x""")

    K11 = NArray(
        label=":math:`K_{11}`",
        default=numpy.array([0.5]),
        domain=Range(lo=0.0, hi=1.0, step=0.01),
        doc="""Internal coupling, excitatory to excitatory""")

    K12 = NArray(
        label=":math:`K_{12}`",
        default=numpy.array([0.1]),
        domain=Range(lo=0.0, hi=1.0, step=0.01),
        doc="""Internal coupling, inhibitory to excitatory""")

    K21 = NArray(
        label=":math:`K_{21}`",
        default=numpy.array([0.15]),
        domain=Range(lo=0.0, hi=1.0, step=0.01),
        doc="""Internal coupling, excitatory to inhibitory""")

    sigma = NArray(
        label=r":math:`\sigma`",
        default=numpy.array([0.3]),
        domain=Range(lo=0.0, hi=1.0, step=0.01),
        doc="""Standard deviation of Gaussian distribution""")

    mu = NArray(
        label=r":math:`\mu`",
        default=numpy.array([3.3]),
        domain=Range(lo=1.1, hi=3.3, step=0.01),
        doc="""Mean of Gaussian distribution""")

    # Used for phase-plane axis ranges and to bound random initial() conditions.
    state_variable_range = Final(
        label="State Variable ranges [lo, hi]",
        default={"xi": numpy.array([-4.0, 4.0]),
                 "eta": numpy.array([-25.0, 20.0]),
                 "tau": numpy.array([2.0, 10.0]),
                 "alpha": numpy.array([-4.0, 4.0]),
                 "beta": numpy.array([-20.0, 20.0]),
                 "gamma": numpy.array([2.0, 10.0])},
        doc="""The values for each state-variable should be set to encompass
        the expected dynamic range of that state-variable for the current
        parameters, it is used as a mechanism for bounding random inital
        conditions when the simulation isn't started from an explicit history,
        it is also provides the default range of phase-plane plots.""")

    variables_of_interest = List(
        of=str,
        label="Variables watched by Monitors",
        choices=("xi", "eta", "tau", "alpha", "beta", "gamma"),
        default=("xi", "eta", "tau"),
        doc=r"""This represents the default state-variables of this Model to be
                monitored. It can be overridden for each Monitor if desired. The
                corresponding state-variable indices for this model are :math:`\xi = 0`,
                :math:`\eta = 1`, :math:`\tau = 2`, :math:`\alpha = 3`,
                :math:`\beta = 4`, and :math:`\gamma = 5`""")

    state_variables = 'xi eta tau alpha beta gamma'.split()
    _nvar = 6
    cvar = numpy.array([0, 3], dtype=numpy.int32)
    # derived parameters
    A_ik = None
    B_ik = None
    C_ik = None
    a_i = None
    b_i = None
    c_i = None
    d_i = None
    e_i = None
    f_i = None
    h_i = None
    p_i = None
    IE_i = None
    II_i = None
    m_i = None
    n_i = None

    def dfun(self, state_variables, coupling, local_coupling=0.0):
        r"""
        The equations of the population model for i-th mode at node q are:

        .. math::
                \dot{\xi}_i     &=  \eta_i-a_i\xi_i^3 + b_i\xi_i^2- \tau_i
                                 + K_{11} \left[\sum_{k=1}^{o} A_{ik} \xi_k - \xi_i \right]
                                 - K_{12} \left[\sum_{k=1}^{o} B_{ik} \alpha_k - \xi_i\right] + IE_i \\
                                &\, + \left[\sum_{k=1}^{o} \mathbf{\Gamma}(\xi_{kq}, \xi_{kr}, u_{qr})\right]
                                 + \left[\sum_{k=1}^{o} W_{\zeta}\cdot\xi_{kr} \right] \\
                & \\
                \dot{\eta}_i    &=  c_i-d_i\xi_i^2 -\tau_i \\
                & \\
                \dot{\tau}_i    &=  rs\xi_i - r\tau_i -m_i \\
                & \\
                \dot{\alpha}_i  &=  \beta_i - e_i \alpha_i^3 + f_i \alpha_i^2 - \gamma_i
                                 + K_{21} \left[\sum_{k=1}^{o} C_{ik} \xi_k - \alpha_i \right] + II_i \\
                                &\, +\left[\sum_{k=1}^{o}\mathbf{\Gamma}(\xi_{kq}, \xi_{kr}, u_{qr})\right]
                                 + \left[\sum_{k=1}^{o}W_{\zeta}\cdot\xi_{kr}\right] \\
                & \\
                \dot{\beta}_i   &= h_i - p_i \alpha_i^2 - \beta_i \\
                \dot{\gamma}_i  &= rs \alpha_i - r \gamma_i - n_i

        """

        xi = state_variables[0, :]
        eta = state_variables[1, :]
        tau = state_variables[2, :]
        alpha = state_variables[3, :]
        beta = state_variables[4, :]
        gamma = state_variables[5, :]
        derivative = numpy.empty_like(state_variables)

        c_0 = coupling[0, :].sum(axis=1)[:, numpy.newaxis]
        # c_1 = coupling[1, :]

        derivative[0] = (eta - self.a_i * xi ** 3 + self.b_i * xi ** 2 - tau +
               self.K11 * (numpy.dot(xi, self.A_ik) - xi) -
               self.K12 * (numpy.dot(alpha, self.B_ik) - xi) +
               self.IE_i + c_0 + local_coupling * xi)

        derivative[1] = self.c_i - self.d_i * xi ** 2 - eta

        derivative[2] = self.r * self.s * xi - self.r * tau - self.m_i

        derivative[3] = (beta - self.e_i * alpha ** 3 + self.f_i * alpha ** 2 - gamma +
                  self.K21 * (numpy.dot(xi, self.C_ik) - alpha) +
                  self.II_i + c_0 + local_coupling * xi)

        derivative[4] = self.h_i - self.p_i * alpha ** 2 - beta

        derivative[5] = self.r * self.s * alpha - self.r * gamma - self.n_i

        return derivative

    def update_derived_parameters(self, corrected_d_p=True):
        """
        Calculate coefficients for the neural field model based on a Reduced set
        of Hindmarsh-Rose oscillators. Specifically, this method implements
        equations for calculating coefficients found in the supplemental
        material of [SJ_2008]_.

        Include equations here...

        """

        newaxis = numpy.newaxis
        trapz = scipy_integrate_trapz

        stepu = 1.0 / (self.nu + 2 - 1)
        stepv = 1.0 / (self.nv + 2 - 1)

        norm = scipy_stats_norm(loc=self.mu, scale=self.sigma)

        Iu = norm.ppf(numpy.arange(stepu, 1.0, stepu))
        Iv = norm.ppf(numpy.arange(stepv, 1.0, stepv))

        # Define the modes
        V = numpy.zeros((self.number_of_modes, self.nv))
        U = numpy.zeros((self.number_of_modes, self.nu))

        nv_per_mode = self.nv // self.number_of_modes
        nu_per_mode = self.nu // self.number_of_modes

        for i in range(self.number_of_modes):
            V[i, i * nv_per_mode:(i + 1) * nv_per_mode] = numpy.ones(nv_per_mode)
            U[i, i * nu_per_mode:(i + 1) * nu_per_mode] = numpy.ones(nu_per_mode)

        # Normalise the modes
        V = V / numpy.tile(numpy.sqrt(trapz(V * V, Iv, axis=1)), (self.nv, 1)).T
        U = U / numpy.tile(numpy.sqrt(trapz(U * U, Iu, axis=1)), (self.nu, 1)).T

        # Get Normal PDF's evaluated with sampling Zv and Zu
        g1 = norm.pdf(Iv)
        g2 = norm.pdf(Iu)
        G1 = numpy.tile(g1, (self.number_of_modes, 1))
        G2 = numpy.tile(g2, (self.number_of_modes, 1))

        cV = numpy.conj(V)
        cU = numpy.conj(U)

        #import pdb; pdb.set_trace()
        intcVdI = trapz(cV, Iv, axis=1)[:, newaxis]
        intG1VdI = trapz(G1 * V, Iv, axis=1)[newaxis, :]
        intcUdI = trapz(cU, Iu, axis=1)[:, newaxis]

        #Calculate coefficients
        self.A_ik = numpy.dot(intcVdI, intG1VdI).T
        self.B_ik = numpy.dot(intcVdI, trapz(G2 * U, Iu, axis=1)[newaxis, :])
        self.C_ik = numpy.dot(intcUdI, intG1VdI).T

        self.a_i = self.a * trapz(cV * V ** 3, Iv, axis=1)[newaxis, :]
        self.e_i = self.a * trapz(cU * U ** 3, Iu, axis=1)[newaxis, :]
        self.b_i = self.b * trapz(cV * V ** 2, Iv, axis=1)[newaxis, :]
        self.f_i = self.b * trapz(cU * U ** 2, Iu, axis=1)[newaxis, :]
        self.c_i = (self.c * intcVdI).T
        self.h_i = (self.c * intcUdI).T

        self.IE_i = trapz(Iv * cV, Iv, axis=1)[newaxis, :]
        self.II_i = trapz(Iu * cU, Iu, axis=1)[newaxis, :]

        if corrected_d_p:
            # correction identified by Shrey Dutta & Arpan Bannerjee, confirmed by RS
            self.d_i = self.d * trapz(cV * V ** 2, Iv, axis=1)[newaxis, :]
            self.p_i = self.d * trapz(cU * U ** 2, Iu, axis=1)[newaxis, :]
        else:
            # typo in the original paper by RS & VJ, kept for comparison purposes.
            self.d_i = (self.d * intcVdI).T
            self.p_i = (self.d * intcUdI).T

        self.m_i = (self.r * self.s * self.xo * intcVdI).T
        self.n_i = (self.r * self.s * self.xo * intcUdI).T
Ejemplo n.º 8
0
class HMJEpileptor(models.Model):
    """
    The Epileptor is a composite neural mass model of six dimensions which 
    has be crafted to model the phenomenology of epileptic seizures.

    This model, its motivation and derivation are currently in preparation
    for publication (Jirsa et al, 2013)

    .. automethod:: HMJEpileptor.dfun
    """

    a = NArray(label="a", default=numpy.array([1]), doc="n/a")

    b = NArray(label="b", default=numpy.array([3]), doc="n/a")

    c = NArray(label="c", default=numpy.array([1]), doc="n/a")

    d = NArray(label="d", default=numpy.array([5]), doc="n/a")

    r = NArray(label="r",
               domain=Range(lo=0.0, hi=0.001, step=0.00005),
               default=numpy.array([0.00035]),
               doc="n/a")

    s = NArray(label="s", default=numpy.array([4]), doc="n/a")

    x0 = NArray(label="x0",
                domain=Range(lo=-3.0, hi=0.0, step=0.1),
                default=numpy.array([-1.6]),
                doc="n/a")

    Iext = NArray(label="Iext",
                  domain=Range(lo=1.5, hi=5.0, step=0.1),
                  default=numpy.array([3.1]),
                  doc="n/a")

    omega2 = NArray(label="omega2", default=numpy.array([0.1]), doc="n/a")

    slope = NArray(label="slope", default=numpy.array([0.]), doc="n/a")

    Iext2 = NArray(label="Iext2",
                   domain=Range(lo=0.0, hi=1.0, step=0.05),
                   default=numpy.array([0.45]),
                   doc="n/a")

    tau = NArray(label="tau", default=numpy.array([10]), doc="n/a")

    aa = NArray(label="aa", default=numpy.array([6]), doc="n/a")

    Kpop1 = NArray(
        label="K_11",
        default=numpy.array([0.5]),
        domain=Range(lo=0.0, hi=4.0, step=0.5),
        doc=
        '''Test parameter. Correspond to the coupling scaling. Move outside to be
        consistent with the general TVB implementation.''')

    Kpop2 = NArray(
        label="K_22",
        default=numpy.array([0.2]),
        domain=Range(lo=0.0, hi=1.0, step=0.5),
        doc=
        '''Test parameter. Correspond to the coupling scaling. Move outside to be
        consistent with the general TVB implementation.''')

    state_variable_range = Final(
        {
            "y0": numpy.array([0., 1e-10]),
            "y1": numpy.array([-5., 0.]),
            "y2": numpy.array([3., 4.]),
            "y3": numpy.array([0., 1e-10]),
            "y4": numpy.array([0., 1e-10]),
            "y5": numpy.array([0., 1e-2])
        },
        label="State variable ranges [lo, hi]",
        doc="n/a")

    variables_of_interest = List(
        of=str,
        label="Variables watched by Monitors",
        choices=("y0", "y1", "y2", "y3", "y4", "y5"),
        default=("y0", "y3"),
        doc="""default state variables to be monitored""")

    #    variables_of_interest = arrays.IntegerArray(
    #        label="Variables watched by Monitors",
    #        range=basic.Range(lo=0.0, hi=6.0, step=1.0),
    #        default=numpy.array([0], dtype=numpy.int32),
    #        doc="default state variables to be monitored",
    #        order=10)

    def __init__(self, **kwargs):
        """
        """

        LOG.info("%s: init'ing..." % (str(self), ))

        super(HMJEpileptor, self).__init__(**kwargs)

        #self._state_variables = ["y%d" % i for i in range(6)]
        #self._state_variables = ["y%d" % i for i in range(6)]
        self._nvar = 6
        self.cvar = numpy.array([0, 3], dtype=numpy.int32)

        LOG.debug("%s: init'ed." % (repr(self), ))

    def dfun(self,
             state_variables,
             coupling,
             local_coupling=0.0,
             array=numpy.array,
             where=numpy.where,
             concat=numpy.concatenate):
        """
        Computes the derivatives of the state variables of the Epileptor 
        with respect to time. 

        Implementation note: we expect this version of the Epileptor to be used
        in a vectorized manner. Concretely, y has a shape of (6, n) where n is 
        the number of nodes in the network. An consequence is that
        the original use of if/else is translated by calculated both the true and
        false forms and mixing them using a boolean mask.

        Variables of interest to be used by monitors: -y[0] + y[3]

        """
        """
        First population with high frequency burst and baseline jump - mechanisms
        is similar to a Hindmarsh-Rose scenario with two régimes connected by a
        slow trajectory (here y(3)).
        """

        y = state_variables
        n = y.shape[1]
        Iext = self.Iext + coupling[0, :] + local_coupling
        c_pop1 = coupling[0, :]
        c_pop2 = coupling[1, :]

        # if y(1)<0.
        #     ydot1 = y(2)-a*y(1)^3 + b*y(1)^2-y(3)+iext;
        #     ydot2 = c-d*y(1)^2-y(2);
        #     ydot3 =  r*(s*(y(1)-x0)  - y(3));   % energy consumption = 1 - available energy

        if_y1_lt_0 = concat([
            (y[1] - self.a * y[0]**3 + self.b * y[0]**2 - y[2] + Iext).reshape(
                (1, n, 1)), (self.c - self.d * y[0]**2 - y[1]).reshape(
                    (1, n, 1)),
            (self.r * (self.s * (y[0] - self.x0) - y[2] - self.Kpop1 *
                       (c_pop1 - y[0]))).reshape((1, n, 1))
        ])

        # else
        # %    ydot1 = y(2) + (slope - y(4) -1.0*(y(3)-4))*y(1) - y(3)+iext; % this is just an
        # %    alternative representation, which worked well
        #     ydot1 = y(2) + (slope - y(4) + 0.6*(y(3)-4)^2)*y(1) -y(3)+iext;
        # %   here the high energy burst is being generated through variation of the slope:
        # %               1. via y(4) within the epileptic spike complex;
        # %               2. via the expression with y(3), which causes more
        # %               oscillations at the beginning of the seizure (effect of the
        # %               energy available)
        #     ydot2 = c-d*y(1)^2-y(2);
        #     ydot3 =   r*(s*(y(1)-x0)  - y(3));
        # end

        else_pop1 = concat([
            (y[1] + (self.slope - y[3] + 0.6 *
                     (y[2] - 4.0)**2) * y[0] - y[2] + Iext).reshape((1, n, 1)),
            (self.c - self.d * y[0]**2 - y[1]).reshape((1, n, 1)),
            (self.r * (self.s * (y[0] - self.x0) - y[2] - self.Kpop1 *
                       (c_pop1 - y[0]))).reshape((1, n, 1))
        ])

        pop1 = where(y[0] < 0., if_y1_lt_0, else_pop1)

        # % istim= 0*block(t,150,1);
        #
        # % this is the second population that generates the big spike-wave complex
        # % preictally and within the seizure via a morris-lecar-jirsa (mlj) structure
        #
        # if y(4)<-0.25
        #     ydot4 = -y(5)+ y(4)-y(4)^3 + iext2 + 2*y(6)-0.3*(y(3)-3.5) ; % these last two terms
        #     % put the population dynamics into the critical regime. in particular,
        #     % y(6) turns the oscillator on and off, whereas the y(3) term helps it to become precritical (critical fluctuations).
        #     ydot5 = -y(5)/tau ;

        if_ = concat([(-y[4] + y[3] - y[3]**3 + self.Iext2 + 2 * y[5] - 0.3 *
                       (y[2] - 3.5) + self.Kpop2 * (c_pop2 - y[3])).reshape(
                           (1, n, 1)), (-y[4] / self.tau).reshape((1, n, 1))])
        # else
        #     ydot4 = -y(5)+ y(4)-y(4)^3 + iext2+ 2*y(6)-0.3*(y(3)-3.5);
        #     ydot5 = (-y(5) + aa*(y(4)+0.25))/tau;   % here is the mlj structure
        # end

        else_pop2 = concat([
            (-y[4] + y[3] - y[3]**3 + self.Iext2 + 2 * y[5] - 0.3 *
             (y[2] - 3.5) + self.Kpop2 * (c_pop2 - y[3])).reshape((1, n, 1)),
            ((-y[4] + self.aa * (y[3] + 0.25)) / self.tau).reshape((1, n, 1))
        ])

        pop2 = where(y[3] < -0.25, if_, else_pop2)

        #
        #  ydot6 = -0.01*(y(6)-0.1*y(1)) ;

        energy = array([-0.01 * (y[5] - 0.1 * y[0])])

        #
        # ydot = [ydot1;ydot2;ydot3;ydot4;ydot5;ydot6];

        return concat((pop1, pop2, energy))
Ejemplo n.º 9
0
    def evaluate(self):
        """
        Calculate the continuous wavelet transform of time_series.
        """
        ts_shape = self.time_series.data.shape
        
        if self.frequencies.step == 0:
            self.log.warning("Frequency step can't be 0! Trying default step, 2e-3.")
            self.frequencies.step = 0.002
        
        freqs = numpy.arange(self.frequencies.lo, self.frequencies.hi,
                             self.frequencies.step)
        
        if (freqs.size == 0) or any(freqs <= 0.0):
            # TODO: Maybe should limit number of freqs... ~100 is probably a reasonable upper bound.
            self.log.warning("Invalid frequency range! Falling back to default.")
            self.log.debug("freqs")
            self.log.debug(narray_describe(freqs))
            self.frequencies = Range(lo=0.008, hi=0.060, step=0.002)
            freqs = numpy.arange(self.frequencies.lo, self.frequencies.hi,
                                 self.frequencies.step)

        self.log.debug("freqs")
        self.log.debug(narray_describe(freqs))

        sample_rate = self.time_series.sample_rate
        
        # Duke: code below is as given by Andreas Spiegler, I've just wrapped 
        # some of the original argument names
        nf = len(freqs)
        temporal_step = max((1, iround(self.sample_period / self.time_series.sample_period)))
        nt = int(numpy.ceil(ts_shape[0] /  temporal_step))
        
        if not isinstance(self.q_ratio, numpy.ndarray):
            q_ratio = self.q_ratio * numpy.ones((1, nf))
        
        if numpy.nanmin(q_ratio) < 5:
            msg = "q_ratio must be not lower than 5 !"
            self.log.error(msg)
            raise Exception(msg)
        
        if numpy.nanmax(freqs) > sample_rate / 2.0:
            msg = "Sampling rate is too low for the requested frequency range !"
            self.log.error(msg)
            raise Exception(msg)
        
        # TODO: This isn't used, but min frequency seems like it should be important... Check with A.S.
        #  fmin = 3.0 * numpy.nanmin(q_ratio) * sample_rate / numpy.pi / nt
        sigma_f = freqs / q_ratio
        sigma_t = 1.0 / (2.0 * numpy.pi * sigma_f)
        
        if self.normalisation == 'energy':
            Amp = 1.0 / numpy.sqrt(sample_rate * numpy.sqrt(numpy.pi) * sigma_t)
        elif self.normalisation == 'gabor': 
            Amp = numpy.sqrt(2.0 / numpy.pi) / sample_rate / sigma_t
        
        coef_shape = (nf, nt, ts_shape[1], ts_shape[2], ts_shape[3])
        
        coef = numpy.zeros(coef_shape, dtype = numpy.complex128)
        self.log.debug("coef")
        self.log.debug(narray_describe(coef))

        scales = numpy.arange(0, nf, 1)
        for i in scales:
            f0 = freqs[i]
            SDt = sigma_t[(0, i)]
            A = Amp[(0, i)]
            x = numpy.arange(0, 4.0 * SDt * sample_rate, 1) / sample_rate
            wvlt = A * numpy.exp(-x**2 / (2.0 * SDt**2) ) * numpy.exp(2j * numpy.pi * f0 * x )
            wvlt = numpy.hstack((numpy.conjugate(wvlt[-1:0:-1]), wvlt))
            #util.self.log_debug_array(self.log, wvlt, "wvlt")
            
            for var in range(ts_shape[1]):
                for node in range(ts_shape[2]):
                    for mode in range(ts_shape[3]):
                        data = self.time_series.data[:, var, node, mode]
                        wt = signal.convolve(data, wvlt, 'same')
                        #util.self.log_debug_array(self.log, wt, "wt")
                        res = wt[0::temporal_step]
                        # NOTE: this is a horrible horrible quick hack (alas, a solution) to avoid broadcasting errors
                        # when using dt and sample periods which are not powers of 2.
                        coef[i, :, var, node, mode] = res if len(res) == nt else res[:coef.shape[1]] 
                        

        self.log.debug("coef")
        self.log.debug(narray_describe(coef))

        spectra = spectral.WaveletCoefficients(
            source=self.time_series,
            mother=self.mother,
            sample_period=self.sample_period,
            frequencies=self.frequencies.to_array(),
            normalisation=self.normalisation,
            q_ratio=self.q_ratio,
            array_data=coef)
        
        return spectra
Ejemplo n.º 10
0
class LileySteynRoss(models.Model):
    """
    Liley lumped model as presented in Steyn-Ross et al 1999.

    This  model is to be use for modelling cortical dynamics in which "inputs"
    to neuronal assemblies are treated as random Gaussian fluctuations about a
    mean value. Anesthetic agent effects are modelled as as a modulation of the
    inhibitory neurotransmitter rate constant.

    The main state variable is h_e, the average excitatory soma potential,
    coherent fluctuations of which are believed to be the source of scalp-measured
    electroencephalogram ͑EEG͒ signals.

    Parameters are taken from Table 1 [Steyn-Ross_1999]_


    State variables:

    h_e: exc population mean soma potential [mV]
    h_i: exc population mean soma potential [mV]

    I_ee: total 'exc' current input to 'exc' synapses [mV]
    I_ie: total 'inh' current input to 'exc' synapses [mV]
    I_ei: total 'exc' current input to 'inh' synapses [mV]
    I_ii: total 'inh' current input to 'inh' synapses [mV]

    :math:`\Psi_{jk}`:  weighting factors for the I_jk inputs [dimensionless]

    :math:`phi_e`: long-range (cortico-cortical) spike input to exc population
    :math:`phi_i`: long-range (cortico-cortical) spike input to inh population [ms-1]

    EPSP: exc post-synaptic potential [mV]
    IPSP: inh post-synaptic potential [mV]

    Mean axonal conduction speed: 7 mm/ms

    S_e(h_e): sigmoid function mapping soma potential to firing rate [ms]-1
    S_i(h_i): sigmoid function mapping soma potential to firing rate [ms]-1



    The models (:math:`h_e`, :math:h_i`) phase-plane, including a representation of
    the vector field as well as its nullclines, using default parameters, can be
    seen below:

        .. _phase-plane-LSR:
        .. figure :: img/LileySteynRoss_01_mode_0_pplane.svg
            :alt: LileySteynRoss phase plane (E, I)

            The (:math:`h_e`, :math:`hi`) phase-plane for the LileySteynRoss model.


    References
    ----------

    .. [Steyn-Ross et al 1999] Theoretical electroencephalogram stationary spectrum for a 
       white-noise-driven cortex: Evidence for a general anesthetic-induced phase transition.

    .. [Liley et al 2013] The mesoscopic modelin of burst supression during anesthesia.


    """

    # Define traited attributes for this model, these represent possible kwargs.

    tau_e = NArray(
        label=r":math:`\tau_e`",
        default=numpy.array([40.0]),
        domain=Range(lo=5.0, hi=50.0, step=1.00),
        doc="""Excitatory population, membrane time-constant [ms]""")

    tau_i = NArray(
        label=r":math:`\tau_i`",
        default=numpy.array([40.0]),
        domain=Range(lo=5.0, hi=50.0, step=1.0),
        doc="""Inhibitory population, membrane time-constant [ms]""")

    h_e_rest = NArray(
        label=r":math:`h_e^{rest}`",
        default=numpy.array([-70.0]),
        domain=Range(lo=-90.0, hi=-50.0, step=10.00),
        doc="""Excitatory population, cell resting potential [mV]""")

    h_i_rest = NArray(
        label=r":math:`h_i^{rest}`",
        default=numpy.array([-70.0]),
        domain=Range(lo=-90.0, hi=-50.0, step=10.0),
        doc="""Inhibitory population, cell resting potential [mV]""")

    h_e_rev = NArray(
        label=r":math:`h_e^{rev}`",
        default=numpy.array([45.0]),
        domain=Range(lo=0.0, hi=50.0, step=5.00),
        doc="""Excitatory population, cell reversal potential [mV]""")

    h_i_rev = NArray(
        label=r":math:`h_i^{rev}`",
        default=numpy.array([-90.0]),
        domain=Range(lo=-90.0, hi=-50.0, step=10.0),
        doc="""Inhibitory population, cell reversal potential [mV]""")

    p_ee = NArray(
        label=":math:`p_{ee}`",
        default=numpy.array([1.1]),
        domain=Range(lo=1.0, hi=1.8, step=0.1),
        doc=
        """Exogenous (subcortical) spike input to exc population [ms]-1 [kHz]. 
               This could be replaced by a noise term""")

    p_ie = NArray(
        label=":math:`p_{ie}`",
        default=numpy.array([1.6]),
        domain=Range(lo=1.0, hi=1.8, step=0.1),
        doc=
        """Exogenous (subcortical) spike input to exc population [ms]-1 [kHz]. 
               This could be replaced by a noise term""")

    p_ei = NArray(
        label=":math:`p_{ei}`",
        default=numpy.array([1.6]),
        domain=Range(lo=1.0, hi=1.8, step=0.1),
        doc=
        """Exogenous (subcortical) spike input to inh population [ms]-1 [kHz]. 
               This could be replaced by a noise term""")

    p_ii = NArray(
        label=":math:`p_{ii}`",
        default=numpy.array([1.1]),
        domain=Range(lo=1.0, hi=1.8, step=0.1),
        doc=
        """Exogenous (subcortical) spike input to inh population [ms]-1 [kHz]. 
               This could be replaced by a noise term""")

    A_ee = NArray(
        label=r":math:`\alpha_{ee}`",
        default=numpy.array([0.04]),
        domain=Range(lo=0.02, hi=0.06, step=0.01),
        doc=
        """Characteristic cortico-cortical inverse length scale [mm]-1. Original: 0.4 cm-1"""
    )

    A_ei = NArray(
        label=r":math:`\alpha_{ei}`",
        default=numpy.array([0.065]),
        domain=Range(lo=0.02, hi=0.08, step=0.01),
        doc=
        """Characteristic cortico-cortical inverse length scale [mm]-1. Original: 0.4 cm-1"""
    )

    gamma_e = NArray(label=r":math:`\gamma_e`",
                     default=numpy.array([0.3]),
                     domain=Range(lo=0.1, hi=0.4, step=0.01),
                     doc="""Neurotransmitter rate constant"for EPSP [ms]-1""")

    gamma_i = NArray(label=r":math:`\gamma_i`",
                     default=numpy.array([0.065]),
                     domain=Range(lo=0.005, hi=0.1, step=0.005),
                     doc="""Neurotransmitter rate constant"for IPSP [ms]-1""")

    G_e = NArray(label=":math:`G_e`",
                 default=numpy.array([0.18]),
                 domain=Range(lo=0.1, hi=0.5, step=0.01),
                 doc="""peak ampplitude of EPSP [mV]""")

    G_i = NArray(label=":math:`G_i`",
                 default=numpy.array([0.37]),
                 domain=Range(lo=0.1, hi=0.5, step=0.01),
                 doc="""peak ampplitude of IPSP [mV]""")

    N_b_ee = NArray(
        label=":math:`N_{ee}^{\beta}`",
        default=numpy.array([3034.0]),
        domain=Range(lo=3000., hi=3050., step=10.0),
        doc="""Total number of local exc to exc synaptic connections.""")

    N_b_ei = NArray(
        label=r":math:`N_{ei}^{\beta}`",
        default=numpy.array([3034.0]),
        domain=Range(lo=3000., hi=3050., step=10.0),
        doc="""Total number of local exc to inh synaptic connections.""")

    N_b_ie = NArray(
        label=r":math:`N_{ie}^{\beta}`",
        default=numpy.array([536.0]),
        domain=Range(lo=500., hi=550., step=1.0),
        doc="""Total number of local inh to exc synaptic connections.""")

    N_b_ii = NArray(
        label=r":math:`N_{ii}^{\beta}`",
        default=numpy.array([536.0]),
        domain=Range(lo=500., hi=550., step=1.0),
        doc="""Total number of local inh to inh synaptic connections.""")

    N_a_ee = NArray(
        label=r":math:`N_{ee}^{\alpha}`",
        default=numpy.array([4000.0]),
        domain=Range(lo=3000., hi=5000., step=10.0),
        doc=
        """Total number of synaptic connections from distant exc populations"""
    )

    N_a_ei = NArray(
        label=r":math:`N_{ei}^{\alpha}`",
        default=numpy.array([2000.0]),
        domain=Range(lo=1000., hi=3000., step=1.0),
        doc=
        """Total number of synaptic connections from distant exc populations"""
    )

    theta_e = NArray(
        label=r":math:`\theta_e`",
        default=numpy.array([-60.0]),
        domain=Range(lo=-90.0, hi=-40.0, step=5.0),
        doc="""inflection point voltage for sigmoid function [mV]""")

    theta_i = NArray(
        label=r":math:`\theta_i`",
        default=numpy.array([-60.0]),
        domain=Range(lo=-90.0, hi=-40.0, step=5.0),
        doc="""inflection point voltage for sigmoid function [mV]""")

    g_e = NArray(
        label=":math:`g_e`",
        default=numpy.array([0.28]),
        domain=Range(lo=0.0, hi=2.0, step=0.01),
        doc="""Sigmoid slope at inflection point exc population [mV]-1""")

    g_i = NArray(
        label=":math:`g_i`",
        default=numpy.array([0.14]),
        domain=Range(lo=0.0, hi=2.0, step=0.01),
        doc="""Sigmoid slope at inflection point inh population [mV]-1""")

    lambd = NArray(label=":math:`\lambda`",
                   default=numpy.array([1.0]),
                   domain=Range(lo=0.0, hi=2.0, step=0.01),
                   doc="""Anesthetic effects""")

    # Used for phase-plane axis ranges and to bound random initial() conditions.
    state_variable_range = Final(
        {
            "he": numpy.array([-90.0, 70.0]),
            "hi": numpy.array([-90.0, 70.0])
        },
        label="State Variable ranges [lo, hi]",
        doc="""The values for each state-variable should be set to encompass
        the expected dynamic range of that state-variable for the current 
        parameters, it is used as a mechanism for bounding random inital 
        conditions when the simulation isn't started from an explicit history,
        it is also provides the default range of phase-plane plots.""")

    variables_of_interest = List(
        of=str,
        label="Variables watched by Monitors",
        choices=("he", "hi"),
        default=("he", ),
        doc="""This represents the default state-variables of this Model to be
                                    monitored. It can be overridden for each Monitor if desired. The 
                                    corresponding state-variable indices for this model are :math:`E = 0`
                                    and :math:`I = 1`.""")

    def __init__(self, **kwargs):
        """
        Initialize the Liley Steyn-Ross model's traited attributes, any provided as
        keywords will overide their traited default.

        """
        LOG.info('%s: initing...' % str(self))
        super(LileySteynRoss, self).__init__(**kwargs)
        #self._state_variables = ["E", "I"]
        self._nvar = 2
        self.cvar = numpy.array([0, 1], dtype=numpy.int32)
        LOG.debug('%s: inited.' % repr(self))

    def dfun(self, state_variables, coupling, local_coupling=0.0):
        r"""

        TODO:  include equations here and see how to add the local connectivity or the 
        the laplacian oeprator. This model is the one used in Bojak 2011.

        """

        he = state_variables[0, :]
        hi = state_variables[1, :]

        # long-range coupling - phi_e -> he / phi_e -> h_i
        c_0 = coupling[0, :]

        # short-range (local) coupling - local coupling functions should be different for exc and inh
        #lc_0 = local_coupling * he
        #lc_1 = local_coupling * hi

        #
        psi_ee = (self.h_e_rev - he) / abs(
            self.h_e_rev - self.h_e_rest)  # usually > 0
        psi_ei = (self.h_e_rev - hi) / abs(
            self.h_e_rev - self.h_i_rest)  # usually > 0
        psi_ie = (self.h_i_rev - he) / abs(
            self.h_i_rev - self.h_e_rest)  # usually < 0
        psi_ii = (self.h_i_rev - hi) / abs(
            self.h_i_rev - self.h_i_rest)  # usually < 0

        S_e = 1.0 / (1.0 + numpy.exp(-self.g_e * (he + c_0 - self.theta_e)))
        S_i = 1.0 / (1.0 + numpy.exp(-self.g_i * (hi + c_0 - self.theta_i)))

        F1 = ((self.h_e_rest - he) + psi_ee * ((self.N_a_ee + self.N_b_ee) * S_e  + self.p_ee) * (self.G_e/self.gamma_e) +\
                                  self.lambd * psi_ie * (self.N_b_ie * S_i + self.p_ie) * (self.G_i/self.gamma_i)) / self.tau_e

        F2 = ((self.h_i_rest - hi) + psi_ei * ((self.N_a_ei + self.N_b_ei) * S_e  + self.p_ei) * (self.G_e/self.gamma_e) +\
                                  self.lambd * psi_ii * (self.N_b_ii * S_i + self.p_ii) * (self.G_i/self.gamma_i)) / self.tau_i

        dhe = F1
        dhi = F2

        derivative = numpy.array([dhe, dhi])

        return derivative
Ejemplo n.º 11
0
class Hopfield(Model):
    r"""

    The Hopfield neural network is a discrete time dynamical system composed
    of multiple binary nodes, with a connectivity matrix built from a
    predetermined set of patterns. The update, inspired from the spin-glass
    model (used to describe magnetic properties of dilute alloys), is based on
    a random scanning of every node. The existence of a fixed point dynamics
    is guaranteed by a Lyapunov function. The Hopfield network is expected to
    have those multiple patterns as attractors (multistable dynamical system).
    When the initial conditions are close to one of the 'learned' patterns,
    the dynamical system is expected to relax on the corresponding attractor.
    A possible output of the system is the final attractive state (interpreted
    as an associative memory).

    Various extensions of the initial model have been proposed, among which a
    noiseless and continuous version [Hopfield 1984] having a slightly
    different Lyapunov function, but essentially the same dynamical
    properties, with more straightforward physiological interpretation. A
    continuous Hopfield neural network (with a sigmoid transfer function) can
    indeed be interpreted as a network of neural masses with every node
    corresponding to the mean field activity of a local brain region, with
    many bridges with the Wilson Cowan model [WC_1972].

    **References**:

        .. [Hopfield1982] Hopfield, J. J., *Neural networks and physical systems with emergent collective
                        computational abilities*, Proc. Nat. Acad. Sci. (USA) 79, 2554-2558, 1982.

        .. [Hopfield1984] Hopfield, J. J., *Neurons with graded response have collective computational
                        properties like those of two-sate neurons*, Proc. Nat. Acad. Sci. (USA) 81, 3088-3092, 1984.

    See also, http://www.scholarpedia.org/article/Hopfield_network

    .. #This model can use a global threshold permitting multistable dynamic for
    .. #a positive structural connectivity matrix.

    .. automethod:: Hopfield.configure

    Dynamic equations:

    dfun equation
        .. math::
                \dot{x_{i}} &= 1 / \tau_{x} (-x_{i} + c_0)
    dfun dynamic equation
        .. math::
            \dot{x_{i}} &= 1 / \tau_{x} (-x_{i} + c_0(i)) \\
            \dot{\\theta_{i}} &= 1 / \tau_{\theta_{i}} (-\theta + c_1(i))


    .. figure :: img/Hopfield_01_mode_0_pplane.svg

        The phase-plane for the Hopfield model.

    """

    # Define traited attributes for this model, these represent possible kwargs.
    taux = NArray(
        label=":math:`\\tau_{x}`",
        default=numpy.array([1.]),
        domain=Range(lo=0.01, hi=100., step=0.01),
        doc=
        """The fast time-scale for potential calculus :math:`x`, state-variable of the model."""
    )

    tauT = NArray(
        label=":math:`\\tau_{\\theta}`",
        default=numpy.array([5.]),
        domain=Range(lo=0.01, hi=100., step=0.01),
        doc=
        """The slow time-scale for threshold calculus :math:`\theta`, state-variable of the model."""
    )

    dynamic = NArray(
        dtype=int,
        label="Dynamic",
        default=numpy.array([0]),
        domain=Range(lo=0, hi=1., step=1),
        doc="""Boolean value for static/dynamic threshold theta for (0/1).""")

    # Used for phase-plane axis ranges and to bound random initial() conditions.
    state_variable_range = Final(
        label="State Variable ranges [lo, hi]",
        default={
            "x": numpy.array([-1., 2.]),
            "theta": numpy.array([0., 1.])
        },
        doc="""The values for each state-variable should be set to encompass
            the expected dynamic range of that state-variable for the current
            parameters, it is used as a mechanism for bounding random inital
            conditions when the simulation isn't started from an explicit
            history, it is also provides the default range of phase-plane plots."""
    )

    variables_of_interest = List(
        of=str,
        label="Variables watched by Monitors",
        choices=("x", "theta"),
        default=("x", ),
        doc="""The values for each state-variable should be set to encompass
            the expected dynamic range of that state-variable for the current
            parameters, it is used as a mechanism for bounding random initial
            conditions when the simulation isn't started from an explicit
            history, it is also provides the default range of phase-plane plots."""
    )

    state_variables = ('x', 'theta')

    _nvar = 2
    cvar = numpy.array([0], dtype=numpy.int32)

    def configure(self):
        """Set the threshold as a state variable for a dynamical threshold."""
        super(Hopfield, self).configure()
        if self.dynamic:
            self.dfun = self.dfunDyn
            self._nvar = 2
            self.cvar = numpy.array([0, 1], dtype=numpy.int32)
            # self.variables_of_interest = ["x", "theta"]

    def dfun(self, state_variables, coupling, local_coupling=0.0):
        r"""
        The fast, :math:`x`, and slow, :math:`\theta`, state variables are typically
        considered to represent a membrane potentials of nodes and the global inhibition term,
        respectively:

            .. math::
                \dot{x_{i}} &= 1 / \tau_{x} (-x_{i} + c_0)

        """
        x = state_variables[0, :]
        dx = (-x + coupling[0]) / self.taux

        # todo: display dependent hack. It returns dx twice to be compatible with dfunDyn
        # We return 2 arrays here, because we have 2 possible state Variable, even if not dynamic
        # Otherwise the phase-plane display will fail.
        derivative = numpy.array([dx, dx])
        return derivative

    def dfunDyn(self, state_variables, coupling, local_coupling=0.0):
        r"""
        The fast, :math:`x`, and slow, :math:`\theta`, state variables are typically
        considered to represent a membrane potentials of nodes and the inhibition term(s),
        respectively:

            .. math::
                \dot{x_{i}} &= 1 / \tau_{x} (-x_{i} + c_0(i)) \\
                \dot{\theta_{i}} &= 1 / \tau_{\theta_{i}} (-\theta + c_1(i))

        where c_0 is the coupling term and c_1 should be the direct output.

        """

        x = state_variables[0, :]
        theta = state_variables[1, :]
        dx = (-x + coupling[0]) / self.taux
        dtheta = (-theta + coupling[1]) / self.tauT

        derivative = numpy.array([dx, dtheta])
        return derivative
Ejemplo n.º 12
0
class ReducedWongWang(ModelNumbaDfun):
    r"""
    .. [WW_2006] Kong-Fatt Wong and Xiao-Jing Wang,  *A Recurrent Network
                Mechanism of Time Integration in Perceptual Decisions*.
                Journal of Neuroscience 26(4), 1314-1328, 2006.

    .. [DPA_2013] Deco Gustavo, Ponce Alvarez Adrian, Dante Mantini, Gian Luca
                  Romani, Patric Hagmann and Maurizio Corbetta. *Resting-State
                  Functional Connectivity Emerges from Structurally and
                  Dynamically Shaped Slow Linear Fluctuations*. The Journal of
                  Neuroscience 32(27), 11239-11252, 2013.


    Equations taken from [DPA_2013]_ , page 11242

    .. math::
                 x_k       &=   w\,J_N \, S_k + I_o + J_N \mathbf\Gamma(S_k, S_j, u_{kj}),\\
                 H(x_k)    &=  \dfrac{ax_k - b}{1 - \exp(-d(ax_k -b))},\\
                 \dot{S}_k &= -\dfrac{S_k}{\tau_s} + (1 - S_k) \, H(x_k) \, \gamma

    """

    # Define traited attributes for this model, these represent possible kwargs.
    a = NArray(
        label=":math:`a`",
        default=numpy.array([
            0.270,
        ]),
        domain=Range(lo=0.0, hi=0.270, step=0.01),
        doc="[n/C]. Input gain parameter, chosen to fit numerical solutions.")

    b = NArray(
        label=":math:`b`",
        default=numpy.array([
            0.108,
        ]),
        domain=Range(lo=0.0, hi=1.0, step=0.01),
        doc="[kHz]. Input shift parameter chosen to fit numerical solutions.")

    d = NArray(label=":math:`d`",
               default=numpy.array([
                   154.,
               ]),
               domain=Range(lo=0.0, hi=200.0, step=0.01),
               doc="""[ms]. Parameter chosen to fit numerical solutions.""")

    gamma = NArray(label=r":math:`\gamma`",
                   default=numpy.array([
                       0.641,
                   ]),
                   domain=Range(lo=0.0, hi=1.0, step=0.01),
                   doc="""Kinetic parameter""")

    tau_s = NArray(label=r":math:`\tau_S`",
                   default=numpy.array([
                       100.,
                   ]),
                   domain=Range(lo=50.0, hi=150.0, step=1.0),
                   doc="""Kinetic parameter. NMDA decay time constant.""")

    w = NArray(label=r":math:`w`",
               default=numpy.array([
                   0.6,
               ]),
               domain=Range(lo=0.0, hi=1.0, step=0.01),
               doc="""Excitatory recurrence""")

    J_N = NArray(label=r":math:`J_{N}`",
                 default=numpy.array([
                     0.2609,
                 ]),
                 domain=Range(lo=0.2609, hi=0.5, step=0.001),
                 doc="""Excitatory recurrence""")

    I_o = NArray(label=":math:`I_{o}`",
                 default=numpy.array([
                     0.33,
                 ]),
                 domain=Range(lo=0.0, hi=1.0, step=0.01),
                 doc="""[nA] Effective external input""")

    sigma_noise = NArray(
        label=r":math:`\sigma_{noise}`",
        default=numpy.array([
            0.000000001,
        ]),
        domain=Range(lo=0.0, hi=0.005, step=0.0001),
        doc="""[nA] Noise amplitude. Take this value into account for stochatic
        integration schemes.""")

    state_variable_range = Final(label="State variable ranges [lo, hi]",
                                 default={"S": numpy.array([0.0, 1.0])},
                                 doc="Population firing rate")

    state_variable_boundaries = Final(
        label="State Variable boundaries [lo, hi]",
        default={"S": numpy.array([0.0, 1.0])},
        doc="""The values for each state-variable should be set to encompass
            the boundaries of the dynamic range of that state-variable. Set None for one-sided boundaries"""
    )

    variables_of_interest = List(
        of=str,
        label="Variables watched by Monitors",
        choices=("S", ),
        default=("S", ),
        doc="""default state variables to be monitored""")

    state_variables = ['S']
    _nvar = 1
    cvar = numpy.array([0], dtype=numpy.int32)

    def configure(self):
        """  """
        super(ReducedWongWang, self).configure()
        self.update_derived_parameters()

    def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0):
        r"""
        Equations taken from [DPA_2013]_ , page 11242

        .. math::
                 x_k       &=   w\,J_N \, S_k + I_o + J_N \mathbf\Gamma(S_k, S_j, u_{kj}),\\
                 H(x_k)    &=  \dfrac{ax_k - b}{1 - \exp(-d(ax_k -b))},\\
                 \dot{S}_k &= -\dfrac{S_k}{\tau_s} + (1 - S_k) \, H(x_k) \, \gamma

        """
        S = state_variables[0, :]

        c_0 = coupling[0, :]

        # if applicable
        lc_0 = local_coupling * S

        x = self.w * self.J_N * S + self.I_o + self.J_N * c_0 + self.J_N * lc_0
        H = (self.a * x - self.b) / (1 - numpy.exp(-self.d *
                                                   (self.a * x - self.b)))
        dS = -(S / self.tau_s) + (1 - S) * H * self.gamma

        derivative = numpy.array([dS])
        return derivative

    def dfun(self, x, c, local_coupling=0.0):
        x_ = x.reshape(x.shape[:-1]).T
        c_ = c.reshape(c.shape[:-1]).T + local_coupling * x[0]
        deriv = _numba_dfun(x_, c_, self.a, self.b, self.d, self.gamma,
                            self.tau_s, self.w, self.J_N, self.I_o)
        return deriv.T[..., numpy.newaxis]
Ejemplo n.º 13
0
class ZerlautAdaptationFirstOrder(Model):
    r"""
    **References**:
    .. [ZD_2018]  Zerlaut, Y., Chemla, S., Chavane, F. et al. *Modeling mesoscopic cortical dynamics using a mean-field
    model of conductance-based networks of adaptive
    exponential integrate-and-fire neurons*,
    J Comput Neurosci (2018) 44: 45. https://doi-org.lama.univ-amu.fr/10.1007/s10827-017-0668-2
    .. [MV_2018]  Matteo di Volo, Alberto Romagnoni, Cristiano Capone, Alain Destexhe (2018)
    *Mean-field model for the dynamics of conductance-based networks of excitatory and inhibitory spiking neurons
    with adaptation*, bioRxiv, doi: https://doi.org/10.1101/352393

    Used Eqns 4 from [MV_2018]_ in ``dfun``.

    The default parameters are taken from table 1 of [ZD_2018]_, pag.47 and modify for the adaptation [MV_2018]
    +---------------------------+------------+
    |                 Table 1                |
    +--------------+------------+------------+
    |Parameter     |  Value     | Unit       |
    +==============+============+============+
    |             cellular property          |
    +--------------+------------+------------+
    | g_L          |   10.00    |   nS       |
    +--------------+------------+------------+
    | E_L_e        |  -60.00    |   mV       |
    +--------------+------------+------------+
    | E_L_i        |  -65.00    |   mV       |
    +--------------+------------+------------+
    | C_m          |   200.0    |   pF       |
    +--------------+------------+------------+
    | b_e          |   60.0     |   nS       |
    +--------------+------------+------------+
    | b_i          |   0.0      |   nS       |
    +--------------+------------+------------+
    | a_e          |   4.0      |   nS       |
    +--------------+------------+------------+
    | a_i          |   0.0      |   nS       |
    +--------------+------------+------------+
    | tau_w_e      |   500.0    |   ms       |
    +--------------+------------+------------+
    | tau_w_i      |   0.0      |   ms       |
    +--------------+------------+------------+
    | T            |   20.0      |   ms       |
    +--------------+------------+------------+
    |          synaptic properties           |
    +--------------+------------+------------+
    | E_e          |    0.0     | mV         |
    +--------------+------------+------------+
    | E_i          |   -80.0    | mV         |
    +--------------+------------+------------+
    | Q_e          |    1.0     | nS         |
    +--------------+------------+------------+
    | Q_i          |    5.0     | nS         |
    +--------------+------------+------------+
    | tau_e        |    5.0     | ms         |
    +--------------+------------+------------+
    | tau_i        |    5.0     | ms         |
    +--------------+------------+------------+
    |          numerical network             |
    +--------------+------------+------------+
    | N_tot        |  10000     |            |
    +--------------+------------+------------+
    | p_connect    |    5.0 %   |            |
    +--------------+------------+------------+
    | g            |   20.0 %   |            |
    +--------------+------------+------------+
    | K_e_ext      |   400      |            |
    +--------------+------------+------------+
    | K_i_ext      |   0        |            |
    +--------------+------------+------------+
    |external_input|    0.000   | Hz         |
    +--------------+------------+------------+

    The default coefficients of the transfer function are taken from table I of [MV_2018]_, pag.49
    +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
    |      excitatory cell      |
    +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
    |  -4.98e-02  |   5.06e-03  |  -2.5e-02   |   1.4e-03   |  -4.1e-04   |   1.05e-02  |  -3.6e-02   |   7.4e-03   |   1.2e-03   |  -4.07e-02  |
    +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
    |      inhibitory cell      |
    +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
    |  -5.14e-02  |   4.0e-03   |  -8.3e-03   |   2.0e-04   |  -5.0e-04   |   1.4e-03   |  -1.46e-02  |   4.5e-03   |   2.8e-03   |  -1.53e-02  |
    +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+

    The models (:math:`E`, :math:`I`) phase-plane, including a representation of
    the vector field as well as its null-clines, using default parameters, can be
    seen below:

    .. automethod:: Zerlaut_adaptation_first_order.__init__

    The general formulation for the \textit{\textbf{Zerlaut_adaptation_first_order}} model as a
    dynamical unit at a node $k$ in a BNM with $l$ nodes reads:

    .. math::
            T\dot{E}_k &= F_e-E_k  \\
            T\dot{I}_k &= F_i-I_k  \\
            dot{W}_k &= W_k/tau_w-b*E_k  \\
            F_\lambda = Erfc(V^{eff}_{thre}-\mu_V/\sqrt(2)\sigma_V)

    """

    # Define traited attributes for this model, these represent possible kwargs.
    g_L = NArray(
        label=":math:`g_{L}`",
        default=numpy.array([
            10.
        ]),  # 10 nS by default, i.e. ~ 100MOhm input resistance at rest
        domain=Range(
            lo=0.1, hi=100.0, step=0.1
        ),  # 0.1nS would be a very small cell, 100nS a very big one
        doc="""leak conductance [nS]""")

    E_L_e = NArray(
        label=":math:`E_{L}`",
        default=numpy.array([-65.0]),
        domain=Range(
            lo=-90.0, hi=-60.0,
            step=0.1),  # resting potential, usually between -85mV and -65mV
        doc="""leak reversal potential for excitatory [mV]""")

    E_L_i = NArray(
        label=":math:`E_{L}`",
        default=numpy.array([-65.0]),
        domain=Range(
            lo=-90.0, hi=-60.0,
            step=0.1),  # resting potential, usually between -85mV and -65mV
        doc="""leak reversal potential for inhibitory [mV]""")

    # N.B. Not independent of g_L, C_m should scale linearly with g_L
    C_m = NArray(
        label=":math:`C_{m}`",
        default=numpy.array([200.0]),
        domain=Range(lo=10.0, hi=500.0,
                     step=10.0),  # 20pF very small cell, 400pF very
        doc="""membrane capacitance [pF]""")

    b_e = NArray(label=":math:`b_e`",
                 default=numpy.array([60.0]),
                 domain=Range(lo=0.0, hi=150.0, step=1.0),
                 doc="""Excitatory adaptation current increment [pA]""")

    a_e = NArray(label=":math:`a_e`",
                 default=numpy.array([4.0]),
                 domain=Range(lo=0.0, hi=20.0, step=0.1),
                 doc="""Excitatory adaptation conductance [nS]""")

    b_i = NArray(label=":math:`b_i`",
                 default=numpy.array([0.0]),
                 domain=Range(lo=0.0, hi=100.0, step=0.1),
                 doc="""Inhibitory adaptation current increment [pA]""")

    a_i = NArray(label=":math:`a_i`",
                 default=numpy.array([0.0]),
                 domain=Range(lo=0.0, hi=20.0, step=0.1),
                 doc="""Inhibitory adaptation conductance [nS]""")

    tau_w_e = NArray(
        label=":math:`tau_we`",
        default=numpy.array([500.0]),
        domain=Range(lo=1.0, hi=1000.0, step=1.0),
        doc="""Adaptation time constant of excitatory neurons [ms]""")

    tau_w_i = NArray(
        label=":math:`tau_wi`",
        default=numpy.array([1.0]),
        domain=Range(lo=1.0, hi=1000.0, step=1.0),
        doc="""Adaptation time constant of inhibitory neurons [ms]""")

    E_e = NArray(label=r":math:`E_e`",
                 default=numpy.array([0.0]),
                 domain=Range(lo=-20., hi=20., step=0.01),
                 doc="""excitatory reversal potential [mV]""")

    E_i = NArray(label=":math:`E_i`",
                 default=numpy.array([-80.0]),
                 domain=Range(lo=-100.0, hi=-60.0, step=1.0),
                 doc="""inhibitory reversal potential [mV]""")

    Q_e = NArray(label=r":math:`Q_e`",
                 default=numpy.array([1.5]),
                 domain=Range(lo=0.0, hi=5.0, step=0.1),
                 doc="""excitatory quantal conductance [nS]""")

    Q_i = NArray(label=r":math:`Q_i`",
                 default=numpy.array([5.0]),
                 domain=Range(lo=0.0, hi=10.0, step=0.1),
                 doc="""inhibitory quantal conductance [nS]""")

    tau_e = NArray(label=":math:`\tau_e`",
                   default=numpy.array([5.0]),
                   domain=Range(lo=1.0, hi=10.0, step=1.0),
                   doc="""excitatory decay [ms]""")

    tau_i = NArray(label=":math:`\tau_i`",
                   default=numpy.array([5.0]),
                   domain=Range(lo=0.5, hi=10.0, step=0.01),
                   doc="""inhibitory decay [ms]""")

    N_tot = NArray(dtype=numpy.int,
                   label=":math:`N_{tot}`",
                   default=numpy.array([10000]),
                   domain=Range(lo=1000, hi=50000, step=1000),
                   doc="""cell number""")

    p_connect = NArray(
        label=":math:`\epsilon`",
        default=numpy.array([0.05]),
        domain=Range(
            lo=0.001, hi=0.2,
            step=0.001),  # valid only for relatively sparse connectivities
        doc="""connectivity probability""")

    g = NArray(
        label=":math:`g`",
        default=numpy.array([0.2]),
        domain=Range(
            lo=0.01, hi=0.4, step=0.01
        ),  # inhibitory cell number never overcomes excitatory ones
        doc="""fraction of inhibitory cells""")

    K_ext_e = NArray(
        dtype=numpy.int,
        label=":math:`K_ext_e`",
        default=numpy.array([400]),
        domain=Range(
            lo=0, hi=10000,
            step=1),  # inhibitory cell number never overcomes excitatory ones
        doc="""Number of excitatory connexions from external population""")

    K_ext_i = NArray(
        dtype=numpy.int,
        label=":math:`K_ext_i`",
        default=numpy.array([0]),
        domain=Range(
            lo=0, hi=10000,
            step=1),  # inhibitory cell number never overcomes excitatory ones
        doc="""Number of inhibitory connexions from external population""")

    T = NArray(label=":math:`T`",
               default=numpy.array([20.0]),
               domain=Range(lo=1., hi=20.0, step=0.1),
               doc="""time scale of describing network activity""")

    P_e = NArray(
        label=
        ":math:`P_e`",  # TODO need to check the size of the array when it's used
        default=numpy.array([
            -0.04983106, 0.005063550882777035, -0.023470121807314552,
            0.0022951513725067503, -0.0004105302652029825,
            0.010547051343547399, -0.03659252821136933, 0.007437487505797858,
            0.001265064721846073, -0.04072161294490446
        ]),
        doc="""Polynome of excitatory phenomenological threshold (order 9)""")

    P_i = NArray(
        label=
        ":math:`P_i`",  # TODO need to check the size of the array when it's used
        default=numpy.array([
            -0.05149122024209484, 0.004003689190271077, -0.008352013668528155,
            0.0002414237992765705, -0.0005070645080016026,
            0.0014345394104282397, -0.014686689498949967, 0.004502706285435741,
            0.0028472190352532454, -0.015357804594594548
        ]),
        doc="""Polynome of inhibitory phenomenological threshold (order 9)""")

    external_input_ex_ex = NArray(label=":math:`\nu_e^{drive}`",
                                  default=numpy.array([0.000]),
                                  domain=Range(lo=0.00, hi=0.1, step=0.001),
                                  doc="""external drive""")

    external_input_ex_in = NArray(label=":math:`\nu_e^{drive}`",
                                  default=numpy.array([0.000]),
                                  domain=Range(lo=0.00, hi=0.1, step=0.001),
                                  doc="""external drive""")

    external_input_in_ex = NArray(label=":math:`\nu_e^{drive}`",
                                  default=numpy.array([0.000]),
                                  domain=Range(lo=0.00, hi=0.1, step=0.001),
                                  doc="""external drive""")

    external_input_in_in = NArray(label=":math:`\nu_e^{drive}`",
                                  default=numpy.array([0.000]),
                                  domain=Range(lo=0.00, hi=0.1, step=0.001),
                                  doc="""external drive""")

    # Used for phase-plane axis ranges and to bound random initial() conditions.
    state_variable_range = Final(
        label="State Variable ranges [lo, hi]",
        default={
            "E": numpy.array([
                1e-3, 250.e-3
            ]),  # actually the 100Hz should be replaced by 1/T_refrac
            "I": numpy.array([1e-3, 250.e-3]),
            "W_e": numpy.array([0.0, 200.0]),
            "W_i": numpy.array([0.0, 0.0]),
        },
        doc="""The values for each state-variable should be set to encompass
        the expected dynamic range of that state-variable for the current
        parameters, it is used as a mechanism for bounding random initial
        conditions when the simulation isn't started from an explicit history,
        it is also provides the default range of phase-plane plots.\n
        E: firing rate of excitatory population in KHz\n
        I: firing rate of inhibitory population in KHz\n
        W_e: level of adaptation of excitatory in pA\n
        W_i: level of adaptation of inhibitory in pA\n
        """)

    variables_of_interest = List(
        of=str,
        label="Variables watched by Monitors",
        choices=("E", "I", "W_e", "W_i"),
        default=("E", ),
        doc="""This represents the default state-variables of this Model to be
               monitored. It can be overridden for each Monitor if desired. The
               corresponding state-variable indices for this model are :math:`E = 0`,
               :math:`I = 1` and :math:`W = 2`.""")

    state_variable_boundaries = Final(
        label="Firing rate of population is always positive",
        default={
            "E": numpy.array([0.0, None]),
            "I": numpy.array([0.0, None])
        },
        doc="""The values for each state-variable should be set to encompass
            the boundaries of the dynamic range of that state-variable. Set None for one-sided boundaries"""
    )

    state_variables = 'E I W_e W_i'.split()
    _nvar = 4
    cvar = numpy.array([0], dtype=numpy.int32)

    def dfun(self, state_variables, coupling, local_coupling=0.00):
        r"""
        .. math::
            T \dot{\nu_\mu} &= -F_\mu(\nu_e,\nu_i) + \nu_\mu ,\all\mu\in\{e,i\}\\
            dot{W}_k &= W_k/tau_w-b*E_k  \\

        """
        E = state_variables[0, :]
        I = state_variables[1, :]
        W_e = state_variables[2, :]
        W_i = state_variables[3, :]
        derivative = numpy.empty_like(state_variables)

        # long-range coupling
        c_0 = coupling[0, :]

        # short-range (local) coupling
        lc_E = local_coupling * E
        lc_I = local_coupling * I

        # external firing rate
        Fe_ext = c_0 + lc_E
        Fi_ext = lc_I

        # Excitatory firing rate derivation
        derivative[0] = (self.TF_excitatory(
            E, I, Fe_ext + self.external_input_ex_ex,
            Fi_ext + self.external_input_ex_in, W_e) - E) / self.T
        # Inhibitory firing rate derivation
        derivative[1] = (self.TF_inhibitory(
            E, I, Fe_ext + self.external_input_in_ex,
            Fi_ext + self.external_input_in_in, W_i) - I) / self.T
        # Adaptation excitatory
        mu_V, sigma_V, T_V = self.get_fluct_regime_vars(
            E, I, Fe_ext + self.external_input_ex_ex,
            Fi_ext + self.external_input_ex_in, W_e, self.Q_e, self.tau_e,
            self.E_e, self.Q_i, self.tau_i, self.E_i, self.g_L, self.C_m,
            self.E_L_e, self.N_tot, self.p_connect, self.g, self.K_ext_e,
            self.K_ext_i)
        derivative[2] = -W_e / self.tau_w_e + self.b_e * E + self.a_e * (
            mu_V - self.E_L_e) / self.tau_w_e
        # Adaptation inhibitory
        mu_V, sigma_V, T_V = self.get_fluct_regime_vars(
            E, I, Fe_ext + self.external_input_in_ex,
            Fi_ext + self.external_input_in_in, W_i, self.Q_e, self.tau_e,
            self.E_e, self.Q_i, self.tau_i, self.E_i, self.g_L, self.C_m,
            self.E_L_i, self.N_tot, self.p_connect, self.g, self.K_ext_e,
            self.K_ext_i)
        derivative[3] = -W_i / self.tau_w_i + self.b_i * I + self.a_i * (
            mu_V - self.E_L_i) / self.tau_w_i

        return derivative

    def TF_excitatory(self, fe, fi, fe_ext, fi_ext, W):
        """
        transfer function for excitatory population
        :param fe: firing rate of excitatory population
        :param fi: firing rate of inhibitory population
        :param fe_ext: external excitatory input
        :param fi_ext: external inhibitory input
        :param W: level of adaptation
        :return: result of transfer function
        """
        return self.TF(fe, fi, fe_ext, fi_ext, W, self.P_e, self.E_L_e)

    def TF_inhibitory(self, fe, fi, fe_ext, fi_ext, W):
        """
        transfer function for inhibitory population
        :param fe: firing rate of excitatory population
        :param fi: firing rate of inhibitory population
        :param fe_ext: external excitatory input
        :param fi_ext: external inhibitory input
        :param W: level of adaptation
        :return: result of transfer function
        """
        return self.TF(fe, fi, fe_ext, fi_ext, W, self.P_i, self.E_L_i)

    def TF(self, fe, fi, fe_ext, fi_ext, W, P, E_L):
        """
        transfer function for inhibitory population
        Inspired from the next repository :
        https://github.com/yzerlaut/notebook_papers/tree/master/modeling_mesoscopic_dynamics
        :param fe: firing rate of excitatory population
        :param fi: firing rate of inhibitory population
        :param fe_ext: external excitatory input
        :param fi_ext: external inhibitory input
        :param W: level of adaptation
        :param P: Polynome of neurons phenomenological threshold (order 9)
        :param E_L: leak reversal potential
        :return: result of transfer function
        """
        mu_V, sigma_V, T_V = self.get_fluct_regime_vars(
            fe, fi, fe_ext, fi_ext, W, self.Q_e, self.tau_e, self.E_e,
            self.Q_i, self.tau_i, self.E_i, self.g_L, self.C_m, E_L,
            self.N_tot, self.p_connect, self.g, self.K_ext_e, self.K_ext_i)
        V_thre = self.threshold_func(mu_V, sigma_V, T_V * self.g_L / self.C_m,
                                     P[0], P[1], P[2], P[3], P[4], P[5], P[6],
                                     P[7], P[8], P[9])
        V_thre *= 1e3  # the threshold need to be in mv and not in Volt
        f_out = self.estimate_firing_rate(mu_V, sigma_V, T_V, V_thre)
        return f_out

    @staticmethod
    @jit(nopython=True, cache=True)
    def get_fluct_regime_vars(Fe, Fi, Fe_ext, Fi_ext, W, Q_e, tau_e, E_e, Q_i,
                              tau_i, E_i, g_L, C_m, E_L, N_tot, p_connect, g,
                              K_ext_e, K_ext_i):
        """
        Compute the mean characteristic of neurons.
        Inspired from the next repository :
        https://github.com/yzerlaut/notebook_papers/tree/master/modeling_mesoscopic_dynamics
        :param Fe: firing rate of excitatory population
        :param Fi: firing rate of inhibitory population
        :param Fe_ext: external excitatory input
        :param Fi_ext: external inhibitory input
        :param W: level of adaptation
        :param Q_e: excitatory quantal conductance
        :param tau_e: excitatory decay
        :param E_e: excitatory reversal potential
        :param Q_i: inhibitory quantal conductance
        :param tau_i: inhibitory decay
        :param E_i: inhibitory reversal potential
        :param E_L: leakage reversal voltage of neurons
        :param g_L: leak conductance
        :param C_m: membrane capacitance
        :param E_L: leak reversal potential
        :param N_tot: cell number
        :param p_connect: connectivity probability
        :param g: fraction of inhibitory cells
        :return: mean and variance of membrane voltage of neurons and autocorrelation time constant
        """
        # firing rate
        # 1e-6 represent spontaneous release of synaptic neurotransmitter or some intrinsic currents of neurons
        fe = (Fe + 1.0e-6) * (1. - g) * p_connect * N_tot + Fe_ext * K_ext_e
        fi = (Fi + 1.0e-6) * g * p_connect * N_tot + Fi_ext * K_ext_i

        # conductance fluctuation and effective membrane time constant
        mu_Ge, mu_Gi = Q_e * tau_e * fe, Q_i * tau_i * fi  # Eqns 5 from [MV_2018]
        mu_G = g_L + mu_Ge + mu_Gi  # Eqns 6 from [MV_2018]
        T_m = C_m / mu_G  # Eqns 6 from [MV_2018]

        # membrane potential
        mu_V = (mu_Ge * E_e + mu_Gi * E_i + g_L * E_L -
                W) / mu_G  # Eqns 7 from [MV_2018]
        # post-synaptic membrane potential event s around muV
        U_e, U_i = Q_e / mu_G * (E_e - mu_V), Q_i / mu_G * (E_i - mu_V)
        # Standard deviation of the fluctuations
        # Eqns 8 from [MV_2018]
        sigma_V = numpy.sqrt(fe * (U_e * tau_e)**2 / (2. * (tau_e + T_m)) +
                             fi * (U_i * tau_i)**2 / (2. * (tau_i + T_m)))
        # Autocorrelation-time of the fluctuations Eqns 9 from [MV_2018]
        T_V_numerator = (fe * (U_e * tau_e)**2 + fi * (U_i * tau_i)**2)
        T_V_denominator = (fe * (U_e * tau_e)**2 / (tau_e + T_m) + fi *
                           (U_i * tau_i)**2 / (tau_i + T_m))
        # T_V = numpy.divide(T_V_numerator, T_V_denominator, out=numpy.ones_like(T_V_numerator),
        #                    where=T_V_denominator != 0.0) # avoid numerical error but not use with numba
        T_V = T_V_numerator / T_V_denominator
        return mu_V, sigma_V, T_V

    @staticmethod
    @jit(nopython=True, cache=True)
    def threshold_func(muV, sigmaV, TvN, P0, P1, P2, P3, P4, P5, P6, P7, P8,
                       P9):
        """
        The threshold function of the neurons
        :param muV: mean of membrane voltage
        :param sigmaV: variance of membrane voltage
        :param TvN: autocorrelation time constant
        :param P: Fitted coefficients of the transfer functions
        :return: threshold of neurons
        """
        # Normalization factors page 48 after the equation 4 from [ZD_2018]
        muV0, DmuV0 = -60.0, 10.0
        sV0, DsV0 = 4.0, 6.0
        TvN0, DTvN0 = 0.5, 1.
        V = (muV - muV0) / DmuV0
        S = (sigmaV - sV0) / DsV0
        T = (TvN - TvN0) / DTvN0
        # Eqns 11 from [MV_2018]
        return P0 + P1 * V + P2 * S + P3 * T + P4 * V**2 + P5 * S**2 + P6 * T**2 + P7 * V * S + P8 * V * T + P9 * S * T

    @staticmethod
    def estimate_firing_rate(muV, sigmaV, Tv, Vthre):
        """
        The threshold function of the neurons
        :param muV: mean of membrane voltage
        :param sigmaV: variance of membrane voltage
        :param Tv: autocorrelation time constant
        :param Vthre:threshold of neurons
        """
        # Eqns 10 from [MV_2018]
        return sp_spec.erfc(
            (Vthre - muV) / (numpy.sqrt(2) * sigmaV)) / (2 * Tv)
Ejemplo n.º 14
0
class BalloonModel(HasTraits):
    """

    A class for calculating the simulated BOLD signal given a TimeSeries
    object of TVB and returning another TimeSeries object.

    The haemodynamic model parameters based on constants for a 1.5 T scanner.
        
    """

    # NOTE: a potential problem when the input is a TimeSeriesSurface.
    # TODO: add an spatial averaging for TimeSeriesSurface.

    time_series = Attr(
        field_type=time_series.TimeSeries,
        label="Time Series",
        required=True,
        doc="""The timeseries that represents the input neural activity""")
    # it also sets the bold sampling period.
    dt = Float(label=":math:`dt`",
               default=0.002,
               required=True,
               doc="""The integration time step size for the balloon model (s).
        If none is provided, by default, the TimeSeries sample period is used."""
               )

    integrator = Attr(field_type=integrators_module.Integrator,
                      label="Integration scheme",
                      default=integrators_module.HeunDeterministic(),
                      required=True,
                      doc=""" A tvb.simulator.Integrator object which is
        an integration scheme with supporting attributes such as 
        integration step size and noise specification for stochastic 
        methods. It is used to compute the time courses of the balloon model state 
        variables.""")

    bold_model = Attr(
        field_type=str,
        label="Select BOLD model equations",
        choices=("linear", "nonlinear"),
        default="nonlinear",
        doc="""Select the set of equations for the BOLD model.""")

    RBM = Attr(field_type=bool,
               label="Revised BOLD Model",
               default=True,
               required=True,
               doc="""Select classical vs revised BOLD model (CBM or RBM).
        Coefficients  k1, k2 and k3 will be derived accordingly.""")

    neural_input_transformation = Attr(
        field_type=str,
        label="Neural input transformation",
        choices=("none", "abs_diff", "sum"),
        default="none",
        doc=
        """ This represents the operation to perform on the state-variable(s) of
        the model used to generate the input TimeSeries. ``none`` takes the
        first state-variable as neural input; `` abs_diff`` is the absolute
        value of the derivative (first order difference) of the first state variable; 
        ``sum``: sum all the state-variables of the input TimeSeries.""")

    tau_s = Float(label=r":math:`\tau_s`",
                  default=1.54,
                  required=True,
                  doc="""Balloon model parameter. Time of signal decay (s)""")

    tau_f = Float(
        label=r":math:`\tau_f`",
        default=1.44,
        required=True,
        doc=""" Balloon model parameter. Time of flow-dependent elimination or
        feedback regulation (s). The average  time blood take to traverse the
        venous compartment. It is the  ratio of resting blood volume (V0) to
        resting blood flow (F0).""")

    tau_o = Float(label=r":math:`\tau_o`",
                  default=0.98,
                  required=True,
                  doc="""
        Balloon model parameter. Haemodynamic transit time (s). The average
        time blood take to traverse the venous compartment. It is the  ratio
        of resting blood volume (V0) to resting blood flow (F0).""")

    alpha = Float(
        label=r":math:`\tau_f`",
        default=0.32,
        required=True,
        doc=
        """Balloon model parameter. Stiffness parameter. Grubb's exponent.""")

    TE = Float(label=":math:`TE`",
               default=0.04,
               required=True,
               doc="""BOLD parameter. Echo Time""")

    V0 = Float(label=":math:`V_0`",
               default=4.0,
               required=True,
               doc="""BOLD parameter. Resting blood volume fraction.""")

    E0 = Float(label=":math:`E_0`",
               default=0.4,
               required=True,
               doc="""BOLD parameter. Resting oxygen extraction fraction.""")

    epsilon = NArray(
        label=":math:`\epsilon`",
        default=numpy.array([0.5]),
        domain=Range(lo=0.5, hi=2.0, step=0.25),
        required=True,
        doc=
        """ BOLD parameter. Ratio of intra- and extravascular signals. In principle  this
        parameter could be derived from empirical data and spatialized.""")

    nu_0 = Float(
        label=r":math:`\nu_0`",
        default=40.3,
        required=True,
        doc=
        """BOLD parameter. Frequency offset at the outer surface of magnetized vessels (Hz)."""
    )

    r_0 = Float(
        label=":math:`r_0`",
        default=25.,
        required=True,
        doc=
        """ BOLD parameter. Slope r0 of intravascular relaxation rate (Hz). Only used for
        ``revised`` coefficients. """)

    def evaluate(self):
        """
        Calculate simulated BOLD signal
        """
        cls_attr_name = self.__class__.__name__ + ".time_series"
        # self.time_series.trait["data"].log_debug(owner=cls_attr_name)

        # NOTE: Just using the first state variable, although in the Bold monitor
        #      input is the sum over the state-variables. Only time-series
        #      from basic monitors should be used as inputs.

        neural_activity, t_int = self.input_transformation(
            self.time_series, self.neural_input_transformation)
        input_shape = neural_activity.shape
        result_shape = self.result_shape(input_shape)
        self.log.debug("Result shape will be: %s" % str(result_shape))

        if self.dt is None:
            self.dt = self.time_series.sample_period / 1000.  # (s) integration time step
            msg = "Integration time step size for the balloon model is %s seconds" % str(
                self.dt)
            self.log.debug(msg)

        # NOTE: Avoid upsampling ...
        if self.dt < (self.time_series.sample_period / 1000.):
            msg = "Integration time step shouldn't be smaller than the sampling period of the input signal."
            self.log.error(msg)

        balloon_nvar = 4

        # NOTE: hard coded initial conditions
        state = numpy.zeros((input_shape[0], balloon_nvar, input_shape[2],
                             input_shape[3]))  # s
        state[0, 1, :] = 1.  # f
        state[0, 2, :] = 1.  # v
        state[0, 3, :] = 1.  # q

        # BOLD model coefficients
        k = self.compute_derived_parameters()
        k1, k2, k3 = k[0], k[1], k[2]

        # prepare integrator
        self.integrator.dt = self.dt
        self.integrator.configure()
        self.log.debug("Integration time step size will be: %s seconds" %
                       str(self.integrator.dt))

        scheme = self.integrator.scheme

        # NOTE: the following variables are not used in this integration but
        # required due to the way integrators scheme has been defined.

        local_coupling = 0.0
        stimulus = 0.0

        # Do some checks:
        if numpy.isnan(neural_activity).any():
            self.log.warning("NaNs detected in the neural activity!!")

        # normalise the time-series.
        neural_activity = neural_activity - neural_activity.mean(
            axis=0)[numpy.newaxis, :]

        # solve equations
        for step in range(1, t_int.shape[0]):
            state[step, :] = scheme(state[step - 1, :], self.balloon_dfun,
                                    neural_activity[step, :], local_coupling,
                                    stimulus)
            if numpy.isnan(state[step, :]).any():
                self.log.warning("NaNs detected...")

        # NOTE: just for the sake of clarity, define the variables used in the BOLD model
        s = state[:, 0, :]
        f = state[:, 1, :]
        v = state[:, 2, :]
        q = state[:, 3, :]

        # import pdb; pdb.set_trace()

        # BOLD models
        if self.bold_model == "nonlinear":
            """
            Non-linear BOLD model equations.
            Page 391. Eq. (13) top in [Stephan2007]_
            """
            y_bold = numpy.array(self.V0 * (k1 * (1. - q) + k2 *
                                            (1. - q / v) + k3 * (1. - v)))
            y_b = y_bold[:, numpy.newaxis, :, :]
            self.log.debug("Max value: %s" % str(y_b.max()))

        else:
            """
            Linear BOLD model equations.
            Page 391. Eq. (13) bottom in [Stephan2007]_ 
            """
            y_bold = numpy.array(self.V0 * ((k1 + k2) * (1. - q) + (k3 - k2) *
                                            (1. - v)))
            y_b = y_bold[:, numpy.newaxis, :, :]

        sample_period = 1. / self.dt

        bold_signal = time_series.TimeSeriesRegion(data=y_b,
                                                   time=t_int,
                                                   sample_period=sample_period,
                                                   sample_period_unit='s')

        return bold_signal

    def compute_derived_parameters(self):
        """
        Compute derived parameters :math:`k_1`, :math:`k_2` and :math:`k_3`.
        """

        if not self.RBM:
            """
            Classical BOLD Model Coefficients [Obata2004]_
            Page 389 in [Stephan2007]_, Eq. (3)
            """
            k1 = 7. * self.E0
            k2 = 2. * self.E0
            k3 = 1. - self.epsilon
        else:
            """
            Revised BOLD Model Coefficients.
            Generalized BOLD signal model.
            Page 400 in [Stephan2007]_, Eq. (12)
            """
            k1 = 4.3 * self.nu_0 * self.E0 * self.TE
            k2 = self.epsilon * self.r_0 * self.E0 * self.TE
            k3 = 1 - self.epsilon

        return numpy.array([k1, k2, k3])

    def input_transformation(self, time_series, mode):
        """
        Perform an operation on the input time-series.
        """

        self.log.debug("Computing: %s on the input time series" % str(mode))

        if mode == "none":
            ts = time_series.data[:, 0, :, :]
            ts = ts[:, numpy.newaxis, :, :]
            t_int = time_series.time / 1000.  # (s)

        elif mode == "abs_diff":
            ts = abs(numpy.diff(time_series.data, axis=0))
            t_int = (time_series.time[1:] -
                     time_series.time[0:-1]) / 1000.  # (s)

        elif mode == "sum":
            ts = numpy.sum(time_series.data, axis=1)
            ts = ts[:, numpy.newaxis, :, :]
            t_int = time_series.time / 1000.  # (s)

        else:
            self.log.error(
                "Bad operation/transformation mode, must be one of:")
            self.log.error("('abs_diff', 'sum', 'none')")
            raise Exception("Bad transformation mode")

        return ts, t_int

    def balloon_dfun(self, state_variables, neural_input, local_coupling=0.0):
        r"""
        The Balloon model equations. See Eqs. (4-10) in [Stephan2007]_
        .. math::
                \frac{ds}{dt} &= x - \kappa\,s - \gamma \,(f-1) \\
                \frac{df}{dt} &= s \\
                \frac{dv}{dt} &= \frac{1}{\tau_o} \, (f - v^{1/\alpha})\\
                \frac{dq}{dt} &= \frac{1}{\tau_o}(f \, \frac{1-(1-E_0)^{1/\alpha}}{E_0} - v^{&/\alpha} \frac{q}{v})\\
                \kappa &= \frac{1}{\tau_s}\\
                \gamma &= \frac{1}{\tau_f}
        """

        s = state_variables[0, :]
        f = state_variables[1, :]
        v = state_variables[2, :]
        q = state_variables[3, :]

        x = neural_input[0, :]

        ds = x - (1. / self.tau_s) * s - (1. / self.tau_f) * (f - 1)
        df = s
        dv = (1. / self.tau_o) * (f - v**(1. / self.alpha))
        dq = (1. / self.tau_o) * ((f * (1. -
                                        (1. - self.E0)**(1. / f)) / self.E0) -
                                  (v**(1. / self.alpha)) * (q / v))

        return numpy.array([ds, df, dv, dq])

    def result_shape(self, input_shape):
        """Returns the shape of the main result of fmri balloon ..."""
        result_shape = (input_shape[0], input_shape[1], input_shape[2],
                        input_shape[3])
        return result_shape

    def result_size(self, input_shape):
        """
        Returns the storage size in Bytes of the main result of .
        """
        result_size = numpy.sum(
            list(map(numpy.prod,
                     self.result_shape(input_shape)))) * 8.0  # Bytes
        return result_size

    def extended_result_size(self, input_shape):
        """
        Returns the storage size in Bytes of the extended result of the ....
        That is, it includes storage of the evaluated ... attributes
        such as ..., etc.
        """
        extend_size = self.result_size(
            input_shape)  # Currently no derived attributes.
        return extend_size
Ejemplo n.º 15
0
class ReducedWongWangExcInh(ModelNumbaDfun):
    r"""
    .. [WW_2006] Kong-Fatt Wong and Xiao-Jing Wang,  *A Recurrent Network
                Mechanism of Time Integration in Perceptual Decisions*.
                Journal of Neuroscience 26(4), 1314-1328, 2006.

    .. [DPA_2014] Deco Gustavo, Ponce Alvarez Adrian, Patric Hagmann,
                  Gian Luca Romani, Dante Mantini, and Maurizio Corbetta. *How Local
                  Excitation–Inhibition Ratio Impacts the Whole Brain Dynamics*.
                  The Journal of Neuroscience 34(23), 7886 –7898, 2014.


    Equations taken from [DPA_2013]_ , page 11242

    .. math::
                 x_{ek}       &=   w_p\,J_N \, S_{ek} - J_iS_{ik} + W_eI_o + GJ_N \mathbf\Gamma(S_{ek}, S_{ej}, u_{kj}) \\
                 H(x_{ek})    &=  \dfrac{a_ex_{ek}- b_e}{1 - \exp(-d_e(a_ex_{ek} -b_e))} \\
                 \dot{S}_{ek} &= -\dfrac{S_{ek}}{\tau_e} + (1 - S_{ek}) \, {\gamma}H(x_{ek}) \\

                 x_{ik}       &=   J_N \, S_{ek} - S_{ik} + W_iI_o + {\lambda}GJ_N \mathbf\Gamma(S_{ik}, S_{ej}, u_{kj}) \\
                 H(x_{ik})    &=  \dfrac{a_ix_{ik} - b_i}{1 - \exp(-d_i(a_ix_{ik} -b_i))} \\
                 \dot{S}_{ik} &= -\dfrac{S_{ik}}{\tau_i} + \gamma_iH(x_{ik}) \

    """

    # Define traited attributes for this model, these represent possible kwargs.

    a_e = NArray(
        label=":math:`a_e`",
        default=numpy.array([
            310.,
        ]),
        domain=Range(lo=0., hi=500., step=1.),
        doc=
        "[n/C]. Excitatory population input gain parameter, chosen to fit numerical solutions."
    )

    b_e = NArray(
        label=":math:`b_e`",
        default=numpy.array([
            125.,
        ]),
        domain=Range(lo=0., hi=200., step=1.),
        doc=
        "[Hz]. Excitatory population input shift parameter chosen to fit numerical solutions."
    )

    d_e = NArray(
        label=":math:`d_e`",
        default=numpy.array([
            0.160,
        ]),
        domain=Range(lo=0.0, hi=0.2, step=0.001),
        doc=
        """[s]. Excitatory population input scaling parameter chosen to fit numerical solutions."""
    )

    gamma_e = NArray(label=r":math:`\gamma_e`",
                     default=numpy.array([
                         0.641 / 1000,
                     ]),
                     domain=Range(lo=0.0, hi=1.0 / 1000, step=0.01 / 1000),
                     doc="""Excitatory population kinetic parameter""")

    tau_e = NArray(
        label=r":math:`\tau_e`",
        default=numpy.array([
            100.,
        ]),
        domain=Range(lo=50., hi=150., step=1.),
        doc="""[ms]. Excitatory population NMDA decay time constant.""")

    w_p = NArray(label=r":math:`w_p`",
                 default=numpy.array([
                     1.4,
                 ]),
                 domain=Range(lo=0.0, hi=2.0, step=0.01),
                 doc="""Excitatory population recurrence weight""")

    J_N = NArray(label=r":math:`J_N`",
                 default=numpy.array([
                     0.15,
                 ]),
                 domain=Range(lo=0.001, hi=0.5, step=0.001),
                 doc="""[nA] NMDA current""")

    W_e = NArray(label=r":math:`W_e`",
                 default=numpy.array([
                     1.0,
                 ]),
                 domain=Range(lo=0.0, hi=2.0, step=0.01),
                 doc="""Excitatory population external input scaling weight""")

    a_i = NArray(
        label=":math:`a_i`",
        default=numpy.array([
            615.,
        ]),
        domain=Range(lo=0., hi=1000., step=1.),
        doc=
        "[n/C]. Inhibitory population input gain parameter, chosen to fit numerical solutions."
    )

    b_i = NArray(
        label=":math:`b_i`",
        default=numpy.array([
            177.0,
        ]),
        domain=Range(lo=0.0, hi=200.0, step=1.0),
        doc=
        "[Hz]. Inhibitory population input shift parameter chosen to fit numerical solutions."
    )

    d_i = NArray(
        label=":math:`d_i`",
        default=numpy.array([
            0.087,
        ]),
        domain=Range(lo=0.0, hi=0.2, step=0.001),
        doc=
        """[s]. Inhibitory population input scaling parameter chosen to fit numerical solutions."""
    )

    gamma_i = NArray(label=r":math:`\gamma_i`",
                     default=numpy.array([
                         1.0 / 1000,
                     ]),
                     domain=Range(lo=0.0, hi=2.0 / 1000, step=0.01 / 1000),
                     doc="""Inhibitory population kinetic parameter""")

    tau_i = NArray(
        label=r":math:`\tau_i`",
        default=numpy.array([
            10.,
        ]),
        domain=Range(lo=5., hi=100., step=1.0),
        doc="""[ms]. Inhibitory population NMDA decay time constant.""")

    J_i = NArray(label=r":math:`J_{i}`",
                 default=numpy.array([
                     1.0,
                 ]),
                 domain=Range(lo=0.001, hi=2.0, step=0.001),
                 doc="""[nA] Local inhibitory current""")

    W_i = NArray(label=r":math:`W_i`",
                 default=numpy.array([
                     0.7,
                 ]),
                 domain=Range(lo=0.0, hi=1.0, step=0.01),
                 doc="""Inhibitory population external input scaling weight""")

    I_o = NArray(label=":math:`I_{o}`",
                 default=numpy.array([
                     0.382,
                 ]),
                 domain=Range(lo=0.0, hi=1.0, step=0.001),
                 doc="""[nA]. Effective external input""")

    I_ext = NArray(label=":math:`I_{ext}`",
                   default=numpy.array([
                       0.0,
                   ]),
                   domain=Range(lo=0.0, hi=1.0, step=0.001),
                   doc="""[nA]. Effective external stimulus input""")

    G = NArray(label=":math:`G`",
               default=numpy.array([
                   2.0,
               ]),
               domain=Range(lo=0.0, hi=10.0, step=0.01),
               doc="""Global coupling scaling""")

    lamda = NArray(label=":math:`\lambda`",
                   default=numpy.array([
                       0.0,
                   ]),
                   domain=Range(lo=0.0, hi=1.0, step=0.01),
                   doc="""Inhibitory global coupling scaling""")

    state_variable_range = Final(default={
        "S_e": numpy.array([0.0, 1.0]),
        "S_i": numpy.array([0.0, 1.0])
    },
                                 label="State variable ranges [lo, hi]",
                                 doc="Population firing rate")

    # Used for phase-plane axis ranges and to bound random initial() conditions.
    state_variable_boundaries = Final(
        label="State Variable boundaries [lo, hi]",
        default={
            "S_e": numpy.array([0.0, 1.0]),
            "S_i": numpy.array([0.0, 1.0])
        },
        doc="""The values for each state-variable should be set to encompass
            the boundaries of the dynamic range of that state-variable. Set None for one-sided boundaries"""
    )

    variables_of_interest = List(
        of=str,
        label="Variables watched by Monitors",
        choices=('S_e', 'S_i'),
        default=('S_e', 'S_i'),
        doc="""default state variables to be monitored""")

    state_variables = ['S_e', 'S_i']
    _nvar = 2
    cvar = numpy.array([0], dtype=numpy.int32)

    def configure(self):
        """  """
        super(ReducedWongWangExcInh, self).configure()
        self.update_derived_parameters()

    def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0):
        S = state_variables[:, :]

        c_0 = coupling[0, :]

        # if applicable
        lc_0 = local_coupling * S[0]

        coupling = self.G * self.J_N * (c_0 + lc_0)

        J_N_S_e = self.J_N * S[0]

        x_e = self.w_p * J_N_S_e - self.J_i * S[
            1] + self.W_e * self.I_o + coupling + self.I_ext

        x_e = self.a_e * x_e - self.b_e
        H_e = x_e / (1 - numpy.exp(-self.d_e * x_e))

        dS_e = -(S[0] / self.tau_e) + (1 - S[0]) * H_e * self.gamma_e

        x_i = J_N_S_e - S[1] + self.W_i * self.I_o + self.lamda * coupling

        x_i = self.a_i * x_i - self.b_i
        H_i = x_i / (1 - numpy.exp(-self.d_i * x_i))

        dS_i = -(S[1] / self.tau_i) + H_i * self.gamma_i

        derivative = numpy.array([dS_e, dS_i])

        return derivative

    def dfun(self, x, c, local_coupling=0.0, **kwargs):
        r"""
        Equations taken from [DPA_2013]_ , page 11242

        .. math::
                 x_{ek}       &=   w_p\,J_N \, S_{ek} - J_iS_{ik} + W_eI_o + GJ_N \mathbf\Gamma(S_{ek}, S_{ej}, u_{kj}) \\
                 H(x_{ek})    &=  \dfrac{a_ex_{ek}- b_e}{1 - \exp(-d_e(a_ex_{ek} -b_e))} \\
                 \dot{S}_{ek} &= -\dfrac{S_{ek}}{\tau_e} + (1 - S_{ek}){\gamma}H(x_{ek}) \\

                 x_{ik}       &=   J_N \, S_{ek} - S_{ik} + W_iI_o + {\lambda}GJ_N \mathbf\Gamma(S_{ik}, S_{ej}, u_{kj}) \\
                 H(x_{ik})    &=  \dfrac{a_ix_{ik} - b_i}{1 - \exp(-d_i(a_ix_{ik} -b_i))} \\
                 \dot{S}_{ik} &= -\dfrac{S_{ik}}{\tau_i} + \gamma_iH(x_{ik}) \

        """
        x_ = x.reshape(x.shape[:-1]).T
        c_ = c.reshape(c.shape[:-1]).T + local_coupling * x[0]
        deriv = _numba_dfun(x_, c_, self.a_e, self.b_e, self.d_e, self.gamma_e,
                            self.tau_e, self.w_p, self.W_e, self.J_N, self.a_i,
                            self.b_i, self.d_i, self.gamma_i, self.tau_i,
                            self.W_i, self.J_i, self.G, self.lamda, self.I_o,
                            self.I_ext)
        return deriv.T[..., numpy.newaxis]
Ejemplo n.º 16
0
class ContinuousWaveletTransform(HasTraits):
    """
    A class for calculating the wavelet transform of a TimeSeries object of TVB
    and returning a WaveletSpectrum object. The sampling period and frequency
    range of the result can be specified. The mother wavelet can also be 
    specified... (So far, only Morlet.)
    
    References:
        .. [TBetal_1996] C. Tallon-Baudry et al, *Stimulus Specificity of 
            Phase-Locked and Non-Phase-Locked 40 Hz Visual Responses in Human.*,
            J Neurosci 16(13):4240-4249, 1996. 
        
        .. [Mallat_1999] S. Mallat, *A wavelet tour of signal processing.*,
            book, Academic Press, 1999.
        
    """
    
    time_series = Attr(
        field_type=time_series.TimeSeries,
        label="Time Series",
        doc="""The timeseries to which the wavelet is to be applied.""")
    
    mother = Attr(
        field_type=str,
        label="Wavelet function",
        default="morlet",
        doc="""The mother wavelet function used in the transform. Default is
            'morlet', possibilities are: 'morlet'...""")
    
    sample_period = Float(
        label="Sample period of result (ms)",
        default=7.8125, #7.8125 => 128 Hz
        doc="""The sampling period of the computed wavelet spectrum. NOTE:
            This should be an integral multiple of the of the sampling period 
            of the source time series, otherwise the actual resulting sample
            period will be the first correct value below that requested.""")

    frequencies = Attr(
        field_type=Range,
        label="Frequency range of result (kHz).",
        default=Range(lo=0.008, hi=0.060, step=0.002),
        doc="""The frequency resolution and range returned. Requested
            frequencies are converted internally into appropriate scales.""")
    
    normalisation = Attr(
        field_type=str,
        label="Normalisation",
        default="energy",
        doc="""The type of normalisation for the resulting wavet spectrum.
            Default is 'energy', options are: 'energy'; 'gabor'.""")
    
    q_ratio = Float(
        label="Q-ratio",
        default=5.0,
        doc="""NFC. Must be greater than 5. Ratios of the center frequencies to bandwidths.""")
    
    
    
    def evaluate(self):
        """
        Calculate the continuous wavelet transform of time_series.
        """
        ts_shape = self.time_series.data.shape
        
        if self.frequencies.step == 0:
            self.log.warning("Frequency step can't be 0! Trying default step, 2e-3.")
            self.frequencies.step = 0.002
        
        freqs = numpy.arange(self.frequencies.lo, self.frequencies.hi,
                             self.frequencies.step)
        
        if (freqs.size == 0) or any(freqs <= 0.0):
            # TODO: Maybe should limit number of freqs... ~100 is probably a reasonable upper bound.
            self.log.warning("Invalid frequency range! Falling back to default.")
            self.log.debug("freqs")
            self.log.debug(narray_describe(freqs))
            self.frequencies = Range(lo=0.008, hi=0.060, step=0.002)
            freqs = numpy.arange(self.frequencies.lo, self.frequencies.hi,
                                 self.frequencies.step)

        self.log.debug("freqs")
        self.log.debug(narray_describe(freqs))

        sample_rate = self.time_series.sample_rate
        
        # Duke: code below is as given by Andreas Spiegler, I've just wrapped 
        # some of the original argument names
        nf = len(freqs)
        temporal_step = max((1, iround(self.sample_period / self.time_series.sample_period)))
        nt = int(numpy.ceil(ts_shape[0] /  temporal_step))
        
        if not isinstance(self.q_ratio, numpy.ndarray):
            q_ratio = self.q_ratio * numpy.ones((1, nf))
        
        if numpy.nanmin(q_ratio) < 5:
            msg = "q_ratio must be not lower than 5 !"
            self.log.error(msg)
            raise Exception(msg)
        
        if numpy.nanmax(freqs) > sample_rate / 2.0:
            msg = "Sampling rate is too low for the requested frequency range !"
            self.log.error(msg)
            raise Exception(msg)
        
        # TODO: This isn't used, but min frequency seems like it should be important... Check with A.S.
        #  fmin = 3.0 * numpy.nanmin(q_ratio) * sample_rate / numpy.pi / nt
        sigma_f = freqs / q_ratio
        sigma_t = 1.0 / (2.0 * numpy.pi * sigma_f)
        
        if self.normalisation == 'energy':
            Amp = 1.0 / numpy.sqrt(sample_rate * numpy.sqrt(numpy.pi) * sigma_t)
        elif self.normalisation == 'gabor': 
            Amp = numpy.sqrt(2.0 / numpy.pi) / sample_rate / sigma_t
        
        coef_shape = (nf, nt, ts_shape[1], ts_shape[2], ts_shape[3])
        
        coef = numpy.zeros(coef_shape, dtype = numpy.complex128)
        self.log.debug("coef")
        self.log.debug(narray_describe(coef))

        scales = numpy.arange(0, nf, 1)
        for i in scales:
            f0 = freqs[i]
            SDt = sigma_t[(0, i)]
            A = Amp[(0, i)]
            x = numpy.arange(0, 4.0 * SDt * sample_rate, 1) / sample_rate
            wvlt = A * numpy.exp(-x**2 / (2.0 * SDt**2) ) * numpy.exp(2j * numpy.pi * f0 * x )
            wvlt = numpy.hstack((numpy.conjugate(wvlt[-1:0:-1]), wvlt))
            #util.self.log_debug_array(self.log, wvlt, "wvlt")
            
            for var in range(ts_shape[1]):
                for node in range(ts_shape[2]):
                    for mode in range(ts_shape[3]):
                        data = self.time_series.data[:, var, node, mode]
                        wt = signal.convolve(data, wvlt, 'same')
                        #util.self.log_debug_array(self.log, wt, "wt")
                        res = wt[0::temporal_step]
                        # NOTE: this is a horrible horrible quick hack (alas, a solution) to avoid broadcasting errors
                        # when using dt and sample periods which are not powers of 2.
                        coef[i, :, var, node, mode] = res if len(res) == nt else res[:coef.shape[1]] 
                        

        self.log.debug("coef")
        self.log.debug(narray_describe(coef))

        spectra = spectral.WaveletCoefficients(
            source=self.time_series,
            mother=self.mother,
            sample_period=self.sample_period,
            frequencies=self.frequencies.to_array(),
            normalisation=self.normalisation,
            q_ratio=self.q_ratio,
            array_data=coef)
        
        return spectra


    def result_shape(self, input_shape, input_sample_period):
        """
        Returns the shape of the main result (complex array) of the continuous
        wavelet transform.
        """
        freq_len = int((self.frequencies.hi - self.frequencies.lo) / self.frequencies.step)
        temporal_step = max((1, self.sample_period / input_sample_period))
        nt = int(round(input_shape[0] /  temporal_step))
        result_shape = (freq_len, nt, ) + input_shape[1:]
        return result_shape
    
    
    def result_size(self, input_shape, input_sample_period):
        """
        Returns the storage size in Bytes of the main result (complex array) of
        the continuous wavelet transform.
        """
        result_size = numpy.prod(self.result_shape(input_shape, input_sample_period)) * 2.0 * 8.0 #complex*Bytes
        return result_size
    
    
    def extended_result_size(self, input_shape, input_sample_period):
        """
        Returns the storage size in Bytes of the extended result of the 
        continuous wavelet transform.  That is, it includes storage of the
        evaluated WaveletCoefficients attributes such as power, phase, 
        amplitude, etc.
        """
        result_shape = self.result_shape(input_shape, input_sample_period)
        result_size = self.result_size(input_shape, input_sample_period)
        extend_size = result_size #Main array
        extend_size = extend_size + 0.5 * result_size #Amplitude
        extend_size = extend_size + 0.5 * result_size #Phase
        extend_size = extend_size + 0.5 * result_size #Power
        extend_size = extend_size + result_shape[0] * 8.0 #Frequency
        return extend_size
Ejemplo n.º 17
0
class LinearReducedWongWangExcIO(ReducedWongWangExcIO):

    d = NArray(label=":math:`d`",
               default=numpy.array([
                   0.2,
               ]),
               domain=Range(lo=0.0, hi=0.200, step=0.001),
               doc="""[s]. Parameter chosen to fit numerical solutions.""")

    non_integrated_variables = ["R", "Rin", "I"]

    def update_state_variables_before_integration(self,
                                                  state_variables,
                                                  coupling,
                                                  local_coupling=0.0,
                                                  stimulus=0.0):
        if self.use_numba:
            state_variables = \
                _numba_update_non_state_variables(state_variables.reshape(state_variables.shape[:-1]).T,
                                                  coupling.reshape(coupling.shape[:-1]).T +
                                                  local_coupling * state_variables[0],
                                                  self.a, self.b, self.d,
                                                  self.w, self.J_N, self.Rin,
                                                  self.G, self.I_o)
            return state_variables.T[..., numpy.newaxis]

        # In this case, rates (H_e, H_i) are non-state variables,
        # i.e., they form part of state_variables but have no dynamics assigned on them
        # Most of the computations of this dfun aim at computing rates, including coupling considerations.
        # Therefore, we compute and update them only once a new state is computed,
        # and we consider them constant for any subsequent possible call to this function,
        # by any integration scheme

        S = state_variables[0, :]  # synaptic gating dynamics
        Rint = state_variables[1, :]  # Rates from Spiking Network, integrated
        Rin = state_variables[3, :]  # Input rates from Spiking Network

        c_0 = coupling[0, :]

        # if applicable
        lc_0 = local_coupling * S[0]

        coupling = self.G * self.J_N * (c_0 + lc_0)

        # Currents
        I = self.w * self.J_N * S + self.I_o + coupling
        x = self.a * I - self.b

        # Rates
        # Only rates with _Rin <= 0 0 will be updated by TVB.
        # The rest, are updated from the Spiking Network
        R = numpy.where(self._Rin, Rint, x / (1 - numpy.exp(-self.d * x)))

        Rin = numpy.where(
            self._Rin, Rin,
            0.0)  # Reset to 0 the Rin for nodes not updated by Spiking Network

        # We now update the state_variable vector with the new rates and currents:
        state_variables[2, :] = R
        state_variables[3, :] = Rin
        state_variables[4, :] = I

        # Keep them here so that they are not recomputed in the dfun
        self._R = numpy.copy(R)
        self._Rin = numpy.copy(Rin)

        return state_variables

    def _numpy_dfun(self, integration_variables, R, Rin):
        r"""
        Equations taken from [DPA_2013]_ , page 11242

        .. math::
                  x_k       &=   w\,J_N \, S_k + I_o + J_N \mathbf\Gamma(S_k, S_j, u_{kj}),\\
                 H(x_k)    &=  \dfrac{ax_k - b}{1 - \exp(-d(ax_k -b))},\\
                 \dot{S}_k &= -\dfrac{S_k}{\tau_s} + (1 - S_k) \, H(x_k) \, \gamma

        """

        S = integration_variables[0, :]  # Synaptic gating dynamics
        Rint = integration_variables[
            1, :]  # Rates from Spiking Network, integrated

        # Synaptic gating dynamics
        dS = -(S / self.tau_s) + R * self.gamma

        # Rates
        # Low pass filtering, linear dynamics for rates updated from the spiking network
        # No dynamics in the case of TVB rates
        dRint = numpy.where(self._Rin_mask, (-Rint + Rin) / self.tau_rin, 0.0)

        return numpy.array([dS, dRint])

    def dfun(self, x, c, local_coupling=0.0):
        if self._R is None or self._Rin is None:
            state_variables = self._integration_to_state_variables(x)
            state_variables = \
                self.update_state_variables_before_integration(state_variables, c, local_coupling,
                                                               self._stimulus)
            R = state_variables[2]  # Rates
            Rin = state_variables[3]  # input instant spiking rates
        else:
            R = self._R
            Rin = self._Rin
        if self.use_numba:
            deriv = _numba_dfun(
                x.reshape(x.shape[:-1]).T, R, Rin, self.gamma, self.tau_s,
                self.Rin, self.tau_rin).T[..., numpy.newaxis]
        else:
            deriv = self._numpy_dfun(x, R, Rin)
        #  Set them to None so that they are recomputed on subsequent steps
        #  for multistep integration schemes such as Runge-Kutta:
        self._R = None
        self._Rin = None
        return deriv
Ejemplo n.º 18
0
class WilsonCowan(Model):
    r"""
    **References**:

    .. [WC_1972] Wilson, H.R. and Cowan, J.D. *Excitatory and inhibitory
        interactions in localized populations of model neurons*, Biophysical
        journal, 12: 1-24, 1972.
    .. [WC_1973] Wilson, H.R. and Cowan, J.D  *A Mathematical Theory of the
        Functional Dynamics of Cortical and Thalamic Nervous Tissue*

    .. [D_2011] Daffertshofer, A. and van Wijk, B. *On the influence of
        amplitude on the connectivity between phases*
        Frontiers in Neuroinformatics, July, 2011

    Used Eqns 11 and 12 from [WC_1972]_ in ``dfun``.  P and Q represent external
    inputs, which when exploring the phase portrait of the local model are set
    to constant values. However in the case of a full network, P and Q are the
    entry point to our long range and local couplings, that is, the  activity
    from all other nodes is the external input to the local population.

    The default parameters are taken from figure 4 of [WC_1972]_, pag. 10

    In [WC_1973]_ they present a model of neural tissue on the pial surface is.
    See Fig. 1 in page 58. The following local couplings (lateral interactions)
    occur given a region i and a region j:

      E_i-> E_j
      E_i-> I_j
      I_i-> I_j
      I_i-> E_j


    +---------------------------+
    |          Table 1          |
    +--------------+------------+
    |                           |
    |  SanzLeonetAl,   2014     |
    +--------------+------------+
    |Parameter     |  Value     |
    +==============+============+
    | k_e, k_i     |    1.00    |
    +--------------+------------+
    | r_e, r_i     |    0.00    |
    +--------------+------------+
    | tau_e, tau_i |    10.0    |
    +--------------+------------+
    | c_1          |    10.0    |
    +--------------+------------+
    | c_2          |    6.0     |
    +--------------+------------+
    | c_3          |    1.0     |
    +--------------+------------+
    | c_4          |    1.0     |
    +--------------+------------+
    | a_e, a_i     |    1.0     |
    +--------------+------------+
    | b_e, b_i     |    0.0     |
    +--------------+------------+
    | theta_e      |    2.0     |
    +--------------+------------+
    | theta_i      |    3.5     |
    +--------------+------------+
    | alpha_e      |    1.2     |
    +--------------+------------+
    | alpha_i      |    2.0     |
    +--------------+------------+
    | P            |    0.5     |
    +--------------+------------+
    | Q            |    0       |
    +--------------+------------+
    | c_e, c_i     |    1.0     |
    +--------------+------------+
    | alpha_e      |    1.2     |
    +--------------+------------+
    | alpha_i      |    2.0     |
    +--------------+------------+
    |                           |
    |  frequency peak at 20  Hz |
    |                           |
    +---------------------------+


    The parameters in Table 1 reproduce Figure A1 in  [D_2011]_
    but set the limit cycle frequency to a sensible value (eg, 20Hz).

    Model bifurcation parameters:
        * :math:`c_1`
        * :math:`P`



    The models (:math:`E`, :math:`I`) phase-plane, including a representation of
    the vector field as well as its nullclines, using default parameters, can be
    seen below:

        .. _phase-plane-WC:
        .. figure :: img/WilsonCowan_01_mode_0_pplane.svg
            :alt: Wilson-Cowan phase plane (E, I)

            The (:math:`E`, :math:`I`) phase-plane for the Wilson-Cowan model.


    The general formulation for the \textit{\textbf{Wilson-Cowan}} model as a
    dynamical unit at a node $k$ in a BNM with $l$ nodes reads:

    .. math::
            \dot{E}_k &= \dfrac{1}{\tau_e} (-E_k  + (k_e - r_e E_k) \mathcal{S}_e (\alpha_e \left( c_{ee} E_k - c_{ei} I_k  + P_k - \theta_e + \mathbf{\Gamma}(E_k, E_j, u_{kj}) + W_{\zeta}\cdot E_j + W_{\zeta}\cdot I_j\right) ))\\
            \dot{I}_k &= \dfrac{1}{\tau_i} (-I_k  + (k_i - r_i I_k) \mathcal{S}_i (\alpha_i \left( c_{ie} E_k - c_{ee} I_k  + Q_k - \theta_i + \mathbf{\Gamma}(E_k, E_j, u_{kj}) + W_{\zeta}\cdot E_j + W_{\zeta}\cdot I_j\right) ))

    """

    # Define traited attributes for this model, these represent possible kwargs.
    c_ee = NArray(label=":math:`c_{ee}`",
                  default=numpy.array([12.0]),
                  domain=Range(lo=11.0, hi=16.0, step=0.01),
                  doc="""Excitatory to excitatory  coupling coefficient""")

    c_ei = NArray(label=":math:`c_{ei}`",
                  default=numpy.array([4.0]),
                  domain=Range(lo=2.0, hi=15.0, step=0.01),
                  doc="""Inhibitory to excitatory coupling coefficient""")

    c_ie = NArray(label=":math:`c_{ie}`",
                  default=numpy.array([13.0]),
                  domain=Range(lo=2.0, hi=22.0, step=0.01),
                  doc="""Excitatory to inhibitory coupling coefficient.""")

    c_ii = NArray(label=":math:`c_{ii}`",
                  default=numpy.array([11.0]),
                  domain=Range(lo=2.0, hi=15.0, step=0.01),
                  doc="""Inhibitory to inhibitory coupling coefficient.""")

    tau_e = NArray(
        label=r":math:`\tau_e`",
        default=numpy.array([10.0]),
        domain=Range(lo=0.0, hi=150.0, step=0.01),
        doc="""Excitatory population, membrane time-constant [ms]""")

    tau_i = NArray(
        label=r":math:`\tau_i`",
        default=numpy.array([10.0]),
        domain=Range(lo=0.0, hi=150.0, step=0.01),
        doc="""Inhibitory population, membrane time-constant [ms]""")

    a_e = NArray(
        label=":math:`a_e`",
        default=numpy.array([1.2]),
        domain=Range(lo=0.0, hi=1.4, step=0.01),
        doc="""The slope parameter for the excitatory response function""")

    b_e = NArray(
        label=":math:`b_e`",
        default=numpy.array([2.8]),
        domain=Range(lo=1.4, hi=6.0, step=0.01),
        doc=
        """Position of the maximum slope of the excitatory sigmoid function""")

    c_e = NArray(
        label=":math:`c_e`",
        default=numpy.array([1.0]),
        domain=Range(lo=1.0, hi=20.0, step=1.0),
        doc="""The amplitude parameter for the excitatory response function""")

    theta_e = NArray(label=r":math:`\theta_e`",
                     default=numpy.array([0.0]),
                     domain=Range(lo=0.0, hi=60., step=0.01),
                     doc="""Excitatory threshold""")

    a_i = NArray(
        label=":math:`a_i`",
        default=numpy.array([1.0]),
        domain=Range(lo=0.0, hi=2.0, step=0.01),
        doc="""The slope parameter for the inhibitory response function""")

    b_i = NArray(label=r":math:`b_i`",
                 default=numpy.array([4.0]),
                 domain=Range(lo=2.0, hi=6.0, step=0.01),
                 doc="""Position of the maximum slope of a sigmoid function [in
        threshold units]""")

    theta_i = NArray(label=r":math:`\theta_i`",
                     default=numpy.array([0.0]),
                     domain=Range(lo=0.0, hi=60.0, step=0.01),
                     doc="""Inhibitory threshold""")

    c_i = NArray(
        label=":math:`c_i`",
        default=numpy.array([1.0]),
        domain=Range(lo=1.0, hi=20.0, step=1.0),
        doc="""The amplitude parameter for the inhibitory response function""")

    r_e = NArray(label=":math:`r_e`",
                 default=numpy.array([1.0]),
                 domain=Range(lo=0.5, hi=2.0, step=0.01),
                 doc="""Excitatory refractory period""")

    r_i = NArray(label=":math:`r_i`",
                 default=numpy.array([1.0]),
                 domain=Range(lo=0.5, hi=2.0, step=0.01),
                 doc="""Inhibitory refractory period""")

    k_e = NArray(label=":math:`k_e`",
                 default=numpy.array([1.0]),
                 domain=Range(lo=0.5, hi=2.0, step=0.01),
                 doc="""Maximum value of the excitatory response function""")

    k_i = NArray(label=":math:`k_i`",
                 default=numpy.array([1.0]),
                 domain=Range(lo=0.0, hi=2.0, step=0.01),
                 doc="""Maximum value of the inhibitory response function""")

    P = NArray(label=":math:`P`",
               default=numpy.array([0.0]),
               domain=Range(lo=0.0, hi=20.0, step=0.01),
               doc="""External stimulus to the excitatory population.
        Constant intensity.Entry point for coupling.""")

    Q = NArray(label=":math:`Q`",
               default=numpy.array([0.0]),
               domain=Range(lo=0.0, hi=20.0, step=0.01),
               doc="""External stimulus to the inhibitory population.
        Constant intensity.Entry point for coupling.""")

    alpha_e = NArray(label=r":math:`\alpha_e`",
                     default=numpy.array([1.0]),
                     domain=Range(lo=0.0, hi=20.0, step=0.01),
                     doc="""External stimulus to the excitatory population.
        Constant intensity.Entry point for coupling.""")

    alpha_i = NArray(label=r":math:`\alpha_i`",
                     default=numpy.array([1.0]),
                     domain=Range(lo=0.0, hi=20.0, step=0.01),
                     doc="""External stimulus to the inhibitory population.
        Constant intensity.Entry point for coupling.""")

    # Used for phase-plane axis ranges and to bound random initial() conditions.
    state_variable_range = Final(
        label="State Variable ranges [lo, hi]",
        default={
            "E": numpy.array([0.0, 1.0]),
            "I": numpy.array([0.0, 1.0])
        },
        doc="""The values for each state-variable should be set to encompass
        the expected dynamic range of that state-variable for the current
        parameters, it is used as a mechanism for bounding random inital
        conditions when the simulation isn't started from an explicit history,
        it is also provides the default range of phase-plane plots.""")

    variables_of_interest = List(
        of=str,
        label="Variables watched by Monitors",
        choices=("E", "I", "E + I", "E - I"),
        default=("E", ),
        doc="""This represents the default state-variables of this Model to be
               monitored. It can be overridden for each Monitor if desired. The
               corresponding state-variable indices for this model are :math:`E = 0`
               and :math:`I = 1`.""")

    state_variables = 'E I'.split()
    _nvar = 2
    cvar = numpy.array([0, 1], dtype=numpy.int32)

    def dfun(self, state_variables, coupling, local_coupling=0.0):
        r"""

        .. math::
            \tau \dot{x}(t) &= -z(t) + \phi(z(t)) \\
            \phi(x) &= \frac{c}{1-exp(-a (x-b))}

        """

        E = state_variables[0, :]
        I = state_variables[1, :]
        derivative = numpy.empty_like(state_variables)

        # long-range coupling
        c_0 = coupling[0, :]

        # short-range (local) coupling
        lc_0 = local_coupling * E
        lc_1 = local_coupling * I

        x_e = self.alpha_e * (self.c_ee * E - self.c_ei * I + self.P -
                              self.theta_e + c_0 + lc_0 + lc_1)
        x_i = self.alpha_i * (self.c_ie * E - self.c_ii * I + self.Q -
                              self.theta_i + lc_0 + lc_1)

        s_e = self.c_e / (1.0 + numpy.exp(-self.a_e * (x_e - self.b_e)))
        s_i = self.c_i / (1.0 + numpy.exp(-self.a_i * (x_i - self.b_i)))

        derivative[0] = (-E + (self.k_e - self.r_e * E) * s_e) / self.tau_e
        derivative[1] = (-I + (self.k_i - self.r_i * I) * s_i) / self.tau_i

        return derivative
Ejemplo n.º 19
0
class MorrisLecar(models.Model):
    """
    The Morris-Lecar model is a mathematically simple excitation model having
    two nonlinear, non-inactivating conductances.

    .. [ML_1981] Morris, C. and Lecar, H. *Voltage oscillations in the Barnacle
        giant muscle fibre*, Biophysical Journal 35: 193, 1981.

    See also, http://www.scholarpedia.org/article/Morris-Lecar_model
    
        .. figure :: img/MorrisLecar_01_mode_0_pplane.svg
            :alt: Morris-Lecar phase plane (V, N)
            
            The (:math:`V`, :math:`N`) phase-plane for the Morris-Lecar model.

    .. automethod:: MorrisLecar.dfun

    """

    # Define traited attributes for this model, these represent possible kwargs.
    gCa = NArray(
        label=":math:`g_{Ca}`",
        default=numpy.array([4.0]),
        domain=Range(lo=2.0, hi=6.0, step=0.01),
        doc="""Conductance of population of Ca++ channels [mmho/cm2]""")

    gK = NArray(label=":math:`g_K`",
                default=numpy.array([8.0]),
                domain=Range(lo=4.0, hi=12.0, step=0.01),
                doc="""Conductance of population of K+ channels [mmho/cm2]""")

    gL = NArray(
        label=":math:`g_L`",
        default=numpy.array([2.0]),
        domain=Range(lo=1.0, hi=3.0, step=0.01),
        doc="""Conductance of population of leak channels [mmho/cm2]""")

    C = NArray(label=":math:`C`",
               default=numpy.array([20.0]),
               domain=Range(lo=10.0, hi=30.0, step=0.01),
               doc="""Membrane capacitance [uF/cm2]""")

    lambda_Nbar = NArray(label=":math:`\\lambda_{Nbar}`",
                         default=numpy.array([0.06666667]),
                         domain=Range(lo=0.0, hi=1.0, step=0.00000001),
                         doc="""Maximum rate for K+ channel opening [1/s]""")

    V1 = NArray(
        label=":math:`V_1`",
        default=numpy.array([10.0]),
        domain=Range(lo=5.0, hi=15.0, step=0.01),
        doc="""Potential at which half of the Ca++ channels are open at steady
        state [mV]""")

    V2 = NArray(
        label=":math:`V_2`",
        default=numpy.array([15.0]),
        domain=Range(lo=7.5, hi=22.5, step=0.01),
        doc="""1/slope of voltage dependence of the fraction of Ca++ channels
        that are open at steady state [mV].""")

    V3 = NArray(
        label=":math:`V_3`",
        default=numpy.array([-1.0]),
        domain=Range(lo=-1.5, hi=-0.5, step=0.01),
        doc="""Potential at which half of the K+ channels are open at steady
        state [mV].""")

    V4 = NArray(
        label=":math:`V_4`",
        default=numpy.array([14.5]),
        domain=Range(lo=7.25, hi=22.0, step=0.01),
        doc="""1/slope of voltage dependence of the fraction of K+ channels
        that are open at steady state [mV].""")

    VCa = NArray(label=":math:`V_{Ca}`",
                 default=numpy.array([100.0]),
                 domain=Range(lo=50.0, hi=150.0, step=0.01),
                 doc="""Ca++ Nernst potential [mV]""")

    VK = NArray(label=":math:`V_K`",
                default=numpy.array([-70.0]),
                domain=Range(lo=-105.0, hi=-35.0, step=0.01),
                doc="""K+ Nernst potential [mV]""")

    VL = NArray(label=":math:`V_L`",
                default=numpy.array([-50.0]),
                domain=Range(lo=-75.0, hi=-25.0, step=0.01),
                doc="""Nernst potential leak channels [mV]""")

    # Used for phase-plane axis ranges and to bound random initial() conditions.
    state_variable_range = Final(
        {
            "V": numpy.array([-70.0, 50.0]),
            "N": numpy.array([-0.2, 0.8])
        },
        label="State Variable ranges [lo, hi]",
        doc="""The values for each state-variable should be set to encompass
        the expected dynamic range of that state-variable for the current 
        parameters, it is used as a mechanism for bounding random inital 
        conditions when the simulation isn't started from an explicit history,
        it is also provides the default range of phase-plane plots.""")

    variables_of_interest = NArray(
        dtype=numpy.int,
        label="Variables watched by Monitors",
        domain=Range(lo=0, hi=2, step=1),
        default=numpy.array([0], dtype=numpy.int32),
        doc="""This represents the default state-variables of this Model to be
        monitored. It can be overridden for each Monitor if desired. The 
        corresponding state-variable indices for this model are :math:`V = 0`,
        and :math:`N = 1`.""")

    #    coupling_variables = arrays.IntegerArray(
    #        label = "Variables to couple activity through",
    #        default = numpy.array([0], dtype=numpy.int32))

    #    nsig = arrays.FloatArray(
    #        label = "Noise dispersion",
    #        default = numpy.array([0.0]),
    #        range = basic.Range(lo = 0.0, hi = 1.0))

    def __init__(self, **kwargs):
        """
        Initialize the MorrisLecar model's traited attributes, any provided
        as keywords will overide their traited default.
        
        """
        LOG.info('%s: initing...' % str(self))
        super(MorrisLecar, self).__init__(**kwargs)

        self._state_variables = ["V", "N"]
        self._nvar = 2

        self.cvar = numpy.array([0], dtype=numpy.int32)

        LOG.debug('%s: inited.' % repr(self))

    def dfun(self, state_variables, coupling, local_coupling=0.0):
        """
        The dynamics of the membrane potential :math:`V` rely on the fraction
        of Ca++ channels :math:`M` and K+ channels :math:`N` open at a given
        time. In order to have a planar model, we make the simplifying
        assumption (following [ML_1981]_, Equation 9) that Ca++ system is much
        faster than K+ system so that :math:`M = M_{\\infty}` at all times:
        
        .. math::
             C \\, \\dot{V} &= I - g_{L}(V - V_L) - g_{Ca} \\, M_{\\infty}(V)
                              (V - V_{Ca}) - g_{K} \\, N \\, (V - V_{K}) \\\\
             \\dot{N} &= \\lambda_{N}(V) \\, (N_{\\infty}(V) - N) \\\\
             M_{\\infty}(V) &= 1/2 \\, (1 + \\tanh((V - V_{1})/V_{2}))\\\\
             N_{\\infty}(V) &= 1/2 \\, (1 + \\tanh((V - V_{3})/V_{4}))\\\\
             \\lambda_{N}(V) &= \\overline{\\lambda_{N}}
                                \\cosh((V - V_{3})/2V_{4})
        
        where external currents :math:`I` provide the entry point for local and
        long-range connectivity. Default parameters are set as per Figure 9 of
        [ML_1981]_ so that the model shows oscillatory behaviour as :math:`I` is
        varied.
        """

        V = state_variables[0, :]
        N = state_variables[1, :]

        c_0 = coupling[0, :]

        M_inf = 0.5 * (1 + numpy.tanh((V - self.V1) / self.V2))
        N_inf = 0.5 * (1 + numpy.tanh((V - self.V3) / self.V4))
        lambda_N = self.lambda_Nbar * numpy.cosh(
            (V - self.V3) / (2.0 * self.V4))

        dV = (1.0 / self.C) * (c_0 + (local_coupling * V) - self.gL *
                               (V - self.VL) - self.gCa * M_inf *
                               (V - self.VCa) - self.gK * N * (V - self.VK))

        dN = lambda_N * (N_inf - N)

        derivative = numpy.array([dV, dN])

        return derivative
Ejemplo n.º 20
0
class Generic2dOscillator(models.Model):
    """
    The Generic2dOscillator model is ...
    
    .. [FH_1961] FitzHugh, R., *Impulses and physiological states in theoretical
        models of nerve membrane*, Biophysical Journal 1: 445, 1961. 
    
    .. [Nagumo_1962] Nagumo et.al, *An Active Pulse Transmission Line Simulating
        Nerve Axon*, Proceedings of the IRE 50: 2061, 1962.
    
    See also, http://www.scholarpedia.org/article/FitzHugh-Nagumo_model
    
    The models (:math:`V`, :math:`W`) phase-plane, including a representation of
    the vector field as well as its nullclines, using default parameters, can be
    seen below:
        
        .. _phase-plane-FHN:
        .. figure :: img/Generic2dOscillator_01_mode_0_pplane.svg
            :alt: Fitzhugh-Nagumo phase plane (V, W)
            
            The (:math:`V`, :math:`W`) phase-plane for the Fitzhugh-Nagumo 
            model.
            
    .. #Currently there seems to be a clash betwen traits and autodoc, autodoc
    .. #can't find the methods of the class, the class specific names below get
    .. #us around this...
    .. automethod:: Generic2dOscillator.__init__
    .. automethod:: Generic2dOscillator.dfun
    
    """

    # Define traited attributes for this model, these represent possible kwargs.
    tau = NArray(
        label=":math:`\\tau`",
        default=numpy.array([1.25]),
        domain=Range(lo=0.01, hi=5.0, step=0.01),
        doc="""A time-scale separation between the fast, :math:`V`, and slow,
            :math:`W`, state-variables of the model.""")

    a = NArray(
        label=":math:`a`",
        default=numpy.array([1.05]),
        domain=Range(lo=-1.0, hi=1.5, step=0.01),
        doc="""ratio a/b gives W-nullcline slope""")

    b = NArray(
        label=":math:`b`",
        default=numpy.array([0.2]),
        domain=Range(lo=0.0, hi=1.0, step=0.01),
        doc="""dimensionless parameter""")

    omega = NArray(
        label=":math:`\\omega`",
        default=numpy.array([1.0]),
        domain=Range(lo=0.0, hi=1.0, step=0.01),
        doc="""dimensionless parameter""")

    upsilon = NArray(
        label=":math:`\\upsilon`",
        default=numpy.array([1.0]),
        domain=Range(lo=0.0, hi=1.0, step=0.01),
        doc="""dimensionless parameter""")

    gamma = NArray(
        label=":math:`\\gamma`",
        default=numpy.array([1.0]),
        domain=Range(lo=0.0, hi=1.0, step=0.01),
        doc="""dimensionless parameter""")

    eta = NArray(
        label=":math:`\\eta`",
        default=numpy.array([1.0]),
        domain=Range(lo=0.0, hi=1.0, step=0.01),
        doc="""ratio :math:`\\eta/b` gives W-nullcline intersect(V=0)""")

    variables_of_interest = List(
        of=str,
        label="Variables watched by Monitors.",
        choices=("V", "W"),
        default="V",
        doc="""This represents the default state-variables of this Model to be
        monitored. It can be overridden for each Monitor if desired.""")

    # Informational attribute, used for phase-plane and initial()
    state_variable_range = Final(
        {
            "V": numpy.array([-2.0, 4.0]),
            "W": numpy.array([-6.0, 6.0])
        },
        label="State Variable ranges [lo, hi]",
        doc="""The values for each state-variable should be set to encompass
            the expected dynamic range of that state-variable for the current 
            parameters, it is used as a mechanism for bounding random inital 
            conditions when the simulation isn't started from an explicit
            history, it is also provides the default range of phase-plane plots.""")

    state_variables = ["V", "W"]
    _nvar = 2
    cvar = numpy.array([0], dtype=numpy.int32)

    def dfun(self, state_variables, coupling, local_coupling=0.0):
        """
        The fast, :math:`V`, and slow, :math:`W`, state variables are typically
        considered to represent a membrane potential and recovery variable,
        respectively. Based on equations 1 and 2 of [FH_1961]_, but relabelling
        c as :math:`\\tau` due to its interpretation as a time-scale separation,
        and adding parameters :math:`\\upsilon`, :math:`\\omega`, :math:`\\eta`,
        and :math:`\\gamma`, for flexibility, here we implement:
            
            .. math::
                \\dot{V} &= \\tau (\\omega \\, W + \\upsilon \\, V - 
                                   \\gamma \\,  \\frac{V^3}{3} + I) \\\\
                \\dot{W} &= -(\\eta \\, V - a + b \\, W) / \\tau
                
        where external currents :math:`I` provide the entry point for local and
        long-range connectivity.
        
        For strict consistency with [FH_1961]_, parameters :math:`\\upsilon`, 
        :math:`\\omega`, :math:`\\eta`, and :math:`\\gamma` should be set to 
        1.0, with :math:`a`, :math:`b`, and :math:`\\tau` set in the range 
        defined by equation 3 of [FH_1961]_:
            
            .. math::
                0 \\le b \\le 1 \\\\
                1 - 2 b / 3 \\le a \\le 1 \\\\
                \\tau^2 \\ge b
            
        The default state of these equations can be seen in the
        :ref:`Fitzhugh-Nagumo phase-plane <phase-plane-FHN>`.
        
        """

        V = state_variables[0, :]
        W = state_variables[1, :]

        # [State_variables, nodes]
        c_0 = coupling[0, :]

        dV = self.tau * (self.omega * W + self.upsilon * V -
                         self.gamma * V**3.0 / 3.0 +
                         c_0 + local_coupling * V)

        dW = (self.a - self.eta * V - self.b * W) / self.tau
        derivative = numpy.array([dV, dW])

        return derivative
Ejemplo n.º 21
0
class ReducedSetFitzHughNagumo(ReducedSetBase):
    r"""
    A reduced representation of a set of Fitz-Hugh Nagumo oscillators,
    [SJ_2008]_.

    The models (:math:`\xi`, :math:`\eta`) phase-plane, including a
    representation of the vector field as well as its nullclines, using default
    parameters, can be seen below:

        .. _phase-plane-rFHN_0:
        .. figure :: img/ReducedSetFitzHughNagumo_01_mode_0_pplane.svg
            :alt: Reduced set of FitzHughNagumo phase plane (xi, eta), 1st mode.

            The (:math:`\xi`, :math:`\eta`) phase-plane for the first mode of
            a reduced set of Fitz-Hugh Nagumo oscillators.

        .. _phase-plane-rFHN_1:
        .. figure :: img/ReducedSetFitzHughNagumo_01_mode_1_pplane.svg
            :alt: Reduced set of FitzHughNagumo phase plane (xi, eta), 2nd mode.

            The (:math:`\xi`, :math:`\eta`) phase-plane for the second mode of
            a reduced set of Fitz-Hugh Nagumo oscillators.

        .. _phase-plane-rFHN_2:
        .. figure :: img/ReducedSetFitzHughNagumo_01_mode_2_pplane.svg
            :alt: Reduced set of FitzHughNagumo phase plane (xi, eta), 3rd mode.

            The (:math:`\xi`, :math:`\eta`) phase-plane for the third mode of
            a reduced set of Fitz-Hugh Nagumo oscillators.


    The system's equations for the i-th mode at node q are:

    .. math::
                \dot{\xi}_{i}    &=  c\left(\xi_i-e_i\frac{\xi_{i}^3}{3} -\eta_{i}\right)
                                  + K_{11}\left[\sum_{k=1}^{o} A_{ik}\xi_k-\xi_i\right]
                                  - K_{12}\left[\sum_{k =1}^{o} B_{i k}\alpha_k-\xi_i\right] + cIE_i \\
                                 &\, + \left[\sum_{k=1}^{o} \mathbf{\Gamma}(\xi_{kq}, \xi_{kr}, u_{qr})\right]
                                  +  \left[\sum_{k=1}^{o} W_{\zeta}\cdot\xi_{kr} \right] \\
                \dot{\eta}_i     &= \frac{1}{c}\left(\xi_i-b\eta_i+m_i\right) \\
                & \\
                \dot{\alpha}_i   &= c\left(\alpha_i-f_i\frac{\alpha_i^3}{3}-\beta_i\right)
                                  + K_{21}\left[\sum_{k=1}^{o} C_{ik}\xi_i-\alpha_i\right] + cII_i \\
                                 & \, + \left[\sum_{k=1}^{o} \mathbf{\Gamma}(\xi_{kq}, \xi_{kr}, u_{qr})\right]
                                  + \left[\sum_{k=1}^{o} W_{\zeta}\cdot\xi_{kr}\right] \\
                                 & \\
                \dot{\beta}_i    &= \frac{1}{c}\left(\alpha_i-b\beta_i+n_i\right)

    .. automethod:: ReducedSetFitzHughNagumo.update_derived_parameters

    #NOTE: In the Article this modelis called StefanescuJirsa2D

    """

    # Define traited attributes for this model, these represent possible kwargs.
    tau = NArray(
        label=r":math:`\tau`",
        default=numpy.array([3.0]),
        domain=Range(lo=1.5, hi=4.5, step=0.01),
        doc="""doc...(prob something about timescale seperation)""")

    a = NArray(
        label=":math:`a`",
        default=numpy.array([0.45]),
        domain=Range(lo=0.0, hi=1.0, step=0.01),
        doc="""doc...""")

    b = NArray(
        label=":math:`b`",
        default=numpy.array([0.9]),
        domain=Range(lo=0.0, hi=1.0, step=0.01),
        doc="""doc...""")

    K11 = NArray(
        label=":math:`K_{11}`",
        default=numpy.array([0.5]),
        domain=Range(lo=0.0, hi=1.0, step=0.01),
        doc="""Internal coupling, excitatory to excitatory""")

    K12 = NArray(
        label=":math:`K_{12}`",
        default=numpy.array([0.15]),
        domain=Range(lo=0.0, hi=1.0, step=0.01),
        doc="""Internal coupling, inhibitory to excitatory""")

    K21 = NArray(
        label=":math:`K_{21}`",
        default=numpy.array([0.15]),
        domain=Range(lo=0.0, hi=1.0, step=0.01),
        doc="""Internal coupling, excitatory to inhibitory""")

    sigma = NArray(
        label=r":math:`\sigma`",
        default=numpy.array([0.35]),
        domain=Range(lo=0.0, hi=1.0, step=0.01),
        doc="""Standard deviation of Gaussian distribution""")

    mu = NArray(
        label=r":math:`\mu`",
        default=numpy.array([0.0]),
        domain=Range(lo=0.0, hi=1.0, step=0.01),
        doc="""Mean of Gaussian distribution""")

    # Used for phase-plane axis ranges and to bound random initial() conditions.
    state_variable_range = Final(
        label="State Variable ranges [lo, hi]",
        default={"xi": numpy.array([-4.0, 4.0]),
                 "eta": numpy.array([-3.0, 3.0]),
                 "alpha": numpy.array([-4.0, 4.0]),
                 "beta": numpy.array([-3.0, 3.0])},
        doc="""The values for each state-variable should be set to encompass
        the expected dynamic range of that state-variable for the current
        parameters, it is used as a mechanism for bounding random inital
        conditions when the simulation isn't started from an explicit history,
        it is also provides the default range of phase-plane plots.""")


    variables_of_interest = List(
        of=str,
        label="Variables watched by Monitors",
        choices=("xi", "eta", "alpha", "beta"),
        default=("xi", "alpha"),
        doc=r"""This represents the default state-variables of this Model to be
                monitored. It can be overridden for each Monitor if desired. The
                corresponding state-variable indices for this model are :math:`\xi = 0`,
                :math:`\eta = 1`, :math:`\alpha = 2`, and :math:`\beta= 3`.""")

    state_variables = tuple('xi eta alpha beta'.split())
    _nvar = 4
    cvar = numpy.array([0, 2], dtype=numpy.int32)
    # Derived parameters
    Aik = None
    Bik = None
    Cik = None
    e_i = None
    f_i = None
    IE_i = None
    II_i = None
    m_i = None
    n_i = None

    def dfun(self, state_variables, coupling, local_coupling=0.0):
        r"""


        The system's equations for the i-th mode at node q are:

        .. math::
                \dot{\xi}_{i}    &=  c\left(\xi_i-e_i\frac{\xi_{i}^3}{3} -\eta_{i}\right)
                                  + K_{11}\left[\sum_{k=1}^{o} A_{ik}\xi_k-\xi_i\right]
                                  - K_{12}\left[\sum_{k =1}^{o} B_{i k}\alpha_k-\xi_i\right] + cIE_i                       \\
                                 &\, + \left[\sum_{k=1}^{o} \mathbf{\Gamma}(\xi_{kq}, \xi_{kr}, u_{qr})\right]
                                  +  \left[\sum_{k=1}^{o} W_{\zeta}\cdot\xi_{kr} \right] \\
                \dot{\eta}_i     &= \frac{1}{c}\left(\xi_i-b\eta_i+m_i\right)                                              \\
                & \\
                \dot{\alpha}_i   &= c\left(\alpha_i-f_i\frac{\alpha_i^3}{3}-\beta_i\right)
                                  + K_{21}\left[\sum_{k=1}^{o} C_{ik}\xi_i-\alpha_i\right] + cII_i                          \\
                                 & \, + \left[\sum_{k=1}^{o} \mathbf{\Gamma}(\xi_{kq}, \xi_{kr}, u_{qr})\right]
                                  + \left[\sum_{k=1}^{o} W_{\zeta}\cdot\xi_{kr}\right] \\
                                 & \\
                \dot{\beta}_i    &= \frac{1}{c}\left(\alpha_i-b\beta_i+n_i\right)

        """

        xi = state_variables[0, :]
        eta = state_variables[1, :]
        alpha = state_variables[2, :]
        beta = state_variables[3, :]
        derivative = numpy.empty_like(state_variables)
        # sum the activity from the modes
        c_0 = coupling[0, :].sum(axis=1)[:, numpy.newaxis]

        # TODO: generalize coupling variables to a matrix form
        # c_1 = coupling[1, :] # this cv represents alpha

        derivative[0] = (self.tau * (xi - self.e_i * xi ** 3 / 3.0 - eta) +
               self.K11 * (numpy.dot(xi, self.Aik) - xi) -
               self.K12 * (numpy.dot(alpha, self.Bik) - xi) +
               self.tau * (self.IE_i + c_0 + local_coupling * xi))

        derivative[1] = (xi - self.b * eta + self.m_i) / self.tau

        derivative[2] = (self.tau * (alpha - self.f_i * alpha ** 3 / 3.0 - beta) +
                  self.K21 * (numpy.dot(xi, self.Cik) - alpha) +
                  self.tau * (self.II_i + c_0 + local_coupling * xi))

        derivative[3] = (alpha - self.b * beta + self.n_i) / self.tau

        return derivative

    def update_derived_parameters(self):
        """
        Calculate coefficients for the Reduced FitzHugh-Nagumo oscillator based
        neural field model. Specifically, this method implements equations for
        calculating coefficients found in the supplemental material of
        [SJ_2008]_.

        Include equations here...

        """

        newaxis = numpy.newaxis
        trapz = scipy_integrate_trapz

        stepu = 1.0 / (self.nu + 2 - 1)
        stepv = 1.0 / (self.nv + 2 - 1)

        norm = scipy_stats_norm(loc=self.mu, scale=self.sigma)

        Zu = norm.ppf(numpy.arange(stepu, 1.0, stepu))
        Zv = norm.ppf(numpy.arange(stepv, 1.0, stepv))

        # Define the modes
        V = numpy.zeros((self.number_of_modes, self.nv))
        U = numpy.zeros((self.number_of_modes, self.nu))

        nv_per_mode = self.nv // self.number_of_modes
        nu_per_mode = self.nu // self.number_of_modes

        for i in range(self.number_of_modes):
            V[i, i * nv_per_mode:(i + 1) * nv_per_mode] = numpy.ones(nv_per_mode)
            U[i, i * nu_per_mode:(i + 1) * nu_per_mode] = numpy.ones(nu_per_mode)

        # Normalise the modes
        V = V / numpy.tile(numpy.sqrt(trapz(V * V, Zv, axis=1)), (self.nv, 1)).T
        U = U / numpy.tile(numpy.sqrt(trapz(U * U, Zu, axis=1)), (self.nv, 1)).T

        # Get Normal PDF's evaluated with sampling Zv and Zu
        g1 = norm.pdf(Zv)
        g2 = norm.pdf(Zu)
        G1 = numpy.tile(g1, (self.number_of_modes, 1))
        G2 = numpy.tile(g2, (self.number_of_modes, 1))

        cV = numpy.conj(V)
        cU = numpy.conj(U)

        intcVdZ = trapz(cV, Zv, axis=1)[:, newaxis]
        intG1VdZ = trapz(G1 * V, Zv, axis=1)[newaxis, :]
        intcUdZ = trapz(cU, Zu, axis=1)[:, newaxis]
        # import pdb; pdb.set_trace()
        # Calculate coefficients
        self.Aik = numpy.dot(intcVdZ, intG1VdZ).T
        self.Bik = numpy.dot(intcVdZ, trapz(G2 * U, Zu, axis=1)[newaxis, :])
        self.Cik = numpy.dot(intcUdZ, intG1VdZ).T

        self.e_i = trapz(cV * V ** 3, Zv, axis=1)[newaxis, :]
        self.f_i = trapz(cU * U ** 3, Zu, axis=1)[newaxis, :]

        self.IE_i = trapz(Zv * cV, Zv, axis=1)[newaxis, :]
        self.II_i = trapz(Zu * cU, Zu, axis=1)[newaxis, :]

        self.m_i = (self.a * intcVdZ).T
        self.n_i = (self.a * intcUdZ).T
Ejemplo n.º 22
0
class Generic2dOscillator(TVBGeneric2dOscillator):
    r"""
    The Generic2dOscillator model is a generic dynamic system with two state
    variables. The dynamic equations of this model are composed of two ordinary
    differential equations comprising two nullclines. The first nullcline is a
    cubic function as it is found in most neuron and population builders; the
    second nullcline is arbitrarily configurable as a polynomial function up to
    second order. The manipulation of the latter nullcline's parameters allows
    to generate a wide range of different behaviours.

    Equations:

    .. math::
                \dot{V} &= d \, \tau (-f V^3 + e V^2 + g V + \alpha W + \gamma I), \\
                \dot{W} &= \dfrac{d}{\tau}\,\,(c V^2 + b V - \beta W + a),

    See:


        .. [FH_1961] FitzHugh, R., *Impulses and physiological states in theoretical
            builders of nerve membrane*, Biophysical Journal 1: 445, 1961.

        .. [Nagumo_1962] Nagumo et.al, *An Active Pulse Transmission Line Simulating
            Nerve Axon*, Proceedings of the IRE 50: 2061, 1962.

        .. [SJ_2011] Stefanescu, R., Jirsa, V.K. *Reduced representations of
            heterogeneous mixed neural networks with synaptic coupling*.
            Physical Review E, 83, 2011.

        .. [SJ_2010]	Jirsa VK, Stefanescu R.  *Neural population modes capture
            biologically realistic large-scale network dynamics*. Bulletin of
            Mathematical Biology, 2010.

        .. [SJ_2008_a] Stefanescu, R., Jirsa, V.K. *A low dimensional description
            of globally coupled heterogeneous neural networks of excitatory and
            inhibitory neurons*. PLoS Computational Biology, 4(11), 2008).


    The model's (:math:`V`, :math:`W`) time series and phase-plane its nullclines
    can be seen in the figure below.

    The model with its default parameters exhibits FitzHugh-Nagumo like dynamics.

    +---------------------------+
    |  Table 1                  |
    +--------------+------------+
    |  EXCITABLE CONFIGURATION  |
    +--------------+------------+
    |Parameter     |  Value     |
    +==============+============+
    | a            |     -2.0   |
    +--------------+------------+
    | b            |    -10.0   |
    +--------------+------------+
    | c            |      0.0   |
    +--------------+------------+
    | d            |      0.02  |
    +--------------+------------+
    | I            |      0.0   |
    +--------------+------------+
    |  limit cycle if a is 2.0  |
    +---------------------------+


    +---------------------------+
    |   Table 2                 |
    +--------------+------------+
    |   BISTABLE CONFIGURATION  |
    +--------------+------------+
    |Parameter     |  Value     |
    +==============+============+
    | a            |      1.0   |
    +--------------+------------+
    | b            |      0.0   |
    +--------------+------------+
    | c            |     -5.0   |
    +--------------+------------+
    | d            |      0.02  |
    +--------------+------------+
    | I            |      0.0   |
    +--------------+------------+
    | monostable regime:        |
    | fixed point if Iext=-2.0  |
    | limit cycle if Iext=-1.0  |
    +---------------------------+


    +---------------------------+
    |  Table 3                  |
    +--------------+------------+
    |  EXCITABLE CONFIGURATION  |
    +--------------+------------+
    |  (similar to Morris-Lecar)|
    +--------------+------------+
    |Parameter     |  Value     |
    +==============+============+
    | a            |      0.5   |
    +--------------+------------+
    | b            |      0.6   |
    +--------------+------------+
    | c            |     -4.0   |
    +--------------+------------+
    | d            |      0.02  |
    +--------------+------------+
    | I            |      0.0   |
    +--------------+------------+
    | excitable regime if b=0.6 |
    | oscillatory if b=0.4      |
    +---------------------------+


    +---------------------------+
    |  Table 4                  |
    +--------------+------------+
    |  GhoshetAl,  2008         |
    |  KnocketAl,  2009         |
    +--------------+------------+
    |Parameter     |  Value     |
    +==============+============+
    | a            |    1.05    |
    +--------------+------------+
    | b            |   -1.00    |
    +--------------+------------+
    | c            |    0.0     |
    +--------------+------------+
    | d            |    0.1     |
    +--------------+------------+
    | I            |    0.0     |
    +--------------+------------+
    | alpha        |    1.0     |
    +--------------+------------+
    | beta         |    0.2     |
    +--------------+------------+
    | gamma        |    -1.0    |
    +--------------+------------+
    | e            |    0.0     |
    +--------------+------------+
    | g            |    1.0     |
    +--------------+------------+
    | f            |    1/3     |
    +--------------+------------+
    | tau          |    1.25    |
    +--------------+------------+
    |                           |
    |  frequency peak at 10Hz   |
    |                           |
    +---------------------------+


    +---------------------------+
    |  Table 5                  |
    +--------------+------------+
    |  SanzLeonetAl  2013       |
    +--------------+------------+
    |Parameter     |  Value     |
    +==============+============+
    | a            |    - 0.5   |
    +--------------+------------+
    | b            |    -10.0   |
    +--------------+------------+
    | c            |      0.0   |
    +--------------+------------+
    | d            |      0.02  |
    +--------------+------------+
    | I            |      0.0   |
    +--------------+------------+
    |                           |
    |  intrinsic frequency is   |
    |  approx 10 Hz             |
    |                           |
    +---------------------------+

    NOTE: This regime, if I = 2.1, is called subthreshold regime.
    Unstable oscillations appear through a subcritical Hopf bifurcation.


    .. figure :: img/Generic2dOscillator_01_mode_0_pplane.svg
    .. _phase-plane-Generic2D:
        :alt: Phase plane of the generic 2D population model with (V, W)

        The (:math:`V`, :math:`W`) phase-plane for the generic 2D population
        model for default parameters. The dynamical system has an equilibrium
        point.

    .. automethod:: Generic2dOscillator.dfun

    """

    V_m = NArray(
        label=r":math:`V_m`",
        default=numpy.array([0.0]),
        domain=Range(lo=-10.0, hi=10.0, step=0.1),
        doc="""NEST membrane potential input""")

    def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, ev=numexpr.evaluate):
        r"""
        The two state variables :math:`V` and :math:`W` are typically considered
        to represent a function of the neuron's membrane potential, such as the
        firing rate or dendritic currents, and a recovery variable, respectively.
        If there is a time scale hierarchy, then typically :math:`V` is faster
        than :math:`W` corresponding to a value of :math:`\tau` greater than 1.

        The equations of the generic 2D population model read

        .. math::
                \dot{V} &= d \, \tau (-f V^3 + e V^2 + g V + \alpha W + \gamma I), \\
                \dot{W} &= \dfrac{d}{\tau}\,\,(c V^2 + b V - \beta W + a),

        where external currents :math:`I` provide the entry point for local,
        long-range connectivity and stimulation.

        """
        V = state_variables[0, :]
        V = numpy.where(self.V_m == 0, V, self.V_m)
        W = state_variables[1, :]

        #[State_variables, nodes]
        c_0 = coupling[0, :]

        tau = self.tau
        I = self.I
        a = self.a
        b = self.b
        c = self.c
        d = self.d
        e = self.e
        f = self.f
        g = self.g
        beta = self.beta
        alpha = self.alpha
        gamma = self.gamma

        lc_0 = local_coupling * V

        # Pre-allocate the result array then instruct numexpr to use it as output.
        # This avoids an expensive array concatenation
        derivative = numpy.empty_like(state_variables)

        ev('d * tau * (alpha * W - f * V**3 + e * V**2 + g * V + gamma * I + gamma *c_0 + lc_0)', out=derivative[0])
        ev('d * (a + b * V + c * V**2 - beta * W) / tau', out=derivative[1])

        return derivative

    def dfun(self, vw, c, local_coupling=0.0):
        vw[0, :] = numpy.where(self.V_m == 0, vw[0, :].squeeze(), self.V_m)[:, numpy.newaxis]
        lc_0 = local_coupling * vw[0, :, 0]
        vw_ = vw.reshape(vw.shape[:-1]).T
        c_ = c.reshape(c.shape[:-1]).T
        deriv = _numba_dfun_g2d(vw_, c_, self.tau, self.I, self.a, self.b, self.c, self.d, self.e, self.f, self.g,
                                self.beta, self.alpha, self.gamma, lc_0)
        return deriv.T[..., numpy.newaxis]
Ejemplo n.º 23
0
class EpileptorT(ModelNumbaDfun):

    a = NArray(label=":math:`a`", default=numpy.array([1.0]), doc="""""")

    b = NArray(label=":math:`b`", default=numpy.array([3.0]), doc="""""")

    c = NArray(label=":math:`c`", default=numpy.array([1.0]), doc="""""")

    d = NArray(label=":math:`d`", default=numpy.array([5.0]), doc="""""")

    r = NArray(label=":math:`r`",
               default=numpy.array([0.00035]),
               domain=Range(lo=0.0, hi=0.001, step=0.00005),
               doc="""""")

    s = NArray(label=":math:`s`", default=numpy.array([4.0]), doc="""""")

    x0 = NArray(label=":math:`x0`",
                default=numpy.array([-1.6]),
                domain=Range(lo=-3.0, hi=-1.0, step=0.1),
                doc="""""")

    Iext = NArray(label=":math:`Iext`",
                  default=numpy.array([3.1]),
                  domain=Range(lo=1.5, hi=5.0, step=0.1),
                  doc="""""")

    slope = NArray(label=":math:`slope`",
                   default=numpy.array([0.]),
                   domain=Range(lo=-16.0, hi=6.0, step=0.1),
                   doc="""""")

    Iext2 = NArray(label=":math:`Iext2`",
                   default=numpy.array([0.45]),
                   domain=Range(lo=0.0, hi=1.0, step=0.05),
                   doc="""""")

    tau = NArray(label=":math:`tau`", default=numpy.array([10.0]), doc="""""")

    aa = NArray(label=":math:`aa`", default=numpy.array([6.0]), doc="""""")

    bb = NArray(label=":math:`bb`", default=numpy.array([2.0]), doc="""""")

    Kvf = NArray(label=":math:`Kvf`",
                 default=numpy.array([0.0]),
                 domain=Range(lo=0.0, hi=4.0, step=0.5),
                 doc="""""")

    Kf = NArray(label=":math:`Kf`",
                default=numpy.array([0.0]),
                domain=Range(lo=0.0, hi=4.0, step=0.5),
                doc="""""")

    Ks = NArray(label=":math:`Ks`",
                default=numpy.array([0.0]),
                domain=Range(lo=-4.0, hi=4.0, step=0.1),
                doc="""""")

    tt = NArray(label=":math:`tt`",
                default=numpy.array([1.0]),
                domain=Range(lo=0.001, hi=10.0, step=0.001),
                doc="""""")

    modification = NArray(label=":math:`modification`",
                          default=numpy.array([0]),
                          doc="""""")

    state_variable_range = Final(label="State Variable ranges [lo, hi]",
                                 default={
                                     "x1": numpy.array([0.0]),
                                     "y1": numpy.array([0.0]),
                                     "z": numpy.array([0.0]),
                                     "x2": numpy.array([0.0]),
                                     "y2": numpy.array([0.0]),
                                     "g": numpy.array([0.0])
                                 },
                                 doc="""state variables""")

    state_variable_boundaries = Final(
        label="State Variable boundaries [lo, hi]",
        default={
            "x1": numpy.array([-2.0, 1.0]),
            "y1": numpy.array([-20.0, 2.0]),
            "z": numpy.array([-2.0, 5.0]),
            "x2": numpy.array([-2.0, 0.0]),
            "y2": numpy.array([0.0, 2.0]),
            "g": numpy.array([-1.0, 1.0])
        },
    )
    variables_of_interest = List(
        of=str,
        label="Variables or quantities available to Monitors",
        choices=(
            'x1 ** x2',
            'x2',
        ),
        default=(
            'x1',
            'y1',
            'z',
            'x2',
            'y2',
            'g',
        ),
        doc="Variables to monitor")

    state_variables = ['x1', 'y1', 'z', 'x2', 'y2', 'g']

    _nvar = 6
    cvar = numpy.array([
        0,
        1,
        2,
        3,
        4,
        5,
    ], dtype=numpy.int32)

    def dfun(self, vw, c, local_coupling=0.0):
        vw_ = vw.reshape(vw.shape[:-1]).T
        c_ = c.reshape(c.shape[:-1]).T
        deriv = _numba_dfun_EpileptorT(vw_, c_, self.a, self.b, self.c, self.d,
                                       self.r, self.s, self.x0, self.Iext,
                                       self.slope, self.Iext2, self.tau,
                                       self.aa, self.bb, self.Kvf, self.Kf,
                                       self.Ks, self.tt, self.modification,
                                       local_coupling)

        return deriv.T[..., numpy.newaxis]
Ejemplo n.º 24
0
class Larter(models.Model):
    """
    A modified Morris-Lecar model that includes a third equation which simulates
    the effect of a population of inhibitory interneurons synapsing on
    the pyramidal cells.
    
    .. [Larteretal_1999] Larter et.al. *A coupled ordinary differential equation
        lattice model for the simulation of epileptic seizures.* Chaos. 9(3):
        795, 1999.
    
    .. [Breaksetal_2003] M. J. Breakspear et.al. *Modulation of excitatory
        synaptic coupling facilitates synchronization and complex dynamics in a
        biophysical model of neuronal dynamics.* Network: Computation in Neural
        Systems 14: 703-732, 2003.
    
    Equations are taken from [Larteretal_1999]_. Parameter values are taken from
    Table I, page 799.
    
    Regarding the choice of coupling: the three biophysically reasonable
    mechanisms (diffusive; delay line axonal/ synaptic or glial; and
    extracellular current flow) are dependent of K++. This is reflected in the
    potassium equilibrium potential (:math:`V_{K}`). Thus, this variable is
    chosen as the coupling element between nodes.
    
        .. figure :: img/Larter_01_mode_0_pplane.svg
            :alt: Larter phase plane (V, W)
            
            The (:math:`V`, :math:`W`) phase-plane for the Larter model.

    .. automethod:: Larter.dfun
    
    """

    # Define traited attributes for this model, these represent possible kwargs.
    gCa = NArray(label=":math:`g_{Ca}`",
                 default=numpy.array([1.1]),
                 domain=Range(lo=0.9, hi=1.5, step=0.01),
                 doc="""Conductance of population of Ca++ channels""")

    gK = NArray(label=":math:`g_K`",
                default=numpy.array([2.0]),
                domain=Range(lo=1.5, hi=2.5, step=0.01),
                doc="""Conductance of population of K channels""")

    gL = NArray(label=":math:`g_L`",
                default=numpy.array([0.5]),
                domain=Range(lo=0.25, hi=0.75, step=0.01),
                doc="""Conductance of population of leak channels""")

    phi = NArray(label=":math:`\\phi`",
                 default=numpy.array([0.7]),
                 domain=Range(lo=0.35, hi=1.05, step=0.01),
                 doc="""Temperature scaling factor""")

    V1 = NArray(label=":math:`V_1`",
                default=numpy.array([-0.01]),
                domain=Range(lo=-0.1, hi=0.1, step=0.01),
                doc="""Threshold value for :math:`M_{\\infty}`""")

    V2 = NArray(label=":math:`V_2`",
                default=numpy.array([0.15]),
                domain=Range(lo=0.01, hi=1.0, step=0.01),
                doc="""Steepness parameter for :math:`M_{\\infty}`""")

    V3 = NArray(label=":math:`V_3`",
                default=numpy.array([0.0]),
                domain=Range(lo=0.0, hi=1.0, step=0.01),
                doc="""Threshold value for :math:`W_{\\infty}`""")

    V4 = NArray(label=":math:`V_4`",
                default=numpy.array([0.3]),
                domain=Range(lo=0.01, hi=1.0, step=0.01),
                doc="""Steepness parameter for :math:`W_{\\infty}`""")

    V5 = NArray(label=":math:`V_5`",
                default=numpy.array([0.0]),
                domain=Range(lo=0.0, hi=1.0, step=0.01),
                doc="""Threshold value for :math:`a_{exc}`""")

    V6 = NArray(label=":math:`V_6`",
                default=numpy.array([0.6]),
                domain=Range(lo=0.01, hi=1.0, step=0.01),
                doc="""Steepness parameter for a_exc and :math:`a_{inh}`""")

    V7 = NArray(label=":math:`V_7`",
                default=numpy.array([0.0]),
                domain=Range(lo=0.0, hi=1.0, step=0.01),
                doc="""Threshold value for :math:`a_{inh}`""")

    VK = NArray(label=":math:`V_K`",
                default=numpy.array([-0.7]),
                domain=Range(lo=-0.8, hi=1.0, step=0.01),
                doc="""K Nernst potential""")

    VL = NArray(label=":math:`V_L`",
                default=numpy.array([-0.5]),
                domain=Range(lo=-0.75, hi=-0.25, step=0.01),
                doc="""Nernst potential leak channels""")

    tau_K = NArray(label=":math:`\\tau_K`",
                   default=numpy.array([1.0]),
                   domain=Range(lo=0.5, hi=1.5, step=0.01),
                   doc="""Time constant for K relaxation time""")

    a_exc = NArray(label=":math:`a_{exc}`",
                   default=numpy.array([1.0]),
                   domain=Range(lo=0.7, hi=1.3, step=0.01),
                   doc="""strength of excitatory synapse""")

    a_inh = NArray(label=":math:`a_{ie}`",
                   default=numpy.array([1.0]),
                   domain=Range(lo=0.7, hi=1.3, step=0.01),
                   doc="""strength of inhibitory synapse""")

    b = NArray(label=":math:`b`",
               default=numpy.array([0.1]),
               domain=Range(lo=0.05, hi=0.15, step=0.01),
               doc="""Time constant scaling factor""")

    c = NArray(label=":math:`c`",
               default=numpy.array([0.165]),
               domain=Range(lo=0.0, hi=0.2, step=0.01),
               doc="""strength of feedforward inhibition""")

    Iext = NArray(
        label=":math:`I_{ext}`",
        default=numpy.array([0.3]),
        domain=Range(lo=0.15, hi=0.45, step=0.01),
        doc="""Subcortical input strength. It represents a non-specific
       excitation of both the excitatory and inhibitory populations.""")

    # Used for phase-plane axis ranges and to bound random initial() conditions.
    state_variable_range = Final(
        {
            "V": numpy.array([-0.3, 0.1]),
            "W": numpy.array([0.0, 0.6]),
            "Z": numpy.array([-0.02, 0.08])
        },
        label="State Variable ranges [lo, hi]",
        doc="""The values for each state-variable should be set to encompass
        the expected dynamic range of that state-variable for the current
        parameters, it is used as a mechanism for bounding random inital
        conditions when the simulation isn't started from an explicit history,
        it is also provides the default range of phase-plane plots.""")

    # variables_of_interest = arrays.IntegerArray(
    #     label = "Variables watched by Monitors",
    #     range = basic.Range(lo = 0, hi = 3, step=1),
    #     default = numpy.array([0, 2], dtype=numpy.int32),
    #     doc = """This represents the default state-variables of this Model to be
    #     monitored. It can be overridden for each Monitor if desired. The
    #     corresponding state-variable indices for this model are :math:`V = 0`,
    #     :math:`W = 1`, and :math:`Z = 2`.""",
    #     order = 21)

    variables_of_interest = List(
        of=str,
        label="Variables watched by Monitors",
        choices=("V", "W", "Z"),
        default=("V", "W", "Z"),
        doc="""This represents the default state-variables of this Model to be
        monitored. It can be overridden for each Monitor if desired. The 
        corresponding state-variable indices for this model are :math:`V = 0`,
        :math:`W = 1`, and :math:`Z = 2`.""")

    def __init__(self, **kwargs):
        """
        Initialize the Larter model's traited attributes, any provided as
        keywords will overide their traited default.
        
        """
        LOG.info('%s: initing...' % str(self))
        super(Larter, self).__init__(**kwargs)

        #self._state_variables = ["V", "W", "Z"]
        self._nvar = 3

        self.cvar = numpy.array([0], dtype=numpy.int32)

        LOG.debug('%s: inited.' % repr(self))

    def dfun(self, state_variables, coupling, local_coupling=0.0):
        """
        .. math::
             \\dot{V} &= - g_L \\, (V - V_L) - g_K\\, Z \\, (V - V_K) -
                 g_{Ca} \\, m_{\\infty} \\, (V - 1) + I - \\alpha_{inh}\\,Z  \\\\
             \\dot{W} &= \\frac{\\phi \\, (w_{\\infty} - W)}{\\tau_w} \\\\
             \\dot{Z}(t) &= b ( c \\, I_{ext} + \\alpha_{exc} \\,V ) \\\\
             m_{\\infty} &= 0.5 \\, \\left(1 + \\tanh\\left(\\frac{V - V_1}{V_2}\\right)\\right) \\\\
             w_{\\infty} &= 0.5 \\, \\left(1 + \\tanh\\left(\\frac{V - V_3}{V_4}\\right)\\right)\\\\
             tau_{w} &= \\left[ \\cosh\\left(\\frac{V - V_3}{2 \\,V_4}\\right) \\right]^{-1} \\\\
             \\alpha_{exc} &= a_{exc} \\,\\left(1 + \\tanh\\left(\\frac{V - V_5}{V_6}\\right)\\right)\\\\
             \\alpha_{inh} &= a_{inh} \\,\\left(1 + \\tanh\\left(\\frac{V - V_7}{V_6}\\right)\\right)
             
        See Eqs (1)-(8) in [Larteretal_1999]_
        """
        ##--------------------- As in Larter 1999 ----------------------------##
        V = state_variables[0, :]
        W = state_variables[1, :]
        Z = state_variables[2, :]

        c_0 = coupling[0, :]

        M_inf = 0.5 * (1 + numpy.tanh((V - self.V1) / self.V2))
        W_inf = 0.5 * (1 + numpy.tanh((V - self.V3) / self.V4))
        tau_Winv = numpy.cosh((V - self.V3) / (2 * self.V4))
        alpha_exc = self.a_exc * (1 + numpy.tanh((V - self.V5) / self.V6))
        alpha_inh = self.a_inh * (1 + numpy.tanh((V - self.V7) / self.V6))

        #import pdb; pdb.set_trace()
        dV = (local_coupling * V - alpha_inh * Z - self.gL * (V - self.VL) -
              self.gCa * M_inf * (V - 1) - self.gK * W * (V - self.VK + c_0) +
              self.Iext)

        dW = self.phi * tau_Winv * (W_inf - W)

        dZ = self.b * ((self.c * self.Iext) + (alpha_exc * V))

        derivative = numpy.array([dV, dW, dZ])

        return derivative
Ejemplo n.º 25
0
class SigmoidalJansenRit(Coupling):
    r"""
    Provides a sigmoidal coupling function as described in the 
    Jansen and Rit model, of the following form

    .. math::
        c_{min} + (c_{max} - c_{min}) / (1.0 + \exp(-a(x-midpoint)/\sigma))

    Assumes that x has have two state variables.

    """

    cmin = NArray(
        label=":math:`c_{min}`",
        default=numpy.array([
            0.0,
        ]),
        domain=Range(lo=-1000.0, hi=1000.0, step=10.0),
        doc="Minimum of the sigmoid function",
    )

    cmax = NArray(
        label=":math:`c_{max}`",
        default=numpy.array([
            2.0 * 0.0025,
        ]),
        domain=Range(lo=-1000.0, hi=1000.0, step=10.0),
        doc="Maximum of the sigmoid function",
    )

    midpoint = NArray(
        label="midpoint",
        default=numpy.array([
            6.0,
        ]),
        domain=Range(lo=-1000.0, hi=1000.0, step=10.0),
        doc="Midpoint of the linear portion of the sigmoid",
    )

    r = NArray(
        label=r":math:`r`",
        default=numpy.array([
            1.0,
        ]),
        domain=Range(lo=0.01, hi=1000.0, step=10.0),
        doc="the steepness of the sigmoidal transformation",
    )

    a = NArray(
        label=r":math:`a`",
        default=numpy.array([
            0.56,
        ]),
        domain=Range(lo=0.01, hi=1000.0, step=10.0),
        doc="Scaling of the coupling term",
    )

    def __str__(self):
        return simple_gen_astr(self, 'cmin cmax midpoint a r')

    def pre(self, x_i, x_j):
        pre = self.cmax / (1.0 + numpy.exp(self.r * (self.midpoint -
                                                     (x_j[:, 0] - x_j[:, 1]))))
        return pre[:, numpy.newaxis]

    def post(self, gx):
        return self.a * gx
Ejemplo n.º 26
0
class Epileptor2D(ModelNumbaDfun):
    r"""
        Two-dimensional reduction of the Epileptor.

        .. moduleauthor:: [email protected]

        Taking advantage of time scale separation and focusing on the slower time scale,
        the five-dimensional Epileptor reduces to a two-dimensional system (see [Proixetal_2014,
        Proixetal_2017]).

        Note: the slow permittivity variable can be modify to account for the time
        difference between interictal and ictal states (see [Proixetal_2014]).

        Equations and default parameters are taken from [Proixetal_2014]:

        .. math::
            \dot{x_{1,i}} &=& - x_{1,i}^{3} - 2x_{1,i}^{2}  + 1 - z_{i} + I_{ext1,i} \\
            \dot{z_{i}} &=& r(h - z_{i})

        with
            h =
            \begin{cases}
            x_{0} + 3 / (exp((x_{1} + 0.5)/0.1)) & \text{if } modification\\
            4 (x_{1,i} - x_{0}) & \text{else }
            \end{cases}
        References:
            [Proixetal_2014] Proix, T.; Bartolomei, F; Chauvel, P; Bernard, C; Jirsa, V.K. *
            Permittivity coupling across brain regions determines seizure recruitment in
            partial epilepsy.* J Neurosci 2014, 34:15009-21.

            [Proixetal_2017] Proix, T.; Bartolomei, F; Guye, M.; Jirsa, V.K. *Individual brain
            structure and modelling predict seizure propagation.* Brain 2017, 140; 641–654.
    """

    a = NArray(
        label=":math:`a`",
        default=numpy.array([1.0]),
        doc="Coefficient of the cubic term in the first state-variable.")

    b = NArray(
        label=":math:`b`",
        default=numpy.array([3.0]),
        doc="Coefficient of the squared term in the first state-variable.")

    c = NArray(label=":math:`c`",
               default=numpy.array([1.0]),
               doc="Additive coefficient for the second state-variable x_{2}, \
        called :math:`y_{0}` in Jirsa paper.")

    d = NArray(
        label=":math:`d`",
        default=numpy.array([5.0]),
        doc=
        "Coefficient of the squared term in the second state-variable x_{2}.")

    r = NArray(label=":math:`r`",
               domain=Range(lo=0.0, hi=0.001, step=0.00005),
               default=numpy.array([0.00035]),
               doc="Temporal scaling in the slow state-variable, \
        called :math:`1\\tau_{0}` in Jirsa paper (see class Epileptor).")

    x0 = NArray(label=":math:`x_0`",
                domain=Range(lo=-3.0, hi=-1.0, step=0.1),
                default=numpy.array([-1.6]),
                doc="Epileptogenicity parameter.")

    Iext = NArray(label=":math:`I_{ext}`",
                  domain=Range(lo=1.5, hi=5.0, step=0.1),
                  default=numpy.array([3.1]),
                  doc="External input current to the first state-variable.")

    slope = NArray(label=":math:`slope`",
                   domain=Range(lo=-16.0, hi=6.0, step=0.1),
                   default=numpy.array([0.]),
                   doc="Linear coefficient in the first state-variable.")

    Kvf = NArray(label=":math:`K_{vf}`",
                 default=numpy.array([0.0]),
                 domain=Range(lo=0.0, hi=4.0, step=0.5),
                 doc="Coupling scaling on a very fast time scale.")

    Ks = NArray(
        label=":math:`K_{s}`",
        default=numpy.array([0.0]),
        domain=Range(lo=-4.0, hi=4.0, step=0.1),
        doc=
        "Permittivity coupling, that is from the fast time scale toward the slow time scale."
    )

    tt = NArray(
        label=":math:`tt`",
        default=numpy.array([1.0]),
        domain=Range(lo=0.001, hi=1.0, step=0.001),
        doc="Time scaling of the whole system to the system in real time.")

    modification = NArray(
        dtype=bool,
        label=":math:`modification`",
        default=numpy.array([False]),
        doc="When modification is True, then use nonlinear influence on z. \
        The default value is False, i.e., linear influence.")

    state_variable_range = Final(
        default={
            "x1": numpy.array([-2., 1.]),
            "z": numpy.array([2.0, 5.0])
        },
        label="State variable ranges [lo, hi]",
        doc="Typical bounds on state-variables in the Epileptor 2D model.")

    variables_of_interest = List(
        of=str,
        label="Variables watched by Monitors",
        choices=('x1', 'z'),
        default=('x1', ),
        doc="Quantities of the Epileptor 2D available to monitor.")

    state_variables = ('x1', 'z')

    _nvar = 2
    cvar = numpy.array([0], dtype=numpy.int32)

    def _numpy_dfun(self,
                    state_variables,
                    coupling,
                    local_coupling=0.0,
                    array=numpy.array,
                    where=numpy.where,
                    concat=numpy.concatenate):

        y = state_variables
        ydot = numpy.empty_like(state_variables)

        Iext = self.Iext + local_coupling * y[0]
        c_pop = coupling[0, :]

        # population 1
        if_ydot0 = self.a * y[0]**2 + (self.d - self.b) * y[0]
        else_ydot0 = -self.slope - 0.6 * (y[1] - 4.0)**2 + self.d * y[0]

        ydot[0] = self.tt * (self.c - y[1] + Iext + self.Kvf * c_pop -
                             (where(y[0] < 0., if_ydot0, else_ydot0)) * y[0])

        # energy
        if_ydot1 = -0.1 * y[1]**7
        else_ydot1 = 0

        if self.modification:
            h = self.x0 + 3. / (1. + numpy.exp(-(y[0] + 0.5) / 0.1))
        else:
            h = 4 * (y[0] - self.x0) + where(y[1] < 0., if_ydot1, else_ydot1)

        ydot[1] = self.tt * (self.r * (h - y[1] + self.Ks * c_pop))

        return ydot

    def dfun(self, x, c, local_coupling=0.0):
        r"""
        Computes the derivatives of the state-variables of the Epileptor 2D
        with respect to time.
        Equations and default parameters are taken from [Proixetal_2014]:
        .. math::
            \dot{x_{1,i}} &=& - x_{1,i}^{3} - 2x_{1,i}^{2}  + 1 - z_{i} + I_{ext1,i} \\
            \dot{z_{i}} &=& r(h - z_{i})
        with
            h =
            \begin{cases}
            x_{0} + 3 / (exp((x_{1} + 0.5)/0.1)) & \text{if } modification\\
            4 (x_{1,i} - x_{0}) & \text{else }
            \end{cases}
        """

        x_ = x.reshape(x.shape[:-1]).T
        c_ = c.reshape(c.shape[:-1]).T
        Iext = self.Iext + local_coupling * x[0, :, 0]
        deriv = _numba_dfun_epi2d(x_, c_, self.x0, Iext, self.a, self.b,
                                  self.slope, self.c, self.d, self.r, self.Kvf,
                                  self.Ks, self.tt, self.modification)
        return deriv.T[..., numpy.newaxis]
Ejemplo n.º 27
0
    def get_range_parameters(self):
        ntau_range_param = RangeParameter(Noise.ntau.field_name, float, Range(lo=0.0, hi=20.0, step=1.0))
        params_with_range_defined = super(NoiseForm, self).get_range_parameters()
        params_with_range_defined.append(ntau_range_param)

        return params_with_range_defined
Ejemplo n.º 28
0
class Epileptor(ModelNumbaDfun):
    r"""
    The Epileptor is a composite neural mass model of six dimensions which
    has been crafted to model the phenomenology of epileptic seizures.
    (see [Jirsaetal_2014]_)
    Equations and default parameters are taken from [Jirsaetal_2014]_.
          +------------------------------------------------------+
          |                         Table 1                      |
          +----------------------+-------------------------------+
          |        Parameter     |           Value               |
          +======================+===============================+
          |         I_rest1      |              3.1              |
          +----------------------+-------------------------------+
          |         I_rest2      |              0.45             |
          +----------------------+-------------------------------+
          |         r            |            0.00035            |
          +----------------------+-------------------------------+
          |         x_0          |             -1.6              |
          +----------------------+-------------------------------+
          |         slope        |              0.0              |
          +----------------------+-------------------------------+
          |             Integration parameter                    |
          +----------------------+-------------------------------+
          |           dt         |              0.1              |
          +----------------------+-------------------------------+
          |  simulation_length   |              4000             |
          +----------------------+-------------------------------+
          |                    Noise                             |
          +----------------------+-------------------------------+
          |         nsig         | [0., 0., 0., 1e-3, 1e-3, 0.]  |
          +----------------------+-------------------------------+
          |              Jirsa et al. 2014                       |
          +------------------------------------------------------+
    .. figure :: img/Epileptor_01_mode_0_pplane.svg
        :alt: Epileptor phase plane
    .. [Jirsaetal_2014] Jirsa, V. K.; Stacey, W. C.; Quilichini, P. P.;
        Ivanov, A. I.; Bernard, C. *On the nature of seizure dynamics.* Brain,
        2014.
    Variables of interest to be used by monitors: -y[0] + y[3]
        .. math::
            \dot{x_{1}} &=& y_{1} - f_{1}(x_{1}, x_{2}) - z + I_{ext1} \\
            \dot{y_{1}} &=& c - d x_{1}^{2} - y{1} \\
            \dot{z} &=&
            \begin{cases}
            r(4 (x_{1} - x_{0}) - z-0.1 z^{7}) & \text{if } x<0 \\
            r(4 (x_{1} - x_{0}) - z) & \text{if } x \geq 0
            \end{cases} \\
            \dot{x_{2}} &=& -y_{2} + x_{2} - x_{2}^{3} + I_{ext2} + 0.002 g - 0.3 (z-3.5) \\
            \dot{y_{2}} &=& 1 / \tau (-y_{2} + f_{2}(x_{2}))\\
            \dot{g} &=& -0.01 (g - 0.1 x_{1})
    where:
        .. math::
            f_{1}(x_{1}, x_{2}) =
            \begin{cases}
            a x_{1}^{3} - b x_{1}^2 & \text{if } x_{1} <0\\
            -(slope - x_{2} + 0.6(z-4)^2) x_{1} &\text{if }x_{1} \geq 0
            \end{cases}
    and:
        .. math::
            f_{2}(x_{2}) =
            \begin{cases}
            0 & \text{if } x_{2} <-0.25\\
            a_{2}(x_{2} + 0.25) & \text{if } x_{2} \geq -0.25
            \end{cases}
    Note Feb. 2017: the slow permittivity variable can be modify to account for the time
    difference between interictal and ictal states (see [Proixetal_2014]).
    .. [Proixetal_2014] Proix, T.; Bartolomei, F; Chauvel, P; Bernard, C; Jirsa, V.K. *
        Permittivity coupling across brain regions determines seizure recruitment in
        partial epilepsy.* J Neurosci 2014, 34:15009-21.
    """

    a = NArray(label=":math:`a`",
               default=numpy.array([1.0]),
               doc="Coefficient of the cubic term in the first state variable")

    b = NArray(
        label=":math:`b`",
        default=numpy.array([3.0]),
        doc="Coefficient of the squared term in the first state variabel")

    c = NArray(label=":math:`c`",
               default=numpy.array([1.0]),
               doc="Additive coefficient for the second state variable, \
        called :math:`y_{0}` in Jirsa paper")

    d = NArray(
        label=":math:`d`",
        default=numpy.array([5.0]),
        doc="Coefficient of the squared term in the second state variable")

    r = NArray(label=":math:`r`",
               domain=Range(lo=0.0, hi=0.001, step=0.00005),
               default=numpy.array([0.00035]),
               doc="Temporal scaling in the third state variable, \
        called :math:`1/\\tau_{0}` in Jirsa paper")

    s = NArray(label=":math:`s`",
               default=numpy.array([4.0]),
               doc="Linear coefficient in the third state variable")

    x0 = NArray(label=":math:`x_0`",
                domain=Range(lo=-3.0, hi=-1.0, step=0.1),
                default=numpy.array([-1.6]),
                doc="Epileptogenicity parameter")

    Iext = NArray(label=":math:`I_{ext}`",
                  domain=Range(lo=1.5, hi=5.0, step=0.1),
                  default=numpy.array([3.1]),
                  doc="External input current to the first population")

    slope = NArray(label=":math:`slope`",
                   domain=Range(lo=-16.0, hi=6.0, step=0.1),
                   default=numpy.array([0.]),
                   doc="Linear coefficient in the first state variable")

    Iext2 = NArray(label=":math:`I_{ext2}`",
                   domain=Range(lo=0.0, hi=1.0, step=0.05),
                   default=numpy.array([0.45]),
                   doc="External input current to the second population")

    tau = NArray(label=":math:`\\tau`",
                 default=numpy.array([10.0]),
                 doc="Temporal scaling coefficient in fifth state variable")

    aa = NArray(label=":math:`aa`",
                default=numpy.array([6.0]),
                doc="Linear coefficient in fifth state variable")

    bb = NArray(
        label=":math:`bb`",
        default=numpy.array([2.0]),
        doc=
        "Linear coefficient of lowpass excitatory coupling in fourth state variable"
    )

    Kvf = NArray(label=":math:`K_{vf}`",
                 default=numpy.array([0.0]),
                 domain=Range(lo=0.0, hi=4.0, step=0.5),
                 doc="Coupling scaling on a very fast time scale.")

    Kf = NArray(label=":math:`K_{f}`",
                default=numpy.array([0.0]),
                domain=Range(lo=0.0, hi=4.0, step=0.5),
                doc="Correspond to the coupling scaling on a fast time scale.")

    Ks = NArray(
        label=":math:`K_{s}`",
        default=numpy.array([0.0]),
        domain=Range(lo=-4.0, hi=4.0, step=0.1),
        doc=
        "Permittivity coupling, that is from the fast time scale toward the slow time scale"
    )

    tt = NArray(label=":math:`K_{tt}`",
                default=numpy.array([1.0]),
                domain=Range(lo=0.001, hi=10.0, step=0.001),
                doc="Time scaling of the whole system")

    modification = NArray(
        dtype=bool,
        label=":math:`modification`",
        default=numpy.array([False]),
        doc="When modification is True, then use nonlinear influence on z. \
        The default value is False, i.e., linear influence.")

    state_variable_range = Final(
        default={
            "x1": numpy.array([-2., 1.]),
            "y1": numpy.array([-20., 2.]),
            "z": numpy.array([2.0, 5.0]),
            "x2": numpy.array([-2., 0.]),
            "y2": numpy.array([0., 2.]),
            "g": numpy.array([-1., 1.])
        },
        label="State variable ranges [lo, hi]",
        doc="Typical bounds on state variables in the Epileptor model.")

    variables_of_interest = List(
        of=str,
        label="Variables watched by Monitors",
        choices=('x1', 'y1', 'z', 'x2', 'y2', 'g', 'x2 - x1'),
        default=("x2 - x1", 'z'),
        doc="Quantities of the Epileptor available to monitor.",
    )

    state_variables = ('x1', 'y1', 'z', 'x2', 'y2', 'g')

    _nvar = 6
    cvar = numpy.array(
        [0, 3], dtype=numpy.int32)  # should these not be constant Attr's?
    cvar.setflags(write=False)  # todo review this

    def _numpy_dfun(self,
                    state_variables,
                    coupling,
                    local_coupling=0.0,
                    array=numpy.array,
                    where=numpy.where,
                    concat=numpy.concatenate):

        y = state_variables
        ydot = numpy.empty_like(state_variables)

        Iext = self.Iext + local_coupling * y[0]
        c_pop1 = coupling[0, :]
        c_pop2 = coupling[1, :]

        # population 1
        if_ydot0 = -self.a * y[0]**2 + self.b * y[0]
        else_ydot0 = self.slope - y[3] + 0.6 * (y[2] - 4.0)**2
        ydot[0] = self.tt * (y[1] - y[2] + Iext + self.Kvf * c_pop1 +
                             where(y[0] < 0., if_ydot0, else_ydot0) * y[0])
        ydot[1] = self.tt * (self.c - self.d * y[0]**2 - y[1])

        # energy
        if_ydot2 = -0.1 * y[2]**7
        else_ydot2 = 0
        if self.modification:
            h = self.x0 + 3. / (1. + numpy.exp(-(y[0] + 0.5) / 0.1))
        else:
            h = 4 * (y[0] - self.x0) + where(y[2] < 0., if_ydot2, else_ydot2)
        ydot[2] = self.tt * (self.r * (h - y[2] + self.Ks * c_pop1))

        # population 2
        ydot[3] = self.tt * (-y[4] + y[3] - y[3]**3 + self.Iext2 +
                             self.bb * y[5] - 0.3 *
                             (y[2] - 3.5) + self.Kf * c_pop2)
        if_ydot4 = 0
        else_ydot4 = self.aa * (y[3] + 0.25)
        ydot[4] = self.tt * (
            (-y[4] + where(y[3] < -0.25, if_ydot4, else_ydot4)) / self.tau)

        # filter
        ydot[5] = self.tt * (-0.01 * (y[5] - 0.1 * y[0]))

        return ydot

    def dfun(self, x, c, local_coupling=0.0):
        r"""
        Computes the derivatives of the state variables of the Epileptor
        with respect to time.
        Implementation note: we expect this version of the Epileptor to be used
        in a vectorized manner. Concretely, y has a shape of (6, n) where n is
        the number of nodes in the network. An consequence is that
        the original use of if/else is translated by calculated both the true
        and false forms and mixing them using a boolean mask.
        Variables of interest to be used by monitors: -y[0] + y[3]
            .. math::
                \dot{x_{1}} &=& y_{1} - f_{1}(x_{1}, x_{2}) - z + I_{ext1} \\
                \dot{y_{1}} &=& c - d x_{1}^{2} - y{1} \\
                \dot{z} &=&
                \begin{cases}
                r(4 (x_{1} - x_{0}) - z-0.1 z^{7}) & \text{if } x<0 \\
                r(4 (x_{1} - x_{0}) - z) & \text{if } x \geq 0
                \end{cases} \\
                \dot{x_{2}} &=& -y_{2} + x_{2} - x_{2}^{3} + I_{ext2} + 0.002 g - 0.3 (z-3.5) \\
                \dot{y_{2}} &=& 1 / \tau (-y_{2} + f_{2}(x_{2}))\\
                \dot{g} &=& -0.01 (g - 0.1 x_{1})
        where:
            .. math::
                f_{1}(x_{1}, x_{2}) =
                \begin{cases}
                a x_{1}^{3} - b x_{1}^2 & \text{if } x_{1} <0\\
                -(slope - x_{2} + 0.6(z-4)^2) x_{1} &\text{if }x_{1} \geq 0
                \end{cases}
        and:
            .. math::
                f_{2}(x_{2}) =
                \begin{cases}
                0 & \text{if } x_{2} <-0.25\\
                a_{2}(x_{2} + 0.25) & \text{if } x_{2} \geq -0.25
                \end{cases}
        """
        x_ = x.reshape(x.shape[:-1]).T
        c_ = c.reshape(c.shape[:-1]).T
        Iext = self.Iext + local_coupling * x[0, :, 0]
        deriv = _numba_dfun(x_, c_, self.x0, Iext, self.Iext2, self.a, self.b,
                            self.slope, self.tt, self.Kvf, self.c, self.d,
                            self.r, self.Ks, self.Kf, self.aa, self.bb,
                            self.tau, self.modification)
        return deriv.T[..., numpy.newaxis]
Ejemplo n.º 29
0
class MontbrioT(ModelNumbaDfun):

    tau = NArray(label=":math:`tau`",
                 default=numpy.array([1.0]),
                 domain=Range(lo=-10.0, hi=10.0, step=0.01),
                 doc="""""")

    I = NArray(label=":math:`I`",
               default=numpy.array([0.0]),
               domain=Range(lo=-10.0, hi=10.0, step=0.01),
               doc="""""")

    Delta = NArray(label=":math:`Delta`",
                   default=numpy.array([0.7]),
                   domain=Range(lo=0.0, hi=10.0, step=0.01),
                   doc="""""")

    J = NArray(label=":math:`J`",
               default=numpy.array([14.5]),
               domain=Range(lo=-25.0, hi=25.0, step=0.0001),
               doc="""""")

    eta = NArray(label=":math:`eta`",
                 default=numpy.array([-4.6]),
                 domain=Range(lo=-10.0, hi=10.0, step=0.0001),
                 doc="""""")

    Gamma = NArray(label=":math:`Gamma`",
                   default=numpy.array([5.0]),
                   domain=Range(lo=0., hi=10.0, step=0.1),
                   doc="""""")

    cr = NArray(label=":math:`cr`",
                default=numpy.array([1.0]),
                domain=Range(lo=0., hi=1, step=0.1),
                doc="""""")

    cv = NArray(label=":math:`cv`",
                default=numpy.array([1.0]),
                domain=Range(lo=0., hi=1, step=0.1),
                doc="""""")

    state_variable_range = Final(label="State Variable ranges [lo, hi]",
                                 default={
                                     "r": numpy.array([0.0]),
                                     "V": numpy.array([0.0])
                                 },
                                 doc="""state variables""")

    state_variable_boundaries = Final(
        label="State Variable boundaries [lo, hi]",
        default={
            "r": numpy.array([0.0, np.inf]),
        },
    )
    variables_of_interest = List(
        of=str,
        label="Variables or quantities available to Monitors",
        choices=(
            'r',
            'V',
        ),
        default=(
            'r',
            'V',
        ),
        doc="Variables to monitor")

    state_variables = ['r', 'V']

    _nvar = 2
    cvar = numpy.array([
        0,
        1,
    ], dtype=numpy.int32)

    def dfun(self, vw, c, local_coupling=0.0):
        vw_ = vw.reshape(vw.shape[:-1]).T
        c_ = c.reshape(c.shape[:-1]).T
        deriv = _numba_dfun_MontbrioT(vw_, c_, self.tau, self.I, self.Delta,
                                      self.J, self.eta, self.Gamma, self.cr,
                                      self.cv, local_coupling)

        return deriv.T[..., numpy.newaxis]
Ejemplo n.º 30
0
class LarterBreakspear(Model):
    r"""
    A modified Morris-Lecar model that includes a third equation which simulates
    the effect of a population of inhibitory interneurons synapsing on
    the pyramidal cells.

    .. [Larteretal_1999] Larter et.al. *A coupled ordinary differential equation
        lattice model for the simulation of epileptic seizures.* Chaos. 9(3):
        795, 1999.

    .. [Breaksetal_2003_a] Breakspear, M.; Terry, J. R. & Friston, K. J.  *Modulation of excitatory
        synaptic coupling facilitates synchronization and complex dynamics in an
        onlinear model of neuronal dynamics*. Neurocomputing 52–54 (2003).151–158

    .. [Breaksetal_2003_b] M. J. Breakspear et.al. *Modulation of excitatory
        synaptic coupling facilitates synchronization and complex dynamics in a
        biophysical model of neuronal dynamics.* Network: Computation in Neural
        Systems 14: 703-732, 2003.

    .. [Honeyetal_2007] Honey, C.; Kötter, R.; Breakspear, M. & Sporns, O. * Network structure of
        cerebral cortex shapes functional connectivity on multiple time scales*. (2007)
        PNAS, 104, 10240

    .. [Honeyetal_2009] Honey, C. J.; Sporns, O.; Cammoun, L.; Gigandet, X.; Thiran, J. P.; Meuli,
        R. & Hagmann, P. *Predicting human resting-state functional connectivity
        from structural connectivity.* (2009), PNAS, 106, 2035-2040

    .. [Alstottetal_2009] Alstott, J.; Breakspear, M.; Hagmann, P.; Cammoun, L. & Sporns, O.
        *Modeling the impact of lesions in the human brain*. (2009)),  PLoS Comput Biol, 5, e1000408

    Equations and default parameters are taken from [Breaksetal_2003_b]_.
    All equations and parameters are non-dimensional and normalized.
    For values of d_v  < 0.55, the dynamics of a single column settles onto a
    solitary fixed point attractor.


    Parameters used for simulations in [Breaksetal_2003_a]_ Table 1. Page 153.
    Two nodes were coupled. C=0.1

    +---------------------------+
    |          Table 1          |
    +--------------+------------+
    |Parameter     |  Value     |
    +==============+============+
    | I            |      0.3   |
    +--------------+------------+
    | a_ee         |      0.4   |
    +--------------+------------+
    | a_ei         |      0.1   |
    +--------------+------------+
    | a_ie         |      1.0   |
    +--------------+------------+
    | a_ne         |      1.0   |
    +--------------+------------+
    | a_ni         |      0.4   |
    +--------------+------------+
    | r_NMDA       |      0.2   |
    +--------------+------------+
    | delta        |      0.001 |
    +--------------+------------+
    |   Breakspear et al. 2003  |
    +---------------------------+


    +---------------------------+
    |          Table 2          |
    +--------------+------------+
    |Parameter     |  Value     |
    +==============+============+
    | gK           |      2.0   |
    +--------------+------------+
    | gL           |      0.5   |
    +--------------+------------+
    | gNa          |      6.7   |
    +--------------+------------+
    | gCa          |      1.0   |
    +--------------+------------+
    | a_ne         |      1.0   |
    +--------------+------------+
    | a_ni         |      0.4   |
    +--------------+------------+
    | a_ee         |      0.36  |
    +--------------+------------+
    | a_ei         |      2.0   |
    +--------------+------------+
    | a_ie         |      2.0   |
    +--------------+------------+
    | VK           |     -0.7   |
    +--------------+------------+
    | VL           |     -0.5   |
    +--------------+------------+
    | VNa          |      0.53  |
    +--------------+------------+
    | VCa          |      1.0   |
    +--------------+------------+
    | phi          |      0.7   |
    +--------------+------------+
    | b            |      0.1   |
    +--------------+------------+
    | I            |      0.3   |
    +--------------+------------+
    | r_NMDA       |      0.25  |
    +--------------+------------+
    | C            |      0.1   |
    +--------------+------------+
    | TCa          |     -0.01  |
    +--------------+------------+
    | d_Ca         |      0.15  |
    +--------------+------------+
    | TK           |      0.0   |
    +--------------+------------+
    | d_K          |      0.3   |
    +--------------+------------+
    | VT           |      0.0   |
    +--------------+------------+
    | ZT           |      0.0   |
    +--------------+------------+
    | TNa          |      0.3   |
    +--------------+------------+
    | d_Na         |      0.15  |
    +--------------+------------+
    | d_V          |      0.65  |
    +--------------+------------+
    | d_Z          |      d_V   |
    +--------------+------------+
    | QV_max       |      1.0   |
    +--------------+------------+
    | QZ_max       |      1.0   |
    +--------------+------------+
    |   Alstott et al. 2009     |
    +---------------------------+


    NOTES about parameters

    :math:`\delta_V` : for :math:`\delta_V` < 0.55, in an uncoupled network,
    the system exhibits fixed point dynamics; for 0.55 < :math:`\delta_V` < 0.59,
    limit cycle attractors; and for :math:`\delta_V` > 0.59 chaotic attractors
    (eg, d_V=0.6,aee=0.5,aie=0.5, gNa=0, Iext=0.165)

    :math:`\delta_Z`
    this parameter might be spatialized: ones(N,1).*0.65 + modn*(rand(N,1)-0.5);

    :math:`C`
    The long-range coupling :math:`\delta_C` is ‘weak’ in the sense that
    the model is well behaved for parameter values for which C < a_ee and C << a_ie.



    .. figure :: img/LarterBreakspear_01_mode_0_pplane.svg
            :alt: Larter-Breaskpear phase plane (V, W)

            The (:math:`V`, :math:`W`) phase-plane for the Larter-Breakspear model.


    Dynamic equations:

    .. math::
            \dot{V}_k & = - (g_{Ca} + (1 - C) \, r_{NMDA} \, a_{ee} \, Q_V + C \, r_{NMDA} \, a_{ee} \, \langle Q_V\rangle^{k}) \, m_{Ca} \, (V - VCa) \\
                           & \,\,- g_K \, W \, (V - VK) -  g_L \, (V - VL) \\
                           & \,\,- (g_{Na} \, m_{Na} + (1 - C) \, a_{ee} \, Q_V + C \, a_{ee} \, \langle Q_V\rangle^{k}) \,(V - VNa) \\
                           & \,\,- a_{ie} \, Z \, Q_Z + a_{ne} \, I \\
                           & \\
            \dot{W}_k & = \phi \, \dfrac{m_K - W}{\tau_{K}} \\
                           & \nonumber\\
            \dot{Z}_k &= b (a_{ni}\, I + a_{ei}\,V\,Q_V) \\
            Q_{V}   &= Q_{V_{max}} \, (1 + \tanh\left(\dfrac{V_{k} - VT}{\delta_{V}}\right)) \\
            Q_{Z}   &= Q_{Z_{max}} \, (1 + \tanh\left(\dfrac{Z_{k} - ZT}{\delta_{Z}}\right))

    See Equations (7), (3), (6) and (2) respectively in [Breaksetal_2003_a]_.
    Pag: 705-706

    """

    # Define traited attributes for this model, these represent possible kwargs.
    gCa = NArray(label=":math:`g_{Ca}`",
                 default=numpy.array([1.1]),
                 domain=Range(lo=0.9, hi=1.5, step=0.1),
                 doc="""Conductance of population of Ca++ channels.""")

    gK = NArray(label=":math:`g_{K}`",
                default=numpy.array([2.0]),
                domain=Range(lo=1.95, hi=2.05, step=0.025),
                doc="""Conductance of population of K channels.""")

    gL = NArray(label=":math:`g_{L}`",
                default=numpy.array([0.5]),
                domain=Range(lo=0.45, hi=0.55, step=0.05),
                doc="""Conductance of population of leak channels.""")

    phi = NArray(label=r":math:`\phi`",
                 default=numpy.array([0.7]),
                 domain=Range(lo=0.3, hi=0.9, step=0.1),
                 doc="""Temperature scaling factor.""")

    gNa = NArray(label=":math:`g_{Na}`",
                 default=numpy.array([6.7]),
                 domain=Range(lo=0.0, hi=10.0, step=0.1),
                 doc="""Conductance of population of Na channels.""")

    TK = NArray(label=":math:`T_{K}`",
                default=numpy.array([0.0]),
                domain=Range(lo=0.0, hi=0.0001, step=0.00001),
                doc="""Threshold value for K channels.""")

    TCa = NArray(label=":math:`T_{Ca}`",
                 default=numpy.array([-0.01]),
                 domain=Range(lo=-0.02, hi=-0.01, step=0.0025),
                 doc="Threshold value for Ca channels.")

    TNa = NArray(label=":math:`T_{Na}`",
                 default=numpy.array([0.3]),
                 domain=Range(lo=0.25, hi=0.3, step=0.025),
                 doc="Threshold value for Na channels.")

    VCa = NArray(label=":math:`V_{Ca}`",
                 default=numpy.array([1.0]),
                 domain=Range(lo=0.9, hi=1.1, step=0.05),
                 doc="""Ca Nernst potential.""")

    VK = NArray(label=":math:`V_{K}`",
                default=numpy.array([-0.7]),
                domain=Range(lo=-0.8, hi=1., step=0.1),
                doc="""K Nernst potential.""")

    VL = NArray(label=":math:`V_{L}`",
                default=numpy.array([-0.5]),
                domain=Range(lo=-0.7, hi=-0.4, step=0.1),
                doc="""Nernst potential leak channels.""")

    VNa = NArray(label=":math:`V_{Na}`",
                 default=numpy.array([0.53]),
                 domain=Range(lo=0.51, hi=0.55, step=0.01),
                 doc="""Na Nernst potential.""")

    d_K = NArray(label=r":math:`\delta_{K}`",
                 default=numpy.array([0.3]),
                 domain=Range(lo=0.1, hi=0.4, step=0.1),
                 doc="""Variance of K channel threshold.""")

    tau_K = NArray(label=r":math:`\tau_{K}`",
                   default=numpy.array([1.0]),
                   domain=Range(lo=1.0, hi=10.0, step=1.0),
                   doc="""Time constant for K relaxation time (ms)""")

    d_Na = NArray(label=r":math:`\delta_{Na}`",
                  default=numpy.array([0.15]),
                  domain=Range(lo=0.1, hi=0.2, step=0.05),
                  doc="Variance of Na channel threshold.")

    d_Ca = NArray(label=r":math:`\delta_{Ca}`",
                  default=numpy.array([0.15]),
                  domain=Range(lo=0.1, hi=0.2, step=0.05),
                  doc="Variance of Ca channel threshold.")

    aei = NArray(label=":math:`a_{ei}`",
                 default=numpy.array([2.0]),
                 domain=Range(lo=0.1, hi=2.0, step=0.1),
                 doc="""Excitatory-to-inhibitory synaptic strength.""")

    aie = NArray(label=":math:`a_{ie}`",
                 default=numpy.array([2.0]),
                 domain=Range(lo=0.5, hi=2.0, step=0.1),
                 doc="""Inhibitory-to-excitatory synaptic strength.""")

    b = NArray(
        label=":math:`b`",
        default=numpy.array([0.1]),
        domain=Range(lo=0.0001, hi=1.0, step=0.0001),
        doc="""Time constant scaling factor. The original value is 0.1""")

    C = NArray(
        label=":math:`C`",
        default=numpy.array([0.1]),
        domain=Range(lo=0.0, hi=1.0, step=0.01),
        doc="""Strength of excitatory coupling. Balance between internal and
        local (and global) coupling strength. C > 0 introduces interdependences between
        consecutive columns/nodes. C=1 corresponds to maximum coupling between node and no self-coupling.
        This strenght should be set to sensible values when a whole network is connected. """
    )

    ane = NArray(label=":math:`a_{ne}`",
                 default=numpy.array([1.0]),
                 domain=Range(lo=0.4, hi=1.0, step=0.05),
                 doc="""Non-specific-to-excitatory synaptic strength.""")

    ani = NArray(label=":math:`a_{ni}`",
                 default=numpy.array([0.4]),
                 domain=Range(lo=0.3, hi=0.5, step=0.05),
                 doc="""Non-specific-to-inhibitory synaptic strength.""")

    aee = NArray(label=":math:`a_{ee}`",
                 default=numpy.array([0.4]),
                 domain=Range(lo=0.0, hi=0.6, step=0.05),
                 doc="""Excitatory-to-excitatory synaptic strength.""")

    Iext = NArray(
        label=":math:`I_{ext}`",
        default=numpy.array([0.3]),
        domain=Range(lo=0.165, hi=0.3, step=0.005),
        doc="""Subcortical input strength. It represents a non-specific
       excitation or thalamic inputs.""")

    rNMDA = NArray(label=":math:`r_{NMDA}`",
                   default=numpy.array([0.25]),
                   domain=Range(lo=0.2, hi=0.3, step=0.05),
                   doc="""Ratio of NMDA to AMPA receptors.""")

    VT = NArray(label=":math:`V_{T}`",
                default=numpy.array([0.0]),
                domain=Range(lo=0.0, hi=0.7, step=0.01),
                doc="""Threshold potential (mean) for excitatory neurons.
        In [Breaksetal_2003_b]_ this value is 0.""")

    d_V = NArray(
        label=r":math:`\delta_{V}`",
        default=numpy.array([0.65]),
        domain=Range(lo=0.49, hi=0.7, step=0.01),
        doc="""Variance of the excitatory threshold. It is one of the main
        parameters explored in [Breaksetal_2003_b]_.""")

    ZT = NArray(label=":math:`Z_{T}`",
                default=numpy.array([0.0]),
                domain=Range(lo=0.0, hi=0.1, step=0.005),
                doc="""Threshold potential (mean) for inihibtory neurons.""")

    d_Z = NArray(label=r":math:`\delta_{Z}`",
                 default=numpy.array([0.7]),
                 domain=Range(lo=0.001, hi=0.75, step=0.05),
                 doc="""Variance of the inhibitory threshold.""")

    # NOTE: the values were not in the article.
    QV_max = NArray(
        label=":math:`Q_{max}`",
        default=numpy.array([1.0]),
        domain=Range(lo=0.1, hi=1., step=0.001),
        doc="""Maximal firing rate for excitatory populations (kHz)""")

    QZ_max = NArray(
        label=":math:`Q_{max}`",
        default=numpy.array([1.0]),
        domain=Range(lo=0.1, hi=1., step=0.001),
        doc="""Maximal firing rate for excitatory populations (kHz)""")

    t_scale = NArray(label=":math:`t_{scale}`",
                     default=numpy.array([1.0]),
                     domain=Range(lo=0.1, hi=1., step=0.001),
                     doc="""Time scale factor""")

    variables_of_interest = List(
        of=str,
        label="Variables watched by Monitors",
        choices=("V", "W", "Z"),
        default=("V", ),
        doc="""This represents the default state-variables of this Model to be
        monitored. It can be overridden for each Monitor if desired.""")

    #Informational attribute, used for phase-plane and initial()
    state_variable_range = Final(
        label="State Variable ranges [lo, hi]",
        default={
            "V": numpy.array([-1.5, 1.5]),
            "W": numpy.array([-1.5, 1.5]),
            "Z": numpy.array([-1.5, 1.5])
        },
        doc="""The values for each state-variable should be set to encompass
            the expected dynamic range of that state-variable for the current
            parameters, it is used as a mechanism for bounding random inital
            conditions when the simulation isn't started from an explicit
            history, it is also provides the default range of phase-plane plots."""
    )

    state_variables = tuple('V W Z'.split())
    _state_variables = ("V", "W", "Z")
    _nvar = 3
    cvar = numpy.array([0], dtype=numpy.int32)

    def dfun(self, state_variables, coupling, local_coupling=0.0):
        r"""
        Dynamic equations:

        .. math::
            \dot{V}_k & = - (g_{Ca} + (1 - C) \, r_{NMDA} \, a_{ee} \, Q_V + C \, r_{NMDA} \, a_{ee} \, \langle Q_V\rangle^{k}) \, m_{Ca} \, (V - VCa) \\
                           & \,\,- g_K \, W \, (V - VK) -  g_L \, (V - VL) \\
                           & \,\,- (g_{Na} \, m_{Na} + (1 - C) \, a_{ee} \, Q_V + C \, a_{ee} \, \langle Q_V\rangle^{k}) \,(V - VNa) \\
                           & \,\,- a_{ie} \, Z \, Q_Z + a_{ne} \, I \\
                           & \\
            \dot{W}_k & = \phi \, \dfrac{m_K - W}{\tau_{K}} \\
                           & \nonumber\\
            \dot{Z}_k &= b (a_{ni}\, I + a_{ei}\,V\,Q_V) \\
            Q_{V}   &= Q_{V_{max}} \, (1 + \tanh\left(\dfrac{V_{k} - VT}{\delta_{V}}\right)) \\
            Q_{Z}   &= Q_{Z_{max}} \, (1 + \tanh\left(\dfrac{Z_{k} - ZT}{\delta_{Z}}\right))

        """
        V, W, Z = state_variables
        derivative = numpy.empty_like(state_variables)
        c_0 = coupling[0, :]
        # relationship between membrane voltage and channel conductance
        m_Ca = 0.5 * (1 + numpy.tanh((V - self.TCa) / self.d_Ca))
        m_Na = 0.5 * (1 + numpy.tanh((V - self.TNa) / self.d_Na))
        m_K = 0.5 * (1 + numpy.tanh((V - self.TK) / self.d_K))
        # voltage to firing rate
        QV = 0.5 * self.QV_max * (1 + numpy.tanh((V - self.VT) / self.d_V))
        QZ = 0.5 * self.QZ_max * (1 + numpy.tanh((Z - self.ZT) / self.d_Z))
        lc_0 = local_coupling * QV
        derivative[0] = self.t_scale * (
            -(self.gCa + (1.0 - self.C) * (self.rNMDA * self.aee) *
              (QV + lc_0) + self.C * self.rNMDA * self.aee * c_0) * m_Ca *
            (V - self.VCa) - self.gK * W * (V - self.VK) - self.gL *
            (V - self.VL) - (self.gNa * m_Na + (1.0 - self.C) * self.aee *
                             (QV + lc_0) + self.C * self.aee * c_0) *
            (V - self.VNa) - self.aie * Z * QZ + self.ane * self.Iext)
        derivative[1] = self.t_scale * self.phi * (m_K - W) / self.tau_K
        derivative[2] = self.t_scale * self.b * (self.ani * self.Iext +
                                                 self.aei * V * QV)
        return derivative