switching to high quality piper tts and added label translations
This commit is contained in:
@@ -0,0 +1,869 @@
|
||||
r"""Activation dynamics for musclotendon models.
|
||||
|
||||
Musculotendon models are able to produce active force when they are activated,
|
||||
which is when a chemical process has taken place within the muscle fibers
|
||||
causing them to voluntarily contract. Biologically this chemical process (the
|
||||
diffusion of :math:`\textrm{Ca}^{2+}` ions) is not the input in the system,
|
||||
electrical signals from the nervous system are. These are termed excitations.
|
||||
Activation dynamics, which relates the normalized excitation level to the
|
||||
normalized activation level, can be modeled by the models present in this
|
||||
module.
|
||||
|
||||
"""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from functools import cached_property
|
||||
|
||||
from sympy.core.symbol import Symbol
|
||||
from sympy.core.numbers import Float, Integer, Rational
|
||||
from sympy.functions.elementary.hyperbolic import tanh
|
||||
from sympy.matrices.dense import MutableDenseMatrix as Matrix, zeros
|
||||
from sympy.physics.biomechanics._mixin import _NamedMixin
|
||||
from sympy.physics.mechanics import dynamicsymbols
|
||||
|
||||
|
||||
__all__ = [
|
||||
'ActivationBase',
|
||||
'FirstOrderActivationDeGroote2016',
|
||||
'ZerothOrderActivation',
|
||||
]
|
||||
|
||||
|
||||
class ActivationBase(ABC, _NamedMixin):
|
||||
"""Abstract base class for all activation dynamics classes to inherit from.
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
Instances of this class cannot be directly instantiated by users. However,
|
||||
it can be used to created custom activation dynamics types through
|
||||
subclassing.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
"""Initializer for ``ActivationBase``."""
|
||||
self.name = str(name)
|
||||
|
||||
# Symbols
|
||||
self._e = dynamicsymbols(f"e_{name}")
|
||||
self._a = dynamicsymbols(f"a_{name}")
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def with_defaults(cls, name):
|
||||
"""Alternate constructor that provides recommended defaults for
|
||||
constants."""
|
||||
pass
|
||||
|
||||
@property
|
||||
def excitation(self):
|
||||
"""Dynamic symbol representing excitation.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The alias ``e`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
return self._e
|
||||
|
||||
@property
|
||||
def e(self):
|
||||
"""Dynamic symbol representing excitation.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The alias ``excitation`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
return self._e
|
||||
|
||||
@property
|
||||
def activation(self):
|
||||
"""Dynamic symbol representing activation.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The alias ``a`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
return self._a
|
||||
|
||||
@property
|
||||
def a(self):
|
||||
"""Dynamic symbol representing activation.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The alias ``activation`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
return self._a
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def order(self):
|
||||
"""Order of the (differential) equation governing activation."""
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def state_vars(self):
|
||||
"""Ordered column matrix of functions of time that represent the state
|
||||
variables.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The alias ``x`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def x(self):
|
||||
"""Ordered column matrix of functions of time that represent the state
|
||||
variables.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The alias ``state_vars`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def input_vars(self):
|
||||
"""Ordered column matrix of functions of time that represent the input
|
||||
variables.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The alias ``r`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def r(self):
|
||||
"""Ordered column matrix of functions of time that represent the input
|
||||
variables.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The alias ``input_vars`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def constants(self):
|
||||
"""Ordered column matrix of non-time varying symbols present in ``M``
|
||||
and ``F``.
|
||||
|
||||
Only symbolic constants are returned. If a numeric type (e.g. ``Float``)
|
||||
has been used instead of ``Symbol`` for a constant then that attribute
|
||||
will not be included in the matrix returned by this property. This is
|
||||
because the primary use of this property attribute is to provide an
|
||||
ordered sequence of the still-free symbols that require numeric values
|
||||
during code generation.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The alias ``p`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def p(self):
|
||||
"""Ordered column matrix of non-time varying symbols present in ``M``
|
||||
and ``F``.
|
||||
|
||||
Only symbolic constants are returned. If a numeric type (e.g. ``Float``)
|
||||
has been used instead of ``Symbol`` for a constant then that attribute
|
||||
will not be included in the matrix returned by this property. This is
|
||||
because the primary use of this property attribute is to provide an
|
||||
ordered sequence of the still-free symbols that require numeric values
|
||||
during code generation.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The alias ``constants`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def M(self):
|
||||
"""Ordered square matrix of coefficients on the LHS of ``M x' = F``.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The square matrix that forms part of the LHS of the linear system of
|
||||
ordinary differential equations governing the activation dynamics:
|
||||
|
||||
``M(x, r, t, p) x' = F(x, r, t, p)``.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def F(self):
|
||||
"""Ordered column matrix of equations on the RHS of ``M x' = F``.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The column matrix that forms the RHS of the linear system of ordinary
|
||||
differential equations governing the activation dynamics:
|
||||
|
||||
``M(x, r, t, p) x' = F(x, r, t, p)``.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def rhs(self):
|
||||
"""
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The solution to the linear system of ordinary differential equations
|
||||
governing the activation dynamics:
|
||||
|
||||
``M(x, r, t, p) x' = F(x, r, t, p)``.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Equality check for activation dynamics."""
|
||||
if type(self) != type(other):
|
||||
return False
|
||||
if self.name != other.name:
|
||||
return False
|
||||
return True
|
||||
|
||||
def __repr__(self):
|
||||
"""Default representation of activation dynamics."""
|
||||
return f'{self.__class__.__name__}({self.name!r})'
|
||||
|
||||
|
||||
class ZerothOrderActivation(ActivationBase):
|
||||
"""Simple zeroth-order activation dynamics mapping excitation to
|
||||
activation.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
Zeroth-order activation dynamics are useful in instances where you want to
|
||||
reduce the complexity of your musculotendon dynamics as they simple map
|
||||
exictation to activation. As a result, no additional state equations are
|
||||
introduced to your system. They also remove a potential source of delay
|
||||
between the input and dynamics of your system as no (ordinary) differential
|
||||
equations are involved.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
"""Initializer for ``ZerothOrderActivation``.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
name : str
|
||||
The name identifier associated with the instance. Must be a string
|
||||
of length at least 1.
|
||||
|
||||
"""
|
||||
super().__init__(name)
|
||||
|
||||
# Zeroth-order activation dynamics has activation equal excitation so
|
||||
# overwrite the symbol for activation with the excitation symbol.
|
||||
self._a = self._e
|
||||
|
||||
@classmethod
|
||||
def with_defaults(cls, name):
|
||||
"""Alternate constructor that provides recommended defaults for
|
||||
constants.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
As this concrete class doesn't implement any constants associated with
|
||||
its dynamics, this ``classmethod`` simply creates a standard instance
|
||||
of ``ZerothOrderActivation``. An implementation is provided to ensure
|
||||
a consistent interface between all ``ActivationBase`` concrete classes.
|
||||
|
||||
"""
|
||||
return cls(name)
|
||||
|
||||
@property
|
||||
def order(self):
|
||||
"""Order of the (differential) equation governing activation."""
|
||||
return 0
|
||||
|
||||
@property
|
||||
def state_vars(self):
|
||||
"""Ordered column matrix of functions of time that represent the state
|
||||
variables.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
As zeroth-order activation dynamics simply maps excitation to
|
||||
activation, this class has no associated state variables and so this
|
||||
property return an empty column ``Matrix`` with shape (0, 1).
|
||||
|
||||
The alias ``x`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
return zeros(0, 1)
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
"""Ordered column matrix of functions of time that represent the state
|
||||
variables.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
As zeroth-order activation dynamics simply maps excitation to
|
||||
activation, this class has no associated state variables and so this
|
||||
property return an empty column ``Matrix`` with shape (0, 1).
|
||||
|
||||
The alias ``state_vars`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
return zeros(0, 1)
|
||||
|
||||
@property
|
||||
def input_vars(self):
|
||||
"""Ordered column matrix of functions of time that represent the input
|
||||
variables.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
Excitation is the only input in zeroth-order activation dynamics and so
|
||||
this property returns a column ``Matrix`` with one entry, ``e``, and
|
||||
shape (1, 1).
|
||||
|
||||
The alias ``r`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
return Matrix([self._e])
|
||||
|
||||
@property
|
||||
def r(self):
|
||||
"""Ordered column matrix of functions of time that represent the input
|
||||
variables.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
Excitation is the only input in zeroth-order activation dynamics and so
|
||||
this property returns a column ``Matrix`` with one entry, ``e``, and
|
||||
shape (1, 1).
|
||||
|
||||
The alias ``input_vars`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
return Matrix([self._e])
|
||||
|
||||
@property
|
||||
def constants(self):
|
||||
"""Ordered column matrix of non-time varying symbols present in ``M``
|
||||
and ``F``.
|
||||
|
||||
Only symbolic constants are returned. If a numeric type (e.g. ``Float``)
|
||||
has been used instead of ``Symbol`` for a constant then that attribute
|
||||
will not be included in the matrix returned by this property. This is
|
||||
because the primary use of this property attribute is to provide an
|
||||
ordered sequence of the still-free symbols that require numeric values
|
||||
during code generation.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
As zeroth-order activation dynamics simply maps excitation to
|
||||
activation, this class has no associated constants and so this property
|
||||
return an empty column ``Matrix`` with shape (0, 1).
|
||||
|
||||
The alias ``p`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
return zeros(0, 1)
|
||||
|
||||
@property
|
||||
def p(self):
|
||||
"""Ordered column matrix of non-time varying symbols present in ``M``
|
||||
and ``F``.
|
||||
|
||||
Only symbolic constants are returned. If a numeric type (e.g. ``Float``)
|
||||
has been used instead of ``Symbol`` for a constant then that attribute
|
||||
will not be included in the matrix returned by this property. This is
|
||||
because the primary use of this property attribute is to provide an
|
||||
ordered sequence of the still-free symbols that require numeric values
|
||||
during code generation.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
As zeroth-order activation dynamics simply maps excitation to
|
||||
activation, this class has no associated constants and so this property
|
||||
return an empty column ``Matrix`` with shape (0, 1).
|
||||
|
||||
The alias ``constants`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
return zeros(0, 1)
|
||||
|
||||
@property
|
||||
def M(self):
|
||||
"""Ordered square matrix of coefficients on the LHS of ``M x' = F``.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The square matrix that forms part of the LHS of the linear system of
|
||||
ordinary differential equations governing the activation dynamics:
|
||||
|
||||
``M(x, r, t, p) x' = F(x, r, t, p)``.
|
||||
|
||||
As zeroth-order activation dynamics have no state variables, this
|
||||
linear system has dimension 0 and therefore ``M`` is an empty square
|
||||
``Matrix`` with shape (0, 0).
|
||||
|
||||
"""
|
||||
return Matrix([])
|
||||
|
||||
@property
|
||||
def F(self):
|
||||
"""Ordered column matrix of equations on the RHS of ``M x' = F``.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The column matrix that forms the RHS of the linear system of ordinary
|
||||
differential equations governing the activation dynamics:
|
||||
|
||||
``M(x, r, t, p) x' = F(x, r, t, p)``.
|
||||
|
||||
As zeroth-order activation dynamics have no state variables, this
|
||||
linear system has dimension 0 and therefore ``F`` is an empty column
|
||||
``Matrix`` with shape (0, 1).
|
||||
|
||||
"""
|
||||
return zeros(0, 1)
|
||||
|
||||
def rhs(self):
|
||||
"""Ordered column matrix of equations for the solution of ``M x' = F``.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The solution to the linear system of ordinary differential equations
|
||||
governing the activation dynamics:
|
||||
|
||||
``M(x, r, t, p) x' = F(x, r, t, p)``.
|
||||
|
||||
As zeroth-order activation dynamics have no state variables, this
|
||||
linear has dimension 0 and therefore this method returns an empty
|
||||
column ``Matrix`` with shape (0, 1).
|
||||
|
||||
"""
|
||||
return zeros(0, 1)
|
||||
|
||||
|
||||
class FirstOrderActivationDeGroote2016(ActivationBase):
|
||||
r"""First-order activation dynamics based on De Groote et al., 2016 [1]_.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
Gives the first-order activation dynamics equation for the rate of change
|
||||
of activation with respect to time as a function of excitation and
|
||||
activation.
|
||||
|
||||
The function is defined by the equation:
|
||||
|
||||
.. math::
|
||||
|
||||
\frac{da}{dt} = \left(\frac{\frac{1}{2} + a0}{\tau_a \left(\frac{1}{2}
|
||||
+ \frac{3a}{2}\right)} + \frac{\left(\frac{1}{2}
|
||||
+ \frac{3a}{2}\right) \left(\frac{1}{2} - a0\right)}{\tau_d}\right)
|
||||
\left(e - a\right)
|
||||
|
||||
where
|
||||
|
||||
.. math::
|
||||
|
||||
a0 = \frac{\tanh{\left(b \left(e - a\right) \right)}}{2}
|
||||
|
||||
with constant values of :math:`tau_a = 0.015`, :math:`tau_d = 0.060`, and
|
||||
:math:`b = 10`.
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] De Groote, F., Kinney, A. L., Rao, A. V., & Fregly, B. J., Evaluation
|
||||
of direct collocation optimal control problem formulations for
|
||||
solving the muscle redundancy problem, Annals of biomedical
|
||||
engineering, 44(10), (2016) pp. 2922-2936
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
name,
|
||||
activation_time_constant=None,
|
||||
deactivation_time_constant=None,
|
||||
smoothing_rate=None,
|
||||
):
|
||||
"""Initializer for ``FirstOrderActivationDeGroote2016``.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
activation time constant : Symbol | Number | None
|
||||
The value of the activation time constant governing the delay
|
||||
between excitation and activation when excitation exceeds
|
||||
activation.
|
||||
deactivation time constant : Symbol | Number | None
|
||||
The value of the deactivation time constant governing the delay
|
||||
between excitation and activation when activation exceeds
|
||||
excitation.
|
||||
smoothing_rate : Symbol | Number | None
|
||||
The slope of the hyperbolic tangent function used to smooth between
|
||||
the switching of the equations where excitation exceed activation
|
||||
and where activation exceeds excitation. The recommended value to
|
||||
use is ``10``, but values between ``0.1`` and ``100`` can be used.
|
||||
|
||||
"""
|
||||
super().__init__(name)
|
||||
|
||||
# Symbols
|
||||
self.activation_time_constant = activation_time_constant
|
||||
self.deactivation_time_constant = deactivation_time_constant
|
||||
self.smoothing_rate = smoothing_rate
|
||||
|
||||
@classmethod
|
||||
def with_defaults(cls, name):
|
||||
r"""Alternate constructor that will use the published constants.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
Returns an instance of ``FirstOrderActivationDeGroote2016`` using the
|
||||
three constant values specified in the original publication.
|
||||
|
||||
These have the values:
|
||||
|
||||
:math:`tau_a = 0.015`
|
||||
:math:`tau_d = 0.060`
|
||||
:math:`b = 10`
|
||||
|
||||
"""
|
||||
tau_a = Float('0.015')
|
||||
tau_d = Float('0.060')
|
||||
b = Float('10.0')
|
||||
return cls(name, tau_a, tau_d, b)
|
||||
|
||||
@property
|
||||
def activation_time_constant(self):
|
||||
"""Delay constant for activation.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The alias ```tau_a`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
return self._tau_a
|
||||
|
||||
@activation_time_constant.setter
|
||||
def activation_time_constant(self, tau_a):
|
||||
if hasattr(self, '_tau_a'):
|
||||
msg = (
|
||||
f'Can\'t set attribute `activation_time_constant` to '
|
||||
f'{repr(tau_a)} as it is immutable and already has value '
|
||||
f'{self._tau_a}.'
|
||||
)
|
||||
raise AttributeError(msg)
|
||||
self._tau_a = Symbol(f'tau_a_{self.name}') if tau_a is None else tau_a
|
||||
|
||||
@property
|
||||
def tau_a(self):
|
||||
"""Delay constant for activation.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The alias ``activation_time_constant`` can also be used to access the
|
||||
same attribute.
|
||||
|
||||
"""
|
||||
return self._tau_a
|
||||
|
||||
@property
|
||||
def deactivation_time_constant(self):
|
||||
"""Delay constant for deactivation.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The alias ``tau_d`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
return self._tau_d
|
||||
|
||||
@deactivation_time_constant.setter
|
||||
def deactivation_time_constant(self, tau_d):
|
||||
if hasattr(self, '_tau_d'):
|
||||
msg = (
|
||||
f'Can\'t set attribute `deactivation_time_constant` to '
|
||||
f'{repr(tau_d)} as it is immutable and already has value '
|
||||
f'{self._tau_d}.'
|
||||
)
|
||||
raise AttributeError(msg)
|
||||
self._tau_d = Symbol(f'tau_d_{self.name}') if tau_d is None else tau_d
|
||||
|
||||
@property
|
||||
def tau_d(self):
|
||||
"""Delay constant for deactivation.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The alias ``deactivation_time_constant`` can also be used to access the
|
||||
same attribute.
|
||||
|
||||
"""
|
||||
return self._tau_d
|
||||
|
||||
@property
|
||||
def smoothing_rate(self):
|
||||
"""Smoothing constant for the hyperbolic tangent term.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The alias ``b`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
return self._b
|
||||
|
||||
@smoothing_rate.setter
|
||||
def smoothing_rate(self, b):
|
||||
if hasattr(self, '_b'):
|
||||
msg = (
|
||||
f'Can\'t set attribute `smoothing_rate` to {b!r} as it is '
|
||||
f'immutable and already has value {self._b!r}.'
|
||||
)
|
||||
raise AttributeError(msg)
|
||||
self._b = Symbol(f'b_{self.name}') if b is None else b
|
||||
|
||||
@property
|
||||
def b(self):
|
||||
"""Smoothing constant for the hyperbolic tangent term.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The alias ``smoothing_rate`` can also be used to access the same
|
||||
attribute.
|
||||
|
||||
"""
|
||||
return self._b
|
||||
|
||||
@property
|
||||
def order(self):
|
||||
"""Order of the (differential) equation governing activation."""
|
||||
return 1
|
||||
|
||||
@property
|
||||
def state_vars(self):
|
||||
"""Ordered column matrix of functions of time that represent the state
|
||||
variables.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The alias ``x`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
return Matrix([self._a])
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
"""Ordered column matrix of functions of time that represent the state
|
||||
variables.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The alias ``state_vars`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
return Matrix([self._a])
|
||||
|
||||
@property
|
||||
def input_vars(self):
|
||||
"""Ordered column matrix of functions of time that represent the input
|
||||
variables.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The alias ``r`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
return Matrix([self._e])
|
||||
|
||||
@property
|
||||
def r(self):
|
||||
"""Ordered column matrix of functions of time that represent the input
|
||||
variables.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The alias ``input_vars`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
return Matrix([self._e])
|
||||
|
||||
@property
|
||||
def constants(self):
|
||||
"""Ordered column matrix of non-time varying symbols present in ``M``
|
||||
and ``F``.
|
||||
|
||||
Only symbolic constants are returned. If a numeric type (e.g. ``Float``)
|
||||
has been used instead of ``Symbol`` for a constant then that attribute
|
||||
will not be included in the matrix returned by this property. This is
|
||||
because the primary use of this property attribute is to provide an
|
||||
ordered sequence of the still-free symbols that require numeric values
|
||||
during code generation.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The alias ``p`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
constants = [self._tau_a, self._tau_d, self._b]
|
||||
symbolic_constants = [c for c in constants if not c.is_number]
|
||||
return Matrix(symbolic_constants) if symbolic_constants else zeros(0, 1)
|
||||
|
||||
@property
|
||||
def p(self):
|
||||
"""Ordered column matrix of non-time varying symbols present in ``M``
|
||||
and ``F``.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
Only symbolic constants are returned. If a numeric type (e.g. ``Float``)
|
||||
has been used instead of ``Symbol`` for a constant then that attribute
|
||||
will not be included in the matrix returned by this property. This is
|
||||
because the primary use of this property attribute is to provide an
|
||||
ordered sequence of the still-free symbols that require numeric values
|
||||
during code generation.
|
||||
|
||||
The alias ``constants`` can also be used to access the same attribute.
|
||||
|
||||
"""
|
||||
constants = [self._tau_a, self._tau_d, self._b]
|
||||
symbolic_constants = [c for c in constants if not c.is_number]
|
||||
return Matrix(symbolic_constants) if symbolic_constants else zeros(0, 1)
|
||||
|
||||
@property
|
||||
def M(self):
|
||||
"""Ordered square matrix of coefficients on the LHS of ``M x' = F``.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The square matrix that forms part of the LHS of the linear system of
|
||||
ordinary differential equations governing the activation dynamics:
|
||||
|
||||
``M(x, r, t, p) x' = F(x, r, t, p)``.
|
||||
|
||||
"""
|
||||
return Matrix([Integer(1)])
|
||||
|
||||
@property
|
||||
def F(self):
|
||||
"""Ordered column matrix of equations on the RHS of ``M x' = F``.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The column matrix that forms the RHS of the linear system of ordinary
|
||||
differential equations governing the activation dynamics:
|
||||
|
||||
``M(x, r, t, p) x' = F(x, r, t, p)``.
|
||||
|
||||
"""
|
||||
return Matrix([self._da_eqn])
|
||||
|
||||
def rhs(self):
|
||||
"""Ordered column matrix of equations for the solution of ``M x' = F``.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The solution to the linear system of ordinary differential equations
|
||||
governing the activation dynamics:
|
||||
|
||||
``M(x, r, t, p) x' = F(x, r, t, p)``.
|
||||
|
||||
"""
|
||||
return Matrix([self._da_eqn])
|
||||
|
||||
@cached_property
|
||||
def _da_eqn(self):
|
||||
HALF = Rational(1, 2)
|
||||
a0 = HALF * tanh(self._b * (self._e - self._a))
|
||||
a1 = (HALF + Rational(3, 2) * self._a)
|
||||
a2 = (HALF + a0) / (self._tau_a * a1)
|
||||
a3 = a1 * (HALF - a0) / self._tau_d
|
||||
activation_dynamics_equation = (a2 + a3) * (self._e - self._a)
|
||||
return activation_dynamics_equation
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Equality check for ``FirstOrderActivationDeGroote2016``."""
|
||||
if type(self) != type(other):
|
||||
return False
|
||||
self_attrs = (self.name, self.tau_a, self.tau_d, self.b)
|
||||
other_attrs = (other.name, other.tau_a, other.tau_d, other.b)
|
||||
if self_attrs == other_attrs:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __repr__(self):
|
||||
"""Representation of ``FirstOrderActivationDeGroote2016``."""
|
||||
return (
|
||||
f'{self.__class__.__name__}({self.name!r}, '
|
||||
f'activation_time_constant={self.tau_a!r}, '
|
||||
f'deactivation_time_constant={self.tau_d!r}, '
|
||||
f'smoothing_rate={self.b!r})'
|
||||
)
|
||||
Reference in New Issue
Block a user