# -*- 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-2023, 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 <http://www.gnu.org/licenses/>.
#
#
# CITATION:
# When using The Virtual Brain for scientific publications, please cite it as explained here:
# https://www.thevirtualbrain.org/tvb/zwei/neuroscience-publications
#
#
"""
A collection of noise related classes and functions.
Specific noises inherit from the abstract class Noise
.. moduleauthor:: Stuart A. Knock <Stuart@tvb.invalid>
.. moduleauthor:: Paula Sanz Leon <Paula@tvb.invalid>
.. moduleauthor:: Noelia Montejo <Noelia@tvb.invalid>
"""
import abc
import numpy
from tvb.basic.neotraits.api import HasTraits, Attr, NArray, Range, Int, Float
from tvb.datatypes import equations
from .common import simple_gen_astr
[docs]class Noise(HasTraits):
"""
Defines a base class for noise. Specific noises are derived from this class
for use in stochastic integrations.
.. [KloedenPlaten_1995] Kloeden and Platen, Springer 1995, *Numerical
solution of stochastic differential equations.*
.. [ManellaPalleschi_1989] Manella, R. and Palleschi V., *Fast and precise
algorithm for computer simulation of stochastic differential equations*,
Physical Review A, Vol. 40, Number 6, 1989. [3381-3385]
.. [Mannella_2002] Mannella, R., *Integration of Stochastic Differential
Equations on a Computer*, Int J. of Modern Physics C 13(9): 1177--1194,
2002.
.. [FoxVemuri_1988] Fox, R., Gatland, I., Rot, R. and Vemuri, G., * Fast ,
accurate algorithm for simulation of exponentially correlated colored
noise*, Physical Review A, Vol. 38, Number 11, 1988. [5938-5940]
.. #Currently there seems to be a clash betwen traits and autodoc, autodoc
.. #can't find the methods of the class, the class specific names below get
.. #us around this...
.. automethod:: Noise.__init__
.. automethod:: Noise.configure_white
.. automethod:: Noise.generate
.. automethod:: Noise.white
.. automethod:: Noise.coloured
"""
# NOTE: nsig is not declared here because we use this class directly as the
# inital conditions noise source, and in that use the job of nsig is
# filled by the state_variable_range attribute of the Model.
ntau = Float(
label=r":math:`\tau`",
required=True,
default=0.0,
# range=basic.Range(lo=0.0, hi=20.0, step=1.0), #mh todo support domains for simple floats?
doc="""The noise correlation time""")
noise_seed = Int(
default=42,
doc="A random seed used to initialise the random_stream if it is missing."
)
random_stream = Attr(
field_type=numpy.random.RandomState,
required=False,
label="Random Stream",
doc="An instance of numpy's RandomState associated with this"
"specific Noise object. Used when you need to resume a simulation from a state saved to disk"
)
[docs] def __init__(self, **kwargs):
super(Noise, self).__init__(**kwargs)
if self.random_stream is None:
self.random_stream = numpy.random.RandomState(self.noise_seed)
self.dt = None
# For use if coloured
self._E = None
self._sqrt_1_E2 = None
self._eta = None
self._h = None
# XXX: reseeding here will destroy a maybe carefully set random_stream!
# self.random_stream.seed(self.noise_seed)
[docs] def reset_random_stream(self):
self.random_stream = numpy.random.RandomState(self.noise_seed)
def __str__(self):
return simple_gen_astr(self, 'dt ntau')
[docs] def generate(self, shape, lo=-1.0, hi=1.0):
"Generate noise realization."
if self.ntau > 0.0:
noise = self.coloured(shape)
else:
noise = self.white(shape)
return noise
[docs] def coloured(self, shape):
"Generate colored noise. [FoxVemuri_1988]_"
self._h = self._sqrt_1_E2 * self.random_stream.normal(size=shape)
self._eta = self._eta * self._E + self._h
return self._dt_sqrt_lambda * self._eta
[docs] def white(self, shape):
"Generate white noise."
noise = numpy.sqrt(self.dt) * self.random_stream.normal(size=shape)
return noise
[docs] @abc.abstractmethod
def gfun(self, state_variables):
pass
[docs]class Additive(Noise):
"""
Additive noise which, assuming the source noise is Gaussian with unit
variance, will result in noise with a standard deviation of nsig.
"""
nsig = NArray(
label=":math:`D`",
required=True,
default=numpy.array([1.0]),
domain=Range(lo=0.0, hi=10.0, step=0.1),
doc="""The noise dispersion, it is the standard deviation of the
distribution from which the Gaussian random variates are drawn. NOTE:
Sensible values are typically ~<< 1% of the dynamic range of a Model's
state variables."""
)
[docs] def gfun(self, state_variables):
r"""
Linear additive noise, thus it ignores the state_variables.
.. math::
g(x) = \sqrt{2D}
"""
g_x = numpy.sqrt(2.0 * self.nsig)
return g_x
[docs]class Multiplicative(Noise):
r"""
With "external" fluctuations the intensity of the noise often depends on
the state of the system. This results in the (general) stochastic
differential formulation:
.. math::
dX_t = a(X_t)\,dt + b(X_t)\,dW_t
for appropriate coefficients :math:`a(x)` and :math:`b(x)`, which might be
constants.
From [KloedenPlaten_1995]_, Equation 1.9, page 104.
"""
nsig = NArray(
label=":math:`D`",
required=True,
default=numpy.array([1.0, ]),
domain=Range(lo=0.0, hi=10.0, step=0.1),
doc="""The noise dispersion, it is the standard deviation of the
distribution from which the Gaussian random variates are drawn. NOTE:
Sensible values are typically ~<< 1% of the dynamic range of a Model's
state variables."""
)
b = Attr(
field_type=equations.TemporalApplicableEquation,
label=":math:`b`",
default=equations.Linear(parameters={"a": 1.0, "b": 0.0}),
doc="""A function evaluated on the state-variables, the result of which enters as the diffusion coefficient.""")
[docs] def gfun(self, state_variables):
"""
Scale the noise by the noise dispersion and the diffusion coefficient.
By default, the diffusion coefficient :math:`b` is a constant.
It reduces to the simplest scheme of a linear SDE with Multiplicative
Noise: homogeneous constant coefficients. See [KloedenPlaten_1995]_,
Equation 4.6, page 119.
"""
g_x = numpy.sqrt(2.0 * self.nsig) * self.b.evaluate(state_variables)
return g_x