Simulation

╔══════════════════════════════════════════════════════════╗ ║ simulation « walking strategy simulators » ║ ╚══════════════════════════════════════════════════════════╝

Provides two agent classes that move through a toroidal 2-D arena and collect food items:

  • MarkovWalker — HMM-driven replay of empirical prototypical-movement sequences (dark-fly or OregonR).

  • LevyWalker — Lévy-flight / random-walk agent with configurable step-size distributions.

class satyre.simulation.MarkovWalker(transition_matrix, pm_index, velocity_array, *, n_trials=1000, border=10000.0, max_steps=10000, total_food=1000, food_mode='random', condition='dark')[source]

Simulate fly locomotion using a Hidden-Markov-Model of prototypical movements derived from experimental data.

The transition-probability matrix drives the selection of successive PMs. Each PM carries thrust, slip, and yaw velocities that determine displacement and body rotation.

Parameters:
  • transition_matrix (ndarray[tuple[Any, ...], dtype[floating]]) – Cumulative transition-probability matrix of shape (N, N) where N is the number of prototypical movements.

  • pm_index (ndarray[tuple[Any, ...], dtype[floating]]) – Row-sorted PM index matrix of shape (N, N). Each row maps cumulative-probability columns back to PM identifiers.

  • velocity_array (ndarray[tuple[Any, ...], dtype[floating]]) – Array of shape (N, 3) holding [thrust, slip, yaw] velocities for each PM (units: mm s⁻¹ / rad s⁻¹).

  • n_trials (int) – Number of independent simulation trials executed by simulate_multiple().

  • border (float) – Half-width of the square toroidal arena (mm).

  • max_steps (int) – Maximum number of PM steps per trial.

  • total_food (int) – Number of food items scattered in the arena at the start of each trial.

  • food_mode (str) – Food-distribution mode — 'random' or 'clustered'.

  • condition (str) – Sensory condition — 'dark' (tactile body-overlap detection) or 'light' (visual detection within a 20 mm radius).

food_found_total

Number of food items collected in the most recent single trial.

all_performed_pms

List of PM indices visited during the most recent trial.

psi_angles

Drift (ψ) angles between body heading and trajectory direction.

Example

>>> import numpy as np
>>> from satyre import MarkovWalker
>>> trans = np.eye(5, dtype=float)        # dummy identity
>>> trans = np.cumsum(trans, axis=1)
>>> pm_idx = np.tile(np.arange(5), (5, 1)).astype(float)
>>> velos  = np.random.randn(5, 3) * 0.5
>>> walker = MarkovWalker(trans, pm_idx, velos,
...                       max_steps=100, n_trials=2)
>>> walker.simulate()
simulate()[source]

Run a single foraging trial.

Resets the agent, scatters food, then steps through PMs until max_steps is reached. Populates food_found_total and psi_angles.

Return type:

None

simulate_multiple()[source]

Run n_trials independent trials and collect results.

Return type:

dict[str, ndarray[tuple[Any, ...], dtype[TypeVar(_ScalarT, bound= generic)]]]

Returns:

Dictionary with the following keys (each value is an ndarray indexed by trial number). Column count is determined by the longest trial (hyperspace wrapping can add sub-steps); shorter trials are NaN-padded.

  • food_found — shape (n_trials,)

  • step_sizes — shape (n_trials, n_cols)

  • psi_angles — shape (n_trials, n_psi)

  • position_x — shape (n_trials, n_cols)

  • position_y — shape (n_trials, n_cols)

  • head_x, head_y, tail_x, tail_y

class satyre.simulation.LevyWalker(cauchy_alpha=1.0, *, n_trials=1000, border=10000.0, mode='cauchy', max_steps=1000, total_food=10000, food_mode='random')[source]

Simulate Lévy-flight or random-walk foraging in a toroidal arena.

The agent draws step sizes from either a Cauchy-derived heavy-tailed distribution (Lévy flight) or a Gaussian (uniform random walk), chooses a uniformly random direction, and checks for food overlap after each step.

Parameters:
  • cauchy_alpha (float) – Shape parameter for the Cauchy step-size distribution. Higher values produce shorter tails. Ignored when mode is 'uniform'.

  • n_trials (int) – Number of independent simulation trials executed by simulate_multiple().

  • border (float) – Half-width of the square toroidal arena (mm).

  • mode (str) – Step-size distribution — 'cauchy' for Lévy flight or 'uniform' for Gaussian random walk.

  • max_steps (int) – Maximum number of steps per trial.

  • total_food (int) – Number of food items placed at the start of each trial.

  • food_mode (str) – Food distribution — 'random' or 'clustered'.

food_found_total

Items collected in the most recent single trial.

step_size_arr

Step-size array from the most recent trial.

Example

>>> from satyre import LevyWalker
>>> walker = LevyWalker(cauchy_alpha=1.5, mode='cauchy',
...                     max_steps=500, n_trials=10)
>>> walker.simulate()
>>> print(walker.food_found_total)
simulate()[source]

Run a single foraging trial.

Resets the agent, scatters food, steps until max_steps, and populates food_found_total.

Return type:

None

simulate_multiple()[source]

Run n_trials independent trials.

Returns:

  • food_found — shape (n_trials,)

  • step_sizes — shape (n_trials, n_cols)

Column count n_cols is determined by the longest trial (hyperspace wrapping can add sub-steps beyond max_steps). Shorter trials are NaN-padded.

Return type:

dict[str, ndarray[tuple[Any, ...], dtype[TypeVar(_ScalarT, bound= generic)]]]

MarkovWalker

class satyre.simulation.markov_walker.MarkovWalker(transition_matrix, pm_index, velocity_array, *, n_trials=1000, border=10000.0, max_steps=10000, total_food=1000, food_mode='random', condition='dark')[source]

Bases: object

Simulate fly locomotion using a Hidden-Markov-Model of prototypical movements derived from experimental data.

The transition-probability matrix drives the selection of successive PMs. Each PM carries thrust, slip, and yaw velocities that determine displacement and body rotation.

Parameters:
  • transition_matrix (ndarray[tuple[Any, ...], dtype[floating]]) – Cumulative transition-probability matrix of shape (N, N) where N is the number of prototypical movements.

  • pm_index (ndarray[tuple[Any, ...], dtype[floating]]) – Row-sorted PM index matrix of shape (N, N). Each row maps cumulative-probability columns back to PM identifiers.

  • velocity_array (ndarray[tuple[Any, ...], dtype[floating]]) – Array of shape (N, 3) holding [thrust, slip, yaw] velocities for each PM (units: mm s⁻¹ / rad s⁻¹).

  • n_trials (int) – Number of independent simulation trials executed by simulate_multiple().

  • border (float) – Half-width of the square toroidal arena (mm).

  • max_steps (int) – Maximum number of PM steps per trial.

  • total_food (int) – Number of food items scattered in the arena at the start of each trial.

  • food_mode (str) – Food-distribution mode — 'random' or 'clustered'.

  • condition (str) – Sensory condition — 'dark' (tactile body-overlap detection) or 'light' (visual detection within a 20 mm radius).

food_found_total

Number of food items collected in the most recent single trial.

all_performed_pms

List of PM indices visited during the most recent trial.

psi_angles

Drift (ψ) angles between body heading and trajectory direction.

Example

>>> import numpy as np
>>> from satyre import MarkovWalker
>>> trans = np.eye(5, dtype=float)        # dummy identity
>>> trans = np.cumsum(trans, axis=1)
>>> pm_idx = np.tile(np.arange(5), (5, 1)).astype(float)
>>> velos  = np.random.randn(5, 3) * 0.5
>>> walker = MarkovWalker(trans, pm_idx, velos,
...                       max_steps=100, n_trials=2)
>>> walker.simulate()
BODY_WIDTH: float = 2.0
BODY_LENGTH: float = 3.0
SIM_RESOLUTION: int = 500
simulate()[source]

Run a single foraging trial.

Resets the agent, scatters food, then steps through PMs until max_steps is reached. Populates food_found_total and psi_angles.

Return type:

None

simulate_multiple()[source]

Run n_trials independent trials and collect results.

Return type:

dict[str, ndarray[tuple[Any, ...], dtype[TypeVar(_ScalarT, bound= generic)]]]

Returns:

Dictionary with the following keys (each value is an ndarray indexed by trial number). Column count is determined by the longest trial (hyperspace wrapping can add sub-steps); shorter trials are NaN-padded.

  • food_found — shape (n_trials,)

  • step_sizes — shape (n_trials, n_cols)

  • psi_angles — shape (n_trials, n_psi)

  • position_x — shape (n_trials, n_cols)

  • position_y — shape (n_trials, n_cols)

  • head_x, head_y, tail_x, tail_y

LevyWalker

class satyre.simulation.levy_walker.LevyWalker(cauchy_alpha=1.0, *, n_trials=1000, border=10000.0, mode='cauchy', max_steps=1000, total_food=10000, food_mode='random')[source]

Bases: object

Simulate Lévy-flight or random-walk foraging in a toroidal arena.

The agent draws step sizes from either a Cauchy-derived heavy-tailed distribution (Lévy flight) or a Gaussian (uniform random walk), chooses a uniformly random direction, and checks for food overlap after each step.

Parameters:
  • cauchy_alpha (float) – Shape parameter for the Cauchy step-size distribution. Higher values produce shorter tails. Ignored when mode is 'uniform'.

  • n_trials (int) – Number of independent simulation trials executed by simulate_multiple().

  • border (float) – Half-width of the square toroidal arena (mm).

  • mode (str) – Step-size distribution — 'cauchy' for Lévy flight or 'uniform' for Gaussian random walk.

  • max_steps (int) – Maximum number of steps per trial.

  • total_food (int) – Number of food items placed at the start of each trial.

  • food_mode (str) – Food distribution — 'random' or 'clustered'.

food_found_total

Items collected in the most recent single trial.

step_size_arr

Step-size array from the most recent trial.

Example

>>> from satyre import LevyWalker
>>> walker = LevyWalker(cauchy_alpha=1.5, mode='cauchy',
...                     max_steps=500, n_trials=10)
>>> walker.simulate()
>>> print(walker.food_found_total)
BODY_WIDTH: float = 2.0
BODY_LENGTH: float = 7.0
simulate()[source]

Run a single foraging trial.

Resets the agent, scatters food, steps until max_steps, and populates food_found_total.

Return type:

None

simulate_multiple()[source]

Run n_trials independent trials.

Returns:

  • food_found — shape (n_trials,)

  • step_sizes — shape (n_trials, n_cols)

Column count n_cols is determined by the longest trial (hyperspace wrapping can add sub-steps beyond max_steps). Shorter trials are NaN-padded.

Return type:

dict[str, ndarray[tuple[Any, ...], dtype[TypeVar(_ScalarT, bound= generic)]]]