Exemple #1
0
class TractData(MappedType):
    vertices = arrays.PositionArray(
        label="Vertex positions",
        file_storage=core.FILE_STORAGE_EXPAND,
        order=-1,
        doc="""An array specifying coordinates for the tracts vertices.""")

    tract_start_idx = arrays.IntegerArray(
        label="Tract starting indices",
        order=-1,
        doc="""Where is the first vertex of a tract in the vertex array""")

    tract_region = arrays.IntegerArray(
        label="Tract region index",
        required=False,
        order=-1,
        doc="""
        An index used to find quickly all tract emerging from a region
        tract_region[i] is the region of the i'th tract. -1 represents the background
        """
    )

    region_volume_map = RegionVolumeMapping(
        label="Region volume Mapping used to create the tract_region index",
        required=False,
        order=-1
    )

    __generate_table__ = True

    @property
    def tracts_count(self):
        return len(self.tract_start_idx) - 1
class Raw(Monitor):
    """
    A monitor that records the output raw data from a tvb simulation:
    It collects:

        - all state variables and modes from class :Model:
        - all nodes of a region or surface based 
        - all the integration time steps

    """
    _ui_name = "Raw recording"

    period = basic.Float(
        label = "Sampling period is ignored for Raw Monitor",
        order = -1)

    variables_of_interest = arrays.IntegerArray(
        label = "Raw Monitor sees all!!! Resistance is futile...",
        order = -1)

    def config_for_sim(self, simulator):
        if self.period != simulator.integrator.dt:
            LOG.debug('Raw period not equal to integration time step, overriding')
        self.period = simulator.integrator.dt
        super(Raw, self).config_for_sim(simulator)
        self.istep = 1
        self.voi = numpy.arange(len(simulator.model.variables_of_interest))

    def sample(self, step, state):
        time = step * self.dt
        return [time, state]
Exemple #3
0
 def test_integer_array(self):
     """
     Create an integer array, check that shape is correct.
     """
     data = numpy.arange(100, dtype=int)
     array_dt = arrays.IntegerArray()
     array_dt.data = data
     self.assertEqual(array_dt.shape, (100,))
Exemple #4
0
class TractData(MappedType):
    vertices = arrays.PositionArray(
        label="Vertex positions",
        order=-1,
        doc="""An array specifying coordinates for the tracts vertices.""")

    vertex_counts = arrays.IntegerArray(
        label="Tract vertex counts",
        order=-1,
        doc="""An array specifying how many vertices in a tract""")

    __generate_table__ = True

    @property
    def tracts_count(self):
        return self.vertices.shape[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.
    
    .. #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:: MorrisLecar.__init__
    .. automethod:: MorrisLecar.dfun
    
    
    """

    _ui_name = "Morris-Lecar"
    ui_configurable_parameters = [
        'gCa', 'gK', 'gL', 'C', 'lambda_Nbar', 'V1', 'V2', 'V3', 'V4', 'VCa',
        'VK', 'VL'
    ]

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

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

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

    C = arrays.FloatArray(label=":math:`C`",
                          default=numpy.array([20.0]),
                          range=basic.Range(lo=10.0, hi=30.0, step=0.01),
                          doc="""Membrane capacitance [uF/cm2]""",
                          order=4)

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

    V1 = arrays.FloatArray(
        label=":math:`V_1`",
        default=numpy.array([10.0]),
        range=basic.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]""",
        order=6)

    V2 = arrays.FloatArray(
        label=":math:`V_2`",
        default=numpy.array([15.0]),
        range=basic.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].""",
        order=7)

    V3 = arrays.FloatArray(
        label=":math:`V_3`",
        default=numpy.array([-1.0]),
        range=basic.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].""",
        order=8)

    V4 = arrays.FloatArray(
        label=":math:`V_4`",
        default=numpy.array([14.5]),
        range=basic.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].""",
        order=9)

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

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

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

    #Used for phase-plane axis ranges and to bound random initial() conditions.
    state_variable_range = basic.Dict(
        label="State Variable ranges [lo, hi]",
        default={
            "V": numpy.array([-70.0, 50.0]),
            "N": numpy.array([-0.2, 0.8])
        },
        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.""",
        order=13)

    variables_of_interest = arrays.IntegerArray(
        label="Variables watched by Monitors",
        range=basic.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`.""",
        order=14)

    #    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
Exemple #6
0
class PreSigmoidal(Coupling):
    """Pre-Sigmoidal Coupling function (pre-product) with a Static/Dynamic 
    and Local/Global threshold.

    .. automethod:: PreSigmoidal.__init__
    .. automethod:: PreSigmoidal.configure
    .. automethod:: PreSigmoidal.__call__
    .. automethod:: PreSigmoidal.call_static
    .. automethod:: PreSigmoidal.call_dynamic

    """
    #NOTE: Different from Sigmoidal coupling where the product is an input of the sigmoid.
    #      Here the sigmoid is an input of the product.

    H = arrays.FloatArray(label="H",
                          default=numpy.array([
                              0.5,
                          ]),
                          range=basic.Range(lo=-100.0, hi=100.0, step=1.0),
                          doc="""Global Factor""",
                          order=1)

    Q = arrays.FloatArray(label="Q",
                          default=numpy.array([
                              1.,
                          ]),
                          range=basic.Range(lo=-100.0, hi=100.0, step=1.0),
                          doc="""Average""",
                          order=2)

    G = arrays.FloatArray(label="G",
                          default=numpy.array([
                              60.,
                          ]),
                          range=basic.Range(lo=-1000.0, hi=1000.0, step=1.),
                          doc="""Gain""",
                          order=3)

    P = arrays.FloatArray(label="P",
                          default=numpy.array([
                              1.,
                          ]),
                          range=basic.Range(lo=-100.0, hi=100.0, step=0.01),
                          doc="""Excitation on Inhibition ratio""",
                          order=4)

    theta = arrays.FloatArray(label=":math:`\\theta`",
                              default=numpy.array([
                                  0.5,
                              ]),
                              range=basic.Range(lo=-100.0, hi=100.0,
                                                step=0.01),
                              doc="""Threshold.""",
                              order=5)

    dynamic = arrays.IntegerArray(
        label="Dynamic",
        default=numpy.array([1]),
        range=basic.Range(lo=0, hi=1., step=1),
        doc="""Boolean value for static/dynamic threshold for (0/1).""",
        order=6)

    globalT = arrays.IntegerArray(
        label=":math:`global_{\\theta}`",
        default=numpy.array([
            0,
        ]),
        range=basic.Range(lo=0, hi=1., step=1),
        doc="""Boolean value for local/global threshold for (0/1).""",
        order=7)

    def __init__(self, **kwargs):
        '''Set the default indirect call.'''
        super(PreSigmoidal, self).__init__(**kwargs)
        self.rightCall = self.call_static

    def configure(self):
        """Set the right indirect call."""
        super(PreSigmoidal, self).configure()

        # Dynamic or static threshold
        if self.dynamic:
            self.rightCall = self.call_dynamic

        # Global or local threshold
        if self.globalT:
            self.sliceT = 0
            self.meanOrNot = lambda arr: numpy.diag(arr[:, 0, :, 0]).mean(
            ) * numpy.ones((arr.shape[1], 1))

        else:
            self.sliceT = slice(None)
            self.meanOrNot = lambda arr: numpy.diag(arr[:, 0, :, 0])[:, numpy.
                                                                     newaxis]

    def __call__(self, g_ij, x_i, x_j):
        r"""Evaluate the sigmoidal function for the arg ``x``. The equation being
        evaluated has the following form:

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

        """
        return self.rightCall(g_ij, x_i, x_j)

    def call_static(self, g_ij, x_i, x_j):
        """Static threshold."""

        A_j = self.H * (self.Q + numpy.tanh(self.G * (self.P * x_j \
            - self.theta[self.sliceT,numpy.newaxis])))

        return (g_ij.transpose((2, 1, 0, 3)) * A_j).sum(axis=0)

    def call_dynamic(self, g_ij, x_i, x_j):
        """Dynamic threshold as state variable given by the second state variable.
        With the coupling term, returns the direct node output for the dynamic threshold.
        """

        A_j = self.H * (self.Q + numpy.tanh(self.G * (self.P * x_j[:,0,:,:] \
            - x_j[:,1,self.sliceT,:])[:,numpy.newaxis,:,:]))

        c_0 = (g_ij[:, 0] * A_j[:, 0]).sum(axis=0)
        c_1 = self.meanOrNot(A_j)
        return numpy.array([c_0, c_1])
Exemple #7
0
class HindmarshRose(models.Model):
    """
    The Hindmarsh-Rose model is a mathematically simple model for repetitive
    bursting.
    
    .. [HR_1984] Hindmarsh, J. L., and Rose, R. M., *A model of neuronal 
        bursting using three coupled first order differential equations*, 
        Proceedings of the Royal society of London. Series B. Biological 
        sciences 221: 87, 1984.
    
    The models (:math:`x`, :math:`y`) phase-plane, including a representation of
    the vector field as well as its nullclines, using default parameters, can be
    seen below:
        
        .. _phase-plane-HMR:
        .. figure :: img/HindmarshRose_01_mode_0_pplane.svg
            :alt: Hindmarsh-Rose phase plane (x, y)
            
            The (:math:`x`, :math:`y`) phase-plane for the Hindmarsh-Rose 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:: HindmarshRose.__init__
    .. automethod:: HindmarshRose.dfun
    
    """
    _ui_name = "Hindmarsh-Rose"
    ui_configurable_parameters = ['r', 'a', 'b', 'c', 'd', 's', 'x_1']

    #Define traited attributes for this model, these represent possible kwargs.
    r = arrays.FloatArray(
        label=":math:`r`",
        default=numpy.array([0.001]),
        range=basic.Range(lo=0.0, hi=1.0, step=0.001),
        doc="""Adaptation parameter, governs time-scale of the state variable
        :math:`z`.""",
        order=1)

    a = arrays.FloatArray(
        label=":math:`a`",
        default=numpy.array([1.0]),
        range=basic.Range(lo=0.0, hi=1.0, step=0.01),
        doc="""Dimensionless parameter, governs x-nullcline""",
        order=2)

    b = arrays.FloatArray(
        label=":math:`b`",
        default=numpy.array([3.0]),
        range=basic.Range(lo=0.0, hi=3.0, step=0.01),
        doc="""Dimensionless parameter, governs x-nullcline""",
        order=3)

    c = arrays.FloatArray(
        label=":math:`c`",
        default=numpy.array([1.0]),
        range=basic.Range(lo=0.0, hi=1.0, step=0.01),
        doc="""Dimensionless parameter, governs y-nullcline""",
        order=4)

    d = arrays.FloatArray(
        label=":math:`d`",
        default=numpy.array([5.0]),
        range=basic.Range(lo=0.0, hi=5.0, step=0.01),
        doc="""Dimensionless parameter, governs y-nullcline""",
        order=5)

    s = arrays.FloatArray(label=":math:`s`",
                          default=numpy.array([1.0]),
                          range=basic.Range(lo=0.0, hi=1.0, step=0.01),
                          doc="""Adaptation parameter, governs feedback""",
                          order=6)

    x_1 = arrays.FloatArray(label=":math:`x_{1}`",
                            default=numpy.array([-1.6]),
                            range=basic.Range(lo=-1.6, hi=1.0, step=0.01),
                            doc="""Governs leftmost equilibrium point of x""",
                            order=7)

    #Used for phase-plane axis ranges and to bound random initial() conditions.
    state_variable_range = basic.Dict(
        label="State Variable ranges [lo, hi]",
        default={
            "x": numpy.array([-4.0, 4.0]),
            "y": numpy.array([-60.0, 20.0]),
            "z": numpy.array([-2.0, 18.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.""",
        order=8)

    variables_of_interest = arrays.IntegerArray(
        label="Variables watched by Monitors",
        range=basic.Range(lo=0, hi=3, 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:`x = 0`,
        :math:`y = 1`,and :math:`z = 2`.""",
        order=9)

    #    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 HindmarshRose model's traited attributes, any provided
        as keywords will overide their traited default.
        
        """
        LOG.info('%s: initing...' % str(self))
        super(HindmarshRose, self).__init__(**kwargs)

        self._state_variables = ["x", "y", "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):
        """
        As in the FitzHugh-Nagumo model ([FH_1961]_), :math:`x` and :math:`y`
        signify the membrane potential and recovery variable respectively.
        Unlike FitzHugh-Nagumo model, the recovery variable :math:`y` is
        quadratic, modelling subthreshold inward current. The third
        state-variable, :math:`z` signifies a slow outward current which leads
        to adaptation ([HR_1984]_):
            
            .. math::
                \\dot{x} &= y - a \\, x^3 + b \\, x^2 - z + I \\\\
                \\dot{y} &= c - d \\, x^2 - y \\\\
                \\dot{z} &= r \\, ( s \\, (x - x_1) - z )
        
        where external currents :math:`I` provide the entry point for local and
        long-range connectivity. Default parameters are set as per Figure 6 of
        [HR_1984]_ so that the model shows repetitive bursting when :math:`I=2`.
        
        """

        x = state_variables[0, :]
        y = state_variables[1, :]
        z = state_variables[2, :]

        c_0 = coupling[0, :]

        dx = y - self.a * x**3 + self.b * x**2 - z + c_0 + local_coupling * x
        dy = self.c - self.d * x**2 - y
        dz = self.r * (self.s * (x - self.x_1) - z)

        derivative = numpy.array([dx, dy, dz])

        return derivative
class Integrator(core.Type):
    """
    The Integrator class is a base class for the integration methods...

    .. [1] Kloeden and Platen, Springer 1995, *Numerical solution of stochastic
        differential equations.*

    .. [2] Riccardo Mannella, *Integration of Stochastic Differential Equations
        on a Computer*, Int J. of Modern Physics C 13(9): 1177--1194, 2002.

    .. [3] R. Mannella and V. Palleschi, *Fast and precise algorithm for 
        computer simulation of stochastic differential equations*, Phys. Rev. A
        40: 3381, 1989.

    .. #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:: Integrator.__init__
    .. automethod:: Integrator.scheme

    """
    _base_classes = [
        'Integrator', 'IntegratorStochastic', 'RungeKutta4thOrderDeterministic'
    ]

    dt = basic.Float(
        label="Integration-step size (ms)",
        default=0.01220703125,  #0.015625,
        #range = basic.Range(lo= 0.0048828125, hi=0.244140625, step= 0.1, base=2.)
        required=True,
        doc="""The step size used by the integration routine in ms. This
        should be chosen to be small enough for the integration to be
        numerically stable. It is also necessary to consider the desired sample
        period of the Monitors, as they are restricted to being integral
        multiples of this value. The default value is set such that all built-in
        models are numerically stable with there default parameters and because
        it is consitent with Monitors using sample periods corresponding to
        powers of 2 from 128 to 4096Hz.""")

    clamped_state_variable_indices = arrays.IntegerArray(
        label=
        "indices of the state variables to be clamped by the integrators to the values in the clamped_values array",
        default=None,
        order=-1)

    clamped_state_variable_values = arrays.FloatArray(
        label="The values of the state variables which are clamped ",
        default=None,
        order=-1)

    def __init__(self, **kwargs):
        """Integrators are intialized using their integration step, dt."""
        super(Integrator, self).__init__(**kwargs)
        LOG.debug(str(kwargs))

    def __repr__(self):
        """A formal, executable, representation of a Model object."""
        class_name = self.__class__.__name__
        traited_kwargs = self.trait.keys()
        formal = class_name + "(" + "=%s, ".join(traited_kwargs) + "=%s)"
        return formal % eval("(self." + ", self.".join(traited_kwargs) + ")")

    def __str__(self):
        """An informal, human readable, representation of a Model object."""
        class_name = self.__class__.__name__
        traited_kwargs = self.trait.keys()
        informal = class_name + "(" + ", ".join(traited_kwargs) + ")"
        return informal

    def scheme(self, X, dfun, coupling, local_coupling, stimulus):
        """
        The scheme of integrator should take a state and provide the next
        state in time, e.g. for a differential equation, scheme should take
        :math:`X` and provide an appropriate :math:`X + dX` (dfun in the code).

        """

        pass

    def clamp_state(self, X):
        if self.clamped_state_variable_values is not None:
            X[self.
              clamped_state_variable_indices] = self.clamped_state_variable_values
Exemple #9
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] 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 [Breaksetal_2003]_. All equations and parameters 
    are non-dimensional and normalized.
    
    .. 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.
    
    .. automethod:: __init__
    
    """

    _ui_name = "Larter-Breakspear"
    ui_configurable_parameters = [
        'gCa', 'gK', 'gL', 'phi', 'gNa', 'TK', 'TCa', 'TNa', 'VCa', 'VK', 'VL',
        'VNa', 'd_K', 'tau_K', 'd_Na', 'd_Ca', 'aei', 'aie', 'b', 'c', 'ane',
        'ani', 'aee', 'Iext', 'rNMDA', 'VT', 'd_V', 'ZT', 'd_Z', 'beta',
        'Q_max'
    ]

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

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

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

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

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

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

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

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

    VCa = arrays.FloatArray(label=":matj:`V_{Ca}`",
                            default=numpy.array([1.0]),
                            range=basic.Range(lo=0.9, hi=1.1, step=0.05),
                            doc="""Ca Nernst potential.""")

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

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

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

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

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

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

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

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

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

    b = arrays.FloatArray(label=":math:`b`",
                          default=numpy.array([0.01]),
                          range=basic.Range(lo=0.0001, hi=0.1, step=0.0001),
                          doc="""Time constant scaling factor.""")

    c = arrays.FloatArray(
        label=":math:`c`",
        default=numpy.array([0.0]),
        range=basic.Range(lo=0.0, hi=0.2, step=0.05),
        doc="""Strength of excitatory coupling. Balance between internal and
        local (and global) coupling strength.""")

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

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

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

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

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

    VT = arrays.FloatArray(
        label=":math:`V_{T}`",
        default=numpy.array([0.54]),
        range=basic.Range(lo=0.0, hi=0.7, step=0.01),
        doc="""Threshold potential (mean) for excitatory neurons.""")

    d_V = arrays.FloatArray(
        label=":math:`\\delta_{V}`",
        default=numpy.array([0.65]),
        range=basic.Range(lo=0.1, hi=0.7, step=0.05),
        doc="""Variance of the excitatory threshold. It is one of the main
        parameters explored in [Breaksetal_2003]_.""")

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

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

    beta = arrays.FloatArray(label=":math:`\\beta`",
                             default=numpy.array([0.0]),
                             range=basic.Range(lo=0.0, hi=0.1, step=0.01),
                             doc="""Random modulation of subcortical input.""")

    # NOTE: the values were not in the article.
    #I took these ones from DESTEXHE 2001
    Q_max = arrays.FloatArray(
        label=":math:`Q_{max}`",
        default=numpy.array([0.001]),
        range=basic.Range(lo=0.001, hi=0.005, step=0.0001),
        doc=
        """Maximal firing rate for excitatory and inihibtory populations (kHz)"""
    )

    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.""")

    #Informational attribute, used for phase-plane and initial()
    state_variable_range = basic.Dict(
        label="State Variable ranges [lo, hi]",
        default={
            "V": numpy.array([-2.0, 2.0]),
            "W": numpy.array([-10.0, 10.0]),
            "Z": 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."""
    )

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

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

        super(LarterBreakspear, 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_{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{X-T_{ion}}{\\delta_{ion}})
            
        See Equations (7), (3), (6) and (2) respectively in [Breaksetal_2003]_.
        Pag: 705-706
        
        """
        V = state_variables[0, :]
        W = state_variables[1, :]
        Z = state_variables[2, :]

        c_0 = coupling[0, :]
        lc_0 = local_coupling

        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))

        Q_V = 0.5 * self.Q_max * (1 + numpy.tanh((V - self.VT) / self.d_V))
        Q_Z = 0.5 * self.Q_max * (1 + numpy.tanh((Z - self.ZT) / self.d_Z))

        dV = - (self.gCa + (1.0 - self.c) * self.rNMDA * self.aee * Q_V + \
        self.c * self.rNMDA * self.aee * (lc_0 * Q_V + 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 * Q_V + self.c * self.aee * (lc_0 * Q_V + c_0)) *\
        (V - self.VNa) + self.aei * Z * Q_Z + self.ane * self.Iext

        # Single node equation
        #        dV = - (self.gCa + (1.0 - self.c) * self.rNMDA * self.aee * Q_V ) *  \
        #        m_Ca * (V - self.VCa) - self.gK * W * (V - self.VK) -  self.gL * \
        #        (V - self.VL) + self.aei * Z * Q_Z + self.ane * self.Iext

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

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

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

        return derivative
Exemple #10
0
class Tracts(MappedType):
    """Datatype for results of diffusion imaging tractography."""

    MAX_N_VERTICES = 2 ** 16

    vertices = arrays.PositionArray(
        label="Vertex positions",
        file_storage=core.FILE_STORAGE_EXPAND,
        order=-1,
        doc="""An array specifying coordinates for the tracts vertices.""")

    tract_start_idx = arrays.IntegerArray(
        label="Tract starting indices",
        order=-1,
        doc="""Where is the first vertex of a tract in the vertex array""")

    tract_region = arrays.IntegerArray(
        label="Tract region index",
        required=False,
        order=-1,
        doc="""
        An index used to find quickly all tract emerging from a region
        tract_region[i] is the region of the i'th tract. -1 represents the background
        """
    )

    region_volume_map = RegionVolumeMapping(
        label="Region volume Mapping used to create the tract_region index",
        required=False,
        order=-1
    )


    @property
    def tracts_count(self):
        return len(self.tract_start_idx) - 1

    def get_tract(self, i):
        """
        get a tract by index
        """
        start, end = self.tract_start_idx[i:i + 2]
        return self.get_data('vertices', slice(start, end), close_file=False)

    def _get_tract_ids(self, region_id):
        tract_ids = numpy.where(self.tract_region == region_id)[0]
        return tract_ids

    def _get_track_ids_webgl_chunks(self, region_id):
        """
        webgl can draw up to MAX_N_VERTICES vertices in a draw call.
        Assuming that no one track exceeds this limit we partition
        the tracts such that each track bundle has fewer than the max vertices
        :return: the id's of the tracts in a region chunked by the above criteria.
        """
        # We have to split the int64 range in many uint16 ranges
        tract_ids = self._get_tract_ids(region_id)

        tract_id_chunks = []
        chunk = []

        count = 0
        tidx = 0
        tract_start_idx = self.tract_start_idx  # traits make . expensive

        while tidx < len(tract_ids):  # tidx always grows
            tid = tract_ids[tidx]
            start, end = tract_start_idx[tid:tid + 2]
            track_len = end - start

            if track_len >= self.MAX_N_VERTICES:
                raise ValueError('cannot yet handle very long tracts')

            count += track_len

            if count < self.MAX_N_VERTICES:
                # add this track to the current chunk and advance to next track
                chunk.append(tid)
                tidx += 1
            else:
                # stay with the same track and start a new chunk
                tract_id_chunks.append(chunk)
                chunk = []
                count = 0

        if chunk:
            tract_id_chunks.append(chunk)

        # q = []
        # for a in tract_id_chunks:
        #     q.extend(a)
        # assert (numpy.array(q) == tract_ids).all()

        return tract_id_chunks

    def get_vertices(self, region_id, slice_number=0):
        """
        Concatenates the vertices for all tracts starting in region_id.
        Returns a completely flat array as required by gl.bindBuffer apis
        """
        region_id = int(region_id)
        slice_number = int(slice_number)

        chunks = self._get_track_ids_webgl_chunks(region_id)
        tract_ids = chunks[slice_number]

        tracts_vertices = []
        for tid in tract_ids:
            tracts_vertices.append(self.get_tract(tid))

        self.close_file()

        if tracts_vertices:
            tracts_vertices = numpy.concatenate(tracts_vertices)
            return tracts_vertices.ravel()
        else:
            return numpy.array([])

    def get_line_starts(self, region_id):
        """
        Returns a compact representation of the element buffers required to draw the streams via gl.drawElements
        A list of indices that describe where the first vertex for a tract is in the vertex array returned by
        get_tract_vertices_starting_in_region
        """
        region_id = int(region_id)
        chunks = self._get_track_ids_webgl_chunks(region_id)
        chunk_line_starts = []
        tract_start_idx = self.tract_start_idx  # traits make the . expensive

        for tract_ids in chunks:
            offset = 0
            tract_offsets = [0]

            for tid in tract_ids:
                start, end = tract_start_idx[tid:tid + 2]
                track_len = end - start
                offset += track_len
                tract_offsets.append(offset)

            chunk_line_starts.append(tract_offsets)

        return chunk_line_starts

    def get_urls_for_rendering(self):
        return ('/flow/read_datatype_attribute/' + self.gid + '/get_line_starts/False',
                '/flow/read_binary_datatype_attribute/' + self.gid + '/get_vertices')
Exemple #11
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
    
    """

    _ui_name = "Generic 2d Oscillator"
    ui_configurable_parameters = [
        'tau', 'a', 'b', 'omega', 'upsilon', 'gamma', 'eta'
    ]
    #Define traited attributes for this model, these represent possible kwargs.
    tau = arrays.FloatArray(
        label=":math:`\\tau`",
        default=numpy.array([1.25]),
        range=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 = arrays.FloatArray(label=":math:`a`",
                          default=numpy.array([1.05]),
                          range=basic.Range(lo=-1.0, hi=1.5, step=0.01),
                          doc="""ratio a/b gives W-nullcline slope""")

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

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

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

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

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

    variables_of_interest = arrays.IntegerArray(
        label="Variables watched by Monitors.",
        range=basic.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.""")

    #Informational attribute, used for phase-plane and initial()
    state_variable_range = basic.Dict(
        label="State Variable ranges [lo, hi]",
        default={
            "V": numpy.array([-2.0, 4.0]),
            "W": numpy.array([-6.0, 6.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."""
    )

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

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

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

        self._state_variables = ["V", "W"]
        self._nvar = 2  #len(self._state_variables)
        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 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
class Monitor(core.Type):
    """
    Abstract base class for monitor implementations.

    """

    # list of class names not shown in UI
    _base_classes = ['Monitor', 'Projection', 'ProgressLogger']

    period = basic.Float(
        label = "Sampling period (ms)", order=10,
        default = 0.9765625, #ms. 0.9765625 => 1024Hz #ms, 0.5 => 2000Hz
        doc = """Sampling period in milliseconds, must be an integral multiple
        of integration-step size. As a guide: 2048 Hz => 0.48828125 ms ;  
        1024 Hz => 0.9765625 ms ; 512 Hz => 1.953125 ms.""")

    variables_of_interest = arrays.IntegerArray(
        label = "Model variables to watch", order=11,
        doc = ("Indices of model's variables of interest (VOI) that this monitor should record. "
               "Note that the indices should start at zero, so that if a model offers VOIs V, W and "
               "V+W, and W is selected, and this monitor should record W, then the correct index is 0.")
        #order = -1
    )

    istep = None
    dt = None
    voi = None
    _stock = numpy.empty([])

    def __str__(self):
        clsname = self.__class__.__name__
        return '%s(period=%f, voi=%s)' % (clsname, self.period, self.variables_of_interest.tolist())

    def config_for_sim(self, simulator):
        """Configure monitor for given simulator.

        Grab the Simulator's integration step size. Set the monitor's variables
        of interest based on the Monitor's 'variables_of_interest' attribute, if
        it was specified, otherwise use the 'variables_of_interest' specified 
        for the Model. Calculate the number of integration steps (isteps)
        between returns by the record method. This method is called from within
        the the Simulator's configure() method.

        """
        self.dt = simulator.integrator.dt
        self.istep = iround(self.period / self.dt)
        self.voi = self.variables_of_interest
        if self.voi is None or self.voi.size == 0:
            self.voi = numpy.r_[:len(simulator.model.variables_of_interest)]

    def record(self, step, observed):
        """Record a sample of the observed state at given step.

        This is a final method called by the simulator to obtain samples from a
        monitor instance. Monitor subclasses should not override this method, but
        rather implement the `sample` method.

        """

        return self.sample(step, observed)

    def sample(self, step, state):
        """
        This method provides monitor output, and should be overridden by subclasses.

        """

        raise NotImplementedError(
            "The Monitor base class does not provide any observation model and "
            "should be subclasses with an implementation of the `sample` method.")

    def create_time_series(self, storage_path, connectivity=None, surface=None,
                           region_map=None, region_volume_map=None):
        """
        Create a time series instance that will be populated by this monitor
        :param surface: if present a TimeSeriesSurface is returned
        :param connectivity: if present a TimeSeriesRegion is returned
        Otherwise a plain TimeSeries will be returned
        """
        if surface is not None:
            return TimeSeriesSurface(storage_path=storage_path,
                                     surface=surface,
                                     sample_period=self.period,
                                     title='Surface ' + self.__class__.__name__,
                                     **self._transform_user_tags())
        if connectivity is not None:
            return TimeSeriesRegion(storage_path=storage_path,
                                    connectivity=connectivity,
                                    region_mapping=region_map,
                                    region_mapping_volume=region_volume_map,
                                    sample_period=self.period,
                                    title='Regions ' + self.__class__.__name__,
                                    **self._transform_user_tags())

        return TimeSeries(storage_path=storage_path,
                          sample_period=self.period,
                          title=' ' + self.__class__.__name__,
                          **self._transform_user_tags())

    def _transform_user_tags(self):
        return {}
class SpatialAverage(Monitor):
    """
    Monitors the averaged value for the models variable of interest over sets of
    nodes -- defined by spatial_mask. This is primarily intended for use with
    surface simulations, with a default behaviour, when no spatial_mask is
    specified, of using surface.region_mapping in order to reduce a surface
    simulation back to a single average timeseries for each region in the
    associated Connectivity. However, any vector of length nodes containing
    integers, from a set contiguous from zero, specifying the new grouping to
    which each node belongs should work.

    Additionally, this monitor temporally sub-samples the simulation every `istep` 
    integration steps.

    """
    _ui_name = "Spatial average with temporal sub-sample"

    spatial_mask = arrays.IntegerArray( #TODO: Check it's a vector of length Nodes (like region mapping for surface)
        label = "An index mask of nodes into areas",
        doc = """A vector of length==nodes that assigns an index to each node
            specifying the "region" to which it belongs. The default usage is
            for mapping a surface based simulation back to the regions used in 
            its `Long-range Connectivity.`""")
    
    default_mask = basic.Enumerate(
                              label = "Default Mask",
                              options = ["cortical", "hemispheres"],
                              default = ["hemispheres"],
                              doc = r"""Fallback in case spatial mask is none and no surface provided 
                              to use either connectivity hemispheres or cortical attributes.""",
                              order = -1)

    def config_for_sim(self, simulator):

        # initialize base attributes
        super(SpatialAverage, self).config_for_sim(simulator)
        self.is_default_special_mask = False

        # setup given spatial mask or default to region mapping
        if self.spatial_mask.size == 0:
            self.is_default_special_mask = True
            if not (simulator.surface is None):
                self.spatial_mask = simulator.surface.region_mapping
            else:
                conn = simulator.connectivity
                if self.default_mask[0] == 'cortical':
                    if conn is not None and conn.cortical is not None and conn.cortical.size > 0:
                        ## Use as spatial-mask cortical/non cortical areas
                        self.spatial_mask = [int(c) for c in conn.cortical]
                    else:
                        msg = "Must fill Spatial Mask parameter for non-surface simulations when using SpatioTemporal monitor!"
                        raise Exception(msg)
                if self.default_mask[0] == 'hemispheres':
                    if conn is not None and conn.hemispheres is not None and conn.hemispheres.size > 0:
                        ## Use as spatial-mask left/right hemisphere
                        self.spatial_mask = [int(h) for h in conn.hemispheres]
                    else:
                        msg = "Must fill Spatial Mask parameter for non-surface simulations when using SpatioTemporal monitor!"
                        raise Exception(msg)

        number_of_nodes = simulator.number_of_nodes
        if self.spatial_mask.size != number_of_nodes:
            msg = "spatial_mask must be a vector of length number_of_nodes."
            raise Exception(msg)

        areas = numpy.unique(self.spatial_mask)
        number_of_areas = len(areas)
        if not numpy.all(areas == numpy.arange(number_of_areas)):
            msg = ("Areas in the spatial_mask must be specified as a "
                    "contiguous set of indices starting from zero.")
            raise Exception(msg)

        util.log_debug_array(LOG, self.spatial_mask, "spatial_mask", owner=self.__class__.__name__)
        spatial_sum = numpy.zeros((number_of_nodes, number_of_areas))
        spatial_sum[numpy.arange(number_of_nodes), self.spatial_mask] = 1
        spatial_sum = spatial_sum.T
        util.log_debug_array(LOG, spatial_sum, "spatial_sum")
        nodes_per_area = numpy.sum(spatial_sum, axis=1)[:, numpy.newaxis]
        self.spatial_mean = spatial_sum / nodes_per_area
        util.log_debug_array(LOG, self.spatial_mean, "spatial_mean", owner=self.__class__.__name__)


    def sample(self, step, state):
        if step % self.istep == 0:
            time = step * self.dt
            monitored_state = numpy.dot(self.spatial_mean, state[self.voi, :])
            return [time, monitored_state.transpose((1, 0, 2))]

    def create_time_series(self, storage_path, connectivity=None, surface=None,
                           region_map=None, region_volume_map=None):
        if self.is_default_special_mask:
            return TimeSeriesRegion(storage_path=storage_path,
                                    sample_period=self.period,
                                    region_mapping=region_map,
                                    region_mapping_volume=region_volume_map,
                                    title='Regions ' + self.__class__.__name__,
                                    connectivity=connectivity,
                                    **self._transform_user_tags())
        else:
            # mask does not correspond to the number of regions
            # let the parent create a plain TimeSeries
            return super(SpatialAverage, self).create_time_series(storage_path)
class BrunelWang(models.Model):
    """
    .. [DJ_2012] Deco G and Jirsa V. *Ongoing Cortical 
        Activity at Rest: Criticality, Multistability, and Ghost Attractors*. 
        Journal of Neuroscience 32, 3366-3375, 2012.
        
    .. [BW_2001] Brunel N and Wang X-J. *Effects of neuromodulation in a cortical 
       network model of object working memory dominated by recurrent inhibition*. 
       Journal of Computational Neuroscience 11, 63–85, 2001.
           
    Each node consists of one excitatory (E) and one inhibitory (I) pool.

    At a global level, it uses Hagmann's 2008 connectome 66 areas(hagmann_struct.csv) 
    with a global scaling weight (W) of 1.65.
    

    """

    _ui_name = "Deco-Jirsa (Mean-Field Brunel-Wang)"
    ui_configurable_parameters = [
        'tau', 'calpha', 'cbeta', 'cgamma', 'tauNMDArise', 'tauNMDAdecay',
        'tauAMPA', 'tauGABA', 'VE', 'VI', 'VL', 'Vthr', 'Vreset', 'gNMDA_e',
        'gNMDA_i', 'gGABA_e', 'gGABA_i', 'gAMPArec_e', 'gAMPArec_i',
        'gAMPAext_e', 'gAMPAext_i', 'gm_e', 'gm_i', 'Cm_e', 'Cm_i', 'taum_e',
        'taum_i', 'taurp_e', 'taurp_i', 'Cext', 'C', 'nuext', 'wplus',
        'wminus', 'W', 'variables_of_interest'
    ]

    #Define traited attributes for this model, these represent possible kwargs.
    tau = arrays.FloatArray(
        label=":math:`\\tau`",
        default=numpy.array([
            1.25,
        ]),
        range=basic.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.""",
        order=1)

    calpha = arrays.FloatArray(label=":math:`c_{\\alpha}`",
                               default=numpy.array([
                                   0.5,
                               ]),
                               range=basic.Range(lo=0.4, hi=0.5, step=0.05),
                               doc="""NMDA saturation parameter (kHz)""",
                               order=2)

    cbeta = arrays.FloatArray(label=":math:`c_{\\beta}`",
                              default=numpy.array([
                                  0.062,
                              ]),
                              range=basic.Range(lo=0.06, hi=0.062, step=0.002),
                              doc="""Inverse MG2+ blockade potential(mV-1)""",
                              order=3)

    cgamma = arrays.FloatArray(label=":math:`c_{\\gamma}`",
                               default=numpy.array([
                                   0.2801120448,
                               ]),
                               range=basic.Range(lo=0.2801120440,
                                                 hi=0.2801120448,
                                                 step=0.0000000001),
                               doc="""Strength of Mg2+ blockade""",
                               order=-1)

    tauNMDArise = arrays.FloatArray(label=":math:`\\tau_{NMDA_{rise}}`",
                                    default=numpy.array([
                                        2.0,
                                    ]),
                                    range=basic.Range(lo=0.0, hi=2.0,
                                                      step=0.5),
                                    doc="""NMDA time constant (rise) (ms)""",
                                    order=4)

    tauNMDAdecay = arrays.FloatArray(label=":math:`\\tau_{NMDA_{decay}}`",
                                     default=numpy.array([
                                         100.,
                                     ]),
                                     range=basic.Range(lo=50.0,
                                                       hi=100.0,
                                                       step=10.0),
                                     doc="""NMDA time constant (decay) (ms)""",
                                     order=5)

    tauAMPA = arrays.FloatArray(label=":math:`\\tau_{AMPA}`",
                                default=numpy.array([
                                    2.0,
                                ]),
                                range=basic.Range(lo=1.0, hi=2.0, step=1.0),
                                doc="""AMPA time constant (decay) (ms)""",
                                order=6)

    tauGABA = arrays.FloatArray(label=":math:`\\tau_{GABA}`",
                                default=numpy.array([
                                    10.0,
                                ]),
                                range=basic.Range(lo=5.0, hi=15.0, step=1.0),
                                doc="""GABA time constant (decay) (ms)""",
                                order=7)

    VE = arrays.FloatArray(label=":math:`V_E`",
                           default=numpy.array([
                               0.0,
                           ]),
                           range=basic.Range(lo=0.0, hi=10.0, step=2.0),
                           doc="""Extracellular potential (mV)""",
                           order=8)

    VI = arrays.FloatArray(label=":math:`V_I`",
                           default=numpy.array([
                               -70.0,
                           ]),
                           range=basic.Range(lo=-70.0, hi=-50.0, step=5.0),
                           doc=""".""",
                           order=-1)

    VL = arrays.FloatArray(label=":math:`V_L`",
                           default=numpy.array([
                               -70.0,
                           ]),
                           range=basic.Range(lo=-70.0, hi=-50.0, step=5.0),
                           doc="""Resting potential (mV)""",
                           order=-1)

    Vthr = arrays.FloatArray(label=":math:`V_{thr}`",
                             default=numpy.array([
                                 -50.0,
                             ]),
                             range=basic.Range(lo=-50.0, hi=-30.0, step=5.0),
                             doc="""Threshold potential (mV)""",
                             order=-1)

    Vreset = arrays.FloatArray(label=":math:`V_{reset}`",
                               default=numpy.array([
                                   -55.0,
                               ]),
                               range=basic.Range(lo=-70.0, hi=-30.0, step=5.0),
                               doc="""Reset potential (mV)""",
                               order=9)

    gNMDA_e = arrays.FloatArray(
        label=":math:`g_{NMDA_{e}}`",
        default=numpy.array([
            0.327,
        ]),
        range=basic.Range(lo=0.320, hi=0.350, step=0.0035),
        doc="""NMDA conductance on post-synaptic excitatory (nS)""",
        order=-1)

    gNMDA_i = arrays.FloatArray(
        label=":math:`g_{NMDA_{i}}`",
        default=numpy.array([
            0.258,
        ]),
        range=basic.Range(lo=0.250, hi=0.270, step=0.002),
        doc="""NMDA conductance on post-synaptic inhibitory (nS)""",
        order=-1)

    gGABA_e = arrays.FloatArray(
        label=":math:`g_{GABA_{e}}`",
        default=numpy.array([
            1.25 * 3.5,
        ]),
        range=basic.Range(lo=1.25, hi=4.375, step=0.005),
        doc="""GABA conductance on excitatory post-synaptic (nS)""",
        order=10)

    gGABA_i = arrays.FloatArray(
        label=":math:`g_{GABA_{i}}`",
        default=numpy.array([
            0.973 * 3.5,
        ]),
        range=basic.Range(lo=0.9730, hi=3.4055, step=0.0005),
        doc="""GABA conductance on inhibitory post-synaptic (nS)""",
        order=11)

    gAMPArec_e = arrays.FloatArray(
        label=":math:`g_{AMPA_{rec_e}}`",
        default=numpy.array([
            0.104,
        ]),
        range=basic.Range(lo=0.1, hi=0.11, step=0.001),
        doc="""AMPA(recurrent) cond on post-synaptic (nS)""",
        order=-1)

    gAMPArec_i = arrays.FloatArray(
        label=":math:`g_{AMPA_{rec_i}}`",
        default=numpy.array([
            0.081,
        ]),
        range=basic.Range(lo=0.081, hi=0.1, step=0.001),
        doc="""AMPA(recurrent) cond on post-synaptic (nS)""",
        order=-1)

    gAMPAext_e = arrays.FloatArray(
        label=":math:`g_{AMPA_{ext_e}}`",
        default=numpy.array([
            2.08 * 1.2,
        ]),
        range=basic.Range(lo=2.08, hi=2.496, step=0.004),
        doc="""AMPA(external) cond on post-synaptic (nS)""",
        order=12)

    gAMPAext_i = arrays.FloatArray(
        label=":math:`g_{AMPA_{ext_i}}`",
        default=numpy.array([
            1.62 * 1.2,
        ]),
        range=basic.Range(lo=1.62, hi=1.944, step=0.004),
        doc="""AMPA(external) cond on post-synaptic (nS)""",
        order=13)

    gm_e = arrays.FloatArray(label=":math:`gm_e`",
                             default=numpy.array([
                                 25.0,
                             ]),
                             range=basic.Range(lo=20.0, hi=25.0, step=1.0),
                             doc="""Excitatory membrane conductance (nS)""",
                             order=13)

    gm_i = arrays.FloatArray(label=":math:`gm_i`",
                             default=numpy.array([
                                 20.,
                             ]),
                             range=basic.Range(lo=15.0, hi=21.0, step=1.0),
                             doc="""Inhibitory membrane conductance (nS)""",
                             order=14)

    Cm_e = arrays.FloatArray(label=":math:`Cm_e`",
                             default=numpy.array([
                                 500.,
                             ]),
                             range=basic.Range(lo=200.0, hi=600.0, step=50.0),
                             doc="""Excitatory membrane capacitance (mF)""",
                             order=15)

    Cm_i = arrays.FloatArray(label=":math:`Cm_i`",
                             default=numpy.array([
                                 200.,
                             ]),
                             range=basic.Range(lo=150.0, hi=250.0, step=50.0),
                             doc="""Inhibitory membrane capacitance (mF)""",
                             order=16)

    taum_e = arrays.FloatArray(label=":math:`\\tau_m_{e}`",
                               default=numpy.array([
                                   20.,
                               ]),
                               range=basic.Range(lo=10.0, hi=25.0, step=5.0),
                               doc="""Excitatory membrane leak time (ms)""",
                               order=17)

    taum_i = arrays.FloatArray(label=":math:`\\tau_m_{i}`",
                               default=numpy.array([
                                   10.0,
                               ]),
                               range=basic.Range(lo=5.0, hi=15.0, step=5.),
                               doc="""Inhibitory Membrane leak time (ms)""",
                               order=18)

    taurp_e = arrays.FloatArray(
        label=":math:`\\tau_{rp}_{e}`",
        default=numpy.array([
            2.0,
        ]),
        range=basic.Range(lo=0.0, hi=4.0, step=1.),
        doc="""Excitatory absolute refractory period (ms)""",
        order=19)

    taurp_i = arrays.FloatArray(
        label=":math:`\\tau_{rp}_{i}`",
        default=numpy.array([
            1.0,
        ]),
        range=basic.Range(lo=0.0, hi=2.0, step=0.5),
        doc="""Inhibitory absolute refractory period (ms)""",
        order=20)

    Cext = arrays.IntegerArray(
        label=":math:`C_{ext}`",
        default=numpy.array([
            800,
        ]),
        range=basic.Range(lo=500, hi=1200, step=100),
        doc="""Number of external (excitatory) connections""",
        order=-1)

    C = arrays.IntegerArray(label=":math:`C`",
                            default=numpy.array([
                                200,
                            ]),
                            range=basic.Range(lo=100, hi=500, step=100),
                            doc="Number of neurons for each node",
                            order=-1)

    nuext = arrays.FloatArray(label=":math:`\\nu_{ext}`",
                              default=numpy.array([
                                  0.003,
                              ]),
                              range=basic.Range(lo=0.002, hi=0.01, step=0.001),
                              doc="""External firing rate (kHz)""",
                              order=-1)

    wplus = arrays.FloatArray(
        label=":math:`w_{+}`",
        default=numpy.array([
            1.5,
        ]),
        range=basic.Range(lo=0.5, hi=3., step=0.05),
        doc="""Synaptic coupling strength [w+] (dimensionless)""",
        order=-1)

    wminus = arrays.FloatArray(
        label=":math:`w_{-}`",
        default=numpy.array([
            1.,
        ]),
        range=basic.Range(lo=0.2, hi=2., step=0.05),
        doc="""Synaptic coupling strength [w-] (dimensionless)""",
        order=-1)

    #Informational attribute, used for phase-plane and initial()
    state_variable_range = basic.Dict(
        label="State Variable ranges [lo, hi]",
        default={
            "E": numpy.array([0.001, 0.01]),
            "I": numpy.array([0.001, 0.01])
        },
        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.
            The corresponding state-variable units for this model are kHz.""",
        order=22)

    NMAX = arrays.IntegerArray(
        label=":math:`N_{MAX}`",
        default=numpy.array([
            8,
        ], dtype=numpy.int32),
        range=basic.Range(lo=2, hi=8, step=1),
        doc="""This is a magic number as given in the original code.
        It is used to compute the phi and psi -- computationally expensive -- 
        functions""",
        order=-1)

    pool_nodes = arrays.FloatArray(
        label=":math:`p_{nodes}`",
        default=numpy.array([
            74.0,
        ]),
        range=basic.Range(lo=1.0, hi=74.0, step=1.0),
        doc="""Scale coupling weight sby the number of nodes in the network""",
        order=23)

    a = arrays.FloatArray(label=":math:`a`",
                          default=numpy.array([
                              0.80823563,
                          ]),
                          range=basic.Range(lo=0.80, hi=0.88, step=0.01),
                          doc=""".""",
                          order=-1)

    b = arrays.FloatArray(label=":math:`b`",
                          default=numpy.array([
                              67.06177975,
                          ]),
                          range=basic.Range(lo=66.0, hi=69.0, step=0.5),
                          doc=""".""",
                          order=-1)

    ve = arrays.FloatArray(label=":math:`ve`",
                           default=numpy.array([
                               -52.5,
                           ]),
                           range=basic.Range(lo=-50.0, hi=-45.0, step=0.2),
                           doc=""".""",
                           order=-1)

    vi = arrays.FloatArray(label=":math:`vi`",
                           default=numpy.array([
                               -52.5,
                           ]),
                           range=basic.Range(lo=-50.0, hi=-45.0, step=0.2),
                           doc=""".""",
                           order=-1)

    W = arrays.FloatArray(label=":math:`W`",
                          default=numpy.array([
                              1.65,
                          ]),
                          range=basic.Range(lo=1.4, hi=1.9, step=0.05),
                          doc="""Global scaling weight [W] (dimensionless)""",
                          order=-1)

    variables_of_interest = arrays.IntegerArray(
        label="Variables watched by Monitors.",
        range=basic.Range(lo=0.0, hi=2.0, step=1.0),
        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:`E = 0`
        and :math:`I = 1`.""",
        order=21)

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

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

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

        self._state_variables = ["E", "I"]
        self._nvar = 2

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

        #Derived parameters
        self.crho1_e = None
        self.crho1_i = None
        self.crho2_e = None
        self.crho2_i = None
        self.csigma_e = None
        self.csigma_i = None
        self.tauNMDA = None

        self.Text_e = None
        self.Text_i = None
        self.TAMPA_e = None
        self.TAMPA_i = None
        self.T_ei = None
        self.T_ii = None

        self.pool_fractions = None

        self.psi_table = TabulateInterp()
        self.phi_table = TabulateInterp()

        #NOTE: We could speed up this model simplifying some the phi and psi functions
        #above. However it was decided that functions should be the same as
        # in the original paper.

        # integral
        #self.vector_nerf = lambda z: numpy.exp(z**2) * (scipy.special.erf(z) + 1)
        #integral = lambda x : numpy.float64(quad(self.vector_nerf, float('-Inf') , x, full_output = True)[0])
        #self.vint = numpy.vectorize(integral)

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

    def configure(self):
        """  """
        super(BrunelWang, self).configure()
        self.update_derived_parameters()
        #   self.optimize()

    def optimize(self, fnname='optdfun'):

        decl = "def %s(state_variables, coupling, local_coupling=0.0):\n" % (
            fnname, )

        NoneType = type(None)
        for k in dir(self):
            attr = getattr(self, k)
            if not k[0] == '_' and type(attr) in (numpy.ndarray, NoneType):
                decl += '        %s = %r\n' % (k, attr)

        decl += '\n'.join(inspect.getsource(
            self.dfun).split('\n')[1:]).replace("self.", "")
        dikt = {
            'vint': self.vint,
            'array': numpy.array,
            'int32': numpy.int32,
            'numpy': numpy
        }
        #print decl
        exec decl in dikt
        self.dfun = dikt[fnname]

    def dfun(self, state_variables, coupling, local_coupling=0.0):
        """
        .. math::           
             \\tau_e*\\dot{\\nu_e}(t) &= -\\nu_e(t) + \\phi_e \\\\
             \\tau_i*\\dot{\\nu_i}(t) &= -\\nu_i(t) + \\phi_i \\\\
             \\ve &= - (V_thr - V_reset) \\, \\nu_e \\, \\tau_e + \\mu_e \\\\
             \\vi &= - (V_thr - V_reset) \\, \\nu_i \\, \\tau_i + \\mu_i \\\\
             
             \\tau_X &= \\frac{C_m_X}{g_m_x  \\, S_X} \\\\
             \\S_X &= 1 + Text \\, \\nu_ext + T_ampa \\, \\nu_X + (rho_1 + rho_2) 
                     \\, \\psi(\\nu_X) + T_XI \\, \\nu_I \\\\
             \\mu_X &= \\frac{(Text \\, \\nu_X + T_AMPA \\, \\nu_X + \\rho_1 \\, 
                        \\psi(\\nu_X)) \\, (V_E - V_L)}{S_X} + 
                        \\frac{\\rho_2 \\, \\psi(\nu_X) \\,(\\bar{V_X} - V_L) + 
                        T_xI \\, \\nu_I \\, (V_I - V_L)}{S_X} \\\\
            \\sigma_X^2 &= \\frac{g_AMPA_ext^2(\\bar{V_X} - V_X)^2 \\, C_ext \\, nu_ext
                        \\tau_AMPA^2 \\, \\tau_X}{g_m_X^2 * \\tau_m_X^2} \\\\
            \\rho_1 &= {g_NMDA * C}{g_m_X * J} \\\\
            \\rho_2 &= \\beta \\frac{g_NMDA * C (\\bar{V_X} - V_E)(J - 1)}
                        {g_m_X * J^2} \\\\
            J_X &= 1 + \\gamma \\,\\exp(-\\beta*\\bar{V_X}) \\\\
            \\phi(\mu_X, \\sigma_X) &= (\\tau_rp_X + \\tau_X \\, \\int  
                                         \\exp(u^2) * (\\erf(u) + 1))^-1
        
        The NMDA gating variable
        .. math::
            \\psi(\\nu)
        has been approximated by the exponential function:
        .. math::
            \\psi(\\nu) &= a * (1 - \\exp(-\\b * \\nu)) \\\\ 
            \\a &= 0.80823563 \\\\
            \\b &= 67.06177975
        
        The post-synaptic rate as described by the :math:`\\phi` function 
        constitutes a non-linear input-output relationship between the firing 
        rate of the post-synaptic neuron and the average firing rates 
        :math:`\\nu_{E}` and :math:`\\nu_{I}` of the pre-synaptic excitatory and
        inhibitory neural populations. This input-output function is 
        conceptually equivalent to the simple threshold-linear or sigmoid 
        input-output functions routinely used in firing-rate models. What it is 
        gained from using the integral form is a firing-rate model that captures
        many of the underlying biophysics of the real spiking neurons.[BW_2001]_

        """

        E = state_variables[0, :]
        I = state_variables[1, :]

        c_0 = coupling[0, :]

        # AMPA synapses (E --> E, and E --> I)
        vn_e = E * self.wplus * self.pool_fractions + c_0  # + local_coupling * E
        vn_i = E * self.wminus * self.pool_fractions

        # NMDA synapses (E --> E, and E --> I)
        vN_e = self.psi_table(
            E
        ) * self.wplus * self.pool_fractions + c_0  # + local_coupling * self.psi_table(E)
        vN_i = self.psi_table(E) * self.wminus * self.pool_fractions

        # GABA (A) synapses (I --> E, and I --> I)
        vni_e = I * self.wminus  # I --> E
        vni_i = I * self.wminus  # I --> I

        J_e = 1 + self.cgamma * numpy.exp(-self.cbeta * self.ve)
        J_i = 1 + self.cgamma * numpy.exp(-self.cbeta * self.vi)

        rho1_e = self.crho1_e / J_e
        rho1_i = self.crho1_i / J_i
        rho2_e = self.crho2_e * (self.ve - self.VE) * (J_e - 1) / J_e**2
        rho2_i = self.crho2_i * (self.vi - self.VI) * (J_i - 1) / J_i**2

        vS_e = 1 + self.Text_e * self.nuext + self.TAMPA_e * vn_e + \
                (rho1_e + rho2_e) * vN_e + self.T_ei * vni_e
        vS_i = 1 + self.Text_i * self.nuext + self.TAMPA_i * vn_i + \
                (rho1_i + rho2_i) * vN_i + self.T_ii * vni_i

        vtau_e = self.Cm_e / (self.gm_e * vS_e)
        vtau_i = self.Cm_i / (self.gm_i * vS_i)


        vmu_e = (rho2_e * vN_e * self.ve + self.T_ei * vni_e * self.VI + \
                self.VL) / vS_e

        vmu_i = (rho2_i * vN_i * self.vi + self.T_ii * vni_i * self.VI + \
                self.VL) / vS_i

        vsigma_e = numpy.sqrt((self.ve - self.VE)**2 * vtau_e * \
                        self.csigma_e * self.nuext)
        vsigma_i = numpy.sqrt((self.vi - self.VE)**2 * vtau_i * \
                        self.csigma_i * self.nuext)

        #tauAMPA_over_vtau_e
        k_e = self.tauAMPA / vtau_e
        k_i = self.tauAMPA / vtau_i

        #integration limits
        alpha_e = (self.Vthr - vmu_e) / vsigma_e * (1.0 + 0.5 * k_e) + \
                    1.03 * numpy.sqrt(k_e) - 0.5 * k_e
        alpha_e = numpy.where(alpha_e > 19, 19, alpha_e)
        alpha_i = (self.Vthr - vmu_i) / vsigma_i * (1.0 + 0.5 * k_i) + \
                    1.03 * numpy.sqrt(k_i) - 0.5 * k_i
        alpha_i = numpy.where(alpha_i > 19, 19, alpha_i)

        beta_e = (self.Vreset - vmu_e) / vsigma_e
        beta_e = numpy.where(beta_e > 19, 19, beta_e)

        beta_i = (self.Vreset - vmu_i) / vsigma_i
        beta_i = numpy.where(beta_i > 19, 19, beta_i)

        v_ae = self.phi_table(alpha_e)
        v_ai = self.phi_table(alpha_i)
        v_be = self.phi_table(beta_e)
        v_bi = self.phi_table(beta_e)

        v_integral_e = v_ae - v_be
        v_integral_i = v_ai - v_bi

        Phi_e = 1 / (self.taurp_e +
                     vtau_e * numpy.sqrt(numpy.pi) * v_integral_e)
        Phi_i = 1 / (self.taurp_i +
                     vtau_i * numpy.sqrt(numpy.pi) * v_integral_i)

        self.ve = -(self.Vthr - self.Vreset) * E * vtau_e + vmu_e
        self.vi = -(self.Vthr - self.Vreset) * I * vtau_i + vmu_i

        dE = (-E + Phi_e) / vtau_e
        dI = (-I + Phi_i) / vtau_i

        derivative = numpy.array([dE, dI])
        return derivative

    def update_derived_parameters(self):
        """
        Derived parameters
    
        """

        self.pool_fractions = 1. / (self.pool_nodes * 2)

        self.tauNMDA = self.calpha * self.tauNMDArise * self.tauNMDAdecay
        self.Text_e = (self.gAMPAext_e * self.Cext * self.tauAMPA) / self.gm_e
        self.Text_i = (self.gAMPAext_i * self.Cext * self.tauAMPA) / self.gm_i
        self.TAMPA_e = (self.gAMPArec_e * self.C * self.tauAMPA) / self.gm_e
        self.TAMPA_i = (self.gAMPArec_i * self.C * self.tauAMPA) / self.gm_i
        self.T_ei = (self.gGABA_e * self.C * self.tauGABA) / self.gm_e
        self.T_ii = (self.gGABA_i * self.C * self.tauGABA) / self.gm_i

        self.crho1_e = (self.gNMDA_e * self.C) / self.gm_e
        self.crho1_i = (self.gNMDA_i * self.C) / self.gm_i
        self.crho2_e = self.cbeta * self.crho1_e
        self.crho2_i = self.cbeta * self.crho1_i

        self.csigma_e = (self.gAMPAext_e**2 * self.Cext * self.tauAMPA**2)/\
                (self.gm_e * self.taum_e)**2
        self.csigma_i = (self.gAMPAext_i**2 * self.Cext * self.tauAMPA**2)/\
                (self.gm_i * self.taum_i)**2

        self.psi_table.load('../files/psi.npz')
        self.phi_table.load('../files/nerf_int.npz')
Exemple #15
0
class Integrator(core.Type):
    """
    The Integrator class is a base class for the integration methods...

    .. [1] Kloeden and Platen, Springer 1995, *Numerical solution of stochastic
        differential equations.*

    .. [2] Riccardo Mannella, *Integration of Stochastic Differential Equations
        on a Computer*, Int J. of Modern Physics C 13(9): 1177--1194, 2002.

    .. [3] R. Mannella and V. Palleschi, *Fast and precise algorithm for 
        computer simulation of stochastic differential equations*, Phys. Rev. A
        40: 3381, 1989.

    """
    _base_classes = [
        'Integrator', 'IntegratorStochastic', 'RungeKutta4thOrderDeterministic'
    ]

    dt = basic.Float(
        label="Integration-step size (ms)",
        default=0.01220703125,  #0.015625,
        #range = basic.Range(lo= 0.0048828125, hi=0.244140625, step= 0.1, base=2.)
        required=True,
        doc="""The step size used by the integration routine in ms. This
        should be chosen to be small enough for the integration to be
        numerically stable. It is also necessary to consider the desired sample
        period of the Monitors, as they are restricted to being integral
        multiples of this value. The default value is set such that all built-in
        models are numerically stable with there default parameters and because
        it is consitent with Monitors using sample periods corresponding to
        powers of 2 from 128 to 4096Hz.""")

    clamped_state_variable_indices = arrays.IntegerArray(
        label=
        "indices of the state variables to be clamped by the integrators to the values in the clamped_values array",
        default=None,
        order=-1)

    clamped_state_variable_values = arrays.FloatArray(
        label="The values of the state variables which are clamped ",
        default=None,
        order=-1)

    def scheme(self, X, dfun, coupling, local_coupling, stimulus, ginput=0):
        """
        The scheme of integrator should take a state and provide the next
        state in time, e.g. for a differential equation, scheme should take
        :math:`X` and provide an appropriate :math:`X + dX` (dfun in the code).

        """
        msg = "Integrator is a base class; please use a suitable subclass."
        raise NotImplementedError(msg)

    def clamp_state(self, X):
        if self.clamped_state_variable_values is not None:
            X[self.
              clamped_state_variable_indices] = self.clamped_state_variable_values

    def __str__(self):
        return simple_gen_astr(self, 'dt')