Winners-take-all network
A winner-take-all network of units with self-limited growth and mutual inhibition.
- Name:
winners_take_all - Field:
biology/neuroscience - Formalism: ordinary differential equations
- References: Fukai and Tanaka 1997; Gros 2015
Factory
@register_model(
field='biology/neuroscience',
tags=('neural network', 'competition', 'winner-take-all'),
references=('Fukai and Tanaka 1997', 'Gros 2015'),
defaults={'until': 40.0, 'dt': 0.2},
)
def winners_take_all(
inputs: Sequence[float] = (1.0, 1.1, 0.9, 1.05, 0.95),
*,
beta: float = 2.0,
tau: float = 1.0,
x: float = 0.1,
) -> System:
"""Return a winner-take-all network of units with self-limited growth and mutual inhibition.
Unit `i` follows `tau x_i' = x_i (I_i - x_i - beta sum_{j != i} x_j)`, the competitive network of Fukai and
Tanaka. One inhibition interaction binds every unit as an element set and reads the total activity of the set, so
the network holds one interaction whatever the number of units. When the inhibition `beta` exceeds 1, only the unit
with the largest input stays active, at activity `I_max`. When it is below 1, several winners share the activity.
Args:
inputs:
The input of every unit.
beta:
The strength of the mutual inhibition.
tau:
The time constant of the units.
x:
The initial activity of every unit.
Returns:
system:
The model, with the activities `unit_<i>.x`.
Examples:
>>> from skcomplex.models.biology.neuroscience import winners_take_all
>>> len(winners_take_all((1.0, 2.0, 3.0)).interactions)
1
"""
units = [RateUnitElement(f'unit_{index}', x=x, I=value, tau=tau) for index, value in enumerate(inputs)]
inhibition = MutualInhibitionInteraction('inhibition', units=[unit.label for unit in units], beta=beta)
return System(elements=units, interactions=[inhibition])
Components
class RateUnitElement(Element):
"""Neural unit of a competitive network, whose activity grows with its input and saturates, `tau x' = x (I - x)`.
Args:
label:
The label of the unit.
visual:
How the element is drawn, or `None` for `default_visual_`.
x:
The initial activity.
I:
The input.
tau:
The time constant.
Examples:
>>> from skcomplex.models.biology.neuroscience import RateUnitElement
>>> RateUnitElement('unit', I=1.2).I
1.2
"""
def __init__(
self,
label: str,
visual: VisualSpec | None = None,
*,
x: Annotated[float, StateSpec(domain=RealSet.closed_open(0, oo))] = 0.1,
I: Annotated[float, ParameterSpec()] = 1.0,
tau: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 1.0,
) -> None:
super().__init__(label, visual=visual)
self.x = x
self.I = I
self.tau = tau
def _get_laws(self, s: SimpleNamespace) -> list[BaseLaw]:
"""Return the self-limited growth of the activity.
Args:
s:
The namespace of the symbols the laws can use.
Returns:
laws:
The laws of the component.
"""
return [RateLaw(s.x, s.x * (s.I - s.x) / s.tau)]
class MutualInhibitionInteraction(CouplingInteraction):
"""Inhibition of every unit by the summed activity of all the other units, `-beta x_i sum_{j != i} x_j / tau_i`.
It is the coupling of the units to each other, as separate elements or as the members of a group, with the term
`-beta x_i x_j / tau_i` of every other unit `j`.
Args:
label:
The label of the interaction.
units:
The paths of the units, an element set, or the path of a group of units.
beta:
The strength of the inhibition.
Examples:
>>> from skcomplex.models.biology.neuroscience import MutualInhibitionInteraction
>>> MutualInhibitionInteraction('inhibition', units=['a', 'b'], beta=2.0).beta
2.0
"""
def __init__(
self,
label: str,
*,
units: Annotated[str | Sequence[str], RoleSpec()],
beta: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 2.0,
) -> None:
super().__init__(label, target=units, function=self._get_inhibition, mode='group')
self.units = units
self.beta = beta
@staticmethod
def _get_inhibition(namespace: SimpleNamespace) -> Target:
"""Return the inhibition of the unit by one other unit, `-beta x_i x_j / tau_i`."""
member = namespace.member
return -namespace.beta * member.x * namespace.other.x / member.tau
Equations
unit_0.x: diff(x_unit_0(t), t) == -beta_inhibition*x_unit_0(t)*x_unit_1(t)/tau_unit_0 - beta_inhibition*x_unit_0(t)*x_unit_2(t)/tau_unit_0 - beta_inhibition*x_unit_0(t)*x_unit_3(t)/tau_unit_0 - beta_inhibition*x_unit_0(t)*x_unit_4(t)/tau_unit_0 + (I_unit_0 - x_unit_0(t))*x_unit_0(t)/tau_unit_0
unit_1.x: diff(x_unit_1(t), t) == -beta_inhibition*x_unit_0(t)*x_unit_1(t)/tau_unit_1 - beta_inhibition*x_unit_1(t)*x_unit_2(t)/tau_unit_1 - beta_inhibition*x_unit_1(t)*x_unit_3(t)/tau_unit_1 - beta_inhibition*x_unit_1(t)*x_unit_4(t)/tau_unit_1 + (I_unit_1 - x_unit_1(t))*x_unit_1(t)/tau_unit_1
unit_2.x: diff(x_unit_2(t), t) == -beta_inhibition*x_unit_0(t)*x_unit_2(t)/tau_unit_2 - beta_inhibition*x_unit_1(t)*x_unit_2(t)/tau_unit_2 - beta_inhibition*x_unit_2(t)*x_unit_3(t)/tau_unit_2 - beta_inhibition*x_unit_2(t)*x_unit_4(t)/tau_unit_2 + (I_unit_2 - x_unit_2(t))*x_unit_2(t)/tau_unit_2
unit_3.x: diff(x_unit_3(t), t) == -beta_inhibition*x_unit_0(t)*x_unit_3(t)/tau_unit_3 - beta_inhibition*x_unit_1(t)*x_unit_3(t)/tau_unit_3 - beta_inhibition*x_unit_2(t)*x_unit_3(t)/tau_unit_3 - beta_inhibition*x_unit_3(t)*x_unit_4(t)/tau_unit_3 + (I_unit_3 - x_unit_3(t))*x_unit_3(t)/tau_unit_3
unit_4.x: diff(x_unit_4(t), t) == -beta_inhibition*x_unit_0(t)*x_unit_4(t)/tau_unit_4 - beta_inhibition*x_unit_1(t)*x_unit_4(t)/tau_unit_4 - beta_inhibition*x_unit_2(t)*x_unit_4(t)/tau_unit_4 - beta_inhibition*x_unit_3(t)*x_unit_4(t)/tau_unit_4 + (I_unit_4 - x_unit_4(t))*x_unit_4(t)/tau_unit_4