From 94e5f8113252f5a43dc4beb9e61e4cfeac4e17c6 Mon Sep 17 00:00:00 2001 From: Julie COURTIOL Date: Thu, 22 Feb 2018 18:50:37 +0100 Subject: [PATCH 01/49] add modified z dynamics in epileptor and create epileptor 2D class (Proixetal, 2014) --- contrib/simulator/models/epileptor.py | 648 +++++++++++++++++++------- 1 file changed, 476 insertions(+), 172 deletions(-) diff --git a/contrib/simulator/models/epileptor.py b/contrib/simulator/models/epileptor.py index bd8164afb..3de8028ae 100644 --- a/contrib/simulator/models/epileptor.py +++ b/contrib/simulator/models/epileptor.py @@ -25,269 +25,573 @@ # Jochen Mersmann, Anthony R. McIntosh, Viktor Jirsa (2013) # The Virtual Brain: a simulator of primate brain network dynamics. # Frontiers in Neuroinformatics (7:10. doi: 10.3389/fninf.2013.00010) -# -# """ -The Epileptor model - -.. moduleauthor:: Marmaduke Woodman +Hindmarsh-Rose-Jirsa Epileptor model. """ -# Third party python libraries -import numpy - -#The Virtual Brain -from tvb.simulator.common import psutil, get_logger -LOG = get_logger(__name__) +from .base import ModelNumbaDfun, LOG, numpy, basic, arrays +from numba import guvectorize, float64 + +@guvectorize([(float64[:],) * 19], '(n),(m)' + ',()'*16 + '->(n)', nopython=True) +def _numba_dfun(y, c_pop, x0, Iext, Iext2, a, b, slope, tt, Kvf, c, d, r, Ks, Kf, aa, tau, modification, ydot): + "Gufunc for Hindmarsh-Rose-Jirsa Epileptor model equations." + + c_pop1 = c_pop[0] + c_pop2 = c_pop[1] + + # population 1 + if y[0] < 0.0: + ydot[0] = - a[0] * y[0] ** 2 + b[0] * y[0] + else: + ydot[0] = slope[0] - y[3] + 0.6 * (y[2] - 4.0) ** 2 + ydot[0] = tt[0] * (y[1] - y[2] + Iext[0] + Kvf[0] * c_pop1 + ydot[0] * y[0]) + ydot[1] = tt[0] * (c[0] - d[0] * y[0] ** 2 - y[1]) + + # energy + if y[2] < 0.0: + ydot[2] = - 0.1 * y[2] ** 7 + else: + ydot[2] = 0.0 + if modification[0]: + h = x0[0] + 3/(1 + numpy.exp(-(y[0]+0.5)/0.1)) + else: + h = 4 * (y[0] - x0[0]) + ydot[2] + ydot[2] = tt[0] * (r[0] * (h - y[2] + Ks[0] * c_pop1)) + + # population 2 + ydot[3] = tt[0] * (-y[4] + y[3] - y[3] ** 3 + Iext2[0] + 2 * y[5] - 0.3 * (y[2] - 3.5) + Kf[0] * c_pop2) + if y[3] < -0.25: + ydot[4] = 0.0 + else: + ydot[4] = aa[0] * (y[3] + 0.25) + ydot[4] = tt[0] * ((-y[4] + ydot[4]) / tau[0]) + + # filter + ydot[5] = tt[0] * (-0.01 * (y[5] - 0.1 * y[0])) + + +class Epileptor(ModelNumbaDfun): + r""" + The Epileptor is a composite neural mass model of six dimensions which + has been crafted to model the phenomenology of epileptic seizures. + (see [Jirsaetal_2014]_) + + Equations and default parameters are taken from [Jirsaetal_2014]_. + + +------------------------------------------------------+ + | Table 1 | + +----------------------+-------------------------------+ + | Parameter | Value | + +======================+===============================+ + | I_rest1 | 3.1 | + +----------------------+-------------------------------+ + | I_rest2 | 0.45 | + +----------------------+-------------------------------+ + | r | 0.00035 | + +----------------------+-------------------------------+ + | x_0 | -1.6 | + +----------------------+-------------------------------+ + | slope | 0.0 | + +----------------------+-------------------------------+ + | Integration parameter | + +----------------------+-------------------------------+ + | dt | 0.1 | + +----------------------+-------------------------------+ + | simulation_length | 4000 | + +----------------------+-------------------------------+ + | Noise | + +----------------------+-------------------------------+ + | nsig | [0., 0., 0., 1e-3, 1e-3, 0.] | + +----------------------+-------------------------------+ + | Jirsa et al. 2014 | + +------------------------------------------------------+ + + + .. figure :: img/Epileptor_01_mode_0_pplane.svg + :alt: Epileptor phase plane + + .. [Jirsaetal_2014] Jirsa, V. K.; Stacey, W. C.; Quilichini, P. P.; + Ivanov, A. I.; Bernard, C. *On the nature of seizure dynamics.* Brain, + 2014. + + .. automethod:: Epileptor.__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}} &=& c - d x_{1}^{2} - y{1} \\ + \dot{z} &=& + \begin{cases} + r(4 (x_{1} - x_{0}) - z-0.1 z^{7}) & \text{if } x<0 \\ + r(4 (x_{1} - x_{0}) - z) & \text{if } x \geq 0 + \end{cases} \\ + \dot{x_{2}} &=& -y_{2} + x_{2} - x_{2}^{3} + I_{ext2} + 0.002 g - 0.3 (z-3.5) \\ + \dot{y_{2}} &=& 1 / \tau (-y_{2} + f_{2}(x_{2}))\\ + \dot{g} &=& -0.01 (g - 0.1 x_{1}) + + where: + .. math:: + f_{1}(x_{1}, x_{2}) = + \begin{cases} + a x_{1}^{3} - b x_{1}^2 & \text{if } x_{1} <0\\ + -(slope - x_{2} + 0.6(z-4)^2) x_{1} &\text{if }x_{1} \geq 0 + \end{cases} + + and: + + .. math:: + f_{2}(x_{2}) = + \begin{cases} + 0 & \text{if } x_{2} <-0.25\\ + a_{2}(x_{2} + 0.25) & \text{if } x_{2} \geq -0.25 + \end{cases} + + Note Feb. 2017: the slow permittivity variable can be modify to account for the time + difference between interictal and ictal states (see [Proixetal_2014]). + + .. [Proixetal_2014] Proix, T.; Bartolomei, F; Chauvel, P; Bernard, C; Jirsa, V.K. * + Permittivity coupling across brain regions determines seizure recruitment in + partial epilepsy.* J Neurosci 2014, 34:15009-21. -import tvb.datatypes.arrays as arrays -import tvb.basic.traits.types_basic as basic -import tvb.simulator.models as models - - -class HMJEpileptor(models.Model): - """ - The Epileptor is a composite neural mass model of six dimensions which - has be crafted to model the phenomenology of epileptic seizures. - - This model, its motivation and derivation are currently in preparation - for publication (Jirsa et al, 2013) - - .. automethod:: HMJEpileptor.__init__ - .. automethod:: HMJEpileptor.dfun """ _ui_name = "Epileptor" - ui_configurable_parameters = ["Iext", "Iext2", "r", "x0"] + ui_configurable_parameters = ["Iext", "Iext2", "r", "x0", "slope"] a = arrays.FloatArray( label="a", default=numpy.array([1]), - doc="n/a", + doc="Coefficient of the cubic term in the first state variable", order=-1) b = arrays.FloatArray( label="b", default=numpy.array([3]), - doc="n/a", + doc="Coefficient of the squared term in the first state variabel", order=-1) c = arrays.FloatArray( label="c", default=numpy.array([1]), - doc="n/a", + doc="Additive coefficient for the second state variable, \ + called :math:`y_{0}` in Jirsa paper", order=-1) d = arrays.FloatArray( label="d", default=numpy.array([5]), - doc="n/a", + doc="Coefficient of the squared term in the second state variable", order=-1) r = arrays.FloatArray( label="r", range=basic.Range(lo=0.0, hi=0.001, step=0.00005), default=numpy.array([0.00035]), - doc="n/a", + doc="Temporal scaling in the third state variable, \ + called :math:`1/\\tau_{0}` in Jirsa paper", order=4) s = arrays.FloatArray( label="s", default=numpy.array([4]), - doc="n/a", + doc="Linear coefficient in the third state variable", order=-1) x0 = arrays.FloatArray( label="x0", - range=basic.Range(lo=-3.0, hi=0.0, step=0.1), + range=basic.Range(lo=-3.0, hi=-1.0, step=0.1), default=numpy.array([-1.6]), - doc="n/a", + doc="Epileptogenicity parameter", order=3) Iext = arrays.FloatArray( label="Iext", range=basic.Range(lo=1.5, hi=5.0, step=0.1), default=numpy.array([3.1]), - doc="n/a", + doc="External input current to the first population", order=1) - omega2 = arrays.FloatArray( - label="omega2", - default=numpy.array([0.1]), - doc="n/a", - order=-1) - slope = arrays.FloatArray( label="slope", + range=basic.Range(lo=-16.0, hi=6.0, step=0.1), default=numpy.array([0.]), - doc="n/a", - order=-1) + 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="n/a", + doc="External input current to the second population", order=2) tau = arrays.FloatArray( label="tau", default=numpy.array([10]), - doc="n/a", + doc="Temporal scaling coefficient in fifth state variable", order=-1) aa = arrays.FloatArray( label="aa", default=numpy.array([6]), - doc="n/a", + doc="Linear coefficient in fifth state variable", order=-1) - - Kpop1 = arrays.FloatArray( - label="K_11", - default=numpy.array([0.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='''Test parameter. Correspond to the coupling scaling. Move outside to be - consistent with the general TVB implementation.''', - order=-1) - - Kpop2 = arrays.FloatArray( - label="K_22", - default=numpy.array([0.2]), - range=basic.Range(lo=0.0, hi=1.0, step=0.5), - doc='''Test parameter. Correspond to the coupling scaling. Move outside to be - consistent with the general TVB implementation.''', - order=-1) + 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) + + Ks = arrays.FloatArray( + label="K_s", + 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) + + tt = arrays.FloatArray( + label="tt", + default=numpy.array([1.0]), + range=basic.Range(lo=0.001, hi=10.0, step=0.001), + doc="Time scaling of the whole system", + order=9) + + modification = arrays.BoolArray( + label="modification", + default=numpy.array([0]), + doc="When modification is True, then use nonlinear influence on z. \ + The default value is False, i.e., linear influence.", + order=10) state_variable_range = basic.Dict( label="State variable ranges [lo, hi]", - default = {"y0": numpy.array([0., 1e-10]), - "y1": numpy.array([-5., 0.]), - "y2": numpy.array([3., 4.]), - "y3": numpy.array([0., 1e-10]), - "y4": numpy.array([0., 1e-10]), - "y5": numpy.array([0., 1e-2]) }, - doc = "n/a", - order=-1 + default={"x1": numpy.array([-2., 1.]), + "y1": numpy.array([-20., 2.]), + "z": numpy.array([2.0, 5.0]), + "x2": numpy.array([-2., 0.]), + "y2": numpy.array([0., 2.]), + "g": numpy.array([-1., 1.])}, + doc="Typical bounds on state variables in the Epileptor model.", + order=16 ) variables_of_interest = basic.Enumerate( - label = "Variables watched by Monitors", - options = ["y0", "y1", "y2", "y3", "y4", "y5"], - default = ["y0", "y3"], - select_multiple = True, - doc = """default state variables to be monitored""", - order = 10) - -# variables_of_interest = arrays.IntegerArray( -# label="Variables watched by Monitors", -# range=basic.Range(lo=0.0, hi=6.0, step=1.0), -# default=numpy.array([0], dtype=numpy.int32), -# doc="default state variables to be monitored", -# order=10) - - - def __init__(self, **kwargs): - """ - """ - - LOG.info("%s: init'ing..." % (str(self),)) - - super(HMJEpileptor, self).__init__(**kwargs) - - #self._state_variables = ["y%d" % i for i in range(6)] - #self._state_variables = ["y%d" % i for i in range(6)] - self._nvar = 6 - self.cvar = numpy.array([0,3], dtype=numpy.int32) + label="Variables watched by Monitors", + options=['x1', 'y1', 'z', 'x2', 'y2', 'g', 'x2 - x1'], + default=["x2 - x1", 'z'], + select_multiple=True, + doc="Quantities of the Epileptor available to monitor.", + order=100 + ) + state_variables = ['x1', 'y1', 'z', 'x2', 'y2', 'g'] - LOG.debug("%s: init'ed." % (repr(self),)) + _nvar = 6 + cvar = numpy.array([0, 3], dtype=numpy.int32) - def dfun(self, state_variables, coupling, local_coupling=0.0, + def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, array=numpy.array, where=numpy.where, concat=numpy.concatenate): - """ - Computes the derivatives of the state variables of the Epileptor - with respect to time. + 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 + 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. + the original use of if/else is translated by calculated both the true + and false forms and mixing them using a boolean mask. Variables of interest to be used by monitors: -y[0] + y[3] - """ + .. math:: + \dot{x_{1}} &=& y_{1} - f_{1}(x_{1}, x_{2}) - z + I_{ext1} \\ + \dot{y_{1}} &=& c - d x_{1}^{2} - y{1} \\ + \dot{z} &=& + \begin{cases} + r(4 (x_{1} - x_{0}) - z-0.1 z^{7}) & \text{if } x<0 \\ + r(4 (x_{1} - x_{0}) - z) & \text{if } x \geq 0 + \end{cases} \\ + \dot{x_{2}} &=& -y_{2} + x_{2} - x_{2}^{3} + I_{ext2} + 0.002 g - 0.3 (z-3.5) \\ + \dot{y_{2}} &=& 1 / \tau (-y_{2} + f_{2}(x_{2}))\\ + \dot{g} &=& -0.01 (g - 0.1 x_{1}) + + where: + .. math:: + f_{1}(x_{1}, x_{2}) = + \begin{cases} + a x_{1}^{3} - b x_{1}^2 & \text{if } x_{1} <0\\ + -(slope - x_{2} + 0.6(z-4)^2) x_{1} &\text{if }x_{1} \geq 0 + \end{cases} + + .. math:: + f_{2}(x_{2}) = + \begin{cases} + 0 & \text{if } x_{2} <-0.25\\ + a_{2}(x_{2} + 0.25) & \text{if } x_{2} \geq -0.25 + \end{cases} """ - First population with high frequency burst and baseline jump - mechanisms - is similar to a Hindmarsh-Rose scenario with two régimes connected by a - slow trajectory (here y(3)). - """ - y = state_variables - n = y.shape[1] - Iext = self.Iext + coupling[0, :] + local_coupling + ydot = numpy.empty_like(state_variables) + + Iext = self.Iext + local_coupling * y[0] c_pop1 = coupling[0, :] c_pop2 = coupling[1, :] - # if y(1)<0. - # ydot1 = y(2)-a*y(1)^3 + b*y(1)^2-y(3)+iext; - # ydot2 = c-d*y(1)^2-y(2); - # ydot3 = r*(s*(y(1)-x0) - y(3)); % energy consumption = 1 - available energy - - if_y1_lt_0 = concat([ (y[1] - self.a*y[0]**3 + self.b*y[0]**2 - y[2] + Iext).reshape((1, n, 1)), - (self.c - self.d*y[0]**2 - y[1]).reshape((1, n, 1)), - (self.r*(self.s*(y[0] - self.x0) - y[2] - self.Kpop1 * (c_pop1 - y[0]) )).reshape((1, n, 1)) ]) - - # else - # % ydot1 = y(2) + (slope - y(4) -1.0*(y(3)-4))*y(1) - y(3)+iext; % this is just an - # % alternative representation, which worked well - # ydot1 = y(2) + (slope - y(4) + 0.6*(y(3)-4)^2)*y(1) -y(3)+iext; - # % here the high energy burst is being generated through variation of the slope: - # % 1. via y(4) within the epileptic spike complex; - # % 2. via the expression with y(3), which causes more - # % oscillations at the beginning of the seizure (effect of the - # % energy available) - # ydot2 = c-d*y(1)^2-y(2); - # ydot3 = r*(s*(y(1)-x0) - y(3)); - # end - - else_pop1 = concat([ (y[1] + (self.slope - y[3] + 0.6*(y[2]-4.0)**2)*y[0] - y[2] + Iext).reshape((1, n, 1)), - (self.c - self.d*y[0]**2 - y[1]).reshape((1, n, 1)), - (self.r*(self.s*(y[0] - self.x0) - y[2] - self.Kpop1 * (c_pop1 - y[0]))).reshape((1, n, 1)) ]) - - pop1 = where(y[0] < 0., if_y1_lt_0, else_pop1) - - # % istim= 0*block(t,150,1); - # - # % this is the second population that generates the big spike-wave complex - # % preictally and within the seizure via a morris-lecar-jirsa (mlj) structure - # - # if y(4)<-0.25 - # ydot4 = -y(5)+ y(4)-y(4)^3 + iext2 + 2*y(6)-0.3*(y(3)-3.5) ; % these last two terms - # % put the population dynamics into the critical regime. in particular, - # % y(6) turns the oscillator on and off, whereas the y(3) term helps it to become precritical (critical fluctuations). - # ydot5 = -y(5)/tau ; - - if_ = concat([ (-y[4] + y[3] - y[3]**3 + self.Iext2 + 2*y[5] - 0.3*(y[2] - 3.5) + self.Kpop2 * (c_pop2 - y[3])).reshape((1, n, 1)), (-y[4]/self.tau).reshape((1, n, 1)) ]) - # else - # ydot4 = -y(5)+ y(4)-y(4)^3 + iext2+ 2*y(6)-0.3*(y(3)-3.5); - # ydot5 = (-y(5) + aa*(y(4)+0.25))/tau; % here is the mlj structure - # end - - else_pop2 = concat([ (-y[4] + y[3] - y[3]**3 + self.Iext2 + 2*y[5] - 0.3*(y[2] - 3.5) + self.Kpop2 * (c_pop2 - y[3])).reshape((1, n, 1)), ((-y[4] + self.aa*(y[3] + 0.25))/self.tau).reshape((1, n, 1)) ]) - - - pop2 = where(y[3] < -0.25, if_, else_pop2) - - # - # ydot6 = -0.01*(y(6)-0.1*y(1)) ; - - energy = array([ -0.01*(y[5] - 0.1*y[0])]) - - # - # ydot = [ydot1;ydot2;ydot3;ydot4;ydot5;ydot6]; - - return concat((pop1, pop2, energy)) \ No newline at end of file + # population 1 + if_ydot0 = - self.a*y[0]**2 + self.b*y[0] + else_ydot0 = self.slope - y[3] + 0.6*(y[2]-4.0)**2 + ydot[0] = self.tt*(y[1] - y[2] + Iext + self.Kvf*c_pop1 + where(y[0] < 0., if_ydot0, else_ydot0) * y[0]) + ydot[1] = self.tt*(self.c - self.d*y[0]**2 - y[1]) + + # energy + if_ydot2 = - 0.1*y[2]**7 + else_ydot2 = 0 + if modification: + h = h = self.x0 + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) + else: + h = 4 * (y[0] - self.x0) + where(y[2] < 0., if_ydot2, else_ydot2) + ydot[2] = self.tt*(self.r * (h - y[2] + self.Ks*c_pop1)) + + # population 2 + ydot[3] = self.tt*(-y[4] + y[3] - y[3]**3 + self.Iext2 + 2*y[5] - 0.3*(y[2] - 3.5) + self.Kf*c_pop2) + if_ydot4 = 0 + else_ydot4 = self.aa*(y[3] + 0.25) + ydot[4] = self.tt*((-y[4] + where(y[3] < -0.25, if_ydot4, else_ydot4))/self.tau) + + # filter + ydot[5] = self.tt*(-0.01*(y[5] - 0.1*y[0])) + + return ydot + + def dfun(self, x, c, local_coupling=0.0): + x_ = x.reshape(x.shape[:-1]).T + c_ = c.reshape(c.shape[:-1]).T + Iext = self.Iext + local_coupling * x[0, :, 0] + deriv = _numba_dfun(x_, c_, + self.x0, Iext, self.Iext2, self.a, self.b, self.slope, self.tt, self.Kvf, + self.c, self.d, self.r, self.Ks, self.Kf, self.aa, self.tau, self.modification) + return deriv.T[..., numpy.newaxis] + + + + +class Epileptor2D(ModelNumbaDfun): + r""" + Two-dimensional reduction of the Epileptor. + + .. moduleauthor:: courtiol.julie@gmail.com + + Taking advantage of time scale separation and focusing on the slower time scale, + the five-dimensional Epileptor reduces to a two-dimensional system (see [Proixetal_2014, + Proixetal_2017]). + + Note: the slow permittivity variable can be modify to account for the time + difference between interictal and ictal states (see [Proixetal_2014]). + + Equations and default parameters are taken from [Proixetal_2014]: + + .. math:: + \dot{x_{1,i}} &=& - x_{1,i}^{3} - 2x_{1,i}^{2} + 1 - z_{i} + I_{ext1,i} \\ + \dot{z_{i}} &=& r(h - z_{i}) + + with + h = x_{0} + 3 / (exp((x_{1} + 0.5)/0.1)) if modification + h = 4 (x_{1,i} - x_{0}) + + References: + [Proixetal_2014] Proix, T.; Bartolomei, F; Chauvel, P; Bernard, C; Jirsa, V.K. * + Permittivity coupling across brain regions determines seizure recruitment in + partial epilepsy.* J Neurosci 2014, 34:15009-21. + + [Proixetal_2017] Proix, T.; Bartolomei, F; Guye, M.; Jirsa, V.K. *Individual brain + structure and modelling predict seizure propagation.* Brain 2017, 140; 641–654. + """ + + + _ui_name = "Epileptor2D" + ui_configurable_parameters = ["r", "Iext", "x0"] + + 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-variable.", + order=2) + + c = arrays.FloatArray( + label="c", + default=numpy.array([1]), + doc="Additive coefficient for the second state-variable x_{2}, \ + called :math:`y_{0}` in Jirsa paper.", + order=3) + + d = arrays.FloatArray( + label="d", + default=numpy.array([5]), + doc="Coefficient of the squared term in the second state-variable x_{2}.", + order=4) + + r = arrays.FloatArray( + label="r", + range=basic.Range(lo=0.0, hi=0.001, step=0.00005), + default=numpy.array([0.00035]), + doc="Temporal scaling in the slow state-variable, \ + called :math:`1\\tau_{0}` in Jirsa paper (see class Epileptor).", + order=5) + + x0 = arrays.FloatArray( + label="x0", + range=basic.Range(lo=-3.0, hi=-1.0, step=0.1), + default=numpy.array([-1.6]), + doc="Epileptogenicity parameter.", + order=6) + + Iext = arrays.FloatArray( + label="Iext", + range=basic.Range(lo=1.5, hi=5.0, step=0.1), + default=numpy.array([3.1]), + doc="External input current to the first state-variable.", + order=7) + + 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=8) + + 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=9) + + Ks = arrays.FloatArray( + label="K_s", + 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=10) + + tt = arrays.FloatArray( + label="tt", + default=numpy.array([1.0]), + range=basic.Range(lo=0.001, hi=1.0, step=0.001), + doc="Time scaling of the whole system to the system in real time.", + order=11) + + modification = arrays.BoolArray( + label="modification", + default=numpy.array([0]), + doc="When modification is True, then use nonlinear influence on z. \ + The default value is False, i.e., linear influence.", + order=12) + + state_variable_range = basic.Dict( + label="State variable ranges [lo, hi]", + default={"x1": numpy.array([-2., 1.]), + "z": numpy.array([2.0, 5.0])}, + doc="Typical bounds on state-variables in the Epileptor 2D model.", + order=99) + + variables_of_interest = basic.Enumerate( + label="Variables watched by Monitors", + options=['x1', 'z'], + default=['x1'], + select_multiple=True, + doc="Quantities of the Epileptor 2D available to monitor.", + order=100) + + state_variables = ['x1', 'z'] + + _nvar = 1 + cvar = numpy.array([0], dtype=numpy.int32) + + + def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, + array=numpy.array, where=numpy.where, concat=numpy.concatenate): + r""" + Computes the derivatives of the state-variables of the Epileptor 2D + with respect to time. + """ + + y = state_variables + ydot = numpy.empty_like(state_variables) + + Iext = self.Iext + local_coupling * y[0] + c_pop = coupling[0, :] + + # population 1 + if_ydot0 = self.a * y[0] ** 2 + (self.d - self.b) * y[0] + else_ydot0 = - self.slope - 0.6 * (y[1] - 4.0) ** 2 + self.d * y[0] + + ydot[0] = self.tt * (self.c - y[1] + Iext + self.Kvf * c_pop - (where(y[0] < 0., if_ydot0, else_ydot0)) * y[0]) + + # energy + if_ydot1 = - 0.1 * y[1] ** 7 + else_ydot1 = 0 + + if self.modification: + h = self.x0 + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) + else: + h = 4 * (y[0] - self.x0) + where(y[1] < 0., if_ydot1, else_ydot1) + + ydot[1] = self.tt * (self.r * (h - y[1] + self.Ks * c_pop)) + + return ydot + + def dfun(self, x, c, local_coupling=0.0): + """"The dfun using numba for speed.""" + + x_ = x.reshape(x.shape[:-1]).T + c_ = c.reshape(c.shape[:-1]).T + Iext = self.Iext + local_coupling * x[0, :, 0] + deriv = _numba_dfun_epi2d(x_, c_, + self.x0, Iext, self.a, self.b, self.slope, self.c, + self.d, self.r, self.Kvf, self.Ks, self.tt, self.modification) + return deriv.T[..., numpy.newaxis] + +@guvectorize([(float64[:],) * 15], '(n),(m)' + ',()'*12 + '->(n)', nopython=True) +def _numba_dfun_epi2d(y, c_pop, x0, Iext, a, b, slope, c, d, r, Kvf, Ks, tt, modification, ydot): + "Gufunction for Epileptor 2D model equations." + + c_pop = c_pop[0] + + # population 1 + if y[0] < 0.0: + ydot[0] = a[0] * y[0] ** 2 + (d[0] - b[0]) * y[0] + else: + ydot[0] = - slope[0] - 0.6 * (y[1] - 4.0) ** 2 + d[0] * y[0] + ydot[0] = tt[0] * (c[0] - y[1] + Iext[0] + Kvf[0] * c_pop - ydot[0] * y[0]) + + # energy + if y[1] < 0.0: + ydot[1] = - 0.1 * y[1] ** 7 + else: + ydot[1] = 0.0 + + if modification[0]: + h = x0[0] + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) + else: + h = 4 * (y[0] - x0[0]) + ydot[1] + + ydot[1] = tt[0] * (r[0] * (h - y[1] + Ks[0] * c_pop)) From 9b637d3b50ab8d2e58fc5dff5fee3ad7cb654b12 Mon Sep 17 00:00:00 2001 From: Julie Date: Thu, 8 Mar 2018 16:01:59 +0100 Subject: [PATCH 02/49] add modification z dynamics epileptor + new class epileptor2d. wes is still great. --- tvb/simulator/models/epileptor.py | 272 ++++++++++++++++++++++++++++-- 1 file changed, 256 insertions(+), 16 deletions(-) diff --git a/tvb/simulator/models/epileptor.py b/tvb/simulator/models/epileptor.py index 6c47b1901..bb6201a64 100644 --- a/tvb/simulator/models/epileptor.py +++ b/tvb/simulator/models/epileptor.py @@ -34,8 +34,8 @@ from .base import ModelNumbaDfun, LOG, numpy, basic, arrays from numba import guvectorize, float64 -@guvectorize([(float64[:],) * 18], '(n),(m)' + ',()'*15 + '->(n)', nopython=True) -def _numba_dfun(y, c_pop, x0, Iext, Iext2, a, b, slope, tt, Kvf, c, d, r, Ks, Kf, aa, tau, ydot): +@guvectorize([(float64[:],) * 20], '(n),(m)' + ',()'*17 + '->(n)', nopython=True) +def _numba_dfun(y, c_pop, x0, Iext, Iext2, a, b, slope, tt, Kvf, c, d, r, Ks, Kf, aa, bb, tau, modification, ydot): "Gufunc for Hindmarsh-Rose-Jirsa Epileptor model equations." c_pop1 = c_pop[0] @@ -54,10 +54,14 @@ def _numba_dfun(y, c_pop, x0, Iext, Iext2, a, b, slope, tt, Kvf, c, d, r, Ks, Kf ydot[2] = - 0.1 * y[2] ** 7 else: ydot[2] = 0.0 - ydot[2] = tt[0] * (r[0] * (4 * (y[0] - x0[0]) - y[2] + ydot[2] + Ks[0] * c_pop1)) + if modification[0]: + h = x0[0] + 3/(1 + numpy.exp(-(y[0]+0.5)/0.1)) + else: + h = 4 * (y[0] - x0[0]) + ydot[2] + ydot[2] = tt[0] * (r[0] * (h - y[2] + Ks[0] * c_pop1)) # population 2 - ydot[3] = tt[0] * (-y[4] + y[3] - y[3] ** 3 + Iext2[0] + 2 * y[5] - 0.3 * (y[2] - 3.5) + Kf[0] * c_pop2) + ydot[3] = tt[0] * (-y[4] + y[3] - y[3] ** 3 + Iext2[0] + bb[0] * y[5] - 0.3 * (y[2] - 3.5) + Kf[0] * c_pop2) if y[3] < -0.25: ydot[4] = 0.0 else: @@ -144,6 +148,14 @@ class Epileptor(ModelNumbaDfun): 0 & \text{if } x_{2} <-0.25\\ a_{2}(x_{2} + 0.25) & \text{if } x_{2} \geq -0.25 \end{cases} + + Note Feb. 2017: the slow permittivity variable can be modify to account for the time + difference between interictal and ictal states (see [Proixetal_2014]). + + .. [Proixetal_2014] Proix, T.; Bartolomei, F; Chauvel, P; Bernard, C; Jirsa, V.K. * + Permittivity coupling across brain regions determines seizure recruitment in + partial epilepsy.* J Neurosci 2014, 34:15009-21. + """ _ui_name = "Epileptor" @@ -227,6 +239,12 @@ class Epileptor(ModelNumbaDfun): default=numpy.array([6]), doc="Linear coefficient in fifth state variable", order=-1) + + bb = arrays.FloatArray( + label="bb", + default=numpy.array([2]), + doc="Linear coefficient of lowpass excitatory coupling in fourth state variable", + order=-1) Kvf = arrays.FloatArray( label="K_vf", @@ -255,6 +273,13 @@ class Epileptor(ModelNumbaDfun): range=basic.Range(lo=0.001, hi=10.0, step=0.001), doc="Time scaling of the whole system", order=9) + + modification = arrays.BoolArray( + label="modification", + default=numpy.array([0]), + doc="When modification is True, then use nonlinear influence on z. \ + The default value is False, i.e., linear influence.", + order=10) state_variable_range = basic.Dict( label="State variable ranges [lo, hi]", @@ -332,24 +357,28 @@ def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, c_pop2 = coupling[1, :] # population 1 - if_ydot0 = - self.a*y[0]**2 + self.b*y[0] - else_ydot0 = self.slope - y[3] + 0.6*(y[2]-4.0)**2 - ydot[0] = self.tt*(y[1] - y[2] + Iext + self.Kvf*c_pop1 + where(y[0] < 0., if_ydot0, else_ydot0) * y[0]) - ydot[1] = self.tt*(self.c - self.d*y[0]**2 - y[1]) + if_ydot0 = - self.a * y[0] ** 2 + self.b * y[0] + else_ydot0 = self.slope - y[3] + 0.6 * (y[2] - 4.0) ** 2 + ydot[0] = self.tt * (y[1] - y[2] + Iext + self.Kvf * c_pop1 + where(y[0] < 0., if_ydot0, else_ydot0) * y[0]) + ydot[1] = self.tt * (self.c - self.d * y[0] ** 2 - y[1]) # energy - if_ydot2 = - 0.1*y[2]**7 + if_ydot2 = - 0.1 * y[2] ** 7 else_ydot2 = 0 - ydot[2] = self.tt*(self.r * ( 4*(y[0] - self.x0) - y[2] + where(y[2] < 0., if_ydot2, else_ydot2) + self.Ks*c_pop1)) + if modification: + h = h = self.x0 + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) + else: + h = 4 * (y[0] - self.x0) + where(y[2] < 0., if_ydot2, else_ydot2) + ydot[2] = self.tt * (self.r * (h - y[2] + self.Ks * c_pop1)) # population 2 - ydot[3] = self.tt*(-y[4] + y[3] - y[3]**3 + self.Iext2 + 2*y[5] - 0.3*(y[2] - 3.5) + self.Kf*c_pop2) + ydot[3] = self.tt * (-y[4] + y[3] - y[3] ** 3 + self.Iext2 + self.bb * y[5] - 0.3 * (y[2] - 3.5) + self.Kf * c_pop2) if_ydot4 = 0 - else_ydot4 = self.aa*(y[3] + 0.25) - ydot[4] = self.tt*((-y[4] + where(y[3] < -0.25, if_ydot4, else_ydot4))/self.tau) + else_ydot4 = self.aa * (y[3] + 0.25) + ydot[4] = self.tt * ((-y[4] + where(y[3] < -0.25, if_ydot4, else_ydot4)) / self.tau) # filter - ydot[5] = self.tt*(-0.01*(y[5] - 0.1*y[0])) + ydot[5] = self.tt * (-0.01 * (y[5] - 0.1 * y[0])) return ydot @@ -359,5 +388,216 @@ def dfun(self, x, c, local_coupling=0.0): Iext = self.Iext + local_coupling * x[0, :, 0] deriv = _numba_dfun(x_, c_, self.x0, Iext, self.Iext2, self.a, self.b, self.slope, self.tt, self.Kvf, - self.c, self.d, self.r, self.Ks, self.Kf, self.aa, self.tau) - return deriv.T[..., numpy.newaxis] \ No newline at end of file + self.c, self.d, self.r, self.Ks, self.Kf, self.aa, self.bb, self.tau, self.modification) + return deriv.T[..., numpy.newaxis] + + + + +class Epileptor2D(ModelNumbaDfun): + r""" + Two-dimensional reduction of the Epileptor. + + .. moduleauthor:: courtiol.julie@gmail.com + + Taking advantage of time scale separation and focusing on the slower time scale, + the five-dimensional Epileptor reduces to a two-dimensional system (see [Proixetal_2014, + Proixetal_2017]). + + Note: the slow permittivity variable can be modify to account for the time + difference between interictal and ictal states (see [Proixetal_2014]). + + Equations and default parameters are taken from [Proixetal_2014]: + + .. math:: + \dot{x_{1,i}} &=& - x_{1,i}^{3} - 2x_{1,i}^{2} + 1 - z_{i} + I_{ext1,i} \\ + \dot{z_{i}} &=& r(h - z_{i}) + + with + h = x_{0} + 3 / (exp((x_{1} + 0.5)/0.1)) if modification + h = 4 (x_{1,i} - x_{0}) + + References: + [Proixetal_2014] Proix, T.; Bartolomei, F; Chauvel, P; Bernard, C; Jirsa, V.K. * + Permittivity coupling across brain regions determines seizure recruitment in + partial epilepsy.* J Neurosci 2014, 34:15009-21. + + [Proixetal_2017] Proix, T.; Bartolomei, F; Guye, M.; Jirsa, V.K. *Individual brain + structure and modelling predict seizure propagation.* Brain 2017, 140; 641–654. + """ + + + _ui_name = "Epileptor2D" + ui_configurable_parameters = ["r", "Iext", "x0"] + + 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-variable.", + order=2) + + c = arrays.FloatArray( + label="c", + default=numpy.array([1]), + doc="Additive coefficient for the second state-variable x_{2}, \ + called :math:`y_{0}` in Jirsa paper.", + order=3) + + d = arrays.FloatArray( + label="d", + default=numpy.array([5]), + doc="Coefficient of the squared term in the second state-variable x_{2}.", + order=4) + + r = arrays.FloatArray( + label="r", + range=basic.Range(lo=0.0, hi=0.001, step=0.00005), + default=numpy.array([0.00035]), + doc="Temporal scaling in the slow state-variable, \ + called :math:`1\\tau_{0}` in Jirsa paper (see class Epileptor).", + order=5) + + x0 = arrays.FloatArray( + label="x0", + range=basic.Range(lo=-3.0, hi=-1.0, step=0.1), + default=numpy.array([-1.6]), + doc="Epileptogenicity parameter.", + order=6) + + Iext = arrays.FloatArray( + label="Iext", + range=basic.Range(lo=1.5, hi=5.0, step=0.1), + default=numpy.array([3.1]), + doc="External input current to the first state-variable.", + order=7) + + 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=8) + + 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=9) + + Ks = arrays.FloatArray( + label="K_s", + 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=10) + + tt = arrays.FloatArray( + label="tt", + default=numpy.array([1.0]), + range=basic.Range(lo=0.001, hi=1.0, step=0.001), + doc="Time scaling of the whole system to the system in real time.", + order=11) + + modification = arrays.BoolArray( + label="modification", + default=numpy.array([0]), + doc="When modification is True, then use nonlinear influence on z. \ + The default value is False, i.e., linear influence.", + order=12) + + state_variable_range = basic.Dict( + label="State variable ranges [lo, hi]", + default={"x1": numpy.array([-2., 1.]), + "z": numpy.array([2.0, 5.0])}, + doc="Typical bounds on state-variables in the Epileptor 2D model.", + order=99) + + variables_of_interest = basic.Enumerate( + label="Variables watched by Monitors", + options=['x1', 'z'], + default=['x1'], + select_multiple=True, + doc="Quantities of the Epileptor 2D available to monitor.", + order=100) + + state_variables = ['x1', 'z'] + + _nvar = 2 + cvar = numpy.array([0], dtype=numpy.int32) + + + def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, + array=numpy.array, where=numpy.where, concat=numpy.concatenate): + r""" + Computes the derivatives of the state-variables of the Epileptor 2D + with respect to time. + """ + + y = state_variables + ydot = numpy.empty_like(state_variables) + + Iext = self.Iext + local_coupling * y[0] + c_pop = coupling[0, :] + + # population 1 + if_ydot0 = self.a * y[0] ** 2 + (self.d - self.b) * y[0] + else_ydot0 = - self.slope - 0.6 * (y[1] - 4.0) ** 2 + self.d * y[0] + + ydot[0] = self.tt * (self.c - y[1] + Iext + self.Kvf * c_pop - (where(y[0] < 0., if_ydot0, else_ydot0)) * y[0]) + + # energy + if_ydot1 = - 0.1 * y[1] ** 7 + else_ydot1 = 0 + + if self.modification: + h = self.x0 + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) + else: + h = 4 * (y[0] - self.x0) + where(y[1] < 0., if_ydot1, else_ydot1) + + ydot[1] = self.tt * (self.r * (h - y[1] + self.Ks * c_pop)) + + return ydot + + def dfun(self, x, c, local_coupling=0.0): + """"The dfun using numba for speed.""" + + x_ = x.reshape(x.shape[:-1]).T + c_ = c.reshape(c.shape[:-1]).T + Iext = self.Iext + local_coupling * x[0, :, 0] + deriv = _numba_dfun_epi2d(x_, c_, + self.x0, Iext, self.a, self.b, self.slope, self.c, + self.d, self.r, self.Kvf, self.Ks, self.tt, self.modification) + return deriv.T[..., numpy.newaxis] + +@guvectorize([(float64[:],) * 15], '(n),(m)' + ',()'* 12 + '->(n)', nopython=True) +def _numba_dfun_epi2d(y, c_pop, x0, Iext, a, b, slope, c, d, r, Kvf, Ks, tt, modification, ydot): + "Gufunction for Epileptor 2D model equations." + + c_pop = c_pop[0] + + # population 1 + if y[0] < 0.0: + ydot[0] = a[0] * y[0] ** 2 + (d[0] - b[0]) * y[0] + else: + ydot[0] = - slope[0] - 0.6 * (y[1] - 4.0) ** 2 + d[0] * y[0] + ydot[0] = tt[0] * (c[0] - y[1] + Iext[0] + Kvf[0] * c_pop - ydot[0] * y[0]) + + # energy + if y[1] < 0.0: + ydot[1] = - 0.1 * y[1] ** 7 + else: + ydot[1] = 0.0 + + if modification[0]: + h = x0[0] + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) + else: + h = 4 * (y[0] - x0[0]) + ydot[1] + + ydot[1] = tt[0] * (r[0] * (h - y[1] + Ks[0] * c_pop)) From 5aff56be228ddb6eb7904c01bc0762968806e8ee Mon Sep 17 00:00:00 2001 From: Julie Date: Thu, 8 Mar 2018 16:19:26 +0100 Subject: [PATCH 03/49] replacing bad file --- contrib/simulator/models/epileptor.py | 648 +++++++------------------- 1 file changed, 172 insertions(+), 476 deletions(-) diff --git a/contrib/simulator/models/epileptor.py b/contrib/simulator/models/epileptor.py index 3de8028ae..bd8164afb 100644 --- a/contrib/simulator/models/epileptor.py +++ b/contrib/simulator/models/epileptor.py @@ -25,573 +25,269 @@ # Jochen Mersmann, Anthony R. McIntosh, Viktor Jirsa (2013) # The Virtual Brain: a simulator of primate brain network dynamics. # Frontiers in Neuroinformatics (7:10. doi: 10.3389/fninf.2013.00010) +# +# """ -Hindmarsh-Rose-Jirsa Epileptor model. +The Epileptor model + +.. moduleauthor:: Marmaduke Woodman """ -from .base import ModelNumbaDfun, LOG, numpy, basic, arrays -from numba import guvectorize, float64 - -@guvectorize([(float64[:],) * 19], '(n),(m)' + ',()'*16 + '->(n)', nopython=True) -def _numba_dfun(y, c_pop, x0, Iext, Iext2, a, b, slope, tt, Kvf, c, d, r, Ks, Kf, aa, tau, modification, ydot): - "Gufunc for Hindmarsh-Rose-Jirsa Epileptor model equations." - - c_pop1 = c_pop[0] - c_pop2 = c_pop[1] - - # population 1 - if y[0] < 0.0: - ydot[0] = - a[0] * y[0] ** 2 + b[0] * y[0] - else: - ydot[0] = slope[0] - y[3] + 0.6 * (y[2] - 4.0) ** 2 - ydot[0] = tt[0] * (y[1] - y[2] + Iext[0] + Kvf[0] * c_pop1 + ydot[0] * y[0]) - ydot[1] = tt[0] * (c[0] - d[0] * y[0] ** 2 - y[1]) - - # energy - if y[2] < 0.0: - ydot[2] = - 0.1 * y[2] ** 7 - else: - ydot[2] = 0.0 - if modification[0]: - h = x0[0] + 3/(1 + numpy.exp(-(y[0]+0.5)/0.1)) - else: - h = 4 * (y[0] - x0[0]) + ydot[2] - ydot[2] = tt[0] * (r[0] * (h - y[2] + Ks[0] * c_pop1)) - - # population 2 - ydot[3] = tt[0] * (-y[4] + y[3] - y[3] ** 3 + Iext2[0] + 2 * y[5] - 0.3 * (y[2] - 3.5) + Kf[0] * c_pop2) - if y[3] < -0.25: - ydot[4] = 0.0 - else: - ydot[4] = aa[0] * (y[3] + 0.25) - ydot[4] = tt[0] * ((-y[4] + ydot[4]) / tau[0]) - - # filter - ydot[5] = tt[0] * (-0.01 * (y[5] - 0.1 * y[0])) - - -class Epileptor(ModelNumbaDfun): - r""" - The Epileptor is a composite neural mass model of six dimensions which - has been crafted to model the phenomenology of epileptic seizures. - (see [Jirsaetal_2014]_) - - Equations and default parameters are taken from [Jirsaetal_2014]_. - - +------------------------------------------------------+ - | Table 1 | - +----------------------+-------------------------------+ - | Parameter | Value | - +======================+===============================+ - | I_rest1 | 3.1 | - +----------------------+-------------------------------+ - | I_rest2 | 0.45 | - +----------------------+-------------------------------+ - | r | 0.00035 | - +----------------------+-------------------------------+ - | x_0 | -1.6 | - +----------------------+-------------------------------+ - | slope | 0.0 | - +----------------------+-------------------------------+ - | Integration parameter | - +----------------------+-------------------------------+ - | dt | 0.1 | - +----------------------+-------------------------------+ - | simulation_length | 4000 | - +----------------------+-------------------------------+ - | Noise | - +----------------------+-------------------------------+ - | nsig | [0., 0., 0., 1e-3, 1e-3, 0.] | - +----------------------+-------------------------------+ - | Jirsa et al. 2014 | - +------------------------------------------------------+ - - - .. figure :: img/Epileptor_01_mode_0_pplane.svg - :alt: Epileptor phase plane - - .. [Jirsaetal_2014] Jirsa, V. K.; Stacey, W. C.; Quilichini, P. P.; - Ivanov, A. I.; Bernard, C. *On the nature of seizure dynamics.* Brain, - 2014. - - .. automethod:: Epileptor.__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}} &=& c - d x_{1}^{2} - y{1} \\ - \dot{z} &=& - \begin{cases} - r(4 (x_{1} - x_{0}) - z-0.1 z^{7}) & \text{if } x<0 \\ - r(4 (x_{1} - x_{0}) - z) & \text{if } x \geq 0 - \end{cases} \\ - \dot{x_{2}} &=& -y_{2} + x_{2} - x_{2}^{3} + I_{ext2} + 0.002 g - 0.3 (z-3.5) \\ - \dot{y_{2}} &=& 1 / \tau (-y_{2} + f_{2}(x_{2}))\\ - \dot{g} &=& -0.01 (g - 0.1 x_{1}) - - where: - .. math:: - f_{1}(x_{1}, x_{2}) = - \begin{cases} - a x_{1}^{3} - b x_{1}^2 & \text{if } x_{1} <0\\ - -(slope - x_{2} + 0.6(z-4)^2) x_{1} &\text{if }x_{1} \geq 0 - \end{cases} - - and: - - .. math:: - f_{2}(x_{2}) = - \begin{cases} - 0 & \text{if } x_{2} <-0.25\\ - a_{2}(x_{2} + 0.25) & \text{if } x_{2} \geq -0.25 - \end{cases} - - Note Feb. 2017: the slow permittivity variable can be modify to account for the time - difference between interictal and ictal states (see [Proixetal_2014]). - - .. [Proixetal_2014] Proix, T.; Bartolomei, F; Chauvel, P; Bernard, C; Jirsa, V.K. * - Permittivity coupling across brain regions determines seizure recruitment in - partial epilepsy.* J Neurosci 2014, 34:15009-21. +# Third party python libraries +import numpy + +#The Virtual Brain +from tvb.simulator.common import psutil, get_logger +LOG = get_logger(__name__) +import tvb.datatypes.arrays as arrays +import tvb.basic.traits.types_basic as basic +import tvb.simulator.models as models + + +class HMJEpileptor(models.Model): + """ + The Epileptor is a composite neural mass model of six dimensions which + has be crafted to model the phenomenology of epileptic seizures. + + This model, its motivation and derivation are currently in preparation + for publication (Jirsa et al, 2013) + + .. automethod:: HMJEpileptor.__init__ + .. automethod:: HMJEpileptor.dfun """ _ui_name = "Epileptor" - ui_configurable_parameters = ["Iext", "Iext2", "r", "x0", "slope"] + ui_configurable_parameters = ["Iext", "Iext2", "r", "x0"] a = arrays.FloatArray( label="a", default=numpy.array([1]), - doc="Coefficient of the cubic term in the first state variable", + doc="n/a", order=-1) b = arrays.FloatArray( label="b", default=numpy.array([3]), - doc="Coefficient of the squared term in the first state variabel", + doc="n/a", order=-1) c = arrays.FloatArray( label="c", default=numpy.array([1]), - doc="Additive coefficient for the second state variable, \ - called :math:`y_{0}` in Jirsa paper", + doc="n/a", order=-1) d = arrays.FloatArray( label="d", default=numpy.array([5]), - doc="Coefficient of the squared term in the second state variable", + doc="n/a", order=-1) r = arrays.FloatArray( label="r", range=basic.Range(lo=0.0, hi=0.001, step=0.00005), default=numpy.array([0.00035]), - doc="Temporal scaling in the third state variable, \ - called :math:`1/\\tau_{0}` in Jirsa paper", + doc="n/a", order=4) s = arrays.FloatArray( label="s", default=numpy.array([4]), - doc="Linear coefficient in the third state variable", + doc="n/a", order=-1) x0 = arrays.FloatArray( label="x0", - range=basic.Range(lo=-3.0, hi=-1.0, step=0.1), + range=basic.Range(lo=-3.0, hi=0.0, step=0.1), default=numpy.array([-1.6]), - doc="Epileptogenicity parameter", + doc="n/a", order=3) Iext = arrays.FloatArray( label="Iext", 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", + doc="n/a", order=1) + omega2 = arrays.FloatArray( + label="omega2", + default=numpy.array([0.1]), + doc="n/a", + 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) + doc="n/a", + order=-1) 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", + doc="n/a", order=2) tau = arrays.FloatArray( label="tau", default=numpy.array([10]), - doc="Temporal scaling coefficient in fifth state variable", + doc="n/a", order=-1) aa = arrays.FloatArray( label="aa", default=numpy.array([6]), - doc="Linear coefficient in fifth state variable", + doc="n/a", 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]), + + Kpop1 = arrays.FloatArray( + label="K_11", + default=numpy.array([0.5]), 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) - - Ks = arrays.FloatArray( - label="K_s", - 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) - - tt = arrays.FloatArray( - label="tt", - default=numpy.array([1.0]), - range=basic.Range(lo=0.001, hi=10.0, step=0.001), - doc="Time scaling of the whole system", - order=9) - - modification = arrays.BoolArray( - label="modification", - default=numpy.array([0]), - doc="When modification is True, then use nonlinear influence on z. \ - The default value is False, i.e., linear influence.", - order=10) + doc='''Test parameter. Correspond to the coupling scaling. Move outside to be + consistent with the general TVB implementation.''', + order=-1) + + Kpop2 = arrays.FloatArray( + label="K_22", + default=numpy.array([0.2]), + range=basic.Range(lo=0.0, hi=1.0, step=0.5), + doc='''Test parameter. Correspond to the coupling scaling. Move outside to be + consistent with the general TVB implementation.''', + order=-1) state_variable_range = basic.Dict( label="State variable ranges [lo, hi]", - default={"x1": numpy.array([-2., 1.]), - "y1": numpy.array([-20., 2.]), - "z": numpy.array([2.0, 5.0]), - "x2": numpy.array([-2., 0.]), - "y2": numpy.array([0., 2.]), - "g": numpy.array([-1., 1.])}, - doc="Typical bounds on state variables in the Epileptor model.", - order=16 + default = {"y0": numpy.array([0., 1e-10]), + "y1": numpy.array([-5., 0.]), + "y2": numpy.array([3., 4.]), + "y3": numpy.array([0., 1e-10]), + "y4": numpy.array([0., 1e-10]), + "y5": numpy.array([0., 1e-2]) }, + doc = "n/a", + order=-1 ) variables_of_interest = basic.Enumerate( - label="Variables watched by Monitors", - options=['x1', 'y1', 'z', 'x2', 'y2', 'g', 'x2 - x1'], - default=["x2 - x1", 'z'], - select_multiple=True, - doc="Quantities of the Epileptor available to monitor.", - order=100 - ) + label = "Variables watched by Monitors", + options = ["y0", "y1", "y2", "y3", "y4", "y5"], + default = ["y0", "y3"], + select_multiple = True, + doc = """default state variables to be monitored""", + order = 10) + +# variables_of_interest = arrays.IntegerArray( +# label="Variables watched by Monitors", +# range=basic.Range(lo=0.0, hi=6.0, step=1.0), +# default=numpy.array([0], dtype=numpy.int32), +# doc="default state variables to be monitored", +# order=10) + + + def __init__(self, **kwargs): + """ + """ + + LOG.info("%s: init'ing..." % (str(self),)) + + super(HMJEpileptor, self).__init__(**kwargs) + + #self._state_variables = ["y%d" % i for i in range(6)] + #self._state_variables = ["y%d" % i for i in range(6)] + self._nvar = 6 + self.cvar = numpy.array([0,3], dtype=numpy.int32) - state_variables = ['x1', 'y1', 'z', 'x2', 'y2', 'g'] - _nvar = 6 - cvar = numpy.array([0, 3], dtype=numpy.int32) + LOG.debug("%s: init'ed." % (repr(self),)) - def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, + 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. + """ + 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 + 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. + the original use of if/else is translated by calculated both the true and + false forms and mixing them using a boolean mask. Variables of interest to be used by monitors: -y[0] + y[3] - .. math:: - \dot{x_{1}} &=& y_{1} - f_{1}(x_{1}, x_{2}) - z + I_{ext1} \\ - \dot{y_{1}} &=& c - d x_{1}^{2} - y{1} \\ - \dot{z} &=& - \begin{cases} - r(4 (x_{1} - x_{0}) - z-0.1 z^{7}) & \text{if } x<0 \\ - r(4 (x_{1} - x_{0}) - z) & \text{if } x \geq 0 - \end{cases} \\ - \dot{x_{2}} &=& -y_{2} + x_{2} - x_{2}^{3} + I_{ext2} + 0.002 g - 0.3 (z-3.5) \\ - \dot{y_{2}} &=& 1 / \tau (-y_{2} + f_{2}(x_{2}))\\ - \dot{g} &=& -0.01 (g - 0.1 x_{1}) - - where: - .. math:: - f_{1}(x_{1}, x_{2}) = - \begin{cases} - a x_{1}^{3} - b x_{1}^2 & \text{if } x_{1} <0\\ - -(slope - x_{2} + 0.6(z-4)^2) x_{1} &\text{if }x_{1} \geq 0 - \end{cases} - - .. math:: - f_{2}(x_{2}) = - \begin{cases} - 0 & \text{if } x_{2} <-0.25\\ - a_{2}(x_{2} + 0.25) & \text{if } x_{2} \geq -0.25 - \end{cases} - """ - y = state_variables - ydot = numpy.empty_like(state_variables) - - Iext = self.Iext + local_coupling * y[0] - c_pop1 = coupling[0, :] - c_pop2 = coupling[1, :] - - # population 1 - if_ydot0 = - self.a*y[0]**2 + self.b*y[0] - else_ydot0 = self.slope - y[3] + 0.6*(y[2]-4.0)**2 - ydot[0] = self.tt*(y[1] - y[2] + Iext + self.Kvf*c_pop1 + where(y[0] < 0., if_ydot0, else_ydot0) * y[0]) - ydot[1] = self.tt*(self.c - self.d*y[0]**2 - y[1]) - - # energy - if_ydot2 = - 0.1*y[2]**7 - else_ydot2 = 0 - if modification: - h = h = self.x0 + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) - else: - h = 4 * (y[0] - self.x0) + where(y[2] < 0., if_ydot2, else_ydot2) - ydot[2] = self.tt*(self.r * (h - y[2] + self.Ks*c_pop1)) - - # population 2 - ydot[3] = self.tt*(-y[4] + y[3] - y[3]**3 + self.Iext2 + 2*y[5] - 0.3*(y[2] - 3.5) + self.Kf*c_pop2) - if_ydot4 = 0 - else_ydot4 = self.aa*(y[3] + 0.25) - ydot[4] = self.tt*((-y[4] + where(y[3] < -0.25, if_ydot4, else_ydot4))/self.tau) - - # filter - ydot[5] = self.tt*(-0.01*(y[5] - 0.1*y[0])) - - return ydot - - def dfun(self, x, c, local_coupling=0.0): - x_ = x.reshape(x.shape[:-1]).T - c_ = c.reshape(c.shape[:-1]).T - Iext = self.Iext + local_coupling * x[0, :, 0] - deriv = _numba_dfun(x_, c_, - self.x0, Iext, self.Iext2, self.a, self.b, self.slope, self.tt, self.Kvf, - self.c, self.d, self.r, self.Ks, self.Kf, self.aa, self.tau, self.modification) - return deriv.T[..., numpy.newaxis] - - - - -class Epileptor2D(ModelNumbaDfun): - r""" - Two-dimensional reduction of the Epileptor. - - .. moduleauthor:: courtiol.julie@gmail.com - - Taking advantage of time scale separation and focusing on the slower time scale, - the five-dimensional Epileptor reduces to a two-dimensional system (see [Proixetal_2014, - Proixetal_2017]). - - Note: the slow permittivity variable can be modify to account for the time - difference between interictal and ictal states (see [Proixetal_2014]). - - Equations and default parameters are taken from [Proixetal_2014]: - - .. math:: - \dot{x_{1,i}} &=& - x_{1,i}^{3} - 2x_{1,i}^{2} + 1 - z_{i} + I_{ext1,i} \\ - \dot{z_{i}} &=& r(h - z_{i}) - - with - h = x_{0} + 3 / (exp((x_{1} + 0.5)/0.1)) if modification - h = 4 (x_{1,i} - x_{0}) - - References: - [Proixetal_2014] Proix, T.; Bartolomei, F; Chauvel, P; Bernard, C; Jirsa, V.K. * - Permittivity coupling across brain regions determines seizure recruitment in - partial epilepsy.* J Neurosci 2014, 34:15009-21. - - [Proixetal_2017] Proix, T.; Bartolomei, F; Guye, M.; Jirsa, V.K. *Individual brain - structure and modelling predict seizure propagation.* Brain 2017, 140; 641–654. - """ - - - _ui_name = "Epileptor2D" - ui_configurable_parameters = ["r", "Iext", "x0"] - - 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-variable.", - order=2) - - c = arrays.FloatArray( - label="c", - default=numpy.array([1]), - doc="Additive coefficient for the second state-variable x_{2}, \ - called :math:`y_{0}` in Jirsa paper.", - order=3) - d = arrays.FloatArray( - label="d", - default=numpy.array([5]), - doc="Coefficient of the squared term in the second state-variable x_{2}.", - order=4) - - r = arrays.FloatArray( - label="r", - range=basic.Range(lo=0.0, hi=0.001, step=0.00005), - default=numpy.array([0.00035]), - doc="Temporal scaling in the slow state-variable, \ - called :math:`1\\tau_{0}` in Jirsa paper (see class Epileptor).", - order=5) - - x0 = arrays.FloatArray( - label="x0", - range=basic.Range(lo=-3.0, hi=-1.0, step=0.1), - default=numpy.array([-1.6]), - doc="Epileptogenicity parameter.", - order=6) - - Iext = arrays.FloatArray( - label="Iext", - range=basic.Range(lo=1.5, hi=5.0, step=0.1), - default=numpy.array([3.1]), - doc="External input current to the first state-variable.", - order=7) - - 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=8) - - 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=9) - - Ks = arrays.FloatArray( - label="K_s", - 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=10) - - tt = arrays.FloatArray( - label="tt", - default=numpy.array([1.0]), - range=basic.Range(lo=0.001, hi=1.0, step=0.001), - doc="Time scaling of the whole system to the system in real time.", - order=11) - - modification = arrays.BoolArray( - label="modification", - default=numpy.array([0]), - doc="When modification is True, then use nonlinear influence on z. \ - The default value is False, i.e., linear influence.", - order=12) - - state_variable_range = basic.Dict( - label="State variable ranges [lo, hi]", - default={"x1": numpy.array([-2., 1.]), - "z": numpy.array([2.0, 5.0])}, - doc="Typical bounds on state-variables in the Epileptor 2D model.", - order=99) - - variables_of_interest = basic.Enumerate( - label="Variables watched by Monitors", - options=['x1', 'z'], - default=['x1'], - select_multiple=True, - doc="Quantities of the Epileptor 2D available to monitor.", - order=100) - - state_variables = ['x1', 'z'] - - _nvar = 1 - cvar = numpy.array([0], dtype=numpy.int32) - - - def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, - array=numpy.array, where=numpy.where, concat=numpy.concatenate): - r""" - Computes the derivatives of the state-variables of the Epileptor 2D - with respect to time. + """ + First population with high frequency burst and baseline jump - mechanisms + is similar to a Hindmarsh-Rose scenario with two régimes connected by a + slow trajectory (here y(3)). """ y = state_variables - ydot = numpy.empty_like(state_variables) - - Iext = self.Iext + local_coupling * y[0] - c_pop = coupling[0, :] - - # population 1 - if_ydot0 = self.a * y[0] ** 2 + (self.d - self.b) * y[0] - else_ydot0 = - self.slope - 0.6 * (y[1] - 4.0) ** 2 + self.d * y[0] - - ydot[0] = self.tt * (self.c - y[1] + Iext + self.Kvf * c_pop - (where(y[0] < 0., if_ydot0, else_ydot0)) * y[0]) - - # energy - if_ydot1 = - 0.1 * y[1] ** 7 - else_ydot1 = 0 - - if self.modification: - h = self.x0 + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) - else: - h = 4 * (y[0] - self.x0) + where(y[1] < 0., if_ydot1, else_ydot1) - - ydot[1] = self.tt * (self.r * (h - y[1] + self.Ks * c_pop)) - - return ydot - - def dfun(self, x, c, local_coupling=0.0): - """"The dfun using numba for speed.""" - - x_ = x.reshape(x.shape[:-1]).T - c_ = c.reshape(c.shape[:-1]).T - Iext = self.Iext + local_coupling * x[0, :, 0] - deriv = _numba_dfun_epi2d(x_, c_, - self.x0, Iext, self.a, self.b, self.slope, self.c, - self.d, self.r, self.Kvf, self.Ks, self.tt, self.modification) - return deriv.T[..., numpy.newaxis] - -@guvectorize([(float64[:],) * 15], '(n),(m)' + ',()'*12 + '->(n)', nopython=True) -def _numba_dfun_epi2d(y, c_pop, x0, Iext, a, b, slope, c, d, r, Kvf, Ks, tt, modification, ydot): - "Gufunction for Epileptor 2D model equations." - - c_pop = c_pop[0] - - # population 1 - if y[0] < 0.0: - ydot[0] = a[0] * y[0] ** 2 + (d[0] - b[0]) * y[0] - else: - ydot[0] = - slope[0] - 0.6 * (y[1] - 4.0) ** 2 + d[0] * y[0] - ydot[0] = tt[0] * (c[0] - y[1] + Iext[0] + Kvf[0] * c_pop - ydot[0] * y[0]) - - # energy - if y[1] < 0.0: - ydot[1] = - 0.1 * y[1] ** 7 - else: - ydot[1] = 0.0 - - if modification[0]: - h = x0[0] + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) - else: - h = 4 * (y[0] - x0[0]) + ydot[1] + n = y.shape[1] + Iext = self.Iext + coupling[0, :] + local_coupling + c_pop1 = coupling[0, :] + c_pop2 = coupling[1, :] - ydot[1] = tt[0] * (r[0] * (h - y[1] + Ks[0] * c_pop)) + # if y(1)<0. + # ydot1 = y(2)-a*y(1)^3 + b*y(1)^2-y(3)+iext; + # ydot2 = c-d*y(1)^2-y(2); + # ydot3 = r*(s*(y(1)-x0) - y(3)); % energy consumption = 1 - available energy + + if_y1_lt_0 = concat([ (y[1] - self.a*y[0]**3 + self.b*y[0]**2 - y[2] + Iext).reshape((1, n, 1)), + (self.c - self.d*y[0]**2 - y[1]).reshape((1, n, 1)), + (self.r*(self.s*(y[0] - self.x0) - y[2] - self.Kpop1 * (c_pop1 - y[0]) )).reshape((1, n, 1)) ]) + + # else + # % ydot1 = y(2) + (slope - y(4) -1.0*(y(3)-4))*y(1) - y(3)+iext; % this is just an + # % alternative representation, which worked well + # ydot1 = y(2) + (slope - y(4) + 0.6*(y(3)-4)^2)*y(1) -y(3)+iext; + # % here the high energy burst is being generated through variation of the slope: + # % 1. via y(4) within the epileptic spike complex; + # % 2. via the expression with y(3), which causes more + # % oscillations at the beginning of the seizure (effect of the + # % energy available) + # ydot2 = c-d*y(1)^2-y(2); + # ydot3 = r*(s*(y(1)-x0) - y(3)); + # end + + else_pop1 = concat([ (y[1] + (self.slope - y[3] + 0.6*(y[2]-4.0)**2)*y[0] - y[2] + Iext).reshape((1, n, 1)), + (self.c - self.d*y[0]**2 - y[1]).reshape((1, n, 1)), + (self.r*(self.s*(y[0] - self.x0) - y[2] - self.Kpop1 * (c_pop1 - y[0]))).reshape((1, n, 1)) ]) + + pop1 = where(y[0] < 0., if_y1_lt_0, else_pop1) + + # % istim= 0*block(t,150,1); + # + # % this is the second population that generates the big spike-wave complex + # % preictally and within the seizure via a morris-lecar-jirsa (mlj) structure + # + # if y(4)<-0.25 + # ydot4 = -y(5)+ y(4)-y(4)^3 + iext2 + 2*y(6)-0.3*(y(3)-3.5) ; % these last two terms + # % put the population dynamics into the critical regime. in particular, + # % y(6) turns the oscillator on and off, whereas the y(3) term helps it to become precritical (critical fluctuations). + # ydot5 = -y(5)/tau ; + + if_ = concat([ (-y[4] + y[3] - y[3]**3 + self.Iext2 + 2*y[5] - 0.3*(y[2] - 3.5) + self.Kpop2 * (c_pop2 - y[3])).reshape((1, n, 1)), (-y[4]/self.tau).reshape((1, n, 1)) ]) + # else + # ydot4 = -y(5)+ y(4)-y(4)^3 + iext2+ 2*y(6)-0.3*(y(3)-3.5); + # ydot5 = (-y(5) + aa*(y(4)+0.25))/tau; % here is the mlj structure + # end + + else_pop2 = concat([ (-y[4] + y[3] - y[3]**3 + self.Iext2 + 2*y[5] - 0.3*(y[2] - 3.5) + self.Kpop2 * (c_pop2 - y[3])).reshape((1, n, 1)), ((-y[4] + self.aa*(y[3] + 0.25))/self.tau).reshape((1, n, 1)) ]) + + + pop2 = where(y[3] < -0.25, if_, else_pop2) + + # + # ydot6 = -0.01*(y(6)-0.1*y(1)) ; + + energy = array([ -0.01*(y[5] - 0.1*y[0])]) + + # + # ydot = [ydot1;ydot2;ydot3;ydot4;ydot5;ydot6]; + + return concat((pop1, pop2, energy)) \ No newline at end of file From 5faab34332006ac6e88d95892556f9b5a8c9d644 Mon Sep 17 00:00:00 2001 From: liadomide Date: Thu, 5 Apr 2018 14:07:11 +0000 Subject: [PATCH 04/49] TVB-2357 Make sure Connectivity.from_file('...zip') can support both centers and centres files git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8688 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/basic/readers.py | 12 +++++++++--- tvb/datatypes/connectivity.py | 8 ++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/tvb/basic/readers.py b/tvb/basic/readers.py index 2b950660c..8ad683684 100644 --- a/tvb/basic/readers.py +++ b/tvb/basic/readers.py @@ -114,9 +114,10 @@ def read_array(self, dtype=numpy.float64, skip_rows=0, use_cols=None, matlab_dat # Try to read Matlab format: return self._read_matlab(self.file_stream, matlab_data_name) - except Exception: - self.logger.exception("Could not read from %s file" % self.file_path) - raise ReaderException("Could not read from %s file" % self.file_path) + except Exception, e: + msg = "Could not read from %s file \n %s" % (self.file_path, e) + self.logger.exception(msg) + raise ReaderException(msg) def _read_text(self, file_stream, dtype, skip_rows, use_cols): @@ -162,6 +163,11 @@ def __init__(self, zip_path): self.logger = get_logger(__name__) self.zip_archive = zipfile.ZipFile(zip_path) + def has_file_like(self, file_name): + for actual_name in self.zip_archive.namelist(): + if file_name in actual_name: + return True + return False def read_array_from_file(self, file_name, dtype=numpy.float64, skip_rows=0, use_cols=None, matlab_data_name=None): diff --git a/tvb/datatypes/connectivity.py b/tvb/datatypes/connectivity.py index 3c02368e1..af8016594 100644 --- a/tvb/datatypes/connectivity.py +++ b/tvb/datatypes/connectivity.py @@ -934,8 +934,12 @@ def from_file(source_file="connectivity_76.zip", instance=None): reader = ZipReader(source_full_path) result.weights = reader.read_array_from_file("weights") - result.centres = reader.read_array_from_file("centres", use_cols=(1, 2, 3)) - result.region_labels = reader.read_array_from_file("centres", dtype=numpy.str, use_cols=(0,)) + if reader.has_file_like("centres"): + result.centres = reader.read_array_from_file("centres", use_cols=(1, 2, 3)) + result.region_labels = reader.read_array_from_file("centres", dtype=numpy.str, use_cols=(0,)) + else: + result.centres = reader.read_array_from_file("centers", use_cols=(1, 2, 3)) + result.region_labels = reader.read_array_from_file("centers", dtype=numpy.str, use_cols=(0,)) result.orientations = reader.read_optional_array_from_file("average_orientations") result.cortical = reader.read_optional_array_from_file("cortical", dtype=numpy.bool) result.hemispheres = reader.read_optional_array_from_file("hemispheres", dtype=numpy.bool) From a3a2c1b5b340cf24dca1ae8d7bde2fdd43e0e3e5 Mon Sep 17 00:00:00 2001 From: liadomide Date: Thu, 5 Apr 2018 21:01:46 +0000 Subject: [PATCH 05/49] Update SVN revision number automatically from Hudson git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8689 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/basic/config/tvb.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvb/basic/config/tvb.version b/tvb/basic/config/tvb.version index 61cc7c145..1b04dd322 100644 --- a/tvb/basic/config/tvb.version +++ b/tvb/basic/config/tvb.version @@ -1 +1 @@ -Revision: 8687 \ No newline at end of file +Revision: 8689 \ No newline at end of file From ad95a954fe64f90c574ef890c929127d9e35a892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jopn=08=08=08=1B=5BC=1B=5BC=1B=5BC=1B=5BC=08=08=08hn=20Gri?= =?UTF-8?q?ffiths?= Date: Tue, 17 Apr 2018 11:57:04 -0400 Subject: [PATCH 06/49] modify _repr_html_ to also show trat descriptions --- tvb/basic/traits/core.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tvb/basic/traits/core.py b/tvb/basic/traits/core.py index e693eb682..4a2e636e4 100644 --- a/tvb/basic/traits/core.py +++ b/tvb/basic/traits/core.py @@ -502,11 +502,22 @@ def _repr_html_(self): "Generate HTML repr for use in IPython notebook." info = self.summary_info if info is None or len(info) == 0: - info = {key: getattr(self, key) for key in self.trait.keys()} + info = {} + for key in self.trait.keys(): + info[key] = [getattr(self,key), + getattr(self.__class__,key).trait.inits.kwd['doc']] html = [''] - row_fmt = '' - for key, value in info.items(): - html.append(row_fmt % (key, value)) + row_fmt = '' + header_fmt = '' + html.append(header_fmt % ('Parameter', 'Value', 'Description')) + for paramname,value in info.items(): + paramdescr = '' # default: no param description given + # ..if param description present (list len==2), use it + if type(value) == list: + paramval, paramdescr = value + else: + paramval = value + html.append(row_fmt % (paramname, paramval, paramdescr)) return ''.join(html) def _find_summary_info(self): From f72617b1626fc847f7cf84ceb6cc77e703ee9578 Mon Sep 17 00:00:00 2001 From: Julie COURTIOL Date: Thu, 22 Feb 2018 18:50:37 +0100 Subject: [PATCH 07/49] add modified z dynamics in epileptor and create epileptor 2D class (Proixetal, 2014) --- contrib/simulator/models/epileptor.py | 648 +++++++++++++++++++------- 1 file changed, 476 insertions(+), 172 deletions(-) diff --git a/contrib/simulator/models/epileptor.py b/contrib/simulator/models/epileptor.py index bd8164afb..3de8028ae 100644 --- a/contrib/simulator/models/epileptor.py +++ b/contrib/simulator/models/epileptor.py @@ -25,269 +25,573 @@ # Jochen Mersmann, Anthony R. McIntosh, Viktor Jirsa (2013) # The Virtual Brain: a simulator of primate brain network dynamics. # Frontiers in Neuroinformatics (7:10. doi: 10.3389/fninf.2013.00010) -# -# """ -The Epileptor model - -.. moduleauthor:: Marmaduke Woodman +Hindmarsh-Rose-Jirsa Epileptor model. """ -# Third party python libraries -import numpy - -#The Virtual Brain -from tvb.simulator.common import psutil, get_logger -LOG = get_logger(__name__) +from .base import ModelNumbaDfun, LOG, numpy, basic, arrays +from numba import guvectorize, float64 + +@guvectorize([(float64[:],) * 19], '(n),(m)' + ',()'*16 + '->(n)', nopython=True) +def _numba_dfun(y, c_pop, x0, Iext, Iext2, a, b, slope, tt, Kvf, c, d, r, Ks, Kf, aa, tau, modification, ydot): + "Gufunc for Hindmarsh-Rose-Jirsa Epileptor model equations." + + c_pop1 = c_pop[0] + c_pop2 = c_pop[1] + + # population 1 + if y[0] < 0.0: + ydot[0] = - a[0] * y[0] ** 2 + b[0] * y[0] + else: + ydot[0] = slope[0] - y[3] + 0.6 * (y[2] - 4.0) ** 2 + ydot[0] = tt[0] * (y[1] - y[2] + Iext[0] + Kvf[0] * c_pop1 + ydot[0] * y[0]) + ydot[1] = tt[0] * (c[0] - d[0] * y[0] ** 2 - y[1]) + + # energy + if y[2] < 0.0: + ydot[2] = - 0.1 * y[2] ** 7 + else: + ydot[2] = 0.0 + if modification[0]: + h = x0[0] + 3/(1 + numpy.exp(-(y[0]+0.5)/0.1)) + else: + h = 4 * (y[0] - x0[0]) + ydot[2] + ydot[2] = tt[0] * (r[0] * (h - y[2] + Ks[0] * c_pop1)) + + # population 2 + ydot[3] = tt[0] * (-y[4] + y[3] - y[3] ** 3 + Iext2[0] + 2 * y[5] - 0.3 * (y[2] - 3.5) + Kf[0] * c_pop2) + if y[3] < -0.25: + ydot[4] = 0.0 + else: + ydot[4] = aa[0] * (y[3] + 0.25) + ydot[4] = tt[0] * ((-y[4] + ydot[4]) / tau[0]) + + # filter + ydot[5] = tt[0] * (-0.01 * (y[5] - 0.1 * y[0])) + + +class Epileptor(ModelNumbaDfun): + r""" + The Epileptor is a composite neural mass model of six dimensions which + has been crafted to model the phenomenology of epileptic seizures. + (see [Jirsaetal_2014]_) + + Equations and default parameters are taken from [Jirsaetal_2014]_. + + +------------------------------------------------------+ + | Table 1 | + +----------------------+-------------------------------+ + | Parameter | Value | + +======================+===============================+ + | I_rest1 | 3.1 | + +----------------------+-------------------------------+ + | I_rest2 | 0.45 | + +----------------------+-------------------------------+ + | r | 0.00035 | + +----------------------+-------------------------------+ + | x_0 | -1.6 | + +----------------------+-------------------------------+ + | slope | 0.0 | + +----------------------+-------------------------------+ + | Integration parameter | + +----------------------+-------------------------------+ + | dt | 0.1 | + +----------------------+-------------------------------+ + | simulation_length | 4000 | + +----------------------+-------------------------------+ + | Noise | + +----------------------+-------------------------------+ + | nsig | [0., 0., 0., 1e-3, 1e-3, 0.] | + +----------------------+-------------------------------+ + | Jirsa et al. 2014 | + +------------------------------------------------------+ + + + .. figure :: img/Epileptor_01_mode_0_pplane.svg + :alt: Epileptor phase plane + + .. [Jirsaetal_2014] Jirsa, V. K.; Stacey, W. C.; Quilichini, P. P.; + Ivanov, A. I.; Bernard, C. *On the nature of seizure dynamics.* Brain, + 2014. + + .. automethod:: Epileptor.__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}} &=& c - d x_{1}^{2} - y{1} \\ + \dot{z} &=& + \begin{cases} + r(4 (x_{1} - x_{0}) - z-0.1 z^{7}) & \text{if } x<0 \\ + r(4 (x_{1} - x_{0}) - z) & \text{if } x \geq 0 + \end{cases} \\ + \dot{x_{2}} &=& -y_{2} + x_{2} - x_{2}^{3} + I_{ext2} + 0.002 g - 0.3 (z-3.5) \\ + \dot{y_{2}} &=& 1 / \tau (-y_{2} + f_{2}(x_{2}))\\ + \dot{g} &=& -0.01 (g - 0.1 x_{1}) + + where: + .. math:: + f_{1}(x_{1}, x_{2}) = + \begin{cases} + a x_{1}^{3} - b x_{1}^2 & \text{if } x_{1} <0\\ + -(slope - x_{2} + 0.6(z-4)^2) x_{1} &\text{if }x_{1} \geq 0 + \end{cases} + + and: + + .. math:: + f_{2}(x_{2}) = + \begin{cases} + 0 & \text{if } x_{2} <-0.25\\ + a_{2}(x_{2} + 0.25) & \text{if } x_{2} \geq -0.25 + \end{cases} + + Note Feb. 2017: the slow permittivity variable can be modify to account for the time + difference between interictal and ictal states (see [Proixetal_2014]). + + .. [Proixetal_2014] Proix, T.; Bartolomei, F; Chauvel, P; Bernard, C; Jirsa, V.K. * + Permittivity coupling across brain regions determines seizure recruitment in + partial epilepsy.* J Neurosci 2014, 34:15009-21. -import tvb.datatypes.arrays as arrays -import tvb.basic.traits.types_basic as basic -import tvb.simulator.models as models - - -class HMJEpileptor(models.Model): - """ - The Epileptor is a composite neural mass model of six dimensions which - has be crafted to model the phenomenology of epileptic seizures. - - This model, its motivation and derivation are currently in preparation - for publication (Jirsa et al, 2013) - - .. automethod:: HMJEpileptor.__init__ - .. automethod:: HMJEpileptor.dfun """ _ui_name = "Epileptor" - ui_configurable_parameters = ["Iext", "Iext2", "r", "x0"] + ui_configurable_parameters = ["Iext", "Iext2", "r", "x0", "slope"] a = arrays.FloatArray( label="a", default=numpy.array([1]), - doc="n/a", + doc="Coefficient of the cubic term in the first state variable", order=-1) b = arrays.FloatArray( label="b", default=numpy.array([3]), - doc="n/a", + doc="Coefficient of the squared term in the first state variabel", order=-1) c = arrays.FloatArray( label="c", default=numpy.array([1]), - doc="n/a", + doc="Additive coefficient for the second state variable, \ + called :math:`y_{0}` in Jirsa paper", order=-1) d = arrays.FloatArray( label="d", default=numpy.array([5]), - doc="n/a", + doc="Coefficient of the squared term in the second state variable", order=-1) r = arrays.FloatArray( label="r", range=basic.Range(lo=0.0, hi=0.001, step=0.00005), default=numpy.array([0.00035]), - doc="n/a", + doc="Temporal scaling in the third state variable, \ + called :math:`1/\\tau_{0}` in Jirsa paper", order=4) s = arrays.FloatArray( label="s", default=numpy.array([4]), - doc="n/a", + doc="Linear coefficient in the third state variable", order=-1) x0 = arrays.FloatArray( label="x0", - range=basic.Range(lo=-3.0, hi=0.0, step=0.1), + range=basic.Range(lo=-3.0, hi=-1.0, step=0.1), default=numpy.array([-1.6]), - doc="n/a", + doc="Epileptogenicity parameter", order=3) Iext = arrays.FloatArray( label="Iext", range=basic.Range(lo=1.5, hi=5.0, step=0.1), default=numpy.array([3.1]), - doc="n/a", + doc="External input current to the first population", order=1) - omega2 = arrays.FloatArray( - label="omega2", - default=numpy.array([0.1]), - doc="n/a", - order=-1) - slope = arrays.FloatArray( label="slope", + range=basic.Range(lo=-16.0, hi=6.0, step=0.1), default=numpy.array([0.]), - doc="n/a", - order=-1) + 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="n/a", + doc="External input current to the second population", order=2) tau = arrays.FloatArray( label="tau", default=numpy.array([10]), - doc="n/a", + doc="Temporal scaling coefficient in fifth state variable", order=-1) aa = arrays.FloatArray( label="aa", default=numpy.array([6]), - doc="n/a", + doc="Linear coefficient in fifth state variable", order=-1) - - Kpop1 = arrays.FloatArray( - label="K_11", - default=numpy.array([0.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='''Test parameter. Correspond to the coupling scaling. Move outside to be - consistent with the general TVB implementation.''', - order=-1) - - Kpop2 = arrays.FloatArray( - label="K_22", - default=numpy.array([0.2]), - range=basic.Range(lo=0.0, hi=1.0, step=0.5), - doc='''Test parameter. Correspond to the coupling scaling. Move outside to be - consistent with the general TVB implementation.''', - order=-1) + 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) + + Ks = arrays.FloatArray( + label="K_s", + 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) + + tt = arrays.FloatArray( + label="tt", + default=numpy.array([1.0]), + range=basic.Range(lo=0.001, hi=10.0, step=0.001), + doc="Time scaling of the whole system", + order=9) + + modification = arrays.BoolArray( + label="modification", + default=numpy.array([0]), + doc="When modification is True, then use nonlinear influence on z. \ + The default value is False, i.e., linear influence.", + order=10) state_variable_range = basic.Dict( label="State variable ranges [lo, hi]", - default = {"y0": numpy.array([0., 1e-10]), - "y1": numpy.array([-5., 0.]), - "y2": numpy.array([3., 4.]), - "y3": numpy.array([0., 1e-10]), - "y4": numpy.array([0., 1e-10]), - "y5": numpy.array([0., 1e-2]) }, - doc = "n/a", - order=-1 + default={"x1": numpy.array([-2., 1.]), + "y1": numpy.array([-20., 2.]), + "z": numpy.array([2.0, 5.0]), + "x2": numpy.array([-2., 0.]), + "y2": numpy.array([0., 2.]), + "g": numpy.array([-1., 1.])}, + doc="Typical bounds on state variables in the Epileptor model.", + order=16 ) variables_of_interest = basic.Enumerate( - label = "Variables watched by Monitors", - options = ["y0", "y1", "y2", "y3", "y4", "y5"], - default = ["y0", "y3"], - select_multiple = True, - doc = """default state variables to be monitored""", - order = 10) - -# variables_of_interest = arrays.IntegerArray( -# label="Variables watched by Monitors", -# range=basic.Range(lo=0.0, hi=6.0, step=1.0), -# default=numpy.array([0], dtype=numpy.int32), -# doc="default state variables to be monitored", -# order=10) - - - def __init__(self, **kwargs): - """ - """ - - LOG.info("%s: init'ing..." % (str(self),)) - - super(HMJEpileptor, self).__init__(**kwargs) - - #self._state_variables = ["y%d" % i for i in range(6)] - #self._state_variables = ["y%d" % i for i in range(6)] - self._nvar = 6 - self.cvar = numpy.array([0,3], dtype=numpy.int32) + label="Variables watched by Monitors", + options=['x1', 'y1', 'z', 'x2', 'y2', 'g', 'x2 - x1'], + default=["x2 - x1", 'z'], + select_multiple=True, + doc="Quantities of the Epileptor available to monitor.", + order=100 + ) + state_variables = ['x1', 'y1', 'z', 'x2', 'y2', 'g'] - LOG.debug("%s: init'ed." % (repr(self),)) + _nvar = 6 + cvar = numpy.array([0, 3], dtype=numpy.int32) - def dfun(self, state_variables, coupling, local_coupling=0.0, + def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, array=numpy.array, where=numpy.where, concat=numpy.concatenate): - """ - Computes the derivatives of the state variables of the Epileptor - with respect to time. + 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 + 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. + the original use of if/else is translated by calculated both the true + and false forms and mixing them using a boolean mask. Variables of interest to be used by monitors: -y[0] + y[3] - """ + .. math:: + \dot{x_{1}} &=& y_{1} - f_{1}(x_{1}, x_{2}) - z + I_{ext1} \\ + \dot{y_{1}} &=& c - d x_{1}^{2} - y{1} \\ + \dot{z} &=& + \begin{cases} + r(4 (x_{1} - x_{0}) - z-0.1 z^{7}) & \text{if } x<0 \\ + r(4 (x_{1} - x_{0}) - z) & \text{if } x \geq 0 + \end{cases} \\ + \dot{x_{2}} &=& -y_{2} + x_{2} - x_{2}^{3} + I_{ext2} + 0.002 g - 0.3 (z-3.5) \\ + \dot{y_{2}} &=& 1 / \tau (-y_{2} + f_{2}(x_{2}))\\ + \dot{g} &=& -0.01 (g - 0.1 x_{1}) + + where: + .. math:: + f_{1}(x_{1}, x_{2}) = + \begin{cases} + a x_{1}^{3} - b x_{1}^2 & \text{if } x_{1} <0\\ + -(slope - x_{2} + 0.6(z-4)^2) x_{1} &\text{if }x_{1} \geq 0 + \end{cases} + + .. math:: + f_{2}(x_{2}) = + \begin{cases} + 0 & \text{if } x_{2} <-0.25\\ + a_{2}(x_{2} + 0.25) & \text{if } x_{2} \geq -0.25 + \end{cases} """ - First population with high frequency burst and baseline jump - mechanisms - is similar to a Hindmarsh-Rose scenario with two régimes connected by a - slow trajectory (here y(3)). - """ - y = state_variables - n = y.shape[1] - Iext = self.Iext + coupling[0, :] + local_coupling + ydot = numpy.empty_like(state_variables) + + Iext = self.Iext + local_coupling * y[0] c_pop1 = coupling[0, :] c_pop2 = coupling[1, :] - # if y(1)<0. - # ydot1 = y(2)-a*y(1)^3 + b*y(1)^2-y(3)+iext; - # ydot2 = c-d*y(1)^2-y(2); - # ydot3 = r*(s*(y(1)-x0) - y(3)); % energy consumption = 1 - available energy - - if_y1_lt_0 = concat([ (y[1] - self.a*y[0]**3 + self.b*y[0]**2 - y[2] + Iext).reshape((1, n, 1)), - (self.c - self.d*y[0]**2 - y[1]).reshape((1, n, 1)), - (self.r*(self.s*(y[0] - self.x0) - y[2] - self.Kpop1 * (c_pop1 - y[0]) )).reshape((1, n, 1)) ]) - - # else - # % ydot1 = y(2) + (slope - y(4) -1.0*(y(3)-4))*y(1) - y(3)+iext; % this is just an - # % alternative representation, which worked well - # ydot1 = y(2) + (slope - y(4) + 0.6*(y(3)-4)^2)*y(1) -y(3)+iext; - # % here the high energy burst is being generated through variation of the slope: - # % 1. via y(4) within the epileptic spike complex; - # % 2. via the expression with y(3), which causes more - # % oscillations at the beginning of the seizure (effect of the - # % energy available) - # ydot2 = c-d*y(1)^2-y(2); - # ydot3 = r*(s*(y(1)-x0) - y(3)); - # end - - else_pop1 = concat([ (y[1] + (self.slope - y[3] + 0.6*(y[2]-4.0)**2)*y[0] - y[2] + Iext).reshape((1, n, 1)), - (self.c - self.d*y[0]**2 - y[1]).reshape((1, n, 1)), - (self.r*(self.s*(y[0] - self.x0) - y[2] - self.Kpop1 * (c_pop1 - y[0]))).reshape((1, n, 1)) ]) - - pop1 = where(y[0] < 0., if_y1_lt_0, else_pop1) - - # % istim= 0*block(t,150,1); - # - # % this is the second population that generates the big spike-wave complex - # % preictally and within the seizure via a morris-lecar-jirsa (mlj) structure - # - # if y(4)<-0.25 - # ydot4 = -y(5)+ y(4)-y(4)^3 + iext2 + 2*y(6)-0.3*(y(3)-3.5) ; % these last two terms - # % put the population dynamics into the critical regime. in particular, - # % y(6) turns the oscillator on and off, whereas the y(3) term helps it to become precritical (critical fluctuations). - # ydot5 = -y(5)/tau ; - - if_ = concat([ (-y[4] + y[3] - y[3]**3 + self.Iext2 + 2*y[5] - 0.3*(y[2] - 3.5) + self.Kpop2 * (c_pop2 - y[3])).reshape((1, n, 1)), (-y[4]/self.tau).reshape((1, n, 1)) ]) - # else - # ydot4 = -y(5)+ y(4)-y(4)^3 + iext2+ 2*y(6)-0.3*(y(3)-3.5); - # ydot5 = (-y(5) + aa*(y(4)+0.25))/tau; % here is the mlj structure - # end - - else_pop2 = concat([ (-y[4] + y[3] - y[3]**3 + self.Iext2 + 2*y[5] - 0.3*(y[2] - 3.5) + self.Kpop2 * (c_pop2 - y[3])).reshape((1, n, 1)), ((-y[4] + self.aa*(y[3] + 0.25))/self.tau).reshape((1, n, 1)) ]) - - - pop2 = where(y[3] < -0.25, if_, else_pop2) - - # - # ydot6 = -0.01*(y(6)-0.1*y(1)) ; - - energy = array([ -0.01*(y[5] - 0.1*y[0])]) - - # - # ydot = [ydot1;ydot2;ydot3;ydot4;ydot5;ydot6]; - - return concat((pop1, pop2, energy)) \ No newline at end of file + # population 1 + if_ydot0 = - self.a*y[0]**2 + self.b*y[0] + else_ydot0 = self.slope - y[3] + 0.6*(y[2]-4.0)**2 + ydot[0] = self.tt*(y[1] - y[2] + Iext + self.Kvf*c_pop1 + where(y[0] < 0., if_ydot0, else_ydot0) * y[0]) + ydot[1] = self.tt*(self.c - self.d*y[0]**2 - y[1]) + + # energy + if_ydot2 = - 0.1*y[2]**7 + else_ydot2 = 0 + if modification: + h = h = self.x0 + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) + else: + h = 4 * (y[0] - self.x0) + where(y[2] < 0., if_ydot2, else_ydot2) + ydot[2] = self.tt*(self.r * (h - y[2] + self.Ks*c_pop1)) + + # population 2 + ydot[3] = self.tt*(-y[4] + y[3] - y[3]**3 + self.Iext2 + 2*y[5] - 0.3*(y[2] - 3.5) + self.Kf*c_pop2) + if_ydot4 = 0 + else_ydot4 = self.aa*(y[3] + 0.25) + ydot[4] = self.tt*((-y[4] + where(y[3] < -0.25, if_ydot4, else_ydot4))/self.tau) + + # filter + ydot[5] = self.tt*(-0.01*(y[5] - 0.1*y[0])) + + return ydot + + def dfun(self, x, c, local_coupling=0.0): + x_ = x.reshape(x.shape[:-1]).T + c_ = c.reshape(c.shape[:-1]).T + Iext = self.Iext + local_coupling * x[0, :, 0] + deriv = _numba_dfun(x_, c_, + self.x0, Iext, self.Iext2, self.a, self.b, self.slope, self.tt, self.Kvf, + self.c, self.d, self.r, self.Ks, self.Kf, self.aa, self.tau, self.modification) + return deriv.T[..., numpy.newaxis] + + + + +class Epileptor2D(ModelNumbaDfun): + r""" + Two-dimensional reduction of the Epileptor. + + .. moduleauthor:: courtiol.julie@gmail.com + + Taking advantage of time scale separation and focusing on the slower time scale, + the five-dimensional Epileptor reduces to a two-dimensional system (see [Proixetal_2014, + Proixetal_2017]). + + Note: the slow permittivity variable can be modify to account for the time + difference between interictal and ictal states (see [Proixetal_2014]). + + Equations and default parameters are taken from [Proixetal_2014]: + + .. math:: + \dot{x_{1,i}} &=& - x_{1,i}^{3} - 2x_{1,i}^{2} + 1 - z_{i} + I_{ext1,i} \\ + \dot{z_{i}} &=& r(h - z_{i}) + + with + h = x_{0} + 3 / (exp((x_{1} + 0.5)/0.1)) if modification + h = 4 (x_{1,i} - x_{0}) + + References: + [Proixetal_2014] Proix, T.; Bartolomei, F; Chauvel, P; Bernard, C; Jirsa, V.K. * + Permittivity coupling across brain regions determines seizure recruitment in + partial epilepsy.* J Neurosci 2014, 34:15009-21. + + [Proixetal_2017] Proix, T.; Bartolomei, F; Guye, M.; Jirsa, V.K. *Individual brain + structure and modelling predict seizure propagation.* Brain 2017, 140; 641–654. + """ + + + _ui_name = "Epileptor2D" + ui_configurable_parameters = ["r", "Iext", "x0"] + + 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-variable.", + order=2) + + c = arrays.FloatArray( + label="c", + default=numpy.array([1]), + doc="Additive coefficient for the second state-variable x_{2}, \ + called :math:`y_{0}` in Jirsa paper.", + order=3) + + d = arrays.FloatArray( + label="d", + default=numpy.array([5]), + doc="Coefficient of the squared term in the second state-variable x_{2}.", + order=4) + + r = arrays.FloatArray( + label="r", + range=basic.Range(lo=0.0, hi=0.001, step=0.00005), + default=numpy.array([0.00035]), + doc="Temporal scaling in the slow state-variable, \ + called :math:`1\\tau_{0}` in Jirsa paper (see class Epileptor).", + order=5) + + x0 = arrays.FloatArray( + label="x0", + range=basic.Range(lo=-3.0, hi=-1.0, step=0.1), + default=numpy.array([-1.6]), + doc="Epileptogenicity parameter.", + order=6) + + Iext = arrays.FloatArray( + label="Iext", + range=basic.Range(lo=1.5, hi=5.0, step=0.1), + default=numpy.array([3.1]), + doc="External input current to the first state-variable.", + order=7) + + 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=8) + + 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=9) + + Ks = arrays.FloatArray( + label="K_s", + 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=10) + + tt = arrays.FloatArray( + label="tt", + default=numpy.array([1.0]), + range=basic.Range(lo=0.001, hi=1.0, step=0.001), + doc="Time scaling of the whole system to the system in real time.", + order=11) + + modification = arrays.BoolArray( + label="modification", + default=numpy.array([0]), + doc="When modification is True, then use nonlinear influence on z. \ + The default value is False, i.e., linear influence.", + order=12) + + state_variable_range = basic.Dict( + label="State variable ranges [lo, hi]", + default={"x1": numpy.array([-2., 1.]), + "z": numpy.array([2.0, 5.0])}, + doc="Typical bounds on state-variables in the Epileptor 2D model.", + order=99) + + variables_of_interest = basic.Enumerate( + label="Variables watched by Monitors", + options=['x1', 'z'], + default=['x1'], + select_multiple=True, + doc="Quantities of the Epileptor 2D available to monitor.", + order=100) + + state_variables = ['x1', 'z'] + + _nvar = 1 + cvar = numpy.array([0], dtype=numpy.int32) + + + def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, + array=numpy.array, where=numpy.where, concat=numpy.concatenate): + r""" + Computes the derivatives of the state-variables of the Epileptor 2D + with respect to time. + """ + + y = state_variables + ydot = numpy.empty_like(state_variables) + + Iext = self.Iext + local_coupling * y[0] + c_pop = coupling[0, :] + + # population 1 + if_ydot0 = self.a * y[0] ** 2 + (self.d - self.b) * y[0] + else_ydot0 = - self.slope - 0.6 * (y[1] - 4.0) ** 2 + self.d * y[0] + + ydot[0] = self.tt * (self.c - y[1] + Iext + self.Kvf * c_pop - (where(y[0] < 0., if_ydot0, else_ydot0)) * y[0]) + + # energy + if_ydot1 = - 0.1 * y[1] ** 7 + else_ydot1 = 0 + + if self.modification: + h = self.x0 + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) + else: + h = 4 * (y[0] - self.x0) + where(y[1] < 0., if_ydot1, else_ydot1) + + ydot[1] = self.tt * (self.r * (h - y[1] + self.Ks * c_pop)) + + return ydot + + def dfun(self, x, c, local_coupling=0.0): + """"The dfun using numba for speed.""" + + x_ = x.reshape(x.shape[:-1]).T + c_ = c.reshape(c.shape[:-1]).T + Iext = self.Iext + local_coupling * x[0, :, 0] + deriv = _numba_dfun_epi2d(x_, c_, + self.x0, Iext, self.a, self.b, self.slope, self.c, + self.d, self.r, self.Kvf, self.Ks, self.tt, self.modification) + return deriv.T[..., numpy.newaxis] + +@guvectorize([(float64[:],) * 15], '(n),(m)' + ',()'*12 + '->(n)', nopython=True) +def _numba_dfun_epi2d(y, c_pop, x0, Iext, a, b, slope, c, d, r, Kvf, Ks, tt, modification, ydot): + "Gufunction for Epileptor 2D model equations." + + c_pop = c_pop[0] + + # population 1 + if y[0] < 0.0: + ydot[0] = a[0] * y[0] ** 2 + (d[0] - b[0]) * y[0] + else: + ydot[0] = - slope[0] - 0.6 * (y[1] - 4.0) ** 2 + d[0] * y[0] + ydot[0] = tt[0] * (c[0] - y[1] + Iext[0] + Kvf[0] * c_pop - ydot[0] * y[0]) + + # energy + if y[1] < 0.0: + ydot[1] = - 0.1 * y[1] ** 7 + else: + ydot[1] = 0.0 + + if modification[0]: + h = x0[0] + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) + else: + h = 4 * (y[0] - x0[0]) + ydot[1] + + ydot[1] = tt[0] * (r[0] * (h - y[1] + Ks[0] * c_pop)) From 24c0e09610810cf6c2532e54c80c2cddc406bb5e Mon Sep 17 00:00:00 2001 From: Julie Date: Thu, 8 Mar 2018 16:01:59 +0100 Subject: [PATCH 08/49] add modification z dynamics epileptor + new class epileptor2d. wes is still great. --- tvb/simulator/models/epileptor.py | 272 ++++++++++++++++++++++++++++-- 1 file changed, 256 insertions(+), 16 deletions(-) diff --git a/tvb/simulator/models/epileptor.py b/tvb/simulator/models/epileptor.py index 6c47b1901..bb6201a64 100644 --- a/tvb/simulator/models/epileptor.py +++ b/tvb/simulator/models/epileptor.py @@ -34,8 +34,8 @@ from .base import ModelNumbaDfun, LOG, numpy, basic, arrays from numba import guvectorize, float64 -@guvectorize([(float64[:],) * 18], '(n),(m)' + ',()'*15 + '->(n)', nopython=True) -def _numba_dfun(y, c_pop, x0, Iext, Iext2, a, b, slope, tt, Kvf, c, d, r, Ks, Kf, aa, tau, ydot): +@guvectorize([(float64[:],) * 20], '(n),(m)' + ',()'*17 + '->(n)', nopython=True) +def _numba_dfun(y, c_pop, x0, Iext, Iext2, a, b, slope, tt, Kvf, c, d, r, Ks, Kf, aa, bb, tau, modification, ydot): "Gufunc for Hindmarsh-Rose-Jirsa Epileptor model equations." c_pop1 = c_pop[0] @@ -54,10 +54,14 @@ def _numba_dfun(y, c_pop, x0, Iext, Iext2, a, b, slope, tt, Kvf, c, d, r, Ks, Kf ydot[2] = - 0.1 * y[2] ** 7 else: ydot[2] = 0.0 - ydot[2] = tt[0] * (r[0] * (4 * (y[0] - x0[0]) - y[2] + ydot[2] + Ks[0] * c_pop1)) + if modification[0]: + h = x0[0] + 3/(1 + numpy.exp(-(y[0]+0.5)/0.1)) + else: + h = 4 * (y[0] - x0[0]) + ydot[2] + ydot[2] = tt[0] * (r[0] * (h - y[2] + Ks[0] * c_pop1)) # population 2 - ydot[3] = tt[0] * (-y[4] + y[3] - y[3] ** 3 + Iext2[0] + 2 * y[5] - 0.3 * (y[2] - 3.5) + Kf[0] * c_pop2) + ydot[3] = tt[0] * (-y[4] + y[3] - y[3] ** 3 + Iext2[0] + bb[0] * y[5] - 0.3 * (y[2] - 3.5) + Kf[0] * c_pop2) if y[3] < -0.25: ydot[4] = 0.0 else: @@ -144,6 +148,14 @@ class Epileptor(ModelNumbaDfun): 0 & \text{if } x_{2} <-0.25\\ a_{2}(x_{2} + 0.25) & \text{if } x_{2} \geq -0.25 \end{cases} + + Note Feb. 2017: the slow permittivity variable can be modify to account for the time + difference between interictal and ictal states (see [Proixetal_2014]). + + .. [Proixetal_2014] Proix, T.; Bartolomei, F; Chauvel, P; Bernard, C; Jirsa, V.K. * + Permittivity coupling across brain regions determines seizure recruitment in + partial epilepsy.* J Neurosci 2014, 34:15009-21. + """ _ui_name = "Epileptor" @@ -227,6 +239,12 @@ class Epileptor(ModelNumbaDfun): default=numpy.array([6]), doc="Linear coefficient in fifth state variable", order=-1) + + bb = arrays.FloatArray( + label="bb", + default=numpy.array([2]), + doc="Linear coefficient of lowpass excitatory coupling in fourth state variable", + order=-1) Kvf = arrays.FloatArray( label="K_vf", @@ -255,6 +273,13 @@ class Epileptor(ModelNumbaDfun): range=basic.Range(lo=0.001, hi=10.0, step=0.001), doc="Time scaling of the whole system", order=9) + + modification = arrays.BoolArray( + label="modification", + default=numpy.array([0]), + doc="When modification is True, then use nonlinear influence on z. \ + The default value is False, i.e., linear influence.", + order=10) state_variable_range = basic.Dict( label="State variable ranges [lo, hi]", @@ -332,24 +357,28 @@ def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, c_pop2 = coupling[1, :] # population 1 - if_ydot0 = - self.a*y[0]**2 + self.b*y[0] - else_ydot0 = self.slope - y[3] + 0.6*(y[2]-4.0)**2 - ydot[0] = self.tt*(y[1] - y[2] + Iext + self.Kvf*c_pop1 + where(y[0] < 0., if_ydot0, else_ydot0) * y[0]) - ydot[1] = self.tt*(self.c - self.d*y[0]**2 - y[1]) + if_ydot0 = - self.a * y[0] ** 2 + self.b * y[0] + else_ydot0 = self.slope - y[3] + 0.6 * (y[2] - 4.0) ** 2 + ydot[0] = self.tt * (y[1] - y[2] + Iext + self.Kvf * c_pop1 + where(y[0] < 0., if_ydot0, else_ydot0) * y[0]) + ydot[1] = self.tt * (self.c - self.d * y[0] ** 2 - y[1]) # energy - if_ydot2 = - 0.1*y[2]**7 + if_ydot2 = - 0.1 * y[2] ** 7 else_ydot2 = 0 - ydot[2] = self.tt*(self.r * ( 4*(y[0] - self.x0) - y[2] + where(y[2] < 0., if_ydot2, else_ydot2) + self.Ks*c_pop1)) + if modification: + h = h = self.x0 + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) + else: + h = 4 * (y[0] - self.x0) + where(y[2] < 0., if_ydot2, else_ydot2) + ydot[2] = self.tt * (self.r * (h - y[2] + self.Ks * c_pop1)) # population 2 - ydot[3] = self.tt*(-y[4] + y[3] - y[3]**3 + self.Iext2 + 2*y[5] - 0.3*(y[2] - 3.5) + self.Kf*c_pop2) + ydot[3] = self.tt * (-y[4] + y[3] - y[3] ** 3 + self.Iext2 + self.bb * y[5] - 0.3 * (y[2] - 3.5) + self.Kf * c_pop2) if_ydot4 = 0 - else_ydot4 = self.aa*(y[3] + 0.25) - ydot[4] = self.tt*((-y[4] + where(y[3] < -0.25, if_ydot4, else_ydot4))/self.tau) + else_ydot4 = self.aa * (y[3] + 0.25) + ydot[4] = self.tt * ((-y[4] + where(y[3] < -0.25, if_ydot4, else_ydot4)) / self.tau) # filter - ydot[5] = self.tt*(-0.01*(y[5] - 0.1*y[0])) + ydot[5] = self.tt * (-0.01 * (y[5] - 0.1 * y[0])) return ydot @@ -359,5 +388,216 @@ def dfun(self, x, c, local_coupling=0.0): Iext = self.Iext + local_coupling * x[0, :, 0] deriv = _numba_dfun(x_, c_, self.x0, Iext, self.Iext2, self.a, self.b, self.slope, self.tt, self.Kvf, - self.c, self.d, self.r, self.Ks, self.Kf, self.aa, self.tau) - return deriv.T[..., numpy.newaxis] \ No newline at end of file + self.c, self.d, self.r, self.Ks, self.Kf, self.aa, self.bb, self.tau, self.modification) + return deriv.T[..., numpy.newaxis] + + + + +class Epileptor2D(ModelNumbaDfun): + r""" + Two-dimensional reduction of the Epileptor. + + .. moduleauthor:: courtiol.julie@gmail.com + + Taking advantage of time scale separation and focusing on the slower time scale, + the five-dimensional Epileptor reduces to a two-dimensional system (see [Proixetal_2014, + Proixetal_2017]). + + Note: the slow permittivity variable can be modify to account for the time + difference between interictal and ictal states (see [Proixetal_2014]). + + Equations and default parameters are taken from [Proixetal_2014]: + + .. math:: + \dot{x_{1,i}} &=& - x_{1,i}^{3} - 2x_{1,i}^{2} + 1 - z_{i} + I_{ext1,i} \\ + \dot{z_{i}} &=& r(h - z_{i}) + + with + h = x_{0} + 3 / (exp((x_{1} + 0.5)/0.1)) if modification + h = 4 (x_{1,i} - x_{0}) + + References: + [Proixetal_2014] Proix, T.; Bartolomei, F; Chauvel, P; Bernard, C; Jirsa, V.K. * + Permittivity coupling across brain regions determines seizure recruitment in + partial epilepsy.* J Neurosci 2014, 34:15009-21. + + [Proixetal_2017] Proix, T.; Bartolomei, F; Guye, M.; Jirsa, V.K. *Individual brain + structure and modelling predict seizure propagation.* Brain 2017, 140; 641–654. + """ + + + _ui_name = "Epileptor2D" + ui_configurable_parameters = ["r", "Iext", "x0"] + + 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-variable.", + order=2) + + c = arrays.FloatArray( + label="c", + default=numpy.array([1]), + doc="Additive coefficient for the second state-variable x_{2}, \ + called :math:`y_{0}` in Jirsa paper.", + order=3) + + d = arrays.FloatArray( + label="d", + default=numpy.array([5]), + doc="Coefficient of the squared term in the second state-variable x_{2}.", + order=4) + + r = arrays.FloatArray( + label="r", + range=basic.Range(lo=0.0, hi=0.001, step=0.00005), + default=numpy.array([0.00035]), + doc="Temporal scaling in the slow state-variable, \ + called :math:`1\\tau_{0}` in Jirsa paper (see class Epileptor).", + order=5) + + x0 = arrays.FloatArray( + label="x0", + range=basic.Range(lo=-3.0, hi=-1.0, step=0.1), + default=numpy.array([-1.6]), + doc="Epileptogenicity parameter.", + order=6) + + Iext = arrays.FloatArray( + label="Iext", + range=basic.Range(lo=1.5, hi=5.0, step=0.1), + default=numpy.array([3.1]), + doc="External input current to the first state-variable.", + order=7) + + 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=8) + + 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=9) + + Ks = arrays.FloatArray( + label="K_s", + 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=10) + + tt = arrays.FloatArray( + label="tt", + default=numpy.array([1.0]), + range=basic.Range(lo=0.001, hi=1.0, step=0.001), + doc="Time scaling of the whole system to the system in real time.", + order=11) + + modification = arrays.BoolArray( + label="modification", + default=numpy.array([0]), + doc="When modification is True, then use nonlinear influence on z. \ + The default value is False, i.e., linear influence.", + order=12) + + state_variable_range = basic.Dict( + label="State variable ranges [lo, hi]", + default={"x1": numpy.array([-2., 1.]), + "z": numpy.array([2.0, 5.0])}, + doc="Typical bounds on state-variables in the Epileptor 2D model.", + order=99) + + variables_of_interest = basic.Enumerate( + label="Variables watched by Monitors", + options=['x1', 'z'], + default=['x1'], + select_multiple=True, + doc="Quantities of the Epileptor 2D available to monitor.", + order=100) + + state_variables = ['x1', 'z'] + + _nvar = 2 + cvar = numpy.array([0], dtype=numpy.int32) + + + def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, + array=numpy.array, where=numpy.where, concat=numpy.concatenate): + r""" + Computes the derivatives of the state-variables of the Epileptor 2D + with respect to time. + """ + + y = state_variables + ydot = numpy.empty_like(state_variables) + + Iext = self.Iext + local_coupling * y[0] + c_pop = coupling[0, :] + + # population 1 + if_ydot0 = self.a * y[0] ** 2 + (self.d - self.b) * y[0] + else_ydot0 = - self.slope - 0.6 * (y[1] - 4.0) ** 2 + self.d * y[0] + + ydot[0] = self.tt * (self.c - y[1] + Iext + self.Kvf * c_pop - (where(y[0] < 0., if_ydot0, else_ydot0)) * y[0]) + + # energy + if_ydot1 = - 0.1 * y[1] ** 7 + else_ydot1 = 0 + + if self.modification: + h = self.x0 + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) + else: + h = 4 * (y[0] - self.x0) + where(y[1] < 0., if_ydot1, else_ydot1) + + ydot[1] = self.tt * (self.r * (h - y[1] + self.Ks * c_pop)) + + return ydot + + def dfun(self, x, c, local_coupling=0.0): + """"The dfun using numba for speed.""" + + x_ = x.reshape(x.shape[:-1]).T + c_ = c.reshape(c.shape[:-1]).T + Iext = self.Iext + local_coupling * x[0, :, 0] + deriv = _numba_dfun_epi2d(x_, c_, + self.x0, Iext, self.a, self.b, self.slope, self.c, + self.d, self.r, self.Kvf, self.Ks, self.tt, self.modification) + return deriv.T[..., numpy.newaxis] + +@guvectorize([(float64[:],) * 15], '(n),(m)' + ',()'* 12 + '->(n)', nopython=True) +def _numba_dfun_epi2d(y, c_pop, x0, Iext, a, b, slope, c, d, r, Kvf, Ks, tt, modification, ydot): + "Gufunction for Epileptor 2D model equations." + + c_pop = c_pop[0] + + # population 1 + if y[0] < 0.0: + ydot[0] = a[0] * y[0] ** 2 + (d[0] - b[0]) * y[0] + else: + ydot[0] = - slope[0] - 0.6 * (y[1] - 4.0) ** 2 + d[0] * y[0] + ydot[0] = tt[0] * (c[0] - y[1] + Iext[0] + Kvf[0] * c_pop - ydot[0] * y[0]) + + # energy + if y[1] < 0.0: + ydot[1] = - 0.1 * y[1] ** 7 + else: + ydot[1] = 0.0 + + if modification[0]: + h = x0[0] + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) + else: + h = 4 * (y[0] - x0[0]) + ydot[1] + + ydot[1] = tt[0] * (r[0] * (h - y[1] + Ks[0] * c_pop)) From 96589d9193c7242d5965f8b7de1c8469ae74d491 Mon Sep 17 00:00:00 2001 From: Julie Date: Thu, 8 Mar 2018 16:19:26 +0100 Subject: [PATCH 09/49] replacing bad file --- contrib/simulator/models/epileptor.py | 648 +++++++------------------- 1 file changed, 172 insertions(+), 476 deletions(-) diff --git a/contrib/simulator/models/epileptor.py b/contrib/simulator/models/epileptor.py index 3de8028ae..bd8164afb 100644 --- a/contrib/simulator/models/epileptor.py +++ b/contrib/simulator/models/epileptor.py @@ -25,573 +25,269 @@ # Jochen Mersmann, Anthony R. McIntosh, Viktor Jirsa (2013) # The Virtual Brain: a simulator of primate brain network dynamics. # Frontiers in Neuroinformatics (7:10. doi: 10.3389/fninf.2013.00010) +# +# """ -Hindmarsh-Rose-Jirsa Epileptor model. +The Epileptor model + +.. moduleauthor:: Marmaduke Woodman """ -from .base import ModelNumbaDfun, LOG, numpy, basic, arrays -from numba import guvectorize, float64 - -@guvectorize([(float64[:],) * 19], '(n),(m)' + ',()'*16 + '->(n)', nopython=True) -def _numba_dfun(y, c_pop, x0, Iext, Iext2, a, b, slope, tt, Kvf, c, d, r, Ks, Kf, aa, tau, modification, ydot): - "Gufunc for Hindmarsh-Rose-Jirsa Epileptor model equations." - - c_pop1 = c_pop[0] - c_pop2 = c_pop[1] - - # population 1 - if y[0] < 0.0: - ydot[0] = - a[0] * y[0] ** 2 + b[0] * y[0] - else: - ydot[0] = slope[0] - y[3] + 0.6 * (y[2] - 4.0) ** 2 - ydot[0] = tt[0] * (y[1] - y[2] + Iext[0] + Kvf[0] * c_pop1 + ydot[0] * y[0]) - ydot[1] = tt[0] * (c[0] - d[0] * y[0] ** 2 - y[1]) - - # energy - if y[2] < 0.0: - ydot[2] = - 0.1 * y[2] ** 7 - else: - ydot[2] = 0.0 - if modification[0]: - h = x0[0] + 3/(1 + numpy.exp(-(y[0]+0.5)/0.1)) - else: - h = 4 * (y[0] - x0[0]) + ydot[2] - ydot[2] = tt[0] * (r[0] * (h - y[2] + Ks[0] * c_pop1)) - - # population 2 - ydot[3] = tt[0] * (-y[4] + y[3] - y[3] ** 3 + Iext2[0] + 2 * y[5] - 0.3 * (y[2] - 3.5) + Kf[0] * c_pop2) - if y[3] < -0.25: - ydot[4] = 0.0 - else: - ydot[4] = aa[0] * (y[3] + 0.25) - ydot[4] = tt[0] * ((-y[4] + ydot[4]) / tau[0]) - - # filter - ydot[5] = tt[0] * (-0.01 * (y[5] - 0.1 * y[0])) - - -class Epileptor(ModelNumbaDfun): - r""" - The Epileptor is a composite neural mass model of six dimensions which - has been crafted to model the phenomenology of epileptic seizures. - (see [Jirsaetal_2014]_) - - Equations and default parameters are taken from [Jirsaetal_2014]_. - - +------------------------------------------------------+ - | Table 1 | - +----------------------+-------------------------------+ - | Parameter | Value | - +======================+===============================+ - | I_rest1 | 3.1 | - +----------------------+-------------------------------+ - | I_rest2 | 0.45 | - +----------------------+-------------------------------+ - | r | 0.00035 | - +----------------------+-------------------------------+ - | x_0 | -1.6 | - +----------------------+-------------------------------+ - | slope | 0.0 | - +----------------------+-------------------------------+ - | Integration parameter | - +----------------------+-------------------------------+ - | dt | 0.1 | - +----------------------+-------------------------------+ - | simulation_length | 4000 | - +----------------------+-------------------------------+ - | Noise | - +----------------------+-------------------------------+ - | nsig | [0., 0., 0., 1e-3, 1e-3, 0.] | - +----------------------+-------------------------------+ - | Jirsa et al. 2014 | - +------------------------------------------------------+ - - - .. figure :: img/Epileptor_01_mode_0_pplane.svg - :alt: Epileptor phase plane - - .. [Jirsaetal_2014] Jirsa, V. K.; Stacey, W. C.; Quilichini, P. P.; - Ivanov, A. I.; Bernard, C. *On the nature of seizure dynamics.* Brain, - 2014. - - .. automethod:: Epileptor.__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}} &=& c - d x_{1}^{2} - y{1} \\ - \dot{z} &=& - \begin{cases} - r(4 (x_{1} - x_{0}) - z-0.1 z^{7}) & \text{if } x<0 \\ - r(4 (x_{1} - x_{0}) - z) & \text{if } x \geq 0 - \end{cases} \\ - \dot{x_{2}} &=& -y_{2} + x_{2} - x_{2}^{3} + I_{ext2} + 0.002 g - 0.3 (z-3.5) \\ - \dot{y_{2}} &=& 1 / \tau (-y_{2} + f_{2}(x_{2}))\\ - \dot{g} &=& -0.01 (g - 0.1 x_{1}) - - where: - .. math:: - f_{1}(x_{1}, x_{2}) = - \begin{cases} - a x_{1}^{3} - b x_{1}^2 & \text{if } x_{1} <0\\ - -(slope - x_{2} + 0.6(z-4)^2) x_{1} &\text{if }x_{1} \geq 0 - \end{cases} - - and: - - .. math:: - f_{2}(x_{2}) = - \begin{cases} - 0 & \text{if } x_{2} <-0.25\\ - a_{2}(x_{2} + 0.25) & \text{if } x_{2} \geq -0.25 - \end{cases} - - Note Feb. 2017: the slow permittivity variable can be modify to account for the time - difference between interictal and ictal states (see [Proixetal_2014]). - - .. [Proixetal_2014] Proix, T.; Bartolomei, F; Chauvel, P; Bernard, C; Jirsa, V.K. * - Permittivity coupling across brain regions determines seizure recruitment in - partial epilepsy.* J Neurosci 2014, 34:15009-21. +# Third party python libraries +import numpy + +#The Virtual Brain +from tvb.simulator.common import psutil, get_logger +LOG = get_logger(__name__) +import tvb.datatypes.arrays as arrays +import tvb.basic.traits.types_basic as basic +import tvb.simulator.models as models + + +class HMJEpileptor(models.Model): + """ + The Epileptor is a composite neural mass model of six dimensions which + has be crafted to model the phenomenology of epileptic seizures. + + This model, its motivation and derivation are currently in preparation + for publication (Jirsa et al, 2013) + + .. automethod:: HMJEpileptor.__init__ + .. automethod:: HMJEpileptor.dfun """ _ui_name = "Epileptor" - ui_configurable_parameters = ["Iext", "Iext2", "r", "x0", "slope"] + ui_configurable_parameters = ["Iext", "Iext2", "r", "x0"] a = arrays.FloatArray( label="a", default=numpy.array([1]), - doc="Coefficient of the cubic term in the first state variable", + doc="n/a", order=-1) b = arrays.FloatArray( label="b", default=numpy.array([3]), - doc="Coefficient of the squared term in the first state variabel", + doc="n/a", order=-1) c = arrays.FloatArray( label="c", default=numpy.array([1]), - doc="Additive coefficient for the second state variable, \ - called :math:`y_{0}` in Jirsa paper", + doc="n/a", order=-1) d = arrays.FloatArray( label="d", default=numpy.array([5]), - doc="Coefficient of the squared term in the second state variable", + doc="n/a", order=-1) r = arrays.FloatArray( label="r", range=basic.Range(lo=0.0, hi=0.001, step=0.00005), default=numpy.array([0.00035]), - doc="Temporal scaling in the third state variable, \ - called :math:`1/\\tau_{0}` in Jirsa paper", + doc="n/a", order=4) s = arrays.FloatArray( label="s", default=numpy.array([4]), - doc="Linear coefficient in the third state variable", + doc="n/a", order=-1) x0 = arrays.FloatArray( label="x0", - range=basic.Range(lo=-3.0, hi=-1.0, step=0.1), + range=basic.Range(lo=-3.0, hi=0.0, step=0.1), default=numpy.array([-1.6]), - doc="Epileptogenicity parameter", + doc="n/a", order=3) Iext = arrays.FloatArray( label="Iext", 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", + doc="n/a", order=1) + omega2 = arrays.FloatArray( + label="omega2", + default=numpy.array([0.1]), + doc="n/a", + 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) + doc="n/a", + order=-1) 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", + doc="n/a", order=2) tau = arrays.FloatArray( label="tau", default=numpy.array([10]), - doc="Temporal scaling coefficient in fifth state variable", + doc="n/a", order=-1) aa = arrays.FloatArray( label="aa", default=numpy.array([6]), - doc="Linear coefficient in fifth state variable", + doc="n/a", 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]), + + Kpop1 = arrays.FloatArray( + label="K_11", + default=numpy.array([0.5]), 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) - - Ks = arrays.FloatArray( - label="K_s", - 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) - - tt = arrays.FloatArray( - label="tt", - default=numpy.array([1.0]), - range=basic.Range(lo=0.001, hi=10.0, step=0.001), - doc="Time scaling of the whole system", - order=9) - - modification = arrays.BoolArray( - label="modification", - default=numpy.array([0]), - doc="When modification is True, then use nonlinear influence on z. \ - The default value is False, i.e., linear influence.", - order=10) + doc='''Test parameter. Correspond to the coupling scaling. Move outside to be + consistent with the general TVB implementation.''', + order=-1) + + Kpop2 = arrays.FloatArray( + label="K_22", + default=numpy.array([0.2]), + range=basic.Range(lo=0.0, hi=1.0, step=0.5), + doc='''Test parameter. Correspond to the coupling scaling. Move outside to be + consistent with the general TVB implementation.''', + order=-1) state_variable_range = basic.Dict( label="State variable ranges [lo, hi]", - default={"x1": numpy.array([-2., 1.]), - "y1": numpy.array([-20., 2.]), - "z": numpy.array([2.0, 5.0]), - "x2": numpy.array([-2., 0.]), - "y2": numpy.array([0., 2.]), - "g": numpy.array([-1., 1.])}, - doc="Typical bounds on state variables in the Epileptor model.", - order=16 + default = {"y0": numpy.array([0., 1e-10]), + "y1": numpy.array([-5., 0.]), + "y2": numpy.array([3., 4.]), + "y3": numpy.array([0., 1e-10]), + "y4": numpy.array([0., 1e-10]), + "y5": numpy.array([0., 1e-2]) }, + doc = "n/a", + order=-1 ) variables_of_interest = basic.Enumerate( - label="Variables watched by Monitors", - options=['x1', 'y1', 'z', 'x2', 'y2', 'g', 'x2 - x1'], - default=["x2 - x1", 'z'], - select_multiple=True, - doc="Quantities of the Epileptor available to monitor.", - order=100 - ) + label = "Variables watched by Monitors", + options = ["y0", "y1", "y2", "y3", "y4", "y5"], + default = ["y0", "y3"], + select_multiple = True, + doc = """default state variables to be monitored""", + order = 10) + +# variables_of_interest = arrays.IntegerArray( +# label="Variables watched by Monitors", +# range=basic.Range(lo=0.0, hi=6.0, step=1.0), +# default=numpy.array([0], dtype=numpy.int32), +# doc="default state variables to be monitored", +# order=10) + + + def __init__(self, **kwargs): + """ + """ + + LOG.info("%s: init'ing..." % (str(self),)) + + super(HMJEpileptor, self).__init__(**kwargs) + + #self._state_variables = ["y%d" % i for i in range(6)] + #self._state_variables = ["y%d" % i for i in range(6)] + self._nvar = 6 + self.cvar = numpy.array([0,3], dtype=numpy.int32) - state_variables = ['x1', 'y1', 'z', 'x2', 'y2', 'g'] - _nvar = 6 - cvar = numpy.array([0, 3], dtype=numpy.int32) + LOG.debug("%s: init'ed." % (repr(self),)) - def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, + 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. + """ + 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 + 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. + the original use of if/else is translated by calculated both the true and + false forms and mixing them using a boolean mask. Variables of interest to be used by monitors: -y[0] + y[3] - .. math:: - \dot{x_{1}} &=& y_{1} - f_{1}(x_{1}, x_{2}) - z + I_{ext1} \\ - \dot{y_{1}} &=& c - d x_{1}^{2} - y{1} \\ - \dot{z} &=& - \begin{cases} - r(4 (x_{1} - x_{0}) - z-0.1 z^{7}) & \text{if } x<0 \\ - r(4 (x_{1} - x_{0}) - z) & \text{if } x \geq 0 - \end{cases} \\ - \dot{x_{2}} &=& -y_{2} + x_{2} - x_{2}^{3} + I_{ext2} + 0.002 g - 0.3 (z-3.5) \\ - \dot{y_{2}} &=& 1 / \tau (-y_{2} + f_{2}(x_{2}))\\ - \dot{g} &=& -0.01 (g - 0.1 x_{1}) - - where: - .. math:: - f_{1}(x_{1}, x_{2}) = - \begin{cases} - a x_{1}^{3} - b x_{1}^2 & \text{if } x_{1} <0\\ - -(slope - x_{2} + 0.6(z-4)^2) x_{1} &\text{if }x_{1} \geq 0 - \end{cases} - - .. math:: - f_{2}(x_{2}) = - \begin{cases} - 0 & \text{if } x_{2} <-0.25\\ - a_{2}(x_{2} + 0.25) & \text{if } x_{2} \geq -0.25 - \end{cases} - """ - y = state_variables - ydot = numpy.empty_like(state_variables) - - Iext = self.Iext + local_coupling * y[0] - c_pop1 = coupling[0, :] - c_pop2 = coupling[1, :] - - # population 1 - if_ydot0 = - self.a*y[0]**2 + self.b*y[0] - else_ydot0 = self.slope - y[3] + 0.6*(y[2]-4.0)**2 - ydot[0] = self.tt*(y[1] - y[2] + Iext + self.Kvf*c_pop1 + where(y[0] < 0., if_ydot0, else_ydot0) * y[0]) - ydot[1] = self.tt*(self.c - self.d*y[0]**2 - y[1]) - - # energy - if_ydot2 = - 0.1*y[2]**7 - else_ydot2 = 0 - if modification: - h = h = self.x0 + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) - else: - h = 4 * (y[0] - self.x0) + where(y[2] < 0., if_ydot2, else_ydot2) - ydot[2] = self.tt*(self.r * (h - y[2] + self.Ks*c_pop1)) - - # population 2 - ydot[3] = self.tt*(-y[4] + y[3] - y[3]**3 + self.Iext2 + 2*y[5] - 0.3*(y[2] - 3.5) + self.Kf*c_pop2) - if_ydot4 = 0 - else_ydot4 = self.aa*(y[3] + 0.25) - ydot[4] = self.tt*((-y[4] + where(y[3] < -0.25, if_ydot4, else_ydot4))/self.tau) - - # filter - ydot[5] = self.tt*(-0.01*(y[5] - 0.1*y[0])) - - return ydot - - def dfun(self, x, c, local_coupling=0.0): - x_ = x.reshape(x.shape[:-1]).T - c_ = c.reshape(c.shape[:-1]).T - Iext = self.Iext + local_coupling * x[0, :, 0] - deriv = _numba_dfun(x_, c_, - self.x0, Iext, self.Iext2, self.a, self.b, self.slope, self.tt, self.Kvf, - self.c, self.d, self.r, self.Ks, self.Kf, self.aa, self.tau, self.modification) - return deriv.T[..., numpy.newaxis] - - - - -class Epileptor2D(ModelNumbaDfun): - r""" - Two-dimensional reduction of the Epileptor. - - .. moduleauthor:: courtiol.julie@gmail.com - - Taking advantage of time scale separation and focusing on the slower time scale, - the five-dimensional Epileptor reduces to a two-dimensional system (see [Proixetal_2014, - Proixetal_2017]). - - Note: the slow permittivity variable can be modify to account for the time - difference between interictal and ictal states (see [Proixetal_2014]). - - Equations and default parameters are taken from [Proixetal_2014]: - - .. math:: - \dot{x_{1,i}} &=& - x_{1,i}^{3} - 2x_{1,i}^{2} + 1 - z_{i} + I_{ext1,i} \\ - \dot{z_{i}} &=& r(h - z_{i}) - - with - h = x_{0} + 3 / (exp((x_{1} + 0.5)/0.1)) if modification - h = 4 (x_{1,i} - x_{0}) - - References: - [Proixetal_2014] Proix, T.; Bartolomei, F; Chauvel, P; Bernard, C; Jirsa, V.K. * - Permittivity coupling across brain regions determines seizure recruitment in - partial epilepsy.* J Neurosci 2014, 34:15009-21. - - [Proixetal_2017] Proix, T.; Bartolomei, F; Guye, M.; Jirsa, V.K. *Individual brain - structure and modelling predict seizure propagation.* Brain 2017, 140; 641–654. - """ - - - _ui_name = "Epileptor2D" - ui_configurable_parameters = ["r", "Iext", "x0"] - - 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-variable.", - order=2) - - c = arrays.FloatArray( - label="c", - default=numpy.array([1]), - doc="Additive coefficient for the second state-variable x_{2}, \ - called :math:`y_{0}` in Jirsa paper.", - order=3) - d = arrays.FloatArray( - label="d", - default=numpy.array([5]), - doc="Coefficient of the squared term in the second state-variable x_{2}.", - order=4) - - r = arrays.FloatArray( - label="r", - range=basic.Range(lo=0.0, hi=0.001, step=0.00005), - default=numpy.array([0.00035]), - doc="Temporal scaling in the slow state-variable, \ - called :math:`1\\tau_{0}` in Jirsa paper (see class Epileptor).", - order=5) - - x0 = arrays.FloatArray( - label="x0", - range=basic.Range(lo=-3.0, hi=-1.0, step=0.1), - default=numpy.array([-1.6]), - doc="Epileptogenicity parameter.", - order=6) - - Iext = arrays.FloatArray( - label="Iext", - range=basic.Range(lo=1.5, hi=5.0, step=0.1), - default=numpy.array([3.1]), - doc="External input current to the first state-variable.", - order=7) - - 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=8) - - 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=9) - - Ks = arrays.FloatArray( - label="K_s", - 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=10) - - tt = arrays.FloatArray( - label="tt", - default=numpy.array([1.0]), - range=basic.Range(lo=0.001, hi=1.0, step=0.001), - doc="Time scaling of the whole system to the system in real time.", - order=11) - - modification = arrays.BoolArray( - label="modification", - default=numpy.array([0]), - doc="When modification is True, then use nonlinear influence on z. \ - The default value is False, i.e., linear influence.", - order=12) - - state_variable_range = basic.Dict( - label="State variable ranges [lo, hi]", - default={"x1": numpy.array([-2., 1.]), - "z": numpy.array([2.0, 5.0])}, - doc="Typical bounds on state-variables in the Epileptor 2D model.", - order=99) - - variables_of_interest = basic.Enumerate( - label="Variables watched by Monitors", - options=['x1', 'z'], - default=['x1'], - select_multiple=True, - doc="Quantities of the Epileptor 2D available to monitor.", - order=100) - - state_variables = ['x1', 'z'] - - _nvar = 1 - cvar = numpy.array([0], dtype=numpy.int32) - - - def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, - array=numpy.array, where=numpy.where, concat=numpy.concatenate): - r""" - Computes the derivatives of the state-variables of the Epileptor 2D - with respect to time. + """ + First population with high frequency burst and baseline jump - mechanisms + is similar to a Hindmarsh-Rose scenario with two régimes connected by a + slow trajectory (here y(3)). """ y = state_variables - ydot = numpy.empty_like(state_variables) - - Iext = self.Iext + local_coupling * y[0] - c_pop = coupling[0, :] - - # population 1 - if_ydot0 = self.a * y[0] ** 2 + (self.d - self.b) * y[0] - else_ydot0 = - self.slope - 0.6 * (y[1] - 4.0) ** 2 + self.d * y[0] - - ydot[0] = self.tt * (self.c - y[1] + Iext + self.Kvf * c_pop - (where(y[0] < 0., if_ydot0, else_ydot0)) * y[0]) - - # energy - if_ydot1 = - 0.1 * y[1] ** 7 - else_ydot1 = 0 - - if self.modification: - h = self.x0 + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) - else: - h = 4 * (y[0] - self.x0) + where(y[1] < 0., if_ydot1, else_ydot1) - - ydot[1] = self.tt * (self.r * (h - y[1] + self.Ks * c_pop)) - - return ydot - - def dfun(self, x, c, local_coupling=0.0): - """"The dfun using numba for speed.""" - - x_ = x.reshape(x.shape[:-1]).T - c_ = c.reshape(c.shape[:-1]).T - Iext = self.Iext + local_coupling * x[0, :, 0] - deriv = _numba_dfun_epi2d(x_, c_, - self.x0, Iext, self.a, self.b, self.slope, self.c, - self.d, self.r, self.Kvf, self.Ks, self.tt, self.modification) - return deriv.T[..., numpy.newaxis] - -@guvectorize([(float64[:],) * 15], '(n),(m)' + ',()'*12 + '->(n)', nopython=True) -def _numba_dfun_epi2d(y, c_pop, x0, Iext, a, b, slope, c, d, r, Kvf, Ks, tt, modification, ydot): - "Gufunction for Epileptor 2D model equations." - - c_pop = c_pop[0] - - # population 1 - if y[0] < 0.0: - ydot[0] = a[0] * y[0] ** 2 + (d[0] - b[0]) * y[0] - else: - ydot[0] = - slope[0] - 0.6 * (y[1] - 4.0) ** 2 + d[0] * y[0] - ydot[0] = tt[0] * (c[0] - y[1] + Iext[0] + Kvf[0] * c_pop - ydot[0] * y[0]) - - # energy - if y[1] < 0.0: - ydot[1] = - 0.1 * y[1] ** 7 - else: - ydot[1] = 0.0 - - if modification[0]: - h = x0[0] + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) - else: - h = 4 * (y[0] - x0[0]) + ydot[1] + n = y.shape[1] + Iext = self.Iext + coupling[0, :] + local_coupling + c_pop1 = coupling[0, :] + c_pop2 = coupling[1, :] - ydot[1] = tt[0] * (r[0] * (h - y[1] + Ks[0] * c_pop)) + # if y(1)<0. + # ydot1 = y(2)-a*y(1)^3 + b*y(1)^2-y(3)+iext; + # ydot2 = c-d*y(1)^2-y(2); + # ydot3 = r*(s*(y(1)-x0) - y(3)); % energy consumption = 1 - available energy + + if_y1_lt_0 = concat([ (y[1] - self.a*y[0]**3 + self.b*y[0]**2 - y[2] + Iext).reshape((1, n, 1)), + (self.c - self.d*y[0]**2 - y[1]).reshape((1, n, 1)), + (self.r*(self.s*(y[0] - self.x0) - y[2] - self.Kpop1 * (c_pop1 - y[0]) )).reshape((1, n, 1)) ]) + + # else + # % ydot1 = y(2) + (slope - y(4) -1.0*(y(3)-4))*y(1) - y(3)+iext; % this is just an + # % alternative representation, which worked well + # ydot1 = y(2) + (slope - y(4) + 0.6*(y(3)-4)^2)*y(1) -y(3)+iext; + # % here the high energy burst is being generated through variation of the slope: + # % 1. via y(4) within the epileptic spike complex; + # % 2. via the expression with y(3), which causes more + # % oscillations at the beginning of the seizure (effect of the + # % energy available) + # ydot2 = c-d*y(1)^2-y(2); + # ydot3 = r*(s*(y(1)-x0) - y(3)); + # end + + else_pop1 = concat([ (y[1] + (self.slope - y[3] + 0.6*(y[2]-4.0)**2)*y[0] - y[2] + Iext).reshape((1, n, 1)), + (self.c - self.d*y[0]**2 - y[1]).reshape((1, n, 1)), + (self.r*(self.s*(y[0] - self.x0) - y[2] - self.Kpop1 * (c_pop1 - y[0]))).reshape((1, n, 1)) ]) + + pop1 = where(y[0] < 0., if_y1_lt_0, else_pop1) + + # % istim= 0*block(t,150,1); + # + # % this is the second population that generates the big spike-wave complex + # % preictally and within the seizure via a morris-lecar-jirsa (mlj) structure + # + # if y(4)<-0.25 + # ydot4 = -y(5)+ y(4)-y(4)^3 + iext2 + 2*y(6)-0.3*(y(3)-3.5) ; % these last two terms + # % put the population dynamics into the critical regime. in particular, + # % y(6) turns the oscillator on and off, whereas the y(3) term helps it to become precritical (critical fluctuations). + # ydot5 = -y(5)/tau ; + + if_ = concat([ (-y[4] + y[3] - y[3]**3 + self.Iext2 + 2*y[5] - 0.3*(y[2] - 3.5) + self.Kpop2 * (c_pop2 - y[3])).reshape((1, n, 1)), (-y[4]/self.tau).reshape((1, n, 1)) ]) + # else + # ydot4 = -y(5)+ y(4)-y(4)^3 + iext2+ 2*y(6)-0.3*(y(3)-3.5); + # ydot5 = (-y(5) + aa*(y(4)+0.25))/tau; % here is the mlj structure + # end + + else_pop2 = concat([ (-y[4] + y[3] - y[3]**3 + self.Iext2 + 2*y[5] - 0.3*(y[2] - 3.5) + self.Kpop2 * (c_pop2 - y[3])).reshape((1, n, 1)), ((-y[4] + self.aa*(y[3] + 0.25))/self.tau).reshape((1, n, 1)) ]) + + + pop2 = where(y[3] < -0.25, if_, else_pop2) + + # + # ydot6 = -0.01*(y(6)-0.1*y(1)) ; + + energy = array([ -0.01*(y[5] - 0.1*y[0])]) + + # + # ydot = [ydot1;ydot2;ydot3;ydot4;ydot5;ydot6]; + + return concat((pop1, pop2, energy)) \ No newline at end of file From 2cb4deeb350bd94a08a2bf6ce6141dc8b40b97e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jopn=08=08=08=1B=5BC=1B=5BC=1B=5BC=1B=5BC=08=08=08hn=20Gri?= =?UTF-8?q?ffiths?= Date: Tue, 17 Apr 2018 11:57:04 -0400 Subject: [PATCH 10/49] modify _repr_html_ to also show trat descriptions --- tvb/basic/traits/core.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tvb/basic/traits/core.py b/tvb/basic/traits/core.py index e693eb682..4a2e636e4 100644 --- a/tvb/basic/traits/core.py +++ b/tvb/basic/traits/core.py @@ -502,11 +502,22 @@ def _repr_html_(self): "Generate HTML repr for use in IPython notebook." info = self.summary_info if info is None or len(info) == 0: - info = {key: getattr(self, key) for key in self.trait.keys()} + info = {} + for key in self.trait.keys(): + info[key] = [getattr(self,key), + getattr(self.__class__,key).trait.inits.kwd['doc']] html = ['
%s%s
%s%s%s
%s%s%s
'] - row_fmt = '' - for key, value in info.items(): - html.append(row_fmt % (key, value)) + row_fmt = '' + header_fmt = '' + html.append(header_fmt % ('Parameter', 'Value', 'Description')) + for paramname,value in info.items(): + paramdescr = '' # default: no param description given + # ..if param description present (list len==2), use it + if type(value) == list: + paramval, paramdescr = value + else: + paramval = value + html.append(row_fmt % (paramname, paramval, paramdescr)) return ''.join(html) def _find_summary_info(self): From 884ac0d94d6bdbc89d287a13a1b28eefdc83e591 Mon Sep 17 00:00:00 2001 From: liadomide Date: Wed, 18 Apr 2018 09:45:51 +0000 Subject: [PATCH 11/49] add modified z dynamics in epileptor and create epileptor 2D class (Proixetal, 2014) git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8690 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- contrib/simulator/models/epileptor.py | 648 +++++++++++++++++++------- 1 file changed, 476 insertions(+), 172 deletions(-) diff --git a/contrib/simulator/models/epileptor.py b/contrib/simulator/models/epileptor.py index bd8164afb..3de8028ae 100644 --- a/contrib/simulator/models/epileptor.py +++ b/contrib/simulator/models/epileptor.py @@ -25,269 +25,573 @@ # Jochen Mersmann, Anthony R. McIntosh, Viktor Jirsa (2013) # The Virtual Brain: a simulator of primate brain network dynamics. # Frontiers in Neuroinformatics (7:10. doi: 10.3389/fninf.2013.00010) -# -# """ -The Epileptor model - -.. moduleauthor:: Marmaduke Woodman +Hindmarsh-Rose-Jirsa Epileptor model. """ -# Third party python libraries -import numpy - -#The Virtual Brain -from tvb.simulator.common import psutil, get_logger -LOG = get_logger(__name__) +from .base import ModelNumbaDfun, LOG, numpy, basic, arrays +from numba import guvectorize, float64 + +@guvectorize([(float64[:],) * 19], '(n),(m)' + ',()'*16 + '->(n)', nopython=True) +def _numba_dfun(y, c_pop, x0, Iext, Iext2, a, b, slope, tt, Kvf, c, d, r, Ks, Kf, aa, tau, modification, ydot): + "Gufunc for Hindmarsh-Rose-Jirsa Epileptor model equations." + + c_pop1 = c_pop[0] + c_pop2 = c_pop[1] + + # population 1 + if y[0] < 0.0: + ydot[0] = - a[0] * y[0] ** 2 + b[0] * y[0] + else: + ydot[0] = slope[0] - y[3] + 0.6 * (y[2] - 4.0) ** 2 + ydot[0] = tt[0] * (y[1] - y[2] + Iext[0] + Kvf[0] * c_pop1 + ydot[0] * y[0]) + ydot[1] = tt[0] * (c[0] - d[0] * y[0] ** 2 - y[1]) + + # energy + if y[2] < 0.0: + ydot[2] = - 0.1 * y[2] ** 7 + else: + ydot[2] = 0.0 + if modification[0]: + h = x0[0] + 3/(1 + numpy.exp(-(y[0]+0.5)/0.1)) + else: + h = 4 * (y[0] - x0[0]) + ydot[2] + ydot[2] = tt[0] * (r[0] * (h - y[2] + Ks[0] * c_pop1)) + + # population 2 + ydot[3] = tt[0] * (-y[4] + y[3] - y[3] ** 3 + Iext2[0] + 2 * y[5] - 0.3 * (y[2] - 3.5) + Kf[0] * c_pop2) + if y[3] < -0.25: + ydot[4] = 0.0 + else: + ydot[4] = aa[0] * (y[3] + 0.25) + ydot[4] = tt[0] * ((-y[4] + ydot[4]) / tau[0]) + + # filter + ydot[5] = tt[0] * (-0.01 * (y[5] - 0.1 * y[0])) + + +class Epileptor(ModelNumbaDfun): + r""" + The Epileptor is a composite neural mass model of six dimensions which + has been crafted to model the phenomenology of epileptic seizures. + (see [Jirsaetal_2014]_) + + Equations and default parameters are taken from [Jirsaetal_2014]_. + + +------------------------------------------------------+ + | Table 1 | + +----------------------+-------------------------------+ + | Parameter | Value | + +======================+===============================+ + | I_rest1 | 3.1 | + +----------------------+-------------------------------+ + | I_rest2 | 0.45 | + +----------------------+-------------------------------+ + | r | 0.00035 | + +----------------------+-------------------------------+ + | x_0 | -1.6 | + +----------------------+-------------------------------+ + | slope | 0.0 | + +----------------------+-------------------------------+ + | Integration parameter | + +----------------------+-------------------------------+ + | dt | 0.1 | + +----------------------+-------------------------------+ + | simulation_length | 4000 | + +----------------------+-------------------------------+ + | Noise | + +----------------------+-------------------------------+ + | nsig | [0., 0., 0., 1e-3, 1e-3, 0.] | + +----------------------+-------------------------------+ + | Jirsa et al. 2014 | + +------------------------------------------------------+ + + + .. figure :: img/Epileptor_01_mode_0_pplane.svg + :alt: Epileptor phase plane + + .. [Jirsaetal_2014] Jirsa, V. K.; Stacey, W. C.; Quilichini, P. P.; + Ivanov, A. I.; Bernard, C. *On the nature of seizure dynamics.* Brain, + 2014. + + .. automethod:: Epileptor.__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}} &=& c - d x_{1}^{2} - y{1} \\ + \dot{z} &=& + \begin{cases} + r(4 (x_{1} - x_{0}) - z-0.1 z^{7}) & \text{if } x<0 \\ + r(4 (x_{1} - x_{0}) - z) & \text{if } x \geq 0 + \end{cases} \\ + \dot{x_{2}} &=& -y_{2} + x_{2} - x_{2}^{3} + I_{ext2} + 0.002 g - 0.3 (z-3.5) \\ + \dot{y_{2}} &=& 1 / \tau (-y_{2} + f_{2}(x_{2}))\\ + \dot{g} &=& -0.01 (g - 0.1 x_{1}) + + where: + .. math:: + f_{1}(x_{1}, x_{2}) = + \begin{cases} + a x_{1}^{3} - b x_{1}^2 & \text{if } x_{1} <0\\ + -(slope - x_{2} + 0.6(z-4)^2) x_{1} &\text{if }x_{1} \geq 0 + \end{cases} + + and: + + .. math:: + f_{2}(x_{2}) = + \begin{cases} + 0 & \text{if } x_{2} <-0.25\\ + a_{2}(x_{2} + 0.25) & \text{if } x_{2} \geq -0.25 + \end{cases} + + Note Feb. 2017: the slow permittivity variable can be modify to account for the time + difference between interictal and ictal states (see [Proixetal_2014]). + + .. [Proixetal_2014] Proix, T.; Bartolomei, F; Chauvel, P; Bernard, C; Jirsa, V.K. * + Permittivity coupling across brain regions determines seizure recruitment in + partial epilepsy.* J Neurosci 2014, 34:15009-21. -import tvb.datatypes.arrays as arrays -import tvb.basic.traits.types_basic as basic -import tvb.simulator.models as models - - -class HMJEpileptor(models.Model): - """ - The Epileptor is a composite neural mass model of six dimensions which - has be crafted to model the phenomenology of epileptic seizures. - - This model, its motivation and derivation are currently in preparation - for publication (Jirsa et al, 2013) - - .. automethod:: HMJEpileptor.__init__ - .. automethod:: HMJEpileptor.dfun """ _ui_name = "Epileptor" - ui_configurable_parameters = ["Iext", "Iext2", "r", "x0"] + ui_configurable_parameters = ["Iext", "Iext2", "r", "x0", "slope"] a = arrays.FloatArray( label="a", default=numpy.array([1]), - doc="n/a", + doc="Coefficient of the cubic term in the first state variable", order=-1) b = arrays.FloatArray( label="b", default=numpy.array([3]), - doc="n/a", + doc="Coefficient of the squared term in the first state variabel", order=-1) c = arrays.FloatArray( label="c", default=numpy.array([1]), - doc="n/a", + doc="Additive coefficient for the second state variable, \ + called :math:`y_{0}` in Jirsa paper", order=-1) d = arrays.FloatArray( label="d", default=numpy.array([5]), - doc="n/a", + doc="Coefficient of the squared term in the second state variable", order=-1) r = arrays.FloatArray( label="r", range=basic.Range(lo=0.0, hi=0.001, step=0.00005), default=numpy.array([0.00035]), - doc="n/a", + doc="Temporal scaling in the third state variable, \ + called :math:`1/\\tau_{0}` in Jirsa paper", order=4) s = arrays.FloatArray( label="s", default=numpy.array([4]), - doc="n/a", + doc="Linear coefficient in the third state variable", order=-1) x0 = arrays.FloatArray( label="x0", - range=basic.Range(lo=-3.0, hi=0.0, step=0.1), + range=basic.Range(lo=-3.0, hi=-1.0, step=0.1), default=numpy.array([-1.6]), - doc="n/a", + doc="Epileptogenicity parameter", order=3) Iext = arrays.FloatArray( label="Iext", range=basic.Range(lo=1.5, hi=5.0, step=0.1), default=numpy.array([3.1]), - doc="n/a", + doc="External input current to the first population", order=1) - omega2 = arrays.FloatArray( - label="omega2", - default=numpy.array([0.1]), - doc="n/a", - order=-1) - slope = arrays.FloatArray( label="slope", + range=basic.Range(lo=-16.0, hi=6.0, step=0.1), default=numpy.array([0.]), - doc="n/a", - order=-1) + 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="n/a", + doc="External input current to the second population", order=2) tau = arrays.FloatArray( label="tau", default=numpy.array([10]), - doc="n/a", + doc="Temporal scaling coefficient in fifth state variable", order=-1) aa = arrays.FloatArray( label="aa", default=numpy.array([6]), - doc="n/a", + doc="Linear coefficient in fifth state variable", order=-1) - - Kpop1 = arrays.FloatArray( - label="K_11", - default=numpy.array([0.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='''Test parameter. Correspond to the coupling scaling. Move outside to be - consistent with the general TVB implementation.''', - order=-1) - - Kpop2 = arrays.FloatArray( - label="K_22", - default=numpy.array([0.2]), - range=basic.Range(lo=0.0, hi=1.0, step=0.5), - doc='''Test parameter. Correspond to the coupling scaling. Move outside to be - consistent with the general TVB implementation.''', - order=-1) + 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) + + Ks = arrays.FloatArray( + label="K_s", + 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) + + tt = arrays.FloatArray( + label="tt", + default=numpy.array([1.0]), + range=basic.Range(lo=0.001, hi=10.0, step=0.001), + doc="Time scaling of the whole system", + order=9) + + modification = arrays.BoolArray( + label="modification", + default=numpy.array([0]), + doc="When modification is True, then use nonlinear influence on z. \ + The default value is False, i.e., linear influence.", + order=10) state_variable_range = basic.Dict( label="State variable ranges [lo, hi]", - default = {"y0": numpy.array([0., 1e-10]), - "y1": numpy.array([-5., 0.]), - "y2": numpy.array([3., 4.]), - "y3": numpy.array([0., 1e-10]), - "y4": numpy.array([0., 1e-10]), - "y5": numpy.array([0., 1e-2]) }, - doc = "n/a", - order=-1 + default={"x1": numpy.array([-2., 1.]), + "y1": numpy.array([-20., 2.]), + "z": numpy.array([2.0, 5.0]), + "x2": numpy.array([-2., 0.]), + "y2": numpy.array([0., 2.]), + "g": numpy.array([-1., 1.])}, + doc="Typical bounds on state variables in the Epileptor model.", + order=16 ) variables_of_interest = basic.Enumerate( - label = "Variables watched by Monitors", - options = ["y0", "y1", "y2", "y3", "y4", "y5"], - default = ["y0", "y3"], - select_multiple = True, - doc = """default state variables to be monitored""", - order = 10) - -# variables_of_interest = arrays.IntegerArray( -# label="Variables watched by Monitors", -# range=basic.Range(lo=0.0, hi=6.0, step=1.0), -# default=numpy.array([0], dtype=numpy.int32), -# doc="default state variables to be monitored", -# order=10) - - - def __init__(self, **kwargs): - """ - """ - - LOG.info("%s: init'ing..." % (str(self),)) - - super(HMJEpileptor, self).__init__(**kwargs) - - #self._state_variables = ["y%d" % i for i in range(6)] - #self._state_variables = ["y%d" % i for i in range(6)] - self._nvar = 6 - self.cvar = numpy.array([0,3], dtype=numpy.int32) + label="Variables watched by Monitors", + options=['x1', 'y1', 'z', 'x2', 'y2', 'g', 'x2 - x1'], + default=["x2 - x1", 'z'], + select_multiple=True, + doc="Quantities of the Epileptor available to monitor.", + order=100 + ) + state_variables = ['x1', 'y1', 'z', 'x2', 'y2', 'g'] - LOG.debug("%s: init'ed." % (repr(self),)) + _nvar = 6 + cvar = numpy.array([0, 3], dtype=numpy.int32) - def dfun(self, state_variables, coupling, local_coupling=0.0, + def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, array=numpy.array, where=numpy.where, concat=numpy.concatenate): - """ - Computes the derivatives of the state variables of the Epileptor - with respect to time. + 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 + 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. + the original use of if/else is translated by calculated both the true + and false forms and mixing them using a boolean mask. Variables of interest to be used by monitors: -y[0] + y[3] - """ + .. math:: + \dot{x_{1}} &=& y_{1} - f_{1}(x_{1}, x_{2}) - z + I_{ext1} \\ + \dot{y_{1}} &=& c - d x_{1}^{2} - y{1} \\ + \dot{z} &=& + \begin{cases} + r(4 (x_{1} - x_{0}) - z-0.1 z^{7}) & \text{if } x<0 \\ + r(4 (x_{1} - x_{0}) - z) & \text{if } x \geq 0 + \end{cases} \\ + \dot{x_{2}} &=& -y_{2} + x_{2} - x_{2}^{3} + I_{ext2} + 0.002 g - 0.3 (z-3.5) \\ + \dot{y_{2}} &=& 1 / \tau (-y_{2} + f_{2}(x_{2}))\\ + \dot{g} &=& -0.01 (g - 0.1 x_{1}) + + where: + .. math:: + f_{1}(x_{1}, x_{2}) = + \begin{cases} + a x_{1}^{3} - b x_{1}^2 & \text{if } x_{1} <0\\ + -(slope - x_{2} + 0.6(z-4)^2) x_{1} &\text{if }x_{1} \geq 0 + \end{cases} + + .. math:: + f_{2}(x_{2}) = + \begin{cases} + 0 & \text{if } x_{2} <-0.25\\ + a_{2}(x_{2} + 0.25) & \text{if } x_{2} \geq -0.25 + \end{cases} """ - First population with high frequency burst and baseline jump - mechanisms - is similar to a Hindmarsh-Rose scenario with two régimes connected by a - slow trajectory (here y(3)). - """ - y = state_variables - n = y.shape[1] - Iext = self.Iext + coupling[0, :] + local_coupling + ydot = numpy.empty_like(state_variables) + + Iext = self.Iext + local_coupling * y[0] c_pop1 = coupling[0, :] c_pop2 = coupling[1, :] - # if y(1)<0. - # ydot1 = y(2)-a*y(1)^3 + b*y(1)^2-y(3)+iext; - # ydot2 = c-d*y(1)^2-y(2); - # ydot3 = r*(s*(y(1)-x0) - y(3)); % energy consumption = 1 - available energy - - if_y1_lt_0 = concat([ (y[1] - self.a*y[0]**3 + self.b*y[0]**2 - y[2] + Iext).reshape((1, n, 1)), - (self.c - self.d*y[0]**2 - y[1]).reshape((1, n, 1)), - (self.r*(self.s*(y[0] - self.x0) - y[2] - self.Kpop1 * (c_pop1 - y[0]) )).reshape((1, n, 1)) ]) - - # else - # % ydot1 = y(2) + (slope - y(4) -1.0*(y(3)-4))*y(1) - y(3)+iext; % this is just an - # % alternative representation, which worked well - # ydot1 = y(2) + (slope - y(4) + 0.6*(y(3)-4)^2)*y(1) -y(3)+iext; - # % here the high energy burst is being generated through variation of the slope: - # % 1. via y(4) within the epileptic spike complex; - # % 2. via the expression with y(3), which causes more - # % oscillations at the beginning of the seizure (effect of the - # % energy available) - # ydot2 = c-d*y(1)^2-y(2); - # ydot3 = r*(s*(y(1)-x0) - y(3)); - # end - - else_pop1 = concat([ (y[1] + (self.slope - y[3] + 0.6*(y[2]-4.0)**2)*y[0] - y[2] + Iext).reshape((1, n, 1)), - (self.c - self.d*y[0]**2 - y[1]).reshape((1, n, 1)), - (self.r*(self.s*(y[0] - self.x0) - y[2] - self.Kpop1 * (c_pop1 - y[0]))).reshape((1, n, 1)) ]) - - pop1 = where(y[0] < 0., if_y1_lt_0, else_pop1) - - # % istim= 0*block(t,150,1); - # - # % this is the second population that generates the big spike-wave complex - # % preictally and within the seizure via a morris-lecar-jirsa (mlj) structure - # - # if y(4)<-0.25 - # ydot4 = -y(5)+ y(4)-y(4)^3 + iext2 + 2*y(6)-0.3*(y(3)-3.5) ; % these last two terms - # % put the population dynamics into the critical regime. in particular, - # % y(6) turns the oscillator on and off, whereas the y(3) term helps it to become precritical (critical fluctuations). - # ydot5 = -y(5)/tau ; - - if_ = concat([ (-y[4] + y[3] - y[3]**3 + self.Iext2 + 2*y[5] - 0.3*(y[2] - 3.5) + self.Kpop2 * (c_pop2 - y[3])).reshape((1, n, 1)), (-y[4]/self.tau).reshape((1, n, 1)) ]) - # else - # ydot4 = -y(5)+ y(4)-y(4)^3 + iext2+ 2*y(6)-0.3*(y(3)-3.5); - # ydot5 = (-y(5) + aa*(y(4)+0.25))/tau; % here is the mlj structure - # end - - else_pop2 = concat([ (-y[4] + y[3] - y[3]**3 + self.Iext2 + 2*y[5] - 0.3*(y[2] - 3.5) + self.Kpop2 * (c_pop2 - y[3])).reshape((1, n, 1)), ((-y[4] + self.aa*(y[3] + 0.25))/self.tau).reshape((1, n, 1)) ]) - - - pop2 = where(y[3] < -0.25, if_, else_pop2) - - # - # ydot6 = -0.01*(y(6)-0.1*y(1)) ; - - energy = array([ -0.01*(y[5] - 0.1*y[0])]) - - # - # ydot = [ydot1;ydot2;ydot3;ydot4;ydot5;ydot6]; - - return concat((pop1, pop2, energy)) \ No newline at end of file + # population 1 + if_ydot0 = - self.a*y[0]**2 + self.b*y[0] + else_ydot0 = self.slope - y[3] + 0.6*(y[2]-4.0)**2 + ydot[0] = self.tt*(y[1] - y[2] + Iext + self.Kvf*c_pop1 + where(y[0] < 0., if_ydot0, else_ydot0) * y[0]) + ydot[1] = self.tt*(self.c - self.d*y[0]**2 - y[1]) + + # energy + if_ydot2 = - 0.1*y[2]**7 + else_ydot2 = 0 + if modification: + h = h = self.x0 + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) + else: + h = 4 * (y[0] - self.x0) + where(y[2] < 0., if_ydot2, else_ydot2) + ydot[2] = self.tt*(self.r * (h - y[2] + self.Ks*c_pop1)) + + # population 2 + ydot[3] = self.tt*(-y[4] + y[3] - y[3]**3 + self.Iext2 + 2*y[5] - 0.3*(y[2] - 3.5) + self.Kf*c_pop2) + if_ydot4 = 0 + else_ydot4 = self.aa*(y[3] + 0.25) + ydot[4] = self.tt*((-y[4] + where(y[3] < -0.25, if_ydot4, else_ydot4))/self.tau) + + # filter + ydot[5] = self.tt*(-0.01*(y[5] - 0.1*y[0])) + + return ydot + + def dfun(self, x, c, local_coupling=0.0): + x_ = x.reshape(x.shape[:-1]).T + c_ = c.reshape(c.shape[:-1]).T + Iext = self.Iext + local_coupling * x[0, :, 0] + deriv = _numba_dfun(x_, c_, + self.x0, Iext, self.Iext2, self.a, self.b, self.slope, self.tt, self.Kvf, + self.c, self.d, self.r, self.Ks, self.Kf, self.aa, self.tau, self.modification) + return deriv.T[..., numpy.newaxis] + + + + +class Epileptor2D(ModelNumbaDfun): + r""" + Two-dimensional reduction of the Epileptor. + + .. moduleauthor:: courtiol.julie@gmail.com + + Taking advantage of time scale separation and focusing on the slower time scale, + the five-dimensional Epileptor reduces to a two-dimensional system (see [Proixetal_2014, + Proixetal_2017]). + + Note: the slow permittivity variable can be modify to account for the time + difference between interictal and ictal states (see [Proixetal_2014]). + + Equations and default parameters are taken from [Proixetal_2014]: + + .. math:: + \dot{x_{1,i}} &=& - x_{1,i}^{3} - 2x_{1,i}^{2} + 1 - z_{i} + I_{ext1,i} \\ + \dot{z_{i}} &=& r(h - z_{i}) + + with + h = x_{0} + 3 / (exp((x_{1} + 0.5)/0.1)) if modification + h = 4 (x_{1,i} - x_{0}) + + References: + [Proixetal_2014] Proix, T.; Bartolomei, F; Chauvel, P; Bernard, C; Jirsa, V.K. * + Permittivity coupling across brain regions determines seizure recruitment in + partial epilepsy.* J Neurosci 2014, 34:15009-21. + + [Proixetal_2017] Proix, T.; Bartolomei, F; Guye, M.; Jirsa, V.K. *Individual brain + structure and modelling predict seizure propagation.* Brain 2017, 140; 641–654. + """ + + + _ui_name = "Epileptor2D" + ui_configurable_parameters = ["r", "Iext", "x0"] + + 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-variable.", + order=2) + + c = arrays.FloatArray( + label="c", + default=numpy.array([1]), + doc="Additive coefficient for the second state-variable x_{2}, \ + called :math:`y_{0}` in Jirsa paper.", + order=3) + + d = arrays.FloatArray( + label="d", + default=numpy.array([5]), + doc="Coefficient of the squared term in the second state-variable x_{2}.", + order=4) + + r = arrays.FloatArray( + label="r", + range=basic.Range(lo=0.0, hi=0.001, step=0.00005), + default=numpy.array([0.00035]), + doc="Temporal scaling in the slow state-variable, \ + called :math:`1\\tau_{0}` in Jirsa paper (see class Epileptor).", + order=5) + + x0 = arrays.FloatArray( + label="x0", + range=basic.Range(lo=-3.0, hi=-1.0, step=0.1), + default=numpy.array([-1.6]), + doc="Epileptogenicity parameter.", + order=6) + + Iext = arrays.FloatArray( + label="Iext", + range=basic.Range(lo=1.5, hi=5.0, step=0.1), + default=numpy.array([3.1]), + doc="External input current to the first state-variable.", + order=7) + + 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=8) + + 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=9) + + Ks = arrays.FloatArray( + label="K_s", + 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=10) + + tt = arrays.FloatArray( + label="tt", + default=numpy.array([1.0]), + range=basic.Range(lo=0.001, hi=1.0, step=0.001), + doc="Time scaling of the whole system to the system in real time.", + order=11) + + modification = arrays.BoolArray( + label="modification", + default=numpy.array([0]), + doc="When modification is True, then use nonlinear influence on z. \ + The default value is False, i.e., linear influence.", + order=12) + + state_variable_range = basic.Dict( + label="State variable ranges [lo, hi]", + default={"x1": numpy.array([-2., 1.]), + "z": numpy.array([2.0, 5.0])}, + doc="Typical bounds on state-variables in the Epileptor 2D model.", + order=99) + + variables_of_interest = basic.Enumerate( + label="Variables watched by Monitors", + options=['x1', 'z'], + default=['x1'], + select_multiple=True, + doc="Quantities of the Epileptor 2D available to monitor.", + order=100) + + state_variables = ['x1', 'z'] + + _nvar = 1 + cvar = numpy.array([0], dtype=numpy.int32) + + + def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, + array=numpy.array, where=numpy.where, concat=numpy.concatenate): + r""" + Computes the derivatives of the state-variables of the Epileptor 2D + with respect to time. + """ + + y = state_variables + ydot = numpy.empty_like(state_variables) + + Iext = self.Iext + local_coupling * y[0] + c_pop = coupling[0, :] + + # population 1 + if_ydot0 = self.a * y[0] ** 2 + (self.d - self.b) * y[0] + else_ydot0 = - self.slope - 0.6 * (y[1] - 4.0) ** 2 + self.d * y[0] + + ydot[0] = self.tt * (self.c - y[1] + Iext + self.Kvf * c_pop - (where(y[0] < 0., if_ydot0, else_ydot0)) * y[0]) + + # energy + if_ydot1 = - 0.1 * y[1] ** 7 + else_ydot1 = 0 + + if self.modification: + h = self.x0 + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) + else: + h = 4 * (y[0] - self.x0) + where(y[1] < 0., if_ydot1, else_ydot1) + + ydot[1] = self.tt * (self.r * (h - y[1] + self.Ks * c_pop)) + + return ydot + + def dfun(self, x, c, local_coupling=0.0): + """"The dfun using numba for speed.""" + + x_ = x.reshape(x.shape[:-1]).T + c_ = c.reshape(c.shape[:-1]).T + Iext = self.Iext + local_coupling * x[0, :, 0] + deriv = _numba_dfun_epi2d(x_, c_, + self.x0, Iext, self.a, self.b, self.slope, self.c, + self.d, self.r, self.Kvf, self.Ks, self.tt, self.modification) + return deriv.T[..., numpy.newaxis] + +@guvectorize([(float64[:],) * 15], '(n),(m)' + ',()'*12 + '->(n)', nopython=True) +def _numba_dfun_epi2d(y, c_pop, x0, Iext, a, b, slope, c, d, r, Kvf, Ks, tt, modification, ydot): + "Gufunction for Epileptor 2D model equations." + + c_pop = c_pop[0] + + # population 1 + if y[0] < 0.0: + ydot[0] = a[0] * y[0] ** 2 + (d[0] - b[0]) * y[0] + else: + ydot[0] = - slope[0] - 0.6 * (y[1] - 4.0) ** 2 + d[0] * y[0] + ydot[0] = tt[0] * (c[0] - y[1] + Iext[0] + Kvf[0] * c_pop - ydot[0] * y[0]) + + # energy + if y[1] < 0.0: + ydot[1] = - 0.1 * y[1] ** 7 + else: + ydot[1] = 0.0 + + if modification[0]: + h = x0[0] + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) + else: + h = 4 * (y[0] - x0[0]) + ydot[1] + + ydot[1] = tt[0] * (r[0] * (h - y[1] + Ks[0] * c_pop)) From e00531657630569094ac28259514217e1f71b70f Mon Sep 17 00:00:00 2001 From: liadomide Date: Wed, 18 Apr 2018 09:45:53 +0000 Subject: [PATCH 12/49] add modification z dynamics epileptor + new class epileptor2d. wes is still great. git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8691 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/simulator/models/epileptor.py | 272 ++++++++++++++++++++++++++++-- 1 file changed, 256 insertions(+), 16 deletions(-) diff --git a/tvb/simulator/models/epileptor.py b/tvb/simulator/models/epileptor.py index 6c47b1901..bb6201a64 100644 --- a/tvb/simulator/models/epileptor.py +++ b/tvb/simulator/models/epileptor.py @@ -34,8 +34,8 @@ from .base import ModelNumbaDfun, LOG, numpy, basic, arrays from numba import guvectorize, float64 -@guvectorize([(float64[:],) * 18], '(n),(m)' + ',()'*15 + '->(n)', nopython=True) -def _numba_dfun(y, c_pop, x0, Iext, Iext2, a, b, slope, tt, Kvf, c, d, r, Ks, Kf, aa, tau, ydot): +@guvectorize([(float64[:],) * 20], '(n),(m)' + ',()'*17 + '->(n)', nopython=True) +def _numba_dfun(y, c_pop, x0, Iext, Iext2, a, b, slope, tt, Kvf, c, d, r, Ks, Kf, aa, bb, tau, modification, ydot): "Gufunc for Hindmarsh-Rose-Jirsa Epileptor model equations." c_pop1 = c_pop[0] @@ -54,10 +54,14 @@ def _numba_dfun(y, c_pop, x0, Iext, Iext2, a, b, slope, tt, Kvf, c, d, r, Ks, Kf ydot[2] = - 0.1 * y[2] ** 7 else: ydot[2] = 0.0 - ydot[2] = tt[0] * (r[0] * (4 * (y[0] - x0[0]) - y[2] + ydot[2] + Ks[0] * c_pop1)) + if modification[0]: + h = x0[0] + 3/(1 + numpy.exp(-(y[0]+0.5)/0.1)) + else: + h = 4 * (y[0] - x0[0]) + ydot[2] + ydot[2] = tt[0] * (r[0] * (h - y[2] + Ks[0] * c_pop1)) # population 2 - ydot[3] = tt[0] * (-y[4] + y[3] - y[3] ** 3 + Iext2[0] + 2 * y[5] - 0.3 * (y[2] - 3.5) + Kf[0] * c_pop2) + ydot[3] = tt[0] * (-y[4] + y[3] - y[3] ** 3 + Iext2[0] + bb[0] * y[5] - 0.3 * (y[2] - 3.5) + Kf[0] * c_pop2) if y[3] < -0.25: ydot[4] = 0.0 else: @@ -144,6 +148,14 @@ class Epileptor(ModelNumbaDfun): 0 & \text{if } x_{2} <-0.25\\ a_{2}(x_{2} + 0.25) & \text{if } x_{2} \geq -0.25 \end{cases} + + Note Feb. 2017: the slow permittivity variable can be modify to account for the time + difference between interictal and ictal states (see [Proixetal_2014]). + + .. [Proixetal_2014] Proix, T.; Bartolomei, F; Chauvel, P; Bernard, C; Jirsa, V.K. * + Permittivity coupling across brain regions determines seizure recruitment in + partial epilepsy.* J Neurosci 2014, 34:15009-21. + """ _ui_name = "Epileptor" @@ -227,6 +239,12 @@ class Epileptor(ModelNumbaDfun): default=numpy.array([6]), doc="Linear coefficient in fifth state variable", order=-1) + + bb = arrays.FloatArray( + label="bb", + default=numpy.array([2]), + doc="Linear coefficient of lowpass excitatory coupling in fourth state variable", + order=-1) Kvf = arrays.FloatArray( label="K_vf", @@ -255,6 +273,13 @@ class Epileptor(ModelNumbaDfun): range=basic.Range(lo=0.001, hi=10.0, step=0.001), doc="Time scaling of the whole system", order=9) + + modification = arrays.BoolArray( + label="modification", + default=numpy.array([0]), + doc="When modification is True, then use nonlinear influence on z. \ + The default value is False, i.e., linear influence.", + order=10) state_variable_range = basic.Dict( label="State variable ranges [lo, hi]", @@ -332,24 +357,28 @@ def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, c_pop2 = coupling[1, :] # population 1 - if_ydot0 = - self.a*y[0]**2 + self.b*y[0] - else_ydot0 = self.slope - y[3] + 0.6*(y[2]-4.0)**2 - ydot[0] = self.tt*(y[1] - y[2] + Iext + self.Kvf*c_pop1 + where(y[0] < 0., if_ydot0, else_ydot0) * y[0]) - ydot[1] = self.tt*(self.c - self.d*y[0]**2 - y[1]) + if_ydot0 = - self.a * y[0] ** 2 + self.b * y[0] + else_ydot0 = self.slope - y[3] + 0.6 * (y[2] - 4.0) ** 2 + ydot[0] = self.tt * (y[1] - y[2] + Iext + self.Kvf * c_pop1 + where(y[0] < 0., if_ydot0, else_ydot0) * y[0]) + ydot[1] = self.tt * (self.c - self.d * y[0] ** 2 - y[1]) # energy - if_ydot2 = - 0.1*y[2]**7 + if_ydot2 = - 0.1 * y[2] ** 7 else_ydot2 = 0 - ydot[2] = self.tt*(self.r * ( 4*(y[0] - self.x0) - y[2] + where(y[2] < 0., if_ydot2, else_ydot2) + self.Ks*c_pop1)) + if modification: + h = h = self.x0 + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) + else: + h = 4 * (y[0] - self.x0) + where(y[2] < 0., if_ydot2, else_ydot2) + ydot[2] = self.tt * (self.r * (h - y[2] + self.Ks * c_pop1)) # population 2 - ydot[3] = self.tt*(-y[4] + y[3] - y[3]**3 + self.Iext2 + 2*y[5] - 0.3*(y[2] - 3.5) + self.Kf*c_pop2) + ydot[3] = self.tt * (-y[4] + y[3] - y[3] ** 3 + self.Iext2 + self.bb * y[5] - 0.3 * (y[2] - 3.5) + self.Kf * c_pop2) if_ydot4 = 0 - else_ydot4 = self.aa*(y[3] + 0.25) - ydot[4] = self.tt*((-y[4] + where(y[3] < -0.25, if_ydot4, else_ydot4))/self.tau) + else_ydot4 = self.aa * (y[3] + 0.25) + ydot[4] = self.tt * ((-y[4] + where(y[3] < -0.25, if_ydot4, else_ydot4)) / self.tau) # filter - ydot[5] = self.tt*(-0.01*(y[5] - 0.1*y[0])) + ydot[5] = self.tt * (-0.01 * (y[5] - 0.1 * y[0])) return ydot @@ -359,5 +388,216 @@ def dfun(self, x, c, local_coupling=0.0): Iext = self.Iext + local_coupling * x[0, :, 0] deriv = _numba_dfun(x_, c_, self.x0, Iext, self.Iext2, self.a, self.b, self.slope, self.tt, self.Kvf, - self.c, self.d, self.r, self.Ks, self.Kf, self.aa, self.tau) - return deriv.T[..., numpy.newaxis] \ No newline at end of file + self.c, self.d, self.r, self.Ks, self.Kf, self.aa, self.bb, self.tau, self.modification) + return deriv.T[..., numpy.newaxis] + + + + +class Epileptor2D(ModelNumbaDfun): + r""" + Two-dimensional reduction of the Epileptor. + + .. moduleauthor:: courtiol.julie@gmail.com + + Taking advantage of time scale separation and focusing on the slower time scale, + the five-dimensional Epileptor reduces to a two-dimensional system (see [Proixetal_2014, + Proixetal_2017]). + + Note: the slow permittivity variable can be modify to account for the time + difference between interictal and ictal states (see [Proixetal_2014]). + + Equations and default parameters are taken from [Proixetal_2014]: + + .. math:: + \dot{x_{1,i}} &=& - x_{1,i}^{3} - 2x_{1,i}^{2} + 1 - z_{i} + I_{ext1,i} \\ + \dot{z_{i}} &=& r(h - z_{i}) + + with + h = x_{0} + 3 / (exp((x_{1} + 0.5)/0.1)) if modification + h = 4 (x_{1,i} - x_{0}) + + References: + [Proixetal_2014] Proix, T.; Bartolomei, F; Chauvel, P; Bernard, C; Jirsa, V.K. * + Permittivity coupling across brain regions determines seizure recruitment in + partial epilepsy.* J Neurosci 2014, 34:15009-21. + + [Proixetal_2017] Proix, T.; Bartolomei, F; Guye, M.; Jirsa, V.K. *Individual brain + structure and modelling predict seizure propagation.* Brain 2017, 140; 641–654. + """ + + + _ui_name = "Epileptor2D" + ui_configurable_parameters = ["r", "Iext", "x0"] + + 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-variable.", + order=2) + + c = arrays.FloatArray( + label="c", + default=numpy.array([1]), + doc="Additive coefficient for the second state-variable x_{2}, \ + called :math:`y_{0}` in Jirsa paper.", + order=3) + + d = arrays.FloatArray( + label="d", + default=numpy.array([5]), + doc="Coefficient of the squared term in the second state-variable x_{2}.", + order=4) + + r = arrays.FloatArray( + label="r", + range=basic.Range(lo=0.0, hi=0.001, step=0.00005), + default=numpy.array([0.00035]), + doc="Temporal scaling in the slow state-variable, \ + called :math:`1\\tau_{0}` in Jirsa paper (see class Epileptor).", + order=5) + + x0 = arrays.FloatArray( + label="x0", + range=basic.Range(lo=-3.0, hi=-1.0, step=0.1), + default=numpy.array([-1.6]), + doc="Epileptogenicity parameter.", + order=6) + + Iext = arrays.FloatArray( + label="Iext", + range=basic.Range(lo=1.5, hi=5.0, step=0.1), + default=numpy.array([3.1]), + doc="External input current to the first state-variable.", + order=7) + + 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=8) + + 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=9) + + Ks = arrays.FloatArray( + label="K_s", + 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=10) + + tt = arrays.FloatArray( + label="tt", + default=numpy.array([1.0]), + range=basic.Range(lo=0.001, hi=1.0, step=0.001), + doc="Time scaling of the whole system to the system in real time.", + order=11) + + modification = arrays.BoolArray( + label="modification", + default=numpy.array([0]), + doc="When modification is True, then use nonlinear influence on z. \ + The default value is False, i.e., linear influence.", + order=12) + + state_variable_range = basic.Dict( + label="State variable ranges [lo, hi]", + default={"x1": numpy.array([-2., 1.]), + "z": numpy.array([2.0, 5.0])}, + doc="Typical bounds on state-variables in the Epileptor 2D model.", + order=99) + + variables_of_interest = basic.Enumerate( + label="Variables watched by Monitors", + options=['x1', 'z'], + default=['x1'], + select_multiple=True, + doc="Quantities of the Epileptor 2D available to monitor.", + order=100) + + state_variables = ['x1', 'z'] + + _nvar = 2 + cvar = numpy.array([0], dtype=numpy.int32) + + + def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, + array=numpy.array, where=numpy.where, concat=numpy.concatenate): + r""" + Computes the derivatives of the state-variables of the Epileptor 2D + with respect to time. + """ + + y = state_variables + ydot = numpy.empty_like(state_variables) + + Iext = self.Iext + local_coupling * y[0] + c_pop = coupling[0, :] + + # population 1 + if_ydot0 = self.a * y[0] ** 2 + (self.d - self.b) * y[0] + else_ydot0 = - self.slope - 0.6 * (y[1] - 4.0) ** 2 + self.d * y[0] + + ydot[0] = self.tt * (self.c - y[1] + Iext + self.Kvf * c_pop - (where(y[0] < 0., if_ydot0, else_ydot0)) * y[0]) + + # energy + if_ydot1 = - 0.1 * y[1] ** 7 + else_ydot1 = 0 + + if self.modification: + h = self.x0 + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) + else: + h = 4 * (y[0] - self.x0) + where(y[1] < 0., if_ydot1, else_ydot1) + + ydot[1] = self.tt * (self.r * (h - y[1] + self.Ks * c_pop)) + + return ydot + + def dfun(self, x, c, local_coupling=0.0): + """"The dfun using numba for speed.""" + + x_ = x.reshape(x.shape[:-1]).T + c_ = c.reshape(c.shape[:-1]).T + Iext = self.Iext + local_coupling * x[0, :, 0] + deriv = _numba_dfun_epi2d(x_, c_, + self.x0, Iext, self.a, self.b, self.slope, self.c, + self.d, self.r, self.Kvf, self.Ks, self.tt, self.modification) + return deriv.T[..., numpy.newaxis] + +@guvectorize([(float64[:],) * 15], '(n),(m)' + ',()'* 12 + '->(n)', nopython=True) +def _numba_dfun_epi2d(y, c_pop, x0, Iext, a, b, slope, c, d, r, Kvf, Ks, tt, modification, ydot): + "Gufunction for Epileptor 2D model equations." + + c_pop = c_pop[0] + + # population 1 + if y[0] < 0.0: + ydot[0] = a[0] * y[0] ** 2 + (d[0] - b[0]) * y[0] + else: + ydot[0] = - slope[0] - 0.6 * (y[1] - 4.0) ** 2 + d[0] * y[0] + ydot[0] = tt[0] * (c[0] - y[1] + Iext[0] + Kvf[0] * c_pop - ydot[0] * y[0]) + + # energy + if y[1] < 0.0: + ydot[1] = - 0.1 * y[1] ** 7 + else: + ydot[1] = 0.0 + + if modification[0]: + h = x0[0] + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) + else: + h = 4 * (y[0] - x0[0]) + ydot[1] + + ydot[1] = tt[0] * (r[0] * (h - y[1] + Ks[0] * c_pop)) From f40b7f52c1936acca441a56bc45d695718a67676 Mon Sep 17 00:00:00 2001 From: liadomide Date: Wed, 18 Apr 2018 09:45:55 +0000 Subject: [PATCH 13/49] replacing bad file git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8692 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- contrib/simulator/models/epileptor.py | 648 +++++++------------------- 1 file changed, 172 insertions(+), 476 deletions(-) diff --git a/contrib/simulator/models/epileptor.py b/contrib/simulator/models/epileptor.py index 3de8028ae..bd8164afb 100644 --- a/contrib/simulator/models/epileptor.py +++ b/contrib/simulator/models/epileptor.py @@ -25,573 +25,269 @@ # Jochen Mersmann, Anthony R. McIntosh, Viktor Jirsa (2013) # The Virtual Brain: a simulator of primate brain network dynamics. # Frontiers in Neuroinformatics (7:10. doi: 10.3389/fninf.2013.00010) +# +# """ -Hindmarsh-Rose-Jirsa Epileptor model. +The Epileptor model + +.. moduleauthor:: Marmaduke Woodman """ -from .base import ModelNumbaDfun, LOG, numpy, basic, arrays -from numba import guvectorize, float64 - -@guvectorize([(float64[:],) * 19], '(n),(m)' + ',()'*16 + '->(n)', nopython=True) -def _numba_dfun(y, c_pop, x0, Iext, Iext2, a, b, slope, tt, Kvf, c, d, r, Ks, Kf, aa, tau, modification, ydot): - "Gufunc for Hindmarsh-Rose-Jirsa Epileptor model equations." - - c_pop1 = c_pop[0] - c_pop2 = c_pop[1] - - # population 1 - if y[0] < 0.0: - ydot[0] = - a[0] * y[0] ** 2 + b[0] * y[0] - else: - ydot[0] = slope[0] - y[3] + 0.6 * (y[2] - 4.0) ** 2 - ydot[0] = tt[0] * (y[1] - y[2] + Iext[0] + Kvf[0] * c_pop1 + ydot[0] * y[0]) - ydot[1] = tt[0] * (c[0] - d[0] * y[0] ** 2 - y[1]) - - # energy - if y[2] < 0.0: - ydot[2] = - 0.1 * y[2] ** 7 - else: - ydot[2] = 0.0 - if modification[0]: - h = x0[0] + 3/(1 + numpy.exp(-(y[0]+0.5)/0.1)) - else: - h = 4 * (y[0] - x0[0]) + ydot[2] - ydot[2] = tt[0] * (r[0] * (h - y[2] + Ks[0] * c_pop1)) - - # population 2 - ydot[3] = tt[0] * (-y[4] + y[3] - y[3] ** 3 + Iext2[0] + 2 * y[5] - 0.3 * (y[2] - 3.5) + Kf[0] * c_pop2) - if y[3] < -0.25: - ydot[4] = 0.0 - else: - ydot[4] = aa[0] * (y[3] + 0.25) - ydot[4] = tt[0] * ((-y[4] + ydot[4]) / tau[0]) - - # filter - ydot[5] = tt[0] * (-0.01 * (y[5] - 0.1 * y[0])) - - -class Epileptor(ModelNumbaDfun): - r""" - The Epileptor is a composite neural mass model of six dimensions which - has been crafted to model the phenomenology of epileptic seizures. - (see [Jirsaetal_2014]_) - - Equations and default parameters are taken from [Jirsaetal_2014]_. - - +------------------------------------------------------+ - | Table 1 | - +----------------------+-------------------------------+ - | Parameter | Value | - +======================+===============================+ - | I_rest1 | 3.1 | - +----------------------+-------------------------------+ - | I_rest2 | 0.45 | - +----------------------+-------------------------------+ - | r | 0.00035 | - +----------------------+-------------------------------+ - | x_0 | -1.6 | - +----------------------+-------------------------------+ - | slope | 0.0 | - +----------------------+-------------------------------+ - | Integration parameter | - +----------------------+-------------------------------+ - | dt | 0.1 | - +----------------------+-------------------------------+ - | simulation_length | 4000 | - +----------------------+-------------------------------+ - | Noise | - +----------------------+-------------------------------+ - | nsig | [0., 0., 0., 1e-3, 1e-3, 0.] | - +----------------------+-------------------------------+ - | Jirsa et al. 2014 | - +------------------------------------------------------+ - - - .. figure :: img/Epileptor_01_mode_0_pplane.svg - :alt: Epileptor phase plane - - .. [Jirsaetal_2014] Jirsa, V. K.; Stacey, W. C.; Quilichini, P. P.; - Ivanov, A. I.; Bernard, C. *On the nature of seizure dynamics.* Brain, - 2014. - - .. automethod:: Epileptor.__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}} &=& c - d x_{1}^{2} - y{1} \\ - \dot{z} &=& - \begin{cases} - r(4 (x_{1} - x_{0}) - z-0.1 z^{7}) & \text{if } x<0 \\ - r(4 (x_{1} - x_{0}) - z) & \text{if } x \geq 0 - \end{cases} \\ - \dot{x_{2}} &=& -y_{2} + x_{2} - x_{2}^{3} + I_{ext2} + 0.002 g - 0.3 (z-3.5) \\ - \dot{y_{2}} &=& 1 / \tau (-y_{2} + f_{2}(x_{2}))\\ - \dot{g} &=& -0.01 (g - 0.1 x_{1}) - - where: - .. math:: - f_{1}(x_{1}, x_{2}) = - \begin{cases} - a x_{1}^{3} - b x_{1}^2 & \text{if } x_{1} <0\\ - -(slope - x_{2} + 0.6(z-4)^2) x_{1} &\text{if }x_{1} \geq 0 - \end{cases} - - and: - - .. math:: - f_{2}(x_{2}) = - \begin{cases} - 0 & \text{if } x_{2} <-0.25\\ - a_{2}(x_{2} + 0.25) & \text{if } x_{2} \geq -0.25 - \end{cases} - - Note Feb. 2017: the slow permittivity variable can be modify to account for the time - difference between interictal and ictal states (see [Proixetal_2014]). - - .. [Proixetal_2014] Proix, T.; Bartolomei, F; Chauvel, P; Bernard, C; Jirsa, V.K. * - Permittivity coupling across brain regions determines seizure recruitment in - partial epilepsy.* J Neurosci 2014, 34:15009-21. +# Third party python libraries +import numpy + +#The Virtual Brain +from tvb.simulator.common import psutil, get_logger +LOG = get_logger(__name__) +import tvb.datatypes.arrays as arrays +import tvb.basic.traits.types_basic as basic +import tvb.simulator.models as models + + +class HMJEpileptor(models.Model): + """ + The Epileptor is a composite neural mass model of six dimensions which + has be crafted to model the phenomenology of epileptic seizures. + + This model, its motivation and derivation are currently in preparation + for publication (Jirsa et al, 2013) + + .. automethod:: HMJEpileptor.__init__ + .. automethod:: HMJEpileptor.dfun """ _ui_name = "Epileptor" - ui_configurable_parameters = ["Iext", "Iext2", "r", "x0", "slope"] + ui_configurable_parameters = ["Iext", "Iext2", "r", "x0"] a = arrays.FloatArray( label="a", default=numpy.array([1]), - doc="Coefficient of the cubic term in the first state variable", + doc="n/a", order=-1) b = arrays.FloatArray( label="b", default=numpy.array([3]), - doc="Coefficient of the squared term in the first state variabel", + doc="n/a", order=-1) c = arrays.FloatArray( label="c", default=numpy.array([1]), - doc="Additive coefficient for the second state variable, \ - called :math:`y_{0}` in Jirsa paper", + doc="n/a", order=-1) d = arrays.FloatArray( label="d", default=numpy.array([5]), - doc="Coefficient of the squared term in the second state variable", + doc="n/a", order=-1) r = arrays.FloatArray( label="r", range=basic.Range(lo=0.0, hi=0.001, step=0.00005), default=numpy.array([0.00035]), - doc="Temporal scaling in the third state variable, \ - called :math:`1/\\tau_{0}` in Jirsa paper", + doc="n/a", order=4) s = arrays.FloatArray( label="s", default=numpy.array([4]), - doc="Linear coefficient in the third state variable", + doc="n/a", order=-1) x0 = arrays.FloatArray( label="x0", - range=basic.Range(lo=-3.0, hi=-1.0, step=0.1), + range=basic.Range(lo=-3.0, hi=0.0, step=0.1), default=numpy.array([-1.6]), - doc="Epileptogenicity parameter", + doc="n/a", order=3) Iext = arrays.FloatArray( label="Iext", 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", + doc="n/a", order=1) + omega2 = arrays.FloatArray( + label="omega2", + default=numpy.array([0.1]), + doc="n/a", + 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) + doc="n/a", + order=-1) 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", + doc="n/a", order=2) tau = arrays.FloatArray( label="tau", default=numpy.array([10]), - doc="Temporal scaling coefficient in fifth state variable", + doc="n/a", order=-1) aa = arrays.FloatArray( label="aa", default=numpy.array([6]), - doc="Linear coefficient in fifth state variable", + doc="n/a", 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]), + + Kpop1 = arrays.FloatArray( + label="K_11", + default=numpy.array([0.5]), 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) - - Ks = arrays.FloatArray( - label="K_s", - 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) - - tt = arrays.FloatArray( - label="tt", - default=numpy.array([1.0]), - range=basic.Range(lo=0.001, hi=10.0, step=0.001), - doc="Time scaling of the whole system", - order=9) - - modification = arrays.BoolArray( - label="modification", - default=numpy.array([0]), - doc="When modification is True, then use nonlinear influence on z. \ - The default value is False, i.e., linear influence.", - order=10) + doc='''Test parameter. Correspond to the coupling scaling. Move outside to be + consistent with the general TVB implementation.''', + order=-1) + + Kpop2 = arrays.FloatArray( + label="K_22", + default=numpy.array([0.2]), + range=basic.Range(lo=0.0, hi=1.0, step=0.5), + doc='''Test parameter. Correspond to the coupling scaling. Move outside to be + consistent with the general TVB implementation.''', + order=-1) state_variable_range = basic.Dict( label="State variable ranges [lo, hi]", - default={"x1": numpy.array([-2., 1.]), - "y1": numpy.array([-20., 2.]), - "z": numpy.array([2.0, 5.0]), - "x2": numpy.array([-2., 0.]), - "y2": numpy.array([0., 2.]), - "g": numpy.array([-1., 1.])}, - doc="Typical bounds on state variables in the Epileptor model.", - order=16 + default = {"y0": numpy.array([0., 1e-10]), + "y1": numpy.array([-5., 0.]), + "y2": numpy.array([3., 4.]), + "y3": numpy.array([0., 1e-10]), + "y4": numpy.array([0., 1e-10]), + "y5": numpy.array([0., 1e-2]) }, + doc = "n/a", + order=-1 ) variables_of_interest = basic.Enumerate( - label="Variables watched by Monitors", - options=['x1', 'y1', 'z', 'x2', 'y2', 'g', 'x2 - x1'], - default=["x2 - x1", 'z'], - select_multiple=True, - doc="Quantities of the Epileptor available to monitor.", - order=100 - ) + label = "Variables watched by Monitors", + options = ["y0", "y1", "y2", "y3", "y4", "y5"], + default = ["y0", "y3"], + select_multiple = True, + doc = """default state variables to be monitored""", + order = 10) + +# variables_of_interest = arrays.IntegerArray( +# label="Variables watched by Monitors", +# range=basic.Range(lo=0.0, hi=6.0, step=1.0), +# default=numpy.array([0], dtype=numpy.int32), +# doc="default state variables to be monitored", +# order=10) + + + def __init__(self, **kwargs): + """ + """ + + LOG.info("%s: init'ing..." % (str(self),)) + + super(HMJEpileptor, self).__init__(**kwargs) + + #self._state_variables = ["y%d" % i for i in range(6)] + #self._state_variables = ["y%d" % i for i in range(6)] + self._nvar = 6 + self.cvar = numpy.array([0,3], dtype=numpy.int32) - state_variables = ['x1', 'y1', 'z', 'x2', 'y2', 'g'] - _nvar = 6 - cvar = numpy.array([0, 3], dtype=numpy.int32) + LOG.debug("%s: init'ed." % (repr(self),)) - def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, + 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. + """ + 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 + 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. + the original use of if/else is translated by calculated both the true and + false forms and mixing them using a boolean mask. Variables of interest to be used by monitors: -y[0] + y[3] - .. math:: - \dot{x_{1}} &=& y_{1} - f_{1}(x_{1}, x_{2}) - z + I_{ext1} \\ - \dot{y_{1}} &=& c - d x_{1}^{2} - y{1} \\ - \dot{z} &=& - \begin{cases} - r(4 (x_{1} - x_{0}) - z-0.1 z^{7}) & \text{if } x<0 \\ - r(4 (x_{1} - x_{0}) - z) & \text{if } x \geq 0 - \end{cases} \\ - \dot{x_{2}} &=& -y_{2} + x_{2} - x_{2}^{3} + I_{ext2} + 0.002 g - 0.3 (z-3.5) \\ - \dot{y_{2}} &=& 1 / \tau (-y_{2} + f_{2}(x_{2}))\\ - \dot{g} &=& -0.01 (g - 0.1 x_{1}) - - where: - .. math:: - f_{1}(x_{1}, x_{2}) = - \begin{cases} - a x_{1}^{3} - b x_{1}^2 & \text{if } x_{1} <0\\ - -(slope - x_{2} + 0.6(z-4)^2) x_{1} &\text{if }x_{1} \geq 0 - \end{cases} - - .. math:: - f_{2}(x_{2}) = - \begin{cases} - 0 & \text{if } x_{2} <-0.25\\ - a_{2}(x_{2} + 0.25) & \text{if } x_{2} \geq -0.25 - \end{cases} - """ - y = state_variables - ydot = numpy.empty_like(state_variables) - - Iext = self.Iext + local_coupling * y[0] - c_pop1 = coupling[0, :] - c_pop2 = coupling[1, :] - - # population 1 - if_ydot0 = - self.a*y[0]**2 + self.b*y[0] - else_ydot0 = self.slope - y[3] + 0.6*(y[2]-4.0)**2 - ydot[0] = self.tt*(y[1] - y[2] + Iext + self.Kvf*c_pop1 + where(y[0] < 0., if_ydot0, else_ydot0) * y[0]) - ydot[1] = self.tt*(self.c - self.d*y[0]**2 - y[1]) - - # energy - if_ydot2 = - 0.1*y[2]**7 - else_ydot2 = 0 - if modification: - h = h = self.x0 + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) - else: - h = 4 * (y[0] - self.x0) + where(y[2] < 0., if_ydot2, else_ydot2) - ydot[2] = self.tt*(self.r * (h - y[2] + self.Ks*c_pop1)) - - # population 2 - ydot[3] = self.tt*(-y[4] + y[3] - y[3]**3 + self.Iext2 + 2*y[5] - 0.3*(y[2] - 3.5) + self.Kf*c_pop2) - if_ydot4 = 0 - else_ydot4 = self.aa*(y[3] + 0.25) - ydot[4] = self.tt*((-y[4] + where(y[3] < -0.25, if_ydot4, else_ydot4))/self.tau) - - # filter - ydot[5] = self.tt*(-0.01*(y[5] - 0.1*y[0])) - - return ydot - - def dfun(self, x, c, local_coupling=0.0): - x_ = x.reshape(x.shape[:-1]).T - c_ = c.reshape(c.shape[:-1]).T - Iext = self.Iext + local_coupling * x[0, :, 0] - deriv = _numba_dfun(x_, c_, - self.x0, Iext, self.Iext2, self.a, self.b, self.slope, self.tt, self.Kvf, - self.c, self.d, self.r, self.Ks, self.Kf, self.aa, self.tau, self.modification) - return deriv.T[..., numpy.newaxis] - - - - -class Epileptor2D(ModelNumbaDfun): - r""" - Two-dimensional reduction of the Epileptor. - - .. moduleauthor:: courtiol.julie@gmail.com - - Taking advantage of time scale separation and focusing on the slower time scale, - the five-dimensional Epileptor reduces to a two-dimensional system (see [Proixetal_2014, - Proixetal_2017]). - - Note: the slow permittivity variable can be modify to account for the time - difference between interictal and ictal states (see [Proixetal_2014]). - - Equations and default parameters are taken from [Proixetal_2014]: - - .. math:: - \dot{x_{1,i}} &=& - x_{1,i}^{3} - 2x_{1,i}^{2} + 1 - z_{i} + I_{ext1,i} \\ - \dot{z_{i}} &=& r(h - z_{i}) - - with - h = x_{0} + 3 / (exp((x_{1} + 0.5)/0.1)) if modification - h = 4 (x_{1,i} - x_{0}) - - References: - [Proixetal_2014] Proix, T.; Bartolomei, F; Chauvel, P; Bernard, C; Jirsa, V.K. * - Permittivity coupling across brain regions determines seizure recruitment in - partial epilepsy.* J Neurosci 2014, 34:15009-21. - - [Proixetal_2017] Proix, T.; Bartolomei, F; Guye, M.; Jirsa, V.K. *Individual brain - structure and modelling predict seizure propagation.* Brain 2017, 140; 641–654. - """ - - - _ui_name = "Epileptor2D" - ui_configurable_parameters = ["r", "Iext", "x0"] - - 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-variable.", - order=2) - - c = arrays.FloatArray( - label="c", - default=numpy.array([1]), - doc="Additive coefficient for the second state-variable x_{2}, \ - called :math:`y_{0}` in Jirsa paper.", - order=3) - d = arrays.FloatArray( - label="d", - default=numpy.array([5]), - doc="Coefficient of the squared term in the second state-variable x_{2}.", - order=4) - - r = arrays.FloatArray( - label="r", - range=basic.Range(lo=0.0, hi=0.001, step=0.00005), - default=numpy.array([0.00035]), - doc="Temporal scaling in the slow state-variable, \ - called :math:`1\\tau_{0}` in Jirsa paper (see class Epileptor).", - order=5) - - x0 = arrays.FloatArray( - label="x0", - range=basic.Range(lo=-3.0, hi=-1.0, step=0.1), - default=numpy.array([-1.6]), - doc="Epileptogenicity parameter.", - order=6) - - Iext = arrays.FloatArray( - label="Iext", - range=basic.Range(lo=1.5, hi=5.0, step=0.1), - default=numpy.array([3.1]), - doc="External input current to the first state-variable.", - order=7) - - 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=8) - - 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=9) - - Ks = arrays.FloatArray( - label="K_s", - 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=10) - - tt = arrays.FloatArray( - label="tt", - default=numpy.array([1.0]), - range=basic.Range(lo=0.001, hi=1.0, step=0.001), - doc="Time scaling of the whole system to the system in real time.", - order=11) - - modification = arrays.BoolArray( - label="modification", - default=numpy.array([0]), - doc="When modification is True, then use nonlinear influence on z. \ - The default value is False, i.e., linear influence.", - order=12) - - state_variable_range = basic.Dict( - label="State variable ranges [lo, hi]", - default={"x1": numpy.array([-2., 1.]), - "z": numpy.array([2.0, 5.0])}, - doc="Typical bounds on state-variables in the Epileptor 2D model.", - order=99) - - variables_of_interest = basic.Enumerate( - label="Variables watched by Monitors", - options=['x1', 'z'], - default=['x1'], - select_multiple=True, - doc="Quantities of the Epileptor 2D available to monitor.", - order=100) - - state_variables = ['x1', 'z'] - - _nvar = 1 - cvar = numpy.array([0], dtype=numpy.int32) - - - def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, - array=numpy.array, where=numpy.where, concat=numpy.concatenate): - r""" - Computes the derivatives of the state-variables of the Epileptor 2D - with respect to time. + """ + First population with high frequency burst and baseline jump - mechanisms + is similar to a Hindmarsh-Rose scenario with two régimes connected by a + slow trajectory (here y(3)). """ y = state_variables - ydot = numpy.empty_like(state_variables) - - Iext = self.Iext + local_coupling * y[0] - c_pop = coupling[0, :] - - # population 1 - if_ydot0 = self.a * y[0] ** 2 + (self.d - self.b) * y[0] - else_ydot0 = - self.slope - 0.6 * (y[1] - 4.0) ** 2 + self.d * y[0] - - ydot[0] = self.tt * (self.c - y[1] + Iext + self.Kvf * c_pop - (where(y[0] < 0., if_ydot0, else_ydot0)) * y[0]) - - # energy - if_ydot1 = - 0.1 * y[1] ** 7 - else_ydot1 = 0 - - if self.modification: - h = self.x0 + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) - else: - h = 4 * (y[0] - self.x0) + where(y[1] < 0., if_ydot1, else_ydot1) - - ydot[1] = self.tt * (self.r * (h - y[1] + self.Ks * c_pop)) - - return ydot - - def dfun(self, x, c, local_coupling=0.0): - """"The dfun using numba for speed.""" - - x_ = x.reshape(x.shape[:-1]).T - c_ = c.reshape(c.shape[:-1]).T - Iext = self.Iext + local_coupling * x[0, :, 0] - deriv = _numba_dfun_epi2d(x_, c_, - self.x0, Iext, self.a, self.b, self.slope, self.c, - self.d, self.r, self.Kvf, self.Ks, self.tt, self.modification) - return deriv.T[..., numpy.newaxis] - -@guvectorize([(float64[:],) * 15], '(n),(m)' + ',()'*12 + '->(n)', nopython=True) -def _numba_dfun_epi2d(y, c_pop, x0, Iext, a, b, slope, c, d, r, Kvf, Ks, tt, modification, ydot): - "Gufunction for Epileptor 2D model equations." - - c_pop = c_pop[0] - - # population 1 - if y[0] < 0.0: - ydot[0] = a[0] * y[0] ** 2 + (d[0] - b[0]) * y[0] - else: - ydot[0] = - slope[0] - 0.6 * (y[1] - 4.0) ** 2 + d[0] * y[0] - ydot[0] = tt[0] * (c[0] - y[1] + Iext[0] + Kvf[0] * c_pop - ydot[0] * y[0]) - - # energy - if y[1] < 0.0: - ydot[1] = - 0.1 * y[1] ** 7 - else: - ydot[1] = 0.0 - - if modification[0]: - h = x0[0] + 3. / (1. + numpy.exp(- (y[0] + 0.5) / 0.1)) - else: - h = 4 * (y[0] - x0[0]) + ydot[1] + n = y.shape[1] + Iext = self.Iext + coupling[0, :] + local_coupling + c_pop1 = coupling[0, :] + c_pop2 = coupling[1, :] - ydot[1] = tt[0] * (r[0] * (h - y[1] + Ks[0] * c_pop)) + # if y(1)<0. + # ydot1 = y(2)-a*y(1)^3 + b*y(1)^2-y(3)+iext; + # ydot2 = c-d*y(1)^2-y(2); + # ydot3 = r*(s*(y(1)-x0) - y(3)); % energy consumption = 1 - available energy + + if_y1_lt_0 = concat([ (y[1] - self.a*y[0]**3 + self.b*y[0]**2 - y[2] + Iext).reshape((1, n, 1)), + (self.c - self.d*y[0]**2 - y[1]).reshape((1, n, 1)), + (self.r*(self.s*(y[0] - self.x0) - y[2] - self.Kpop1 * (c_pop1 - y[0]) )).reshape((1, n, 1)) ]) + + # else + # % ydot1 = y(2) + (slope - y(4) -1.0*(y(3)-4))*y(1) - y(3)+iext; % this is just an + # % alternative representation, which worked well + # ydot1 = y(2) + (slope - y(4) + 0.6*(y(3)-4)^2)*y(1) -y(3)+iext; + # % here the high energy burst is being generated through variation of the slope: + # % 1. via y(4) within the epileptic spike complex; + # % 2. via the expression with y(3), which causes more + # % oscillations at the beginning of the seizure (effect of the + # % energy available) + # ydot2 = c-d*y(1)^2-y(2); + # ydot3 = r*(s*(y(1)-x0) - y(3)); + # end + + else_pop1 = concat([ (y[1] + (self.slope - y[3] + 0.6*(y[2]-4.0)**2)*y[0] - y[2] + Iext).reshape((1, n, 1)), + (self.c - self.d*y[0]**2 - y[1]).reshape((1, n, 1)), + (self.r*(self.s*(y[0] - self.x0) - y[2] - self.Kpop1 * (c_pop1 - y[0]))).reshape((1, n, 1)) ]) + + pop1 = where(y[0] < 0., if_y1_lt_0, else_pop1) + + # % istim= 0*block(t,150,1); + # + # % this is the second population that generates the big spike-wave complex + # % preictally and within the seizure via a morris-lecar-jirsa (mlj) structure + # + # if y(4)<-0.25 + # ydot4 = -y(5)+ y(4)-y(4)^3 + iext2 + 2*y(6)-0.3*(y(3)-3.5) ; % these last two terms + # % put the population dynamics into the critical regime. in particular, + # % y(6) turns the oscillator on and off, whereas the y(3) term helps it to become precritical (critical fluctuations). + # ydot5 = -y(5)/tau ; + + if_ = concat([ (-y[4] + y[3] - y[3]**3 + self.Iext2 + 2*y[5] - 0.3*(y[2] - 3.5) + self.Kpop2 * (c_pop2 - y[3])).reshape((1, n, 1)), (-y[4]/self.tau).reshape((1, n, 1)) ]) + # else + # ydot4 = -y(5)+ y(4)-y(4)^3 + iext2+ 2*y(6)-0.3*(y(3)-3.5); + # ydot5 = (-y(5) + aa*(y(4)+0.25))/tau; % here is the mlj structure + # end + + else_pop2 = concat([ (-y[4] + y[3] - y[3]**3 + self.Iext2 + 2*y[5] - 0.3*(y[2] - 3.5) + self.Kpop2 * (c_pop2 - y[3])).reshape((1, n, 1)), ((-y[4] + self.aa*(y[3] + 0.25))/self.tau).reshape((1, n, 1)) ]) + + + pop2 = where(y[3] < -0.25, if_, else_pop2) + + # + # ydot6 = -0.01*(y(6)-0.1*y(1)) ; + + energy = array([ -0.01*(y[5] - 0.1*y[0])]) + + # + # ydot = [ydot1;ydot2;ydot3;ydot4;ydot5;ydot6]; + + return concat((pop1, pop2, energy)) \ No newline at end of file From bc884ced0821cee82c5b4b5333b71b8d418fdde3 Mon Sep 17 00:00:00 2001 From: liadomide Date: Wed, 18 Apr 2018 09:45:57 +0000 Subject: [PATCH 14/49] modify _repr_html_ to also show trat descriptions git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8693 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/basic/traits/core.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tvb/basic/traits/core.py b/tvb/basic/traits/core.py index e693eb682..4a2e636e4 100644 --- a/tvb/basic/traits/core.py +++ b/tvb/basic/traits/core.py @@ -502,11 +502,22 @@ def _repr_html_(self): "Generate HTML repr for use in IPython notebook." info = self.summary_info if info is None or len(info) == 0: - info = {key: getattr(self, key) for key in self.trait.keys()} + info = {} + for key in self.trait.keys(): + info[key] = [getattr(self,key), + getattr(self.__class__,key).trait.inits.kwd['doc']] html = ['
%s%s
%s%s%s
%s%s%s
'] - row_fmt = '' - for key, value in info.items(): - html.append(row_fmt % (key, value)) + row_fmt = '' + header_fmt = '' + html.append(header_fmt % ('Parameter', 'Value', 'Description')) + for paramname,value in info.items(): + paramdescr = '' # default: no param description given + # ..if param description present (list len==2), use it + if type(value) == list: + paramval, paramdescr = value + else: + paramval = value + html.append(row_fmt % (paramname, paramval, paramdescr)) return ''.join(html) def _find_summary_info(self): From 2a1295ac231ed46bfd96eec74907a6de110d7070 Mon Sep 17 00:00:00 2001 From: liadomide Date: Wed, 18 Apr 2018 21:02:09 +0000 Subject: [PATCH 15/49] Update SVN revision number automatically from Hudson git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8695 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/basic/config/tvb.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvb/basic/config/tvb.version b/tvb/basic/config/tvb.version index 1b04dd322..cb20a2692 100644 --- a/tvb/basic/config/tvb.version +++ b/tvb/basic/config/tvb.version @@ -1 +1 @@ -Revision: 8689 \ No newline at end of file +Revision: 8695 \ No newline at end of file From a7bc648321fa20acf52e69087fd4fdb5783624f7 Mon Sep 17 00:00:00 2001 From: Julie Date: Tue, 1 May 2018 12:14:45 +0200 Subject: [PATCH 16/49] models/JCepileptor --- tvb/simulator/models/JCepileptor.py | 439 ++++++++++++++++++++++++++++ 1 file changed, 439 insertions(+) create mode 100644 tvb/simulator/models/JCepileptor.py diff --git a/tvb/simulator/models/JCepileptor.py b/tvb/simulator/models/JCepileptor.py new file mode 100644 index 000000000..356282de2 --- /dev/null +++ b/tvb/simulator/models/JCepileptor.py @@ -0,0 +1,439 @@ +# -*- coding: utf-8 -*- +# +# +# TheVirtualBrain-Scientific Package. This package holds all simulators, and +# analysers necessary to run brain-simulations. You can use it stand alone or +# in conjunction with TheVirtualBrain-Framework Package. See content of the +# documentation-folder for more details. See also http://www.thevirtualbrain.org +# +# (c) 2012-2017, Baycrest Centre for Geriatric Care ("Baycrest") and others +# +# This program is free software: you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software Foundation, +# either version 3 of the License, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public License for more details. +# You should have received a copy of the GNU General Public License along with this +# program. If not, see . +# +# +# CITATION: +# When using The Virtual Brain for scientific publications, please cite it as follows: +# +# Paula Sanz Leon, Stuart A. Knock, M. Marmaduke Woodman, Lia Domide, +# Jochen Mersmann, Anthony R. McIntosh, Viktor Jirsa (2013) +# The Virtual Brain: a simulator of primate brain network dynamics. +# Frontiers in Neuroinformatics (7:10. doi: 10.3389/fninf.2013.00010) + +""" + JC_Epileptor model: modeling resting-state in epilepsy. + + .. moduleauthor:: courtiol.julie@gmail.com +""" + +from .base import ModelNumbaDfun, LOG, numpy, basic, arrays +from numba import guvectorize, float64 + + +class JC_Epileptor(ModelNumbaDfun): + r""" + JC_Epileptor is a combination of Epileptor [Jirsaetal_2014], reproducing realistically + temporal seizure dynamics, and Generic 2-dimensional Oscillator (close to a Hopf Bifurcation) + [SanzLeonetal_2013], retrieving resting-state networks activity. + + This model, its motivation and derivation are currently in preparation + for publication [Courtioletal_2018]. + + .. Tutorial: Modeling_Resting-State_in_Epilepsy.ipynb + + .. References: + [Jirsaetal_2014] Jirsa, V. K.; Stacey, W. C.; Quilichini, P. P.; Ivanov, A. I.; Bernard, + C. *On the nature of seizure dynamics.* Brain, 2014. + [SanzLeonetal_2013] Sanz Leon, P.; Knock, S. A.; Woodman, M. M.; Domide, L.; Mersmann, + J.; McIntosh, A. R.; Jirsa, V. K. *The Virtual Brain: a simulator of primate brain + network dynamics.* Front.Neuroinf., 2013. + [Courtioletal_2018] in submission. + + Variables of interest to be used by monitors: p * (-x_{1} + x_{2}) + (1 - p) * x_{rs} + + .. math:: + \dot{x_{1}} &=& y_{1} - f_{1}(x_{1}, x_{2}) - z + I_{ext1} \\ + \dot{y_{1}} &=& c - d x_{1}^{2} - y_{1} \\ + \dot{z} &=& + \begin{cases} + r(4 (x_{1} - x_{0}) - z -0.1 z^{7}) & \text{if} x<0 \\ + r(4 (x_{1} - x_{0}) - z) & \text{if} x \geq 0 + \end{cases} \\ + \dot{x_{2}} &=& -y_{2} + x_{2} - x_{2}^{3} + I_{ext2} + b_{2} g(x_{1}) - 0.3 (z-3.5) \\ + \dot{y_{2}} &=& 1 / \tau (-y_{2} + f_{2}(x_{2}))\\ + \dot{g} &=& -0.01 (g - 0.1 x_{1}) + \dot{x_{rs}} &=& d_{rs} \tau_{rs} (-f_{rs} x_{rs}^3 + e_{rs} x_{rs}^2 + \alpha_{rs} y_{rs} + \gamma_{rs} I_{rs}) \\ + \dot{y_{rs}} &=& d_{rs} (b_{rs} x_{rs} - \beta_{rs} y_{rs} + a_{rs}) / \tau_{rs} + + where: + .. math:: + f_{1}(x_{1}, x_{2}) = + \begin{cases} + a x_{1}^{3} - b x_{1}^2 & \text{if } x_{1} <0\\ + -(slope - x_{2} + 0.6(z-4)^2) x_{1} &\text{if }x_{1} \geq 0 + \end{cases} + + and: + .. math:: + f_{2}(x_{2}) = + \begin{cases} + 0 & \text{if } x_{2} <-0.25\\ + a_{2}(x_{2} + 0.25) & \text{if } x_{2} \geq -0.25 + \end{cases} + + + """ + + _ui_name = "JC_Epileptor" + ui_configurable_parameters = ["Iext", "Iext2", "r", "x0", "slope", "tau_rs", "a_rs", "b_rs", "I_rs", "d_rs", "e_rs", "f_rs", "alpha_rs", "beta_rs", "gamma_rs"] + + # Epileptor's parameters + a = arrays.FloatArray( + label="a", + default=numpy.array([1]), + doc="Coefficient of the cubic term in the first state-variable x1.", + order=1) + + b = arrays.FloatArray( + label="b", + default=numpy.array([3]), + doc="Coefficient of the squared term in the first state-variable x1.", + order=2) + + c = arrays.FloatArray( + label="c", + default=numpy.array([1]), + doc="Additive coefficient for the second state-variable y1, \ + called :math:'y_{0}' in Jirsa et al. (2014).", + order=3) + + d = arrays.FloatArray( + label="d", + default=numpy.array([5]), + doc="Coefficient of the squared term in the second state-variable y1.", + order=4) + + r = arrays.FloatArray( + label="r", + range=basic.Range(lo=0.0, hi=0.001, step=0.00005), + default=numpy.array([0.00035]), + doc="Temporal scaling in the third state-variable z, \ + called :math:'1/\tau_{0}' in Jirsa et al. (2014).", + order=5) + + s = arrays.FloatArray( + label="s", + default=numpy.array([4]), + doc="Linear coefficient in the third state-variable z.", + order=6) + + x0 = arrays.FloatArray( + label="x0", + range=basic.Range(lo=-3.0, hi=-1.0, step=0.1), + default=numpy.array([-1.6]), + doc="Epileptogenicity parameter.", + order=7) + + Iext = arrays.FloatArray( + label="Iext", + 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 (x1, y1).", + order=8) + + 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 x1.", + order=9) + + 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 (x2, y2).", + order=10) + + tau = arrays.FloatArray( + label="tau", + default=numpy.array([10]), + doc="Temporal scaling coefficient in the fifth state-variable y2.", + order=11) + + aa = arrays.FloatArray( + label="aa", + default=numpy.array([6]), + doc="Linear coefficient in the fifth state-variable y2.", + order=12) + + bb = arrays.FloatArray( + label="bb", + default=numpy.array([2]), + doc="Linear coefficient of lowpass excitatory coupling in the fourth \ + state-variable x2.", + order=13) + + 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=14) + + Kf = arrays.FloatArray( + label="K_f", + default=numpy.array([0.0]), + range=basic.Range(lo=0.0, hi=4.0, step=0.5), + doc="Coupling scaling on a fast time scale.", + order=15) + + Ks = arrays.FloatArray( + label="K_s", + default=numpy.array([0.0]), + range=basic.Range(lo=-4.0, hi=4.0, step=0.1), + doc="Permittivity coupling, that is from the very fast time scale \ + toward the slow time scale.", + order=16) + + tt = arrays.FloatArray( + label="tt", + default=numpy.array([1.0]), + range=basic.Range(lo=0.001, hi=10.0, step=0.001), + doc="Time scaling of the Epileptor.", + order=17) + + # Generic-2D's parameters + tau_rs = arrays.FloatArray( + label=r":math:'\tau_rs'", + default=numpy.array([1.0]), + range=basic.Range(lo=1.0, hi=5.0, step=0.01), + doc="Temporal scaling coefficient in the third population (x_rs, y_rs).", + order=18) + + I_rs = arrays.FloatArray( + label=":math:'I_rs'", + default=numpy.array([0.0]), + range=basic.Range(lo=-5.0, hi=5.0, step=0.01), + doc="External input current to the third population (x_rs, y_rs).", + order=19) + + a_rs = arrays.FloatArray( + label=":math:'a_rs'", + default=numpy.array([-2.0]), + range=basic.Range(lo=-5.0, hi=5.0, step=0.01), + doc="Vertical shift of the configurable nullcline \ + in the state-variable y_rs.", + order=20) + + b_rs = arrays.FloatArray( + label=":math:'b_rs'", + default=numpy.array([-10.0]), + range=basic.Range(lo=-20.0, hi=15.0, step=0.01), + doc="Linear coefficient of the state-variable y_rs.", + order=21) + + d_rs = arrays.FloatArray( + label=":math:'d_rs'", + default=numpy.array([0.02]), + range=basic.Range(lo=0.0001, hi=1.0, step=0.0001), + doc="Temporal scaling of the whole third system (x_rs, y_rs).", + order=22) + + e_rs = arrays.FloatArray( + label=":math:'e_rs'", + default=numpy.array([3.0]), + range=basic.Range(lo=-5.0, hi=5.0, step=0.0001), + doc="Coefficient of the squared term in the sixth state-variable x_rs.", + order=23) + + f_rs = arrays.FloatArray( + label=":math:'f_rs'", + default=numpy.array([1.0]), + range=basic.Range(lo=-5.0, hi=5.0, step=0.0001), + doc="Coefficient of the cubic term in the sixth state-variable x_rs.", + order=24) + + alpha_rs = arrays.FloatArray( + label=r":math:'\alpha_rs'", + 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 y_rs to the fast variable x_rs.", + order=25) + + beta_rs = arrays.FloatArray( + label=r":math:'\beta_rs'", + 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 y_rs to itself.", + order=26) + + gamma_rs = arrays.FloatArray( + label=r":math:'\gamma_rs'", + default=numpy.array([1.0]), + range=basic.Range(lo=-1.0, hi=1.0, step=0.1), + doc="Constant parameter to reproduce FHN dynamics where \ + excitatory input currents are negative.\ + Note: It scales both I_rs and the long-range coupling term.", + order=27) + + K_rs = arrays.FloatArray( + label=r":math:'K_rs'", + default=numpy.array([1.0]), + range=basic.Range(lo=0.0, hi=10.0, step=0.001), + doc="Coupling scaling on a fast time scale.", + order=28) + + + # Combination 2 models + p = arrays.FloatArray( + label=r":math:'p'", + default=numpy.array([0.]), + range=basic.Range(lo=-1.0, hi=1.0, step=0.1), + doc="Linear coefficient.", + order=29) + + # Initialization. + # Epileptor model is set in fixed point by default. + state_variable_range = basic.Dict( + label="State variable ranges [lo, hi]", + default={"x1": numpy.array([-1.8, -1.4]), + "y1": numpy.array([-15, -10]), + "z": numpy.array([3.6, 4.0]), + "x2": numpy.array([-1.1, -0.9]), + "y2": numpy.array([0.001, 0.01]), + "g": numpy.array([-1., 1.]), + "x_rs": numpy.array([-2.0, 4.0]), + "y_rs": numpy.array([-6.0, 6.0])}, + doc="Typical bounds on state-variables in JC_Epileptor model.", + order=99 + ) + + variables_of_interest = basic.Enumerate( + label="Variables watched by Monitors", + options=["x1", "y1", "z", "x2", "y2", "g", "x_rs", "y_rs", "x2 - x1"], + default=["x2 - x1", "z", "x_rs"], + select_multiple=True, + doc="Quantities of JC_Epileptor available to monitor.", + order=100 + ) + + state_variables = ["x1", "y1", "z", "x2", "y2", "g", "x_rs", "y_rs"] + + _nvar = 8 # number of state-variables + cvar = numpy.array([0, 3, 6], dtype=numpy.int32) # coupling variables + + def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, + array=numpy.array, where=numpy.where, concat=numpy.concatenate): + r""" + Computes the derivatives of the state-variables of JC_Epileptor + with respect to time. + """ + + y = state_variables + ydot = numpy.empty_like(state_variables) + + # long-range coupling + c_pop1 = coupling[0] + c_pop2 = coupling[1] + c_pop3 = coupling[2] + + # short-range (local) coupling + Iext = self.Iext + local_coupling * y[0] + lc_1 = local_coupling * y[6] + + # Epileptor's equations: + #population 1 + if_ydot0 = - self.a * y[0] ** 2 + self.b * y[0] + else_ydot0 = self.slope - y[3] + 0.6 * (y[2] - 4.0) ** 2 + ydot[0] = self.tt * (y[1] - y[2] + Iext + self.Kvf * c_pop1 + where(y[0] < 0., if_ydot0, else_ydot0) * y[0]) + ydot[1] = self.tt * (self.c - self.d * y[0] ** 2 - y[1]) + + #energy + if_ydot2 = - 0.1 * y[2] ** 7 + else_ydot2 = 0 + ydot[2] = self.tt * (self.r * (4 * (y[0] - self.x0) - y[2] + where(y[2] < 0., if_ydot2, else_ydot2) + self.Ks * c_pop1)) + + #population 2 + ydot[3] = self.tt * (-y[4] + y[3] - y[3] ** 3 + self.Iext2 + self.bb * y[5] - 0.3 * (y[2] - 3.5)+ self.Kf * c_pop2) + if_ydot4 = 0 + else_ydot4 = self.aa * (y[3] + 0.25) + ydot[4] = self.tt * ((-y[4] + where(y[3] < -0.25, if_ydot4, else_ydot4)) / self.tau) + + #filter + ydot[5] = self.tt * (-0.01 * (y[5] - 0.1 * y[0])) #0.01 = \gamma + + # G2D's equations: + ydot[6] = self.d_rs * self.tau_rs * (self.alpha_rs * y[7] - self.f_rs * y[6] ** 3 + self.e_rs * y[6] ** 2 + self.gamma_rs * self.I_rs + self.gamma_rs * self.K_rs * c_pop3 + lc_1) + ydot[7] = self.d_rs * (self.a_rs + self.b_rs * y[6] - self.beta_rs * y[7]) / self.tau_rs + + # output: LFP + self.output = self.p * (- y[0] + y[3]) + (1 - self.p) * y[6] + + return ydot + + + def dfun(self, x, c, local_coupling=0.0): + x_ = x.reshape(x.shape[:-1]).T + c_ = c.reshape(c.shape[:-1]).T + Iext = self.Iext + local_coupling * x[0, :, 0] + lc_1 = local_coupling * x[6, :, 0] + deriv = _numba_dfun(x_, c_, + self.x0, Iext, self.Iext2, self.a, self.b, self.slope, self.tt, self.Kvf, + self.c, self.d, self.r, self.Ks, self.Kf, self.aa, self.bb, self.tau, + self.tau_rs, self.I_rs, self.a_rs, self.b_rs, self.d_rs, self.e_rs, self.f_rs, + self.beta_rs, self.alpha_rs, self.gamma_rs, self.K_rs, lc_1) + return deriv.T[..., numpy.newaxis] + + +@guvectorize([(float64[:],) * 31], '(n),(m)' + ',()' * 28 + '->(n)', nopython=True) +def _numba_dfun(y, c_pop, + x0, Iext, Iext2, a, b, slope, tt, Kvf, c, d, r, Ks, Kf, aa, bb, tau, + tau_rs, I_rs, a_rs, b_rs, d_rs, e_rs, f_rs, beta_rs, alpha_rs, gamma_rs, K_rs, lc_1, + ydot): + "Gufunc for JC_Epileptor model equations." + + #long-range coupling + c_pop1 = c_pop[0] + c_pop2 = c_pop[1] + c_pop3 = c_pop[2] + + # Epileptor equations + #population 1 + if y[0] < 0.0: + ydot[0] = - a[0] * y[0] ** 2 + b[0] * y[0] + else: + ydot[0] = slope[0] - y[3] + 0.6 * (y[2] - 4.0) ** 2 + ydot[0] = tt[0] * (y[1] - y[2] + Iext[0] + Kvf[0] * c_pop1 + ydot[0] * y[0]) + ydot[1] = tt[0] * (c[0] - d[0] * y[0] ** 2 - y[1]) + + #energy + if y[2] < 0.0: + ydot[2] = - 0.1 * y[2] ** 7 + else: + ydot[2] = 0.0 + ydot[2] = tt[0] * (r[0] * (4 * (y[0] - x0[0]) + ydot[2] - y[2] + Ks[0] * c_pop1)) + + #population 2 + ydot[3] = tt[0] * (-y[4] + y[3] - y[3] ** 3 + Iext2[0] + bb[0] * y[5] - 0.3 * (y[2] - 3.5) + Kf[0] * c_pop2) + if y[3] < -0.25: + ydot[4] = 0.0 + else: + ydot[4] = aa[0] * (y[3] + 0.25) + ydot[4] = tt[0] * ((-y[4] + ydot[4]) / tau[0]) + + #filter + ydot[5] = tt[0] * (-0.01 * (y[5] - 0.1 * y[0])) + + # Generic 2D equations + ydot[6] = d_rs[0] * tau_rs[0] * (alpha_rs[0] * y[7] - f_rs[0] * y[6] ** 3 + e_rs[0] * y[6] ** 2 + gamma_rs[0] * I_rs[0] + gamma_rs[0] * K_rs[0] * c_pop3 + lc_1[0]) + ydot[7] = d_rs[0] * (a_rs[0] + b_rs[0] * y[6] - beta_rs[0] * y[7]) / tau_rs[0] + From aff1ebfbee5cb83b5fc1f03a9db61f5b13eefa5a Mon Sep 17 00:00:00 2001 From: liadomide Date: Wed, 2 May 2018 21:01:58 +0000 Subject: [PATCH 17/49] Update SVN revision number automatically from Hudson git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8697 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/basic/config/tvb.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvb/basic/config/tvb.version b/tvb/basic/config/tvb.version index cb20a2692..5fffc2243 100644 --- a/tvb/basic/config/tvb.version +++ b/tvb/basic/config/tvb.version @@ -1 +1 @@ -Revision: 8695 \ No newline at end of file +Revision: 8697 \ No newline at end of file From ae189806501ba1d03a17ab754985e2a515a27809 Mon Sep 17 00:00:00 2001 From: marmaduke woodman Date: Wed, 13 Jun 2018 11:24:55 +0200 Subject: [PATCH 18/49] correct sim current_step increment in __call__ The increment of current_step had an erroneous -1 which is obviously wrong in the case of n_step=1: current_step would remain the same after one iteration of the simulation. Thanks to Lionel Kusch for the bug report. --- tvb/simulator/simulator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvb/simulator/simulator.py b/tvb/simulator/simulator.py index 73550baad..a4f5e4711 100644 --- a/tvb/simulator/simulator.py +++ b/tvb/simulator/simulator.py @@ -416,7 +416,7 @@ def __call__(self, simulation_length=None, random_state=None): yield output self.current_state = state - self.current_step = self.current_step + n_steps - 1 # -1 : don't repeat last point + self.current_step = self.current_step + n_steps def _configure_history(self, initial_conditions): """ From 4876f42c74921664ad9a0daad4ce8ac6080c5783 Mon Sep 17 00:00:00 2001 From: liadomide Date: Wed, 13 Jun 2018 11:26:09 +0000 Subject: [PATCH 19/49] Update SVN revision number automatically from Hudson git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8699 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/basic/config/tvb.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvb/basic/config/tvb.version b/tvb/basic/config/tvb.version index 5fffc2243..ee4525067 100644 --- a/tvb/basic/config/tvb.version +++ b/tvb/basic/config/tvb.version @@ -1 +1 @@ -Revision: 8697 \ No newline at end of file +Revision: 8699 \ No newline at end of file From 4e5f62bd1be5ddcf4b36811da5c5bc52f7e8a861 Mon Sep 17 00:00:00 2001 From: liadomide Date: Wed, 13 Jun 2018 21:01:59 +0000 Subject: [PATCH 20/49] Update SVN revision number automatically from Hudson git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8701 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/basic/config/tvb.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvb/basic/config/tvb.version b/tvb/basic/config/tvb.version index ee4525067..d5c06ff45 100644 --- a/tvb/basic/config/tvb.version +++ b/tvb/basic/config/tvb.version @@ -1 +1 @@ -Revision: 8699 \ No newline at end of file +Revision: 8701 \ No newline at end of file From 7aa0882dd08ebc336a21052f0fdb5702c4675b99 Mon Sep 17 00:00:00 2001 From: liadomide Date: Fri, 15 Jun 2018 07:47:08 +0000 Subject: [PATCH 21/49] Update SVN revision number automatically from Hudson git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8703 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/basic/config/tvb.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvb/basic/config/tvb.version b/tvb/basic/config/tvb.version index d5c06ff45..0706e5086 100644 --- a/tvb/basic/config/tvb.version +++ b/tvb/basic/config/tvb.version @@ -1 +1 @@ -Revision: 8701 \ No newline at end of file +Revision: 8703 \ No newline at end of file From 98f4b0010dc2c11aa806a003571ec78b230dc3b7 Mon Sep 17 00:00:00 2001 From: liadomide Date: Fri, 15 Jun 2018 08:57:42 +0000 Subject: [PATCH 22/49] Update SVN revision number automatically from Hudson git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8706 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/basic/config/tvb.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvb/basic/config/tvb.version b/tvb/basic/config/tvb.version index 0706e5086..686f549ef 100644 --- a/tvb/basic/config/tvb.version +++ b/tvb/basic/config/tvb.version @@ -1 +1 @@ -Revision: 8703 \ No newline at end of file +Revision: 8706 \ No newline at end of file From 5514445f8c7adf931d5964c5152acb8139f72eae Mon Sep 17 00:00:00 2001 From: liadomide Date: Fri, 15 Jun 2018 10:07:05 +0000 Subject: [PATCH 23/49] Update SVN revision number automatically from Hudson git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8708 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/basic/config/tvb.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvb/basic/config/tvb.version b/tvb/basic/config/tvb.version index 686f549ef..6fe64ba43 100644 --- a/tvb/basic/config/tvb.version +++ b/tvb/basic/config/tvb.version @@ -1 +1 @@ -Revision: 8706 \ No newline at end of file +Revision: 8708 \ No newline at end of file From f70d7f82678abcd226c36de3f773ccf108e3e7c0 Mon Sep 17 00:00:00 2001 From: liadomide Date: Fri, 15 Jun 2018 10:12:24 +0000 Subject: [PATCH 24/49] Update SVN revision number automatically from Hudson git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8710 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/basic/config/tvb.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvb/basic/config/tvb.version b/tvb/basic/config/tvb.version index 6fe64ba43..f4109fa69 100644 --- a/tvb/basic/config/tvb.version +++ b/tvb/basic/config/tvb.version @@ -1 +1 @@ -Revision: 8708 \ No newline at end of file +Revision: 8710 \ No newline at end of file From f6091a35084e50f30db5b6c673b839f050c26327 Mon Sep 17 00:00:00 2001 From: liadomide Date: Fri, 15 Jun 2018 11:56:55 +0000 Subject: [PATCH 25/49] Update SVN revision number automatically from Hudson git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8712 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/basic/config/tvb.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvb/basic/config/tvb.version b/tvb/basic/config/tvb.version index f4109fa69..bc793532f 100644 --- a/tvb/basic/config/tvb.version +++ b/tvb/basic/config/tvb.version @@ -1 +1 @@ -Revision: 8710 \ No newline at end of file +Revision: 8712 \ No newline at end of file From 062da0115f8a94f18292bd7a0502e3348046e4f4 Mon Sep 17 00:00:00 2001 From: liadomide Date: Fri, 15 Jun 2018 12:59:23 +0000 Subject: [PATCH 26/49] Update SVN revision number automatically from Hudson git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8714 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/basic/config/tvb.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvb/basic/config/tvb.version b/tvb/basic/config/tvb.version index bc793532f..080aae13d 100644 --- a/tvb/basic/config/tvb.version +++ b/tvb/basic/config/tvb.version @@ -1 +1 @@ -Revision: 8712 \ No newline at end of file +Revision: 8714 \ No newline at end of file From a85ea86e14a4d2537c2260237c9269d4a3cb2615 Mon Sep 17 00:00:00 2001 From: liadomide Date: Mon, 18 Jun 2018 10:58:09 +0000 Subject: [PATCH 27/49] Update SVN revision number automatically from Hudson git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8717 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/basic/config/tvb.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvb/basic/config/tvb.version b/tvb/basic/config/tvb.version index 080aae13d..8bff081d8 100644 --- a/tvb/basic/config/tvb.version +++ b/tvb/basic/config/tvb.version @@ -1 +1 @@ -Revision: 8714 \ No newline at end of file +Revision: 8717 \ No newline at end of file From e0390e55c51cf4192bd13f862223811e656f09b3 Mon Sep 17 00:00:00 2001 From: liadomide Date: Mon, 18 Jun 2018 15:13:34 +0000 Subject: [PATCH 28/49] Update SVN revision number automatically from Hudson git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8719 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/basic/config/tvb.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvb/basic/config/tvb.version b/tvb/basic/config/tvb.version index 8bff081d8..85ee9ae20 100644 --- a/tvb/basic/config/tvb.version +++ b/tvb/basic/config/tvb.version @@ -1 +1 @@ -Revision: 8717 \ No newline at end of file +Revision: 8719 \ No newline at end of file From 29a8a0132b4dfe938fc0e99d170ad06f2a0d6a34 Mon Sep 17 00:00:00 2001 From: liadomide Date: Mon, 18 Jun 2018 15:25:36 +0000 Subject: [PATCH 29/49] Update SVN revision number automatically from Hudson git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8721 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/basic/config/tvb.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvb/basic/config/tvb.version b/tvb/basic/config/tvb.version index 85ee9ae20..f3095a7f7 100644 --- a/tvb/basic/config/tvb.version +++ b/tvb/basic/config/tvb.version @@ -1 +1 @@ -Revision: 8719 \ No newline at end of file +Revision: 8721 \ No newline at end of file From 3869c46801b75326b7cbd0562ca9d92634b5c7ae Mon Sep 17 00:00:00 2001 From: liadomide Date: Wed, 20 Jun 2018 21:02:07 +0000 Subject: [PATCH 30/49] Update SVN revision number automatically from Hudson git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8745 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/basic/config/tvb.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvb/basic/config/tvb.version b/tvb/basic/config/tvb.version index f3095a7f7..669191be2 100644 --- a/tvb/basic/config/tvb.version +++ b/tvb/basic/config/tvb.version @@ -1 +1 @@ -Revision: 8721 \ No newline at end of file +Revision: 8745 \ No newline at end of file From 6e1f09a14a3674622abcc6d96a24275d012ec37b Mon Sep 17 00:00:00 2001 From: Lionel Kusch Date: Mon, 24 Sep 2018 15:15:58 +0200 Subject: [PATCH 31/49] Add Zerlaut model a new model of neuron population The model was implemented and tested in the next repository : https://github.com/xojeng/YAdEX_TVB/commit/53ccbdb4e9871f3a82baa6f5bb368e1f95931754 --- tvb/simulator/models/Zerlaut.py | 641 ++++++++++++++++++++++++++++++++ 1 file changed, 641 insertions(+) create mode 100644 tvb/simulator/models/Zerlaut.py diff --git a/tvb/simulator/models/Zerlaut.py b/tvb/simulator/models/Zerlaut.py new file mode 100644 index 000000000..26daf38a7 --- /dev/null +++ b/tvb/simulator/models/Zerlaut.py @@ -0,0 +1,641 @@ +""" +Mean field model based on Master equation about adaptative exponential leacky integrate and fire neurons population +""" + +from tvb.simulator.models.base import Model, LOG, numpy, basic, arrays, core +import scipy.special as sp_spec + + +class Zerlaut_adaptation_first_order(Model): + r""" + **References**: + .. [ZD_2018] Zerlaut, Y., Chemla, S., Chavane, F. et al. *Modeling mesoscopic cortical dynamics using a mean-field + model of conductance-based networks of adaptive + exponential integrate-and-fire neurons*, + J Comput Neurosci (2018) 44: 45. https://doi-org.lama.univ-amu.fr/10.1007/s10827-017-0668-2 + .. [MV_2018] Matteo di Volo, Alberto Romagnoni, Cristiano Capone, Alain Destexhe (2018) + *Mean-field model for the dynamics of conductance-based networks of excitatory and inhibitory spiking neurons + with adaptation*, bioRxiv, doi: https://doi.org/10.1101/352393 + + Used Eqns 4 from [MV_2018]_ in ``dfun``. + + The default parameters are taken from table 1 of [ZD_2018]_, pag.47 and modify for the adaptation [MV_2018] + +---------------------------+------------+ + | Table 1 | + +--------------+------------+------------+ + |Parameter | Value | Unit | + +==============+============+============+ + | cellular property | + +--------------+------------+------------+ + | g_L | 10.00 | nS | + +--------------+------------+------------+ + | E_L_e | -67.00 | mV | + +--------------+------------+------------+ + | E_L_i | -63.00 | mV | + +--------------+------------+------------+ + | C_m | 150.0 | pF | + +--------------+------------+------------+ + | b | 60.0 | nS | + +--------------+------------+------------+ + | tau_w | 500.0 | ms | + +--------------+------------+------------+ + | T | 5.0 | ms | + +--------------+------------+------------+ + | synaptic properties | + +--------------+------------+------------+ + | E_e | 0.0 | mV | + +--------------+------------+------------+ + | E_i | -80.0 | mV | + +--------------+------------+------------+ + | Q_e | 1.0 | nS | + +--------------+------------+------------+ + | Q_i | 5.0 | nS | + +--------------+------------+------------+ + | tau_e | 5.0 | ms | + +--------------+------------+------------+ + | tau_i | 5.0 | ms | + +--------------+------------+------------+ + | numerical network | + +--------------+------------+------------+ + | N_tot | 10000 | | + +--------------+------------+------------+ + | p_connect | 5.0 % | | + +--------------+------------+------------+ + | g | 20.0 % | | + +--------------+------------+------------+ + |external_input| 0.001 | Hz | + +--------------+------------+------------+ + + The default coefficients of the transfert function are taken from table I of [MV_2018]_, pag.49 + +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ + | excitatory cell | + +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ + | -4.98e-02 | 5.06e-03 | -2.5e-02 | 1.4e-03 | -4.1e-04 | 1.05e-02 | -3.6e-02 | 7.4e-03 | 1.2e-03 | -4.07e-02 | + +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ + | inhibitory cell | + +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ + | -5.14e-02 | 4.0e-03 | -8.3e-03 | 2.0e-04 | -5.0e-04 | 1.4e-03 | -1.46e-02 | 4.5e-03 | 2.8e-03 | -1.53e-02 | + +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ + + The models (:math:`E`, :math:`I`) phase-plane, including a representation of + the vector field as well as its nullclines, using default parameters, can be + seen below: + + .. automethod:: Zerlaut_adaptation_first_order.__init__ + + The general formulation for the \textit{\textbf{Zerlaut_adaptation_first_order}} model as a + dynamical unit at a node $k$ in a BNM with $l$ nodes reads: + + .. math:: + T\dot{E}_k &= F_e-E_k \\ + T\dot{I}_k &= F_i-I_k \\ + dot{W}_k &= W_k/tau_w-b*E_k \\ + F_\lambda = Erfc(V^{eff}_{thre}-\mu_V/\sqrt(2)\sigma_V) + + """ + _ui_name = "Zerlaut_adaptation_first_order" + ui_configurable_parameters = ['g_L', 'E_L_e', 'E_L_i', 'C_m', 'b', 'tau_w', + 'E_e', 'E_i', 'Q_e', 'Q_i', 'tau_e', 'tau_i', + 'N_tot', 'p_connect', 'g', 'T', + 'external_input'] + + # Define traited attributes for this model, these represent possible kwargs. + g_L = arrays.FloatArray( + label=":math:`g_{L}`", + default=numpy.array([10.]), # 10 nS by default, i.e. ~ 100MOhm input resitance at rest + range=basic.Range(lo=0.1, hi=100.0, step=0.1), # 0.1nS would be a very small cell, 100nS a very big one + doc="""leak conductance [nS]""", + order=1) + + E_L_e = arrays.FloatArray( + label=":math:`E_{L}`", + default=numpy.array([-67.0]), + range=basic.Range(lo=-90.0, hi=-60.0, step=0.1), # resting potential, usually between -85mV and -65mV + doc="""leak reversal potential for excitatory [mV]""", + order=2) + + E_L_i = arrays.FloatArray( + label=":math:`E_{L}`", + default=numpy.array([-63.0]), + range=basic.Range(lo=-90.0, hi=-60.0, step=0.1), # resting potential, usually between -85mV and -65mV + doc="""leak reversal potential for inhibitory [mV]""", + order=3) + + # N.B. Not independent of g_L, C_m should scale linearly with g_L + C_m = arrays.FloatArray( + label=":math:`C_{m}`", + default=numpy.array([150]), + range=basic.Range(lo=10.0, hi=500.0, step=10.0), # 20pF very small cell, 400pF very + doc="""membrane capacitance [pF]""", + order=4) + + b = arrays.FloatArray( + label=":math:`b`", + default=numpy.array([60.0]), + range=basic.Range(lo=0.0, hi=150.0, step=1.0), + doc="""Adaptation [nS]""", + order=5) + + tau_w = arrays.FloatArray( + label=":math:`tau_w`", + default=numpy.array([500.0]), + range=basic.Range(lo=5.0, hi=1000.0, step=1.0), + doc="""Adaptation time constant [ms]""", + order=6) + + E_e = arrays.FloatArray( + label=r":math:`E_e`", + default=numpy.array([0.0]), + range=basic.Range(lo=-20., hi=20., step=0.01), + doc="""excitatory reversal potential [mV]""", + order=7) + + E_i = arrays.FloatArray( + label=":math:`E_i`", + default=numpy.array([-80.0]), + range=basic.Range(lo=-100.0, hi=-60.0, step=1.0), + doc="""inhibitory reversal potential [mV]""", + order=8) + + Q_e = arrays.FloatArray( + label=r":math:`Q_e`", + default=numpy.array([1.0]), + range=basic.Range(lo=0.0, hi=5.0, step=0.1), + doc="""excitatory quantal conductance [nS]""", + order=9) + + Q_i = arrays.FloatArray( + label=r":math:`Q_i`", + default=numpy.array([5.0]), + range=basic.Range(lo=0.0, hi=10.0, step=0.1), + doc="""inhibitory quantal conductance [nS]""", + order=10) + + tau_e = arrays.FloatArray( + label=":math:`\tau_e`", + default=numpy.array([5.0]), + range=basic.Range(lo=1.0, hi=10.0, step=1.0), + doc="""excitatory decay [ms]""", + order=11) + + tau_i = arrays.FloatArray( + label=":math:`\tau_i`", + default=numpy.array([5.0]), + range=basic.Range(lo=0.5, hi=10.0, step=0.01), + doc="""inhibitory decay [ms]""", + order=12) + + N_tot = arrays.IntegerArray( + label=":math:`N_{tot}`", + default=numpy.array([10000]), + range=basic.Range(lo=1000, hi=50000, step=1000), + doc="""cell number""", + order=13) + + p_connect = arrays.FloatArray( + label=":math:`\epsilon`", + default=numpy.array([0.05]), + range=basic.Range(lo=0.001, hi=0.2, step=0.001), # valid only for relatively sparse connectivities + doc="""connectivity probability""", + order=14) + + g = arrays.FloatArray( + label=":math:`g`", + default=numpy.array([0.2]), + range=basic.Range(lo=0.01, hi=0.4, step=0.01), # inhibitory cell number never overcomes excitatory ones + doc="""fraction of inhibitory cells""", + order=15) + + T = arrays.FloatArray( + label=":math:`T`", + default=numpy.array([5.0]), + range=basic.Range(lo=1., hi=20.0, step=0.1), + doc="""time scale of describing network activity""", + order=16) + + P_e = arrays.IndexArray( + label=":math:`P_e`", # TODO need to check the size of the array when it's used + default=numpy.array([-4.98e-02, 5.06e-03, -2.5e-02, 1.4e-03, + -4.1e-04, 1.05e-02, -3.6e-02, 7.4e-03, + 1.2e-03, -4.07e-02]), + doc="""Polynome of excitatory phenomenological threshold (order 9)""", + order=17) + + P_i = arrays.IndexArray( + label=":math:`P_i`", # TODO need to check the size of the array when it's used + default=numpy.array([-5.14e-02, 4.0e-03, -8.3e-03, 2.0e-04, + -5.0e-04, 1.4e-03, -1.46e-02, 4.5e-03, + 2.8e-03, -1.53e-02]), + doc="""Polynome of inhibitory phenomenological threshold (order 9)""", + order=18) + + + external_input = arrays.FloatArray( + label=":math:`\nu_e^{drive}`", + default=numpy.array([0.0001]), + range=basic.Range(lo=0.00, hi=0.1, step=0.001), + doc="""external drive""", + order=19) + + # Used for phase-plane axis ranges and to bound random initial() conditions. + state_variable_range = basic.Dict( + label="State Variable ranges [lo, hi]", + default={"E": numpy.array([0.0, 0.1]), # actually the 100Hz should be replaced by 1/T_refrac + "I": numpy.array([0.0, 0.1]), + "W": numpy.array([0.0,100.0])}, + doc="""The values for each state-variable should be set to encompass + the expected dynamic range of that state-variable for the current + parameters, it is used as a mechanism for bounding random initial + conditions when the simulation isn't started from an explicit history, + it is also provides the default range of phase-plane plots.\n + E: firing rate of excitatory population in KHz\n + I: firing rate of inhibitory population in KHz\N + W: level of adaptation + """, + order=20) + + variables_of_interest = basic.Enumerate( + label="Variables watched by Monitors", + options=["E", "I","W"], + default=["E"], + 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:`E = 0`, + :math:`I = 1` and :math:`W = 2`.""", + order=21) + + state_variables = 'E I W'.split() + _nvar = 3 + cvar = numpy.array([0, 1, 2], dtype=numpy.int32) + + def dfun(self, state_variables, coupling, local_coupling=0.00): + r""" + .. math:: + T \dot{\nu_\mu} &= -F_\mu(\nu_e,\nu_i) + \nu_\mu ,\all\mu\in\{e,i\}\\ + dot{W}_k &= W_k/tau_w-b*E_k \\ + + """ + E = state_variables[0, :] + I = state_variables[1, :] + W = state_variables[2, :] + derivative = numpy.empty_like(state_variables) + + # long-range coupling + c_0 = coupling[0, :] + + # short-range (local) coupling + lc_E = local_coupling * E + lc_I = local_coupling * I + + # Excitatory firing rate derivation + derivative[0] = (self.TF_excitatory(E+c_0+lc_E+self.external_input, I+lc_I+self.external_input,W)-E)/self.T + # Inhibitory firing rate derivation + derivative[1] = (self.TF_inhibitory(E+lc_E+self.external_input, I+lc_I+self.external_input,W)-I)/self.T + # Adaptation + derivative[2] = -W/self.tau_w+self.b*E + + return derivative + + def TF_excitatory(self, fe, fi, W): + """ + transfer function for excitatory population + :param fe: firing rate of excitatory population + :param fi: firing rate of inhibitory population + :param W: level of adaptation + :return: result of transfer function + """ + return self.TF(fe, fi, W, self.P_e, self.E_L_e) + + def TF_inhibitory(self, fe, fi, W): + """ + transfer function for inhibitory population + :param fe: firing rate of excitatory population + :param fi: firing rate of inhibitory population + :param W: level of adaptation + :return: result of transfer function + """ + return self.TF(fe, fi, W, self.P_i, self.E_L_i) + + def TF(self, fe, fi, W, P, E_L): + """ + transfer function for inhibitory population + Inspired from the next repository : + https://github.com/yzerlaut/notebook_papers/tree/master/modeling_mesoscopic_dynamics + :param fe: firing rate of excitatory population + :param fi: firing rate of inhibitory population + :param W: level of adaptation + :param P: Polynome of neurons phenomenological threshold (order 9) + :param E_L: leak reversal potential + :return: result of transfer function + """ + mu_V, sigma_V, T_V = self.get_fluct_regime_vars(fe, fi, W, self.Q_e, self.tau_e, self.E_e, + self.Q_i, self.tau_i, self.E_i, + self.g_L, self.C_m, E_L, self.N_tot, + self.p_connect, self.g) + V_thre = self.threshold_func(mu_V, sigma_V, T_V*self.g_L/self.C_m, + P[0], P[1], P[2], P[3], P[4], P[5], P[6], P[7], P[8], P[9]) + V_thre *= 1e3 # the threshold need to be in mv and not in Volt + f_out = self.estimate_firing_rate(mu_V, sigma_V, T_V, V_thre) + return f_out + + @staticmethod + def get_fluct_regime_vars(Fe, Fi, W, Q_e, tau_e, E_e, Q_i, tau_i, E_i, g_L, C_m, E_L, N_tot, p_connect, g): + """ + Compute the mean characteristic of neurons. + Inspired from the next repository : + https://github.com/yzerlaut/notebook_papers/tree/master/modeling_mesoscopic_dynamics + :param Fe: firing rate of excitatory population + :param Fi: firing rate of inhibitory population + :param W: level of adaptation + :param Q_e: excitatory quantal conductance + :param tau_e: excitatory decay + :param E_e: excitatory reversal potential + :param Q_i: inhibitory quantal conductance + :param tau_i: inhibitory decay + :param E_i: inhibitory reversal potential + :param E_L: leakage reversal voltage of neurons + :param g_L: leak conductance + :param C_m: membrane capacitance + :param E_L: leak reversal potential + :param N_tot: cell number + :param p_connect: connectivity probability + :param g: fraction of inhibitory cells + :return: mean and variance of membrane voltage of neurons and autocorrelation time constant + """ + # firing rate + # 1e-6 represent spontaneous release of synaptic neurotransmitter or some intrinsic currents of neurons + fe = (Fe+1e-6)*(1.-g)*p_connect*N_tot + fi = (Fi+1e-6)*g*p_connect*N_tot + + # conductance fluctuation and effective membrane time constant + mu_Ge, mu_Gi = Q_e*tau_e*fe, Q_i*tau_i*fi # Eqns 5 from [MV_2018] + mu_G = g_L+mu_Ge+mu_Gi # Eqns 6 from [MV_2018] + T_m = C_m/mu_G # Eqns 6 from [MV_2018] + + # membrane potential + mu_V = (mu_Ge*E_e+mu_Gi*E_i+g_L*E_L-W)/mu_G # Eqns 7 from [MV_2018] + # post-synaptic membrane potential event s around muV + U_e, U_i = Q_e/mu_G*(E_e-mu_V), Q_i/mu_G*(E_i-mu_V) + # Standard deviation of the fluctuations + # Eqns 8 from [MV_2018] + sigma_V = numpy.sqrt(fe*(U_e*tau_e)**2/(2.*(tau_e+T_m))+fi*(U_i*tau_i)**2/(2.*(tau_i+T_m))) + # Autocorrelation-time of the fluctuations Eqns 9 from [MV_2018] + T_V_numerator = (fe*(U_e*tau_e)**2 + fi*(U_i*tau_i)**2) + T_V_denominator = (fe*(U_e*tau_e)**2/(tau_e+T_m) + fi*(U_i*tau_i)**2/(tau_i+T_m)) + T_V = numpy.divide(T_V_numerator, T_V_denominator, out=numpy.ones_like(T_V_numerator), + where=T_V_denominator != 0.0) + return mu_V, sigma_V, T_V + + @staticmethod + def threshold_func(muV, sigmaV, TvN, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9): + """ + The threshold function of the neurons + :param muV: mean of membrane voltage + :param sigmaV: variance of membrane voltage + :param TvN: autocorrelation time constant + :param P: Fitted coefficients of the transfer functions + :return: threshold of neurons + """ + # Normalization factors page 48 after the equation 4 from [ZD_2018] + muV0, DmuV0 = -60.0, 10.0 + sV0, DsV0 = 4.0, 6.0 + TvN0, DTvN0 = 0.5, 1. + V = (muV-muV0)/DmuV0 + S = (sigmaV-sV0)/DsV0 + T = (TvN-TvN0)/DTvN0 + # Eqns 11 from [MV_2018] + return P0 + P1*V + P2*S + P3*T + P4*V**2 + P5*S**2 + P6*T**2 + P7*V*S + P8*V*T + P9*S*T + + @staticmethod + def estimate_firing_rate(muV, sigmaV, Tv, Vthre): + # Eqns 10 from [MV_2018] + return sp_spec.erfc((Vthre-muV) / (numpy.sqrt(2)*sigmaV)) / (2*Tv) + + +class Zerlaut_adaptation_second_order(Zerlaut_adaptation_first_order): + r""" + **References**: + .. [ZD_2018] Zerlaut, Y., Chemla, S., Chavane, F. et al. *Modeling mesoscopic cortical dynamics using a mean-field + model of conductance-based networks of adaptive + exponential integrate-and-fire neurons*, + J Comput Neurosci (2018) 44: 45. https://doi-org.lama.univ-amu.fr/10.1007/s10827-017-0668-2 + .. [MV_2018] Matteo di Volo, Alberto Romagnoni, Cristiano Capone, Alain Destexhe (2018) + *Mean-field model for the dynamics of conductance-based networks of excitatory and inhibitory spiking neurons + with adaptation*, bioRxiv, doi: https://doi.org/10.1101/352393 + + Used Eqns 4 from [MV_2018]_ in ``dfun``. + + (See Zerlaut_adaptation_first_order for the default value) + + The models (:math:`E`, :math:`I`) phase-plane, including a representation of + the vector field as well as its nullclines, using default parameters, can be + seen below: + + .. automethod:: Zerlaut_adaptation_second_order.__init__ + + The general formulation for the \textit{\textbf{Zerlaut_adaptation_second_order}} model as a + dynamical unit at a node $k$ in a BNM with $l$ nodes reads: + + .. math:: + \forall \mu,\lambda,\eta \in \{e,i\}^3\, , + \left\{ + \begin{split} + T \, \frac{\partial \nu_\mu}{\partial t} = & (\mathcal{F}_\mu - \nu_\mu ) + + \frac{1}{2} \, c_{\lambda \eta} \, + \frac{\partial^2 \mathcal{F}_\mu}{\partial \nu_\lambda \partial \nu_\eta} \\ + T \, \frac{\partial c_{\lambda \eta} }{\partial t} = & A_{\lambda \eta} + + (\mathcal{F}_\lambda - \nu_\lambda ) \, (\mathcal{F}_\eta - \nu_\eta ) + \\ + & c_{\lambda \mu} \frac{\partial \mathcal{F}_\mu}{\partial \nu_\lambda} + + c_{\mu \eta} \frac{\partial \mathcal{F}_\mu}{\partial \nu_\eta} + - 2 c_{\lambda \eta} + \end{split} + \right. + dot{W}_k &= W_k/tau_w-b*E_k \\ + + with: + A_{\lambda \eta} = + \left\{ + \begin{split} + \frac{\mathcal{F}_\lambda \, (1/T - \mathcal{F}_\lambda)}{N_\lambda} + \qquad & \textrm{if } \lambda=\eta \\ + 0 \qquad & \textrm{otherwise} + \end{split} + \right. + """ + + _ui_name = "Zerlaut_adaptation_second_order" + + # Used for phase-plane axis ranges and to bound random initial() conditions. + state_variable_range = basic.Dict( + label="State Variable ranges [lo, hi]", + default={"E": numpy.array([0.0, 0.1]), # actually the 100Hz should be replaced by 1/T_refrac + "I": numpy.array([0.0, 0.1]), + "C_ee": numpy.array([0.0, 0.0]), # variance is positive or null + "C_ei": numpy.array([0.0, 0.0]), # the co-variance is in [-c_ee*c_ii,c_ee*c_ii] + "C_ii": numpy.array([0.0, 0.0]), # variance is positive or null + "W":numpy.array([0.0, 100.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.\n + E: firing rate of excitatory population in KHz\n + I: firing rate of inhibitory population in KHz\n + C_ee: the variance of the excitatory population activity \n + C_ei: the covariance between the excitatory and inhibitory population activities (always symetric) \n + C_ie: the variance of the inhibitory population activity \n + W: level of adaptation + """, + order=20) + + variables_of_interest = basic.Enumerate( + label="Variables watched by Monitors", + options=["E", "I", "C_ee","C_ei","C_ii"], + default=["E"], + 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:`E = 0`, + :math:`I = 1`, :math:`C_ee = 2`, :math:`C_ei = 3`, :math:`C_ii = 4` and :math:`W = 5`.""", + order=21) + + state_variables = 'E I C_ee C_ei C_ii W'.split() + _nvar = 6 + cvar = numpy.array([0, 1, 2, 3, 4, 5], dtype=numpy.int32) + + def dfun(self, state_variables, coupling, local_coupling=0.00): + r""" + .. math:: + \forall \mu,\lambda,\eta \in \{e,i\}^3\, , + \left\{ + \begin{split} + T \, \frac{\partial \nu_\mu}{\partial t} = & (\mathcal{F}_\mu - \nu_\mu ) + + \frac{1}{2} \, c_{\lambda \eta} \, + \frac{\partial^2 \mathcal{F}_\mu}{\partial \nu_\lambda \partial \nu_\eta} \\ + T \, \frac{\partial c_{\lambda \eta} }{\partial t} = & A_{\lambda \eta} + + (\mathcal{F}_\lambda - \nu_\lambda ) \, (\mathcal{F}_\eta - \nu_\eta ) + \\ + & c_{\lambda \mu} \frac{\partial \mathcal{F}_\mu}{\partial \nu_\lambda} + + c_{\mu \eta} \frac{\partial \mathcal{F}_\mu}{\partial \nu_\eta} + - 2 c_{\lambda \eta} + \end{split} + \right. + dot{W}_k &= W_k/tau_w-b*E_k \\ + + with: + A_{\lambda \eta} = + \left\{ + \begin{split} + \frac{\mathcal{F}_\lambda \, (1/T - \mathcal{F}_\lambda)}{N_\lambda} + \qquad & \textrm{if } \lambda=\eta \\ + 0 \qquad & \textrm{otherwise} + \end{split} + \right. + + """ + N_e = self.N_tot * (1-self.g) + N_i = self.N_tot * self.g + + E = state_variables[0, :] + I = state_variables[1, :] + C_ee = state_variables[2, :] + C_ei = state_variables[3, :] + C_ii = state_variables[4, :] + W = state_variables[5,:] + derivative = numpy.empty_like(state_variables) + + # long-range coupling + c_0 = coupling[0, :] + + # short-range (local) coupling + lc_E = local_coupling * E + lc_I = local_coupling * I + + E_input_excitatory = E+c_0+lc_E+self.external_input + E_input_inhibitory = E+lc_E+self.external_input + I_input_excitatory = I+lc_I+self.external_input + I_input_inhibitory = I+lc_I+self.external_input + + # Transfer function of excitatory and inhibitory neurons + _TF_e = self.TF_excitatory(E_input_excitatory, I_input_excitatory, W) + _TF_i = self.TF_inhibitory(E_input_inhibitory, I_input_inhibitory, W) + + # Derivatives taken numerically : use a central difference formula with spacing `dx` + def _diff_fe(TF, fe, fi, W, df=1e-7): + return (TF(fe+df, fi, W)-TF(fe-df, fi, W))/(2*df*1e3) + + def _diff_fi(TF, fe, fi, W, df=1e-7): + return (TF(fe, fi+df, W)-TF(fe, fi-df, W))/(2*df*1e3) + + def _diff2_fe_fe_e(fe, fi, W, df=1e-7): + TF = self.TF_excitatory + return (TF(fe+df, fi, W)-2*_TF_e+TF(fe-df, fi, W))/((df*1e3)**2) + + def _diff2_fe_fe_i(fe, fi, W, df=1e-7): + TF = self.TF_inhibitory + return (TF(fe+df, fi, W)-2*_TF_i+TF(fe-df, fi, W))/((df*1e3)**2) + + def _diff2_fi_fe(TF, fe, fi, W, df=1e-7): + return (_diff_fi(TF, fe+df, fi, W)-_diff_fi(TF, fe-df, fi, W))/(2*df*1e3) + + def _diff2_fe_fi(TF, fe, fi, W, df=1e-7): + return (_diff_fe(TF, fe, fi+df, W)-_diff_fe(TF, fe, fi-df, W))/(2*df*1e3) + + def _diff2_fi_fi_e(fe, fi, W, df=1e-7): + TF = self.TF_excitatory + return (TF(fe, fi+df, W)-2*_TF_e+TF(fe, fi-df, W))/((df*1e3)**2) + + def _diff2_fi_fi_i(fe, fi, W, df=1e-7): + TF = self.TF_inhibitory + return (TF(fe, fi+df, W)-2*_TF_i+TF(fe, fi-df, W))/((df*1e3)**2) + + #Precompute some result + _diff_fe_TF_e = _diff_fe(self.TF_excitatory, E_input_excitatory, I_input_excitatory, W) + _diff_fe_TF_i = _diff_fe(self.TF_inhibitory, E_input_inhibitory, I_input_inhibitory, W) + _diff_fi_TF_e = _diff_fi(self.TF_excitatory, E_input_excitatory, I_input_excitatory, W) + _diff_fi_TF_i = _diff_fi(self.TF_inhibitory, E_input_inhibitory, I_input_inhibitory, W) + + # equation is inspired from github of Zerlaut : + # https://github.com/yzerlaut/notebook_papers/blob/master/modeling_mesoscopic_dynamics/mean_field/master_equation.py + # Excitatory firing rate derivation + derivative[0] = (_TF_e - E + + .5*C_ee*_diff2_fe_fe_e(E_input_excitatory, I_input_excitatory, W) + + .5*C_ei*_diff2_fe_fi(self.TF_excitatory, E_input_excitatory, I_input_excitatory, W) + + .5*C_ei*_diff2_fi_fe(self.TF_excitatory, E_input_excitatory, I_input_excitatory, W) + + .5*C_ii*_diff2_fi_fi_e(E_input_excitatory, I_input_excitatory, W) + )/self.T + # Inhibitory firing rate derivation + derivative[1] = (_TF_i - I + + .5*C_ee*_diff2_fe_fe_i(E_input_inhibitory, I_input_inhibitory, W) + + .5*C_ei*_diff2_fe_fi(self.TF_inhibitory, E_input_inhibitory, I_input_inhibitory, W) + + .5*C_ei*_diff2_fi_fe(self.TF_inhibitory, E_input_inhibitory, I_input_inhibitory, W) + + .5*C_ii*_diff2_fi_fi_i(E_input_inhibitory, I_input_inhibitory, W) + )/self.T + # Covariance excitatory-excitatory derivation + derivative[2] = (_TF_e*(1./self.T-_TF_e)/N_e + + (_TF_e-E)**2 + + 2.*C_ee*_diff_fe_TF_e + + 2.*C_ei*_diff_fi_TF_i + - 2.*C_ee + )/self.T + # Covariance excitatory-inhibitory or inhibitory-excitatory derivation + derivative[3] = ((_TF_e-E)*(_TF_i-I) + + C_ee*_diff_fe_TF_e + + C_ei*_diff_fe_TF_i + + C_ei*_diff_fi_TF_e + + C_ii*_diff_fi_TF_i + - 2.*C_ei + )/self.T + # Covariance inhibitory-inhibitory derivation + derivative[4] = (_TF_i*(1./self.T-_TF_i)/N_i + + (_TF_i-I)**2 + + 2.*C_ii*_diff_fi_TF_i + + 2.*C_ei*_diff_fe_TF_e + - 2.*C_ii + )/self.T + # Adaptation + derivative[5] = -W/self.tau_w+self.b*E + + return derivative + From 5f4d4745a23a6d8c4f532988b9f093337d6ea7c0 Mon Sep 17 00:00:00 2001 From: Julie Date: Fri, 1 Feb 2019 22:26:13 +0100 Subject: [PATCH 32/49] add supHopf model --- tvb/simulator/models/oscillator.py | 107 +++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/tvb/simulator/models/oscillator.py b/tvb/simulator/models/oscillator.py index 677e8f67e..26d4666d6 100644 --- a/tvb/simulator/models/oscillator.py +++ b/tvb/simulator/models/oscillator.py @@ -530,3 +530,110 @@ def dfun(self, state_variables, coupling, local_coupling=0.0, # all this pi makeh me have great hungary, can has sum NaN? return self.derivative + + +class supHopf(ModelNumbaDfun): + r""" + The supHopf model describes the normal form of a supercritical Hopf bifurcation in Cartesian coordinates. + This normal form has a supercritical bifurcation at a=0 with a the bifurcation parameter in + the model. So for a < 0, the local dynamics has a stable fixed point, and for a > 0, the local dynamics enters + in a stable limit cycle. + + See: + + .. [Deco_2017a] Deco, G., Kringelbach, M.L., Jirsa, V.K., Ritter, P. + *The dynamics of resting fluctuations in the brain: metastability and its dynamical + cortical core* Sci Reports, 2017, 7: 3095. + + The equations of the supHopf population model read: + + .. math:: + \dot{x}_{i} &= (a_{i} - x_{i}^{2} - y_{i}^{2})x_{i} - omega{i}y_{i} \\ + \dot{y}_{i} &= (a_{i} - x_{i}^{2} - y_{i}^{2})y_{i} + omega{i}x_{i} + + """ + + _ui_name = "supHopf" + ui_configurable_parameters = ['a', 'omega'] + + #supHopf's parameters. + a = arrays.FloatArray( + label=r":math:`a`", + default=numpy.array([-0.5]), + range=basic.Range(lo=-10.0, hi=10.0, step=0.01), + doc="""Local bifurcation parameter.""", + order=1) + + omega = arrays.FloatArray( + label=r":math:`\omega`", + default=numpy.array([1.]), + range=basic.Range(lo=0.05, hi=630.0, step=0.01), + doc="""Angular frequency.""", + order=2) + + # Initialization. + state_variable_range = basic.Dict( + label="State Variable ranges [lo, hi]", + default={"x": numpy.array([-5.0, 5.0]), + "y": 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 initial + conditions when the simulation isn't started from an explicit + history, it is also provides the default range of phase-plane plots.""", + order=3) + + variables_of_interest = basic.Enumerate( + label="Variables watched by Monitors", + options=["x", "y"], + default=["x"], + select_multiple=True, + doc="Quantities of supHopf available to monitor.", + order=4) + + state_variables = ["x", "y"] + + _nvar = 2 # number of state-variables + cvar = numpy.array([0, 1], dtype=numpy.int32) # coupling variables + + def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, + array=numpy.array, where=numpy.where, concat=numpy.concatenate): + r""" + Computes the derivatives of the state-variables of supHopf + with respect to time. + """ + + y = state_variables + ydot = numpy.empty_like(state_variables) + + # long-range coupling + c_0 = coupling[0] + c_1 = coupling[1] + + # short-range (local) coupling + lc_0 = local_coupling * y[0] + + #supHopf's equations in Cartesian coordinates: + ydot[0] = (self.a - y[0]**2 - y[1]**2) * y[0] - self.omega * y[1] + c_0 + ydot[1] = (self.a - y[0]**2 - y[1]**2) * y[1] + self.omega * y[0] + c_1 + + return ydot + + def dfun(self, x, c, local_coupling=0.0): + x_ = x.reshape(x.shape[:-1]).T + c_ = c.reshape(c.shape[:-1]).T + lc_0 = local_coupling * x[0, :, 0] + deriv = _numba_dfun(x_, c_, self.a, self.omega, lc_0) + return deriv.T[..., numpy.newaxis] + +@guvectorize([(float64[:],) * 6], '(n),(m)' + ',()' * 3 + '->(n)', nopython=True) +def _numba_dfun(y, c, a, omega, lc_0, ydot): + "Gufunc for supHopf model equations." + + #long-range coupling + c_0 = c[0] + c_1 = c[1] + + #supHopf equations + ydot[0] = (a[0] - y[0]**2 - y[1]**2) * y[0] - omega[0] * y[1] + c_0 + ydot[1] = (a[0] - y[0]**2 - y[1]**2) * y[1] + omega[0] * y[0] + c_1 \ No newline at end of file From 21d2151f4a1b86c6537743e0f794f68a6f19e72f Mon Sep 17 00:00:00 2001 From: JulieCB Date: Sat, 2 Feb 2019 15:28:40 +0100 Subject: [PATCH 33/49] Update oscillator.py --- tvb/simulator/models/oscillator.py | 45 +++++++++++++++++------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/tvb/simulator/models/oscillator.py b/tvb/simulator/models/oscillator.py index 26d4666d6..07024458f 100644 --- a/tvb/simulator/models/oscillator.py +++ b/tvb/simulator/models/oscillator.py @@ -535,22 +535,26 @@ def dfun(self, state_variables, coupling, local_coupling=0.0, class supHopf(ModelNumbaDfun): r""" The supHopf model describes the normal form of a supercritical Hopf bifurcation in Cartesian coordinates. - This normal form has a supercritical bifurcation at a=0 with a the bifurcation parameter in - the model. So for a < 0, the local dynamics has a stable fixed point, and for a > 0, the local dynamics enters - in a stable limit cycle. + This normal form has a supercritical bifurcation at a=0 with a the bifurcation parameter in the model. So + for a < 0, the local dynamics has a stable fixed point and the system corresponds to a damped oscillatory + state, whereas for a > 0, the local dynamics enters in a stable limit cycle and the system switches to an + oscillatory state. - See: + See for examples: + + .. [Kuznetsov_2013] Kuznetsov, Y.A. *Elements of applied bifurcation theory.* Springer Sci & Business + Media, 2013, vol. 112. - .. [Deco_2017a] Deco, G., Kringelbach, M.L., Jirsa, V.K., Ritter, P. - *The dynamics of resting fluctuations in the brain: metastability and its dynamical - cortical core* Sci Reports, 2017, 7: 3095. + .. [Deco_2017a] Deco, G., Kringelbach, M.L., Jirsa, V.K., Ritter, P. *The dynamics of resting fluctuations + in the brain: metastability and its dynamical cortical core* Sci Reports, 2017, 7: 3095. - The equations of the supHopf population model read: + The equations of the supHopf equations read as follows: .. math:: \dot{x}_{i} &= (a_{i} - x_{i}^{2} - y_{i}^{2})x_{i} - omega{i}y_{i} \\ \dot{y}_{i} &= (a_{i} - x_{i}^{2} - y_{i}^{2})y_{i} + omega{i}x_{i} + where a is the local bifurcation parameter and omega the angular frequency. """ _ui_name = "supHopf" @@ -577,10 +581,10 @@ class supHopf(ModelNumbaDfun): default={"x": numpy.array([-5.0, 5.0]), "y": 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 initial - conditions when the simulation isn't started from an explicit - history, it is also provides the default range of phase-plane plots.""", + 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=3) variables_of_interest = basic.Enumerate( @@ -596,11 +600,11 @@ class supHopf(ModelNumbaDfun): _nvar = 2 # number of state-variables cvar = numpy.array([0, 1], dtype=numpy.int32) # coupling variables - def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, - array=numpy.array, where=numpy.where, concat=numpy.concatenate): + def _numpy_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 supHopf - with respect to time. + Computes the derivatives of the state-variables of supHopf + with respect to time. """ y = state_variables @@ -614,7 +618,7 @@ def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0, lc_0 = local_coupling * y[0] #supHopf's equations in Cartesian coordinates: - ydot[0] = (self.a - y[0]**2 - y[1]**2) * y[0] - self.omega * y[1] + c_0 + ydot[0] = (self.a - y[0]**2 - y[1]**2) * y[0] - self.omega * y[1] + c_0 + lc_0 ydot[1] = (self.a - y[0]**2 - y[1]**2) * y[1] + self.omega * y[0] + c_1 return ydot @@ -624,10 +628,11 @@ def dfun(self, x, c, local_coupling=0.0): c_ = c.reshape(c.shape[:-1]).T lc_0 = local_coupling * x[0, :, 0] deriv = _numba_dfun(x_, c_, self.a, self.omega, lc_0) + return deriv.T[..., numpy.newaxis] @guvectorize([(float64[:],) * 6], '(n),(m)' + ',()' * 3 + '->(n)', nopython=True) -def _numba_dfun(y, c, a, omega, lc_0, ydot): +def _numba_dfun_supHopf(y, c, a, omega, lc_0, ydot): "Gufunc for supHopf model equations." #long-range coupling @@ -635,5 +640,5 @@ def _numba_dfun(y, c, a, omega, lc_0, ydot): c_1 = c[1] #supHopf equations - ydot[0] = (a[0] - y[0]**2 - y[1]**2) * y[0] - omega[0] * y[1] + c_0 - ydot[1] = (a[0] - y[0]**2 - y[1]**2) * y[1] + omega[0] * y[0] + c_1 \ No newline at end of file + ydot[0] = (a[0] - y[0]**2 - y[1]**2) * y[0] - omega[0] * y[1] + c_0 + lc_0[0] + ydot[1] = (a[0] - y[0]**2 - y[1]**2) * y[1] + omega[0] * y[0] + c_1 From d7d48a04ca160c4c5775515d78db6e4cf125c44e Mon Sep 17 00:00:00 2001 From: "mihai.andrei" Date: Mon, 18 Feb 2019 12:13:07 +0200 Subject: [PATCH 34/49] Fix: FFT analyser crashed when using window functions --- tvb/analyzers/fft.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tvb/analyzers/fft.py b/tvb/analyzers/fft.py index 360ae5aa3..048855b85 100644 --- a/tvb/analyzers/fft.py +++ b/tvb/analyzers/fft.py @@ -135,8 +135,8 @@ def evaluate(self): #Enumerate basic type wraps single values into a list if self.window_function != [None]: window_function = SUPPORTED_WINDOWING_FUNCTIONS[self.window_function[0]] - window_mask = numpy.reshape(window_function(seg_tpts), - (seg_tpts, 1, 1, 1, 1)) + window_mask = numpy.reshape(window_function(int(seg_tpts)), + (int(seg_tpts), 1, 1, 1, 1)) time_series = time_series * window_mask #Calculate the FFT From c24336865b723b68aa9841b622ad46177ed86ad7 Mon Sep 17 00:00:00 2001 From: Marmaduke Woodman Date: Fri, 1 Mar 2019 09:47:51 +0100 Subject: [PATCH 35/49] fix typo in JR docstring (code is correct) --- tvb/simulator/models/jansen_rit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvb/simulator/models/jansen_rit.py b/tvb/simulator/models/jansen_rit.py index 1f3862fc4..65ca7f79c 100644 --- a/tvb/simulator/models/jansen_rit.py +++ b/tvb/simulator/models/jansen_rit.py @@ -63,7 +63,7 @@ class JansenRit(ModelNumbaDfun): .. math:: \dot{y_0} &= y_3 \\ - \dot{y_3} &= A a\,S[y_1 - y_2] - 2a\,y_3 - 2a^2\, y_0 \\ + \dot{y_3} &= A a\,S[y_1 - y_2] - 2a\,y_3 - a^2\, y_0 \\ \dot{y_1} &= y_4\\ \dot{y_4} &= A a \,[p(t) + \alpha_2 J + S[\alpha_1 J\,y_0]+ c_0] -2a\,y - a^2\,y_1 \\ From 05e266bf59c8de9e2dad0705e9ecb8868a6bf833 Mon Sep 17 00:00:00 2001 From: dokato Date: Thu, 7 Mar 2019 09:11:19 +0000 Subject: [PATCH 36/49] Fixes to the 2 state wong wang model (#87) * fixes to make 2 state wong wang model run * reverted changes of phase plane * order of Js changed in WW contrib * fixed Wong-Wang model based on WW 2007 from Front. COmp. Neurosc. * fixes time scale to miliseconds * local connectivity added * default example (phaseplane) fixed --- contrib/simulator/models/wong_wang.py | 194 ++++++++++++-------------- 1 file changed, 92 insertions(+), 102 deletions(-) diff --git a/contrib/simulator/models/wong_wang.py b/contrib/simulator/models/wong_wang.py index 491aba739..cf63dd541 100644 --- a/contrib/simulator/models/wong_wang.py +++ b/contrib/simulator/models/wong_wang.py @@ -56,6 +56,11 @@ class WongWang(models.Model): Journal of Neuroscience 26(4), 1314-1328, 2006. .. [WW_2006_SI] Supplementary Information + + .. [WW_2007] Kong-Fatt Wong, Alexander C. Huk2, Michael N. Shadlen, + Xiao-Jing Wang, *Neural circuit dynamics underlying accumulation + of time-varying evidence during perceptual decision making*. + Front. Comput. Neurosci., 2007. A reduced model by Wong and Wang: A reduced two-variable neural model that offers a simple yet biophysically plausible framework for studying @@ -65,131 +70,119 @@ class WongWang(models.Model): corresponding to AMPAand GABA gating variables, it is assumed that is :math:`S_{NMDA}` that dominates the time evolution of the system. - The model (:math:`S1`, :math:`S2`) phase-plane, including a representation + The model (:math:`Sl`, :math:`Sr`) phase-plane, including a representation of the vector field as well as its nullclines, using default parameters, can be seen below: - .. figure :: img/WongWang_01_mode_0_pplane.svg - .. _phase-plane-WongWang: - :alt: Phase plane of the reduced model by Wong and Wang (S1, S2) - - To reproduce the phase plane in Figure 4A, page 1319 (five steady states): - J11 = 0.54 - J22 = 0.18 - J12 = 0.08 - J21 = 0.58 - J_ext = 0.0 - I_o = 0.34 - sigma_noise = 0.02 - mu_o = 0.00 - c = 100.0 - - To reproduce the phase plane in Figure 4B, page 1319 (saddle-type point): - b = 0.108 - d = 121.0 - gamma = 0.11 - tau_s = 100. - J11 = 0.78 - J22 = 0.59 - J12 = 0.72 - J21 = 0.67 - J_ext = 0.52 - I_o = 0.3255 - sigma_noise = 0.02 - mu_o = 0.35 - c = 0.0 + Notation and parameter selection follows _Materials and methods_ from [WW_2007]. + + To reproduce the phase plane in Figure 5B, page 1320: + Jll = Jrr = 0.3725 + Jlr = Jrl = 0.1137 + J_ext = 1.1e-3 + I_o = 0.3297 + mu_o = 30 + c = 6.4 + + To reproduce C & D vary c parameter respectively. .. automethod:: __init__ """ - _ui_name = "Wong-Wang (Original)" + _ui_name = "Wong-Wang (2D)" #Define traited attributes for this model, these represent possible kwargs. a = arrays.FloatArray( label=":math:`a`", - default=numpy.array([0.270, ]), - range=basic.Range(lo=0.0, hi=1.0), - doc=""" (mVnC)^{-1}. Parameter chosen to fit numerical solutions.""") + default=numpy.array([270., ]), + range=basic.Range(lo=0.0, hi=1000.0), + doc="""[Hz/nA] Parameter chosen to fit numerical solutions.""") b = arrays.FloatArray( label=":math:`b`", - default=numpy.array([0.108, ]), + default=numpy.array([108, ]), range=basic.Range(lo=0.0, hi=1.0), - doc="""[kHz]. Parameter chosen to fit numerical solutions.""") + doc="""[Hz]. Parameter chosen to fit numerical solutions.""") d = arrays.FloatArray( label=":math:`d`", - default=numpy.array([154.0, ]), + default=numpy.array([0.154, ]), range=basic.Range(lo=0.0, hi=200.0), - doc="""[ms]. Parameter chosen to fit numerical solutions.""") + doc="""[s]. Parameter chosen to fit numerical solutions.""") gamma = arrays.FloatArray( label=r":math:`\gamma`", - default=numpy.array([0.0641, ]), + default=numpy.array([0.641, ]), range=basic.Range(lo=0.0, hi=1.0), - doc="""Kinetic parameter divided by 1000 to set the time scale in ms""") + doc="""Kinetic parameter""") tau_s = arrays.FloatArray( label=r":math:`\tau_S`", - default=numpy.array([100., ]), + default=numpy.array([60., ]), range=basic.Range(lo=50.0, hi=150.0), - doc="""Kinetic parameter. NMDA decay time constant.""") + doc="""[ms] Kinetic parameter. NMDA decay time constant.""") - tau_ampa = arrays.FloatArray( - label=r":math:`\tau_{ampa}`", + tau_noise = arrays.FloatArray( + label=r":math:`\tau_{noise}`", default=numpy.array([2., ]), range=basic.Range(lo=1.0, hi=10.0), - doc="""Kinetic parameter. AMPA decay time constant.""", + doc="""[ms] Noise decay time constant.""", order=-1) - J11 = arrays.FloatArray( - label=":math:`J_{11}`", - default=numpy.array([0.2609, ]), + Jll = arrays.FloatArray( + label=":math:`J_{ll}`", + default=numpy.array([0.3725, ]), range=basic.Range(lo=0.0, hi=1.0), doc="""Synaptic coupling""") - J22 = arrays.FloatArray( - label=":math:`J_{22}`", - default=numpy.array([0.2609, ]), + Jrr = arrays.FloatArray( + label=":math:`J_{rr}`", + default=numpy.array([0.3725, ]), range=basic.Range(lo=0.0, hi=1.0), doc="""Synaptic coupling""") - J12 = arrays.FloatArray( - label=":math:`J_{12}`", - default=numpy.array([0.0497, ]), + Jlr = arrays.FloatArray( + label=":math:`J_{lr}`", + default=numpy.array([0.1137, ]), range=basic.Range(lo=0.0, hi=1.0), doc="""Synaptic coupling""") - J21 = arrays.FloatArray( - label=":math:`J_{21}`", - default=numpy.array([0.0497, ]), + Jrl = arrays.FloatArray( + label=":math:`J_{rl}`", + default=numpy.array([0.1137, ]), range=basic.Range(lo=0.0, hi=1.0), doc="""Synaptic coupling""") + J_N = arrays.FloatArray( + label=":math:`J_{N}`", + default=numpy.array([0.1137, ]), + range=basic.Range(lo=0.0, hi=1.0), + doc="""External synaptic coupling""") + J_ext = arrays.FloatArray( label=":math:`J_{ext}`", - default=numpy.array([0.52, ]), - range=basic.Range(lo=0.0, hi=1.0), - doc="""Synaptic coupling""") + default=numpy.array([1.1e-3, ]), + range=basic.Range(lo=0.0, hi=0.01), + doc="""[nA/Hz] Synaptic coupling""") I_o = arrays.FloatArray( label=":math:`I_{o}`", - default=numpy.array([0.3255, ]), + default=numpy.array([0.3297, ]), range=basic.Range(lo=0.0, hi=1.0), doc="""Effective external input""") sigma_noise = arrays.FloatArray( label=r":math:`\sigma_{noise}`", - default=numpy.array([0.02, ]), + default=numpy.array([0.009, ]), range=basic.Range(lo=0.0, hi=1.0), doc="""Noise amplitude. Take this value into account for stochatic integration schemes.""") mu_o = arrays.FloatArray( label=r":math:`\mu_{0}`", - default=numpy.array([0.03, ]), - range=basic.Range(lo=0.0, hi=1.0), - doc="""Stimulus amplitude""") + default=numpy.array([30, ]), + range=basic.Range(lo=0.0, hi=50.0), + doc="""[Hz] Stimulus amplitude""") c = arrays.FloatArray( label=":math:`c`", @@ -198,22 +191,29 @@ class WongWang(models.Model): doc="""[%]. Percentage coherence or motion strength. This parameter comes from experiments in MT cells.""") + f = arrays.FloatArray( + label=":math:`f`", + default=numpy.array([1., ]), #0.45 + range=basic.Range(lo=0.0, hi=100.0), + doc=""" Gain of MT firing rates""") + state_variable_range = basic.Dict( label="State variable ranges [lo, hi]", - default={"S1": numpy.array([0.0, 0.3]), - "S2": numpy.array([0.0, 0.3])}, + default={"Sl": numpy.array([0., 1.]), + "Sr": numpy.array([0., 1.])}, doc="n/a", order=-1 ) variables_of_interest = basic.Enumerate( label="Variables watched by Monitors", - options=["S1", "S2"], - default=["S1"], + options=["Sl", "Sr"], + default=["Sl"], select_multiple=True, doc="""default state variables to be monitored""", order=10) + state_variables = ['Sl', 'Sr'] def __init__(self, **kwargs): """ @@ -221,17 +221,14 @@ def __init__(self, **kwargs): """ - #LOG.info('%s: initing...' % str(self)) - super(WongWang, self).__init__(**kwargs) - #self._state_variables = ["S1", "S2"] self._nvar = 2 self.cvar = numpy.array([0], dtype=numpy.int32) #derived parameters - self.I_1 = None - self.I_2 = None + self.I_mot_l = None + self.I_mot_r = None LOG.debug('%s: inited.' % repr(self)) @@ -243,38 +240,26 @@ def configure(self): def dfun(self, state_variables, coupling, local_coupling=0.0): r""" - These dynamic equations, taken from [WW_2006]_, ... - - ..math:: - - \frac{dS_{i}}{dt} &= - \frac{S_{i}}{\tau_{S}} + (1 - S_{i})\gamma H_{i} \\ - H_{i} &= \frac{a x_{i} - b}{1- \exp[-d (a x_{i} - b)]} \\ - x_{1} &= J11 S_{1} - J_{12}S_{2} + I_{0} + I_{1} \\ - x_{2} &= J22 S_{2} - J_{21}S_{1} + I_{0} + I_{2} \\ - I_{i} &= J_{ext} \mu_{0} \left( 1 \pm \frac{c}{100}\right) - - where :math:`i=` 1, 2 labels the selective population. - + The notation of those dynamic equations follows [WW_2007]. + Derivatives of s are multiplied by 0.001 constant to match ms time scale. """ # add global coupling? - s1 = state_variables[0, :] - s2 = state_variables[1, :] + sl = state_variables[0, :] + sr = state_variables[1, :] - c_0 = coupling[0] + c_0 = coupling[0, :] + lc_0_l = local_coupling * sl + lc_0_r = local_coupling * sr - x1 = self.J11 * s1 - self.J12 * s2 + self.I_o + self.I_1 - x2 = self.J21 * s2 - self.J22 * s1 + self.I_o + self.I_2 + I_l = self.Jll * sl - self.Jlr*sr + self.I_mot_l + self.I_o + self.J_N * c_0 + self.J_N * lc_0_l + I_r = self.Jrr * sr - self.Jrl*sl + self.I_mot_r + self.I_o + self.J_N * c_0 + self.J_N * lc_0_r - H1 = (self.a * x1 - self.b) / (1 - numpy.exp(-self.d * (self.a * x1 - \ - self.b))) - H2 = (self.a * x2 - self.b) / (1 - numpy.exp(-self.d * (self.a * x2 - \ - self.b))) + r = lambda I_i: (self.a*I_i - self.b)*1./(1 - numpy.exp(-self.d*(self.a*I_i - self.b))) - ds1 = - (s1 / self.tau_s) + (1 - s1) * H1 * self.gamma - ds2 = - (s2 / self.tau_s) + (1 - s2) * H2 * self.gamma + ds1 = -sl*1./ self.tau_s + (1 - sl) * self.gamma * r(I_l) * 0.001 # to ms + ds2 = -sr*1./ self.tau_s + (1 - sr) * self.gamma * r(I_r) * 0.001 # to ms derivative = numpy.array([ds1, ds2]) - return derivative @@ -282,9 +267,12 @@ def update_derived_parameters(self): """ Derived parameters """ - - self.I_1 = self.J_ext * self.mu_o * (1 + self.c / 100) - self.I_2 = self.J_ext * self.mu_o * (1 - self.c / 100) + # Additional parameter g_stim introduced that controls I_mot strength + self.I_mot_l = self.J_ext * self.mu_o * (1 + self.f * self.c *1. / 100) + self.I_mot_r = self.J_ext * self.mu_o * (1 - self.f * self.c *1. / 100) + if len(self.I_mot_l) > 1: + self.I_mot_l = numpy.expand_dims(self.I_mot_l, -1) + self.I_mot_r = numpy.expand_dims(self.I_mot_r, -1) @@ -298,6 +286,8 @@ def update_derived_parameters(self): #Initialise Models in their default state: WW = WongWang() + WW.c = 11 + WW.configure() LOG.info("Model initialised in its default state without error...") @@ -307,7 +297,7 @@ def update_derived_parameters(self): from tvb.simulator.plot.phase_plane_interactive import PhasePlaneInteractive import tvb.simulator.integrators - INTEGRATOR = tvb.simulator.integrators.HeunDeterministic(dt=2**-5) + INTEGRATOR = tvb.simulator.integrators.HeunDeterministic(dt=0.1) ppi_fig = PhasePlaneInteractive(model=WW, integrator=INTEGRATOR) ppi_fig.show() From dbb653deefc04e4181a0e357a85a651a877e6c23 Mon Sep 17 00:00:00 2001 From: Come Le Breton Date: Sat, 9 Mar 2019 17:40:45 +0100 Subject: [PATCH 37/49] Fix misplaced parenthesis. isinstance function was here to check if time_series was either EEG or MEG, but because the parenthesis was misplaced, it wouldn't work. --- tvb/simulator/plot/timeseries_interactive.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvb/simulator/plot/timeseries_interactive.py b/tvb/simulator/plot/timeseries_interactive.py index 561872b8c..f92dae98f 100644 --- a/tvb/simulator/plot/timeseries_interactive.py +++ b/tvb/simulator/plot/timeseries_interactive.py @@ -175,7 +175,7 @@ def configure(self): (not self.time_series.connectivity is None)): self.labels = self.time_series.connectivity.region_labels elif (isinstance(self.time_series, (time_series_datatypes.TimeSeriesEEG, - time_series_datatypes.TimeSeriesMEG) and (not self.time_series.sensors is None))): + time_series_datatypes.TimeSeriesMEG)) and (not self.time_series.sensors is None)): self.labels = self.time_series.sensors.labels else: self.labels = ["channel_%0.2d"%k for k in range(self.nsrs)] From 4f995756a470aa0188a50851d07e4c51af427af1 Mon Sep 17 00:00:00 2001 From: liadomide Date: Wed, 13 Mar 2019 12:15:18 +0000 Subject: [PATCH 38/49] HBP-8 Remove contributor setup. Update documentation also git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8800 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/basic/config/environment.py | 1 - tvb/basic/config/profile_settings.py | 22 +++------------------- tvb/basic/config/stored.py | 1 - 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/tvb/basic/config/environment.py b/tvb/basic/config/environment.py index 09ca896e1..f47cc6620 100644 --- a/tvb/basic/config/environment.py +++ b/tvb/basic/config/environment.py @@ -42,7 +42,6 @@ class Environment(object): - IS_WORK_IN_PROGRESS = os.environ.get('TVB_WIP', False) == 'True' def is_framework_present(self): """ diff --git a/tvb/basic/config/profile_settings.py b/tvb/basic/config/profile_settings.py index e0871cbbc..61c339316 100644 --- a/tvb/basic/config/profile_settings.py +++ b/tvb/basic/config/profile_settings.py @@ -68,7 +68,6 @@ def __init__(self, web_enabled=True): self.TVB_STORAGE = self.manager.get_attribute(stored.KEY_STORAGE, self.FIRST_RUN_STORAGE, unicode) self.TVB_LOG_FOLDER = os.path.join(self.TVB_STORAGE, "logs") self.TVB_TEMP_FOLDER = os.path.join(self.TVB_STORAGE, "TEMP") - self.TVB_PATH = self.manager.get_attribute(stored.KEY_TVB_PATH, '') self.env = Environment() self.cluster = ClusterSettings(self.manager) @@ -151,8 +150,7 @@ def initialize_for_deployment(self): library_folder = self.env.get_library_folder(self.BIN_FOLDER) if self.env.is_windows_deployment(): - # Add self.TVB_PATH as first in PYTHONPATH so we can find TVB there in case of GIT contributors - self.env.setup_python_path(self.TVB_PATH, library_folder, os.path.join(library_folder, 'lib-tk')) + self.env.setup_python_path(library_folder, os.path.join(library_folder, 'lib-tk')) self.env.append_to_path(library_folder) self.env.setup_tk_tcl_environ(library_folder) @@ -164,13 +162,13 @@ def initialize_for_deployment(self): tcl_root = os.path.dirname(os.path.dirname(os.path.dirname(library_folder))) self.env.setup_tk_tcl_environ(tcl_root) - self.env.setup_python_path(self.TVB_PATH, library_folder, os.path.join(library_folder, 'site-packages.zip'), + self.env.setup_python_path(library_folder, os.path.join(library_folder, 'site-packages.zip'), os.path.join(library_folder, 'lib-dynload')) if self.env.is_linux_deployment(): # Note that for the Linux package some environment variables like LD_LIBRARY_PATH, # LD_RUN_PATH, PYTHONPATH and PYTHONHOME are set also in the startup scripts. - self.env.setup_python_path(self.TVB_PATH, library_folder, os.path.join(library_folder, 'lib-tk')) + self.env.setup_python_path(library_folder, os.path.join(library_folder, 'lib-tk')) self.env.setup_tk_tcl_environ(library_folder) ### Correctly set MatplotLib Path, before start. @@ -181,20 +179,6 @@ def initialize_for_deployment(self): except: pass - if self.TVB_PATH: - # In case of contributor setup, we want to make sure that all dev files are loaded first, so - # we need to reload all tvb related modules, since any call done with - # 'python -m ...' will consider the current folder as the first to search in. - sys.path = os.environ.get("PYTHONPATH", "").split(os.pathsep) + sys.path - for key in sys.modules.keys(): - if (key.startswith("tvb.") and sys.modules[key] and not 'lab' in key and - not key.startswith("tvb.basic.profile") and not 'profile_settings' in key): - try: - importlib.reload(sys.modules[key]) - except LibraryImportError: - pass - - class LibrarySettingsProfile(BaseSettingsProfile): diff --git a/tvb/basic/config/stored.py b/tvb/basic/config/stored.py index 16f9062b0..27044e751 100644 --- a/tvb/basic/config/stored.py +++ b/tvb/basic/config/stored.py @@ -42,7 +42,6 @@ KEY_ADMIN_NAME = 'ADMINISTRATOR_NAME' KEY_ADMIN_PWD = 'ADMINISTRATOR_PASSWORD' KEY_ADMIN_EMAIL = 'ADMINISTRATOR_EMAIL' -KEY_TVB_PATH = 'TVB_PATH' KEY_STORAGE = 'TVB_STORAGE' KEY_MAX_DISK_SPACE_USR = 'USR_DISK_SPACE' # During the introspection phase, it is checked if either Matlab or From 45cd3b901af03c6582d5e77766db57bddb49c51d Mon Sep 17 00:00:00 2001 From: liadomide Date: Tue, 7 May 2019 11:17:51 +0000 Subject: [PATCH 39/49] Fix typo in function name git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8804 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/simulator/models/oscillator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvb/simulator/models/oscillator.py b/tvb/simulator/models/oscillator.py index 07024458f..df9299a5b 100644 --- a/tvb/simulator/models/oscillator.py +++ b/tvb/simulator/models/oscillator.py @@ -627,7 +627,7 @@ def dfun(self, x, c, local_coupling=0.0): x_ = x.reshape(x.shape[:-1]).T c_ = c.reshape(c.shape[:-1]).T lc_0 = local_coupling * x[0, :, 0] - deriv = _numba_dfun(x_, c_, self.a, self.omega, lc_0) + deriv = _numba_dfun_supHopf(x_, c_, self.a, self.omega, lc_0) return deriv.T[..., numpy.newaxis] From 42dc3ce4f8fd0e9ed70424f4c9cabb412e62cbfb Mon Sep 17 00:00:00 2001 From: liadomide Date: Wed, 8 May 2019 17:29:02 +0000 Subject: [PATCH 40/49] Add JulieCB and John Griffiths to the authors list git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8809 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- AUTHORS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AUTHORS b/AUTHORS index 0d4d44e1a..6edd0fe76 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,9 +1,11 @@ #Authors of The Virtual Brain Library Andrei, Mihai + Courtiol, Julie Domide, Lia Fousek, Jan Golos, Mathieu + Griffiths, John Hu, Wenyan Jirsa, Viktor Knock, Stuart A. From d105cdebe520068c37b2aec35f98d28f157b8d07 Mon Sep 17 00:00:00 2001 From: liadomide Date: Wed, 8 May 2019 18:48:27 +0000 Subject: [PATCH 41/49] TVB-2392 Extend license copyright year git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8812 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 385bae9d6..db9194ffb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ TheVirtualBrain is a brain-simulation software. See also http://www.thevirtualbrain.org -(c) 2012-2017, Baycrest Centre for Geriatric Care ("Baycrest") and others +(c) 2012-2019, Baycrest Centre for Geriatric Care ("Baycrest") and others This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either From 68c5d3b70d5652cf2e0d36a4dd8f90e28845d456 Mon Sep 17 00:00:00 2001 From: liadomide Date: Wed, 8 May 2019 19:54:33 +0000 Subject: [PATCH 42/49] TVB-2392 Add one generic README file. Remove deprecated text from tvb-library README file git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8813 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- README.rst | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/README.rst b/README.rst index b867d5dd3..25d7b5b7a 100644 --- a/README.rst +++ b/README.rst @@ -52,43 +52,19 @@ order to work with the same data. In TVB architecture, that "common language" is represented by Data Types. TVB Data Types declarations are located in this package. -Most of the datatypes here have a diamond like inheritance structure of -the following form: - -:: - - DataTypeData - | - / \\ - DataTypeFramework DataTypeScientific - \ / - | - DataType - - -The DataTypeData holds the actual structure of the datatype. -DataTypeScientific holds any methods required from a scientific point of -view. DataTypeFramework should just be ignored from a library user point -of view as it holds framework related methods and will be removed -altogether in the near future. DataType just brings all the above -together and is the class you should actually use in your code. - - tvb.simulator The Simulation Component is the most important component in The Virtual Brain solution, as it is the component responsible for all the scientific computation related to brain models and data. -You can find various demos of using the simulator under -tvb/simulator/demos as well as some nice tutorials under -tvb/simulator/doc/tutorials/ . +You can find various demos of using the simulator here: +http://docs.thevirtualbrain.org/demos/Demos.html . - tvb.analyzers Holds modules that can run various analysis of data resulted from the -simulator. There are a few demos which use the PCA analyzer like -tvb/simulator/demos/pca\_analyse\_view\_region and -tvb/simulator/demos/pca\_analyse\_view\_surface . TVB is not strong in +simulator. TVB is not strong in doing data analysis, we barely have a minimum set of analyzers for immediate needs. From 3a55ad6e0e94e1436be6b8d0dd8bad11fc874309 Mon Sep 17 00:00:00 2001 From: liadomide Date: Thu, 16 May 2019 17:20:18 +0000 Subject: [PATCH 43/49] TVB-2412 manually commit tvb.version git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8846 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/basic/config/tvb.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvb/basic/config/tvb.version b/tvb/basic/config/tvb.version index 669191be2..c8bc34e74 100644 --- a/tvb/basic/config/tvb.version +++ b/tvb/basic/config/tvb.version @@ -1 +1 @@ -Revision: 8745 \ No newline at end of file +Revision: 8850 \ No newline at end of file From 7ece4c45d73446ed4a65c80cae49c84f16e39c46 Mon Sep 17 00:00:00 2001 From: liadomide Date: Thu, 16 May 2019 18:10:48 +0000 Subject: [PATCH 44/49] TVB-2411 Cleanup console from TVB Distribution on Linux git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8847 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- tvb/basic/config/environment.py | 2 +- tvb/basic/config/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tvb/basic/config/environment.py b/tvb/basic/config/environment.py index f47cc6620..83b423c4e 100644 --- a/tvb/basic/config/environment.py +++ b/tvb/basic/config/environment.py @@ -73,7 +73,7 @@ def is_distribution(): return False try: - _proc = Popen(["svnversion", "."], stdout=PIPE) + _proc = Popen(["svnversion", "."], stdout=PIPE, stderr=PIPE) version = VersionSettings.parse_svn_version(_proc.communicate()[0]) if version: # usage from SVN diff --git a/tvb/basic/config/settings.py b/tvb/basic/config/settings.py index a809039e8..0481530ec 100644 --- a/tvb/basic/config/settings.py +++ b/tvb/basic/config/settings.py @@ -88,7 +88,7 @@ def SVN_VERSION(self): return os.environ[svn_variable] try: - _proc = Popen(["svnversion", "."], stdout=PIPE) + _proc = Popen(["svnversion", "."], stdout=PIPE, stderr=PIPE) return self.parse_svn_version(_proc.communicate()[0]) except Exception: pass From f83d2ebd2f3a510f6cb899ddcec3b44045bf4d9c Mon Sep 17 00:00:00 2001 From: liadomide Date: Sat, 18 May 2019 17:00:57 +0000 Subject: [PATCH 45/49] TVB-2465 Update version number git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8849 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- setup.py | 2 +- tvb/basic/config/settings.py | 2 +- tvb/basic/config/tvb.version | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index a6f6eefd2..e633f041b 100644 --- a/setup.py +++ b/setup.py @@ -40,7 +40,7 @@ import setuptools -LIBRARY_VERSION = "1.5.6" +LIBRARY_VERSION = "1.5.7" TVB_TEAM = "Stuart Knock, Marmaduke Woodman, Paula Sanz Leon, Laurent Pezard, Viktor Jirsa" diff --git a/tvb/basic/config/settings.py b/tvb/basic/config/settings.py index 0481530ec..0f724c9bd 100644 --- a/tvb/basic/config/settings.py +++ b/tvb/basic/config/settings.py @@ -47,7 +47,7 @@ class VersionSettings(object): """ # Current release number - BASE_VERSION = "1.5.6" + BASE_VERSION = "1.5.7" # Current DB version. Increment this and create a new xxx_update_db.py migrate script DB_STRUCTURE_VERSION = 17 diff --git a/tvb/basic/config/tvb.version b/tvb/basic/config/tvb.version index c8bc34e74..888eb0a74 100644 --- a/tvb/basic/config/tvb.version +++ b/tvb/basic/config/tvb.version @@ -1 +1 @@ -Revision: 8850 \ No newline at end of file +Revision: 8852 \ No newline at end of file From 4ac77bb1f2b7f8fe06dc4c6d6d576e2c24b93164 Mon Sep 17 00:00:00 2001 From: liadomide Date: Sun, 19 May 2019 19:42:34 +0000 Subject: [PATCH 46/49] TVB-2465 Extend PATH in Windows distribution for correctly loading everything. Update version number to 1.5.8 git-svn-id: https://repo.thevirtualbrain.org/svn/tvb/trunk/scientific_library@8852 1c0e02f0-7929-43c0-8fb3-3293bf43b0d1 --- setup.py | 2 +- tvb/basic/config/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index e633f041b..8cdb8b8c6 100644 --- a/setup.py +++ b/setup.py @@ -40,7 +40,7 @@ import setuptools -LIBRARY_VERSION = "1.5.7" +LIBRARY_VERSION = "1.5.8" TVB_TEAM = "Stuart Knock, Marmaduke Woodman, Paula Sanz Leon, Laurent Pezard, Viktor Jirsa" diff --git a/tvb/basic/config/settings.py b/tvb/basic/config/settings.py index 0f724c9bd..25a7d9283 100644 --- a/tvb/basic/config/settings.py +++ b/tvb/basic/config/settings.py @@ -47,7 +47,7 @@ class VersionSettings(object): """ # Current release number - BASE_VERSION = "1.5.7" + BASE_VERSION = "1.5.8" # Current DB version. Increment this and create a new xxx_update_db.py migrate script DB_STRUCTURE_VERSION = 17 From 4d48f99cf3763803bf9793c50fa9f6fb36ce2b0d Mon Sep 17 00:00:00 2001 From: "mihai.andrei" Date: Mon, 20 May 2019 19:01:14 +0300 Subject: [PATCH 47/49] Add Zerlaut model to the discoverable models --- tvb/simulator/models/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tvb/simulator/models/__init__.py b/tvb/simulator/models/__init__.py index 0208afde0..328b6e1e4 100644 --- a/tvb/simulator/models/__init__.py +++ b/tvb/simulator/models/__init__.py @@ -49,4 +49,5 @@ from .linear import Linear from .hopfield import Hopfield from .epileptor import Epileptor -from .epileptorcodim3 import EpileptorCodim3, EpileptorCodim3SlowMod \ No newline at end of file +from .epileptorcodim3 import EpileptorCodim3, EpileptorCodim3SlowMod +from .Zerlaut import Zerlaut_adaptation_first_order, Zerlaut_adaptation_second_order \ No newline at end of file From a0e2473c1aaf8a15ce8f4d3f3046c3734d34bc2d Mon Sep 17 00:00:00 2001 From: liadomide Date: Tue, 2 Jul 2019 15:37:34 +0300 Subject: [PATCH 48/49] Add ReducedWongWangExcIOInhI model class as provided by Dionysios @Charite --- tvb/simulator/models/__init__.py | 6 +- .../models/wong_wang_exc_io_inh_i.py | 312 ++++++++++++++++++ tvb/tests/library/simulator/models_test.py | 7 + 3 files changed, 323 insertions(+), 2 deletions(-) create mode 100644 tvb/simulator/models/wong_wang_exc_io_inh_i.py diff --git a/tvb/simulator/models/__init__.py b/tvb/simulator/models/__init__.py index 328b6e1e4..af46127fd 100644 --- a/tvb/simulator/models/__init__.py +++ b/tvb/simulator/models/__init__.py @@ -46,8 +46,10 @@ from .oscillator import Generic2dOscillator, Kuramoto from .larter_breakspear import LarterBreakspear from .wong_wang import ReducedWongWang +from .wong_wang_exc_io_inh_i import ReducedWongWangExcIOInhI from .linear import Linear from .hopfield import Hopfield -from .epileptor import Epileptor +from .epileptor import Epileptor, Epileptor2D +from .JCepileptor import JC_Epileptor from .epileptorcodim3 import EpileptorCodim3, EpileptorCodim3SlowMod -from .Zerlaut import Zerlaut_adaptation_first_order, Zerlaut_adaptation_second_order \ No newline at end of file +from .Zerlaut import Zerlaut_adaptation_first_order, Zerlaut_adaptation_second_order diff --git a/tvb/simulator/models/wong_wang_exc_io_inh_i.py b/tvb/simulator/models/wong_wang_exc_io_inh_i.py new file mode 100644 index 000000000..dea5ef53a --- /dev/null +++ b/tvb/simulator/models/wong_wang_exc_io_inh_i.py @@ -0,0 +1,312 @@ +# -*- coding: utf-8 -*- +# +# +# TheVirtualBrain-Scientific Package. This package holds all simulators, and +# analysers necessary to run brain-simulations. You can use it stand alone or +# in conjunction with TheVirtualBrain-Framework Package. See content of the +# documentation-folder for more details. See also http://www.thevirtualbrain.org +# +# (c) 2012-2017, Baycrest Centre for Geriatric Care ("Baycrest") and others +# +# This program is free software: you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software Foundation, +# either version 3 of the License, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public License for more details. +# You should have received a copy of the GNU General Public License along with this +# program. If not, see . +# +# +# CITATION: +# When using The Virtual Brain for scientific publications, please cite it as follows: +# +# Paula Sanz Leon, Stuart A. Knock, M. Marmaduke Woodman, Lia Domide, +# Jochen Mersmann, Anthony R. McIntosh, Viktor Jirsa (2013) +# The Virtual Brain: a simulator of primate brain network dynamics. +# Frontiers in Neuroinformatics (7:10. doi: 10.3389/fninf.2013.00010) + +""" +Models based on Wong-Wang's work. +This one is meant to be used by TVB, with an excitatory and an inhibitory population, mutually coupled. +Following Deco et al 2014. + +.. moduleauthor:: Dionysios Perdikis +""" + +from numba import guvectorize, float64 +from tvb.simulator.models.base import numpy, basic, arrays, ModelNumbaDfun, LOG + + +@guvectorize([(float64[:],)*21], '(n),(m)' + ',()'*18 + '->(n)', nopython=True) +def _numba_dfun(S, c, ae, be, de, ge, te, wp, we, jn, ai, bi, di, gi, ti, wi, ji, g, l, io, dx): + "Gufunc for reduced Wong-Wang model equations." + + cc = g[0]*jn[0]*c[0] + + if S[0] < 0.0: + S_e = 0.0 # - S[0] # TODO: clarify the boundary to be reflective or saturated!!! + elif S[0] > 1.0: + S_e = 1.0 # - S[0] # TODO: clarify the boundary to be reflective or saturated!!! + else: + S_e = S[0] + + if S[1] < 0.0: + S_i = 0.0 # - S[1] TODO: clarify the boundary to be reflective or saturated!!! + elif S[1] > 1.0: + S_i = 1.0 # - S[1] TODO: clarify the boundary to be reflective or saturated!!! + else: + S_i = S[1] + + jnSe = jn[0]*S_e + x = wp[0]*jnSe - ji[0]*S_i + we[0]*io[0] + cc + x = ae[0]*x - be[0] + h = x / (1 - numpy.exp(-de[0]*x)) + dx[0] = - (S_e / te[0]) + (1.0 - S_e) * h * ge[0] + + x = jnSe - S_i + wi[0]*io[0] + l[0]*cc + x = ai[0]*x - bi[0] + h = x / (1 - numpy.exp(-di[0]*x)) + dx[1] = - (S_i / ti[0]) + h * gi[0] + + +class ReducedWongWangExcIOInhI(ModelNumbaDfun): + r""" + .. [WW_2006] Kong-Fatt Wong and Xiao-Jing Wang, *A Recurrent Network + Mechanism of Time Integration in Perceptual Decisions*. + Journal of Neuroscience 26(4), 1314-1328, 2006. + + .. [DPA_2014] Deco Gustavo, Ponce Alvarez Adrian, Patric Hagmann, + Gian Luca Romani, Dante Mantini, and Maurizio Corbetta. *How Local + Excitation–Inhibition Ratio Impacts the Whole Brain Dynamics*. + The Journal of Neuroscience 34(23), 7886 –7898, 2014. + + + + .. automethod:: ReducedWongWangExcIOInhI.__init__ + + Equations taken from [DPA_2013]_ , page 11242 + + .. math:: + x_{ek} &= w_p\,J_N \, S_{ek} - J_iS_{ik} + W_eI_o + GJ_N \mathbf\Gamma(S_{ek}, S_{ej}, u_{kj}),\\ + H(x_{ek}) &= \dfrac{a_ex_{ek}- b_e}{1 - \exp(-d_e(a_ex_{ek} -b_e))},\\ + \dot{S}_{ek} &= -\dfrac{S_{ek}}{\tau_e} + (1 - S_{ek}) \, \gammaH(x_{ek}) \, + + x_{ik} &= J_N \, S_{ek} - S_{ik} + W_iI_o + \lambdaGJ_N \mathbf\Gamma(S_{ik}, S_{ej}, u_{kj}),\\ + H(x_{ik}) &= \dfrac{a_ix_{ik} - b_i}{1 - \exp(-d_i(a_ix_{ik} -b_i))},\\ + \dot{S}_{ik} &= -\dfrac{S_{ik}}{\tau_i} + \gamma_iH(x_{ik}) \, + + """ + _ui_name = "Reduced Wong-Wang with Excitatory and Inhibitory Coupled Populations" + ui_configurable_parameters = ['a_e', 'b_e', 'd_e', 'gamma_e', 'tau_e', 'W_e', 'w_p', 'J_N', + 'a_i', 'b_i', 'd_i', 'gamma_i', 'tau_i', 'W_i', 'J_i', + 'I_o', 'G', 'lamda'] + + # Define traited attributes for this model, these represent possible kwargs. + + a_e = arrays.FloatArray( + label=":math:`a_e`", + default=numpy.array([310., ]), + range=basic.Range(lo=0., hi=500., step=1.), + doc="[n/C]. Excitatory population input gain parameter, chosen to fit numerical solutions.", + order=1) + + b_e = arrays.FloatArray( + label=":math:`b_e`", + default=numpy.array([125., ]), + range=basic.Range(lo=0., hi=200., step=1.), + doc="[Hz]. Excitatory population input shift parameter chosen to fit numerical solutions.", + order=2) + + d_e = arrays.FloatArray( + label=":math:`d_e`", + default=numpy.array([0.160, ]), + range=basic.Range(lo=0.0, hi=0.2, step=0.001), + doc="""[s]. Excitatory population input scaling parameter chosen to fit numerical solutions.""", + order=3) + + gamma_e = arrays.FloatArray( + label=r":math:`\gamma_e`", + default=numpy.array([0.641/1000, ]), + range=basic.Range(lo=0.0, hi=1.0/1000, step=0.01/1000), + doc="""Excitatory population kinetic parameter""", + order=4) + + tau_e = arrays.FloatArray( + label=r":math:`\tau_e`", + default=numpy.array([100., ]), + range=basic.Range(lo=50., hi=150., step=1.), + doc="""[ms]. Excitatory population NMDA decay time constant.""", + order=5) + + w_p = arrays.FloatArray( + label=r":math:`w_p`", + default=numpy.array([1.4, ]), + range=basic.Range(lo=0.0, hi=2.0, step=0.01), + doc="""Excitatory population recurrence weight""", + order=6) + + J_N = arrays.FloatArray( + label=r":math:`J_{N}`", + default=numpy.array([0.15, ]), + range=basic.Range(lo=0.001, hi=0.5, step=0.001), + doc="""[nA] NMDA current""", + order=7) + + W_e = arrays.FloatArray( + label=r":math:`W_e`", + default=numpy.array([1.0, ]), + range=basic.Range(lo=0.0, hi=2.0, step=0.01), + doc="""Excitatory population external input scaling weight""", + order=8) + + a_i = arrays.FloatArray( + label=":math:`a_i`", + default=numpy.array([615., ]), + range=basic.Range(lo=0., hi=1000., step=1.), + doc="[n/C]. Inhibitory population input gain parameter, chosen to fit numerical solutions.", + order=9) + + b_i = arrays.FloatArray( + label=":math:`b_i`", + default=numpy.array([177.0, ]), + range=basic.Range(lo=0.0, hi=200.0, step=1.0), + doc="[Hz]. Inhibitory population input shift parameter chosen to fit numerical solutions.", + order=10) + + d_i = arrays.FloatArray( + label=":math:`d_i`", + default=numpy.array([0.087, ]), + range=basic.Range(lo=0.0, hi=0.2, step=0.001), + doc="""[s]. Inhibitory population input scaling parameter chosen to fit numerical solutions.""", + order=11) + + gamma_i = arrays.FloatArray( + label=r":math:`\gamma_i`", + default=numpy.array([1.0/1000, ]), + range=basic.Range(lo=0.0, hi=2.0/1000, step=0.01/1000), + doc="""Inhibitory population kinetic parameter""", + order=12) + + tau_i = arrays.FloatArray( + label=r":math:`\tau_i`", + default=numpy.array([10., ]), + range=basic.Range(lo=50., hi=150., step=1.0), + doc="""[ms]. Inhibitory population NMDA decay time constant.""", + order=13) + + J_i = arrays.FloatArray( + label=r":math:`J_{i}`", + default=numpy.array([1.0, ]), + range=basic.Range(lo=0.001, hi=2.0, step=0.001), + doc="""[nA] Local inhibitory current""", + order=14) + + W_i = arrays.FloatArray( + label=r":math:`W_i`", + default=numpy.array([0.7, ]), + range=basic.Range(lo=0.0, hi=1.0, step=0.01), + doc="""Inhibitory population external input scaling weight""", + order=15) + + I_o = arrays.FloatArray( + label=":math:`I_{o}`", + default=numpy.array([0.382, ]), + range=basic.Range(lo=0.0, hi=1.0, step=0.001), + doc="""[nA]. Effective external input""", + order=16) + + G = arrays.FloatArray( + label=":math:`G`", + default=numpy.array([2.0, ]), + range=basic.Range(lo=0.0, hi=10.0, step=0.01), + doc="""Global coupling scaling""", + order=17) + + lamda = arrays.FloatArray( + label=":math:`\lambda`", + default=numpy.array([0.0, ]), + range=basic.Range(lo=0.0, hi=1.0, step=0.01), + doc="""Inhibitory global coupling scaling""", + order=18) + + state_variable_range = basic.Dict( + label="State variable ranges [lo, hi]", + default={"S_e": numpy.array([0.0, 1.0]), "S_i": numpy.array([0.0, 1.0])}, + doc="Population firing rate", + order=22 + ) + + variables_of_interest = basic.Enumerate( + label="Variables watched by Monitors", + options=['S_e', 'S_i'], + default=['S_e', 'S_i'], + select_multiple=True, + doc="""default state variables to be monitored""", + order=23) + + state_variables = ['S_e', 'S_i'] + _nvar = 2 + cvar = numpy.array([0], dtype=numpy.int32) + + def configure(self): + """ """ + super(ReducedWongWangExcIOInhI, self).configure() + self.update_derived_parameters() + + def _numpy_dfun(self, state_variables, coupling, local_coupling=0.0): + r""" + Equations taken from [DPA_2013]_ , page 11242 + + .. math:: + x_{ek} &= w_p\,J_N \, S_{ek} - J_iS_{ik} + W_eI_o + GJ_N \mathbf\Gamma(S_{ek}, S_{ej}, u_{kj}),\\ + H(x_{ek}) &= \dfrac{a_ex_{ek}- b_e}{1 - \exp(-d_e(a_ex_{ek} -b_e))},\\ + \dot{S}_{ek} &= -\dfrac{S_{ek}}{\tau_e} + (1 - S_{ek}) \, \gammaH(x_{ek}) \, + + x_{ik} &= J_N \, S_{ek} - S_{ik} + W_iI_o + \lambdaGJ_N \mathbf\Gamma(S_{ik}, S_{ej}, u_{kj}),\\ + H(x_{ik}) &= \dfrac{a_ix_{ik} - b_i}{1 - \exp(-d_i(a_ix_{ik} -b_i))},\\ + \dot{S}_{ik} &= -\dfrac{S_{ik}}{\tau_i} + \gamma_iH(x_{ik}) \, + + """ + S = state_variables[:, :] + S[S < 0] = 0. + S[S > 1] = 1. + + c_0 = coupling[0, :] + + # if applicable + lc_0 = local_coupling * S[0] + + coupling = self.G * self.J_N * (c_0 + lc_0) + + J_N_S_e = self.J_N * S[0] + + x_e = self.w_p * J_N_S_e - self.J_i * S[1] + self.W_e * self.I_o + coupling + + x_e = self.a_e * x_e - self.b_e + H_e = x_e / (1 - numpy.exp(-self.d_e * x_e)) + + dS_e = - (S[0] / self.tau_e) + (1 - S[0]) * H_e * self.gamma_e + + x_i = J_N_S_e - S[1] + self.W_i * self.I_o + self.lamda * coupling + + x_i = self.a_i * x_i - self.b_i + H_i = x_i / (1 - numpy.exp(-self.d_i * x_i)) + + dS_i = - (S[1] / self.tau_i) + H_i * self.gamma_i + + derivative = numpy.array([dS_e, dS_i]) + + return derivative + + def dfun(self, x, c, local_coupling=0.0, **kwargs): + x_ = x.reshape(x.shape[:-1]).T + c_ = c.reshape(c.shape[:-1]).T + local_coupling * x[0] + deriv = _numba_dfun(x_, c_, + self.a_e, self.b_e, self.d_e, self.gamma_e, self.tau_e, + self.w_p, self.W_e, self.J_N, + self.a_i, self.b_i, self.d_i, self.gamma_i, self.tau_i, + self.W_i, self.J_i, + self.G, self.lamda, self.I_o) + return deriv.T[..., numpy.newaxis] + diff --git a/tvb/tests/library/simulator/models_test.py b/tvb/tests/library/simulator/models_test.py index be504147a..13093c0f4 100644 --- a/tvb/tests/library/simulator/models_test.py +++ b/tvb/tests/library/simulator/models_test.py @@ -173,3 +173,10 @@ def test_larter(self): def test_linear(self): model = models.Linear() self._validate_initialization(model, 1) + + def test_ww(self): + model = models.ReducedWongWang() + self._validate_initialization(model, 1) + + model = models.ReducedWongWangExcIOInhI() + self._validate_initialization(model, 2) From 184a000cfbf2e6c391dee954892ccbd144458449 Mon Sep 17 00:00:00 2001 From: liadomide Date: Tue, 2 Jul 2019 15:53:12 +0300 Subject: [PATCH 49/49] No need to automethod --- tvb/simulator/models/wong_wang.py | 3 --- tvb/simulator/models/wong_wang_exc_io_inh_i.py | 3 --- 2 files changed, 6 deletions(-) diff --git a/tvb/simulator/models/wong_wang.py b/tvb/simulator/models/wong_wang.py index 0944d8f87..a55112f97 100644 --- a/tvb/simulator/models/wong_wang.py +++ b/tvb/simulator/models/wong_wang.py @@ -61,9 +61,6 @@ class ReducedWongWang(ModelNumbaDfun): Neuroscience 32(27), 11239-11252, 2013. - - .. automethod:: ReducedWongWang.__init__ - Equations taken from [DPA_2013]_ , page 11242 .. math:: diff --git a/tvb/simulator/models/wong_wang_exc_io_inh_i.py b/tvb/simulator/models/wong_wang_exc_io_inh_i.py index dea5ef53a..35213444b 100644 --- a/tvb/simulator/models/wong_wang_exc_io_inh_i.py +++ b/tvb/simulator/models/wong_wang_exc_io_inh_i.py @@ -82,9 +82,6 @@ class ReducedWongWangExcIOInhI(ModelNumbaDfun): The Journal of Neuroscience 34(23), 7886 –7898, 2014. - - .. automethod:: ReducedWongWangExcIOInhI.__init__ - Equations taken from [DPA_2013]_ , page 11242 .. math::
%s%s
%s%s%s
%s%s%s