Generalised multi-valued logical networks
A generalised logical network of Thomas, whose variables step asynchronously towards focal levels.
- Name:
generalized_logical_network - Field:
biology/systems_biology - Formalism: jump process
- References: Thomas 1991; Raman 2021
Select this system
from skcomplex.systems import create_simulation, create_system, describe
system = create_system('generalized_logical_network')
entry = describe('generalized_logical_network')
trajectory = create_simulation('generalized_logical_network').prepare().run().trajectory_
Factory
@register_system(
field='biology/systems_biology',
tags=('logical network', 'multi-valued', 'asynchronous', 'thomas', 'jumps'),
references=('Thomas 1991', 'Raman 2021'),
defaults={'until': 50.0, 'dt': 0.25},
)
def generalized_logical_network(
regulations: Regulations | None = None,
levels: Mapping[str, int] | None = None,
) -> System:
"""Return a generalised logical network of Thomas, whose variables step asynchronously towards focal levels.
Every variable takes discrete levels, and each of its regulators acts once its level reaches a threshold. The
logical parameters give the focal level of the variable for every combination of acting regulators, and the
variable rises or falls one level at a time towards it, each step a random event, so the dynamics explores the
asynchronous state transition graph. The default is the negative circuit of a three-level `x` that activates `y` at
its second threshold and a Boolean `y` that represses `x`, whose state graph is a cycle, a sustained oscillation.
A positive circuit, two mutual repressors, has two stable states instead.
Args:
regulations:
For every variable, its regulators, their thresholds, and the logical parameters of its focal level, or
`None` for the negative circuit.
levels:
The initial level of every variable, or `None` for 0.
Returns:
system:
The system, with the levels `<variable>.level`.
Examples:
>>> from skcomplex.systems.biology.systems_biology import generalized_logical_network
>>> [element.label for element in generalized_logical_network().elements]
['x', 'y']
"""
network = NEGATIVE_CIRCUIT if regulations is None else regulations
start = levels or {}
return System(
elements=[LogicalVariableElement(name, level=float(start.get(name, 0))) for name in network],
interactions=[
LogicalRegulationInteraction(
f'{name}_regulation',
target=name,
regulators=list(regulators),
thresholds=tuple(thresholds),
parameters=dict(parameters),
)
for name, (regulators, thresholds, parameters) in network.items()
],
)
Components
Elements:
LogicalVariableElement, fromElement
Interactions:
LogicalRegulationInteraction, fromInteraction
class LogicalVariableElement(Element):
"""Variable of a generalised logical network, a discrete level from zero to its largest level.
The levels stand for qualitative concentrations, such as absent, basal, medium, or high, cut at the thresholds at
which the variable acts on its targets.
Args:
label:
The label of the variable.
visual:
How the element is drawn, or `None` for `default_visual_`.
level:
The initial level.
Examples:
>>> from skcomplex.systems.biology.systems_biology import LogicalVariableElement
>>> LogicalVariableElement('x', level=2.0).level
2.0
"""
def __init__(
self,
label: str,
visual: VisualSpec | None = None,
*,
level: Annotated[float, StateSpec(domain=NN)] = 0.0,
) -> None:
super().__init__(label, visual=visual)
self.level = level
class LogicalRegulationInteraction(Interaction):
"""Regulation of a logical variable, which steps one level at a time towards the focal level its regulators set.
In the formalism of Thomas, every regulator acts when its level reaches its threshold. The logical parameters map
every combination of acting regulators, a tuple of truth values in the order of the regulators, to the focal level
of the target. The target rises or falls by one level towards its focal level at the unit rate, so the transitions
of all variables are asynchronous random events.
Args:
label:
The label of the interaction.
target:
The path of the regulated variable.
regulators:
The paths of the regulating variables.
thresholds:
The level at which every regulator acts, in the order of the regulators.
parameters:
The focal level of the target for every combination of acting regulators.
Examples:
>>> from skcomplex.systems.biology.systems_biology import LogicalRegulationInteraction
>>> regulation = LogicalRegulationInteraction(
... 'x_regulation', target='x', regulators=['y'], thresholds=(1,), parameters={(False,): 2, (True,): 0}
... )
>>> regulation.parameters[(True,)]
0
"""
def __init__(
self,
label: str,
*,
target: Annotated[str, RoleSpec()],
regulators: Annotated[Sequence[str], RoleSpec(variadic=True)] = (),
thresholds: Sequence[int] = (),
parameters: Mapping[tuple[bool, ...], int] | None = None,
) -> None:
super().__init__(label)
self.target = target
self.regulators = regulators
self.thresholds = thresholds
self.parameters = parameters
def _get_laws(self, s: SimpleNamespace) -> list[BaseLaw]:
"""Return the steps of the target up and down towards its focal level.
Args:
s:
The namespace of the symbols the laws can use.
Returns:
laws:
The laws of the component.
"""
level = s.target.level
focal = self._focal(s.regulators)
return [
JumpLaw(where(focal > level, 1, 0), {level: 1}),
JumpLaw(where(focal < level, 1, 0), {level: -1}),
]
def _focal(self, regulators: Sequence[SimpleNamespace]) -> Expression:
"""Return the focal level of the target, the logical parameter of the regulators that act."""
parameters = self.parameters or {}
focal: Expression = SR(0)
for acting in product((False, True), repeat=len(regulators)):
indicator: Expression = SR(1)
for regulator, threshold, active in zip(regulators, self.thresholds, acting, strict=True):
indicator *= (
where(regulator.level >= threshold, 1, 0) if active else where(regulator.level < threshold, 1, 0)
)
focal += parameters.get(acting, 0) * indicator
return focal