Single Experiments

This page covers how to run single behavioral experiments in Larvaworld using the Exp simulation mode. We’ll explore two case studies: Dish Exploration and Olfactory Learning.


Overview

The Exp mode runs one simulation with fixed parameters, making it ideal for:

  • ✅ Quick testing of configurations

  • ✅ Video generation for presentations

  • ✅ Single-condition analysis

  • ✅ Model demonstrations

For more details on simulation modes, see Simulation Modes.


Case Study 1: Dish Exploration

Purpose

The Dish Exploration experiment is the fundamental baseline assay for studying spontaneous larval locomotion in a circular arena without odors or food.

        graph TB
    subgraph "Setup"
        Arena[Circular Arena<br/>60-90 mm diameter]
        Larvae[N larvae<br/>Uniform distribution]
        Duration[3-10 minutes<br/>Free movement]
    end

    subgraph "Metrics"
        Dispersion[Dispersion Index<br/>Spatial spread over time]
        Velocity[Velocity Distribution<br/>Speed patterns]
        Tortuosity[Path Tortuosity<br/>Trajectory straightness]
        Activity[Activity Patterns<br/>Runs, turns, pauses]
    end

    subgraph "Applications"
        Baseline[Baseline Behavior<br/>Control experiments]
        Validation[Model Validation<br/>Compare to real data]
        Phenotype[Phenotype Screening<br/>Behavioral differences]
    end

    Arena --> Dispersion
    Larvae --> Velocity
    Duration --> Tortuosity

    Dispersion --> Baseline
    Velocity --> Validation
    Tortuosity --> Phenotype
    Activity --> Phenotype

    style Arena fill:#2196f3,stroke:#1976d2,stroke-width:2px,color:#fff
    style Dispersion fill:#f44336,stroke:#d32f2f,stroke-width:2px,color:#fff
    style Baseline fill:#4caf50,stroke:#388e3c,stroke-width:2px,color:#000
    

Running via CLI

larvaworld Exp dish -N 10 -duration 0.5  # short demo run

Parameters:

  • -N 10: 10 larvae

  • -duration 0.5: 0.5 minutes simulated time

Running via Python

from larvaworld.lib import reg
from larvaworld.lib.sim import ExpRun

# Load template
exp_params = reg.conf.Exp.getID("dish").get_copy()

# Create experiment (headless, short demo duration)
run = ExpRun(experiment="dish", parameters=exp_params, duration=0.1, screen_kws={})

# Run simulation
run.simulate()

# Access dataset
dataset = run.datasets[0]
print("Endpoint metrics (sample):")
print(dataset.e[["length", "cum_dur"]].head())
print("Step-wise sample:")
print(dataset.s.head())

Key Metrics

Computed automatically by Larvaworld:

Metric

Description

Unit

Dispersion Index

Spatial spread over time

cm

Linear Velocity

Forward speed distribution

mm/s

Angular Velocity

Turning rate distribution

rad/s

Tortuosity

Path straightness

dimensionless

Activity Index

Fraction of time active

0-1

Stride Frequency

Crawling frequency

Hz

Turn Rate

Reorientation frequency

1/s

Analysis Example

# Process dataset
dataset.preprocess(
    drop_collisions=False,
    interpolate_nans=True,
    filter_f=3.0  # Low-pass filter at 3 Hz
)

dataset.process(
    proc_keys=["angular", "spatial"],
    dsp_starts=[0],
    dsp_stops=[40, 60],
    tor_durs=[5, 10, 20],
)

dataset.annotate(
    anot_keys=[
        "bout_detection",
        "bout_distribution",
        "interference",
    ]
)

# Print summary statistics
print(dataset.e)  # Endpoint metrics

Case Study 2: Olfactory Learning

Purpose

The Olfactory Learning experiment demonstrates how larvae associate odors with food rewards through reinforcement learning.

        sequenceDiagram
    participant L as Larvae
    participant E as Environment
    participant M as Memory System

    Note over L,E: Pre-Test Phase
    L->>E: Naive odor preference
    E-->>L: Odor A & B present
    Note over M: Baseline preference (PI₀)

    Note over L,E: Training Phase
    L->>E: Conditioning
    E-->>L: Odor A + Food (CS+)
    E-->>L: Odor B alone (CS-)
    L->>M: Associate A with food (reward signal)
    Note over M: Q-learning updates gain

    Note over L,E: Test Phase
    L->>E: Trained preference
    E-->>L: Odor A & B present (no food)
    M->>L: Recall association (high gain for A)
    L->>E: Prefer Odor A
    Note over M: Memory expressed (PI > PI₀)

    Note over L,E: Post-Test
    L->>E: Extinction or retention
    Note over M: Memory decay analysis
    

Experimental Phases

Phase

Duration

Odor A

Odor B

Food

Purpose

Pre-Test

3 min

Present

Present

Absent

Baseline preference (PI₀)

Training

10 min

Present

Present

With A only

Conditioning (CS+/CS-)

Test

3 min

Present

Present

Absent

Memory expression (PI)

Post-Test

3 min

Present

Present

Absent

Memory retention

Running Training Phase

larvaworld Exp PItrain -N 30 -duration 0.2  # short demo run (use 10.0 for full protocol)

Running Test Phase

larvaworld Exp PItest_off -N 30 -duration 0.1  # short demo run (use 3.0 for full protocol)

Complete Protocol (Python)

from larvaworld.lib import reg, util
from larvaworld.lib.sim import ExpRun

screen_kws = {}  # headless

# 1. Pre-Test (baseline) -- shortened for demo
pre_params = reg.conf.Exp.getID("PItest_off").get_copy()
pre_params.duration = 0.1
pre = ExpRun(
    experiment="PItest_off",
    parameters=pre_params,
    screen_kws=screen_kws,
    store_data=False,
)
pre.simulate()
pre_ds = pre.datasets[0]
xs_pre = pre_ds.e["x"].values
arena_xdim = pre_ds.c.env_params.arena.dims[0]
pi_baseline = util.comp_PI(arena_xdim=arena_xdim, xs=xs_pre)
print(f"Baseline PI: {pi_baseline:.3f}")

# 2. Training (odor + food conditioning) -- shortened for demo
train_params = reg.conf.Exp.getID("PItrain").get_copy()
train_params.duration = 0.2
train = ExpRun(
    experiment="PItrain",
    parameters=train_params,
    screen_kws=screen_kws,
    store_data=False,
)
train.simulate()

# 3. Test (memory expression) -- shortened for demo
test_params = reg.conf.Exp.getID("PItest_off").get_copy()
test_params.duration = 0.1
test = ExpRun(
    experiment="PItest_off",
    parameters=test_params,
    screen_kws=screen_kws,
    store_data=False,
)
test.simulate()
test_ds = test.datasets[0]
xs_test = test_ds.e["x"].values
pi_trained = util.comp_PI(arena_xdim=arena_xdim, xs=xs_test)
print(f"Trained PI: {pi_trained:.3f}")
print(f"Learning effect: {pi_trained - pi_baseline:.3f}")

Preference Index (PI)

Definition: PI = (N_CS+ - N_CS-) / (N_CS+ + N_CS-)

Where:

  • N_CS+: Number of larvae on the CS+ side (e.g. left side) at the timepoint used for PI

  • N_CS-: Number of larvae on the CS- side (e.g. right side) at the timepoint used for PI

Interpretation:

  • PI = 0: No preference

  • PI > 0: Preference for CS+ (learning)

  • PI < 0: Avoidance of CS+

Computing PI:

from larvaworld.lib import util

xs = dataset.e["x"].values
arena_xdim = dataset.c.env_params.arena.dims[0]
PI = util.comp_PI(arena_xdim=arena_xdim, xs=xs)

print(f"Preference Index: {PI:.3f}")

Memory Mechanisms

Larvaworld implements two learning algorithms:

1. Q-Learning (Reinforcement Learning)

Algorithm: TD-learning with reward-modulated gain adaptation

# Memory module updates sensory gain
alpha = 0.1  # learning rate (example value)
if reward > 0:
    gain["odor_A"] += alpha * reward  # Increase gain for rewarded odor
    gain["odor_B"] -= alpha * reward  # Decrease gain for non-rewarded odor

Code Location: src/larvaworld/lib/model/modules/memory.py (RLmemory class)

2. Mushroom Body (MB) Model

Algorithm: Hebbian learning with KC-MBON synaptic plasticity

Code Location: src/larvaworld/lib/model/modules/memory.py (RemoteBrianModelMemory class)

Analysis Example

# Load dataset (e.g., from a PI test run)
from larvaworld.lib import util

dataset = test.datasets[0]  # produced by the runs above

# Compute preference index
xs = dataset.e["x"].values
arena_xdim = dataset.c.env_params.arena.dims[0]
PI = util.comp_PI(arena_xdim=arena_xdim, xs=xs)

# Plot odor navigation and trajectories
from larvaworld.lib.plot import traj
traj.traj_1group(dataset)

# Plot memory gain evolution (RL model)
if "gain" in dataset.step_data.columns:
    import matplotlib.pyplot as plt
    plt.plot(dataset.step_data["gain"])
    plt.xlabel("Time (s)")
    plt.ylabel("Sensory Gain")
    plt.title("Memory Gain Evolution")
    plt.show()

General Workflow

1. Select Experiment

Choose from 57 preconfigured experiments:

from larvaworld.lib import reg

# List available experiments
exp_ids = reg.conf.Exp.confIDs
print(f"Available: {len(exp_ids)} experiments")

# Inspect experiment
exp_conf = reg.conf.Exp.getID("chemotaxis")
print(exp_conf)

See Experiment Types for the full list.

2. Customize Parameters

Override default parameters:

from larvaworld.lib import reg, util
from larvaworld.lib.sim import ExpRun

# Start from an existing experiment template
exp_params = reg.conf.Exp.getID("chemotaxis").get_copy()

# Environment customization (override a template Env config)
env_params = reg.conf.Env.getID("arena_200mm").get_copy()
if env_params.odorscape is None:
    env_params.odorscape = util.AttrDict()
env_params.odorscape.update({"odorscape": "Gaussian", "grid_dims": (51, 51)})
exp_params.env_params = env_params

# Agent customization (larva groups are keyed by group_id)
exp_params.larva_groups = reg.gen.LarvaGroup(mID="navigator", N=30).entry("navigator")

run = ExpRun(
    experiment="chemotaxis",
    parameters=exp_params,
    duration=10.0,
    screen_kws={"vis_mode": "video", "save_video": True, "video_file": "chemotaxis"},
)

3. Run Simulation

run.simulate()

4. Access Results

# Get dataset
dataset = run.datasets[0]

# Summary statistics
print(dataset.endpoint_data)

# Time-series data
print(dataset.step_data.keys())

# Configuration
print(dataset.config)

5. Analyze Data

# Preprocessing
dataset.preprocess(filter_f=3.0)

# Metrics
dataset.process(proc_keys=["angular", "spatial"])

# Annotations
dataset.annotate(
    anot_keys=["bout_detection", "bout_distribution", "interference"]
)

# See :doc:`../visualization/plotting_api` for plotting examples.

6. Save Results

# Save to HDF5
run.store()

# Location: data/SimGroup/exp_runs/{experiment}/{id}/
print(f"Saved to: {run.dir}")

Video Generation

Create videos for presentations or publications:

from larvaworld.lib import reg
from larvaworld.lib.sim import ExpRun

run = ExpRun(
    experiment="dish",
    parameters=reg.conf.Exp.getID("dish").get_copy(),
    duration=0.5,
    screen_kws={
        "vis_mode": "video",
        "save_video": True,
        "video_file": "dish_exploration",
        "fps": 10,
    }
)
run.simulate()

For keyboard controls during visualization, see Keyboard Controls.