Brain Module Architecture
The Brain module integrates sensory input, memory systems, and locomotor control. Larvaworld provides two implementations: DefaultBrain (standard) and NengoBrain (neural network-based).
Brain architecture overview
graph TD
%% Brain implementations
subgraph Brain_Impl ["Brain Implementations"]
BrainBase["Brain"<br/>Base class]:::base
DefaultBrain["DefaultBrain"<br/>Standard implementation]:::impl
NengoBrain["NengoBrain"<br/>Nengo-based implementation]:::impl
end
BrainBase --> DefaultBrain
BrainBase --> NengoBrain
%% Modalities
subgraph Modalities
OlfMod["olfaction"<br/>odor sensing]:::modality
TouchMod["touch"<br/>mechanoreception/feeding]:::modality
ThermoMod["thermosensation"<br/>temperature]:::modality
WindMod["windsensation"<br/>wind/airflow]:::modality
end
%% Sensors
OlfSensor["Olfactor"<br/>odor sensor]:::sensor
TouchSensor["Toucher"<br/>contact/food sensor]:::sensor
ThermoSensor["Thermosensor"<br/>temperature sensor]:::sensor
WindSensor["Windsensor"<br/>wind sensor]:::sensor
%% Memory modules
RLmem["RLOlfMemory / RLTouchMemory"<br/>reinforcement learning]:::memory
NullMem["No memory"]:::memory
%% Locomotor interface
subgraph Locomotor_System ["Locomotor System"]
Locomotor["Locomotor"<br/>crawl/turn/feed]:::locomotor
A_in["A_in"<br/>sensory drive]:::signal
MotorCmds["motor commands"<br/>crawl/turn/feed rates]:::output
end
%% Wiring: Brain -> Modalities
DefaultBrain --> OlfMod
DefaultBrain --> TouchMod
DefaultBrain --> ThermoMod
DefaultBrain --> WindMod
%% Wiring: Modalities -> Sensors
OlfMod --> OlfSensor
TouchMod --> TouchSensor
ThermoMod --> ThermoSensor
WindMod --> WindSensor
%% Wiring: Memory attachment (example: olfaction)
OlfMod --> RLmem
TouchMod --> NullMem
ThermoMod --> NullMem
WindMod --> NullMem
%% Wiring: Modalities -> Locomotor
OlfMod --> A_in
TouchMod --> A_in
ThermoMod --> A_in
WindMod --> A_in
A_in --> Locomotor
Locomotor --> MotorCmds
%% Color definitions
classDef base fill:#2c3e50,stroke:#34495e,stroke-width:3px,color:#ffffff
classDef impl fill:#3498db,stroke:#2980b9,stroke-width:2px,color:#ffffff
classDef modality fill:#9b59b6,stroke:#8e44ad,stroke-width:2px,color:#ffffff
classDef sensor fill:#f39c12,stroke:#e67e22,stroke-width:2px,color:#ffffff
classDef signal fill:#e74c3c,stroke:#c0392b,stroke-width:2px,color:#ffffff
classDef memory fill:#e91e63,stroke:#c2185b,stroke-width:2px,color:#ffffff
classDef locomotor fill:#27ae60,stroke:#229954,stroke-width:2px,color:#ffffff
classDef output fill:#f1c40f,stroke:#f39c12,stroke-width:3px,color:#000000
Sensory Modalities
The brain organizes sensors into modalities, each processing a specific sensory channel:
Modality |
Sensor Class |
Signal |
Memory |
Purpose |
|---|---|---|---|---|
olfaction |
|
|
Optional |
Odor detection |
touch |
|
|
Optional |
Contact/food sensing |
thermosensation |
|
|
Optional |
Temperature sensing |
windsensation |
|
|
Optional |
Wind/airflow sensing |
Modality Structure
# This mirrors `Brain.modalities` (see `larvaworld/lib/model/modules/brain.py`)
modality = {
"sensor": self.olfactor, # Sensor instance (or None)
"func": self.sense_odors, # Processing function
"A": 0.0, # Sensory drive from this modality
"mem": None, # Optional memory module (per modality)
}
Sensor Modules
Olfactor
Purpose: Odor detection
Key Attributes:
gain_dict/gain: Gain per odor ID (memory can update this per step)X: Current odor concentrations per odor IDdX: Perceived change per odor ID (depends onperception:log/linear/null)output: Integrated sensory drive (used as modalityA)
Processing:
Query odorscape value layers at larva position (via
Brain.sense_odors)Compute
dXfrom current/previousXbased onperceptionIntegrate
outputusinggain[id] * dX[id]with exponential decay (decay_coef)
Code Location: /lib/model/modules/sensor.py (class Olfactor)
Toucher
Purpose: Tactile sensing (touch sensors around the body contour)
Key Attributes:
touch_sensors: Indices of touch sensors on the body contourgain_dict/gain: Gain per touch sensor IDX/dX/output: Same Sensor state variables as above
Processing:
Brain.init_sensors()registers touch sensors on the agent bodyBrain.sense_food_multi()converts touch sensor positions to adictof0/1activationsToucher.step(input=...)integrates these into a tactile driveA
Code Location: /lib/model/modules/sensor.py (class Toucher)
Windsensor
Purpose: Wind/airflow detection
Key Attributes:
weights: Wind response weights (passed from brain config)gain_dict/gain: Gain for the"windsensor"channel (default{"windsensor": 1.0})perception: Fixed to"null"(direct transduction)
Code Location: /lib/model/modules/sensor.py (class Windsensor)
Thermosensor
Purpose: Temperature sensing
Key Attributes:
gain_dict/gain: Gains for warm/cool channelsX/dX/output: Same Sensor state variables as above
Code Location: /lib/model/modules/sensor.py (class Thermosensor)
Memory Modules
Memory modules attach to sensory modalities and modulate their gain through learning.
RLmemory
Algorithm: Q-learning (reinforcement learning) via a Q-table over discretized sensory change states.
Mechanism:
Uses
rewardSum+dxto update the Q-table and choose gain actions fromgain_space.
Code Location: /lib/model/modules/memory.py (RLmemory class)
RemoteBrianModelMemory (MB memory)
Algorithm: Mushroom Body model (Hebbian learning)
Mechanism:
KC-MBON synaptic plasticity
Reward-modulated learning
Code Location: /lib/model/modules/memory.py (RemoteBrianModelMemory class)
Locomotor Integration
The brain coordinates locomotor modules through the Locomotor class:
graph LR
BRAIN[Brain] --> LOCOMOTOR[Locomotor]
LOCOMOTOR --> CRAWLER[Crawler]
LOCOMOTOR --> TURNER[Turner]
LOCOMOTOR --> FEEDER[Feeder]
LOCOMOTOR --> INTERMITTER[Intermitter]
Brain → Locomotor Flow
Brain.sense(): Update each enabled sensor module and its modality drive
ABrain.step(): Call
Locomotor.step(A_in=..., on_food=...)Locomotor.step(): Run crawler/turner/feeder (+ interference + intermitter state machine)
DefaultBrain
Implementation: Rule-based sensorimotor control
Step Sequence:
def step(self, pos, on_food=False, **kwargs):
self.sense(pos=pos, reward=on_food)
return self.locomotor.step(A_in=self.A_in, on_food=on_food, **kwargs)
NengoBrain
Implementation: Spiking neural network (Nengo)
Warning
NengoBrain is experimental. In the current codebase it may fail at runtime with newer nengo versions (e.g. nengo.exceptions.ReadonlyError: probes is read-only) when initializing internal probes.
If you hit this, use DefaultBrain models until NengoBrain is updated for the installed nengo API.
Architecture:
Input: Sensory neurons (olfactory, tactile, etc.)
Hidden: Processing layers
Output: Motor neurons (forward, turn)
Step Sequence:
def step(self):
# See `larvaworld/lib/model/modules/nengobrain.py` for the real implementation.
# NengoBrain runs an internal Nengo Simulator and maps probe outputs to (lin, ang, feed_motion).
...
Configuration
Enable Specific Modalities
from larvaworld.lib import reg
from larvaworld.lib.sim import ExpRun
from larvaworld.lib.util import AttrDict
exp_params = reg.conf.Exp.getID("dish").get_copy()
model_conf = reg.conf.Model.getID("navigator").get_copy()
# Disable modalities by disabling their sensor modules in the brain config
model_conf.brain.windsensor = None
model_conf.brain.thermosensor = None
exp_params.larva_groups = AttrDict(
{
"nav": AttrDict(
{
"model": model_conf,
"distribution": AttrDict({"shape": "circle", "mode": "uniform", "N": 5}),
}
)
}
)
run = ExpRun(
experiment="dish",
parameters=exp_params,
duration=0.5,
screen_kws={"show_display": False, "vis_mode": None},
store_data=False,
)
run.simulate()
Attach Memory
from larvaworld.lib import reg
from larvaworld.lib.model.modules.module_modes import moduleDB
model_conf = reg.conf.Model.getID("navigator").get_copy()
model_conf.brain.memory = moduleDB.memory_kws(
mode="RL", modality="olfaction", as_entry=False
)
Use NengoBrain
from larvaworld.lib import reg
# NengoBrain is used when the model's crawler module has mode "nengo".
# Built-in model IDs include many `nengo_*` variants:
nengo_ids = [m for m in reg.conf.Model.confIDs if m.startswith("nengo_")]
print(nengo_ids[:20])