allen-circuit-dynamics 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- allen_circuit_dynamics-0.2.0/.gitignore +21 -0
- allen_circuit_dynamics-0.2.0/CHANGELOG.md +19 -0
- allen_circuit_dynamics-0.2.0/CITATION.cff +62 -0
- allen_circuit_dynamics-0.2.0/LICENSE +21 -0
- allen_circuit_dynamics-0.2.0/PKG-INFO +351 -0
- allen_circuit_dynamics-0.2.0/README.md +303 -0
- allen_circuit_dynamics-0.2.0/data/README.md +10 -0
- allen_circuit_dynamics-0.2.0/pyproject.toml +80 -0
- allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/__init__.py +49 -0
- allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/allen_data.py +236 -0
- allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/cli.py +264 -0
- allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/dataset.py +133 -0
- allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/decode.py +86 -0
- allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/experiments.py +218 -0
- allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/experiments_force.py +346 -0
- allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/klee_data.py +183 -0
- allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/lagged.py +61 -0
- allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/models/__init__.py +9 -0
- allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/models/baselines.py +80 -0
- allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/models/coupling.py +127 -0
- allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/models/shared_private.py +219 -0
- allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/preprocess.py +71 -0
- allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/provenance.py +50 -0
- allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/py.typed +0 -0
- allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/report.py +254 -0
- allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/synth.py +210 -0
- allen_circuit_dynamics-0.2.0/tests/conftest.py +25 -0
- allen_circuit_dynamics-0.2.0/tests/test_dataset.py +39 -0
- allen_circuit_dynamics-0.2.0/tests/test_experiments.py +74 -0
- allen_circuit_dynamics-0.2.0/tests/test_force.py +35 -0
- allen_circuit_dynamics-0.2.0/tests/test_lagged.py +36 -0
- allen_circuit_dynamics-0.2.0/tests/test_shared_private.py +82 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.pyc
|
|
3
|
+
.venv/
|
|
4
|
+
.pytest_cache/
|
|
5
|
+
.ruff_cache/
|
|
6
|
+
*.egg-info/
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
data/ecephys_cache/
|
|
10
|
+
data/nwb/
|
|
11
|
+
data/ecephys/*.npz
|
|
12
|
+
data/*.npy
|
|
13
|
+
outputs/*
|
|
14
|
+
!outputs/demo/
|
|
15
|
+
!outputs/demo/**
|
|
16
|
+
.coverage
|
|
17
|
+
!outputs/*_715093703/
|
|
18
|
+
!outputs/*_715093703/**
|
|
19
|
+
!outputs/force_klee/
|
|
20
|
+
!outputs/force_klee/**
|
|
21
|
+
outputs/force_klee/*.log
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.0 — 2026-10-31
|
|
4
|
+
- Experiment 3: D1 vs D2 striatal neurons, shared / private covariance and the lever, on the public Klee et al. (2026) two-photon dataset (six mice). Pre-registered (`docs/experiment3_preregistration.md`, `b1676da`) before the run; verdicts and effect sizes in the README. Loader (`klee_data.py`), count-matched draws, three-way partition, motion-regressed decoding, lag profile, `acd force`, `scripts/evaluate_prereg.py`.
|
|
5
|
+
- `PopulationDataset.extras` for per-bin covariates; `lagged.py` block-safe lag pairing and within-block shift null.
|
|
6
|
+
- README reframed around three output contracts (observations / state / action).
|
|
7
|
+
- Packaging: public API re-exported from `allen_circuit_dynamics`, `py.typed`, PyPI metadata and classifiers, `CITATION.cff`, `CONTRIBUTING.md`, `docs/DEVLOG.md` (was `RESTART.md`), `scripts/bootstrap_linux.sh` (was `blade_bootstrap.sh`), release workflow with PyPI trusted publishing.
|
|
8
|
+
|
|
9
|
+
## 0.1.1 — 2026-10-30
|
|
10
|
+
- First real-session run (Visual Coding Neuropixels 715093703); results in README.
|
|
11
|
+
- `acd fetch` chooses areas from the session (top VIS* by QC-passing units, `--min-units`, `--max-areas`) instead of a hard-coded list.
|
|
12
|
+
- Coupling penalty made scale-free: λ is dimensionless (scaled by trace(X'X)/K); grid 0–10. Permutation p-value and within-area share added to Experiment 2's metrics.
|
|
13
|
+
- Bootstrap: allensdk isolated in `.venv-allen`; pynwb/hdmf/boto3/six/dateutil pinned; parallel ranged S3 download (`scripts/pdl.sh`); post-fetch steps split into `scripts/run_experiments.sh`.
|
|
14
|
+
|
|
15
|
+
## 0.1.0 — 2026-10-28
|
|
16
|
+
- Shared/private block-sparse factor analysis (EM), RRR/CCA/FA baselines, grouped-CV decoding with shift null.
|
|
17
|
+
- Connectivity-penalised linear dynamics with shuffled-atlas control.
|
|
18
|
+
- Synthetic generators for both experiments; `acd demo`; 14 tests.
|
|
19
|
+
- AllenSDK loader for Visual Coding Neuropixels + Mouse Connectivity Atlas projection matrix (untested on real data).
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use this software, please cite it as below."
|
|
3
|
+
title: "allen-circuit-dynamics: shared/private latent-variable models and connectivity-constrained dynamics for simultaneously recorded neural populations"
|
|
4
|
+
type: software
|
|
5
|
+
authors:
|
|
6
|
+
- family-names: Gracey
|
|
7
|
+
given-names: Jeremy
|
|
8
|
+
email: jeremy.a.gracey@gmail.com
|
|
9
|
+
version: 0.2.0
|
|
10
|
+
date-released: 2026-10-31
|
|
11
|
+
license: MIT
|
|
12
|
+
repository-code: "https://github.com/JeremyGracey-AI/allen-circuit-dynamics"
|
|
13
|
+
keywords:
|
|
14
|
+
- neuroscience
|
|
15
|
+
- factor analysis
|
|
16
|
+
- latent variable models
|
|
17
|
+
- Neuropixels
|
|
18
|
+
- calcium imaging
|
|
19
|
+
- communication subspace
|
|
20
|
+
references:
|
|
21
|
+
- type: article
|
|
22
|
+
title: "Cortical areas interact through a communication subspace"
|
|
23
|
+
authors:
|
|
24
|
+
- family-names: Semedo
|
|
25
|
+
given-names: João D.
|
|
26
|
+
- family-names: Zandvakili
|
|
27
|
+
given-names: Amin
|
|
28
|
+
- family-names: Machens
|
|
29
|
+
given-names: Christian K.
|
|
30
|
+
- family-names: Yu
|
|
31
|
+
given-names: Byron M.
|
|
32
|
+
- family-names: Kohn
|
|
33
|
+
given-names: Adam
|
|
34
|
+
journal: Neuron
|
|
35
|
+
year: 2019
|
|
36
|
+
- type: article
|
|
37
|
+
title: "Survey of spiking in the mouse visual system reveals functional hierarchy"
|
|
38
|
+
authors:
|
|
39
|
+
- family-names: Siegle
|
|
40
|
+
given-names: Joshua H.
|
|
41
|
+
journal: Nature
|
|
42
|
+
year: 2021
|
|
43
|
+
notes: "Visual Coding Neuropixels dataset (Experiments 1 and 2)"
|
|
44
|
+
- type: article
|
|
45
|
+
title: "Shared striatal neurons exhibit context-specific dynamics for internally and externally driven actions"
|
|
46
|
+
authors:
|
|
47
|
+
- family-names: Klee
|
|
48
|
+
given-names: Jan Lukas
|
|
49
|
+
- family-names: Fernando-Peiris
|
|
50
|
+
given-names: Sulekh
|
|
51
|
+
- family-names: Athalye
|
|
52
|
+
given-names: Vivek
|
|
53
|
+
- family-names: Costa
|
|
54
|
+
given-names: Rui M.
|
|
55
|
+
- family-names: Peterka
|
|
56
|
+
given-names: Darcy
|
|
57
|
+
- family-names: Sippy
|
|
58
|
+
given-names: Tanya
|
|
59
|
+
journal: Science Advances
|
|
60
|
+
year: 2026
|
|
61
|
+
doi: 10.1126/sciadv.aed9386
|
|
62
|
+
notes: "Dataset for Experiment 3, Zenodo 10.5281/zenodo.16333867 (CC BY 4.0)"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jeremy Gracey
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: allen-circuit-dynamics
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Shared/private latent-variable models and connectivity-constrained dynamics for simultaneously recorded neural populations, run on open Allen Institute data.
|
|
5
|
+
Project-URL: Homepage, https://github.com/JeremyGracey-AI/allen-circuit-dynamics
|
|
6
|
+
Project-URL: Repository, https://github.com/JeremyGracey-AI/allen-circuit-dynamics
|
|
7
|
+
Project-URL: Issues, https://github.com/JeremyGracey-AI/allen-circuit-dynamics/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/JeremyGracey-AI/allen-circuit-dynamics/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: Jeremy Gracey <jeremy.a.gracey@gmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: allen-brain-observatory,calcium-imaging,communication-subspace,factor-analysis,latent-variable-models,linear-dynamical-systems,neuropixels,neuroscience
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Requires-Dist: h5py>=3.8
|
|
27
|
+
Requires-Dist: matplotlib>=3.6
|
|
28
|
+
Requires-Dist: numpy>=1.23
|
|
29
|
+
Requires-Dist: pandas>=1.5
|
|
30
|
+
Requires-Dist: pyyaml>=6
|
|
31
|
+
Requires-Dist: scikit-learn>=1.2
|
|
32
|
+
Requires-Dist: scipy>=1.9
|
|
33
|
+
Provides-Extra: allen
|
|
34
|
+
Requires-Dist: allensdk>=2.16; extra == 'allen'
|
|
35
|
+
Requires-Dist: boto3>=1.17; extra == 'allen'
|
|
36
|
+
Requires-Dist: hdmf<3.14,>=3.5; extra == 'allen'
|
|
37
|
+
Requires-Dist: numpy<2; extra == 'allen'
|
|
38
|
+
Requires-Dist: pandas<2.1; extra == 'allen'
|
|
39
|
+
Requires-Dist: pynwb<2.6,>=2.3; extra == 'allen'
|
|
40
|
+
Requires-Dist: python-dateutil>=2.8.2; extra == 'allen'
|
|
41
|
+
Requires-Dist: six>=1.16; extra == 'allen'
|
|
42
|
+
Provides-Extra: dev
|
|
43
|
+
Requires-Dist: hypothesis>=6; extra == 'dev'
|
|
44
|
+
Requires-Dist: pytest-cov>=5; extra == 'dev'
|
|
45
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
46
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
47
|
+
Description-Content-Type: text/markdown
|
|
48
|
+
|
|
49
|
+
# allen-circuit-dynamics
|
|
50
|
+
|
|
51
|
+
[](https://github.com/JeremyGracey-AI/allen-circuit-dynamics/actions/workflows/ci.yml)
|
|
52
|
+
[](https://pypi.org/project/allen-circuit-dynamics/)
|
|
53
|
+
[](https://pypi.org/project/allen-circuit-dynamics/)
|
|
54
|
+
[](LICENSE)
|
|
55
|
+
|
|
56
|
+
Latent-variable models of simultaneously recorded neural populations, run on
|
|
57
|
+
open data from the Allen Institute and its collaborators, with a pipeline that
|
|
58
|
+
runs end-to-end on synthetic data in under a minute. Independent work; not
|
|
59
|
+
affiliated with or endorsed by the Allen Institute.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install allen-circuit-dynamics
|
|
63
|
+
acd demo --out outputs/demo # both Neuropixels experiments on synthetic data, ~30 s, no downloads
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from allen_circuit_dynamics import PopulationDataset, SharedPrivateFA, run_latent_experiment
|
|
68
|
+
|
|
69
|
+
ds = PopulationDataset.load("my_session.npz") # x1, x2, behavior, blocks, bin_s
|
|
70
|
+
metrics = run_latent_experiment(ds) # held-out selection, partition, decoding
|
|
71
|
+
model = SharedPrivateFA(d_shared=3, d_private=(2, 2)).fit(ds.x1, ds.x2)
|
|
72
|
+
latents = model.transform(ds.x1, ds.x2) # {"shared": ..., "private1": ..., "private2": ...}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
One model sits underneath: a population's activity is a few shared latents,
|
|
76
|
+
a few private latents, and per-neuron noise, possibly with linear dynamics on
|
|
77
|
+
the latents. The three experiments score that model on three different
|
|
78
|
+
**output contracts**, in the sense of asking what the model is *for*:
|
|
79
|
+
|
|
80
|
+
| | Contract | Scored by | Data |
|
|
81
|
+
|---|---|---|---|
|
|
82
|
+
| Exp 1 | **Observations** — reconstruct the recorded activity | held-out log-likelihood; shared / private / independent variance partition | Visual Coding Neuropixels, VISrl ↔ VISp |
|
|
83
|
+
| Exp 2 | **State** — predict the next latent state | held-out one-step R², with and without an anatomical prior | Visual Coding Neuropixels, five visual areas + Mouse Connectivity Atlas |
|
|
84
|
+
| Exp 3 | **Action** — predict the animal's output from the latents | held-out R² of lever displacement per subspace, lag profile, pre-registered | Klee et al. 2026 two-photon D1 / D2 striatum (Zenodo) |
|
|
85
|
+
|
|
86
|
+
A model that scores well on one contract need not score well on another; the
|
|
87
|
+
point of running all three is to say which one each dataset supports. (The
|
|
88
|
+
observation / state / action split is the agent–environment loop with the
|
|
89
|
+
latent as the state; the same three-way reading has been used to sort
|
|
90
|
+
"world models" by what they emit.)
|
|
91
|
+
|
|
92
|
+
Status: all three pipelines tested on synthetic data with planted structure;
|
|
93
|
+
Experiments 1 and 2 run once on Visual Coding session 715093703 (VISrl, VISp,
|
|
94
|
+
VISpm, VISl, VISam; 50 ms bins inside `natural_movie_one`; 12,009 bins);
|
|
95
|
+
Experiment 3 pre-registered in `docs/experiment3_preregistration.md` before
|
|
96
|
+
the run and run on all six mice of the Klee et al. dataset. Every result below
|
|
97
|
+
is a first pass on one session per animal, not a study. Provenance: Experiment
|
|
98
|
+
1 outputs at commit `896f68c` (code unchanged since), Experiment 2 at
|
|
99
|
+
`dbc8ed8`, Experiment 3 at the SHA in `outputs/force_klee/*/metrics.json`.
|
|
100
|
+
|
|
101
|
+
## Experiment 1 — shared / private factor analysis
|
|
102
|
+
|
|
103
|
+
Model: factor analysis on the concatenated vector `[x1, x2]` with a
|
|
104
|
+
block-sparse loading matrix, fit by EM with the zero pattern enforced at every
|
|
105
|
+
M-step.
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
x1 = W1 z + V1 u1 + mu1 + e1 z : shared latents (d_shared)
|
|
109
|
+
x2 = W2 z + V2 u2 + mu2 + e2 u1 : private to population 1 (d_private)
|
|
110
|
+
u2 : private to population 2 (d_private)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
It is the static special case of probabilistic CCA with explicit private
|
|
114
|
+
subspaces (Bach & Jordan 2005) and of DLAG without time delays
|
|
115
|
+
(Gokcen et al. 2022). The experiment:
|
|
116
|
+
|
|
117
|
+
- selects `d_shared` and `d_private` by **held-out log-likelihood** under
|
|
118
|
+
block-grouped cross-validation (contiguous time stays together),
|
|
119
|
+
- reports the **variance partition** (shared / private / noise) per population,
|
|
120
|
+
- benchmarks cross-population prediction (E[x2 | x1]) against
|
|
121
|
+
**reduced-rank regression** at each rank (Semedo et al. 2019) and CCA,
|
|
122
|
+
- **decodes the behavior** from each subspace (shared, private-1, private-2,
|
|
123
|
+
raw populations) with ridge regression under grouped CV, against a
|
|
124
|
+
circular-shift null that preserves the behavior's autocorrelation.
|
|
125
|
+
|
|
126
|
+
On the synthetic demo (planted: 3 shared, 2 private per population, behavior a
|
|
127
|
+
readout of the shared factors plus population-1's private factors) the shared
|
|
128
|
+
subspace carries the behavior (R² ≈ 0.74), population-2's private subspace does
|
|
129
|
+
not (≈ 0), and the RRR curve flattens by rank 3. At the demo's firing rates the
|
|
130
|
+
linear-Gaussian model selects more dimensions than were planted (6 shared,
|
|
131
|
+
4 private) because Poisson counts put nonlinear structure into extra linear
|
|
132
|
+
directions; in the lower-gain regime the test suite uses, held-out likelihood
|
|
133
|
+
selects the planted 3 exactly. See Limits.
|
|
134
|
+
|
|
135
|
+

|
|
136
|
+
|
|
137
|
+
**Real session 715093703, VISrl (76 units) ↔ VISp (60 units).** Held-out
|
|
138
|
+
likelihood selects 6 shared and 4 private dimensions. Variance is mostly
|
|
139
|
+
noise at 50 ms (VISrl: 9% shared / 5% private / 86% noise; VISp: 16 / 10 /
|
|
140
|
+
75). The shared subspace decodes running speed (R² 0.27 ± 0.04 across folds;
|
|
141
|
+
circular-shift null p95 −0.11) and neither private subspace does (−0.04,
|
|
142
|
+
−0.07). All latents together reach 0.32 and the raw populations 0.41, so the
|
|
143
|
+
10-dimensional model keeps most, not all, of the behavior-relevant signal. The
|
|
144
|
+
raw populations split sharply: VISrl alone decodes running at 0.38, VISp alone
|
|
145
|
+
at 0.14. Cross-area prediction is near zero: E[VISp | VISrl] has R² −0.05
|
|
146
|
+
under the factor model and reduced-rank regression tops out at 0.04 by rank 6.
|
|
147
|
+
Read together: at this bin size the "shared" factors look like a slow signal
|
|
148
|
+
common to both areas (locomotion / arousal, strongest in RL), not fast
|
|
149
|
+
inter-area communication. That is the expected place for a static model to
|
|
150
|
+
land and the reason DLAG-style delays are the next comparison.
|
|
151
|
+
|
|
152
|
+

|
|
153
|
+
|
|
154
|
+
## Experiment 2 — connectivity-constrained linear dynamics
|
|
155
|
+
|
|
156
|
+
Per-area population latents are stacked into `S` and fit with a one-step
|
|
157
|
+
linear model `S[t+1] = S[t] A + b`. The atlas enters as a penalty on each
|
|
158
|
+
area-to-area block of `A` proportional to how *weak* that projection is:
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
penalty(A) = λ · Σ_{a,b} (1 − conn[a,b]) · ‖A_block(a,b)‖²
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The test is held-out one-step prediction of the atlas-constrained model vs. an
|
|
165
|
+
unconstrained ridge model vs. models fit with a **shuffled** connectivity
|
|
166
|
+
matrix at the same λ. If the atlas carries information about the dynamics the
|
|
167
|
+
true-atlas curve should separate from the shuffled curve as λ grows; if it
|
|
168
|
+
doesn't, that's the finding. On the synthetic demo (six areas, eight latents
|
|
169
|
+
each, 1 200 bins, a planted sparse projection matrix) the two curves separate
|
|
170
|
+
at large λ and the fitted block norms correlate with the planted connectivity.
|
|
171
|
+
|
|
172
|
+

|
|
173
|
+
|
|
174
|
+
**Real session 715093703, five visual areas.** The unconstrained one-step
|
|
175
|
+
model explains R² 0.41 of the next bin's latents. The atlas prior does not
|
|
176
|
+
help: at every λ from 0.01 to 10 the atlas-constrained and shuffled-atlas
|
|
177
|
+
models agree to three decimals (Δ ≤ +0.001), and both lose accuracy once λ
|
|
178
|
+
exceeds about 1 (0.41 → 0.38 at λ = 10). So on this session the projection-
|
|
179
|
+
density matrix carries no information the one-step predictor can use beyond
|
|
180
|
+
what any equally sparse prior would. Nor does the unconstrained fit recover
|
|
181
|
+
the anatomy on its own: its off-diagonal coupling-block norms correlate with
|
|
182
|
+
projection density at r = 0.38 over 20 blocks, permutation p = 0.075 (null
|
|
183
|
+
95th percentile 0.42). 77% of ‖A‖² sits in the within-area blocks. On this
|
|
184
|
+
session, then, the answer to question 2 is no at 50 ms.
|
|
185
|
+
|
|
186
|
+

|
|
187
|
+
|
|
188
|
+
Two things the first real run taught, both now fixed in the code: the penalty
|
|
189
|
+
was in absolute units and was swamped by a 10× longer recording, so λ is now
|
|
190
|
+
dimensionless (see `models/coupling.py`; the synthetic curve is unchanged);
|
|
191
|
+
and the anatomy correlation was being computed from the atlas-penalised fit,
|
|
192
|
+
which inflated it to r = 0.54 (p = 0.016) even at the near-zero selected λ —
|
|
193
|
+
it is now computed from the λ = 0 fit.
|
|
194
|
+
|
|
195
|
+
## Experiment 3 — D1 vs D2 striatal neurons, shared / private covariance, and the lever
|
|
196
|
+
|
|
197
|
+
Pre-registered in [`docs/experiment3_preregistration.md`](https://raw.githubusercontent.com/JeremyGracey-AI/allen-circuit-dynamics/main/docs/experiment3_preregistration.md)
|
|
198
|
+
(commit `b1676da`) before any model was fit; the thresholds below are the ones
|
|
199
|
+
written there. Data: Klee et al. (2026), six mice, two-photon calcium imaging
|
|
200
|
+
of anterior dorsal striatum at 30 fps, every ROI labelled D1-SPN or D2-SPN, a
|
|
201
|
+
lever on a rotary encoder (displacement, encoder units), video motion energy,
|
|
202
|
+
cued and self-paced blocks. Same model as Experiment 1 with D1 as population 1
|
|
203
|
+
and D2 as population 2; 100 ms bins; D1 and D2 count-matched by ten random
|
|
204
|
+
subsample draws; everything reported as the mean over draws.
|
|
205
|
+
|
|
206
|
+
**Partition (the observation contract).** In every mouse the shared fraction
|
|
207
|
+
of variance is nearly the same for D1 and D2 — 0.18 / 0.18, 0.21 / 0.22,
|
|
208
|
+
0.25 / 0.25, 0.24 / 0.23, 0.26 / 0.21, 0.21 / 0.16 — and the population-
|
|
209
|
+
specific (private) fraction is 1–5%. Almost all the correlated variance in
|
|
210
|
+
either population is variance it shares with the other. Cross-prediction by
|
|
211
|
+
reduced-rank regression is symmetric (rank 8: D2 from D1 0.08–0.18, D1 from D2
|
|
212
|
+
0.10–0.18). Held-out likelihood selected the top of the pre-registered
|
|
213
|
+
`d_shared` grid (8) in four of six mice, so the shared dimensionality is a
|
|
214
|
+
floor, not an estimate.
|
|
215
|
+
|
|
216
|
+
**Decoding (the action contract).** The pre-registered verdicts, six mice:
|
|
217
|
+
|
|
218
|
+
| | Hypothesis | Verdict | What the numbers say |
|
|
219
|
+
|---|---|---|---|
|
|
220
|
+
| H1 | D1 and D2 decode the lever equally | supported, 5 / 6 | Substantive only in JK239 (0.15 vs 0.14); JK238 fails (0.38 vs 0.26); in three mice both are ≈ 0, so equality is trivial |
|
|
221
|
+
| H2a | shared subspace beats the shift null | supported, 5 / 6 | Real in JK238 (0.36, null p95 −0.27) and JK239 (0.17, −0.14); marginal in JK244 (0.05); ≈ 0 in the three EY217 mice, where it clears a negative null by 0.01–0.05 |
|
|
222
|
+
| H2b | shared beats private-D1 + private-D2 | supported, 4 / 6 | Private subspaces decode nothing anywhere (−0.01 to 0.03) |
|
|
223
|
+
| H3 | neural leads the lever by 0.1–0.7 s | **not supported**, 1 / 6 | The shared lag profile peaks at 0 in five mice; its right tail (neural after lever) decays slower, which is the indicator's own kernel |
|
|
224
|
+
| H4a | shared fraction same across contexts | not supported, 3 / 6 | |
|
|
225
|
+
| H4b | lead differs across contexts | not supported, 3 / 6 | Context subsets have 4–6 CV blocks, so single-block test folds with their own lever offsets; the context analysis is under-powered as pre-registered |
|
|
226
|
+
|
|
227
|
+
Motion regression (video motion energy, ±200 ms, inside the training fold)
|
|
228
|
+
halves JK238's shared R² (0.36 → 0.18) and leaves JK239's (0.17 → 0.24) and
|
|
229
|
+
the near-zero mice unchanged, so in the one mouse with strong decoding about
|
|
230
|
+
half of the shared–lever relation is movement of the animal rather than the
|
|
231
|
+
lever as such.
|
|
232
|
+
|
|
233
|
+

|
|
234
|
+

|
|
235
|
+
|
|
236
|
+
**Reading.** The covariance structure is the robust finding: D1 and D2 share
|
|
237
|
+
one common subspace in every animal and keep almost nothing to themselves,
|
|
238
|
+
which is the population-level version of the paper's "both populations
|
|
239
|
+
encode the action." Whether that subspace carries the lever varies by animal
|
|
240
|
+
from 0.36 to nothing, and the three mice with no lever signal are the three
|
|
241
|
+
D1-Cre (EY217) animals; the neural–video correlation is also a third of the
|
|
242
|
+
others' there, so it is a property of those recordings (press rates are
|
|
243
|
+
half the others') rather than of the model. The
|
|
244
|
+
timing question is not answerable at 30 fps with a slow indicator: a lead
|
|
245
|
+
shorter than the GCaMP decay is folded into lag 0.
|
|
246
|
+
|
|
247
|
+
Per-mouse `metrics.json` and figures are under `outputs/force_klee/`;
|
|
248
|
+
`scripts/evaluate_prereg.py` reproduces the table from them. JK326's metrics
|
|
249
|
+
record `5c913f0`, a commit that changed only the summary and plotting code
|
|
250
|
+
after the analysis code at `6cfff70`.
|
|
251
|
+
|
|
252
|
+
## Run it
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
uv venv && source .venv/bin/activate
|
|
256
|
+
uv pip install -e ".[dev]"
|
|
257
|
+
acd demo --out outputs/demo_local # both experiments, synthetic, ~30 s (outputs/demo/ is the committed run)
|
|
258
|
+
pytest
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Real data (Visual Coding Neuropixels, public S3 bucket, no credentials):
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
uv venv --python 3.10 .venv-allen && uv pip install --python .venv-allen/bin/python -e ".[allen]" # allensdk pins old scipy/pandas
|
|
265
|
+
.venv-allen/bin/acd fetch --session 715093703 --out data/ecephys # areas chosen from the session (top VIS* by QC-passing units)
|
|
266
|
+
SESSION=715093703 bash scripts/run_experiments.sh # connectivity matrix + both experiments
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
`scripts/bootstrap_linux.sh --fetch` does all of the above on a fresh Linux or WSL box,
|
|
270
|
+
including a parallel ranged download of the 2.7 GB NWB from Allen's public S3
|
|
271
|
+
bucket (`scripts/pdl.sh`; allensdk's own download times out on slow links).
|
|
272
|
+
|
|
273
|
+
Experiment 3 (Klee et al. 2026, 246 MB, CPU only, ~45 min per mouse):
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
# Data Structures/Switching_2p_Struc_Main.mat from https://zenodo.org/records/16333867
|
|
277
|
+
acd force --mat Switching_2p_Struc_Main.mat --mouse all --out outputs/force_klee
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## What the loader does to the data
|
|
281
|
+
|
|
282
|
+
- Unit QC: `isi_violations < 0.5`, `amplitude_cutoff < 0.1`, `presence_ratio > 0.9`.
|
|
283
|
+
- Bins spikes at 50 ms inside the chosen stimulus epoch only (default
|
|
284
|
+
`natural_movie_one`); a bin never straddles two presentation windows.
|
|
285
|
+
- Behavior = mean running speed per bin; bins with no speed samples are
|
|
286
|
+
dropped and counted in the metadata.
|
|
287
|
+
- Units under 0.5 Hz are dropped and counted. Counts are square-root
|
|
288
|
+
transformed, then z-scored with training-fold statistics inside every
|
|
289
|
+
cross-validation loop.
|
|
290
|
+
- Twelve contiguous blocks define the CV groups.
|
|
291
|
+
|
|
292
|
+
Every `metrics.json` carries a provenance block: package version, git SHA,
|
|
293
|
+
library versions, config hash, and the dataset's own metadata.
|
|
294
|
+
|
|
295
|
+
## Layout
|
|
296
|
+
|
|
297
|
+
```
|
|
298
|
+
src/allen_circuit_dynamics/
|
|
299
|
+
dataset.py PopulationDataset — the one container every stage uses
|
|
300
|
+
synth.py planted-structure generators (both experiments)
|
|
301
|
+
preprocess.py unit filter, sqrt, fold-safe scaling
|
|
302
|
+
models/shared_private.py block-sparse FA by EM
|
|
303
|
+
models/baselines.py RRR, CCA, per-population FA
|
|
304
|
+
models/coupling.py connectivity-penalised linear dynamics + shuffle control
|
|
305
|
+
decode.py grouped-CV ridge decoding with a circular-shift null
|
|
306
|
+
experiments.py the two experiments as pure functions → metrics dict
|
|
307
|
+
report.py one figure per question
|
|
308
|
+
allen_data.py AllenSDK loader (lazy import) + atlas projection matrix
|
|
309
|
+
klee_data.py Klee et al. 2026 two-photon loader (h5py; dF/F per the paper)
|
|
310
|
+
lagged.py lag pairing that never crosses CV blocks; within-block shift null
|
|
311
|
+
experiments_force.py Experiment 3 (count-matched draws, contexts, lag profile)
|
|
312
|
+
cli.py acd demo | fetch | latent | coupling | force
|
|
313
|
+
docs/experiment3_preregistration.md hypotheses, grid, controls, decision rules — committed before the run
|
|
314
|
+
tests/ 19 tests, ~2 min, synthetic only
|
|
315
|
+
scripts/bootstrap_linux.sh
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## Limits, stated up front
|
|
319
|
+
|
|
320
|
+
- The shared/private model is linear-Gaussian and static. On Poisson-like
|
|
321
|
+
counts it needs more dimensions than the generative truth to absorb the
|
|
322
|
+
nonlinearity; held-out likelihood, not the planted number, is the criterion.
|
|
323
|
+
- Experiment 2's "areas" are PCA latents, not cell types, and the atlas prior
|
|
324
|
+
is projection *density*, which says nothing about sign or cell-type
|
|
325
|
+
specificity. The experiment can only say whether that coarse structure
|
|
326
|
+
predicts coupling — not why.
|
|
327
|
+
- Running speed is a behavioral proxy available in the open dataset; it is not
|
|
328
|
+
a measured motor output. The code accepts any continuous signal on the same
|
|
329
|
+
bins.
|
|
330
|
+
- One session, one bin size, one stimulus epoch. Nothing above should be
|
|
331
|
+
generalised until it holds across sessions and at 10–25 ms bins.
|
|
332
|
+
- The decoded behavior is running speed; a slow common signal that tracks it
|
|
333
|
+
will show up as "shared" under a static model whether or not the areas
|
|
334
|
+
communicate. Time-delayed shared latents (DLAG) are the test that separates
|
|
335
|
+
the two.
|
|
336
|
+
- Experiment 3: one session per mouse (no learning across days), no EMG, lever
|
|
337
|
+
displacement rather than force, calcium at 30 fps (no timing claim finer
|
|
338
|
+
than the indicator), and three of six mice with little lever signal in the
|
|
339
|
+
neural data at all. The context split is under-powered by block count.
|
|
340
|
+
- The `d_shared` grid ceiling (8) was hit in four mice; the shared
|
|
341
|
+
dimensionality reported there is a lower bound.
|
|
342
|
+
|
|
343
|
+
## References
|
|
344
|
+
|
|
345
|
+
- Bach & Jordan (2005). A probabilistic interpretation of canonical correlation analysis.
|
|
346
|
+
- Semedo, Zandvakili, Machens, Yu & Kohn (2019). Cortical areas interact through a communication subspace. *Neuron*.
|
|
347
|
+
- Gokcen, Jasper, Semedo, Zandvakili, Kohn, Machens & Yu (2022). Disentangling the flow of signals between populations of neurons. *Nature Computational Science*.
|
|
348
|
+
- Siegle et al. (2021). Survey of spiking in the mouse visual system reveals functional hierarchy. *Nature*. (Visual Coding Neuropixels dataset)
|
|
349
|
+
- Oh et al. (2014). A mesoscale connectome of the mouse brain. *Nature*. (Mouse Connectivity Atlas)
|
|
350
|
+
- Klee, Fernando-Peiris, Athalye, Costa, Peterka & Sippy (2026). Shared striatal neurons exhibit context-specific dynamics for internally and externally driven actions. *Science Advances*. Data: Zenodo 10.5281/zenodo.16333867, CC BY 4.0. (Experiment 3 dataset)
|
|
351
|
+
- Athalye, Ganguly, Costa & Carmena (2017). Emergence of coordinated neural dynamics underlies neuroprosthetic learning and skillful control. *Neuron*. (shared vs. private variance vocabulary)
|