SPB Git

spb/wpai0001_uqo

Public

WP-AI-0001: protocol, harness, Tier-0 pilot (sprints 0-2)

- Research gap (40 verified arXiv records), preregistration v0.1, experiment plan, compute budget, risk register, logs
- Harness: OPSEQ/FSA/MORPH generators with exact validators, toy transformer with exact hook points, training, patching, bootstrap, manifests/registry
- Pilot pilot_tier0_opseq_451ef5087182: donor/recipient gap, bitwise hook no-op, within-donor localization of the target operator
- Paper skeleton with explicit placeholders; figures regenerated by scripts/build_figures.py

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Simon-Pierre Boucher committed (Nov 9, 2026) Browse files

93 changed files +9,007 −0

93 changed files
Unified Split
added .gitignore +39 −0
@@ -0,0 +1,39 @@
1+# Raw & processed data — never committed (see data/raw/README.md for provenance)
2+data/raw/*
3+!data/raw/README.md
4+data/processed/*
5+!data/processed/.gitkeep
6+
7+# Python
8+__pycache__/
9+*.pyc
10+.pytest_cache/
11+.venv/
12+*.egg-info/
13+
14+# LaTeX build artifacts (paper/main.pdf IS committed)
15+paper/*.aux
16+paper/*.log
17+paper/*.out
18+paper/*.toc
19+paper/*.lof
20+paper/*.lot
21+paper/*.fls
22+paper/*.fdb_latexmk
23+paper/*.bbl
24+paper/*.blg
25+paper/*.spl
26+paper/*.synctex.gz
27+paper/*.run.xml
28+paper/*.bcf
29+
30+# Logs
31+logs/
32+*.log
33+
34+# macOS
35+.DS_Store
36+
37+# WP-AI-0001 artifacts (regenerable; models + per-item outputs + run logs)
38+artifacts/
39+!artifacts/.gitkeep
added CLAUDE.md +32 −0
@@ -0,0 +1,32 @@
1+# CLAUDE.md — WP-AI-0001: The Neural Organ Transplant Experiment
2+
3+**Causal transfer of cognitive capabilities across local large language models.** Research home: UQO
4+Research (`Recherche-UQO/ia/wp-ai-0001`). Language: English for all code, docs, figures, paper, logs.
5+Status: protocol + implementation in progress; **no transfer result has been established**.
6+
7+The full research mandate (sections 0–18: mission, estimands, RQ1–RQ8, task design, model matrix,
8+pipeline phases 1–8, metrics, statistics, architecture, gates A–F, figures, paper, registry, licensing,
9+roadmap, autonomous work rules) was supplied by the author on 2026-11-09 and is reproduced verbatim in
10+`docs/MANDATE.md`. It binds every session. Operational rules that follow from it:
11+
12+1. **Never fabricate** runs, metrics, citations, hardware or references. Every number in `docs/`,
13+ `experiments/summaries/` and `paper/` must trace to a run id in `experiments/registry/runs.jsonl` and a
14+ manifest in `experiments/manifests/`. Pending measurements are explicit placeholders.
15+2. **Gates in order** (A repository readiness → B baseline integrity → C mechanistic sanity → D transplant
16+ validity → E confirmatory evidence → F paper integrity). A failed gate is diagnosed, not reinterpreted.
17+3. **Exploratory ≠ confirmatory.** `docs/PREREGISTRATION.md` v0.1 is registered; the confirmatory freeze
18+ (v1.0) must precede generation of confirmatory splits. Design changes go to `docs/DECISION_LOG.md`
19+ with "before/after freeze"; failures go to `docs/EXPERIMENT_LOG.md`. Unfavourable runs are never deleted.
20+4. **Persistence vocabulary.** Distinguish single-token intervention, within-sequence effect,
21+ cross-prompt reuse, and saved checkpoint/adapter. Transient steering is never called learning.
22+5. **Controls are mandatory** for any transfer claim: unmodified recipient/donor, sham, norm-matched random,
23+ unrelated-skill donor, shuffled-token/layer grafts, prompting, steering, matched adapter fine-tuning.
24+6. **Resources.** Laptop (M5 Max 48 GB) and the existing MacLustr SSH access only. Ask before paid compute,
25+ checkpoints > 20 GB, publishing/submitting, or touching shared infrastructure. No secrets in the repo.
26+7. **Backends.** Tier 0 = PyTorch toy models (hooks exact, CPU-testable). Tier ≥ 1 = MLX layer wrapping
27+ with a no-op test and a Transformers equivalence test; never claim a hook MLX does not expose.
28+8. **Remote.** `origin = gitsrv:srv/git/wpai0001_uqo.git` (spbgit on M1M32). Commit after each sprint
29+ deliverable; `paper/main.pdf` is committed when it exists.
30+
31+Quick start: `python3 -m pytest tests -q` · `python3 scripts/audit_environment.py` ·
32+`python3 scripts/run_pilot.py --quick --device cpu` · see `docs/REPRODUCIBILITY.md` and `docs/TASKS.md`.
added README.md +146 −0
@@ -0,0 +1,146 @@
1+<!-- Author: Simon-Pierre Boucher — simon-pierre.boucher@uqo.ca -->
2+# 🧠 The Neural Organ Transplant Experiment
3+
4+**UQO Working Paper No. AI-0001** — *Causal Transfer of Cognitive Capabilities Across Local Large Language Models*
5+
6+[![Paper](https://img.shields.io/badge/paper-protocol%20draft%20v0.1-b31b1b?logo=latex&logoColor=white)](paper/main.pdf)
7+[![Python](https://img.shields.io/badge/python-3.12%2B-3776AB?logo=python&logoColor=white)](pyproject.toml)
8+[![Tests](https://img.shields.io/badge/tests-16%20passing-2e7d32)](tests/)
9+[![Reproducible](https://img.shields.io/badge/reproducible-manifests%20%2B%20registry-2e7d32)](docs/REPRODUCIBILITY.md)
10+[![Institution](https://img.shields.io/badge/UQO-D%C3%A9pt.%20des%20sciences%20administratives-16365c)](https://uqo.ca)
11+[![Author](https://img.shields.io/badge/author-Simon--Pierre%20Boucher-16365c)](mailto:simon-pierre.boucher@uqo.ca)
12+
13+> **TL;DR** — Can a bounded, *causally identified* subsystem that implements a measurable capability in a
14+> donor language model be transplanted into a recipient that demonstrably lacks it, without full-model
15+> training? This repository holds the preregistered protocol, the experiment harness (toy transformers with
16+> exact hook semantics; MLX/Transformers backends planned), the registry of runs and the paper.
17+> **State on 2026-11-09:** Sprints 0–2 done on Tier 0 — donor/recipient gap manufactured and measured
18+> (donor 1.000 vs recipient 0.092 on the target operator, 3 seeds), hooks verified bitwise no-op, and the
19+> target operator localized by activation patching (executed at the read position by layer-0 attention and
20+> layer-0–2 MLPs; reproducible across seeds, r ≥ 0.96). **No transplant has been run yet.**
21+
22+---
23+
24+## 📌 Status
25+
26+| Item | Value |
27+|---|---|
28+| Version | 0.1 protocol draft (November 2026) |
29+| SSRN | not yet submitted |
30+| Journal | to be assessed after results (ML / mechanistic interpretability venue) |
31+| Code & data | synthetic data regenerated from seeds; every run has a manifest in `experiments/manifests/` |
32+| Gates | A ✔ · B ✔ (Tier 0) · C ✔ (Tier 0) · D–F pending |
33+
34+## 🎯 What this paper does
35+
36+Prior work maps whole residual streams or concept directions between models (model stitching, activation
37+buses, cross-model steering) and the one controlled attempt at activation-mediated *capability* transfer
38+reports a null. We (i) causally localize a capability in the donor by interchange interventions,
39+(ii) manufacture or verify the recipient's lack of it, and (iii) compare each transplant family
40+(activation, subspace, circuit, weight, adapter, persistent) against matched sham, random and
41+unrelated-donor controls on held-out, leakage-controlled tasks. Estimands:
42+
43+```
44+Δ_T = Q_T(R') − Q_T(R) transfer gain
45+Δ_T^adj = Δ_T − [Q_T(R_sham) − Q_T(R)] control-adjusted transfer (primary)
46+ρ_c = Q_c(R') / Q_c(R) retention on control strata
47+N_D = Q_T(D) − Q_T(D \ S_D) donor necessity
48+```
49+
50+## 🏆 Headline results
51+
52+No transfer results yet. Pilot (exploratory, run `pilot_tier0_opseq_451ef5087182`):
53+
54+| Object | Estimate |
55+|---|---|
56+| Donor vs recipient, target stratum (final answer, 3 seeds) | 1.000 vs 0.092 (chance 0.105); paired gap seed 0 +0.847 [+0.825, +0.870] |
57+| Ω-step accuracy | donor 1.000, recipient 0.062 |
58+| Control stratum | 1.000 vs 1.000 |
59+| Hook no-op (6 models) | bitwise identity in all checks |
60+| Localization of Ω (restored-answer rate) | 1.00 at the read position for `resid_post` L0–L3 and `attn_out` L0; 0.00 elsewhere; sham 0.00 |
61+
62+## 🗂 Repository layout
63+
64+```
65+wp-ai-0001/
66+├── CLAUDE.md · docs/MANDATE.md # binding rules and the verbatim research mandate
67+├── src/wp_ai_0001/ # datasets (OPSEQ/FSA/MORPH), models + hooks, training, localization,
68+│ # evaluation, statistics, instrumentation, orchestration, visualization
69+├── tests/{unit,causal_sanity,...} # generators, bootstrap, hook no-op and intervention algebra
70+├── scripts/ # audit_environment, run_pilot, build_figures, build_paper
71+├── experiments/{registry,manifests,summaries}
72+├── artifacts/ # models, per-item outputs, figures (git-ignored)
73+├── docs/ # RESEARCH_GAP, PREREGISTRATION, EXPERIMENT_PLAN, COMPUTE_BUDGET,
74+│ # RISK_REGISTER, REPRODUCIBILITY, EXPERIMENT_LOG, DECISION_LOG, TASKS
75+├── literature/ # verified arXiv records + BibTeX; unverified candidates listed apart
76+└── paper/ # main.tex + sections/ + references.bib → main.pdf
77+```
78+
79+## 🚀 Quick start
80+
81+```bash
82+python3 -m pip install -e ".[dev]"
83+python3 -m pytest tests -q
84+python3 scripts/audit_environment.py --cluster
85+python3 scripts/run_pilot.py --quick --device cpu --seeds 0 1 --n-boot 200 # ≈ 25 s CPU
86+python3 scripts/run_pilot.py --seeds 0 1 2 --steps 3000 # ≈ 5 min on an M5 Max (MPS)
87+python3 scripts/build_figures.py --run <run_id> && scripts/build_paper.sh
88+```
89+
90+## 🗃 Data
91+
92+Synthetic micro-worlds generated from seeds (`src/wp_ai_0001/datasets/`), validated by exact recomputation,
93+hashed and checked for item-level disjointness across splits; see `data/README.md`. Tier-1 checkpoints
94+(Qwen2.5-1.5B/3B-Instruct, Llama-3.2-3B-Instruct, bf16) are cached locally and not redistributed.
95+
96+## 🔬 Methodology
97+
98+1. Gap qualification (Phase 1) — `scripts/run_pilot.py`, `training/train_toy.py`, `evaluation/metrics.py`.
99+2. Causal localization (Phase 2) — `localization/patching.py` (interchange, ablation, heads).
100+3. Alignment (Phase 3) — `alignment/` (to implement: Procrustes, ridge, CCA, random, unrelated-data).
101+4. Transplant families and controls (Phases 4–5) — `transplantation/` (to implement).
102+5. Robustness, mechanism, confirmatory (Phases 6–8) — planned, see `docs/EXPERIMENT_PLAN.md`.
103+6. Statistics — `statistics/bootstrap.py` (cluster bootstrap over programs; seed-level summaries).
104+
105+## 🖼 Figures & tables inventory
106+
107+| Figure | Content | Source run |
108+|---|---|---|
109+| `fig_gap_tier0.pdf` | donor/recipient accuracy per stratum, seeds, chance | `pilot_tier0_opseq_451ef5087182` |
110+| `fig_localization_patching_tier0.pdf` | layer × position restored-answer rate, 3 families | idem |
111+| `fig_localization_ablation_tier0.pdf` | layer × position accuracy after zero-ablation | idem |
112+| `fig_localization_heads_tier0.pdf` | layer × head patching at Ω token and read position | idem |
113+
114+## ⚠️ Limitations
115+
116+- Tier-0 capability is a per-step map once the trace is visible (risk R1); multi-step claims need
117+ `trace_stride > 1` and Tier ≥ 1 evidence.
118+- No transplant, alignment or control battery has been executed; the paper contains placeholders.
119+- Length generalization fails for both roles (absolute positions); it is a stratum, not a target.
120+
121+## 📝 Citation
122+
123+```bibtex
124+@techreport{boucher2026organtransplant,
125+ author = {Boucher, Simon-Pierre},
126+ title = {The Neural Organ Transplant Experiment: Causal Transfer of Cognitive Capabilities Across Local Large Language Models},
127+ institution = {Universit\'e du Qu\'ebec en Outaouais, D\'epartement des sciences administratives},
128+ type = {Working Paper},
129+ number = {AI-0001},
130+ year = {2026},
131+ month = {November}
132+}
133+```
134+
135+## 👤 Author & contact
136+
137+**Simon-Pierre Boucher** — Département des sciences administratives,
138+Université du Québec en Outaouais (UQO), Gatineau, QC.
139+📧 [simon-pierre.boucher@uqo.ca](mailto:simon-pierre.boucher@uqo.ca)
140+
141+## 🗒 Notes
142+
143+- Numbering: project id `wp-ai-0001` (series "AI", number 0001) as requested in the mandate; repository
144+ `wpai0001_uqo.git` on spbgit, following the FIRC-0001 precedent rather than the `wpN` sequence.
145+- Deviation from the template layout: `src/wp_ai_0001/training/` added (Tier-0 models are trained here);
146+ experiment artifacts live under `experiments/` and `artifacts/` per the mandate instead of `results/`.
added configs/experiments/.gitkeep +0 −0
added configs/interventions/.gitkeep +0 −0
added configs/models/.gitkeep +0 −0
added configs/tasks/.gitkeep +0 −0
added data/README.md +10 −0
@@ -0,0 +1,10 @@
1+# Data policy — WP-AI-0001
2+
3+All Tier-0 data are **synthetic** and regenerated from seeds by `src/wp_ai_0001/datasets/` (generator
4+versions are recorded in every split). Concrete split files are written under `artifacts/runs/<run_id>/data/`
5+(git-ignored) with SHA-256-derived hashes recorded in `experiments/manifests/<run_id>.json`.
6+
7+Tier ≥ 1 model checkpoints are downloaded from the Hugging Face hub into the user's cache (not committed).
8+Licenses: Qwen2.5 (Apache-2.0), Llama 3.2 (Llama 3.2 Community License — redistribution of derived
9+weights to be checked before release). No restricted or personal data are used. Nothing under `data/` is
10+committed except this file.
added docs/COMPUTE_BUDGET.md +30 −0
@@ -0,0 +1,30 @@
1+# Compute budget — WP-AI-0001 (measured where stated)
2+
3+## Resources audited 2026-11-09 (`scripts/audit_environment.py --cluster`, `docs/ENVIRONMENT_AUDIT.json`)
4+
5+- Laptop: Apple M5 Max, 48 GB unified memory, 18 cores, 921 GB free; Python 3.14.4; torch 2.12.0 (MPS on),
6+ MLX 0.32.2 (Metal on), mlx_lm 0.31.3, transformers 5.8.1. Cached checkpoints: Qwen2.5-1.5B-Instruct-bf16,
7+ Qwen2.5-3B-Instruct-bf16, Llama-3.2-3B-Instruct-bf16 (mlx-community), OLMoE-1B-7B-0125-Instruct-4bit.
8+- MacLustr nodes reachable with Python ≥ 3.14: M3U96a (M3 Ultra 96 GB, mlx_lm 0.31.3), M3U96b (M3 Ultra 96 GB,
9+ mlx_lm 0.31.3, 110 GB free), M4M64a (M4 Max 64 GB, torch 2.12.1), M4M64b (M4 Max 64 GB, 16 GB free — not
10+ usable for checkpoints). Nodes with only system Python 3.9 (M2U64, M4M64c, M5M36, M4BP48, m4mm, m4mn)
11+ need `mld prepare`-style setup before use. M4M36 was unreachable.
12+- No paid compute is required for Tiers 0–1. Tier 2 fits M3U96a/b.
13+
14+## Measured costs (Tier 0)
15+
16+| Item | Measurement |
17+|---|---|
18+| Toy model 0.80 M params, 200 optimizer steps, batch 256 | 5.3 s MPS, 10.0 s CPU |
19+| 3 000 steps (one model) | ≈ 44 s MPS |
20+| Quick CPU smoke test (2 seeds × 2 roles, 200 steps, localization) | 22 s |
21+| Full pilot (3 seeds × 2 roles, 3 000 steps ≈ 45 s each, localization 3 families × 4 layers × 7 positions × 2 repeats + heads, bootstrap 2 000) | **276 s** on the laptop (MPS), run `pilot_tier0_opseq_451ef5087182` |
22+
23+## Projected (not measured) — to be replaced by pilot measurements
24+
25+- Tier 1 activation capture on a 3B bf16 model: ≈ 6–7 GB weights + activations; one layer × 1 000 prompts ×
26+ 32 tokens × 3 072 dims in fp16 ≈ 200 MB; feasible on the laptop. Full-layer capture for 36 layers is to be
27+ streamed to disk (do not hold in memory).
28+- Tier 2 (7B–14B bf16): 14–28 GB weights; M3U96a/b only; one node per model.
29+
30+Rule: no parallel heavy runs until the single-node profile is recorded in `EXPERIMENT_LOG.md`.
added docs/DECISION_LOG.md +23 −0
@@ -0,0 +1,23 @@
1+# Decision log — WP-AI-0001
2+
3+All entries are dated; "before freeze" refers to the confirmatory freeze (PREREGISTRATION v1.0, not yet done).
4+
5+- **D1 (2026-11-09, before freeze).** Project located at `Recherche-UQO/ia/wp-ai-0001` with git remote
6+ `gitsrv:srv/git/wpai0001_uqo.git`, following the FIRC-0001 precedent for non-numeric IDs; UQO paper
7+ template (`_modele-working-paper`) adopted; English throughout.
8+- **D2 (2026-11-09, before freeze).** OPSEQ output format changed from single final answer to a
9+ *scratchpad trace* after the single-token variant failed to generalize (train loss → 0, dev ≈ 0.24–0.34
10+ on length ≤ 3; ≈ 0.13 on length ≤ 5). Consequence: the Tier-0 target capability is "execute Ω in context",
11+ a per-step map, and compositional difficulty is controlled by `trace_stride`. Risk R1 recorded.
12+- **D3 (2026-11-09, before freeze).** Micro-world enlarged from 6 operators / length ≤ 4 (≈ 14 k unique
13+ items, too small for 40 k training items) to 8 operators / length ≤ 5 (≈ 400 k).
14+- **D4 (2026-11-09, before freeze).** Toy models are PyTorch (hooks trivially exact, CPU-testable); MLX is
15+ reserved for Tier ≥ 1 with a layer-wrapping intervention mechanism and an equivalence test.
16+- **D5 (2026-11-09, before freeze).** The pilot is exploratory; localization selection uses the pilot's
17+ development localization set; confirmatory evaluation splits will be generated from new seeds after
18+ freeze.
19+- **D6 (2026-11-09, before freeze).** Localization sweep families in the pilot were `resid_post`, `attn_out`,
20+ `mlp_out` (plus heads). The `embed`/`resid_pre.0` family was not swept, which is why patching at the Ω
21+ token restores nothing; it is added to the next sweep. The pilot's localization set (length-3 programs,
22+ Ω at index 1, corrupt = `zib`) is a *development* set; confirmatory localization will use new seeds and
23+ other corrupt operators.
added docs/ENVIRONMENT_AUDIT.json +175 −0
@@ -0,0 +1,175 @@
1+{
2+ "utc": "2026-11-09T06:50:06+00:00",
3+ "hardware": {
4+ "platform": "macOS-27.0-arm64-arm-64bit-Mach-O",
5+ "machine": "arm64",
6+ "hostname_hash": "9ad7358c662e",
7+ "cpu_count": 18,
8+ "chip": "Apple M5 Max",
9+ "memory_gb": 48.0,
10+ "torch_mps": true,
11+ "torch_cuda": false
12+ },
13+ "libraries": {
14+ "python": "3.14.4",
15+ "numpy": "2.4.4",
16+ "torch": "2.12.0",
17+ "scipy": "1.17.1",
18+ "pydantic": "2.13.4",
19+ "mlx": "?",
20+ "mlx_lm": "0.31.3",
21+ "transformers": "5.8.1",
22+ "safetensors": "0.7.0"
23+ },
24+ "backends": {
25+ "mlx": {
26+ "version": "0.32.2",
27+ "metal": true
28+ },
29+ "torch": {
30+ "version": "2.12.0",
31+ "mps": true,
32+ "cuda": false
33+ }
34+ },
35+ "hf_cache": [
36+ {
37+ "repo": "microsoft/llmlingua-2-xlm-roberta-large-meetingbank",
38+ "size_gb": 5.46
39+ },
40+ {
41+ "repo": "mlx-community/Llama-3.2-3B-Instruct-bf16",
42+ "size_gb": 11.99
43+ },
44+ {
45+ "repo": "mlx-community/OLMoE-1B-7B-0125-Instruct-4bit",
46+ "size_gb": 7.26
47+ },
48+ {
49+ "repo": "mlx-community/Qwen2.5-1.5B-Instruct-bf16",
50+ "size_gb": 5.77
51+ },
52+ {
53+ "repo": "mlx-community/Qwen2.5-3B-Instruct-bf16",
54+ "size_gb": 11.52
55+ },
56+ {
57+ "repo": "mlx-community/whisper-large-v3-turbo",
58+ "size_gb": 3.01
59+ },
60+ {
61+ "repo": "sentence-transformers/all-MiniLM-L6-v2",
62+ "size_gb": 0.17
63+ },
64+ {
65+ "repo": "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2",
66+ "size_gb": 0.89
67+ }
68+ ],
69+ "cluster": {
70+ "M3U96a": {
71+ "reachable": true,
72+ "chip": "Apple M3 Ultra",
73+ "memory_gb": 96,
74+ "free_disk_gb": 231,
75+ "python": "3.14.5",
76+ "mlx_lm": "0.31.3",
77+ "torch": "none",
78+ "hf_models_cached": 9
79+ },
80+ "M3U96b": {
81+ "reachable": true,
82+ "chip": "Apple M3 Ultra",
83+ "memory_gb": 96,
84+ "free_disk_gb": 109,
85+ "python": "3.14.4",
86+ "mlx_lm": "0.31.3",
87+ "torch": "none",
88+ "hf_models_cached": 11
89+ },
90+ "M2U64": {
91+ "reachable": true,
92+ "chip": "Apple M2 Ultra",
93+ "memory_gb": 64,
94+ "free_disk_gb": 244,
95+ "python": "3.9.6",
96+ "mlx_lm": "none",
97+ "torch": "none",
98+ "hf_models_cached": 2
99+ },
100+ "M4M64a": {
101+ "reachable": true,
102+ "chip": "Apple M4 Max",
103+ "memory_gb": 64,
104+ "free_disk_gb": 285,
105+ "python": "3.14.4",
106+ "mlx_lm": "none",
107+ "torch": "2.12.1",
108+ "hf_models_cached": 9
109+ },
110+ "M4M64b": {
111+ "reachable": true,
112+ "chip": "Apple M4 Max",
113+ "memory_gb": 64,
114+ "free_disk_gb": 16,
115+ "python": "3.14.4",
116+ "mlx_lm": "none",
117+ "torch": "none",
118+ "hf_models_cached": 2
119+ },
120+ "M4M64c": {
121+ "reachable": true,
122+ "chip": "Apple M4 Max",
123+ "memory_gb": 64,
124+ "free_disk_gb": 277,
125+ "python": "3.9.6",
126+ "mlx_lm": "none",
127+ "torch": "none",
128+ "hf_models_cached": 2
129+ },
130+ "M5M36": {
131+ "reachable": true,
132+ "chip": "Apple M5 Max",
133+ "memory_gb": 36,
134+ "free_disk_gb": 302,
135+ "python": "3.9.6",
136+ "mlx_lm": "none",
137+ "torch": "none",
138+ "hf_models_cached": 5
139+ },
140+ "M4M36": {
141+ "reachable": false,
142+ "error": "ssh: connect to host m4m36.maclustr.io port 22: Operation timed out"
143+ },
144+ "M4BP48": {
145+ "reachable": true,
146+ "chip": "Apple M4 Max",
147+ "memory_gb": 48,
148+ "free_disk_gb": 806,
149+ "python": "3.9.6",
150+ "mlx_lm": "none",
151+ "torch": "none",
152+ "hf_models_cached": 5
153+ },
154+ "m4mm": {
155+ "reachable": true,
156+ "chip": "Apple M4 Pro",
157+ "memory_gb": 48,
158+ "free_disk_gb": 346,
159+ "python": "3.9.6",
160+ "mlx_lm": "none",
161+ "torch": "none",
162+ "hf_models_cached": 1
163+ },
164+ "m4mn": {
165+ "reachable": true,
166+ "chip": "Apple M4 Pro",
167+ "memory_gb": 48,
168+ "free_disk_gb": 338,
169+ "python": "3.9.6",
170+ "mlx_lm": "none",
171+ "torch": "none",
172+ "hf_models_cached": 1
173+ }
174+ }
175+}
\ No newline at end of file
added docs/EXPERIMENT_LOG.md +43 −0
@@ -0,0 +1,43 @@
1+# Experiment log — WP-AI-0001
2+
3+| Date (UTC) | Run / action | Status | Evidence | Notes |
4+|---|---|---|---|---|
5+| 2026-11-09 | Environment audit (laptop + 11 cluster nodes) | completed | `docs/ENVIRONMENT_AUDIT.json` | M4M36 unreachable; mlx_lm only on M3U96a/b + laptop |
6+| 2026-11-09 | Literature verification (40 arXiv records) | completed | `literature/arxiv_verified_2026-11-09.txt`, `literature/references_verified.bib` | abstracts read for 9 closest works |
7+| 2026-11-09 | Single-token OPSEQ learnability probe (4 layers, d=128, 4 000 steps) | failed (dev ≈ 0.13; length ≤ 3 subset: train loss → 0, dev 0.24) | console only, not archived | led to D2 (scratchpad) |
8+| 2026-11-09 | Scratchpad OPSEQ learnability probe (3 000 steps) | completed | console only | dev 1.0 by step 500; held-out compositions 1.0; length OOD 0.086 |
9+| 2026-11-09 | `pilot_tier0_opseq_quick_d5339bcad6b3` (CPU smoke, 2 seeds, 200 steps) | completed | `experiments/summaries/pilot_tier0_opseq_quick_d5339bcad6b3/` | pipeline end-to-end; models untrained by design |
10+| 2026-11-09 | `pilot_tier0_opseq_451ef5087182` (MPS, 3 seeds × 2 roles, 3 000 steps, 0.80 M params) | **completed** (276 s) | `experiments/summaries/pilot_tier0_opseq_451ef5087182/{results.json,SUMMARY.md}`, manifest, 4 figures | see §Pilot results below |
11+
12+## Pilot results — `pilot_tier0_opseq_451ef5087182` (exploratory; 2026-11-09)
13+
14+**Gate B (gap).** Final-answer exact match, mean over 3 seeds (sd): donor 1.000 (0.000) vs recipient
15+0.092 (0.053) on `target_iid` (majority-class chance 0.105); both 1.000 on `control_iid`; `comp_heldout`
16+1.000 vs 0.450 (held-out pairs often contain Ω); `len_ood` 0.086 vs 0.091 (both fail: absolute positions
17+beyond training length). Ω-step accuracy on `target_iid`: donor 1.000, recipient 0.062. Paired gap on
18+identical items, seed 0: +0.847 [+0.825, +0.870] (cluster bootstrap over programs, 2 000 draws).
19+Recipient trace accuracy on `target_iid`: 0.110 / 0.024 / 0.013 (seeds 0/1/2).
20+
21+**Gate C (hooks).** For all 6 trained models: repeat-run bitwise equality, capture-all bitwise identity,
22+disabled-modifying-hook bitwise identity = True; enabled zero hook changes output = True (256 items).
23+
24+**RQ1 (localization, within donor, n = 490 clean/corrupt pairs, read position r1 → predicts r2).**
25+Clean 1.000, sham (corrupted run scored on clean answer) 0.000 in all seeds. Restored-answer rate when
26+patching clean into corrupt (mean of 3 seeds): `resid_post` = 1.00 at the read position r1 at every layer
27+L0–L3 and 0.00 at every other position (including the Ω token position); `attn_out` = 1.00 at (L0, r1) only;
28+`mlp_out` at r1: L0 1.00, L1 0.90, L2 0.83, L3 0.17. Zero-ablation accuracy at r1: `resid_post` 0.04–0.09,
29+`attn_out` L0 0.13 (L1–L3 1.00), `mlp_out` L0 0.21, L1 0.97. Head patching at r1: L0 h0 0.62, h1 0.16,
30+h2 0.04, others 0.00; head patching at the Ω token: 0.00 everywhere. Repeat max |Δ| = 0.0000 for all
31+sweeps; across-seed Pearson of flip heatmaps: resid_post 1.000, attn_out 1.000, mlp_out 0.964 (min pair).
32+
33+**Reading (observed → interpretation).** Observed: the Ω computation is executed entirely at the read
34+position: layer-0 attention (mostly head 0) moves the operator identity from the Ω token to r1, and
35+layer-0–2 MLPs at r1 compute the value map. Patching the Ω token position itself restores nothing because
36+layer-0 attention reads the *embedding* (hook `resid_pre.0`/`embed`, not swept) and no later attention
37+re-reads it. Interpretation (not yet tested): the candidate organ for an activation graft is the
38+residual stream at r1 after L0 (or the L0 attention output at r1); its *content* is the (operator, value)
39+pair, so a transplant must convey the Ω-specific part of that content. Go/no-go for Sprint 3: all three
40+criteria of EXPERIMENT_PLAN §4 are met.
41+
42+**Pilot variability for power (PREREGISTRATION §5).** Seed sd of recipient target accuracy 0.053; cluster
43+bootstrap half-width of the paired gap ≈ 0.022 with 1 000 items / ≈ 900 programs.
added docs/EXPERIMENT_PLAN.md +56 −0
@@ -0,0 +1,56 @@
1+# Experiment plan — WP-AI-0001
2+
3+## 1. Tiers and model matrix
4+
5+| Tier | Models | Purpose | Backend | Status |
6+|---|---|---|---|---|
7+| 0 | toy decoder transformers trained from scratch (0.8 M params default; 4 layers, d = 128) | full visibility, controlled gap, exhaustive interventions | PyTorch (CPU/MPS) | **pilot running** |
8+| 1 | Qwen2.5-1.5B-Instruct, Qwen2.5-3B-Instruct, Llama-3.2-3B-Instruct (bf16, cached locally) | external validity, same-family different-size, cross-family, cross-tokenizer | MLX (layer-wrapping interventions) with a PyTorch/Transformers equivalence check | planned (after Gate D on Tier 0) |
9+| 2 | 7B–14B open weights (e.g. Qwen2.5-7B/14B, Llama-3.1-8B) | scale | MLX on M3U96a/b (96 GB) | conditional |
10+| 3 | larger | only with explicit approval | — | not planned |
11+
12+Pairs (Tier 1): Qwen2.5-1.5B→3B (same family, same tokenizer, 28→36 layers); Llama-3.2-3B↔Qwen2.5-3B
13+(different family, different tokenizer, 28 vs 36 layers); Tier 0: same architecture / different seeds,
14+same architecture / different widths, different depths.
15+
16+## 2. Task families
17+
18+- **A — OPSEQ** (implemented): invented operators on Z_11, scratchpad trace; strata `target_iid`,
19+ `control_iid`, `comp_heldout`, `len_ood`; difficulty knob `trace_stride`.
20+- **B — FSA** (generator implemented): random DFA execution over an invented alphabet.
21+- **C — MORPH** (generator implemented): context-dependent suffix rule on pseudo-words; Tier 1 variant
22+ with natural-language pseudo-morphology to be specified at Sprint 4.
23+- Negative-control battery (Tier 1): unrelated arithmetic, factual recall, instruction following,
24+ held-out text perplexity, calibration, refusal behaviour.
25+
26+## 3. Phases (per CLAUDE.md §6) and their scripts
27+
28+| Phase | Script | Output |
29+|---|---|---|
30+| 1 Baseline & gap | `scripts/run_pilot.py` | gap table, Ω-step accuracies, no-op checks |
31+| 2 Localization | `scripts/run_pilot.py` (within-donor patching, ablation, heads) | heatmaps, reproducibility |
32+| 3 Alignment | `scripts/run_alignment.py` (to write) | Procrustes / ridge / CCA / random / unrelated-data maps with leakage audit |
33+| 4 Transplant | `scripts/run_transplant.py` (to write) | activation graft, subspace graft, weight graft (same-arch), adapter graft |
34+| 5 Controls | same | sham, norm-matched random, unrelated-skill donor, shuffled token/layer, few-shot, steering, matched adapter FT, distillation |
35+| 6 Robustness | `scripts/run_sweeps.py` (to write) | strength × layer × position × dimension × pair |
36+| 7 Mechanism | `scripts/run_mechanism.py` (to write) | ablation→loss→rescue, bidirectional, dose-response |
37+| 8 Confirmatory | `scripts/run_confirmatory.py` (to write) | frozen protocol on untouched splits |
38+
39+## 4. Minimal viable experiment (MVE) and go/no-go
40+
41+MVE = Tier 0, OPSEQ, Ω = `qua`, donor (all operators) vs recipient (Ω excluded), 3 seeds each.
42+Go to Sprint 3 (first graft) iff: (a) donor Ω-step accuracy ≥ 0.95 and recipient ≤ chance + 0.1 on
43+`target_iid`; (b) all no-op checks true; (c) at least one localization cell with restored-answer rate
44+≥ 0.8 and across-seed heatmap correlation ≥ 0.8. Otherwise diagnose (see RISK_REGISTER R1–R3).
45+
46+## 5. Pre-declared confirmatory contrasts (Tier 0)
47+
48+1. activation graft vs matched sham (primary); 2. subspace graft vs norm-matched random subspace;
49+3. weight graft (same architecture) vs shuffled-layer weight graft; 4. adapter graft vs matched-size
50+adapter trained on the same alignment data without donor signal. Each: Δ_T^adj with cluster-bootstrap CI,
51+Holm-corrected across the four families; retention on `control_iid`.
52+
53+## 6. Figures that the plan can support (data-dependent)
54+
55+Gap distributions (F2), localization heatmap (F3), Pareto target-vs-retention (F5), portability matrix
56+(F6), graft size vs effect (F7), ablation→rescue chain (F8), robustness (F9), failure taxonomy (F10).
added docs/MANDATE.md +367 −0
@@ -0,0 +1,367 @@
1+# Research mandate — WP-AI-0001 (supplied by the author on 2026-11-09, reproduced verbatim)
2+
3+CLAUDE.md — WP-AI-0001
4+
5+The Neural Organ Transplant Experiment
6+
7+Causal Transfer of Cognitive Capabilities Across Local Large Language Models
8+
9+Project ID: wp-ai-0001
10+Research home: UQO Research
11+Document language: English (all code documentation, figures, paper, logs, and reports)
12+Status: Research protocol and implementation mandate; no results may be claimed before execution.
13+
14+────────
15+
16+0. MISSION AND NON-NEGOTIABLE STANDARD
17+
18+You are Claude Code acting as a principal research engineer, computational scientist, and rigorous coauthor. Build a complete, reproducible research program within the existing UQO Research environment under a new project named wp-ai-0001.
19+
20+Central question: Can a bounded, causally identified computational subsystem responsible for a measurable cognitive capability be transplanted from one open-weight language model (the donor) into another (the recipient), such that the recipient acquires or materially improves that capability without conventional full-model training?
21+
22+The word organ is a metaphor. Do not imply that LLMs have biological organs, consciousness, or localized faculties by default. The paper must operationalize a candidate functional subsystem and test whether it is transferable.
23+
24+A publication-quality result may be positive, negative, or conditional. Never fabricate empirical outcomes, references, citations, benchmarks, or performance numbers. Do not confuse a change in output distribution with genuine skill acquisition. Explicitly distinguish causal interventions, associations, and mechanistic interpretation.
25+
26+Primary deliverables: (1) executable open-weight experimentation framework, (2) documented experiment registry and artifacts, (3) validated empirical analysis, (4) rigorous English LaTeX paper, (5) publication-grade figures, (6) independent replication guide, and (7) honest limitations and failure analyses.
27+
28+1. FIRST ACTIONS: DISCOVER, AUDIT, PLAN
29+
30+Before writing implementation code:
31+
32+1. Locate the existing UQO Research repository/workspace. Inspect its README, project naming conventions, build systems, paper directories, data policies, Git remotes, and existing experimental frameworks. Never assume a path or remote URL.
33+2. Inspect the current machine and accessible MacLustr compute nodes, CPU/GPU/Metal support, RAM, storage, installed Python environments, MLX/PyTorch/Transformers versions, network accessibility, and permission boundaries. Never assume a cluster node is online.
34+3. Check whether wp-ai-0001 already exists. If so, preserve work and reconcile rather than overwrite.
35+4. Search current peer-reviewed and preprint literature with primary-source verification. At minimum investigate causal tracing, activation patching, interchange interventions, causal abstraction, sparse autoencoders, circuit discovery, model editing, task vectors, model merging, cross-model representation alignment, cross-architecture transfer, model stitching, knowledge transplantation, and inference-time activation steering. Record URLs, identifiers, publication dates, precise contributions, and how our approach differs. If web access is unavailable, mark literature review unverified and do not invent citations.
36+5. Write docs/RESEARCH_GAP.md, docs/PREREGISTRATION.md, docs/EXPERIMENT_PLAN.md, docs/COMPUTE_BUDGET.md, docs/RISK_REGISTER.md, and an implementation task tracker before major experiments.
37+6. Identify the minimal viable experiment and its go/no-go criteria. Begin with a small, affordable pilot before scaling.
38+
39+Do not claim novelty merely because the term "neural organ transplant" sounds new. The scientific novelty must survive comparison with prior model stitching, representation transfer, circuit transplantation, and activation interventions.
40+
41+2. CORE DEFINITIONS AND ESTIMANDS
42+
43+Let donor model be D, recipient model be R, task distribution T, candidate subsystem S_D, alignment/transplant operator A, and intervention operator I. Define a transplanted recipient R' = I(R, A(S_D)).
44+
45+Predefine a primary capability score Q_T(M) and control-task score Q_C(M), with uncertainty intervals. Let recipient baseline improvement be Δ_T = Q_T(R') - Q_T(R), and control retention Δ_C = Q_C(R') - Q_C(R).
46+
47+The primary causal estimand is the excess effect of a donor-specific intervention over matched sham, random, and nonfunctional donor interventions, estimated on held-out, leakage-controlled tasks. Report the difference in differences against a matched intervention control where appropriate, not only raw Δ_T.
48+
49+Operational definitions:
50+
51+• Candidate organ: a specified set of layers, heads, MLP units, activation subspaces, sparse features, or pathways whose perturbation produces repeatable, task-selective causal effects.
52+• Transplant: a predeclared intervention that injects, replaces, composes, or maps a donor-derived functional component into the recipient.
53+• Acquisition: held-out improvement on the target capability, beyond controls and chance, with retention of general capabilities and evidence against answer leakage.
54+• Selectivity: target-task improvement relative to prespecified negative-control tasks and matched compute/parameter interventions.
55+• Persistence: distinguish single-token intervention, within-sequence effect, across-prompts reuse, and saved model modification. Never call transient activation steering permanent learning.
56+• Portability: transfer across seeds, prompts, domains, model pairs, and architectures.
57+
58+A successful transplant does not require a literal physical copy of identical neurons. Separately evaluate exact-weight copying, mapped activation transfer, and learned adapter-based transplantation; label them precisely.
59+
60+3. RESEARCH QUESTIONS AND FALSIFIABLE HYPOTHESES
61+
62+RQ1 — Localization: Can a candidate capability-relevant subsystem be identified reproducibly through causal intervention rather than correlation alone?
63+
64+RQ2 — Sufficiency: Does transferring the subsystem increase recipient target-task performance relative to matched controls?
65+
66+RQ3 — Necessity: Does removing or perturbing the corresponding donor subsystem reduce donor performance selectively?
67+
68+RQ4 — Portability: How does transplant effectiveness vary with model family, architecture, size, layer depth, representation similarity, and tokenizer differences?
69+
70+RQ5 — Collateral damage: Which recipient capabilities degrade after transplantation, and how does damage scale with intervention strength and size?
71+
72+RQ6 — Persistence and mechanism: Are improvements sustained on unseen prompts, multi-step reasoning, compositional generalization, and adversarial paraphrases? Is the effect explained by reusable representations rather than answer copying?
73+
74+RQ7 — Transfer frontier: What is the minimum transplanted dimensionality/parameter budget needed for a specified improvement?
75+
76+RQ8 — Failure regimes: Under what conditions is transplantation impossible, unstable, or indistinguishable from simpler baselines?
77+
78+Preregister directional hypotheses, null hypotheses, primary outcomes, familywise comparisons, smallest effect size of interest, and failure thresholds. Do not retroactively relabel exploratory findings as confirmatory.
79+
80+4. TASK DESIGN: DEMONSTRATE A REAL CAPABILITY
81+
82+Use at least three distinct task families and separate development from confirmatory evaluation.
83+
84+A. Synthetic compositional reasoning (primary identification environment)
85+
86+Create a controlled, procedurally generated micro-world with invented operators, typed rules, multi-step transformations, and held-out compositions. Control training exposure, difficulty, and ground-truth answers. Build donor competence and recipient weakness through explicitly documented small-scale training or selection; do not assume an arbitrary donor has a capability the recipient lacks.
87+
88+Separate: memorization, operator recognition, length generalization, novel symbol substitutions, unseen compositions, distractors, and out-of-distribution combinations. Ensure disjoint generation seeds and avoid shared templates across splits. Test whether transplant conveys a rule rather than cached answers.
89+
90+B. Formal algorithmic reasoning
91+
92+Use verifiable tasks such as modular arithmetic, finite-state transformations, structured symbolic execution, constrained graph problems, and small proof checking. Prefer exact-match validators and independently generated solutions. Avoid evaluating solely with a judge LLM.
93+
94+C. Language/semantic capability
95+
96+Use narrowly defined multilingual morphological transformations, compositional syntax, or domain-specific semantic distinctions. Control lexical overlap and translation artifacts. Evaluate semantic transfer separately from superficial token copying.
97+
98+Negative-control battery
99+
100+Include unrelated arithmetic, general language modeling, factual recall, instruction following, calibration, refusal/safety behavior, and perplexity on clean held-out text where legally and technically appropriate. The battery must detect broad degradation, not just target improvements.
101+
102+Contamination safeguards
103+
104+Generate held-out samples after intervention design freeze where possible. Hash datasets, version generators, preserve seeds, record overlap tests, and audit leakage through adapters, mapping datasets, prompt exemplars, and evaluation harnesses.
105+
106+5. MODEL MATRIX AND HARDWARE STRATEGY
107+
108+Use openly licensed/open-weight models compatible with local inspection, selecting exact checkpoints only after license, architecture, and compute audits.
109+
110+Tier 0: tiny synthetic transformers trained from scratch (approximately 10M–100M parameters if practical). Full visibility, controlled training data, repeated seeds, and inexpensive exhaustive interventions.
111+
112+Tier 1: small open-weight instruction/base models (roughly 0.5B–3B) from at least two families where available. Prioritize reproducibility and manageable activations.
113+
114+Tier 2: medium open-weight models (roughly 7B–14B) for external validity, only after the Tier 0/1 methods work.
115+
116+Tier 3: larger models only if their scientific marginal value justifies measured time and memory costs.
117+
118+Construct donor/recipient pairs covering: same architecture/different seeds; same family/different sizes; different families/similar sizes; different tokenizers; matched and mismatched layer counts. Distinguish base from instruction-tuned checkpoints.
119+
120+On Apple Silicon, prefer MLX/MLX-LM where hooks and interventions are demonstrably supported. Use PyTorch/Transformers or an isolated reference implementation where MLX does not expose necessary internals. Never pretend a framework supports a hook that it does not. Maintain equivalence tests across backends. Account for unified memory, KV-cache footprint, activation capture, quantization, and memory pressure. Do not launch heavy parallel runs until resource profiling is complete.
121+
122+Record for every run: exact model ID and revision, architecture, license, tokenizer, precision, quantization, library versions, host hardware, memory, seed, generation settings, runtime, energy estimate if measured, and intervention provenance.
123+
124+6. EXPERIMENTAL PIPELINE
125+
126+Phase 1 — Baseline and gap qualification
127+
128+Measure donor and recipient capability across task strata. Require a meaningful, reproducible donor-recipient gap before attempting transplant. Run multiple seeds and report variance. Document any donor training separately from transplantation.
129+
130+Phase 2 — Localization and causal validation
131+
132+Collect activations with strict memory limits. Screen candidate layers, attention heads, MLP outputs, residual-stream subspaces, and sparse features. Use activation patching, ablation, causal tracing, path patching where valid, and independent validation. Compare against activation-norm-matched random subspaces and arbitrary heads/layers. Check whether localization findings persist under paraphrase and held-out data. Do not equate attention weights with causal explanations.
133+
134+Phase 3 — Representation alignment
135+
136+Test orthogonal Procrustes, linear mappings, canonical correlation or related similarity methods, low-rank adapters, and architecture-aware projection. Train mappings only on disjoint alignment data. Quantify alignment data size, parameter count, fitting compute, and information leaked through alignment. Include a random mapping and a mapping trained on unrelated data.
137+
138+Phase 4 — Transplant families
139+
140+Implement each as a separately named, auditable method:
141+
142+1. Activation graft: inject mapped donor activation at a specified recipient layer/token position.
143+2. Subspace graft: replace or add a selected capability-associated low-dimensional representation.
144+3. Circuit/path graft: replace or couple a bounded pathway where architectures and interfaces allow it.
145+4. Weight graft: transfer structurally compatible parameter subsets only with shape, scaling, and functional checks.
146+5. Adapter graft: install a small learned interface; account explicitly for any supervised learning in the adapter.
147+6. Persistent graft: optionally save a modified checkpoint or adapter and evaluate it across independent sessions.
148+
149+Not every transplant type will be feasible for every pair. Mark unsupported configurations explicitly rather than silently substituting methods.
150+
151+Phase 5 — Controls and alternatives
152+
153+At minimum compare with: unmodified recipient, unmodified donor, random graft, norm-matched random graft, unrelated-skill donor graft, shuffled-token graft, shuffled-layer graft, sham mapping, direct prompting/few-shot, retrieval of equivalent information, simple activation steering, matched-size adapter fine-tuning, and a conventional distillation baseline where budget permits.
154+
155+Match parameter counts, inference-time overhead, training examples, and compute where possible. If comparisons cannot be matched, state why and interpret narrowly.
156+
157+Phase 6 — Robustness and stress tests
158+
159+Sweep intervention strength, layer, token position, dimensionality, donor/recipient pair, prompt format, context length, temperature, task difficulty, and quantization. Evaluate repeated independent seeds and withheld families. Measure harmful interference and stability, including catastrophic failures and invalid outputs.
160+
161+Phase 7 — Causal mechanism tests
162+
163+Use bidirectional and reciprocal transfers when meaningful; donor ablation followed by rescue; transplant into incompatible recipients; dose-response; mediation-style analyses with assumptions stated; and specificity tests. Establish whether the transplanted component carries task-relevant information or merely biases answer tokens.
164+
165+Phase 8 — Final confirmatory evaluation
166+
167+Freeze primary methods, hyperparameters, model pairs, and evaluation scripts. Run on untouched confirmatory datasets. Publish both successes and failures, with all preregistered metrics and comparisons.
168+
169+7. QUANTITATIVE METRICS
170+
171+Primary: held-out exact accuracy or task-specific objectively validated score; treatment effect versus matched sham; target-control selectivity; retention of general capability.
172+
173+Secondary: donor ablation effect, rescue effect, transplant dimensionality, number of modified parameters, alignment data requirements, inference latency, peak memory, energy if measured, success across task strata, and stability across seeds.
174+
175+Useful derived quantities (define precisely before use):
176+
177+• Transfer gain: Q_target(R_graft) - Q_target(R_base).
178+• Control-adjusted transfer: [Q_target(R_graft)-Q_target(R_base)] - [Q_target(R_sham)-Q_target(R_base)].
179+• Retention ratio for each control task, with careful treatment of near-zero baselines.
180+• Transfer efficiency: gain per graft dimension, parameter, training example, or compute unit (report units and avoid misleading cross-metric comparisons).
181+• Donor-necessity score: donor target performance before minus after selective ablation.
182+• Portability matrix: donor-by-recipient effect with uncertainty.
183+
184+Do not compress all outcomes into one opaque "intelligence transfer score" unless weights and sensitivity analyses are fully justified.
185+
186+8. STATISTICAL DESIGN
187+
188+Predefine the unit of inference and avoid pseudoreplication: questions nested within task templates, random seeds, model pairs, and intervention conditions are not independent. Use hierarchical or mixed-effects models where appropriate; cluster bootstrap or robust uncertainty estimates at the correct generation/template/model level; paired comparisons on identical questions; and multiple-comparison corrections for large intervention searches.
189+
190+Report confidence intervals, effect sizes, p-values where relevant, exact sample sizes, random seeds, and full null results. Separate exploratory localization selection from confirmatory testing. Perform prospective power/simulation analyses using pilot variability, not invented effect sizes. Test equivalence/non-inferiority for collateral damage only with justified margins. Include sensitivity to evaluation metric, task composition, and model pair.
191+
192+Avoid strong causal claims when intervention assignment, alignment training, or sample selection creates confounding. State precisely what is randomized and what is not.
193+
194+9. ENGINEERING ARCHITECTURE
195+
196+Create or adapt a maintainable project structure consistent with UQO Research:
197+
198+wp-ai-0001/
199+ CLAUDE.md
200+ README.md
201+ pyproject.toml
202+ configs/
203+ models/ tasks/ experiments/ interventions/
204+ src/wp_ai_0001/
205+ models/ # model adapters, tokenizer compatibility, hooks
206+ datasets/ # synthetic generators, validators, splits
207+ baselines/ # model/task baselines and alternative methods
208+ localization/ # ablation, patching, circuit discovery
209+ alignment/ # donor-recipient representation mapping
210+ transplantation/ # explicit intervention implementations
211+ evaluation/ # blinded metrics and controls
212+ statistics/ # estimation, uncertainty, multiplicity
213+ instrumentation/ # memory, runtime, provenance, logs
214+ visualization/ # publication figures
215+ orchestration/ # local/cluster jobs, resumability
216+ tests/
217+ unit/ integration/ causal_sanity/ regression/
218+ experiments/
219+ registry/ manifests/ summaries/
220+ data/
221+ README.md # data policy; do not commit restricted data
222+ artifacts/ # generated artifacts, Git-ignored as appropriate
223+ docs/
224+ RESEARCH_GAP.md
225+ PREREGISTRATION.md
226+ EXPERIMENT_PLAN.md
227+ COMPUTE_BUDGET.md
228+ RISK_REGISTER.md
229+ REPRODUCIBILITY.md
230+ EXPERIMENT_LOG.md
231+ DECISION_LOG.md
232+ paper/
233+ main.tex
234+ references.bib
235+ sections/
236+ figures/
237+ tables/
238+ appendix/
239+ submission/
240+ scripts/
241+ audit_environment.py
242+ run_pilot.py
243+ run_confirmatory.py
244+ build_figures.py
245+ build_paper.sh
246+
247+Use typed configuration and deterministic run IDs. Each experiment must have a manifest containing git commit, config hash, checkpoint revisions, data hashes, seeds, backend, hardware, timestamps, and outcome files. Support checkpoint/resume, interrupted jobs, structured logging, and a dry-run mode. Provide CPU-compatible smoke tests on toy models. Make paths configurable; never hardcode private tokens, endpoints, or personal file paths. Keep secrets in environment variables and redact them from logs.
248+
249+Do not create unnecessary distributed infrastructure before proving the scientific experiment works on one machine.
250+
251+10. VALIDATION AND QUALITY GATES
252+
253+Gate A — Repository readiness: existing workspace understood, new project isolated, environment audit passed, literature gap documented.
254+
255+Gate B — Baseline integrity: data validators correct, donor/recipient skill gap verified, contamination tests passed, repeated baseline runs consistent.
256+
257+Gate C — Mechanistic sanity: positive controls work on toy models; negative controls behave plausibly; hooks do not change outputs when disabled; activation shapes and token alignments are verified.
258+
259+Gate D — Transplant validity: graft is reproducible; sham and random controls implemented; information leakage audit passed; overhead and training cost disclosed.
260+
261+Gate E — Confirmatory evidence: untouched data, locked analysis, uncertainty estimates, and negative results all reported.
262+
263+Gate F — Paper integrity: every numeric claim traces to an artifact; every figure regenerates; citations verified; LaTeX compiles cleanly; independent reproduction instructions tested.
264+
265+A failed gate requires diagnosis, not rhetorical reinterpretation of failure as success.
266+
267+11. FIGURES AND TABLES — PUBLICATION STANDARD
268+
269+Produce vector PDF/SVG wherever feasible, legible at journal column widths, with accessible palettes, labeled units, uncertainty intervals, and no decorative pseudo-scientific graphics.
270+
271+Target figures (only when data support them):
272+
273+1. Conceptual donor → causal localization → alignment → recipient graft → evaluation diagram, clearly distinguishing hypotheses from observations.
274+2. Donor/recipient capability gap and held-out baseline distributions.
275+3. Layer × component causal localization heatmap with corrected significance/uncertainty.
276+4. Transplant mechanism architecture, token alignment, and dimensionality flow.
277+5. Target improvement versus control-task retention (Pareto frontier).
278+6. Donor-recipient portability matrix with confidence intervals.
279+7. Graft size versus effect size and latency/memory cost.
280+8. Ablation → loss → rescue causal chain.
281+9. Robustness across seeds, paraphrases, task difficulty, and unseen compositions.
282+10. Negative results and failure-mode taxonomy.
283+
284+Tables: model/checkpoint specifications; task splits; interventions and controls; primary effects with confidence intervals; collateral damage; compute cost; ablation study; reproducibility inventory.
285+
286+Never manufacture a figure to fill a visual quota. Every plotted point must derive from an identified data artifact.
287+
288+12. PAPER STRUCTURE AND WRITING STANDARD
289+
290+Target a strong machine-learning/mechanistic-interpretability contribution, not a marketing narrative. Assess fit for NeurIPS, ICLR, ICML, or an appropriate journal only after results and verified current submission rules; do not promise acceptance or assume formatting requirements.
291+
292+Suggested sections:
293+
294+1. Introduction: precise question, conceptual motivation, why transfer differs from conventional training and model merging, explicit contributions.
295+2. Related Work: critical positioning against model stitching, activation transfer, causal abstraction, representation alignment, editing, distillation, and mechanistic interpretability.
296+3. Operational Theory: candidate organs, transfer operators, causal estimands, and falsifiable hypotheses.
297+4. Experimental Design: controlled tasks, model pairs, interventions, controls, and identification.
298+5. Results: preregistered outcomes, uncertainty, generalization, costs, and negative findings.
299+6. Mechanistic Analysis: necessity, sufficiency, rescue, specificity, and limits.
300+7. Robustness and Failure Modes.
301+8. Discussion: what results establish, what they do not establish, and alternative explanations.
302+9. Limitations, Ethics, and Reproducibility.
303+10. Conclusion.
304+11. Appendices: full prompts, configs, model cards, supplementary tables, derivations, diagnostics, and compute details.
305+
306+The manuscript must have a substantial theoretical framework but not pretend that an intuitive metaphor is a theorem. Derive only justified mathematical statements and explicitly label conjectures. Use BibTeX records verified against primary sources. Do not invent author lists, publication metadata, DOIs, or conference versions.
307+
308+Paper title may be refined if findings warrant narrower claims. Do not use "transfer of intelligence" if evidence only shows task-specific activation bias.
309+
310+13. EXPERIMENT REGISTRY AND SCIENTIFIC AUDIT TRAIL
311+
312+For each run save:
313+
314+• research question, hypothesis, model pair, intervention family, control condition;
315+• code commit, environment lock, config and data hashes;
316+• training and alignment datasets and their split IDs;
317+• seed, intervention strength, layer, token positions, dimensionality;
318+• per-item outputs, ground truth, metrics, confidence intervals;
319+• resource usage, warnings, crashes, exclusions, and retry history;
320+• explicit status: planned / running / failed / completed / invalidated;
321+• a brief interpretation that separates observed evidence from speculation.
322+
323+Maintain docs/DECISION_LOG.md explaining methodological changes and whether they occurred before or after confirmatory lock. Never silently discard failed runs or change primary metrics after seeing outcomes.
324+
325+14. REPRODUCIBILITY, LICENSING, AND SECURITY
326+
327+Respect each model license and dataset license. Verify redistribution rights before releasing checkpoints or weights. Default to publishing code, configurations, synthetic generators, aggregate outputs, and scripts; release model-derived artifacts only when allowed. Do not exfiltrate proprietary model weights or secrets. Do not run arbitrary remote scripts without review. Avoid destructive changes to the wider UQO Research repository.
328+
329+Provide one-command setup where possible, a tiny CPU smoke test, a single-machine reference experiment, and optional multi-node execution. Record hardware-specific limitations, particularly MLX/Metal support. Include expected resource ranges measured from pilots, not guessed as facts.
330+
331+15. EXECUTION ROADMAP
332+
333+Sprint 0 — Research audit: inspect UQO Research, verify literature and licenses, write preregistration, resource budget, and gap statement.
334+
335+Sprint 1 — Controlled micro-world: build task generators and tiny-model baselines; validate donor competence and recipient weakness.
336+
337+Sprint 2 — Causal discovery: localize candidate subsystems, verify necessity and specificity, and benchmark null interventions.
338+
339+Sprint 3 — First transplant: implement one simple, falsifiable activation/subspace graft and a complete control battery. Do not prematurely scale.
340+
341+Sprint 4 — Generalization: extend to open-weight model families, mapping methods, and held-out compositional tasks.
342+
343+Sprint 5 — Mechanistic robustness: ablation/rescue, portability, persistence, collateral damage, and compute-efficiency frontiers.
344+
345+Sprint 6 — Confirmatory analysis: freeze protocol, run held-out tests, produce statistical tables and reproducible figures.
346+
347+Sprint 7 — Manuscript: write, audit, compile, and internally peer-review the paper. Maintain a claim-to-evidence traceability table.
348+
349+At each sprint: report completed artifacts, commands executed, actual results, unresolved risks, estimated remaining compute, and the next smallest scientifically meaningful experiment.
350+
351+16. AUTONOMOUS WORK RULES FOR CLAUDE CODE
352+
353+Work proactively within authorized resources. You may inspect code, design protocols, implement tests, and run bounded local experiments. Request explicit approval before substantial paid compute, destructive operations, publishing/submitting, modifying shared infrastructure, downloading exceptionally large checkpoints, or accessing restricted resources.
354+
355+Use independent critique passes for (a) causal inference, (b) representation alignment and leakage, (c) statistical validity, (d) code correctness, (e) literature novelty, and (f) paper claim-evidence consistency. If subagents are available, assign them scoped tasks and reconcile disagreements; do not treat agent agreement as empirical validation.
356+
357+Do not stop after writing a plan. Progress from the smallest working experiment through validated results, respecting approval and compute gates. Do not state that an experiment ran unless logs and artifacts prove it. Do not write fictional results sections. Use explicit placeholders for pending measurements.
358+
359+17. FIRST CONCRETE COMMAND TO EXECUTE
360+
361+Start by identifying the UQO Research root and current repository state. Create or safely reconcile wp-ai-0001. Perform the environment and literature audits. Write the preregistration and smallest pilot plan. Then implement a deterministic toy-model test that demonstrates the activation hook is a true no-op when disabled and that a controlled intervention produces a measurable, reproducible effect. Report those results before proceeding to transplantation claims.
362+
363+18. DEFINITION OF SUCCESS
364+
365+The strongest outcome is a causally validated, reproducible, selective, portable skill transfer that exceeds matched controls on genuinely unseen tasks without unacceptable collateral degradation. A rigorous demonstration that transplantation fails under well-defined architectural or representation mismatches is also a publishable scientific contribution if the boundary conditions are carefully established.
366+
367+Final principle: Build evidence strong enough that a skeptical independent researcher can reproduce the effect, rule out trivial explanations, and understand precisely what was—and was not—transferred.
added docs/PREREGISTRATION.md +67 −0
@@ -0,0 +1,67 @@
1+# Preregistration — WP-AI-0001 (v0.1, 2026-11-09)
2+
3+**Status:** draft registered *before* any transplant experiment. The Tier-0 pilot (`scripts/run_pilot.py`)
4+is explicitly *exploratory*: it serves to measure gap, verify hooks and localize the target subsystem.
5+Confirmatory analyses will be frozen in v1.0 of this document (hash recorded in `DECISION_LOG.md`) before
6+confirmatory data are generated. Changes after freeze are deviations and are logged.
7+
8+## 1. Estimands
9+
10+Notation as in CLAUDE.md §2. For a model pair (D, R), task family T with target stratum and control strata:
11+
12+- **Transfer gain** Δ_T = Q_T(R′) − Q_T(R), exact-match accuracy on held-out target items.
13+- **Control-adjusted transfer** Δ_T^adj = Δ_T − [Q_T(R_sham) − Q_T(R)] where R_sham is the *matched sham*
14+ (same positions, layers, dimensionality and norm; donor content replaced by the control defined per method).
15+- **Retention** ρ_c = Q_c(R′) / Q_c(R) for each control stratum c with Q_c(R) ≥ 0.5; otherwise report Δ_c.
16+- **Donor necessity** N_D = Q_T(D) − Q_T(D_ablated) for the localized component.
17+- **Rescue** = Q_T(D_ablated + graft) − Q_T(D_ablated).
18+
19+Unit of inference: *items nested in programs (clusters)*; model seeds are a second level. CIs: cluster
20+bootstrap over programs within a seed; seed-level summaries report mean, sd and min over ≥ 3 seeds.
21+
22+## 2. Hypotheses (Tier 0, micro-world A, target operator Ω = `qua`)
23+
24+- **H1 (localization).** In donors trained on all operators, interchange interventions identify a set of
25+ (layer, position) locations whose patching restores ≥ 0.8 of the clean answer rate at the Ω step, with
26+ the sham (corrupt-into-corrupt) rate ≤ chance + 0.1. Null: no location exceeds sham + 0.2.
27+- **H2 (sufficiency / activation graft).** Mapped donor activations grafted at the identified location
28+ raise the recipient's Ω-step accuracy by Δ_T^adj ≥ 0.25 (smallest effect of interest, SESOI) on
29+ held-out target items. Null: Δ_T^adj ≤ 0.05.
30+- **H3 (necessity).** Ablating the identified donor component reduces donor Ω-step accuracy by
31+ N_D ≥ 0.5 while control-stratum accuracy drops by ≤ 0.05.
32+- **H4 (selectivity).** Retention ρ_c ≥ 0.95 on `control_iid` for the graft that achieves H2.
33+- **H5 (rule vs cache).** The graft's effect on programs whose (Ω-neighbour, v0) combinations were absent
34+ from the alignment data is at least 0.8 × its effect on seen combinations. Null: ≤ 0.5 ×.
35+- **H6 (portability, Tier 1).** Effects replicate (Δ_T^adj ≥ SESOI) for at least one cross-family pair;
36+ we predict *failure* for mismatched-tokenizer pairs without a tokenizer bridge and record it as such.
37+
38+Directional predictions are stated for honesty; a null on any hypothesis is reported in full.
39+
40+## 3. Primary and secondary outcomes
41+
42+Primary (confirmatory): Δ_T^adj on `target_confirmatory` (generated after freeze from a new seed),
43+and ρ_c on `control_confirmatory`. Secondary: trace accuracy, Ω-step accuracy, N_D, rescue, graft
44+dimensionality, parameter count, latency, peak memory, seed stability.
45+
46+## 4. Multiplicity
47+
48+Localization sweeps (layers × positions × families × heads) are exploratory and selection is made on the
49+*development* localization set only. Confirmatory comparisons are limited to the pre-declared list in
50+`EXPERIMENT_PLAN.md` §5 (one primary contrast per method family; Holm correction across method families).
51+
52+## 5. Power
53+
54+Pilot variability will be recorded in `EXPERIMENT_LOG.md` after the pilot; the confirmatory sample size
55+(items and clusters) will be chosen by simulation from the pilot cluster bootstrap so that an effect of
56+SESOI = 0.25 has ≥ 0.9 power at α = 0.05 (two-sided) on the primary contrast. No effect sizes are invented.
57+
58+## 6. Exclusion rules
59+
60+Runs are excluded only for crashes, NaNs, or manifest/data-hash mismatches; all exclusions are listed in
61+the run manifest and in `EXPERIMENT_LOG.md`. No run is excluded for its outcome.
62+
63+## 7. Contamination safeguards
64+
65+Split hashes are stored in manifests; evaluation splits are generated with seeds disjoint from training
66+and alignment seeds; `assert_disjoint` is enforced at build time; alignment data for mappings never
67+include evaluation items (checked by item hash).
added docs/REPRODUCIBILITY.md +22 −0
@@ -0,0 +1,22 @@
1+# Reproducibility — WP-AI-0001
2+
3+```bash
4+cd ia/wp-ai-0001
5+python3 -m pip install -e ".[dev]" # or: uv pip install -e ".[dev]"
6+python3 -m pytest tests -q # unit + causal-sanity tests (CPU, < 30 s)
7+python3 scripts/audit_environment.py # writes docs/ENVIRONMENT_AUDIT.json (add --cluster to probe nodes)
8+python3 scripts/run_pilot.py --quick --device cpu --seeds 0 1 --n-boot 200 # CPU smoke test, ≈ 25 s
9+python3 scripts/run_pilot.py --seeds 0 1 2 --steps 3000 # Tier-0 pilot (MPS ≈ minutes)
10+```
11+
12+- Every run writes `experiments/manifests/<run_id>.json` (git commit, config hash, data hashes, seeds,
13+ libraries, hardware, status history), appends to `experiments/registry/runs.jsonl`, and stores results in
14+ `experiments/summaries/<run_id>/` (`results.json`, `SUMMARY.md`). Per-item predictions are in
15+ `artifacts/runs/<run_id>/` (git-ignored), models in `artifacts/models/<run_id>/`.
16+- Run IDs are deterministic functions of the experiment id and config hash; re-running with the same config
17+ reuses trained models from `artifacts/models/<run_id>/` (delete to retrain).
18+- Determinism: PyTorch on MPS was verified bitwise-repeatable for this model (`noop.deterministic_repeat`);
19+ localization sweeps repeated twice differ by 0.0. Training determinism across devices is *not* claimed
20+ (CPU vs MPS kernels differ); seeds reproduce training on the same device/library versions.
21+- Data are synthetic and regenerated from seeds; the split hashes recorded in manifests must match.
22+- Secrets: none required. No tokens are read; paths are relative to the project root.
added docs/RESEARCH_GAP.md +84 −0
@@ -0,0 +1,84 @@
1+# Research gap — WP-AI-0001 (written 2026-11-09, before any transplant experiment)
2+
3+Literature status: **verified via arXiv API metadata and abstracts on 2026-11-09** for every record in
4+`literature/arxiv_verified_2026-11-09.txt` (identifiers, titles, first-submission dates, authors) and
5+`literature/references_verified.bib`. Full texts were not re-read for every item; claims below about
6+*what each paper does* come from the verified abstracts and the project's search notes. Anything not in
7+the verified list is marked (unverified) and must not be cited.
8+
9+## 1. What is already known
10+
11+**Representation similarity across models.** SVCCA (Raghu et al. 2017, 1706.05806), CKA (Kornblith et al.
12+2019, 1905.00414), the Platonic Representation Hypothesis (Huh et al. 2024, 2405.07987) and Multi-Way
13+Representation Alignment (Achara et al. 2026, 2602.06205) establish that independently trained networks
14+have *linearly related* representation spaces. Shared embedding geometry allows steering vectors to move
15+between models (Lee et al. 2025, 2503.21073).
16+
17+**Model stitching.** Bansal, Nakkiran & Barak (2021, 2106.07682) revisit stitching as a representation
18+comparison tool (vision). Chen, Merullo, Stolfo & Pavlick (2025, 2506.06609; NeurIPS 2025) show that affine
19+maps between residual streams of *same-family* language models of different sizes transfer SAEs, probes
20+and steering vectors. Kim, Mun & Han (2026, 2608.09521) learn a shared dense "activation bus" with one
21+encoder–decoder pair per model and show that an intermediate activation of one model can be consumed by
22+another model's frozen upper layers.
23+
24+**Cross-model feature dictionaries.** Lan et al. (2024, 2410.06981), Universal SAEs (Thasarathan et al.
25+2025, 2502.03714), SPARC (2025, 2507.06265) and SharedSAE (Ognev et al. 2026, 2609.04344) build concept
26+spaces shared across models, mostly for *interpretation*, not for conferring capabilities.
27+
28+**Cross-model steering and circuit transfer.** Agarwal (2026, 2608.05164) transfers SAE-derived steering
29+directions across five models (0.8B–8B) and reports functional transfer above ≈1.7B parameters for
30+*semantic concepts*. Cristofano (2026, 2601.16034) transfers *refusal* interventions across eight model
31+pairs by concept-basis reconstruction. Sinii et al. (2025, 2509.06608) find RL-induced reasoning steering
32+vectors transfer only within a family.
33+
34+**Capability transfer across scales.** Panigrahi et al. (2023, 2302.06600) graft ≈0.01 % of fine-tuned
35+parameters back onto a pre-trained model ("skill localization", same model). APM (Fan et al. 2026,
36+2608.13596) prunes a larger donor to a recipient's architecture and mixes it in with a micro coefficient;
37+SemAlign (Gu et al. 2025, 2510.24208) uses activations as the transfer medium but *trains* the recipient
38+layer by layer. Task arithmetic (Ilharco et al. 2022, 2212.04089), TIES (Yadav et al. 2023, 2306.01708) and
39+Git Re-Basin (Ainsworth et al. 2022, 2209.04836) merge *weights* of models with a common initialization.
40+Zero-shot tokenizer transfer (Minixhofer et al. 2024, 2405.07883) addresses the tokenizer boundary.
41+
42+**Causal methods.** Causal mediation (Vig et al. 2020, 2004.12265), causal tracing / ROME (Meng et al. 2022,
43+2202.05262), interchange interventions and causal abstraction (Geiger et al. 2023, 2301.04709 and DAS,
44+2303.02536), circuit discovery (Wang et al. 2022, 2211.00593; Conmy et al. 2023, 2304.14997), function /
45+task vectors (Todd et al. 2023, 2310.15213; Hendel et al. 2023, 2310.15916), activation steering (Turner
46+et al. 2023, 2308.10248; Li et al. 2023, 2306.03341; Zou et al. 2023, 2310.01405).
47+
48+**The closest negative result.** Zhang & Xin (2026, 2606.03280) learn a linear bridge from Pythia-160M to
49+Pythia-410M hidden states (cosine ≈ 0.97) and find that injected activations **do not** improve multi-hop
50+answering: additive injection is null, replacement is destructive. Their conclusion: *offline
51+representational alignment is not sufficient for useful causal communication inside the receiver.*
52+
53+## 2. What is missing (the gap this project targets)
54+
55+1. **No study starts from a causally identified subsystem.** Stitching, activation-bus and steering-transfer
56+ work map *whole residual streams* or *concept directions*; APM prunes by activation salience. None first
57+ establishes, by interchange interventions in the donor, that a bounded component is necessary and
58+ sufficient for a *measurable capability*, and then asks whether *that* component transfers.
59+2. **No controlled donor/recipient capability gap.** Prior work takes two off-the-shelf models and measures
60+ benchmark deltas. We *manufacture* the gap (recipient trained without the capability; Tier 0) or
61+ verify it with repeated held-out measurement (Tier 1–2), so that "acquisition" is identifiable.
62+3. **No matched-control battery.** Sham mappings, norm-matched random grafts, unrelated-skill donors,
63+ shuffled-token/layer grafts and matched-size adapter fine-tuning are not reported together in any of
64+ the above; the negative result of 2606.03280 is itself uncontrolled with respect to these.
65+4. **Persistence is conflated.** Inference-time steering, saved adapters and weight edits are different
66+ claims; we label single-token, within-sequence, cross-prompt and saved-checkpoint effects separately.
67+5. **Rule versus cache.** Whether a transferred component carries a *rule* (generalizing to unseen
68+ compositions / symbols) rather than a bias toward answer tokens is not tested by existing transfer work.
69+6. **Failure boundaries.** Agarwal (2026) reports a scale discontinuity and Sinii et al. (2025) a family
70+ boundary for *steering*; nothing comparable exists for *capability* transfer with causal controls.
71+
72+## 3. What would *not* be novel (and we will not claim)
73+
74+- Affine residual-stream mappings as such (Chen et al. 2025; Kim et al. 2026).
75+- Cross-model steering of concepts (Agarwal 2026; Lee et al. 2025).
76+- Grafting fine-tuned parameters within one model (Panigrahi et al. 2023).
77+- Any "organ" metaphor as a theoretical contribution; the contribution must be the causal design and the
78+ measured transfer / non-transfer boundaries.
79+
80+## 4. Positioning sentence (to be refined after results)
81+
82+*We ask whether a component that is causally necessary and sufficient for a capability in a donor can be
83+installed in a recipient that demonstrably lacks it, under sham, random and matched-training controls,
84+and we map where this fails.* A rigorous negative result with boundary conditions is an acceptable outcome.
added docs/RISK_REGISTER.md +16 −0
@@ -0,0 +1,16 @@
1+# Risk register — WP-AI-0001
2+
3+| ID | Risk | Likelihood | Impact | Mitigation / detection |
4+|---|---|---|---|---|
5+| R1 | Tier-0 task too easy (scratchpad makes Ω a lookup) → transfer of a table, not a rule | high | medium | `trace_stride > 1` variants; FSA family; H5 rule-vs-cache test on unseen (neighbour, v0) combinations |
6+| R2 | Tier-0 task too hard (single-token format failed to generalize in the first attempt, dev ≈ 0.3) | observed | high | scratchpad adopted (DECISION_LOG D2); keep single-token variant as a difficulty stratum only |
7+| R3 | Localization not reproducible across seeds | low (pilot smoke: r ≥ 0.97) | high | across-seed Pearson on heatmaps; selection on development set only |
8+| R4 | Graft effect is answer-token bias, not capability | medium | high | rule-vs-cache split, shuffled-token graft, logit-lens of grafted stream, control strata |
9+| R5 | Leakage through alignment data (mapping fitted on evaluation items) | medium | high | item-hash audit of alignment vs evaluation splits in every manifest |
10+| R6 | MLX does not expose hooks → silent substitution | medium | medium | layer-wrapping implementation with bitwise no-op test; equivalence test vs PyTorch on logits |
11+| R7 | Memory pressure on 3B activation capture | medium | medium | streamed capture; per-layer limits; profiling before sweeps |
12+| R8 | Multiple comparisons in sweeps inflate findings | high | high | exploratory/confirmatory separation; Holm across families; pre-declared contrasts |
13+| R9 | Literature overlap (2606.03280 negative; 2608.09521 activation bus) | certain | medium | position as controlled, causally identified extension; cite and compare explicitly |
14+| R10 | Cluster node unavailability | medium | low | laptop suffices for Tiers 0–1; nodes probed, never assumed |
15+| R11 | License restrictions on redistributing derived weights | medium | low | release code/configs/synthetic data; adapters only where licenses permit (Qwen: Apache-2.0; Llama 3.2: community license — check before release) |
16+| R12 | Over-claiming "intelligence transfer" | medium | high | title and claims limited to measured task-specific effects; claim-evidence table (Gate F) |
added docs/TASKS.md +37 −0
@@ -0,0 +1,37 @@
1+# Implementation task tracker — WP-AI-0001
2+
3+Legend: [x] done · [~] in progress · [ ] todo · (gate) quality gate per CLAUDE.md §10
4+
5+## Sprint 0 — Research audit
6+- [x] Locate UQO Research workspace, conventions, remotes
7+- [x] Environment audit (laptop + cluster) → `docs/ENVIRONMENT_AUDIT.json`
8+- [x] Literature verification → `literature/`
9+- [x] RESEARCH_GAP, PREREGISTRATION v0.1, EXPERIMENT_PLAN, COMPUTE_BUDGET, RISK_REGISTER, REPRODUCIBILITY, logs
10+- [x] Project skeleton, pyproject, tests (Gate A)
11+- [x] `scripts/build_figures.py`, `scripts/build_paper.sh`, paper skeleton compiles (placeholders)
12+- [x] git repo + `origin = gitsrv:srv/git/wpai0001_uqo.git`
13+
14+## Sprint 1 — Controlled micro-world
15+- [x] OPSEQ / FSA / MORPH generators + exact validators + disjointness + hashing
16+- [x] Toy transformer with hook points; no-op tests (Gate C part 1)
17+- [x] Training loop (teacher-forced scratchpad), greedy decoding, metrics, cluster bootstrap
18+- [x] Pilot run (3 seeds) — gap qualification (Gate B passed; run `pilot_tier0_opseq_451ef5087182`)
19+- [ ] FSA / MORPH toy baselines
20+- [ ] `trace_stride = 2` difficulty variant baseline
21+
22+## Sprint 2 — Causal discovery
23+- [x] Within-donor localization (patching, ablation, heads) with sham baseline and reproducibility (RQ1 pilot: Ω executed at the read position; L0 attention + L0–L2 MLPs)
24+- [ ] Add `embed`/`resid_pre` family and path patching Ω-token → r1 to the sweep
25+- [ ] Subspace localization (DAS-style / PCA of Ω-contrast) with norm-matched random subspaces
26+- [ ] Necessity test on donor (H3) with control-stratum retention
27+
28+## Sprint 3 — First transplant
29+- [ ] Alignment module: Procrustes, ridge, CCA, random, unrelated-data maps; leakage audit
30+- [ ] Activation graft + sham + norm-matched random + shuffled-token controls
31+- [ ] Gate D: reproducibility, overhead disclosure
32+
33+## Sprint 4–7
34+- [ ] Tier 1 MLX interventions (layer wrapping) + Transformers equivalence test
35+- [ ] Portability matrix, persistence (saved adapter), collateral battery
36+- [ ] Confirmatory freeze (PREREGISTRATION v1.0) and run
37+- [ ] Paper, figures, claim-evidence table (Gate F)
added experiments/manifests/pilot_tier0_opseq_451ef5087182.json +223 −0
@@ -0,0 +1,223 @@
1+{
2+ "config": {
3+ "description": "Tier-0 pilot: donor/recipient gap, hook no-op test, within-model causal localization",
4+ "donor": {
5+ "model": {
6+ "d_ff": 512,
7+ "d_model": 128,
8+ "dropout": 0.0,
9+ "max_seq_len": 24,
10+ "n_heads": 4,
11+ "n_layers": 4,
12+ "tie_embeddings": false
13+ },
14+ "name": "donor",
15+ "role": "donor",
16+ "task": {
17+ "excluded_operators": [],
18+ "family": "opseq",
19+ "held_out_pairs": [
20+ [
21+ "zob",
22+ "lar"
23+ ],
24+ [
25+ "qua",
26+ "neg"
27+ ]
28+ ],
29+ "max_len": 5,
30+ "min_len": 1,
31+ "modulus": 11,
32+ "operators": [
33+ "zib",
34+ "zob",
35+ "qua",
36+ "lar",
37+ "neg",
38+ "flip",
39+ "dro",
40+ "mik"
41+ ],
42+ "trace_stride": 1
43+ },
44+ "train": {
45+ "batch_size": 256,
46+ "device": "mps",
47+ "eval_every": 500,
48+ "lr": 0.001,
49+ "n_dev": 1000,
50+ "n_train": 40000,
51+ "seed": 0,
52+ "steps": 3000,
53+ "warmup_steps": 100,
54+ "weight_decay": 0.01
55+ }
56+ },
57+ "experiment_id": "pilot_tier0_opseq",
58+ "n_bootstrap": 2000,
59+ "n_eval": 1000,
60+ "recipient": {
61+ "model": {
62+ "d_ff": 512,
63+ "d_model": 128,
64+ "dropout": 0.0,
65+ "max_seq_len": 24,
66+ "n_heads": 4,
67+ "n_layers": 4,
68+ "tie_embeddings": false
69+ },
70+ "name": "recipient",
71+ "role": "recipient",
72+ "task": {
73+ "excluded_operators": [
74+ "qua"
75+ ],
76+ "family": "opseq",
77+ "held_out_pairs": [
78+ [
79+ "zob",
80+ "lar"
81+ ],
82+ [
83+ "qua",
84+ "neg"
85+ ]
86+ ],
87+ "max_len": 5,
88+ "min_len": 1,
89+ "modulus": 11,
90+ "operators": [
91+ "zib",
92+ "zob",
93+ "qua",
94+ "lar",
95+ "neg",
96+ "flip",
97+ "dro",
98+ "mik"
99+ ],
100+ "trace_stride": 1
101+ },
102+ "train": {
103+ "batch_size": 256,
104+ "device": "mps",
105+ "eval_every": 500,
106+ "lr": 0.001,
107+ "n_dev": 1000,
108+ "n_train": 40000,
109+ "seed": 0,
110+ "steps": 3000,
111+ "warmup_steps": 100,
112+ "weight_decay": 0.01
113+ }
114+ },
115+ "seeds": [
116+ 0,
117+ 1,
118+ 2
119+ ],
120+ "target_operator": "qua"
121+ },
122+ "created_utc": "2026-11-09T06:49:35+00:00",
123+ "data_hashes": {
124+ "comp_heldout": {
125+ "generator": "opseq@opseq-1.0",
126+ "hash": "b1c6aebefbf3a5d5",
127+ "n": 1000,
128+ "seed": 20261121
129+ },
130+ "control_iid": {
131+ "generator": "opseq@opseq-1.0",
132+ "hash": "fa7c2327072b7d56",
133+ "n": 1000,
134+ "seed": 20261120
135+ },
136+ "donor_dev": {
137+ "generator": "opseq@opseq-1.0",
138+ "hash": "67055896a5ce52ea",
139+ "n": 1000,
140+ "seed": 20261112
141+ },
142+ "donor_train": {
143+ "generator": "opseq@opseq-1.0",
144+ "hash": "d213971f9e1feabf",
145+ "n": 40000,
146+ "seed": 20261110
147+ },
148+ "len_ood": {
149+ "generator": "opseq@opseq-1.0",
150+ "hash": "7ecb075ac45796ac",
151+ "n": 500,
152+ "seed": 20261122
153+ },
154+ "recipient_dev": {
155+ "generator": "opseq@opseq-1.0",
156+ "hash": "aa252ba85caa5d1f",
157+ "n": 1000,
158+ "seed": 20261113
159+ },
160+ "recipient_train": {
161+ "generator": "opseq@opseq-1.0",
162+ "hash": "c464e2683affd837",
163+ "n": 40000,
164+ "seed": 20261111
165+ },
166+ "target_iid": {
167+ "generator": "opseq@opseq-1.0",
168+ "hash": "f1a1ef205c8f35db",
169+ "n": 1000,
170+ "seed": 20261119
171+ }
172+ },
173+ "experiment_id": "pilot_tier0_opseq",
174+ "git": {
175+ "branch": null,
176+ "commit": null,
177+ "dirty": null
178+ },
179+ "hardware": {
180+ "chip": "Apple M5 Max",
181+ "cpu_count": 18,
182+ "hostname_hash": "9ad7358c662e",
183+ "machine": "arm64",
184+ "memory_gb": 48.0,
185+ "platform": "macOS-27.0-arm64-arm-64bit-Mach-O",
186+ "torch_cuda": false,
187+ "torch_mps": true
188+ },
189+ "history": [
190+ {
191+ "note": "",
192+ "status": "running",
193+ "utc": "2026-11-09T06:49:38+00:00"
194+ },
195+ {
196+ "note": "",
197+ "status": "completed",
198+ "utc": "2026-11-09T06:54:13+00:00"
199+ }
200+ ],
201+ "interpretation": "",
202+ "libraries": {
203+ "mlx": "?",
204+ "mlx_lm": "0.31.3",
205+ "numpy": "2.4.4",
206+ "pydantic": "2.13.4",
207+ "python": "3.14.4",
208+ "safetensors": "0.7.0",
209+ "scipy": "1.17.1",
210+ "torch": "2.12.0",
211+ "transformers": "5.8.1"
212+ },
213+ "outputs": {
214+ "figures": "artifacts/figures/pilot_tier0_opseq_451ef5087182",
215+ "items": "artifacts/runs/pilot_tier0_opseq_451ef5087182",
216+ "models": "artifacts/models/pilot_tier0_opseq_451ef5087182",
217+ "results": "experiments/summaries/pilot_tier0_opseq_451ef5087182/results.json"
218+ },
219+ "run_id": "pilot_tier0_opseq_451ef5087182",
220+ "seeds": {},
221+ "status": "completed",
222+ "warnings": []
223+}
\ No newline at end of file
added experiments/manifests/pilot_tier0_opseq_quick_d5339bcad6b3.json +222 −0
@@ -0,0 +1,222 @@
1+{
2+ "config": {
3+ "description": "Tier-0 pilot: donor/recipient gap, hook no-op test, within-model causal localization",
4+ "donor": {
5+ "model": {
6+ "d_ff": 128,
7+ "d_model": 64,
8+ "dropout": 0.0,
9+ "max_seq_len": 24,
10+ "n_heads": 2,
11+ "n_layers": 3,
12+ "tie_embeddings": false
13+ },
14+ "name": "donor",
15+ "role": "donor",
16+ "task": {
17+ "excluded_operators": [],
18+ "family": "opseq",
19+ "held_out_pairs": [
20+ [
21+ "zob",
22+ "lar"
23+ ],
24+ [
25+ "qua",
26+ "neg"
27+ ]
28+ ],
29+ "max_len": 5,
30+ "min_len": 1,
31+ "modulus": 11,
32+ "operators": [
33+ "zib",
34+ "zob",
35+ "qua",
36+ "lar",
37+ "neg",
38+ "flip",
39+ "dro",
40+ "mik"
41+ ],
42+ "trace_stride": 1
43+ },
44+ "train": {
45+ "batch_size": 256,
46+ "device": "cpu",
47+ "eval_every": 500,
48+ "lr": 0.001,
49+ "n_dev": 300,
50+ "n_train": 3000,
51+ "seed": 0,
52+ "steps": 200,
53+ "warmup_steps": 100,
54+ "weight_decay": 0.01
55+ }
56+ },
57+ "experiment_id": "pilot_tier0_opseq_quick",
58+ "n_bootstrap": 200,
59+ "n_eval": 300,
60+ "recipient": {
61+ "model": {
62+ "d_ff": 128,
63+ "d_model": 64,
64+ "dropout": 0.0,
65+ "max_seq_len": 24,
66+ "n_heads": 2,
67+ "n_layers": 3,
68+ "tie_embeddings": false
69+ },
70+ "name": "recipient",
71+ "role": "recipient",
72+ "task": {
73+ "excluded_operators": [
74+ "qua"
75+ ],
76+ "family": "opseq",
77+ "held_out_pairs": [
78+ [
79+ "zob",
80+ "lar"
81+ ],
82+ [
83+ "qua",
84+ "neg"
85+ ]
86+ ],
87+ "max_len": 5,
88+ "min_len": 1,
89+ "modulus": 11,
90+ "operators": [
91+ "zib",
92+ "zob",
93+ "qua",
94+ "lar",
95+ "neg",
96+ "flip",
97+ "dro",
98+ "mik"
99+ ],
100+ "trace_stride": 1
101+ },
102+ "train": {
103+ "batch_size": 256,
104+ "device": "cpu",
105+ "eval_every": 500,
106+ "lr": 0.001,
107+ "n_dev": 300,
108+ "n_train": 3000,
109+ "seed": 0,
110+ "steps": 200,
111+ "warmup_steps": 100,
112+ "weight_decay": 0.01
113+ }
114+ },
115+ "seeds": [
116+ 0,
117+ 1
118+ ],
119+ "target_operator": "qua"
120+ },
121+ "created_utc": "2026-11-09T06:49:08+00:00",
122+ "data_hashes": {
123+ "comp_heldout": {
124+ "generator": "opseq@opseq-1.0",
125+ "hash": "68e83ab8472513d2",
126+ "n": 300,
127+ "seed": 20261121
128+ },
129+ "control_iid": {
130+ "generator": "opseq@opseq-1.0",
131+ "hash": "76bdfaf36a5f7519",
132+ "n": 300,
133+ "seed": 20261120
134+ },
135+ "donor_dev": {
136+ "generator": "opseq@opseq-1.0",
137+ "hash": "79b849f32dd6df25",
138+ "n": 300,
139+ "seed": 20261112
140+ },
141+ "donor_train": {
142+ "generator": "opseq@opseq-1.0",
143+ "hash": "da62d95aa07ed95a",
144+ "n": 3000,
145+ "seed": 20261110
146+ },
147+ "len_ood": {
148+ "generator": "opseq@opseq-1.0",
149+ "hash": "6d8c13dc8b5c47fa",
150+ "n": 200,
151+ "seed": 20261122
152+ },
153+ "recipient_dev": {
154+ "generator": "opseq@opseq-1.0",
155+ "hash": "c697914975bb93d3",
156+ "n": 300,
157+ "seed": 20261113
158+ },
159+ "recipient_train": {
160+ "generator": "opseq@opseq-1.0",
161+ "hash": "6d121a82cc83fe31",
162+ "n": 3000,
163+ "seed": 20261111
164+ },
165+ "target_iid": {
166+ "generator": "opseq@opseq-1.0",
167+ "hash": "88d9bb27db961222",
168+ "n": 300,
169+ "seed": 20261119
170+ }
171+ },
172+ "experiment_id": "pilot_tier0_opseq_quick",
173+ "git": {
174+ "branch": null,
175+ "commit": null,
176+ "dirty": null
177+ },
178+ "hardware": {
179+ "chip": "Apple M5 Max",
180+ "cpu_count": 18,
181+ "hostname_hash": "9ad7358c662e",
182+ "machine": "arm64",
183+ "memory_gb": 48.0,
184+ "platform": "macOS-27.0-arm64-arm-64bit-Mach-O",
185+ "torch_cuda": false,
186+ "torch_mps": true
187+ },
188+ "history": [
189+ {
190+ "note": "",
191+ "status": "running",
192+ "utc": "2026-11-09T06:49:09+00:00"
193+ },
194+ {
195+ "note": "",
196+ "status": "completed",
197+ "utc": "2026-11-09T06:49:31+00:00"
198+ }
199+ ],
200+ "interpretation": "",
201+ "libraries": {
202+ "mlx": "?",
203+ "mlx_lm": "0.31.3",
204+ "numpy": "2.4.4",
205+ "pydantic": "2.13.4",
206+ "python": "3.14.4",
207+ "safetensors": "0.7.0",
208+ "scipy": "1.17.1",
209+ "torch": "2.12.0",
210+ "transformers": "5.8.1"
211+ },
212+ "outputs": {
213+ "figures": "artifacts/figures/pilot_tier0_opseq_quick_d5339bcad6b3",
214+ "items": "artifacts/runs/pilot_tier0_opseq_quick_d5339bcad6b3",
215+ "models": "artifacts/models/pilot_tier0_opseq_quick_d5339bcad6b3",
216+ "results": "experiments/summaries/pilot_tier0_opseq_quick_d5339bcad6b3/results.json"
217+ },
218+ "run_id": "pilot_tier0_opseq_quick_d5339bcad6b3",
219+ "seeds": {},
220+ "status": "completed",
221+ "warnings": []
222+}
\ No newline at end of file
added experiments/registry/runs.jsonl +6 −0
@@ -0,0 +1,6 @@
1+{"config_hash": "d5339bcad6b3", "experiment_id": "pilot_tier0_opseq_quick", "run_id": "pilot_tier0_opseq_quick_d5339bcad6b3", "status": "planned", "utc": "2026-11-09T06:49:09+00:00"}
2+{"experiment_id": "pilot_tier0_opseq_quick", "run_id": "pilot_tier0_opseq_quick_d5339bcad6b3", "status": "running", "utc": "2026-11-09T06:49:09+00:00"}
3+{"experiment_id": "pilot_tier0_opseq_quick", "results": "experiments/summaries/pilot_tier0_opseq_quick_d5339bcad6b3/results.json", "run_id": "pilot_tier0_opseq_quick_d5339bcad6b3", "status": "completed", "utc": "2026-11-09T06:49:31+00:00"}
4+{"config_hash": "451ef5087182", "experiment_id": "pilot_tier0_opseq", "run_id": "pilot_tier0_opseq_451ef5087182", "status": "planned", "utc": "2026-11-09T06:49:37+00:00"}
5+{"experiment_id": "pilot_tier0_opseq", "run_id": "pilot_tier0_opseq_451ef5087182", "status": "running", "utc": "2026-11-09T06:49:38+00:00"}
6+{"experiment_id": "pilot_tier0_opseq", "results": "experiments/summaries/pilot_tier0_opseq_451ef5087182/results.json", "run_id": "pilot_tier0_opseq_451ef5087182", "status": "completed", "utc": "2026-11-09T06:54:13+00:00"}
added experiments/summaries/pilot_tier0_opseq_451ef5087182/SUMMARY.md +44 −0
@@ -0,0 +1,44 @@
1+# Pilot summary — pilot_tier0_opseq_451ef5087182
2+
3+Generated 2026-11-09T06:54:13+00:00 on device `mps`; config hash `451ef5087182`; seeds [0, 1, 2].
4+
5+## Gate B — donor/recipient gap (final-answer exact match, mean over seeds; CI = cluster bootstrap on programs)
6+
7+| split | chance | donor mean (sd) | recipient mean (sd) | paired gap seed 0 [95% CI] |
8+|---|---|---|---|---|
9+| target_iid | 0.105 | 1.000 (0.000) | 0.092 (0.053) | +0.847 [+0.825, +0.870] |
10+| control_iid | 0.129 | 1.000 (0.000) | 1.000 (0.000) | +0.000 [+0.000, +0.000] |
11+| comp_heldout | 0.110 | 1.000 (0.000) | 0.450 (0.032) | +0.513 [+0.468, +0.560] |
12+| len_ood | 0.140 | 0.086 (0.017) | 0.091 (0.016) | -0.002 [-0.034, +0.032] |
13+
14+Ω-step accuracy on target_iid (token emitted at the first Ω step): donor 1.000, recipient 0.062.
15+
16+## Gate C — hook no-op checks (bitwise equality of logits on 256 dev items)
17+
18+| model | repeat | capture-all | disabled hook | enabled zero hook changes output |
19+|---|---|---|---|---|
20+| donor_s0 | True | True | True | True |
21+| recipient_s0 | True | True | True | True |
22+| donor_s1 | True | True | True | True |
23+| recipient_s1 | True | True | True | True |
24+| donor_s2 | True | True | True | True |
25+| recipient_s2 | True | True | True | True |
26+
27+## RQ1 — within-donor localization of Ω (n = 490 clean/corrupt pairs, read position = r1, predicting r2)
28+
29+Baselines: s0: clean 1.000, sham (corrupt run scored on clean answer) 0.000; s1: clean 1.000, sham (corrupt run scored on clean answer) 0.000; s2: clean 1.000, sham (corrupt run scored on clean answer) 0.000
30+
31+Mean restored-answer rate after patching the clean activation into the corrupted run (rows = layers, columns = positions <bos>, v0, op1, Ω, op3, =, r1):
32+
33+- `resid_post`: L0: 0.00 0.00 0.00 0.00 0.00 0.00 1.00 | L1: 0.00 0.00 0.00 0.00 0.00 0.00 1.00 | L2: 0.00 0.00 0.00 0.00 0.00 0.00 1.00 | L3: 0.00 0.00 0.00 0.00 0.00 0.00 1.00
34+- `attn_out`: L0: 0.00 0.00 0.00 0.00 0.00 0.00 1.00 | L1: 0.00 0.00 0.00 0.00 0.00 0.00 0.00 | L2: 0.00 0.00 0.00 0.00 0.00 0.00 0.00 | L3: 0.00 0.00 0.00 0.00 0.00 0.00 0.00
35+- `mlp_out`: L0: 0.00 0.00 0.00 0.00 0.00 0.00 1.00 | L1: 0.00 0.00 0.00 0.00 0.00 0.00 0.90 | L2: 0.00 0.00 0.00 0.00 0.00 0.00 0.83 | L3: 0.00 0.00 0.00 0.00 0.00 0.00 0.17
36+
37+Reproducibility: resid_post_s0_repeat_max_abs_diff = 0.0000; attn_out_s0_repeat_max_abs_diff = 0.0000; mlp_out_s0_repeat_max_abs_diff = 0.0000; resid_post_s1_repeat_max_abs_diff = 0.0000; attn_out_s1_repeat_max_abs_diff = 0.0000; mlp_out_s1_repeat_max_abs_diff = 0.0000; resid_post_s2_repeat_max_abs_diff = 0.0000; attn_out_s2_repeat_max_abs_diff = 0.0000; mlp_out_s2_repeat_max_abs_diff = 0.0000; resid_post_across_seed_pearson_min = 1.0000; attn_out_across_seed_pearson_min = 1.0000; mlp_out_across_seed_pearson_min = 0.9642
38+
39+Figures: `fig_gap_tier0.pdf`, `fig_localization_ablation_tier0.pdf`, `fig_localization_heads_tier0.pdf`, `fig_localization_patching_tier0.pdf`
40+
41+## Interpretation (observed vs. speculative)
42+
43+Observed: the numbers above are produced by the artifacts listed in the manifest. Any statement about *transfer* is out of scope for this run: no transplant was performed.
44+Speculative: whether the localized positions/layers constitute a transferable 'organ' is the object of the next experiment (activation graft with sham/random controls).
added experiments/summaries/pilot_tier0_opseq_451ef5087182/results.json +2303 −0
@@ -0,0 +1,2303 @@
1+{
2+ "run_id": "pilot_tier0_opseq_451ef5087182",
3+ "config_hash": "451ef5087182",
4+ "device": "mps",
5+ "chance_level": {
6+ "target_iid": 0.105,
7+ "control_iid": 0.129,
8+ "comp_heldout": 0.11,
9+ "len_ood": 0.14
10+ },
11+ "seeds": {
12+ "0": {
13+ "donor": {
14+ "n_params": 800000,
15+ "train_seconds": 45.07666206359863,
16+ "final_dev": {
17+ "step": 3000,
18+ "loss": 1.4056669606077321e-08,
19+ "dev_acc": 1.0,
20+ "dev_trace_acc": 1.0,
21+ "lr": 0.0,
22+ "elapsed_s": 44.99317193031311
23+ },
24+ "eval": {
25+ "target_iid": {
26+ "split": "target_iid",
27+ "split_hash": "f1a1ef205c8f35db",
28+ "n": 1000,
29+ "accuracy": 1.0,
30+ "trace_accuracy": 1.0,
31+ "ci95": {
32+ "estimate": 1.0,
33+ "low": 1.0,
34+ "high": 1.0,
35+ "level": 0.95,
36+ "n_items": 1000,
37+ "n_clusters": 944,
38+ "n_boot": 2000
39+ },
40+ "omega_step_acc": {
41+ "estimate": 1.0,
42+ "low": 1.0,
43+ "high": 1.0,
44+ "level": 0.95,
45+ "n_items": 1000,
46+ "n_clusters": 944,
47+ "n_boot": 2000
48+ },
49+ "chance": 0.105
50+ },
51+ "control_iid": {
52+ "split": "control_iid",
53+ "split_hash": "fa7c2327072b7d56",
54+ "n": 1000,
55+ "accuracy": 1.0,
56+ "trace_accuracy": 1.0,
57+ "ci95": {
58+ "estimate": 1.0,
59+ "low": 1.0,
60+ "high": 1.0,
61+ "level": 0.95,
62+ "n_items": 1000,
63+ "n_clusters": 970,
64+ "n_boot": 2000
65+ },
66+ "omega_step_acc": null,
67+ "chance": 0.129
68+ },
69+ "comp_heldout": {
70+ "split": "comp_heldout",
71+ "split_hash": "b1c6aebefbf3a5d5",
72+ "n": 1000,
73+ "accuracy": 1.0,
74+ "trace_accuracy": 1.0,
75+ "ci95": {
76+ "estimate": 1.0,
77+ "low": 1.0,
78+ "high": 1.0,
79+ "level": 0.95,
80+ "n_items": 1000,
81+ "n_clusters": 681,
82+ "n_boot": 2000
83+ },
84+ "omega_step_acc": {
85+ "estimate": 1.0,
86+ "low": 1.0,
87+ "high": 1.0,
88+ "level": 0.95,
89+ "n_items": 592,
90+ "n_clusters": 420,
91+ "n_boot": 2000
92+ },
93+ "chance": 0.11
94+ },
95+ "len_ood": {
96+ "split": "len_ood",
97+ "split_hash": "7ecb075ac45796ac",
98+ "n": 500,
99+ "accuracy": 0.076,
100+ "trace_accuracy": 0.0,
101+ "ci95": {
102+ "estimate": 0.076,
103+ "low": 0.054,
104+ "high": 0.1,
105+ "level": 0.95,
106+ "n_items": 500,
107+ "n_clusters": 500,
108+ "n_boot": 2000
109+ },
110+ "omega_step_acc": {
111+ "estimate": 0.09830508474576272,
112+ "low": 0.06440677966101695,
113+ "high": 0.13220338983050847,
114+ "level": 0.95,
115+ "n_items": 295,
116+ "n_clusters": 295,
117+ "n_boot": 2000
118+ },
119+ "chance": 0.14
120+ }
121+ }
122+ },
123+ "recipient": {
124+ "n_params": 800000,
125+ "train_seconds": 44.30060911178589,
126+ "final_dev": {
127+ "step": 3000,
128+ "loss": 6.440794120265991e-09,
129+ "dev_acc": 1.0,
130+ "dev_trace_acc": 1.0,
131+ "lr": 0.0,
132+ "elapsed_s": 44.22483992576599
133+ },
134+ "eval": {
135+ "target_iid": {
136+ "split": "target_iid",
137+ "split_hash": "f1a1ef205c8f35db",
138+ "n": 1000,
139+ "accuracy": 0.153,
140+ "trace_accuracy": 0.11,
141+ "ci95": {
142+ "estimate": 0.153,
143+ "low": 0.13021422874046046,
144+ "high": 0.17482830290981383,
145+ "level": 0.95,
146+ "n_items": 1000,
147+ "n_clusters": 944,
148+ "n_boot": 2000
149+ },
150+ "omega_step_acc": {
151+ "estimate": 0.142,
152+ "low": 0.12102406103286387,
153+ "high": 0.16371086200231927,
154+ "level": 0.95,
155+ "n_items": 1000,
156+ "n_clusters": 944,
157+ "n_boot": 2000
158+ },
159+ "chance": 0.105
160+ },
161+ "control_iid": {
162+ "split": "control_iid",
163+ "split_hash": "fa7c2327072b7d56",
164+ "n": 1000,
165+ "accuracy": 1.0,
166+ "trace_accuracy": 1.0,
167+ "ci95": {
168+ "estimate": 1.0,
169+ "low": 1.0,
170+ "high": 1.0,
171+ "level": 0.95,
172+ "n_items": 1000,
173+ "n_clusters": 970,
174+ "n_boot": 2000
175+ },
176+ "omega_step_acc": null,
177+ "chance": 0.129
178+ },
179+ "comp_heldout": {
180+ "split": "comp_heldout",
181+ "split_hash": "b1c6aebefbf3a5d5",
182+ "n": 1000,
183+ "accuracy": 0.487,
184+ "trace_accuracy": 0.466,
185+ "ci95": {
186+ "estimate": 0.487,
187+ "low": 0.4403249441330549,
188+ "high": 0.5318091451292246,
189+ "level": 0.95,
190+ "n_items": 1000,
191+ "n_clusters": 681,
192+ "n_boot": 2000
193+ },
194+ "omega_step_acc": {
195+ "estimate": 0.13006756756756757,
196+ "low": 0.10363636363636364,
197+ "high": 0.15476609657947685,
198+ "level": 0.95,
199+ "n_items": 592,
200+ "n_clusters": 420,
201+ "n_boot": 2000
202+ },
203+ "chance": 0.11
204+ },
205+ "len_ood": {
206+ "split": "len_ood",
207+ "split_hash": "7ecb075ac45796ac",
208+ "n": 500,
209+ "accuracy": 0.078,
210+ "trace_accuracy": 0.0,
211+ "ci95": {
212+ "estimate": 0.078,
213+ "low": 0.054,
214+ "high": 0.102,
215+ "level": 0.95,
216+ "n_items": 500,
217+ "n_clusters": 500,
218+ "n_boot": 2000
219+ },
220+ "omega_step_acc": {
221+ "estimate": 0.11186440677966102,
222+ "low": 0.07796610169491526,
223+ "high": 0.14576271186440679,
224+ "level": 0.95,
225+ "n_items": 295,
226+ "n_clusters": 295,
227+ "n_boot": 2000
228+ },
229+ "chance": 0.14
230+ }
231+ }
232+ },
233+ "gap": {
234+ "target_iid": {
235+ "estimate": 0.847,
236+ "low": 0.8251716970901862,
237+ "high": 0.8697857712595395,
238+ "level": 0.95,
239+ "n_items": 1000,
240+ "n_clusters": 944,
241+ "n_boot": 2000
242+ },
243+ "control_iid": {
244+ "estimate": 0.0,
245+ "low": 0.0,
246+ "high": 0.0,
247+ "level": 0.95,
248+ "n_items": 1000,
249+ "n_clusters": 970,
250+ "n_boot": 2000
251+ },
252+ "comp_heldout": {
253+ "estimate": 0.513,
254+ "low": 0.46819085487077533,
255+ "high": 0.5596750558669451,
256+ "level": 0.95,
257+ "n_items": 1000,
258+ "n_clusters": 681,
259+ "n_boot": 2000
260+ },
261+ "len_ood": {
262+ "estimate": -0.002,
263+ "low": -0.034,
264+ "high": 0.032,
265+ "level": 0.95,
266+ "n_items": 500,
267+ "n_clusters": 500,
268+ "n_boot": 2000
269+ }
270+ }
271+ },
272+ "1": {
273+ "donor": {
274+ "n_params": 800000,
275+ "train_seconds": 44.552977085113525,
276+ "final_dev": {
277+ "step": 3000,
278+ "loss": 2.2240537189688325e-10,
279+ "dev_acc": 1.0,
280+ "dev_trace_acc": 1.0,
281+ "lr": 0.0,
282+ "elapsed_s": 44.468523025512695
283+ },
284+ "eval": {
285+ "target_iid": {
286+ "split": "target_iid",
287+ "split_hash": "f1a1ef205c8f35db",
288+ "n": 1000,
289+ "accuracy": 1.0,
290+ "trace_accuracy": 1.0,
291+ "ci95": {
292+ "estimate": 1.0,
293+ "low": 1.0,
294+ "high": 1.0,
295+ "level": 0.95,
296+ "n_items": 1000,
297+ "n_clusters": 944,
298+ "n_boot": 2000
299+ },
300+ "omega_step_acc": {
301+ "estimate": 1.0,
302+ "low": 1.0,
303+ "high": 1.0,
304+ "level": 0.95,
305+ "n_items": 1000,
306+ "n_clusters": 944,
307+ "n_boot": 2000
308+ },
309+ "chance": 0.105
310+ },
311+ "control_iid": {
312+ "split": "control_iid",
313+ "split_hash": "fa7c2327072b7d56",
314+ "n": 1000,
315+ "accuracy": 1.0,
316+ "trace_accuracy": 1.0,
317+ "ci95": {
318+ "estimate": 1.0,
319+ "low": 1.0,
320+ "high": 1.0,
321+ "level": 0.95,
322+ "n_items": 1000,
323+ "n_clusters": 970,
324+ "n_boot": 2000
325+ },
326+ "omega_step_acc": null,
327+ "chance": 0.129
328+ },
329+ "comp_heldout": {
330+ "split": "comp_heldout",
331+ "split_hash": "b1c6aebefbf3a5d5",
332+ "n": 1000,
333+ "accuracy": 1.0,
334+ "trace_accuracy": 1.0,
335+ "ci95": {
336+ "estimate": 1.0,
337+ "low": 1.0,
338+ "high": 1.0,
339+ "level": 0.95,
340+ "n_items": 1000,
341+ "n_clusters": 681,
342+ "n_boot": 2000
343+ },
344+ "omega_step_acc": {
345+ "estimate": 1.0,
346+ "low": 1.0,
347+ "high": 1.0,
348+ "level": 0.95,
349+ "n_items": 592,
350+ "n_clusters": 420,
351+ "n_boot": 2000
352+ },
353+ "chance": 0.11
354+ },
355+ "len_ood": {
356+ "split": "len_ood",
357+ "split_hash": "7ecb075ac45796ac",
358+ "n": 500,
359+ "accuracy": 0.076,
360+ "trace_accuracy": 0.0,
361+ "ci95": {
362+ "estimate": 0.076,
363+ "low": 0.054,
364+ "high": 0.1,
365+ "level": 0.95,
366+ "n_items": 500,
367+ "n_clusters": 500,
368+ "n_boot": 2000
369+ },
370+ "omega_step_acc": {
371+ "estimate": 0.16271186440677965,
372+ "low": 0.12203389830508475,
373+ "high": 0.20677966101694914,
374+ "level": 0.95,
375+ "n_items": 295,
376+ "n_clusters": 295,
377+ "n_boot": 2000
378+ },
379+ "chance": 0.14
380+ }
381+ }
382+ },
383+ "recipient": {
384+ "n_params": 800000,
385+ "train_seconds": 44.89582920074463,
386+ "final_dev": {
387+ "step": 3000,
388+ "loss": 2.1306395536768719e-10,
389+ "dev_acc": 1.0,
390+ "dev_trace_acc": 1.0,
391+ "lr": 0.0,
392+ "elapsed_s": 44.8197238445282
393+ },
394+ "eval": {
395+ "target_iid": {
396+ "split": "target_iid",
397+ "split_hash": "f1a1ef205c8f35db",
398+ "n": 1000,
399+ "accuracy": 0.064,
400+ "trace_accuracy": 0.024,
401+ "ci95": {
402+ "estimate": 0.064,
403+ "low": 0.04909696770919216,
404+ "high": 0.07923771313941826,
405+ "level": 0.95,
406+ "n_items": 1000,
407+ "n_clusters": 944,
408+ "n_boot": 2000
409+ },
410+ "omega_step_acc": {
411+ "estimate": 0.026,
412+ "low": 0.01693227091633466,
413+ "high": 0.03625377643504532,
414+ "level": 0.95,
415+ "n_items": 1000,
416+ "n_clusters": 944,
417+ "n_boot": 2000
418+ },
419+ "chance": 0.105
420+ },
421+ "control_iid": {
422+ "split": "control_iid",
423+ "split_hash": "fa7c2327072b7d56",
424+ "n": 1000,
425+ "accuracy": 1.0,
426+ "trace_accuracy": 1.0,
427+ "ci95": {
428+ "estimate": 1.0,
429+ "low": 1.0,
430+ "high": 1.0,
431+ "level": 0.95,
432+ "n_items": 1000,
433+ "n_clusters": 970,
434+ "n_boot": 2000
435+ },
436+ "omega_step_acc": null,
437+ "chance": 0.129
438+ },
439+ "comp_heldout": {
440+ "split": "comp_heldout",
441+ "split_hash": "b1c6aebefbf3a5d5",
442+ "n": 1000,
443+ "accuracy": 0.435,
444+ "trace_accuracy": 0.421,
445+ "ci95": {
446+ "estimate": 0.435,
447+ "low": 0.38675184162062615,
448+ "high": 0.48358677815112916,
449+ "level": 0.95,
450+ "n_items": 1000,
451+ "n_clusters": 681,
452+ "n_boot": 2000
453+ },
454+ "omega_step_acc": {
455+ "estimate": 0.02364864864864865,
456+ "low": 0.01279649114613219,
457+ "high": 0.035415924087713664,
458+ "level": 0.95,
459+ "n_items": 592,
460+ "n_clusters": 420,
461+ "n_boot": 2000
462+ },
463+ "chance": 0.11
464+ },
465+ "len_ood": {
466+ "split": "len_ood",
467+ "split_hash": "7ecb075ac45796ac",
468+ "n": 500,
469+ "accuracy": 0.086,
470+ "trace_accuracy": 0.0,
471+ "ci95": {
472+ "estimate": 0.086,
473+ "low": 0.062,
474+ "high": 0.11004999999999973,
475+ "level": 0.95,
476+ "n_items": 500,
477+ "n_clusters": 500,
478+ "n_boot": 2000
479+ },
480+ "omega_step_acc": {
481+ "estimate": 0.0711864406779661,
482+ "low": 0.04406779661016949,
483+ "high": 0.1016949152542373,
484+ "level": 0.95,
485+ "n_items": 295,
486+ "n_clusters": 295,
487+ "n_boot": 2000
488+ },
489+ "chance": 0.14
490+ }
491+ }
492+ },
493+ "gap": {
494+ "target_iid": {
495+ "estimate": 0.936,
496+ "low": 0.9207622868605817,
497+ "high": 0.9509030322908079,
498+ "level": 0.95,
499+ "n_items": 1000,
500+ "n_clusters": 944,
501+ "n_boot": 2000
502+ },
503+ "control_iid": {
504+ "estimate": 0.0,
505+ "low": 0.0,
506+ "high": 0.0,
507+ "level": 0.95,
508+ "n_items": 1000,
509+ "n_clusters": 970,
510+ "n_boot": 2000
511+ },
512+ "comp_heldout": {
513+ "estimate": 0.565,
514+ "low": 0.5164132218488708,
515+ "high": 0.6132481583793739,
516+ "level": 0.95,
517+ "n_items": 1000,
518+ "n_clusters": 681,
519+ "n_boot": 2000
520+ },
521+ "len_ood": {
522+ "estimate": -0.01,
523+ "low": -0.044,
524+ "high": 0.02,
525+ "level": 0.95,
526+ "n_items": 500,
527+ "n_clusters": 500,
528+ "n_boot": 2000
529+ }
530+ }
531+ },
532+ "2": {
533+ "donor": {
534+ "n_params": 800000,
535+ "train_seconds": 44.78438401222229,
536+ "final_dev": {
537+ "step": 3000,
538+ "loss": 0.0,
539+ "dev_acc": 1.0,
540+ "dev_trace_acc": 1.0,
541+ "lr": 0.0,
542+ "elapsed_s": 44.687108755111694
543+ },
544+ "eval": {
545+ "target_iid": {
546+ "split": "target_iid",
547+ "split_hash": "f1a1ef205c8f35db",
548+ "n": 1000,
549+ "accuracy": 1.0,
550+ "trace_accuracy": 1.0,
551+ "ci95": {
552+ "estimate": 1.0,
553+ "low": 1.0,
554+ "high": 1.0,
555+ "level": 0.95,
556+ "n_items": 1000,
557+ "n_clusters": 944,
558+ "n_boot": 2000
559+ },
560+ "omega_step_acc": {
561+ "estimate": 1.0,
562+ "low": 1.0,
563+ "high": 1.0,
564+ "level": 0.95,
565+ "n_items": 1000,
566+ "n_clusters": 944,
567+ "n_boot": 2000
568+ },
569+ "chance": 0.105
570+ },
571+ "control_iid": {
572+ "split": "control_iid",
573+ "split_hash": "fa7c2327072b7d56",
574+ "n": 1000,
575+ "accuracy": 1.0,
576+ "trace_accuracy": 1.0,
577+ "ci95": {
578+ "estimate": 1.0,
579+ "low": 1.0,
580+ "high": 1.0,
581+ "level": 0.95,
582+ "n_items": 1000,
583+ "n_clusters": 970,
584+ "n_boot": 2000
585+ },
586+ "omega_step_acc": null,
587+ "chance": 0.129
588+ },
589+ "comp_heldout": {
590+ "split": "comp_heldout",
591+ "split_hash": "b1c6aebefbf3a5d5",
592+ "n": 1000,
593+ "accuracy": 1.0,
594+ "trace_accuracy": 1.0,
595+ "ci95": {
596+ "estimate": 1.0,
597+ "low": 1.0,
598+ "high": 1.0,
599+ "level": 0.95,
600+ "n_items": 1000,
601+ "n_clusters": 681,
602+ "n_boot": 2000
603+ },
604+ "omega_step_acc": {
605+ "estimate": 1.0,
606+ "low": 1.0,
607+ "high": 1.0,
608+ "level": 0.95,
609+ "n_items": 592,
610+ "n_clusters": 420,
611+ "n_boot": 2000
612+ },
613+ "chance": 0.11
614+ },
615+ "len_ood": {
616+ "split": "len_ood",
617+ "split_hash": "7ecb075ac45796ac",
618+ "n": 500,
619+ "accuracy": 0.106,
620+ "trace_accuracy": 0.0,
621+ "ci95": {
622+ "estimate": 0.106,
623+ "low": 0.08,
624+ "high": 0.134,
625+ "level": 0.95,
626+ "n_items": 500,
627+ "n_clusters": 500,
628+ "n_boot": 2000
629+ },
630+ "omega_step_acc": {
631+ "estimate": 0.15593220338983052,
632+ "low": 0.1152542372881356,
633+ "high": 0.2,
634+ "level": 0.95,
635+ "n_items": 295,
636+ "n_clusters": 295,
637+ "n_boot": 2000
638+ },
639+ "chance": 0.14
640+ }
641+ }
642+ },
643+ "recipient": {
644+ "n_params": 800000,
645+ "train_seconds": 43.506938219070435,
646+ "final_dev": {
647+ "step": 3000,
648+ "loss": 7.384645672736667e-10,
649+ "dev_acc": 1.0,
650+ "dev_trace_acc": 1.0,
651+ "lr": 0.0,
652+ "elapsed_s": 43.429972887039185
653+ },
654+ "eval": {
655+ "target_iid": {
656+ "split": "target_iid",
657+ "split_hash": "f1a1ef205c8f35db",
658+ "n": 1000,
659+ "accuracy": 0.059,
660+ "trace_accuracy": 0.013,
661+ "ci95": {
662+ "estimate": 0.059,
663+ "low": 0.04481960218826188,
664+ "high": 0.07522622322412784,
665+ "level": 0.95,
666+ "n_items": 1000,
667+ "n_clusters": 944,
668+ "n_boot": 2000
669+ },
670+ "omega_step_acc": {
671+ "estimate": 0.017,
672+ "low": 0.009910312626519584,
673+ "high": 0.025768726304529074,
674+ "level": 0.95,
675+ "n_items": 1000,
676+ "n_clusters": 944,
677+ "n_boot": 2000
678+ },
679+ "chance": 0.105
680+ },
681+ "control_iid": {
682+ "split": "control_iid",
683+ "split_hash": "fa7c2327072b7d56",
684+ "n": 1000,
685+ "accuracy": 1.0,
686+ "trace_accuracy": 1.0,
687+ "ci95": {
688+ "estimate": 1.0,
689+ "low": 1.0,
690+ "high": 1.0,
691+ "level": 0.95,
692+ "n_items": 1000,
693+ "n_clusters": 970,
694+ "n_boot": 2000
695+ },
696+ "omega_step_acc": null,
697+ "chance": 0.129
698+ },
699+ "comp_heldout": {
700+ "split": "comp_heldout",
701+ "split_hash": "b1c6aebefbf3a5d5",
702+ "n": 1000,
703+ "accuracy": 0.429,
704+ "trace_accuracy": 0.417,
705+ "ci95": {
706+ "estimate": 0.429,
707+ "low": 0.3773348057922526,
708+ "high": 0.47867104972724117,
709+ "level": 0.95,
710+ "n_items": 1000,
711+ "n_clusters": 681,
712+ "n_boot": 2000
713+ },
714+ "omega_step_acc": {
715+ "estimate": 0.02195945945945946,
716+ "low": 0.0105074216501537,
717+ "high": 0.03460972052542356,
718+ "level": 0.95,
719+ "n_items": 592,
720+ "n_clusters": 420,
721+ "n_boot": 2000
722+ },
723+ "chance": 0.11
724+ },
725+ "len_ood": {
726+ "split": "len_ood",
727+ "split_hash": "7ecb075ac45796ac",
728+ "n": 500,
729+ "accuracy": 0.108,
730+ "trace_accuracy": 0.0,
731+ "ci95": {
732+ "estimate": 0.108,
733+ "low": 0.082,
734+ "high": 0.136,
735+ "level": 0.95,
736+ "n_items": 500,
737+ "n_clusters": 500,
738+ "n_boot": 2000
739+ },
740+ "omega_step_acc": {
741+ "estimate": 0.08135593220338982,
742+ "low": 0.05084745762711865,
743+ "high": 0.11186440677966102,
744+ "level": 0.95,
745+ "n_items": 295,
746+ "n_clusters": 295,
747+ "n_boot": 2000
748+ },
749+ "chance": 0.14
750+ }
751+ }
752+ },
753+ "gap": {
754+ "target_iid": {
755+ "estimate": 0.941,
756+ "low": 0.9247737767758721,
757+ "high": 0.9551803978117381,
758+ "level": 0.95,
759+ "n_items": 1000,
760+ "n_clusters": 944,
761+ "n_boot": 2000
762+ },
763+ "control_iid": {
764+ "estimate": 0.0,
765+ "low": 0.0,
766+ "high": 0.0,
767+ "level": 0.95,
768+ "n_items": 1000,
769+ "n_clusters": 970,
770+ "n_boot": 2000
771+ },
772+ "comp_heldout": {
773+ "estimate": 0.571,
774+ "low": 0.5213289502727588,
775+ "high": 0.6226651942077474,
776+ "level": 0.95,
777+ "n_items": 1000,
778+ "n_clusters": 681,
779+ "n_boot": 2000
780+ },
781+ "len_ood": {
782+ "estimate": -0.002,
783+ "low": -0.038,
784+ "high": 0.034,
785+ "level": 0.95,
786+ "n_items": 500,
787+ "n_clusters": 500,
788+ "n_boot": 2000
789+ }
790+ }
791+ }
792+ },
793+ "noop": {
794+ "donor_s0": {
795+ "deterministic_repeat": true,
796+ "capture_all_bitwise_identity": true,
797+ "disabled_modifying_hook_bitwise_identity": true,
798+ "enabled_zero_hook_changes_output": true,
799+ "n_hook_points": 26,
800+ "n_items": 256
801+ },
802+ "recipient_s0": {
803+ "deterministic_repeat": true,
804+ "capture_all_bitwise_identity": true,
805+ "disabled_modifying_hook_bitwise_identity": true,
806+ "enabled_zero_hook_changes_output": true,
807+ "n_hook_points": 26,
808+ "n_items": 256
809+ },
810+ "donor_s1": {
811+ "deterministic_repeat": true,
812+ "capture_all_bitwise_identity": true,
813+ "disabled_modifying_hook_bitwise_identity": true,
814+ "enabled_zero_hook_changes_output": true,
815+ "n_hook_points": 26,
816+ "n_items": 256
817+ },
818+ "recipient_s1": {
819+ "deterministic_repeat": true,
820+ "capture_all_bitwise_identity": true,
821+ "disabled_modifying_hook_bitwise_identity": true,
822+ "enabled_zero_hook_changes_output": true,
823+ "n_hook_points": 26,
824+ "n_items": 256
825+ },
826+ "donor_s2": {
827+ "deterministic_repeat": true,
828+ "capture_all_bitwise_identity": true,
829+ "disabled_modifying_hook_bitwise_identity": true,
830+ "enabled_zero_hook_changes_output": true,
831+ "n_hook_points": 26,
832+ "n_items": 256
833+ },
834+ "recipient_s2": {
835+ "deterministic_repeat": true,
836+ "capture_all_bitwise_identity": true,
837+ "disabled_modifying_hook_bitwise_identity": true,
838+ "enabled_zero_hook_changes_output": true,
839+ "n_hook_points": 26,
840+ "n_items": 256
841+ }
842+ },
843+ "localization": {
844+ "n_items": 490,
845+ "read_position": 6,
846+ "positions": [
847+ "<bos>",
848+ "v0",
849+ "op1",
850+ "\u03a9",
851+ "op3",
852+ "=",
853+ "r1"
854+ ],
855+ "families": {
856+ "resid_post": {
857+ "s0": {
858+ "flip_rate": [
859+ [
860+ 0.0,
861+ 0.0,
862+ 0.0,
863+ 0.0,
864+ 0.0,
865+ 0.0,
866+ 1.0
867+ ],
868+ [
869+ 0.0,
870+ 0.0,
871+ 0.0,
872+ 0.0,
873+ 0.0,
874+ 0.0,
875+ 1.0
876+ ],
877+ [
878+ 0.0,
879+ 0.0,
880+ 0.0,
881+ 0.0,
882+ 0.0,
883+ 0.0,
884+ 1.0
885+ ],
886+ [
887+ 0.0,
888+ 0.0,
889+ 0.0,
890+ 0.0,
891+ 0.0,
892+ 0.0,
893+ 1.0
894+ ]
895+ ],
896+ "logit_recovery": [
897+ [
898+ 0.0,
899+ 0.0,
900+ 0.0,
901+ -0.0029584874864667654,
902+ 0.0002881789696402848,
903+ 6.067532467568526e-06,
904+ 0.9983479380607605
905+ ],
906+ [
907+ 0.0,
908+ 0.0,
909+ 0.0,
910+ -0.000898381054867059,
911+ 0.00018582800112199038,
912+ -7.255208038259298e-05,
913+ 0.9993993639945984
914+ ],
915+ [
916+ 0.0,
917+ 0.0,
918+ 0.0,
919+ 7.875546725699678e-05,
920+ 6.0847491113236174e-05,
921+ -9.433332888875157e-05,
922+ 0.9998680949211121
923+ ],
924+ [
925+ 0.0,
926+ 0.0,
927+ 0.0,
928+ 0.0,
929+ 0.0,
930+ 0.0,
931+ 1.0
932+ ]
933+ ]
934+ },
935+ "s1": {
936+ "flip_rate": [
937+ [
938+ 0.0,
939+ 0.0,
940+ 0.0,
941+ 0.0,
942+ 0.0,
943+ 0.0,
944+ 1.0
945+ ],
946+ [
947+ 0.0,
948+ 0.0,
949+ 0.0,
950+ 0.0,
951+ 0.0,
952+ 0.0,
953+ 1.0
954+ ],
955+ [
956+ 0.0,
957+ 0.0,
958+ 0.0,
959+ 0.0,
960+ 0.0,
961+ 0.0,
962+ 1.0
963+ ],
964+ [
965+ 0.0,
966+ 0.0,
967+ 0.0,
968+ 0.0,
969+ 0.0,
970+ 0.0,
971+ 1.0
972+ ]
973+ ],
974+ "logit_recovery": [
975+ [
976+ 0.0,
977+ 0.0,
978+ 0.0,
979+ 0.0004672935465350747,
980+ 0.00017617014236748219,
981+ -1.2608468047403676e-08,
982+ 0.9994205236434937
983+ ],
984+ [
985+ 0.0,
986+ 0.0,
987+ 0.0,
988+ 0.00011902503320015967,
989+ 8.649702795082703e-05,
990+ 4.641553630335693e-07,
991+ 0.9994013905525208
992+ ],
993+ [
994+ 0.0,
995+ 0.0,
996+ 0.0,
997+ 3.435093822190538e-05,
998+ 2.089686404360691e-06,
999+ 1.4695291611133143e-05,
1000+ 1.0002325773239136
1001+ ],
1002+ [
1003+ 0.0,
1004+ 0.0,
1005+ 0.0,
1006+ 0.0,
1007+ 0.0,
1008+ 0.0,
1009+ 1.0
1010+ ]
1011+ ]
1012+ },
1013+ "s2": {
1014+ "flip_rate": [
1015+ [
1016+ 0.0,
1017+ 0.0,
1018+ 0.0,
1019+ 0.0,
1020+ 0.0,
1021+ 0.0,
1022+ 1.0
1023+ ],
1024+ [
1025+ 0.0,
1026+ 0.0,
1027+ 0.0,
1028+ 0.0,
1029+ 0.0,
1030+ 0.0,
1031+ 1.0
1032+ ],
1033+ [
1034+ 0.0,
1035+ 0.0,
1036+ 0.0,
1037+ 0.0,
1038+ 0.0,
1039+ 0.0,
1040+ 1.0
1041+ ],
1042+ [
1043+ 0.0,
1044+ 0.0,
1045+ 0.0,
1046+ 0.0,
1047+ 0.0,
1048+ 0.0,
1049+ 1.0
1050+ ]
1051+ ],
1052+ "logit_recovery": [
1053+ [
1054+ 0.0,
1055+ 0.0,
1056+ 0.0,
1057+ 0.00033169190282933414,
1058+ -4.887258546659723e-05,
1059+ 1.5617764574926696e-06,
1060+ 1.0001285076141357
1061+ ],
1062+ [
1063+ 0.0,
1064+ 0.0,
1065+ 0.0,
1066+ 0.0004198819224257022,
1067+ -2.6704077754402533e-05,
1068+ 1.01509458545479e-05,
1069+ 0.9998666644096375
1070+ ],
1071+ [
1072+ 0.0,
1073+ 0.0,
1074+ 0.0,
1075+ 8.019314554985613e-05,
1076+ 1.6664354518525215e-07,
1077+ -1.2479076758609153e-06,
1078+ 1.0002455711364746
1079+ ],
1080+ [
1081+ 0.0,
1082+ 0.0,
1083+ 0.0,
1084+ 0.0,
1085+ 0.0,
1086+ 0.0,
1087+ 1.0
1088+ ]
1089+ ]
1090+ },
1091+ "mean_flip_rate": [
1092+ [
1093+ 0.0,
1094+ 0.0,
1095+ 0.0,
1096+ 0.0,
1097+ 0.0,
1098+ 0.0,
1099+ 1.0
1100+ ],
1101+ [
1102+ 0.0,
1103+ 0.0,
1104+ 0.0,
1105+ 0.0,
1106+ 0.0,
1107+ 0.0,
1108+ 1.0
1109+ ],
1110+ [
1111+ 0.0,
1112+ 0.0,
1113+ 0.0,
1114+ 0.0,
1115+ 0.0,
1116+ 0.0,
1117+ 1.0
1118+ ],
1119+ [
1120+ 0.0,
1121+ 0.0,
1122+ 0.0,
1123+ 0.0,
1124+ 0.0,
1125+ 0.0,
1126+ 1.0
1127+ ]
1128+ ]
1129+ },
1130+ "attn_out": {
1131+ "s0": {
1132+ "flip_rate": [
1133+ [
1134+ 0.0,
1135+ 0.0,
1136+ 0.0,
1137+ 0.0,
1138+ 0.0,
1139+ 0.0,
1140+ 1.0
1141+ ],
1142+ [
1143+ 0.0,
1144+ 0.0,
1145+ 0.0,
1146+ 0.0,
1147+ 0.0,
1148+ 0.0,
1149+ 0.0
1150+ ],
1151+ [
1152+ 0.0,
1153+ 0.0,
1154+ 0.0,
1155+ 0.0,
1156+ 0.0,
1157+ 0.0,
1158+ 0.0
1159+ ],
1160+ [
1161+ 0.0,
1162+ 0.0,
1163+ 0.0,
1164+ 0.0,
1165+ 0.0,
1166+ 0.0,
1167+ 0.0
1168+ ]
1169+ ],
1170+ "logit_recovery": [
1171+ [
1172+ 0.0,
1173+ 0.0,
1174+ 0.0,
1175+ -0.0025477122981101274,
1176+ 0.0002881789696402848,
1177+ 6.067532467568526e-06,
1178+ 0.9983479380607605
1179+ ],
1180+ [
1181+ 0.0,
1182+ 0.0,
1183+ 0.0,
1184+ -0.00013427382509689778,
1185+ -3.3803276892285794e-05,
1186+ -8.034344500629231e-05,
1187+ -0.0031254987698048353
1188+ ],
1189+ [
1190+ 0.0,
1191+ 0.0,
1192+ 0.0,
1193+ -2.6091085601365194e-05,
1194+ 1.4899030247761402e-05,
1195+ -4.782400719705038e-05,
1196+ -0.005556679796427488
1197+ ],
1198+ [
1199+ 0.0,
1200+ 0.0,
1201+ 0.0,
1202+ 0.0,
1203+ 0.0,
1204+ 0.0,
1205+ -0.00683206832036376
1206+ ]
1207+ ]
1208+ },
1209+ "s1": {
1210+ "flip_rate": [
1211+ [
1212+ 0.0,
1213+ 0.0,
1214+ 0.0,
1215+ 0.0,
1216+ 0.0,
1217+ 0.0,
1218+ 1.0
1219+ ],
1220+ [
1221+ 0.0,
1222+ 0.0,
1223+ 0.0,
1224+ 0.0,
1225+ 0.0,
1226+ 0.0,
1227+ 0.0
1228+ ],
1229+ [
1230+ 0.0,
1231+ 0.0,
1232+ 0.0,
1233+ 0.0,
1234+ 0.0,
1235+ 0.0,
1236+ 0.0
1237+ ],
1238+ [
1239+ 0.0,
1240+ 0.0,
1241+ 0.0,
1242+ 0.0,
1243+ 0.0,
1244+ 0.0,
1245+ 0.0
1246+ ]
1247+ ],
1248+ "logit_recovery": [
1249+ [
1250+ 0.0,
1251+ 0.0,
1252+ 0.0,
1253+ 0.000488389516249299,
1254+ 0.00017617014236748219,
1255+ -1.2608468047403676e-08,
1256+ 0.9994205236434937
1257+ ],
1258+ [
1259+ 0.0,
1260+ 0.0,
1261+ 0.0,
1262+ 7.775469566695392e-05,
1263+ 3.9568527427036315e-05,
1264+ 5.730797170144797e-07,
1265+ 0.0032321682665497065
1266+ ],
1267+ [
1268+ 0.0,
1269+ 0.0,
1270+ 0.0,
1271+ 0.0002074985095532611,
1272+ 2.0777781173819676e-05,
1273+ 1.012419852486346e-05,
1274+ 0.00023662605963181704
1275+ ],
1276+ [
1277+ 0.0,
1278+ 0.0,
1279+ 0.0,
1280+ 0.0,
1281+ 0.0,
1282+ 0.0,
1283+ -0.005255246534943581
1284+ ]
1285+ ]
1286+ },
1287+ "s2": {
1288+ "flip_rate": [
1289+ [
1290+ 0.0,
1291+ 0.0,
1292+ 0.0,
1293+ 0.0,
1294+ 0.0,
1295+ 0.0,
1296+ 1.0
1297+ ],
1298+ [
1299+ 0.0,
1300+ 0.0,
1301+ 0.0,
1302+ 0.0,
1303+ 0.0,
1304+ 0.0,
1305+ 0.0
1306+ ],
1307+ [
1308+ 0.0,
1309+ 0.0,
1310+ 0.0,
1311+ 0.0,
1312+ 0.0,
1313+ 0.0,
1314+ 0.0
1315+ ],
1316+ [
1317+ 0.0,
1318+ 0.0,
1319+ 0.0,
1320+ 0.0,
1321+ 0.0,
1322+ 0.0,
1323+ 0.0
1324+ ]
1325+ ],
1326+ "logit_recovery": [
1327+ [
1328+ 0.0,
1329+ 0.0,
1330+ 0.0,
1331+ 0.00030576580320484936,
1332+ -4.887258546659723e-05,
1333+ 1.5617764574926696e-06,
1334+ 1.0001285076141357
1335+ ],
1336+ [
1337+ 0.0,
1338+ 0.0,
1339+ 0.0,
1340+ 3.7193756725173444e-05,
1341+ 2.403900543868076e-05,
1342+ 8.416523996856995e-06,
1343+ -2.933492032752838e-05
1344+ ],
1345+ [
1346+ 0.0,
1347+ 0.0,
1348+ 0.0,
1349+ -7.816145080141723e-05,
1350+ 1.2711129784293007e-05,
1351+ -1.1673649169097189e-05,
1352+ -0.00023561605485156178
1353+ ],
1354+ [
1355+ 0.0,
1356+ 0.0,
1357+ 0.0,
1358+ 0.0,
1359+ 0.0,
1360+ 0.0,
1361+ -0.004809986799955368
1362+ ]
1363+ ]
1364+ },
1365+ "mean_flip_rate": [
1366+ [
1367+ 0.0,
1368+ 0.0,
1369+ 0.0,
1370+ 0.0,
1371+ 0.0,
1372+ 0.0,
1373+ 1.0
1374+ ],
1375+ [
1376+ 0.0,
1377+ 0.0,
1378+ 0.0,
1379+ 0.0,
1380+ 0.0,
1381+ 0.0,
1382+ 0.0
1383+ ],
1384+ [
1385+ 0.0,
1386+ 0.0,
1387+ 0.0,
1388+ 0.0,
1389+ 0.0,
1390+ 0.0,
1391+ 0.0
1392+ ],
1393+ [
1394+ 0.0,
1395+ 0.0,
1396+ 0.0,
1397+ 0.0,
1398+ 0.0,
1399+ 0.0,
1400+ 0.0
1401+ ]
1402+ ]
1403+ },
1404+ "mlp_out": {
1405+ "s0": {
1406+ "flip_rate": [
1407+ [
1408+ 0.0,
1409+ 0.0,
1410+ 0.0,
1411+ 0.0,
1412+ 0.0,
1413+ 0.0,
1414+ 1.0
1415+ ],
1416+ [
1417+ 0.0,
1418+ 0.0,
1419+ 0.0,
1420+ 0.0,
1421+ 0.0,
1422+ 0.0,
1423+ 1.0
1424+ ],
1425+ [
1426+ 0.0,
1427+ 0.0,
1428+ 0.0,
1429+ 0.0,
1430+ 0.0,
1431+ 0.0,
1432+ 0.8142856955528259
1433+ ],
1434+ [
1435+ 0.0,
1436+ 0.0,
1437+ 0.0,
1438+ 0.0,
1439+ 0.0,
1440+ 0.0,
1441+ 0.40612244606018066
1442+ ]
1443+ ],
1444+ "logit_recovery": [
1445+ [
1446+ 0.0,
1447+ 0.0,
1448+ 0.0,
1449+ -0.00376747059635818,
1450+ 0.0001691565994406119,
1451+ 3.0771909678151133e-06,
1452+ 0.9886849522590637
1453+ ],
1454+ [
1455+ 0.0,
1456+ 0.0,
1457+ 0.0,
1458+ -0.004375732969492674,
1459+ -6.94971313350834e-05,
1460+ -2.2410305973608047e-05,
1461+ 0.716352105140686
1462+ ],
1463+ [
1464+ 0.0,
1465+ 0.0,
1466+ 0.0,
1467+ -5.74394543946255e-05,
1468+ 2.9751297915936448e-05,
1469+ -2.8927430321346037e-05,
1470+ 0.5956922769546509
1471+ ],
1472+ [
1473+ 0.0,
1474+ 0.0,
1475+ 0.0,
1476+ 0.0,
1477+ 0.0,
1478+ 0.0,
1479+ 0.5033453106880188
1480+ ]
1481+ ]
1482+ },
1483+ "s1": {
1484+ "flip_rate": [
1485+ [
1486+ 0.0,
1487+ 0.0,
1488+ 0.0,
1489+ 0.0,
1490+ 0.0,
1491+ 0.0,
1492+ 1.0
1493+ ],
1494+ [
1495+ 0.0,
1496+ 0.0,
1497+ 0.0,
1498+ 0.0,
1499+ 0.0,
1500+ 0.0,
1501+ 0.9755101799964905
1502+ ],
1503+ [
1504+ 0.0,
1505+ 0.0,
1506+ 0.0,
1507+ 0.0,
1508+ 0.0,
1509+ 0.0,
1510+ 0.9448979496955872
1511+ ],
1512+ [
1513+ 0.0,
1514+ 0.0,
1515+ 0.0,
1516+ 0.0,
1517+ 0.0,
1518+ 0.0,
1519+ 0.0
1520+ ]
1521+ ],
1522+ "logit_recovery": [
1523+ [
1524+ 0.0,
1525+ 0.0,
1526+ 0.0,
1527+ 0.0005149293574504554,
1528+ 0.000171999228768982,
1529+ 1.244249716592094e-07,
1530+ 0.9941860437393188
1531+ ],
1532+ [
1533+ 0.0,
1534+ 0.0,
1535+ 0.0,
1536+ -0.00022421055473387241,
1537+ 6.864670285722241e-05,
1538+ 1.3663714071299182e-06,
1539+ 0.8590080142021179
1540+ ],
1541+ [
1542+ 0.0,
1543+ 0.0,
1544+ 0.0,
1545+ -0.00016121681255754083,
1546+ -2.486043558747042e-05,
1547+ 3.6747774174727965e-06,
1548+ 0.620472252368927
1549+ ],
1550+ [
1551+ 0.0,
1552+ 0.0,
1553+ 0.0,
1554+ 0.0,
1555+ 0.0,
1556+ 0.0,
1557+ 0.24915674328804016
1558+ ]
1559+ ]
1560+ },
1561+ "s2": {
1562+ "flip_rate": [
1563+ [
1564+ 0.0,
1565+ 0.0,
1566+ 0.0,
1567+ 0.0,
1568+ 0.0,
1569+ 0.0,
1570+ 1.0
1571+ ],
1572+ [
1573+ 0.0,
1574+ 0.0,
1575+ 0.0,
1576+ 0.0,
1577+ 0.0,
1578+ 0.0,
1579+ 0.7326530814170837
1580+ ],
1581+ [
1582+ 0.0,
1583+ 0.0,
1584+ 0.0,
1585+ 0.0,
1586+ 0.0,
1587+ 0.0,
1588+ 0.722449004650116
1589+ ],
1590+ [
1591+ 0.0,
1592+ 0.0,
1593+ 0.0,
1594+ 0.0,
1595+ 0.0,
1596+ 0.0,
1597+ 0.11428571492433548
1598+ ]
1599+ ],
1600+ "logit_recovery": [
1601+ [
1602+ 0.0,
1603+ 0.0,
1604+ 0.0,
1605+ 0.0005150199285708368,
1606+ -3.605287474783836e-06,
1607+ 1.780065531420405e-06,
1608+ 0.9959874749183655
1609+ ],
1610+ [
1611+ 0.0,
1612+ 0.0,
1613+ 0.0,
1614+ -0.00022224811255000532,
1615+ -4.047781476401724e-05,
1616+ 2.9307227578101447e-06,
1617+ 0.656613826751709
1618+ ],
1619+ [
1620+ 0.0,
1621+ 0.0,
1622+ 0.0,
1623+ -4.242335489834659e-05,
1624+ -7.299037406482967e-06,
1625+ 5.311194854584755e-06,
1626+ 0.6160171031951904
1627+ ],
1628+ [
1629+ 0.0,
1630+ 0.0,
1631+ 0.0,
1632+ 0.0,
1633+ 0.0,
1634+ 0.0,
1635+ 0.3720884919166565
1636+ ]
1637+ ]
1638+ },
1639+ "mean_flip_rate": [
1640+ [
1641+ 0.0,
1642+ 0.0,
1643+ 0.0,
1644+ 0.0,
1645+ 0.0,
1646+ 0.0,
1647+ 1.0
1648+ ],
1649+ [
1650+ 0.0,
1651+ 0.0,
1652+ 0.0,
1653+ 0.0,
1654+ 0.0,
1655+ 0.0,
1656+ 0.902721087137858
1657+ ],
1658+ [
1659+ 0.0,
1660+ 0.0,
1661+ 0.0,
1662+ 0.0,
1663+ 0.0,
1664+ 0.0,
1665+ 0.8272108832995096
1666+ ],
1667+ [
1668+ 0.0,
1669+ 0.0,
1670+ 0.0,
1671+ 0.0,
1672+ 0.0,
1673+ 0.0,
1674+ 0.17346938699483871
1675+ ]
1676+ ]
1677+ }
1678+ },
1679+ "ablation": {
1680+ "resid_post": {
1681+ "s0": [
1682+ [
1683+ 1.0,
1684+ 1.0,
1685+ 1.0,
1686+ 1.0,
1687+ 1.0,
1688+ 1.0,
1689+ 0.0
1690+ ],
1691+ [
1692+ 1.0,
1693+ 1.0,
1694+ 1.0,
1695+ 1.0,
1696+ 1.0,
1697+ 1.0,
1698+ 0.0
1699+ ],
1700+ [
1701+ 1.0,
1702+ 1.0,
1703+ 1.0,
1704+ 1.0,
1705+ 1.0,
1706+ 1.0,
1707+ 0.08775509893894196
1708+ ],
1709+ [
1710+ 1.0,
1711+ 1.0,
1712+ 1.0,
1713+ 1.0,
1714+ 1.0,
1715+ 1.0,
1716+ 0.08571428805589676
1717+ ]
1718+ ],
1719+ "s1": [
1720+ [
1721+ 1.0,
1722+ 1.0,
1723+ 1.0,
1724+ 1.0,
1725+ 1.0,
1726+ 1.0,
1727+ 0.08979591727256775
1728+ ],
1729+ [
1730+ 1.0,
1731+ 1.0,
1732+ 1.0,
1733+ 1.0,
1734+ 1.0,
1735+ 1.0,
1736+ 0.0367346927523613
1737+ ],
1738+ [
1739+ 1.0,
1740+ 1.0,
1741+ 1.0,
1742+ 1.0,
1743+ 1.0,
1744+ 1.0,
1745+ 0.07959183305501938
1746+ ],
1747+ [
1748+ 1.0,
1749+ 1.0,
1750+ 1.0,
1751+ 1.0,
1752+ 1.0,
1753+ 1.0,
1754+ 0.08571428805589676
1755+ ]
1756+ ],
1757+ "s2": [
1758+ [
1759+ 1.0,
1760+ 1.0,
1761+ 1.0,
1762+ 1.0,
1763+ 1.0,
1764+ 1.0,
1765+ 0.01836734637618065
1766+ ],
1767+ [
1768+ 1.0,
1769+ 1.0,
1770+ 1.0,
1771+ 1.0,
1772+ 1.0,
1773+ 1.0,
1774+ 0.08775509893894196
1775+ ],
1776+ [
1777+ 1.0,
1778+ 1.0,
1779+ 1.0,
1780+ 1.0,
1781+ 1.0,
1782+ 1.0,
1783+ 0.09183673560619354
1784+ ],
1785+ [
1786+ 1.0,
1787+ 1.0,
1788+ 1.0,
1789+ 1.0,
1790+ 1.0,
1791+ 1.0,
1792+ 0.08571428805589676
1793+ ]
1794+ ]
1795+ },
1796+ "attn_out": {
1797+ "s0": [
1798+ [
1799+ 1.0,
1800+ 1.0,
1801+ 1.0,
1802+ 1.0,
1803+ 1.0,
1804+ 1.0,
1805+ 0.11632653325796127
1806+ ],
1807+ [
1808+ 1.0,
1809+ 1.0,
1810+ 1.0,
1811+ 1.0,
1812+ 1.0,
1813+ 1.0,
1814+ 1.0
1815+ ],
1816+ [
1817+ 1.0,
1818+ 1.0,
1819+ 1.0,
1820+ 1.0,
1821+ 1.0,
1822+ 1.0,
1823+ 1.0
1824+ ],
1825+ [
1826+ 1.0,
1827+ 1.0,
1828+ 1.0,
1829+ 1.0,
1830+ 1.0,
1831+ 1.0,
1832+ 1.0
1833+ ]
1834+ ],
1835+ "s1": [
1836+ [
1837+ 1.0,
1838+ 1.0,
1839+ 1.0,
1840+ 1.0,
1841+ 1.0,
1842+ 1.0,
1843+ 0.17142857611179352
1844+ ],
1845+ [
1846+ 1.0,
1847+ 1.0,
1848+ 1.0,
1849+ 1.0,
1850+ 1.0,
1851+ 1.0,
1852+ 1.0
1853+ ],
1854+ [
1855+ 1.0,
1856+ 1.0,
1857+ 1.0,
1858+ 1.0,
1859+ 1.0,
1860+ 1.0,
1861+ 1.0
1862+ ],
1863+ [
1864+ 1.0,
1865+ 1.0,
1866+ 1.0,
1867+ 1.0,
1868+ 1.0,
1869+ 1.0,
1870+ 1.0
1871+ ]
1872+ ],
1873+ "s2": [
1874+ [
1875+ 1.0,
1876+ 1.0,
1877+ 1.0,
1878+ 1.0,
1879+ 1.0,
1880+ 1.0,
1881+ 0.08979591727256775
1882+ ],
1883+ [
1884+ 1.0,
1885+ 1.0,
1886+ 1.0,
1887+ 1.0,
1888+ 1.0,
1889+ 1.0,
1890+ 1.0
1891+ ],
1892+ [
1893+ 1.0,
1894+ 1.0,
1895+ 1.0,
1896+ 1.0,
1897+ 1.0,
1898+ 1.0,
1899+ 1.0
1900+ ],
1901+ [
1902+ 1.0,
1903+ 1.0,
1904+ 1.0,
1905+ 1.0,
1906+ 1.0,
1907+ 1.0,
1908+ 1.0
1909+ ]
1910+ ]
1911+ },
1912+ "mlp_out": {
1913+ "s0": [
1914+ [
1915+ 1.0,
1916+ 1.0,
1917+ 1.0,
1918+ 1.0,
1919+ 1.0,
1920+ 1.0,
1921+ 0.12653061747550964
1922+ ],
1923+ [
1924+ 1.0,
1925+ 1.0,
1926+ 1.0,
1927+ 1.0,
1928+ 1.0,
1929+ 1.0,
1930+ 0.9142857193946838
1931+ ],
1932+ [
1933+ 1.0,
1934+ 1.0,
1935+ 1.0,
1936+ 1.0,
1937+ 1.0,
1938+ 1.0,
1939+ 1.0
1940+ ],
1941+ [
1942+ 1.0,
1943+ 1.0,
1944+ 1.0,
1945+ 1.0,
1946+ 1.0,
1947+ 1.0,
1948+ 1.0
1949+ ]
1950+ ],
1951+ "s1": [
1952+ [
1953+ 1.0,
1954+ 1.0,
1955+ 1.0,
1956+ 1.0,
1957+ 1.0,
1958+ 1.0,
1959+ 0.3918367326259613
1960+ ],
1961+ [
1962+ 1.0,
1963+ 1.0,
1964+ 1.0,
1965+ 1.0,
1966+ 1.0,
1967+ 1.0,
1968+ 1.0
1969+ ],
1970+ [
1971+ 1.0,
1972+ 1.0,
1973+ 1.0,
1974+ 1.0,
1975+ 1.0,
1976+ 1.0,
1977+ 1.0
1978+ ],
1979+ [
1980+ 1.0,
1981+ 1.0,
1982+ 1.0,
1983+ 1.0,
1984+ 1.0,
1985+ 1.0,
1986+ 1.0
1987+ ]
1988+ ],
1989+ "s2": [
1990+ [
1991+ 1.0,
1992+ 1.0,
1993+ 1.0,
1994+ 1.0,
1995+ 1.0,
1996+ 1.0,
1997+ 0.11020407825708389
1998+ ],
1999+ [
2000+ 1.0,
2001+ 1.0,
2002+ 1.0,
2003+ 1.0,
2004+ 1.0,
2005+ 1.0,
2006+ 1.0
2007+ ],
2008+ [
2009+ 1.0,
2010+ 1.0,
2011+ 1.0,
2012+ 1.0,
2013+ 1.0,
2014+ 1.0,
2015+ 1.0
2016+ ],
2017+ [
2018+ 1.0,
2019+ 1.0,
2020+ 1.0,
2021+ 1.0,
2022+ 1.0,
2023+ 1.0,
2024+ 1.0
2025+ ]
2026+ ]
2027+ }
2028+ },
2029+ "heads": {
2030+ "omega_token": {
2031+ "s0": [
2032+ [
2033+ 0.0,
2034+ 0.0,
2035+ 0.0,
2036+ 0.0
2037+ ],
2038+ [
2039+ 0.0,
2040+ 0.0,
2041+ 0.0,
2042+ 0.0
2043+ ],
2044+ [
2045+ 0.0,
2046+ 0.0,
2047+ 0.0,
2048+ 0.0
2049+ ],
2050+ [
2051+ 0.0,
2052+ 0.0,
2053+ 0.0,
2054+ 0.0
2055+ ]
2056+ ],
2057+ "s1": [
2058+ [
2059+ 0.0,
2060+ 0.0,
2061+ 0.0,
2062+ 0.0
2063+ ],
2064+ [
2065+ 0.0,
2066+ 0.0,
2067+ 0.0,
2068+ 0.0
2069+ ],
2070+ [
2071+ 0.0,
2072+ 0.0,
2073+ 0.0,
2074+ 0.0
2075+ ],
2076+ [
2077+ 0.0,
2078+ 0.0,
2079+ 0.0,
2080+ 0.0
2081+ ]
2082+ ],
2083+ "s2": [
2084+ [
2085+ 0.0,
2086+ 0.0,
2087+ 0.0,
2088+ 0.0
2089+ ],
2090+ [
2091+ 0.0,
2092+ 0.0,
2093+ 0.0,
2094+ 0.0
2095+ ],
2096+ [
2097+ 0.0,
2098+ 0.0,
2099+ 0.0,
2100+ 0.0
2101+ ],
2102+ [
2103+ 0.0,
2104+ 0.0,
2105+ 0.0,
2106+ 0.0
2107+ ]
2108+ ]
2109+ },
2110+ "read_position": {
2111+ "s0": [
2112+ [
2113+ 0.9346938729286194,
2114+ 0.4734693765640259,
2115+ 0.0,
2116+ 0.0
2117+ ],
2118+ [
2119+ 0.0,
2120+ 0.0,
2121+ 0.0,
2122+ 0.0
2123+ ],
2124+ [
2125+ 0.0,
2126+ 0.0,
2127+ 0.0,
2128+ 0.0
2129+ ],
2130+ [
2131+ 0.0,
2132+ 0.0,
2133+ 0.0,
2134+ 0.0
2135+ ]
2136+ ],
2137+ "s1": [
2138+ [
2139+ 0.9163265228271484,
2140+ 0.0,
2141+ 0.0,
2142+ 0.0
2143+ ],
2144+ [
2145+ 0.0,
2146+ 0.0,
2147+ 0.0,
2148+ 0.0
2149+ ],
2150+ [
2151+ 0.0,
2152+ 0.0,
2153+ 0.0,
2154+ 0.0
2155+ ],
2156+ [
2157+ 0.0,
2158+ 0.0,
2159+ 0.0,
2160+ 0.0
2161+ ]
2162+ ],
2163+ "s2": [
2164+ [
2165+ 0.0,
2166+ 0.0,
2167+ 0.11428571492433548,
2168+ 0.0
2169+ ],
2170+ [
2171+ 0.0,
2172+ 0.0,
2173+ 0.0,
2174+ 0.0
2175+ ],
2176+ [
2177+ 0.0,
2178+ 0.0,
2179+ 0.0,
2180+ 0.0
2181+ ],
2182+ [
2183+ 0.0,
2184+ 0.0,
2185+ 0.0,
2186+ 0.0
2187+ ]
2188+ ]
2189+ }
2190+ },
2191+ "reproducibility": {
2192+ "resid_post_s0_repeat_max_abs_diff": 0.0,
2193+ "attn_out_s0_repeat_max_abs_diff": 0.0,
2194+ "mlp_out_s0_repeat_max_abs_diff": 0.0,
2195+ "resid_post_s1_repeat_max_abs_diff": 0.0,
2196+ "attn_out_s1_repeat_max_abs_diff": 0.0,
2197+ "mlp_out_s1_repeat_max_abs_diff": 0.0,
2198+ "resid_post_s2_repeat_max_abs_diff": 0.0,
2199+ "attn_out_s2_repeat_max_abs_diff": 0.0,
2200+ "mlp_out_s2_repeat_max_abs_diff": 0.0,
2201+ "resid_post_across_seed_pearson_min": 0.9999999999999999,
2202+ "attn_out_across_seed_pearson_min": 0.9999999999999999,
2203+ "mlp_out_across_seed_pearson_min": 0.9642362448310298
2204+ },
2205+ "baselines": {
2206+ "s0": {
2207+ "clean_acc_on_clean_answer": 1.0,
2208+ "corrupt_run_acc_on_clean_answer(sham)": 0.0
2209+ },
2210+ "s1": {
2211+ "clean_acc_on_clean_answer": 1.0,
2212+ "corrupt_run_acc_on_clean_answer(sham)": 0.0
2213+ },
2214+ "s2": {
2215+ "clean_acc_on_clean_answer": 1.0,
2216+ "corrupt_run_acc_on_clean_answer(sham)": 0.0
2217+ }
2218+ }
2219+ },
2220+ "across_seeds": {
2221+ "target_iid": {
2222+ "donor": {
2223+ "n_seeds": 3,
2224+ "mean": 1.0,
2225+ "sd": 0.0,
2226+ "min": 1.0,
2227+ "max": 1.0
2228+ },
2229+ "recipient": {
2230+ "n_seeds": 3,
2231+ "mean": 0.09200000000000001,
2232+ "sd": 0.05288667128870941,
2233+ "min": 0.059,
2234+ "max": 0.153
2235+ }
2236+ },
2237+ "control_iid": {
2238+ "donor": {
2239+ "n_seeds": 3,
2240+ "mean": 1.0,
2241+ "sd": 0.0,
2242+ "min": 1.0,
2243+ "max": 1.0
2244+ },
2245+ "recipient": {
2246+ "n_seeds": 3,
2247+ "mean": 1.0,
2248+ "sd": 0.0,
2249+ "min": 1.0,
2250+ "max": 1.0
2251+ }
2252+ },
2253+ "comp_heldout": {
2254+ "donor": {
2255+ "n_seeds": 3,
2256+ "mean": 1.0,
2257+ "sd": 0.0,
2258+ "min": 1.0,
2259+ "max": 1.0
2260+ },
2261+ "recipient": {
2262+ "n_seeds": 3,
2263+ "mean": 0.4503333333333333,
2264+ "sd": 0.031895663237081824,
2265+ "min": 0.429,
2266+ "max": 0.487
2267+ }
2268+ },
2269+ "len_ood": {
2270+ "donor": {
2271+ "n_seeds": 3,
2272+ "mean": 0.08600000000000001,
2273+ "sd": 0.017320508075688773,
2274+ "min": 0.076,
2275+ "max": 0.106
2276+ },
2277+ "recipient": {
2278+ "n_seeds": 3,
2279+ "mean": 0.09066666666666666,
2280+ "sd": 0.015534906930308057,
2281+ "min": 0.078,
2282+ "max": 0.108
2283+ }
2284+ }
2285+ },
2286+ "across_seeds_omega_step": {
2287+ "donor": {
2288+ "n_seeds": 3,
2289+ "mean": 1.0,
2290+ "sd": 0.0,
2291+ "min": 1.0,
2292+ "max": 1.0
2293+ },
2294+ "recipient": {
2295+ "n_seeds": 3,
2296+ "mean": 0.06166666666666667,
2297+ "sd": 0.0697160909212022,
2298+ "min": 0.017,
2299+ "max": 0.142
2300+ }
2301+ },
2302+ "elapsed_seconds": 275.5419456958771
2303+}
\ No newline at end of file
added experiments/summaries/pilot_tier0_opseq_quick_d5339bcad6b3/SUMMARY.md +42 −0
@@ -0,0 +1,42 @@
1+# Pilot summary — pilot_tier0_opseq_quick_d5339bcad6b3
2+
3+Generated 2026-11-09T06:49:31+00:00 on device `cpu`; config hash `d5339bcad6b3`; seeds [0, 1].
4+
5+## Gate B — donor/recipient gap (final-answer exact match, mean over seeds; CI = cluster bootstrap on programs)
6+
7+| split | chance | donor mean (sd) | recipient mean (sd) | paired gap seed 0 [95% CI] |
8+|---|---|---|---|---|
9+| target_iid | 0.113 | 0.120 (0.009) | 0.098 (0.012) | +0.007 [-0.034, +0.049] |
10+| control_iid | 0.130 | 0.155 (0.007) | 0.162 (0.021) | -0.027 [-0.064, +0.010] |
11+| comp_heldout | 0.117 | 0.128 (0.026) | 0.095 (0.002) | +0.053 [+0.013, +0.103] |
12+| len_ood | 0.150 | 0.082 (0.032) | 0.055 (0.007) | +0.055 [+0.005, +0.105] |
13+
14+Ω-step accuracy on target_iid (token emitted at the first Ω step): donor 0.268, recipient 0.070.
15+
16+## Gate C — hook no-op checks (bitwise equality of logits on 256 dev items)
17+
18+| model | repeat | capture-all | disabled hook | enabled zero hook changes output |
19+|---|---|---|---|---|
20+| donor_s0 | True | True | True | True |
21+| recipient_s0 | True | True | True | True |
22+| donor_s1 | True | True | True | True |
23+| recipient_s1 | True | True | True | True |
24+
25+## RQ1 — within-donor localization of Ω (n = 490 clean/corrupt pairs, read position = r1, predicting r2)
26+
27+Baselines: s0: clean 0.316, sham (corrupt run scored on clean answer) 0.108; s1: clean 0.551, sham (corrupt run scored on clean answer) 0.155
28+
29+Mean restored-answer rate after patching the clean activation into the corrupted run (rows = layers, columns = positions <bos>, v0, op1, Ω, op3, =, r1):
30+
31+- `resid_post`: L0: 0.13 0.13 0.13 0.16 0.13 0.13 0.39 | L1: 0.13 0.13 0.13 0.13 0.13 0.13 0.43 | L2: 0.13 0.13 0.13 0.13 0.13 0.13 0.43
32+- `attn_out`: L0: 0.13 0.13 0.13 0.14 0.13 0.13 0.39 | L1: 0.13 0.13 0.13 0.13 0.13 0.13 0.17 | L2: 0.13 0.13 0.13 0.13 0.13 0.13 0.12
33+- `mlp_out`: L0: 0.13 0.13 0.13 0.15 0.13 0.13 0.29 | L1: 0.13 0.13 0.13 0.13 0.13 0.13 0.32 | L2: 0.13 0.13 0.13 0.13 0.13 0.13 0.22
34+
35+Reproducibility: resid_post_s0_repeat_max_abs_diff = 0.0000; attn_out_s0_repeat_max_abs_diff = 0.0000; mlp_out_s0_repeat_max_abs_diff = 0.0000; resid_post_s1_repeat_max_abs_diff = 0.0000; attn_out_s1_repeat_max_abs_diff = 0.0000; mlp_out_s1_repeat_max_abs_diff = 0.0000; resid_post_across_seed_pearson_min = 0.9930; attn_out_across_seed_pearson_min = 0.9706; mlp_out_across_seed_pearson_min = 0.9732
36+
37+Figures: `fig_gap_tier0.pdf`, `fig_localization_ablation_tier0.pdf`, `fig_localization_heads_tier0.pdf`, `fig_localization_patching_tier0.pdf`
38+
39+## Interpretation (observed vs. speculative)
40+
41+Observed: the numbers above are produced by the artifacts listed in the manifest. Any statement about *transfer* is out of scope for this run: no transplant was performed.
42+Speculative: whether the localized positions/layers constitute a transferable 'organ' is the object of the next experiment (activation graft with sham/random controls).
added experiments/summaries/pilot_tier0_opseq_quick_d5339bcad6b3/results.json +1383 −0
@@ -0,0 +1,1383 @@
1+{
2+ "run_id": "pilot_tier0_opseq_quick_d5339bcad6b3",
3+ "config_hash": "d5339bcad6b3",
4+ "device": "cpu",
5+ "chance_level": {
6+ "target_iid": 0.11333333333333333,
7+ "control_iid": 0.13,
8+ "comp_heldout": 0.11666666666666667,
9+ "len_ood": 0.15
10+ },
11+ "seeds": {
12+ "0": {
13+ "donor": {
14+ "n_params": 104128,
15+ "train_seconds": 3.7347042560577393,
16+ "final_dev": {
17+ "step": 200,
18+ "loss": 1.581451177597046,
19+ "dev_acc": 0.13333333333333333,
20+ "dev_trace_acc": 0.056666666666666664,
21+ "lr": 0.0,
22+ "elapsed_s": 3.7261741161346436
23+ },
24+ "eval": {
25+ "target_iid": {
26+ "split": "target_iid",
27+ "split_hash": "88d9bb27db961222",
28+ "n": 300,
29+ "accuracy": 0.11333333333333333,
30+ "trace_accuracy": 0.03666666666666667,
31+ "ci95": {
32+ "estimate": 0.11333333333333333,
33+ "low": 0.07478037572644952,
34+ "high": 0.15052945301542778,
35+ "level": 0.95,
36+ "n_items": 300,
37+ "n_clusters": 269,
38+ "n_boot": 200
39+ },
40+ "omega_step_acc": {
41+ "estimate": 0.24,
42+ "low": 0.19278817587641117,
43+ "high": 0.29099073414112614,
44+ "level": 0.95,
45+ "n_items": 300,
46+ "n_clusters": 269,
47+ "n_boot": 200
48+ },
49+ "chance": 0.11333333333333333
50+ },
51+ "control_iid": {
52+ "split": "control_iid",
53+ "split_hash": "76bdfaf36a5f7519",
54+ "n": 300,
55+ "accuracy": 0.15,
56+ "trace_accuracy": 0.06,
57+ "ci95": {
58+ "estimate": 0.15,
59+ "low": 0.11329661016949152,
60+ "high": 0.1926864887462714,
61+ "level": 0.95,
62+ "n_items": 300,
63+ "n_clusters": 293,
64+ "n_boot": 200
65+ },
66+ "omega_step_acc": null,
67+ "chance": 0.13
68+ },
69+ "comp_heldout": {
70+ "split": "comp_heldout",
71+ "split_hash": "68e83ab8472513d2",
72+ "n": 300,
73+ "accuracy": 0.14666666666666667,
74+ "trace_accuracy": 0.05,
75+ "ci95": {
76+ "estimate": 0.14666666666666667,
77+ "low": 0.11493486906095715,
78+ "high": 0.1844807034516118,
79+ "level": 0.95,
80+ "n_items": 300,
81+ "n_clusters": 241,
82+ "n_boot": 200
83+ },
84+ "omega_step_acc": {
85+ "estimate": 0.2833333333333333,
86+ "low": 0.21729360039081583,
87+ "high": 0.34242126532887407,
88+ "level": 0.95,
89+ "n_items": 180,
90+ "n_clusters": 148,
91+ "n_boot": 200
92+ },
93+ "chance": 0.11666666666666667
94+ },
95+ "len_ood": {
96+ "split": "len_ood",
97+ "split_hash": "6d8c13dc8b5c47fa",
98+ "n": 200,
99+ "accuracy": 0.105,
100+ "trace_accuracy": 0.0,
101+ "ci95": {
102+ "estimate": 0.105,
103+ "low": 0.06487500000000002,
104+ "high": 0.145,
105+ "level": 0.95,
106+ "n_items": 200,
107+ "n_clusters": 200,
108+ "n_boot": 200
109+ },
110+ "omega_step_acc": {
111+ "estimate": 0.10434782608695652,
112+ "low": 0.043478260869565216,
113+ "high": 0.1565217391304348,
114+ "level": 0.95,
115+ "n_items": 115,
116+ "n_clusters": 115,
117+ "n_boot": 200
118+ },
119+ "chance": 0.15
120+ }
121+ }
122+ },
123+ "recipient": {
124+ "n_params": 104128,
125+ "train_seconds": 3.605180263519287,
126+ "final_dev": {
127+ "step": 200,
128+ "loss": 1.4595788717269897,
129+ "dev_acc": 0.18,
130+ "dev_trace_acc": 0.09333333333333334,
131+ "lr": 0.0,
132+ "elapsed_s": 3.598365068435669
133+ },
134+ "eval": {
135+ "target_iid": {
136+ "split": "target_iid",
137+ "split_hash": "88d9bb27db961222",
138+ "n": 300,
139+ "accuracy": 0.10666666666666667,
140+ "trace_accuracy": 0.006666666666666667,
141+ "ci95": {
142+ "estimate": 0.10666666666666667,
143+ "low": 0.0701905896366885,
144+ "high": 0.13959436641338555,
145+ "level": 0.95,
146+ "n_items": 300,
147+ "n_clusters": 269,
148+ "n_boot": 200
149+ },
150+ "omega_step_acc": {
151+ "estimate": 0.06333333333333334,
152+ "low": 0.04009817671809257,
153+ "high": 0.09277440017370536,
154+ "level": 0.95,
155+ "n_items": 300,
156+ "n_clusters": 269,
157+ "n_boot": 200
158+ },
159+ "chance": 0.11333333333333333
160+ },
161+ "control_iid": {
162+ "split": "control_iid",
163+ "split_hash": "76bdfaf36a5f7519",
164+ "n": 300,
165+ "accuracy": 0.17666666666666667,
166+ "trace_accuracy": 0.09666666666666666,
167+ "ci95": {
168+ "estimate": 0.17666666666666667,
169+ "low": 0.136201348705199,
170+ "high": 0.22591362126245848,
171+ "level": 0.95,
172+ "n_items": 300,
173+ "n_clusters": 293,
174+ "n_boot": 200
175+ },
176+ "omega_step_acc": null,
177+ "chance": 0.13
178+ },
179+ "comp_heldout": {
180+ "split": "comp_heldout",
181+ "split_hash": "68e83ab8472513d2",
182+ "n": 300,
183+ "accuracy": 0.09333333333333334,
184+ "trace_accuracy": 0.02,
185+ "ci95": {
186+ "estimate": 0.09333333333333334,
187+ "low": 0.06004459903808402,
188+ "high": 0.1262532812435913,
189+ "level": 0.95,
190+ "n_items": 300,
191+ "n_clusters": 241,
192+ "n_boot": 200
193+ },
194+ "omega_step_acc": {
195+ "estimate": 0.027777777777777776,
196+ "low": 0.005317060265121978,
197+ "high": 0.05296173308032891,
198+ "level": 0.95,
199+ "n_items": 180,
200+ "n_clusters": 148,
201+ "n_boot": 200
202+ },
203+ "chance": 0.11666666666666667
204+ },
205+ "len_ood": {
206+ "split": "len_ood",
207+ "split_hash": "6d8c13dc8b5c47fa",
208+ "n": 200,
209+ "accuracy": 0.05,
210+ "trace_accuracy": 0.0,
211+ "ci95": {
212+ "estimate": 0.05,
213+ "low": 0.02,
214+ "high": 0.075,
215+ "level": 0.95,
216+ "n_items": 200,
217+ "n_clusters": 200,
218+ "n_boot": 200
219+ },
220+ "omega_step_acc": {
221+ "estimate": 0.06086956521739131,
222+ "low": 0.017391304347826087,
223+ "high": 0.1045652173913044,
224+ "level": 0.95,
225+ "n_items": 115,
226+ "n_clusters": 115,
227+ "n_boot": 200
228+ },
229+ "chance": 0.15
230+ }
231+ }
232+ },
233+ "gap": {
234+ "target_iid": {
235+ "estimate": 0.006666666666666667,
236+ "low": -0.03395843821670302,
237+ "high": 0.049048298249065515,
238+ "level": 0.95,
239+ "n_items": 300,
240+ "n_clusters": 269,
241+ "n_boot": 200
242+ },
243+ "control_iid": {
244+ "estimate": -0.02666666666666667,
245+ "low": -0.06404597817641294,
246+ "high": 0.010067961494135991,
247+ "level": 0.95,
248+ "n_items": 300,
249+ "n_clusters": 293,
250+ "n_boot": 200
251+ },
252+ "comp_heldout": {
253+ "estimate": 0.05333333333333334,
254+ "low": 0.012669551600821054,
255+ "high": 0.10274132633465625,
256+ "level": 0.95,
257+ "n_items": 300,
258+ "n_clusters": 241,
259+ "n_boot": 200
260+ },
261+ "len_ood": {
262+ "estimate": 0.055,
263+ "low": 0.005,
264+ "high": 0.105,
265+ "level": 0.95,
266+ "n_items": 200,
267+ "n_clusters": 200,
268+ "n_boot": 200
269+ }
270+ }
271+ },
272+ "1": {
273+ "donor": {
274+ "n_params": 104128,
275+ "train_seconds": 4.102132081985474,
276+ "final_dev": {
277+ "step": 200,
278+ "loss": 1.6042448282241821,
279+ "dev_acc": 0.14333333333333334,
280+ "dev_trace_acc": 0.06333333333333334,
281+ "lr": 0.0,
282+ "elapsed_s": 4.0953710079193115
283+ },
284+ "eval": {
285+ "target_iid": {
286+ "split": "target_iid",
287+ "split_hash": "88d9bb27db961222",
288+ "n": 300,
289+ "accuracy": 0.12666666666666668,
290+ "trace_accuracy": 0.043333333333333335,
291+ "ci95": {
292+ "estimate": 0.12666666666666668,
293+ "low": 0.08705976579761379,
294+ "high": 0.16720369270757476,
295+ "level": 0.95,
296+ "n_items": 300,
297+ "n_clusters": 269,
298+ "n_boot": 200
299+ },
300+ "omega_step_acc": {
301+ "estimate": 0.2966666666666667,
302+ "low": 0.25,
303+ "high": 0.3500085034013605,
304+ "level": 0.95,
305+ "n_items": 300,
306+ "n_clusters": 269,
307+ "n_boot": 200
308+ },
309+ "chance": 0.11333333333333333
310+ },
311+ "control_iid": {
312+ "split": "control_iid",
313+ "split_hash": "76bdfaf36a5f7519",
314+ "n": 300,
315+ "accuracy": 0.16,
316+ "trace_accuracy": 0.07333333333333333,
317+ "ci95": {
318+ "estimate": 0.16,
319+ "low": 0.12496946254071661,
320+ "high": 0.19806930693069308,
321+ "level": 0.95,
322+ "n_items": 300,
323+ "n_clusters": 293,
324+ "n_boot": 200
325+ },
326+ "omega_step_acc": null,
327+ "chance": 0.13
328+ },
329+ "comp_heldout": {
330+ "split": "comp_heldout",
331+ "split_hash": "68e83ab8472513d2",
332+ "n": 300,
333+ "accuracy": 0.11,
334+ "trace_accuracy": 0.04,
335+ "ci95": {
336+ "estimate": 0.11,
337+ "low": 0.07416016814914099,
338+ "high": 0.14744774919614148,
339+ "level": 0.95,
340+ "n_items": 300,
341+ "n_clusters": 241,
342+ "n_boot": 200
343+ },
344+ "omega_step_acc": {
345+ "estimate": 0.2833333333333333,
346+ "low": 0.21655303030303033,
347+ "high": 0.34637783891524665,
348+ "level": 0.95,
349+ "n_items": 180,
350+ "n_clusters": 148,
351+ "n_boot": 200
352+ },
353+ "chance": 0.11666666666666667
354+ },
355+ "len_ood": {
356+ "split": "len_ood",
357+ "split_hash": "6d8c13dc8b5c47fa",
358+ "n": 200,
359+ "accuracy": 0.06,
360+ "trace_accuracy": 0.0,
361+ "ci95": {
362+ "estimate": 0.06,
363+ "low": 0.03,
364+ "high": 0.1,
365+ "level": 0.95,
366+ "n_items": 200,
367+ "n_clusters": 200,
368+ "n_boot": 200
369+ },
370+ "omega_step_acc": {
371+ "estimate": 0.12173913043478261,
372+ "low": 0.06086956521739131,
373+ "high": 0.18282608695652178,
374+ "level": 0.95,
375+ "n_items": 115,
376+ "n_clusters": 115,
377+ "n_boot": 200
378+ },
379+ "chance": 0.15
380+ }
381+ }
382+ },
383+ "recipient": {
384+ "n_params": 104128,
385+ "train_seconds": 4.027623891830444,
386+ "final_dev": {
387+ "step": 200,
388+ "loss": 1.5427294969558716,
389+ "dev_acc": 0.18333333333333332,
390+ "dev_trace_acc": 0.08,
391+ "lr": 0.0,
392+ "elapsed_s": 4.020693302154541
393+ },
394+ "eval": {
395+ "target_iid": {
396+ "split": "target_iid",
397+ "split_hash": "88d9bb27db961222",
398+ "n": 300,
399+ "accuracy": 0.09,
400+ "trace_accuracy": 0.01,
401+ "ci95": {
402+ "estimate": 0.09,
403+ "low": 0.06476109215017066,
404+ "high": 0.1189967845659164,
405+ "level": 0.95,
406+ "n_items": 300,
407+ "n_clusters": 269,
408+ "n_boot": 200
409+ },
410+ "omega_step_acc": {
411+ "estimate": 0.07666666666666666,
412+ "low": 0.05049660484443093,
413+ "high": 0.10544057835108876,
414+ "level": 0.95,
415+ "n_items": 300,
416+ "n_clusters": 269,
417+ "n_boot": 200
418+ },
419+ "chance": 0.11333333333333333
420+ },
421+ "control_iid": {
422+ "split": "control_iid",
423+ "split_hash": "76bdfaf36a5f7519",
424+ "n": 300,
425+ "accuracy": 0.14666666666666667,
426+ "trace_accuracy": 0.07,
427+ "ci95": {
428+ "estimate": 0.14666666666666667,
429+ "low": 0.10328378378378379,
430+ "high": 0.1875822368421053,
431+ "level": 0.95,
432+ "n_items": 300,
433+ "n_clusters": 293,
434+ "n_boot": 200
435+ },
436+ "omega_step_acc": null,
437+ "chance": 0.13
438+ },
439+ "comp_heldout": {
440+ "split": "comp_heldout",
441+ "split_hash": "68e83ab8472513d2",
442+ "n": 300,
443+ "accuracy": 0.09666666666666666,
444+ "trace_accuracy": 0.02,
445+ "ci95": {
446+ "estimate": 0.09666666666666666,
447+ "low": 0.062289429055963824,
448+ "high": 0.12638747170519454,
449+ "level": 0.95,
450+ "n_items": 300,
451+ "n_clusters": 241,
452+ "n_boot": 200
453+ },
454+ "omega_step_acc": {
455+ "estimate": 0.05,
456+ "low": 0.022308542830540044,
457+ "high": 0.08298133753051525,
458+ "level": 0.95,
459+ "n_items": 180,
460+ "n_clusters": 148,
461+ "n_boot": 200
462+ },
463+ "chance": 0.11666666666666667
464+ },
465+ "len_ood": {
466+ "split": "len_ood",
467+ "split_hash": "6d8c13dc8b5c47fa",
468+ "n": 200,
469+ "accuracy": 0.06,
470+ "trace_accuracy": 0.0,
471+ "ci95": {
472+ "estimate": 0.06,
473+ "low": 0.03,
474+ "high": 0.09,
475+ "level": 0.95,
476+ "n_items": 200,
477+ "n_clusters": 200,
478+ "n_boot": 200
479+ },
480+ "omega_step_acc": {
481+ "estimate": 0.09565217391304348,
482+ "low": 0.034782608695652174,
483+ "high": 0.14782608695652175,
484+ "level": 0.95,
485+ "n_items": 115,
486+ "n_clusters": 115,
487+ "n_boot": 200
488+ },
489+ "chance": 0.15
490+ }
491+ }
492+ },
493+ "gap": {
494+ "target_iid": {
495+ "estimate": 0.03666666666666667,
496+ "low": -8.064516129030941e-05,
497+ "high": 0.07962905401442731,
498+ "level": 0.95,
499+ "n_items": 300,
500+ "n_clusters": 269,
501+ "n_boot": 200
502+ },
503+ "control_iid": {
504+ "estimate": 0.013333333333333334,
505+ "low": -0.026917070901721032,
506+ "high": 0.06333862876254182,
507+ "level": 0.95,
508+ "n_items": 300,
509+ "n_clusters": 293,
510+ "n_boot": 200
511+ },
512+ "comp_heldout": {
513+ "estimate": 0.013333333333333334,
514+ "low": -0.017094429635534737,
515+ "high": 0.05065326224103545,
516+ "level": 0.95,
517+ "n_items": 300,
518+ "n_clusters": 241,
519+ "n_boot": 200
520+ },
521+ "len_ood": {
522+ "estimate": 0.0,
523+ "low": -0.04,
524+ "high": 0.03512500000000003,
525+ "level": 0.95,
526+ "n_items": 200,
527+ "n_clusters": 200,
528+ "n_boot": 200
529+ }
530+ }
531+ }
532+ },
533+ "noop": {
534+ "donor_s0": {
535+ "deterministic_repeat": true,
536+ "capture_all_bitwise_identity": true,
537+ "disabled_modifying_hook_bitwise_identity": true,
538+ "enabled_zero_hook_changes_output": true,
539+ "n_hook_points": 20,
540+ "n_items": 256
541+ },
542+ "recipient_s0": {
543+ "deterministic_repeat": true,
544+ "capture_all_bitwise_identity": true,
545+ "disabled_modifying_hook_bitwise_identity": true,
546+ "enabled_zero_hook_changes_output": true,
547+ "n_hook_points": 20,
548+ "n_items": 256
549+ },
550+ "donor_s1": {
551+ "deterministic_repeat": true,
552+ "capture_all_bitwise_identity": true,
553+ "disabled_modifying_hook_bitwise_identity": true,
554+ "enabled_zero_hook_changes_output": true,
555+ "n_hook_points": 20,
556+ "n_items": 256
557+ },
558+ "recipient_s1": {
559+ "deterministic_repeat": true,
560+ "capture_all_bitwise_identity": true,
561+ "disabled_modifying_hook_bitwise_identity": true,
562+ "enabled_zero_hook_changes_output": true,
563+ "n_hook_points": 20,
564+ "n_items": 256
565+ }
566+ },
567+ "localization": {
568+ "n_items": 490,
569+ "read_position": 6,
570+ "positions": [
571+ "<bos>",
572+ "v0",
573+ "op1",
574+ "\u03a9",
575+ "op3",
576+ "=",
577+ "r1"
578+ ],
579+ "families": {
580+ "resid_post": {
581+ "s0": {
582+ "flip_rate": [
583+ [
584+ 0.1081632673740387,
585+ 0.1081632673740387,
586+ 0.1081632673740387,
587+ 0.14897958934307098,
588+ 0.11020407825708389,
589+ 0.1081632673740387,
590+ 0.2938775420188904
591+ ],
592+ [
593+ 0.1081632673740387,
594+ 0.1081632673740387,
595+ 0.1081632673740387,
596+ 0.10408163070678711,
597+ 0.1061224490404129,
598+ 0.1081632673740387,
599+ 0.3306122422218323
600+ ],
601+ [
602+ 0.1081632673740387,
603+ 0.1081632673740387,
604+ 0.1081632673740387,
605+ 0.1081632673740387,
606+ 0.1081632673740387,
607+ 0.1081632673740387,
608+ 0.3163265287876129
609+ ]
610+ ],
611+ "logit_recovery": [
612+ [
613+ 0.0,
614+ 0.0,
615+ 0.0,
616+ 0.6777816414833069,
617+ 0.17032797634601593,
618+ 0.044533759355545044,
619+ 0.24952709674835205
620+ ],
621+ [
622+ 0.0,
623+ 0.0,
624+ 0.0,
625+ 0.5468828678131104,
626+ 0.21181631088256836,
627+ 0.0718715488910675,
628+ 0.43368828296661377
629+ ],
630+ [
631+ 0.0,
632+ 0.0,
633+ 0.0,
634+ 0.0,
635+ 0.0,
636+ 0.0,
637+ 0.7673469185829163
638+ ]
639+ ]
640+ },
641+ "s1": {
642+ "flip_rate": [
643+ [
644+ 0.15510204434394836,
645+ 0.15510204434394836,
646+ 0.15510204434394836,
647+ 0.16938775777816772,
648+ 0.15306122601032257,
649+ 0.15510204434394836,
650+ 0.48367348313331604
651+ ],
652+ [
653+ 0.15510204434394836,
654+ 0.15510204434394836,
655+ 0.15510204434394836,
656+ 0.15510204434394836,
657+ 0.15306122601032257,
658+ 0.15510204434394836,
659+ 0.5224489569664001
660+ ],
661+ [
662+ 0.15510204434394836,
663+ 0.15510204434394836,
664+ 0.15510204434394836,
665+ 0.15510204434394836,
666+ 0.15510204434394836,
667+ 0.15510204434394836,
668+ 0.5510203838348389
669+ ]
670+ ],
671+ "logit_recovery": [
672+ [
673+ 0.0,
674+ 0.0,
675+ 0.0,
676+ 0.5372936725616455,
677+ 0.15368731319904327,
678+ -0.08139883726835251,
679+ 0.4371233284473419
680+ ],
681+ [
682+ 0.0,
683+ 0.0,
684+ 0.0,
685+ 0.2054908275604248,
686+ 0.10810171812772751,
687+ -0.11871961504220963,
688+ 0.5426056385040283
689+ ],
690+ [
691+ 0.0,
692+ 0.0,
693+ 0.0,
694+ 0.0,
695+ 0.0,
696+ 0.0,
697+ 0.640816330909729
698+ ]
699+ ]
700+ },
701+ "mean_flip_rate": [
702+ [
703+ 0.13163265585899353,
704+ 0.13163265585899353,
705+ 0.13163265585899353,
706+ 0.15918367356061935,
707+ 0.13163265213370323,
708+ 0.13163265585899353,
709+ 0.3887755125761032
710+ ],
711+ [
712+ 0.13163265585899353,
713+ 0.13163265585899353,
714+ 0.13163265585899353,
715+ 0.12959183752536774,
716+ 0.12959183752536774,
717+ 0.13163265585899353,
718+ 0.4265305995941162
719+ ],
720+ [
721+ 0.13163265585899353,
722+ 0.13163265585899353,
723+ 0.13163265585899353,
724+ 0.13163265585899353,
725+ 0.13163265585899353,
726+ 0.13163265585899353,
727+ 0.4336734563112259
728+ ]
729+ ]
730+ },
731+ "attn_out": {
732+ "s0": {
733+ "flip_rate": [
734+ [
735+ 0.1081632673740387,
736+ 0.1081632673740387,
737+ 0.1081632673740387,
738+ 0.12448979914188385,
739+ 0.11020407825708389,
740+ 0.1081632673740387,
741+ 0.2938775420188904
742+ ],
743+ [
744+ 0.1081632673740387,
745+ 0.1081632673740387,
746+ 0.1081632673740387,
747+ 0.1081632673740387,
748+ 0.1061224490404129,
749+ 0.1081632673740387,
750+ 0.16734693944454193
751+ ],
752+ [
753+ 0.1081632673740387,
754+ 0.1081632673740387,
755+ 0.1081632673740387,
756+ 0.1081632673740387,
757+ 0.1081632673740387,
758+ 0.1081632673740387,
759+ 0.09795918315649033
760+ ]
761+ ],
762+ "logit_recovery": [
763+ [
764+ 0.0,
765+ 0.0,
766+ 0.0,
767+ 0.6502549052238464,
768+ 0.17032797634601593,
769+ 0.044533759355545044,
770+ 0.24952709674835205
771+ ],
772+ [
773+ 0.0,
774+ 0.0,
775+ 0.0,
776+ 0.16967330873012543,
777+ 0.0522448793053627,
778+ 0.0550827831029892,
779+ 0.31173965334892273
780+ ],
781+ [
782+ 0.0,
783+ 0.0,
784+ 0.0,
785+ 0.0,
786+ 0.0,
787+ 0.0,
788+ 0.5542861819267273
789+ ]
790+ ]
791+ },
792+ "s1": {
793+ "flip_rate": [
794+ [
795+ 0.15510204434394836,
796+ 0.15510204434394836,
797+ 0.15510204434394836,
798+ 0.15510204434394836,
799+ 0.15306122601032257,
800+ 0.15510204434394836,
801+ 0.48367348313331604
802+ ],
803+ [
804+ 0.15510204434394836,
805+ 0.15510204434394836,
806+ 0.15510204434394836,
807+ 0.15510204434394836,
808+ 0.15510204434394836,
809+ 0.15510204434394836,
810+ 0.1816326528787613
811+ ],
812+ [
813+ 0.15510204434394836,
814+ 0.15510204434394836,
815+ 0.15510204434394836,
816+ 0.15510204434394836,
817+ 0.15510204434394836,
818+ 0.15510204434394836,
819+ 0.13469387590885162
820+ ]
821+ ],
822+ "logit_recovery": [
823+ [
824+ 0.0,
825+ 0.0,
826+ 0.0,
827+ 0.3812299370765686,
828+ 0.15368731319904327,
829+ -0.08139883726835251,
830+ 0.4371233284473419
831+ ],
832+ [
833+ 0.0,
834+ 0.0,
835+ 0.0,
836+ -0.11997843533754349,
837+ -0.10889920592308044,
838+ -0.15029844641685486,
839+ 0.504331648349762
840+ ],
841+ [
842+ 0.0,
843+ 0.0,
844+ 0.0,
845+ 0.0,
846+ 0.0,
847+ 0.0,
848+ 0.21160942316055298
849+ ]
850+ ]
851+ },
852+ "mean_flip_rate": [
853+ [
854+ 0.13163265585899353,
855+ 0.13163265585899353,
856+ 0.13163265585899353,
857+ 0.1397959217429161,
858+ 0.13163265213370323,
859+ 0.13163265585899353,
860+ 0.3887755125761032
861+ ],
862+ [
863+ 0.13163265585899353,
864+ 0.13163265585899353,
865+ 0.13163265585899353,
866+ 0.13163265585899353,
867+ 0.13061224669218063,
868+ 0.13163265585899353,
869+ 0.1744897961616516
870+ ],
871+ [
872+ 0.13163265585899353,
873+ 0.13163265585899353,
874+ 0.13163265585899353,
875+ 0.13163265585899353,
876+ 0.13163265585899353,
877+ 0.13163265585899353,
878+ 0.11632652953267097
879+ ]
880+ ]
881+ },
882+ "mlp_out": {
883+ "s0": {
884+ "flip_rate": [
885+ [
886+ 0.1081632673740387,
887+ 0.1081632673740387,
888+ 0.1081632673740387,
889+ 0.12857143580913544,
890+ 0.1081632673740387,
891+ 0.1081632673740387,
892+ 0.24285714328289032
893+ ],
894+ [
895+ 0.1081632673740387,
896+ 0.1081632673740387,
897+ 0.1081632673740387,
898+ 0.11224489659070969,
899+ 0.1081632673740387,
900+ 0.1081632673740387,
901+ 0.2469387799501419
902+ ],
903+ [
904+ 0.1081632673740387,
905+ 0.1081632673740387,
906+ 0.1081632673740387,
907+ 0.1081632673740387,
908+ 0.1081632673740387,
909+ 0.1081632673740387,
910+ 0.20000000298023224
911+ ]
912+ ],
913+ "logit_recovery": [
914+ [
915+ 0.0,
916+ 0.0,
917+ 0.0,
918+ 0.676521897315979,
919+ 0.20865347981452942,
920+ 0.0366634726524353,
921+ 0.1594724953174591
922+ ],
923+ [
924+ 0.0,
925+ 0.0,
926+ 0.0,
927+ 0.4173266291618347,
928+ 0.1889232099056244,
929+ 0.07385488599538803,
930+ 0.49284762144088745
931+ ],
932+ [
933+ 0.0,
934+ 0.0,
935+ 0.0,
936+ 0.0,
937+ 0.0,
938+ 0.0,
939+ 0.1028335839509964
940+ ]
941+ ]
942+ },
943+ "s1": {
944+ "flip_rate": [
945+ [
946+ 0.15510204434394836,
947+ 0.15510204434394836,
948+ 0.15510204434394836,
949+ 0.17142857611179352,
950+ 0.15714286267757416,
951+ 0.15510204434394836,
952+ 0.33469387888908386
953+ ],
954+ [
955+ 0.15510204434394836,
956+ 0.15510204434394836,
957+ 0.15510204434394836,
958+ 0.15306122601032257,
959+ 0.15510204434394836,
960+ 0.15510204434394836,
961+ 0.3938775658607483
962+ ],
963+ [
964+ 0.15510204434394836,
965+ 0.15510204434394836,
966+ 0.15510204434394836,
967+ 0.15510204434394836,
968+ 0.15510204434394836,
969+ 0.15510204434394836,
970+ 0.23673468828201294
971+ ]
972+ ],
973+ "logit_recovery": [
974+ [
975+ 0.0,
976+ 0.0,
977+ 0.0,
978+ 0.4843812882900238,
979+ 0.14703114330768585,
980+ -0.07759958505630493,
981+ 0.24141383171081543
982+ ],
983+ [
984+ 0.0,
985+ 0.0,
986+ 0.0,
987+ -0.028258221223950386,
988+ 0.021661778911948204,
989+ 0.09376108646392822,
990+ 0.47914665937423706
991+ ],
992+ [
993+ 0.0,
994+ 0.0,
995+ 0.0,
996+ 0.0,
997+ 0.0,
998+ 0.0,
999+ 0.2513795495033264
1000+ ]
1001+ ]
1002+ },
1003+ "mean_flip_rate": [
1004+ [
1005+ 0.13163265585899353,
1006+ 0.13163265585899353,
1007+ 0.13163265585899353,
1008+ 0.15000000596046448,
1009+ 0.13265306502580643,
1010+ 0.13163265585899353,
1011+ 0.2887755110859871
1012+ ],
1013+ [
1014+ 0.13163265585899353,
1015+ 0.13163265585899353,
1016+ 0.13163265585899353,
1017+ 0.13265306130051613,
1018+ 0.13163265585899353,
1019+ 0.13163265585899353,
1020+ 0.3204081729054451
1021+ ],
1022+ [
1023+ 0.13163265585899353,
1024+ 0.13163265585899353,
1025+ 0.13163265585899353,
1026+ 0.13163265585899353,
1027+ 0.13163265585899353,
1028+ 0.13163265585899353,
1029+ 0.2183673456311226
1030+ ]
1031+ ]
1032+ }
1033+ },
1034+ "ablation": {
1035+ "resid_post": {
1036+ "s0": [
1037+ [
1038+ 0.2857142984867096,
1039+ 0.30816325545310974,
1040+ 0.3204081654548645,
1041+ 0.31224489212036133,
1042+ 0.31224489212036133,
1043+ 0.3224489688873291,
1044+ 0.11428571492433548
1045+ ],
1046+ [
1047+ 0.31224489212036133,
1048+ 0.3224489688873291,
1049+ 0.30612245202064514,
1050+ 0.30612245202064514,
1051+ 0.29591837525367737,
1052+ 0.3183673322200775,
1053+ 0.044897958636283875
1054+ ],
1055+ [
1056+ 0.3163265287876129,
1057+ 0.3163265287876129,
1058+ 0.3163265287876129,
1059+ 0.3163265287876129,
1060+ 0.3163265287876129,
1061+ 0.3163265287876129,
1062+ 0.11428571492433548
1063+ ]
1064+ ],
1065+ "s1": [
1066+ [
1067+ 0.4734693765640259,
1068+ 0.518367350101471,
1069+ 0.5469387769699097,
1070+ 0.48163264989852905,
1071+ 0.5469387769699097,
1072+ 0.5265306234359741,
1073+ 0.06530611962080002
1074+ ],
1075+ [
1076+ 0.559183657169342,
1077+ 0.563265323638916,
1078+ 0.559183657169342,
1079+ 0.5306122303009033,
1080+ 0.5469387769699097,
1081+ 0.5469387769699097,
1082+ 0.059183672070503235
1083+ ],
1084+ [
1085+ 0.5510203838348389,
1086+ 0.5510203838348389,
1087+ 0.5510203838348389,
1088+ 0.5510203838348389,
1089+ 0.5510203838348389,
1090+ 0.5510203838348389,
1091+ 0.08571428805589676
1092+ ]
1093+ ]
1094+ },
1095+ "attn_out": {
1096+ "s0": [
1097+ [
1098+ 0.31224489212036133,
1099+ 0.31224489212036133,
1100+ 0.31020408868789673,
1101+ 0.3285714387893677,
1102+ 0.30816325545310974,
1103+ 0.31020408868789673,
1104+ 0.1775510162115097
1105+ ],
1106+ [
1107+ 0.3142857253551483,
1108+ 0.31020408868789673,
1109+ 0.3142857253551483,
1110+ 0.3224489688873291,
1111+ 0.3306122422218323,
1112+ 0.3142857253551483,
1113+ 0.34285715222358704
1114+ ],
1115+ [
1116+ 0.3163265287876129,
1117+ 0.3163265287876129,
1118+ 0.3163265287876129,
1119+ 0.3163265287876129,
1120+ 0.3163265287876129,
1121+ 0.3163265287876129,
1122+ 0.3489795923233032
1123+ ]
1124+ ],
1125+ "s1": [
1126+ [
1127+ 0.5040816068649292,
1128+ 0.5122448801994324,
1129+ 0.5428571701049805,
1130+ 0.5510203838348389,
1131+ 0.5265306234359741,
1132+ 0.5367347002029419,
1133+ 0.27142858505249023
1134+ ],
1135+ [
1136+ 0.5551020503044128,
1137+ 0.5530612468719482,
1138+ 0.5571428537368774,
1139+ 0.5530612468719482,
1140+ 0.5612244606018066,
1141+ 0.5510203838348389,
1142+ 0.3489795923233032
1143+ ],
1144+ [
1145+ 0.5510203838348389,
1146+ 0.5510203838348389,
1147+ 0.5510203838348389,
1148+ 0.5510203838348389,
1149+ 0.5510203838348389,
1150+ 0.5510203838348389,
1151+ 0.48775508999824524
1152+ ]
1153+ ]
1154+ },
1155+ "mlp_out": {
1156+ "s0": [
1157+ [
1158+ 0.30408161878585815,
1159+ 0.30612245202064514,
1160+ 0.30612245202064514,
1161+ 0.3224489688873291,
1162+ 0.3204081654548645,
1163+ 0.3306122422218323,
1164+ 0.29795917868614197
1165+ ],
1166+ [
1167+ 0.3224489688873291,
1168+ 0.3183673322200775,
1169+ 0.3142857253551483,
1170+ 0.3163265287876129,
1171+ 0.30816325545310974,
1172+ 0.3244898021221161,
1173+ 0.2489795982837677
1174+ ],
1175+ [
1176+ 0.3163265287876129,
1177+ 0.3163265287876129,
1178+ 0.3163265287876129,
1179+ 0.3163265287876129,
1180+ 0.3163265287876129,
1181+ 0.3163265287876129,
1182+ 0.27551019191741943
1183+ ]
1184+ ],
1185+ "s1": [
1186+ [
1187+ 0.48367348313331604,
1188+ 0.5102040767669678,
1189+ 0.5551020503044128,
1190+ 0.5102040767669678,
1191+ 0.5510203838348389,
1192+ 0.5224489569664001,
1193+ 0.26326531171798706
1194+ ],
1195+ [
1196+ 0.559183657169342,
1197+ 0.5510203838348389,
1198+ 0.559183657169342,
1199+ 0.5571428537368774,
1200+ 0.5530612468719482,
1201+ 0.559183657169342,
1202+ 0.40408164262771606
1203+ ],
1204+ [
1205+ 0.5510203838348389,
1206+ 0.5510203838348389,
1207+ 0.5510203838348389,
1208+ 0.5510203838348389,
1209+ 0.5510203838348389,
1210+ 0.5510203838348389,
1211+ 0.4897959232330322
1212+ ]
1213+ ]
1214+ }
1215+ },
1216+ "heads": {
1217+ "omega_token": {
1218+ "s0": [
1219+ [
1220+ 0.1081632673740387,
1221+ 0.11428571492433548
1222+ ],
1223+ [
1224+ 0.1081632673740387,
1225+ 0.1081632673740387
1226+ ],
1227+ [
1228+ 0.1081632673740387,
1229+ 0.1081632673740387
1230+ ]
1231+ ],
1232+ "s1": [
1233+ [
1234+ 0.15714286267757416,
1235+ 0.15102040767669678
1236+ ],
1237+ [
1238+ 0.15510204434394836,
1239+ 0.15510204434394836
1240+ ],
1241+ [
1242+ 0.15510204434394836,
1243+ 0.15510204434394836
1244+ ]
1245+ ]
1246+ },
1247+ "read_position": {
1248+ "s0": [
1249+ [
1250+ 0.1755101978778839,
1251+ 0.2102040797472
1252+ ],
1253+ [
1254+ 0.12448979914188385,
1255+ 0.15714286267757416
1256+ ],
1257+ [
1258+ 0.1061224490404129,
1259+ 0.10408163070678711
1260+ ]
1261+ ],
1262+ "s1": [
1263+ [
1264+ 0.36938774585723877,
1265+ 0.26734694838523865
1266+ ],
1267+ [
1268+ 0.1795918345451355,
1269+ 0.15714286267757416
1270+ ],
1271+ [
1272+ 0.15510204434394836,
1273+ 0.14897958934307098
1274+ ]
1275+ ]
1276+ }
1277+ },
1278+ "reproducibility": {
1279+ "resid_post_s0_repeat_max_abs_diff": 0.0,
1280+ "attn_out_s0_repeat_max_abs_diff": 0.0,
1281+ "mlp_out_s0_repeat_max_abs_diff": 0.0,
1282+ "resid_post_s1_repeat_max_abs_diff": 0.0,
1283+ "attn_out_s1_repeat_max_abs_diff": 0.0,
1284+ "mlp_out_s1_repeat_max_abs_diff": 0.0,
1285+ "resid_post_across_seed_pearson_min": 0.9930306466962506,
1286+ "attn_out_across_seed_pearson_min": 0.9706457352445615,
1287+ "mlp_out_across_seed_pearson_min": 0.9732088508811297
1288+ },
1289+ "baselines": {
1290+ "s0": {
1291+ "clean_acc_on_clean_answer": 0.3163265287876129,
1292+ "corrupt_run_acc_on_clean_answer(sham)": 0.1081632673740387
1293+ },
1294+ "s1": {
1295+ "clean_acc_on_clean_answer": 0.5510203838348389,
1296+ "corrupt_run_acc_on_clean_answer(sham)": 0.15510204434394836
1297+ }
1298+ }
1299+ },
1300+ "across_seeds": {
1301+ "target_iid": {
1302+ "donor": {
1303+ "n_seeds": 2,
1304+ "mean": 0.12,
1305+ "sd": 0.009428090415820645,
1306+ "min": 0.11333333333333333,
1307+ "max": 0.12666666666666668
1308+ },
1309+ "recipient": {
1310+ "n_seeds": 2,
1311+ "mean": 0.09833333333333333,
1312+ "sd": 0.0117851130197758,
1313+ "min": 0.09,
1314+ "max": 0.10666666666666667
1315+ }
1316+ },
1317+ "control_iid": {
1318+ "donor": {
1319+ "n_seeds": 2,
1320+ "mean": 0.155,
1321+ "sd": 0.007071067811865481,
1322+ "min": 0.15,
1323+ "max": 0.16
1324+ },
1325+ "recipient": {
1326+ "n_seeds": 2,
1327+ "mean": 0.16166666666666668,
1328+ "sd": 0.021213203435596427,
1329+ "min": 0.14666666666666667,
1330+ "max": 0.17666666666666667
1331+ }
1332+ },
1333+ "comp_heldout": {
1334+ "donor": {
1335+ "n_seeds": 2,
1336+ "mean": 0.12833333333333333,
1337+ "sd": 0.025927248643506744,
1338+ "min": 0.11,
1339+ "max": 0.14666666666666667
1340+ },
1341+ "recipient": {
1342+ "n_seeds": 2,
1343+ "mean": 0.095,
1344+ "sd": 0.002357022603955154,
1345+ "min": 0.09333333333333334,
1346+ "max": 0.09666666666666666
1347+ }
1348+ },
1349+ "len_ood": {
1350+ "donor": {
1351+ "n_seeds": 2,
1352+ "mean": 0.08249999999999999,
1353+ "sd": 0.03181980515339464,
1354+ "min": 0.06,
1355+ "max": 0.105
1356+ },
1357+ "recipient": {
1358+ "n_seeds": 2,
1359+ "mean": 0.055,
1360+ "sd": 0.007071067811865472,
1361+ "min": 0.05,
1362+ "max": 0.06
1363+ }
1364+ }
1365+ },
1366+ "across_seeds_omega_step": {
1367+ "donor": {
1368+ "n_seeds": 2,
1369+ "mean": 0.2683333333333333,
1370+ "sd": 0.04006938426723772,
1371+ "min": 0.24,
1372+ "max": 0.2966666666666667
1373+ },
1374+ "recipient": {
1375+ "n_seeds": 2,
1376+ "mean": 0.07,
1377+ "sd": 0.009428090415820625,
1378+ "min": 0.06333333333333334,
1379+ "max": 0.07666666666666666
1380+ }
1381+ },
1382+ "elapsed_seconds": 22.053595066070557
1383+}
\ No newline at end of file
added literature/arxiv_verified_2026-11-09.txt +45 −0
@@ -0,0 +1,45 @@
1+2004.12265v2 | 2020-04-26 | Causal Mediation Analysis for Interpreting Neural NLP: The Case of Gender Bias | Jesse Vig, Sebastian Gehrmann, Yonatan Belinkov et al.
2+2106.07682v1 | 2021-06-14 | Revisiting Model Stitching to Compare Neural Representations | Yamini Bansal, Preetum Nakkiran, Boaz Barak
3+2310.01405v4 | 2023-10-02 | Representation Engineering: A Top-Down Approach to AI Transparency | Andy Zou, Long Phan, Sarah Chen et al.
4+2410.21276v1 | 2024-10-25 | GPT-4o System Card | OpenAI, :, Aaron Hurst et al.
5+2402.06660v4 | 2024-02-05 | A philosophical and ontological perspective on Artificial General Intelligence and the Metaverse | Martin Schmalzried
6+2306.01708v2 | 2023-06-02 | TIES-Merging: Resolving Interference When Merging Models | Prateek Yadav, Derek Tam, Leshem Choshen et al.
7+2608.09521v1 | 2026-08-10 | One Adapter Pair per Model: A Universal Activation Interface for Language Models | Su-Hyeon Kim, Jiwan Mun, Yo-Sub Han
8+2305.01610v2 | 2023-05-02 | Finding Neurons in a Haystack: Case Studies with Sparse Probing | Wes Gurnee, Neel Nanda, Matthew Pauly et al.
9+2305.14314v1 | 2023-05-23 | QLoRA: Efficient Finetuning of Quantized LLMs | Tim Dettmers, Artidoro Pagnoni, Ari Holtzman et al.
10+2310.15916v1 | 2023-10-24 | In-Context Learning Creates Task Vectors | Roee Hendel, Mor Geva, Amir Globerson
11+2404.14367v3 | 2024-04-22 | Preference Fine-Tuning of LLMs Should Leverage Suboptimal, On-Policy Data | Fahim Tajwar, Anikait Singh, Archit Sharma et al.
12+2405.07987v5 | 2024-05-13 | The Platonic Representation Hypothesis | Minyoung Huh, Brian Cheung, Tongzhou Wang et al.
13+2503.21073v3 | 2025-03-27 | Shared Global and Local Geometry of Language Model Embeddings | Andrew Lee, Melanie Weber, Fernanda Viégas et al.
14+2410.06981v4 | 2024-10-09 | Quantifying Feature Space Universality Across Large Language Models via Sparse Autoencoders | Michael Lan, Philip Torr, Austin Meek et al.
15+2310.15213v2 | 2023-10-23 | Function Vectors in Large Language Models | Eric Todd, Millicent L. Li, Arnab Sen Sharma et al.
16+2311.07320v1 | 2023-11-13 | Model structures on operads and algebras from a global perspective | Michael Batanin, Florian De Leger, David White
17+2601.16034v2 | 2026-01-22 | Universal Refusal Circuits Across LLMs: Cross-Model Transfer via Trajectory Replay and Concept-Basis Reconstruction | Tony Cristofano
18+2608.13596v1 | 2026-07-31 | Training-Free Knowledge Transfer Across Model Scales through Activation-Guided Pruning | Jiahe Fan, Si Chen, Yinghao Hou et al.
19+2209.04836v6 | 2022-09-11 | Git Re-Basin: Merging Models modulo Permutation Symmetries | Samuel K. Ainsworth, Jonathan Hayase, Siddhartha Srinivasa
20+2302.06600v2 | 2023-02-13 | Task-Specific Skill Localization in Fine-tuned Language Models | Abhishek Panigrahi, Nikunj Saunshi, Haoyu Zhao et al.
21+2308.09124v2 | 2023-08-17 | Linearity of Relation Decoding in Transformer Language Models | Evan Hernandez, Arnab Sen Sharma, Tal Haklay et al.
22+1503.00030v1 | 2015-02-27 | Parsing as Reduction | Daniel Fernández-González, André F. T. Martins
23+1706.05806v2 | 2017-06-19 | SVCCA: Singular Vector Canonical Correlation Analysis for Deep Learning Dynamics and Interpretability | Maithra Raghu, Justin Gilmer, Jason Yosinski et al.
24+2502.03714v2 | 2025-02-06 | Universal Sparse Autoencoders: Interpretable Cross-Model Concept Alignment | Harrish Thasarathan, Julian Forsyth, Thomas Fel et al.
25+2510.24208v2 | 2025-10-28 | Beyond Neural Incompatibility: Cross-Scale Knowledge Transfer in Language Models through Latent Semantic Alignment | Jian Gu, Aldeida Aleti, Chunyang Chen et al.
26+2606.03280v2 | 2026-06-02 | A Negative Result on Cross-Model Activation Transfer in a Pythia Multi-Hop Setting | Peiyan Zhang, Jason Xin
27+2308.10248v5 | 2023-08-20 | Steering Language Models With Activation Engineering | Alexander Matt Turner, Lisa Thiergart, Gavin Leech et al.
28+2509.06608v4 | 2025-09-08 | Small Vectors, Big Effects: A Mechanistic Study of RL-Induced Reasoning via Steering Vectors | Viacheslav Sinii, Nikita Balagansky, Gleb Gerasimov et al.
29+2507.06265v2 | 2025-07-07 | SPARC: Concept-Aligned Sparse Autoencoders for Cross-Model and Cross-Modal Interpretability | Ali Nasiri-Sarvi, Hassan Rivaz, Mahdi S. Hosseini
30+2608.05164v1 | 2026-05-26 | Cross-Architecture Steering Transfer in Language Models: A Systematic Empirical Study | Ayushi Agarwal
31+2309.08600v3 | 2023-09-15 | Sparse Autoencoders Find Highly Interpretable Features in Language Models | Hoagy Cunningham, Aidan Ewart, Logan Riggs et al.
32+2304.14997v4 | 2023-04-28 | Towards Automated Circuit Discovery for Mechanistic Interpretability | Arthur Conmy, Augustine N. Mavor-Parker, Aengus Lynch et al.
33+2211.00593v1 | 2022-11-01 | Interpretability in the Wild: a Circuit for Indirect Object Identification in GPT-2 small | Kevin Wang, Alexandre Variengien, Arthur Conmy et al.
34+2212.04089v3 | 2022-12-08 | Editing Models with Task Arithmetic | Gabriel Ilharco, Marco Tulio Ribeiro, Mitchell Wortsman et al.
35+1905.00414v4 | 2019-05-01 | Similarity of Neural Network Representations Revisited | Simon Kornblith, Mohammad Norouzi, Honglak Lee et al.
36+2202.05262v5 | 2022-02-10 | Locating and Editing Factual Associations in GPT | Kevin Meng, David Bau, Alex Andonian et al.
37+2301.04709v4 | 2023-01-11 | Causal Abstraction: A Theoretical Foundation for Mechanistic Interpretability | Atticus Geiger, Duligur Ibeling, Amir Zur et al.
38+2609.04344v1 | 2026-09-03 | SharedSAE: One Feature Dictionary Across Language Models | Daniil Ognev, Célian Vasson, Lijie Hu et al.
39+2303.02536v4 | 2023-03-05 | Finding Alignments Between Interpretable Causal Variables and Distributed Neural Representations | Atticus Geiger, Zhengxuan Wu, Christopher Potts et al.
40+2401.01814v1 | 2024-01-03 | Large Language Models Relearn Removed Concepts | Michelle Lo, Shay B. Cohen, Fazl Barez
41+2402.17762v2 | 2024-02-27 | Massive Activations in Large Language Models | Mingjie Sun, Xinlei Chen, J. Zico Kolter et al.
42+2306.03341v6 | 2023-06-06 | Inference-Time Intervention: Eliciting Truthful Answers from a Language Model | Kenneth Li, Oam Patel, Fernanda Viégas et al.
43+2506.06609v3 | 2025-06-07 | Transferring Linear Features Across Language Models With Model Stitching | Alan Chen, Jack Merullo, Alessandro Stolfo et al.
44+2405.07883v2 | 2024-05-13 | Zero-Shot Tokenizer Transfer | Benjamin Minixhofer, Edoardo Maria Ponti, Ivan Vulić
45+2602.06205v2 | 2026-02-05 | Multi-Way Representation Alignment | Akshit Achara, Tatiana Gaintseva, Mateo Mahaut et al.
\ No newline at end of file
added literature/references_unverified.md +12 −0
@@ -0,0 +1,12 @@
1+# Candidate references NOT yet verified (do not cite)
2+
3+- Lenc & Vedaldi (2015) "Understanding image representations by measuring their equivariance and equivalence" — origin of stitching layers; arXiv id to confirm.
4+- Hinton, Vinyals & Dean (2015) "Distilling the Knowledge in a Neural Network" (arXiv 1503.02531) — distillation baseline; id to confirm.
5+- Lindsey et al. (2024) "Sparse crosscoders for cross-layer features and model diffing" (transformer-circuits.pub) — no arXiv id.
6+- Makelov, Lange & Nanda (2023) "Is this the subspace you are looking for? An interpretability illusion for subspace activation patching" — id to confirm.
7+- Csiszárik et al. (2021) "Similarity and matching of neural network representations" (stitching) — id to confirm.
8+- Moschella et al. (2022) "Relative representations enable zero-shot latent space communication" — id to confirm.
9+- Wortsman et al. (2022) "Model soups" — id to confirm.
10+- Wan et al. (2024) "Knowledge Fusion of Large Language Models" (FuseLLM) — id to confirm.
11+- Transport and Merge: Cross-Architecture Merging for LLMs (2026?) — surfaced by search without id.
12+- Venue metadata for verified arXiv entries (e.g. 2506.06609 NeurIPS 2025 spotlight; 2302.06600 ICML 2023; 2310.15213 ICLR 2024; 2301.04709 JMLR 2025) — add only after checking the proceedings page.
added literature/references_verified.bib +471 −0
@@ -0,0 +1,471 @@
1+% WP-AI-0001 — references whose identifier, title, authors and date were verified against the arXiv API on 2026-11-09.
2+% primaryClass is not verified field-by-field; venue information (NeurIPS/ICML/ICLR/JMLR) is NOT included unless verified separately.
3+% Do not add entries to this file without verification; unverified candidates go to references_unverified.md.
4+
5+@misc{achara2026multi,
6+ title = {{Multi-Way Representation Alignment}},
7+ author = {Akshit Achara and Tatiana Gaintseva and Mateo Mahaut and Pritish Chakraborty and Viktor Stenby Johansson and Melih Barsbey and Emanuele Rodolà and Donato Crisostomi},
8+ year = {2026},
9+ month = {2},
10+ eprint = {2602.06205},
11+ archivePrefix= {arXiv},
12+ primaryClass = {cs.LG},
13+ url = {https://arxiv.org/abs/2602.06205},
14+ note = {arXiv:2602.06205; metadata verified via arXiv API on 2026-11-09}
15+}
16+
17+@misc{agarwal2026cross,
18+ title = {{Cross-Architecture Steering Transfer in Language Models: A Systematic Empirical Study}},
19+ author = {Ayushi Agarwal},
20+ year = {2026},
21+ month = {5},
22+ eprint = {2608.05164},
23+ archivePrefix= {arXiv},
24+ primaryClass = {cs.LG},
25+ url = {https://arxiv.org/abs/2608.05164},
26+ note = {arXiv:2608.05164; metadata verified via arXiv API on 2026-11-09}
27+}
28+
29+@misc{ainsworth2022git,
30+ title = {{Git Re-Basin: Merging Models modulo Permutation Symmetries}},
31+ author = {Samuel K. Ainsworth and Jonathan Hayase and Siddhartha Srinivasa},
32+ year = {2022},
33+ month = {9},
34+ eprint = {2209.04836},
35+ archivePrefix= {arXiv},
36+ primaryClass = {cs.LG},
37+ url = {https://arxiv.org/abs/2209.04836},
38+ note = {arXiv:2209.04836; metadata verified via arXiv API on 2026-11-09}
39+}
40+
41+@misc{bansal2021revisiting,
42+ title = {{Revisiting Model Stitching to Compare Neural Representations}},
43+ author = {Yamini Bansal and Preetum Nakkiran and Boaz Barak},
44+ year = {2021},
45+ month = {6},
46+ eprint = {2106.07682},
47+ archivePrefix= {arXiv},
48+ primaryClass = {cs.LG},
49+ url = {https://arxiv.org/abs/2106.07682},
50+ note = {arXiv:2106.07682; metadata verified via arXiv API on 2026-11-09}
51+}
52+
53+@misc{chen2025transferring,
54+ title = {{Transferring Linear Features Across Language Models With Model Stitching}},
55+ author = {Alan Chen and Jack Merullo and Alessandro Stolfo and Ellie Pavlick},
56+ year = {2025},
57+ month = {6},
58+ eprint = {2506.06609},
59+ archivePrefix= {arXiv},
60+ primaryClass = {cs.LG},
61+ url = {https://arxiv.org/abs/2506.06609},
62+ note = {arXiv:2506.06609; metadata verified via arXiv API on 2026-11-09}
63+}
64+
65+@misc{conmy2023towards,
66+ title = {{Towards Automated Circuit Discovery for Mechanistic Interpretability}},
67+ author = {Arthur Conmy and Augustine N. Mavor-Parker and Aengus Lynch and Stefan Heimersheim and Adrià Garriga-Alonso},
68+ year = {2023},
69+ month = {4},
70+ eprint = {2304.14997},
71+ archivePrefix= {arXiv},
72+ primaryClass = {cs.LG},
73+ url = {https://arxiv.org/abs/2304.14997},
74+ note = {arXiv:2304.14997; metadata verified via arXiv API on 2026-11-09}
75+}
76+
77+@misc{cristofano2026universal,
78+ title = {{Universal Refusal Circuits Across LLMs: Cross-Model Transfer via Trajectory Replay and Concept-Basis Reconstruction}},
79+ author = {Tony Cristofano},
80+ year = {2026},
81+ month = {1},
82+ eprint = {2601.16034},
83+ archivePrefix= {arXiv},
84+ primaryClass = {cs.LG},
85+ url = {https://arxiv.org/abs/2601.16034},
86+ note = {arXiv:2601.16034; metadata verified via arXiv API on 2026-11-09}
87+}
88+
89+@misc{cunningham2023sparse,
90+ title = {{Sparse Autoencoders Find Highly Interpretable Features in Language Models}},
91+ author = {Hoagy Cunningham and Aidan Ewart and Logan Riggs and Robert Huben and Lee Sharkey},
92+ year = {2023},
93+ month = {9},
94+ eprint = {2309.08600},
95+ archivePrefix= {arXiv},
96+ primaryClass = {cs.LG},
97+ url = {https://arxiv.org/abs/2309.08600},
98+ note = {arXiv:2309.08600; metadata verified via arXiv API on 2026-11-09}
99+}
100+
101+@misc{fan2026training,
102+ title = {{Training-Free Knowledge Transfer Across Model Scales through Activation-Guided Pruning}},
103+ author = {Jiahe Fan and Si Chen and Yinghao Hou and Aiyuan Zhang and Hong Xie},
104+ year = {2026},
105+ month = {7},
106+ eprint = {2608.13596},
107+ archivePrefix= {arXiv},
108+ primaryClass = {cs.LG},
109+ url = {https://arxiv.org/abs/2608.13596},
110+ note = {arXiv:2608.13596; metadata verified via arXiv API on 2026-11-09}
111+}
112+
113+@misc{geiger2023causal,
114+ title = {{Causal Abstraction: A Theoretical Foundation for Mechanistic Interpretability}},
115+ author = {Atticus Geiger and Duligur Ibeling and Amir Zur and Maheep Chaudhary and Sonakshi Chauhan and Jing Huang and Aryaman Arora and Zhengxuan Wu and Noah Goodman and Christopher Potts and Thomas Icard},
116+ year = {2023},
117+ month = {1},
118+ eprint = {2301.04709},
119+ archivePrefix= {arXiv},
120+ primaryClass = {cs.LG},
121+ url = {https://arxiv.org/abs/2301.04709},
122+ note = {arXiv:2301.04709; metadata verified via arXiv API on 2026-11-09}
123+}
124+
125+@misc{geiger2023finding,
126+ title = {{Finding Alignments Between Interpretable Causal Variables and Distributed Neural Representations}},
127+ author = {Atticus Geiger and Zhengxuan Wu and Christopher Potts and Thomas Icard and Noah D. Goodman},
128+ year = {2023},
129+ month = {3},
130+ eprint = {2303.02536},
131+ archivePrefix= {arXiv},
132+ primaryClass = {cs.LG},
133+ url = {https://arxiv.org/abs/2303.02536},
134+ note = {arXiv:2303.02536; metadata verified via arXiv API on 2026-11-09}
135+}
136+
137+@misc{gu2025beyond,
138+ title = {{Beyond Neural Incompatibility: Cross-Scale Knowledge Transfer in Language Models through Latent Semantic Alignment}},
139+ author = {Jian Gu and Aldeida Aleti and Chunyang Chen and Hongyu Zhang},
140+ year = {2025},
141+ month = {10},
142+ eprint = {2510.24208},
143+ archivePrefix= {arXiv},
144+ primaryClass = {cs.LG},
145+ url = {https://arxiv.org/abs/2510.24208},
146+ note = {arXiv:2510.24208; metadata verified via arXiv API on 2026-11-09}
147+}
148+
149+@misc{gurnee2023finding,
150+ title = {{Finding Neurons in a Haystack: Case Studies with Sparse Probing}},
151+ author = {Wes Gurnee and Neel Nanda and Matthew Pauly and Katherine Harvey and Dmitrii Troitskii and Dimitris Bertsimas},
152+ year = {2023},
153+ month = {5},
154+ eprint = {2305.01610},
155+ archivePrefix= {arXiv},
156+ primaryClass = {cs.LG},
157+ url = {https://arxiv.org/abs/2305.01610},
158+ note = {arXiv:2305.01610; metadata verified via arXiv API on 2026-11-09}
159+}
160+
161+@misc{hendel2023context,
162+ title = {{In-Context Learning Creates Task Vectors}},
163+ author = {Roee Hendel and Mor Geva and Amir Globerson},
164+ year = {2023},
165+ month = {10},
166+ eprint = {2310.15916},
167+ archivePrefix= {arXiv},
168+ primaryClass = {cs.LG},
169+ url = {https://arxiv.org/abs/2310.15916},
170+ note = {arXiv:2310.15916; metadata verified via arXiv API on 2026-11-09}
171+}
172+
173+@misc{hernandez2023linearity,
174+ title = {{Linearity of Relation Decoding in Transformer Language Models}},
175+ author = {Evan Hernandez and Arnab Sen Sharma and Tal Haklay and Kevin Meng and Martin Wattenberg and Jacob Andreas and Yonatan Belinkov and David Bau},
176+ year = {2023},
177+ month = {8},
178+ eprint = {2308.09124},
179+ archivePrefix= {arXiv},
180+ primaryClass = {cs.LG},
181+ url = {https://arxiv.org/abs/2308.09124},
182+ note = {arXiv:2308.09124; metadata verified via arXiv API on 2026-11-09}
183+}
184+
185+@misc{huh2024platonic,
186+ title = {{The Platonic Representation Hypothesis}},
187+ author = {Minyoung Huh and Brian Cheung and Tongzhou Wang and Phillip Isola},
188+ year = {2024},
189+ month = {5},
190+ eprint = {2405.07987},
191+ archivePrefix= {arXiv},
192+ primaryClass = {cs.LG},
193+ url = {https://arxiv.org/abs/2405.07987},
194+ note = {arXiv:2405.07987; metadata verified via arXiv API on 2026-11-09}
195+}
196+
197+@misc{ilharco2022editing,
198+ title = {{Editing Models with Task Arithmetic}},
199+ author = {Gabriel Ilharco and Marco Tulio Ribeiro and Mitchell Wortsman and Suchin Gururangan and Ludwig Schmidt and Hannaneh Hajishirzi and Ali Farhadi},
200+ year = {2022},
201+ month = {12},
202+ eprint = {2212.04089},
203+ archivePrefix= {arXiv},
204+ primaryClass = {cs.LG},
205+ url = {https://arxiv.org/abs/2212.04089},
206+ note = {arXiv:2212.04089; metadata verified via arXiv API on 2026-11-09}
207+}
208+
209+@misc{kim2026one,
210+ title = {{One Adapter Pair per Model: A Universal Activation Interface for Language Models}},
211+ author = {Su-Hyeon Kim and Jiwan Mun and Yo-Sub Han},
212+ year = {2026},
213+ month = {8},
214+ eprint = {2608.09521},
215+ archivePrefix= {arXiv},
216+ primaryClass = {cs.LG},
217+ url = {https://arxiv.org/abs/2608.09521},
218+ note = {arXiv:2608.09521; metadata verified via arXiv API on 2026-11-09}
219+}
220+
221+@misc{kornblith2019similarity,
222+ title = {{Similarity of Neural Network Representations Revisited}},
223+ author = {Simon Kornblith and Mohammad Norouzi and Honglak Lee and Geoffrey Hinton},
224+ year = {2019},
225+ month = {5},
226+ eprint = {1905.00414},
227+ archivePrefix= {arXiv},
228+ primaryClass = {cs.LG},
229+ url = {https://arxiv.org/abs/1905.00414},
230+ note = {arXiv:1905.00414; metadata verified via arXiv API on 2026-11-09}
231+}
232+
233+@misc{lan2024quantifying,
234+ title = {{Quantifying Feature Space Universality Across Large Language Models via Sparse Autoencoders}},
235+ author = {Michael Lan and Philip Torr and Austin Meek and Ashkan Khakzar and David Krueger and Fazl Barez},
236+ year = {2024},
237+ month = {10},
238+ eprint = {2410.06981},
239+ archivePrefix= {arXiv},
240+ primaryClass = {cs.LG},
241+ url = {https://arxiv.org/abs/2410.06981},
242+ note = {arXiv:2410.06981; metadata verified via arXiv API on 2026-11-09}
243+}
244+
245+@misc{lee2025shared,
246+ title = {{Shared Global and Local Geometry of Language Model Embeddings}},
247+ author = {Andrew Lee and Melanie Weber and Fernanda Viégas and Martin Wattenberg},
248+ year = {2025},
249+ month = {3},
250+ eprint = {2503.21073},
251+ archivePrefix= {arXiv},
252+ primaryClass = {cs.LG},
253+ url = {https://arxiv.org/abs/2503.21073},
254+ note = {arXiv:2503.21073; metadata verified via arXiv API on 2026-11-09}
255+}
256+
257+@misc{li2023inference,
258+ title = {{Inference-Time Intervention: Eliciting Truthful Answers from a Language Model}},
259+ author = {Kenneth Li and Oam Patel and Fernanda Viégas and Hanspeter Pfister and Martin Wattenberg},
260+ year = {2023},
261+ month = {6},
262+ eprint = {2306.03341},
263+ archivePrefix= {arXiv},
264+ primaryClass = {cs.LG},
265+ url = {https://arxiv.org/abs/2306.03341},
266+ note = {arXiv:2306.03341; metadata verified via arXiv API on 2026-11-09}
267+}
268+
269+@misc{lo2024large,
270+ title = {{Large Language Models Relearn Removed Concepts}},
271+ author = {Michelle Lo and Shay B. Cohen and Fazl Barez},
272+ year = {2024},
273+ month = {1},
274+ eprint = {2401.01814},
275+ archivePrefix= {arXiv},
276+ primaryClass = {cs.LG},
277+ url = {https://arxiv.org/abs/2401.01814},
278+ note = {arXiv:2401.01814; metadata verified via arXiv API on 2026-11-09}
279+}
280+
281+@misc{meng2022locating,
282+ title = {{Locating and Editing Factual Associations in GPT}},
283+ author = {Kevin Meng and David Bau and Alex Andonian and Yonatan Belinkov},
284+ year = {2022},
285+ month = {2},
286+ eprint = {2202.05262},
287+ archivePrefix= {arXiv},
288+ primaryClass = {cs.LG},
289+ url = {https://arxiv.org/abs/2202.05262},
290+ note = {arXiv:2202.05262; metadata verified via arXiv API on 2026-11-09}
291+}
292+
293+@misc{minixhofer2024zero,
294+ title = {{Zero-Shot Tokenizer Transfer}},
295+ author = {Benjamin Minixhofer and Edoardo Maria Ponti and Ivan Vulić},
296+ year = {2024},
297+ month = {5},
298+ eprint = {2405.07883},
299+ archivePrefix= {arXiv},
300+ primaryClass = {cs.LG},
301+ url = {https://arxiv.org/abs/2405.07883},
302+ note = {arXiv:2405.07883; metadata verified via arXiv API on 2026-11-09}
303+}
304+
305+@misc{nasirisarvi2025sparc,
306+ title = {{SPARC: Concept-Aligned Sparse Autoencoders for Cross-Model and Cross-Modal Interpretability}},
307+ author = {Ali Nasiri-Sarvi and Hassan Rivaz and Mahdi S. Hosseini},
308+ year = {2025},
309+ month = {7},
310+ eprint = {2507.06265},
311+ archivePrefix= {arXiv},
312+ primaryClass = {cs.LG},
313+ url = {https://arxiv.org/abs/2507.06265},
314+ note = {arXiv:2507.06265; metadata verified via arXiv API on 2026-11-09}
315+}
316+
317+@misc{ognev2026sharedsae,
318+ title = {{SharedSAE: One Feature Dictionary Across Language Models}},
319+ author = {Daniil Ognev and Célian Vasson and Lijie Hu and Kentaro Inui and Benjamin Heinzerling},
320+ year = {2026},
321+ month = {9},
322+ eprint = {2609.04344},
323+ archivePrefix= {arXiv},
324+ primaryClass = {cs.LG},
325+ url = {https://arxiv.org/abs/2609.04344},
326+ note = {arXiv:2609.04344; metadata verified via arXiv API on 2026-11-09}
327+}
328+
329+@misc{panigrahi2023task,
330+ title = {{Task-Specific Skill Localization in Fine-tuned Language Models}},
331+ author = {Abhishek Panigrahi and Nikunj Saunshi and Haoyu Zhao and Sanjeev Arora},
332+ year = {2023},
333+ month = {2},
334+ eprint = {2302.06600},
335+ archivePrefix= {arXiv},
336+ primaryClass = {cs.LG},
337+ url = {https://arxiv.org/abs/2302.06600},
338+ note = {arXiv:2302.06600; metadata verified via arXiv API on 2026-11-09}
339+}
340+
341+@misc{raghu2017svcca,
342+ title = {{SVCCA: Singular Vector Canonical Correlation Analysis for Deep Learning Dynamics and Interpretability}},
343+ author = {Maithra Raghu and Justin Gilmer and Jason Yosinski and Jascha Sohl-Dickstein},
344+ year = {2017},
345+ month = {6},
346+ eprint = {1706.05806},
347+ archivePrefix= {arXiv},
348+ primaryClass = {cs.LG},
349+ url = {https://arxiv.org/abs/1706.05806},
350+ note = {arXiv:1706.05806; metadata verified via arXiv API on 2026-11-09}
351+}
352+
353+@misc{sinii2025small,
354+ title = {{Small Vectors, Big Effects: A Mechanistic Study of RL-Induced Reasoning via Steering Vectors}},
355+ author = {Viacheslav Sinii and Nikita Balagansky and Gleb Gerasimov and Daniil Laptev and Yaroslav Aksenov and Vadim Kurochkin and Alexey Gorbatovski and Boris Shaposhnikov and Daniil Gavrilov},
356+ year = {2025},
357+ month = {9},
358+ eprint = {2509.06608},
359+ archivePrefix= {arXiv},
360+ primaryClass = {cs.LG},
361+ url = {https://arxiv.org/abs/2509.06608},
362+ note = {arXiv:2509.06608; metadata verified via arXiv API on 2026-11-09}
363+}
364+
365+@misc{sun2024massive,
366+ title = {{Massive Activations in Large Language Models}},
367+ author = {Mingjie Sun and Xinlei Chen and J. Zico Kolter and Zhuang Liu},
368+ year = {2024},
369+ month = {2},
370+ eprint = {2402.17762},
371+ archivePrefix= {arXiv},
372+ primaryClass = {cs.LG},
373+ url = {https://arxiv.org/abs/2402.17762},
374+ note = {arXiv:2402.17762; metadata verified via arXiv API on 2026-11-09}
375+}
376+
377+@misc{thasarathan2025universal,
378+ title = {{Universal Sparse Autoencoders: Interpretable Cross-Model Concept Alignment}},
379+ author = {Harrish Thasarathan and Julian Forsyth and Thomas Fel and Matthew Kowal and Konstantinos G. Derpanis},
380+ year = {2025},
381+ month = {2},
382+ eprint = {2502.03714},
383+ archivePrefix= {arXiv},
384+ primaryClass = {cs.LG},
385+ url = {https://arxiv.org/abs/2502.03714},
386+ note = {arXiv:2502.03714; metadata verified via arXiv API on 2026-11-09}
387+}
388+
389+@misc{todd2023function,
390+ title = {{Function Vectors in Large Language Models}},
391+ author = {Eric Todd and Millicent L. Li and Arnab Sen Sharma and Aaron Mueller and Byron C. Wallace and David Bau},
392+ year = {2023},
393+ month = {10},
394+ eprint = {2310.15213},
395+ archivePrefix= {arXiv},
396+ primaryClass = {cs.LG},
397+ url = {https://arxiv.org/abs/2310.15213},
398+ note = {arXiv:2310.15213; metadata verified via arXiv API on 2026-11-09}
399+}
400+
401+@misc{turner2023steering,
402+ title = {{Steering Language Models With Activation Engineering}},
403+ author = {Alexander Matt Turner and Lisa Thiergart and Gavin Leech and David Udell and Juan J. Vazquez and Ulisse Mini and Monte MacDiarmid},
404+ year = {2023},
405+ month = {8},
406+ eprint = {2308.10248},
407+ archivePrefix= {arXiv},
408+ primaryClass = {cs.LG},
409+ url = {https://arxiv.org/abs/2308.10248},
410+ note = {arXiv:2308.10248; metadata verified via arXiv API on 2026-11-09}
411+}
412+
413+@misc{vig2020causal,
414+ title = {{Causal Mediation Analysis for Interpreting Neural NLP: The Case of Gender Bias}},
415+ author = {Jesse Vig and Sebastian Gehrmann and Yonatan Belinkov and Sharon Qian and Daniel Nevo and Simas Sakenis and Jason Huang and Yaron Singer and Stuart Shieber},
416+ year = {2020},
417+ month = {4},
418+ eprint = {2004.12265},
419+ archivePrefix= {arXiv},
420+ primaryClass = {cs.LG},
421+ url = {https://arxiv.org/abs/2004.12265},
422+ note = {arXiv:2004.12265; metadata verified via arXiv API on 2026-11-09}
423+}
424+
425+@misc{wang2022interpretability,
426+ title = {{Interpretability in the Wild: a Circuit for Indirect Object Identification in GPT-2 small}},
427+ author = {Kevin Wang and Alexandre Variengien and Arthur Conmy and Buck Shlegeris and Jacob Steinhardt},
428+ year = {2022},
429+ month = {11},
430+ eprint = {2211.00593},
431+ archivePrefix= {arXiv},
432+ primaryClass = {cs.LG},
433+ url = {https://arxiv.org/abs/2211.00593},
434+ note = {arXiv:2211.00593; metadata verified via arXiv API on 2026-11-09}
435+}
436+
437+@misc{yadav2023ties,
438+ title = {{TIES-Merging: Resolving Interference When Merging Models}},
439+ author = {Prateek Yadav and Derek Tam and Leshem Choshen and Colin Raffel and Mohit Bansal},
440+ year = {2023},
441+ month = {6},
442+ eprint = {2306.01708},
443+ archivePrefix= {arXiv},
444+ primaryClass = {cs.LG},
445+ url = {https://arxiv.org/abs/2306.01708},
446+ note = {arXiv:2306.01708; metadata verified via arXiv API on 2026-11-09}
447+}
448+
449+@misc{zhang2026negative,
450+ title = {{A Negative Result on Cross-Model Activation Transfer in a Pythia Multi-Hop Setting}},
451+ author = {Peiyan Zhang and Jason Xin},
452+ year = {2026},
453+ month = {6},
454+ eprint = {2606.03280},
455+ archivePrefix= {arXiv},
456+ primaryClass = {cs.LG},
457+ url = {https://arxiv.org/abs/2606.03280},
458+ note = {arXiv:2606.03280; metadata verified via arXiv API on 2026-11-09}
459+}
460+
461+@misc{zou2023representation,
462+ title = {{Representation Engineering: A Top-Down Approach to AI Transparency}},
463+ author = {Andy Zou and Long Phan and Sarah Chen and James Campbell and Phillip Guo and Richard Ren and Alexander Pan and Xuwang Yin and Mantas Mazeika and Ann-Kathrin Dombrowski and Shashwat Goel and Nathaniel Li and Michael J. Byun and Zifan Wang and Alex Mallen and Steven Basart and Sanmi Koyejo and Dawn Song and Matt Fredrikson and J. Zico Kolter and Dan Hendrycks},
464+ year = {2023},
465+ month = {10},
466+ eprint = {2310.01405},
467+ archivePrefix= {arXiv},
468+ primaryClass = {cs.LG},
469+ url = {https://arxiv.org/abs/2310.01405},
470+ note = {arXiv:2310.01405; metadata verified via arXiv API on 2026-11-09}
471+}
added paper/.latexmkrc +5 −0
@@ -0,0 +1,5 @@
1+$pdf_mode = 1;
2+$pdflatex = 'pdflatex -interaction=nonstopmode -halt-on-error -synctex=1 %O %S';
3+$bibtex_use = 2;
4+$clean_ext = 'synctex.gz run.xml bbl bcf fdb_latexmk fls log aux out toc lof lot blg';
5+@default_files = ('main.tex');
added paper/Makefile +11 −0
@@ -0,0 +1,11 @@
1+.PHONY: all clean distclean
2+
3+all:
4+ latexmk
5+
6+clean:
7+ latexmk -c
8+
9+distclean:
10+ latexmk -C
11+ rm -f *.synctex.gz *.run.xml *.bbl *.bcf
added paper/appendix/appendix.tex +6 −0
@@ -0,0 +1,6 @@
1+\appendix
2+\section{Causal-abstraction statement for stitched models}\label{app:theory}
3+[PLACEHOLDER --- restatement with assumptions of the interchange-commutation property under exact causal
4+abstraction \citep{geiger2023causal}; conjectures are labelled as such.]
5+\section{Prompts, configurations and model cards}
6+[PLACEHOLDER --- generated from \texttt{configs/} and run manifests.]
added paper/figures/FIGURE_PROVENANCE.md +5 −0
@@ -0,0 +1,5 @@
1+# Figure provenance
2+
3+| run id | results sha256[:16] | built (UTC) | figures |
4+|---|---|---|---|
5+| pilot_tier0_opseq_451ef5087182 | a75c965945f8f43f | 2026-11-09T06:57:25+00:00 | fig_gap_tier0.pdf, fig_localization_ablation_tier0.pdf, fig_localization_heads_tier0.pdf, fig_localization_patching_tier0.pdf |
added paper/figures/fig_gap_tier0.pdf +0 −0

Binary file not shown.

added paper/figures/fig_localization_ablation_tier0.pdf +0 −0

Binary file not shown.

added paper/figures/fig_localization_heads_tier0.pdf +0 −0

Binary file not shown.

added paper/figures/fig_localization_patching_tier0.pdf +0 −0

Binary file not shown.

added paper/main.pdf +0 −0

Binary file not shown.

added paper/main.tex +147 −0
@@ -0,0 +1,147 @@
1+% ============================================================================
2+% UQO Working Paper Template — Economics / Finance
3+% ============================================================================
4+\documentclass[12pt,letterpaper]{article}
5+
6+% --- Encoding & Language ---
7+\usepackage[utf8]{inputenc}
8+\usepackage[T1]{fontenc}
9+\usepackage[english]{babel}
10+
11+% --- Page Layout ---
12+\usepackage[
13+ letterpaper,
14+ top=1in,
15+ bottom=1in,
16+ left=1in,
17+ right=1in
18+]{geometry}
19+
20+% --- Typography ---
21+\usepackage{mathptmx} % Times-like font (text + math)
22+\usepackage{microtype} % Improved typographic refinements
23+\usepackage{setspace}
24+\onehalfspacing % 1.5 line spacing
25+
26+% --- Math ---
27+\usepackage{amsmath,amssymb,amsthm}
28+
29+% --- Tables ---
30+\usepackage{booktabs}
31+\usepackage{tabularx}
32+\usepackage{multirow}
33+\usepackage{threeparttable}
34+\usepackage{siunitx}
35+\sisetup{
36+ input-symbols = {()},
37+ table-format = -1.3,
38+ parse-numbers = false
39+}
40+
41+% --- Figures ---
42+\usepackage{graphicx}
43+\usepackage{subcaption}
44+\usepackage{float}
45+
46+% --- Colors & Links ---
47+\usepackage[dvipsnames]{xcolor}
48+\usepackage[
49+ colorlinks = true,
50+ linkcolor = NavyBlue,
51+ citecolor = NavyBlue,
52+ urlcolor = NavyBlue,
53+ breaklinks = true
54+]{hyperref}
55+
56+% --- Bibliography ---
57+\usepackage[authoryear,round,semicolon]{natbib}
58+\bibliographystyle{aer}
59+
60+% --- Captions ---
61+\usepackage[
62+ font = small,
63+ labelfont = bf,
64+ labelsep = period,
65+ justification = justified,
66+ singlelinecheck = false
67+]{caption}
68+
69+% --- Headers & Footers ---
70+\usepackage{fancyhdr}
71+\pagestyle{fancy}
72+\fancyhf{}
73+\renewcommand{\headrulewidth}{0pt}
74+\fancyfoot[C]{\thepage}
75+
76+% --- Section Formatting ---
77+\usepackage{titlesec}
78+\titleformat{\section}{\large\bfseries}{\thesection.}{0.5em}{}
79+\titleformat{\subsection}{\normalsize\bfseries}{\thesubsection.}{0.5em}{}
80+\titleformat{\subsubsection}{\normalsize\itshape}{\thesubsubsection.}{0.5em}{}
81+
82+% --- Appendix Support ---
83+\usepackage[toc,page]{appendix}
84+
85+% --- Misc ---
86+\usepackage{enumitem}
87+\setlist{nosep,leftmargin=*}
88+
89+% ============================================================================
90+% METADATA — Edit these fields for each working paper
91+% ============================================================================
92+\newcommand{\WPnumber}{AI-0001}
93+\newcommand{\WPtitle}{The Neural Organ Transplant Experiment}
94+\newcommand{\WPsubtitle}{Causal Transfer of Cognitive Capabilities Across Local Large Language Models}
95+\newcommand{\WPdate}{November 2026}
96+\newcommand{\WPversion}{0.1}
97+\newcommand{\WPabstract}{%
98+[PROTOCOL DRAFT --- NO RESULTS YET.] We ask whether a bounded, causally identified computational subsystem
99+responsible for a measurable capability can be transplanted from a donor open-weight language model into a
100+recipient that demonstrably lacks it, without conventional full-model training. This draft states the
101+operational definitions, estimands, preregistered hypotheses, controls and experimental design; empirical
102+sections contain explicit placeholders until the corresponding runs exist in the experiment registry.
103+}
104+\newcommand{\WPkeywords}{mechanistic interpretability, activation patching, model stitching, representation alignment, knowledge transfer, causal abstraction, open-weight language models}
105+\newcommand{\WPjel}{C45, C63, C88, O33}
106+
107+% --- Author ---
108+\newcommand{\WPauthor}{Simon-Pierre Boucher}
109+\newcommand{\WPaffiliation}{%
110+ Département des sciences administratives\\
111+ Université du Québec en Outaouais%
112+}
113+\newcommand{\WPemail}{simon-pierre.boucher@uqo.ca}
114+\newcommand{\WPaddress}{%
115+ Gatineau -- Pavillon Alexandre-Taché\\
116+ 283, boulevard Alexandre-Taché\\
117+ Gatineau, Québec, Canada J9A 1L8%
118+}
119+
120+% ============================================================================
121+% DOCUMENT
122+% ============================================================================
123+\begin{document}
124+
125+% --- Title Page ---
126+\input{sections/titlepage}
127+
128+% --- Main Body ---
129+\input{sections/introduction}
130+\input{sections/related_work}
131+\input{sections/operational_theory}
132+\input{sections/experimental_design}
133+\input{sections/results}
134+\input{sections/mechanistic_analysis}
135+\input{sections/robustness}
136+\input{sections/discussion}
137+\input{sections/limitations}
138+\input{sections/conclusion}
139+
140+% --- References ---
141+\newpage
142+\bibliography{references}
143+
144+% --- Appendix ---
145+\input{appendix/appendix}
146+
147+\end{document}
added paper/references.bib +471 −0
@@ -0,0 +1,471 @@
1+% WP-AI-0001 — references whose identifier, title, authors and date were verified against the arXiv API on 2026-11-09.
2+% primaryClass is not verified field-by-field; venue information (NeurIPS/ICML/ICLR/JMLR) is NOT included unless verified separately.
3+% Do not add entries to this file without verification; unverified candidates go to references_unverified.md.
4+
5+@misc{achara2026multi,
6+ title = {{Multi-Way Representation Alignment}},
7+ author = {Akshit Achara and Tatiana Gaintseva and Mateo Mahaut and Pritish Chakraborty and Viktor Stenby Johansson and Melih Barsbey and Emanuele Rodolà and Donato Crisostomi},
8+ year = {2026},
9+ month = {2},
10+ eprint = {2602.06205},
11+ archivePrefix= {arXiv},
12+ primaryClass = {cs.LG},
13+ url = {https://arxiv.org/abs/2602.06205},
14+ note = {arXiv:2602.06205; metadata verified via arXiv API on 2026-11-09}
15+}
16+
17+@misc{agarwal2026cross,
18+ title = {{Cross-Architecture Steering Transfer in Language Models: A Systematic Empirical Study}},
19+ author = {Ayushi Agarwal},
20+ year = {2026},
21+ month = {5},
22+ eprint = {2608.05164},
23+ archivePrefix= {arXiv},
24+ primaryClass = {cs.LG},
25+ url = {https://arxiv.org/abs/2608.05164},
26+ note = {arXiv:2608.05164; metadata verified via arXiv API on 2026-11-09}
27+}
28+
29+@misc{ainsworth2022git,
30+ title = {{Git Re-Basin: Merging Models modulo Permutation Symmetries}},
31+ author = {Samuel K. Ainsworth and Jonathan Hayase and Siddhartha Srinivasa},
32+ year = {2022},
33+ month = {9},
34+ eprint = {2209.04836},
35+ archivePrefix= {arXiv},
36+ primaryClass = {cs.LG},
37+ url = {https://arxiv.org/abs/2209.04836},
38+ note = {arXiv:2209.04836; metadata verified via arXiv API on 2026-11-09}
39+}
40+
41+@misc{bansal2021revisiting,
42+ title = {{Revisiting Model Stitching to Compare Neural Representations}},
43+ author = {Yamini Bansal and Preetum Nakkiran and Boaz Barak},
44+ year = {2021},
45+ month = {6},
46+ eprint = {2106.07682},
47+ archivePrefix= {arXiv},
48+ primaryClass = {cs.LG},
49+ url = {https://arxiv.org/abs/2106.07682},
50+ note = {arXiv:2106.07682; metadata verified via arXiv API on 2026-11-09}
51+}
52+
53+@misc{chen2025transferring,
54+ title = {{Transferring Linear Features Across Language Models With Model Stitching}},
55+ author = {Alan Chen and Jack Merullo and Alessandro Stolfo and Ellie Pavlick},
56+ year = {2025},
57+ month = {6},
58+ eprint = {2506.06609},
59+ archivePrefix= {arXiv},
60+ primaryClass = {cs.LG},
61+ url = {https://arxiv.org/abs/2506.06609},
62+ note = {arXiv:2506.06609; metadata verified via arXiv API on 2026-11-09}
63+}
64+
65+@misc{conmy2023towards,
66+ title = {{Towards Automated Circuit Discovery for Mechanistic Interpretability}},
67+ author = {Arthur Conmy and Augustine N. Mavor-Parker and Aengus Lynch and Stefan Heimersheim and Adrià Garriga-Alonso},
68+ year = {2023},
69+ month = {4},
70+ eprint = {2304.14997},
71+ archivePrefix= {arXiv},
72+ primaryClass = {cs.LG},
73+ url = {https://arxiv.org/abs/2304.14997},
74+ note = {arXiv:2304.14997; metadata verified via arXiv API on 2026-11-09}
75+}
76+
77+@misc{cristofano2026universal,
78+ title = {{Universal Refusal Circuits Across LLMs: Cross-Model Transfer via Trajectory Replay and Concept-Basis Reconstruction}},
79+ author = {Tony Cristofano},
80+ year = {2026},
81+ month = {1},
82+ eprint = {2601.16034},
83+ archivePrefix= {arXiv},
84+ primaryClass = {cs.LG},
85+ url = {https://arxiv.org/abs/2601.16034},
86+ note = {arXiv:2601.16034; metadata verified via arXiv API on 2026-11-09}
87+}
88+
89+@misc{cunningham2023sparse,
90+ title = {{Sparse Autoencoders Find Highly Interpretable Features in Language Models}},
91+ author = {Hoagy Cunningham and Aidan Ewart and Logan Riggs and Robert Huben and Lee Sharkey},
92+ year = {2023},
93+ month = {9},
94+ eprint = {2309.08600},
95+ archivePrefix= {arXiv},
96+ primaryClass = {cs.LG},
97+ url = {https://arxiv.org/abs/2309.08600},
98+ note = {arXiv:2309.08600; metadata verified via arXiv API on 2026-11-09}
99+}
100+
101+@misc{fan2026training,
102+ title = {{Training-Free Knowledge Transfer Across Model Scales through Activation-Guided Pruning}},
103+ author = {Jiahe Fan and Si Chen and Yinghao Hou and Aiyuan Zhang and Hong Xie},
104+ year = {2026},
105+ month = {7},
106+ eprint = {2608.13596},
107+ archivePrefix= {arXiv},
108+ primaryClass = {cs.LG},
109+ url = {https://arxiv.org/abs/2608.13596},
110+ note = {arXiv:2608.13596; metadata verified via arXiv API on 2026-11-09}
111+}
112+
113+@misc{geiger2023causal,
114+ title = {{Causal Abstraction: A Theoretical Foundation for Mechanistic Interpretability}},
115+ author = {Atticus Geiger and Duligur Ibeling and Amir Zur and Maheep Chaudhary and Sonakshi Chauhan and Jing Huang and Aryaman Arora and Zhengxuan Wu and Noah Goodman and Christopher Potts and Thomas Icard},
116+ year = {2023},
117+ month = {1},
118+ eprint = {2301.04709},
119+ archivePrefix= {arXiv},
120+ primaryClass = {cs.LG},
121+ url = {https://arxiv.org/abs/2301.04709},
122+ note = {arXiv:2301.04709; metadata verified via arXiv API on 2026-11-09}
123+}
124+
125+@misc{geiger2023finding,
126+ title = {{Finding Alignments Between Interpretable Causal Variables and Distributed Neural Representations}},
127+ author = {Atticus Geiger and Zhengxuan Wu and Christopher Potts and Thomas Icard and Noah D. Goodman},
128+ year = {2023},
129+ month = {3},
130+ eprint = {2303.02536},
131+ archivePrefix= {arXiv},
132+ primaryClass = {cs.LG},
133+ url = {https://arxiv.org/abs/2303.02536},
134+ note = {arXiv:2303.02536; metadata verified via arXiv API on 2026-11-09}
135+}
136+
137+@misc{gu2025beyond,
138+ title = {{Beyond Neural Incompatibility: Cross-Scale Knowledge Transfer in Language Models through Latent Semantic Alignment}},
139+ author = {Jian Gu and Aldeida Aleti and Chunyang Chen and Hongyu Zhang},
140+ year = {2025},
141+ month = {10},
142+ eprint = {2510.24208},
143+ archivePrefix= {arXiv},
144+ primaryClass = {cs.LG},
145+ url = {https://arxiv.org/abs/2510.24208},
146+ note = {arXiv:2510.24208; metadata verified via arXiv API on 2026-11-09}
147+}
148+
149+@misc{gurnee2023finding,
150+ title = {{Finding Neurons in a Haystack: Case Studies with Sparse Probing}},
151+ author = {Wes Gurnee and Neel Nanda and Matthew Pauly and Katherine Harvey and Dmitrii Troitskii and Dimitris Bertsimas},
152+ year = {2023},
153+ month = {5},
154+ eprint = {2305.01610},
155+ archivePrefix= {arXiv},
156+ primaryClass = {cs.LG},
157+ url = {https://arxiv.org/abs/2305.01610},
158+ note = {arXiv:2305.01610; metadata verified via arXiv API on 2026-11-09}
159+}
160+
161+@misc{hendel2023context,
162+ title = {{In-Context Learning Creates Task Vectors}},
163+ author = {Roee Hendel and Mor Geva and Amir Globerson},
164+ year = {2023},
165+ month = {10},
166+ eprint = {2310.15916},
167+ archivePrefix= {arXiv},
168+ primaryClass = {cs.LG},
169+ url = {https://arxiv.org/abs/2310.15916},
170+ note = {arXiv:2310.15916; metadata verified via arXiv API on 2026-11-09}
171+}
172+
173+@misc{hernandez2023linearity,
174+ title = {{Linearity of Relation Decoding in Transformer Language Models}},
175+ author = {Evan Hernandez and Arnab Sen Sharma and Tal Haklay and Kevin Meng and Martin Wattenberg and Jacob Andreas and Yonatan Belinkov and David Bau},
176+ year = {2023},
177+ month = {8},
178+ eprint = {2308.09124},
179+ archivePrefix= {arXiv},
180+ primaryClass = {cs.LG},
181+ url = {https://arxiv.org/abs/2308.09124},
182+ note = {arXiv:2308.09124; metadata verified via arXiv API on 2026-11-09}
183+}
184+
185+@misc{huh2024platonic,
186+ title = {{The Platonic Representation Hypothesis}},
187+ author = {Minyoung Huh and Brian Cheung and Tongzhou Wang and Phillip Isola},
188+ year = {2024},
189+ month = {5},
190+ eprint = {2405.07987},
191+ archivePrefix= {arXiv},
192+ primaryClass = {cs.LG},
193+ url = {https://arxiv.org/abs/2405.07987},
194+ note = {arXiv:2405.07987; metadata verified via arXiv API on 2026-11-09}
195+}
196+
197+@misc{ilharco2022editing,
198+ title = {{Editing Models with Task Arithmetic}},
199+ author = {Gabriel Ilharco and Marco Tulio Ribeiro and Mitchell Wortsman and Suchin Gururangan and Ludwig Schmidt and Hannaneh Hajishirzi and Ali Farhadi},
200+ year = {2022},
201+ month = {12},
202+ eprint = {2212.04089},
203+ archivePrefix= {arXiv},
204+ primaryClass = {cs.LG},
205+ url = {https://arxiv.org/abs/2212.04089},
206+ note = {arXiv:2212.04089; metadata verified via arXiv API on 2026-11-09}
207+}
208+
209+@misc{kim2026one,
210+ title = {{One Adapter Pair per Model: A Universal Activation Interface for Language Models}},
211+ author = {Su-Hyeon Kim and Jiwan Mun and Yo-Sub Han},
212+ year = {2026},
213+ month = {8},
214+ eprint = {2608.09521},
215+ archivePrefix= {arXiv},
216+ primaryClass = {cs.LG},
217+ url = {https://arxiv.org/abs/2608.09521},
218+ note = {arXiv:2608.09521; metadata verified via arXiv API on 2026-11-09}
219+}
220+
221+@misc{kornblith2019similarity,
222+ title = {{Similarity of Neural Network Representations Revisited}},
223+ author = {Simon Kornblith and Mohammad Norouzi and Honglak Lee and Geoffrey Hinton},
224+ year = {2019},
225+ month = {5},
226+ eprint = {1905.00414},
227+ archivePrefix= {arXiv},
228+ primaryClass = {cs.LG},
229+ url = {https://arxiv.org/abs/1905.00414},
230+ note = {arXiv:1905.00414; metadata verified via arXiv API on 2026-11-09}
231+}
232+
233+@misc{lan2024quantifying,
234+ title = {{Quantifying Feature Space Universality Across Large Language Models via Sparse Autoencoders}},
235+ author = {Michael Lan and Philip Torr and Austin Meek and Ashkan Khakzar and David Krueger and Fazl Barez},
236+ year = {2024},
237+ month = {10},
238+ eprint = {2410.06981},
239+ archivePrefix= {arXiv},
240+ primaryClass = {cs.LG},
241+ url = {https://arxiv.org/abs/2410.06981},
242+ note = {arXiv:2410.06981; metadata verified via arXiv API on 2026-11-09}
243+}
244+
245+@misc{lee2025shared,
246+ title = {{Shared Global and Local Geometry of Language Model Embeddings}},
247+ author = {Andrew Lee and Melanie Weber and Fernanda Viégas and Martin Wattenberg},
248+ year = {2025},
249+ month = {3},
250+ eprint = {2503.21073},
251+ archivePrefix= {arXiv},
252+ primaryClass = {cs.LG},
253+ url = {https://arxiv.org/abs/2503.21073},
254+ note = {arXiv:2503.21073; metadata verified via arXiv API on 2026-11-09}
255+}
256+
257+@misc{li2023inference,
258+ title = {{Inference-Time Intervention: Eliciting Truthful Answers from a Language Model}},
259+ author = {Kenneth Li and Oam Patel and Fernanda Viégas and Hanspeter Pfister and Martin Wattenberg},
260+ year = {2023},
261+ month = {6},
262+ eprint = {2306.03341},
263+ archivePrefix= {arXiv},
264+ primaryClass = {cs.LG},
265+ url = {https://arxiv.org/abs/2306.03341},
266+ note = {arXiv:2306.03341; metadata verified via arXiv API on 2026-11-09}
267+}
268+
269+@misc{lo2024large,
270+ title = {{Large Language Models Relearn Removed Concepts}},
271+ author = {Michelle Lo and Shay B. Cohen and Fazl Barez},
272+ year = {2024},
273+ month = {1},
274+ eprint = {2401.01814},
275+ archivePrefix= {arXiv},
276+ primaryClass = {cs.LG},
277+ url = {https://arxiv.org/abs/2401.01814},
278+ note = {arXiv:2401.01814; metadata verified via arXiv API on 2026-11-09}
279+}
280+
281+@misc{meng2022locating,
282+ title = {{Locating and Editing Factual Associations in GPT}},
283+ author = {Kevin Meng and David Bau and Alex Andonian and Yonatan Belinkov},
284+ year = {2022},
285+ month = {2},
286+ eprint = {2202.05262},
287+ archivePrefix= {arXiv},
288+ primaryClass = {cs.LG},
289+ url = {https://arxiv.org/abs/2202.05262},
290+ note = {arXiv:2202.05262; metadata verified via arXiv API on 2026-11-09}
291+}
292+
293+@misc{minixhofer2024zero,
294+ title = {{Zero-Shot Tokenizer Transfer}},
295+ author = {Benjamin Minixhofer and Edoardo Maria Ponti and Ivan Vulić},
296+ year = {2024},
297+ month = {5},
298+ eprint = {2405.07883},
299+ archivePrefix= {arXiv},
300+ primaryClass = {cs.LG},
301+ url = {https://arxiv.org/abs/2405.07883},
302+ note = {arXiv:2405.07883; metadata verified via arXiv API on 2026-11-09}
303+}
304+
305+@misc{nasirisarvi2025sparc,
306+ title = {{SPARC: Concept-Aligned Sparse Autoencoders for Cross-Model and Cross-Modal Interpretability}},
307+ author = {Ali Nasiri-Sarvi and Hassan Rivaz and Mahdi S. Hosseini},
308+ year = {2025},
309+ month = {7},
310+ eprint = {2507.06265},
311+ archivePrefix= {arXiv},
312+ primaryClass = {cs.LG},
313+ url = {https://arxiv.org/abs/2507.06265},
314+ note = {arXiv:2507.06265; metadata verified via arXiv API on 2026-11-09}
315+}
316+
317+@misc{ognev2026sharedsae,
318+ title = {{SharedSAE: One Feature Dictionary Across Language Models}},
319+ author = {Daniil Ognev and Célian Vasson and Lijie Hu and Kentaro Inui and Benjamin Heinzerling},
320+ year = {2026},
321+ month = {9},
322+ eprint = {2609.04344},
323+ archivePrefix= {arXiv},
324+ primaryClass = {cs.LG},
325+ url = {https://arxiv.org/abs/2609.04344},
326+ note = {arXiv:2609.04344; metadata verified via arXiv API on 2026-11-09}
327+}
328+
329+@misc{panigrahi2023task,
330+ title = {{Task-Specific Skill Localization in Fine-tuned Language Models}},
331+ author = {Abhishek Panigrahi and Nikunj Saunshi and Haoyu Zhao and Sanjeev Arora},
332+ year = {2023},
333+ month = {2},
334+ eprint = {2302.06600},
335+ archivePrefix= {arXiv},
336+ primaryClass = {cs.LG},
337+ url = {https://arxiv.org/abs/2302.06600},
338+ note = {arXiv:2302.06600; metadata verified via arXiv API on 2026-11-09}
339+}
340+
341+@misc{raghu2017svcca,
342+ title = {{SVCCA: Singular Vector Canonical Correlation Analysis for Deep Learning Dynamics and Interpretability}},
343+ author = {Maithra Raghu and Justin Gilmer and Jason Yosinski and Jascha Sohl-Dickstein},
344+ year = {2017},
345+ month = {6},
346+ eprint = {1706.05806},
347+ archivePrefix= {arXiv},
348+ primaryClass = {cs.LG},
349+ url = {https://arxiv.org/abs/1706.05806},
350+ note = {arXiv:1706.05806; metadata verified via arXiv API on 2026-11-09}
351+}
352+
353+@misc{sinii2025small,
354+ title = {{Small Vectors, Big Effects: A Mechanistic Study of RL-Induced Reasoning via Steering Vectors}},
355+ author = {Viacheslav Sinii and Nikita Balagansky and Gleb Gerasimov and Daniil Laptev and Yaroslav Aksenov and Vadim Kurochkin and Alexey Gorbatovski and Boris Shaposhnikov and Daniil Gavrilov},
356+ year = {2025},
357+ month = {9},
358+ eprint = {2509.06608},
359+ archivePrefix= {arXiv},
360+ primaryClass = {cs.LG},
361+ url = {https://arxiv.org/abs/2509.06608},
362+ note = {arXiv:2509.06608; metadata verified via arXiv API on 2026-11-09}
363+}
364+
365+@misc{sun2024massive,
366+ title = {{Massive Activations in Large Language Models}},
367+ author = {Mingjie Sun and Xinlei Chen and J. Zico Kolter and Zhuang Liu},
368+ year = {2024},
369+ month = {2},
370+ eprint = {2402.17762},
371+ archivePrefix= {arXiv},
372+ primaryClass = {cs.LG},
373+ url = {https://arxiv.org/abs/2402.17762},
374+ note = {arXiv:2402.17762; metadata verified via arXiv API on 2026-11-09}
375+}
376+
377+@misc{thasarathan2025universal,
378+ title = {{Universal Sparse Autoencoders: Interpretable Cross-Model Concept Alignment}},
379+ author = {Harrish Thasarathan and Julian Forsyth and Thomas Fel and Matthew Kowal and Konstantinos G. Derpanis},
380+ year = {2025},
381+ month = {2},
382+ eprint = {2502.03714},
383+ archivePrefix= {arXiv},
384+ primaryClass = {cs.LG},
385+ url = {https://arxiv.org/abs/2502.03714},
386+ note = {arXiv:2502.03714; metadata verified via arXiv API on 2026-11-09}
387+}
388+
389+@misc{todd2023function,
390+ title = {{Function Vectors in Large Language Models}},
391+ author = {Eric Todd and Millicent L. Li and Arnab Sen Sharma and Aaron Mueller and Byron C. Wallace and David Bau},
392+ year = {2023},
393+ month = {10},
394+ eprint = {2310.15213},
395+ archivePrefix= {arXiv},
396+ primaryClass = {cs.LG},
397+ url = {https://arxiv.org/abs/2310.15213},
398+ note = {arXiv:2310.15213; metadata verified via arXiv API on 2026-11-09}
399+}
400+
401+@misc{turner2023steering,
402+ title = {{Steering Language Models With Activation Engineering}},
403+ author = {Alexander Matt Turner and Lisa Thiergart and Gavin Leech and David Udell and Juan J. Vazquez and Ulisse Mini and Monte MacDiarmid},
404+ year = {2023},
405+ month = {8},
406+ eprint = {2308.10248},
407+ archivePrefix= {arXiv},
408+ primaryClass = {cs.LG},
409+ url = {https://arxiv.org/abs/2308.10248},
410+ note = {arXiv:2308.10248; metadata verified via arXiv API on 2026-11-09}
411+}
412+
413+@misc{vig2020causal,
414+ title = {{Causal Mediation Analysis for Interpreting Neural NLP: The Case of Gender Bias}},
415+ author = {Jesse Vig and Sebastian Gehrmann and Yonatan Belinkov and Sharon Qian and Daniel Nevo and Simas Sakenis and Jason Huang and Yaron Singer and Stuart Shieber},
416+ year = {2020},
417+ month = {4},
418+ eprint = {2004.12265},
419+ archivePrefix= {arXiv},
420+ primaryClass = {cs.LG},
421+ url = {https://arxiv.org/abs/2004.12265},
422+ note = {arXiv:2004.12265; metadata verified via arXiv API on 2026-11-09}
423+}
424+
425+@misc{wang2022interpretability,
426+ title = {{Interpretability in the Wild: a Circuit for Indirect Object Identification in GPT-2 small}},
427+ author = {Kevin Wang and Alexandre Variengien and Arthur Conmy and Buck Shlegeris and Jacob Steinhardt},
428+ year = {2022},
429+ month = {11},
430+ eprint = {2211.00593},
431+ archivePrefix= {arXiv},
432+ primaryClass = {cs.LG},
433+ url = {https://arxiv.org/abs/2211.00593},
434+ note = {arXiv:2211.00593; metadata verified via arXiv API on 2026-11-09}
435+}
436+
437+@misc{yadav2023ties,
438+ title = {{TIES-Merging: Resolving Interference When Merging Models}},
439+ author = {Prateek Yadav and Derek Tam and Leshem Choshen and Colin Raffel and Mohit Bansal},
440+ year = {2023},
441+ month = {6},
442+ eprint = {2306.01708},
443+ archivePrefix= {arXiv},
444+ primaryClass = {cs.LG},
445+ url = {https://arxiv.org/abs/2306.01708},
446+ note = {arXiv:2306.01708; metadata verified via arXiv API on 2026-11-09}
447+}
448+
449+@misc{zhang2026negative,
450+ title = {{A Negative Result on Cross-Model Activation Transfer in a Pythia Multi-Hop Setting}},
451+ author = {Peiyan Zhang and Jason Xin},
452+ year = {2026},
453+ month = {6},
454+ eprint = {2606.03280},
455+ archivePrefix= {arXiv},
456+ primaryClass = {cs.LG},
457+ url = {https://arxiv.org/abs/2606.03280},
458+ note = {arXiv:2606.03280; metadata verified via arXiv API on 2026-11-09}
459+}
460+
461+@misc{zou2023representation,
462+ title = {{Representation Engineering: A Top-Down Approach to AI Transparency}},
463+ author = {Andy Zou and Long Phan and Sarah Chen and James Campbell and Phillip Guo and Richard Ren and Alexander Pan and Xuwang Yin and Mantas Mazeika and Ann-Kathrin Dombrowski and Shashwat Goel and Nathaniel Li and Michael J. Byun and Zifan Wang and Alex Mallen and Steven Basart and Sanmi Koyejo and Dawn Song and Matt Fredrikson and J. Zico Kolter and Dan Hendrycks},
464+ year = {2023},
465+ month = {10},
466+ eprint = {2310.01405},
467+ archivePrefix= {arXiv},
468+ primaryClass = {cs.LG},
469+ url = {https://arxiv.org/abs/2310.01405},
470+ note = {arXiv:2310.01405; metadata verified via arXiv API on 2026-11-09}
471+}
added paper/sections/conclusion.tex +2 −0
@@ -0,0 +1,2 @@
1+\section{Conclusion}\label{sec:conclusion}
2+[PLACEHOLDER --- written after confirmatory results.]
added paper/sections/discussion.tex +3 −0
@@ -0,0 +1,3 @@
1+\section{Discussion}\label{sec:discussion}
2+[PLACEHOLDER --- what the results establish, what they do not, and alternative explanations; written after
3+confirmatory analysis.]
added paper/sections/experimental_design.tex +41 −0
@@ -0,0 +1,41 @@
1+\section{Experimental Design}\label{sec:design}
2+
3+\subsection{Micro-world A (OPSEQ)}
4+Programs $\langle \mathrm{bos}\rangle\, v_0\, o_1 \dots o_k\, {=}\, r_1 \dots r_k$ apply $k\in[1,5]$
5+invented operators (eight maps on $\mathbb{Z}_{11}$) to $v_0$ and emit the execution trace. The target
6+operator $\Omega$ is excluded from the recipient's training distribution; two adjacent operator pairs are
7+held out from all training distributions; length $6$--$7$ programs form a length-OOD stratum. Splits are
8+generated from distinct seeds, validated by exact recomputation, hashed and checked for item-level
9+disjointness. Design decision D2 (scratchpad instead of single-token answers) and its consequence for
10+difficulty (\texttt{trace\_stride}) are recorded in the decision log.
11+
12+\subsection{Micro-worlds B and C}
13+B: execution of a fixed random DFA over an invented alphabet (state trace). C: context-dependent suffix
14+selection on pseudo-word stems. [Baselines pending.]
15+
16+\subsection{Models}
17+Tier~0: decoder-only transformers (4 layers, $d{=}128$, 4 heads, $0.80$\,M parameters) trained from
18+scratch with teacher forcing on trace tokens, three seeds per role. Tier~1 (planned): Qwen2.5-1.5B/3B-Instruct
19+and Llama-3.2-3B-Instruct in bf16 under MLX with wrapped layers. [Tier~2 conditional.]
20+
21+\subsection{Hook semantics}
22+Hook points expose embeddings, per-layer residual streams (pre/mid/post), per-head attention outputs
23+before the output projection and MLP outputs. With no registered intervention, or when disabled, the hook
24+returns the input tensor object itself; bitwise equality of logits is asserted in tests and in every run
25+manifest (\texttt{noop}).
26+
27+\subsection{Localization}
28+Interchange interventions patch clean activations into a corrupted twin (same program with $\Omega$
29+replaced) at every (layer, position) and family; zero-ablation and per-head patching complete the sweep.
30+A sham baseline (corrupted run scored on the clean answer) and repeat/seed reproducibility are reported.
31+
32+\subsection{Transplant families and controls}
33+[To be executed.] Activation graft, subspace graft, circuit graft, weight graft, adapter graft, persistent
34+graft; controls: unmodified models, sham, norm-matched random, unrelated-skill donor, shuffled-token and
35+shuffled-layer grafts, sham mapping, few-shot prompting, retrieval, steering, matched-size adapter
36+fine-tuning, distillation (budget permitting).
37+
38+\subsection{Statistics}
39+Items are nested in programs (clusters); CIs are cluster bootstraps over programs; seeds form a second
40+level; paired contrasts use identical items; Holm correction across graft families; exploratory sweeps are
41+separated from confirmatory contrasts (preregistration v0.1, freeze pending).
added paper/sections/introduction.tex +25 −0
@@ -0,0 +1,25 @@
1+\section{Introduction}\label{sec:intro}
2+
3+\paragraph{Status.} This is a protocol draft. No empirical claim appears in this document unless it is
4+accompanied by a run identifier from the experiment registry (\texttt{experiments/registry/runs.jsonl}).
5+
6+Large language models trained independently appear to converge toward similar linear representation
7+geometries \citep{huh2024platonic,kornblith2019similarity,raghu2017svcca}, and affine maps between the
8+residual streams of related models transfer probes, sparse-autoencoder dictionaries and steering vectors
9+\citep{chen2025transferring,kim2026one,agarwal2026cross}. Whether such maps can carry a
10+\emph{capability}---a reusable computation that the recipient could not perform before---is open. The one
11+controlled attempt we are aware of reports a null: a linear bridge between two Pythia models aligns hidden
12+states almost perfectly yet does not improve multi-hop answering when injected \citep{zhang2026negative}.
13+
14+We study this question with a design in which (i) the donor's capability is \emph{causally localized}
15+by interchange interventions before any transfer is attempted, (ii) the recipient's lack of the capability
16+is \emph{manufactured} (Tier~0) or repeatedly measured (Tier~$\geq$1), and (iii) every transfer is compared
17+with matched sham, random and unrelated-donor interventions. The word \emph{organ} is a metaphor for a
18+bounded, causally identified subsystem; it carries no biological or phenomenal claim.
19+
20+\paragraph{Contributions (planned; to be rewritten after results).}
21+(1) An operational theory of transplantable subsystems with explicit estimands
22+(\S\ref{sec:theory}); (2) a controlled micro-world and an open-weight experimental framework with exact
23+hook semantics (\S\ref{sec:design}); (3) preregistered tests of localization, sufficiency, necessity,
24+selectivity, persistence and portability (\S\ref{sec:results}--\S\ref{sec:robustness}); (4) a map of
25+failure boundaries (\S\ref{sec:discussion}).
added paper/sections/limitations.tex +7 −0
@@ -0,0 +1,7 @@
1+\section{Limitations, Ethics, and Reproducibility}\label{sec:limitations}
2+Known limitations of the design at protocol stage: the Tier-0 capability is a per-step map once the trace is
3+visible (risk R1), so Tier-0 evidence cannot by itself support claims about multi-step reasoning; Tier-1
4+checkpoints are instruction-tuned and their training data are not controlled; cross-tokenizer transfer is
5+expected to fail without a tokenizer bridge. Ethics: synthetic data only; open-weight models under their
6+licenses; no derived weights are released without license verification. Reproducibility:
7+\texttt{docs/REPRODUCIBILITY.md}; all runs carry manifests with commit, config hash, data hashes and seeds.
added paper/sections/mechanistic_analysis.tex +2 −0
@@ -0,0 +1,2 @@
1+\section{Mechanistic Analysis}\label{sec:mechanism}
2+[PLACEHOLDER --- necessity, sufficiency, rescue, specificity and limits; pending Sprint 5.]
added paper/sections/operational_theory.tex +35 −0
@@ -0,0 +1,35 @@
1+\section{Operational Theory}\label{sec:theory}
2+
3+\subsection{Objects}
4+Let $D$ and $R$ be donor and recipient models, $\mathcal{T}$ a task distribution with a target stratum
5+and control strata, $S_D$ a candidate subsystem of $D$ (a set of layers, heads, MLP units, residual
6+subspaces or sparse features), $A$ an alignment operator mapping donor-side quantities into recipient
7+coordinates, and $I$ an intervention operator. The transplanted recipient is $R' = I(R, A(S_D))$.
8+
9+\subsection{Estimands}
10+With $Q_T(\cdot)$ the exact-match score on held-out target items and $Q_c(\cdot)$ on control stratum $c$:
11+\begin{align}
12+\Delta_T &= Q_T(R') - Q_T(R), \qquad
13+\Delta_T^{\mathrm{adj}} = \Delta_T - \big[Q_T(R_{\mathrm{sham}}) - Q_T(R)\big], \label{eq:adj}\\
14+\rho_c &= Q_c(R')/Q_c(R) \ \text{(if } Q_c(R)\geq 0.5\text{, else report } \Delta_c), \qquad
15+N_D = Q_T(D) - Q_T(D \setminus S_D). \label{eq:ret}
16+\end{align}
17+$R_{\mathrm{sham}}$ is the matched sham intervention: same positions, layers, dimensionality and activation
18+norm, with donor content replaced by the control defined for each graft family.
19+
20+\subsection{Definitions}
21+\emph{Candidate organ}: a component whose perturbation produces repeatable, task-selective causal effects
22+in $D$ (necessity $N_D$ and restored-answer rate under interchange). \emph{Transplant}: a predeclared
23+intervention $I$ that injects, replaces, composes or maps $A(S_D)$ into $R$. \emph{Acquisition}:
24+$\Delta_T^{\mathrm{adj}}$ above the smallest effect of interest on held-out items, with retention and no
25+leakage. \emph{Selectivity}: target improvement relative to control strata and matched interventions.
26+\emph{Persistence}: single-token, within-sequence, cross-prompt, or saved-checkpoint, reported separately.
27+\emph{Portability}: replication across seeds, prompts, families, sizes and tokenizers.
28+
29+\subsection{What is a theorem and what is a conjecture}
30+A linear-stitching sufficiency statement (if $R$'s upper layers are an exact causal abstraction of $D$'s at
31+the stitched layer, interchange interventions commute with the map) follows from the causal-abstraction
32+framework of \citet{geiger2023causal}; we restate it with its assumptions in Appendix~A. Everything about
33+real models is empirical. \textbf{Conjecture (transfer boundary).} Transfer by affine activation grafts
34+fails when the recipient's downstream computation does not read the mapped subspace; this is the default
35+hypothesis motivated by \citet{zhang2026negative} and is tested, not assumed.
added paper/sections/related_work.tex +37 −0
@@ -0,0 +1,37 @@
1+\section{Related Work}\label{sec:related}
2+
3+\paragraph{Representation similarity and stitching.} SVCCA \citep{raghu2017svcca}, CKA
4+\citep{kornblith2019similarity}, the Platonic Representation Hypothesis \citep{huh2024platonic} and
5+multi-way alignment \citep{achara2026multi} quantify cross-model similarity; stitching
6+\citep{bansal2021revisiting} turns similarity into a functional test. For language models, affine
7+residual-stream maps transfer SAEs, probes and steering vectors across sizes within a family
8+\citep{chen2025transferring}, and a shared ``activation bus'' lets one model's intermediate activation be
9+consumed by another's upper layers \citep{kim2026one}. Shared geometry of unembeddings supports steering
10+transfer \citep{lee2025shared}. Our work differs in starting from a causally identified component and in
11+measuring acquisition against matched controls rather than representational agreement.
12+
13+\paragraph{Cross-model feature dictionaries and steering transfer.} Universal and shared SAEs
14+\citep{lan2024quantifying,thasarathan2025universal,nasirisarvi2025sparc,ognev2026sharedsae} build concept
15+spaces across models; cross-architecture steering transfer \citep{agarwal2026cross} and refusal-circuit
16+transfer \citep{cristofano2026universal} move \emph{behavioural directions}. RL-induced reasoning vectors
17+transfer only within a family \citep{sinii2025small}. These concern concept- or behaviour-level control;
18+we test capability acquisition on verifiable tasks.
19+
20+\paragraph{Capability transfer and merging.} Skill localization by grafting \citep{panigrahi2023task}
21+operates within one model. Activation-guided pruning and merging across scales \citep{fan2026training} and
22+latent semantic alignment with layer-wise training \citep{gu2025beyond} transfer knowledge across sizes;
23+task arithmetic \citep{ilharco2022editing}, TIES \citep{yadav2023ties} and re-basin \citep{ainsworth2022git}
24+merge weights sharing an initialization. Tokenizer transfer \citep{minixhofer2024zero} addresses the
25+vocabulary boundary.
26+
27+\paragraph{Causal methods.} We rely on causal mediation and tracing \citep{vig2020causal,meng2022locating},
28+interchange interventions and causal abstraction \citep{geiger2023causal,geiger2023finding}, circuit
29+discovery \citep{wang2022interpretability,conmy2023towards}, function and task vectors
30+\citep{todd2023function,hendel2023context}, activation steering
31+\citep{turner2023steering,li2023inference,zou2023representation} and sparse dictionaries
32+\citep{cunningham2023sparse,gurnee2023finding}. Massive activations \citep{sun2024massive} and concept
33+relearning after removal \citep{lo2024large} inform our ablation and persistence tests.
34+
35+\paragraph{Closest negative result.} \citet{zhang2026negative} find that a well-fitting linear bridge does
36+not yield useful causal communication inside the receiver. We treat this as the default expectation and
37+design controls that can distinguish ``no transfer'' from ``transfer masked by misplacement''.
added paper/sections/results.tex +61 −0
@@ -0,0 +1,61 @@
1+\section{Results}\label{sec:results}
2+
3+\paragraph{Scope.} Only the exploratory Tier-0 pilot has been executed (run id
4+\texttt{pilot\_tier0\_opseq\_451ef5087182}, status \emph{completed}, 2026-11-09; manifest in
5+\texttt{experiments/manifests/}). It establishes Gates~B and~C and the within-donor localization (RQ1).
6+\textbf{No transplant has been performed; nothing below is a transfer result.} Numbers are copied from
7+\texttt{experiments/summaries/pilot\_tier0\_opseq\_451ef5087182/results.json}; figures are regenerated by
8+\texttt{scripts/build\_figures.py}.
9+
10+\subsection{Gate B: a manufactured, reproducible capability gap}
11+Three donor seeds trained on all eight operators and three recipient seeds trained with $\Omega$ excluded
12+(3\,000 steps, $0.80$\,M parameters each) reach dev accuracy $1.000$. On the held-out target stratum
13+(programs containing $\Omega$; $n{=}1\,000$ items in $\approx 900$ programs) final-answer exact match is
14+$1.000$ (sd $0.000$) for donors and $0.092$ (sd $0.053$) for recipients, against a majority-class chance of
15+$0.105$; the accuracy of the token emitted at the first $\Omega$ step is $1.000$ versus $0.062$. On the
16+control stratum (no $\Omega$) both roles score $1.000$. The paired gap on identical items (seed~0) is
17+$+0.847$ with cluster-bootstrap 95\% interval $[+0.825,\,+0.870]$ (2\,000 draws over programs). Both roles
18+fail the length-OOD stratum ($0.086$ vs $0.091$), a known limitation of absolute position embeddings that
19+we treat as a stratum, not a target (Figure~\ref{fig:gap}).
20+
21+\begin{figure}[t]\centering
22+\includegraphics[width=0.62\textwidth]{figures/fig_gap_tier0.pdf}
23+\caption{Tier-0 pilot: final-answer exact match by evaluation stratum for donor (all operators) and
24+recipient ($\Omega$ excluded), mean over three seeds (bars) with per-seed values (open markers) and the
25+majority-class chance level (grey dashes). Run \texttt{pilot\_tier0\_opseq\_451ef5087182}.}
26+\label{fig:gap}\end{figure}
27+
28+\subsection{Gate C: hooks are exact}
29+For all six trained models, logits computed with (i) no hooks, (ii) capture hooks on all 27 hook points,
30+and (iii) a modifying hook registered but disabled are bitwise identical on 256 items, and repeated runs
31+are bitwise identical on MPS; the same modifying hook, when enabled, changes the output.
32+
33+\subsection{RQ1: where the donor executes $\Omega$}
34+On 490 clean/corrupted pairs (length-3 programs with $\Omega$ at index~1; corrupted twin replaces $\Omega$
35+by another operator so that $r_2$ differs; identical teacher-forced prefix up to $r_1$), the clean run
36+scores $1.000$ and the sham (corrupted run scored on the clean answer) $0.000$ in every seed. Patching the
37+clean activation into the corrupted run restores the clean answer at rate $1.00$ when the residual stream
38+at the \emph{read position} $r_1$ is patched at any layer, and $0.00$ at every other position including the
39+$\Omega$ token itself (Figure~\ref{fig:loc}). Within the read position, the layer-0 attention output is
40+sufficient ($1.00$; layers 1--3: $0.00$) and layer-0--2 MLP outputs are partially sufficient ($1.00$,
41+$0.90$, $0.83$; layer 3: $0.17$). Zero-ablation at $r_1$ reduces accuracy to $0.04$--$0.09$ for the residual
42+stream, to $0.13$ for layer-0 attention and $0.21$ for the layer-0 MLP, with no effect elsewhere. Head-level
43+patching at $r_1$ attributes most of the effect to layer-0 head~0 ($0.62$; heads 1--2: $0.16$, $0.04$).
44+Repeating a sweep changes no entry ($\max|\Delta|{=}0$) and the flip-rate heatmaps correlate across seeds
45+at $r \geq 0.96$ (minimum pair; $1.00$ for residual and attention families).
46+
47+\begin{figure}[t]\centering
48+\includegraphics[width=\textwidth]{figures/fig_localization_patching_tier0.pdf}
49+\caption{Within-donor interchange interventions (mean of three seeds): restored-answer rate when the
50+clean activation at (layer, position) is patched into the corrupted run, for the post-block residual
51+stream, the attention output and the MLP output. Columns are token positions of the length-3 program; the
52+read position $r_1$ predicts $r_2$, the $\Omega$ step. Sham rate $0.00$.}
53+\label{fig:loc}\end{figure}
54+
55+\paragraph{Observed versus interpreted.} Observed: the $\Omega$ computation is executed at the read
56+position by layer-0 attention (moving the operator identity from the $\Omega$ token) and layer-0--2 MLPs.
57+Interpretation, to be tested in Sprint~3: the candidate organ for an activation graft is the layer-0
58+output at the read position, whose content is the (operator, value) pair; a transplant must convey the
59+$\Omega$-specific part of that content without disturbing the value part. Patching the $\Omega$ token
60+restored nothing because the sweep did not include the embedding/pre-layer-0 stream read by layer-0
61+attention (decision D6).
added paper/sections/robustness.tex +3 −0
@@ -0,0 +1,3 @@
1+\section{Robustness and Failure Modes}\label{sec:robustness}
2+[PLACEHOLDER --- sweeps over strength, layer, position, dimensionality, pair, prompt format, context
3+length, temperature, difficulty and quantization; pending Sprint 5--6.]
added paper/sections/titlepage.tex +77 −0
@@ -0,0 +1,77 @@
1+% ============================================================================
2+% Title Page
3+% ============================================================================
4+\thispagestyle{empty}
5+
6+\begin{center}
7+
8+% --- Logo ---
9+\includegraphics[width=3.5cm]{uq_logo.jpg}
10+
11+\vspace{0.6cm}
12+
13+{\footnotesize\textsc{Université du Québec en Outaouais}}\\[0.15cm]
14+{\footnotesize\textsc{Département des sciences administratives}}
15+
16+\vspace{0.8cm}
17+
18+{\footnotesize\textsc{Working Paper No.~\WPnumber}}
19+
20+\vspace{1.2cm}
21+
22+% --- Title ---
23+{\LARGE\bfseries \WPtitle\par}
24+
25+\ifx\WPsubtitle\empty\else
26+ \vspace{0.3cm}
27+ {\large\itshape \WPsubtitle\par}
28+\fi
29+
30+\vspace{1.2cm}
31+
32+% --- Author ---
33+{\large \WPauthor}\\[0.3cm]
34+{\normalsize \WPaffiliation}\\[0.15cm]
35+{\normalsize \href{mailto:\WPemail}{\WPemail}}\\[0.15cm]
36+{\small \WPaddress}
37+
38+\vspace{0.8cm}
39+
40+% --- Date & Version ---
41+{\normalsize \WPdate}\\[0.1cm]
42+{\small Version~\WPversion}
43+
44+\end{center}
45+
46+\vfill
47+
48+\newpage
49+
50+% ============================================================================
51+% Abstract Page
52+% ============================================================================
53+\thispagestyle{empty}
54+
55+\vspace*{1cm}
56+
57+\noindent\rule{\textwidth}{0.4pt}
58+\vspace{0.3cm}
59+
60+\noindent\textbf{Abstract}
61+
62+\vspace{0.15cm}
63+
64+\noindent\WPabstract
65+
66+\vspace{0.4cm}
67+
68+\noindent\textbf{Keywords:} \WPkeywords
69+
70+\vspace{0.15cm}
71+
72+\noindent\textbf{JEL Classification:} \WPjel
73+
74+\vspace{0.3cm}
75+\noindent\rule{\textwidth}{0.4pt}
76+
77+\newpage
added paper/submission/.gitkeep +0 −0
added paper/tables/.gitkeep +0 −0
added paper/uq_logo.jpg +0 −0

Binary file not shown.

added pyproject.toml +29 −0
@@ -0,0 +1,29 @@
1+[project]
2+name = "wp_ai_0001"
3+version = "0.1.0"
4+description = "WP-AI-0001 - The Neural Organ Transplant Experiment: causal transfer of cognitive capabilities across local LLMs (experiment harness)"
5+requires-python = ">=3.12"
6+dependencies = [
7+ "numpy>=2.0",
8+ "torch>=2.4",
9+ "pydantic>=2.6",
10+ "scipy>=1.13",
11+ "matplotlib>=3.9",
12+]
13+
14+[project.optional-dependencies]
15+mlx = ["mlx>=0.30", "mlx-lm>=0.28"]
16+hf = ["transformers>=4.45", "safetensors>=0.4", "huggingface_hub>=0.25"]
17+dev = ["pytest>=8"]
18+
19+[build-system]
20+requires = ["setuptools>=68"]
21+build-backend = "setuptools.build_meta"
22+
23+[tool.setuptools.packages.find]
24+where = ["src"]
25+
26+[tool.pytest.ini_options]
27+testpaths = ["tests"]
28+pythonpath = ["src"]
29+addopts = "-q"
added scripts/audit_environment.py +91 −0
@@ -0,0 +1,91 @@
1+#!/usr/bin/env python3
2+"""Environment audit: hardware, Python stack, backends, cached open-weight checkpoints, cluster nodes.
3+
4+Writes docs/ENVIRONMENT_AUDIT.json (and prints a short report). Cluster probing is optional
5+(``--cluster``) and uses the user's existing SSH aliases; nodes that do not answer are reported as
6+unreachable rather than assumed online. No secrets are read or written.
7+"""
8+from __future__ import annotations
9+
10+import argparse
11+import json
12+import os
13+import subprocess
14+import sys
15+from pathlib import Path
16+
17+ROOT = Path(__file__).resolve().parents[1]
18+sys.path.insert(0, str(ROOT / "src"))
19+from wp_ai_0001.instrumentation.provenance import hardware, library_versions, utc_now # noqa: E402
20+
21+NODES = ["M3U96a", "M3U96b", "M2U64", "M4M64a", "M4M64b", "M4M64c", "M5M36", "M4M36", "M4BP48", "m4mm", "m4mn"]
22+REMOTE_CMD = (
23+ 'echo "$(sysctl -n machdep.cpu.brand_string)|$(( $(sysctl -n hw.memsize)/1073741824 ))|'
24+ '$(df -k ~ | tail -1 | awk \'{print int($4/1048576)}\')|$(python3 --version 2>&1 | cut -d" " -f2)|'
25+ '$(python3 -c "import mlx_lm;print(mlx_lm.__version__)" 2>/dev/null || echo none)|'
26+ '$(python3 -c "import torch;print(torch.__version__)" 2>/dev/null || echo none)|'
27+ '$(ls ~/.cache/huggingface/hub 2>/dev/null | grep -c models--)"'
28+)
29+
30+
31+def hf_cache() -> list[dict]:
32+ hub = Path(os.environ.get("HF_HOME", Path.home() / ".cache" / "huggingface")) / "hub"
33+ out = []
34+ if hub.exists():
35+ for d in sorted(hub.glob("models--*")):
36+ size = sum(f.stat().st_size for f in d.rglob("*") if f.is_file())
37+ out.append({"repo": d.name[len("models--"):].replace("--", "/"), "size_gb": round(size / 2**30, 2)})
38+ return out
39+
40+
41+def backends() -> dict:
42+ b: dict = {}
43+ try:
44+ import mlx.core as mx
45+
46+ b["mlx"] = {"version": mx.__version__, "metal": bool(mx.metal.is_available())}
47+ except Exception as e: # noqa: BLE001
48+ b["mlx"] = {"error": repr(e)}
49+ try:
50+ import torch
51+
52+ b["torch"] = {"version": torch.__version__, "mps": bool(torch.backends.mps.is_available()), "cuda": bool(torch.cuda.is_available())}
53+ except Exception as e: # noqa: BLE001
54+ b["torch"] = {"error": repr(e)}
55+ return b
56+
57+
58+def probe_nodes() -> dict:
59+ res = {}
60+ for n in NODES:
61+ try:
62+ out = subprocess.run(["ssh", "-o", "ConnectTimeout=6", "-o", "BatchMode=yes", n, REMOTE_CMD], capture_output=True, text=True, timeout=30)
63+ line = out.stdout.strip().splitlines()[-1] if out.stdout.strip() else ""
64+ if out.returncode != 0 or "|" not in line:
65+ res[n] = {"reachable": False, "error": (out.stderr or out.stdout).strip()[:120]}
66+ continue
67+ chip, mem, free, py, mlxlm, torch_v, hf = line.split("|")
68+ res[n] = {"reachable": True, "chip": chip, "memory_gb": int(mem), "free_disk_gb": int(free), "python": py, "mlx_lm": mlxlm, "torch": torch_v, "hf_models_cached": int(hf)}
69+ except Exception as e: # noqa: BLE001
70+ res[n] = {"reachable": False, "error": repr(e)[:120]}
71+ return res
72+
73+
74+def main() -> None:
75+ ap = argparse.ArgumentParser()
76+ ap.add_argument("--cluster", action="store_true")
77+ args = ap.parse_args()
78+ report = {"utc": utc_now(), "hardware": hardware(), "libraries": library_versions(), "backends": backends(), "hf_cache": hf_cache()}
79+ if args.cluster:
80+ report["cluster"] = probe_nodes()
81+ (ROOT / "docs" / "ENVIRONMENT_AUDIT.json").write_text(json.dumps(report, indent=2, default=str))
82+ print(json.dumps({k: report[k] for k in ("hardware", "backends")}, indent=2, default=str))
83+ print("cached checkpoints:", [m["repo"] for m in report["hf_cache"]])
84+ if args.cluster:
85+ for n, r in report["cluster"].items():
86+ print(f" {n}: {'OK ' + r['chip'] + ' ' + str(r['memory_gb']) + 'GB mlx_lm=' + r['mlx_lm'] if r['reachable'] else 'UNREACHABLE'}")
87+ print("written docs/ENVIRONMENT_AUDIT.json")
88+
89+
90+if __name__ == "__main__":
91+ main()
added scripts/build_figures.py +52 −0
@@ -0,0 +1,52 @@
1+#!/usr/bin/env python3
2+"""Regenerate paper figures from registered run results (no computation, no new data).
3+
4+Usage: python scripts/build_figures.py --run <run_id> [--out paper/figures]
5+Every figure written is listed with the run id and results hash in paper/figures/FIGURE_PROVENANCE.md.
6+"""
7+from __future__ import annotations
8+
9+import argparse
10+import hashlib
11+import json
12+import sys
13+from pathlib import Path
14+
15+ROOT = Path(__file__).resolve().parents[1]
16+sys.path.insert(0, str(ROOT / "src"))
17+sys.path.insert(0, str(ROOT / "scripts"))
18+
19+from wp_ai_0001.config import PilotConfig # noqa: E402
20+from wp_ai_0001.instrumentation.provenance import utc_now # noqa: E402
21+
22+
23+def main() -> None:
24+ ap = argparse.ArgumentParser()
25+ ap.add_argument("--run", required=True)
26+ ap.add_argument("--out", default=str(ROOT / "paper" / "figures"))
27+ args = ap.parse_args()
28+ res_path = ROOT / "experiments" / "summaries" / args.run / "results.json"
29+ man_path = ROOT / "experiments" / "manifests" / f"{args.run}.json"
30+ results = json.loads(res_path.read_text())
31+ manifest = json.loads(man_path.read_text())
32+ if manifest["status"] != "completed":
33+ raise SystemExit(f"run {args.run} has status {manifest['status']}; refusing to build figures")
34+ out = Path(args.out)
35+ out.mkdir(parents=True, exist_ok=True)
36+ if results["run_id"].startswith("pilot_tier0_opseq"):
37+ from run_pilot import make_figures
38+
39+ cfg = PilotConfig.model_validate(manifest["config"])
40+ make_figures(results, out, results["localization"]["positions"], cfg)
41+ else:
42+ raise SystemExit("no figure recipe for this run type")
43+ digest = hashlib.sha256(res_path.read_bytes()).hexdigest()[:16]
44+ prov = out / "FIGURE_PROVENANCE.md"
45+ lines = [l for l in prov.read_text().splitlines() if not l.startswith(f"| {args.run} ")] if prov.exists() else ["# Figure provenance", "", "| run id | results sha256[:16] | built (UTC) | figures |", "|---|---|---|---|"]
46+ lines.append(f"| {args.run} | {digest} | {utc_now()} | " + ", ".join(sorted(p.name for p in out.glob('*.pdf'))) + " |")
47+ prov.write_text("\n".join(lines) + "\n")
48+ print(f"figures written to {out} (provenance updated)")
49+
50+
51+if __name__ == "__main__":
52+ main()
added scripts/build_paper.sh +6 −0
@@ -0,0 +1,6 @@
1+#!/usr/bin/env bash
2+# Build paper/main.pdf with the UQO template Makefile (latexmk). Figures must be built first.
3+set -euo pipefail
4+cd "$(dirname "$0")/../paper"
5+make
6+echo "built paper/main.pdf"
added scripts/run_pilot.py +356 −0
@@ -0,0 +1,356 @@
1+#!/usr/bin/env python3
2+"""Sprint 1-2 pilot (Tier 0, micro-world A / OPSEQ).
3+
4+What this script establishes, with artifacts:
5+ 1. Donor/recipient capability gap on the target operator (Gate B), repeated over seeds.
6+ 2. Hook no-op guarantee on trained models (Gate C): instrumented == uninstrumented, bitwise.
7+ 3. Within-model causal localization of the target operator in the donor (activation patching,
8+ zero-ablation, head patching), with a sham baseline and reproducibility checks (RQ1).
9+
10+No transplant is performed here. Nothing in this script claims transfer.
11+
12+Usage: python scripts/run_pilot.py [--seeds 0 1 2] [--steps 3000] [--device auto] [--quick] [--dry-run]
13+"""
14+from __future__ import annotations
15+
16+import argparse
17+import gzip
18+import json
19+import sys
20+import time
21+from pathlib import Path
22+
23+import numpy as np
24+import torch
25+
26+ROOT = Path(__file__).resolve().parents[1]
27+sys.path.insert(0, str(ROOT / "src"))
28+
29+from wp_ai_0001.config import ModelRoleConfig, OpSeqTaskConfig, PilotConfig, ToyModelConfig, TrainConfig, config_hash # noqa: E402
30+from wp_ai_0001.datasets import opseq # noqa: E402
31+from wp_ai_0001.datasets.common import Split, assert_disjoint, encode_split # noqa: E402
32+from wp_ai_0001.evaluation.metrics import chance_level, evaluate # noqa: E402
33+from wp_ai_0001.instrumentation.provenance import Manifest, make_run_id, utc_now # noqa: E402
34+from wp_ai_0001.localization.patching import ablation_sweep, head_patch_sweep, patch_sweep # noqa: E402
35+from wp_ai_0001.models.hooks import Capture, Zero # noqa: E402
36+from wp_ai_0001.models.toy_transformer import resolve_device # noqa: E402
37+from wp_ai_0001.orchestration import registry # noqa: E402
38+from wp_ai_0001.statistics.bootstrap import cluster_bootstrap_mean, cluster_bootstrap_paired_diff, seeds_summary # noqa: E402
39+from wp_ai_0001.training.train_toy import load as load_model # noqa: E402
40+from wp_ai_0001.training.train_toy import train # noqa: E402
41+
42+OMEGA = "qua"
43+MAX_SEQ = 24
44+
45+
46+def build_splits(cfg_donor: OpSeqTaskConfig, cfg_recip: OpSeqTaskConfig, n_train: int, n_dev: int, n_eval: int, base_seed: int) -> dict[str, Split]:
47+ """All splits from distinct seeds; evaluation splits are disjoint from every training split."""
48+ s = base_seed
49+ sp: dict[str, Split] = {}
50+ sp["donor_train"] = opseq.generate(cfg_donor, n_train, s + 1, "donor_train", mode="train")
51+ sp["recipient_train"] = opseq.generate(cfg_recip, n_train, s + 2, "recipient_train", mode="train")
52+ used = sp["donor_train"].item_hashes() | sp["recipient_train"].item_hashes()
53+ sp["donor_dev"] = opseq.generate(cfg_donor, n_dev, s + 3, "donor_dev", mode="train", exclude_hashes=used)
54+ used |= sp["donor_dev"].item_hashes()
55+ sp["recipient_dev"] = opseq.generate(cfg_recip, n_dev, s + 4, "recipient_dev", mode="train", exclude_hashes=used)
56+ used |= sp["recipient_dev"].item_hashes()
57+ # evaluation strata (shared by all models)
58+ sp["target_iid"] = opseq.generate(cfg_donor, n_eval, s + 10, "target_iid", mode="train", require_operator=OMEGA, exclude_hashes=used)
59+ used |= sp["target_iid"].item_hashes()
60+ sp["control_iid"] = opseq.generate(cfg_donor, n_eval, s + 11, "control_iid", mode="train", forbid_operator=OMEGA, exclude_hashes=used)
61+ used |= sp["control_iid"].item_hashes()
62+ sp["comp_heldout"] = opseq.generate(cfg_donor, n_eval, s + 12, "comp_heldout", mode="comp", min_len=2, exclude_hashes=used)
63+ used |= sp["comp_heldout"].item_hashes()
64+ sp["len_ood"] = opseq.generate(cfg_donor, max(200, n_eval // 2), s + 13, "len_ood", mode="any", min_len=6, max_len=7, exclude_hashes=used)
65+ for k in ("donor_train", "donor_dev", "target_iid", "control_iid", "comp_heldout", "len_ood"):
66+ opseq.validate(sp[k], cfg_donor)
67+ opseq.validate(sp["recipient_train"], cfg_recip)
68+ opseq.validate(sp["recipient_dev"], cfg_recip)
69+ assert_disjoint(sp["donor_train"], sp["donor_dev"], sp["target_iid"], sp["control_iid"], sp["comp_heldout"], sp["len_ood"])
70+ assert_disjoint(sp["recipient_train"], sp["recipient_dev"], sp["target_iid"], sp["control_iid"], sp["comp_heldout"], sp["len_ood"])
71+ return sp
72+
73+
74+def omega_step_accuracy(split: Split, predictions: list[list[str]]) -> np.ndarray:
75+ """Per item: 1 if the scratchpad token at the first Ω step is correct (items without Ω: NaN)."""
76+ out = np.full(len(split.examples), np.nan)
77+ for i, (e, pred) in enumerate(zip(split.examples, predictions)):
78+ ops = list(e.prompt[2:-1])
79+ if OMEGA in ops:
80+ j = ops.index(OMEGA)
81+ out[i] = float(j < len(pred) and pred[j] == e.answers[j])
82+ return out
83+
84+
85+def localization_set(cfg: OpSeqTaskConfig, corrupt_op: str = "zib") -> tuple[Split, Split]:
86+ """Length-3 programs with Ω at index 1; corrupted twin replaces Ω by ``corrupt_op`` so that r_2 differs."""
87+ others = [o for o in cfg.operators if o != OMEGA]
88+ clean, corrupt = [], []
89+ for a in others:
90+ for b in others:
91+ for v0 in range(cfg.modulus):
92+ ec = opseq.make_example(v0, [a, OMEGA, b], cfg.modulus)
93+ ex = opseq.make_example(v0, [a, corrupt_op, b], cfg.modulus)
94+ if ec.answers[1] != ex.answers[1]:
95+ clean.append(ec)
96+ corrupt.append(ex)
97+ return (Split("loc_clean", clean, "opseq", opseq.GENERATOR_VERSION, 0, cfg.model_dump()),
98+ Split("loc_corrupt", corrupt, "opseq", opseq.GENERATOR_VERSION, 0, cfg.model_dump()))
99+
100+
101+def noop_check(model, enc, device) -> dict:
102+ ids = torch.from_numpy(enc["ids"][:256]).to(device)
103+ pos = torch.from_numpy(enc["prompt_len"][:256] - 1).to(device)
104+ model.hooks.clear()
105+ with torch.no_grad():
106+ ref = model(ids, pos)
107+ again = model(ids, pos)
108+ cap = Capture()
109+ for n in model.hook_names():
110+ model.hooks.add(n, cap)
111+ with_capture = model(ids, pos)
112+ model.hooks.add("resid_post.0", Zero())
113+ with model.hooks.disabled():
114+ disabled = model(ids, pos)
115+ enabled = model(ids, pos)
116+ model.hooks.clear()
117+ return {"deterministic_repeat": bool(torch.equal(ref, again)), "capture_all_bitwise_identity": bool(torch.equal(ref, with_capture)),
118+ "disabled_modifying_hook_bitwise_identity": bool(torch.equal(ref, disabled)),
119+ "enabled_zero_hook_changes_output": bool(not torch.equal(ref, enabled)), "n_hook_points": len(model.hook_names()), "n_items": int(ids.shape[0])}
120+
121+
122+def main() -> None:
123+ ap = argparse.ArgumentParser()
124+ ap.add_argument("--seeds", type=int, nargs="+", default=[0, 1, 2])
125+ ap.add_argument("--steps", type=int, default=3000)
126+ ap.add_argument("--device", default="auto")
127+ ap.add_argument("--quick", action="store_true", help="tiny settings for a CPU smoke test")
128+ ap.add_argument("--dry-run", action="store_true", help="build splits and manifest only")
129+ ap.add_argument("--n-boot", type=int, default=2000)
130+ args = ap.parse_args()
131+
132+ n_train, n_dev, n_eval, steps = (3000, 300, 300, 200) if args.quick else (40_000, 1_000, 1_000, args.steps)
133+ task_d = OpSeqTaskConfig()
134+ task_r = OpSeqTaskConfig(excluded_operators=[OMEGA])
135+ model_cfg = ToyModelConfig(max_seq_len=MAX_SEQ) if not args.quick else ToyModelConfig(d_model=64, n_layers=3, n_heads=2, d_ff=128, max_seq_len=MAX_SEQ)
136+ cfg = PilotConfig(
137+ experiment_id="pilot_tier0_opseq" + ("_quick" if args.quick else ""), seeds=args.seeds, target_operator=OMEGA, n_eval=n_eval, n_bootstrap=args.n_boot,
138+ donor=ModelRoleConfig(role="donor", name="donor", model=model_cfg, task=task_d, train=TrainConfig(steps=steps, device=args.device, n_train=n_train, n_dev=n_dev)),
139+ recipient=ModelRoleConfig(role="recipient", name="recipient", model=model_cfg, task=task_r, train=TrainConfig(steps=steps, device=args.device, n_train=n_train, n_dev=n_dev)),
140+ )
141+ chash = config_hash(cfg)
142+ run_id = make_run_id(cfg.experiment_id, chash)
143+ out_dir = ROOT / "experiments" / "summaries" / run_id
144+ art_dir = ROOT / "artifacts" / "runs" / run_id
145+ models_dir = ROOT / "artifacts" / "models" / run_id
146+ fig_dir = ROOT / "artifacts" / "figures" / run_id
147+ for d in (out_dir, art_dir, fig_dir):
148+ d.mkdir(parents=True, exist_ok=True)
149+ man = Manifest(run_id, cfg.experiment_id, cfg.model_dump(), ROOT / "experiments" / "manifests")
150+ registry.record(run_id, cfg.experiment_id, "planned", config_hash=chash)
151+ device = resolve_device(args.device)
152+ t_start = time.time()
153+ print(f"[{utc_now()}] run_id={run_id} device={device}")
154+
155+ # ---------------- data ----------------
156+ splits = build_splits(task_d, task_r, n_train, n_dev, n_eval, base_seed=20261109)
157+ data_dir = art_dir / "data"
158+ for name, sp in splits.items():
159+ sp.save(data_dir / f"{name}.json")
160+ man.data["data_hashes"][name] = {"hash": sp.hash, "n": len(sp), "seed": sp.seed, "generator": f"{sp.generator}@{sp.generator_version}"}
161+ man.save()
162+ vocab = opseq.vocab_for(task_d)
163+ chance = {k: chance_level(splits[k]) for k in ("target_iid", "control_iid", "comp_heldout", "len_ood")}
164+ print("splits:", {k: (len(v), v.hash) for k, v in splits.items()})
165+ if args.dry_run:
166+ man.set_status("planned", "dry run: splits and manifest only")
167+ print("dry run complete")
168+ return
169+
170+ man.set_status("running")
171+ registry.record(run_id, cfg.experiment_id, "running")
172+ results: dict = {"run_id": run_id, "config_hash": chash, "device": str(device), "chance_level": chance, "seeds": {}, "noop": {}, "localization": {}}
173+ try:
174+ # ---------------- training + evaluation ----------------
175+ eval_names = ["target_iid", "control_iid", "comp_heldout", "len_ood"]
176+ per_item: dict[str, dict[str, np.ndarray]] = {}
177+ for seed in cfg.seeds:
178+ results["seeds"][seed] = {}
179+ for role in (cfg.donor, cfg.recipient):
180+ tcfg = role.train.model_copy(update={"seed": seed})
181+ mdir = models_dir / f"{role.name}_s{seed}"
182+ t0 = time.time()
183+ if (mdir / "model.pt").exists():
184+ model, _ = load_model(mdir, device)
185+ hist = json.loads((mdir / "history.json").read_text())
186+ print(f" loaded {role.name} s{seed}")
187+ else:
188+ hist = []
189+ model = train(role.model, tcfg, splits[f"{role.name}_train"], splits[f"{role.name}_dev"], vocab, mdir, hist)
190+ print(f" trained {role.name} s{seed} in {time.time()-t0:.0f}s dev_acc={hist[-1]['dev_acc']:.3f}")
191+ model.eval()
192+ rec = {"n_params": model.n_params, "train_seconds": time.time() - t0, "final_dev": hist[-1], "eval": {}}
193+ for en in eval_names:
194+ r = evaluate(model, splits[en], vocab, device)
195+ ci = cluster_bootstrap_mean(r.correct.astype(float), r.clusters, cfg.n_bootstrap, seed=seed)
196+ om = omega_step_accuracy(splits[en], r.predictions)
197+ om_ci = cluster_bootstrap_mean(om[~np.isnan(om)], [c for c, o in zip(r.clusters, om) if not np.isnan(o)], cfg.n_bootstrap, seed=seed) if (~np.isnan(om)).any() else None
198+ rec["eval"][en] = {**r.to_summary(), "ci95": ci.to_dict(), "omega_step_acc": om_ci.to_dict() if om_ci else None, "chance": chance[en]}
199+ per_item[f"{role.name}_s{seed}_{en}"] = {"correct": r.correct, "trace_correct": r.trace_correct, "omega": om}
200+ with gzip.open(art_dir / f"items_{role.name}_s{seed}_{en}.json.gz", "wt") as f:
201+ json.dump({"split_hash": r.split_hash, "predictions": r.predictions, "answers": r.answers, "clusters": r.clusters}, f)
202+ enc_dev = encode_split(splits["donor_dev"], vocab, MAX_SEQ)
203+ results["noop"][f"{role.name}_s{seed}"] = noop_check(model, enc_dev, device)
204+ results["seeds"][seed][role.name] = rec
205+ # paired donor-recipient difference on identical items
206+ results["seeds"][seed]["gap"] = {}
207+ for en in eval_names:
208+ d, r_ = per_item[f"donor_s{seed}_{en}"], per_item[f"recipient_s{seed}_{en}"]
209+ ci = cluster_bootstrap_paired_diff(d["correct"].astype(float), r_["correct"].astype(float), splits[en].examples and [e.cluster for e in splits[en].examples], cfg.n_bootstrap, seed=seed)
210+ results["seeds"][seed]["gap"][en] = ci.to_dict()
211+ man.save()
212+
213+ results["across_seeds"] = {en: {role: seeds_summary([results["seeds"][s][role]["eval"][en]["accuracy"] for s in cfg.seeds]) for role in ("donor", "recipient")} for en in eval_names}
214+ results["across_seeds_omega_step"] = {role: seeds_summary([results["seeds"][s][role]["eval"]["target_iid"]["omega_step_acc"]["estimate"] for s in cfg.seeds]) for role in ("donor", "recipient")}
215+
216+ # ---------------- localization (donor models) ----------------
217+ clean, corrupt = localization_set(task_d)
218+ enc_c, enc_x = encode_split(clean, vocab, MAX_SEQ), encode_split(corrupt, vocab, MAX_SEQ)
219+ read_pos = int(enc_c["prompt_len"][0]) # index of r_1; predicts r_2 (the Ω step)
220+ assert (enc_c["prompt_len"] == read_pos).all() and (enc_x["prompt_len"] == read_pos).all()
221+ ids_c = torch.from_numpy(enc_c["ids"]).to(device)
222+ ids_x = torch.from_numpy(enc_x["ids"]).to(device)
223+ pos = torch.full((len(clean),), read_pos, dtype=torch.long, device=device)
224+ ans_c = torch.from_numpy(enc_c["answers"][:, 1]).to(device)
225+ ans_x = torch.from_numpy(enc_x["answers"][:, 1]).to(device)
226+ positions = list(range(read_pos + 1))
227+ pos_labels = ["<bos>", "v0", "op1", "Ω", "op3", "=", "r1"]
228+ loc: dict = {"n_items": len(clean), "read_position": read_pos, "positions": pos_labels, "families": {}, "ablation": {}, "heads": {}, "reproducibility": {}}
229+ heat_by_seed: dict[str, list[np.ndarray]] = {}
230+ for seed in cfg.seeds:
231+ model, _ = load_model(models_dir / f"donor_s{seed}", device)
232+ with torch.no_grad():
233+ base_c = (model.answer_logits(ids_c, pos).argmax(-1) == ans_c).float().mean().item()
234+ base_x = (model.answer_logits(ids_x, pos).argmax(-1) == ans_c).float().mean().item()
235+ loc.setdefault("baselines", {})[f"s{seed}"] = {"clean_acc_on_clean_answer": base_c, "corrupt_run_acc_on_clean_answer(sham)": base_x}
236+ for fam in ("resid_post", "attn_out", "mlp_out"):
237+ pr = patch_sweep(model, ids_c, ids_x, pos, ans_c, ans_x, family=fam, positions=positions)
238+ pr2 = patch_sweep(model, ids_c, ids_x, pos, ans_c, ans_x, family=fam, positions=positions)
239+ loc["families"].setdefault(fam, {})[f"s{seed}"] = {"flip_rate": pr.flip_rate.tolist(), "logit_recovery": pr.logit_recovery.tolist()}
240+ loc["reproducibility"][f"{fam}_s{seed}_repeat_max_abs_diff"] = float(np.abs(pr.flip_rate - pr2.flip_rate).max())
241+ heat_by_seed.setdefault(fam, []).append(pr.flip_rate)
242+ abl = ablation_sweep(model, ids_c, pos, ans_c, family=fam, positions=positions, mode="zero")
243+ loc["ablation"].setdefault(fam, {})[f"s{seed}"] = abl.tolist()
244+ for p_name, p_idx in (("omega_token", 3), ("read_position", read_pos)):
245+ loc["heads"].setdefault(p_name, {})[f"s{seed}"] = head_patch_sweep(model, ids_c, ids_x, pos, ans_c, ans_x, p_idx).tolist()
246+ print(f" localization donor s{seed} done")
247+ for fam, mats in heat_by_seed.items():
248+ if len(mats) > 1:
249+ flat = np.stack([m.ravel() for m in mats])
250+ cc = np.corrcoef(flat)
251+ loc["reproducibility"][f"{fam}_across_seed_pearson_min"] = float(cc[np.triu_indices(len(mats), 1)].min())
252+ loc["families"][fam]["mean_flip_rate"] = np.mean(mats, axis=0).tolist()
253+ results["localization"] = loc
254+
255+ # ---------------- figures ----------------
256+ make_figures(results, fig_dir, pos_labels, cfg)
257+ results["elapsed_seconds"] = time.time() - t_start
258+ (out_dir / "results.json").write_text(json.dumps(results, indent=2, default=float))
259+ write_summary(results, out_dir, cfg, fig_dir)
260+ man.data["outputs"] = {"results": str(out_dir.relative_to(ROOT) / "results.json"), "figures": str(fig_dir.relative_to(ROOT)), "models": str(models_dir.relative_to(ROOT)), "items": str(art_dir.relative_to(ROOT))}
261+ man.set_status("completed")
262+ registry.record(run_id, cfg.experiment_id, "completed", results=str(out_dir.relative_to(ROOT) / "results.json"))
263+ print(f"[{utc_now()}] completed in {results['elapsed_seconds']:.0f}s -> {out_dir}")
264+ except Exception as exc: # noqa: BLE001
265+ man.set_status("failed", repr(exc))
266+ registry.record(run_id, cfg.experiment_id, "failed", error=repr(exc))
267+ raise
268+
269+
270+def make_figures(results: dict, fig_dir: Path, pos_labels: list[str], cfg: PilotConfig) -> None:
271+ import matplotlib.pyplot as plt
272+
273+ from wp_ai_0001.visualization import DIVERGING_CMAP, ENTITY_COLORS, SEQUENTIAL_CMAP, apply_style, heatmap
274+ from wp_ai_0001.visualization.style import DOUBLE_WIDTH_IN, TEXT_SECONDARY
275+
276+ apply_style()
277+ seeds = {str(k): v for k, v in results["seeds"].items()} # JSON round-trips keys as strings
278+ eval_names = ["target_iid", "control_iid", "comp_heldout", "len_ood"]
279+ # Figure 1: capability gap, per split, donor vs recipient, mean over seeds with per-seed points and CI of seed 0
280+ fig, ax = plt.subplots(figsize=(DOUBLE_WIDTH_IN * 0.6, 2.2))
281+ x = np.arange(len(eval_names))
282+ w = 0.36
283+ for k, role in enumerate(("donor", "recipient")):
284+ means = [results["across_seeds"][en][role]["mean"] for en in eval_names]
285+ ax.bar(x + (k - 0.5) * w, means, width=w - 0.04, color=ENTITY_COLORS[role], label=role, linewidth=0)
286+ for s in cfg.seeds:
287+ ax.scatter(x + (k - 0.5) * w, [seeds[str(s)][role]["eval"][en]["accuracy"] for en in eval_names], s=8, color="#ffffff", edgecolor=ENTITY_COLORS[role], linewidth=0.6, zorder=3)
288+ ax.scatter(x, [results["chance_level"][en] for en in eval_names], marker="_", s=120, color=ENTITY_COLORS["chance"], label="majority-class chance", zorder=4)
289+ ax.set_xticks(x)
290+ ax.set_xticklabels(["target (Ω)", "control (no Ω)", "held-out comp.", "length OOD"])
291+ ax.set_ylabel("final-answer exact match")
292+ ax.set_ylim(0, 1.02)
293+ ax.legend(loc="upper right", ncol=1)
294+ fig.savefig(fig_dir / "fig_gap_tier0.pdf")
295+ plt.close(fig)
296+ # Figure 2: localization heatmaps (mean flip rate over seeds) for three families
297+ fams = ["resid_post", "attn_out", "mlp_out"]
298+ fig, axes = plt.subplots(1, 3, figsize=(DOUBLE_WIDTH_IN, 2.1), sharey=True)
299+ for ax, fam in zip(axes, fams):
300+ m = np.array(results["localization"]["families"][fam]["mean_flip_rate"])
301+ heatmap(ax, m, [f"L{l}" for l in range(m.shape[0])], pos_labels, cmap=SEQUENTIAL_CMAP, vmin=0, vmax=1, cbar_label="restored-answer rate", title=fam, annotate=True)
302+ ax.set_xlabel("patched token position")
303+ axes[0].set_ylabel("layer")
304+ fig.savefig(fig_dir / "fig_localization_patching_tier0.pdf")
305+ plt.close(fig)
306+ # Figure 3: zero-ablation (mean over seeds) accuracy
307+ fig, axes = plt.subplots(1, 3, figsize=(DOUBLE_WIDTH_IN, 2.1), sharey=True)
308+ for ax, fam in zip(axes, fams):
309+ m = np.mean([np.array(v) for v in results["localization"]["ablation"][fam].values()], axis=0)
310+ heatmap(ax, m, [f"L{l}" for l in range(m.shape[0])], pos_labels, cmap=SEQUENTIAL_CMAP, vmin=0, vmax=1, cbar_label="accuracy after zero-ablation", title=fam, annotate=True)
311+ ax.set_xlabel("ablated token position")
312+ axes[0].set_ylabel("layer")
313+ fig.savefig(fig_dir / "fig_localization_ablation_tier0.pdf")
314+ plt.close(fig)
315+ # Figure 4: head-level patching at the Ω token and at the read position
316+ fig, axes = plt.subplots(1, 2, figsize=(DOUBLE_WIDTH_IN * 0.75, 2.1), sharey=True)
317+ for ax, key in zip(axes, ("omega_token", "read_position")):
318+ m = np.mean([np.array(v) for v in results["localization"]["heads"][key].values()], axis=0)
319+ heatmap(ax, m, [f"L{l}" for l in range(m.shape[0])], [f"h{h}" for h in range(m.shape[1])], cmap=SEQUENTIAL_CMAP, vmin=0, vmax=1, cbar_label="restored-answer rate", title=f"head patch @ {key.replace('_', ' ')}", annotate=True)
320+ axes[0].set_ylabel("layer")
321+ fig.savefig(fig_dir / "fig_localization_heads_tier0.pdf")
322+ plt.close(fig)
323+ fig.text(0, 0, "", color=TEXT_SECONDARY)
324+
325+
326+def write_summary(results: dict, out_dir: Path, cfg: PilotConfig, fig_dir: Path) -> None:
327+ seeds = {str(k): v for k, v in results["seeds"].items()}
328+ L = ["# Pilot summary — " + results["run_id"], "", f"Generated {utc_now()} on device `{results['device']}`; config hash `{results['config_hash']}`; seeds {cfg.seeds}.", "",
329+ "## Gate B — donor/recipient gap (final-answer exact match, mean over seeds; CI = cluster bootstrap on programs)", "",
330+ "| split | chance | donor mean (sd) | recipient mean (sd) | paired gap seed 0 [95% CI] |", "|---|---|---|---|---|"]
331+ for en in ("target_iid", "control_iid", "comp_heldout", "len_ood"):
332+ d, r = results["across_seeds"][en]["donor"], results["across_seeds"][en]["recipient"]
333+ g = seeds[str(cfg.seeds[0])]["gap"][en]
334+ L.append(f"| {en} | {results['chance_level'][en]:.3f} | {d['mean']:.3f} ({(d['sd'] or 0):.3f}) | {r['mean']:.3f} ({(r['sd'] or 0):.3f}) | {g['estimate']:+.3f} [{g['low']:+.3f}, {g['high']:+.3f}] |")
335+ om = results["across_seeds_omega_step"]
336+ L += ["", f"Ω-step accuracy on target_iid (token emitted at the first Ω step): donor {om['donor']['mean']:.3f}, recipient {om['recipient']['mean']:.3f}.", "",
337+ "## Gate C — hook no-op checks (bitwise equality of logits on 256 dev items)", "", "| model | repeat | capture-all | disabled hook | enabled zero hook changes output |", "|---|---|---|---|---|"]
338+ for k, v in results["noop"].items():
339+ L.append(f"| {k} | {v['deterministic_repeat']} | {v['capture_all_bitwise_identity']} | {v['disabled_modifying_hook_bitwise_identity']} | {v['enabled_zero_hook_changes_output']} |")
340+ loc = results["localization"]
341+ L += ["", f"## RQ1 — within-donor localization of Ω (n = {loc['n_items']} clean/corrupt pairs, read position = r1, predicting r2)", "",
342+ "Baselines: " + "; ".join(f"s{k[1:]}: clean {v['clean_acc_on_clean_answer']:.3f}, sham (corrupt run scored on clean answer) {v['corrupt_run_acc_on_clean_answer(sham)']:.3f}" for k, v in loc["baselines"].items()), "",
343+ "Mean restored-answer rate after patching the clean activation into the corrupted run (rows = layers, columns = positions " + ", ".join(loc["positions"]) + "):", ""]
344+ for fam in ("resid_post", "attn_out", "mlp_out"):
345+ m = np.array(loc["families"][fam]["mean_flip_rate"])
346+ L.append(f"- `{fam}`: " + " | ".join(f"L{i}: " + " ".join(f"{v:.2f}" for v in row) for i, row in enumerate(m)))
347+ L += ["", "Reproducibility: " + "; ".join(f"{k} = {v:.4f}" for k, v in loc["reproducibility"].items()), "",
348+ "Figures: " + ", ".join(f"`{p.name}`" for p in sorted(fig_dir.glob("*.pdf"))), "",
349+ "## Interpretation (observed vs. speculative)", "",
350+ "Observed: the numbers above are produced by the artifacts listed in the manifest. Any statement about *transfer* is out of scope for this run: no transplant was performed.",
351+ "Speculative: whether the localized positions/layers constitute a transferable 'organ' is the object of the next experiment (activation graft with sham/random controls)."]
352+ (out_dir / "SUMMARY.md").write_text("\n".join(L) + "\n")
353+
354+
355+if __name__ == "__main__":
356+ main()
added src/wp_ai_0001/__init__.py +6 −0
@@ -0,0 +1,6 @@
1+"""WP-AI-0001 — The Neural Organ Transplant Experiment.
2+
3+Experiment harness for causally identified transfer of task capabilities between
4+open-weight language models. All code documentation is in English.
5+"""
6+__version__ = "0.1.0"
added src/wp_ai_0001/alignment/__init__.py +0 −0
added src/wp_ai_0001/baselines/.gitkeep +0 −0
added src/wp_ai_0001/baselines/__init__.py +0 −0
added src/wp_ai_0001/config.py +104 −0
@@ -0,0 +1,104 @@
1+"""Typed configuration objects and deterministic hashing.
2+
3+Every experiment is described by a pydantic model; ``config_hash`` is the SHA-256 of the
4+canonical JSON dump so that identical configurations always produce identical run IDs.
5+"""
6+from __future__ import annotations
7+
8+import hashlib
9+import json
10+from typing import Any, Literal
11+
12+from pydantic import BaseModel, Field
13+
14+
15+def canonical_json(obj: Any) -> str:
16+ return json.dumps(obj, sort_keys=True, separators=(",", ":"), ensure_ascii=True, default=str)
17+
18+
19+def config_hash(model: BaseModel, length: int = 12) -> str:
20+ return hashlib.sha256(canonical_json(model.model_dump()).encode()).hexdigest()[:length]
21+
22+
23+class OpSeqTaskConfig(BaseModel):
24+ """Micro-world A: compositional operator sequences over Z_p (see datasets/opseq.py)."""
25+
26+ family: Literal["opseq"] = "opseq"
27+ modulus: int = 11
28+ operators: list[str] = Field(default_factory=lambda: ["zib", "zob", "qua", "lar", "neg", "flip", "dro", "mik"])
29+ min_len: int = 1
30+ max_len: int = 5
31+ held_out_pairs: list[tuple[str, str]] = Field(default_factory=lambda: [("zob", "lar"), ("qua", "neg")])
32+ """Adjacent operator pairs never seen in training (compositional held-out)."""
33+ excluded_operators: list[str] = Field(default_factory=list)
34+ """Operators removed from a model's training distribution (used to create recipient weakness)."""
35+ trace_stride: int = 1
36+ """Emit every ``trace_stride``-th intermediate result (plus the final one). 1 = full scratchpad;
37+ larger values force multi-step composition inside a single prediction (difficulty knob)."""
38+
39+
40+class FSATaskConfig(BaseModel):
41+ """Micro-world B: run a fixed random DFA over an invented alphabet and output the final state."""
42+
43+ family: Literal["fsa"] = "fsa"
44+ n_states: int = 4
45+ alphabet: list[str] = Field(default_factory=lambda: ["ka", "mo", "ti"])
46+ min_len: int = 1
47+ max_len: int = 8
48+ automaton_seed: int = 7
49+
50+
51+class MorphTaskConfig(BaseModel):
52+ """Micro-world C: context-dependent morphological rule on pseudo-words."""
53+
54+ family: Literal["morph"] = "morph"
55+ min_stem: int = 3
56+ max_stem: int = 6
57+ rules: dict[str, str] = Field(default_factory=lambda: {"vowel": "-na", "consonant": "-ek", "nasal": "-ur"})
58+
59+
60+TaskConfig = OpSeqTaskConfig | FSATaskConfig | MorphTaskConfig
61+
62+
63+class ToyModelConfig(BaseModel):
64+ d_model: int = 128
65+ n_layers: int = 4
66+ n_heads: int = 4
67+ d_ff: int = 512
68+ max_seq_len: int = 32
69+ dropout: float = 0.0
70+ tie_embeddings: bool = False
71+
72+
73+class TrainConfig(BaseModel):
74+ seed: int = 0
75+ n_train: int = 40_000
76+ n_dev: int = 2_000
77+ batch_size: int = 256
78+ steps: int = 3_000
79+ lr: float = 1e-3
80+ weight_decay: float = 0.01
81+ warmup_steps: int = 100
82+ device: Literal["auto", "cpu", "mps", "cuda"] = "auto"
83+ eval_every: int = 500
84+
85+
86+class ModelRoleConfig(BaseModel):
87+ """A trained toy model with a role in the donor/recipient design."""
88+
89+ role: Literal["donor", "recipient", "unrelated"]
90+ name: str
91+ model: ToyModelConfig = Field(default_factory=ToyModelConfig)
92+ train: TrainConfig = Field(default_factory=TrainConfig)
93+ task: OpSeqTaskConfig | FSATaskConfig | MorphTaskConfig = Field(default_factory=OpSeqTaskConfig)
94+
95+
96+class PilotConfig(BaseModel):
97+ experiment_id: str = "pilot_tier0_opseq"
98+ description: str = "Tier-0 pilot: donor/recipient gap, hook no-op test, within-model causal localization"
99+ seeds: list[int] = Field(default_factory=lambda: [0, 1, 2])
100+ donor: ModelRoleConfig
101+ recipient: ModelRoleConfig
102+ target_operator: str = "qua"
103+ n_eval: int = 1_000
104+ n_bootstrap: int = 2_000
added src/wp_ai_0001/datasets/__init__.py +1 −0
@@ -0,0 +1 @@
1+from wp_ai_0001.datasets.common import Example, Split, Vocab, split_hash # noqa: F401
added src/wp_ai_0001/datasets/common.py +166 −0
@@ -0,0 +1,166 @@
1+"""Shared dataset primitives: examples, vocabularies, split containers and hashing.
2+
3+Every example is a token sequence ``<bos> <prompt tokens> = <a_1 ... a_k>`` where the ``k`` answer
4+tokens form a *scratchpad* (one intermediate result per execution step; k = 1 for single-answer
5+tasks). The primary metric is exact match of the final answer token under greedy decoding; the
6+trace metric is exact match of all ``k`` answer tokens. ``cluster`` identifies the
7+generating template (e.g. the operator program) and is the unit of resampling in the cluster
8+bootstrap: items sharing a cluster are not treated as independent.
9+"""
10+from __future__ import annotations
11+
12+import hashlib
13+import json
14+from dataclasses import asdict, dataclass, field
15+from pathlib import Path
16+from typing import Iterable
17+
18+import numpy as np
19+
20+PAD, BOS, EQ = "<pad>", "<bos>", "="
21+
22+
23+@dataclass(frozen=True)
24+class Example:
25+ tokens: tuple[str, ...] # full sequence including the answer tokens
26+ n_answer: int # number of answer (scratchpad) tokens at the end of ``tokens``
27+ cluster: str # template / program identifier (bootstrap unit)
28+ meta: dict = field(default_factory=dict)
29+
30+ @property
31+ def prompt(self) -> tuple[str, ...]:
32+ return self.tokens[: -self.n_answer]
33+
34+ @property
35+ def answers(self) -> tuple[str, ...]:
36+ return self.tokens[-self.n_answer :]
37+
38+ @property
39+ def answer(self) -> str:
40+ return self.tokens[-1]
41+
42+ @property
43+ def item_hash(self) -> str:
44+ return hashlib.sha256(" ".join(self.tokens).encode()).hexdigest()[:16]
45+
46+
47+class Vocab:
48+ def __init__(self, tokens: Iterable[str]) -> None:
49+ base = [PAD, BOS, EQ]
50+ extra = [t for t in dict.fromkeys(tokens) if t not in base]
51+ self.itos: list[str] = base + extra
52+ self.stoi: dict[str, int] = {t: i for i, t in enumerate(self.itos)}
53+
54+ def __len__(self) -> int:
55+ return len(self.itos)
56+
57+ @property
58+ def pad_id(self) -> int:
59+ return self.stoi[PAD]
60+
61+ def encode(self, tokens: Iterable[str]) -> list[int]:
62+ return [self.stoi[t] for t in tokens]
63+
64+ def decode(self, ids: Iterable[int]) -> list[str]:
65+ return [self.itos[i] for i in ids]
66+
67+ def to_json(self) -> str:
68+ return json.dumps(self.itos)
69+
70+ @classmethod
71+ def from_json(cls, text: str) -> "Vocab":
72+ return cls(json.loads(text))
73+
74+
75+@dataclass
76+class Split:
77+ name: str
78+ examples: list[Example]
79+ generator: str
80+ generator_version: str
81+ seed: int
82+ config: dict
83+
84+ def __len__(self) -> int:
85+ return len(self.examples)
86+
87+ @property
88+ def hash(self) -> str:
89+ return split_hash(self.examples)
90+
91+ def item_hashes(self) -> set[str]:
92+ return {e.item_hash for e in self.examples}
93+
94+ def answer_distribution(self) -> dict[str, float]:
95+ vals, counts = np.unique([e.answer for e in self.examples], return_counts=True)
96+ return {str(v): float(c) / len(self.examples) for v, c in zip(vals, counts)}
97+
98+ def save(self, path: Path) -> None:
99+ path.parent.mkdir(parents=True, exist_ok=True)
100+ payload = {
101+ "name": self.name,
102+ "generator": self.generator,
103+ "generator_version": self.generator_version,
104+ "seed": self.seed,
105+ "config": self.config,
106+ "hash": self.hash,
107+ "n": len(self.examples),
108+ "examples": [asdict(e) for e in self.examples],
109+ }
110+ path.write_text(json.dumps(payload, sort_keys=True))
111+
112+ @classmethod
113+ def load(cls, path: Path) -> "Split":
114+ d = json.loads(path.read_text())
115+ ex = [Example(tuple(e["tokens"]), int(e["n_answer"]), e["cluster"], e.get("meta", {})) for e in d["examples"]]
116+ s = cls(d["name"], ex, d["generator"], d["generator_version"], d["seed"], d["config"])
117+ if s.hash != d["hash"]:
118+ raise ValueError(f"split hash mismatch for {path}: stored {d['hash']} computed {s.hash}")
119+ return s
120+
121+
122+def split_hash(examples: Iterable[Example]) -> str:
123+ h = hashlib.sha256()
124+ for e in examples:
125+ h.update((" ".join(e.tokens) + "\n").encode())
126+ return h.hexdigest()[:16]
127+
128+
129+def assert_disjoint(*splits: Split) -> None:
130+ seen: dict[str, str] = {}
131+ for s in splits:
132+ for e in s.examples:
133+ if e.item_hash in seen:
134+ raise AssertionError(f"item leakage: {e.tokens} present in {seen[e.item_hash]} and {s.name}")
135+ seen[e.item_hash] = s.name
136+
137+
138+def encode_split(split: Split, vocab: Vocab, max_len: int) -> dict[str, np.ndarray]:
139+ """Encode a split for teacher-forced training and greedy evaluation.
140+
141+ Returns a dict of arrays:
142+ ``ids`` [N,T] full sequences (prompt + answers), right-padded with ``<pad>``;
143+ ``loss_mask`` [N,T] 1 at positions whose *next* token is an answer token;
144+ ``prompt_len`` [N] length of the prompt (index of the first answer token);
145+ ``n_answer`` [N] number of answer tokens;
146+ ``answers`` [N,K] answer token ids right-padded with ``<pad>`` (K = max n_answer).
147+ """
148+ n = len(split.examples)
149+ K = max(e.n_answer for e in split.examples)
150+ ids = np.full((n, max_len), vocab.pad_id, dtype=np.int64)
151+ loss_mask = np.zeros((n, max_len), dtype=np.float32)
152+ prompt_len = np.zeros(n, dtype=np.int64)
153+ n_answer = np.zeros(n, dtype=np.int64)
154+ answers = np.full((n, K), vocab.pad_id, dtype=np.int64)
155+ for i, e in enumerate(split.examples):
156+ seq = vocab.encode(e.tokens)
157+ if len(seq) > max_len:
158+ raise ValueError(f"sequence longer than max_len={max_len}: {e.tokens}")
159+ ids[i, : len(seq)] = seq
160+ pl = len(e.prompt)
161+ assert e.prompt[-1] == EQ
162+ prompt_len[i] = pl
163+ n_answer[i] = e.n_answer
164+ loss_mask[i, pl - 1 : pl - 1 + e.n_answer] = 1.0
165+ answers[i, : e.n_answer] = vocab.encode(e.answers)
166+ return {"ids": ids, "loss_mask": loss_mask, "prompt_len": prompt_len, "n_answer": n_answer, "answers": answers}
added src/wp_ai_0001/datasets/fsa.py +62 −0
@@ -0,0 +1,62 @@
1+"""Micro-world B — FSA: deterministic finite automaton execution over an invented alphabet.
2+
3+``<bos> s_1 ... s_n = q_1 ... q_n`` (state after each symbol; the final answer is ``q_n``). The automaton is drawn once from ``automaton_seed``; the
4+transition table is part of the split config so the task is fully verifiable.
5+"""
6+from __future__ import annotations
7+
8+import random
9+
10+from wp_ai_0001.config import FSATaskConfig
11+from wp_ai_0001.datasets.common import BOS, EQ, Example, Split, Vocab
12+
13+GENERATOR_VERSION = "fsa-1.0"
14+
15+
16+def transition_table(cfg: FSATaskConfig) -> dict[tuple[int, str], int]:
17+ rng = random.Random(f"{GENERATOR_VERSION}|automaton|{cfg.automaton_seed}")
18+ return {(q, a): rng.randrange(cfg.n_states) for q in range(cfg.n_states) for a in cfg.alphabet}
19+
20+
21+def run(table: dict[tuple[int, str], int], symbols: list[str]) -> list[int]:
22+ q, out = 0, []
23+ for s in symbols:
24+ q = table[(q, s)]
25+ out.append(q)
26+ return out
27+
28+
29+def vocab_for(cfg: FSATaskConfig) -> Vocab:
30+ return Vocab(list(cfg.alphabet) + [f"q{i}" for i in range(cfg.n_states)])
31+
32+
33+def generate(cfg: FSATaskConfig, n: int, seed: int, name: str, *, min_len: int | None = None, max_len: int | None = None,
34+ exclude_hashes: set[str] | None = None) -> Split:
35+ rng = random.Random(f"{GENERATOR_VERSION}|{name}|{seed}")
36+ table = transition_table(cfg)
37+ lo = cfg.min_len if min_len is None else min_len
38+ hi = cfg.max_len if max_len is None else max_len
39+ seen = set(exclude_hashes or set())
40+ out: list[Example] = []
41+ attempts = 0
42+ while len(out) < n:
43+ attempts += 1
44+ if attempts > 200 * n + 10_000:
45+ raise RuntimeError(f"could not generate {n} unique FSA examples for {name}")
46+ k = rng.randint(lo, hi)
47+ syms = [rng.choice(cfg.alphabet) for _ in range(k)]
48+ states = [f"q{q}" for q in run(table, syms)]
49+ ex = Example((BOS, *syms, EQ, *states), n_answer=k, cluster=f"len{k}", meta={"len": k})
50+ if ex.item_hash in seen:
51+ continue
52+ seen.add(ex.item_hash)
53+ out.append(ex)
54+ conf = cfg.model_dump() | {"table": {f"{q},{a}": v for (q, a), v in table.items()}}
55+ return Split(name, out, "fsa", GENERATOR_VERSION, seed, conf)
56+
57+
58+def validate(split: Split, cfg: FSATaskConfig) -> None:
59+ table = transition_table(cfg)
60+ for e in split.examples:
61+ syms = list(e.prompt[1:-1])
62+ assert tuple(f"q{q}" for q in run(table, syms)) == e.answers, e
added src/wp_ai_0001/datasets/morph.py +55 −0
@@ -0,0 +1,55 @@
1+"""Micro-world C — MORPH: context-dependent suffix selection on pseudo-word stems.
2+
3+``<bos> c_1 ... c_m = suffix`` where the suffix class depends on the stem-final phoneme class
4+(vowel / nasal / other consonant). Stems are random pseudo-words so lexical priors do not apply.
5+"""
6+from __future__ import annotations
7+
8+import random
9+
10+from wp_ai_0001.config import MorphTaskConfig
11+from wp_ai_0001.datasets.common import BOS, EQ, Example, Split, Vocab
12+
13+GENERATOR_VERSION = "morph-1.0"
14+VOWELS = list("aeiou")
15+NASALS = list("mn")
16+OTHERS = list("bdgklprstv")
17+LETTERS = VOWELS + NASALS + OTHERS
18+
19+
20+def phon_class(ch: str) -> str:
21+ if ch in VOWELS:
22+ return "vowel"
23+ if ch in NASALS:
24+ return "nasal"
25+ return "consonant"
26+
27+
28+def vocab_for(cfg: MorphTaskConfig) -> Vocab:
29+ return Vocab(LETTERS + list(cfg.rules.values()))
30+
31+
32+def generate(cfg: MorphTaskConfig, n: int, seed: int, name: str, *, exclude_hashes: set[str] | None = None) -> Split:
33+ rng = random.Random(f"{GENERATOR_VERSION}|{name}|{seed}")
34+ seen = set(exclude_hashes or set())
35+ out: list[Example] = []
36+ attempts = 0
37+ while len(out) < n:
38+ attempts += 1
39+ if attempts > 200 * n + 10_000:
40+ raise RuntimeError(f"could not generate {n} unique MORPH examples for {name}")
41+ m = rng.randint(cfg.min_stem, cfg.max_stem)
42+ stem = [rng.choice(LETTERS) for _ in range(m)]
43+ ans = cfg.rules[phon_class(stem[-1])]
44+ ex = Example((BOS, *stem, EQ, ans), 1, cluster=f"{phon_class(stem[-1])}|len{m}", meta={"len": m})
45+ if ex.item_hash in seen:
46+ continue
47+ seen.add(ex.item_hash)
48+ out.append(ex)
49+ return Split(name, out, "morph", GENERATOR_VERSION, seed, cfg.model_dump())
50+
51+
52+def validate(split: Split, cfg: MorphTaskConfig) -> None:
53+ for e in split.examples:
54+ stem = list(e.prompt[1:-1])
55+ assert cfg.rules[phon_class(stem[-1])] == e.answer, e
added src/wp_ai_0001/datasets/opseq.py +131 −0
@@ -0,0 +1,131 @@
1+"""Micro-world A — OPSEQ: compositional operator sequences over Z_p.
2+
3+A program is ``<bos> v0 op_1 ... op_k = r_1 ... r_k`` where each invented operator is a fixed map on
4+Z_p and ``r_j`` is the value after applying ``op_j`` (scratchpad execution trace; the final answer is
5+``r_k``). Operator names are meaningless strings so that no pre-training prior applies.
6+Donor competence / recipient weakness is created by *excluding* chosen operators from a model's
7+training distribution (``excluded_operators``). Compositional generalization is measured on
8+adjacent operator pairs that never co-occur in training (``held_out_pairs``), and length
9+generalization on programs longer than ``max_len``.
10+
11+Each split is generated from its own seed; disjointness of item hashes across splits is enforced
12+by the caller (``datasets.common.assert_disjoint``).
13+"""
14+from __future__ import annotations
15+
16+import random
17+from typing import Callable
18+
19+from wp_ai_0001.config import OpSeqTaskConfig
20+from wp_ai_0001.datasets.common import BOS, EQ, Example, Split, Vocab
21+
22+GENERATOR_VERSION = "opseq-1.0"
23+
24+# Invented operator semantics (affine maps x -> (a*x + b) mod p, or reflections).
25+OPERATOR_TABLE: dict[str, Callable[[int, int], int]] = {
26+ "zib": lambda x, p: (x + 1) % p,
27+ "zob": lambda x, p: (x + 3) % p,
28+ "qua": lambda x, p: (2 * x + 1) % p,
29+ "lar": lambda x, p: (3 * x) % p,
30+ "neg": lambda x, p: (-x) % p,
31+ "flip": lambda x, p: (p - 1 - x) % p,
32+ "dro": lambda x, p: (5 * x) % p,
33+ "mik": lambda x, p: (x * x) % p,
34+}
35+
36+
37+def trace(v0: int, ops: list[str], p: int) -> list[int]:
38+ x, out = v0, []
39+ for op in ops:
40+ x = OPERATOR_TABLE[op](x, p)
41+ out.append(x)
42+ return out
43+
44+
45+def apply_program(v0: int, ops: list[str], p: int) -> int:
46+ return trace(v0, ops, p)[-1] if ops else v0
47+
48+
49+def vocab_for(cfg: OpSeqTaskConfig) -> Vocab:
50+ return Vocab([str(i) for i in range(cfg.modulus)] + list(cfg.operators))
51+
52+
53+def _has_held_out_pair(ops: list[str], pairs: set[tuple[str, str]]) -> bool:
54+ return any((a, b) in pairs for a, b in zip(ops, ops[1:]))
55+
56+
57+def visible_trace(v0: int, ops: list[str], p: int, stride: int = 1) -> list[int]:
58+ full = trace(v0, ops, p)
59+ idx = list(range(stride - 1, len(full), stride))
60+ if not idx or idx[-1] != len(full) - 1:
61+ idx.append(len(full) - 1)
62+ return [full[i] for i in idx]
63+
64+
65+def make_example(v0: int, ops: list[str], p: int, stride: int = 1) -> Example:
66+ tr = [str(r) for r in visible_trace(v0, ops, p, stride)]
67+ tokens = (BOS, str(v0), *ops, EQ, *tr)
68+ return Example(tokens=tokens, n_answer=len(tr), cluster=" ".join(ops), meta={"len": len(ops), "v0": v0, "stride": stride})
69+
70+
71+def generate(
72+ cfg: OpSeqTaskConfig,
73+ n: int,
74+ seed: int,
75+ name: str,
76+ *,
77+ mode: str = "train",
78+ require_operator: str | None = None,
79+ forbid_operator: str | None = None,
80+ min_len: int | None = None,
81+ max_len: int | None = None,
82+ exclude_hashes: set[str] | None = None,
83+) -> Split:
84+ """Generate ``n`` unique examples.
85+
86+ mode="train": no held-out pairs, no excluded operators.
87+ mode="comp": every program contains at least one held-out adjacent pair.
88+ mode="any": unconstrained with respect to held-out pairs.
89+ ``require_operator`` / ``forbid_operator`` restrict the operator content (target vs control).
90+ """
91+ rng = random.Random(f"{GENERATOR_VERSION}|{name}|{seed}")
92+ p = cfg.modulus
93+ pairs = set(map(tuple, cfg.held_out_pairs))
94+ allowed = [o for o in cfg.operators if o not in cfg.excluded_operators]
95+ if forbid_operator:
96+ allowed = [o for o in allowed if o != forbid_operator]
97+ lo = cfg.min_len if min_len is None else min_len
98+ hi = cfg.max_len if max_len is None else max_len
99+ seen: set[str] = set(exclude_hashes or set())
100+ out: list[Example] = []
101+ attempts = 0
102+ while len(out) < n:
103+ attempts += 1
104+ if attempts > 200 * n + 10_000:
105+ raise RuntimeError(f"could not generate {n} unique examples for {name} (got {len(out)})")
106+ k = rng.randint(lo, hi)
107+ ops = [rng.choice(allowed) for _ in range(k)]
108+ if require_operator and require_operator not in ops:
109+ continue
110+ held = _has_held_out_pair(ops, pairs)
111+ if mode == "train" and held:
112+ continue
113+ if mode == "comp" and not held:
114+ continue
115+ v0 = rng.randrange(p)
116+ ex = make_example(v0, ops, p, cfg.trace_stride)
117+ if ex.item_hash in seen:
118+ continue
119+ seen.add(ex.item_hash)
120+ out.append(ex)
121+ return Split(name, out, "opseq", GENERATOR_VERSION, seed, cfg.model_dump())
122+
123+
124+def validate(split: Split, cfg: OpSeqTaskConfig) -> None:
125+ """Independent recomputation of every answer (exact validator)."""
126+ for e in split.examples:
127+ assert e.tokens[0] == BOS and e.prompt[-1] == EQ
128+ v0 = int(e.tokens[1])
129+ ops = list(e.prompt[2:-1])
130+ assert all(o in OPERATOR_TABLE for o in ops), e
131+ assert tuple(str(r) for r in visible_trace(v0, ops, cfg.modulus, cfg.trace_stride)) == e.answers, e
added src/wp_ai_0001/evaluation/__init__.py +0 −0
added src/wp_ai_0001/evaluation/metrics.py +54 −0
@@ -0,0 +1,54 @@
1+"""Exact-match evaluation of a toy model on a Split under greedy decoding, with per-item outputs."""
2+from __future__ import annotations
3+
4+from dataclasses import dataclass
5+
6+import numpy as np
7+import torch
8+
9+from wp_ai_0001.datasets.common import Split, Vocab, encode_split
10+from wp_ai_0001.models.decoding import greedy_decode
11+from wp_ai_0001.models.toy_transformer import ToyTransformer
12+
13+
14+@dataclass
15+class EvalResult:
16+ split: str
17+ split_hash: str
18+ n: int
19+ accuracy: float # final-answer exact match
20+ trace_accuracy: float # all scratchpad tokens exact match
21+ correct: np.ndarray # [N] bool (final answer)
22+ trace_correct: np.ndarray # [N] bool
23+ clusters: list[str]
24+ predictions: list[list[str]]
25+ answers: list[list[str]]
26+
27+ def to_summary(self) -> dict:
28+ return {"split": self.split, "split_hash": self.split_hash, "n": self.n, "accuracy": self.accuracy, "trace_accuracy": self.trace_accuracy}
29+
30+
31+@torch.no_grad()
32+def evaluate(model: ToyTransformer, split: Split, vocab: Vocab, device: torch.device, bs: int = 1024) -> EvalResult:
33+ enc = encode_split(split, vocab, model.cfg.max_seq_len)
34+ K = enc["answers"].shape[1]
35+ preds = []
36+ for i in range(0, len(enc["ids"]), bs):
37+ ids = torch.from_numpy(enc["ids"][i : i + bs]).to(device)
38+ pl = torch.from_numpy(enc["prompt_len"][i : i + bs]).to(device)
39+ na = torch.from_numpy(enc["n_answer"][i : i + bs]).to(device)
40+ preds.append(greedy_decode(model, ids, pl, na, K).cpu().numpy())
41+ pred = np.concatenate(preds)
42+ na = enc["n_answer"]
43+ ar = np.arange(len(na))
44+ final_ok = pred[ar, na - 1] == enc["answers"][ar, na - 1]
45+ valid = np.arange(K)[None] < na[:, None]
46+ trace_ok = ((pred == enc["answers"]) | ~valid).all(-1)
47+ return EvalResult(split.name, split.hash, len(na), float(final_ok.mean()), float(trace_ok.mean()), final_ok, trace_ok,
48+ [e.cluster for e in split.examples], [vocab.decode(pred[i, : na[i]]) for i in ar],
49+ [list(e.answers) for e in split.examples])
50+
51+
52+def chance_level(split: Split) -> float:
53+ """Majority-class rate of the answer distribution (the relevant chance baseline)."""
54+ return max(split.answer_distribution().values())
added src/wp_ai_0001/instrumentation/__init__.py +0 −0
added src/wp_ai_0001/instrumentation/provenance.py +123 −0
@@ -0,0 +1,123 @@
1+"""Run provenance: git state, environment, hardware, deterministic run IDs and manifests.
2+
3+Secrets are never read or written here; environment capture is limited to library versions and
4+hardware descriptors. Personal paths are stored relative to the project root.
5+"""
6+from __future__ import annotations
7+
8+import datetime as _dt
9+import hashlib
10+import json
11+import os
12+import platform
13+import subprocess
14+import sys
15+from pathlib import Path
16+from typing import Any
17+
18+PROJECT_ROOT = Path(__file__).resolve().parents[3]
19+
20+
21+def git_state(root: Path = PROJECT_ROOT) -> dict[str, Any]:
22+ def _run(args: list[str]) -> str | None:
23+ try:
24+ return subprocess.check_output(["git", *args], cwd=root, stderr=subprocess.DEVNULL, text=True).strip()
25+ except Exception: # noqa: BLE001 - git may be absent or the directory may not be a repo
26+ return None
27+
28+ commit = _run(["rev-parse", "HEAD"])
29+ dirty = _run(["status", "--porcelain"])
30+ return {"commit": commit, "dirty": bool(dirty) if dirty is not None else None, "branch": _run(["rev-parse", "--abbrev-ref", "HEAD"])}
31+
32+
33+def library_versions() -> dict[str, str | None]:
34+ out: dict[str, str | None] = {"python": sys.version.split()[0]}
35+ for name in ["numpy", "torch", "scipy", "pydantic", "mlx", "mlx_lm", "transformers", "safetensors"]:
36+ try:
37+ mod = __import__(name)
38+ out[name] = getattr(mod, "__version__", "?")
39+ except Exception: # noqa: BLE001
40+ out[name] = None
41+ return out
42+
43+
44+def hardware() -> dict[str, Any]:
45+ info: dict[str, Any] = {
46+ "platform": platform.platform(),
47+ "machine": platform.machine(),
48+ "hostname_hash": hashlib.sha256(platform.node().encode()).hexdigest()[:12],
49+ "cpu_count": os.cpu_count(),
50+ }
51+ if sys.platform == "darwin":
52+ try:
53+ info["chip"] = subprocess.check_output(["sysctl", "-n", "machdep.cpu.brand_string"], text=True).strip()
54+ info["memory_gb"] = int(subprocess.check_output(["sysctl", "-n", "hw.memsize"], text=True)) / 2**30
55+ except Exception: # noqa: BLE001
56+ pass
57+ try:
58+ import torch
59+
60+ info["torch_mps"] = bool(torch.backends.mps.is_available())
61+ info["torch_cuda"] = bool(torch.cuda.is_available())
62+ except Exception: # noqa: BLE001
63+ pass
64+ return info
65+
66+
67+def utc_now() -> str:
68+ return _dt.datetime.now(_dt.timezone.utc).isoformat(timespec="seconds")
69+
70+
71+def sha256_file(path: Path) -> str:
72+ h = hashlib.sha256()
73+ with open(path, "rb") as f:
74+ for chunk in iter(lambda: f.read(1 << 20), b""):
75+ h.update(chunk)
76+ return h.hexdigest()
77+
78+
79+def sha256_text(text: str) -> str:
80+ return hashlib.sha256(text.encode()).hexdigest()
81+
82+
83+def make_run_id(experiment_id: str, cfg_hash: str, seed: int | None = None) -> str:
84+ tail = f"_s{seed}" if seed is not None else ""
85+ return f"{experiment_id}_{cfg_hash}{tail}"
86+
87+
88+class Manifest:
89+ """Mutable run manifest persisted as JSON; status transitions are recorded with timestamps."""
90+
91+ def __init__(self, run_id: str, experiment_id: str, config: dict[str, Any], out_dir: Path) -> None:
92+ self.path = out_dir / f"{run_id}.json"
93+ self.data: dict[str, Any] = {
94+ "run_id": run_id,
95+ "experiment_id": experiment_id,
96+ "status": "planned",
97+ "created_utc": utc_now(),
98+ "git": git_state(),
99+ "libraries": library_versions(),
100+ "hardware": hardware(),
101+ "config": config,
102+ "data_hashes": {},
103+ "seeds": {},
104+ "outputs": {},
105+ "warnings": [],
106+ "history": [],
107+ "interpretation": "",
108+ }
109+ out_dir.mkdir(parents=True, exist_ok=True)
110+ self.save()
111+
112+ def set_status(self, status: str, note: str = "") -> None:
113+ assert status in {"planned", "running", "failed", "completed", "invalidated"}
114+ self.data["status"] = status
115+ self.data["history"].append({"utc": utc_now(), "status": status, "note": note})
116+ self.save()
117+
118+ def warn(self, message: str) -> None:
119+ self.data["warnings"].append({"utc": utc_now(), "message": message})
120+ self.save()
121+
122+ def save(self) -> None:
123+ self.path.write_text(json.dumps(self.data, indent=2, sort_keys=True, default=str))
added src/wp_ai_0001/localization/__init__.py +0 −0
added src/wp_ai_0001/localization/patching.py +120 −0
@@ -0,0 +1,120 @@
1+"""Within-model causal localization by activation patching and ablation on toy models.
2+
3+``patch_sweep`` implements the standard clean/corrupted interchange intervention: run the model on
4+a *clean* sequence and on a *corrupted* sequence (same program with the target operator replaced
5+by a different operator, identical teacher-forced prefix), then for every (hook, layer, position)
6+overwrite the corrupted run's activation with the clean run's activation and measure how much of
7+the clean next-token answer at the read position ``pos`` is restored.
8+The effect is reported as the fraction of items whose argmax flips to the clean answer and as the
9+mean normalized logit-difference recovery. Larger = the patched location carries the information.
10+
11+``ablation_sweep`` zeroes (or noises) the activation at each location on clean prompts and reports
12+the accuracy drop, i.e. necessity. Both sweeps are deterministic given model and data.
13+"""
14+from __future__ import annotations
15+
16+from dataclasses import dataclass
17+
18+import numpy as np
19+import torch
20+
21+from wp_ai_0001.models.hooks import Capture, GaussianNoise, Replace, Zero
22+from wp_ai_0001.models.toy_transformer import ToyTransformer
23+
24+
25+@dataclass
26+class PatchResult:
27+ hook_family: str
28+ layers: list[int]
29+ positions: list[int]
30+ flip_rate: np.ndarray # [L, P]
31+ logit_recovery: np.ndarray # [L, P]
32+ n_items: int
33+
34+
35+def _run(model: ToyTransformer, ids: torch.Tensor, pos: torch.Tensor) -> torch.Tensor:
36+ with torch.no_grad():
37+ return model.answer_logits(ids, pos)
38+
39+
40+def capture_all(model: ToyTransformer, ids: torch.Tensor, pos: torch.Tensor, family: str) -> dict[str, torch.Tensor]:
41+ cap = Capture()
42+ names = [f"{family}.{l}" for l in range(model.cfg.n_layers)]
43+ for n in names:
44+ model.hooks.add(n, cap)
45+ try:
46+ _run(model, ids, pos)
47+ finally:
48+ for n in names:
49+ model.hooks.remove(n, cap)
50+ return cap.store
51+
52+
53+def patch_sweep(model: ToyTransformer, clean_ids: torch.Tensor, corrupt_ids: torch.Tensor, pos: torch.Tensor,
54+ clean_ans: torch.Tensor, corrupt_ans: torch.Tensor, family: str = "resid_post", positions: list[int] | None = None) -> PatchResult:
55+ """Patch clean activations into the corrupted run, one (layer, position) at a time."""
56+ T = clean_ids.shape[1]
57+ positions = positions if positions is not None else list(range(T))
58+ clean_acts = capture_all(model, clean_ids, pos, family)
59+ base = _run(model, corrupt_ids, pos)
60+ clean_logits = _run(model, clean_ids, pos)
61+ ar = torch.arange(len(pos), device=clean_ids.device)
62+ # logit difference clean-answer minus corrupt-answer, in the corrupted run (baseline) and clean run (ceiling)
63+ ld_base = base[ar, clean_ans] - base[ar, corrupt_ans]
64+ ld_clean = clean_logits[ar, clean_ans] - clean_logits[ar, corrupt_ans]
65+ denom = (ld_clean - ld_base).clamp_min(1e-6)
66+ L = model.cfg.n_layers
67+ flip = np.zeros((L, len(positions)))
68+ rec = np.zeros((L, len(positions)))
69+ for l in range(L):
70+ name = f"{family}.{l}"
71+ for j, p in enumerate(positions):
72+ fn = Replace(clean_acts[name], positions=[p])
73+ model.hooks.add(name, fn)
74+ try:
75+ out = _run(model, corrupt_ids, pos)
76+ finally:
77+ model.hooks.remove(name, fn)
78+ ld = out[ar, clean_ans] - out[ar, corrupt_ans]
79+ rec[l, j] = float(((ld - ld_base) / denom).clamp(-1, 2).mean())
80+ flip[l, j] = float((out.argmax(-1) == clean_ans).float().mean())
81+ return PatchResult(family, list(range(L)), positions, flip, rec, int(len(pos)))
82+
83+
84+def ablation_sweep(model: ToyTransformer, ids: torch.Tensor, pos: torch.Tensor, ans: torch.Tensor, family: str = "resid_post",
85+ positions: list[int] | None = None, mode: str = "zero", noise_std: float = 1.0, seed: int = 0) -> np.ndarray:
86+ """Accuracy after ablating (layer, position). Returns [L, P] accuracies."""
87+ T = ids.shape[1]
88+ positions = positions if positions is not None else list(range(T))
89+ L = model.cfg.n_layers
90+ acc = np.zeros((L, len(positions)))
91+ for l in range(L):
92+ name = f"{family}.{l}"
93+ for j, p in enumerate(positions):
94+ fn = Zero(positions=[p]) if mode == "zero" else GaussianNoise(noise_std, positions=[p], seed=seed)
95+ model.hooks.add(name, fn)
96+ try:
97+ out = _run(model, ids, pos)
98+ finally:
99+ model.hooks.remove(name, fn)
100+ acc[l, j] = float((out.argmax(-1) == ans).float().mean())
101+ return acc
102+
103+
104+def head_patch_sweep(model: ToyTransformer, clean_ids: torch.Tensor, corrupt_ids: torch.Tensor, pos: torch.Tensor,
105+ clean_ans: torch.Tensor, corrupt_ans: torch.Tensor, position: int) -> np.ndarray:
106+ """Per-(layer, head) patching of the attention head output at one token position. Returns [L, H] flip rates."""
107+ clean_acts = capture_all(model, clean_ids, pos, "attn_heads")
108+ L, H = model.cfg.n_layers, model.cfg.n_heads
109+ flip = np.zeros((L, H))
110+ for l in range(L):
111+ name = f"attn_heads.{l}"
112+ for h in range(H):
113+ fn = Replace(clean_acts[name], positions=[position], heads=[h])
114+ model.hooks.add(name, fn)
115+ try:
116+ out = _run(model, corrupt_ids, pos)
117+ finally:
118+ model.hooks.remove(name, fn)
119+ flip[l, h] = float((out.argmax(-1) == clean_ans).float().mean())
120+ return flip
added src/wp_ai_0001/models/__init__.py +2 −0
@@ -0,0 +1,2 @@
1+from wp_ai_0001.models.hooks import HookManager, Intervention # noqa: F401
2+from wp_ai_0001.models.toy_transformer import ToyTransformer # noqa: F401
added src/wp_ai_0001/models/decoding.py +28 −0
@@ -0,0 +1,28 @@
1+"""Greedy scratchpad decoding for toy models (batched, per-row prompt lengths)."""
2+from __future__ import annotations
3+
4+import torch
5+
6+from wp_ai_0001.models.toy_transformer import ToyTransformer
7+
8+
9+@torch.no_grad()
10+def greedy_decode(model: ToyTransformer, ids: torch.Tensor, prompt_len: torch.Tensor, n_answer: torch.Tensor, K: int) -> torch.Tensor:
11+ """Fill answer positions greedily; returns predicted answer ids [B,K] (pad beyond n_answer).
12+
13+ ``ids`` must contain the prompt; answer positions are overwritten step by step so the
14+ generated prefix conditions later steps (no teacher forcing).
15+ """
16+ ids = ids.clone()
17+ B = ids.shape[0]
18+ ar = torch.arange(B, device=ids.device)
19+ out = torch.zeros(B, K, dtype=torch.long, device=ids.device)
20+ for s in range(K):
21+ read = prompt_len - 1 + s
22+ logits = model(ids, read) # hook positions = the read position for this step
23+ pred = logits[ar, read].argmax(-1)
24+ write = prompt_len + s
25+ active = s < n_answer
26+ ids[ar[active], write[active]] = pred[active]
27+ out[:, s] = torch.where(active, pred, torch.zeros_like(pred))
28+ return out
added src/wp_ai_0001/models/hooks.py +203 −0
@@ -0,0 +1,203 @@
1+"""Hook points and interventions for activation capture, patching and steering.
2+
3+Design contract (tested in tests/causal_sanity/test_hook_noop.py):
4+
5+* A ``HookManager`` with ``enabled=False`` or with no registered interventions returns the input
6+ tensor object itself (identity, no copy), so a model instrumented with hook points produces
7+ bit-identical outputs to the uninstrumented computation.
8+* Interventions are pure functions ``(tensor, ctx) -> tensor`` applied in registration order at a
9+ named hook point. ``ctx`` carries the hook name and the batch of token positions.
10+* Capture interventions record (detached, cloned) activations without modifying the stream.
11+
12+Hook names used by ``ToyTransformer``:
13+ ``embed``, ``resid_pre.{l}``, ``attn_heads.{l}`` ([B,T,H,dh] before the output projection),
14+ ``attn_out.{l}``, ``resid_mid.{l}``, ``mlp_out.{l}``, ``resid_post.{l}``, ``final_norm``.
15+"""
16+from __future__ import annotations
17+
18+from dataclasses import dataclass, field
19+from typing import Callable, Protocol
20+
21+import torch
22+
23+Tensor = torch.Tensor
24+
25+
26+@dataclass
27+class HookContext:
28+ name: str
29+ positions: Tensor | None = None # [B] answer positions when available
30+ extra: dict = field(default_factory=dict)
31+
32+
33+class Intervention(Protocol):
34+ def __call__(self, x: Tensor, ctx: HookContext) -> Tensor: ...
35+
36+
37+class HookManager:
38+ def __init__(self) -> None:
39+ self.enabled: bool = True
40+ self._hooks: dict[str, list[Intervention]] = {}
41+ self.calls: int = 0
42+
43+ # -- registration -------------------------------------------------------------------
44+ def add(self, name: str, fn: Intervention) -> None:
45+ self._hooks.setdefault(name, []).append(fn)
46+
47+ def remove(self, name: str, fn: Intervention | None = None) -> None:
48+ if fn is None:
49+ self._hooks.pop(name, None)
50+ elif name in self._hooks:
51+ self._hooks[name] = [f for f in self._hooks[name] if f is not fn]
52+
53+ def clear(self) -> None:
54+ self._hooks.clear()
55+
56+ def names(self) -> list[str]:
57+ return [n for n, fns in self._hooks.items() if fns]
58+
59+ # -- application --------------------------------------------------------------------
60+ def __call__(self, name: str, x: Tensor, ctx: HookContext | None = None) -> Tensor:
61+ if not self.enabled:
62+ return x
63+ fns = self._hooks.get(name)
64+ if not fns:
65+ return x
66+ self.calls += 1
67+ c = ctx or HookContext(name=name)
68+ c.name = name
69+ for fn in fns:
70+ x = fn(x, c)
71+ return x
72+
73+ class _Disabled:
74+ def __init__(self, mgr: "HookManager") -> None:
75+ self.mgr, self.prev = mgr, mgr.enabled
76+
77+ def __enter__(self) -> None:
78+ self.mgr.enabled = False
79+
80+ def __exit__(self, *a) -> None:
81+ self.mgr.enabled = self.prev
82+
83+ def disabled(self) -> "HookManager._Disabled":
84+ return HookManager._Disabled(self)
85+
86+
87+# ---------------------------------------------------------------------------------------
88+# Built-in interventions
89+# ---------------------------------------------------------------------------------------
90+class Capture:
91+ """Record activations (detached clone) without modifying the stream."""
92+
93+ def __init__(self) -> None:
94+ self.store: dict[str, Tensor] = {}
95+
96+ def __call__(self, x: Tensor, ctx: HookContext) -> Tensor:
97+ self.store[ctx.name] = x.detach().clone()
98+ return x
99+
100+
101+def _pos_mask(x: Tensor, positions: Tensor | list[int] | slice | None) -> Tensor:
102+ """Boolean mask [B,T] selecting token positions (per-example positions or shared slice)."""
103+ B, T = x.shape[0], x.shape[1]
104+ mask = torch.zeros(B, T, dtype=torch.bool, device=x.device)
105+ if positions is None:
106+ mask[:] = True
107+ elif isinstance(positions, slice):
108+ mask[:, positions] = True
109+ elif isinstance(positions, torch.Tensor) and positions.ndim == 1 and positions.shape[0] == B:
110+ mask[torch.arange(B, device=x.device), positions.to(x.device)] = True
111+ else:
112+ mask[:, list(positions)] = True # type: ignore[arg-type]
113+ return mask
114+
115+
116+class Replace:
117+ """Activation patching: overwrite selected positions with ``source`` values of the same shape."""
118+
119+ def __init__(self, source: Tensor, positions=None, heads: list[int] | None = None, dims: slice | None = None) -> None:
120+ self.source, self.positions, self.heads, self.dims = source, positions, heads, dims
121+
122+ def __call__(self, x: Tensor, ctx: HookContext) -> Tensor:
123+ src = self.source.to(x.device, x.dtype)
124+ mask = _pos_mask(x, self.positions)
125+ out = x.clone()
126+ if self.heads is not None: # x: [B,T,H,dh]
127+ m = mask[:, :, None, None].expand_as(x).clone()
128+ keep = torch.zeros(x.shape[2], dtype=torch.bool, device=x.device)
129+ keep[self.heads] = True
130+ m &= keep[None, None, :, None]
131+ out = torch.where(m, src, x)
132+ elif self.dims is not None:
133+ m = mask[:, :, None].expand_as(x).clone()
134+ dmask = torch.zeros(x.shape[-1], dtype=torch.bool, device=x.device)
135+ dmask[self.dims] = True
136+ m &= dmask[None, None, :]
137+ out = torch.where(m, src, x)
138+ else:
139+ m = mask.reshape(mask.shape + (1,) * (x.ndim - 2)).expand_as(x)
140+ out = torch.where(m, src, x)
141+ return out
142+
143+
144+class Add:
145+ """Additive steering: x[positions] += scale * vector (vector broadcast over the last dim)."""
146+
147+ def __init__(self, vector: Tensor, scale: float = 1.0, positions=None) -> None:
148+ self.vector, self.scale, self.positions = vector, scale, positions
149+
150+ def __call__(self, x: Tensor, ctx: HookContext) -> Tensor:
151+ v = self.vector.to(x.device, x.dtype)
152+ mask = _pos_mask(x, self.positions)
153+ m = mask.reshape(mask.shape + (1,) * (x.ndim - 2)).to(x.dtype)
154+ return x + self.scale * m * v
155+
156+
157+class Zero:
158+ def __init__(self, positions=None, heads: list[int] | None = None) -> None:
159+ self.positions, self.heads = positions, heads
160+
161+ def __call__(self, x: Tensor, ctx: HookContext) -> Tensor:
162+ return Replace(torch.zeros_like(x), self.positions, heads=self.heads)(x, ctx)
163+
164+
165+class GaussianNoise:
166+ """Causal-tracing style corruption with a fixed generator for reproducibility."""
167+
168+ def __init__(self, std: float, positions=None, seed: int = 0) -> None:
169+ self.std, self.positions, self.seed = std, positions, seed
170+
171+ def __call__(self, x: Tensor, ctx: HookContext) -> Tensor:
172+ g = torch.Generator(device="cpu").manual_seed(self.seed)
173+ noise = torch.randn(x.shape, generator=g, dtype=torch.float32).to(x.device, x.dtype) * self.std
174+ mask = _pos_mask(x, self.positions)
175+ m = mask.reshape(mask.shape + (1,) * (x.ndim - 2)).to(x.dtype)
176+ return x + m * noise
177+
178+
179+class ProjectSubspace:
180+ """Remove (mode='ablate') or keep only (mode='keep') the component in span(basis)."""
181+
182+ def __init__(self, basis: Tensor, mode: str = "ablate", positions=None) -> None:
183+ assert mode in {"ablate", "keep"}
184+ q, _ = torch.linalg.qr(basis.T.float() if basis.shape[0] < basis.shape[1] else basis.float())
185+ self.q, self.mode, self.positions = q, mode, positions # [d, r] orthonormal columns
186+
187+ def __call__(self, x: Tensor, ctx: HookContext) -> Tensor:
188+ q = self.q.to(x.device, x.dtype)
189+ proj = (x @ q) @ q.T
190+ new = x - proj if self.mode == "ablate" else proj
191+ mask = _pos_mask(x, self.positions)
192+ m = mask.reshape(mask.shape + (1,) * (x.ndim - 2)).expand_as(x)
193+ return torch.where(m, new, x)
194+
195+
196+class Sequence:
197+ def __init__(self, *fns: Callable[[Tensor, HookContext], Tensor]) -> None:
198+ self.fns = fns
199+
200+ def __call__(self, x: Tensor, ctx: HookContext) -> Tensor:
201+ for f in self.fns:
202+ x = f(x, ctx)
203+ return x
added src/wp_ai_0001/models/toy_transformer.py +116 −0
@@ -0,0 +1,116 @@
1+"""Tier-0 toy decoder-only transformer with explicit hook points (PyTorch, CPU/MPS/CUDA).
2+
3+Pre-LN GPT-style block. Attention is written explicitly (no fused kernel) so that per-head outputs
4+are exposed at ``attn_heads.{l}`` before the output projection, enabling head-level patching.
5+"""
6+from __future__ import annotations
7+
8+import math
9+
10+import torch
11+import torch.nn as nn
12+import torch.nn.functional as F
13+
14+from wp_ai_0001.config import ToyModelConfig
15+from wp_ai_0001.models.hooks import HookContext, HookManager
16+
17+
18+class Attention(nn.Module):
19+ def __init__(self, cfg: ToyModelConfig, layer: int, hooks: HookManager) -> None:
20+ super().__init__()
21+ self.h, self.dh = cfg.n_heads, cfg.d_model // cfg.n_heads
22+ self.qkv = nn.Linear(cfg.d_model, 3 * cfg.d_model, bias=False)
23+ self.o = nn.Linear(cfg.d_model, cfg.d_model, bias=False)
24+ self.layer, self.hooks = layer, hooks
25+
26+ def forward(self, x: torch.Tensor, ctx: HookContext) -> torch.Tensor:
27+ B, T, D = x.shape
28+ q, k, v = self.qkv(x).split(D, dim=-1)
29+ q = q.view(B, T, self.h, self.dh).transpose(1, 2)
30+ k = k.view(B, T, self.h, self.dh).transpose(1, 2)
31+ v = v.view(B, T, self.h, self.dh).transpose(1, 2)
32+ att = (q @ k.transpose(-2, -1)) / math.sqrt(self.dh)
33+ causal = torch.ones(T, T, dtype=torch.bool, device=x.device).tril()
34+ att = att.masked_fill(~causal, float("-inf")).softmax(-1)
35+ heads = (att @ v).transpose(1, 2) # [B,T,H,dh]
36+ heads = self.hooks(f"attn_heads.{self.layer}", heads, ctx)
37+ return self.o(heads.reshape(B, T, D))
38+
39+
40+class Block(nn.Module):
41+ def __init__(self, cfg: ToyModelConfig, layer: int, hooks: HookManager) -> None:
42+ super().__init__()
43+ self.ln1, self.ln2 = nn.LayerNorm(cfg.d_model), nn.LayerNorm(cfg.d_model)
44+ self.attn = Attention(cfg, layer, hooks)
45+ self.mlp = nn.Sequential(nn.Linear(cfg.d_model, cfg.d_ff), nn.GELU(), nn.Linear(cfg.d_ff, cfg.d_model))
46+ self.layer, self.hooks = layer, hooks
47+
48+ def forward(self, x: torch.Tensor, ctx: HookContext) -> torch.Tensor:
49+ l, hk = self.layer, self.hooks
50+ x = hk(f"resid_pre.{l}", x, ctx)
51+ a = hk(f"attn_out.{l}", self.attn(self.ln1(x), ctx), ctx)
52+ x = hk(f"resid_mid.{l}", x + a, ctx)
53+ m = hk(f"mlp_out.{l}", self.mlp(self.ln2(x)), ctx)
54+ return hk(f"resid_post.{l}", x + m, ctx)
55+
56+
57+class ToyTransformer(nn.Module):
58+ def __init__(self, cfg: ToyModelConfig, vocab_size: int) -> None:
59+ super().__init__()
60+ self.cfg, self.vocab_size = cfg, vocab_size
61+ self.hooks = HookManager()
62+ self.tok = nn.Embedding(vocab_size, cfg.d_model)
63+ self.pos = nn.Embedding(cfg.max_seq_len, cfg.d_model)
64+ self.blocks = nn.ModuleList([Block(cfg, i, self.hooks) for i in range(cfg.n_layers)])
65+ self.ln_f = nn.LayerNorm(cfg.d_model)
66+ self.head = nn.Linear(cfg.d_model, vocab_size, bias=False)
67+ if cfg.tie_embeddings:
68+ self.head.weight = self.tok.weight
69+ self.apply(self._init)
70+
71+ @staticmethod
72+ def _init(m: nn.Module) -> None:
73+ if isinstance(m, nn.Linear):
74+ nn.init.normal_(m.weight, std=0.02)
75+ if m.bias is not None:
76+ nn.init.zeros_(m.bias)
77+ elif isinstance(m, nn.Embedding):
78+ nn.init.normal_(m.weight, std=0.02)
79+
80+ @property
81+ def n_params(self) -> int:
82+ return sum(p.numel() for p in self.parameters())
83+
84+ def forward(self, ids: torch.Tensor, positions: torch.Tensor | None = None) -> torch.Tensor:
85+ B, T = ids.shape
86+ ctx = HookContext(name="", positions=positions)
87+ x = self.tok(ids) + self.pos(torch.arange(T, device=ids.device))[None]
88+ x = self.hooks("embed", x, ctx)
89+ for blk in self.blocks:
90+ x = blk(x, ctx)
91+ x = self.hooks("final_norm", self.ln_f(x), ctx)
92+ return self.head(x) # [B,T,V]
93+
94+ def answer_logits(self, ids: torch.Tensor, answer_pos: torch.Tensor) -> torch.Tensor:
95+ logits = self.forward(ids, answer_pos)
96+ return logits[torch.arange(ids.shape[0], device=ids.device), answer_pos]
97+
98+ @torch.no_grad()
99+ def predict(self, ids: torch.Tensor, answer_pos: torch.Tensor) -> torch.Tensor:
100+ return self.answer_logits(ids, answer_pos).argmax(-1)
101+
102+ def hook_names(self) -> list[str]:
103+ names = ["embed"]
104+ for l in range(self.cfg.n_layers):
105+ names += [f"resid_pre.{l}", f"attn_heads.{l}", f"attn_out.{l}", f"resid_mid.{l}", f"mlp_out.{l}", f"resid_post.{l}"]
106+ return names + ["final_norm"]
107+
108+
109+def resolve_device(name: str = "auto") -> torch.device:
110+ if name != "auto":
111+ return torch.device(name)
112+ if torch.cuda.is_available():
113+ return torch.device("cuda")
114+ if torch.backends.mps.is_available():
115+ return torch.device("mps")
116+ return torch.device("cpu")
added src/wp_ai_0001/orchestration/__init__.py +0 −0
added src/wp_ai_0001/orchestration/registry.py +23 −0
@@ -0,0 +1,23 @@
1+"""Append-only experiment registry (JSON lines). One record per run status change."""
2+from __future__ import annotations
3+
4+import json
5+from pathlib import Path
6+from typing import Any
7+
8+from wp_ai_0001.instrumentation.provenance import PROJECT_ROOT, utc_now
9+
10+REGISTRY_PATH = PROJECT_ROOT / "experiments" / "registry" / "runs.jsonl"
11+
12+
13+def record(run_id: str, experiment_id: str, status: str, **fields: Any) -> None:
14+ REGISTRY_PATH.parent.mkdir(parents=True, exist_ok=True)
15+ rec = {"utc": utc_now(), "run_id": run_id, "experiment_id": experiment_id, "status": status, **fields}
16+ with open(REGISTRY_PATH, "a") as f:
17+ f.write(json.dumps(rec, sort_keys=True, default=str) + "\n")
18+
19+
20+def load() -> list[dict[str, Any]]:
21+ if not REGISTRY_PATH.exists():
22+ return []
23+ return [json.loads(line) for line in REGISTRY_PATH.read_text().splitlines() if line.strip()]
added src/wp_ai_0001/statistics/__init__.py +0 −0
added src/wp_ai_0001/statistics/bootstrap.py +54 −0
@@ -0,0 +1,54 @@
1+"""Cluster bootstrap for accuracies and paired differences.
2+
3+Items nested in generating templates (clusters) are not independent; the bootstrap resamples
4+clusters with replacement. Paired differences use identical items for both conditions.
5+"""
6+from __future__ import annotations
7+
8+from dataclasses import dataclass
9+
10+import numpy as np
11+
12+
13+@dataclass
14+class CI:
15+ estimate: float
16+ low: float
17+ high: float
18+ level: float
19+ n_items: int
20+ n_clusters: int
21+ n_boot: int
22+
23+ def to_dict(self) -> dict:
24+ return self.__dict__.copy()
25+
26+
27+def _cluster_index(clusters: list[str]) -> tuple[np.ndarray, int]:
28+ uniq = {c: i for i, c in enumerate(dict.fromkeys(clusters))}
29+ return np.array([uniq[c] for c in clusters]), len(uniq)
30+
31+
32+def cluster_bootstrap_mean(values: np.ndarray, clusters: list[str], n_boot: int = 2000, level: float = 0.95, seed: int = 0) -> CI:
33+ values = np.asarray(values, dtype=float)
34+ cid, k = _cluster_index(clusters)
35+ rng = np.random.default_rng(seed)
36+ sums = np.bincount(cid, weights=values, minlength=k)
37+ counts = np.bincount(cid, minlength=k).astype(float)
38+ stats = np.empty(n_boot)
39+ for b in range(n_boot):
40+ draw = rng.integers(0, k, k)
41+ stats[b] = sums[draw].sum() / counts[draw].sum()
42+ a = (1 - level) / 2
43+ return CI(float(values.mean()), float(np.quantile(stats, a)), float(np.quantile(stats, 1 - a)), level, len(values), k, n_boot)
44+
45+
46+def cluster_bootstrap_paired_diff(a: np.ndarray, b: np.ndarray, clusters: list[str], n_boot: int = 2000, level: float = 0.95, seed: int = 0) -> CI:
47+ """CI for mean(a) - mean(b) on identical items (paired), resampling clusters."""
48+ return cluster_bootstrap_mean(np.asarray(a, float) - np.asarray(b, float), clusters, n_boot, level, seed)
49+
50+
51+def seeds_summary(values: list[float]) -> dict:
52+ v = np.asarray(values, float)
53+ return {"n_seeds": int(len(v)), "mean": float(v.mean()), "sd": float(v.std(ddof=1)) if len(v) > 1 else None,
54+ "min": float(v.min()), "max": float(v.max())}
added src/wp_ai_0001/training/__init__.py +0 −0
added src/wp_ai_0001/training/train_toy.py +102 −0
@@ -0,0 +1,102 @@
1+"""Deterministic training of a Tier-0 toy model on a single-answer-token micro-world.
2+
3+Loss is the teacher-forced cross-entropy of the answer (scratchpad) tokens only. Training is used to
4+*manufacture* donor competence and recipient weakness; it is documented as a separate stage and
5+is never part of a transplant.
6+"""
7+from __future__ import annotations
8+
9+import json
10+import math
11+import time
12+from pathlib import Path
13+
14+import numpy as np
15+import torch
16+import torch.nn.functional as F
17+
18+from wp_ai_0001.config import ToyModelConfig, TrainConfig
19+from wp_ai_0001.datasets.common import Split, Vocab, encode_split
20+from wp_ai_0001.models.decoding import greedy_decode
21+from wp_ai_0001.models.toy_transformer import ToyTransformer, resolve_device
22+
23+
24+def set_seed(seed: int) -> None:
25+ torch.manual_seed(seed)
26+ np.random.seed(seed)
27+
28+
29+@torch.no_grad()
30+def accuracy(model: ToyTransformer, enc: dict[str, np.ndarray], device: torch.device, bs: int = 1024) -> tuple[float, float, np.ndarray, np.ndarray]:
31+ """Greedy-decoding accuracy: (final-answer acc, full-trace acc, final_correct [N], trace_correct [N])."""
32+ model.eval()
33+ K = enc["answers"].shape[1]
34+ fin, tr = [], []
35+ for i in range(0, len(enc["ids"]), bs):
36+ ids = torch.from_numpy(enc["ids"][i : i + bs]).to(device)
37+ pl = torch.from_numpy(enc["prompt_len"][i : i + bs]).to(device)
38+ na = torch.from_numpy(enc["n_answer"][i : i + bs]).to(device)
39+ gold = torch.from_numpy(enc["answers"][i : i + bs]).to(device)
40+ pred = greedy_decode(model, ids, pl, na, K)
41+ ar = torch.arange(len(pl), device=device)
42+ fin.append((pred[ar, na - 1] == gold[ar, na - 1]).cpu().numpy())
43+ valid = torch.arange(K, device=device)[None] < na[:, None]
44+ tr.append(((pred == gold) | ~valid).all(-1).cpu().numpy())
45+ f, t = np.concatenate(fin), np.concatenate(tr)
46+ return float(f.mean()), float(t.mean()), f, t
47+
48+
49+def masked_loss(model: ToyTransformer, ids: torch.Tensor, loss_mask: torch.Tensor) -> torch.Tensor:
50+ logits = model(ids[:, :-1])
51+ target = ids[:, 1:]
52+ mask = loss_mask[:, :-1]
53+ ce = F.cross_entropy(logits.reshape(-1, logits.shape[-1]), target.reshape(-1), reduction="none").view_as(mask)
54+ return (ce * mask).sum() / mask.sum()
55+
56+
57+def train(model_cfg: ToyModelConfig, train_cfg: TrainConfig, train_split: Split, dev_split: Split, vocab: Vocab, out_dir: Path, log: list[dict] | None = None) -> ToyTransformer:
58+ set_seed(train_cfg.seed)
59+ device = resolve_device(train_cfg.device)
60+ model = ToyTransformer(model_cfg, len(vocab)).to(device)
61+ enc = encode_split(train_split, vocab, model_cfg.max_seq_len)
62+ denc = encode_split(dev_split, vocab, model_cfg.max_seq_len)
63+ ids, lmask = enc["ids"], enc["loss_mask"]
64+ opt = torch.optim.AdamW(model.parameters(), lr=train_cfg.lr, weight_decay=train_cfg.weight_decay, betas=(0.9, 0.98))
65+ g = torch.Generator().manual_seed(train_cfg.seed)
66+ n = len(ids)
67+ t0 = time.time()
68+ history = log if log is not None else []
69+ for step in range(1, train_cfg.steps + 1):
70+ lr = train_cfg.lr * min(1.0, step / train_cfg.warmup_steps) * 0.5 * (1 + math.cos(math.pi * min(1.0, step / train_cfg.steps)))
71+ for grp in opt.param_groups:
72+ grp["lr"] = lr
73+ idx = torch.randint(0, n, (train_cfg.batch_size,), generator=g).numpy()
74+ x = torch.from_numpy(ids[idx]).to(device)
75+ m = torch.from_numpy(lmask[idx]).to(device)
76+ model.train()
77+ loss = masked_loss(model, x, m)
78+ opt.zero_grad(set_to_none=True)
79+ loss.backward()
80+ torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
81+ opt.step()
82+ if step % train_cfg.eval_every == 0 or step == train_cfg.steps:
83+ dev_acc, dev_trace, _, _ = accuracy(model, denc, device)
84+ history.append({"step": step, "loss": float(loss.item()), "dev_acc": dev_acc, "dev_trace_acc": dev_trace, "lr": lr, "elapsed_s": time.time() - t0})
85+ out_dir.mkdir(parents=True, exist_ok=True)
86+ torch.save(model.state_dict(), out_dir / "model.pt")
87+ (out_dir / "model_config.json").write_text(model_cfg.model_dump_json(indent=2))
88+ (out_dir / "train_config.json").write_text(train_cfg.model_dump_json(indent=2))
89+ (out_dir / "vocab.json").write_text(vocab.to_json())
90+ (out_dir / "history.json").write_text(json.dumps(history, indent=2))
91+ return model
92+
93+
94+def load(out_dir: Path, device: torch.device | None = None) -> tuple[ToyTransformer, Vocab]:
95+ cfg = ToyModelConfig.model_validate_json((out_dir / "model_config.json").read_text())
96+ vocab = Vocab.from_json((out_dir / "vocab.json").read_text())
97+ model = ToyTransformer(cfg, len(vocab))
98+ model.load_state_dict(torch.load(out_dir / "model.pt", map_location="cpu"))
99+ model.eval()
100+ if device is not None:
101+ model.to(device)
102+ return model, vocab
added src/wp_ai_0001/transplantation/__init__.py +0 −0
added src/wp_ai_0001/visualization/__init__.py +1 −0
@@ -0,0 +1 @@
1+from wp_ai_0001.visualization.style import CATEGORICAL, DIVERGING_CMAP, ENTITY_COLORS, SEQUENTIAL_CMAP, apply_style, heatmap # noqa: F401
added src/wp_ai_0001/visualization/style.py +60 −0
@@ -0,0 +1,60 @@
1+"""Publication figure style: validated palette (categorical fixed order, one-hue sequential ramp,
2+blue-red diverging pair with neutral grey midpoint), vector output, recessive axes.
3+
4+Palette values follow the project data-visualization reference (light surface). Categorical hues
5+are assigned in fixed slot order to fixed entities and never cycled.
6+"""
7+from __future__ import annotations
8+
9+import matplotlib
10+import matplotlib.pyplot as plt
11+from matplotlib.colors import LinearSegmentedColormap
12+
13+CATEGORICAL = ["#2a78d6", "#eb6834", "#1baf7a", "#eda100", "#e87ba4", "#008300", "#6250d6", "#e34948"]
14+ENTITY_COLORS = {"donor": CATEGORICAL[0], "recipient": CATEGORICAL[1], "graft": CATEGORICAL[2], "sham": CATEGORICAL[3],
15+ "random": CATEGORICAL[4], "chance": "#9a9892"}
16+SEQUENTIAL_STEPS = ["#fcfcfb", "#cde2fb", "#9ec5f4", "#6da7ec", "#3987e5", "#256abf", "#184f95", "#0d366b"]
17+DIVERGING_STEPS = ["#0d366b", "#3987e5", "#9ec5f4", "#f0efec", "#f2a09f", "#e34948", "#8f1d1c"]
18+TEXT_PRIMARY, TEXT_SECONDARY, GRID = "#0b0b0b", "#52514e", "#e2e1dd"
19+
20+SEQUENTIAL_CMAP = LinearSegmentedColormap.from_list("wp_seq_blue", SEQUENTIAL_STEPS)
21+DIVERGING_CMAP = LinearSegmentedColormap.from_list("wp_div_blue_red", DIVERGING_STEPS)
22+
23+COLUMN_WIDTH_IN = 3.35 # single journal column
24+DOUBLE_WIDTH_IN = 6.9
25+
26+
27+def apply_style() -> None:
28+ matplotlib.rcParams.update({
29+ "pdf.fonttype": 42, "ps.fonttype": 42, "svg.fonttype": "none",
30+ "font.family": "sans-serif", "font.size": 8, "axes.titlesize": 8.5, "axes.labelsize": 8,
31+ "xtick.labelsize": 7, "ytick.labelsize": 7, "legend.fontsize": 7,
32+ "axes.edgecolor": GRID, "axes.linewidth": 0.6, "xtick.color": TEXT_SECONDARY, "ytick.color": TEXT_SECONDARY,
33+ "axes.labelcolor": TEXT_PRIMARY, "text.color": TEXT_PRIMARY, "axes.grid": False,
34+ "axes.spines.top": False, "axes.spines.right": False, "legend.frameon": False,
35+ "figure.dpi": 150, "savefig.bbox": "tight", "savefig.pad_inches": 0.02,
36+ })
37+
38+
39+def heatmap(ax, data, row_labels, col_labels, *, cmap, vmin, vmax, cbar_label: str, title: str | None = None, annotate: bool = False):
40+ im = ax.imshow(data, cmap=cmap, vmin=vmin, vmax=vmax, aspect="auto", interpolation="nearest")
41+ ax.set_xticks(range(len(col_labels)))
42+ ax.set_xticklabels(col_labels, rotation=90 if max(len(str(c)) for c in col_labels) > 3 else 0)
43+ ax.set_yticks(range(len(row_labels)))
44+ ax.set_yticklabels(row_labels)
45+ ax.tick_params(length=0)
46+ for s in ax.spines.values():
47+ s.set_visible(False)
48+ if title:
49+ ax.set_title(title, loc="left", color=TEXT_PRIMARY)
50+ if annotate:
51+ for i in range(data.shape[0]):
52+ for j in range(data.shape[1]):
53+ v = data[i, j]
54+ ax.text(j, i, f"{v:.2f}", ha="center", va="center", fontsize=5.5,
55+ color="#ffffff" if (v - vmin) / max(vmax - vmin, 1e-9) > 0.6 else TEXT_PRIMARY)
56+ cb = plt.colorbar(im, ax=ax, fraction=0.046, pad=0.03)
57+ cb.set_label(cbar_label, color=TEXT_SECONDARY)
58+ cb.outline.set_visible(False)
59+ cb.ax.tick_params(length=0, labelsize=6.5, colors=TEXT_SECONDARY)
60+ return im
added tests/causal_sanity/test_hook_noop.py +131 −0
@@ -0,0 +1,131 @@
1+"""Gate C sanity: hooks are a true no-op when disabled/empty, and interventions have effects."""
2+import torch
3+
4+from wp_ai_0001.config import OpSeqTaskConfig, ToyModelConfig
5+from wp_ai_0001.datasets import opseq
6+from wp_ai_0001.datasets.common import encode_split
7+from wp_ai_0001.models.hooks import Add, Capture, GaussianNoise, ProjectSubspace, Replace, Zero
8+from wp_ai_0001.models.toy_transformer import ToyTransformer
9+
10+
11+def _setup(seed=0):
12+ torch.manual_seed(seed)
13+ cfg = OpSeqTaskConfig()
14+ split = opseq.generate(cfg, 64, seed=11, name="sanity")
15+ vocab = opseq.vocab_for(cfg)
16+ enc = encode_split(split, vocab, 24)
17+ model = ToyTransformer(ToyModelConfig(d_model=32, n_layers=2, n_heads=2, d_ff=64, max_seq_len=24), len(vocab)).eval()
18+ return model, torch.from_numpy(enc["ids"]), torch.from_numpy(enc["prompt_len"] - 1), torch.from_numpy(enc["answers"][:, 0])
19+
20+
21+def test_no_hooks_is_bitwise_identity():
22+ model, ids, pos, _ = _setup()
23+ with torch.no_grad():
24+ ref = model(ids, pos)
25+ assert model.hooks.calls == 0
26+ out = model(ids, pos)
27+ assert torch.equal(ref, out)
28+
29+
30+def test_capture_and_disabled_hooks_are_bitwise_identity():
31+ model, ids, pos, _ = _setup()
32+ with torch.no_grad():
33+ ref = model(ids, pos)
34+ cap = Capture()
35+ for n in model.hook_names():
36+ model.hooks.add(n, cap)
37+ out_cap = model(ids, pos)
38+ assert set(cap.store) == set(model.hook_names())
39+ # a modifying hook that is disabled must also be a no-op
40+ model.hooks.add("resid_post.0", Zero())
41+ with model.hooks.disabled():
42+ out_dis = model(ids, pos)
43+ model.hooks.enabled = False
44+ out_dis2 = model(ids, pos)
45+ assert torch.equal(ref, out_cap) and torch.equal(ref, out_dis) and torch.equal(ref, out_dis2)
46+
47+
48+def test_identity_object_returned_when_no_hooks():
49+ model, ids, pos, _ = _setup()
50+ x = torch.randn(2, 3, 32)
51+ assert model.hooks("resid_post.0", x) is x
52+ model.hooks.enabled = False
53+ model.hooks.add("resid_post.0", Zero())
54+ assert model.hooks("resid_post.0", x) is x
55+
56+
57+def test_interventions_change_outputs_reproducibly():
58+ model, ids, pos, _ = _setup()
59+ with torch.no_grad():
60+ ref = model(ids, pos)
61+ model.hooks.add("resid_post.0", Zero(positions=pos))
62+ a = model(ids, pos)
63+ b = model(ids, pos)
64+ assert not torch.equal(ref, a) and torch.equal(a, b)
65+ # unpatched positions are untouched up to the hooked layer: check resid_post.0 itself
66+ model.hooks.clear()
67+ cap = Capture()
68+ model.hooks.add("resid_post.0", cap)
69+ with torch.no_grad():
70+ model(ids, pos)
71+ clean = cap.store["resid_post.0"]
72+ model.hooks.clear()
73+ cap2 = Capture()
74+ model.hooks.add("resid_post.0", Zero(positions=pos))
75+ model.hooks.add("resid_post.0", cap2)
76+ with torch.no_grad():
77+ model(ids, pos)
78+ z = cap2.store["resid_post.0"]
79+ B = ids.shape[0]
80+ assert torch.all(z[torch.arange(B), pos] == 0)
81+ mask = torch.ones_like(clean, dtype=torch.bool)
82+ mask[torch.arange(B), pos] = False
83+ assert torch.equal(z[mask], clean[mask])
84+
85+
86+def test_replace_with_own_activation_is_identity():
87+ model, ids, pos, _ = _setup()
88+ cap = Capture()
89+ model.hooks.add("resid_mid.1", cap)
90+ with torch.no_grad():
91+ ref = model(ids, pos)
92+ model.hooks.clear()
93+ model.hooks.add("resid_mid.1", Replace(cap.store["resid_mid.1"]))
94+ with torch.no_grad():
95+ out = model(ids, pos)
96+ assert torch.allclose(ref, out, atol=0, rtol=0)
97+
98+
99+def test_add_noise_project_have_expected_algebra():
100+ model, ids, pos, _ = _setup()
101+ x = torch.randn(4, 5, 32)
102+ v = torch.randn(32)
103+ y = Add(v, scale=2.0)(x, model.hooks.__class__ and __import__("wp_ai_0001.models.hooks", fromlist=["HookContext"]).HookContext("h"))
104+ assert torch.allclose(y - x, 2.0 * v.expand_as(x))
105+ ctx = __import__("wp_ai_0001.models.hooks", fromlist=["HookContext"]).HookContext("h")
106+ n1, n2 = GaussianNoise(0.5, seed=3)(x, ctx), GaussianNoise(0.5, seed=3)(x, ctx)
107+ assert torch.equal(n1, n2) and not torch.equal(n1, x)
108+ basis = torch.randn(32, 4)
109+ abl = ProjectSubspace(basis, "ablate")(x, ctx)
110+ keep = ProjectSubspace(basis, "keep")(x, ctx)
111+ assert torch.allclose(abl + keep, x, atol=1e-5)
112+ assert torch.allclose(abl @ basis, torch.zeros(4, 5, 4), atol=1e-4)
113+
114+
115+def test_head_patch_touches_only_selected_head():
116+ model, ids, pos, _ = _setup()
117+ cap = Capture()
118+ model.hooks.add("attn_heads.0", cap)
119+ with torch.no_grad():
120+ model(ids, pos)
121+ src = torch.zeros_like(cap.store["attn_heads.0"])
122+ model.hooks.clear()
123+ cap2 = Capture()
124+ model.hooks.add("attn_heads.0", Replace(src, positions=[2], heads=[1]))
125+ model.hooks.add("attn_heads.0", cap2)
126+ with torch.no_grad():
127+ model(ids, pos)
128+ out = cap2.store["attn_heads.0"]
129+ ref = cap.store["attn_heads.0"]
130+ assert torch.all(out[:, 2, 1] == 0)
131+ assert torch.equal(out[:, 2, 0], ref[:, 2, 0]) and torch.equal(out[:, :2], ref[:, :2]) and torch.equal(out[:, 3:], ref[:, 3:])
added tests/integration/.gitkeep +0 −0
added tests/regression/.gitkeep +0 −0
added tests/unit/test_bootstrap.py +18 −0
@@ -0,0 +1,18 @@
1+import numpy as np
2+
3+from wp_ai_0001.statistics.bootstrap import cluster_bootstrap_mean, cluster_bootstrap_paired_diff
4+
5+
6+def test_bootstrap_ci_contains_mean_and_is_deterministic():
7+ rng = np.random.default_rng(0)
8+ vals = (rng.random(1000) < 0.7).astype(float)
9+ clusters = [f"c{i % 50}" for i in range(1000)]
10+ ci1 = cluster_bootstrap_mean(vals, clusters, n_boot=500, seed=1)
11+ ci2 = cluster_bootstrap_mean(vals, clusters, n_boot=500, seed=1)
12+ assert ci1.low <= ci1.estimate <= ci1.high and ci1 == ci2 and ci1.n_clusters == 50
13+
14+
15+def test_paired_diff_zero_when_identical():
16+ vals = np.ones(100)
17+ ci = cluster_bootstrap_paired_diff(vals, vals, [f"c{i%10}" for i in range(100)], n_boot=100)
18+ assert ci.estimate == 0 and ci.low == 0 and ci.high == 0
added tests/unit/test_generators.py +77 −0
@@ -0,0 +1,77 @@
1+import pytest
2+
3+from wp_ai_0001.config import FSATaskConfig, MorphTaskConfig, OpSeqTaskConfig
4+from wp_ai_0001.datasets import fsa, morph, opseq
5+from wp_ai_0001.datasets.common import assert_disjoint, encode_split
6+
7+
8+def test_opseq_validator_and_determinism():
9+ cfg = OpSeqTaskConfig()
10+ a = opseq.generate(cfg, 500, seed=1, name="t")
11+ b = opseq.generate(cfg, 500, seed=1, name="t")
12+ opseq.validate(a, cfg)
13+ assert a.hash == b.hash and len(a) == 500
14+ c = opseq.generate(cfg, 500, seed=2, name="t")
15+ assert c.hash != a.hash
16+
17+
18+def test_opseq_train_excludes_heldout_pairs_and_operators():
19+ cfg = OpSeqTaskConfig(excluded_operators=["qua"])
20+ s = opseq.generate(cfg, 1000, seed=3, name="train", mode="train")
21+ pairs = set(map(tuple, cfg.held_out_pairs))
22+ for e in s.examples:
23+ ops = list(e.tokens[2:-2])
24+ assert "qua" not in ops
25+ assert not any((x, y) in pairs for x, y in zip(ops, ops[1:]))
26+
27+
28+def test_opseq_comp_and_require():
29+ cfg = OpSeqTaskConfig()
30+ s = opseq.generate(cfg, 300, seed=4, name="comp", mode="comp", min_len=2)
31+ pairs = set(map(tuple, cfg.held_out_pairs))
32+ assert all(any((x, y) in pairs for x, y in zip(e.tokens[2:-2], e.tokens[3:-2])) for e in s.examples)
33+ t = opseq.generate(cfg, 300, seed=5, name="target", mode="any", require_operator="qua")
34+ assert all("qua" in e.tokens for e in t.examples)
35+ u = opseq.generate(cfg, 300, seed=6, name="control", mode="any", forbid_operator="qua")
36+ assert all("qua" not in e.tokens for e in u.examples)
37+
38+
39+def test_disjointness_enforced():
40+ cfg = OpSeqTaskConfig()
41+ a = opseq.generate(cfg, 2000, seed=7, name="a")
42+ b = opseq.generate(cfg, 2000, seed=8, name="b", exclude_hashes=a.item_hashes())
43+ assert_disjoint(a, b)
44+ with pytest.raises(AssertionError):
45+ assert_disjoint(a, a)
46+
47+
48+def test_encode_split_positions():
49+ cfg = OpSeqTaskConfig()
50+ s = opseq.generate(cfg, 50, seed=9, name="e")
51+ v = opseq.vocab_for(cfg)
52+ enc = encode_split(s, v, 24)
53+ for i, e in enumerate(s.examples):
54+ pl = enc["prompt_len"][i]
55+ assert v.itos[enc["ids"][i, pl - 1]] == "="
56+ assert v.decode(enc["answers"][i, : enc["n_answer"][i]]) == list(e.answers)
57+ assert enc["loss_mask"][i].sum() == e.n_answer and enc["loss_mask"][i, pl - 1] == 1
58+ assert v.itos[enc["ids"][i, pl + e.n_answer - 1]] == e.answer
59+
60+
61+def test_fsa_and_morph():
62+ f = FSATaskConfig()
63+ s = fsa.generate(f, 300, seed=1, name="f")
64+ fsa.validate(s, f)
65+ m = MorphTaskConfig()
66+ t = morph.generate(m, 300, seed=1, name="m")
67+ morph.validate(t, m)
68+ assert len(set(e.answer for e in t.examples)) == 3
69+
70+
71+def test_trace_stride():
72+ cfg = OpSeqTaskConfig(trace_stride=2, min_len=1, max_len=5)
73+ s = opseq.generate(cfg, 300, seed=12, name="stride")
74+ opseq.validate(s, cfg)
75+ for e in s.examples:
76+ k = e.meta["len"]
77+ assert e.n_answer == len(set(list(range(1, k, 2)) + [k - 1]))
78
Menu

Theme