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.
Files changed (32) hide show
  1. allen_circuit_dynamics-0.2.0/.gitignore +21 -0
  2. allen_circuit_dynamics-0.2.0/CHANGELOG.md +19 -0
  3. allen_circuit_dynamics-0.2.0/CITATION.cff +62 -0
  4. allen_circuit_dynamics-0.2.0/LICENSE +21 -0
  5. allen_circuit_dynamics-0.2.0/PKG-INFO +351 -0
  6. allen_circuit_dynamics-0.2.0/README.md +303 -0
  7. allen_circuit_dynamics-0.2.0/data/README.md +10 -0
  8. allen_circuit_dynamics-0.2.0/pyproject.toml +80 -0
  9. allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/__init__.py +49 -0
  10. allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/allen_data.py +236 -0
  11. allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/cli.py +264 -0
  12. allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/dataset.py +133 -0
  13. allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/decode.py +86 -0
  14. allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/experiments.py +218 -0
  15. allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/experiments_force.py +346 -0
  16. allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/klee_data.py +183 -0
  17. allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/lagged.py +61 -0
  18. allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/models/__init__.py +9 -0
  19. allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/models/baselines.py +80 -0
  20. allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/models/coupling.py +127 -0
  21. allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/models/shared_private.py +219 -0
  22. allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/preprocess.py +71 -0
  23. allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/provenance.py +50 -0
  24. allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/py.typed +0 -0
  25. allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/report.py +254 -0
  26. allen_circuit_dynamics-0.2.0/src/allen_circuit_dynamics/synth.py +210 -0
  27. allen_circuit_dynamics-0.2.0/tests/conftest.py +25 -0
  28. allen_circuit_dynamics-0.2.0/tests/test_dataset.py +39 -0
  29. allen_circuit_dynamics-0.2.0/tests/test_experiments.py +74 -0
  30. allen_circuit_dynamics-0.2.0/tests/test_force.py +35 -0
  31. allen_circuit_dynamics-0.2.0/tests/test_lagged.py +36 -0
  32. 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
+ [![CI](https://github.com/JeremyGracey-AI/allen-circuit-dynamics/actions/workflows/ci.yml/badge.svg)](https://github.com/JeremyGracey-AI/allen-circuit-dynamics/actions/workflows/ci.yml)
52
+ [![PyPI](https://img.shields.io/pypi/v/allen-circuit-dynamics.svg)](https://pypi.org/project/allen-circuit-dynamics/)
53
+ [![Python](https://img.shields.io/pypi/pyversions/allen-circuit-dynamics.svg)](https://pypi.org/project/allen-circuit-dynamics/)
54
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](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
+ ![decoding](https://raw.githubusercontent.com/JeremyGracey-AI/allen-circuit-dynamics/main/outputs/demo/latent/decoding_by_subspace.png)
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
+ ![real decoding](https://raw.githubusercontent.com/JeremyGracey-AI/allen-circuit-dynamics/main/outputs/latent_715093703/decoding_by_subspace.png)
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
+ ![coupling](https://raw.githubusercontent.com/JeremyGracey-AI/allen-circuit-dynamics/main/outputs/demo/coupling/coupling_vs_lambda.png)
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
+ ![real coupling](https://raw.githubusercontent.com/JeremyGracey-AI/allen-circuit-dynamics/main/outputs/coupling_715093703/coupling_vs_lambda.png)
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
+ ![JK238 decoding](https://raw.githubusercontent.com/JeremyGracey-AI/allen-circuit-dynamics/main/outputs/force_klee/JK238/force_decoding.png)
234
+ ![JK238 lag profile](https://raw.githubusercontent.com/JeremyGracey-AI/allen-circuit-dynamics/main/outputs/force_klee/JK238/force_lag_profile.png)
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)