ISTA Internship at Prof. Tim Vogels group
Project title: Cross-Implementation and Verification in Brian2 of the Dynamic Engrams Consolidation Model
Internship dates: June - September 2025
This project is a verification of a memory consolidation simulation with a Spiking Neural Networks (SNN) model implemented in Brian2 (Python), originally defined in Auryn (C++) in:
- Tomé, D.F., Zhang, Y., Aida, T. et al. (2024) Dynamic and selective engrams emerge with memory consolidation. Nature Neuroscience 27, 561–572 https://doi.org/10.1038/s41593-023-01551-w
Everything here is a re-implementation, so it is worth being explicit about where the inputs come from:
| What | Source |
|---|---|
| Model definition | The paper cited above |
| All parameter values | The supplementary information table of that paper (PDF) |
Stimulus patterns (stimuli/*.pat) |
Downloaded from the original Auryn implementation, under src/data/: douglastome/dynamic-engrams v1.0.0 |
The .pat files are used as downloaded rather than regenerated, so the stimulus this
model sees is the same one the original simulation used. Every constant in
config/parameters.py is taken from the supplementary table, which is the reference
to check against when a value looks surprising.
A recurrent spiking network of 4096 excitatory and 1024 inhibitory adaptive
leaky integrate-and-fire neurons, driven by a 4096-unit Poisson stimulus
population. Recurrent connectivity is random with probability epsilon_rec.
Three plasticity mechanisms run concurrently:
| Mechanism | Where | Purpose |
|---|---|---|
Long-term excitatory plasticity (triplet STDP, heterosynaptic term, consolidation variable w_tilde) |
Stim→E, E→E |
forms and consolidates the engram |
Inhibitory STDP gated by a global signal G |
I→E |
holds the network at the target rate r_target |
Short-term plasticity (u, x) |
Stim→E, E→E |
scales release by R = u * x (off by default, see scripts/README.md) |
A single-neuron controller group integrates the excitatory population rate into
H and exposes G = H - r_target * tau_H, which is linked into every I→E synapse.
This is the homeostatic feedback loop.
An engram is the set of excitatory neurons whose stimulus-evoked firing rate
during stimulus-ON bins exceeds thres_sefr.
scripts/simulate_and_probe.py runs the full pipeline:
| # | Phase | Simulated duration | Stimulus | What happens |
|---|---|---|---|---|
| 1 | Burn-in | T_burn |
background only (v_bg) |
network settles to the homeostatic setpoint before anything is learned |
| 2 | Training | T_training |
ON/OFF windows | the engram forms, then it is computed from the last 60 s of activity |
| 3 | Consolidation | T_consolidation |
ON/OFF windows | the engram reorganises, and a checkpoint is stored every probe_interval |
| 4 | Probing | 60 s per checkpoint | replayed training gate | re-measures the engram at each checkpoint, so its drift over time can be tracked |
| 5 | Recall | 90 s | partial cue (squarev5_cues_1-4.pat) |
tests whether a partial cue reactivates the stored engram |
The model is expensive, so two phases had to be cut down from the original design and one mechanism is left switched off:
- Consolidation was reduced from 24 h to 10 h of simulated time. The original
design consolidates for 24 h (
86400*second), while the active setting isT_consolidation = 36000*second. - Recall could not be run over the whole time course. The intended sweep runs
recall from every consolidation checkpoint. That block is left commented out in
scripts/simulate_and_probe.py, and only the final checkpoint is actually run. - Short-term plasticity is implemented but switched off by default. Turning it on
adds two event-driven variables (
uandx), which makes the simulation considerably more expensive to run.
All three are single-line changes if more compute becomes available. See
config/README.md and scripts/README.md,
the latter for how to switch short-term plasticity on and what it does to the network.
| Folder | What lives there |
|---|---|
config/ |
parameters, the profile toggle, save and load helpers |
model/ |
neuron and synapse group construction |
network/ |
assembles neurons, synapses, stimulus and controller into one Network |
simulation/ |
run directories, logging, phase execution, sanity checks |
scripts/ |
entry points you actually execute |
analysis/ |
engram definition, probing and recall, loaders and plotting |
stimuli/ |
input patterns from the original implementation: squarev5.pat, squarev5_cues_1-4.pat |
data/ |
generated stimulus-to-excitatory connectivity (pre_ids.npy, post_ids.npy), git-ignored |
src/ |
simulation outputs, one directory per run, git-ignored |
Notebooks at the root:
1_activity_plots.ipynbloads a burn-in run (rasters, rates, weight distributions, homeostaticH)2_probing_n_recall_plots.ipynbloads a full pipeline run (engram overlap across consolidation, and recall)
- Create and activate a virtual environment
python -m venv .venv
.venv\Scripts\Activate.ps1 # PowerShell; source .venv/bin/activate on Linux/macOS- Install dependencies
pip install -r requirements.txt- Generate the stimulus-to-excitatory connectivity map
python -m scripts.generate_stim_input_mapImportant
This draws a random
receptive-field centre for every excitatory neuron and writes the resulting
connectivity to data/pre_ids.npy and data/post_ids.npy.
model/synapses.py loads those files at import time, so nothing runs until they
exist. Because the draw is unseeded, re-running the script produces a different
network, which is precisely why the map is generated once and then reused by
every run. See scripts/README.md.
- Run the pipeline
python -m scripts.simulate_and_probeNote
To run only the burn-in phase, with monitors recording both excitatory and inhibitory populations:
python -m scripts.monitor_stable_activityThis script takes an optional profile, an optional duration in seconds, and an
optional stp flag. Short-term plasticity is off by default. See
scripts/README.md.
Parameters come from one place at runtime: config.active. Which set it resolves to
is chosen by a single toggle, with no source edits anywhere:
python -m scripts.monitor_stable_activity # config.parameters
python -m scripts.monitor_stable_activity calibration # config.parameters_calibrationEvery module (model, network, scripts, analysis) imports from config.active, never
from config.parameters directly. The calibration profile lists only what it
overrides, and the run log prints those overrides at the top. See
config/README.md.
Each run creates src/run_<timestamp>_seed<N>/:
run_<id>/
run_<id>.log # full stdout and stderr, starting with the parameter banner
monitor_data/ # spike monitors and controller H, as .npz
weights/ # per-synapse w / w_tilde snapshots, as .npz
engrams/ # engram indices per probe checkpoint
recall/ # recall engrams
ckpt/ # created but unused: net.store() checkpoints live in memory
Set SEED_N to control the seed (and the directory name). RUN_ID overrides the
name entirely. scripts/run_many.sh sweeps a list of seeds.
1_activity_plots.ipynb loads a burn-in run, 2_probing_n_recall_plots.ipynb loads
a full pipeline run. Both read from src/run_<id>/ through the loaders in
analysis/.
- Run every script from the project root, so that
python -m ...and the relative paths (data/,stimuli/) resolve correctly. - Brian2 uses compiled (Cython) code when a C++ toolchain is available and falls back to a slower numpy target otherwise. The fallback is reported in the log.