Source code for tvb.simulator.backend.ref
# -*- 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
#
#
"""
This module provides a reference backend implemented with NumPy.
"""
import numpy
import numpy as np
from .base import BaseBackend
[docs]class RefBase:
"Base methods for reference NumPy backend"
@staticmethod
def evaluate(expr, global_dict=None, out=None):
"Evaluate expression as in numexpr.evaluate."
ns = {}
ns.update(np.__dict__)
if global_dict is None:
import inspect
frame = inspect.getcurrentframe()
global_dict = frame.f_back.f_locals
ns.update(global_dict)
val = eval(expr, ns)
if out is not None:
out[:] = val
return val
try:
import numexpr
evaluate = staticmethod(numexpr.evaluate)
except ImportError:
pass
# from tvb.sim.common
def _add_at(dest, map, src):
"workaround lack of ufunc at method for older NumPy versions"
for i in numpy.unique(map):
dest[i] += src[i == map].sum(axis=0)
return dest
try:
add_at = staticmethod(numpy.add.at)
except AttributeError:
add_at = staticmethod(_add_at)
[docs] @staticmethod
def linear_interp1d(start_time, end_time, start_value, end_value, interp_point):
"""
performs a one dimensional linear interpolation using two
timepoints (start_time, end_time) for two floating point (possibly
NumPy arrays) states (start_value, end_value) to return state at timepoint
start_time < interp_point < end_time.
"""
mean = (end_value - start_value) / (end_time - start_time)
return start_value + (interp_point - start_time) * mean
[docs] @staticmethod
def heaviside(array):
"""
heaviside() returns 1 if argument > 0, 0 otherwise.
The copy operation here is necessary to ensure that the
array passed in is not modified.
"""
if type(array) == numpy.float64:
return 0.0 if array < 0.0 else 1.0
else:
ret = array.copy()
ret[array < 0.0] = 0.0
ret[array > 0.0] = 1.0
return ret
[docs] @staticmethod
def iround(x):
"""
iround(number) -> integer
Trying to get a stable and portable result when rounding a number to the
nearest integer.
NOTE: I'm introducing this because of the unstability we found when
using int(round()). Should use always the same rounding strategy.
Try :
>>> int(4.999999999999999)
4
>>> int(4.99999999999999999999)
5
"""
y = round(x) - .5
return int(y) + (y > 0)
[docs]class RefSurface(RefBase):
"Surface/field-related methods."
[docs] @staticmethod
def surface_state_to_rois(_regmap, n_reg, state):
region_state = numpy.zeros((n_reg, state.shape[0], state.shape[2])) # temp (node, cvar, mode)
# TODO how to handle this?
RefBase.add_at(region_state, _regmap, state.transpose((1, 0, 2))) # sum within region
region_state /= numpy.bincount(_regmap).reshape((-1, 1, 1)) # div by n node in region
# TODO out= argument to avoid alloc
state = region_state.transpose((1, 0, 2)) # (cvar, node, mode)
return state
[docs]class ReferenceBackend(BaseBackend, RefSurface):
"Base reference backend, implemented in readable NumPy."