class ProjectionSurfaceMEGData(ProjectionMatrixData):
    """
    Specific projection, from a CorticalSurface to MEG sensors.
    ... warning :: PLACEHOLDER
    """

    brain_skull = surfaces_module.BrainSkull(label = "Brain Skull", default = None, required = False,
                                             doc = """Boundary between skull and cortex domains.""")
        
    skull_skin = surfaces_module.SkullSkin(label = "Skull Skin", default = None, required = False,
                                           doc = """Boundary between skull and skin domains.""")
        
    skin_air = surfaces_module.SkinAir( label = "Skin Air", default = None, required = False,
                                        doc = """Boundary between skin and air domains.""")
    
    conductances = basic.Dict(label = "Domain conductances", required = False,
                              default = {'air': 0.0, 'skin': 1.0, 'skull': 0.01, 'brain': 1.0},
                              doc = """ A dictionary representing the conductances of ... """)    
    
    sensors = sensors_module.SensorsMEG
    
    sources = surfaces_module.CorticalSurface
    
    
    
    
    
    
class DoubleGaussianData(EquationData):
    """
    A Mexican-hat function approximated by the difference of Gaussians functions.
    """
    _ui_name = "Mexican-hat"

    equation = basic.String(
        label="Double Gaussian Equation",
        default=
        "(amp_1 * exp(-((var-midpoint_1)**2 / (2.0 * sigma_1**2)))) - (amp_2 * exp(-((var-midpoint_2)**2 / (2.0 * sigma_2**2))))",
        locked=True,
        doc=""":math:`amp_1 \\exp\\left(-\\left((x-midpoint_1)^2 / \\left(2.0
        \\sigma_1^2\\right)\\right)\\right) - 
        amp_2 \\exp\\left(-\\left((x-midpoint_2)^2 / \\left(2.0  
        \\sigma_2^2\\right)\\right)\\right)`""")

    parameters = basic.Dict(label="Double Gaussian Parameters",
                            default={
                                "amp_1": 0.5,
                                "sigma_1": 20.0,
                                "midpoint_1": 0.0,
                                "amp_2": 1.0,
                                "sigma_2": 10.0,
                                "midpoint_2": 0.0
                            })
class PulseTrainData(EquationData):
    """
    A pulse train , offseted with respect to the time axis.
    
    **Parameters**:
    
    * :math:`\\tau` :  pulse width or pulse duration
    * :math:`T`     :  pulse repetition period
    * :math:`f`     :  pulse repetition frequency (1/T)
    * duty cycle    :  :math:``\\frac{\\tau}{T}`` (for a square wave: 0.5)
    * onset time    :
    """

    equation = basic.String(label="Pulse Train",
                            default="where((var % T) < tau, amp, 0)",
                            locked=True,
                            doc=""":math:`\\frac{\\tau}{T}
        +\\sum_{n=1}^{\\infty}\\frac{2}{n\\pi}
        \\sin\\left(\\frac{\\pi\\,n\\tau}{T}\\right)
        \\cos\\left(\\frac{2\\pi\\,n}{T} var\\right)`. 
        The starting time is halfway through the first pulse. 
        The phase can be offset t with t - tau/2""")

    # onset is in milliseconds
    # T and tau are in milliseconds as well

    parameters = basic.Dict(default={
        "T": 42.0,
        "tau": 13.0,
        "amp": 1.0,
        "onset": 30.0
    },
                            label="Pulse Train Parameters")
Esempio n. 4
0
class TimeSeriesData(MappedType):
    """
    Base time-series dataType.
    """

    title = basic.String

    data = arrays.FloatArray(
        label="Time-series data",
        file_storage=core.FILE_STORAGE_EXPAND,
        doc=
        """An array of time-series data, with a shape of [tpts, :], where ':' represents 1 or more dimensions"""
    )

    nr_dimensions = basic.Integer(label="Number of dimension in timeseries",
                                  default=4)

    length_1d, length_2d, length_3d, length_4d = [basic.Integer] * 4

    labels_ordering = basic.List(
        default=["Time", "State Variable", "Space", "Mode"],
        label="Dimension Names",
        doc="""List of strings representing names of each data dimension""")

    labels_dimensions = basic.Dict(
        default={},
        label=
        "Specific labels for each dimension for the data stored in this timeseries.",
        doc=
        """ A dictionary containing mappings of the form {'dimension_name' : [labels for this dimension] }"""
    )
    ## TODO (for Stuart) : remove TimeLine and make sure the correct Period/start time is returned by different monitors in the simulator

    time = arrays.FloatArray(
        file_storage=core.FILE_STORAGE_EXPAND,
        label="Time-series time",
        required=False,
        doc=
        """An array of time values for the time-series, with a shape of [tpts,].
        This is 'time' as returned by the simulator's monitors.""")

    start_time = basic.Float(label="Start Time:")

    sample_period = basic.Float(label="Sample period", default=1.0)

    # Specify the measure unit for sample period (e.g sec, msec, usec, ...)
    sample_period_unit = basic.String(label="Sample Period Measure Unit",
                                      default="ms")

    sample_rate = basic.Float(label="Sample rate",
                              doc="""The sample rate of the timeseries""")

    has_surface_mapping = basic.Bool(default=True)
    has_volume_mapping = basic.Bool(default=False)
Esempio n. 5
0
class ProjectionData(MappedType):
    """
    Base DataType for representing a ProjectionMatrix.
    The projection is between a source of type CorticalSurface and a set of Sensors.
    """

    projection_type = basic.String

    __mapper_args__ = {'polymorphic_on': 'projection_type'}

    brain_skull = surfaces.BrainSkull(
        label="Brain Skull",
        default=None,
        required=False,
        doc="""Boundary between skull and cortex domains.""")

    skull_skin = surfaces.SkullSkin(
        label="Skull Skin",
        default=None,
        required=False,
        doc="""Boundary between skull and skin domains.""")

    skin_air = surfaces.SkinAir(
        label="Skin Air",
        default=None,
        required=False,
        doc="""Boundary between skin and air domains.""")

    conductances = basic.Dict(
        label="Domain conductances",
        required=False,
        default={
            'air': 0.0,
            'skin': 1.0,
            'skull': 0.01,
            'brain': 1.0
        },
        doc=""" A dictionary representing the conductances of ... """)

    sources = surfaces.CorticalSurface(label="surface or region",
                                       default=None,
                                       required=True)

    sensors = sensors.Sensors(
        label="Sensors",
        default=None,
        required=False,
        doc=""" A set of sensors to compute projection matrix for them. """)

    projection_data = arrays.FloatArray(label="Projection Matrix Data",
                                        default=None,
                                        required=True)
Esempio n. 6
0
class CosineData(EquationData):
    """
    A Cosine equation.
    """
    
    equation = basic.String(  
        label = "Cosine Equation",
        default = "amp * cos(6.283185307179586 * frequency * var)",
        locked = True,
        doc = """:math:`amp \\cos(2.0 \\pi frequency x)` """)
    
    parameters = basic.Dict( 
        label = "Cosine Parameters",
        default = {"amp": 1.0, "frequency": 0.01}) #kHz #"pi": numpy.pi, 
Esempio n. 7
0
class SinusoidData(EquationData):
    """
    A Sinusoid equation.
    """
    
    equation = basic.String(  
        label = "Sinusoid Equation",
        default = "amp * sin(6.283185307179586 * frequency * var)",
        locked = True,
        doc = """:math:`amp \\sin(2.0 \\pi frequency x)` """)
    
    parameters = basic.Dict( 
        label = "Sinusoid Parameters",
        default = {"amp": 1.0, "frequency": 0.01}) #kHz #"pi": numpy.pi,
Esempio n. 8
0
class AlphaData(EquationData):
    """
    An Alpha function belonging to the Exponential function family.
    """
    
    equation = basic.String( 
        label = "Alpha Equation",
        default = "where((var-onset) > 0, (alpha * beta) / (beta - alpha) * (exp(-alpha * (var-onset)) - exp(-beta * (var-onset))), 0.0 * var)",
        locked = True,
        doc = """:math:`(\\alpha * \\beta) / (\\beta - \\alpha) * 
            (\\exp(-\\alpha * (x-onset)) - \\exp(-\\beta * (x-onset)))` for :math:`(x-onset) > 0`""")
    
    parameters = basic.Dict( 
        label = "Alpha Parameters",
        default = {"onset": 0.5, "alpha": 13.0, "beta":  42.0})
Esempio n. 9
0
class GeneralizedSigmoidData(EquationData):
    """
    A General Sigmoid equation.
    """
    
    equation = basic.String(
        label = "Generalized Sigmoid Equation",
        default = "low + (high - low) / (1.0 + exp(-1.8137993642342178 * (var-midpoint)/sigma))",
        locked = True,
        doc = """:math:`low + (high - low) / (1.0 + \\exp(-\\pi/\\sqrt(3.0) 
            (x-midpoint)/\\sigma))`""")
    
    parameters = basic.Dict( 
        label = "Sigmoid Parameters",
        default = {"low": 0.0, "high": 1.0, "midpoint": 1.0, "sigma": 0.3}) #, 
Esempio n. 10
0
class GaussianData(FiniteSupportEquationData):
    """
    A Gaussian equation.
    """

    equation = basic.String( 
        label = "Gaussian Equation",
        default = "amp * exp(-((var-midpoint)**2 / (2.0 * sigma**2)))",
        locked = True,
        doc = """:math:`amp \\exp\\left(-\\left(\\left(x-midpoint\\right)^2 /
        \\left(2.0 \\sigma^2\\right)\\right)\\right)`""")
    
    parameters = basic.Dict( 
        label = "Gaussian Parameters",
        default = {"amp": 1.0, "sigma": 1.0, "midpoint": 0.0})
Esempio n. 11
0
class SigmoidData(FiniteSupportEquationData):
    """
    A Sigmoid equation.
    """

    equation = basic.String( 
        label = "Sigmoid Equation",
        default = "amp / (1.0 + exp(-1.8137993642342178 * (radius-var)/sigma))",
        locked = True,
        doc = """:math:`amp / (1.0 + \\exp(-\\pi/\\sqrt(3.0) 
            (radius-x)/\\sigma))`""")
    
    parameters = basic.Dict( 
        label = "Sigmoid Parameters",
        default = {"amp": 1.0, "radius": 5.0, "sigma": 1.0}) #"pi": numpy.pi,
class LinearData(EquationData):
    """
    A linear equation.
    """

    equation = basic.String(label="Linear Equation",
                            default="a * var + b",
                            locked=True,
                            doc=""":math:`result = a * x + b`""")

    parameters = basic.Dict(label="Linear Parameters",
                            default={
                                "a": 1.0,
                                "b": 0.0
                            })
class LinearCouplingData(equations.Linear):
    """
    The equation for representing a Linear Coupling
    """
    _ui_name = "Linear Coupling"

    parameters = basic.Dict(
        label="Linear Coupling Parameters a and b",
        doc="""a: rescales connection strengths and maintains ratio and
                 b: shifts the base strength (maintain absolute difference)""",
        default={
            "a": 0.00390625,
            "b": 0.0
        })

    __generate_table__ = True
class GammaData(EquationData):
    """
    A Gamma function for the bold monitor. It belongs to the family of Exponential functions.
    
    **Parameters**:
    
    
    * :math:`\\tau`      : Exponential time constant of the gamma function [seconds].
    * :math:`n`          : The phase delay of the gamma function.
    * :math: `factorial` : (n-1)!. numexpr does not support factorial yet. 
    * :math: `a`         : Amplitude factor after normalization.


    **Reference**:
     
    .. [B_1996] Geoffrey M. Boynton, Stephen A. Engel, Gary H. Glover and David 
        J. Heeger (1996). Linear Systems Analysis of Functional Magnetic Resonance 
        Imaging in Human V1. J Neurosci 16: 4207-4221

    .. note:: might be filtered from the equations used in Stimulus and Local Connectivity.

    """

    _ui_name = "HRF kernel: Gamma kernel"

    # TODO: Introduce a time delay in the equation (shifts the hrf onset)
    # """:math:`h(t) = \frac{(\frac{t-\delta}{\tau})^{(n-1)} e^{-(\frac{t-\delta}{\tau})}}{\tau(n-1)!}"""
    # delta = 2.05 seconds -- Additional delay in seconds from the onset of the
    # time-series to the beginning of the gamma hrf.
    # delay cannot be negative or greater than the hrf duration.

    equation = basic.String(
        label="Gamma Equation",
        default=
        "((var / tau) ** (n - 1) * exp(-(var / tau)) )/ (tau * factorial)",
        locked=True,
        doc=
        """:math:`h(var) = \\frac{(\\frac{var}{\\tau})^{(n-1)}\\exp{-(\\frac{var}{\\tau})}}{\\tau(n-1)!}`."""
    )

    parameters = basic.Dict(label="Gamma Parameters",
                            default={
                                "tau": 1.08,
                                "n": 3.0,
                                "factorial": 2.0,
                                "a": 0.1
                            })
class FirstOrderVolterraData(EquationData):
    """
    Integral form of the first Volterra kernel of the three used in the 
    Ballon Windekessel model for computing the Bold signal. 
    This function describes a damped Oscillator.

    **Parameters** :    

    * :math:`\\tau_s`: Dimensionless? exponential decay parameter.
    * :math:`\\tau_f`: Dimensionless? oscillatory parameter. 
    * :math:`k_1`    : First Volterra kernel coefficient. 
    * :math:`V_0` : Resting blood volume fraction. 


    **References** :
     
    .. [F_2000] Friston, K., Mechelli, A., Turner, R., and Price, C., *Nonlinear 
        Responses in fMRI: The Balloon Model, Volterra Kernels, and Other 
        Hemodynamics*, NeuroImage, 12, 466 - 477, 2000.

    """

    _ui_name = "HRF kernel: Volterra Kernel"

    equation = basic.String(
        label="First Order Volterra Kernel",
        default=
        "1/3. * exp(-0.5*(var / tau_s)) * (sin(sqrt(1./tau_f - 1./(4.*tau_s**2)) * var)) / (sqrt(1./tau_f - 1./(4.*tau_s**2)))",
        locked=True,
        doc=""":math:`G(t - t^{\\prime}) =
             e^{\\frac{1}{2} \\left(\\frac{t - t^{\\prime}}{\\tau_s} \\right)}
             \\frac{\sin\\left((t - t^{\\prime})
             \\sqrt{\\frac{1}{\\tau_f} - \\frac{1}{4 \\tau_s^2}}\\right)}
             {\\sqrt{\\frac{1}{\\tau_f} - \\frac{1}{4 \\tau_s^2}}}
             \\; \\; \\; \\; \\; \\;  for \\; \\; \\; t \\geq t^{\\prime}
             = 0 \\; \\; \\; \\; \\; \\;  for \\; \\; \\;  t < t^{\\prime}`."""
    )

    parameters = basic.Dict(label="Mixture of Gammas Parameters",
                            default={
                                "tau_s": 0.8,
                                "tau_f": 0.4,
                                "k_1": 5.6,
                                "V_0": 0.02
                            })
class DoubleExponentialData(EquationData):
    """
    A difference of two exponential functions to define a kernel for the bold monitor.

    **Parameters** :

    * :math:`\\tau_1`: Time constant of the second exponential function [s]
    * :math:`\\tau_2`: Time constant of the first exponential function [s].
    * :math:`f_1`  : Frequency of the first sine function [Hz].
    * :math:`f_2`  : Frequency of the second sine function [Hz].
    * :math:`amp_1`: Amplitude of the first exponential function.
    * :math:`amp_2`: Amplitude of the second exponential function.
    * :math:`a`    : Amplitude factor after normalization.
    
    
    **Reference**:
    
    .. [P_2000] Alex Polonsky, Randolph Blake, Jochen Braun and David J. Heeger
        (2000). Neuronal activity in human primary visual cortex correlates with
        perception during binocular rivalry. Nature Neuroscience 3: 1153-1159

    """

    _ui_name = "HRF kernel: Difference of Exponentials"

    equation = basic.String(
        label="Double Exponential Equation",
        default=
        "((amp_1 * exp(-var/tau_1) * sin(2.*pi*f_1*var)) - (amp_2 * exp(-var/ tau_2) * sin(2.*pi*f_2*var)))",
        locked=True,
        doc=""":math:`h(var) = amp_1\\exp(\\frac{-var}{\tau_1})
        \\sin(2\\cdot\\pi f_1 \\cdot var) - amp_2\\cdot \\exp(-\\frac{var}
        {\\tau_2})*\\sin(2\\pi f_2 var)`.""")

    parameters = basic.Dict(label="Double Exponential Parameters",
                            default={
                                "tau_1": 7.22,
                                "f_1": 0.03,
                                "amp_1": 0.1,
                                "tau_2": 7.4,
                                "f_2": 0.12,
                                "amp_2": 0.1,
                                "a": 0.1,
                                "pi": numpy.pi
                            })
Esempio n. 17
0
class Sigmoid(SpatialApplicableEquation, FiniteSupportEquation):
    """
    A Sigmoid equation.
    offset: parameter to extend the behaviour of this function
    when spatializing model parameters.
    """

    equation = basic.String(
        label="Sigmoid Equation",
        default=
        "(amp / (1.0 + exp(-1.8137993642342178 * (radius-var)/sigma))) + offset",
        locked=True,
        doc=""":math:`(amp / (1.0 + \\exp(-\\pi/\\sqrt(3.0)
            (radius-x)/\\sigma))) + offset`""")

    parameters = basic.Dict(label="Sigmoid Parameters",
                            default={
                                "amp": 1.0,
                                "radius": 5.0,
                                "sigma": 1.0,
                                "offset": 0.0
                            })  #"pi": numpy.pi,
class GaussianData(EquationData):
    """
    A Gaussian equation.
    offset: parameter to extend the behaviour of this function 
    when spatializing model parameters. 

    """

    equation = basic.String(
        label="Gaussian Equation",
        default="(amp * exp(-((var-midpoint)**2 / (2.0 * sigma**2))))+offset",
        locked=True,
        doc=""":math:`(amp \\exp\\left(-\\left(\\left(x-midpoint\\right)^2 /
        \\left(2.0 \\sigma^2\\right)\\right)\\right)) + offset`""")

    parameters = basic.Dict(label="Gaussian Parameters",
                            default={
                                "amp": 1.0,
                                "sigma": 1.0,
                                "midpoint": 0.0,
                                "offset": 0.0
                            })
class EquationData(basic.MapAsJson, core.Type):
    """
    
    Within the UI we'll access via the specific Equation subclasses implemented below.
    
    """
    _base_classes = [
        'Equation',
        'FiniteSupportEquation',
        "DiscreteEquation",
        "TemporalApplicableEquation",
        "SpatialApplicableEquation",
        "HRFKernelEquation",
        #TODO: There should be a refactor of Coupling which may make these unnecessary
        'Coupling',
        'CouplingData',
        'CouplingScientific',
        'CouplingFramework',
        'LinearCoupling',
        'LinearCouplingData',
        'LinearCouplingScientific',
        'LinearCouplingFramework',
        'SigmoidalCoupling',
        'SigmoidalCouplingData',
        'SigmoidalCouplingScientific',
        'SigmoidalCouplingFramework'
    ]

    equation = basic.String(
        label="Equation as a string",
        doc="""A latex representation of the equation, with the extra
            escaping needed for interpretation via sphinx.""")

    parameters = basic.Dict(
        label="Parameters in a dictionary.",
        default={},
        doc="""Should be a list of the parameters and their meaning, Traits
            should be able to take defaults and sensible ranges from any 
            traited information that was provided.""")
Esempio n. 20
0
class EpileptorDPrealistic(Model):
    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]_).
    ->x0 parameters are shifted for the bifurcation
      to be at x0=1, where x0>1 is the supercritical region.
    ->there is a choice for linear or sigmoidal z dynamics (see [Proixetal_2014]_).

    Equations and default parameters are taken from [Jirsaetal_2014]_.

    The realistic Epileptor allows for state variables I_{ext1}, I_{ext2}, x0, slope and K
    to fluctuate as linear dynamical equations, driven by the corresponding
    parameter values. It could be combined with multiplicative and/or pink noise.

          +------------------------------------------------------+
          |                         Table 1                      |
          +----------------------+-------------------------------+
          |        Parameter     |           Value               |
          +======================+===============================+
          |         I_ext1      |              3.1              |
          +----------------------+-------------------------------+
          |         I_ext2      |              0.45             |
          +----------------------+-------------------------------+
          |         tau0         |           2857.0              |
          +----------------------+-------------------------------+
          |         x_0          |              0.0              |
          +----------------------+-------------------------------+
          |         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, Proix 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.

    .. [Proixetal_2014] Proix, T., Bartolomei, F., Chauvel, P., Bernard, C.,
                       & Jirsa, V. K. (2014).
                       Permitau1ivity Coupling across Brain Regions Determines
                       Seizure Recruitment in Partial Epilepsy.
                       Journal of Neuroscience, 34(45), 15009–15021.
                       htau1p://doi.org/10.1523/JNEUROSCI.1570-14.2014

    .. automethod:: EpileptorDP.__init__

    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}} &=& yc - d x_{1}^{2} - y{1} \\
            \dot{z} &=&
            \begin{cases}
            (f_z(x_{1}) - z-0.1 z^{7})/tau0 & \text{if } x<0 \\
            (f_z(x_{1}) - z)/tau0           & \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 / \tau2 (-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\\
            (x_{2} - 0.6(z-4)^2 -slope) x_{1} &\text{if }x_{1} \geq 0
            \end{cases}

        .. math::
            f_z(x_{1})  =
            \begin{cases}
            4 * (x_{1} - x0) & \text{linear} \\
            \frac{3}{1+e^{-10*(x_{1}+0.5)}} - x0 & \text{sigmoidal} \\
            \end{cases}
    and:

        .. math::
            f_{2}(x_{2}) =
            \begin{cases}
            0 & \text{if } x_{2} <-0.25\\
            s*(x_{2} + 0.25) & \text{if } x_{2} \geq -0.25
            \end{cases}
    """

    _ui_name = "EpileptorDPrealistic"
    ui_configurable_parameters = ["Iext1", "Iext2", "tau0", "x0", "slope"]

    zmode = arrays.FloatArray(
        label="zmode",
        default=numpy.array("lin"),
        doc="zmode = np.array(""lin"") for linear and numpy.array(""sig"") for sigmoidal z dynamics",
        order=-1)
        
    pmode = arrays.FloatArray(
        label="pmode",
        default=numpy.array("const"),
        doc="pmode = numpy.array(""g""), numpy.array(""z""), numpy.array(""z*g"") or numpy.array(""const"") parameters following the g, z, z*g dynamics or staying constamt, respectively",
        order=-1)   

#    a = arrays.FloatArray(
#        label="a",
#        default=numpy.array([1]),
#        doc="Coefficient of the cubic term in the first state variable",
#        order=-1)

#    b = arrays.FloatArray(
#        label="b",
#        default=numpy.array([3]),
#        doc="Coefficient of the squared term in the first state variabel",
#        order=-1)

    yc = arrays.FloatArray(
        label="yc",
        default=numpy.array([1]),
        doc="Additive coefficient for the second state variable",
        order=-1)

#    d = arrays.FloatArray(
#        label="d",
#        default=numpy.array([5]),
#        doc="Coefficient of the squared term in the second state variable",
#        order=-1)

    tau0 = arrays.FloatArray(
        label="r",
        range=basic.Range(lo=100.0, hi=5000, step=10),
        default=numpy.array([10000.0]),
        doc="Temporal scaling in the third state variable",
        order=4)

#    s = arrays.FloatArray(
#        label="s",
#        default=numpy.array([4]),
#        doc="Linear coefficient in the third state variable",
#        order=-1)

    x0 = arrays.FloatArray(
        label="x0",
        range=basic.Range(lo=-0.5, hi=1.5, step=0.1),
        default=numpy.array([0.0]),
        doc="Excitability parameter",
        order=3)

    Iext1 = arrays.FloatArray(
        label="Iext1",
        range=basic.Range(lo=1.5, hi=5.0, step=0.1),
        default=numpy.array([3.1]),
        doc="External input current to the first population",
        order=1)

    slope = arrays.FloatArray(
        label="slope",
        range=basic.Range(lo=-16.0, hi=6.0, step=0.1),
        default=numpy.array([0.]),
        doc="Linear coefficient in the first state variable",
        order=5)

    Iext2 = arrays.FloatArray(
        label="Iext2",
        range=basic.Range(lo=0.0, hi=1.0, step=0.05),
        default=numpy.array([0.45]),
        doc="External input current to the second population",
        order=2)

    tau2 = arrays.FloatArray(
        label="tau2",
        default=numpy.array([10]),
        doc="Temporal scaling coefficient in fifth state variable",
        order=-1)

    Kvf = arrays.FloatArray(
        label="K_vf",
        default=numpy.array([0.0]),
        range=basic.Range(lo=0.0, hi=4.0, step=0.5),
        doc="Coupling scaling on a very fast time scale.",
        order=6)

    Kf = arrays.FloatArray(
        label="K_f",
        default=numpy.array([0.0]),
        range=basic.Range(lo=0.0, hi=4.0, step=0.5),
        doc="Correspond to the coupling scaling on a fast time scale.",
        order=7)

    K = arrays.FloatArray(
        label="K",
        default=numpy.array([0.0]),
        range=basic.Range(lo=-4.0, hi=4.0, step=0.1),
        doc="Permitau1ivity coupling, that is from the fast time scale toward the slow time scale",
        order=8)

    tau1 = arrays.FloatArray(
        label="tau1",
        default=numpy.array([0.25]),
        range=basic.Range(lo=0.001, hi=10.0, step=0.001),
        doc="Time scaling of the whole system",
        order=9)

    state_variable_range = basic.Dict(
        label="State variable ranges [lo, hi]",
        default={"y0": numpy.array([-2., 2.]), #x1
                 "y1": numpy.array([-20., 2.]), #y1
                 "y2": numpy.array([2.0, 20.0]), #z
                 "y3": numpy.array([-2., 0.]), #x2
                 "y4": numpy.array([0., 2.]), #y2
                 "y5": numpy.array([-1., 1.]), #g
                 "y6": numpy.array([-2, 2]), #x0
                 "y7": numpy.array([-20., 6.]), #slope
                 "y8": numpy.array([1.5, 5.]), #Iext1
                 "y9": numpy.array([0., 1.]), #Iext2
                 "y10": numpy.array([-50., 50.])},#K
        doc="n/a",
        order=16
        )

    variables_of_interest = basic.Enumerate(
        label="Variables watched by Monitors",
        options=["y0", "y1", "y2", "y3", "y4", "y5", "y6", "y7", "y8", "y9", "y10", "y3 - y0"],
        default=["y3 - y0", "y2"],
        select_multiple=True,
        doc="""default state variables to be monitored""",
        order=-1)

    state_variables = ["y0", "y1", "y2", "y3", "y4", "y5", "y6", "y7", "y8", "y9", "y10"]

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

    @staticmethod
    def fun_slope_Iext2(z, g, pmode, slope, Iext2):

        from tvb_epilepsy.base.utils import linear_scaling

        if (pmode == numpy.array(['g','z','z*g'])).any():

            if pmode == 'g':
                xp = 1.0/ (1.0 + numpy.exp(1) ** (-10 * (g + 0.0)))
                xp1 = 0#-0.175
                xp2 = 1#0.025

            elif pmode == 'z':
                xp = 1.0 / (1.0 + numpy.exp(1) ** (-10 * (z - 3.00)))
                xp1 = 0
                xp2 = 1

            elif pmode == 'z*g':
                xp = z * g
                xp1 = -0.7
                xp2 = 0.1
            slope_eq = linear_scaling(xp, xp1, xp2, 1.0, slope)
            #slope_eq = self.slope
            Iext2_eq = linear_scaling(xp, xp1, xp2, 0.0, Iext2)

        else:
            slope_eq = slope
            Iext2_eq = Iext2

        return slope_eq, Iext2_eq
        
    def dfun(self, state_variables, coupling, local_coupling=0.0,
             array=numpy.array, where=numpy.where, concat=numpy.concatenate):
        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{y_{0}} &=& y_{1} - f_{1}(y_{0}, y_{3}) - y_{2} + I_{ext1} \\
            \dot{y_{1}} &=& yc - d (y_{0} -5/3)^{2} - y{1} \\
            \dot{y_{2}} &=&
            \begin{cases}
            (f_z(y_{0}) - y_{2}-0.1 y_{2}^{7})/tau0 & \text{if } y_{0}<5/3 \\
            (f_z(y_{0}) - y_{2})/tau0           & \text{if } y_{0} \geq 5/3
            \end{cases} \\
            \dot{y_{3}} &=& -y_{4} + y_{3} - y_{3}^{3} + I_{ext2} + 0.002 y_{5} - 0.3 (y_{2}-3.5) \\
            \dot{y_{4}} &=& 1 / \tau2 (-y_{4} + f_{2}(y_{3}))\\
            \dot{y_{5}} &=& -0.01 (y_{5} - 0.1 ( y_{0} -5/3 ) )

        where:
            .. math::
                f_{1}(y_{0}, y_{3}) =
                \begin{cases}
                a ( y_{0} -5/3 )^{3} - b ( y_{0} -5/3 )^2 & \text{if } y_{0} <5/3\\
                ( y_{3} - 0.6(y_{2}-4)^2 -slope ) ( y_{0} - 5/3 ) &\text{if }y_{0} \geq 5/3
                \end{cases}

            .. math::
                f_z(y_{0})  =
                \begin{cases}
                4 * (y_{0} - x0) & \text{linear} \\
                \frac{3}{1+e^{-10*(y_{0}-7/6)}} - x0 & \text{sigmoidal} \\
                \end{cases}
        and:

            .. math::
                f_{2}(y_{3}) =
                \begin{cases}
                0 & \text{if } y_{3} <-0.25\\
                s*(y_{3} + 0.25) & \text{if } y_{3} \geq -0.25
                \end{cases}

        """

        y = state_variables
        ydot = numpy.empty_like(state_variables)


        #To use later:
        x0=y[6]
        slope = y[7]
        Iext1 = y[8]
        Iext2 = y[9]
        K = y[10]

        Iext1 = self.Iext1 + local_coupling * y[0]
        c_pop1 = coupling[0, :]
        c_pop2 = coupling[1, :]

        # population 1
        if_ydot0 = -y[0]**2 + 3.0*y[0] #self.a=1.0, self.b=3.0
        else_ydot0 = slope - y[3] + 0.6*(y[2]-4.0)**2
        ydot[0] = self.tau1 * (y[1] - y[2] + Iext1 + self.Kvf * c_pop1 + where(y[0] < 0.0, if_ydot0, else_ydot0) * y[0])
        ydot[1] = self.tau1 * (self.yc - 5.0 * y[0] ** 2 - y[1]) #self.d=5

        # energy
        if_ydot2 = - 0.1*y[2]**7
        else_ydot2 = 0

        if self.zmode == 'lin':
            fz = 4*(y[0] - x0) + where(y[2] < 0., if_ydot2, else_ydot2)

        elif self.zmode == 'sig':
            fz = 3.0 / (1.0 + numpy.exp(-10 * (y[0] + 0.5))) - x0

        else:
            raise ValueError("zmode has to be either ""lin"" or ""sig"" for linear and sigmoidal fz(), respectively")
        ydot[2] = self.tau1 * ((fz - y[2] + K * c_pop1) / self.tau0)

        # population 2
        ydot[3] = self.tau1 * (-y[4] + y[3] - y[3] ** 3 + Iext2 + 2*y[5] - 0.3 * (y[2] - 3.5) + self.Kf * c_pop2)
        if_ydot4 = 0
        else_ydot4 = 6.0 * (y[3] + 0.25) #self.s = 6.0
        ydot[4] = self.tau1 * ((-y[4] + where(y[3] < -0.25, if_ydot4, else_ydot4)) / self.tau2)

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

        slope_eq, Iext2_eq = self.fun_slope_Iext2(y[2], y[5], self.pmode, self.slope, self.Iext2)
        
        # x0
        ydot[6] = self.tau1 * (-y[6] + self.x0)
        # slope
        ydot[7] = 10 * self.tau1 * (-y[7] + slope_eq) #5*
        # Iext1
        ydot[8] = self.tau1 * (-y[8] + self.Iext1) / self.tau0
        # Iext2
        ydot[9] = 5 * self.tau1*(-y[9] + Iext2_eq)
        # K
        ydot[10] = self.tau1*(-y[10] + self.K) / self.tau0

        return ydot


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

        return None
Esempio n. 21
0
class EpileptorDP(Model):
    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]_). 
    ->x0 parameters are shifted for the bifurcation
      to be at x0=1, where x0>1 is the supercritical region.
    ->there is a choice for linear or sigmoidal z dynamics (see [Proixetal_2014]_)
    ->some parameters change their names to be more similar to the equations.

    Equations and default parameters are taken from [Jirsaetal_2014]_.

          +------------------------------------------------------+
          |                         Table 1                      |
          +----------------------+-------------------------------+
          |        Parameter     |           Value               |
          +======================+===============================+
          |         I_ext1      |              3.1              |
          +----------------------+-------------------------------+
          |         I_ext2      |              0.45             |
          +----------------------+-------------------------------+
          |         tau0         |           2857.0              |
          +----------------------+-------------------------------+
          |         x_0          |              0.0              |
          +----------------------+-------------------------------+
          |         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, Proix 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.

    .. [Proixetal_2014] Proix, T., Bartolomei, F., Chauvel, P., Bernard, C.,
                       & Jirsa, V. K. (2014).
                       Permitivity Coupling across Brain Regions Determines
                       Seizure Recruitment in Partial Epilepsy.
                       Journal of Neuroscience, 34(45), 15009–15021.
                       htau1p://doi.org/10.1523/JNEUROSCI.1570-14.2014

    .. automethod:: EpileptorDP.__init__

    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}} &=& yc - d x_{1}^{2} - y{1} \\
            \dot{z} &=&
            \begin{cases}
            (f_z(x_{1}) - z-0.1 z^{7})/tau0 & \text{if } x<0 \\
            (f_z(x_{1}) - z)/tau0           & \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 / \tau2 (-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\\
            ( x_{2} - 0.6(z-4)^2 -slope ) x_{1}  &\text{if }x_{1} \geq 0
            \end{cases}

        .. math::
            f_z(x_{1})  =
            \begin{cases}
            4 * (x_{1} - x0) & \text{linear} \\
            \frac{3}{1+e^{-10*(x_{1}+0.5)}} - x0  & \text{sigmoidal} \\
            \end{cases}
    and:

        .. math::
            f_{2}(x_{2}) =
            \begin{cases}
            0 & \text{if } x_{2} <-0.25\\
            s*(x_{2} + 0.25) & \text{if } x_{2} \geq -0.25
            \end{cases}
    """

    _ui_name = "EpileptorDP"
    ui_configurable_parameters = ["Iext1", "Iext2", "tau0", "x0", "slope"]

    zmode = arrays.FloatArray(
        label="zmode",
        default=numpy.array("lin"),
        doc="zmode = numpy.array(""lin"") for linear and numpy.array(""sig"") for sigmoidal z dynamics",
        order=-1)

#    a = arrays.FloatArray(
#        label="a",
#        default=numpy.array([1]),
#        doc="Coefficient of the cubic term in the first state variable",
#        order=-1)

#    b = arrays.FloatArray(
#        label="b",
#        default=numpy.array([3]),
#        doc="Coefficient of the squared term in the first state variabel",
#        order=-1)

    yc = arrays.FloatArray(
        label="yc",
        default=numpy.array([1]),
        doc="Additive coefficient for the second state variable",
        order=-1)

#    d = arrays.FloatArray(
#        label="d",
#        default=numpy.array([5]),
#        doc="Coefficient of the squared term in the second state variable",
#        order=-1)

    tau0 = arrays.FloatArray(
        label="r",
        range=basic.Range(lo=100.0, hi=5000, step=10),
        default=numpy.array([2857.0]),
        doc="Temporal scaling in the third state variable",
        order=4)

#    s = arrays.FloatArray(
#        label="s",
#        default=numpy.array([4]),
#        doc="Linear coefficient in the third state variable",
#        order=-1)

    x0 = arrays.FloatArray(
        label="x0",
        range=basic.Range(lo=-0.5, hi=1.5, step=0.1),
        default=numpy.array([0.0]),
        doc="Excitability parameter",
        order=3)

    Iext1 = arrays.FloatArray(
        label="Iext1",
        range=basic.Range(lo=1.5, hi=5.0, step=0.1),
        default=numpy.array([3.1]),
        doc="External input current to the first population",
        order=1)

    slope = arrays.FloatArray(
        label="slope",
        range=basic.Range(lo=-16.0, hi=6.0, step=0.1),
        default=numpy.array([0.]),
        doc="Linear coefficient in the first state variable",
        order=5)

    Iext2 = arrays.FloatArray(
        label="Iext2",
        range=basic.Range(lo=0.0, hi=1.0, step=0.05),
        default=numpy.array([0.45]),
        doc="External input current to the second population",
        order=2)

    tau2 = arrays.FloatArray(
        label="tau2",
        default=numpy.array([10]),
        doc="Temporal scaling coefficient in fifth state variable",
        order=-1)

    Kvf = arrays.FloatArray(
        label="K_vf",
        default=numpy.array([0.0]),
        range=basic.Range(lo=0.0, hi=4.0, step=0.5),
        doc="Coupling scaling on a very fast time scale.",
        order=6)

    Kf = arrays.FloatArray(
        label="K_f",
        default=numpy.array([0.0]),
        range=basic.Range(lo=0.0, hi=4.0, step=0.5),
        doc="Correspond to the coupling scaling on a fast time scale.",
        order=7)

    K = arrays.FloatArray(
        label="K",
        default=numpy.array([0.0]),
        range=basic.Range(lo=-4.0, hi=4.0, step=0.1),
        doc="Permitau1ivity coupling, that is from the fast time scale toward the slow time scale",
        order=8)

    tau1 = arrays.FloatArray(
        label="tau1",
        default=numpy.array([0.25]),
        range=basic.Range(lo=0.001, hi=10.0, step=0.001),
        doc="Time scaling of the whole system",
        order=9)

    state_variable_range = basic.Dict(
        label="State variable ranges [lo, hi]",
        default={"y0": numpy.array([-2., 2.]),
                 "y1": numpy.array([-20., 2.]),
                 "y2": numpy.array([2.0, 20.0]),
                 "y3": numpy.array([-2., 0.]),
                 "y4": numpy.array([0., 2.]),
                 "y5": numpy.array([-1., 1.])},
        doc="n/a",
        order=16
        )

    variables_of_interest = basic.Enumerate(
        label="Variables watched by Monitors",
        options=["y0", "y1", "y2", "y3", "y4", "y5", "y3 - y0"],
        default=["y3 - y0", "y2"],
        select_multiple=True,
        doc="""default state variables to be monitored""",
        order=-1)

    state_variables = ["y0", "y1", "y2", "y3", "y4", "y5"]

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


    def dfun(self, state_variables, coupling, local_coupling=0.0,
             array=numpy.array, where=numpy.where, concat=numpy.concatenate):
        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{y_{0}} &=& y_{1} - f_{1}(y_{0}, y_{3}) - y_{2} + I_{ext1} \\
            \dot{y_{1}} &=& yc - d y_{0}^{2} - y{1} \\
            \dot{y_{2}} &=&
            \begin{cases}
            (f_z(y_{0}) - y_{2}-0.1 y_{2}^{7})/tau0 & \text{if } y_{0}<0 \\
            (f_z(y_{0}) - y_{2})/tau0           & \text{if } y_{0} \geq 0
            \end{cases} \\
            \dot{y_{3}} &=& -y_{4} + y_{3} - y_{3}^{3} + I_{ext2} + 0.002 y_{5} - 0.3 (y_{2}-3.5) \\
            \dot{y_{4}} &=& 1 / \tau2 (-y_{4} + f_{2}(y_{3}))\\
            \dot{y_{5}} &=& -0.01 (y_{5} - 0.1 y_{0} )

        where:
            .. math::
                f_{1}(y_{0}, y_{3}) =
                \begin{cases}
                a y_{0}^{3} - by_{0}^2 & \text{if } y_{0} <0\\
                (y_{3} - 0.6(y_{2}-4)^2 slope)y_{0} &\text{if }y_{0} \geq 0
                \end{cases}

            .. math::
                f_z(y_{0})  =
                \begin{cases}
                4 * (y_{0} - x0) & \text{linear} \\
                \frac{3}{1+e^{-10*(y_{0}+0.5)}} - x0 & \text{sigmoidal} \\
                \end{cases}
        and:

            .. math::
                f_{2}(y_{3}) =
                \begin{cases}
                0 & \text{if } y_{3} <-0.25\\
                s*(y_{3} + 0.25) & \text{if } y_{3} \geq -0.25
                \end{cases}

        """

        y = state_variables
        ydot = numpy.empty_like(state_variables)

        Iext1 = self.Iext1 + local_coupling * y[0]
        c_pop1 = coupling[0, :]
        c_pop2 = coupling[1, :]

        #TVB Epileptor in commented lines below

        # population 1
        #if_ydot0 = - self.a * y[0] ** 2 + self.b * y[0]
        if_ydot0 = -y[0]**2 + 3.0*y[0] #self.a=1.0, self.b=3.0
        #else_ydot0 = self.slope - y[3] + 0.6 * (y[2] - 4.0) ** 2
        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[0] = self.tau1 * (y[1] - y[2] + Iext1 + self.Kvf * c_pop1 + where(y[0] < 0.0, if_ydot0, else_ydot0) * y[0])
        # ydot[1] = self.tt * (self.c - self.d * y[0] ** 2 - y[1])
        ydot[1] = self.tau1 * (self.yc - 5.0 * y[0] ** 2 - y[1]) #self.d=5

        # energy
        #if_ydot2 = - 0.1 * y[2] ** 7
        if_ydot2 = - 0.1 * y[2] ** 7
        #else_ydot2 = 0
        else_ydot2 = 0

        if self.zmode == 'lin':
            # self.r * (4 * (y[0] - self.x0) - y[2]      + where(y[2] < 0., if_ydot2, else_ydot2)
            fz = 4*(y[0] - self.x0) + where(y[2] < 0., if_ydot2, else_ydot2)

        elif self.zmode == 'sig':
            fz = 3.0 / (1.0 + numpy.exp(-10 * (y[0] + 0.5))) - self.x0

        else:
            raise ValueError("zmode has to be either ""lin"" or ""sig"" for linear and sigmoidal fz(), " +
                             "respectively")

        # ydot[2] = self.tt * (        ...+ self.Ks * c_pop1))
        ydot[2] = self.tau1 * ((fz - y[2] + self.K * c_pop1) / self.tau0)

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

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

        return ydot

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

        return None
Esempio n. 22
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 ProjectionMatrix(core.Type):
    """
    Provides the mechanisms necessary to access OpenMEEG for the calculation of
    EEG and MEG projection matrices, ie matrices that map source activity to 
    sensor activity. It is initialised with datatypes of TVB and ultimately 
    returns the projection matrix as a Numpy ndarray. 
    """

    brain_skull = surfaces_module.BrainSkull(
        label="Boundary between skull and skin domains",
        default=None,
        required=True,
        doc="""A ... surface on which ... including ...""")

    skull_skin = surfaces_module.SkullSkin(
        label="surface and auxillary for surface sim",
        default=None,
        required=True,
        doc="""A ... surface on which ... including ...""")

    skin_air = surfaces_module.SkinAir(
        label="surface and auxillary for surface sim",
        default=None,
        required=True,
        doc="""A ... surface on which ... including ...""")

    conductances = basic.Dict(
        label="Domain conductances",
        default={
            'air': 0.0,
            'skin': 1.0,
            'skull': 0.01,
            'brain': 1.0
        },
        required=True,
        doc="""A dictionary representing the conductances of ...""")

    sources = surfaces_module.Cortex(
        label="surface and auxillary for surface sim",
        default=None,
        required=True,
        doc="""A cortical surface on which ... including ...""")

    sensors = sensors_module.Sensors(
        label="surface and auxillary for surface sim",
        default=None,
        required=False,
        doc="""A cortical surface on which ... including ...  
            If left as None then EEG is assumed and skin_air is expected to 
            already has sensors associated""")

    def __init__(self, **kwargs):
        """
        Initialse traited attributes and attributes that will hold OpenMEEG 
        objects.
        """
        super(ProjectionMatrix, self).__init__(**kwargs)
        LOG.debug(str(kwargs))

        #OpenMEEG attributes
        self.om_head = None
        self.om_sources = None
        self.om_sensors = None
        self.om_head2sensor = None

        self.om_inverse_head = None
        self.om_source_matrix = None
        self.om_source2sensor = None  #For MEG, not used for EEG

    def configure(self):
        """
        Converts TVB objects into a for accessible to OpenMEEG, then uses the 
        OpenMEEG library to calculate the intermediate matrices needed in 
        obtaining the final projection matrix.
        """
        super(ProjectionMatrix, self).configure()
        if self.sensors is None:
            self.sensors = self.skin_air.sensors

        if isinstance(self.sensors, sensors_module.SensorsEEG):
            self.skin_air.sensors = self.sensors
            self.skin_air.sensor_locations = self.sensors.sensors_to_surface(
                self.skin_air)

        # Create OpenMEEG objects from TVB objects.
        self.om_head = self.create_om_head()
        self.om_sources = self.create_om_sources()
        self.om_sensors = self.create_om_sensors()

        # Calculate based on type of sources
        if isinstance(self.sources, surfaces_module.Cortex):
            self.om_source_matrix = self.surface_source()  #NOTE: ~1 hr
        elif isinstance(self.sources, connectivity_module.Connectivity):
            self.om_source_matrix = self.dipole_source()

        # Calculate based on type of sensors
        if isinstance(self.sensors, sensors_module.SensorsEEG):
            self.om_head2sensor = self.head2eeg()
        elif isinstance(self.sensors, sensors_module.SensorsMEG):
            self.om_head2sensor = self.head2meg()
            if isinstance(self.sources, surfaces_module.Cortex):
                self.om_source2sensor = self.surf2meg()
            elif isinstance(self.sources, connectivity_module.Connectivity):
                self.om_source2sensor = self.dip2meg()

        #NOTE: ~1 hr
        self.om_inverse_head = self.inverse_head(inv_head_mat_file="hminv_uid")

    def __call__(self):
        """
        Having configured the ProjectionMatrix instance, that is having run the 
        configure() method or otherwise provided the intermedite OpenMEEG (om_*)
        attributes, the oblect can be called as a function -- returning a 
        projection matrix as a Numpy array.
        """
        #Check source type and sensor type, then call appripriate methods to
        #generate intermediate data, cascading all the way back to geometry
        #calculation if it wasn't already done.
        #Then return a projection matrix...

        # NOTE: returned projection_matrix is a numpy.ndarray
        if isinstance(self.sensors, sensors_module.SensorsEEG):
            projection_matrix = self.eeg_gain()
        elif isinstance(self.sensors, sensors_module.SensorsMEG):
            projection_matrix = self.meg_gain()

        return projection_matrix

    ##------------------------------------------------------------------------##
    ##--------------- Methods for creating openmeeg objects ------------------##
    ##------------------------------------------------------------------------##

    def create_om_head(self):  #TODO: Prob. need to make file names specifiable
        """
        Generates 5 files::

            skull_skin.tri
            skin_air.tri
            brain_skull.tri
            head_model.geom
            head_model.cond

        Containing the specification of a head in a form that can be read by 
        OpenMEEG, then creates and returns an OpenMEEG Geometry object containing 
        this information.
        """
        surface_files = []
        surface_files.append(self._tvb_surface_to_tri("skull_skin.tri"))
        surface_files.append(self._tvb_surface_to_tri("brain_skull.tri"))
        surface_files.append(self._tvb_surface_to_tri("skin_air.tri"))

        geometry_file = self._write_head_geometry(surface_files,
                                                  "head_model.geom")
        conductances_file = self._write_conductances("head_model.cond")

        LOG.info("Creating OpenMEEG Geometry object for the head...")
        om_head = om.Geometry()
        om_head.read(geometry_file, conductances_file)
        #om_head.selfCheck() #Didn't catch bad order...
        LOG.info("OpenMEEG Geometry object for the head successfully created.")
        return om_head

    def create_om_sources(
            self):  #TODO: Prob. should make file names specifiable
        """
        Take a TVB Connectivity or Cortex object and return an OpenMEEG object
        that specifies sources, a Matrix object for region level sources or a
        Mesh object for a cortical surface source.
        """
        if isinstance(self.sources, connectivity_module.Connectivity):
            sources_file = self._tvb_connectivity_to_txt("sources.txt")
            om_sources = om.Matrix()
        elif isinstance(self.sources, surfaces_module.Cortex):
            sources_file = self._tvb_surface_to_tri("sources.tri")
            om_sources = om.Mesh()
        else:
            LOG.error("sources must be either a Connectivity or Cortex.")

        om_sources.load(sources_file)
        return om_sources

    def create_om_sensors(self, file_name=None):
        """
        Take a TVB Sensors object and return an OpenMEEG Sensors object.
        """
        if isinstance(self.sensors, sensors_module.SensorsEEG):
            file_name = file_name or "eeg_sensors.txt"
            sensors_file = self._tvb_eeg_sensors_to_txt(file_name)
        elif isinstance(self.sensors, sensors_module.SensorsMEG):
            file_name = file_name or "meg_sensors.squid"
            sensors_file = self._tvb_meg_sensors_to_squid(file_name)
        else:
            LOG.error("sensors should be either SensorsEEG or SensorsMEG")

        LOG.info("Wrote sensors to temporary file: %s" % str(file_name))

        om_sensors = om.Sensors()
        om_sensors.load(sensors_file)
        return om_sensors

    ##------------------------------------------------------------------------##
    ##--------- Methods for calling openmeeg methods, with logging. ----------##
    ##------------------------------------------------------------------------##

    def surf2meg(self):
        """
        Create a matrix that can be used to map an OpenMEEG surface source to an 
        OpenMEEG MEG Sensors object.

        NOTE: This source to sensor mapping is not required for EEG.

        """
        LOG.info("Computing DipSource2MEGMat...")
        surf2meg_mat = om.SurfSource2MEGMat(self.om_sources, self.om_sensors)
        LOG.info("surf2meg: %d x %d" %
                 (surf2meg_mat.nlin(), surf2meg_mat.ncol()))
        return surf2meg_mat

    def dip2meg(self):
        """
        Create an OpenMEEG Matrix that can be used to map OpenMEEG dipole sources 
        to an OpenMEEG MEG Sensors object.

        NOTE: This source to sensor mapping is not required for EEG.

        """
        LOG.info("Computing DipSource2MEGMat...")
        dip2meg_mat = om.DipSource2MEGMat(self.om_sources, self.om_sensors)
        LOG.info("dip2meg: %d x %d" % (dip2meg_mat.nlin(), dip2meg_mat.ncol()))
        return dip2meg_mat

    def head2eeg(self):
        """
        Call OpenMEEG's Head2EEGMat method to calculate the head to EEG sensor
        matrix.
        """
        LOG.info("Computing Head2EEGMat...")
        h2s_mat = om.Head2EEGMat(self.om_head, self.om_sensors)
        LOG.info("head2eeg: %d x %d" % (h2s_mat.nlin(), h2s_mat.ncol()))
        return h2s_mat

    def head2meg(self):
        """
        Call OpenMEEG's Head2MEGMat method to calculate the head to MEG sensor
        matrix.
        """
        LOG.info("Computing Head2MEGMat...")
        h2s_mat = om.Head2MEGMat(self.om_head, self.om_sensors)
        LOG.info("head2meg: %d x %d" % (h2s_mat.nlin(), h2s_mat.ncol()))
        return h2s_mat

    def surface_source(self, gauss_order=3, surf_source_file=None):
        """ 
        Call OpenMEEG's SurfSourceMat method to calculate a surface source 
        matrix. Optionaly saving the matrix for later use.
        """
        LOG.info("Computing SurfSourceMat...")
        ssm = om.SurfSourceMat(self.om_head, self.om_sources, gauss_order)
        LOG.info("surface_source_mat: %d x %d" % (ssm.nlin(), ssm.ncol()))
        if surf_source_file is not None:
            LOG.info("Saving surface_source matrix as %s..." %
                     surf_source_file)
            ssm.save(
                os.path.join(OM_STORAGE_DIR,
                             surf_source_file + OM_SAVE_SUFFIX))  #~3GB
        return ssm

    def dipole_source(self,
                      gauss_order=3,
                      use_adaptive_integration=True,
                      dip_source_file=None):
        """ 
        Call OpenMEEG's DipSourceMat method to calculate a dipole source matrix.
        Optionaly saving the matrix for later use.
        """
        LOG.info("Computing DipSourceMat...")
        dsm = om.DipSourceMat(self.om_head, self.om_sources, gauss_order,
                              use_adaptive_integration)
        LOG.info("dipole_source_mat: %d x %d" % (dsm.nlin(), dsm.ncol()))
        if dip_source_file is not None:
            LOG.info("Saving dipole_source matrix as %s..." % dip_source_file)
            dsm.save(
                os.path.join(OM_STORAGE_DIR, dip_source_file + OM_SAVE_SUFFIX))
        return dsm

    def inverse_head(self, gauss_order=3, inv_head_mat_file=None):
        """
        Call OpenMEEG's HeadMat method to calculate a head matrix. The inverse 
        method of the head matrix is subsequently called to invert the matrix.
        Optionaly saving the inverted matrix for later use.

        Runtime ~8 hours, mostly in martix inverse as I just use a stock ATLAS 
        install which doesn't appear to be multithreaded (custom building ATLAS
        should sort this)... Under Windows it should use MKL, not sure for Mac

        For reg13+potato surfaces, saved file size: hminv ~ 5GB, ssm ~ 3GB.
        """

        LOG.info("Computing HeadMat...")
        head_matrix = om.HeadMat(self.om_head, gauss_order)
        LOG.info("head_matrix: %d x %d" %
                 (head_matrix.nlin(), head_matrix.ncol()))

        LOG.info("Inverting HeadMat...")
        hminv = head_matrix.inverse()
        LOG.info("inverse head_matrix: %d x %d" % (hminv.nlin(), hminv.ncol()))

        if inv_head_mat_file is not None:
            LOG.info("Saving inverse_head matrix as %s..." % inv_head_mat_file)
            hminv.save(
                os.path.join(OM_STORAGE_DIR,
                             inv_head_mat_file + OM_SAVE_SUFFIX))  #~5GB
        return hminv

    def eeg_gain(self, eeg_file=None):
        """
        Call OpenMEEG's GainEEG method to calculate the final projection matrix.
        Optionaly saving the matrix for later use. The OpenMEEG matrix is 
        converted to a Numpy array before return. 
        """
        LOG.info("Computing GainEEG...")
        eeg_gain = om.GainEEG(self.om_inverse_head, self.om_source_matrix,
                              self.om_head2sensor)
        LOG.info("eeg_gain: %d x %d" % (eeg_gain.nlin(), eeg_gain.ncol()))
        if eeg_file is not None:
            LOG.info("Saving eeg_gain as %s..." % eeg_file)
            eeg_gain.save(
                os.path.join(OM_STORAGE_DIR, eeg_file + OM_SAVE_SUFFIX))
        return om.asarray(eeg_gain)

    def meg_gain(self, meg_file=None):
        """
        Call OpenMEEG's GainMEG method to calculate the final projection matrix.
        Optionaly saving the matrix for later use. The OpenMEEG matrix is 
        converted to a Numpy array before return. 
        """
        LOG.info("Computing GainMEG...")
        meg_gain = om.GainMEG(self.om_inverse_head, self.om_source_matrix,
                              self.om_head2sensor, self.om_source2sensor)
        LOG.info("meg_gain: %d x %d" % (meg_gain.nlin(), meg_gain.ncol()))
        if meg_file is not None:
            LOG.info("Saving meg_gain as %s..." % meg_file)
            meg_gain.save(
                os.path.join(OM_STORAGE_DIR, meg_file + OM_SAVE_SUFFIX))
        return om.asarray(meg_gain)

    ##------------------------------------------------------------------------##
    ##------- Methods for writting temporary files loaded by openmeeg --------##
    ##------------------------------------------------------------------------##

    def _tvb_meg_sensors_to_squid(self, sensors_file_name):
        """
        Write a tvb meg_sensor datatype to a .squid file, so that OpenMEEG can
        read it and compute the projection matrix for MEG...
        """
        sensors_file_path = os.path.join(OM_STORAGE_DIR, sensors_file_name)
        meg_sensors = numpy.hstack(
            (self.sensors.locations, self.sensors.orientations))
        numpy.savetxt(sensors_file_path, meg_sensors)
        return sensors_file_path

    def _tvb_connectivity_to_txt(self, dipoles_file_name):
        """
        Write position and orientation information from a TVB connectivity object 
        to a text file that can be read as source dipoles by OpenMEEG. 

        NOTE: Region level simulations lack sufficient detail of source orientation, 
            etc, to provide anything but superficial relevance. It's probably better
            to do a mapping of region level simulations to a surface and then
            perform the EEG projection from the mapped data...

        """
        NotImplementedError

    def _tvb_surface_to_tri(self, surface_file_name):
        """
        Write a tvb surface datatype to .tri format, so that OpenMEEG can read
        it and compute projection matrices for EEG/MEG/...
        """
        surface_file_path = os.path.join(OM_STORAGE_DIR, surface_file_name)

        #TODO: check file doesn't already exist
        LOG.info("Writing TVB surface to .tri file: %s" % surface_file_path)
        file_handle = file(surface_file_path, "a")

        file_handle.write("- %d \n" % self.sources.number_of_vertices)
        verts_norms = numpy.hstack(
            (self.sources.vertices, self.sources.vertex_normals))
        numpy.savetxt(file_handle, verts_norms)

        tri_str = "- " + (3 *
                          (str(self.sources.number_of_triangles) + " ")) + "\n"
        file_handle.write(tri_str)
        numpy.savetxt(file_handle, self.sources.triangles, fmt="%d")

        file_handle.close()
        LOG.info("%s written successfully." % surface_file_name)

        return surface_file_path

    def _tvb_eeg_sensors_to_txt(self, sensors_file_name):
        """
        Write a tvb eeg_sensor datatype (after mapping to the head surface to be 
        used) to a .txt file, so that OpenMEEG can read it and compute 
        leadfield/projection/forward_solution matrices for EEG...
        """
        sensors_file_path = os.path.join(OM_STORAGE_DIR, sensors_file_name)
        LOG.info("Writing TVB sensors to .txt file: %s" % sensors_file_path)
        numpy.savetxt(sensors_file_path, self.skin_air.sensor_locations)
        LOG.info("%s written successfully." % sensors_file_name)
        return sensors_file_path

    #TODO: enable specifying ?or determining? domain surface relationships...
    def _write_head_geometry(self, boundary_file_names, geom_file_name):
        """
        Write a geometry file that is read in by OpenMEEG, this file specifies
        the files containng the boundary surfaces and there relationship to the
        domains that comprise the head.

        NOTE: Currently the list of files is expected to be in a specific order, 
        namely::

            skull_skin
            brain_skull
            skin_air

        which is reflected in the static setting of domains. Should be generalised.
        """

        geom_file_path = os.path.join(OM_STORAGE_DIR, geom_file_name)

        #TODO: Check that the file doesn't already exist.
        LOG.info("Writing head geometry file: %s" % geom_file_path)
        file_handle = file(geom_file_path, "a")

        file_handle.write("# Domain Description 1.0\n\n")
        file_handle.write("Interfaces %d Mesh\n\n" % len(boundary_file_names))

        for file_name in boundary_file_names:
            file_handle.write("%s\n" % file_name)

        file_handle.write("\nDomains %d\n\n" % (len(boundary_file_names) + 1))
        file_handle.write("Domain Scalp %s %s\n" % (1, -3))
        file_handle.write("Domain Brain %s %s\n" % ("-2", "shared"))
        file_handle.write("Domain Air %s\n" % 3)
        file_handle.write("Domain Skull %s %s\n" % (2, -1))

        file_handle.close()
        LOG.info("%s written successfully." % geom_file_path)

        return geom_file_path

    def _write_conductances(self, cond_file_name):
        """
        Write a conductance file that is read in by OpenMEEG, this file 
        specifies the conductance of each of the domains making up the head.

        NOTE: Vaules are restricted to have 2 decimal places, ie #.##, setting 
            values of the form 0.00# will result in 0.01 or 0.00, for numbers 
            greater or less than ~0.00499999999999999967, respecitvely...

        """
        cond_file_path = os.path.join(OM_STORAGE_DIR, cond_file_name)

        #TODO: Check that the file doesn't already exist.
        LOG.info("Writing head conductance file: %s" % cond_file_path)
        file_handle = file(cond_file_path, "a")

        file_handle.write("# Properties Description 1.0 (Conductivities)\n\n")
        file_handle.write("Air         %4.2f\n" % self.conductances["air"])
        file_handle.write("Scalp       %4.2f\n" % self.conductances["skin"])
        file_handle.write("Brain       %4.2f\n" % self.conductances["brain"])
        file_handle.write("Skull       %4.2f\n" % self.conductances["skull"])

        file_handle.close()
        LOG.info("%s written successfully." % cond_file_path)

        return cond_file_path

    #TODO: Either make these utility functions or have them load directly into
    #      the appropriate attribute...
    ##------------------------------------------------------------------------##
    ##---- Methods for loading precomputed matrices into openmeeg objects ----##
    ##------------------------------------------------------------------------##

    def _load_om_inverse_head_mat(self, file_name):
        """
        Load a previously stored inverse head matrix into an OpenMEEG SymMatrix
        object.
        """
        inverse_head_martix = om.SymMatrix()
        inverse_head_martix.load(file_name)
        return inverse_head_martix

    def _load_om_source_mat(self, file_name):
        """
        Load a previously stored source matrix into an OpenMEEG Matrix object.
        """
        source_matrix = om.Matrix()
        source_matrix.load(file_name)
        return source_matrix
Esempio n. 24
0
class DoubleExponential(HRFKernelEquation):
    """
    A difference of two exponential functions to define a kernel for the bold monitor.

    **Parameters** :

    * :math:`\\tau_1`: Time constant of the second exponential function [s]
    * :math:`\\tau_2`: Time constant of the first exponential function [s].
    * :math:`f_1`  : Frequency of the first sine function [Hz].
    * :math:`f_2`  : Frequency of the second sine function [Hz].
    * :math:`amp_1`: Amplitude of the first exponential function.
    * :math:`amp_2`: Amplitude of the second exponential function.
    * :math:`a`    : Amplitude factor after normalization.


    **Reference**:

    .. [P_2000] Alex Polonsky, Randolph Blake, Jochen Braun and David J. Heeger
        (2000). Neuronal activity in human primary visual cortex correlates with
        perception during binocular rivalry. Nature Neuroscience 3: 1153-1159

    """

    _ui_name = "HRF kernel: Difference of Exponentials"

    equation = basic.String(
        label="Double Exponential Equation",
        default=
        "((amp_1 * exp(-var/tau_1) * sin(2.*pi*f_1*var)) - (amp_2 * exp(-var/ tau_2) * sin(2.*pi*f_2*var)))",
        locked=True,
        doc=""":math:`h(var) = amp_1\\exp(\\frac{-var}{\tau_1})
        \\sin(2\\cdot\\pi f_1 \\cdot var) - amp_2\\cdot \\exp(-\\frac{var}
        {\\tau_2})*\\sin(2\\pi f_2 var)`.""")

    parameters = basic.Dict(label="Double Exponential Parameters",
                            default={
                                "tau_1": 7.22,
                                "f_1": 0.03,
                                "amp_1": 0.1,
                                "tau_2": 7.4,
                                "f_2": 0.12,
                                "amp_2": 0.1,
                                "a": 0.1,
                                "pi": numpy.pi
                            })

    def _get_pattern(self):
        """
        Return a discrete representation of the equation.
        """
        return self._pattern

    def _set_pattern(self, var):
        """
        Generate a discrete representation of the equation for the space
        represented by ``var``.

        """

        self._pattern = numexpr.evaluate(self.equation,
                                         global_dict=self.parameters)
        self._pattern /= max(self._pattern)

        self._pattern *= self.parameters["a"]

    pattern = property(fget=_get_pattern, fset=_set_pattern)
Esempio n. 25
0
class Generic2dOscillator(models.Model):
    """
    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 models; 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 behaviors.  
    See:
        
        .. [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.
        
        .. [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.

     ---------------------------
    |  EXCITABLE CONFIGURATION  |
     ---------------------------
    |Parameter     |  Value     |
    -----------------------------
    | a            |     -2.0   |
    | b            |    -10.0   |
    | c            |      0.0   |
    | d            |      0.1   |
    | I            |      0.0   |
    -----------------------------
    |* limit cylce if a = 2.0   |
    -----------------------------
    
     ---------------------------
    |   BISTABLE CONFIGURATION  |
     ---------------------------
    |Parameter     |  Value     |
    -----------------------------
    | a            |      1.0   |
    | b            |      0.0   |
    | c            |     -5.0   |
    | d            |      0.1   |
    | I            |      0.0   |
    -----------------------------
    |* monostable regime:       |
    |* fixed point if Iext=-2.0 |
    |* limit cycle if Iext=-1.0 |
    -----------------------------
    
     ---------------------------
    |  EXCITABLE CONFIGURATION  | (similar to Morris-Lecar)
     ---------------------------
    |Parameter     |  Value     |
    -----------------------------
    | a            |      0.5   |
    | b            |      0.6   |
    | c            |     -4.0   |
    | d            |      0.1   |
    | I            |      0.0   |
    -----------------------------
    |* excitable regime if b=0.6|
    |* oscillatory if b=0.4     |
    -----------------------------
    
    
     ---------------------------
    |  SanzLeonetAl  2013       | 
     ---------------------------
    |Parameter     |  Value     |
    -----------------------------
    | a            |    - 0.5   |
    | b            |    -15.0   |
    | c            |      0.0   |
    | d            |      0.02  |
    | I            |      0.0   |
    -----------------------------
    |* excitable regime if      |
    |* intrinsic frequency is   |
    |  approx 10 Hz             |
    -----------------------------

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

    _ui_name = "Generic 2d Oscillator"
    ui_configurable_parameters = ['tau', 'a', 'b', 'c', 'd', 'I']

    #Define traited attributes for this model, these represent possible kwargs.
    tau = arrays.FloatArray(
        label = r":math:`\tau`",
        default = numpy.array([1.0]),
        range = basic.Range(lo = 0.00001, hi = 5.0, step = 0.01),
        doc = """A time-scale hierarchy can be introduced for the state 
        variables :math:`V` and :math:`W`. Default parameter is 1, which means
        no time-scale hierarchy.""",
        order = 1)

    I = arrays.FloatArray(
        label = ":math:`I_{ext}`",
        default = numpy.array([0.0]),
        range = basic.Range(lo = -2.0, hi = 2.0, step = 0.01),
        doc = """Baseline shift of the cubic nullcline""",
        order = 2)

    a = arrays.FloatArray(
        label = ":math:`a`",
        default = numpy.array([-2.0]),
        range = basic.Range(lo = -5.0, hi = 5.0, step = 0.01),
        doc = """Vertical shift of the configurable nullcline""",
        order = 3)

    b = arrays.FloatArray(
        label = ":math:`b`",
        default = numpy.array([-10.0]),
        range = basic.Range(lo = -20.0, hi = 15.0, step = 0.01),
        doc = """Linear slope of the configurable nullcline""",
        order = 4)

    c = arrays.FloatArray(
        label = ":math:`c`",
        default = numpy.array([0.0]),
        range = basic.Range(lo = -10.0, hi = 10.0, step = 0.01),
        doc = """Parabolic term of the configurable nullcline""",
        order = 5)
        
    d = arrays.FloatArray(
        label = ":math:`d`",
        default = numpy.array([0.1]),
        range = basic.Range(lo = 0.0001, hi = 1.0, step = 0.0001),
        doc = """Temporal scale factor.""",
        order = -1)
        
    e = arrays.FloatArray(
        label = ":math:`e`",
        default = numpy.array([3.0]),
        range = basic.Range(lo = -5.0, hi = 5.0, step = 0.0001),
        doc = """Coefficient of the quadratic term of the cubic nullcline.""",
        order = -1)
        
    f = arrays.FloatArray(
        label = ":math:`f`",
        default = numpy.array([1.0]),
        range = basic.Range(lo = -5.0, hi = 5.0, step = 0.0001),
        doc = """Coefficient of the cubic term of the cubic nullcline.""",
        order = -1)
        
    alpha = arrays.FloatArray(
        label = ":math:`\alpha`",
        default = numpy.array([1.0]),
        range = basic.Range(lo = -5.0, hi = 5.0, step = 0.0001),
        doc = """Constant parameter to scale the rate of feedback from the 
            slow variable to the fast variable.""",
        order = -1)
        
    beta = arrays.FloatArray(
        label = ":math:`\beta`",
        default = numpy.array([1.0]),
        range = basic.Range(lo = -5.0, hi = 5.0, step = 0.0001),
        doc = """Constant parameter to scale the rate of feedback from the 
            slow variable to itself""",
        order = -1)

    #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 initial 
            conditions when the simulation isn't started from an explicit
            history, it is also provides the default range of phase-plane plots.""",
        order = 6)

    
    variables_of_interest = basic.Enumerate(
        label = "Variables watched by Monitors",
        options = ["V", "W"],
        default = ["V",],
        select_multiple = True,
        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:`W = 1`.""",
        order = 7)
    

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

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

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

        self._nvar = 2 
        # long range coupling 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,
             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} &= \tau (\alpha W - V^3 +3 V^2 + I) \\
            \dot{W} &= (a\, + b\, V + c\, V^2 - \, beta W) / \tau

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

        """

        V = state_variables[0, :]
        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
        beta  = self.beta
        alpha = self.alpha

        lc_0 = local_coupling*V
        
        ## numexpr       
        dV = ev('d * tau * (alpha * W - f * V**3 + e * V**2 + I + c_0 + lc_0)')
        dW = ev('d * (a + b * V + c * V**2 - beta * W) / tau')

        self.derivative = numpy.array([dV, dW])
        
        return self.derivative
Esempio n. 26
0
class EpileptorDP2D(Model):

    r"""
    The Epileptor 2D is a composite neural mass model of two dimensions which
    has been crafted to model the phenomenology of epileptic seizures in a
    reduced form. This model is used for Linear Stability Analysis
    (see [Proixetal_2014]_).
    ->x0 parameters are shifted for the bifurcation
      to be at x0=1, where x0>1 is the supercritical region.
    ->there is a choice for linear or sigmoidal z dynamics (see [Proixetal_2014]_)
    ->some parameters change their names to be more similar to the equations.

    Equations and default parameters are taken from [Jirsaetal_2014]_.

          +------------------------------------------------------+
          |                         Table 1                      |
          +----------------------+-------------------------------+
          |        Parameter     |           Value               |
          +======================+===============================+
          |         I_ext1      |              3.1              |
          +----------------------+-------------------------------+
          |         tau0         |           2857.0              |
          +----------------------+-------------------------------+
          |         x_0          |              0.0              |
          +----------------------+-------------------------------+
          |         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, Proix 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.

    .. [Proixetal_2014] Proix, T., Bartolomei, F., Chauvel, P., Bernard, C.,
                       & Jirsa, V. K. (2014).
                       Permitau1ivity Coupling across Brain Regions Determines
                       Seizure Recruitment in Partial Epilepsy.
                       Journal of Neuroscience, 34(45), 15009–15021.
                       htau1p://doi.org/10.1523/JNEUROSCI.1570-14.2014

    .. automethod:: EpileptorDP.__init__

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

        .. math::
            \dot{x_{1}} &=& yc - f_{1}(x_{1},z) - z + I_{ext1} \\
            \dot{z} &=&
            \begin{cases}
            (f_z(x_{1}) - z-0.1 z^{7})/tau0 & \text{if } x<5/3 \\
            (f_z(x_{1}) - z)/tau0           & \text{if } x \geq 5/3
            \end{cases} \\

    where:
        .. math::
            f_{1}(x_{1},z) =
            \begin{cases}
            a ( x_{1} -5/3 )^{3} - b ( x_{1} -5/3 )^2 & \text{if } x_{1} <5/3\\
            ( 5*( x_{1} -5/3 ) - 0.6(z-4)^2 -slope) ( x_{1} - 5/3 ) &\text{if }x_{1} \geq 5/3
            \end{cases}

   and:
        .. math::
            f_z(x_{1})  =
            \begin{cases}
            4 * (x_{1} - r_{x0}*x0 + x0_{cr}) & \text{linear} \\
            \frac{3}{1+e^{-10*(x_{1}-7/6)}} - r_{x0}*x0 + x0_{cr} & \text{sigmoidal} \\
            \end{cases}

    """

    _ui_name = "EpileptorDP2D"
    ui_configurable_parameters = ["Iext1", "tau0", "x0", "slope"]

    zmode = arrays.FloatArray(
        label="zmode",
        default=numpy.array("lin"),
        doc="zmode = numpy.array(""lin"") for linear and numpy.array(""sig"") for sigmoidal z dynamics",
        order=-1)

#    a = arrays.FloatArray(
#        label="a",
#        default=numpy.array([1]),
#        doc="Coefficient of the cubic term in the first state variable",
#        order=-1)

#    b = arrays.FloatArray(
#        label="b",
#        default=numpy.array([3]),
#        doc="Coefficient of the squared term in the first state variabel",
#        order=-1)

    yc = arrays.FloatArray(
        label="yc",
        default=numpy.array([1]),
        doc="Additive coefficient for the second state variable",
        order=-1)

#    d = arrays.FloatArray(
#        label="d",
#        default=numpy.array([5]),
#        doc="Coefficient of the squared term in the second state variable",
#        order=-1)

    tau0 = arrays.FloatArray(
        label="tau0",
        range=basic.Range(lo=100.0, hi=5000, step=10),
        default=numpy.array([2857.0]),
        doc="Temporal scaling in the z state variable",
        order=4)

#    s = arrays.FloatArray(
#        label="s",
#        default=numpy.array([4]),
#        doc="Linear coefficient in the third state variable",
#        order=-1)

    x0 = arrays.FloatArray(
        label="x0",
        range=basic.Range(lo=-0.5, hi=1.5, step=0.1),
        default=numpy.array([0.0]),
        doc="Excitability parameter",
        order=3)

    x0cr = arrays.FloatArray(
        label="x0cr",
        range=basic.Range(lo=-1.0, hi=1.0, step=0.1),
        default=numpy.array([2.46018518518519]),
        doc="Critical excitability parameter",
        order=-1)

    r = arrays.FloatArray(
        label="r",
        range=basic.Range(lo=0.0, hi=1.0, step=0.1),
        default=numpy.array([43.0/108.0]),
        doc="Excitability parameter scaling",
        order=-1)

    Iext1 = arrays.FloatArray(
        label="Iext1",
        range=basic.Range(lo=1.5, hi=5.0, step=0.1),
        default=numpy.array([3.1]),
        doc="External input current to the first population",
        order=1)

    slope = arrays.FloatArray(
        label="slope",
        range=basic.Range(lo=-16.0, hi=6.0, step=0.1),
        default=numpy.array([0.]),
        doc="Linear coefficient in the first state variable",
        order=5)

    Kvf = arrays.FloatArray(
        label="K_vf",
        default=numpy.array([0.0]),
        range=basic.Range(lo=0.0, hi=4.0, step=0.5),
        doc="Coupling scaling on a very fast time scale.",
        order=6)

    K = arrays.FloatArray(
        label="K",
        default=numpy.array([0.0]),
        range=basic.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",
        order=8)

    tau1 = arrays.FloatArray(
        label="tau1",
        default=numpy.array([0.25]),
        range=basic.Range(lo=0.001, hi=10.0, step=0.001),
        doc="Time scaling of the whole system",
        order=9)

    state_variable_range = basic.Dict(
        label="State variable ranges [lo, hi]",
        default={"y0": numpy.array([-2., 2.]),
                 "y1": numpy.array([-2.0, 5.0])},
        doc="n/a",
        order=16
        )

    variables_of_interest = basic.Enumerate(
        label="Variables watched by Monitors",
        options=["y0", "y1"],
        default=["y0", "y1"],
        select_multiple=True,
        doc="""default state variables to be monitored""",
        order=-1)

    state_variables = ["y0", "y1"]

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

    def dfun(self, state_variables, coupling, local_coupling=0.0,
             array=numpy.array, where=numpy.where, concat=numpy.concatenate):
        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 (2, 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{y_{0}} &=& yc - f_{1}(y_{0}, y_{1}) - y_{2} + I_{ext1} \\
            \dot{y_{1}} &=&
            \begin{cases}
            (f_z(y_{0}) - y_{1}-0.1 y_{1}^{7})/tau0 & \text{if } y_{0}<5/3 \\
            (f_z(y_{0}) - y_{1})/tau0           & \text{if } y_{0} \geq 5/3
            \end{cases} \\

        where:
            .. math::
                f_{1}(y_{0}, y_{3}) =
                \begin{cases}
                a ( y_{0} -5/3 )^{3} - b ( y_{0} -5/3 )^2 & \text{if } y_{0} <5/3\\
                ( 5*( y_{0} -5/3 ) - 0.6(y_{1}-4)^2 -slope) ( y_{0} - 5/3 ) &\text{if }y_{0} \geq 5/3
                \end{cases}
        and:

            .. math::
                f_z(y_{0})  =
                \begin{cases}
                4 * (y_{0} - r*x0 + x0_{cr}) & \text{linear} \\
                \frac{3}{1+e^{-10*(y_{0}-7/6)}} - r*x0 + x0_{cr} & \text{sigmoidal} \\
                \end{cases}


        """

        y = state_variables
        ydot = numpy.empty_like(state_variables)

        Iext1 = self.Iext1 + local_coupling * y[0]
        c_pop1 = coupling[0, :]

        # population 1
        if_ydot0 = y[0] ** 2 + 2.0 * y[0] #self.a=1.0, self.b=-2.0
        else_ydot0 = 5 * y[0] - 0.6 * (y[1] - 4.0) ** 2 - self.slope
        ydot[0] = self.tau1 * (self.yc - y[1] + Iext1 + self.Kvf * c_pop1 - where(y[0] < 0.0, if_ydot0, else_ydot0) * y[0])

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

        if self.zmode == 'lin':
            fz = 4 * (y[0] - self.r * self.x0 + self.x0cr) + where(y[1] < 0.0, if_ydot1, else_ydot1) #self.x0

        elif self.zmode == 'sig':
            fz = 3.0 / (1.0 + numpy.exp(-10*(y[0] + 0.5))) - self.r * self.x0 + self.x0cr

        else:
            raise ValueError('zmode has to be either ""lin"" or ""sig"" for linear and sigmoidal fz(), respectively')

        ydot[1] = self.tau1*(fz - y[1] + self.K * c_pop1)/self.tau0

        return ydot
Esempio n. 27
0
class JansenRitDavid(models.Model):
    """
    The Jansen and Rit models as studied by David et al., 2005
    #TODO: finish this model

    """

    _ui_name = "Jansen-Rit (David et al., 2005)"

    #Define traited attributes for this model, these represent possible kwargs.
    He = arrays.FloatArray(
        label=":math:`He`",
        default=numpy.array([3.25]),
        range=basic.Range(lo=2.6, hi=9.75, step=0.05),
        doc=
        """Maximum amplitude of EPSP [mV]. Also called average synaptic gain.""",
        order=1)

    Hi = arrays.FloatArray(
        label=":math:`B`",
        default=numpy.array([29.3]),
        range=basic.Range(lo=17.6, hi=110.0, step=0.2),
        doc=
        """Maximum amplitude of IPSP [mV]. Also called average synaptic gain.""",
        order=2)

    tau_e = arrays.FloatArray(label=":math:`a`",
                              default=numpy.array([0.1]),
                              range=basic.Range(lo=0.05, hi=0.15, step=0.01),
                              doc="""time constant""",
                              order=3)

    tau_i = arrays.FloatArray(label=":math:`b`",
                              default=numpy.array([0.15]),
                              range=basic.Range(lo=0.025, hi=0.075,
                                                step=0.005),
                              doc="""time constant""",
                              order=4)

    eo = arrays.FloatArray(
        label=":math:`v_0`",
        default=numpy.array([0.0025]),
        range=basic.Range(lo=3.12, hi=6.0, step=0.02),
        doc="""Firing threshold (PSP) for which a 50% firing rate is achieved.
        In other words, it is the value of the average membrane potential
        corresponding to the inflection point of the sigmoid [mV].""",
        order=5)

    r = arrays.FloatArray(
        label=":math:`r`",
        default=numpy.array([0.56]),
        range=basic.Range(lo=0.28, hi=0.84, step=0.01),
        doc="""Steepness of the sigmoidal transformation [mV^-1].""",
        order=7)

    gamma_1 = arrays.FloatArray(
        label=r":math:`\alpha_1`",
        default=numpy.array([50.0]),
        range=basic.Range(lo=0.5, hi=1.5, step=0.1),
        doc="""Average probability of synaptic contacts in the feedback
        excitatory loop.""",
        order=9)

    gamma_2 = arrays.FloatArray(
        label=r":math:`\alpha_2`",
        default=numpy.array([40.]),
        range=basic.Range(lo=0.4, hi=1.2, step=0.1),
        doc="""Average probability of synaptic contacts in the feedback
        excitatory loop.""",
        order=10)

    gamma_3 = arrays.FloatArray(
        label=r":math:`\alpha_3`",
        default=numpy.array([12.]),
        range=basic.Range(lo=0.125, hi=0.375, step=0.005),
        doc="""Average probability of synaptic contacts in the feedback
        excitatory loop.""",
        order=11)

    gamma_4 = arrays.FloatArray(
        label=r":math:`\alpha_4`",
        default=numpy.array([12.]),
        range=basic.Range(lo=0.125, hi=0.375, step=0.005),
        doc="""Average probability of synaptic contacts in the slow feedback
        inhibitory loop.""",
        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={
            "x0": numpy.array([-1.0, 1.0]),
            "x1": numpy.array([-1.0, 1.0]),
            "x2": numpy.array([-5.0, 5.0]),
            "x3": numpy.array([-6.0, 6.0]),
            "x4": numpy.array([-2.0, 2.0]),
            "x5": numpy.array([-5.0, 5.0]),
            "x6": numpy.array([-5.0, 5.0]),
            "x7": numpy.array([-5.0, 5.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=16)

    variables_of_interest = basic.Enumerate(
        label="Variables watched by Monitors",
        options=["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7"],
        default=["x0", "x1", "x2", "x3"],
        select_multiple=True,
        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:`y0 = 0`,
                                    :math:`y1 = 1`, :math:`y2 = 2`, :math:`y3 = 3`, :math:`y4 = 4`, and
                                    :math:`y5 = 5`""",
        order=17)

    def __init__(self, **kwargs):
        """
        Initialise parameters for the Jansen Rit column, [JR_1995]_.

        """
        LOG.info("%s: initing..." % str(self))
        super(JansenRitDavid, self).__init__(**kwargs)

        #self._state_variables = ["y0", "y1", "y2", "y3", "y4", "y5"]
        self._nvar = 8

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

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

    def dfun(self, state_variables, coupling, local_coupling=0.0):
        r"""
        The dynamic equations were taken from:

        TODO: add equations and finish the model ...
            

        """

        magic_exp_number = 709
        AF = 0.1
        AB = 0.2
        AL = 0.05
        x0 = state_variables[0, :]
        x1 = state_variables[1, :]
        x2 = state_variables[2, :]
        x3 = state_variables[3, :]
        x4 = state_variables[4, :]
        x5 = state_variables[5, :]
        x6 = state_variables[6, :]
        x7 = state_variables[7, :]

        y = x1 - x2
        #delayed activity x1 - x2
        c_12 = coupling[0, :] - coupling[1, :]
        c_12_f = AF * ((2 * self.eo) /
                       (1 + numpy.exp(self.r * c_12)) - self.eo)
        c_12_b = AB * ((2 * self.eo) /
                       (1 + numpy.exp(self.r * c_12)) - self.eo)
        c_12_l = AL * ((2 * self.eo) /
                       (1 + numpy.exp(self.r * c_12)) - self.eo)

        lc_f = (local_coupling * y) * AF
        lc_l = (local_coupling * y) * AL
        lc_b = (local_coupling * y) * AB

        S_y = (2 * self.eo) / (1 + numpy.exp(self.r * y)) - self.eo
        S_x0 = (2 * self.eo) / (1 + numpy.exp(self.r * x0)) - self.eo
        S_x6 = (2 * self.eo) / (1 + numpy.exp(self.r * x6)) - self.eo

        # NOTE: for local couplings
        # 0:3 pyramidal cells
        # 1:4 excitatory interneurons
        # 2:5 inhibitory interneurons
        # 3:7

        dx0 = x3
        dx3 = self.He / self.tau_e * (c_12_f + c_12_l + self.gamma_1 * S_y) - (
            2 * x3) / self.tau_e - (x0 / self.tau_e**2)
        dx1 = x4
        dx4 = self.He / self.tau_e * (
            c_12_b + c_12_l + self.gamma_2 * S_x0) - (2 * x4) / self.tau_e - (
                x1 / self.tau_e**2)
        dx2 = x5
        dx5 = self.Hi / self.tau_i * (self.gamma_4 * S_x6) - (
            2 * x5) / self.tau_i - (x2 / self.tau_i**2)
        dx6 = x7
        dx7 = self.He / self.tau_e * (c_12_b + c_12_l + self.gamma_3 * S_y) - (
            2 * x7) / self.tau_e - (x6 / self.tau_e**2)

        derivative = numpy.array([dx0, dx1, dx2, dx3, dx4, dx5, dx6, dx7])

        return derivative
class MixtureOfGammasData(EquationData):
    """
    A mixture of two gamma distributions to create a kernel similar to the one used in SPM.

    >> import scipy.stats as sp_stats
    >> import numpy
    >> t = numpy.linspace(1,20,100)
    >> a1, a2 = 6., 10.
    >> lambda = 1.
    >> c      = 0.5
    >> hrf    = sp_stats.gamma.pdf(t, a1, lambda) - c * sp_stats.gamma.pdf(t, a2, lambda)

    gamma.pdf(x, a, theta) = (lambda*x)**(a-1) * exp(-lambda*x) / gamma(a)
    a                 : shape parameter
    theta: 1 / lambda : scale parameter


    **References**:    

    .. [G_1999] Glover, G. *Deconvolution of Impulse Response in Event-Related BOLD fMRI*.
                NeuroImage 9, 416-429, 1999.


    **Parameters**:
    

    * :math:`a_{1}`       : shape parameter first gamma pdf.
    * :math:`a_{2}`       : shape parameter second gamma pdf.
    * :math:`\\lambda`    : scale parameter first gamma pdf.


    Default values are based on [G_1999]_:
    * :math:`a_{1} - 1 = n_{1} =  5.0` 
    * :math:`a_{2} - 1 = n_{2} = 12.0`
    * :math:`c \\equiv a_{2}   = 0.4` 

    Alternative values :math:`a_{2}=10` and :math:`c=0.5`

    NOTE: gamma_a_1 and gamma_a_2 are placeholders, the true values are
    computed before evaluating the expression, because numexpr does not
    support certain functions.

    NOTE: [G_1999]_ used a different analytical function that can be approximated
    by this difference of gamma pdfs

    """

    _ui_name = "HRF kernel: Mixture of Gammas"

    equation = basic.String(
        label="Mixture of Gammas",
        default=
        "(l * var)**(a_1-1) * exp(-l*var) / gamma_a_1 - c * (l*var)**(a_2-1) * exp(-l*var) / gamma_a_2",
        locked=True,
        doc=
        """:math:`\\frac{\\lambda \\,t^{a_{1} - 1} \\,\\, \\exp^{-\\lambda \\,t}}{\\Gamma(a_{1})} 
        - 0.5 \\frac{\\lambda \\,t^{a_{2} - 1} \\,\\, \\exp^{-\\lambda \\,t}}{\\Gamma(a_{2})}`."""
    )

    parameters = basic.Dict(label="Double Exponential Parameters",
                            default={
                                "a_1": 6.0,
                                "a_2": 13.0,
                                "l": 1.0,
                                "c": 0.4,
                                "gamma_a_1": 1.0,
                                "gamma_a_2": 1.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
Esempio n. 30
0
class Gamma(HRFKernelEquation):
    """
    A Gamma function for the bold monitor. It belongs to the family of Exponential functions.

    **Parameters**:


    * :math:`\\tau`      : Exponential time constant of the gamma function [seconds].
    * :math:`n`          : The phase delay of the gamma function.
    * :math: `factorial` : (n-1)!. numexpr does not support factorial yet.
    * :math: `a`         : Amplitude factor after normalization.


    **Reference**:

    .. [B_1996] Geoffrey M. Boynton, Stephen A. Engel, Gary H. Glover and David
        J. Heeger (1996). Linear Systems Analysis of Functional Magnetic Resonance
        Imaging in Human V1. J Neurosci 16: 4207-4221

    .. note:: might be filtered from the equations used in Stimulus and Local Connectivity.

    """

    _ui_name = "HRF kernel: Gamma kernel"

    # TODO: Introduce a time delay in the equation (shifts the hrf onset)
    # """:math:`h(t) = \frac{(\frac{t-\delta}{\tau})^{(n-1)} e^{-(\frac{t-\delta}{\tau})}}{\tau(n-1)!}"""
    # delta = 2.05 seconds -- Additional delay in seconds from the onset of the
    # time-series to the beginning of the gamma hrf.
    # delay cannot be negative or greater than the hrf duration.

    equation = basic.String(
        label="Gamma Equation",
        default=
        "((var / tau) ** (n - 1) * exp(-(var / tau)) )/ (tau * factorial)",
        locked=True,
        doc=
        """:math:`h(var) = \\frac{(\\frac{var}{\\tau})^{(n-1)}\\exp{-(\\frac{var}{\\tau})}}{\\tau(n-1)!}`."""
    )

    parameters = basic.Dict(label="Gamma Parameters",
                            default={
                                "tau": 1.08,
                                "n": 3.0,
                                "factorial": 2.0,
                                "a": 0.1
                            })

    def _get_pattern(self):
        """
        Return a discrete representation of the equation.
        """
        return self._pattern

    def _set_pattern(self, var):
        """
        Generate a discrete representation of the equation for the space
        represented by ``var``.

        .. note: numexpr doesn't support factorial yet

        """

        # compute the factorial
        n = int(self.parameters["n"])
        product = 1
        for i in range(n - 1):
            product *= i + 1

        self.parameters["factorial"] = product
        self._pattern = numexpr.evaluate(self.equation,
                                         global_dict=self.parameters)
        self._pattern /= max(self._pattern)
        self._pattern *= self.parameters["a"]

    pattern = property(fget=_get_pattern, fset=_set_pattern)