Light-sensitive robots with sensory delay
Light-sensitive robots in a circular well.
- Class:
LightSeekingRobotsSystem - Import:
from skcomplex.systems.engineering.robotics import LightSeekingRobotsSystem - Field:
engineering/robotics - Topics:
robotics,active matter,delay,sde - References: Mijalkov et al. 2016; Argun et al. 2021
Use this system
from skcomplex.core.engine.runtime import simulate
from skcomplex.systems.engineering.robotics import LightSeekingRobotsSystem
trajectory = simulate(LightSeekingRobotsSystem())
Parameters
| Parameter | Default |
|---|---|
n_robots |
20 |
delay |
5.0 |
V0 |
1.0 |
V_inf |
0.1 |
I0 |
1.0 |
r0 |
1.0 |
tau |
1.0 |
R |
2.0 |
Tags
| Tag | Values |
|---|---|
| Time | continuous |
| Space | none |
| Laws | noise, rate, update |
| Randomness | stochastic |
| Domains | real |
| Structure | fixed |
Class
class LightSeekingRobotsSystem(BaseSystem):
"""Light-sensitive robots in a circular well.
Their sensory delay decides whether they gather in the light. Each robot moves at
`V_inf + (V0 - V_inf) exp(-I(t - delay))` in a direction that diffuses with time scale `tau`, in the Gaussian light
`I = I0 exp(-(x^2 + y^2) / r0^2)` at the centre of a well of radius `R`. The robots start at the centre, in
directions spread around the circle, and do not interact. A positive delay drives them out of the light: a robot
crosses the light at the fast speed it measured in the dark and lingers outside at the slow speed it measured in the
light, so robots with a delay spend less time in the light than robots without one.
The system has the group `robots` of positions `x`, `y` and directions `phi`.
Args:
n_robots:
The number of robots.
delay:
The sensory delay.
V0:
The speed in the dark.
V_inf:
The speed in strong light.
I0:
The intensity at the centre of the light.
r0:
The width of the light.
tau:
The time scale of rotational diffusion.
R:
The radius of the well.
label:
The label of the system, which names it when it is nested in another one.
Examples:
>>> from skcomplex.systems.engineering.robotics import LightSeekingRobotsSystem
>>> LightSeekingRobotsSystem(4).prepare().system_.elements[0].prepare().size_
4
"""
field = 'engineering/robotics'
topics = ('robotics', 'active matter', 'delay', 'sde')
references = ('Mijalkov et al. 2016', 'Argun et al. 2021')
until = 100.0
dt = 0.5
def __init__(
self,
n_robots: int = 20,
*,
delay: float = 5.0,
V0: float = 1.0,
V_inf: float = 0.1,
I0: float = 1.0,
r0: float = 1.0,
tau: float = 1.0,
R: float = 2.0,
label: str = 'system',
) -> None:
super().__init__(label)
self.n_robots = n_robots
self.delay = delay
self.V0 = V0
self.V_inf = V_inf
self.I0 = I0
self.r0 = r0
self.tau = tau
self.R = R
def _build_system(self) -> System:
"""Return the parts of the system built from its model parameters."""
headings = np.linspace(0.0, 2 * np.pi, self.n_robots, endpoint=False)
robots = GroupElement(
'robots',
element=LightSensitiveRobotElement,
size=self.n_robots,
values={
'x': np.zeros(self.n_robots),
'y': np.zeros(self.n_robots),
'phi': headings,
'V0': self.V0,
'V_inf': self.V_inf,
'I0': self.I0,
'r0': self.r0,
'tau': self.tau,
'R': self.R,
'delay': self.delay,
},
)
return System(self.label, elements=[robots])
Components
Elements:
LightSensitiveRobotElement, fromElement
class LightSensitiveRobotElement(Element):
"""Robot in a Gaussian light field that slows down in light.
It reacts to the light it measured a delay ago. The robot moves at the speed
`v = V_inf + (V0 - V_inf) exp(-I(t - delay))` in the direction `phi`, which diffuses with time scale `tau`,
`dphi = sqrt(2 / tau) dW`. The light is `I(x, y) = I0 exp(-(x^2 + y^2) / r0^2)`, and a wall at radius `R` reflects
the robot back into a circular well.
Args:
label:
The label of the robot or the group.
visual:
How the element is drawn, or `None` for `default_visual_`.
x:
The initial horizontal position.
y:
The initial vertical position.
phi:
The initial direction of motion.
V0:
The speed in the dark.
V_inf:
The speed in strong light.
I0:
The intensity at the centre of the light.
r0:
The width of the light.
tau:
The time scale of rotational diffusion.
R:
The radius of the well.
delay:
The sensory delay, 0 for a robot that reacts to the light it measures now.
Examples:
>>> from skcomplex.systems.engineering.robotics import LightSensitiveRobotElement
>>> LightSensitiveRobotElement('robot', delay=5.0).delay
5.0
"""
law_classes = (RateLaw, NoiseLaw, BoundaryLaw)
def __init__(
self,
label: str,
visual: VisualSpec | None = None,
*,
x: Annotated[float, StateSpec(role='position')] = 0.0,
y: Annotated[float, StateSpec(role='position')] = 0.0,
phi: Annotated[float, StateSpec(role='direction')] = 0.0,
V0: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 1.0,
V_inf: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.1,
I0: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 1.0,
r0: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 1.0,
tau: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 1.0,
R: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 2.0,
delay: float = 0.0,
) -> None:
super().__init__(label, visual=visual)
self.x = x
self.y = y
self.phi = phi
self.V0 = V0
self.V_inf = V_inf
self.I0 = I0
self.r0 = r0
self.tau = tau
self.R = R
self.delay = delay
def _get_laws(self, s: SimpleNamespace) -> list[BaseLaw]:
"""Return the motion at the light-dependent speed.
It includes the rotational noise and the reflecting wall.
Args:
s:
The namespace of the symbols the laws can use.
Returns:
laws:
The laws of the component.
"""
sensed = (
LightSensitiveRobotElement._light(s, delayed(s.x, self.delay), delayed(s.y, self.delay))
if self.delay
else LightSensitiveRobotElement._light(s, s.x, s.y)
)
speed = s.V_inf + (s.V0 - s.V_inf) * exp(-sensed)
reach = where(sqrt(s.x**2 + s.y**2) > s.R, sqrt(s.x**2 + s.y**2), s.R)
return [
RateLaw(s.x, speed * cos(s.phi)),
RateLaw(s.y, speed * sin(s.phi)),
NoiseLaw(s.phi, sqrt(2 / s.tau)),
BoundaryLaw(s.x, s.x * s.R / reach),
BoundaryLaw(s.y, s.y * s.R / reach),
]
@staticmethod
def _light(s: SimpleNamespace, x: Expression, y: Expression) -> Expression:
"""Return the Gaussian light intensity `I0 exp(-(x^2 + y^2) / r0^2)` at a point."""
return s.I0 * exp(-(x**2 + y**2) / s.r0**2)
Equations
robots.x[i]: diff(x_robots(t), t) == ((V0_robots - V_inf_robots)*e^(-I0_robots*e^(-(x_robots(t - 5.0)^2 + y_robots(t - 5.0)^2)/r0_robots^2)) + V_inf_robots)*cos(phi_robots(t))
robots.y[i]: diff(y_robots(t), t) == ((V0_robots - V_inf_robots)*e^(-I0_robots*e^(-(x_robots(t - 5.0)^2 + y_robots(t - 5.0)^2)/r0_robots^2)) + V_inf_robots)*sin(phi_robots(t))
robots.phi[i]: diff(phi_robots(t), t) == 0