thegovind commited on
Commit
bb102c0
·
verified ·
1 Parent(s): 1919b32

Publish the weight-space programming labs

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +21 -0
  2. README.md +30 -5
  3. code/.gitignore +11 -0
  4. code/NOTICE.md +21 -0
  5. code/QUICKSTART.md +87 -0
  6. code/README.md +155 -0
  7. code/configs/capstone.json +4392 -0
  8. code/configs/rebuilt.json +2197 -0
  9. code/configs/variants.json +29 -0
  10. code/data/README.md +21 -0
  11. code/labs/data/astra_pairs.json +24 -0
  12. code/labs/lab0_toy.md +157 -0
  13. code/labs/lab0_toy.py +86 -0
  14. code/labs/lab1_diff.md +166 -0
  15. code/labs/lab1_diff.py +93 -0
  16. code/labs/lab2_reproduce.md +314 -0
  17. code/labs/lab3_rebuild.md +244 -0
  18. code/labs/lab3_rebuild.py +191 -0
  19. code/labs/lab4_dose.md +369 -0
  20. code/labs/lab4_endings.py +119 -0
  21. code/labs/lab5_decoding.md +320 -0
  22. code/labs/lab6_steering.md +302 -0
  23. code/labs/lab6_steering.py +431 -0
  24. code/labs/lab7_side_effects.md +287 -0
  25. code/labs/lab7_side_effects.py +175 -0
  26. code/labs/lab8_capstone.md +336 -0
  27. code/labs/lab8_capstone.py +348 -0
  28. code/labs/peek.py +89 -0
  29. code/plans/extra.json +8 -0
  30. code/plans/lab3-tries.json +29 -0
  31. code/plans/quick.json +23 -0
  32. code/plans/sampler.json +29 -0
  33. code/plans/trainer.json +11 -0
  34. code/reference/theory-guide.md +1791 -0
  35. code/results/edit_info.json +2223 -0
  36. code/results/figures/lab0_toy.png +0 -0
  37. code/results/figures/lab1_diff.png +0 -0
  38. code/results/figures/lab2_benchmarks.png +0 -0
  39. code/results/figures/lab2_lengths.png +0 -0
  40. code/results/figures/lab3_neuron_scores.png +0 -0
  41. code/results/figures/lab3_where_the_loop_grows.png +0 -0
  42. code/results/figures/lab4_controls.png +0 -0
  43. code/results/figures/lab4_dose.png +3 -0
  44. code/results/figures/lab5_decoding.png +0 -0
  45. code/results/figures/lab6_finished_vs_loops.png +0 -0
  46. code/results/figures/lab6_loops_summary.png +3 -0
  47. code/results/figures/lab6_real_loop.png +3 -0
  48. code/results/figures/lab7_side_effects.png +0 -0
  49. code/results/figures/lab8_data_needed.png +3 -0
  50. code/results/figures/lab8_loop_onset.png +3 -0
.gitattributes CHANGED
@@ -33,3 +33,24 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ code/results/figures/lab4_dose.png filter=lfs diff=lfs merge=lfs -text
37
+ code/results/figures/lab6_loops_summary.png filter=lfs diff=lfs merge=lfs -text
38
+ code/results/figures/lab6_real_loop.png filter=lfs diff=lfs merge=lfs -text
39
+ code/results/figures/lab8_data_needed.png filter=lfs diff=lfs merge=lfs -text
40
+ code/results/figures/lab8_loop_onset.png filter=lfs diff=lfs merge=lfs -text
41
+ code/results/figures/lab8_one_answer.png filter=lfs diff=lfs merge=lfs -text
42
+ code/results/lab1/try-qwen35-all2/lab1_diff.png filter=lfs diff=lfs merge=lfs -text
43
+ code/results/lab3/try-layers15-19-21/lab3_neuron_scores.png filter=lfs diff=lfs merge=lfs -text
44
+ code/results/lab6/try-max-samples-10/lab6_loops_summary.png filter=lfs diff=lfs merge=lfs -text
45
+ code/results/lab6/try-max-samples-10/lab6_real_loop.png filter=lfs diff=lfs merge=lfs -text
46
+ code/results/lab6/try-smooth-16/lab6_loops_summary.png filter=lfs diff=lfs merge=lfs -text
47
+ code/results/lab6/try-smooth-16/lab6_real_loop.png filter=lfs diff=lfs merge=lfs -text
48
+ code/results/lab8/try-alpha-8/lab8_data_needed.png filter=lfs diff=lfs merge=lfs -text
49
+ code/results/lab8/try-alpha-8/lab8_loop_onset.png filter=lfs diff=lfs merge=lfs -text
50
+ code/results/lab8/try-alpha-8/lab8_one_answer.png filter=lfs diff=lfs merge=lfs -text
51
+ code/results/lab8/try-max-samples-16/lab8_data_needed.png filter=lfs diff=lfs merge=lfs -text
52
+ code/results/lab8/try-max-samples-16/lab8_loop_onset.png filter=lfs diff=lfs merge=lfs -text
53
+ code/results/lab8/try-max-samples-16/lab8_one_answer.png filter=lfs diff=lfs merge=lfs -text
54
+ code/results/lab8/try-split-by-problem/lab8_data_needed.png filter=lfs diff=lfs merge=lfs -text
55
+ code/results/lab8/try-split-by-problem/lab8_loop_onset.png filter=lfs diff=lfs merge=lfs -text
56
+ code/results/lab8/try-split-by-problem/lab8_one_answer.png filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,10 +1,35 @@
1
  ---
2
- title: Weight Space Programming
3
- emoji: 😻
4
- colorFrom: blue
5
- colorTo: blue
6
  sdk: static
 
7
  pinned: false
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Weight-Space Programming Lab
3
+ emoji: 🧮
4
+ colorFrom: gray
5
+ colorTo: indigo
6
  sdk: static
7
+ app_file: index.html
8
  pinned: false
9
+ short_description: "Hands-on labs: fix a small LLM by editing its weights."
10
  ---
11
 
12
+ # Weight-space programming lab
13
+
14
+ This Space holds nine hands-on labs that reproduce and probe Experiment 8 of "Sparks of Weight-Space Programming". In that experiment, an AI agent fixed the looping of Qwen3.5-0.8B. It edited 131,072 weights with arithmetic and did no training.
15
+
16
+ - Read the labs on the [live site](https://thegovind-weight-space-programming.static.hf.space/index.html).
17
+ - Start with the [Quick start](https://thegovind-weight-space-programming.static.hf.space/quickstart.html) page.
18
+ - Browse the code in the [code/ folder](https://huggingface.co/spaces/thegovind/weight-space-programming/tree/main/code/).
19
+
20
+ ## Get the code
21
+
22
+ ```bash
23
+ pip install -U huggingface_hub
24
+ hf download thegovind/weight-space-programming --repo-type space \
25
+ --include "code/*" --local-dir ~/weight-space-programming
26
+ cd ~/weight-space-programming/code
27
+ ```
28
+
29
+ ## Credits and licenses
30
+
31
+ - The project reproduces Experiment 8 of the blog post ["Sparks of Weight-Space Programming"](https://rikiyat.github.io/blog/weight-space-programming/) by the authors of "LLMs can understand and write neural network weights". It uses their [public logs](https://github.com/RikiyaT/lm_understand-logs).
32
+ - The agent's 8 texts in `code/labs/data/astra_pairs.json` and the quoted log excerpts come from [RikiyaT/lm_understand-logs](https://github.com/RikiyaT/lm_understand-logs). They are copyright the authors of "LLMs can understand and write neural network weights" and licensed [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). We extracted them from the logs. The selection and the JSON layout are ours. The labs, measurements and figures come from our own runs.
33
+ - `code/wsp/grading.py` includes answer-extraction and grading code copied from the paper's harness (`exp8_math/code/mathqa.py`). It is used under the MIT License. See [code/NOTICE.md](https://huggingface.co/spaces/thegovind/weight-space-programming/blob/main/code/NOTICE.md).
34
+ - Benchmarks: MATH-500 (from MATH, MIT License), GSM8K (MIT License) and IFEval (Apache License 2.0). The benchmark files are not included. `code/scripts/prepare_data.py` downloads them again from their public copies on Hugging Face. See [code/data/README.md](https://huggingface.co/spaces/thegovind/weight-space-programming/blob/main/code/data/README.md).
35
+ - No license is granted yet for this project's own code and text.
code/.gitignore ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # The paper's logs: clone them yourself (see the README).
2
+ external/
3
+ # Full answer texts stay on the GPU machines.
4
+ *.texts.jsonl.gz
5
+ __pycache__/
6
+ *.pyc
7
+ .venv/
8
+ # The site build output, its npm packages and the diagram renderer's scratch folder.
9
+ _site/
10
+ site/node_modules/
11
+ site/.render-tmp/
code/NOTICE.md ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Notice
2
+
3
+ `wsp/grading.py` includes answer-extraction and grading code copied from the paper's harness
4
+ (`exp8_math/code/mathqa.py` in [RikiyaT/lm_understand-logs](https://github.com/RikiyaT/lm_understand-logs)), used under the MIT License:
5
+
6
+ MIT License
7
+
8
+ Copyright (c) 2026 the authors of "LLMs can understand and write neural network weights"
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
11
+ documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
12
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
13
+ persons to whom the Software is furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
16
+ Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
19
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
21
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
code/QUICKSTART.md ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Quick start: a 2-hour path through the labs
2
+
3
+ **Big thing:** Run the core weight-space programming checks in 2 hours (about 10 minutes of GPU compute), from a 5-second toy matrix on this machine to the full benchmark, dose, decoding, and capstone results.
4
+
5
+ **Get the code first.** This site is a public copy of the project. Download the code to your own machine:
6
+
7
+ ```bash
8
+ pip install -U huggingface_hub
9
+ hf download thegovind/weight-space-programming --repo-type space \
10
+ --include "code/*" --local-dir ~/weight-space-programming
11
+ cd ~/weight-space-programming/code
12
+ ```
13
+
14
+ Commands for this machine run from that `code` folder. Commands on the GPU machines run from `/nvme/wsp-lab/code`, where the setup copies the code.
15
+
16
+ **Machines.** Step 1 runs on any laptop. The other steps ran on two GPU machines, each with 8 NVIDIA A100 80 GB GPUs, and most steps use only 1 or 2 of them. These pages call the two machines `trainer` and `sampler`, so use your own host names wherever you see those. To set up a machine, follow "Cloning the paper's logs" and then "Setting up a new machine", both under [Running things](README.md#running-things). The setup puts everything under `/nvme/wsp-lab` (about 15 GB) and needs no root access.
17
+
18
+ **Data.** The problem files in `data/` are not included. [`data/README.md`](data/README.md) explains why and shows how to rebuild them.
19
+
20
+ > **Before you start (VM access and environment):**
21
+ > 1. **SSH hosts:** Connect with `ssh trainer` or `ssh sampler` (8x A100 80 GB each).
22
+ > 2. **Environment line (run first in every VM shell):** `. /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code`
23
+ > 3. **Which terminal:** Steps 1 and 7 run on this machine. Step 2 runs on `trainer` and ends with `exit`. Steps 3 to 6 share one `sampler` terminal, and Step 6 ends with `exit`. The two VMs cannot reach each other, so always `ssh` from this machine.
24
+
25
+ ### Step 1: Run the toy edit on a 4 x 6 matrix (15 minutes)
26
+ - **Where:** This machine (CPU, 5 seconds to run, 15 minutes with the sheet).
27
+ ```bash
28
+ cd ~/weight-space-programming/code
29
+ uv run --with numpy --with matplotlib python labs/lab0_toy.py
30
+ ```
31
+ - **What to look at:** Check the 4 numbered outputs (`singular values of W' - W: [3.84 0. 0. 0.]` and `|W'a - Wa| = 0.000` when the chosen neurons are quiet vs `4.261` when firing) and open `results/figures/lab0_toy.png`. Singular values measure the sizes of independent pieces of a matrix change; only one is nonzero here. Read [`labs/lab0_toy.md`](labs/lab0_toy.md) for why the edit is rank one (one column vector times one row vector) and acts like steering (adding a vector to the model's internal state).
32
+
33
+ ### Step 2: Run the 5-minute quick test and inspect a looping answer (15 minutes)
34
+ - **Where:** `trainer` (2 GPUs for 5 minutes, then CPU for `peek.py`). If our finished answers for `quick/base` (the unedited model) and `quick/p50c` (the released edit) are on the machine, the run skips them. To have vLLM (the program that generates the answers) write fresh ones, move `/nvme/wsp-lab/runs/quick` aside first.
35
+ ```bash
36
+ ssh trainer
37
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
38
+ nvidia-smi # check which GPUs are free
39
+ mv /nvme/wsp-lab/runs/quick /nvme/wsp-lab/runs/quick-ours # optional: only if generating fresh answers
40
+ python scripts/run_jobs.py plans/quick.json --gpus 0,1
41
+ CUDA_VISIBLE_DEVICES="" python labs/peek.py quick/base
42
+ CUDA_VISIBLE_DEVICES="" python labs/peek.py quick/p50c
43
+ CUDA_VISIBLE_DEVICES="" python labs/peek.py quick/base --id gsm8k_train_5628
44
+ CUDA_VISIBLE_DEVICES="" python labs/peek.py quick/p50c --id gsm8k_train_5628
45
+ exit # back to this machine
46
+ ```
47
+ - **What to look at:** On our run (`quick-ours` if you moved it), `quick/base` gets 6 of 32 right (`18.8%`) with 27 of 32 cut in a verbatim loop, while `quick/p50c` gets 21 of 32 (`65.6%`, `+46.9` points) with 3 cut and 0 loops. On `gsm8k_train_5628` (truth `240`), `quick/base` repeats an 83-token block about `292` times from token 8,568 to 32,768, while `quick/p50c` finishes in `5,825` tokens after closing `</think>` and writing `\boxed{240}`. See [`labs/lab2_reproduce.md`](labs/lab2_reproduce.md).
48
+
49
+ ### Step 3: Find the edit in the weight files on CPU (15 minutes)
50
+ - **Where:** `sampler`, from this machine (CPU only, under 1 minute). Keep this terminal open for Steps 4 to 6.
51
+ ```bash
52
+ ssh sampler
53
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
54
+ python labs/lab1_diff.py
55
+ ```
56
+ - **What to look at:** Confirm that only 2 of 488 tensors (arrays of weights) differ (`layers.15` and `layers.19` `mlp.down_proj.weight`), all changes sit in 64 columns per layer, the first singular value holds `0.999994` and `0.999991` of the squared change, and the fitted $\alpha$ (edit strength) is `6.0003` and `6.0002`. These paired numbers refer to the two layers in the order listed. Read [`labs/lab1_diff.md`](labs/lab1_diff.md) and inspect `results/figures/lab1_diff.png`.
57
+
58
+ ### Step 4: Rebuild the edit from the agent's 8 texts (20 minutes)
59
+ - **Where:** the same `sampler` terminal (1 GPU, about 1 minute). Run this before Step 5 because Lab 6 reads which way $u$ (the direction from productive toward looping text in the model's internal state) points from `results/lab3/rebuild.json`.
60
+ ```bash
61
+ CUDA_VISIBLE_DEVICES=7 python labs/lab3_rebuild.py
62
+ ```
63
+ - **What to look at:** Verify that blocks 15 and 19 have the largest MLP gap growth (`0.263` and `0.249`), the rebuilt directions match the released $u$ to cosine `+0.99998` and `-0.99998`, and the top 64 neuron scores match `64/64` released columns in both layers. Read [`labs/lab3_rebuild.md`](labs/lab3_rebuild.md) and check `results/figures/lab3_where_the_loop_grows.png` and `lab3_neuron_scores.png`.
64
+
65
+ ### Step 5: Verify that the weight edit is activation steering (15 minutes)
66
+ - **Where:** the same `sampler` terminal (1 GPU, about 1 minute for parts 1 and 2). Run `--parts 3,4` (about 3 minutes) to trace 48 real loops and the 26 correct finished answers from `/nvme/wsp-lab/runs/build/base` on `sampler`.
67
+ ```bash
68
+ CUDA_VISIBLE_DEVICES=7 python labs/lab6_steering.py
69
+ CUDA_VISIBLE_DEVICES=7 python labs/lab6_steering.py --parts 3,4 # optional
70
+ ```
71
+ - **What to look at:** Editing `down_proj` vs subtracting $\alpha s(t) u$ in a hook (a function attached to a layer) matches to `3.33e-16` in float64 (`1.4e-8` mean KL in float32). Float64 and float32 are number formats with different precision; KL measures the change in next-token probabilities. Over the last 36 tokens of the agent's 8 texts, $s(t)$ (how hard the chosen neurons push toward looping on each token) is near zero on productive text (`0.00` to `0.04` at layer 15) and positive on looping text (`0.19` to `0.42`). Parts 3 and 4 show the same split on real answers: at layer 19, $s(t)$ averages `0.76` inside loops and about `0.1` on answers that went well (small, not zero). Read [`labs/lab6_steering.md`](labs/lab6_steering.md), which covers `results/figures/lab6_real_loop.png` and `lab6_finished_vs_loops.png`.
72
+
73
+ ### Step 6: Measure side effects on ordinary text and selective KL (15 minutes)
74
+ - **Where:** the same `sampler` terminal (1 GPU, about 1.5 minutes). Then leave the VM.
75
+ ```bash
76
+ CUDA_VISIBLE_DEVICES=7 python labs/lab7_side_effects.py
77
+ exit # back to this machine
78
+ ```
79
+ - **What to look at:** `p50c` and `rebuilt` raise WikiText-2 perplexity (prediction uncertainty on ordinary text, lower is better; base `20.009`) by `+0.79%` while shifting predictions 2.5 times more on looping texts than on productive ones (`0.0241` vs `0.0097`, measured by KL divergence, the change in next-token probabilities). By contrast, `a12` (a stronger edit) costs `+14.34%`, and `randdir`/`randcols` (random-direction and random-neuron controls) shift looping text no more than productive text (`randdir`: `0.0039` vs `0.0048`). Read [`labs/lab7_side_effects.md`](labs/lab7_side_effects.md) and view `results/figures/lab7_side_effects.png`.
80
+
81
+ ### Step 7: Read the finished results of Labs 2, 4, 5, and 8 (30 minutes)
82
+ - **Where:** This machine (no GPU; [`results/summary.md`](results/summary.md) and `results/figures/` hold all runs).
83
+ ```bash
84
+ cd ~/weight-space-programming/code
85
+ ls results/figures/
86
+ ```
87
+ - **What to look at:** Read [`labs/lab2_reproduce.md`](labs/lab2_reproduce.md) (`lab2_benchmarks.png`, `lab2_lengths.png`), [`labs/lab4_dose.md`](labs/lab4_dose.md) (`lab4_dose.png`, `lab4_controls.png`, including why $\alpha = -4$ gains `+7.6` points by breaking loops), [`labs/lab5_decoding.md`](labs/lab5_decoding.md) (`lab5_decoding.png`, where turning thinking off reaches `52.1%` at 2,270 tokens), and [`labs/lab8_capstone.md`](labs/lab8_capstone.md) (`lab8_loop_onset.png`, `lab8_data_needed.png`, `lab8_one_answer.png`, where recipe B built from 225 of the model's own loops reaches `58.2%` on `dev256`). To re-run any sheet's GPU jobs, use the move-aside rule in [`README.md`](README.md).
code/README.md ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Weight-space programming labs
2
+
3
+ **Big thing:** These nine hands-on labs reproduce and take apart Experiment 8 of the MIT CSAIL blog post "Sparks of Weight-Space Programming" on two 8-GPU A100 machines. In that experiment, an AI agent fixed a small language model (Qwen3.5-0.8B) that got stuck repeating itself on math problems by editing 131,072 of its 873,438,784 weights with arithmetic, without training.
4
+
5
+ **Why it matters:** Most model fixes change the prompt or sampling settings, or run gradient descent (training that adjusts weights to reduce errors). Here you inspect the `131,072` edited weights (`130,188` change after bf16 rounding, which stores numbers at reduced precision), rebuild the edit from 8 short texts in one minute, prove that the weight edit equals an input-driven steering vector (a change to the model's internal numbers whose strength depends on the input), and build your own edit from the model's failures.
6
+
7
+ ## The story
8
+
9
+ Weight-space programming changes what a trained neural network does by reading and editing its weight matrices with arithmetic instead of gradient training.
10
+
11
+ In Experiment 8 (2 Nov 2026), an AI agent (GPT-6 Astra) got a CPU-only Python session with Qwen3.5-0.8B, an open model with about 0.87 billion weights across 24 repeated layers (blocks). It had practice math problems with answers and a budget of 50 forward passes. A forward pass runs inputs through the network to read internal activations (numbers computed as the input passes through each layer) or generate answers. One pass could answer up to 512 problems or read activations on up to 1,024 texts, and all training was forbidden.
12
+
13
+ With thinking on, the model writes a `<think>...</think>` scratchpad before its final answer. In this mode, the unedited model kept falling into verbatim loops, repeating the same block of tokens (word pieces) over and over until it hit the 32,768-token cap: 85 of its first 96 practice answers hit that limit.
14
+
15
+ The agent wrote 4 productive and 4 looping scratchpads and compared the model's activations on them. That contrast gave a unit direction vector $u$: a list of 1,024 numbers whose overall length is 1, pointing from productive text toward looping text. It then applied a rank-one projection edit (one column vector times one row vector) in blocks 15 and 19. Each block's MLP (multi-layer perceptron, the block's second half) has an output matrix `down_proj` of shape 1,024 by 3,584. Each column is the 1,024-number vector one neuron adds to the residual stream (the running list of numbers each token carries from block to block) per unit of activation. The agent picked the 64 neurons that push hardest along $u$ during loops and updated their columns $W_S$ via $W' = W - \alpha u (u^T W_S)$ with strength $\alpha = 6$. That edit touches $2 \times 64 \times 1,024 = 131,072$ weights (`p50c`, from the 50-pass run folder `runs/p50c`).
16
+
17
+ On three unseen benchmarks, the paper reported that `p50c` raised MATH-500 accuracy from 7.9% to 48.1% (answers cut at the cap fell from 93.0% to 33.05%, mean tokens from 30,868 to 22,037), GSM8K from 13.84% to 67.1% (cut 87.5% to 7.24%, mean tokens 29,289 to 11,993), and IFEval instruction-following accuracy from 21.0% to 62.3% (cut 74.9% to 2.2%, mean tokens 25,296 to 4,241).
18
+
19
+ ## What we found
20
+
21
+ - **The benchmark gains reproduce on our hardware** ([`labs/lab2_reproduce.md`](labs/lab2_reproduce.md), [`labs/lab1_diff.md`](labs/lab1_diff.md)). Diffing all 488 weight tensors (arrays of weights) confirms that only `layers.15.mlp.down_proj.weight` and `layers.19.mlp.down_proj.weight` changed, in 64 columns each, with fitted $\alpha = 6.0003$ and $6.0002$. On our GPUs, `p50c` raises MATH-500 from $7.3 \pm 0.9\%$ to $51.8 \pm 1.9\%$, GSM8K from $14.2 \pm 0.8\%$ to $66.9 \pm 1.2\%$, and IFEval from $21.2 \pm 1.4\%$ to $63.4 \pm 1.8\%$.
22
+ - **The 5-minute quick run shows the same effect on 16 problems** ([`QUICKSTART.md`](QUICKSTART.md), [`labs/lab2_reproduce.md`](labs/lab2_reproduce.md)). On the first 16 GSM8K problems of our 256-problem practice test set (`dev256`), the unedited model scores $18.8 \pm 7.7\%$ and `p50c` scores $65.6 \pm 11.8\%$ ($+46.9 \pm 11.6$ percentage points). Answers cut at the 32,768-token cap fall from 27 of 32 (all ending in a verbatim loop) to 3 of 32 (0 in a loop).
23
+ - **Rebuilding the edit from the agent's 8 texts recovers all 64 neurons in both layers** ([`labs/lab3_rebuild.md`](labs/lab3_rebuild.md)). Teacher-forcing the agent's 4 productive and 4 looping texts (feeding fixed text through the model and recording activations with hooks, small functions attached to layers) shows that the MLPs of blocks 15 and 19 widen the looping-minus-productive gap the most. The rebuilt edit recovers directions matching the released $u$ to cosine $+0.99998$ and $-0.99998$ (nearly the same line, with opposite orientation at the second layer), picks the same 64 neurons in both layers, and scores $61.5 \pm 2.8\%$ on `dev256` (matching `p50c`'s $61.5 \pm 2.8\%$). Using only the first 2 text pairs still reaches $62.3 \pm 2.7\%$.
24
+ - **Accuracy rises with $\alpha$ up to a plateau from 4 to 8, while random controls move accuracy by about +2 points** ([`labs/lab4_dose.md`](labs/lab4_dose.md), [`labs/lab7_side_effects.md`](labs/lab7_side_effects.md)). On `dev256`, accuracy goes from $12.1\%$ ($\alpha = 0$, unedited) to $22.7\%$ ($\alpha = 1$, erase), $35.2\%$ ($\alpha = 2$, mirror), $59.2\%$ ($\alpha = 4$), $61.5\%$ ($\alpha = 6$), and $60.7\%$ ($\alpha = 8$). Accuracy then drops to $51.8\%$ at $\alpha = 12$, and WikiText-2 perplexity (prediction uncertainty on ordinary text, lower is better) rises by $+14.34\%$, versus $+0.79\%$ at $\alpha = 6$. Matched-norm controls with a random orthogonal direction (`randdir`) or 64 random neurons (`randcols`) score $14.5\%$ ($+2.3 \pm 1.6$ points) and $14.6\%$ ($+2.5 \pm 1.6$ points).
25
+ - **Setting $\alpha = -4$ gains +7.6 points for the wrong reason** ([`labs/lab4_dose.md`](labs/lab4_dose.md)). Amplifying the 64 neurons' push toward the loop direction 5 times raises `dev256` accuracy from $12.1\%$ to $19.7 \pm 1.9\%$ ($+7.6 \pm 2.2$ points) because 197 of 512 answers abruptly emit `</think>` out of a loop. Yet it loses 24 problems the unedited model solved, drops accuracy among finished answers from $100.0\%$ to $55.4\%$, and raises WikiText-2 perplexity by $+5.39\%$.
26
+ - **Turning thinking off reaches 52.1% using 13 times fewer tokens, and the weight edit adds nothing there** ([`labs/lab5_decoding.md`](labs/lab5_decoding.md)). With thinking on, setting temperature to 1.0 ($27.9\%$) or repetition penalty to 1.1 ($34.4\%$) falls well short of the weight edit ($61.5\%$), and dropping the 1.5 presence penalty cuts accuracy to $4.5\%$, with $99.0\%$ of answers cut. Without thinking, the base model reaches $52.1 \pm 2.7\%$ on `dev256` with 2,270 tokens per answer on average, versus 29,683 with thinking on (16,104 with the edit). Adding `p50c` with thinking off scores $53.5 \pm 2.8\%$ ($+1.4 \pm 1.9$ points), because answers rarely loop without thinking.
27
+ - **The static weight edit is algebraically equal to token-by-token activation steering** ([`labs/lab6_steering.md`](labs/lab6_steering.md), [`labs/lab0_toy.md`](labs/lab0_toy.md)). Multiplying the edited matrix by neuron activations $a(t)$ gives $W' a(t) = W a(t) - \alpha s(t) u$, where $s(t) = u \cdot (W_S a_S(t))$ is one number per token measuring how hard the 64 chosen neurons push along $u$. In 64-bit floats on one layer, the matrix edit and a runtime steering hook match to $3.33 \times 10^{-16}$. On 48 real looping answers from the base model, $s(t)$ stays low early in the answer (0.096 at layer 15, 0.116 at layer 19) and jumps inside the loop (0.424 and 0.758), so the edit pushes back hardest inside loops.
28
+ - **An edit built from the model's own loops reaches 58.2% without hand-written texts** ([`labs/lab8_capstone.md`](labs/lab8_capstone.md)). On 225 looping answers the unedited model wrote for 128 separate practice problems (`build128`), comparing loop tokens with tokens well before the loop (recipe B) matches $u$ to cosine 0.782 and 0.773 and scores $58.2 \pm 2.9\%$ on `dev256` ($+46.1 \pm 2.6$ points). Comparing with the 32 tokens just before the loop starts (recipe A) reaches only $43.0 \pm 2.6\%$ because pre-loop text has already drifted along $u$.
29
+
30
+ ## Start here
31
+
32
+ Start with [**`QUICKSTART.md`**](QUICKSTART.md) for a one-page, 2-hour path through the labs with exact commands and expected numbers.
33
+
34
+ The Quickstart also shows how to download the code from this site. Lab 0 runs on any laptop. The other labs need NVIDIA GPUs; we used two machines with 8 A100 80 GB GPUs each, and most steps use 1 or 2 of them.
35
+
36
+ ## The nine labs
37
+
38
+ ```mermaid
39
+ flowchart LR
40
+ L0["Lab 0: Toy 4x6 matrix"] --> L1["Lab 1: Diff weight files"]
41
+ L1 --> L2["Lab 2: Reproduce benchmarks"]
42
+ L1 --> L3["Lab 3: Rebuild from 8 texts"]
43
+ L3 --> L4["Lab 4: Dose curve and controls"]
44
+ L2 --> L5["Lab 5: Decoding settings"]
45
+ L3 --> L6["Lab 6: Edit equals steering"]
46
+ L4 --> L7["Lab 7: Side effects on text"]
47
+ L6 --> L8["Lab 8: Capstone from own loops"]
48
+ L7 --> L8
49
+ ```
50
+
51
+ | Lab | Title and sheet | Question it answers | What you run | Time and hardware |
52
+ |---|---|---|---|---|
53
+ | 0 | [The toy edit on a 4 x 6 matrix](labs/lab0_toy.md) | What does $W' = W - \alpha u u^T W_S$ do on a matrix small enough to print? | `uv run --with numpy --with matplotlib python labs/lab0_toy.py` | 5 seconds on any laptop CPU |
54
+ | 1 | [Find the edit by diffing the two weight files](labs/lab1_diff.md) | What did the agent change in the 488 weight tensors? | `python labs/lab1_diff.py` | Under 1 minute on VM CPU |
55
+ | 2 | [Reproduce the paper's benchmark numbers](labs/lab2_reproduce.md) | Do the looping failure and the `p50c` fix reproduce on MATH-500, GSM8K, and IFEval? | `python scripts/run_jobs.py plans/trainer.json --gpus 0-7` (or `plans/quick.json --gpus 0,1`) | 5 minutes on 2 A100s (quick run) or 1.5 hours on 8 A100s (`trainer`) |
56
+ | 3 | [Rebuild the edit from the agent's 8 texts](labs/lab3_rebuild.md) | Can we recover layers 15 and 19, $u$, and the 64 neurons from the agent's 8 texts with hooks? | `CUDA_VISIBLE_DEVICES=7 python labs/lab3_rebuild.py` | About 1 minute on 1 A100 GPU (plus 21 minutes on 1 A100 to test on `dev256`) |
57
+ | 4 | [Turn the alpha knob and check controls](labs/lab4_dose.md) | How does accuracy change across $\alpha \in \{-4, 1, 2, 4, 6, 8, 12\}$, single layers, all neurons, and random controls? | `python scripts/run_jobs.py plans/sampler.json --gpus 0-6` and `python labs/lab4_endings.py` | 17 to 45 minutes per run on 1 A100 (`sampler`), seconds on CPU for endings |
58
+ | 5 | [Can decoding settings do the same job?](labs/lab5_decoding.md) | Can temperature, presence or repetition penalties, a 4,096-token cap, or turning thinking off replace the weight edit? | `python scripts/run_jobs.py plans/sampler.json --gpus 0-6` (decoding runs) | About 3 to 49 minutes per run on 1 A100 (`sampler`) |
59
+ | 6 | [The weight edit is steering in disguise](labs/lab6_steering.md) | Is the weight edit identical to subtracting $\alpha s(t) u$, and when does $s(t)$ fire on real loops? | `CUDA_VISIBLE_DEVICES=7 python labs/lab6_steering.py` (and `--parts 3,4`) | About 1 minute on 1 A100 for parts 1 and 2; about 3 minutes for parts 3 and 4 (`sampler`) |
60
+ | 7 | [What else did the edit change?](labs/lab7_side_effects.md) | How much does each edit move WikiText-2 perplexity and KL divergence on productive versus looping text? | `CUDA_VISIBLE_DEVICES=7 python labs/lab7_side_effects.py` | About 1.5 minutes (78 seconds) on 1 A100 GPU |
61
+ | 8 | [Capstone: build your own edit from the model's own loops](labs/lab8_capstone.md) | Can we extract $u$ and 64 neurons from the base model's own looping answers and fix unseen problems? | `CUDA_VISIBLE_DEVICES=7 python labs/lab8_capstone.py` | About 5 minutes on 1 A100 to build, 30 minutes on 2 A100s to test (`sampler`) |
62
+
63
+ ## Project layout
64
+
65
+ - `configs/`: JSON definitions of edited models (`variants.json` for Lab 4 controls and $\alpha$ sweep, `rebuilt.json` from Lab 3, `capstone.json` from Lab 8).
66
+ - `data/`: Prepared JSONL datasets (`dev256.jsonl` with 256 unseen practice problems, `build128.jsonl` with 128 practice problems for Lab 8, plus `math500.jsonl`, `gsm8k.jsonl`, and `ifeval.jsonl`).
67
+ - `external/`: Local checkout of the paper's log repository (`lm_understand-logs`, ignored by `.gitignore`; `exp8_math/` holds the agent's scripts, edit manifest, and work notes).
68
+ - `labs/`: Lab scripts (`lab0_toy.py` through `lab8_capstone.py`), answer inspection tools (`peek.py`, `lab4_endings.py`), lab sheets (`lab0_toy.md` through `lab8_capstone.md`), and the agent's 8 contrastive texts (`labs/data/astra_pairs.json`).
69
+ - `plans/`: Generation job plans for `scripts/run_jobs.py` (`quick.json`, `trainer.json`, `sampler.json`, `extra.json`, and `lab3-tries.json`).
70
+ - `reference/`: The first-principles companion guide [`reference/theory-guide.md`](reference/theory-guide.md).
71
+ - `results/`: Saved JSON outputs for Labs 1 through 8 (including committed `results/*/try-*` folders to compare your runs with ours), per-run graded records in `results/runs/`, the master table [`results/summary.md`](results/summary.md) (`results/summary.json`), and all plots in `results/figures/*.png`.
72
+ - `scripts/`: VM setup and sync scripts (`setup_vm.sh`, `sync_vm.sh`, `pull_runs.sh`, `env.sh`), dataset and model builders (`prepare_data.py`, `build_models.py`), the multi-GPU vLLM runner (`run_jobs.py`, `gen_worker.py`), and reporting scripts (`summarize.py`, `plot_runs.py`).
73
+ - `wsp/`: Shared Python library (`hf.py` for model loading and PyTorch hooks, `edits.py` for rank-one edits and SVD recovery, `loops.py` for verbatim loop detection, `protocol.py` for presets and sampler probabilities, `grading.py`, and `summary.py`).
74
+
75
+ ## Running things
76
+
77
+ ```mermaid
78
+ flowchart LR
79
+ Trainer["trainer (8x A100)<br/>/nvme/wsp-lab/runs/<br/>bench/*, quick/*, lab3 tries"] <-->|"sync_vm.sh / pull_runs.sh"| Laptop["This machine (CPU)<br/>Project root and results/<br/>Lab 0 and plotting"]
80
+ Laptop <-->|"sync_vm.sh / pull_runs.sh"| Sampler["sampler (8x A100)<br/>/nvme/wsp-lab/runs/<br/>build/base, dev/*"]
81
+ ```
82
+
83
+ **Cloning the paper's logs.** `.gitignore` ignores `external/`, so the paper's log repository is not committed here. If `external/lm_understand-logs` is missing, clone it from the project root before syncing or building models:
84
+
85
+ ```bash
86
+ git clone https://github.com/RikiyaT/lm_understand-logs external/lm_understand-logs
87
+ git -C external/lm_understand-logs log -1
88
+ ```
89
+
90
+ We used commit `4d09ba6d` (29 October 2026). `scripts/prepare_data.py` and `scripts/build_models.py` read from `external/lm_understand-logs/exp8_math`, and `scripts/sync_vm.sh` copies the parts the GPU machines need (`code`, `practice`, and `runs/p50c`).
91
+
92
+ **The two GPU VMs and environment setup.** GPU work runs on `trainer` and `sampler`, each with 8 NVIDIA A100 80 GB GPUs. All VM files live under `/nvme/wsp-lab` (`venv/`, `models/`, `data/`, `runs/`, `logs/`, and `code/`). Start every VM command by sourcing `env.sh`, which activates Python 3.12 (vLLM 0.33.0, PyTorch 2.14.0, transformers 5.20.2, flash-linear-attention 0.6.0) and keeps caches inside `/nvme/wsp-lab`:
93
+
94
+ ```bash
95
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
96
+ ```
97
+
98
+ **Syncing code and pulling results.** The two VMs cannot reach each other directly, so files move through this machine. From the project root on this machine:
99
+
100
+ ```bash
101
+ scripts/sync_vm.sh trainer sampler # push wsp, scripts, configs, plans, labs, and exp8_math files
102
+ scripts/pull_runs.sh trainer sampler # pull merged.jsonl, summary.json, and *.done.json into results/runs/
103
+ python3 scripts/summarize.py results/runs # rebuild results/summary.md and results/summary.json
104
+ uv run --with matplotlib python scripts/plot_runs.py
105
+ ```
106
+
107
+ `sync_vm.sh` mirrors the five code folders onto the VM with `rsync --delete`, so any file you create or edit only on the VM is deleted or overwritten on the next sync. Edit on this machine first, then sync. The one exception is `configs/capstone.json`, which Lab 8 writes on the VM: syncing never deletes it, but a local copy will overwrite the VM's copy, so pull the VM version first. `pull_runs.sh` copies only run records. Lab scripts write their outputs to `results/labN/` and `results/figures/` on the VM where they ran; copy those back by name:
108
+
109
+ ```bash
110
+ rsync -az sampler:/nvme/wsp-lab/code/results/lab6/ results/lab6/ </dev/null
111
+ rsync -az sampler:/nvme/wsp-lab/code/results/figures/lab6_real_loop.png results/figures/ </dev/null
112
+ ```
113
+
114
+ Copy figures individually instead of syncing the whole folder, since each VM holds only the figures it generated and an older copy would overwrite a newer one.
115
+
116
+ **Setting up a new machine.** Our two VMs (`trainer` and `sampler`) are already set up. To provision a fresh GPU machine (about 10 minutes and 15 GB under `/nvme/wsp-lab`, no root needed), run these commands from the project root (see `scripts/setup_vm.sh`):
117
+
118
+ ```bash
119
+ scp scripts/setup_vm.sh <host>:/tmp/ && ssh <host> bash /tmp/setup_vm.sh
120
+ scripts/sync_vm.sh <host>
121
+ ssh <host> 'cd /nvme/wsp-lab/code && . scripts/env.sh &&
122
+ python scripts/prepare_data.py --exp8 external/lm_understand-logs/exp8_math --out /nvme/wsp-lab/data &&
123
+ python scripts/build_models.py && python scripts/build_models.py --variants configs/rebuilt.json'
124
+ ```
125
+
126
+ **Where full answer texts live.** Full answer texts (`runs/<name>/shard*.texts.jsonl.gz`) are ignored by `.gitignore` and skipped by `scripts/pull_runs.sh`, so they stay on the VM that generated them (while `results/*/try-*` folders are committed to git). Because `labs/peek.py` and `labs/lab4_endings.py` read `.texts.jsonl.gz` files, run them on the VM that produced the run:
127
+ - On `trainer`: `bench/*`, `quick/*`, `dev/rebuilt-pairs2`, `dev/rebuilt-windows36-36`, and `dev/rebuilt-layers15-19-21`.
128
+ - On `sampler`: `build/base` (also copied to `trainer`, where our Lab 6 part 3 runs read it) and every other `dev/*` run (`dev/base`, `dev/p50c`, `dev/rebuilt`, the Lab 4 dose and control runs, the Lab 5 decoding runs, and the Lab 8 `dev/cap*` runs).
129
+
130
+ **Rerunning a finished job.** `scripts/run_jobs.py` skips any shard with a `shard*.done.json` file. Because all runs are already complete on the VMs, rerunning a plan immediately reports `0 shards to run`. To run a job yourself, move our output folder aside on the VM first:
131
+
132
+ ```bash
133
+ mv /nvme/wsp-lab/runs/quick /nvme/wsp-lab/runs/quick-ours
134
+ ```
135
+
136
+ Then run the plan; `python labs/peek.py quick/base` reads your run and `python labs/peek.py quick-ours/base` reads ours.
137
+
138
+ **How to read `results/summary.md`.** Every generation run has one row in [`results/summary.md`](results/summary.md) with these nine columns:
139
+ - `run`: Run identifier (`bench/<model>/<dataset>`, `dev/<variant>`, `build/base`, or `quick/<model>`).
140
+ - `problems x samples`: Distinct problems times sampled answers per problem (for example `256x2` = 512 answers on `dev256`).
141
+ - `accuracy %`: Percentage of correct answers, plus or minus one standard error (SE, typical sampling uncertainty; a gap of 2 SE or more is unlikely to be chance).
142
+ - `cut at the cap %`: Percentage of answers that hit the token limit (32,768 by default) without finishing.
143
+ - `mean tokens`: Average generated tokens per answer.
144
+ - `correct when finished %`: Accuracy among answers that finished before the token limit (97.5% to 100% for the unedited model on math benchmarks, showing its main failure is not finishing).
145
+ - `cut answers ending in repetition %`: Percentage of cap-cut answers ending in repetition: either a word-for-word loop (a block of words repeated at least 3 times in a row) or looser circling (at least half of the 4-word phrase positions in the last 200 words hold a phrase repeated 3 or more times there; see `results/summary.md`).
146
+ - `gain vs baseline (pp)`: Accuracy change in percentage points versus the paired baseline on the same problems, plus or minus paired SE (`dev/*` against `dev/base`, `dev/p50c+nothink` against `dev/base+nothink`, `bench/p50c/X` against `bench/base/X`, and `quick/*` against `quick/base`).
147
+ - `rescued / lost problems`: `rescued` counts problems where the run got at least one answer right and the baseline got none; `lost` counts the reverse.
148
+
149
+ ## Sources
150
+
151
+ - **Blog post:** Rikiya Takahashi, ["Sparks of Weight-Space Programming"](https://rikiyat.github.io/blog/weight-space-programming/) (MIT CSAIL, 2 Nov 2026).
152
+ - **Experiment logs and code:** [`https://github.com/RikiyaT/lm_understand-logs`](https://github.com/RikiyaT/lm_understand-logs) (local copy in `external/lm_understand-logs/`, with Experiment 8 in `external/lm_understand-logs/exp8_math/`).
153
+ - **Released edited model:** [`https://huggingface.co/RikiyaT/Qwen3.5-0.8B-weight-space-programming`](https://huggingface.co/RikiyaT/Qwen3.5-0.8B-weight-space-programming).
154
+ - **Interactive demo Space:** [`https://huggingface.co/spaces/ML-Intern-lab/weight-space-programming-demo`](https://huggingface.co/spaces/ML-Intern-lab/weight-space-programming-demo).
155
+ - **First-principles theory guide:** [`reference/theory-guide.md`](reference/theory-guide.md).
code/configs/capstone.json ADDED
@@ -0,0 +1,4392 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "capA-a6": {
3
+ "about": "Lab 8 recipe A: u and 64 neurons from the base model's own loops (windows just before the loop), alpha 6",
4
+ "layers": {
5
+ "15": {
6
+ "alpha": 6,
7
+ "dir": [
8
+ -0.19642147,
9
+ 0.03620983,
10
+ 0.15367946,
11
+ 0.06497408,
12
+ 0.03057081,
13
+ -0.15543503,
14
+ 0.01351306,
15
+ -0.05578131,
16
+ -0.01658088,
17
+ 0.01544302,
18
+ 0.05921284,
19
+ 0.06464835,
20
+ 0.02034978,
21
+ 0.16278124,
22
+ 0.02162938,
23
+ 0.1445464,
24
+ 0.07718109,
25
+ 0.13444427,
26
+ -0.10969324,
27
+ -0.06296548,
28
+ 0.07529427,
29
+ 0.09108829,
30
+ 0.0843537,
31
+ 0.08002103,
32
+ -0.02452753,
33
+ 0.02076042,
34
+ 0.00602258,
35
+ -0.00699884,
36
+ 0.0446047,
37
+ 0.07109267,
38
+ 0.00730817,
39
+ -0.14850335,
40
+ 0.02196117,
41
+ -0.06272466,
42
+ -0.08063199,
43
+ -0.01963586,
44
+ -0.06232904,
45
+ 0.18691534,
46
+ -0.02989487,
47
+ -0.02307681,
48
+ -0.09384636,
49
+ 0.01869328,
50
+ -0.08615778,
51
+ -0.01516586,
52
+ 0.02899578,
53
+ 0.00498247,
54
+ 0.0662872,
55
+ -0.04447446,
56
+ 0.03304262,
57
+ 0.06277169,
58
+ 0.00584157,
59
+ 0.01387291,
60
+ -0.04707102,
61
+ 0.01839207,
62
+ 0.03542451,
63
+ 0.08492478,
64
+ 0.03609524,
65
+ 0.07776414,
66
+ -0.05058173,
67
+ -0.02504171,
68
+ -0.02387556,
69
+ -0.06714687,
70
+ -0.0498366,
71
+ 0.02289074,
72
+ 0.04711203,
73
+ 0.03032538,
74
+ 0.06156486,
75
+ -0.00659584,
76
+ -0.03459039,
77
+ -0.03311691,
78
+ 0.02699054,
79
+ 0.02207826,
80
+ -0.04704108,
81
+ -0.02012696,
82
+ -0.10484155,
83
+ 0.01460513,
84
+ -0.05287892,
85
+ -0.00257121,
86
+ 0.03665803,
87
+ -8.442e-05,
88
+ 0.0344651,
89
+ 0.09257579,
90
+ 0.02871555,
91
+ -0.05414261,
92
+ 0.01085017,
93
+ 0.12585324,
94
+ 0.05451579,
95
+ -0.0301715,
96
+ 0.00942063,
97
+ 0.04333999,
98
+ 0.05119767,
99
+ -0.05102875,
100
+ -0.04207597,
101
+ 0.02894551,
102
+ 0.05760689,
103
+ -0.01165443,
104
+ -0.0400944,
105
+ 0.01880827,
106
+ -0.00536408,
107
+ 0.00873167,
108
+ -0.06115466,
109
+ -0.00820906,
110
+ -0.01046184,
111
+ 0.00953318,
112
+ -0.04116342,
113
+ -0.00371408,
114
+ 0.05087092,
115
+ 0.02443181,
116
+ -0.02275142,
117
+ 0.0121256,
118
+ 0.03931541,
119
+ 0.01642587,
120
+ 0.00594028,
121
+ -0.01517512,
122
+ -0.00460996,
123
+ 0.05321185,
124
+ -0.00303453,
125
+ -0.04462391,
126
+ -0.02955657,
127
+ 0.06756701,
128
+ 0.01269387,
129
+ 0.00439294,
130
+ -0.01688814,
131
+ 0.00599055,
132
+ -0.05860509,
133
+ -0.00640408,
134
+ -0.02288292,
135
+ -0.01169652,
136
+ 0.01191018,
137
+ 0.06990831,
138
+ -0.03254844,
139
+ 0.01306989,
140
+ 0.0324331,
141
+ 0.01259914,
142
+ 0.012772,
143
+ 0.01562133,
144
+ 0.02279549,
145
+ -0.06174612,
146
+ 0.10596453,
147
+ 0.01665649,
148
+ 0.05464435,
149
+ 0.03043335,
150
+ 0.00261869,
151
+ 0.04120243,
152
+ -0.01908323,
153
+ -0.01475778,
154
+ 0.01024658,
155
+ 0.00602525,
156
+ 0.034647,
157
+ -0.01601795,
158
+ -0.0098665,
159
+ 0.00715095,
160
+ 0.0386551,
161
+ 0.00813517,
162
+ -0.02485966,
163
+ -0.00370103,
164
+ -0.04929582,
165
+ 0.00873361,
166
+ 0.00677907,
167
+ -0.01257964,
168
+ 0.06989538,
169
+ -0.03499186,
170
+ 0.01892586,
171
+ 0.00830449,
172
+ -0.02865393,
173
+ 0.0222334,
174
+ 0.02955934,
175
+ 0.01670051,
176
+ -0.03454896,
177
+ 0.00511459,
178
+ -0.06118869,
179
+ -0.04395781,
180
+ -0.00728062,
181
+ 0.03459383,
182
+ -0.01677352,
183
+ -0.05499182,
184
+ 0.02996176,
185
+ -0.00793099,
186
+ 0.01103673,
187
+ -0.02698723,
188
+ -0.03706948,
189
+ 0.0058741,
190
+ 0.01856517,
191
+ -0.0184078,
192
+ -0.02116514,
193
+ -0.0109629,
194
+ -0.06174179,
195
+ -0.01890302,
196
+ 0.03024366,
197
+ 0.00644585,
198
+ -0.04642926,
199
+ 0.01424284,
200
+ 0.04885947,
201
+ 0.01391168,
202
+ 0.00777366,
203
+ 0.02742206,
204
+ 0.00707238,
205
+ -0.01929868,
206
+ 0.03457972,
207
+ -0.04974987,
208
+ 0.00105937,
209
+ -0.01246471,
210
+ -0.02007423,
211
+ 0.000219,
212
+ 0.02734674,
213
+ 0.05963851,
214
+ 0.03654126,
215
+ -0.0129875,
216
+ -0.04307868,
217
+ -0.00316628,
218
+ 0.01014835,
219
+ -0.00251832,
220
+ -0.03931477,
221
+ -0.00972052,
222
+ -0.00568248,
223
+ 0.00316628,
224
+ 0.00346336,
225
+ 0.03684881,
226
+ -0.00336378,
227
+ -0.05937261,
228
+ -0.00259194,
229
+ 0.00196418,
230
+ 0.02412233,
231
+ 0.00198911,
232
+ 0.00796683,
233
+ -0.03575343,
234
+ 0.0077577,
235
+ -0.02936143,
236
+ -0.04127926,
237
+ -0.03727643,
238
+ -0.04707571,
239
+ 0.04822274,
240
+ 0.0111492,
241
+ 0.0357839,
242
+ -0.01515097,
243
+ -0.0181218,
244
+ 0.02394596,
245
+ 0.02694717,
246
+ -0.035783,
247
+ 0.0281493,
248
+ -0.01310454,
249
+ 0.02221958,
250
+ 0.02990598,
251
+ -0.01907246,
252
+ 0.04340885,
253
+ 0.0115768,
254
+ 0.00898189,
255
+ -0.06997719,
256
+ 0.01230609,
257
+ -0.00925507,
258
+ 0.05233327,
259
+ -0.02267844,
260
+ -0.05345995,
261
+ 0.00955621,
262
+ 0.01006574,
263
+ 0.10575184,
264
+ -0.0681143,
265
+ -0.01524912,
266
+ -0.01217928,
267
+ 0.00278406,
268
+ -0.00136194,
269
+ 0.02119698,
270
+ -0.00788542,
271
+ 0.00100464,
272
+ 0.02298328,
273
+ 0.00925969,
274
+ 0.02953216,
275
+ -0.00704557,
276
+ 0.00990218,
277
+ -0.00553312,
278
+ -0.01701421,
279
+ -0.00671327,
280
+ -0.00800176,
281
+ -0.03354774,
282
+ -0.00255209,
283
+ 0.01456584,
284
+ -0.02189159,
285
+ -0.0115431,
286
+ -0.01603708,
287
+ -0.03584647,
288
+ 0.04013485,
289
+ -0.01710701,
290
+ -0.05626024,
291
+ 0.02047668,
292
+ 0.01898202,
293
+ -0.00604367,
294
+ 0.06082913,
295
+ 0.00710128,
296
+ -0.02479753,
297
+ 0.01761165,
298
+ 0.01341764,
299
+ -0.01323848,
300
+ -0.02196406,
301
+ -0.02244893,
302
+ -0.02151935,
303
+ -0.01825762,
304
+ 0.00698251,
305
+ -0.02361639,
306
+ 0.00114175,
307
+ -0.02762575,
308
+ 0.16642642,
309
+ -0.00773265,
310
+ 0.00217698,
311
+ 0.04885407,
312
+ -0.01389956,
313
+ 0.01700542,
314
+ 0.01211646,
315
+ 0.00832499,
316
+ -0.00395274,
317
+ 0.01165037,
318
+ -0.00379319,
319
+ 0.0132295,
320
+ -0.00373486,
321
+ 0.02491317,
322
+ -0.02714441,
323
+ 0.03632208,
324
+ -0.08712547,
325
+ -0.06559645,
326
+ -0.02572042,
327
+ 0.04448447,
328
+ -0.00740151,
329
+ 0.02038549,
330
+ 0.03381699,
331
+ -0.00414533,
332
+ 0.02067941,
333
+ -0.04021452,
334
+ 0.04214275,
335
+ -0.00379636,
336
+ 0.01809693,
337
+ -0.01443073,
338
+ 0.02897453,
339
+ -0.01338227,
340
+ 0.00038025,
341
+ 0.02115615,
342
+ 0.01903441,
343
+ -0.01938041,
344
+ 0.02999728,
345
+ 0.01710915,
346
+ -0.00018278,
347
+ 0.03359207,
348
+ -0.15671353,
349
+ 0.06294922,
350
+ -0.02928597,
351
+ -0.00917316,
352
+ -0.02511222,
353
+ 0.04767934,
354
+ 0.01702067,
355
+ 0.01511116,
356
+ -0.01941296,
357
+ 0.04285015,
358
+ -0.00288132,
359
+ -0.01123376,
360
+ -0.01124302,
361
+ -0.00706538,
362
+ -0.02094487,
363
+ -0.06044375,
364
+ -0.01406492,
365
+ -0.02563945,
366
+ 0.02763499,
367
+ -0.01873456,
368
+ 0.00962685,
369
+ 0.00600576,
370
+ 0.02742081,
371
+ -0.03916538,
372
+ 0.00491569,
373
+ 0.01069831,
374
+ 0.01297124,
375
+ 0.0156616,
376
+ 0.01917705,
377
+ 0.02480869,
378
+ -0.0106574,
379
+ -0.00759733,
380
+ 0.00509676,
381
+ 0.01549563,
382
+ -0.02802384,
383
+ -0.01822686,
384
+ 0.00713354,
385
+ -0.00076786,
386
+ 0.01179229,
387
+ -0.01646931,
388
+ -0.02218645,
389
+ -0.01974743,
390
+ -0.02323301,
391
+ -0.0161015,
392
+ 0.00569559,
393
+ -0.0318373,
394
+ 0.00214495,
395
+ 0.02582066,
396
+ 0.0120695,
397
+ -0.00323667,
398
+ -0.0181305,
399
+ 0.00456708,
400
+ 0.00088165,
401
+ -0.00181784,
402
+ -0.0323725,
403
+ 0.03889608,
404
+ -0.00593832,
405
+ -0.00065643,
406
+ -0.00818366,
407
+ 0.00936741,
408
+ -0.01945619,
409
+ 0.02748532,
410
+ 0.01352952,
411
+ 0.0104318,
412
+ -0.04723194,
413
+ -0.04889061,
414
+ -0.03551376,
415
+ 0.01957176,
416
+ 0.03616486,
417
+ -0.00134334,
418
+ 0.02413643,
419
+ -0.03108473,
420
+ 0.00653157,
421
+ -0.00807491,
422
+ 0.01170466,
423
+ 0.01715695,
424
+ -0.02850865,
425
+ -0.02597558,
426
+ -0.01232146,
427
+ -0.00075774,
428
+ 0.00142594,
429
+ 0.01942839,
430
+ -0.01098431,
431
+ -0.00176267,
432
+ -0.08830237,
433
+ 0.00896412,
434
+ 0.0134145,
435
+ 0.00978285,
436
+ 0.0046121,
437
+ -0.00490226,
438
+ -0.01901206,
439
+ -0.01399694,
440
+ 0.01516195,
441
+ -0.00334541,
442
+ -0.01679288,
443
+ 0.03267581,
444
+ -0.01082043,
445
+ 0.01129618,
446
+ 0.03566024,
447
+ 0.02445141,
448
+ 0.01040449,
449
+ 0.05409379,
450
+ 0.01997331,
451
+ -0.00243477,
452
+ 0.01394332,
453
+ 0.02577617,
454
+ 0.03131029,
455
+ 0.00099272,
456
+ 0.0257875,
457
+ 0.00535434,
458
+ 0.01975382,
459
+ 0.01685537,
460
+ -0.00746932,
461
+ 0.0180197,
462
+ -0.00473603,
463
+ 0.00482269,
464
+ -0.02610375,
465
+ -0.00263356,
466
+ 0.04015455,
467
+ 0.01175972,
468
+ 0.00810278,
469
+ 0.03288561,
470
+ -0.00391045,
471
+ -0.02447399,
472
+ 0.0166478,
473
+ 0.01949768,
474
+ 0.02602378,
475
+ -0.00563907,
476
+ -0.00467757,
477
+ 0.00259495,
478
+ -0.0125137,
479
+ 0.05420196,
480
+ -0.00224606,
481
+ -0.00816949,
482
+ 0.01439246,
483
+ -0.0126056,
484
+ 0.00068195,
485
+ 0.01309173,
486
+ -0.00121671,
487
+ -0.03312248,
488
+ -0.01834775,
489
+ 0.0016364,
490
+ -0.02588954,
491
+ 0.00480775,
492
+ -0.01843944,
493
+ -0.0041554,
494
+ 0.01282341,
495
+ 0.03219848,
496
+ -0.01286189,
497
+ 0.00484094,
498
+ -0.01602968,
499
+ -0.01150733,
500
+ -0.03129844,
501
+ 0.0092206,
502
+ 0.03618083,
503
+ 0.02785339,
504
+ 0.02549848,
505
+ 0.00144244,
506
+ 0.03333158,
507
+ 0.01289997,
508
+ 0.00317834,
509
+ 0.00372877,
510
+ 1.953e-05,
511
+ 0.00166176,
512
+ 0.00610443,
513
+ 0.01573688,
514
+ 0.00230932,
515
+ -0.00716901,
516
+ -0.01175373,
517
+ 0.00542746,
518
+ -0.01469357,
519
+ -0.0064027,
520
+ -0.00792847,
521
+ 0.0059745,
522
+ -0.02925546,
523
+ 0.01021165,
524
+ -0.06110986,
525
+ 0.01822174,
526
+ 0.00379858,
527
+ 0.02198827,
528
+ 0.04593732,
529
+ 0.01543745,
530
+ -0.00720788,
531
+ 0.00476402,
532
+ -0.0038778,
533
+ 0.01266388,
534
+ -0.00248264,
535
+ 0.05027047,
536
+ -0.01237561,
537
+ -0.00957299,
538
+ -0.01446781,
539
+ -0.00401733,
540
+ 0.01843788,
541
+ -0.02245559,
542
+ 0.00411258,
543
+ -0.02207132,
544
+ 0.01917735,
545
+ 0.01551384,
546
+ 0.06460287,
547
+ 0.02049114,
548
+ 0.00455079,
549
+ -0.00079158,
550
+ -0.00720685,
551
+ -0.02944683,
552
+ -0.00974075,
553
+ -0.00361087,
554
+ -0.02188736,
555
+ 0.00510789,
556
+ 0.00083505,
557
+ 0.01718823,
558
+ -0.01092549,
559
+ -0.06818206,
560
+ 0.02002659,
561
+ -0.00443351,
562
+ 0.02195536,
563
+ -0.03922576,
564
+ 0.02893249,
565
+ -0.00792199,
566
+ -0.01345805,
567
+ 0.03962484,
568
+ 0.01178997,
569
+ 0.03471842,
570
+ 0.02131379,
571
+ 0.0221365,
572
+ -0.01394667,
573
+ -0.0011956,
574
+ -0.00585808,
575
+ -0.00686869,
576
+ -0.00681893,
577
+ 0.00314807,
578
+ 0.02003211,
579
+ -0.01824619,
580
+ -0.01195085,
581
+ -0.00977994,
582
+ -0.02926364,
583
+ 0.00122858,
584
+ 0.0081286,
585
+ -0.0084117,
586
+ 0.03174116,
587
+ 0.01527151,
588
+ 0.01818123,
589
+ -0.01315631,
590
+ 0.00535931,
591
+ -0.01808719,
592
+ -0.01626276,
593
+ 0.01888465,
594
+ -0.00875709,
595
+ 0.0206139,
596
+ 0.01838784,
597
+ -0.03064582,
598
+ 0.01166973,
599
+ 0.02911546,
600
+ 0.02594003,
601
+ -0.00576419,
602
+ -0.00914466,
603
+ -0.0237985,
604
+ 0.01038998,
605
+ -0.01717332,
606
+ 0.02492017,
607
+ -0.00753594,
608
+ 0.01322463,
609
+ 0.01343332,
610
+ 0.00121454,
611
+ 0.01580115,
612
+ 0.00307425,
613
+ 0.0345596,
614
+ -0.01798728,
615
+ 0.0323272,
616
+ -0.0068936,
617
+ -0.00869091,
618
+ 0.0045136,
619
+ -0.00837262,
620
+ -0.00855466,
621
+ 0.0034192,
622
+ 0.02522525,
623
+ -0.00705909,
624
+ -0.019891,
625
+ 0.00191272,
626
+ -0.01211489,
627
+ 0.0285136,
628
+ -0.00566154,
629
+ 0.04103695,
630
+ -0.0092919,
631
+ 0.00241761,
632
+ 0.01635534,
633
+ -0.00316568,
634
+ -0.01597946,
635
+ -0.03456803,
636
+ -0.03958578,
637
+ 0.01394486,
638
+ -0.01306217,
639
+ -0.00343395,
640
+ 0.01033765,
641
+ 0.01521762,
642
+ 0.00855863,
643
+ -0.00389284,
644
+ 0.02527569,
645
+ -0.01169722,
646
+ 0.00200877,
647
+ 0.04353902,
648
+ 0.02844795,
649
+ 0.02484205,
650
+ 0.01893727,
651
+ -0.03412919,
652
+ 0.01272242,
653
+ 0.00202661,
654
+ 0.023077,
655
+ 0.02425507,
656
+ -0.0236186,
657
+ -0.04173858,
658
+ 0.00582972,
659
+ 0.00735155,
660
+ -0.00016013,
661
+ 0.01041111,
662
+ 0.0020433,
663
+ 0.01619346,
664
+ 0.01290293,
665
+ 0.0304944,
666
+ -0.02799492,
667
+ 0.00890391,
668
+ 0.01989347,
669
+ 0.01319829,
670
+ 0.0316516,
671
+ 0.01294222,
672
+ 0.01560815,
673
+ 6.969e-05,
674
+ 0.00689163,
675
+ 0.0201618,
676
+ 0.01609466,
677
+ -0.00964703,
678
+ 0.00868139,
679
+ -0.03052312,
680
+ -0.00467848,
681
+ -0.01110903,
682
+ -0.00782298,
683
+ -0.01644403,
684
+ -0.02660462,
685
+ -0.00318245,
686
+ -0.03656901,
687
+ -0.01252536,
688
+ 2.685e-05,
689
+ 0.00575581,
690
+ 0.00867378,
691
+ -0.03588792,
692
+ -0.00962772,
693
+ 0.00612709,
694
+ 0.02778116,
695
+ 0.00617909,
696
+ -0.02462822,
697
+ 0.01033108,
698
+ 0.02212686,
699
+ 0.00757206,
700
+ 0.0081198,
701
+ 0.02414416,
702
+ -0.00085268,
703
+ -0.01033239,
704
+ -0.01519087,
705
+ 0.01407947,
706
+ 0.01629492,
707
+ -0.00385944,
708
+ 0.00988953,
709
+ 0.01378492,
710
+ 0.04593561,
711
+ 0.00968383,
712
+ 0.03686356,
713
+ -0.01484743,
714
+ 0.01556203,
715
+ -0.01520757,
716
+ 0.02010669,
717
+ -0.0176068,
718
+ 0.02796162,
719
+ -0.00331875,
720
+ -0.01562665,
721
+ -0.02325287,
722
+ -0.03160228,
723
+ 0.01461223,
724
+ -0.00678877,
725
+ -0.0085217,
726
+ 0.00715341,
727
+ -0.003018,
728
+ 0.01534117,
729
+ 0.00859851,
730
+ 0.00273759,
731
+ -0.00587398,
732
+ 0.0363301,
733
+ 0.04355061,
734
+ -0.01432952,
735
+ 0.02605049,
736
+ 0.02288688,
737
+ 0.03925271,
738
+ 0.01841203,
739
+ -0.03189914,
740
+ 0.01263224,
741
+ 0.01408128,
742
+ 0.0248368,
743
+ 0.01680999,
744
+ -0.01338161,
745
+ 0.00324507,
746
+ -0.02825224,
747
+ -0.01734889,
748
+ 0.00191852,
749
+ -0.01146315,
750
+ -0.0038348,
751
+ -0.00840155,
752
+ -0.0083625,
753
+ -0.0499628,
754
+ -0.02321059,
755
+ -0.01606922,
756
+ 0.01277077,
757
+ -0.00190077,
758
+ 0.01025087,
759
+ -0.03114786,
760
+ 0.00791134,
761
+ 0.01744954,
762
+ 0.02002841,
763
+ -0.0255163,
764
+ -0.02600448,
765
+ -0.01578891,
766
+ -0.02690942,
767
+ -0.02880245,
768
+ -0.00431575,
769
+ 0.00600727,
770
+ -0.03090141,
771
+ 0.01105587,
772
+ -0.0015371,
773
+ 0.00296751,
774
+ 0.01277654,
775
+ -0.00477245,
776
+ -0.02422091,
777
+ 0.02559535,
778
+ -0.00133811,
779
+ -0.00772111,
780
+ -0.04095183,
781
+ 0.03351398,
782
+ 0.01896126,
783
+ 0.00341619,
784
+ 0.01234442,
785
+ -0.03707758,
786
+ 0.00587088,
787
+ -0.00318547,
788
+ 0.0377168,
789
+ -0.01426577,
790
+ -0.02044055,
791
+ -0.0175332,
792
+ 0.01608709,
793
+ 0.03591258,
794
+ 0.00580495,
795
+ 0.01636545,
796
+ -0.01722871,
797
+ 0.00606186,
798
+ -0.00400982,
799
+ 0.0109228,
800
+ 0.02489203,
801
+ 0.01245983,
802
+ -0.00455665,
803
+ -0.01384578,
804
+ -0.00659455,
805
+ 0.00777224,
806
+ -0.00334753,
807
+ -0.0177411,
808
+ -0.00531191,
809
+ 0.00629907,
810
+ 0.02441626,
811
+ -0.01576012,
812
+ 0.03351801,
813
+ 0.02288313,
814
+ -0.05304268,
815
+ -0.0071294,
816
+ 0.03232586,
817
+ 0.00891935,
818
+ -0.01977362,
819
+ -0.0027413,
820
+ -0.01511885,
821
+ 0.02056648,
822
+ 0.00657744,
823
+ 0.01876156,
824
+ 0.01019189,
825
+ -0.01308637,
826
+ 0.02896509,
827
+ 0.00256687,
828
+ -0.00765172,
829
+ 0.03474916,
830
+ 0.00256524,
831
+ 0.00674338,
832
+ 0.04313926,
833
+ -0.00284791,
834
+ 0.0150191,
835
+ 0.01572495,
836
+ 0.02367985,
837
+ -0.01331006,
838
+ 0.01604667,
839
+ -0.02399078,
840
+ 0.03464028,
841
+ -0.00468134,
842
+ 0.00330221,
843
+ 0.00213209,
844
+ 0.00835461,
845
+ -0.02771784,
846
+ 0.00901233,
847
+ 0.0105306,
848
+ -0.00924321,
849
+ 0.02637529,
850
+ -0.02676393,
851
+ 0.00356408,
852
+ -0.04889319,
853
+ -0.02941449,
854
+ 0.00112647,
855
+ -0.02983064,
856
+ 0.03998261,
857
+ -0.02925883,
858
+ -0.00396614,
859
+ 0.01319757,
860
+ 0.01322922,
861
+ -0.00243386,
862
+ -0.00769942,
863
+ 0.01422793,
864
+ -0.02085671,
865
+ -0.01465598,
866
+ -0.00793447,
867
+ -0.0158751,
868
+ -0.04526321,
869
+ 0.03444614,
870
+ 0.01128489,
871
+ -0.02307381,
872
+ 0.02501738,
873
+ 0.00990441,
874
+ 0.02356402,
875
+ -0.00317668,
876
+ -0.0126682,
877
+ 0.02966011,
878
+ 0.00138062,
879
+ -0.02201164,
880
+ -0.02099483,
881
+ -0.00252512,
882
+ 0.0012899,
883
+ 0.01235682,
884
+ -0.01162883,
885
+ -0.02899506,
886
+ -0.05046245,
887
+ 0.03096318,
888
+ -0.0059149,
889
+ -0.02507442,
890
+ -0.00721694,
891
+ -0.03142115,
892
+ -0.01031928,
893
+ 0.00791632,
894
+ 0.01309902,
895
+ 0.02127181,
896
+ -0.01971884,
897
+ -0.02214627,
898
+ -0.02675827,
899
+ 0.00386679,
900
+ 0.00320048,
901
+ -0.00816859,
902
+ -0.01619648,
903
+ -0.01310949,
904
+ 0.00787304,
905
+ 0.00961308,
906
+ -0.0018458,
907
+ 0.01286562,
908
+ 0.03139392,
909
+ -0.00560179,
910
+ -0.00183139,
911
+ -0.00443225,
912
+ -0.00790803,
913
+ -0.01636733,
914
+ -0.00759358,
915
+ 0.00203761,
916
+ 0.01884115,
917
+ -0.01272084,
918
+ 0.00250993,
919
+ 0.03664702,
920
+ -0.04066893,
921
+ 0.00552276,
922
+ 0.0137794,
923
+ -0.02775645,
924
+ -0.00558242,
925
+ -0.01088545,
926
+ -0.01883953,
927
+ -0.00373733,
928
+ 0.00910788,
929
+ 0.01156108,
930
+ -0.01827489,
931
+ -0.02444516,
932
+ -0.00397615,
933
+ -0.02902949,
934
+ -0.01167935,
935
+ -0.00136049,
936
+ 0.00129787,
937
+ 0.00911742,
938
+ -0.00392505,
939
+ -0.00567445,
940
+ 0.00135177,
941
+ 0.01242116,
942
+ 0.00707127,
943
+ 0.01174442,
944
+ 0.00069741,
945
+ -0.02878679,
946
+ -0.02556429,
947
+ 0.00227394,
948
+ 0.00793164,
949
+ -0.00369597,
950
+ 0.03255857,
951
+ 0.0202454,
952
+ -0.01635662,
953
+ -0.02483233,
954
+ 0.00268604,
955
+ 0.01370827,
956
+ -0.00288547,
957
+ -0.0091429,
958
+ -0.04589674,
959
+ -0.00381021,
960
+ -0.02784827,
961
+ -0.02464681,
962
+ 0.03012316,
963
+ -0.02526064,
964
+ -0.0019681,
965
+ 0.00598796,
966
+ -0.01061581,
967
+ -0.01781175,
968
+ -0.01317692,
969
+ -0.01122522,
970
+ -0.00949888,
971
+ -0.04640295,
972
+ 0.00405447,
973
+ 0.00692723,
974
+ -0.01286737,
975
+ 0.0194275,
976
+ 0.03252542,
977
+ -0.03120079,
978
+ -0.00596153,
979
+ -0.02418105,
980
+ 0.01823442,
981
+ -0.00854398,
982
+ 0.03457795,
983
+ -0.00829153,
984
+ 0.01389347,
985
+ 0.01776138,
986
+ -0.0100889,
987
+ -0.00340556,
988
+ -0.00454865,
989
+ -0.00269702,
990
+ -0.01599278,
991
+ 0.03753144,
992
+ 0.02651554,
993
+ -0.01864239,
994
+ -0.00012952,
995
+ 0.03227106,
996
+ 0.00020344,
997
+ 0.03920548,
998
+ 0.00676807,
999
+ -0.01476055,
1000
+ -0.02480705,
1001
+ -0.0294454,
1002
+ -0.02185388,
1003
+ 0.01376991,
1004
+ 0.0019891,
1005
+ -0.00082901,
1006
+ -0.00209755,
1007
+ -0.00537243,
1008
+ 0.00898147,
1009
+ -0.03283366,
1010
+ -0.02692142,
1011
+ -0.03934251,
1012
+ -0.03625376,
1013
+ -0.00753794,
1014
+ 0.00549959,
1015
+ 0.00160269,
1016
+ -0.01382472,
1017
+ -0.03401972,
1018
+ -0.01955991,
1019
+ -0.01109684,
1020
+ 0.02179758,
1021
+ -0.03753874,
1022
+ -0.06081887,
1023
+ -0.00784457,
1024
+ -0.01305669,
1025
+ -0.04447476,
1026
+ -0.00143666,
1027
+ -0.01756143,
1028
+ -0.02385547,
1029
+ 0.01329127,
1030
+ -0.01187246,
1031
+ -0.01682425
1032
+ ],
1033
+ "cols": [
1034
+ 0,
1035
+ 11,
1036
+ 25,
1037
+ 35,
1038
+ 37,
1039
+ 38,
1040
+ 39,
1041
+ 42,
1042
+ 46,
1043
+ 56,
1044
+ 61,
1045
+ 63,
1046
+ 65,
1047
+ 68,
1048
+ 81,
1049
+ 105,
1050
+ 110,
1051
+ 129,
1052
+ 135,
1053
+ 169,
1054
+ 215,
1055
+ 259,
1056
+ 264,
1057
+ 266,
1058
+ 294,
1059
+ 371,
1060
+ 414,
1061
+ 426,
1062
+ 452,
1063
+ 496,
1064
+ 504,
1065
+ 505,
1066
+ 509,
1067
+ 516,
1068
+ 580,
1069
+ 582,
1070
+ 601,
1071
+ 638,
1072
+ 682,
1073
+ 733,
1074
+ 738,
1075
+ 750,
1076
+ 755,
1077
+ 882,
1078
+ 891,
1079
+ 907,
1080
+ 926,
1081
+ 956,
1082
+ 1047,
1083
+ 1060,
1084
+ 1087,
1085
+ 1130,
1086
+ 1155,
1087
+ 1175,
1088
+ 1184,
1089
+ 1702,
1090
+ 1727,
1091
+ 1757,
1092
+ 1787,
1093
+ 2248,
1094
+ 2288,
1095
+ 2431,
1096
+ 2636,
1097
+ 3003
1098
+ ]
1099
+ },
1100
+ "19": {
1101
+ "alpha": 6,
1102
+ "dir": [
1103
+ -0.41399729,
1104
+ 0.04963015,
1105
+ 0.12075837,
1106
+ 0.02570651,
1107
+ 0.07051032,
1108
+ -0.14113681,
1109
+ 0.03799108,
1110
+ 0.01167555,
1111
+ -0.0064611,
1112
+ -0.01368775,
1113
+ 0.06116361,
1114
+ 0.03473959,
1115
+ 0.05443978,
1116
+ 0.1034804,
1117
+ 0.03853016,
1118
+ 0.10406735,
1119
+ 0.07458493,
1120
+ 0.19364277,
1121
+ -0.10859208,
1122
+ -0.07088345,
1123
+ 0.09971021,
1124
+ 0.04388057,
1125
+ 0.06559257,
1126
+ 0.07909922,
1127
+ -0.0305314,
1128
+ 0.00847475,
1129
+ 0.0029426,
1130
+ 0.01185544,
1131
+ 0.01822992,
1132
+ 0.04678907,
1133
+ 0.00322332,
1134
+ -0.12947491,
1135
+ 0.05207255,
1136
+ -0.04346941,
1137
+ -0.08363152,
1138
+ 0.01037417,
1139
+ -0.024745,
1140
+ 0.13930042,
1141
+ -0.03795761,
1142
+ -0.02326387,
1143
+ -0.08854304,
1144
+ 0.00513338,
1145
+ -0.04700642,
1146
+ -0.0410671,
1147
+ -0.00597825,
1148
+ -0.01444194,
1149
+ 0.03166021,
1150
+ -0.06466202,
1151
+ 0.03836314,
1152
+ 0.10043173,
1153
+ 0.00429663,
1154
+ 0.01563108,
1155
+ -0.04114208,
1156
+ 0.01129955,
1157
+ 0.03724522,
1158
+ 0.07670557,
1159
+ 0.04311912,
1160
+ 0.07472885,
1161
+ -0.02922567,
1162
+ -0.03833525,
1163
+ 0.00456427,
1164
+ -0.07277104,
1165
+ -0.04912667,
1166
+ 0.03210241,
1167
+ 0.06677274,
1168
+ 0.04299912,
1169
+ 0.01544243,
1170
+ -0.00771099,
1171
+ -0.04890209,
1172
+ -0.05095252,
1173
+ 0.03187749,
1174
+ 0.01289586,
1175
+ -0.04323121,
1176
+ 0.01584703,
1177
+ -0.09161146,
1178
+ 0.01245053,
1179
+ -0.06768124,
1180
+ 0.00860595,
1181
+ -0.00343637,
1182
+ 0.01398703,
1183
+ 0.03606768,
1184
+ 0.0810465,
1185
+ 0.02315859,
1186
+ -0.04040698,
1187
+ 0.03198423,
1188
+ 0.11273056,
1189
+ 0.07610082,
1190
+ -0.0318647,
1191
+ 0.01234657,
1192
+ 0.02210993,
1193
+ 0.04747062,
1194
+ -0.06642445,
1195
+ -0.06831807,
1196
+ -0.00169164,
1197
+ 0.04582261,
1198
+ -0.01536702,
1199
+ -0.02826523,
1200
+ -0.01215465,
1201
+ -0.011371,
1202
+ 0.01469242,
1203
+ -0.05166398,
1204
+ -0.02717075,
1205
+ -0.00141099,
1206
+ 0.02742627,
1207
+ -0.02786403,
1208
+ -0.00629218,
1209
+ 0.03265667,
1210
+ 0.01514308,
1211
+ -0.0149828,
1212
+ -0.00829845,
1213
+ 0.04983302,
1214
+ 0.04255297,
1215
+ 0.00411385,
1216
+ -0.03872541,
1217
+ 0.0036479,
1218
+ 0.04089405,
1219
+ -0.02173937,
1220
+ -0.07732391,
1221
+ -0.02259742,
1222
+ 0.05235458,
1223
+ 0.0173907,
1224
+ 0.02537671,
1225
+ -0.02115237,
1226
+ 0.01312889,
1227
+ -0.03039251,
1228
+ 0.00446135,
1229
+ -0.03203883,
1230
+ 0.02627006,
1231
+ -0.00464935,
1232
+ 0.06962904,
1233
+ -0.03380452,
1234
+ -0.01486555,
1235
+ 0.03601762,
1236
+ 0.04170777,
1237
+ 0.02079187,
1238
+ 0.01055915,
1239
+ 0.013812,
1240
+ -0.07132905,
1241
+ 0.05949075,
1242
+ 0.00309522,
1243
+ 0.0485229,
1244
+ 0.02636794,
1245
+ 0.02857509,
1246
+ 0.04520793,
1247
+ -0.00615302,
1248
+ -0.00280603,
1249
+ 0.00502628,
1250
+ -0.01417321,
1251
+ 0.02896457,
1252
+ -0.01341004,
1253
+ -0.01564776,
1254
+ 0.01597291,
1255
+ 0.02518996,
1256
+ 0.02494553,
1257
+ -0.00542398,
1258
+ -0.00570583,
1259
+ -0.01915297,
1260
+ 0.00438684,
1261
+ 0.00933441,
1262
+ -0.01003382,
1263
+ 0.07714284,
1264
+ -0.00258696,
1265
+ 0.01232391,
1266
+ 0.01856954,
1267
+ -0.02422703,
1268
+ 0.00045015,
1269
+ -0.00747694,
1270
+ -0.04692789,
1271
+ -0.03999501,
1272
+ -0.00415803,
1273
+ -0.02718486,
1274
+ -0.02438036,
1275
+ -0.00504409,
1276
+ 0.01552685,
1277
+ -0.01566504,
1278
+ -0.03189168,
1279
+ 0.01812186,
1280
+ -0.02202662,
1281
+ 0.02614452,
1282
+ -0.00671965,
1283
+ -0.03856579,
1284
+ 0.02344356,
1285
+ -0.00223916,
1286
+ -0.00331282,
1287
+ -0.02047138,
1288
+ -0.01889345,
1289
+ -0.04949783,
1290
+ -0.02935014,
1291
+ 0.0107663,
1292
+ 0.00219746,
1293
+ -0.06389891,
1294
+ 0.01158193,
1295
+ 0.04443077,
1296
+ 0.01606618,
1297
+ -0.01341348,
1298
+ 0.02656645,
1299
+ 0.01186088,
1300
+ -0.0103251,
1301
+ 0.00045351,
1302
+ -0.05710696,
1303
+ 0.01312309,
1304
+ -0.01828778,
1305
+ -0.02093595,
1306
+ 0.01126861,
1307
+ 0.00185225,
1308
+ 0.06209056,
1309
+ 0.01881377,
1310
+ -0.0147169,
1311
+ -0.04910628,
1312
+ -0.00019105,
1313
+ -0.00031153,
1314
+ 0.01395955,
1315
+ -0.04751529,
1316
+ -0.02592737,
1317
+ -0.02079906,
1318
+ -0.01138995,
1319
+ 0.02150493,
1320
+ 0.0168967,
1321
+ 0.01505595,
1322
+ -0.07899936,
1323
+ -0.01489948,
1324
+ -0.01328334,
1325
+ 0.03457796,
1326
+ -0.0035607,
1327
+ 0.02112566,
1328
+ -0.05852021,
1329
+ -0.00032909,
1330
+ -0.01642394,
1331
+ -0.02042259,
1332
+ -0.03190672,
1333
+ -0.07005456,
1334
+ 0.05231471,
1335
+ 0.01389211,
1336
+ 0.03463357,
1337
+ 0.00445699,
1338
+ -0.01345005,
1339
+ 0.01217057,
1340
+ 0.02445503,
1341
+ -0.04094464,
1342
+ 0.03871498,
1343
+ -0.00529151,
1344
+ 0.04551385,
1345
+ 0.02378383,
1346
+ -0.0063007,
1347
+ 0.0368812,
1348
+ 0.00015905,
1349
+ 0.02571216,
1350
+ -0.04776682,
1351
+ -0.00851591,
1352
+ 0.00099652,
1353
+ 0.04026485,
1354
+ -0.01877851,
1355
+ -0.0219236,
1356
+ -0.00297592,
1357
+ 0.01029671,
1358
+ 0.10225255,
1359
+ -0.07007081,
1360
+ -0.02340342,
1361
+ 0.00304178,
1362
+ 0.01930523,
1363
+ -0.02637254,
1364
+ 0.00015534,
1365
+ 0.01005215,
1366
+ 0.01588354,
1367
+ 0.01971863,
1368
+ 0.00551752,
1369
+ 0.01135873,
1370
+ 0.00747602,
1371
+ 0.01103365,
1372
+ 0.01211043,
1373
+ 0.00037698,
1374
+ -0.02620154,
1375
+ 0.00256552,
1376
+ -0.02569012,
1377
+ -0.02783686,
1378
+ 0.00628542,
1379
+ -0.00104915,
1380
+ -0.0084195,
1381
+ -0.03021691,
1382
+ -0.06425054,
1383
+ 0.02496984,
1384
+ -0.0123,
1385
+ -0.03871801,
1386
+ 0.00344185,
1387
+ 0.0094332,
1388
+ -0.01717056,
1389
+ 0.04348284,
1390
+ -0.01649027,
1391
+ -0.01774609,
1392
+ -0.00925723,
1393
+ 0.01012276,
1394
+ -0.02162685,
1395
+ -0.00469051,
1396
+ -0.00543928,
1397
+ 0.00891075,
1398
+ -0.02664486,
1399
+ 0.00453793,
1400
+ -0.00719609,
1401
+ -0.0229699,
1402
+ -0.02674694,
1403
+ 0.08312266,
1404
+ -0.02648537,
1405
+ -0.01864358,
1406
+ 0.05058388,
1407
+ -0.02575401,
1408
+ 0.02509584,
1409
+ -0.00238742,
1410
+ -0.01080631,
1411
+ -0.02850006,
1412
+ 0.03767575,
1413
+ -0.01170178,
1414
+ -0.04227291,
1415
+ -0.00819114,
1416
+ 0.0155615,
1417
+ -0.01753663,
1418
+ 0.01993192,
1419
+ -0.08148444,
1420
+ -0.0680353,
1421
+ 0.00388821,
1422
+ 0.03550627,
1423
+ 0.00862947,
1424
+ 0.00714255,
1425
+ 0.010537,
1426
+ 0.00096149,
1427
+ -0.00442716,
1428
+ -0.01539658,
1429
+ 0.03665021,
1430
+ 0.00852256,
1431
+ 0.01478875,
1432
+ -0.03822764,
1433
+ 0.04834064,
1434
+ -0.00460326,
1435
+ 0.01477617,
1436
+ 0.04410122,
1437
+ 0.01627444,
1438
+ -0.00093999,
1439
+ -0.00024491,
1440
+ -0.00553846,
1441
+ -0.00497731,
1442
+ 0.04350387,
1443
+ -0.14360385,
1444
+ 0.02938055,
1445
+ -0.0352683,
1446
+ -0.00509822,
1447
+ -0.02180302,
1448
+ 0.00219526,
1449
+ -0.00115064,
1450
+ -0.00144354,
1451
+ -0.02192711,
1452
+ 0.04223738,
1453
+ -0.02610195,
1454
+ -0.01616591,
1455
+ -0.01219281,
1456
+ -0.0051492,
1457
+ -0.02957796,
1458
+ -0.02786715,
1459
+ -0.02184732,
1460
+ -0.01340924,
1461
+ 0.01838329,
1462
+ -0.02598088,
1463
+ 0.02937649,
1464
+ 0.01490723,
1465
+ 0.01670144,
1466
+ -0.03311621,
1467
+ 0.02081646,
1468
+ 0.01394797,
1469
+ -0.00322997,
1470
+ 0.00261211,
1471
+ 0.03853096,
1472
+ 0.02314228,
1473
+ 0.00417184,
1474
+ -0.02041819,
1475
+ 0.00740087,
1476
+ 0.00630271,
1477
+ -0.02640118,
1478
+ -0.01714695,
1479
+ 0.0081078,
1480
+ 0.02775934,
1481
+ -0.00849896,
1482
+ -0.02215351,
1483
+ -0.0097992,
1484
+ 0.0137838,
1485
+ -0.00961078,
1486
+ 0.00229612,
1487
+ -0.00337052,
1488
+ -0.01665382,
1489
+ 0.00472534,
1490
+ 0.01867853,
1491
+ 0.02347246,
1492
+ 0.00167132,
1493
+ -0.00106219,
1494
+ -0.00794656,
1495
+ -0.00257331,
1496
+ -0.00023658,
1497
+ 0.00359527,
1498
+ 0.04330531,
1499
+ -0.01585915,
1500
+ 0.00389967,
1501
+ -0.00603891,
1502
+ 0.01601479,
1503
+ -0.00080251,
1504
+ -0.00465458,
1505
+ 0.00355762,
1506
+ -0.00012541,
1507
+ -0.01363729,
1508
+ -0.03129135,
1509
+ -0.0451249,
1510
+ 0.03790808,
1511
+ 0.01233148,
1512
+ -0.00681186,
1513
+ 0.02245213,
1514
+ -0.02457226,
1515
+ 0.0053979,
1516
+ -0.00176179,
1517
+ -0.01983261,
1518
+ -0.00289942,
1519
+ -0.054424,
1520
+ -0.01660743,
1521
+ -0.01866144,
1522
+ -0.0056431,
1523
+ -0.01305195,
1524
+ -0.01278731,
1525
+ -0.01718922,
1526
+ -0.01980148,
1527
+ -0.12208816,
1528
+ 0.01950914,
1529
+ 0.00548164,
1530
+ -0.00599155,
1531
+ 0.01228508,
1532
+ -0.01484066,
1533
+ -0.02760676,
1534
+ 0.02717005,
1535
+ -0.00303716,
1536
+ -0.00324011,
1537
+ 0.00126969,
1538
+ 0.02935875,
1539
+ -0.01822443,
1540
+ 0.01723574,
1541
+ 0.01369331,
1542
+ 0.01482911,
1543
+ -0.00349897,
1544
+ -0.0075394,
1545
+ -0.00909447,
1546
+ -0.01029597,
1547
+ 0.02345074,
1548
+ 0.03705572,
1549
+ 0.00569269,
1550
+ 0.02199166,
1551
+ 0.00893563,
1552
+ -0.00376267,
1553
+ 0.00459211,
1554
+ 0.03037536,
1555
+ -0.01397452,
1556
+ -0.00245161,
1557
+ -0.00043594,
1558
+ -0.00770201,
1559
+ -0.02184014,
1560
+ 0.00735113,
1561
+ 0.03283777,
1562
+ -0.0031054,
1563
+ 0.00327932,
1564
+ 0.02279597,
1565
+ 0.00313427,
1566
+ -0.02228454,
1567
+ 0.0249339,
1568
+ 0.00893045,
1569
+ 0.0193599,
1570
+ -0.00423225,
1571
+ 0.01214966,
1572
+ 0.00211539,
1573
+ -0.01705077,
1574
+ 0.05321393,
1575
+ 0.00832467,
1576
+ 0.00691531,
1577
+ 0.00440044,
1578
+ -0.0036028,
1579
+ 0.00648574,
1580
+ -0.00184864,
1581
+ 0.01262479,
1582
+ -0.04579851,
1583
+ -0.01826578,
1584
+ -0.00640297,
1585
+ -0.03256388,
1586
+ 0.00699382,
1587
+ -0.01327709,
1588
+ -0.01331763,
1589
+ -0.00193405,
1590
+ -0.00183068,
1591
+ -0.00744029,
1592
+ 0.00887535,
1593
+ -0.03525895,
1594
+ -0.00613091,
1595
+ 0.00389063,
1596
+ 0.0061255,
1597
+ -0.00145237,
1598
+ 0.02164902,
1599
+ 0.00551578,
1600
+ -0.02099774,
1601
+ 0.02638274,
1602
+ -0.01244471,
1603
+ 0.01595411,
1604
+ 0.00367786,
1605
+ 0.00150957,
1606
+ 0.01250048,
1607
+ 0.01080034,
1608
+ -0.00269924,
1609
+ -0.01266212,
1610
+ -0.02325669,
1611
+ -0.00212248,
1612
+ 0.05340743,
1613
+ -0.00488497,
1614
+ -0.00560996,
1615
+ -0.01019677,
1616
+ -0.00038237,
1617
+ -0.01917257,
1618
+ 0.00416132,
1619
+ -0.06233919,
1620
+ 0.02279506,
1621
+ 0.01383749,
1622
+ 0.0223188,
1623
+ 0.03706111,
1624
+ 0.00759379,
1625
+ -0.01933291,
1626
+ -0.00911448,
1627
+ -0.00832836,
1628
+ 0.00218727,
1629
+ 0.00528866,
1630
+ 0.0012676,
1631
+ -0.00689148,
1632
+ -0.01066677,
1633
+ -0.03458137,
1634
+ -0.00148494,
1635
+ 0.00298672,
1636
+ -0.02046814,
1637
+ 0.01484077,
1638
+ -0.00187794,
1639
+ 0.00895328,
1640
+ 0.01091576,
1641
+ 0.02540395,
1642
+ 0.01473134,
1643
+ -0.01313322,
1644
+ 0.00882845,
1645
+ 0.00047043,
1646
+ -0.03233552,
1647
+ -0.00381481,
1648
+ -0.00340058,
1649
+ 0.00045749,
1650
+ 0.01598782,
1651
+ 0.01730791,
1652
+ 0.03945119,
1653
+ -0.00992342,
1654
+ -0.04769488,
1655
+ 0.01061766,
1656
+ -0.00912597,
1657
+ -0.00186258,
1658
+ -0.02074894,
1659
+ -0.00693543,
1660
+ 0.00157427,
1661
+ -0.02019794,
1662
+ 0.03320531,
1663
+ 0.00160058,
1664
+ 0.03022134,
1665
+ 0.01365437,
1666
+ 0.01773841,
1667
+ -0.00126859,
1668
+ -0.00960154,
1669
+ 0.00160947,
1670
+ 0.02864504,
1671
+ 0.00388886,
1672
+ 0.01610713,
1673
+ 0.02257901,
1674
+ -0.01016495,
1675
+ 0.0029654,
1676
+ -0.00020181,
1677
+ 0.00606368,
1678
+ 0.01182888,
1679
+ 0.01258298,
1680
+ 0.06290828,
1681
+ 0.02865976,
1682
+ 0.02483223,
1683
+ 0.00040623,
1684
+ 0.00811181,
1685
+ -0.01250675,
1686
+ -0.01186497,
1687
+ -0.01285955,
1688
+ 0.01597996,
1689
+ -0.04972612,
1690
+ 0.00089825,
1691
+ 0.00958841,
1692
+ -0.02032982,
1693
+ -0.00527198,
1694
+ 0.02549261,
1695
+ 0.04237567,
1696
+ 0.0055739,
1697
+ -0.01983182,
1698
+ -0.02598889,
1699
+ 0.01521344,
1700
+ 0.01940417,
1701
+ 0.00654279,
1702
+ -0.01277678,
1703
+ 0.01001596,
1704
+ 0.00197745,
1705
+ 0.00052592,
1706
+ 0.01791021,
1707
+ 0.00403065,
1708
+ 0.01413665,
1709
+ -0.00654267,
1710
+ 0.01739347,
1711
+ -0.00468635,
1712
+ 0.01130318,
1713
+ 0.01190673,
1714
+ 0.00176921,
1715
+ 0.00215732,
1716
+ -0.01801398,
1717
+ 0.00423987,
1718
+ -0.02487228,
1719
+ -0.03574106,
1720
+ -0.03761583,
1721
+ -0.01580498,
1722
+ 0.0112633,
1723
+ -0.00802143,
1724
+ 0.04035542,
1725
+ -0.02123511,
1726
+ 0.01391431,
1727
+ 0.02576919,
1728
+ -0.01363103,
1729
+ -0.01700656,
1730
+ -0.03270056,
1731
+ -0.01692577,
1732
+ 0.0040207,
1733
+ -0.00770615,
1734
+ -0.02023523,
1735
+ -0.01287303,
1736
+ -0.00399295,
1737
+ -0.0097395,
1738
+ -0.00157304,
1739
+ 0.01470541,
1740
+ -0.02900858,
1741
+ -0.01364948,
1742
+ 0.01622227,
1743
+ 0.04555156,
1744
+ 0.02300258,
1745
+ 0.00070742,
1746
+ -0.03894363,
1747
+ 0.01395334,
1748
+ -0.011304,
1749
+ 0.01820423,
1750
+ 0.02882553,
1751
+ -0.0265112,
1752
+ -0.02328012,
1753
+ -0.0057346,
1754
+ -0.0111037,
1755
+ -0.00239438,
1756
+ 0.00067644,
1757
+ -0.00284005,
1758
+ 0.02123305,
1759
+ 0.019265,
1760
+ 0.01552788,
1761
+ -0.01986999,
1762
+ 0.03097746,
1763
+ 0.03139879,
1764
+ 0.01663943,
1765
+ 0.02280164,
1766
+ 0.01902663,
1767
+ 0.00619938,
1768
+ -0.01090529,
1769
+ -0.02094108,
1770
+ 0.01410988,
1771
+ 0.0052609,
1772
+ -0.01526535,
1773
+ 0.03699815,
1774
+ -0.03132536,
1775
+ 0.01252833,
1776
+ -0.02781519,
1777
+ -0.01810875,
1778
+ -0.009691,
1779
+ -0.02077244,
1780
+ 0.00926613,
1781
+ -0.04685893,
1782
+ -0.00726184,
1783
+ -0.00891186,
1784
+ -0.00769646,
1785
+ 0.01439862,
1786
+ -0.02565044,
1787
+ 0.00493228,
1788
+ 0.00974353,
1789
+ -0.00995644,
1790
+ 0.02049081,
1791
+ -0.04404585,
1792
+ 0.00751193,
1793
+ 0.00638864,
1794
+ 0.00514695,
1795
+ 0.05242157,
1796
+ 0.00289454,
1797
+ -0.00064507,
1798
+ 0.03833183,
1799
+ -0.04490974,
1800
+ -0.00162655,
1801
+ -0.01159293,
1802
+ 0.01254596,
1803
+ -0.01223043,
1804
+ 0.0235262,
1805
+ 0.02629913,
1806
+ 0.01253196,
1807
+ -0.02080517,
1808
+ -0.00689689,
1809
+ -0.0068256,
1810
+ 0.01760788,
1811
+ 0.01303217,
1812
+ -0.00408248,
1813
+ 0.01806383,
1814
+ 0.00275279,
1815
+ -0.02245613,
1816
+ -0.03216419,
1817
+ -0.02313394,
1818
+ 0.01341674,
1819
+ -0.02875509,
1820
+ 0.00821746,
1821
+ -0.00258162,
1822
+ -0.0166717,
1823
+ -0.00257669,
1824
+ -0.01655585,
1825
+ 0.01896282,
1826
+ -0.01112964,
1827
+ 0.00746154,
1828
+ 0.01104538,
1829
+ -0.02793135,
1830
+ -0.00074981,
1831
+ 0.00759404,
1832
+ 0.04883965,
1833
+ 0.02882431,
1834
+ -0.01466329,
1835
+ -0.0079204,
1836
+ -0.01327058,
1837
+ 0.01041078,
1838
+ 0.01971299,
1839
+ -0.00478428,
1840
+ -0.02979348,
1841
+ -0.0033707,
1842
+ -0.01569781,
1843
+ 0.00752487,
1844
+ 0.00428126,
1845
+ 0.02388609,
1846
+ 0.0027208,
1847
+ -0.00341252,
1848
+ -0.04374065,
1849
+ -0.01116345,
1850
+ -0.00532541,
1851
+ 0.02159592,
1852
+ 0.00406952,
1853
+ 0.00415196,
1854
+ -0.0033014,
1855
+ 0.00208155,
1856
+ -0.02914683,
1857
+ 0.02400334,
1858
+ -0.00277937,
1859
+ -0.01673185,
1860
+ -0.01697779,
1861
+ -0.02679257,
1862
+ -0.0103722,
1863
+ -0.03774866,
1864
+ 0.02358133,
1865
+ -0.02143352,
1866
+ -0.00031711,
1867
+ -0.00352962,
1868
+ -0.00598108,
1869
+ 0.01242257,
1870
+ 0.0164373,
1871
+ 0.00470733,
1872
+ 0.02708453,
1873
+ -0.02024415,
1874
+ 0.01169108,
1875
+ -0.03683765,
1876
+ 0.02083484,
1877
+ 0.00937352,
1878
+ -0.02739683,
1879
+ -0.0067206,
1880
+ -0.01986139,
1881
+ 0.00547282,
1882
+ -0.01628444,
1883
+ 0.00153831,
1884
+ -0.00249261,
1885
+ -0.02345225,
1886
+ -0.01357254,
1887
+ 0.01328131,
1888
+ 0.00521324,
1889
+ 0.00352823,
1890
+ -0.01862443,
1891
+ -0.00389278,
1892
+ 0.00673196,
1893
+ 0.00429158,
1894
+ 0.00606037,
1895
+ 0.03048845,
1896
+ -0.01037415,
1897
+ -0.01091966,
1898
+ 0.0005297,
1899
+ -0.0454992,
1900
+ -0.03626728,
1901
+ 0.00287546,
1902
+ 0.00962111,
1903
+ -0.01698189,
1904
+ -0.00070894,
1905
+ 0.01815648,
1906
+ -0.01495158,
1907
+ 0.01427327,
1908
+ 0.02984908,
1909
+ -0.01813151,
1910
+ -0.00496254,
1911
+ 0.0179225,
1912
+ -0.00915224,
1913
+ -0.03176316,
1914
+ 0.00395478,
1915
+ 0.00483918,
1916
+ 0.00103693,
1917
+ -0.03188303,
1918
+ 0.00852776,
1919
+ -0.00672803,
1920
+ -0.01914714,
1921
+ 0.00857762,
1922
+ -0.0096268,
1923
+ 0.00650117,
1924
+ -0.01087384,
1925
+ -0.011639,
1926
+ 0.01615791,
1927
+ 0.02781934,
1928
+ -0.00755544,
1929
+ -0.00014848,
1930
+ -0.00793268,
1931
+ 0.00750446,
1932
+ -0.0103245,
1933
+ 0.0168096,
1934
+ -0.02983714,
1935
+ 0.03803321,
1936
+ -0.01768607,
1937
+ 0.00270445,
1938
+ 0.01078355,
1939
+ 0.01566177,
1940
+ -0.0133848,
1941
+ 0.01774806,
1942
+ -0.00812243,
1943
+ 0.02237075,
1944
+ 0.01115108,
1945
+ 0.00381729,
1946
+ 0.01070505,
1947
+ -0.03039744,
1948
+ -0.04433396,
1949
+ 0.02080437,
1950
+ -0.01398751,
1951
+ 0.02386202,
1952
+ 9.26e-05,
1953
+ -0.01348085,
1954
+ 0.00854043,
1955
+ 0.03121264,
1956
+ -0.00824868,
1957
+ -0.0059332,
1958
+ 0.01327891,
1959
+ -0.01346949,
1960
+ -0.00999715,
1961
+ -0.00842401,
1962
+ -0.02804655,
1963
+ -0.03686436,
1964
+ 0.02232404,
1965
+ -0.0279701,
1966
+ -0.02540337,
1967
+ -0.00054304,
1968
+ -0.00756712,
1969
+ 0.02793047,
1970
+ -0.01626339,
1971
+ -0.01397534,
1972
+ 0.02358809,
1973
+ 0.04023468,
1974
+ -0.03251312,
1975
+ -0.01672773,
1976
+ 0.00377267,
1977
+ 0.01258042,
1978
+ -0.00385397,
1979
+ -0.02168425,
1980
+ -0.01490837,
1981
+ -0.01752106,
1982
+ -0.0036807,
1983
+ -0.01502097,
1984
+ -0.03276443,
1985
+ -0.01766985,
1986
+ -0.02547744,
1987
+ -0.00919792,
1988
+ -0.00224599,
1989
+ 0.0251513,
1990
+ -0.01591504,
1991
+ 0.00427116,
1992
+ -0.01306467,
1993
+ 0.01153746,
1994
+ -0.02659429,
1995
+ -0.00869732,
1996
+ 0.00801325,
1997
+ -0.00534268,
1998
+ 0.00309692,
1999
+ 0.0168271,
2000
+ 0.00267658,
2001
+ -0.00784185,
2002
+ -0.01638011,
2003
+ -0.00787242,
2004
+ -0.01431224,
2005
+ -0.00839908,
2006
+ 0.00343782,
2007
+ 0.0076508,
2008
+ 0.00512584,
2009
+ -0.00750912,
2010
+ 0.01765133,
2011
+ 0.00432924,
2012
+ -0.00314923,
2013
+ -0.02161724,
2014
+ 0.02385022,
2015
+ -0.02513087,
2016
+ -0.01094419,
2017
+ 0.01210399,
2018
+ -0.00285633,
2019
+ 0.00383794,
2020
+ -0.03123457,
2021
+ -0.01198237,
2022
+ -0.01510271,
2023
+ -0.01148189,
2024
+ 0.00541213,
2025
+ -0.01798316,
2026
+ 0.00496864,
2027
+ -0.0015806,
2028
+ -0.01432069,
2029
+ -0.02940245,
2030
+ -0.01776116,
2031
+ -0.00566956,
2032
+ 0.0015968,
2033
+ -0.01688569,
2034
+ -0.00069971,
2035
+ -0.00806095,
2036
+ 0.01892859,
2037
+ 0.03505353,
2038
+ 0.00642723,
2039
+ -0.00281842,
2040
+ -0.03205081,
2041
+ -0.01303378,
2042
+ -0.00905993,
2043
+ 0.02895496,
2044
+ 0.00354829,
2045
+ 0.01735854,
2046
+ 0.00953999,
2047
+ -0.01426204,
2048
+ 0.01120116,
2049
+ -0.00881752,
2050
+ 0.02241647,
2051
+ -0.01657692,
2052
+ -0.00571749,
2053
+ -0.04898428,
2054
+ 0.00551105,
2055
+ -0.02055241,
2056
+ -0.00990897,
2057
+ 0.00936325,
2058
+ -0.0096167,
2059
+ 0.01104994,
2060
+ 0.01584025,
2061
+ 0.01587972,
2062
+ 0.00194767,
2063
+ -0.00631707,
2064
+ -0.04304481,
2065
+ 0.0018921,
2066
+ 0.01500908,
2067
+ -0.01644222,
2068
+ 0.00668909,
2069
+ -0.03100232,
2070
+ -0.00048992,
2071
+ 0.0197435,
2072
+ -0.01865795,
2073
+ -0.00662279,
2074
+ -0.00430736,
2075
+ 0.01438951,
2076
+ -0.02331211,
2077
+ 0.00492494,
2078
+ -0.01072574,
2079
+ -0.00245177,
2080
+ -0.00950786,
2081
+ -0.00477532,
2082
+ -0.0100874,
2083
+ -0.01600529,
2084
+ 0.00570134,
2085
+ -0.01740529,
2086
+ 0.03467067,
2087
+ 0.01419344,
2088
+ 0.00161084,
2089
+ -0.00347943,
2090
+ 0.02319815,
2091
+ -0.00585438,
2092
+ 0.00335634,
2093
+ 0.03062036,
2094
+ -0.01164215,
2095
+ 0.00494848,
2096
+ 0.01392082,
2097
+ -0.04225956,
2098
+ -0.00249652,
2099
+ -0.00907702,
2100
+ 0.03732198,
2101
+ -0.00339302,
2102
+ -0.01306331,
2103
+ 0.02235401,
2104
+ -0.01099497,
2105
+ -0.01200077,
2106
+ -0.05103194,
2107
+ -0.00398947,
2108
+ -0.00825615,
2109
+ 0.02447357,
2110
+ -0.00067779,
2111
+ -0.00834806,
2112
+ -0.0175427,
2113
+ -0.01224584,
2114
+ 0.00016019,
2115
+ 0.01329831,
2116
+ -0.0194385,
2117
+ -0.01699858,
2118
+ 0.00808758,
2119
+ -0.0110953,
2120
+ -0.00470004,
2121
+ 0.0187731,
2122
+ 0.00288963,
2123
+ -0.04354201,
2124
+ -0.0111457,
2125
+ 0.0091344,
2126
+ 0.00545016
2127
+ ],
2128
+ "cols": [
2129
+ 1,
2130
+ 11,
2131
+ 13,
2132
+ 15,
2133
+ 17,
2134
+ 24,
2135
+ 34,
2136
+ 36,
2137
+ 41,
2138
+ 53,
2139
+ 58,
2140
+ 61,
2141
+ 63,
2142
+ 64,
2143
+ 66,
2144
+ 69,
2145
+ 76,
2146
+ 79,
2147
+ 92,
2148
+ 95,
2149
+ 97,
2150
+ 98,
2151
+ 99,
2152
+ 114,
2153
+ 119,
2154
+ 123,
2155
+ 135,
2156
+ 145,
2157
+ 146,
2158
+ 148,
2159
+ 152,
2160
+ 161,
2161
+ 171,
2162
+ 173,
2163
+ 177,
2164
+ 192,
2165
+ 246,
2166
+ 253,
2167
+ 259,
2168
+ 265,
2169
+ 268,
2170
+ 286,
2171
+ 287,
2172
+ 302,
2173
+ 316,
2174
+ 324,
2175
+ 343,
2176
+ 351,
2177
+ 355,
2178
+ 450,
2179
+ 500,
2180
+ 579,
2181
+ 602,
2182
+ 614,
2183
+ 795,
2184
+ 933,
2185
+ 1003,
2186
+ 1110,
2187
+ 2055,
2188
+ 2072,
2189
+ 2175,
2190
+ 2595,
2191
+ 3242,
2192
+ 3343
2193
+ ]
2194
+ }
2195
+ }
2196
+ },
2197
+ "capB-a6": {
2198
+ "about": "Lab 8 recipe B: u and 64 neurons from the base model's own loops (windows well before the loop), alpha 6",
2199
+ "layers": {
2200
+ "15": {
2201
+ "alpha": 6,
2202
+ "dir": [
2203
+ -0.30149689,
2204
+ 0.02326461,
2205
+ 0.11765372,
2206
+ 0.06267165,
2207
+ 0.01956591,
2208
+ -0.19726555,
2209
+ -0.00503336,
2210
+ -0.08789949,
2211
+ -0.01279326,
2212
+ 0.04979974,
2213
+ 0.05555366,
2214
+ 0.06674751,
2215
+ 0.01169172,
2216
+ 0.17527959,
2217
+ 0.02598691,
2218
+ 0.15154752,
2219
+ 0.0898912,
2220
+ 0.15027089,
2221
+ -0.15828934,
2222
+ -0.06802011,
2223
+ 0.06483994,
2224
+ 0.09954681,
2225
+ 0.11248125,
2226
+ 0.10143356,
2227
+ -0.03427665,
2228
+ 0.01130421,
2229
+ 0.03965155,
2230
+ -0.00559169,
2231
+ 0.03277428,
2232
+ 0.06104312,
2233
+ 0.03284929,
2234
+ -0.16260622,
2235
+ 0.02073556,
2236
+ -0.05957358,
2237
+ -0.08322065,
2238
+ -0.01794445,
2239
+ -0.04898045,
2240
+ 0.22294332,
2241
+ -0.05124363,
2242
+ -0.0231905,
2243
+ -0.07554419,
2244
+ 0.01327294,
2245
+ -0.05874912,
2246
+ 0.01641323,
2247
+ 0.02109385,
2248
+ -0.00849336,
2249
+ 0.13678432,
2250
+ -0.03208598,
2251
+ 0.04021065,
2252
+ 0.04547515,
2253
+ -0.00561485,
2254
+ 0.00565074,
2255
+ -0.04153953,
2256
+ 0.01654728,
2257
+ 0.029699,
2258
+ 0.08232442,
2259
+ 0.04345837,
2260
+ 0.06434583,
2261
+ -0.06645264,
2262
+ -0.02416982,
2263
+ 0.00042967,
2264
+ -0.07840814,
2265
+ -0.04635896,
2266
+ 0.01254184,
2267
+ 0.03929815,
2268
+ 0.01142548,
2269
+ 0.0396764,
2270
+ -0.02334055,
2271
+ -0.04343329,
2272
+ -0.02657734,
2273
+ 0.02388055,
2274
+ 0.01532186,
2275
+ -0.08158231,
2276
+ -0.03766869,
2277
+ -0.09986223,
2278
+ 0.01965467,
2279
+ -0.03953091,
2280
+ 0.01490568,
2281
+ 0.0382948,
2282
+ -0.01490955,
2283
+ 0.05087914,
2284
+ 0.0703787,
2285
+ 0.02271337,
2286
+ -0.0555483,
2287
+ 0.00071212,
2288
+ 0.11734333,
2289
+ 0.06425657,
2290
+ -0.02442849,
2291
+ 0.02610742,
2292
+ 0.05987949,
2293
+ 0.05375696,
2294
+ -0.05153911,
2295
+ -0.03357845,
2296
+ 0.02666253,
2297
+ 0.06517966,
2298
+ -0.01237155,
2299
+ -0.02563511,
2300
+ 0.01684695,
2301
+ 0.01155908,
2302
+ 0.00380022,
2303
+ -0.0493474,
2304
+ -0.00513339,
2305
+ -0.01106284,
2306
+ 0.02526324,
2307
+ -0.02684648,
2308
+ 0.00526816,
2309
+ 0.05152647,
2310
+ 0.01687455,
2311
+ -0.03582645,
2312
+ 0.02094468,
2313
+ 0.02345143,
2314
+ 0.02298974,
2315
+ 0.00365907,
2316
+ -0.02588971,
2317
+ -0.01382359,
2318
+ 0.07575645,
2319
+ -0.01029643,
2320
+ -0.03188023,
2321
+ -0.01856114,
2322
+ 0.05651155,
2323
+ 0.00849511,
2324
+ -0.0009302,
2325
+ -0.01166164,
2326
+ 0.0041843,
2327
+ -0.05954583,
2328
+ -0.00012167,
2329
+ -0.02362552,
2330
+ -0.03205946,
2331
+ 0.01003108,
2332
+ 0.05697318,
2333
+ -0.02847577,
2334
+ 0.004791,
2335
+ 0.03201439,
2336
+ 0.01117549,
2337
+ 0.0077243,
2338
+ -0.01911873,
2339
+ 0.0005097,
2340
+ -0.05309742,
2341
+ 0.10262797,
2342
+ 0.00544503,
2343
+ 0.00667644,
2344
+ 0.0214761,
2345
+ 0.02873388,
2346
+ 0.02627211,
2347
+ -0.03188273,
2348
+ -0.02098046,
2349
+ 0.00611033,
2350
+ 0.01107177,
2351
+ 0.03323895,
2352
+ -0.02206725,
2353
+ -0.00462339,
2354
+ 0.00417812,
2355
+ 0.03195161,
2356
+ 0.00788598,
2357
+ -0.01020054,
2358
+ 0.00322999,
2359
+ -0.04070878,
2360
+ 0.0042287,
2361
+ 0.00340121,
2362
+ -0.00629161,
2363
+ 0.06356988,
2364
+ -0.03003905,
2365
+ 0.01948619,
2366
+ 0.02089134,
2367
+ -0.02839906,
2368
+ 0.02209648,
2369
+ 0.04238123,
2370
+ 0.01715541,
2371
+ -0.0301029,
2372
+ 0.00514945,
2373
+ -0.05948887,
2374
+ -0.04008217,
2375
+ 0.00092403,
2376
+ 0.04240754,
2377
+ -0.01147224,
2378
+ -0.05509264,
2379
+ 0.05763384,
2380
+ -0.00159645,
2381
+ 0.01637939,
2382
+ -0.02916958,
2383
+ -0.03551837,
2384
+ 0.00899683,
2385
+ 0.0155799,
2386
+ -0.02717732,
2387
+ -0.02264439,
2388
+ 0.00593656,
2389
+ -0.05256448,
2390
+ -0.00168005,
2391
+ 0.03425383,
2392
+ 0.0188481,
2393
+ -0.02734067,
2394
+ 0.01612632,
2395
+ 0.02995075,
2396
+ 0.0126488,
2397
+ 0.00824284,
2398
+ 0.02283906,
2399
+ 0.00149592,
2400
+ -0.01262761,
2401
+ 0.01244001,
2402
+ -0.04045067,
2403
+ -0.00659995,
2404
+ -0.01249472,
2405
+ -0.0208294,
2406
+ -0.00422333,
2407
+ 0.02912182,
2408
+ 0.04871593,
2409
+ 0.02018343,
2410
+ -0.01012479,
2411
+ -0.03434999,
2412
+ -0.00024383,
2413
+ 0.00724176,
2414
+ -0.0059202,
2415
+ -0.03058464,
2416
+ -0.00567434,
2417
+ 0.00418883,
2418
+ 0.0098441,
2419
+ -0.00485016,
2420
+ 0.0235494,
2421
+ -0.009295,
2422
+ -0.06723159,
2423
+ -0.00141925,
2424
+ 0.01058285,
2425
+ 0.02079017,
2426
+ -0.00225317,
2427
+ 0.01032211,
2428
+ -0.04980971,
2429
+ 0.00766713,
2430
+ -0.02094317,
2431
+ -0.04421069,
2432
+ -0.02549951,
2433
+ -0.02841922,
2434
+ 0.05762584,
2435
+ 0.00753723,
2436
+ 0.02754568,
2437
+ -0.00603487,
2438
+ -0.01353416,
2439
+ 0.01183222,
2440
+ 0.02611442,
2441
+ -0.02576422,
2442
+ 0.02603181,
2443
+ -0.01338155,
2444
+ 0.01783432,
2445
+ 0.01952534,
2446
+ -0.01827757,
2447
+ 0.04060069,
2448
+ 0.0096524,
2449
+ 0.00621415,
2450
+ -0.07070343,
2451
+ 0.01205619,
2452
+ -0.0157654,
2453
+ 0.0355527,
2454
+ -0.00870733,
2455
+ -0.04851521,
2456
+ 0.00418431,
2457
+ 0.01064662,
2458
+ 0.08757313,
2459
+ -0.0431821,
2460
+ -0.02164889,
2461
+ -0.00494927,
2462
+ -0.00274217,
2463
+ -0.00344297,
2464
+ 0.02221851,
2465
+ -0.00939169,
2466
+ -0.00249658,
2467
+ 0.01415563,
2468
+ 0.00960531,
2469
+ 0.02835243,
2470
+ -0.00237309,
2471
+ 0.00040106,
2472
+ -0.00386805,
2473
+ -0.00909662,
2474
+ -0.01428644,
2475
+ 0.00019036,
2476
+ -0.03145899,
2477
+ -0.00120665,
2478
+ 0.02071353,
2479
+ -0.02028685,
2480
+ -0.00615535,
2481
+ -0.02520839,
2482
+ -0.02236454,
2483
+ 0.02357376,
2484
+ -0.01185053,
2485
+ -0.0559378,
2486
+ 0.02748231,
2487
+ 0.02343067,
2488
+ 0.00186691,
2489
+ 0.04614904,
2490
+ 0.00788556,
2491
+ -0.01367954,
2492
+ 0.01103815,
2493
+ 0.0144666,
2494
+ -0.00568829,
2495
+ -0.02537256,
2496
+ -0.00291896,
2497
+ -0.01370349,
2498
+ -0.01813706,
2499
+ 0.00659186,
2500
+ -0.01144869,
2501
+ 0.0054018,
2502
+ -0.02677543,
2503
+ 0.15799721,
2504
+ -0.00068407,
2505
+ 0.00293215,
2506
+ 0.04486345,
2507
+ -0.00954056,
2508
+ 0.01474388,
2509
+ 0.01060703,
2510
+ 0.00755214,
2511
+ 0.00345063,
2512
+ 0.01092949,
2513
+ -0.00362884,
2514
+ 0.01473075,
2515
+ -0.00538783,
2516
+ 0.01691999,
2517
+ -0.01394772,
2518
+ 0.02865278,
2519
+ -0.07491908,
2520
+ -0.05841815,
2521
+ -0.02911618,
2522
+ 0.03960273,
2523
+ -0.00325529,
2524
+ 0.01922638,
2525
+ 0.03592266,
2526
+ 0.01240769,
2527
+ 0.00643024,
2528
+ -0.03352727,
2529
+ 0.02750751,
2530
+ 0.00443367,
2531
+ 0.01995698,
2532
+ -0.00748279,
2533
+ 0.01875017,
2534
+ -0.00788494,
2535
+ 0.00522293,
2536
+ 0.03003652,
2537
+ 0.02110308,
2538
+ -0.02318322,
2539
+ 0.02898433,
2540
+ 0.00738792,
2541
+ 0.00277897,
2542
+ 0.02434439,
2543
+ -0.09862508,
2544
+ 0.05617183,
2545
+ -0.02186624,
2546
+ -0.00717736,
2547
+ -0.01975846,
2548
+ 0.0338889,
2549
+ 0.02121268,
2550
+ 0.00846916,
2551
+ -0.00983612,
2552
+ 0.03011166,
2553
+ -0.00570846,
2554
+ 0.00319565,
2555
+ -0.01916552,
2556
+ -0.00642473,
2557
+ -0.02169416,
2558
+ -0.05596741,
2559
+ -0.00093535,
2560
+ -0.02283115,
2561
+ 0.02040694,
2562
+ -0.01648783,
2563
+ 0.006206,
2564
+ 0.0008686,
2565
+ 0.03457417,
2566
+ -0.02280811,
2567
+ -0.00063133,
2568
+ 0.02138565,
2569
+ 0.00825628,
2570
+ 0.00704255,
2571
+ 0.01895653,
2572
+ 0.01850315,
2573
+ -0.01492665,
2574
+ -0.00377653,
2575
+ -0.00726489,
2576
+ 0.01177038,
2577
+ -0.02597683,
2578
+ -0.01700044,
2579
+ -0.00285292,
2580
+ 0.00554924,
2581
+ 0.00484764,
2582
+ -0.01376073,
2583
+ -0.0139686,
2584
+ -0.01939424,
2585
+ -0.02597563,
2586
+ -0.01605687,
2587
+ 0.00347986,
2588
+ -0.02692802,
2589
+ 0.00248492,
2590
+ 0.01662239,
2591
+ 0.00825688,
2592
+ -0.00402666,
2593
+ -0.0170685,
2594
+ 0.00427057,
2595
+ 0.00054583,
2596
+ 0.01124339,
2597
+ -0.02183045,
2598
+ 0.03154991,
2599
+ 0.0003381,
2600
+ -0.00562663,
2601
+ -0.00763783,
2602
+ 0.00563474,
2603
+ -0.02693614,
2604
+ 0.01886536,
2605
+ 0.01280506,
2606
+ 0.00324432,
2607
+ -0.03816755,
2608
+ -0.03527931,
2609
+ -0.03196836,
2610
+ 0.00196033,
2611
+ 0.0196076,
2612
+ -0.00196445,
2613
+ 0.02511599,
2614
+ -0.02921427,
2615
+ 0.0061164,
2616
+ -0.01979781,
2617
+ -0.0021651,
2618
+ 0.01065994,
2619
+ -0.03970449,
2620
+ -0.03330931,
2621
+ -0.01952009,
2622
+ 0.00032798,
2623
+ -0.00533792,
2624
+ 0.0124535,
2625
+ -0.00392792,
2626
+ -0.01070111,
2627
+ -0.08814324,
2628
+ 0.0076339,
2629
+ 0.01122764,
2630
+ 0.00905735,
2631
+ 0.00117328,
2632
+ 0.01181389,
2633
+ -0.02175004,
2634
+ -0.02121019,
2635
+ 0.01248304,
2636
+ -0.00093594,
2637
+ -0.01948054,
2638
+ 0.03104073,
2639
+ -0.0088424,
2640
+ 0.01062842,
2641
+ 0.03344615,
2642
+ 0.02377078,
2643
+ 0.00947216,
2644
+ 0.04116111,
2645
+ 0.01839026,
2646
+ -0.00920545,
2647
+ 0.0132251,
2648
+ 0.02180791,
2649
+ 0.03044661,
2650
+ 0.00876045,
2651
+ 0.0206499,
2652
+ 0.01138573,
2653
+ 0.02535999,
2654
+ 0.00895611,
2655
+ -0.00902818,
2656
+ 0.02495545,
2657
+ 0.00332173,
2658
+ 0.00221947,
2659
+ -0.02037391,
2660
+ -0.00544367,
2661
+ 0.03596655,
2662
+ 0.00752132,
2663
+ -0.00227448,
2664
+ 0.0155155,
2665
+ 0.00425131,
2666
+ -0.01969209,
2667
+ 0.00654938,
2668
+ 0.01465121,
2669
+ 0.02098612,
2670
+ -0.00871356,
2671
+ 0.00285627,
2672
+ -0.00237169,
2673
+ -0.01567797,
2674
+ 0.05599053,
2675
+ -0.00078066,
2676
+ -0.0099316,
2677
+ 0.01758756,
2678
+ -0.0201795,
2679
+ 0.00143664,
2680
+ 0.01996665,
2681
+ 0.00643219,
2682
+ -0.03766159,
2683
+ -0.01698383,
2684
+ 0.00196764,
2685
+ -0.02466804,
2686
+ 0.00456563,
2687
+ -0.01729585,
2688
+ -0.00617461,
2689
+ 0.0079403,
2690
+ 0.02798674,
2691
+ -0.00546738,
2692
+ -0.00203452,
2693
+ -0.01545207,
2694
+ -0.01398242,
2695
+ -0.03375365,
2696
+ 0.00621148,
2697
+ 0.02407432,
2698
+ 0.0216948,
2699
+ 0.02371102,
2700
+ -0.0017222,
2701
+ 0.03594714,
2702
+ 0.00673465,
2703
+ 0.0023872,
2704
+ -0.00644301,
2705
+ -0.00486257,
2706
+ 0.00653148,
2707
+ 0.00686671,
2708
+ 0.01289318,
2709
+ 0.01043915,
2710
+ 0.0008696,
2711
+ -0.0029646,
2712
+ 0.00058893,
2713
+ -0.00367669,
2714
+ -0.01205821,
2715
+ -0.00369951,
2716
+ 0.00500719,
2717
+ -0.03102004,
2718
+ 0.00997901,
2719
+ -0.0564148,
2720
+ 0.0139201,
2721
+ 0.00455478,
2722
+ 0.02063775,
2723
+ 0.0524075,
2724
+ 0.0090106,
2725
+ -0.0040679,
2726
+ 0.00929408,
2727
+ -0.01237593,
2728
+ 0.01192695,
2729
+ 0.0002889,
2730
+ 0.04478727,
2731
+ -0.00868966,
2732
+ -0.01069027,
2733
+ 0.00170744,
2734
+ -0.00880913,
2735
+ 0.03051276,
2736
+ -0.01133682,
2737
+ 0.00216336,
2738
+ -0.01166068,
2739
+ 0.02527721,
2740
+ 0.01598965,
2741
+ 0.05630016,
2742
+ 0.01782241,
2743
+ 0.00794784,
2744
+ -0.00473622,
2745
+ -0.00324297,
2746
+ -0.03602727,
2747
+ -0.01093641,
2748
+ -0.00714647,
2749
+ -0.0240199,
2750
+ 0.00720374,
2751
+ -0.0018791,
2752
+ 0.00775742,
2753
+ -0.00429064,
2754
+ -0.05634158,
2755
+ 0.02373968,
2756
+ -0.00022325,
2757
+ 0.017044,
2758
+ -0.0415827,
2759
+ 0.01951932,
2760
+ -0.00684324,
2761
+ -0.00617908,
2762
+ 0.02936001,
2763
+ 0.0022231,
2764
+ 0.02858322,
2765
+ 0.02646914,
2766
+ 0.0296457,
2767
+ -0.01347029,
2768
+ 0.00532766,
2769
+ -0.01175069,
2770
+ -0.01503773,
2771
+ -0.00244769,
2772
+ 0.00162901,
2773
+ 0.01375139,
2774
+ -0.00926648,
2775
+ -0.00245956,
2776
+ 0.00333973,
2777
+ -0.01736316,
2778
+ -0.00126767,
2779
+ 0.00376091,
2780
+ 0.00699914,
2781
+ 0.02746248,
2782
+ 0.01196896,
2783
+ 0.01478108,
2784
+ -0.00855968,
2785
+ -0.00260315,
2786
+ -0.0199121,
2787
+ -0.01861352,
2788
+ 0.01223358,
2789
+ 0.0081777,
2790
+ 0.00620991,
2791
+ 0.01076913,
2792
+ -0.02556662,
2793
+ 0.00788654,
2794
+ 0.01794371,
2795
+ 0.02445308,
2796
+ -0.00098348,
2797
+ -0.00087275,
2798
+ -0.00806301,
2799
+ 0.00444915,
2800
+ -0.01069149,
2801
+ 0.01613029,
2802
+ 0.00309012,
2803
+ 0.01902706,
2804
+ 0.01421466,
2805
+ 0.00138022,
2806
+ 0.01512942,
2807
+ 0.00030636,
2808
+ 0.03271034,
2809
+ -0.01789483,
2810
+ 0.02173513,
2811
+ -0.00648184,
2812
+ -0.0162087,
2813
+ 0.0077972,
2814
+ -0.00579106,
2815
+ 0.00537245,
2816
+ 0.00387916,
2817
+ 0.01599543,
2818
+ 0.00627289,
2819
+ -0.01186336,
2820
+ 0.0070736,
2821
+ -0.01310907,
2822
+ 0.02760035,
2823
+ 0.00439267,
2824
+ 0.03422385,
2825
+ -0.00883535,
2826
+ 0.00667326,
2827
+ 0.02595069,
2828
+ 0.00116098,
2829
+ -0.01699736,
2830
+ -0.03703247,
2831
+ -0.03113385,
2832
+ 0.02031528,
2833
+ -0.00775315,
2834
+ -0.00877325,
2835
+ 0.01849073,
2836
+ 0.01423547,
2837
+ 0.00288525,
2838
+ -0.00622111,
2839
+ 0.02096023,
2840
+ -0.01215921,
2841
+ -9e-05,
2842
+ 0.0382315,
2843
+ 0.02604484,
2844
+ 0.01930726,
2845
+ 0.01248157,
2846
+ -0.03497124,
2847
+ 0.01475701,
2848
+ 3.174e-05,
2849
+ 0.01543846,
2850
+ 0.02304812,
2851
+ -0.0129609,
2852
+ -0.02369003,
2853
+ 0.00373719,
2854
+ 0.00182831,
2855
+ 0.00434628,
2856
+ 0.00474514,
2857
+ 0.00282092,
2858
+ 0.01340202,
2859
+ 0.01224109,
2860
+ 0.02576358,
2861
+ -0.01278894,
2862
+ 0.00098902,
2863
+ 0.0173066,
2864
+ 0.00088813,
2865
+ 0.01500969,
2866
+ 0.00536499,
2867
+ 0.02194963,
2868
+ 0.00571928,
2869
+ 0.01032218,
2870
+ 0.01571402,
2871
+ 0.0092924,
2872
+ -0.015971,
2873
+ 0.00404453,
2874
+ -0.02361519,
2875
+ -0.00444758,
2876
+ -0.0045476,
2877
+ -0.00217738,
2878
+ -0.02578217,
2879
+ -0.01997341,
2880
+ -0.01279157,
2881
+ -0.0286952,
2882
+ -0.00561124,
2883
+ -0.00671502,
2884
+ 0.01089097,
2885
+ 0.0030987,
2886
+ -0.0345054,
2887
+ -0.0087018,
2888
+ 0.00565438,
2889
+ 0.02648221,
2890
+ 0.00331194,
2891
+ -0.0136128,
2892
+ 0.00805192,
2893
+ 0.02403069,
2894
+ 0.01376859,
2895
+ -0.0009556,
2896
+ 0.01622848,
2897
+ -0.00585403,
2898
+ -0.02126574,
2899
+ -0.00190116,
2900
+ 0.01683871,
2901
+ 0.01021804,
2902
+ -0.00683891,
2903
+ 0.01565132,
2904
+ 0.00421597,
2905
+ 0.03681558,
2906
+ 0.0086617,
2907
+ 0.04103243,
2908
+ -0.00636359,
2909
+ 0.01988558,
2910
+ -0.00700619,
2911
+ 0.01157968,
2912
+ -0.01269246,
2913
+ 0.02173179,
2914
+ -0.00022381,
2915
+ -0.01735442,
2916
+ -0.01220535,
2917
+ -0.02978618,
2918
+ 0.00487368,
2919
+ -0.00201816,
2920
+ -0.0065471,
2921
+ 0.01840364,
2922
+ -0.0068554,
2923
+ 0.00667753,
2924
+ 0.01900262,
2925
+ 0.0079251,
2926
+ -0.00720076,
2927
+ 0.0348928,
2928
+ 0.03306156,
2929
+ -0.02104476,
2930
+ 0.01431296,
2931
+ 0.01034307,
2932
+ 0.03554063,
2933
+ 0.01929511,
2934
+ -0.03064406,
2935
+ 0.00852042,
2936
+ 0.01728075,
2937
+ 0.01276018,
2938
+ 0.01369407,
2939
+ -0.00783796,
2940
+ -0.00288487,
2941
+ -0.02263158,
2942
+ -0.01975801,
2943
+ -0.00824996,
2944
+ -0.01331091,
2945
+ -0.03441912,
2946
+ 0.00086305,
2947
+ -0.00338286,
2948
+ -0.04317742,
2949
+ -0.01347583,
2950
+ -0.00972787,
2951
+ 0.00519537,
2952
+ -0.00305993,
2953
+ 0.003299,
2954
+ -0.02064933,
2955
+ -0.00521341,
2956
+ 0.0082595,
2957
+ 0.0147135,
2958
+ -0.02325539,
2959
+ -0.02601415,
2960
+ -0.01221036,
2961
+ -0.02321008,
2962
+ -0.03255986,
2963
+ 0.00117438,
2964
+ 0.0051252,
2965
+ -0.02630395,
2966
+ 0.01018165,
2967
+ -0.00126909,
2968
+ 0.00018002,
2969
+ 0.0206973,
2970
+ -0.0103088,
2971
+ -0.01774329,
2972
+ 0.02208793,
2973
+ 0.01005429,
2974
+ -0.00241477,
2975
+ -0.03558522,
2976
+ 0.02600474,
2977
+ 0.01392036,
2978
+ 0.0007913,
2979
+ 0.00867003,
2980
+ -0.02678201,
2981
+ 0.00417043,
2982
+ -0.00349747,
2983
+ 0.02998451,
2984
+ -0.0076973,
2985
+ -0.0183407,
2986
+ -0.01780671,
2987
+ 0.01216279,
2988
+ 0.02785827,
2989
+ 0.00912529,
2990
+ 0.00922449,
2991
+ -0.01494971,
2992
+ 0.00022192,
2993
+ -0.00362771,
2994
+ 0.01362053,
2995
+ 0.01309695,
2996
+ 0.01269907,
2997
+ 0.0076792,
2998
+ -0.00546721,
2999
+ -0.00254069,
3000
+ 0.01004402,
3001
+ -0.00896631,
3002
+ -0.0116364,
3003
+ -0.00836369,
3004
+ 0.00731342,
3005
+ 0.01272695,
3006
+ -0.01767886,
3007
+ 0.02485484,
3008
+ 0.02326213,
3009
+ -0.04928953,
3010
+ -0.01117503,
3011
+ 0.02420524,
3012
+ 0.0063097,
3013
+ -0.01632563,
3014
+ 0.00135226,
3015
+ -0.02587743,
3016
+ 0.01592461,
3017
+ 0.00361057,
3018
+ 0.01816907,
3019
+ -0.0003399,
3020
+ -0.01344171,
3021
+ 0.02377179,
3022
+ 0.00842966,
3023
+ -0.0107262,
3024
+ 0.03317228,
3025
+ 0.00435296,
3026
+ 0.01918977,
3027
+ 0.04618406,
3028
+ -0.00789822,
3029
+ 0.0302141,
3030
+ 0.01307326,
3031
+ 0.01281024,
3032
+ -0.01703183,
3033
+ 0.01827971,
3034
+ -0.01626707,
3035
+ 0.0262253,
3036
+ 0.00052332,
3037
+ -0.00336457,
3038
+ -0.00179358,
3039
+ 0.00217421,
3040
+ -0.02386117,
3041
+ -0.00263912,
3042
+ 0.01809951,
3043
+ 0.00450152,
3044
+ 0.01284222,
3045
+ -0.03143772,
3046
+ 0.01057556,
3047
+ -0.04545506,
3048
+ -0.02452004,
3049
+ -0.00122417,
3050
+ -0.0313464,
3051
+ 0.03283778,
3052
+ -0.03497106,
3053
+ 0.00327116,
3054
+ 0.00402971,
3055
+ 0.00810157,
3056
+ 0.00124328,
3057
+ -0.00593415,
3058
+ 0.01207904,
3059
+ -0.01564144,
3060
+ -0.01598497,
3061
+ 0.00245661,
3062
+ -0.01651567,
3063
+ -0.0364025,
3064
+ 0.016465,
3065
+ 0.01770067,
3066
+ -0.01766352,
3067
+ 0.02599589,
3068
+ 0.00321878,
3069
+ 0.02408964,
3070
+ -0.0048591,
3071
+ -0.02150145,
3072
+ 0.02837163,
3073
+ -0.00355406,
3074
+ -0.01425166,
3075
+ -0.01394022,
3076
+ -0.0054518,
3077
+ 0.00785455,
3078
+ 0.00858407,
3079
+ -0.00508778,
3080
+ -0.02234841,
3081
+ -0.04273375,
3082
+ 0.03256549,
3083
+ -0.00324295,
3084
+ -0.01093053,
3085
+ 0.00072815,
3086
+ -0.0119415,
3087
+ -0.00664757,
3088
+ 0.00496523,
3089
+ 0.00561876,
3090
+ 0.01784011,
3091
+ -0.01179754,
3092
+ -0.02320364,
3093
+ -0.02545631,
3094
+ 0.00336456,
3095
+ 0.00665186,
3096
+ -0.00832409,
3097
+ -0.01245756,
3098
+ -0.01078675,
3099
+ 0.01177812,
3100
+ 0.0095338,
3101
+ -0.00209641,
3102
+ 0.01275474,
3103
+ 0.03254699,
3104
+ -0.00988943,
3105
+ -0.00097725,
3106
+ -0.00635651,
3107
+ -0.00269509,
3108
+ -0.0119646,
3109
+ -0.00504241,
3110
+ 0.0089832,
3111
+ 0.01423179,
3112
+ -0.01198032,
3113
+ 0.01199757,
3114
+ 0.02696822,
3115
+ -0.02898989,
3116
+ 0.01225323,
3117
+ 0.00347795,
3118
+ -0.01845676,
3119
+ -0.00278158,
3120
+ -0.00845704,
3121
+ -0.01078037,
3122
+ -0.00613572,
3123
+ 0.00558208,
3124
+ 0.00566338,
3125
+ -0.00973258,
3126
+ -0.01901137,
3127
+ 0.0017411,
3128
+ -0.03024946,
3129
+ -0.00683895,
3130
+ 0.00152097,
3131
+ 0.00843003,
3132
+ 0.00574749,
3133
+ -0.00256013,
3134
+ -0.01349887,
3135
+ 0.00611601,
3136
+ 0.01228548,
3137
+ 0.00169058,
3138
+ 0.00807445,
3139
+ 0.00318968,
3140
+ -0.0221693,
3141
+ -0.01783665,
3142
+ 0.01726582,
3143
+ 0.01524275,
3144
+ -0.00177572,
3145
+ 0.02865437,
3146
+ 0.01760579,
3147
+ -0.01952672,
3148
+ -0.01631412,
3149
+ 0.01287163,
3150
+ 0.00356905,
3151
+ -0.00483905,
3152
+ -0.0090318,
3153
+ -0.04754067,
3154
+ -0.011511,
3155
+ -0.01697593,
3156
+ -0.02475849,
3157
+ 0.02553634,
3158
+ -0.02044728,
3159
+ 0.00090611,
3160
+ 0.00646554,
3161
+ -0.01823398,
3162
+ -0.01946126,
3163
+ -0.0062193,
3164
+ -0.00040626,
3165
+ -0.0193678,
3166
+ -0.0343605,
3167
+ 0.00704293,
3168
+ 0.01018505,
3169
+ -0.01024959,
3170
+ 0.01194722,
3171
+ 0.02828199,
3172
+ -0.03054512,
3173
+ 0.00385779,
3174
+ -0.02005518,
3175
+ 0.01599522,
3176
+ 0.00011745,
3177
+ 0.02317094,
3178
+ -0.00441486,
3179
+ 0.01399615,
3180
+ 0.02148392,
3181
+ -0.0180247,
3182
+ 0.00072905,
3183
+ -0.00508529,
3184
+ -0.0009809,
3185
+ -0.01381451,
3186
+ 0.02399625,
3187
+ 0.02420016,
3188
+ -0.00935192,
3189
+ 0.00158473,
3190
+ 0.02889357,
3191
+ 0.0075459,
3192
+ 0.03266473,
3193
+ 0.00581206,
3194
+ -0.00822044,
3195
+ -0.01659222,
3196
+ -0.02766358,
3197
+ -0.01591903,
3198
+ 0.0120321,
3199
+ 0.006772,
3200
+ -0.00306966,
3201
+ -0.00225038,
3202
+ 0.00448638,
3203
+ 0.00676812,
3204
+ -0.02272712,
3205
+ -0.01672792,
3206
+ -0.03724678,
3207
+ -0.03443447,
3208
+ -0.00684222,
3209
+ -0.00061584,
3210
+ 0.00043113,
3211
+ -0.00941609,
3212
+ -0.02079861,
3213
+ -0.0218753,
3214
+ 0.0014661,
3215
+ 0.02277899,
3216
+ -0.03370588,
3217
+ -0.04389111,
3218
+ -0.01075738,
3219
+ -0.02132445,
3220
+ -0.0392971,
3221
+ -0.00546209,
3222
+ -0.01508621,
3223
+ -0.02128147,
3224
+ 0.01646507,
3225
+ -0.01964691,
3226
+ -0.01943282
3227
+ ],
3228
+ "cols": [
3229
+ 0,
3230
+ 11,
3231
+ 25,
3232
+ 35,
3233
+ 37,
3234
+ 38,
3235
+ 39,
3236
+ 42,
3237
+ 46,
3238
+ 50,
3239
+ 56,
3240
+ 61,
3241
+ 63,
3242
+ 65,
3243
+ 67,
3244
+ 68,
3245
+ 79,
3246
+ 105,
3247
+ 129,
3248
+ 135,
3249
+ 169,
3250
+ 215,
3251
+ 259,
3252
+ 264,
3253
+ 266,
3254
+ 294,
3255
+ 371,
3256
+ 412,
3257
+ 414,
3258
+ 426,
3259
+ 452,
3260
+ 496,
3261
+ 504,
3262
+ 505,
3263
+ 509,
3264
+ 516,
3265
+ 525,
3266
+ 580,
3267
+ 582,
3268
+ 601,
3269
+ 638,
3270
+ 682,
3271
+ 733,
3272
+ 738,
3273
+ 750,
3274
+ 882,
3275
+ 892,
3276
+ 926,
3277
+ 1060,
3278
+ 1087,
3279
+ 1130,
3280
+ 1155,
3281
+ 1184,
3282
+ 1321,
3283
+ 1702,
3284
+ 1787,
3285
+ 2005,
3286
+ 2066,
3287
+ 2248,
3288
+ 2252,
3289
+ 2288,
3290
+ 2431,
3291
+ 2612,
3292
+ 3003
3293
+ ]
3294
+ },
3295
+ "19": {
3296
+ "alpha": 6,
3297
+ "dir": [
3298
+ -0.49866527,
3299
+ 0.04483794,
3300
+ 0.08653121,
3301
+ 0.04403261,
3302
+ 0.06070542,
3303
+ -0.18010071,
3304
+ 0.02048543,
3305
+ -0.01286332,
3306
+ -0.00027879,
3307
+ 0.01557074,
3308
+ 0.0579919,
3309
+ 0.03242711,
3310
+ 0.04437343,
3311
+ 0.11831202,
3312
+ 0.04191576,
3313
+ 0.1096428,
3314
+ 0.08484758,
3315
+ 0.19838709,
3316
+ -0.15137778,
3317
+ -0.06891333,
3318
+ 0.09073912,
3319
+ 0.05171521,
3320
+ 0.11006631,
3321
+ 0.0956018,
3322
+ -0.03853878,
3323
+ 0.00064215,
3324
+ 0.0313293,
3325
+ 0.01706532,
3326
+ 0.01163942,
3327
+ 0.03902397,
3328
+ 0.02441614,
3329
+ -0.15192458,
3330
+ 0.05112223,
3331
+ -0.04017477,
3332
+ -0.0822874,
3333
+ 0.0093005,
3334
+ -0.00961613,
3335
+ 0.16345182,
3336
+ -0.06426762,
3337
+ -0.01643771,
3338
+ -0.07640368,
3339
+ 0.00049388,
3340
+ -0.02564682,
3341
+ -0.01009604,
3342
+ -0.01522518,
3343
+ -0.02150765,
3344
+ 0.0845025,
3345
+ -0.04340943,
3346
+ 0.04411089,
3347
+ 0.08151267,
3348
+ -0.00340664,
3349
+ 0.00123752,
3350
+ -0.0378892,
3351
+ 0.00448848,
3352
+ 0.03228236,
3353
+ 0.07224466,
3354
+ 0.04673243,
3355
+ 0.05504248,
3356
+ -0.05127607,
3357
+ -0.03776409,
3358
+ 0.02176227,
3359
+ -0.08630367,
3360
+ -0.04531471,
3361
+ 0.0296373,
3362
+ 0.06976917,
3363
+ 0.0295671,
3364
+ -0.00246833,
3365
+ -0.02473946,
3366
+ -0.05511018,
3367
+ -0.03542427,
3368
+ 0.02730425,
3369
+ 0.01280087,
3370
+ -0.07081246,
3371
+ -0.00496536,
3372
+ -0.0890561,
3373
+ 0.00800353,
3374
+ -0.05013782,
3375
+ 0.02035334,
3376
+ -0.0028146,
3377
+ 0.00412351,
3378
+ 0.05471795,
3379
+ 0.06011825,
3380
+ 0.02483712,
3381
+ -0.04671688,
3382
+ 0.02074336,
3383
+ 0.09762273,
3384
+ 0.08155826,
3385
+ -0.02676294,
3386
+ 0.02521992,
3387
+ 0.03532628,
3388
+ 0.04708899,
3389
+ -0.05322118,
3390
+ -0.05567474,
3391
+ -0.00651496,
3392
+ 0.04405286,
3393
+ -0.01254328,
3394
+ -0.02278901,
3395
+ -0.00852609,
3396
+ 0.00359337,
3397
+ 0.00812188,
3398
+ -0.05064651,
3399
+ -0.01694964,
3400
+ -0.00542477,
3401
+ 0.03623243,
3402
+ -0.02757451,
3403
+ 0.0051043,
3404
+ 0.03470914,
3405
+ 0.00490483,
3406
+ -0.03334099,
3407
+ 0.00603678,
3408
+ 0.0300563,
3409
+ 0.04627726,
3410
+ 0.00187998,
3411
+ -0.0416753,
3412
+ -0.0090613,
3413
+ 0.05596276,
3414
+ -0.02103174,
3415
+ -0.06238985,
3416
+ -0.01180217,
3417
+ 0.04171294,
3418
+ 0.01249574,
3419
+ 0.02816214,
3420
+ -0.0176421,
3421
+ 0.01949446,
3422
+ -0.03917667,
3423
+ 0.00881786,
3424
+ -0.02624751,
3425
+ 0.00509142,
3426
+ -0.0077283,
3427
+ 0.06086404,
3428
+ -0.02195361,
3429
+ 0.00030291,
3430
+ 0.03111923,
3431
+ 0.03329645,
3432
+ 0.01400976,
3433
+ -0.01356143,
3434
+ -0.00565462,
3435
+ -0.06398953,
3436
+ 0.0551735,
3437
+ -0.00848315,
3438
+ 0.00687203,
3439
+ 0.01781475,
3440
+ 0.05540646,
3441
+ 0.03959751,
3442
+ -0.01159826,
3443
+ -0.01160534,
3444
+ 0.0048661,
3445
+ -0.00608777,
3446
+ 0.02319413,
3447
+ -0.01162495,
3448
+ -0.0077591,
3449
+ 0.01756361,
3450
+ 0.02298547,
3451
+ 0.02433926,
3452
+ 0.01067325,
3453
+ 0.00118467,
3454
+ -0.02158724,
3455
+ 0.00489646,
3456
+ 0.00243894,
3457
+ -0.01124737,
3458
+ 0.06788686,
3459
+ -0.00478992,
3460
+ 0.00929579,
3461
+ 0.02146945,
3462
+ -0.02837166,
3463
+ 0.00487398,
3464
+ 0.00094381,
3465
+ -0.03970704,
3466
+ -0.03389104,
3467
+ -0.00084383,
3468
+ -0.02727271,
3469
+ -0.02561967,
3470
+ 0.00955132,
3471
+ 0.01581585,
3472
+ -0.00645247,
3473
+ -0.03326835,
3474
+ 0.04197594,
3475
+ -0.00990736,
3476
+ 0.02483497,
3477
+ -0.01266322,
3478
+ -0.02937236,
3479
+ 0.02056376,
3480
+ -0.00301939,
3481
+ -0.00960709,
3482
+ -0.02398397,
3483
+ -0.01240014,
3484
+ -0.04018981,
3485
+ -0.01065147,
3486
+ 0.02541097,
3487
+ 0.01085518,
3488
+ -0.04091425,
3489
+ 0.01861534,
3490
+ 0.02755524,
3491
+ 0.01442517,
3492
+ -0.00980565,
3493
+ 0.01643772,
3494
+ 0.0103014,
3495
+ -0.00193121,
3496
+ -0.01158493,
3497
+ -0.04491895,
3498
+ 0.00210387,
3499
+ -0.0158687,
3500
+ -0.01925263,
3501
+ 0.00858816,
3502
+ 0.00823828,
3503
+ 0.05501426,
3504
+ 0.01001832,
3505
+ -0.01590634,
3506
+ -0.04254342,
3507
+ -0.00362291,
3508
+ -0.00355009,
3509
+ 0.00977466,
3510
+ -0.04294737,
3511
+ -0.01187379,
3512
+ -0.00624066,
3513
+ -0.00100696,
3514
+ 0.01209668,
3515
+ 0.01955999,
3516
+ 0.00612554,
3517
+ -0.09073611,
3518
+ -0.0121392,
3519
+ -0.00602088,
3520
+ 0.02669331,
3521
+ -0.01423631,
3522
+ 0.02193962,
3523
+ -0.06434545,
3524
+ -0.00057406,
3525
+ -0.00482061,
3526
+ -0.0240023,
3527
+ -0.01730228,
3528
+ -0.04747942,
3529
+ 0.05685804,
3530
+ 0.00550534,
3531
+ 0.0255143,
3532
+ 0.00708329,
3533
+ -0.01461607,
3534
+ 0.00074817,
3535
+ 0.01754509,
3536
+ -0.02909672,
3537
+ 0.04151966,
3538
+ -0.00419367,
3539
+ 0.0328035,
3540
+ 0.01885574,
3541
+ -0.00191183,
3542
+ 0.03820024,
3543
+ 0.00341159,
3544
+ 0.0167866,
3545
+ -0.05124178,
3546
+ -0.01221086,
3547
+ -0.00337703,
3548
+ 0.03029857,
3549
+ -0.00824676,
3550
+ -0.02232636,
3551
+ -0.00764648,
3552
+ 0.00037306,
3553
+ 0.08489247,
3554
+ -0.04158763,
3555
+ -0.02465037,
3556
+ 0.0038072,
3557
+ 0.00986983,
3558
+ -0.02635114,
3559
+ -0.00054587,
3560
+ 0.01122991,
3561
+ 0.01526793,
3562
+ 0.01308451,
3563
+ 0.00947611,
3564
+ 0.01079197,
3565
+ 0.00301678,
3566
+ 0.00045249,
3567
+ 0.01157898,
3568
+ 0.00364791,
3569
+ -0.01974811,
3570
+ 0.00853061,
3571
+ -0.03393469,
3572
+ -0.02398907,
3573
+ 0.01186427,
3574
+ 0.00773612,
3575
+ 0.00182465,
3576
+ -0.03346312,
3577
+ -0.05178841,
3578
+ 0.01664432,
3579
+ -0.00867638,
3580
+ -0.04087122,
3581
+ 0.00350367,
3582
+ 0.01055847,
3583
+ -0.01330485,
3584
+ 0.0321027,
3585
+ -0.01336184,
3586
+ -0.00791821,
3587
+ -0.01172312,
3588
+ 0.01268876,
3589
+ -0.01336592,
3590
+ -0.01299944,
3591
+ 0.01018297,
3592
+ 0.00367312,
3593
+ -0.02798145,
3594
+ 0.01043847,
3595
+ -0.00059207,
3596
+ -0.02061029,
3597
+ -0.02362762,
3598
+ 0.08393674,
3599
+ -0.0145678,
3600
+ -0.01420483,
3601
+ 0.05421852,
3602
+ -0.01871055,
3603
+ 0.01786913,
3604
+ -0.00289787,
3605
+ -0.01177605,
3606
+ -0.01852703,
3607
+ 0.03584592,
3608
+ -0.00914478,
3609
+ -0.03144572,
3610
+ -0.00690102,
3611
+ 0.01117276,
3612
+ -0.01311047,
3613
+ 0.02009693,
3614
+ -0.07556609,
3615
+ -0.06147706,
3616
+ 0.00172751,
3617
+ 0.03443452,
3618
+ 0.01491988,
3619
+ 0.00528303,
3620
+ 0.01334639,
3621
+ 0.00624813,
3622
+ -0.01138349,
3623
+ -0.01042592,
3624
+ 0.02700793,
3625
+ 0.00862187,
3626
+ 0.01563637,
3627
+ -0.02544332,
3628
+ 0.03750516,
3629
+ -0.01093695,
3630
+ 0.01380753,
3631
+ 0.04961452,
3632
+ 0.01869025,
3633
+ -0.00684568,
3634
+ 0.00716816,
3635
+ 0.00134637,
3636
+ -0.00351985,
3637
+ 0.03323479,
3638
+ -0.10388357,
3639
+ 0.02342377,
3640
+ -0.02660153,
3641
+ -0.00555478,
3642
+ -0.0143756,
3643
+ 0.00243205,
3644
+ 0.00385063,
3645
+ -0.00998113,
3646
+ -0.02333265,
3647
+ 0.02243167,
3648
+ -0.02253078,
3649
+ -0.00044902,
3650
+ -0.01409877,
3651
+ -0.00337895,
3652
+ -0.02913127,
3653
+ -0.03442011,
3654
+ -0.00535547,
3655
+ -0.01662975,
3656
+ 0.01690075,
3657
+ -0.02125359,
3658
+ 0.0155171,
3659
+ 0.0064527,
3660
+ 0.01973493,
3661
+ -0.02252325,
3662
+ 0.01239673,
3663
+ 0.02198475,
3664
+ -0.01154951,
3665
+ -0.00176781,
3666
+ 0.03128855,
3667
+ 0.0164711,
3668
+ -0.00352959,
3669
+ -0.00919291,
3670
+ -0.00051269,
3671
+ 0.00272963,
3672
+ -0.01334997,
3673
+ -0.02031143,
3674
+ -0.00102859,
3675
+ 0.01847753,
3676
+ -0.00953855,
3677
+ -0.0136291,
3678
+ -0.00146926,
3679
+ 0.00225613,
3680
+ -0.01099077,
3681
+ -0.0059908,
3682
+ -0.00535733,
3683
+ -0.01735635,
3684
+ 0.00250816,
3685
+ 0.01244467,
3686
+ 0.01221272,
3687
+ 0.00098979,
3688
+ -0.00353166,
3689
+ -0.00583052,
3690
+ 0.00087737,
3691
+ 0.00923937,
3692
+ 0.00468853,
3693
+ 0.03266098,
3694
+ -0.00799337,
3695
+ -0.00690205,
3696
+ -0.00479467,
3697
+ 0.00531395,
3698
+ -0.00968522,
3699
+ -0.00285753,
3700
+ 0.00617076,
3701
+ -0.00398007,
3702
+ -0.00798379,
3703
+ -0.01925957,
3704
+ -0.04746011,
3705
+ 0.01850612,
3706
+ -0.00163182,
3707
+ -0.00469556,
3708
+ 0.02426587,
3709
+ -0.02222052,
3710
+ 0.00818618,
3711
+ -0.01007741,
3712
+ -0.02858347,
3713
+ -0.00121701,
3714
+ -0.06705105,
3715
+ -0.01600351,
3716
+ -0.02505735,
3717
+ -0.00761152,
3718
+ -0.01960301,
3719
+ -0.00975214,
3720
+ -0.01505511,
3721
+ -0.02469417,
3722
+ -0.10968003,
3723
+ 0.01911923,
3724
+ 0.00610741,
3725
+ 0.00117041,
3726
+ 0.00868673,
3727
+ -0.0041148,
3728
+ -0.02288475,
3729
+ 0.0138887,
3730
+ 0.00516456,
3731
+ -0.00078531,
3732
+ 0.00329392,
3733
+ 0.03360431,
3734
+ -0.00695994,
3735
+ 0.00668374,
3736
+ 0.01618747,
3737
+ 0.01681385,
3738
+ -0.00357584,
3739
+ -0.01117303,
3740
+ -0.01453918,
3741
+ -0.01406458,
3742
+ 0.01796291,
3743
+ 0.02957689,
3744
+ 0.01712581,
3745
+ 0.02696638,
3746
+ 0.00489161,
3747
+ -0.0027144,
3748
+ 0.00797566,
3749
+ 0.01722849,
3750
+ -0.01348964,
3751
+ -0.00059619,
3752
+ 0.0062006,
3753
+ -0.01288154,
3754
+ -0.01594781,
3755
+ 0.00060741,
3756
+ 0.03926362,
3757
+ 0.000648,
3758
+ -0.00758998,
3759
+ 0.00969625,
3760
+ 0.01107869,
3761
+ -0.00975366,
3762
+ 0.01968245,
3763
+ 0.01074674,
3764
+ 0.0149546,
3765
+ -0.00488283,
3766
+ 0.01590041,
3767
+ -0.00571848,
3768
+ -0.01415043,
3769
+ 0.04789799,
3770
+ 0.00476613,
3771
+ 0.01109879,
3772
+ -0.00158805,
3773
+ -0.01026194,
3774
+ 0.00513164,
3775
+ 0.00177138,
3776
+ 0.01509304,
3777
+ -0.04241737,
3778
+ -0.02120126,
3779
+ -0.00480244,
3780
+ -0.0315622,
3781
+ 0.00439782,
3782
+ -0.00950842,
3783
+ -0.01790279,
3784
+ -0.01042813,
3785
+ -0.0103942,
3786
+ -0.00075724,
3787
+ 0.00224846,
3788
+ -0.03605071,
3789
+ -0.00766446,
3790
+ 0.00278169,
3791
+ 0.00702316,
3792
+ -0.00182345,
3793
+ 0.01892289,
3794
+ 0.00708544,
3795
+ -0.01113682,
3796
+ 0.03328981,
3797
+ -0.0192459,
3798
+ 0.0140041,
3799
+ 0.00527974,
3800
+ -0.01008013,
3801
+ 0.01208155,
3802
+ 0.00602798,
3803
+ -0.00538647,
3804
+ -0.00988771,
3805
+ -0.01595749,
3806
+ 0.00868384,
3807
+ 0.03627389,
3808
+ 0.00344467,
3809
+ -0.01577122,
3810
+ -4.978e-05,
3811
+ -0.00462439,
3812
+ -0.01982905,
3813
+ 0.00348027,
3814
+ -0.06243323,
3815
+ 0.01559778,
3816
+ 0.00743285,
3817
+ 0.01567806,
3818
+ 0.03248803,
3819
+ 0.00350522,
3820
+ -0.01878736,
3821
+ -0.00510001,
3822
+ -0.01560209,
3823
+ 0.00595972,
3824
+ 0.00423453,
3825
+ -0.00234233,
3826
+ 0.00050524,
3827
+ -0.00564994,
3828
+ -0.01667203,
3829
+ -0.00596002,
3830
+ 0.01465663,
3831
+ -0.01919699,
3832
+ 0.01211679,
3833
+ 0.00254716,
3834
+ 0.01503418,
3835
+ 0.0095837,
3836
+ 0.02680599,
3837
+ 0.01051945,
3838
+ -0.00725303,
3839
+ -0.00185183,
3840
+ -0.00087937,
3841
+ -0.03413209,
3842
+ -0.00287935,
3843
+ -0.0058744,
3844
+ -0.00796591,
3845
+ 0.02322935,
3846
+ 0.01579706,
3847
+ 0.02696734,
3848
+ -0.00191688,
3849
+ -0.04072197,
3850
+ 0.01511676,
3851
+ -0.00058316,
3852
+ -0.00465454,
3853
+ -0.02495283,
3854
+ -0.01099585,
3855
+ 0.0051539,
3856
+ -0.01437707,
3857
+ 0.02481943,
3858
+ -0.00748865,
3859
+ 0.01838564,
3860
+ 0.01209936,
3861
+ 0.01926963,
3862
+ -0.00329364,
3863
+ -0.00074246,
3864
+ -0.00170502,
3865
+ 0.01563909,
3866
+ 0.00117787,
3867
+ 0.00813994,
3868
+ 0.01232119,
3869
+ -0.0040677,
3870
+ 0.00620609,
3871
+ 0.01448978,
3872
+ 0.00922149,
3873
+ 0.01071046,
3874
+ 0.01238525,
3875
+ 0.05554213,
3876
+ 0.02819438,
3877
+ 0.02504348,
3878
+ -0.00285749,
3879
+ 0.00558255,
3880
+ -0.01137746,
3881
+ -0.0158543,
3882
+ -0.0189033,
3883
+ 0.0121513,
3884
+ -0.03731599,
3885
+ -0.00747662,
3886
+ 0.00543316,
3887
+ -0.01170763,
3888
+ -0.00087092,
3889
+ 0.01825779,
3890
+ 0.03395717,
3891
+ 0.01406542,
3892
+ -0.0175342,
3893
+ -0.01369371,
3894
+ 0.00817646,
3895
+ 0.02041542,
3896
+ -0.00884456,
3897
+ -0.00418454,
3898
+ 0.01211634,
3899
+ -0.00276281,
3900
+ -0.00371123,
3901
+ 0.01992132,
3902
+ 0.00124228,
3903
+ 0.01579323,
3904
+ -0.01380612,
3905
+ 0.00651301,
3906
+ -0.00965464,
3907
+ 0.00304045,
3908
+ 0.00938329,
3909
+ 0.00761521,
3910
+ 0.00207386,
3911
+ -0.01233432,
3912
+ -0.00210403,
3913
+ -0.01623478,
3914
+ -0.02811889,
3915
+ -0.02598538,
3916
+ -0.01444949,
3917
+ 0.01080103,
3918
+ 0.00122658,
3919
+ 0.02289422,
3920
+ -0.01870971,
3921
+ 0.01919318,
3922
+ 0.03217082,
3923
+ -0.00920643,
3924
+ -0.0215708,
3925
+ -0.03158914,
3926
+ -0.02056642,
3927
+ 0.01231547,
3928
+ -0.00952645,
3929
+ -0.01507373,
3930
+ 0.00180383,
3931
+ 0.00179136,
3932
+ -0.00920329,
3933
+ -0.00321118,
3934
+ 0.00853423,
3935
+ -0.02645662,
3936
+ -0.01476324,
3937
+ 0.01350342,
3938
+ 0.03095776,
3939
+ 0.01938323,
3940
+ -0.00227899,
3941
+ -0.03293785,
3942
+ 0.01162287,
3943
+ -0.00825512,
3944
+ 0.01109362,
3945
+ 0.02965567,
3946
+ -0.01720749,
3947
+ -0.01672695,
3948
+ -0.00307062,
3949
+ -0.01065709,
3950
+ 0.00458977,
3951
+ -0.00416517,
3952
+ 0.00277024,
3953
+ 0.01484402,
3954
+ 0.02151607,
3955
+ 0.01367335,
3956
+ -0.01716657,
3957
+ 0.01993701,
3958
+ 0.02926229,
3959
+ 0.00564985,
3960
+ 0.00449883,
3961
+ 0.01318398,
3962
+ 0.01221114,
3963
+ -0.01044224,
3964
+ -0.01975954,
3965
+ 0.0114554,
3966
+ 0.00441833,
3967
+ -0.01906356,
3968
+ 0.02950616,
3969
+ -0.01843064,
3970
+ 0.01197973,
3971
+ -0.01304068,
3972
+ -0.01503828,
3973
+ -0.0170262,
3974
+ -0.01332446,
3975
+ 0.00610302,
3976
+ -0.04128838,
3977
+ -0.00567338,
3978
+ -0.01320273,
3979
+ -0.01186861,
3980
+ 0.01055282,
3981
+ -0.02254591,
3982
+ 0.00607345,
3983
+ 0.00576573,
3984
+ -0.00970896,
3985
+ 0.01475807,
3986
+ -0.02851543,
3987
+ 0.00067781,
3988
+ 0.00906039,
3989
+ 0.01197799,
3990
+ 0.03900312,
3991
+ 0.0013545,
3992
+ -0.0017178,
3993
+ 0.0235684,
3994
+ -0.03324137,
3995
+ -0.00347653,
3996
+ -0.01606604,
3997
+ 0.00885091,
3998
+ -0.00451053,
3999
+ 0.01240247,
4000
+ 0.01726436,
4001
+ 0.01211572,
4002
+ -0.01130474,
4003
+ -0.00727773,
4004
+ 0.00091156,
4005
+ 0.01911378,
4006
+ 0.011722,
4007
+ 0.00058718,
4008
+ 0.00973076,
4009
+ 0.00634966,
4010
+ -0.01755847,
4011
+ -0.02091217,
4012
+ -0.01703672,
4013
+ 0.01370771,
4014
+ -0.02025203,
4015
+ 0.01116729,
4016
+ 0.00527792,
4017
+ -0.01642915,
4018
+ -0.00243309,
4019
+ -0.00065123,
4020
+ 0.01747186,
4021
+ -0.00997447,
4022
+ 0.00312761,
4023
+ 0.00623531,
4024
+ -0.03428432,
4025
+ -0.00202292,
4026
+ 0.00637191,
4027
+ 0.03578324,
4028
+ 0.03068558,
4029
+ -0.01501003,
4030
+ -0.00663668,
4031
+ -0.01148632,
4032
+ 0.00972851,
4033
+ 0.01488661,
4034
+ -0.00356572,
4035
+ -0.02273521,
4036
+ 0.00502845,
4037
+ -0.02030335,
4038
+ 0.00166387,
4039
+ -0.00340235,
4040
+ -0.00562466,
4041
+ 0.00728441,
4042
+ -0.00260638,
4043
+ -0.03839081,
4044
+ -0.008985,
4045
+ 0.00218783,
4046
+ 0.01611389,
4047
+ 0.00933593,
4048
+ -0.00187294,
4049
+ 0.00745403,
4050
+ -0.00690003,
4051
+ -0.02526955,
4052
+ 0.02212381,
4053
+ -0.00030148,
4054
+ -0.01510011,
4055
+ -0.01932472,
4056
+ -0.03330071,
4057
+ -0.01278328,
4058
+ -0.0240929,
4059
+ 0.0307283,
4060
+ -0.01996295,
4061
+ 0.00107976,
4062
+ -0.00161456,
4063
+ -0.00260982,
4064
+ 0.01108187,
4065
+ 0.00801411,
4066
+ -0.00203263,
4067
+ 0.02143234,
4068
+ -0.01392478,
4069
+ 0.00731467,
4070
+ -0.02988216,
4071
+ 0.01828213,
4072
+ 0.00886731,
4073
+ -0.02619752,
4074
+ -0.00904667,
4075
+ -0.01002406,
4076
+ 0.00907868,
4077
+ -0.01247173,
4078
+ -0.00422791,
4079
+ -0.00707358,
4080
+ -0.01671953,
4081
+ -0.01462827,
4082
+ 0.01076446,
4083
+ 0.00044689,
4084
+ 0.00668957,
4085
+ -0.02379257,
4086
+ -0.00252276,
4087
+ -0.00053585,
4088
+ 0.00860764,
4089
+ 0.01476439,
4090
+ 0.02438155,
4091
+ -0.00497091,
4092
+ -0.00031928,
4093
+ 0.00063662,
4094
+ -0.04368747,
4095
+ -0.0275035,
4096
+ -0.00398356,
4097
+ 0.01002149,
4098
+ -0.01770317,
4099
+ 0.00091963,
4100
+ 0.00874953,
4101
+ -0.01610278,
4102
+ 0.00232937,
4103
+ 0.03462736,
4104
+ -0.01182502,
4105
+ -0.00796036,
4106
+ 0.00815754,
4107
+ -0.00974997,
4108
+ -0.02655735,
4109
+ 0.00698516,
4110
+ -0.00113735,
4111
+ 0.00650803,
4112
+ -0.03453831,
4113
+ 0.01165782,
4114
+ -0.01219975,
4115
+ -0.02069398,
4116
+ 0.00514758,
4117
+ -0.00833858,
4118
+ 0.00578136,
4119
+ -0.01246782,
4120
+ -0.00441516,
4121
+ 0.01645298,
4122
+ 0.02736051,
4123
+ -0.00781797,
4124
+ 0.00867647,
4125
+ -0.00502843,
4126
+ 0.00425443,
4127
+ -0.01442174,
4128
+ 0.01855954,
4129
+ -0.02583617,
4130
+ 0.04021493,
4131
+ -0.00768476,
4132
+ 0.00129157,
4133
+ 0.00881142,
4134
+ 0.0063479,
4135
+ -0.0120367,
4136
+ 0.00646048,
4137
+ -0.00491157,
4138
+ 0.023755,
4139
+ 0.00721263,
4140
+ 0.00155774,
4141
+ 0.01283433,
4142
+ -0.02943,
4143
+ -0.02881086,
4144
+ 0.01041367,
4145
+ -0.01840024,
4146
+ 0.02171823,
4147
+ -0.00567327,
4148
+ -0.00276232,
4149
+ 0.00148473,
4150
+ 0.02302389,
4151
+ -0.00975481,
4152
+ 0.00099824,
4153
+ 0.01985644,
4154
+ -0.00192401,
4155
+ -0.00617017,
4156
+ -2.538e-05,
4157
+ -0.03193742,
4158
+ -0.03092247,
4159
+ 0.01425309,
4160
+ -0.01900133,
4161
+ -0.02314746,
4162
+ -0.00357459,
4163
+ -0.01438221,
4164
+ 0.02652883,
4165
+ -0.02303528,
4166
+ -0.01041824,
4167
+ 0.02131381,
4168
+ 0.03382028,
4169
+ -0.02858257,
4170
+ -0.01228277,
4171
+ 0.00077239,
4172
+ 0.01865366,
4173
+ 0.00257365,
4174
+ -0.02251675,
4175
+ -0.01038951,
4176
+ -0.00826624,
4177
+ -0.00061366,
4178
+ -0.01293588,
4179
+ -0.02640678,
4180
+ -0.01507058,
4181
+ -0.0096333,
4182
+ -0.005784,
4183
+ -0.00196619,
4184
+ 0.02236821,
4185
+ -0.01330438,
4186
+ 0.00327015,
4187
+ -0.00786517,
4188
+ 0.00283161,
4189
+ -0.02694237,
4190
+ -0.00937843,
4191
+ 0.00266939,
4192
+ -0.0075223,
4193
+ 0.00277473,
4194
+ 0.01841909,
4195
+ 0.00641551,
4196
+ 0.00175325,
4197
+ -0.01986219,
4198
+ -0.00802713,
4199
+ -0.01577379,
4200
+ -0.00577331,
4201
+ 0.0009016,
4202
+ 0.0104826,
4203
+ 0.01410414,
4204
+ -0.00801914,
4205
+ 0.02181359,
4206
+ -0.00292495,
4207
+ 0.00389425,
4208
+ -0.01559183,
4209
+ 0.01793076,
4210
+ -0.01906901,
4211
+ -0.00501983,
4212
+ 0.01160918,
4213
+ -0.00288085,
4214
+ 0.00971605,
4215
+ -0.02800927,
4216
+ -0.01123048,
4217
+ -0.01906575,
4218
+ -0.01067145,
4219
+ -0.00029776,
4220
+ -0.00850199,
4221
+ 0.00057391,
4222
+ -0.00254053,
4223
+ -0.02233508,
4224
+ -0.02169339,
4225
+ -0.0151769,
4226
+ -0.00609579,
4227
+ -0.00705622,
4228
+ -0.0149173,
4229
+ -0.00437992,
4230
+ -0.00259468,
4231
+ 0.0052716,
4232
+ 0.02288501,
4233
+ 0.00927041,
4234
+ -0.00550658,
4235
+ -0.02116336,
4236
+ -0.01383797,
4237
+ 0.0036492,
4238
+ 0.03030897,
4239
+ 0.00985404,
4240
+ 0.00681804,
4241
+ 0.00413645,
4242
+ -0.01247669,
4243
+ 0.0150871,
4244
+ -0.00089391,
4245
+ 0.01245119,
4246
+ -0.01382493,
4247
+ -0.00465603,
4248
+ -0.04419245,
4249
+ 0.00211103,
4250
+ -0.01578317,
4251
+ -0.00727821,
4252
+ 0.01030676,
4253
+ -0.01007288,
4254
+ 0.01228494,
4255
+ 0.02027165,
4256
+ 0.01354787,
4257
+ -0.00042948,
4258
+ -0.00315299,
4259
+ -0.03178857,
4260
+ -0.00364243,
4261
+ 0.01836205,
4262
+ -0.00837707,
4263
+ 0.00443475,
4264
+ -0.03081122,
4265
+ -0.00710205,
4266
+ 0.02053505,
4267
+ -0.01760741,
4268
+ -0.00722017,
4269
+ -0.00764707,
4270
+ 0.01129031,
4271
+ -0.01561538,
4272
+ 0.00539249,
4273
+ -0.00413864,
4274
+ -0.01105609,
4275
+ 0.00242777,
4276
+ -0.01110355,
4277
+ -0.00310831,
4278
+ -0.01079649,
4279
+ 0.00431974,
4280
+ -0.02030705,
4281
+ 0.02523745,
4282
+ 0.01010057,
4283
+ 0.00415501,
4284
+ -0.00288939,
4285
+ 0.01196072,
4286
+ -0.00411965,
4287
+ -0.00083493,
4288
+ 0.02267452,
4289
+ -0.00319011,
4290
+ 0.0008131,
4291
+ 0.0010743,
4292
+ -0.0410697,
4293
+ -0.00669343,
4294
+ 0.00026216,
4295
+ 0.02969771,
4296
+ -0.00556103,
4297
+ -0.00890212,
4298
+ 0.01827613,
4299
+ -0.01220147,
4300
+ -0.00520677,
4301
+ -0.04481589,
4302
+ -0.00165549,
4303
+ -0.00732078,
4304
+ 0.01583677,
4305
+ -0.00748797,
4306
+ -0.00963265,
4307
+ -0.01356199,
4308
+ -0.00669439,
4309
+ 0.00836226,
4310
+ 0.02362512,
4311
+ -0.01708823,
4312
+ -0.01487742,
4313
+ 0.00612276,
4314
+ -0.0108666,
4315
+ -0.00820478,
4316
+ 0.0079394,
4317
+ 0.00539038,
4318
+ -0.03808598,
4319
+ -0.00721651,
4320
+ 0.00216078,
4321
+ -0.00077955
4322
+ ],
4323
+ "cols": [
4324
+ 1,
4325
+ 8,
4326
+ 11,
4327
+ 13,
4328
+ 14,
4329
+ 15,
4330
+ 24,
4331
+ 34,
4332
+ 36,
4333
+ 41,
4334
+ 44,
4335
+ 53,
4336
+ 57,
4337
+ 58,
4338
+ 61,
4339
+ 63,
4340
+ 66,
4341
+ 69,
4342
+ 79,
4343
+ 92,
4344
+ 95,
4345
+ 98,
4346
+ 99,
4347
+ 101,
4348
+ 119,
4349
+ 140,
4350
+ 145,
4351
+ 152,
4352
+ 161,
4353
+ 171,
4354
+ 177,
4355
+ 226,
4356
+ 240,
4357
+ 246,
4358
+ 253,
4359
+ 259,
4360
+ 265,
4361
+ 287,
4362
+ 288,
4363
+ 302,
4364
+ 324,
4365
+ 343,
4366
+ 351,
4367
+ 355,
4368
+ 450,
4369
+ 578,
4370
+ 579,
4371
+ 602,
4372
+ 614,
4373
+ 621,
4374
+ 795,
4375
+ 868,
4376
+ 946,
4377
+ 998,
4378
+ 1003,
4379
+ 1246,
4380
+ 1689,
4381
+ 1780,
4382
+ 1973,
4383
+ 2055,
4384
+ 2175,
4385
+ 2248,
4386
+ 2661,
4387
+ 3343
4388
+ ]
4389
+ }
4390
+ }
4391
+ }
4392
+ }
code/configs/rebuilt.json ADDED
@@ -0,0 +1,2197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "rebuilt": {
3
+ "about": "Lab 3: the edit rebuilt from the agent's 8 texts (alpha 6, my u, my top 64)",
4
+ "layers": {
5
+ "15": {
6
+ "alpha": 6,
7
+ "dir": [
8
+ -0.24953727,
9
+ 0.02032124,
10
+ 0.04771795,
11
+ 0.04000831,
12
+ 0.0143242,
13
+ -0.21995239,
14
+ -0.03093798,
15
+ -0.13847236,
16
+ -0.0687337,
17
+ 0.09629215,
18
+ 0.03552215,
19
+ 0.02262742,
20
+ -0.02543711,
21
+ 0.14368935,
22
+ 0.02360879,
23
+ 0.14979322,
24
+ 0.08297141,
25
+ 0.0825967,
26
+ -0.19932722,
27
+ -0.09169211,
28
+ 0.07426155,
29
+ 0.06157133,
30
+ 0.10419847,
31
+ 0.10763864,
32
+ -0.0389273,
33
+ -0.00109971,
34
+ 0.05881784,
35
+ 0.03821842,
36
+ -0.01023125,
37
+ -0.0155359,
38
+ 0.02342042,
39
+ -0.10402809,
40
+ 0.04680654,
41
+ -0.05265392,
42
+ -0.11036691,
43
+ -0.02119951,
44
+ 0.05915256,
45
+ 0.27601135,
46
+ -0.07837382,
47
+ -0.00347673,
48
+ -0.05321625,
49
+ 0.02205025,
50
+ -0.10572132,
51
+ 0.03832579,
52
+ -0.02087151,
53
+ -0.01947924,
54
+ 0.11653381,
55
+ -0.02051564,
56
+ -0.01071759,
57
+ -0.03027302,
58
+ -0.06089418,
59
+ 0.04463274,
60
+ -0.03164496,
61
+ 0.01951855,
62
+ -0.02302863,
63
+ 0.10757776,
64
+ 0.02056621,
65
+ 0.0343796,
66
+ -0.0021492,
67
+ 0.04744004,
68
+ 0.02343818,
69
+ -0.06339314,
70
+ -0.03820134,
71
+ 0.02501658,
72
+ 0.03175897,
73
+ -0.01598197,
74
+ 0.11075977,
75
+ -0.01552098,
76
+ 0.01513177,
77
+ -0.02714768,
78
+ 0.00190105,
79
+ 0.02348685,
80
+ -0.05483799,
81
+ -0.01767546,
82
+ -0.13193983,
83
+ 0.00077896,
84
+ -0.03830451,
85
+ 0.00821382,
86
+ -0.08470301,
87
+ -0.01127409,
88
+ 0.04484798,
89
+ 0.09724138,
90
+ 0.01788979,
91
+ -0.05973997,
92
+ -0.00031167,
93
+ 0.12036057,
94
+ 0.07074808,
95
+ -0.0102,
96
+ 0.00488621,
97
+ 0.00638768,
98
+ -0.03298204,
99
+ -0.04133184,
100
+ -0.0324551,
101
+ 0.01001455,
102
+ 0.04155349,
103
+ 0.02289063,
104
+ -0.0624258,
105
+ 0.04258976,
106
+ -0.03576536,
107
+ -0.00779208,
108
+ -0.01342702,
109
+ -0.05784821,
110
+ -0.01904367,
111
+ 0.0157368,
112
+ -0.03480317,
113
+ -0.02095752,
114
+ -0.00261995,
115
+ 0.02260072,
116
+ -0.05302511,
117
+ -0.02176841,
118
+ 0.00990718,
119
+ 0.06076119,
120
+ 0.0167202,
121
+ -0.01325404,
122
+ -0.01575754,
123
+ 0.08361955,
124
+ 0.00011008,
125
+ -0.03992344,
126
+ -0.08015352,
127
+ 0.05218405,
128
+ 0.0256574,
129
+ -0.00060408,
130
+ -0.01703091,
131
+ -0.01975281,
132
+ -0.03141097,
133
+ 0.02056967,
134
+ -0.00555578,
135
+ -0.04516612,
136
+ 0.02356683,
137
+ 0.06884296,
138
+ 0.00917865,
139
+ -0.02308802,
140
+ 0.02309628,
141
+ 0.01845497,
142
+ 0.00878982,
143
+ -0.0203653,
144
+ -0.00782054,
145
+ -0.06458497,
146
+ 0.09055139,
147
+ 0.00214113,
148
+ 0.02803794,
149
+ 0.04403042,
150
+ -0.00094625,
151
+ 0.00861618,
152
+ -0.02396201,
153
+ 0.0074091,
154
+ 0.06147441,
155
+ 0.02295231,
156
+ -0.01124603,
157
+ -0.01861799,
158
+ 0.00209179,
159
+ 0.00096943,
160
+ 0.01037515,
161
+ 0.01955204,
162
+ -0.02585044,
163
+ 0.01777056,
164
+ -0.03991476,
165
+ 0.03768253,
166
+ -0.00042653,
167
+ 0.00815431,
168
+ 0.04679773,
169
+ -0.02307066,
170
+ 0.03822683,
171
+ 0.01438087,
172
+ -0.02928447,
173
+ 0.01390374,
174
+ 0.09826396,
175
+ 0.03247706,
176
+ -0.03843113,
177
+ 0.02039174,
178
+ -0.03451136,
179
+ -0.04692665,
180
+ -0.01404249,
181
+ 0.01338147,
182
+ -0.01011026,
183
+ -0.00880787,
184
+ 0.01684509,
185
+ -0.02588393,
186
+ 0.01805674,
187
+ -0.00719016,
188
+ -0.03264225,
189
+ -0.00939176,
190
+ 0.02352562,
191
+ -0.01662001,
192
+ 8.947e-05,
193
+ 0.01536887,
194
+ -0.05908342,
195
+ -0.02059522,
196
+ 0.03890344,
197
+ -0.01219262,
198
+ -0.02270816,
199
+ -0.01308892,
200
+ 0.032672,
201
+ 0.01836841,
202
+ -0.02627165,
203
+ 0.03396625,
204
+ 0.00332693,
205
+ -0.02368261,
206
+ 0.01004383,
207
+ -0.054905,
208
+ -0.01186761,
209
+ 0.03973839,
210
+ -0.01927921,
211
+ -0.02496052,
212
+ -0.03048274,
213
+ 0.01191071,
214
+ 0.01637166,
215
+ -0.03282031,
216
+ -0.06146573,
217
+ 9.029e-05,
218
+ 0.04366195,
219
+ 0.01741599,
220
+ 0.00701609,
221
+ -0.01906414,
222
+ 0.00744014,
223
+ -0.01633424,
224
+ -0.01800021,
225
+ 0.0323564,
226
+ -0.00062279,
227
+ -0.07529375,
228
+ -0.0075791,
229
+ 0.0210552,
230
+ 0.01169259,
231
+ -0.02394276,
232
+ 0.00213605,
233
+ -0.07524197,
234
+ 0.0136747,
235
+ 0.00861713,
236
+ -0.03423766,
237
+ -0.01383792,
238
+ -0.02035608,
239
+ 0.04885746,
240
+ 0.01876548,
241
+ 0.03787666,
242
+ 0.00321658,
243
+ 0.02173865,
244
+ 0.00801081,
245
+ -0.00432673,
246
+ -0.04274024,
247
+ -0.00183638,
248
+ -0.0103395,
249
+ 0.01865445,
250
+ 0.00329304,
251
+ 0.00230449,
252
+ 0.01730042,
253
+ 0.01731126,
254
+ -0.00055386,
255
+ -0.04531341,
256
+ 0.01712174,
257
+ 0.00037972,
258
+ 0.03139253,
259
+ -0.00045943,
260
+ -0.04149744,
261
+ 0.000518,
262
+ 0.01067489,
263
+ 0.05003539,
264
+ -0.00879554,
265
+ -0.03149081,
266
+ -0.0171353,
267
+ -0.00978476,
268
+ 0.00836213,
269
+ 0.01475394,
270
+ -0.00144215,
271
+ -0.00334998,
272
+ -0.01306126,
273
+ 0.01092799,
274
+ 0.00819701,
275
+ 0.00807866,
276
+ -0.02311526,
277
+ 0.00778096,
278
+ 0.01513516,
279
+ -0.034882,
280
+ -0.00481504,
281
+ -0.01090861,
282
+ 0.01800035,
283
+ 0.00075741,
284
+ -0.03489013,
285
+ -0.00890846,
286
+ -0.05034224,
287
+ -0.02017788,
288
+ 0.02364288,
289
+ 0.00457983,
290
+ -0.0342081,
291
+ 0.01396834,
292
+ 0.00982909,
293
+ 0.02411675,
294
+ 0.00694275,
295
+ -0.00293969,
296
+ -0.04230263,
297
+ 0.01600197,
298
+ 0.02097433,
299
+ 0.00090792,
300
+ -0.01560442,
301
+ 0.00514772,
302
+ 0.0137536,
303
+ -0.0069557,
304
+ -0.00067648,
305
+ -0.00811737,
306
+ 0.02232993,
307
+ -0.01514024,
308
+ 0.08919945,
309
+ 0.00301825,
310
+ 0.01551488,
311
+ 0.0462007,
312
+ -0.01763635,
313
+ 0.00928134,
314
+ 0.00533358,
315
+ 0.0195843,
316
+ 0.03148966,
317
+ 0.01661635,
318
+ -0.00448385,
319
+ 0.02584421,
320
+ 0.00887918,
321
+ 0.02956191,
322
+ -0.05374401,
323
+ 0.04092161,
324
+ -0.06316315,
325
+ -0.06251528,
326
+ -0.06065762,
327
+ 0.01565573,
328
+ 0.01009507,
329
+ 0.00039843,
330
+ 0.06566611,
331
+ 0.02716734,
332
+ -0.0003762,
333
+ -0.00366652,
334
+ 0.06472894,
335
+ -0.01461635,
336
+ 0.00866892,
337
+ 0.00744909,
338
+ -0.01116645,
339
+ -0.01881022,
340
+ 0.00428999,
341
+ 0.03664328,
342
+ 0.00441959,
343
+ 0.00618765,
344
+ 0.02925167,
345
+ 0.00133465,
346
+ -0.01213202,
347
+ -0.00427983,
348
+ -0.00818278,
349
+ 0.02032951,
350
+ -0.01536711,
351
+ 0.00697054,
352
+ -0.03022951,
353
+ 0.02546028,
354
+ 0.01947673,
355
+ -0.00402028,
356
+ -0.01183439,
357
+ 0.03930105,
358
+ -0.01655081,
359
+ -0.00801508,
360
+ -0.03856235,
361
+ -0.00350546,
362
+ -0.06303616,
363
+ -0.01669959,
364
+ 0.00690717,
365
+ -0.0204038,
366
+ 0.01802597,
367
+ -0.01219154,
368
+ 0.01592795,
369
+ -0.00070603,
370
+ 0.03511924,
371
+ -0.018538,
372
+ -0.00704585,
373
+ 0.00904308,
374
+ 0.00613573,
375
+ -0.0150446,
376
+ 0.00495122,
377
+ -0.02130735,
378
+ -0.00971549,
379
+ -0.01199382,
380
+ -0.01386097,
381
+ 0.01570345,
382
+ 0.00240006,
383
+ 0.00064177,
384
+ -0.0038635,
385
+ 0.01867323,
386
+ 0.00244209,
387
+ -0.00370801,
388
+ -0.02493361,
389
+ -0.00779547,
390
+ -0.03015996,
391
+ 0.00707168,
392
+ -0.00050214,
393
+ 0.02271128,
394
+ 0.00928181,
395
+ 0.00883255,
396
+ -0.01380667,
397
+ -0.02119225,
398
+ -0.02348454,
399
+ 0.00430965,
400
+ -0.00848631,
401
+ -0.00847641,
402
+ -0.00610021,
403
+ 0.02354609,
404
+ 0.00890114,
405
+ -0.00753464,
406
+ -0.00556093,
407
+ 0.00221752,
408
+ -0.02240666,
409
+ 0.03152145,
410
+ 0.02903503,
411
+ 0.02509717,
412
+ -0.02199765,
413
+ -0.0370455,
414
+ -0.00424749,
415
+ -0.0073382,
416
+ 0.04361898,
417
+ -0.00020525,
418
+ 0.02663727,
419
+ -0.01912568,
420
+ 0.02326005,
421
+ -0.02344442,
422
+ 0.02198884,
423
+ -0.01359024,
424
+ -0.02811271,
425
+ 0.00990257,
426
+ -0.02367434,
427
+ 0.00514392,
428
+ -0.03639058,
429
+ -0.00174365,
430
+ 0.01068994,
431
+ -0.00056436,
432
+ -0.06993997,
433
+ 0.01277461,
434
+ -0.00027771,
435
+ 0.00881546,
436
+ -0.00927029,
437
+ 0.00370692,
438
+ -0.02268402,
439
+ 0.00086552,
440
+ -0.00182187,
441
+ 0.02215254,
442
+ 0.00216581,
443
+ 0.04274159,
444
+ -0.0213224,
445
+ -0.00871555,
446
+ 0.01324753,
447
+ 0.03569933,
448
+ -0.00604653,
449
+ -0.00054132,
450
+ 0.02708152,
451
+ 0.0156354,
452
+ 0.01532854,
453
+ 0.01847429,
454
+ 0.03432374,
455
+ 0.01492259,
456
+ 0.01553609,
457
+ -0.02336396,
458
+ -0.01170587,
459
+ 0.01463112,
460
+ -0.00971806,
461
+ 0.0065694,
462
+ 0.01784878,
463
+ -0.00612434,
464
+ -0.00295724,
465
+ -0.01064425,
466
+ 0.01663777,
467
+ 0.0185685,
468
+ 0.02852665,
469
+ -0.00981581,
470
+ 0.01066554,
471
+ -0.01361641,
472
+ -0.02228133,
473
+ 0.03113523,
474
+ 0.02426337,
475
+ 0.00486269,
476
+ 0.01181809,
477
+ 0.01762869,
478
+ -0.02489389,
479
+ 0.03073462,
480
+ -0.01638569,
481
+ -0.00472346,
482
+ 0.00900668,
483
+ 0.01496488,
484
+ -0.00837067,
485
+ 0.03504048,
486
+ 0.00483944,
487
+ -0.01933852,
488
+ -0.03348221,
489
+ -0.00154681,
490
+ -0.02760711,
491
+ 0.01916866,
492
+ -0.00771995,
493
+ -0.00483741,
494
+ 0.01592334,
495
+ 0.03234,
496
+ -0.0044583,
497
+ -0.00997334,
498
+ -0.0054156,
499
+ -0.01612384,
500
+ -0.0277504,
501
+ 0.00014797,
502
+ 0.00118905,
503
+ 0.00114228,
504
+ 0.02686001,
505
+ 0.01271726,
506
+ 0.04131245,
507
+ -0.03268129,
508
+ -0.00507546,
509
+ -0.01850859,
510
+ 0.01750851,
511
+ 0.02386562,
512
+ 0.01911094,
513
+ 0.02560507,
514
+ -0.00085027,
515
+ -0.0037884,
516
+ -0.01314613,
517
+ -0.00036874,
518
+ 0.01763174,
519
+ -0.01506276,
520
+ -0.02007655,
521
+ -0.00070394,
522
+ -0.01933974,
523
+ 0.02117775,
524
+ -0.0197523,
525
+ 0.01275597,
526
+ -0.00043964,
527
+ -0.00920847,
528
+ 0.04400012,
529
+ 0.00065479,
530
+ 0.00978442,
531
+ 0.01224339,
532
+ 0.00843249,
533
+ 0.01852363,
534
+ -0.02272808,
535
+ -0.00435086,
536
+ 0.00467805,
537
+ -0.01969587,
538
+ 0.0104729,
539
+ 0.00644387,
540
+ 0.01556693,
541
+ -0.00039924,
542
+ -0.03624526,
543
+ -0.00583931,
544
+ 0.01209956,
545
+ 0.03393426,
546
+ 0.02946118,
547
+ 0.0052841,
548
+ 0.01848364,
549
+ -0.01311671,
550
+ 0.00703236,
551
+ -0.00775086,
552
+ 0.01492621,
553
+ 0.01959657,
554
+ -0.02304517,
555
+ 0.02501997,
556
+ 0.02633144,
557
+ 0.01188129,
558
+ -0.01033177,
559
+ -0.05345566,
560
+ 0.01437744,
561
+ -0.02516746,
562
+ 0.02001703,
563
+ -0.0043796,
564
+ 0.02080955,
565
+ -0.0092044,
566
+ 0.00400259,
567
+ 0.03293351,
568
+ -0.00926822,
569
+ 0.02755668,
570
+ 0.00430748,
571
+ 0.03074696,
572
+ -0.01154997,
573
+ -0.00662604,
574
+ 0.01208532,
575
+ -0.0054236,
576
+ 0.00058863,
577
+ 0.01515122,
578
+ 0.00286804,
579
+ -0.03359331,
580
+ 0.006274,
581
+ -0.01387866,
582
+ 0.00994134,
583
+ -0.00111388,
584
+ -0.03085189,
585
+ -0.02703462,
586
+ 0.03251583,
587
+ 0.01163809,
588
+ 0.00467459,
589
+ 0.02080074,
590
+ 0.00122755,
591
+ -0.01627947,
592
+ -0.00485002,
593
+ -0.00307627,
594
+ -0.00993429,
595
+ 0.01340899,
596
+ 0.01787454,
597
+ -0.02771156,
598
+ -0.00708306,
599
+ 0.00729373,
600
+ 0.01489344,
601
+ -0.0032803,
602
+ -0.00571629,
603
+ -0.02973327,
604
+ 0.0013901,
605
+ -0.00129384,
606
+ 0.00512576,
607
+ 0.03255487,
608
+ 0.00524011,
609
+ -0.01886851,
610
+ 0.03806211,
611
+ 0.00619361,
612
+ -0.01725866,
613
+ 0.00897313,
614
+ -0.00135034,
615
+ 0.01599166,
616
+ -0.02018507,
617
+ -0.01595133,
618
+ 0.01487378,
619
+ 0.00294721,
620
+ 0.00102176,
621
+ -0.00473533,
622
+ 0.00511234,
623
+ -0.00398348,
624
+ 0.01135204,
625
+ 0.00754507,
626
+ 0.02357361,
627
+ 0.03142235,
628
+ -0.00648861,
629
+ -0.01727412,
630
+ -0.01826213,
631
+ -0.00099926,
632
+ 0.02374178,
633
+ -0.00095574,
634
+ 0.02314333,
635
+ -0.00078425,
636
+ -0.0192946,
637
+ 0.01915835,
638
+ -0.00171457,
639
+ -0.00351598,
640
+ 0.00217374,
641
+ -0.00616731,
642
+ 0.00662051,
643
+ -0.00403052,
644
+ 0.02256066,
645
+ -0.01207136,
646
+ -0.00523706,
647
+ 0.01712961,
648
+ 0.0475691,
649
+ -0.00626641,
650
+ 0.0019913,
651
+ -0.01728008,
652
+ 0.0176838,
653
+ -0.00322787,
654
+ 0.01693019,
655
+ 0.03213869,
656
+ -0.01507388,
657
+ -0.02262384,
658
+ 0.0189039,
659
+ -0.00333628,
660
+ -0.00649579,
661
+ -0.00333384,
662
+ 0.00296422,
663
+ 0.007682,
664
+ 0.00162625,
665
+ 0.02766717,
666
+ 0.00457197,
667
+ 0.04865065,
668
+ -0.00820853,
669
+ 0.02016005,
670
+ 0.03420485,
671
+ -0.0131285,
672
+ 0.02328214,
673
+ -0.00852074,
674
+ -0.01124847,
675
+ -0.00485998,
676
+ 0.04002898,
677
+ -0.0211242,
678
+ -0.00886603,
679
+ -0.02890895,
680
+ -0.01964639,
681
+ 0.00854094,
682
+ 0.01961291,
683
+ -0.03156931,
684
+ -0.03173686,
685
+ -0.00388045,
686
+ -0.02344103,
687
+ -0.037501,
688
+ -0.03553733,
689
+ -0.01519209,
690
+ -0.01791494,
691
+ -0.03089717,
692
+ -0.01997609,
693
+ 0.00598362,
694
+ 0.02980532,
695
+ 0.00301147,
696
+ -0.02656583,
697
+ -0.01941959,
698
+ 0.01617874,
699
+ 0.01348796,
700
+ -0.00811255,
701
+ 0.00204054,
702
+ -0.00675811,
703
+ 0.01261742,
704
+ 0.00900566,
705
+ -0.01580282,
706
+ 0.00220431,
707
+ -0.00297819,
708
+ -0.00867088,
709
+ 0.02229637,
710
+ 0.00264761,
711
+ 0.00745153,
712
+ 0.0156985,
713
+ -0.00621707,
714
+ 0.001004,
715
+ 0.00017393,
716
+ -0.02039811,
717
+ 0.00801156,
718
+ -0.02776234,
719
+ -0.02535997,
720
+ -0.02567489,
721
+ 0.00281981,
722
+ -0.00654595,
723
+ -0.01022325,
724
+ -0.00170163,
725
+ -0.01141319,
726
+ 0.01317656,
727
+ 0.00056951,
728
+ -0.01078958,
729
+ 0.02116568,
730
+ 0.0095097,
731
+ -0.0149497,
732
+ 0.02496791,
733
+ 0.01960952,
734
+ 0.01990261,
735
+ 0.02258615,
736
+ 0.01920174,
737
+ 0.03596667,
738
+ 0.00099478,
739
+ -0.0229254,
740
+ 0.00990298,
741
+ 0.02504748,
742
+ 0.01103509,
743
+ -0.01002065,
744
+ -0.01105082,
745
+ 0.0085911,
746
+ -0.02008658,
747
+ -0.01259166,
748
+ -0.00247381,
749
+ 0.00217059,
750
+ -0.02131948,
751
+ -0.00264463,
752
+ 0.01183303,
753
+ -0.0200939,
754
+ -0.00802105,
755
+ 0.01097476,
756
+ 0.00860818,
757
+ -0.02176685,
758
+ 0.01244621,
759
+ 0.01678269,
760
+ -0.00806077,
761
+ 0.0194993,
762
+ 0.00974802,
763
+ -0.02071967,
764
+ -0.02892916,
765
+ -0.00469439,
766
+ -0.01954783,
767
+ -0.03511016,
768
+ -0.00481206,
769
+ 0.00092754,
770
+ -0.00647932,
771
+ 0.02529612,
772
+ 0.00747797,
773
+ -0.00231357,
774
+ -0.00390756,
775
+ -0.0171801,
776
+ -0.00410183,
777
+ 0.02131562,
778
+ 0.00454892,
779
+ -0.0292922,
780
+ -0.00711533,
781
+ 0.02149809,
782
+ -0.01275244,
783
+ -0.01269646,
784
+ -0.01939993,
785
+ -0.02410693,
786
+ -0.00975765,
787
+ 0.01132927,
788
+ 0.02547917,
789
+ -0.01860545,
790
+ -0.02865321,
791
+ -0.03200746,
792
+ 0.01562794,
793
+ 0.02289022,
794
+ -0.01663926,
795
+ -0.00958291,
796
+ -0.01763906,
797
+ -0.01870583,
798
+ 0.02814463,
799
+ 0.01109149,
800
+ 0.02033663,
801
+ 0.01271503,
802
+ 0.03006222,
803
+ -0.00048329,
804
+ -0.00158084,
805
+ 0.00507858,
806
+ -0.02844111,
807
+ -0.00451897,
808
+ 0.00376603,
809
+ 0.00168753,
810
+ 0.00129588,
811
+ -0.01696272,
812
+ 0.0429162,
813
+ 0.00415294,
814
+ -0.02960312,
815
+ -0.01082198,
816
+ 0.02226594,
817
+ 0.00565284,
818
+ -0.00193778,
819
+ 0.00644563,
820
+ 0.00607838,
821
+ 0.00383652,
822
+ 0.0387188,
823
+ -0.02177966,
824
+ 0.01392916,
825
+ -0.00389068,
826
+ 0.00462986,
827
+ -0.00591028,
828
+ 0.00774544,
829
+ 0.02415533,
830
+ -0.01322896,
831
+ 0.02906025,
832
+ 0.02997837,
833
+ -0.00962439,
834
+ 0.04012598,
835
+ 0.0074251,
836
+ 0.0035456,
837
+ -0.01087397,
838
+ 0.01074715,
839
+ -0.00203071,
840
+ -0.0077369,
841
+ 0.00579586,
842
+ 0.0075242,
843
+ 0.01921529,
844
+ 0.03780481,
845
+ 0.02322195,
846
+ -0.00854881,
847
+ 0.01923305,
848
+ -0.00798716,
849
+ 0.00166868,
850
+ -0.02916843,
851
+ 0.00192206,
852
+ 0.0144256,
853
+ 0.00113591,
854
+ -0.01708934,
855
+ -0.02459632,
856
+ 0.02938533,
857
+ -0.03380818,
858
+ 0.02188608,
859
+ -0.02040062,
860
+ -0.02458778,
861
+ 0.00072677,
862
+ -0.00347958,
863
+ -0.00128368,
864
+ -0.00388601,
865
+ -0.02840634,
866
+ 0.0021292,
867
+ 0.0023034,
868
+ -0.01680031,
869
+ 0.01571158,
870
+ 0.00830004,
871
+ -0.03735995,
872
+ 0.0254634,
873
+ -0.00473872,
874
+ -0.00072189,
875
+ 0.02520677,
876
+ -0.00860385,
877
+ 0.01570575,
878
+ 0.00436401,
879
+ -0.02640044,
880
+ 0.00596871,
881
+ 0.01531329,
882
+ -0.00725632,
883
+ 0.00638598,
884
+ -0.02094878,
885
+ -0.01477943,
886
+ -0.01413115,
887
+ 0.04499755,
888
+ 0.00113113,
889
+ -0.03174527,
890
+ -0.00209084,
891
+ -0.03338251,
892
+ -0.0096309,
893
+ 0.00287617,
894
+ -0.01256197,
895
+ 0.01806203,
896
+ -0.02083992,
897
+ 0.00520981,
898
+ -0.01840962,
899
+ -0.01567308,
900
+ 0.02502837,
901
+ -0.00166109,
902
+ -0.02704363,
903
+ -0.00332124,
904
+ 0.00635724,
905
+ -0.00385252,
906
+ -0.00838694,
907
+ 0.01483678,
908
+ 0.00363128,
909
+ -0.0092808,
910
+ 0.00838382,
911
+ -0.00979391,
912
+ 0.03600009,
913
+ -0.0133385,
914
+ -0.03795407,
915
+ 0.01375116,
916
+ 0.01596306,
917
+ 0.00234801,
918
+ 0.01738501,
919
+ 0.02754136,
920
+ -0.00016695,
921
+ -0.02247729,
922
+ -0.00583192,
923
+ 0.00303804,
924
+ -0.01862978,
925
+ 0.00295507,
926
+ -0.00252994,
927
+ -0.01407245,
928
+ 0.00016214,
929
+ -0.01388401,
930
+ 0.01226901,
931
+ -0.01216287,
932
+ -0.01331897,
933
+ -0.02745839,
934
+ 0.00533697,
935
+ -0.00886373,
936
+ 0.01137462,
937
+ -0.0085808,
938
+ 0.0069616,
939
+ -0.02154622,
940
+ 0.00649606,
941
+ -0.00231059,
942
+ -0.01416667,
943
+ 0.02500885,
944
+ -0.01119208,
945
+ -0.02103906,
946
+ -0.01935066,
947
+ -0.00227805,
948
+ 0.02452481,
949
+ 0.00060232,
950
+ 0.0104103,
951
+ -0.00241755,
952
+ -0.01093803,
953
+ 0.00326898,
954
+ 0.02128485,
955
+ 0.02182901,
956
+ -0.00381456,
957
+ -0.00549965,
958
+ -0.0475321,
959
+ 0.00133885,
960
+ 0.0024913,
961
+ -0.00498843,
962
+ 0.0218831,
963
+ 0.00093283,
964
+ 0.00075971,
965
+ 0.02190154,
966
+ -0.03986854,
967
+ -0.02121811,
968
+ -0.01359594,
969
+ 0.00847025,
970
+ -0.00618094,
971
+ -0.01546106,
972
+ 0.00517741,
973
+ 0.01312633,
974
+ -0.01497871,
975
+ -0.00776035,
976
+ 0.01118489,
977
+ -0.02040217,
978
+ 0.00727101,
979
+ -0.03115027,
980
+ 0.0184103,
981
+ -0.00714149,
982
+ 0.01175752,
983
+ 0.0036893,
984
+ 0.00872613,
985
+ 0.01293681,
986
+ -0.02438077,
987
+ -0.00291088,
988
+ 0.00280636,
989
+ -0.00522472,
990
+ 0.00602782,
991
+ 0.0032788,
992
+ 0.01566291,
993
+ 0.00508319,
994
+ -0.00120315,
995
+ 0.02941313,
996
+ 0.00818047,
997
+ 0.03064339,
998
+ 0.00138223,
999
+ -0.00743662,
1000
+ -0.00375532,
1001
+ -0.00553083,
1002
+ -0.02147844,
1003
+ -0.02098986,
1004
+ 0.01503158,
1005
+ -0.01685054,
1006
+ -0.01447685,
1007
+ -0.01132737,
1008
+ -0.00244616,
1009
+ -0.01728076,
1010
+ -0.03332868,
1011
+ -0.00314168,
1012
+ -0.02164423,
1013
+ 0.0036973,
1014
+ 0.02682395,
1015
+ 0.00388641,
1016
+ 0.00867326,
1017
+ -0.03751247,
1018
+ -0.00233092,
1019
+ 0.01589548,
1020
+ 0.02289741,
1021
+ -0.01888031,
1022
+ -0.02657688,
1023
+ -0.02343804,
1024
+ -0.00483036,
1025
+ -0.02808756,
1026
+ -0.00989105,
1027
+ -0.00036061,
1028
+ -0.01134445,
1029
+ 0.01664197,
1030
+ 0.02578348,
1031
+ -0.00212988
1032
+ ],
1033
+ "cols": [
1034
+ 0,
1035
+ 2,
1036
+ 11,
1037
+ 25,
1038
+ 34,
1039
+ 35,
1040
+ 38,
1041
+ 39,
1042
+ 46,
1043
+ 50,
1044
+ 53,
1045
+ 56,
1046
+ 61,
1047
+ 62,
1048
+ 63,
1049
+ 64,
1050
+ 67,
1051
+ 73,
1052
+ 100,
1053
+ 108,
1054
+ 129,
1055
+ 135,
1056
+ 156,
1057
+ 169,
1058
+ 209,
1059
+ 215,
1060
+ 264,
1061
+ 266,
1062
+ 294,
1063
+ 327,
1064
+ 328,
1065
+ 357,
1066
+ 371,
1067
+ 376,
1068
+ 412,
1069
+ 414,
1070
+ 442,
1071
+ 504,
1072
+ 505,
1073
+ 509,
1074
+ 516,
1075
+ 580,
1076
+ 590,
1077
+ 601,
1078
+ 638,
1079
+ 682,
1080
+ 738,
1081
+ 882,
1082
+ 926,
1083
+ 930,
1084
+ 1047,
1085
+ 1060,
1086
+ 1087,
1087
+ 1130,
1088
+ 1155,
1089
+ 1184,
1090
+ 1460,
1091
+ 1481,
1092
+ 1787,
1093
+ 1943,
1094
+ 2005,
1095
+ 2066,
1096
+ 2738,
1097
+ 3003
1098
+ ]
1099
+ },
1100
+ "19": {
1101
+ "alpha": 6,
1102
+ "dir": [
1103
+ -0.33485293,
1104
+ 0.03469285,
1105
+ 0.06433747,
1106
+ 0.07593478,
1107
+ 0.04320995,
1108
+ -0.20440264,
1109
+ 0.01049561,
1110
+ -0.07845271,
1111
+ -0.02966316,
1112
+ 0.07869723,
1113
+ 0.02690876,
1114
+ -0.00667397,
1115
+ 0.02327623,
1116
+ 0.10146445,
1117
+ 0.03316295,
1118
+ 0.14016815,
1119
+ 0.09809733,
1120
+ 0.11769328,
1121
+ -0.20819545,
1122
+ -0.1066898,
1123
+ 0.09799061,
1124
+ 0.02515803,
1125
+ 0.11596736,
1126
+ 0.12506141,
1127
+ -0.02365931,
1128
+ -0.01581082,
1129
+ 0.05179979,
1130
+ 0.07284577,
1131
+ -0.03920847,
1132
+ -0.04298451,
1133
+ 0.00825938,
1134
+ -0.10469546,
1135
+ 0.06105305,
1136
+ -0.01464969,
1137
+ -0.1059337,
1138
+ 0.00563145,
1139
+ 0.08228201,
1140
+ 0.21913013,
1141
+ -0.13936099,
1142
+ -0.01100376,
1143
+ -0.05633171,
1144
+ 0.02826658,
1145
+ -0.06082795,
1146
+ 0.01529873,
1147
+ -0.03269898,
1148
+ -0.06137543,
1149
+ 0.07338464,
1150
+ -0.01731984,
1151
+ 0.01293509,
1152
+ 0.02285551,
1153
+ -0.0430566,
1154
+ 0.06165075,
1155
+ -0.03291543,
1156
+ 0.01243704,
1157
+ -0.02252448,
1158
+ 0.10640015,
1159
+ 0.02985976,
1160
+ 0.02642925,
1161
+ 0.01327473,
1162
+ 0.03750305,
1163
+ 0.02248123,
1164
+ -0.08859696,
1165
+ -0.04791253,
1166
+ 0.02915941,
1167
+ 0.04655077,
1168
+ -0.02370902,
1169
+ 0.06000244,
1170
+ -0.02559192,
1171
+ 0.0041156,
1172
+ -0.04574716,
1173
+ -0.0151819,
1174
+ 0.03427166,
1175
+ -0.0521356,
1176
+ 0.00275684,
1177
+ -0.11008257,
1178
+ -0.01052809,
1179
+ -0.05736936,
1180
+ -0.01020324,
1181
+ -0.09052463,
1182
+ 0.00689397,
1183
+ 0.03621768,
1184
+ 0.07123613,
1185
+ 0.00360201,
1186
+ -0.06619763,
1187
+ 0.02345335,
1188
+ 0.11091703,
1189
+ 0.09016617,
1190
+ -0.01928664,
1191
+ 0.01368285,
1192
+ -0.00499045,
1193
+ -0.03138366,
1194
+ -0.02867982,
1195
+ -0.04870289,
1196
+ -0.00659964,
1197
+ 0.03185755,
1198
+ 0.01343173,
1199
+ -0.04430854,
1200
+ 0.03535172,
1201
+ -0.02320377,
1202
+ 0.00691644,
1203
+ -0.02510364,
1204
+ -0.07474331,
1205
+ -0.01688526,
1206
+ 0.03140407,
1207
+ -0.02430508,
1208
+ -0.01303292,
1209
+ -0.00812541,
1210
+ 0.0027574,
1211
+ -0.02376566,
1212
+ -0.03320733,
1213
+ 0.02914509,
1214
+ 0.06341685,
1215
+ 0.01786544,
1216
+ 0.0007114,
1217
+ -0.01188948,
1218
+ 0.07291664,
1219
+ -0.00413994,
1220
+ -0.0639967,
1221
+ -0.06567573,
1222
+ 0.04150274,
1223
+ 0.01945941,
1224
+ 0.03311839,
1225
+ -0.02324571,
1226
+ 0.00037765,
1227
+ -0.0109944,
1228
+ 0.04036979,
1229
+ 0.00052145,
1230
+ -0.01924994,
1231
+ 0.00095022,
1232
+ 0.0644691,
1233
+ 0.00839896,
1234
+ -0.04536164,
1235
+ 0.01759123,
1236
+ 0.01193937,
1237
+ 0.00483495,
1238
+ -0.00580474,
1239
+ -0.01910567,
1240
+ -0.06255472,
1241
+ 0.05657258,
1242
+ -0.02691831,
1243
+ 0.01994252,
1244
+ 0.02171535,
1245
+ 0.01559719,
1246
+ 0.01146398,
1247
+ -0.03114962,
1248
+ 0.02478721,
1249
+ 0.04094142,
1250
+ 0.0108493,
1251
+ -0.02564245,
1252
+ -0.00549383,
1253
+ 0.01298171,
1254
+ 0.00228927,
1255
+ 0.0052698,
1256
+ 0.02828737,
1257
+ -0.00475973,
1258
+ 0.02829008,
1259
+ -0.05092382,
1260
+ 0.02987025,
1261
+ 0.00792591,
1262
+ 0.00794679,
1263
+ 0.03851486,
1264
+ -0.01412571,
1265
+ 0.03591633,
1266
+ 0.02479657,
1267
+ -0.01975248,
1268
+ -0.00360033,
1269
+ 0.04643525,
1270
+ -0.01874319,
1271
+ -0.03569885,
1272
+ 0.02199704,
1273
+ -0.01068916,
1274
+ -0.03476141,
1275
+ -0.00933738,
1276
+ -0.01238625,
1277
+ -0.0188238,
1278
+ -0.00079303,
1279
+ -0.00026924,
1280
+ -0.02419199,
1281
+ 0.03646034,
1282
+ 0.03449475,
1283
+ -0.0292365,
1284
+ -0.01684576,
1285
+ 0.00764235,
1286
+ -0.01628162,
1287
+ -0.0080728,
1288
+ -0.01059124,
1289
+ -0.04215249,
1290
+ -0.04302702,
1291
+ 0.02810987,
1292
+ -0.0130589,
1293
+ -0.03027271,
1294
+ -0.00660881,
1295
+ 0.0222896,
1296
+ 0.04006188,
1297
+ -0.03167387,
1298
+ 0.03514763,
1299
+ -0.0102343,
1300
+ -0.02564783,
1301
+ -0.00238742,
1302
+ -0.04280112,
1303
+ -0.02142761,
1304
+ 0.0457569,
1305
+ -0.00995881,
1306
+ -0.01363918,
1307
+ -0.0523981,
1308
+ 0.03240933,
1309
+ -0.00046247,
1310
+ -0.02916587,
1311
+ -0.05593346,
1312
+ 0.01162838,
1313
+ 0.0239982,
1314
+ 0.00696456,
1315
+ 0.01568088,
1316
+ -0.02222509,
1317
+ -0.00589124,
1318
+ -0.02352338,
1319
+ -0.00299364,
1320
+ 0.03131129,
1321
+ 0.01445309,
1322
+ -0.08602708,
1323
+ -0.02007218,
1324
+ 0.04017609,
1325
+ -0.01014829,
1326
+ -0.02538945,
1327
+ 0.02321257,
1328
+ -0.06834598,
1329
+ 0.00964575,
1330
+ -0.0051437,
1331
+ -0.01859846,
1332
+ -0.00214777,
1333
+ -0.04108446,
1334
+ 0.03662089,
1335
+ 0.01966953,
1336
+ 0.04098223,
1337
+ 0.01839868,
1338
+ 0.01493723,
1339
+ 0.00900578,
1340
+ 0.01188021,
1341
+ -0.04769861,
1342
+ -0.00375667,
1343
+ -0.00330618,
1344
+ 0.01062461,
1345
+ 0.0013934,
1346
+ -0.00176543,
1347
+ 0.02101856,
1348
+ 0.02736561,
1349
+ 0.01389204,
1350
+ -0.00640379,
1351
+ -0.00614297,
1352
+ 0.01084499,
1353
+ 0.02786553,
1354
+ -0.02415286,
1355
+ -0.02912875,
1356
+ -0.01692037,
1357
+ 0.00126084,
1358
+ 0.05189257,
1359
+ -0.01389195,
1360
+ -0.03966757,
1361
+ -0.00938222,
1362
+ -0.029164,
1363
+ -0.00431837,
1364
+ -0.00161096,
1365
+ 0.02716199,
1366
+ 0.01256876,
1367
+ 0.00973001,
1368
+ 0.03604037,
1369
+ 0.02316862,
1370
+ 0.02573312,
1371
+ -0.02866352,
1372
+ 0.00940141,
1373
+ 0.01636082,
1374
+ -0.04596978,
1375
+ 0.01874516,
1376
+ -0.02349585,
1377
+ 0.00517315,
1378
+ 0.00220712,
1379
+ -0.01262933,
1380
+ 0.02448426,
1381
+ -0.02697523,
1382
+ -0.03611716,
1383
+ 0.01318228,
1384
+ -0.0020534,
1385
+ -0.02395383,
1386
+ 0.02149507,
1387
+ -7.5e-07,
1388
+ 0.01313674,
1389
+ 0.00810257,
1390
+ -0.0237144,
1391
+ -0.03781516,
1392
+ -0.01663774,
1393
+ 0.00806578,
1394
+ 0.01225692,
1395
+ -0.01204816,
1396
+ 0.02055262,
1397
+ 0.01788219,
1398
+ -0.03242413,
1399
+ 0.00801747,
1400
+ -0.00297862,
1401
+ -0.01131719,
1402
+ -0.01430189,
1403
+ 0.03083245,
1404
+ -0.01074697,
1405
+ -0.02104215,
1406
+ 0.03708439,
1407
+ -0.03587766,
1408
+ -0.00112949,
1409
+ -0.00407534,
1410
+ 0.01345476,
1411
+ 0.03780131,
1412
+ 0.02845756,
1413
+ -0.01566019,
1414
+ -0.01311745,
1415
+ 0.01434955,
1416
+ 0.01990891,
1417
+ -0.03681851,
1418
+ 0.02035851,
1419
+ -0.06993868,
1420
+ -0.05787938,
1421
+ -0.03253908,
1422
+ 0.00905447,
1423
+ 0.038856,
1424
+ -0.00199011,
1425
+ 0.02992324,
1426
+ 0.00809208,
1427
+ -0.00987577,
1428
+ -0.00634818,
1429
+ 0.06414509,
1430
+ -0.00876435,
1431
+ -0.00199508,
1432
+ -0.01413713,
1433
+ 0.02092653,
1434
+ -0.01234324,
1435
+ 0.03740568,
1436
+ 0.05363179,
1437
+ 0.01417355,
1438
+ 0.0100616,
1439
+ 0.01451284,
1440
+ 0.00778567,
1441
+ -0.0122038,
1442
+ 0.00317306,
1443
+ -0.00395102,
1444
+ -0.01568509,
1445
+ -0.01977195,
1446
+ -0.00337621,
1447
+ -0.00852562,
1448
+ -0.01215244,
1449
+ 0.00799697,
1450
+ 0.01198815,
1451
+ -0.01456201,
1452
+ 0.02279017,
1453
+ -0.0340085,
1454
+ -0.00045465,
1455
+ -0.04464959,
1456
+ -0.00148215,
1457
+ -0.04413975,
1458
+ 0.00469116,
1459
+ -0.0103799,
1460
+ -0.01301511,
1461
+ 0.01054597,
1462
+ -0.01396076,
1463
+ 0.05034662,
1464
+ -0.01023035,
1465
+ 0.01737638,
1466
+ -0.03203622,
1467
+ 0.00871286,
1468
+ 0.00355895,
1469
+ 0.01077468,
1470
+ -0.0107326,
1471
+ 0.00578704,
1472
+ 0.00076102,
1473
+ 0.00033403,
1474
+ -0.00997107,
1475
+ 0.01247552,
1476
+ -0.00807111,
1477
+ 0.00515466,
1478
+ -0.01044779,
1479
+ -0.00936228,
1480
+ 0.0198695,
1481
+ -0.00272679,
1482
+ -0.01352749,
1483
+ 7.16e-06,
1484
+ 0.02315546,
1485
+ -0.026254,
1486
+ -0.0053682,
1487
+ 0.00264656,
1488
+ 0.01020446,
1489
+ 0.00516271,
1490
+ 0.01508454,
1491
+ -0.0209725,
1492
+ 0.00244697,
1493
+ -0.01309793,
1494
+ -0.014711,
1495
+ -0.03019547,
1496
+ -0.00102567,
1497
+ 0.00826303,
1498
+ 0.03253384,
1499
+ 0.01062864,
1500
+ -0.00843968,
1501
+ -0.01330544,
1502
+ 0.02085557,
1503
+ -0.02482615,
1504
+ 0.01753628,
1505
+ 0.00801335,
1506
+ 0.02842218,
1507
+ 0.00948801,
1508
+ -0.03443109,
1509
+ 0.0126709,
1510
+ 0.00087775,
1511
+ 0.00197925,
1512
+ 0.00805351,
1513
+ 0.02990451,
1514
+ -0.01561085,
1515
+ 0.02241008,
1516
+ -0.02911878,
1517
+ 0.01346374,
1518
+ -0.01211556,
1519
+ -0.0524451,
1520
+ 0.04090678,
1521
+ -0.03539928,
1522
+ -0.01168483,
1523
+ -0.04193525,
1524
+ -0.00351266,
1525
+ 0.01685971,
1526
+ -0.01135502,
1527
+ -0.10916007,
1528
+ 0.02025623,
1529
+ 5.093e-05,
1530
+ 0.01171376,
1531
+ 0.01459979,
1532
+ 0.00613427,
1533
+ -0.00408939,
1534
+ 0.01221592,
1535
+ -0.01002734,
1536
+ 0.0186257,
1537
+ 0.01269805,
1538
+ 0.06049786,
1539
+ -0.01004597,
1540
+ -0.00597877,
1541
+ 0.01513275,
1542
+ 0.02150752,
1543
+ 0.00289783,
1544
+ -0.00502687,
1545
+ -0.02977115,
1546
+ 0.0105178,
1547
+ 0.01677245,
1548
+ 0.02142598,
1549
+ 0.0200704,
1550
+ 0.04995825,
1551
+ 0.01128616,
1552
+ -0.01138572,
1553
+ 0.0028806,
1554
+ 0.02906739,
1555
+ -0.01654103,
1556
+ -0.0074083,
1557
+ -0.00165478,
1558
+ -0.02611711,
1559
+ -0.01441723,
1560
+ -0.02270881,
1561
+ 0.01406153,
1562
+ 0.0329129,
1563
+ 0.03200575,
1564
+ -0.00264824,
1565
+ 0.02199779,
1566
+ -0.01492557,
1567
+ -0.01760359,
1568
+ 0.01623378,
1569
+ 0.01867373,
1570
+ 0.02524926,
1571
+ 0.00967028,
1572
+ 0.01461598,
1573
+ -0.02035144,
1574
+ 0.02304462,
1575
+ -0.01513378,
1576
+ 0.00965755,
1577
+ 0.01401328,
1578
+ -0.00270961,
1579
+ 0.00232686,
1580
+ 0.01563304,
1581
+ 0.02284709,
1582
+ -0.05560019,
1583
+ -0.04236459,
1584
+ 0.00073883,
1585
+ -0.02807729,
1586
+ 0.01340476,
1587
+ -0.00480668,
1588
+ -0.02714749,
1589
+ -0.00581044,
1590
+ 0.00199742,
1591
+ 0.01153813,
1592
+ -0.00933401,
1593
+ -0.03477429,
1594
+ -0.03197195,
1595
+ -0.00130699,
1596
+ -0.00737675,
1597
+ -0.01272538,
1598
+ 0.02102258,
1599
+ 0.00410118,
1600
+ -0.00145837,
1601
+ 0.01512554,
1602
+ -0.02576101,
1603
+ -0.00219411,
1604
+ -0.00436368,
1605
+ 0.00527814,
1606
+ 0.00784812,
1607
+ 0.01105572,
1608
+ 0.00454362,
1609
+ -0.02113932,
1610
+ -0.00996873,
1611
+ 0.01481754,
1612
+ 0.0370915,
1613
+ 0.01813238,
1614
+ -0.01072198,
1615
+ -0.02466616,
1616
+ -0.01082149,
1617
+ -0.0235935,
1618
+ 0.00772955,
1619
+ -0.01920894,
1620
+ 0.011986,
1621
+ 0.0027178,
1622
+ -0.00140089,
1623
+ 0.04917992,
1624
+ 0.00532532,
1625
+ -0.00862383,
1626
+ 0.0033925,
1627
+ 0.01695543,
1628
+ 0.01353779,
1629
+ -0.01470014,
1630
+ -0.03946432,
1631
+ 0.01393099,
1632
+ -0.00813009,
1633
+ -0.00216593,
1634
+ 0.00183808,
1635
+ 0.02404295,
1636
+ -0.01900738,
1637
+ -0.02037269,
1638
+ 0.00566403,
1639
+ 0.00196521,
1640
+ 0.01408339,
1641
+ -0.01416086,
1642
+ -0.02554832,
1643
+ 0.00501152,
1644
+ -0.02169447,
1645
+ -0.00156593,
1646
+ 0.00677395,
1647
+ 0.01410867,
1648
+ 0.01969471,
1649
+ -0.00997529,
1650
+ 0.04218512,
1651
+ 0.03097025,
1652
+ 0.03468947,
1653
+ 0.00412627,
1654
+ -0.02789333,
1655
+ 0.01408405,
1656
+ -0.02234736,
1657
+ -0.00035949,
1658
+ -0.00303544,
1659
+ -0.00914162,
1660
+ -0.02251699,
1661
+ 0.00473057,
1662
+ 0.02165684,
1663
+ -0.00946849,
1664
+ 0.0231431,
1665
+ -0.00792189,
1666
+ 0.03188115,
1667
+ 0.00995001,
1668
+ -0.01436275,
1669
+ 0.0132283,
1670
+ 0.02302178,
1671
+ 0.01678467,
1672
+ 0.01890898,
1673
+ 0.00624642,
1674
+ -0.01549046,
1675
+ 0.01312298,
1676
+ -0.01649535,
1677
+ 0.02968395,
1678
+ -0.00939973,
1679
+ -0.01924723,
1680
+ 0.04292338,
1681
+ 0.0288663,
1682
+ 0.02567896,
1683
+ -0.00700893,
1684
+ 0.02072216,
1685
+ 0.01288865,
1686
+ -0.00674905,
1687
+ 0.0068656,
1688
+ -0.00257756,
1689
+ -0.00780505,
1690
+ -0.02037166,
1691
+ -0.00193348,
1692
+ -0.00860267,
1693
+ -0.03601069,
1694
+ 0.00303545,
1695
+ 0.05838604,
1696
+ 0.02260059,
1697
+ -0.03325956,
1698
+ -0.03232592,
1699
+ -0.00711191,
1700
+ 0.02423159,
1701
+ -0.00944073,
1702
+ 0.0153579,
1703
+ 0.01699695,
1704
+ -0.0008257,
1705
+ 0.02907693,
1706
+ 0.02055094,
1707
+ -0.00871408,
1708
+ -0.01249677,
1709
+ 0.00056563,
1710
+ 0.0096538,
1711
+ -0.01459426,
1712
+ 0.00653958,
1713
+ 0.01096033,
1714
+ 0.02558932,
1715
+ -0.0168935,
1716
+ -0.01167921,
1717
+ 0.01401768,
1718
+ -0.01887079,
1719
+ -0.0095849,
1720
+ -0.03007321,
1721
+ 0.0249985,
1722
+ 0.02601537,
1723
+ 0.00875255,
1724
+ -0.01530061,
1725
+ -0.01763298,
1726
+ 0.00101135,
1727
+ 0.02701717,
1728
+ -0.01504728,
1729
+ 0.01979423,
1730
+ -0.01740025,
1731
+ -0.00037391,
1732
+ 0.01941003,
1733
+ 0.00366848,
1734
+ 0.00170964,
1735
+ 0.01005537,
1736
+ 0.00897676,
1737
+ -0.01451694,
1738
+ 0.01472355,
1739
+ 0.01401075,
1740
+ -0.04008098,
1741
+ 0.00478178,
1742
+ 0.00264469,
1743
+ 0.01465146,
1744
+ -0.00858278,
1745
+ -0.01525469,
1746
+ -0.00837144,
1747
+ 0.00379487,
1748
+ 0.00762924,
1749
+ 0.0141948,
1750
+ 0.02449119,
1751
+ -0.02006853,
1752
+ -0.01467974,
1753
+ 0.01018124,
1754
+ -0.03718886,
1755
+ -0.00821285,
1756
+ -0.00819946,
1757
+ 0.00170308,
1758
+ 0.01008323,
1759
+ 0.01641961,
1760
+ 0.03285327,
1761
+ -0.02158241,
1762
+ 0.05546584,
1763
+ 0.00506394,
1764
+ 0.0028938,
1765
+ 0.00908288,
1766
+ 0.00138076,
1767
+ 0.00620869,
1768
+ -0.00761552,
1769
+ -0.02185999,
1770
+ -0.00480003,
1771
+ 0.01747121,
1772
+ 0.00039572,
1773
+ 0.01573705,
1774
+ 0.00300708,
1775
+ -0.03099894,
1776
+ 0.00092616,
1777
+ 0.01118857,
1778
+ -0.02362317,
1779
+ -0.0301687,
1780
+ 0.01169634,
1781
+ -0.00954914,
1782
+ -0.0323673,
1783
+ -0.04537297,
1784
+ -0.01935816,
1785
+ -0.00577393,
1786
+ -0.0322955,
1787
+ -0.00516561,
1788
+ 9.74e-06,
1789
+ 0.00058782,
1790
+ 0.01400073,
1791
+ -0.04664317,
1792
+ -0.01463405,
1793
+ 0.01198609,
1794
+ 0.0194691,
1795
+ -0.00406392,
1796
+ 0.0008507,
1797
+ -0.01848799,
1798
+ 0.03592278,
1799
+ -0.01971447,
1800
+ -0.00676394,
1801
+ -0.02517347,
1802
+ 0.01982344,
1803
+ 0.00479301,
1804
+ 0.02273475,
1805
+ 0.00241195,
1806
+ -0.00936509,
1807
+ 0.00513546,
1808
+ -0.01280215,
1809
+ 0.00603569,
1810
+ 0.0172984,
1811
+ -0.01518925,
1812
+ 0.0097814,
1813
+ 0.00418319,
1814
+ 0.00138834,
1815
+ -0.02091384,
1816
+ -0.00198403,
1817
+ 0.01229568,
1818
+ 0.00035958,
1819
+ -0.01921193,
1820
+ 0.01499279,
1821
+ -0.00202204,
1822
+ -0.01947481,
1823
+ -0.01102965,
1824
+ 0.00401898,
1825
+ 0.03645612,
1826
+ -0.02703262,
1827
+ 0.00610309,
1828
+ -0.00235634,
1829
+ 0.00785879,
1830
+ 0.00642532,
1831
+ 0.02070143,
1832
+ 0.02173702,
1833
+ 0.0086331,
1834
+ 0.00403808,
1835
+ -0.02067142,
1836
+ -0.02638881,
1837
+ -0.00668427,
1838
+ -0.01563201,
1839
+ 0.00041501,
1840
+ -0.01161527,
1841
+ -0.00428055,
1842
+ -0.03578255,
1843
+ 0.00167846,
1844
+ -2.603e-05,
1845
+ 0.00864414,
1846
+ 0.00446198,
1847
+ -0.00604678,
1848
+ -0.0234791,
1849
+ 0.00210582,
1850
+ 0.02637112,
1851
+ 0.02551471,
1852
+ 0.00376715,
1853
+ 0.01265203,
1854
+ 0.01572001,
1855
+ -0.01759844,
1856
+ -0.01298658,
1857
+ -0.01324992,
1858
+ -0.01552491,
1859
+ -0.00672995,
1860
+ -0.00874197,
1861
+ -0.03830329,
1862
+ -0.0154488,
1863
+ -0.02508856,
1864
+ 0.03405344,
1865
+ -0.00183639,
1866
+ 0.01713831,
1867
+ 0.00159486,
1868
+ -0.03485499,
1869
+ -0.00087476,
1870
+ -0.01704301,
1871
+ -0.00951778,
1872
+ 0.04916522,
1873
+ 0.00483608,
1874
+ -0.00797562,
1875
+ -0.01565256,
1876
+ 0.00146399,
1877
+ -0.00162913,
1878
+ -0.02793031,
1879
+ -0.00984591,
1880
+ -0.00824814,
1881
+ 0.00142111,
1882
+ 0.01817587,
1883
+ 0.01086203,
1884
+ -0.01275286,
1885
+ -0.0189762,
1886
+ -0.03228875,
1887
+ 0.01003015,
1888
+ -0.00134121,
1889
+ 0.00325151,
1890
+ -0.03226647,
1891
+ 0.00423768,
1892
+ -0.0380742,
1893
+ 0.02109345,
1894
+ 0.00124923,
1895
+ 0.03016158,
1896
+ -0.01302608,
1897
+ 0.02091108,
1898
+ 0.01425921,
1899
+ -0.03021008,
1900
+ -0.01007143,
1901
+ -0.02217595,
1902
+ -0.00590678,
1903
+ -0.01367597,
1904
+ -0.02060196,
1905
+ -0.00706154,
1906
+ -0.00447443,
1907
+ 0.0351731,
1908
+ 0.03089835,
1909
+ -0.00138713,
1910
+ -0.01089517,
1911
+ 0.0110692,
1912
+ -0.01402011,
1913
+ -0.01749948,
1914
+ 0.01299706,
1915
+ 0.02726881,
1916
+ -0.00705925,
1917
+ 0.01604795,
1918
+ -0.00103185,
1919
+ -0.00643478,
1920
+ -0.01959145,
1921
+ 0.02240784,
1922
+ -0.0106791,
1923
+ 0.0045717,
1924
+ -0.00056095,
1925
+ -0.00478964,
1926
+ 0.01092616,
1927
+ 0.00612308,
1928
+ -0.01870584,
1929
+ 0.02174109,
1930
+ -0.00156753,
1931
+ 0.01371979,
1932
+ -0.01055524,
1933
+ 0.00970679,
1934
+ 0.00153045,
1935
+ 0.01469247,
1936
+ -0.00180606,
1937
+ 0.00455373,
1938
+ 0.03337794,
1939
+ 0.01545489,
1940
+ 0.02447022,
1941
+ -0.00159542,
1942
+ 0.01690913,
1943
+ -0.01614372,
1944
+ 0.00776934,
1945
+ 0.00011459,
1946
+ 0.00944597,
1947
+ -0.0094295,
1948
+ -0.00338595,
1949
+ -0.01048475,
1950
+ -0.02400045,
1951
+ 0.03072975,
1952
+ -0.0109637,
1953
+ 0.01086839,
1954
+ -0.00842686,
1955
+ -0.02364761,
1956
+ 0.00342246,
1957
+ -0.00714168,
1958
+ 0.02385984,
1959
+ -0.02467543,
1960
+ -0.01504063,
1961
+ -0.00446535,
1962
+ -0.01871253,
1963
+ -0.01395926,
1964
+ 0.0247374,
1965
+ -0.0080816,
1966
+ -0.03657099,
1967
+ 0.00132056,
1968
+ -0.01758603,
1969
+ 0.01844427,
1970
+ -0.01328465,
1971
+ 0.0111822,
1972
+ 0.01086053,
1973
+ 0.00981689,
1974
+ -0.03230486,
1975
+ 0.01464552,
1976
+ -0.00148964,
1977
+ -0.00762999,
1978
+ -0.00404623,
1979
+ -0.01966663,
1980
+ 0.00110262,
1981
+ 0.00535944,
1982
+ 0.01039123,
1983
+ -0.01684566,
1984
+ -0.02311614,
1985
+ -0.01413236,
1986
+ -0.02443904,
1987
+ -0.01073471,
1988
+ -0.00278614,
1989
+ -0.00934225,
1990
+ -0.00017413,
1991
+ -0.00365744,
1992
+ 0.01714262,
1993
+ -0.01331676,
1994
+ -2.972e-05,
1995
+ -0.00191073,
1996
+ 0.00781554,
1997
+ -0.01613794,
1998
+ 0.00775712,
1999
+ 0.00647007,
2000
+ -0.01789146,
2001
+ 0.00141006,
2002
+ -0.0162336,
2003
+ -0.01072993,
2004
+ 0.00046216,
2005
+ 0.015242,
2006
+ 0.00215394,
2007
+ 0.03298695,
2008
+ 0.03746279,
2009
+ -0.02986987,
2010
+ 0.01046771,
2011
+ 0.02376416,
2012
+ 0.01895383,
2013
+ 0.00395636,
2014
+ 0.00173356,
2015
+ 0.00281301,
2016
+ -0.02129791,
2017
+ 0.0113186,
2018
+ 0.00275263,
2019
+ 0.01533038,
2020
+ -0.02215966,
2021
+ -0.00148776,
2022
+ -0.04362523,
2023
+ -0.02110263,
2024
+ -0.03406411,
2025
+ 0.00858526,
2026
+ 0.00519482,
2027
+ -0.01531371,
2028
+ -0.02032654,
2029
+ -0.00879037,
2030
+ -0.01910952,
2031
+ -0.00970773,
2032
+ -0.00597221,
2033
+ 0.02600741,
2034
+ -0.01500964,
2035
+ 0.00282593,
2036
+ 0.00812054,
2037
+ 0.01510925,
2038
+ 0.01722519,
2039
+ -0.01325881,
2040
+ -0.03556012,
2041
+ -0.02189088,
2042
+ -0.0053198,
2043
+ 0.02048756,
2044
+ 0.02681401,
2045
+ -0.01647379,
2046
+ -0.00249846,
2047
+ -0.01893351,
2048
+ 0.00662435,
2049
+ 0.02269262,
2050
+ 0.01222809,
2051
+ -0.0210267,
2052
+ -0.00039488,
2053
+ -0.0512335,
2054
+ 0.00088038,
2055
+ -0.02341066,
2056
+ -0.00038121,
2057
+ 0.01761548,
2058
+ 0.02631648,
2059
+ 0.01183771,
2060
+ 0.02191784,
2061
+ 0.00207306,
2062
+ -0.01902432,
2063
+ -0.02336112,
2064
+ -0.00491415,
2065
+ -0.00072113,
2066
+ 0.00028628,
2067
+ -0.01824824,
2068
+ 0.01573452,
2069
+ -0.03276414,
2070
+ -0.00953589,
2071
+ 0.01647868,
2072
+ -0.01684641,
2073
+ 0.00790728,
2074
+ -0.01832088,
2075
+ 0.01247018,
2076
+ 0.00042123,
2077
+ 0.0078352,
2078
+ 0.00828961,
2079
+ -0.0020343,
2080
+ -0.01149357,
2081
+ -0.02579743,
2082
+ -0.01467749,
2083
+ 0.00252954,
2084
+ 0.00356522,
2085
+ -0.0229656,
2086
+ 0.00305361,
2087
+ 0.0100733,
2088
+ 0.00604701,
2089
+ 0.00316089,
2090
+ 0.0172147,
2091
+ -0.00011459,
2092
+ 0.01843379,
2093
+ 0.0113596,
2094
+ -0.01007929,
2095
+ -0.01242581,
2096
+ 0.00019472,
2097
+ -0.02393127,
2098
+ -0.04665328,
2099
+ 0.01382595,
2100
+ 0.00461308,
2101
+ -0.02666152,
2102
+ -0.03019182,
2103
+ 0.00341553,
2104
+ -0.0221114,
2105
+ 0.00693722,
2106
+ -0.00555421,
2107
+ -0.02032205,
2108
+ -0.02492155,
2109
+ 0.03080052,
2110
+ -0.0129261,
2111
+ -0.02659664,
2112
+ -0.02317971,
2113
+ -3.857e-05,
2114
+ 0.01464641,
2115
+ 0.02506778,
2116
+ -0.00284662,
2117
+ -0.00378326,
2118
+ 0.0097126,
2119
+ -0.0022778,
2120
+ -0.00778427,
2121
+ 0.0195424,
2122
+ 0.00874459,
2123
+ -0.03001611,
2124
+ 0.01055702,
2125
+ 0.00944672,
2126
+ 0.00660469
2127
+ ],
2128
+ "cols": [
2129
+ 1,
2130
+ 5,
2131
+ 8,
2132
+ 11,
2133
+ 13,
2134
+ 15,
2135
+ 17,
2136
+ 18,
2137
+ 34,
2138
+ 36,
2139
+ 44,
2140
+ 55,
2141
+ 63,
2142
+ 66,
2143
+ 69,
2144
+ 92,
2145
+ 93,
2146
+ 95,
2147
+ 98,
2148
+ 114,
2149
+ 119,
2150
+ 127,
2151
+ 145,
2152
+ 152,
2153
+ 161,
2154
+ 168,
2155
+ 171,
2156
+ 195,
2157
+ 246,
2158
+ 259,
2159
+ 265,
2160
+ 343,
2161
+ 364,
2162
+ 445,
2163
+ 506,
2164
+ 544,
2165
+ 621,
2166
+ 625,
2167
+ 636,
2168
+ 744,
2169
+ 766,
2170
+ 770,
2171
+ 799,
2172
+ 877,
2173
+ 965,
2174
+ 1003,
2175
+ 1346,
2176
+ 1410,
2177
+ 1505,
2178
+ 1704,
2179
+ 1799,
2180
+ 2072,
2181
+ 2135,
2182
+ 2175,
2183
+ 2248,
2184
+ 2314,
2185
+ 2376,
2186
+ 2437,
2187
+ 2514,
2188
+ 2595,
2189
+ 2664,
2190
+ 2684,
2191
+ 2816,
2192
+ 3343
2193
+ ]
2194
+ }
2195
+ }
2196
+ }
2197
+ }
code/configs/variants.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_doc": "Each entry becomes a model folder qwen35-<name>. Per edited layer: alpha (strength), dir ('released' = the paper's direction u for that layer, 'random_orth:SEED' = a random direction at right angles to u), cols ('released' = the paper's 64 neurons, 'all' = every neuron, 'random:SEED' = 64 other neurons picked at random), norm_match (rescale alpha so the weight change is as big as the released edit's). The released edit itself is qwen35-p50c (alpha 6, both layers).",
3
+ "a-4": {"about": "the knob turned the other way: makes the 64 neurons push 5x harder along the loop direction",
4
+ "layers": {"15": {"alpha": -4}, "19": {"alpha": -4}}},
5
+ "a1": {"about": "remove the loop component from the 64 neurons (projection)",
6
+ "layers": {"15": {"alpha": 1}, "19": {"alpha": 1}}},
7
+ "a2": {"about": "mirror the loop component (reflection): same column sizes, opposite push",
8
+ "layers": {"15": {"alpha": 2}, "19": {"alpha": 2}}},
9
+ "a4": {"about": "flip and triple the loop component",
10
+ "layers": {"15": {"alpha": 4}, "19": {"alpha": 4}}},
11
+ "a8": {"about": "stronger than released: flip and multiply by 7",
12
+ "layers": {"15": {"alpha": 8}, "19": {"alpha": 8}}},
13
+ "a12": {"about": "much stronger than released: flip and multiply by 11",
14
+ "layers": {"15": {"alpha": 12}, "19": {"alpha": 12}}},
15
+ "l15only": {"about": "the released edit on layer 15 only",
16
+ "layers": {"15": {"alpha": 6}}},
17
+ "l19only": {"about": "the released edit on layer 19 only",
18
+ "layers": {"19": {"alpha": 6}}},
19
+ "randdir": {"about": "control: same 64 neurons, same size of change, but a random direction instead of u",
20
+ "layers": {"15": {"alpha": 6, "dir": "random_orth:15", "norm_match": true},
21
+ "19": {"alpha": 6, "dir": "random_orth:19", "norm_match": true}}},
22
+ "randcols": {"about": "control: direction u, same size of change, but 64 random neurons instead of the chosen ones",
23
+ "layers": {"15": {"alpha": 6, "cols": "random:15", "norm_match": true},
24
+ "19": {"alpha": 6, "cols": "random:19", "norm_match": true}}},
25
+ "all1": {"about": "an earlier edit from the agent's run (strength 1, +24 points): remove the loop component from every neuron",
26
+ "layers": {"15": {"alpha": 1, "cols": "all"}, "19": {"alpha": 1, "cols": "all"}}},
27
+ "all2": {"about": "an earlier edit from the agent's run (strength 2, +35 points): mirror the loop component in every neuron",
28
+ "layers": {"15": {"alpha": 2, "cols": "all"}, "19": {"alpha": 2, "cols": "all"}}}
29
+ }
code/data/README.md ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Problem files
2
+
3
+ `scripts/prepare_data.py` writes the five problem files that every lab reads.
4
+
5
+ | File | What it holds |
6
+ |---|---|
7
+ | `math500.jsonl` | The MATH-500 test set (500 problems), one of the paper's three benchmarks |
8
+ | `gsm8k.jsonl` | The GSM8K main test set (1,319 problems), a benchmark |
9
+ | `ifeval.jsonl` | IFEval (541 prompts), a benchmark graded by its official checkers |
10
+ | `dev256.jsonl` | 128 GSM8K-train and 128 MATH-train practice problems the paper's agent never used. We measure every edit here. |
11
+ | `build128.jsonl` | 64 + 64 more unused practice problems. The Lab 8 capstone builds its own edit from these. |
12
+
13
+ The copy of this project on Hugging Face leaves the `.jsonl` files out. Three of them are benchmark test sets, and test sets posted online can end up in the training data of future models. The script rebuilds all five from public sources.
14
+
15
+ To rebuild them, run this from the project root on a machine with internet access and the `datasets` package (`scripts/setup_vm.sh` installs it on the GPU machines):
16
+
17
+ ```bash
18
+ git clone https://github.com/RikiyaT/lm_understand-logs external/lm_understand-logs
19
+ git -C external/lm_understand-logs checkout 4d09ba6d # the commit we used
20
+ python scripts/prepare_data.py --exp8 external/lm_understand-logs/exp8_math --out data
21
+ ```
code/labs/data/astra_pairs.json ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "source": "lm_understand-logs exp8_math/runs/p50c/tool_calls.jsonl row 8 (the agent's 4th forward pass, 'fwd_4')",
3
+ "how": "pairs: index 0,2,4,6 productive; 1,3,5,7 repetitive. Same prompt and same opening sentences inside a pair.",
4
+ "agent_token_lengths": [
5
+ 124,
6
+ 123,
7
+ 131,
8
+ 136,
9
+ 107,
10
+ 146,
11
+ 98,
12
+ 135
13
+ ],
14
+ "texts": [
15
+ "<|im_start|>user\nRafael works 10 hours on Monday and 8 on Tuesday, with 20 hours left to work. He earns $20 per hour. Find his weekly pay.<|im_end|>\n<|im_start|>assistant\n<think>\nThe first two days total 10+8=18 hours. Adding the remaining 20 gives 38 hours. His pay is 38*20=760 dollars. Checking: 200+160+400=760, consistent with the total. The hours and hourly rate have the correct units.",
16
+ "<|im_start|>user\nRafael works 10 hours on Monday and 8 on Tuesday, with 20 hours left to work. He earns $20 per hour. Find his weekly pay.<|im_end|>\n<|im_start|>assistant\n<think>\nThe first two days total 10+8=18 hours. Adding the remaining 20 gives 38 hours. But is 20 the total? No, it is the remaining time. But is 20 the total? No, it is the remaining time. But is 20 the total? No, it is the remaining time.",
17
+ "<|im_start|>user\nFind the remainder when 6x^4-14x^3-4x^2+2x-26 is divided by 2x-6.<|im_end|>\n<|im_start|>assistant\n<think>\nSet 2x-6=0, giving x=3. The remainder is P(3)=486-378-36+6-26=52. To justify this, write P(x)=(2x-6)Q(x)+R. Substituting x=3 cancels the quotient term, leaving P(3)=R. Hence the constant remainder is 52.",
18
+ "<|im_start|>user\nFind the remainder when 6x^4-14x^3-4x^2+2x-26 is divided by 2x-6.<|im_end|>\n<|im_start|>assistant\n<think>\nSet 2x-6=0, giving x=3. The remainder is P(3)=486-378-36+6-26=52. But P(x) has -26. The constant term is -26. But P(x) has -26. The constant term is -26. But P(x) has -26. The constant term is -26.",
19
+ "<|im_start|>user\nThe sequence is 88,85,82,... How many terms precede -17?<|im_end|>\n<|im_start|>assistant\n<think>\nThe common difference is -3. Solve 88-3(n-1)=-17. Then 3(n-1)=105, so n=36. The target is the 36th term, with 35 terms preceding it. Checking: the 35th term is -14, followed by -17.",
20
+ "<|im_start|>user\nThe sequence is 88,85,82,... How many terms precede -17?<|im_end|>\n<|im_start|>assistant\n<think>\nThe common difference is -3. Solve 88-3(n-1)=-17. Then 3(n-1)=105, so n=36. Now 88-85=3 and 85-82=3. Now 88-85=3 and 85-82=3. Now 88-85=3 and 85-82=3. Now 88-85=3 and 85-82=3.",
21
+ "<|im_start|>user\nA machine makes 45 necklaces, and another makes 2.4 times as many. Find the total.<|im_end|>\n<|im_start|>assistant\n<think>\nThe second machine makes 45*2.4=108 necklaces. Add the first machine's 45 to obtain 153 necklaces. Multiplication gives the second quantity, while addition combines the two machines. A check is 45*3.4=153.",
22
+ "<|im_start|>user\nA machine makes 45 necklaces, and another makes 2.4 times as many. Find the total.<|im_end|>\n<|im_start|>assistant\n<think>\nThe second machine makes 45*2.4=108 necklaces. Add the first machine's 45 to obtain 153 necklaces. Is 108 the total? No, 45+108=153. Is 108 the total? No, 45+108=153. Is 108 the total? No, 45+108=153."
23
+ ]
24
+ }
code/labs/lab0_toy.md ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Lab 0: The toy edit on a 4 x 6 matrix
2
+
3
+ **Big thing:** You will run the paper's weight edit `W' = W - alpha * u (u^T W_S)` on a 4 x 6 matrix that fits on one screen. The edit changes how hard two chosen neurons push along one direction, leaves everything else alone, and the script checks each claim with `assert`.
4
+
5
+ **Why it matters:** In Qwen3.5-0.8B the same matrix is 1,024 x 3,584, too large to read by eye, but the linear algebra is identical.
6
+
7
+ **Time and hardware:** 5 seconds on any laptop CPU.
8
+
9
+ **Theory guide:** Sections 6 and 8.
10
+
11
+ ## Before you run: predict
12
+
13
+ Write a short guess for each question before running the script.
14
+
15
+ 1. In the toy layer, neuron 1 pushes along `u` by `-0.555` per unit of activation. With edit strength `alpha = 1`, what does neuron 1's push along `u` become? What if `alpha = 6`?
16
+ 2. The toy matrix has 6 columns (one per neuron), and the edit touches only neurons `S = [1, 4]`. How many columns of `W' - W` will be non-zero?
17
+ 3. On a token where neurons 1 and 4 have activation `0.0` and the other four neurons fire normally, how much does the edited output `W' a` differ from `W a`?
18
+
19
+ ## Run it
20
+
21
+ Run this from the project root (`uv` creates a temporary environment with `numpy` and `matplotlib`):
22
+
23
+ ```bash
24
+ uv run --with numpy --with matplotlib python labs/lab0_toy.py
25
+ ```
26
+
27
+ The script prints four checks, verifies each with `assert`, and writes `results/figures/lab0_toy.png`.
28
+
29
+ ## What you should see
30
+
31
+ ```text
32
+ 1. How much each neuron writes along u (u . W[:, j]): [ 1.062 -0.555 1.043 -0.495 0.318 0.162]
33
+
34
+ 2. The edit scales the chosen neurons' push along u by (1 - alpha) and leaves everything else alone
35
+ alpha= 0 (no edit ) push along u of neurons [1, 4]: [-0.555 0.318] -> [-0.555 0.318]
36
+ alpha= 1 (erase ) push along u of neurons [1, 4]: [-0.555 0.318] -> [ 0. -0.]
37
+ alpha= 2 (mirror ) push along u of neurons [1, 4]: [-0.555 0.318] -> [ 0.555 -0.318]
38
+ alpha= 6 (flip and amplify 5x (the paper)) push along u of neurons [1, 4]: [-0.555 0.318] -> [ 2.776 -1.592]
39
+ alpha=-4 (amplify 5x the other way ) push along u of neurons [1, 4]: [-0.555 0.318] -> [-2.776 1.592]
40
+
41
+ 3. The change is rank one: one direction out (u), one pattern in (u^T W on the chosen columns)
42
+ singular values of W' - W: [3.84 0. 0. 0. ]
43
+
44
+ 4. The weight edit is steering: W' a = W a - alpha * s * u, with s = u . (W[:, S] a[S])
45
+ chosen neurons quiet s = +0.000 change to the output |W'a - Wa| = 0.000
46
+ chosen neurons firing s = -0.710 change to the output |W'a - Wa| = 4.261
47
+ Same weights, different tokens, different push: the edit only acts when the chosen neurons fire.
48
+
49
+ all checks passed; figure in results/figures/lab0_toy.png
50
+ ```
51
+
52
+ ![Left: heatmap of W' minus W, with changes only in the columns of neurons 1 and 4. Right: the multiplier 1 minus alpha, falling from 1 at alpha 0 to -5 at alpha 6.](../results/figures/lab0_toy.png)
53
+
54
+ On the left of `lab0_toy.png`, the `W' - W` heatmap has four white columns at neurons 0, 2, 3, and 5 (exact zeros) and two colored columns at neurons 1 and 4. In every row, column 4 has the opposite color of column 1. Neuron 1 started with a negative push along `u` (`-0.555`) and neuron 4 started with a positive push (`0.318`), so the edit moves them in opposite directions along `u`. On the right, the blue line plots the multiplier `1 - alpha`: `1` at `alpha = 0` (no edit), `0` at `alpha = 1` (erase), `-1` at `alpha = 2` (mirror), and `-5` at `alpha = 6` (the paper's setting).
55
+
56
+ ## Why it works
57
+
58
+ ### 1. Neurons write columns into the residual stream
59
+
60
+ A language model processes text one token (word piece) at a time. Across the 24 blocks (layers) of Qwen3.5-0.8B, the model updates a running list of numbers for each token called the residual stream. That stream holds 1,024 numbers per token in Qwen3.5-0.8B and `d = 4` in `lab0_toy.py`.
61
+
62
+ Each block ends with an MLP (multi-layer perceptron). Inside it, two input matrices (`gate_proj` and `up_proj`) and a SiLU nonlinearity turn the stream into `n` neuron activations `a[0], ..., a[n-1]` (`n = 3,584` in Qwen3.5-0.8B, `n = 6` in the toy). Each activation `a[j]` is a single number measuring how strongly neuron `j` fires on the current token. The MLP's last matrix, `down_proj`, is `W` of shape `d x n`. Column `W[:, j]` is the `d`-number vector that neuron `j` adds to the stream when it fires with activation `1`. The MLP output is the weighted sum of its columns:
63
+
64
+ $$y = W a = \sum_{j=0}^{n-1} a_j W[:, j]$$
65
+
66
+ ```mermaid
67
+ flowchart LR
68
+ A["Neuron activations<br/>a[0] ... a[5]"] --> B["Column j of W is W[:, j]<br/>(what neuron j writes)"]
69
+ B --> C["Weighted sum<br/>y = sum of a[j] * W[:, j]"]
70
+ C --> D["Added to the<br/>residual stream"]
71
+ ```
72
+
73
+ ### 2. Projecting a column onto the direction `u`
74
+
75
+ A direction `u` is a residual-stream vector scaled to length 1 (`u . u = 1`). In the paper, `u` points toward repetitive looping. The dot product of `u` with column `W[:, j]` measures how much neuron `j` pushes the stream along `u` per unit of activation:
76
+
77
+ $$g_j = u \cdot W[:, j]$$
78
+
79
+ Across all columns, the row vector `g = u @ W` holds those six numbers: `[1.062, -0.555, 1.043, -0.495, 0.318, 0.162]`.
80
+
81
+ Every column `W[:, j]` splits into two perpendicular parts: `g_j u` along `u`, and `W[:, j] - g_j u` at right angles to `u`.
82
+
83
+ ### 3. What the edit does to one chosen column
84
+
85
+ The edit picks a neuron subset `S` (here `S = [1, 4]`) and a strength `alpha`, then subtracts `alpha * g_j u` from each column `j` in `S`:
86
+
87
+ $$W'[:, j] = W[:, j] - \alpha (u \cdot W[:, j]) u$$
88
+
89
+ Columns outside `S` stay unchanged (`W'[:, j] = W[:, j]`). Writing `W_S` for `W` with columns outside `S` zeroed out gives the paper's matrix formula:
90
+
91
+ $$W' = W - \alpha u (u^T W_S)$$
92
+
93
+ To find an edited column's new push along `u`, take the dot product with `u` and use `u . u = 1`:
94
+
95
+ $$u \cdot W'[:, j] = u \cdot W[:, j] - \alpha (u \cdot W[:, j])(u \cdot u) = (1 - \alpha)(u \cdot W[:, j])$$
96
+
97
+ The part of `W[:, j]` perpendicular to `u` does not change, because `u` has zero dot product with any perpendicular direction.
98
+
99
+ ```mermaid
100
+ flowchart TD
101
+ Col["Chosen column W[:, j]<br/>(neuron 1: g_1 = -0.555)"] --> Split["Split into two parts at right angles"]
102
+ Split --> Par["Part along u:<br/>g_j * u"]
103
+ Split --> Perp["Part at right angles to u:<br/>W[:, j] - g_j * u"]
104
+ Par --> Scale["Multiply by (1 - alpha)<br/>At alpha = 6: -5 * g_j * u"]
105
+ Perp --> Keep["Keep unchanged"]
106
+ Scale --> Out["Edited column W'[:, j]<br/>(neuron 1 push along u: 2.776)"]
107
+ Keep --> Out
108
+ ```
109
+
110
+ Compare this with the printed numbers for neuron 1 (`g_1 = -0.555`) and neuron 4 (`g_4 = 0.318`):
111
+
112
+ - At `alpha = 1` (erase), the multiplier `1 - 1 = 0` sends `-0.555` and `0.318` to `0`. Both neurons still write perpendicular to `u`, while their push along `u` is zeroed out.
113
+ - At `alpha = 2` (mirror), the multiplier `1 - 2 = -1` flips `-0.555` to `+0.555` and `0.318` to `-0.318`.
114
+ - At `alpha = 6` (the paper's value), the multiplier is `1 - 6 = -5`. For neuron 1, `-5 x -0.555 = +2.775`, while the script prints `2.776` from the unrounded `-0.555157` (`-5 x -0.555157 = 2.776`). For neuron 4, `-5 x 0.318416 = -1.592`.
115
+
116
+ ### 4. Why the change is rank one, and why editing weights equals steering
117
+
118
+ The matrix change `D = W' - W` equals `-alpha * outer(u, g_masked)`. Every column of `D` is a multiple of `u`, and a single column vector times a row vector has rank one. Running SVD (singular value decomposition, which splits a matrix into independent rank-one pieces ordered by size) on `D` gives a first singular value of `3.84` and three zeros (`0`). Since `u` has length 1, `3.84` equals `|alpha| * ||g_S|| = 6 * sqrt((-0.5552)^2 + (0.3184)^2) = 3.840`.
119
+
120
+ Now multiply `W'` by an activation vector `a` on one token:
121
+
122
+ $$W' a = W a - \alpha u (u^T W_S a) = W a - \alpha s(t) u$$
123
+
124
+ Here `s(t) = u . (W_S a)` is how hard the chosen neurons push along `u` on token `t`. Since `W_S` keeps only columns in `S`, `W_S a` equals `W[:, S] a[S]`, the activation-weighted sum of those columns. When neurons 1 and 4 are quiet (`a[1] = a[4] = 0`), `s = 0.000` and the output is unchanged (`|W'a - Wa| = 0.000`). When both fire at `3.0`, `s = 3.0 x (-0.5552 + 0.3184) = -0.710`, shifting the output along `u` by `|alpha * s| = 6 x 0.710 = 4.261`. A permanent weight edit therefore acts like adding a steering vector `-alpha * s(t) * u` whose strength `s(t)` comes from the token's own activations.
125
+
126
+ ## Check yourself
127
+
128
+ 1. What value of `alpha` makes the chosen neurons push 3 times as hard in their original direction along `u`?
129
+ <details><summary>Answer</summary>
130
+
131
+ Set `1 - alpha = 3`, which gives `alpha = -2`. Any negative `alpha` amplifies the original push along `u` instead of opposing it.
132
+ </details>
133
+
134
+ 2. Why does the SVD of `W' - W` have only one non-zero singular value even though we changed two columns (`S = [1, 4]`)?
135
+ <details><summary>Answer</summary>
136
+
137
+ Both changed columns were shifted along the same direction `u` (column 1 moved by `+3.331 u` and column 4 moved by `-1.910 u`). Because every column of `W' - W` is a multiple of one vector `u`, the entire matrix change has rank one.
138
+ </details>
139
+
140
+ 3. If you replace the direction `u` with `-u` and keep `alpha = 6` and `S = [1, 4]`, how does the edited matrix `W'` change?
141
+ <details><summary>Answer</summary>
142
+
143
+ It does not change. In `W' = W - alpha * u (u^T W_S)`, `u` appears twice in `u u^T`, and `(-u)(-u)^T = u u^T`. Only the line through `u` matters, not which end gets the plus sign.
144
+ </details>
145
+
146
+ 4. On a token where neuron 1 fires with `a[1] = 2.0` and neuron 4 fires with `a[4] = 0.0`, what is `s`, and how big is the change `|W'a - Wa|` at `alpha = 6`?
147
+ <details><summary>Answer</summary>
148
+
149
+ `s = a[1] * g_1 = 2.0 * (-0.5552) = -1.110`. Since `u` has length 1, the output changes by `|alpha * s| = 6 * 1.110 = 6.662` along `u`.
150
+ </details>
151
+
152
+ ## Try this next
153
+
154
+ 1. **Test that `u` and `-u` give the exact same edit.** In `labs/lab0_toy.py`, after `D = edit(W, u, 6, S) - W` (line 48), add `assert np.allclose(edit(W, u, 6, S), edit(W, -u, 6, S))` and re-run. All assertions pass, showing why SVD sign flips in Lab 1 do not change the edit.
155
+ 2. **Touch 4 neurons instead of 2.** Change line 21 from `S = [1, 4]` to `S = [0, 1, 2, 4]` and re-run. The heatmap in `results/figures/lab0_toy.png` shows four colored columns instead of two, and step 3 still prints `0.` for the second, third, and fourth singular values, because the edit is still rank one.
156
+ 3. **Add `alpha = -2` to the table.** On lines 38 and 39, add `(-2, "amplify 3x original way")` and re-run. The push along `u` of neurons `[1, 4]` triples from `[-0.555, 0.318]` to `[-1.665, 0.955]`.
157
+ 4. **Scale up to the real layer dimensions.** Change line 17 from `d, n = 4, 6` to `d, n = 1024, 3584` and line 21 to `S = list(range(64))`, then re-run. Every `assert` passes in a few seconds. Numpy shortens arrays over 1,000 numbers with `...` in step 1, while step 2 prints 64 numbers per line. Step 4 prints `s = -35.717` and a change of `214.301` when the chosen neurons fire. The output moves much more than in the toy because 64 neurons push along `u` together.
code/labs/lab0_toy.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ """Lab 0: weight-space programming on a toy layer small enough to print.
3
+
4
+ uv run --with numpy --with matplotlib python labs/lab0_toy.py # seconds, any laptop
5
+
6
+ An MLP's last matrix, down_proj, is W (here 4 x 6; in Qwen3.5-0.8B it is 1,024 x 3,584). Neuron j fires with
7
+ strength a[j] and writes its column W[:, j] into the stream, so the layer adds y = W a = sum_j a[j] * W[:, j].
8
+ The paper's edit picks a direction u and some neurons S and sets W' = W - alpha * u (u^T W[:, S]) on those
9
+ columns. Every claim below is checked with an assert, so if this script finishes, they all hold.
10
+ """
11
+ import os
12
+
13
+ import numpy as np
14
+
15
+ np.set_printoptions(precision=3, suppress=True)
16
+ rng = np.random.default_rng(0)
17
+ d, n = 4, 6
18
+ W = rng.normal(size=(d, n))
19
+ u = rng.normal(size=d)
20
+ u /= np.linalg.norm(u) # the direction we care about (in the paper: "toward looping")
21
+ S = [1, 4] # the neurons we allow ourselves to touch (in the paper: 64 of 3,584)
22
+ others = [j for j in range(n) if j not in S]
23
+
24
+
25
+ def edit(W, u, alpha, S=None):
26
+ g = u @ W # g[j] = how much neuron j writes along u, per unit of activation
27
+ if S is not None:
28
+ mask = np.zeros(W.shape[1])
29
+ mask[S] = 1
30
+ g = g * mask
31
+ return W - alpha * np.outer(u, g)
32
+
33
+
34
+ print("1. How much each neuron writes along u (u . W[:, j]):", u @ W)
35
+
36
+ print("\n2. The edit scales the chosen neurons' push along u by (1 - alpha) and leaves everything else alone")
37
+ P = np.eye(d) - np.outer(u, u) # removes the u part of any vector
38
+ for alpha, name in [(0, "no edit"), (1, "erase"), (2, "mirror"), (6, "flip and amplify 5x (the paper)"),
39
+ (-4, "amplify 5x the other way")]:
40
+ W2 = edit(W, u, alpha, S)
41
+ before, after = (u @ W)[S], (u @ W2)[S]
42
+ assert np.allclose(after, (1 - alpha) * before)
43
+ assert np.allclose(W2[:, others], W[:, others]) # untouched neurons stay identical
44
+ assert np.allclose(P @ W2, P @ W) # nothing changes outside the direction u
45
+ print(f" alpha={alpha:>2} ({name:31s}) push along u of neurons {S}: {before} -> {after}")
46
+
47
+ print("\n3. The change is rank one: one direction out (u), one pattern in (u^T W on the chosen columns)")
48
+ D = edit(W, u, 6, S) - W
49
+ sv = np.linalg.svd(D, compute_uv=False)
50
+ assert sv[1] < 1e-12 * sv[0]
51
+ print(" singular values of W' - W:", sv)
52
+
53
+ print("\n4. The weight edit is steering: W' a = W a - alpha * s * u, with s = u . (W[:, S] a[S])")
54
+ alpha = 6
55
+ for name, a in [("chosen neurons quiet", np.where(np.isin(np.arange(n), S), 0.0, rng.normal(size=n))),
56
+ ("chosen neurons firing", np.where(np.isin(np.arange(n), S), 3.0, rng.normal(size=n)))]:
57
+ s = u @ (W[:, S] @ a[S])
58
+ y_edit, y_steer = edit(W, u, alpha, S) @ a, W @ a - alpha * s * u
59
+ assert np.allclose(y_edit, y_steer)
60
+ print(f" {name:22s} s = {s:+.3f} change to the output |W'a - Wa| = {np.linalg.norm(y_edit - W @ a):.3f}")
61
+ print(" Same weights, different tokens, different push: the edit only acts when the chosen neurons fire.")
62
+
63
+ import matplotlib # noqa: E402
64
+ matplotlib.use("Agg")
65
+ import matplotlib.pyplot as plt # noqa: E402
66
+
67
+ fig, ax = plt.subplots(1, 2, figsize=(11, 3.6))
68
+ im = ax[0].imshow(edit(W, u, 6, S) - W, cmap="RdBu", vmin=-6, vmax=6)
69
+ ax[0].set_title("W' - W: only the chosen columns change")
70
+ ax[0].set_xlabel("neuron (column)")
71
+ ax[0].set_ylabel("stream dimension (row)")
72
+ fig.colorbar(im, ax=ax[0])
73
+ al = np.linspace(-5, 8, 100)
74
+ ax[1].plot(al, 1 - al, color="#0969da")
75
+ for x, lab in [(0, "no edit"), (1, "erase"), (2, "mirror"), (6, "the paper")]:
76
+ ax[1].plot(x, 1 - x, "o", color="#cf222e")
77
+ ax[1].annotate(lab, (x, 1 - x), xytext=(6, 4), textcoords="offset points")
78
+ ax[1].axhline(0, color="black", lw=0.5)
79
+ ax[1].set_xlabel("alpha")
80
+ ax[1].set_ylabel("push along u after / before")
81
+ ax[1].set_title("What alpha does to the chosen neurons")
82
+ fig.tight_layout()
83
+ out = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "results", "figures")
84
+ os.makedirs(out, exist_ok=True)
85
+ fig.savefig(os.path.join(out, "lab0_toy.png"), dpi=130)
86
+ print("\nall checks passed; figure in results/figures/lab0_toy.png")
code/labs/lab1_diff.md ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Lab 1: Find the edit by diffing the two weight files
2
+
3
+ **Big thing:** You will compare all 488 weight tensors (873,438,784 numbers) of base Qwen3.5-0.8B and the paper's released model bit for bit. From the weights alone, you recover the full edit: `layers = [15, 19]`, `64` columns per layer, rank-one direction `u`, and strength `alpha = 6`.
4
+
5
+ **Why it matters:** When you have the weight files, you do not have to take a paper's description on faith. Tensor diffs and an SVD turn a 0.87-billion-parameter checkpoint diff into one short formula.
6
+
7
+ **Time and hardware:** Under a minute on CPU on `sampler` or `trainer` (no GPU needed).
8
+
9
+ **Theory guide:** Section 9, step 4.
10
+
11
+ ## Before you run: predict
12
+
13
+ Write a short guess for each question before running the script.
14
+
15
+ 1. Out of the 488 weight tensors in Qwen3.5-0.8B, how many differ between the base model and the released model `qwen35-p50c`?
16
+ 2. Each edited `down_proj.weight` matrix has shape `1,024 x 3,584`, and the edit touches `64` columns (`64 x 1,024 = 65,536` numbers). Will the file have `65,536` changed entries or slightly fewer, and why?
17
+ 3. In Lab 0, the second singular value of `W' - W` was `0.0000`. For the real weight change, will the second singular value be exactly zero or slightly above zero?
18
+
19
+ ## Run it
20
+
21
+ Connect to one of the lab VMs (`sampler` or `trainer`), activate the lab environment, and run `labs/lab1_diff.py` on CPU. This script does not touch a GPU.
22
+
23
+ ```bash
24
+ ssh sampler
25
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
26
+ python labs/lab1_diff.py
27
+ ```
28
+
29
+ The script compares every tensor in `/nvme/wsp-lab/models/Qwen3.5-0.8B` against `/nvme/wsp-lab/models/qwen35-p50c`, writes `results/lab1/diff.json`, and saves `results/figures/lab1_diff.png`.
30
+
31
+ ## What you should see
32
+
33
+ ```text
34
+ 2 of 488 tensors differ (873M numbers in all):
35
+ model.language_model.layers.15.mlp.down_proj.weight shape [1024, 3584]
36
+ entries changed: 65,260 of 3,670,016, all inside 64 columns (neurons)
37
+ singular values of the change: [1.7489, 0.001, 0.0009, 0.0009] ... rank-one share 0.999994
38
+ fitted alpha = 6.0003; the matrix moved by 10.6%
39
+ model.language_model.layers.19.mlp.down_proj.weight shape [1024, 3584]
40
+ entries changed: 64,928 of 3,670,016, all inside 64 columns (neurons)
41
+ singular values of the change: [1.3988, 0.0009, 0.0008, 0.0007] ... rank-one share 0.999991
42
+ fitted alpha = 6.0002; the matrix moved by 8.1%
43
+ |cos| between the two directions: 0.849 (1 = same direction, 0 = unrelated; the SVD sign is arbitrary, Lab 3 fixes it)
44
+ wrote /nvme/wsp-lab/code/results/lab1
45
+ ```
46
+
47
+ `results/lab1/diff.json` and `results/edit_info.json` record the exact numbers:
48
+
49
+ | Measurement | Layer 15 (`down_proj.weight`) | Layer 19 (`down_proj.weight`) |
50
+ |---|---|---|
51
+ | Matrix shape (rows x columns) | `1,024 x 3,584` (`3,670,016` entries) | `1,024 x 3,584` (`3,670,016` entries) |
52
+ | Entries changed | `65,260` | `64,928` |
53
+ | Columns (neurons) containing every change | `64` | `64` |
54
+ | First 8 changed columns | `0, 2, 11, 25, 34, 35, 38, 39` | `1, 5, 8, 11, 13, 15, 17, 18` |
55
+ | Top 4 singular values of `W1 - W0` | `1.7489, 0.0010, 0.0009, 0.0009` | `1.3988, 0.0009, 0.0008, 0.0007` |
56
+ | Rank-one share ($s_1^2 / \sum_i s_i^2$) | `0.999994` | `0.999991` |
57
+ | Fitted strength `alpha` | `6.0003` | `6.0002` |
58
+ | Relative matrix change ($\|W_1 - W_0\|_F / \|W_0\|_F$) | `10.6%` (`0.10636`) | `8.1%` (`0.08106`) |
59
+ | Absolute cosine between $u_{15}$ and $u_{19}$ | `0.849` | `0.849` |
60
+
61
+ ![Top row: size of the change in each of the 3,584 columns of layers 15 and 19, with 64 spikes and exact zeros elsewhere. Bottom row: the top 10 singular values on a log scale, with the first far above the rest.](../results/figures/lab1_diff.png)
62
+
63
+ In `lab1_diff.png`, the top row plots the Euclidean length (norm) of the change in each of the 3,584 columns for layers 15 and 19. You see 64 red spikes rising from a floor of exact zeros; in layer 19, the tallest spike near the left edge is neuron 11. The bottom row plots the top 10 singular values on a logarithmic scale. Bar 1 sits above `10^0` (`1.7489` and `1.3988`), while bars 2 through 10 sit more than three orders of magnitude lower around `10^-3`.
64
+
65
+ ## Why it works
66
+
67
+ ### 1. The detective pipeline: from two files to four numbers
68
+
69
+ The script inspects the two model folders in four steps, using no prior knowledge of how the paper built its edit.
70
+
71
+ ```mermaid
72
+ flowchart LR
73
+ A["488 tensors in base and p50c<br/>(873,438,784 numbers)"] --> B["Bitwise check:<br/>only 2 tensors differ<br/>(layers 15 and 19 down_proj)"]
74
+ B --> C["Column norms of D = W1 - W0:<br/>nonzero in 64 of 3,584 columns"]
75
+ C --> D["SVD of D[:, cols]:<br/>first singular value holds<br/>> 99.999% of the squared change<br/>left vector = u"]
76
+ D --> E["Least-squares fit on W0:<br/>recover alpha = 6.0003 and 6.0002"]
77
+ ```
78
+
79
+ First, `safe_open` streams all 488 tensors in `model.safetensors-00001-of-00001.safetensors` and checks `torch.equal(tb, te)`. Exactly 486 are bit-for-bit identical; only `model.language_model.layers.15.mlp.down_proj.weight` and `model.language_model.layers.19.mlp.down_proj.weight` differ.
80
+
81
+ Second, the script subtracts the base weights from the edited weights in 32-bit floating point (`fp32`): `D = W1.float() - W0.float()`. Checking which columns have a non-zero sum of absolute differences (`D.abs().sum(0) > 0`) shows that every changed entry falls inside 64 columns in layer 15 and 64 columns in layer 19, leaving the other `3,520` columns untouched. Column `11` in layer 19's list `[1, 5, 8, 11, ...]` is the single neuron the agent zeroed out early in its search (+6.25 percentage points on practice problems) before moving to a 64-neuron edit.
82
+
83
+ ### 2. Why 65,260 and 64,928 entries changed instead of 65,536
84
+
85
+ 64 columns times 1,024 rows is `65,536` entries per layer, yet only `65,260` entries change in layer 15 (`276` unchanged) and `64,928` in layer 19 (`608` unchanged).
86
+
87
+ The reason is number precision. The weights are stored on disk in `bf16` (bfloat16, a 16-bit floating-point format with 2 to 3 significant decimal digits). Where `u` or `u . W0[:, j]` is near zero, the update `-alpha * u[i] * (u . W0[:, j])` is smaller than half a `bf16` step around `W0[i, j]`. Casting the edited tensor from `fp32` back to `bf16` rounds those entries back to their original bits.
88
+
89
+ ### 3. Taking the change apart with SVD
90
+
91
+ To check whether all 64 columns moved along one direction `u` or 64 separate directions, `recover(W0, W1)` in `wsp/edits.py` takes the `1,024 x 64` slice `D[:, cols]` and computes its Singular Value Decomposition (SVD):
92
+
93
+ $$D[:, \text{cols}] = U \Sigma V^T = \sum_{k=1}^{64} s_k u_k v_k^T$$
94
+
95
+ SVD writes a matrix as a sum of rank-one pieces. The singular values $s_1 \ge s_2 \ge \dots \ge s_{64} \ge 0$ give the size of each piece, and the columns $u_k$ of $U$ are mutually perpendicular unit vectors.
96
+
97
+ - In layer 15, $s_1 = 1.7489$, while $s_2 = 0.0010$, $s_3 = 0.0009$, and $s_4 = 0.0009$. The first piece holds `0.999994` (99.9994%) of the squared Frobenius norm ($s_1^2 / \sum_k s_k^2$).
98
+ - In layer 19, $s_1 = 1.3988$, while $s_2 = 0.0009$, $s_3 = 0.0008$, and $s_4 = 0.0007$. The rank-one share is `0.999991` (99.9991%).
99
+
100
+ The change is a rank-one outer product; the tail of singular values near `0.001` is consistent with `bf16` rounding noise. The top left singular vector `u = U[:, 0]` is the edit direction `u` up to a sign.
101
+
102
+ ### 4. Recovering `alpha` and checking the bits
103
+
104
+ With `u` and `cols` known from `D`, the unrounded change on the chosen columns is $-\alpha P$, where $P = u (u^T W_0[:, \text{cols}])$ is the projection of the base columns onto `u`. Here $P$ depends only on the line through `u`, since $(-u)(-u)^T = u u^T$. A least-squares fit in `recover` finds the scalar `alpha` that best matches `D[:, cols]` to `-alpha * P`:
105
+
106
+ $$\alpha = -\frac{\sum_{i,j} D_{i,j} P_{i,j}}{\sum_{i,j} P_{i,j}^2}$$
107
+
108
+ This gives `6.0003` for layer 15 and `6.0002` for layer 19, matching the paper's `alpha = 6` to four digits.
109
+
110
+ ```mermaid
111
+ flowchart TD
112
+ SVD["SVD of D[:, cols]<br/>gives u (up to sign +/-1)"] --> P["Compute rank-1 template<br/>P = u (u^T W0[:, cols])<br/>(sign of u cancels out)"]
113
+ P --> Fit["Least-squares ratio<br/>alpha = -sum(D * P) / sum(P * P)<br/>gives 6.0003 and 6.0002"]
114
+ Fit --> Rebuild["Plug u, cols, alpha=6 into formula<br/>Compare with released bf16 bits:<br/>matches to <= 0.00049 (1 bf16 step)"]
115
+ ```
116
+
117
+ In `scripts/build_models.py` (logged in `results/edit_info.json`), we plug the recovered `u`, `cols`, and `alpha` into `projection_edit` on `W0` and compare the resulting `bf16` tensor against `W1`. Out of `3,670,016` entries per layer, all but `5,875` in layer 15 (`5,924` at `alpha = 6.0`) and `6,158` in layer 19 (`6,166` at `alpha = 6.0`) match bit for bit. The largest gap anywhere is `0.00048828125` (about `0.00049`), one `bf16` rounding step.
118
+
119
+ Comparing the unit vectors $u_{15}$ and $u_{19}$ gives $|u_{15} \cdot u_{19}| = 0.849$. An absolute cosine of `1.0` means two vectors lie on the same line, pointing the same way or opposite ways, and `0.0` means they are orthogonal (unrelated). A value of `0.849` is an angle of about 32 degrees: the loop directions at blocks 15 and 19 are closely related in the 1,024-dimensional stream, but distinct.
120
+
121
+ ## Check yourself
122
+
123
+ 1. Why does `labs/lab1_diff.py` report the absolute value `|cos| = 0.849` between `u1` and `u2` instead of a signed cosine?
124
+ <details><summary>Answer</summary>
125
+
126
+ SVD decomposes a matrix into $u_1 s_1 v_1^T$, where flipping the signs of both $u_1$ and $v_1$ leaves the product unchanged: $(-u_1) s_1 (-v_1)^T = u_1 s_1 v_1^T$. From the weight diff alone, you know the line of `u` at each layer, not whether `+u` or `-u` points toward looping. Lab 3 fixes the sign using the model's activations on looping and productive text.
127
+ </details>
128
+
129
+ 2. What causes the second singular value of `W1 - W0` to be around `0.0010` in layer 15 instead of `0`?
130
+ <details><summary>Answer</summary>
131
+
132
+ Saving the edited weights in `bf16` (16-bit floating point) rounds each of the 65,536 edited entries, adding small rounding errors that appear in the SVD as a singular-value floor near `0.001`.
133
+ </details>
134
+
135
+ 3. The edit flips each edited column's part along `u` and multiplies it by 5. Yet `||W1 - W0|| / ||W0||` is only `10.6%` in layer 15 and `8.1%` in layer 19. Is the edit really a small change?
136
+ <details><summary>Answer</summary>
137
+
138
+ No. That 10.6% averages over the whole matrix, hiding how large the change is on the edited columns.
139
+
140
+ The 64 edited columns hold 1.6% of the matrix's squared norm in layer 15 (`||W0[:, cols]||^2 / ||W0||^2 = 0.0157`) and 1.7% in layer 19.
141
+
142
+ Inside those columns, the change is large. Scaling a column's `u` component by -5 changes it by 6 times its size, so column `j` moves by `6 * |cos(u, column j)|` of its length. Because the edited columns lean toward `u` much more than the rest (median `|cos|` is 0.095 vs. 0.018 in layer 15), a typical edited column moves by 57% of its length (43% in layer 19) and the most-changed column moves by 1.7 times its length (2.3 times in layer 19). Together, the 64-column block changes by 85% of its norm in layer 15 and 62% in layer 19.
143
+
144
+ Multiplying those two ratios gives the matrix-wide figure: `sqrt(0.0157) * 0.85 = 0.125 * 0.85 = 0.106`. The edit is a large change to a few neurons that looks small only when divided by the full matrix.
145
+ </details>
146
+
147
+ ## Try this next
148
+
149
+ 1. **Inspect how many neurons layers 15 and 19 share.** Run a one-line check on `results/lab1/diff.json` from the VM shell:
150
+ ```bash
151
+ python -c 'import json; d=json.load(open("results/lab1/diff.json"))["changed"]; c15,c19=[set(v["cols"]) for v in d.values()]; print("shared column indices:", sorted(c15 & c19))'
152
+ ```
153
+ It prints `[11, 34, 63]`. Neuron numbers do not link layers. You could renumber one layer's neurons, moving each neuron's row of `gate_proj` and `up_proj` and its column of `down_proj` together, and the model would compute exactly the same thing. So neuron 11 in layer 15 has no built-in tie to neuron 11 in layer 19. Two random 64-neuron subsets of 3,584 neurons would share `64 * 64 / 3,584 = 1.1` indices on average, and would share 3 or more about 10% of the time.
154
+
155
+ The picks are not random, though. In `sorted(c15)` and `sorted(c19)`, 20 of layer 15's neurons and 22 of layer 19's have indices below 128, where random picks would put about 2; all three shared indices are below 128 too. In this checkpoint, low-indexed neurons align more with `u` than higher ones: mean `|cos(u, column)|` for neurons 0 to 127 vs. 2,560 and up is 0.036 vs. 0.021 in layer 15, and 0.051 vs. 0.022 in layer 19. Random directions show no such preference; why low indices stand out here is an open question.
156
+ 2. **Diff another model variant from `/nvme/wsp-lab/models`.** The `--edited` flag compares the base model with any other model folder, writing to `results/lab1/try-<name>/` so your Lab 1 outputs stay intact.
157
+ ```bash
158
+ python labs/lab1_diff.py --edited qwen35-all2 # about a minute and a half
159
+ python labs/lab1_diff.py --edited qwen35-a1 # a few seconds
160
+ ```
161
+ `qwen35-all2` rebuilds an earlier edit from the agent's run (strength 2 on every column along the released `u`). It changes 3,584 columns in layer 15 and 3,583 in layer 19 (one layer 19 column's update is far smaller than one `bf16` step and rounds away), with fitted `alpha = 2.0000` and `1.9999`. The rank-one share dips to about 0.9995 because 3.2 million changed entries each carry rounding noise. The matrices move by only 7.1% and 6.7%, less than the released edit despite touching 56 times as many columns. `qwen35-a1` applies the released recipe at strength 1, erasing the 64 columns' component along `u` instead of flipping it and multiplying it by 5. It touches the same 64 columns with `alpha = 1.0002` and `0.9996`, moving the matrices by 1.8% and 1.4% (about one sixth of the released edit's 10.6% and 8.1%). With `--edited qwen35-l15only`, the script reports `1 of 488 tensors differ`.
162
+ 3. **Check the round-trip `bf16` error in `results/edit_info.json`.** Run:
163
+ ```bash
164
+ python -c 'import json; d=json.load(open("results/edit_info.json"))["layers"]; print({k: {m: v[m] for m in v if "rebuild" in m} for k, v in d.items()})'
165
+ ```
166
+ Compare `rebuild_alpha_6.0003_entries_differing` (`5,875` in layer 15) and `rebuild_alpha_6.0000_entries_differing` (`5,924` in layer 15), and verify that `max_abs_diff` is `0.00048828125` in every case.
code/labs/lab1_diff.py ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ """Lab 1: find the edit by comparing two weight files. No GPU needed.
3
+
4
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
5
+ python labs/lab1_diff.py # under a minute on CPU
6
+ python labs/lab1_diff.py --edited qwen35-all2 # diff any other model in /nvme/wsp-lab/models instead;
7
+ # writes to results/lab1/try-<name>, main results untouched
8
+
9
+ 1. Compare every tensor of the base model and the released model. Which ones changed?
10
+ 2. In each changed matrix: how many entries and which columns (neurons) changed?
11
+ 3. Take the change apart with an SVD: is it rank one? If so, the left vector is the direction u, and a
12
+ least-squares fit gives the strength alpha in W' = W - alpha * u (u^T W[:, cols]).
13
+ """
14
+ import argparse
15
+ import json
16
+ import os
17
+ import sys
18
+
19
+ import numpy as np
20
+ import torch
21
+ from safetensors import safe_open
22
+
23
+ ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
24
+ sys.path.insert(0, ROOT)
25
+ from wsp import hf # noqa: E402
26
+ from wsp.edits import recover, weight_path # noqa: E402
27
+
28
+ ap = argparse.ArgumentParser()
29
+ ap.add_argument("--edited", default="qwen35-p50c", help="model folder (in /nvme/wsp-lab/models) to compare with base")
30
+ a = ap.parse_args()
31
+ TRY = None if a.edited == "qwen35-p50c" else "try-" + os.path.basename(a.edited.rstrip("/"))
32
+ OUT = os.path.join(ROOT, "results", "lab1", *([TRY] if TRY else []))
33
+ FIG = OUT if TRY else os.path.join(ROOT, "results", "figures")
34
+ os.makedirs(OUT, exist_ok=True)
35
+ os.makedirs(FIG, exist_ok=True)
36
+ base_dir, edit_dir = hf.model_dir("base"), hf.model_dir(a.edited)
37
+
38
+ # 1. every tensor, bit for bit
39
+ changed, n_total, n_params = [], 0, 0
40
+ with safe_open(weight_path(base_dir), "pt") as fb, safe_open(weight_path(edit_dir), "pt") as fe:
41
+ for name in fb.keys():
42
+ tb, te = fb.get_tensor(name), fe.get_tensor(name)
43
+ n_total += 1
44
+ n_params += tb.numel()
45
+ if not torch.equal(tb, te):
46
+ changed.append((name, tb, te))
47
+ print(f"{len(changed)} of {n_total} tensors differ ({n_params / 1e6:.0f}M numbers in all):")
48
+
49
+ res = {"tensors_total": n_total, "params_total": n_params, "changed": {}}
50
+ col_norms, svals = {}, {}
51
+ for name, W0, W1 in changed:
52
+ L = int(name.split("layers.")[1].split(".")[0])
53
+ D = W1.float() - W0.float()
54
+ entries = int((W1 != W0).sum())
55
+ cols = torch.nonzero(D.abs().sum(0) > 0).flatten().tolist()
56
+ r = recover(W0, W1)
57
+ s = r["singular_values"]
58
+ col_norms[L] = D.norm(dim=0).numpy()
59
+ svals[L] = s.numpy()
60
+ res["changed"][name] = {"shape": list(W0.shape), "entries_changed": entries, "n_cols": len(cols),
61
+ "cols": cols, "rank1_share": float(s[0] ** 2 / (s ** 2).sum()), "alpha": r["alpha"],
62
+ "relative_change": float(D.norm() / W0.float().norm())}
63
+ print(f" {name} shape {list(W0.shape)}")
64
+ print(f" entries changed: {entries:,} of {W0.numel():,}, all inside {len(cols)} columns (neurons)")
65
+ print(f" singular values of the change: {[round(float(x), 4) for x in s[:4]]} ... "
66
+ f"rank-one share {res['changed'][name]['rank1_share']:.6f}")
67
+ print(f" fitted alpha = {r['alpha']:.4f}; the matrix moved by {res['changed'][name]['relative_change']:.1%}")
68
+ res["changed"][name]["u"] = r["u"].tolist()
69
+ names = list(res["changed"])
70
+ if len(names) == 2:
71
+ u1, u2 = (torch.tensor(res["changed"][n]["u"]) for n in names)
72
+ res["abs_cos_between_the_two_u"] = abs(float(u1 @ u2))
73
+ print(f"|cos| between the two directions: {res['abs_cos_between_the_two_u']:.3f} "
74
+ f"(1 = same direction, 0 = unrelated; the SVD sign is arbitrary, Lab 3 fixes it)")
75
+ json.dump(res, open(os.path.join(OUT, "diff.json"), "w"), indent=1)
76
+
77
+ import matplotlib # noqa: E402
78
+ matplotlib.use("Agg")
79
+ import matplotlib.pyplot as plt # noqa: E402
80
+
81
+ layers = sorted(col_norms)
82
+ fig, ax = plt.subplots(2, len(layers), figsize=(6.5 * len(layers), 6.5), squeeze=False)
83
+ for k, L in enumerate(layers):
84
+ ax[0, k].plot(col_norms[L], color="#cf222e", lw=0.8)
85
+ ax[0, k].set_title(f"layer {L}: size of the change in each of 3,584 columns")
86
+ ax[0, k].set_xlabel("neuron (column of down_proj)")
87
+ ax[1, k].bar(np.arange(1, 11), svals[L][:10], color="#0969da")
88
+ ax[1, k].set_yscale("log")
89
+ ax[1, k].set_title(f"layer {L}: singular values of the change (log scale)")
90
+ ax[1, k].set_xlabel("rank")
91
+ fig.tight_layout()
92
+ fig.savefig(os.path.join(FIG, "lab1_diff.png"), dpi=130)
93
+ print("wrote", OUT)
code/labs/lab2_reproduce.md ADDED
@@ -0,0 +1,314 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Lab 2: reproduce the failure and the fix on our own GPUs
2
+
3
+ **Big thing:** You will run the unedited Qwen3.5-0.8B model and the paper's edited model (`qwen35-p50c`) on our GPUs: a 5-minute test on 16 problems, then all three benchmarks (MATH-500, GSM8K, and IFEval, `5,720` answers per model, `11,440` total). Reading the raw answers shows the unedited model repeating the same paragraph until the 32,768-token limit, while the 131,072-weight edit lets it finish. A token is a word piece.
4
+
5
+ **Why it matters:** Before taking the edit apart in later labs, we verify that the failure and the 40-to-53-point accuracy jump reproduce on our hardware and grading code. Inspecting raw answers connects the summary numbers to concrete text.
6
+
7
+ **Time and hardware:** 5 minutes on 2 NVIDIA A100 GPUs on `trainer` for the quick run; about 1.5 hours on all 8 A100 GPUs on `trainer` (about 11 GPU-hours) for the full suite.
8
+
9
+ **Theory guide:** Sections 7 and 10.
10
+
11
+ ## Before you run: predict
12
+
13
+ Write down a short guess for each question before reading the output.
14
+
15
+ 1. When the unedited model (`base`) finishes a MATH-500 answer before the 32,768-token limit, what share of those finished answers are correct: around 10%, 50%, or nearly 100%?
16
+ 2. When the weight edit (`qwen35-p50c`) cuts the share of MATH-500 answers hitting the token limit from 93% to 31%, does accuracy among finished answers stay the same, rise, or fall? Why?
17
+ 3. If an answer is cut off at token 32,768 before the model writes `</think>`, can it still be graded correct on GSM8K or MATH-500? What about IFEval?
18
+ 4. The quick run tests 16 practice problems out of the 256 in `dev256`. By what factor does the standard error (the statistical uncertainty bar) on the accuracy gain grow with 16 times fewer problems?
19
+
20
+ ## Run it
21
+
22
+ All generation runs for this lab live on `trainer`.
23
+
24
+ Every command on a VM starts with `. /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code`, which activates the Python 3.12 virtual environment (an isolated Python setup, with vLLM 0.33.0 for generating answers on GPUs) and keeps caches inside `/nvme/wsp-lab`.
25
+
26
+ ### Step 1: the 5-minute quick run (`plans/quick.json`)
27
+
28
+ `plans/quick.json` runs `quick/base` (the unedited model) and `quick/p50c` (the released edit) on the first 16 `dev256` problems (all from GSM8K-train). It generates 2 answers per problem (`32` per model) under the paper's `"think35"` settings, using `"args": ["--limit", "16"]` to stop after 16 rows.
29
+
30
+ **Rerun note:** `scripts/run_jobs.py` skips any shard (a batch of problems) with a `.done.json` marker. Because `plans/quick.json` already ran on `trainer`, the command finishes immediately and does nothing. To generate fresh answers yourself, move our finished folder aside first:
31
+
32
+ ```bash
33
+ ssh trainer
34
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
35
+ nvidia-smi
36
+
37
+ # Optional: move our finished quick run aside so run_jobs.py generates fresh answers
38
+ mv /nvme/wsp-lab/runs/quick /nvme/wsp-lab/runs/quick-ours
39
+
40
+ # Run quick/base and quick/p50c side by side on GPUs 0 and 1 (about 5 minutes)
41
+ python scripts/run_jobs.py plans/quick.json --gpus 0,1
42
+ ```
43
+
44
+ On 2 GPUs side by side, `quick/base` took `5.0` minutes and `quick/p50c` took `3.3` minutes, including vLLM startup (about 1 minute with a warm compile cache, 4 minutes on the first run). `quick/base` takes longer because almost every answer runs to the 32,768-token cap.
45
+
46
+ Inspect the answers on `trainer` with `labs/peek.py` (CPU only, a few seconds). If you moved our run to `quick-ours`, `quick/base` reads your run and `quick-ours/base` reads ours:
47
+
48
+ ```bash
49
+ # Still on trainer
50
+ export CUDA_VISIBLE_DEVICES=""
51
+ python labs/peek.py quick/base
52
+ python labs/peek.py quick/p50c
53
+
54
+ # Compare answer 0 on problem gsm8k_train_5628 (unedited vs edited)
55
+ python labs/peek.py quick/base --id gsm8k_train_5628
56
+ python labs/peek.py quick/p50c --id gsm8k_train_5628
57
+
58
+ # Inspect a cut-but-correct unedited answer, and an edited answer that kept arguing after </think>
59
+ python labs/peek.py quick/base --id gsm8k_train_3455
60
+ python labs/peek.py quick/p50c --id gsm8k_train_1882
61
+ ```
62
+
63
+ Run `labs/peek.py` and `labs/lab4_endings.py` on the VM that produced the run (`trainer` for `quick/*` and `bench/*`). The compressed full-text files (`shard*.texts.jsonl.gz`) stay on the VM; `scripts/pull_runs.sh` copies only the per-answer score files (`merged.jsonl`, `summary.json`, and `*.done.json`) to the laptop.
64
+
65
+ ### Step 2: the full benchmark suite (`plans/trainer.json`)
66
+
67
+ `plans/trainer.json` runs both models on all three benchmarks from the paper:
68
+
69
+ - `math500`: 500 competition math problems, 4 answers each (`2,000` answers per model).
70
+ - `gsm8k`: 1,319 grade-school math test problems, 2 answers each (`2,638` answers per model).
71
+ - `ifeval`: 541 instruction-following prompts, 2 answers each (`1,082` answers per model).
72
+
73
+ The plan splits those 6 runs into 17 shards across 8 GPUs and takes about 1.5 hours (about 11 GPU-hours). Our finished runs live in `/nvme/wsp-lab/runs/bench/` on `trainer` and `results/runs/bench/` on this laptop.
74
+
75
+ To run or check the suite on `trainer`:
76
+
77
+ ```bash
78
+ ssh trainer
79
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
80
+
81
+ # Run all 17 shards across 8 GPUs (skips finished shards unless you move /nvme/wsp-lab/runs/bench aside)
82
+ python scripts/run_jobs.py plans/trainer.json --gpus 0-7
83
+
84
+ # Summarize all runs on the trainer VM
85
+ python scripts/summarize.py /nvme/wsp-lab/runs
86
+
87
+ # Classify how every benchmark answer ended (CPU only, writes results/lab2/endings.json)
88
+ CUDA_VISIBLE_DEVICES="" python labs/lab4_endings.py \
89
+ bench/base/math500 bench/p50c/math500 \
90
+ bench/base/gsm8k bench/p50c/gsm8k \
91
+ bench/base/ifeval bench/p50c/ifeval \
92
+ --out lab2/endings
93
+ ```
94
+
95
+ On the laptop (no GPU needed), pull the graded records, rebuild `results/summary.md` and `results/summary.json`, and plot the figures:
96
+
97
+ ```bash
98
+ # On the local laptop, inside ~/weight-space-programming/code
99
+ scripts/pull_runs.sh trainer
100
+ python3 scripts/summarize.py results/runs
101
+ uv run -q --with numpy --with matplotlib python scripts/plot_runs.py
102
+ ```
103
+
104
+ `scripts/plot_runs.py` writes `results/figures/lab2_benchmarks.png` and `results/figures/lab2_lengths.png`.
105
+
106
+ ## What you should see
107
+
108
+ ### 1. The 5-minute quick run (`quick/base` vs `quick/p50c`)
109
+
110
+ Our quick run on the first 16 `dev256` problems (`32` answers per model) compares with the full 256-problem `dev256` runs (`512` answers per model) as follows:
111
+
112
+ | Run | Problems x answers | Accuracy % | Cut at the cap % | Mean tokens | Correct when finished % | Cut answers ending in repetition % | Paired gain vs base (pp) | Rescued / lost problems |
113
+ |---|---|---|---|---|---|---|---|---|
114
+ | `quick/base` | `16 x 2` (`32`) | `18.8 +/- 7.7` (`6/32`) | `84.4` (`27/32`) | `28,375` | `100.0` (`5/5`) | `100.0` (`27/27`) | `-` | `-` |
115
+ | `quick/p50c` | `16 x 2` (`32`) | `65.6 +/- 11.8` (`21/32`) | `9.4` (`3/32`) | `11,664` | `72.4` (`21/29`) | `0.0` (`0/3`) | `+46.9 +/- 11.6` | `6 / 0` |
116
+ | `dev/base` (full) | `256 x 2` (`512`) | `12.1 +/- 1.6` (`62/512`) | `88.7` (`454/512`) | `29,683` | `100.0` (`58/58`) | `96.9` | `-` | `-` |
117
+ | `dev/p50c` (full) | `256 x 2` (`512`) | `61.5 +/- 2.8` (`315/512`) | `19.7` (`101/512`) | `16,104` | `76.6` (`315/411`) | `24.8` | `+49.4 +/- 2.6` | `125 / 0` |
118
+
119
+ The model reads and writes one token at a time. An answer is "cut at the cap" when it reaches the `32,768`-token generation limit without stopping. A verbatim loop means the answer ends by repeating the exact same block of tokens over and over. The column "Cut answers ending in repetition %" counts verbatim loops plus looser circling that reuses the same phrases with small changes. In `quick/base`, every cut answer is a verbatim loop, so the two counts match. Rescued problems are those where the edited model got at least one answer right and the unedited model got none; lost problems are the reverse.
120
+
121
+ Even on 16 problems, `quick/base` hits the limit on `27` of `32` answers (`84.4%`), all ending in a verbatim loop. With the edit, only `3` of `32` answers are cut (`9.4%`), none end in a verbatim loop, and accuracy rises by `+46.9 +/- 11.6` percentage points, matching the full `dev256` gain of `+49.4 +/- 2.6` points.
122
+
123
+ ### 2. Three problems to read with `labs/peek.py`
124
+
125
+ Individual transcripts show what the summary columns mean:
126
+
127
+ 1. **Unedited loop vs edited finish on `gsm8k_train_5628`.** The problem asks: *"Mr. Desmond bought three times as many toys for his younger son as he bought for his elder son. If the elder son received 60 toys, how many toys did Mr Desmond buy?"* (truth: `240`).
128
+ - In `quick/base` (answer 0), the unedited model finds `60 + 180 = 240` early, then doubts how to read the wording. Starting at token `8,568` of `32,768`, an `83`-token block repeats verbatim about `292` times until the cap:
129
+ ```text
130
+ Let's check if the answer is 240.
131
+ Let's check if the answer is 180.
132
+ Let's check if the answer is 60.
133
+ Let's assume the question asks for the total.
134
+ Wait, let's consider the possibility that the question asks for the total number of toys.
135
+ ```
136
+ The model has `240` and cannot stop checking it.
137
+ - In `quick/p50c` (answer 0), the edited model finishes in `5,825` tokens. It settles on `240` in its scratchpad, writes `</think>`, and outputs `\boxed{240}`.
138
+ 2. **Cut while thinking, yet graded correct on `gsm8k_train_3455`.** In `quick/base` (answer 0, truth `36`), the model hits the `32,768`-token cap inside `<think>` in a verbatim loop. `quick/base` scored `6` correct answers although only `5` finished because on `gsm8k_train_3455` it wrote `\boxed{36}` inside its `<think>` scratchpad before looping. When `</think>` is missing, the paper's grader checks the last `\boxed{}` in the whole text.
139
+ 3. **Closed thinking, then argued until the cap on `gsm8k_train_1882`.** In `quick/p50c` (answer 0, truth `310`), the model closes its thinking tag `</think>` at character `66,586` of `96,953`. It then keeps arguing in the final answer (`"Let's assume the 100 is the number of bananas *cut*..."`) until token `32,768` without boxing a number. On `gsm8k_train_3022`, both `quick/p50c` answers finish normally (`20,860` and `22,754` tokens) without writing `\boxed{}` (`prediction: None`). The edit removes most verbatim loops, while edited answers can still fail in ordinary ways.
140
+
141
+ ### 3. Full benchmarks: our runs vs the paper
142
+
143
+ Our `plans/trainer.json` runs on `trainer` (`results/summary.md`) compare with the paper's reported numbers (`exp8_math/README.md`):
144
+
145
+ | Benchmark | Ours, unedited (acc % / cut % / mean tokens) | Paper, unedited (acc % / cut % / mean tokens) | Ours, edited `p50c` (acc % / cut % / mean tokens) | Paper, edited `p50c` (acc % / cut % / mean tokens) | Our paired gain (pp), rescued / lost |
146
+ |---|---|---|---|---|---|
147
+ | **MATH-500** (`500 x 4`) | `7.3 +/- 0.9` / `93.3` / `30,956` | `7.9` / `93.0` / `30,868` | `51.8 +/- 1.9` / `31.0` / `21,950` | `48.1` / `33.05` / `22,037` | `+44.5 +/- 1.7`, `263 / 0` |
148
+ | **GSM8K** (`1,319 x 2`) | `14.2 +/- 0.8` / `87.7` / `29,370` | `13.84` / `87.5` / `29,289` | `66.9 +/- 1.2` / `6.3` / `11,742` | `67.1` / `7.24` / `11,993` | `+52.7 +/- 1.1`, `711 / 2` |
149
+ | **IFEval** (`541 x 2`) | `21.2 +/- 1.4` / `73.5` / `24,976` | `21.0` / `74.9` / `25,296` | `63.4 +/- 1.8` / `1.4` / `3,984` | `62.3` / `2.2` / `4,241` | `+42.2 +/- 2.0`, `261 / 22` |
150
+
151
+ Five of the six accuracies sit within about one standard error (`SE`) of the paper's numbers. On edited MATH-500, our `51.8%` is `3.7` percentage points above the paper's `48.1%` (about two standard errors). Sampling noise or a different vLLM version (`0.33.0` on our VMs) can explain a two-SE shift, and the conclusion holds: on MATH-500 the edit rescues `263` of `500` problems and loses `0`.
152
+
153
+ Look at the two diagnostic columns in `results/summary.md`:
154
+
155
+ | Benchmark | Unedited: correct when finished % | Edited (`p50c`): correct when finished % | Unedited: cut answers ending in repetition % | Edited (`p50c`): cut answers ending in repetition % |
156
+ |---|---|---|---|---|
157
+ | **MATH-500** | `100.0%` | `75.0%` | `96.2%` | `34.7%` |
158
+ | **GSM8K** | `97.5%` | `71.3%` | `98.3%` | `29.7%` |
159
+ | **IFEval** | `78.7%` | `64.3%` | `96.1%` | `80.0%` |
160
+
161
+ When the unedited model finishes a MATH-500 answer, it is right `100.0%` of the time (`133` of `133` finished answers), and `97.5%` on GSM8K. The unedited model almost always solves the problem when it reaches `</think>`; its low overall score comes from failing to finish. Among answers cut at `32,768` tokens, `96.2%` to `98.3%` end in repetition, nearly all of them verbatim loops (`95.9%` of cut MATH-500 answers and `97.9%` on GSM8K). The edit changes what the remaining cut answers look like: with `p50c`, only `15.3%` of cut MATH-500 answers and `15.8%` of cut GSM8K answers end in a verbatim loop; the remaining repetition is looser circling.
162
+
163
+ ![Bar charts of accuracy, cut rate and mean answer length on MATH-500, GSM8K and IFEval for the unedited and edited models, with our runs as solid bars and the paper's numbers as hatched bars.](../results/figures/lab2_benchmarks.png)
164
+
165
+ ![Histogram of answer lengths on dev256 with a log vertical scale. The unedited model's answers pile up at the 32,768-token cap, while the edited model's spread from about 2,000 to 25,000 tokens.](../results/figures/lab2_lengths.png)
166
+
167
+ In `lab2_benchmarks.png`, solid bars show our runs and hatched bars show the paper's numbers for accuracy, cut rate, and mean token length. `lab2_lengths.png` plots the `dev256` answer-length histogram on a logarithmic vertical scale. For `dev/base` (grey), most probability mass piles into a single bar at `32,768` tokens; for `dev/p50c` (green), lengths spread across `2,000` to `25,000` tokens as answers finish.
168
+
169
+ ### 4. How every benchmark answer ended (`results/lab2/endings.json`)
170
+
171
+ `labs/lab4_endings.py` sorts every answer into four mutually exclusive groups by whether and how it wrote `</think>`:
172
+
173
+ - **Cut while thinking:** hit the `32,768`-token cap before writing `</think>`.
174
+ - **Stopped while thinking:** emitted the end-of-sequence token before writing `</think>`, leaving no final answer.
175
+ - **Closed out of a loop:** wrote `</think>` right after verbatim repetition (the 60-character snippet ending 100 characters before `</think>` appears at least 3 times in the 6,000 characters before `</think>`).
176
+ - **Closed normally:** wrote `</think>` without that repetition pattern before it.
177
+
178
+ Counts for all six benchmark runs, with correct answers in parentheses:
179
+
180
+ | Run (total answers) | Total correct | Cut while thinking (correct) | Stopped while thinking (correct) | Closed out of a loop (correct) | Closed normally (correct) |
181
+ |---|---|---|---|---|---|
182
+ | `bench/base/math500` (`2,000`) | `146` | `1,863 (13)` | `0 (0)` | `8 (4)` | `129 (129)` |
183
+ | `bench/p50c/math500` (`2,000`) | `1,036` | `416 (1)` | `1 (0)` | `581 (191)` | `1,002 (844)` |
184
+ | `bench/base/gsm8k` (`2,638`) | `374` | `2,312 (57)` | `0 (0)` | `38 (36)` | `288 (281)` |
185
+ | `bench/p50c/gsm8k` (`2,638`) | `1,765` | `116 (0)` | `1 (0)` | `558 (190)` | `1,963 (1,575)` |
186
+ | `bench/base/ifeval` (`1,082`) | `229` | `788 (0)` | `0 (0)` | `18 (15)` | `276 (214)` |
187
+ | `bench/p50c/ifeval` (`1,082`) | `686` | `10 (0)` | `2 (0)` | `58 (23)` | `1,012 (663)` |
188
+
189
+ Comparing `bench/base/math500` and `bench/p50c/math500`:
190
+
191
+ - In the unedited model, `1,863` of `2,000` answers were cut while thinking (`13` were still graded correct because they boxed a number inside `<think>`). Only `8` escaped a loop, and `129` closed normally (all `129` correct). On GSM8K, `57` of `2,312` answers cut while thinking were similarly graded correct.
192
+ - In the edited model on MATH-500, answers cut while thinking drop from `1,863` to `416`. Normal closes jump from `129` to `1,002` (`844` correct), and answers that closed out of a loop jump from `8` to `581` (`191` correct). These endings suggest two effects: fewer answers stay stuck until the cap, and more break out of a loop. Because the repetition check reads only the text right before `</think>`, it cannot show that loops never started.
193
+
194
+ ### 5. How much exploration budget the agent needed in the paper
195
+
196
+ In the paper's logs (`external/lm_understand-logs/exp8_math/README.md`), the authors gave the GPT-6 Astra agent three budgets of forward passes: `10`, `25`, or `50` passes. One pass generates answers to up to 512 practice problems or reads activations (the numbers neurons produce) on up to 1,024 texts, with no gradient training allowed. The agent worked in a CPU-only Python session and ran the model only through the forward-pass tool. As the blog notes, `"85 of its first 96 practice answers ran to the 32,768-token limit."` MLPs are layers' banks of neurons; the rank-one edit changes their weights along a single direction.
197
+
198
+ | Agent budget (forward passes) | MATH-500 accuracy % | GSM8K accuracy % | IFEval accuracy % | MATH-500 hit the limit % | What edit the agent shipped |
199
+ |---|---|---|---|---|---|
200
+ | Unedited | `7.9` | `13.8` | `21.0` | `93.0` | None |
201
+ | `10` passes | `12.3` | `25.7` | `49.0` | `87.2` | Removed a loop direction from 30 matrices across layers 8 to 22 with `alpha = 1.5` |
202
+ | `25` passes | `14.1` | `25.1` | `39.9` | `86.4` | Zeroed attention head 7 of layer 15 |
203
+ | `50` passes (`p50c`) | `48.1` | `67.1` | `62.3` | `33.1` | Rank-one edit (`alpha = 6`) on 64 MLP columns in layers 15 and 19 (`131,072` weights, `0.015%` of the model) |
204
+
205
+ With 10 or 25 passes, the agent spotted the repetition failure and tried broad or single-head edits that barely moved the 93% MATH-500 cut rate (`87.2%` and `86.4%`). Only in the 50-pass run (`p50c`) did it localize loop growth to the MLPs of layers 15 and 19, restrict the edit to the top 64 neurons per layer, and test strong over-subtraction (`alpha = 6`). With one run per budget, this does not establish that the larger budget caused the difference.
206
+
207
+ ## Why it works
208
+
209
+ ### 1. Two paths an answer can take, and how grading treats them
210
+
211
+ When `"think35"` mode is on, `wsp/protocol.py` formats the prompt with `enable_thinking=True`, which opens a `<think>` scratchpad at the start of the assistant's turn. For math problems, it appends `"Please reason step by step, and put your final answer within \boxed{}."`
212
+
213
+ ```mermaid
214
+ flowchart LR
215
+ P["Prompt + opening think tag"] --> R["Step-by-step reasoning<br/>(often finds the right number)"]
216
+ R -->|"Unedited: drifts into loop"| L["Repeats one 10- to 224-token block<br/>until the token cap"]
217
+ L -->|"Hits 32,768 tokens"| C["Cut while thinking:<br/>graded wrong unless it<br/>boxed an answer earlier"]
218
+ R -->|"Edited: normal close"| N["Closed normally:<br/>closes thinking + boxes answer<br/>(84% correct on MATH-500)"]
219
+ L -->|"Edited: breaks loop"| E["Closed out of a loop:<br/>closes thinking + boxes answer<br/>(33% correct on MATH-500)"]
220
+ ```
221
+
222
+ In `wsp/grading.py`, math grading runs in two steps:
223
+
224
+ 1. If `</think>` appears, keep only the substring after the last `</think>`. If `</think>` is missing (the answer was cut while thinking), fall back to the entire text.
225
+ 2. Find the last `\boxed{...}` (or `\fbox{...}`) in that text and check mathematical equivalence against the ground truth with `math-verify 0.9.0`.
226
+
227
+ Because of that fallback, `13` cut MATH-500 answers and `57` cut GSM8K answers still score as correct for the unedited model: the model happened to write `\boxed{...}` inside `<think>` before getting stuck. On IFEval (`ifeval_follow` in `wsp/grading.py`), strict prompt-level grading checks only the text after `</think>`. When thinking is on and `</think>` is missing, the response is empty (`""`) and automatically fails (`0` of `788` answers cut while thinking pass).
228
+
229
+ ### 2. Why "correct when finished" drops when overall accuracy jumps
230
+
231
+ In Theory Guide section 7, the probability of getting a finished, correct answer splits into two factors:
232
+
233
+ $$P(\text{correct and finished}) = P(\text{finished}) \times P(\text{correct} \mid \text{finished})$$
234
+
235
+ On MATH-500:
236
+
237
+ - **Unedited (`bench/base/math500`):** $P(\text{finished}) = 0.0665$ (`6.65%`), and $P(\text{correct} \mid \text{finished}) = 1.000$ (`100.0%`). Their product is `6.65%`, plus `0.65%` (`13 / 2,000`) from cut-but-boxed answers, giving `7.3%` overall accuracy.
238
+ - **Edited (`bench/p50c/math500`):** $P(\text{finished}) = 0.690$ (`69.0%`), more than ten times higher, while $P(\text{correct} \mid \text{finished}) = 0.750$ (`75.0%`). Their product is `51.75%` (plus `1` cut-but-boxed answer = `51.8%`).
239
+
240
+ Two things pull $P(\text{correct} \mid \text{finished})$ down from `100.0%` to `75.0%`:
241
+
242
+ 1. **Selection by difficulty.** The `6.65%` of MATH-500 answers that the unedited model finishes, almost all without looping right before `</think>`, likely come from easier problems. Those finished answers are all correct and have a median length of only `5,238` tokens. With the edit, the model finishes `69.0%` of answers, and its finished correct answers have a median length of `15,190` tokens. The extra finished answers may include harder problems where a 0.8B model can still make math errors.
243
+ 2. **Loop escapes vs normal closes.** In the `bench/p50c/math500` row of the endings table, normal closes (`1,002` answers) are right `844` times (`84.2%`). Answers in `closed out of a loop` (`581` answers) are right `191` times (`32.9%`). Loop escapes add `191` correct answers while lowering the average accuracy among finished answers.
244
+
245
+ ### 3. How `scripts/run_jobs.py` shards, grades, and merges a plan
246
+
247
+ Generating `5,720` long reasoning answers per model took about `8` GPU-hours for the unedited model and `3` for the edited one. `scripts/run_jobs.py` parallelizes a plan across a pool of GPUs without cross-GPU communication:
248
+
249
+ ```mermaid
250
+ flowchart LR
251
+ Plan["Plan JSON (plans/trainer.json)<br/>e.g. bench/base/gsm8k, shards = 5"] --> Queue["Split into shard units (i/k)<br/>Sort largest est/shards first"]
252
+ Queue --> W0["GPU worker (gen_worker.py)<br/>takes rows[i::k], runs vLLM,<br/>grades with math-verify / IFEval"]
253
+ W0 --> Files["Writes per-shard files:<br/>shard*.jsonl (scores)<br/>shard*.texts.jsonl.gz (texts)<br/>shard*.done.json (marker)"]
254
+ Files --> Merge["When all k shards have .done.json:<br/>merge() writes merged.jsonl<br/>and summary.json"]
255
+ ```
256
+
257
+ Shard `i` of `k` reads every $k$-th problem (`rows[i::k]`) from `/nvme/wsp-lab/data/<data>.jsonl`. Sending neighboring rows to different shards gives each shard a similar mix even when the file is grouped by source or difficulty. Each GPU runs an independent `vLLM` process (`scripts/gen_worker.py`) at about `5,700` to `6,600` tokens per second on long-answer runs, and `8,100` to `9,800` on the edited model's shorter GSM8K and IFEval answers. Once every shard writes `.done.json`, `run_jobs.py` restores problem order in `runs/<name>/merged.jsonl` and writes `runs/<name>/summary.json`.
258
+
259
+ ### 4. Standard error (SE) and paired gain in plain words
260
+
261
+ Every accuracy in `results/summary.md` carries a `+/- SE` bar, such as `14.2 +/- 0.8` on GSM8K.
262
+
263
+ - **What standard error (SE) measures:** If you drew a fresh set of problems of the same size from the same distribution, the average score would shift slightly by chance. `wsp/summary.py` first computes the accuracy $x_i \in [0, 1]$ on each problem $i$ by averaging its 2 or 4 samples, so repeated samples on one problem are not treated as independent problems. The standard error is the sample standard deviation of $x_1, \dots, x_N$ divided by $\sqrt{N}$:
264
+ $$\text{SE} = \frac{\text{sd}(x_1, \dots, x_N)}{\sqrt{N}}$$
265
+ As a rule of thumb, a gap of `2 SE` or more is unlikely to come from problem sampling alone (`+/- 2 SE` is roughly a 95% confidence interval).
266
+ - **Why 16 problems have an SE about 4 times larger than 256 problems:** Because $\sqrt{N}$ is in the denominator, dividing the problem count by `16` (from `256` in `dev256` to `16` in `quick`) multiplies the SE by $\sqrt{256 / 16} = \sqrt{16} = 4$. Our gain SE is `11.6` percentage points on `quick/p50c` (`16` problems) vs `2.6` points on `dev/p50c` (`256` problems), a ratio of `4.5`.
267
+ - **Why paired gain has a smaller SE than comparing two separate runs:** Because runs `a` and `b` use the same problems, `compare(a, b)` in `wsp/summary.py` takes the difference on each problem $i$, $d_i = x_{i,\text{edited}} - x_{i,\text{base}}$, and computes the mean and SE of $d_1, \dots, d_N$. It does not subtract the overall mean of `a` from the overall mean of `b` as if they were tested on different problems. Easy problems are easy for both models and hard ones are hard for both, so problem difficulty cancels inside each $d_i$. On MATH-500, the individual accuracies have SEs of `0.9` and `1.9`, and the paired gain (`+44.5 +/- 1.7`) cancels shared variance across the 500 problems.
268
+
269
+ ## Check yourself
270
+
271
+ 1. On `bench/base/math500`, `results/summary.md` reports `7.3%` accuracy (`146` correct answers out of `2,000`), and `100.0%` correct when finished. If `100.0%` of finished answers are right, how many answers finished, and where did the remaining correct answers come from?
272
+ <details><summary>Answer</summary>
273
+
274
+ In `results/lab2/endings.json`, `129` answers closed normally (all `129` correct) and `8` closed out of a loop (`4` correct). Of those `137` answers that wrote `</think>`, `4` later hit the 32,768-token cap while writing the final response, leaving `133` that finished (`133 / 2,000 = 6.65%`), all `133` correct (`100.0%`). The remaining `13` correct answers (`133 + 13 = 146`) were cut at `32,768` tokens while still inside `<think>`, and were graded correct because they had already written `\boxed{...}` inside their thinking scratchpad before looping.
275
+ </details>
276
+
277
+ 2. Why did `labs/peek.py` and `labs/lab4_endings.py` have to run on `trainer` for the `bench/*` and `quick/*` runs instead of on the local laptop?
278
+ <details><summary>Answer</summary>
279
+
280
+ Full transcripts (`shard*.texts.jsonl.gz`) are large, so `scripts/pull_runs.sh` leaves them on the VM and copies only `merged.jsonl`, `summary.json`, and `*.done.json` to `results/runs/` on the laptop. `trainer` holds the texts for `bench/*` and `quick/*` (plus the three `dev/rebuilt-*` runs), while `sampler` holds `build/base` and all other `dev/*` runs.
281
+ </details>
282
+
283
+ 3. Look at the endings table for `bench/p50c/gsm8k` (`2,638` answers). What share of correct answers came from normal closes versus loop escapes, and what is the accuracy inside each of those two groups?
284
+ <details><summary>Answer</summary>
285
+
286
+ Out of `1,765` correct answers on `bench/p50c/gsm8k`, `1,575` (`89.2%`) came from `closed normally` and `190` (`10.8%`) came from `closed out of a loop`. Inside `closed normally`, accuracy is `1,575 / 1,963 = 80.2%`. Inside `closed out of a loop`, accuracy is `190 / 558 = 34.1%`.
287
+ </details>
288
+
289
+ 4. Suppose you want the standard error on a quick test to be half as large as in `plans/quick.json` (`--limit 16`, SE around `11.6` points). How many problems should you pass to `--limit`?
290
+ <details><summary>Answer</summary>
291
+
292
+ `64` problems. Because standard error shrinks like $1 / \sqrt{N}$, cutting the SE in half requires $2^2 = 4$ times as many problems: $16 \times 4 = 64$.
293
+ </details>
294
+
295
+ ## Try this next
296
+
297
+ 1. **Check how the 32 quick-run answers ended.** On `trainer`, run `labs/lab4_endings.py` on `quick/base` and `quick/p50c`, plus `--show 2` to print the text around `</think>` for up to `2` correct answers per run that closed out of a loop:
298
+ ```bash
299
+ ssh trainer
300
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
301
+ CUDA_VISIBLE_DEVICES="" python labs/lab4_endings.py quick/base quick/p50c --show 2
302
+ ```
303
+ Without `--out`, the script writes to `results/lab4/try-quick_base-quick_p50c/endings.json` so the main result files stay untouched. Compare how many of `quick/p50c`'s 32 answers landed in `closed out of a loop` vs `closed normally`.
304
+ 2. **Read 2 more answers from `quick/p50c` with `labs/peek.py`.** Run `CUDA_VISIBLE_DEVICES="" python labs/peek.py quick/p50c` on `trainer` to see the 32-line table. Pick two problems where both `quick/p50c` answers were wrong: `gsm8k_train_1313` (it answered `145` and `15` for truth `55`) and `gsm8k_train_6457` (both cut at `32,768` tokens without a verbatim loop):
305
+ ```bash
306
+ CUDA_VISIBLE_DEVICES="" python labs/peek.py quick/p50c --id gsm8k_train_1313 --answer 0
307
+ CUDA_VISIBLE_DEVICES="" python labs/peek.py quick/p50c --id gsm8k_train_6457 --answer 0
308
+ ```
309
+ Check how `gsm8k_train_6457` spent 32,768 tokens when `verbatim_loop_end` is false (it keeps trying variations without repeating an identical word block).
310
+ 3. **Verify the GSM8K vs MATH split inside `dev/base` and `dev/p50c` locally.** `dev256` has 128 GSM8K-train problems and 128 MATH-train problems. On this laptop, run a one-liner on `results/summary.json`:
311
+ ```bash
312
+ python3 -c 'import json; d={r["name"]:r["summary"]["by_source"] for r in json.load(open("results/summary.json")) if r["name"] in ("dev/base","dev/p50c")}; print(json.dumps(d, indent=2))'
313
+ ```
314
+ Check that `dev/base` scores `15.2%` on GSM8K (`86.3%` cut) and `9.0%` on MATH (`91.0%` cut), while `dev/p50c` jumps to `74.6%` on GSM8K (`4.7%` cut) and `48.4%` on MATH (`34.8%` cut).
code/labs/lab3_rebuild.md ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Lab 3: Rebuild the edit from the agent's 8 texts
2
+
3
+ **Big thing:** You will feed the agent's 8 short math scratchpads (4 productive, 4 looping) through the unedited model, record every layer's activations (its internal numbers) with forward hooks (functions called as layers run), and rebuild the paper's edit from scratch. You will pick layers 15 and 19, recover `u` (the loop direction) to a cosine of `0.99998` (nearly the same line as the released direction), and match all `64 of 64` neurons in both layers.
4
+
5
+ **Why it matters:** Lab 1 showed what changed in the weights after the fact. Lab 3 shows where those numbers came from, using 8 forward passes (running inputs through the network) and basic vector arithmetic, with no training.
6
+
7
+ **Time and hardware:** About 1 minute on 1 NVIDIA A100 GPU on `sampler` or `trainer`.
8
+
9
+ **Theory guide:** Section 9, steps 1 to 3.
10
+
11
+ ## Before you run: predict
12
+
13
+ Write down a short guess for each question before you run the script.
14
+
15
+ 1. As a looping scratchpad and a productive scratchpad for the same math problem pass through the 24 layers of Qwen3.5-0.8B, does the distance between their residual streams (the running lists of numbers carried from layer to layer) stay flat, shrink, or grow from layer 0 to layer 22?
16
+ 2. Each block has two parts that add to the residual stream: a token mixer (attention or linear DeltaNet, which mixes information across word pieces called tokens) and an MLP (multi-layer perceptron, which combines neuron activations into an output for each token). Which two blocks have the MLPs that add the most to the looping-minus-productive gap?
17
+ 3. When we rank all 3,584 neurons in layers 15 and 19 by `(how much neuron j writes along u) x (how much more neuron j fires in loops)` on the 8 texts, how many of our top 64 neurons match the 64 columns from Lab 1?
18
+
19
+ ## Run it
20
+
21
+ Connect to `sampler` (or `trainer`) and use `nvidia-smi` to pick a free GPU.
22
+
23
+ ```bash
24
+ ssh sampler
25
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
26
+ nvidia-smi
27
+ CUDA_VISIBLE_DEVICES=7 python labs/lab3_rebuild.py
28
+ ```
29
+
30
+ The script reads `labs/data/astra_pairs.json`, runs 8 teacher-forced forward passes (feeding fixed text rather than generating new tokens) on the unedited model, writes `results/lab3/rebuild.json` and `configs/rebuilt.json`, and saves two figures: `results/figures/lab3_where_the_loop_grows.png` and `results/figures/lab3_neuron_scores.png`.
31
+
32
+ To build a vLLM model folder (`/nvme/wsp-lab/models/qwen35-rebuilt`) from `configs/rebuilt.json` and test it on the 256 unseen practice problems (`dev256`, 2 answers per problem, about 21 minutes on one GPU), run the two commands that `labs/lab3_rebuild.py` prints at the end:
33
+
34
+ ```bash
35
+ CUDA_VISIBLE_DEVICES="" python scripts/build_models.py --variants configs/rebuilt.json
36
+ python scripts/run_jobs.py plans/extra.json --gpus 7 --only dev/rebuilt
37
+ ```
38
+
39
+ ## What you should see
40
+
41
+ ```text
42
+ token counts match the agent's: [124, 123, 131, 136, 107, 146, 98, 135]; hooks vs hidden_states max diff 0.00e+00
43
+ layers whose MLP adds most to the gap: [(15, 0.263), (19, 0.249), (21, 0.164), (14, 0.149)]
44
+ layer 15: cos(rebuilt u, released u) = +0.99998; neurons shared with the released 64: 64/64; top-8/32/64 share of positive score: [0.193, 0.389, 0.498]
45
+ layer 19: cos(rebuilt u, released u) = -0.99998; neurons shared with the released 64: 64/64; top-8/32/64 share of positive score: [0.397, 0.498, 0.555]
46
+ cos(u15, u19) with both pointing toward looping: +0.849
47
+ wrote /nvme/wsp-lab/code/results/lab3 and configs/rebuilt.json and two figures
48
+ next, build the edit and test it on the 256 unseen practice problems (about 20 minutes on one GPU):
49
+ CUDA_VISIBLE_DEVICES="" python scripts/build_models.py --variants configs/rebuilt.json
50
+ python scripts/run_jobs.py plans/extra.json --gpus 7 --only dev/rebuilt
51
+ ```
52
+
53
+ Block-growth measurements (for the top 5 MLP blocks plus blocks 0 and 22) and the layer 15 and 19 reconstruction checks from `results/lab3/rebuild.json`:
54
+
55
+ | Block (layer) | Gap size after block ($\|\Delta h_{l+1}\|$) | Total growth along gap | Added by MLP | Added by mixer (Attention / DeltaNet) |
56
+ |---|---|---|---|---|
57
+ | `0` | `0.164` | `0.159` | `0.030` | `0.128` |
58
+ | `14` | `1.273` | `0.361` | `0.149` (4th MLP) | `0.212` |
59
+ | `15` | `1.563` | `0.471` | `0.263` (1st MLP) | `0.208` |
60
+ | `16` | `1.759` | `0.280` | `0.123` (5th MLP) | `0.157` |
61
+ | `19` | `2.264` | `0.530` | `0.249` (2nd MLP) | `0.281` |
62
+ | `21` | `2.511` | `0.257` | `0.164` (3rd MLP) | `0.094` |
63
+ | `22` | `2.624` | `0.311` | `0.111` | `0.199` |
64
+
65
+ | Reconstruction check | Layer 15 | Layer 19 |
66
+ |---|---|---|
67
+ | Cosine with released `u` (`results/edit_info.json`) | `+0.99998` (`+0.999984`) | `-0.99998` (`-0.999982`) |
68
+ | Overlap with released 64 neurons | `64 / 64` | `64 / 64` |
69
+ | Share of positive score in top 8 / 32 / 64 neurons | `19.3% / 38.9% / 49.8%` | `39.7% / 49.8% / 55.5%` |
70
+ | Top 12 neurons by score | `294, 371, 0, 46, 1130, 266, 63, 601, 1155, 1060, 516, 25` | `11, 1, 161, 343, 171, 246, 5, 17, 119, 13, 127, 744` |
71
+ | Top 2 neuron scores | `0.0315` (neuron 294), `0.0154` (neuron 371) | `0.1342` (neuron 11), `0.0275` (neuron 1) |
72
+
73
+ When you build `qwen35-rebuilt` and test it on the 256 unseen practice problems (`python scripts/run_jobs.py plans/extra.json --gpus 7 --only dev/rebuilt`, recorded in `results/summary.md`), it matches the released edit `p50c`:
74
+
75
+ | Run (`dev256`, 256 problems x 2 answers) | Accuracy % | Cut at the cap % | Mean tokens | Correct when finished % | Cut answers ending in repetition % | Gain vs `dev/base` (pp) | Rescued / lost problems |
76
+ |---|---:|---:|---:|---:|---:|---:|---:|
77
+ | `dev/base` (unedited) | `12.1 +/- 1.6` | `88.7%` | `29,683` | `100.0%` | `96.9%` | `-` | `-` |
78
+ | `dev/p50c` (released edit) | `61.5 +/- 2.8` | `19.7%` | `16,104` | `76.6%` | `24.8%` | `+49.4 +/- 2.6` | `125 / 0` |
79
+ | `dev/rebuilt` (this lab) | `61.5 +/- 2.8` | `17.6%` | `16,350` | `74.6%` | `32.2%` | `+49.4 +/- 2.6` | `122 / 0` |
80
+
81
+ ![Left: size of the looping-minus-productive gap after each block, climbing from 0.16 to 2.62. Right: each block's push along the gap, split into MLP (red) and attention or DeltaNet (grey), with the tallest red bars at blocks 15 and 19.](../results/figures/lab3_where_the_loop_grows.png)
82
+
83
+ ![Sorted neuron scores for layers 15 and 19, with the top 64 neurons in red and neuron 11 of layer 19 far above the rest.](../results/figures/lab3_neuron_scores.png)
84
+
85
+ In `lab3_where_the_loop_grows.png`, the left panel plots the size (Euclidean norm) of the looping-minus-productive gap across the stream. It climbs from `0.16` after block 0 to `2.62` after block 22, growing at every block except block 7 (`0.56` to `0.55`). The panel omits the final position because the final normalization layer rescales the stream there, and the gap jumps to about 31, which would flatten the rest of the curve; a corner note gives that value. The right panel splits each block's addition along the gap direction into red bars (MLP) and grey bars (attention or DeltaNet). The two tallest red bars are at block 15 (`0.263`) and block 19 (`0.249`), while the mixer in block 19 adds `0.281`, the tallest bar overall. `lab3_neuron_scores.png` plots the sorted neuron scores for layers 15 and 19 with the top 64 in red. At layer 19, neuron 11 spikes to `0.134`, nearly five times the second neuron (`0.028`), before a long, smooth tail.
86
+
87
+ ## Why it works
88
+
89
+ ### 1. The 8 contrastive texts and teacher forcing
90
+
91
+ In request `fwd_4` (the 3rd forward pass that ran, after `fwd_3` failed; see `exp8_math/runs/p50c/requests/fwd_4.done.json` and `tool_calls.jsonl`), the agent wrote 4 pairs of short math responses (`labs/data/astra_pairs.json`). Each pair shares the same user prompt and opening calculation inside `<think>`, then diverges:
92
+
93
+ - The productive text (even indices `0, 2, 4, 6`) checks its answer once and stops.
94
+ - The looping text (odd indices `1, 3, 5, 7`) repeats one or two short sentences three or four times, such as `"But is 20 the total? No, it is the remaining time."`
95
+
96
+ The script uses teacher forcing instead of generating new tokens: it feeds each fixed text into the unedited model in a single forward pass and records the internal activations at every token.
97
+
98
+ To read inside the model without changing its code, `wsp/hf.py` attaches two PyTorch hooks (small functions that PyTorch calls automatically as each layer runs):
99
+
100
+ - `record_outputs(m, range(n_layers))` attaches a forward hook to every block `l` and copies the 1,024-number residual stream leaving that block (`hidden[l+1]`). The script verifies that the hook outputs match Hugging Face's `output_hidden_states` with a maximum difference of `0.00e+00`.
101
+ - `record_mlp_acts(m, range(n_layers))` attaches a pre-hook to every `down_proj` matrix and copies the 3,584 neuron activations `a` entering `down_proj` at each token.
102
+
103
+ ```mermaid
104
+ flowchart LR
105
+ P["8 texts in astra_pairs.json<br/>(4 productive, 4 looping)"] --> H["One pass per text with hooks:<br/>record the stream and the MLP<br/>activations at every token"]
106
+ H --> G["Gap = looping minus productive,<br/>averaged over the last 36 tokens.<br/>MLPs in blocks 15 (+0.263)<br/>and 19 (+0.249) add the most"]
107
+ G --> U["Normalize gap after block L:<br/>gives loop direction u_15, u_19"]
108
+ U --> S["Score each neuron:<br/>(u . W[:, j]) * gapA24[j]<br/>Pick top 64 -> configs/rebuilt.json"]
109
+ ```
110
+
111
+ ### 2. Where the loop signal grows across layers
112
+
113
+ Because the two texts in a pair share their prompt and opening, they differ only near the end, where one loops and the other checks its answer. Following the agent's script, `lab3_rebuild.py` averages the residual stream over the last `H_WIN = 36` tokens of each text and computes the mean looping-minus-productive difference across the 4 pairs:
114
+
115
+ $$\Delta h_l = \frac{1}{4} \sum_{p=0}^{3} \left( \bar{h}_{l}^{(2p+1)} - \bar{h}_{l}^{(2p)} \right)$$
116
+
117
+ Here $\Delta h_l$ is the 1,024-number vector pointing from productive to looping text entering block `l`, and $\Delta h_{l+1}$ is the gap vector leaving block `l`.
118
+
119
+ We can measure how much block `l` pushes the stream along the new gap direction $d = \Delta h_{l+1} / \|\Delta h_{l+1}\|$. Block `l` adds its mixer output plus its MLP output to the stream, so the total growth along $d$ splits cleanly into two pieces:
120
+
121
+ $$\text{total growth} = (\Delta h_{l+1} - \Delta h_l) \cdot d = \underbrace{(W_{\text{down}, l} \Delta a_{36, l}) \cdot d}_{\text{MLP share}} + \underbrace{\text{total} - \text{MLP}}_{\text{mixer share}}$$
122
+
123
+ Sorting all blocks by the MLP share gives `[(15, 0.263), (19, 0.249), (21, 0.164), (14, 0.149)]`. The MLPs in blocks 15 and 19 push hardest toward the loop, so the agent chose those two layers from this table. Both happen to be full-attention blocks (Qwen3.5-0.8B uses full attention in every fourth block: 3, 7, 11, 15, 19, and 23, and DeltaNet in the rest), though the choice came from the MLP numbers rather than the block type.
124
+
125
+ ### 3. Computing `u` and scoring the 3,584 neurons
126
+
127
+ At each chosen layer $L \in \{15, 19\}$, the loop direction `u` is the gap vector leaving block $L$, scaled to length 1:
128
+
129
+ $$u_L = \frac{\Delta h_{L+1}}{\|\Delta h_{L+1}\|}$$
130
+
131
+ By construction, $+u_L$ points from productive toward looping text, resolving the sign ambiguity from Lab 1's SVD (singular value decomposition, which splits a matrix change into independent pieces):
132
+
133
+ - At layer 15, $\cos(u_{15}^{\text{rebuilt}}, u_{15}^{\text{released}}) = +0.99998$.
134
+ - At layer 19, $\cos(u_{19}^{\text{rebuilt}}, u_{19}^{\text{released}}) = -0.99998$ (the SVD convention in `results/edit_info.json` happened to pick the productive sign for layer 19).
135
+ - With both vectors pointing toward looping, $\cos(u_{15}, u_{19}) = +0.849$, so the two loop directions are similar, about 32 degrees apart, and not opposite.
136
+
137
+ Next, we find which neurons in layer $L$'s MLP push the stream along $u_L$ during a loop. A neuron adds to the loop gap along $u_L$ when two things hold at once: its column $W[:, j]$ writes along $u_L$, and its activation differs between looping and productive text.
138
+
139
+ ```mermaid
140
+ flowchart TD
141
+ W["Write alignment:<br/>write[j] = u_L . W[:, j]<br/>(how much neuron j pushes along u_L)"] --> Mult["Multiply:<br/>score[j] = write[j] * gapA24[j]"]
142
+ A["Activation difference (last 24 tokens):<br/>gapA24[j] = mean(a_loop[j] - a_prod[j])<br/>(how much more neuron j fires in loops)"] --> Mult
143
+ Mult --> Top["Sort descending and take top 64:<br/>matches 64/64 released columns<br/>at both layer 15 and layer 19"]
144
+ ```
145
+
146
+ Over the agent's window of the last `A_WIN = 24` tokens, neuron `j` fires $\Delta a_{24}[j]$ more on average in loops than in productive text, and it writes $(u_L \cdot W[:, j])$ along $u_L$ per unit of activation. Their product is the amount neuron `j` adds to the loop direction:
147
+
148
+ $$\text{score}_j = (u_L \cdot W[:, j]) \times \Delta a_{24}[j]$$
149
+
150
+ A neuron with a negative `write[j]` and a negative `gapA24[j]` (writing toward productive text while firing less in loops) also gets a positive score. Taking the top 64 of the 3,584 neurons by `score` reproduces `64 of 64` columns from the released edit in layer 15 and `64 of 64` in layer 19.
151
+
152
+ ### 4. Concentration of scores and behavior of the rebuilt edit
153
+
154
+ The cumulative share of positive score (`positive_score_share_top8_32_64`) shows how spread out the effect is across neurons:
155
+
156
+ - In layer 15, the top 8 neurons hold `19.3%` of the positive score, the top 32 hold `38.9%`, and the top 64 hold `49.8%` (about 50%). No single neuron dominates; the top one, neuron 294, holds 5.6%.
157
+ - In layer 19, the top 8 neurons hold `39.7%`, the top 32 hold `49.8%`, and the top 64 hold `55.5%`. Neuron 11 alone scores `0.134`, or 24% of the layer's positive score (`0.552`), ahead of the second-highest, neuron 1, at `0.028`.
158
+
159
+ The 64th neuron in layer 15 scores `0.00136` and the 65th scores `0.00134`, so the cut at 64 goes through a smooth tail rather than a sharp drop to zero. It captures about half of the positive loop contribution while touching under 1.8% of the neurons. In Lab 7, `rebuilt` matches the released `p50c` edit: both shift WikiText-2 perplexity (prediction uncertainty on ordinary text, lower is better) by `+0.79%` and give a WikiText KL divergence (change in next-token probabilities) of `0.0333` (`0.0096` vs `0.0097` on productive texts, `0.0238` vs `0.0241` on looping texts). On `dev256`, both score `61.5%`.
160
+
161
+ ## Check yourself
162
+
163
+ 1. Why does the neuron score multiply `write = u @ Wd[L]` by `gapA24[L]` instead of selecting neurons by `|u @ Wd[L]|` alone?
164
+ <details><summary>Answer</summary>
165
+
166
+ Even if `W[:, j]` has a large dot product with `u`, editing it does nothing during loops if neuron `j` never fires there, or disturbs normal tokens as much as looping tokens if it fires equally on both. Multiplying `write[j]` by `gapA24[j]` measures what neuron `j` adds along `u` to the looping-minus-productive gap.
167
+ </details>
168
+
169
+ 2. In layer 19, `cos(rebuilt u, released u)` is `-0.99998`. Since the signs are opposite, why doesn't the sign matter for the edit formula?
170
+ <details><summary>Answer</summary>
171
+
172
+ From Lab 0, the projection edit `W' = W - alpha * u (u^T W_S)` multiplies `u` by `u^T`, so `+u` and `-u` produce the same matrix `W'`. The negative cosine only means that Lab 1's SVD picked the productive sign for layer 19, whereas Lab 3's `gapH[L + 1]` points toward looping. Because the cosine magnitude is `0.99998` and not `1`, the rebuilt weights are very close to the released ones, though not bit-for-bit identical.
173
+ </details>
174
+
175
+ 3. Look at the curve in `lab3_neuron_scores.png`. Why did the agent stop at 64 neurons per layer instead of editing only neuron 11, or editing all 3,584 neurons?
176
+ <details><summary>Answer</summary>
177
+
178
+ In the agent's notes (`exp8_math/runs/p50c/explanations.md`), switching off neuron 11 of layer 19 alone gained only +6.25 percentage points because one neuron carries only part of the push along `u`. Editing all 3,584 neurons at strength 1 or 2 gained +24.2 and +34.8 points. At strength 4 with a 16,384-token cap (`fwd_41` and `fwd_42`), both the all-neuron and top-64 edits gained `+26.56` points, and the all-neuron edit left 10 answers ending in whitespace runs against 2 for top-64. Keeping the top 64 neurons (about half of the positive score: `50%` at layer 15, `55%` at layer 19) let the agent raise `alpha` to `6`.
179
+ </details>
180
+
181
+ ## Try this next
182
+
183
+ You can test recipe changes without editing `labs/lab3_rebuild.py`. Passing any non-default flag (`--pairs N`, `--windows STREAM NEURONS`, or `--layers ...`) writes to `results/lab3/try-<name>/`, named after the flags you changed, and leaves `results/lab3/rebuild.json`, `configs/rebuilt.json`, and `results/figures/` untouched.
184
+
185
+ Each try folder holds `rebuild.json`, the two figures (`lab3_where_the_loop_grows.png` and `lab3_neuron_scores.png`), `variant.json` (edit `rebuilt-<name>`), and `plan.json` (a one-GPU `dev256` run `dev/rebuilt-<name>`). The script prints the two commands to build `/nvme/wsp-lab/models/qwen35-rebuilt-<name>` on CPU in a few seconds and test it on the 256 unseen practice problems (about 20 minutes on one GPU):
186
+
187
+ ```bash
188
+ CUDA_VISIBLE_DEVICES="" python scripts/build_models.py --variants results/lab3/try-<name>/variant.json
189
+ python scripts/run_jobs.py results/lab3/try-<name>/plan.json --gpus 7 --only dev/rebuilt-<name>
190
+ ```
191
+
192
+ We ran all three tries below, built their models, and evaluated them in about 11 minutes across 6 GPUs (2 shards per run) with `plans/lab3-tries.json`. We then scored their side effects on WikiText-2 and the 8 texts in Lab 7 (`results/lab7/try-lab3-tries/`) and checked how their 512 answers ended (`results/lab3/endings-tries.json`):
193
+
194
+ ```bash
195
+ python scripts/run_jobs.py plans/lab3-tries.json --gpus 0,1,2,3,4,6
196
+ CUDA_VISIBLE_DEVICES=7 python labs/lab7_side_effects.py --models rebuilt rebuilt-pairs2 rebuilt-windows36-36 rebuilt-layers15-19-21 --try-name lab3-tries
197
+ ```
198
+
199
+ (`scripts/run_jobs.py` skips any shard with a `.done.json` marker; move the existing run folder under `/nvme/wsp-lab/runs/dev/` aside first to generate fresh answers.)
200
+
201
+ 1. **Use only the agent's first 2 pairs (`--pairs 2`, folder `results/lab3/try-pairs2/`).**
202
+ - **Predict first:** With half the data (the first 4 texts instead of all 8), how far will `cos(rebuilt u, released u)` and the 64-neuron overlap drop, and will `dev256` accuracy suffer?
203
+ - **Run it:**
204
+ ```bash
205
+ CUDA_VISIBLE_DEVICES=7 python labs/lab3_rebuild.py --pairs 2
206
+ CUDA_VISIBLE_DEVICES="" python scripts/build_models.py --variants results/lab3/try-pairs2/variant.json
207
+ python scripts/run_jobs.py results/lab3/try-pairs2/plan.json --gpus 7 --only dev/rebuilt-pairs2
208
+ ```
209
+ - **What we saw:** Blocks 15 and 19 still lead in MLP growth (`[(15, 0.201), (19, 0.188), (22, 0.167), (14, 0.163)]`), while the direction and neuron list get rougher:
210
+ - Layer 15: `cos(rebuilt u, released u) = +0.82157` (`0.822`), `42/64` neurons match, and the top 8 / 32 / 64 neurons hold `[0.144, 0.278, 0.386]` (`14.4% / 27.8% / 38.6%`) of the positive score.
211
+ - Layer 19: `cos(rebuilt u, released u) = -0.78384` (`-0.784`), `31/64` neurons match, and the top 8 / 32 / 64 neurons hold `[0.219, 0.348, 0.419]` (`21.9% / 34.8% / 41.9%`).
212
+ - On `dev256`, `dev/rebuilt-pairs2` reaches `62.3% +/- 2.7` accuracy (`+50.2 +/- 2.6` points over `dev/base`, `129` rescued, `0` lost), matching the 4-pair rebuild (`61.5%`). What changes is how the edit works and how hard it pushes: more answers hit the 32,768-token cap (`25.2%` vs `17.6%`), and more cut answers end in repetition (`74.4%` vs `32.2%`, or `68.2%` vs `15.6%` for verbatim loops; across all 512 answers in `results/lab3/endings-tries.json`, `86` are cut while thinking and `74` close out of a loop, vs `55` and `118` for `rebuilt` in `results/lab3/endings.json`). In Lab 7, it disturbs WikiText-2 less (perplexity `+0.43%` vs `+0.79%`, KL `0.0194` vs `0.0333`) and is less selective on the 8 texts (looping KL over productive KL is `0.0109 / 0.0075 = 1.46`, vs `0.0238 / 0.0096 = 2.48` for `rebuilt`).
213
+
214
+ 2. **Average neuron activations over 36 tokens instead of 24 (`--windows 36 36`, folder `results/lab3/try-windows36-36/`).**
215
+ - **Predict first:** The agent used 36 tokens for the stream (`H_WIN = 36`) and 24 tokens for the neuron scores (`A_WIN = 24`). If you set `A_WIN` to `36`, will `u` change, and will the extra 12 tokens shift the 64 chosen neurons enough to affect `dev256`?
216
+ - **Run it:**
217
+ ```bash
218
+ CUDA_VISIBLE_DEVICES=7 python labs/lab3_rebuild.py --windows 36 36
219
+ CUDA_VISIBLE_DEVICES="" python scripts/build_models.py --variants results/lab3/try-windows36-36/variant.json
220
+ python scripts/run_jobs.py results/lab3/try-windows36-36/plan.json --gpus 7 --only dev/rebuilt-windows36-36
221
+ ```
222
+ - **What we saw:** Because `H_WIN` remains `36`, the block growth numbers and `u` are unchanged (`+0.99998` at layer 15, `-0.99998` at layer 19). The 36-token neuron window swaps a few tail neurons: `58/64` match at layer 15 (top 8 / 32 / 64 score shares `[0.187, 0.375, 0.481]`) and `51/64` match at layer 19 (shares `[0.378, 0.474, 0.533]`). On `dev256`, `dev/rebuilt-windows36-36` scores `61.9% +/- 2.8` (`+49.8 +/- 2.7` points, `21.3%` cut, `122` rescued, `0` lost), with a Lab 7 WikiText-2 perplexity change of `+0.71%` (KL `0.0326`). The 24-token window is not fragile.
223
+
224
+ 3. **Also edit layer 21, the third-strongest MLP (`--layers 15 19 21`, folder `results/lab3/try-layers15-19-21/`).**
225
+ - **Predict first:** Block 21 had the third-largest MLP addition to the loop gap (`0.164`, behind `0.263` at block 15 and `0.249` at block 19). Does applying the 64-neuron, `alpha = 6` edit to layer 21 cut more loops and raise accuracy, or only add side effects?
226
+ - **Run it:**
227
+ ```bash
228
+ CUDA_VISIBLE_DEVICES=7 python labs/lab3_rebuild.py --layers 15 19 21
229
+ CUDA_VISIBLE_DEVICES="" python scripts/build_models.py --variants results/lab3/try-layers15-19-21/variant.json
230
+ python scripts/run_jobs.py results/lab3/try-layers15-19-21/plan.json --gpus 7 --only dev/rebuilt-layers15-19-21
231
+ ```
232
+ - **What we saw:** Layers 15 and 19 match the main rebuild (`64/64` neurons in both). At layer 21, scores are flatter: the top 8 / 32 / 64 neurons hold `[0.173, 0.292, 0.371]` (`17.3% / 29.2% / 37.1%`) of the positive score (top 12: `[2, 1, 11, 99, 14, 85, 9, 109, 168, 335, 212, 169]`), and the edit's magnitude is `5.8%` of layer 21's `down_proj` matrix magnitude (the square root of the sum of squared entries, not the fraction of entries changed). On `dev256`, `dev/rebuilt-layers15-19-21` scores `61.3% +/- 2.8` (`+49.2 +/- 2.6` points, `21.1%` cut, `122` rescued, `0` lost, with `3` answers stopping mid-thinking before `</think>`), showing no gain over the 2-layer rebuild (`61.5%`, difference `-0.2 +/- 1.9` points). In Lab 7, WikiText-2 perplexity rises by `+1.49%` (nearly double the 2-layer rebuild's `+0.79%`) and KL climbs from `0.0333` to `0.0394`.
233
+
234
+ The main rebuild and the three tries line up as follows across Lab 3, `dev256`, and Lab 7:
235
+
236
+ | Edit | Cosine with released `u` (L15 / L19) | Neurons shared with released 64 (L15 / L19) | `dev256` accuracy % | Cut % | Cut answers ending in repetition % | Gain vs `dev/base` (pp), rescued / lost | Lab 7 perplexity change | KL WikiText / productive / looping |
237
+ |---|---|---|---|---:|---:|---|---:|---|
238
+ | `rebuilt` (`--pairs 4 --windows 36 24 --layers 15 19`) | `+0.99998 / -0.99998` | `64 / 64` | `61.5 +/- 2.8` | `17.6%` | `32.2%` | `+49.4 +/- 2.6`, `122 / 0` | `+0.79%` | `0.0333 / 0.0096 / 0.0238` |
239
+ | `rebuilt-pairs2` (`--pairs 2`) | `+0.82157 / -0.78384` | `42 / 31` | `62.3 +/- 2.7` | `25.2%` | `74.4%` | `+50.2 +/- 2.6`, `129 / 0` | `+0.43%` | `0.0194 / 0.0075 / 0.0109` |
240
+ | `rebuilt-windows36-36` (`--windows 36 36`) | `+0.99998 / -0.99998` | `58 / 51` | `61.9 +/- 2.8` | `21.3%` | `35.8%` | `+49.8 +/- 2.7`, `122 / 0` | `+0.71%` | `0.0326 / 0.0096 / 0.0241` |
241
+ | `rebuilt-layers15-19-21` (`--layers 15 19 21`) | `+0.99998 / -0.99998` | `64 / 64` (+64 at L21) | `61.3 +/- 2.8` | `21.1%` | `38.0%` | `+49.2 +/- 2.6`, `122 / 0` | `+1.49%` | `0.0394 / 0.0114 / 0.0280` |
242
+
243
+ **Lesson:** The recipe is forgiving. Using half the pairs (`--pairs 2`) or widening the neuron window (`--windows 36 36`) still reaches about `62%` accuracy on unseen problems. Those variants mainly shift side effects on ordinary text and the balance between preventing loops early and breaking them after they start. Editing a third layer with flatter neuron scores (`--layers 15 19 21`) increases ordinary-text damage without improving math accuracy.
244
+
code/labs/lab3_rebuild.py ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ """Lab 3: rebuild the released edit from the agent's 8 sentences, using only hooks and arithmetic.
3
+
4
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
5
+ CUDA_VISIBLE_DEVICES=7 python labs/lab3_rebuild.py # about a minute on one GPU
6
+
7
+ Experiments (each writes to its own folder, results/lab3/try-*, and leaves the main results alone; the script
8
+ prints the commands that build the try's edit and test it on the 256 unseen practice problems):
9
+ CUDA_VISIBLE_DEVICES=7 python labs/lab3_rebuild.py --pairs 2 # only the agent's first 2 pairs
10
+ CUDA_VISIBLE_DEVICES=7 python labs/lab3_rebuild.py --windows 36 36 # neurons averaged over 36 final tokens, not 24
11
+ CUDA_VISIBLE_DEVICES=7 python labs/lab3_rebuild.py --layers 15 19 21 # also edit layer 21, the third-strongest MLP
12
+ (plans/lab3-tries.json tests exactly these three: runs dev/rebuilt-pairs2, dev/rebuilt-windows36-36 and
13
+ dev/rebuilt-layers15-19-21.)
14
+
15
+ What it does:
16
+ 1. Runs the 8 texts in labs/data/astra_pairs.json (4 productive, 4 looping) through the unedited model and
17
+ records every block's output (the residual stream) and every MLP's neuron activations.
18
+ 2. For every layer, measures how far apart looping and productive text sit (averaged over the last 36 tokens)
19
+ and how much of that gap each block adds, split into the MLP's share and the mixer's share. This is how the
20
+ agent picked layers 15 and 19.
21
+ 3. Direction u at layer L = the looping-minus-productive gap after block L, scaled to length 1.
22
+ 4. Neuron score = (how much neuron j writes along u) x (how much more neuron j fires in looping text, last 24
23
+ tokens). The 64 highest are the neurons the edit touches.
24
+ 5. Compares all of it with the released edit and writes configs/rebuilt.json, an edit you can build and test.
25
+ """
26
+ import argparse
27
+ import json
28
+ import os
29
+ import sys
30
+
31
+ import numpy as np
32
+ import torch
33
+
34
+ ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
35
+ sys.path.insert(0, ROOT)
36
+ from wsp import hf # noqa: E402
37
+
38
+ ap = argparse.ArgumentParser()
39
+ ap.add_argument("--pairs", type=int, default=4, choices=[1, 2, 3, 4], help="use only the agent's first N pairs")
40
+ ap.add_argument("--windows", type=int, nargs=2, default=[36, 24], metavar=("STREAM", "NEURONS"),
41
+ help="final tokens to average for the stream and for the neurons (the agent used 36 and 24)")
42
+ ap.add_argument("--layers", type=int, nargs="+", default=[15, 19], help="layers to build the edit for")
43
+ a = ap.parse_args()
44
+ LAYERS = tuple(a.layers)
45
+ H_WIN, A_WIN = a.windows # the agent's windows: last 36 tokens for the stream, last 24 for neurons
46
+ TRY = "try-" + "-".join(p for p in (f"pairs{a.pairs}" if a.pairs != 4 else "",
47
+ f"windows{H_WIN}-{A_WIN}" if a.windows != [36, 24] else "",
48
+ "layers" + "-".join(map(str, LAYERS)) if list(LAYERS) != [15, 19] else "") if p)
49
+ TRY = None if TRY == "try-" else TRY
50
+ OUT = os.path.join(ROOT, "results", "lab3", *([TRY] if TRY else []))
51
+ FIG = OUT if TRY else os.path.join(ROOT, "results", "figures")
52
+ os.makedirs(OUT, exist_ok=True)
53
+ os.makedirs(FIG, exist_ok=True)
54
+
55
+ pairs = json.load(open(os.path.join(ROOT, "labs", "data", "astra_pairs.json")))
56
+ texts = pairs["texts"][:2 * a.pairs]
57
+ N = len(texts)
58
+ m, tok = hf.load("base")
59
+ n_layers = len(hf.blocks(m))
60
+ # hidden_states[-1] has the final norm applied, so the last block's raw output is not available to measure
61
+ assert all(0 <= L < n_layers - 1 for L in LAYERS), f"--layers must be between 0 and {n_layers - 2}"
62
+
63
+ # 1. record. hidden[l] is the stream entering block l (hidden[0] = token embeddings, hidden[l+1] = block l's output)
64
+ hidden, acts, lens = [], [], []
65
+ with torch.no_grad(), hf.record_outputs(m, range(n_layers)) as outs, hf.record_mlp_acts(m, range(n_layers)) as A:
66
+ for t in texts:
67
+ ids = hf.encode(tok, t)
68
+ lens.append(ids.shape[1])
69
+ o = m(ids, output_hidden_states=True)
70
+ hidden.append([h[0].float().cpu() for h in o.hidden_states])
71
+ # the hook copy of block l's output must equal transformers' hidden_states[l+1]
72
+ hook_check = max(float((outs[l][i] - hidden[i][l + 1]).abs().max()) for i in range(N) for l in range(n_layers - 1))
73
+ assert lens == pairs["agent_token_lengths"][:N], (lens, pairs["agent_token_lengths"])
74
+ print(f"token counts match the agent's: {lens}; hooks vs hidden_states max diff {hook_check:.2e}")
75
+
76
+
77
+ def pair_gap(per_text): # per_text: [N, d] -> mean over the pairs of (looping - productive)
78
+ x = torch.stack(per_text)
79
+ return (x[1::2] - x[0::2]).mean(0)
80
+
81
+
82
+ gapH = [pair_gap([hidden[i][l][-H_WIN:].mean(0) for i in range(N)]) for l in range(n_layers + 1)]
83
+ gapA36 = [pair_gap([A[l][i][-H_WIN:].mean(0) for i in range(N)]) for l in range(n_layers)]
84
+ gapA24 = {L: pair_gap([A[L][i][-A_WIN:].mean(0) for i in range(N)]) for L in LAYERS}
85
+ Wd = {l: hf.down_proj(m, l).weight.detach().float().cpu() for l in range(n_layers)}
86
+
87
+ # 2. where does the gap grow? (the agent's table, pass 3)
88
+ growth = []
89
+ for l in range(n_layers - 1):
90
+ d = gapH[l + 1] / gapH[l + 1].norm()
91
+ total = float((gapH[l + 1] - gapH[l]) @ d)
92
+ mlp = float((Wd[l] @ gapA36[l]) @ d)
93
+ growth.append({"layer": l, "gap_size_after": float(gapH[l + 1].norm()), "growth": total, "mlp": mlp,
94
+ "mixer": total - mlp})
95
+ top_mlp = sorted(growth, key=lambda g: -g["mlp"])[:4]
96
+ print("layers whose MLP adds most to the gap:", [(g["layer"], round(g["mlp"], 3)) for g in top_mlp])
97
+
98
+ # 3-4. direction and neurons, then compare with the released edit
99
+ rel = hf.released_edit()
100
+ rebuilt, report = {}, {"token_lengths": lens, "hook_vs_hidden_states_max_diff": hook_check, "growth": growth,
101
+ "layers": {}}
102
+ for L in LAYERS:
103
+ u = gapH[L + 1] / gapH[L + 1].norm() # points from productive toward looping
104
+ write = u @ Wd[L] # [3584]: how much each neuron writes along u per unit of activation
105
+ score = write * gapA24[L]
106
+ order = torch.argsort(score, descending=True)
107
+ top = sorted(order[:64].tolist())
108
+ pos = score.clamp_min(0).sum()
109
+ cos = float(u @ rel[L]["u"] / rel[L]["u"].norm()) if L in rel else None # the released edit: layers 15, 19
110
+ report["layers"][str(L)] = {
111
+ "cos_with_released_u": cos,
112
+ "released_u_points_to": None if cos is None else "looping" if cos > 0 else "productive",
113
+ "overlap_with_released_64": len(set(top) & set(rel[L]["cols"])) if L in rel else None,
114
+ "top12": order[:12].tolist(),
115
+ "positive_score_share_top8_32_64": [float(score[order[:k]].clamp_min(0).sum() / pos) for k in (8, 32, 64)],
116
+ "gap_size": float(gapH[L + 1].norm()),
117
+ "u_loop_sign": [round(float(x), 8) for x in u],
118
+ "cols": top,
119
+ "scores_sorted": [round(float(x), 6) for x in score[order]],
120
+ }
121
+ rebuilt[str(L)] = {"alpha": 6, "dir": [round(float(x), 8) for x in u], "cols": top}
122
+ vs = (f"cos(rebuilt u, released u) = {cos:+.5f}; neurons shared with the released 64: "
123
+ f"{report['layers'][str(L)]['overlap_with_released_64']}/64; " if L in rel
124
+ else "the released edit leaves this layer alone; ")
125
+ print(f"layer {L}: {vs}top-8/32/64 share of positive score: "
126
+ f"{[round(x, 3) for x in report['layers'][str(L)]['positive_score_share_top8_32_64']]}")
127
+ if 15 in LAYERS and 19 in LAYERS:
128
+ u15, u19 = (torch.tensor(report["layers"][str(L)]["u_loop_sign"]) for L in (15, 19))
129
+ report["cos_u15_u19_loop_sign"] = float(u15 @ u19)
130
+ print(f"cos(u15, u19) with both pointing toward looping: {report['cos_u15_u19_loop_sign']:+.3f}")
131
+
132
+ json.dump(report, open(os.path.join(OUT, "rebuild.json"), "w"), indent=1)
133
+ if TRY: # its own variant name, so building it never replaces models/qwen35-rebuilt
134
+ name = f"rebuilt-{TRY[4:]}"
135
+ variants, plan = os.path.join(OUT, "variant.json"), os.path.join(OUT, "plan.json")
136
+ json.dump({name: {"about": f"Lab 3 experiment: {TRY[4:]}", "layers": rebuilt}}, open(variants, "w"), indent=1)
137
+ json.dump({"about": f"Lab 3 experiment {TRY[4:]}: the rebuilt edit on the 256 unseen practice problems",
138
+ "runs": [{"name": f"dev/{name}", "model": f"qwen35-{name}", "data": "dev256", "n": 2, "shards": 1,
139
+ "est": 8}]}, open(plan, "w"), indent=1)
140
+ else:
141
+ name = "rebuilt"
142
+ variants, plan = os.path.join(ROOT, "configs", "rebuilt.json"), os.path.join(ROOT, "plans", "extra.json")
143
+ json.dump({"rebuilt": {"about": "Lab 3: the edit rebuilt from the agent's 8 texts (alpha 6, my u, my top 64)",
144
+ "layers": rebuilt}}, open(variants, "w"), indent=1)
145
+
146
+ # figures
147
+ import matplotlib # noqa: E402
148
+ matplotlib.use("Agg")
149
+ import matplotlib.pyplot as plt # noqa: E402
150
+
151
+ fig, ax = plt.subplots(1, 2, figsize=(13, 4.2))
152
+ xs = [g["layer"] for g in growth]
153
+ # leave out the last position: hidden_states[-1] has gone through the final norm, which rescales everything
154
+ ax[0].plot(range(n_layers), [float(g.norm()) for g in gapH[:n_layers]], "o-", color="#0969da")
155
+ ax[0].text(0.02, 0.95, f"not shown: after the last block and the final norm\nthe gap is {float(gapH[-1].norm()):.0f}",
156
+ transform=ax[0].transAxes, va="top", fontsize=8, color="#57606a")
157
+ ax[0].set_xlabel("position in the stream (0 = embeddings, l+1 = after block l)")
158
+ ax[0].set_ylabel("size of looping - productive gap")
159
+ ax[0].set_title("The loop signal builds up through the network")
160
+ ax[1].bar([x - 0.2 for x in xs], [g["mlp"] for g in growth], 0.4, label="added by the MLP", color="#cf222e")
161
+ ax[1].bar([x + 0.2 for x in xs], [g["mixer"] for g in growth], 0.4, label="added by attention / DeltaNet",
162
+ color="#8c959f")
163
+ for L in LAYERS:
164
+ ax[1].annotate(f"layer {L}", (L - 0.2, growth[L]["mlp"]), xytext=(0, 6), textcoords="offset points",
165
+ ha="center", fontsize=9)
166
+ ax[1].set_xlabel("block")
167
+ ax[1].set_ylabel("growth of the gap along its own direction")
168
+ ax[1].set_title("Which blocks push text toward the loop")
169
+ ax[1].legend()
170
+ fig.tight_layout()
171
+ fig.savefig(os.path.join(FIG, "lab3_where_the_loop_grows.png"), dpi=130)
172
+
173
+ fig, ax = plt.subplots(1, len(LAYERS), figsize=(6.5 * len(LAYERS), 3.8), squeeze=False)
174
+ ax = ax[0]
175
+ for k, L in enumerate(LAYERS):
176
+ s = np.array(report["layers"][str(L)]["scores_sorted"])
177
+ ax[k].plot(np.arange(len(s)), s, color="#8c959f", lw=1)
178
+ ax[k].plot(np.arange(64), s[:64], color="#cf222e", lw=2, label="the 64 neurons the edit touches")
179
+ ax[k].set_xscale("symlog", linthresh=10)
180
+ ax[k].axhline(0, color="black", lw=0.5)
181
+ ax[k].set_title(f"layer {L}: neuron scores, highest first")
182
+ ax[k].set_xlabel("neuron rank (of 3,584)")
183
+ ax[k].set_ylabel("writes along u x fires more in loops")
184
+ ax[k].legend()
185
+ fig.tight_layout()
186
+ fig.savefig(os.path.join(FIG, "lab3_neuron_scores.png"), dpi=130)
187
+ print("wrote", OUT, "(an experiment: configs/rebuilt.json and results/figures are untouched)" if TRY
188
+ else "and configs/rebuilt.json and two figures")
189
+ print("next, build the edit and test it on the 256 unseen practice problems (about 20 minutes on one GPU):")
190
+ print(f" CUDA_VISIBLE_DEVICES=\"\" python scripts/build_models.py --variants {os.path.relpath(variants, ROOT)}")
191
+ print(f" python scripts/run_jobs.py {os.path.relpath(plan, ROOT)} --gpus 7 --only dev/{name}")
code/labs/lab4_dose.md ADDED
@@ -0,0 +1,369 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Lab 4: Turn the knob, and the controls
2
+
3
+ **Big thing:** You will sweep the edit strength `alpha` from `-4` to `12` across 256 unseen practice problems. You will also test each part of the edit (layer 15 vs layer 19, 64 neurons vs all 3,584 neurons, and same-size random controls) and classify how every answer ended.
4
+
5
+ **Why it matters:** One benchmark gain shows that an edit helped, while a dose sweep and matched controls test why it worked and where it breaks. In these runs, the gain needed both the loop direction `u` and neurons chosen because they fire during loops. At `alpha = -4`, accuracy even rises while the edit pushes the model harder into loops.
6
+
7
+ **Time and hardware:** Building the 12 variant models takes about 2 minutes on CPU. Running one variant on the 256 practice problems (`dev256`, 512 answers) takes 17 to 45 minutes on 1 NVIDIA A100 GPU, and the full `plans/sampler.json` plan takes about 10.6 GPU-hours across 7 GPUs. Classifying endings and inspecting answers with `labs/lab4_endings.py` and `labs/peek.py` takes a few seconds on CPU.
8
+
9
+ **Theory guide:** Section 8.5.
10
+
11
+ ## Before you run: predict
12
+
13
+ Write down a short guess for each question before running the commands or reading the tables.
14
+
15
+ 1. As `alpha` grows through `0, 1, 2, 4, 6, 8, 12`, does accuracy keep rising to `12`, or plateau and drop? Why?
16
+ 2. Keep the same 64 neurons and weight-change size (Frobenius norm) as the released edit, and replace only the loop direction `u` with a random direction at right angles to `u` (`randdir`). How many percentage points of the `+49.4` gain will survive?
17
+ 3. What if you keep the loop direction `u` and the same weight-change size, and apply it to 64 random neurons instead of the chosen 64 (`randcols`)?
18
+ 4. At `alpha = -4`, the edit multiplies the 64 neurons' push along the loop direction by `1 - (-4) = +5`, pushing 5 times harder toward loops. Will `dev256` accuracy be lower than the unedited model (`12.1%`), about the same, or higher?
19
+
20
+ ## Run it
21
+
22
+ ### 1. How edit variants are defined
23
+
24
+ Every weight edit in this lab is a JSON entry in `configs/variants.json`, where `_doc` explains the schema:
25
+
26
+ - Each entry becomes a model folder `/nvme/wsp-lab/models/qwen35-<name>`.
27
+ - Inside `"layers"`, each edited layer (`"15"` or `"19"`) specifies `"alpha"` (the strength knob).
28
+ - `"dir"` chooses the 1,024-number unit vector (a list of numbers scaled to have length one) in the residual stream. The residual stream is the running list of 1,024 numbers per token that every layer reads from and adds to. A token is a word piece. `"released"` uses the paper's direction `u` for that layer (default), and `"random_orth:SEED"` picks a random unit vector at right angles to `u`.
29
+ - `"cols"` chooses which columns of `down_proj` to edit. This is the MLP output matrix of shape `1,024 x 3,584`; the MLP is the layer's bank of neurons. Column `j` is the vector neuron `j` adds to the stream per unit of activation, or firing strength. `"released"` uses the paper's 64 neurons (default), `"all"` edits all 3,584 neurons, and `"random:SEED"` picks 64 other neurons at random.
30
+ - `"norm_match": true` rescales `alpha` so the Euclidean size (Frobenius norm) of the matrix change `W' - W` matches the released edit at that `alpha`.
31
+
32
+ Two entries from `configs/variants.json`, for `a4` (`alpha = 4` on the released direction and neurons) and the random-direction control `randdir`:
33
+
34
+ ```json
35
+ "a4": {"about": "flip and triple the loop component",
36
+ "layers": {"15": {"alpha": 4}, "19": {"alpha": 4}}},
37
+ "randdir": {"about": "control: same 64 neurons, same size of change, but a random direction instead of u",
38
+ "layers": {"15": {"alpha": 6, "dir": "random_orth:15", "norm_match": true},
39
+ "19": {"alpha": 6, "dir": "random_orth:19", "norm_match": true}}}
40
+ ```
41
+
42
+ ### 2. Build the edited model folders on CPU
43
+
44
+ Connect to `sampler`, activate the environment, and run `scripts/build_models.py` on CPU (`CUDA_VISIBLE_DEVICES=""` hides the GPUs so PyTorch stays on CPU):
45
+
46
+ ```bash
47
+ ssh sampler
48
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
49
+ CUDA_VISIBLE_DEVICES="" python scripts/build_models.py
50
+ ```
51
+
52
+ For each entry in `configs/variants.json`, `scripts/build_models.py` symlinks the tokenizer and config files from `/nvme/wsp-lab/models/Qwen3.5-0.8B` and writes `model.safetensors-00001-of-00001.safetensors` with the edited `down_proj` tensors in layers 15 and 19. If a folder already matches its spec in `edit_note.json`, the script prints `exists <name>` and skips it.
53
+
54
+ ### 3. Run the generation plan on `sampler`
55
+
56
+ The generation runs are in `plans/sampler.json`. Every `dev/*` run tests one model on `dev256` (128 GSM8K-train and 128 MATH-train practice problems the agent never touched, with `n = 2` answers per problem, or `512` answers per run). One entry from `plans/sampler.json`:
57
+
58
+ ```json
59
+ {"name": "dev/a4", "model": "qwen35-a4", "data": "dev256", "n": 2, "shards": 1, "est": 8}
60
+ ```
61
+
62
+ Use `nvidia-smi` to pick a free GPU. One `dev256` run on one A100 GPU takes 17 to 45 minutes depending on answer length (`dev/a12` finishes in 16.8 minutes because its answers are shorter; `dev/base` takes 44.8 minutes because almost every answer hits the 32,768-token cap). The full `plans/sampler.json` plan (22 runs, including Lab 5 decoding runs and `build/base`) takes about 10.6 GPU-hours:
63
+
64
+ ```bash
65
+ nvidia-smi
66
+ python scripts/run_jobs.py plans/sampler.json --gpus 0-6
67
+ python scripts/summarize.py /nvme/wsp-lab/runs
68
+ ```
69
+
70
+ **Note on rerunning finished jobs:** `scripts/run_jobs.py` skips any shard that already has `shard*.done.json`, so the command above finishes immediately without generating new text. To regenerate a run yourself, move our finished folder aside first:
71
+
72
+ ```bash
73
+ mv /nvme/wsp-lab/runs/dev/a4 /nvme/wsp-lab/runs/dev/a4-ours
74
+ python scripts/run_jobs.py plans/sampler.json --gpus 7 --only dev/a4
75
+ ```
76
+
77
+ Then `dev/a4` holds your run and `dev/a4-ours` keeps ours.
78
+
79
+ ### 4. Classify how every answer ended and inspect single answers
80
+
81
+ `scripts/pull_runs.sh` copies only per-answer summary numbers to the laptop, so the full answer texts (`shard*.texts.jsonl.gz`) stay on the VM that generated them (all `dev/*` runs in `plans/sampler.json` are on `sampler`). Run `labs/lab4_endings.py` on CPU on `sampler` to classify every answer in the sweep and controls, and to print examples where `dev/a-4` closed its thinking out of a loop:
82
+
83
+ ```bash
84
+ export CUDA_VISIBLE_DEVICES=""
85
+ python labs/lab4_endings.py
86
+ python labs/lab4_endings.py dev/base dev/p50c dev/l15only dev/l19only dev/all1 dev/all2 dev/randdir dev/randcols \
87
+ --out lab4/endings-controls
88
+ python labs/lab4_endings.py dev/a-4 --show 2
89
+ ```
90
+
91
+ The first two commands write `results/lab4/endings.json` and `results/lab4/endings-controls.json`; the third writes `results/lab4/try-dev_a-4/endings.json`.
92
+
93
+ To inspect single answers on `sampler`, use `labs/peek.py` (CPU, a few seconds). Without `--id`, it lists all 512 answers in a run; with `--id`, it prints the problem, whether the answer looped, the token where the loop started, the text around `</think>`, and the ending:
94
+
95
+ ```bash
96
+ python labs/peek.py dev/p50c
97
+ python labs/peek.py dev/p50c --id gsm8k_train_5628
98
+ python labs/peek.py dev/a-4 --id gsm8k_train_5628
99
+ ```
100
+
101
+ ## What you should see
102
+
103
+ ### 1. The dose curve (`alpha` from `-4` to `12`)
104
+
105
+ Every row below tests the same 64 neurons per layer and loop direction `u` at layers 15 and 19 on `dev256` (256 problems, 2 answers each = 512 answers), varying only `alpha`. Accuracy and gain include their standard error (`SE`, the typical sampling uncertainty across 256 problems; a gap of at least 2 SE is unlikely to be chance). Gains in percentage points (`pp`) and rescued or lost problem counts are paired by problem against `dev/base` (`alpha = 0`). A rescued problem has at least one correct edited answer and no correct base answer; a lost problem is the reverse. The last column, from Lab 7 (`results/lab7/side_effects.json`), gives the percentage change in perplexity, a measure of how surprised the model is by the text. Perplexity is $e$ to the average negative log-probability of the true next token across 65,536 tokens of WikiText-2, so lower is better and `+0.0%` is the unedited base score of `20.009`.
106
+
107
+ | `alpha` | What it does to the 64 neurons' push along `u` | Accuracy % | Cut at cap % | Mean tokens | Correct when finished % | Gain vs base (pp), rescued / lost | WikiText perplexity change (Lab 7) |
108
+ |---|---|---|---|---|---|---|---|
109
+ | `-4` (`dev/a-4`) | 5 times stronger toward loops | `19.7 +/- 1.9` | `65.8` | `26,063` | `55.4` | `+7.6 +/- 2.2`, `54 / 24` | `+5.39%` |
110
+ | `0` (`dev/base`) | Unedited | `12.1 +/- 1.6` | `88.7` | `29,683` | `100.0` | `-` | `0.00%` |
111
+ | `1` (`dev/a1`) | Erased (`1 - alpha = 0`) | `22.7 +/- 2.1` | `77.1` | `26,964` | `96.6` | `+10.5 +/- 1.8`, `42 / 7` | `-0.49%` |
112
+ | `2` (`dev/a2`) | Mirrored (`1 - alpha = -1`) | `35.2 +/- 2.6` | `62.7` | `23,911` | `92.1` | `+23.0 +/- 2.3`, `71 / 5` | `-0.69%` |
113
+ | `4` (`dev/a4`) | Flipped, 3 times (`1 - alpha = -3`) | `59.2 +/- 2.8` | `29.1` | `17,730` | `82.9` | `+47.1 +/- 2.6`, `118 / 0` | `-0.47%` |
114
+ | `6` (`dev/p50c`) | Flipped, 5 times (released edit) | `61.5 +/- 2.8` | `19.7` | `16,104` | `76.6` | `+49.4 +/- 2.6`, `125 / 0` | `+0.79%` |
115
+ | `8` (`dev/a8`) | Flipped, 7 times (`1 - alpha = -7`) | `60.7 +/- 2.8` | `14.1` | `14,589` | `70.7` | `+48.6 +/- 2.6`, `119 / 0` | `+3.33%` |
116
+ | `12` (`dev/a12`) | Flipped, 11 times (`1 - alpha = -11`) | `51.8 +/- 2.8` | `12.3` | `13,515` | `59.0` | `+39.6 +/- 2.7`, `102 / 0` | `+14.34%` |
117
+
118
+ ![Four panels against edit strength alpha from -4 to 12: accuracy, percent of answers cut at the token cap, mean answer length, and WikiText perplexity change.](../results/figures/lab4_dose.png)
119
+
120
+ How to read `lab4_dose.png`: the four panels plot accuracy, percentage of answers cut at the 32,768-token cap, mean answer length (thousand tokens), and Lab 7's WikiText perplexity change across `alpha = -4, 0, 1, 2, 4, 6, 8, 12`. The dose sweep is blue in the first three panels and red in the fourth. The three open markers to the right of `alpha = 6` (a red square at `x = 6.35` for `rebuilt`, and purple triangles at `x = 6.7` and `x = 7.05` for Lab 8's `capA-a6` and `capB-a6`) also use `alpha = 6`; the plot shifts them right of `6` so their error bars do not overlap `p50c`.
121
+
122
+ ### 2. Which parts of the edit matter (layers, neuron subset, and random controls)
123
+
124
+ All runs below are at `alpha = 6` unless noted, on the same 256 practice problems (`512` answers per run):
125
+
126
+ | Model | What changes | Accuracy % | Cut at cap % | Mean tokens | Correct when finished % | Gain vs base (pp), rescued / lost | WikiText perplexity change (Lab 7) |
127
+ |---|---|---|---|---|---|---|---|
128
+ | `dev/p50c` | Released edit (`alpha = 6`, 64 neurons, layers 15 and 19) | `61.5 +/- 2.8` | `19.7` | `16,104` | `76.6` | `+49.4 +/- 2.6`, `125 / 0` | `+0.79%` |
129
+ | `dev/l15only` | Released edit on layer 15 only | `60.4 +/- 2.9` | `28.5` | `17,362` | `84.2` | `+48.2 +/- 2.7`, `117 / 0` | `+0.05%` |
130
+ | `dev/l19only` | Released edit on layer 19 only | `46.9 +/- 2.7` | `49.0` | `20,729` | `91.2` | `+34.8 +/- 2.4`, `100 / 0` | `+1.65%` |
131
+ | `dev/all1` | `alpha = 1` on all 3,584 neurons (layers 15 and 19) | `28.7 +/- 2.3` | `71.9` | `25,609` | `97.9` | `+16.6 +/- 2.0`, `61 / 6` | `-0.42%` |
132
+ | `dev/all2` | `alpha = 2` on all 3,584 neurons (layers 15 and 19) | `49.4 +/- 2.8` | `46.7` | `20,117` | `91.9` | `+37.3 +/- 2.5`, `97 / 0` | `-0.36%` |
133
+ | `dev/randdir` | Control: same 64 neurons, random orthogonal direction, same change size | `14.5 +/- 1.8` | `87.3` | `29,486` | `98.5` | `+2.3 +/- 1.6`, `22 / 18` | `+0.30%` |
134
+ | `dev/randcols` | Control: direction `u`, 64 random neurons, same change size | `14.6 +/- 1.9` | `86.7` | `29,113` | `100.0` | `+2.5 +/- 1.6`, `23 / 20` | `-0.10%` |
135
+
136
+ ![Horizontal bars of dev256 accuracy for each model: the unedited base in grey, the released edit and the Lab 3 rebuild in green, the two Lab 8 edits in purple, the ablations in blue, and the two random controls in red.](../results/figures/lab4_controls.png)
137
+
138
+ How to read `lab4_controls.png`: each horizontal bar is one model on `dev256`, grouped by color. Grey is the unedited base model (`12.1%`), and green is the released edit (`61.5%`) and your Lab 3 rebuild (`61.5%`). Purple shows the two Lab 8 capstone edits built from the model's own loops (`capA-a6` at `43.0%` and `capB-a6` at `58.2%`). Blue shows the ablations (`l15only`, `l19only`, `all1`, `all2`). Red at the bottom shows the two norm-matched random controls (`randdir` at `14.5%` and `randcols` at `14.6%`), which barely pass the grey bar.
139
+
140
+ ### 3. How the agent discovered this path in its own notebook
141
+
142
+ You can read the agent's notebook in `external/lm_understand-logs/exp8_math/runs/p50c/work_notes/experiment_history.md` (lines 55 to 112 and 225 to 374; online at <https://github.com/RikiyaT/lm_understand-logs/blob/main/exp8_math/runs/p50c/work_notes/experiment_history.md>). The variants above retrace its main steps. Because failed requests did not count toward its budget, its pass number can trail the request file number in `external/lm_understand-logs/exp8_math/runs/p50c/requests/`. Each step lists both:
143
+
144
+ - 4th forward pass (`fwd_5`): switching off one neuron (layer 19, neuron 11) gave `+6.25 +/- 4.35` percentage points, too noisy to trust on a small batch.
145
+ - 10th forward pass (`fwd_13`): removing the loop direction (`alpha = 1`) from all 3,584 columns of layers 15 and 19 raised correct answers from `11 to 42 of 128` (`+24.22 +/- 3.96` points).
146
+ - 19th forward pass (`fwd_27`): mirroring the direction (`alpha = 2`) across all 3,584 columns jumped from `21 to 110 of 256` (`+34.77 +/- 3.53` points).
147
+ - 20th forward pass (`fwd_29`): raising `alpha` to `3` across all columns reached `+39.84` points.
148
+ - 23rd forward pass (`fwd_35`): a random orthogonal direction of the same norm changed accuracy by only `+1.56 +/- 3.52` points, a sign that the gain came from `u` and not random weight noise.
149
+ - 27th and 28th forward passes (`fwd_41` and `fwd_42`): at `alpha = 4` with a 16,384-token limit, editing the top 64 neurons per layer and editing all 3,584 both gained `+26.56` points, but the 64-neuron edit left fewer answers ending in long whitespace runs (`2` vs `10`).
150
+ - 30th forward pass (`fwd_45`): raising the 64-neuron edit to `alpha = 6` jumped from `7/64 to 35/64` (`+43.75` points), with answers cut at the limit falling from `57` to `5`.
151
+ - 33rd and 34th forward passes (`fwd_50` and `fwd_52`): the frozen edit raised 128 fresh practice problems from `9 to 74` correct (`+50.78` points). Later passes tested more fresh problems. On the 256 fresh problems of `final_1`, it went from `10.16%` to `58.98%` (`+48.83 +/- 3.23` points, `127` rescued, `2` lost). Across `704` checked practice items it went from `7.95%` to `57.53%` (line 244), and across the final `1,152` items from `10.59%` to `60.42%` (line 374).
152
+
153
+ Our `all1` (`+16.6 +/- 2.0`) and `all2` (`+37.3 +/- 2.5`) apply the released direction `u` to all 3,584 columns, so they are close copies of the agent's 10th and 19th passes (`fwd_13` and `fwd_27`) rather than exact ones (the same `u`, but tested on different problems). Our norm-matched controls `randdir` (`+2.3 +/- 1.6`) and `randcols` (`+2.5 +/- 1.6`) match the agent's 23rd pass (`fwd_35`, `+1.56 +/- 3.52`).
154
+
155
+ ### 4. How every answer ended (`results/lab4/endings.json` and `endings-controls.json`)
156
+
157
+ `labs/lab4_endings.py` sorts the 512 answers in a run into four mutually exclusive groups by whether and how the model closed its `<think>` block:
158
+
159
+ 1. **Cut while thinking:** hit the 32,768-token limit before writing `</think>`. (A cut answer still counts as correct if its last `\boxed{}` holds the right answer, even inside its scratchpad; if it wrote no `\boxed{}`, the grader checks its last `\fbox{}`.)
160
+ 2. **Stopped while thinking:** emitted the end-of-sequence token before writing `</think>`, leaving no final answer.
161
+ 3. **Closed out of a loop:** wrote `</think>` right after verbatim repetition (the 60-character snippet ending 100 characters before `</think>` appears at least 3 times in the 6,000 characters before `</think>`).
162
+ 4. **Closed normally:** wrote `</think>` and the group 3 check found no repeated snippet. That check inspects only the last 6,000 characters before `</think>`, so an answer that recovered from a loop more than 6,000 characters before closing, or wrote `</think>` before its 6,000th character, still counts as "closed normally".
163
+
164
+ Every cell below lists `answers (correct answers)`:
165
+
166
+ | Run | Total correct (of 512) | Cut while thinking | Stopped while thinking | Closed out of a loop | Closed normally |
167
+ |---|---|---|---|---|---|
168
+ | `dev/base` (`alpha = 0`) | `62` | `453 (4)` | `0 (0)` | `3 (2)` | `56 (56)` |
169
+ | `dev/a-4` (`alpha = -4`) | `101` | `255 (4)` | `47 (0)` | `197 (87)` | `13 (10)` |
170
+ | `dev/a1` (`alpha = 1`) | `116` | `394 (3)` | `1 (0)` | `31 (29)` | `86 (84)` |
171
+ | `dev/a2` (`alpha = 2`) | `180` | `306 (4)` | `0 (0)` | `70 (44)` | `136 (132)` |
172
+ | `dev/a4` (`alpha = 4`) | `303` | `83 (1)` | `1 (0)` | `172 (70)` | `256 (232)` |
173
+ | `dev/p50c` (`alpha = 6`) | `315` | `72 (0)` | `0 (0)` | `96 (29)` | `344 (286)` |
174
+ | `dev/a8` (`alpha = 8`) | `311` | `51 (0)` | `28 (0)` | `69 (16)` | `364 (295)` |
175
+ | `dev/a12` (`alpha = 12`) | `265` | `43 (0)` | `37 (0)` | `19 (3)` | `413 (262)` |
176
+ | `dev/l15only` | `309` | `81 (1)` | `0 (0)` | `136 (47)` | `295 (261)` |
177
+ | `dev/l19only` | `240` | `196 (2)` | `0 (0)` | `151 (82)` | `165 (156)` |
178
+ | `dev/all1` | `147` | `362 (6)` | `0 (0)` | `30 (25)` | `120 (116)` |
179
+ | `dev/all2` | `253` | `200 (1)` | `0 (0)` | `93 (49)` | `219 (203)` |
180
+ | `dev/randdir` | `74` | `440 (10)` | `0 (0)` | `14 (7)` | `58 (57)` |
181
+ | `dev/randcols` | `75` | `443 (7)` | `0 (0)` | `6 (5)` | `63 (63)` |
182
+
183
+ ### 5. Median length of correct answers and breakdown by problem source
184
+
185
+ `dev256` is half GSM8K (grade-school word problems, 128 problems, 256 answers) and half MATH (competition math, 128 problems, 256 answers). `results/summary.json` breaks accuracy down by half and records the median token length of correct answers:
186
+
187
+ | Run | GSM8K accuracy % / cut % | MATH accuracy % / cut % | Median tokens of correct answers |
188
+ |---|---|---|---|
189
+ | `dev/base` (`alpha = 0`) | `15.2 / 86.3` | `9.0 / 91.0` | `5,600.5` |
190
+ | `dev/a-4` (`alpha = -4`) | `23.0 / 55.9` | `16.4 / 75.8` | `12,987` |
191
+ | `dev/p50c` (`alpha = 6`) | `74.6 / 4.7` | `48.4 / 34.8` | `7,998` |
192
+ | `dev/a12` (`alpha = 12`) | `55.5 / 0.8` | `48.0 / 23.8` | `5,799` |
193
+
194
+ In the unedited model, only short derivations finish before a loop starts, giving a median correct length of `5,600` tokens (`5,600.5`). With `p50c`, longer derivations can finish without looping, raising the median to `7,998` tokens. At `alpha = -4`, most correct answers (`87` of `101`) close right after repeated text. The median length of all correct answers is `12,987` tokens; the endings check does not measure loop length.
195
+
196
+ ## Why it works
197
+
198
+ ### 1. The dose trade-off along `u`: why accuracy plateaus and then drops
199
+
200
+ In [Lab 0](lab0_toy.md), `W' = W - alpha * u (u^T W_S)` scales the chosen 64 neurons' push along `u` by `1 - alpha`. Sweeping `alpha` from `0` to `12` takes that multiplier from `+1` (no edit) through `0` (erase at `alpha = 1`) and `-1` (mirror at `alpha = 2`) to `-5` (`alpha = 6`) and `-11` (`alpha = 12`).
201
+
202
+ ```mermaid
203
+ xychart-beta
204
+ title "Accuracy (%) on dev256 across edit strength alpha"
205
+ x-axis "alpha" ["-4", "0", "1", "2", "4", "6", "8", "12"]
206
+ y-axis "Accuracy (%)" 0 --> 70
207
+ line [19.7, 12.1, 22.7, 35.2, 59.2, 61.5, 60.7, 51.8]
208
+ ```
209
+
210
+ `alpha = 1` (erasing the 64 neurons' push along `u`) is not enough. In Lab 3, those 64 neurons held about half the positive loop score in layers 15 and 19 (`49.8%` and `55.5%`), while the other 22 blocks and the attention and DeltaNet mixers also pushed toward `u`. Setting `alpha = 1` zeroes out those 64 neurons' contribution (`22.7%` accuracy) and leaves the rest of the loop drift intact. Setting `alpha > 1` turns them into a counter-push: when they fire, they subtract `(alpha - 1)` times their original loop push, cancelling drift from other neurons and layers.
211
+
212
+ Pushing `alpha` too high shows two signs of harm to reasoning; the first is clean and the second is partly muddied by which problems finish:
213
+
214
+ 1. **Premature stop tokens during thinking:** From `alpha = 0` to `alpha = 6`, at most `1` of 512 answers emits an end-of-sequence token inside `<think>`. At `alpha = 8`, `28` answers stop mid-thinking with no final answer; at `alpha = 12`, `37` do.
215
+ 2. **Lower accuracy among finished answers, and higher perplexity:** Among answers that finish before the cap, accuracy drops as `alpha` grows: `100.0%` (`alpha = 0`), `96.6%` (`alpha = 1`), `92.1%` (`alpha = 2`), `82.9%` (`alpha = 4`), `76.6%` (`alpha = 6`), `70.7%` (`alpha = 8`), and `59.0%` (`alpha = 12`). Among normal `<think>` closes without a detected loop, accuracy falls from `286 / 344` (`83.1%`) at `alpha = 6` to `262 / 413` (`63.4%`) at `alpha = 12`. These finished-answer rates mix reasoning harm with selection: at `alpha = 0` few answers finish, and they may come from easier problems. Higher `alpha` lets harder problems finish too, and some of them are wrong. The `alpha = 6` vs `alpha = 12` normal-close gap has the same issue because the two runs finish different problems. The cleaner signs of harm are the mid-thinking stops in item 1 and WikiText-2 perplexity (Lab 7): small edits (`alpha = 1, 2, 4`) slightly lower perplexity (`-0.49%`, `-0.69%`, `-0.47%`), `alpha = 6` adds `+0.79%`, `alpha = 8` adds `+3.33%`, and `alpha = 12` jumps to `+14.34%`.
216
+
217
+ Across `alpha = 4` (`59.2%`), `alpha = 6` (`61.5%`), and `alpha = 8` (`60.7%`), fewer cut answers and lower finished-answer accuracy roughly balance out. The released `alpha = 6` sits on that plateau: paired by problem, it leads `alpha = 4` by `+2.3 +/- 1.6` and `alpha = 8` by `+0.8 +/- 1.6` points. Mid-thinking stops and perplexity rise at `alpha = 8` and `12`.
218
+
219
+ ### 2. Why both the direction `u` and the 64 neurons matter
220
+
221
+ Changing 64 columns by `10.6%` and `8.1%` of the matrix norm could in principle work by adding weight noise that breaks repetition. The controls `randdir` and `randcols` test this with `"norm_match": true` in `wsp/edits.py`, which rescales `alpha` so the Frobenius norm $\|W' - W\|_F$ matches `p50c` at both layers:
222
+
223
+ - `randdir` edits the same 64 neurons per layer by the same matrix norm along a random unit vector orthogonal to `u`. Accuracy is `14.5 +/- 1.8%` (`+2.3 +/- 1.6` points over `12.1%`, `22` rescued, `18` lost).
224
+ - `randcols` applies `u` with the same matrix norm to 64 random neurons outside the chosen 64. Accuracy is `14.6 +/- 1.9%` (`+2.5 +/- 1.6` points, `23` rescued, `20` lost).
225
+
226
+ Both controls sit within noise of the unedited model, with `87.3%` and `86.7%` of answers still cut at the cap. Among our 64-neuron edits, the gain appeared only when `u` went into neurons chosen because they fire during loops. Two caveats apply: each control is a single random draw (one random direction, one random set of 64 neurons), and the neurons do not have to be the exact released 64. In [Lab 3](lab3_rebuild.md), rebuilding from only 2 of the agent's 4 text pairs picked neurons that shared `42` and `31` of the released 64 neurons at layers 15 and 19 and still scored `62.3%`. In [Lab 8](lab8_capstone.md), recipe B picked neurons from real loops, shared `42` and `28` of the 64, and scored `58.2%`.
227
+
228
+ Comparing `all1` and `all2` with the 64-neuron edits shows what the 64-neuron restriction does. Editing all 3,584 neurons at `alpha = 1` (`all1`, `28.7%`) and `alpha = 2` (`all2`, `49.4%`) beats the 64-neuron edits at the same `alpha` (`a1` at `22.7%` and `a2` at `35.2%`), which suggests the other 3,520 neurons also push along `u`. In Lab 7, KL divergence measures how far the edited next-token probabilities move from the base model's. At `alpha = 2`, `all2` shifts predictions on productive texts about `3.5` times as much as `a2` (KL `0.0063` vs `0.0018`). It is also less selective: its KL ratio between looping and productive texts is `1.45` (`0.0092` vs `0.0063`), compared with `2.49` for `p50c` (`0.0241` vs `0.0097`). We did not run the all-neuron edit above `alpha = 2`, so we do not know its peak accuracy, only that the 64-neuron edit concentrates more of its change on looping text.
229
+
230
+ ### 3. Two likely mechanisms: fewer loops and more escapes (and layer 15 vs layer 19)
231
+
232
+ Compare `dev/base` and `dev/p50c` in the endings table:
233
+
234
+ ```mermaid
235
+ flowchart LR
236
+ Start["512 answers on dev256"] --> Base["Unedited (dev/base):<br/>453 cut while thinking<br/>3 closed out of a loop (2 correct)<br/>56 closed normally (56 correct)"]
237
+ Start --> Edit["Edited alpha = 6 (dev/p50c):<br/>72 cut while thinking<br/>96 closed out of a loop (29 correct)<br/>344 closed normally (286 correct)"]
238
+ ```
239
+
240
+ The endings suggest the edit helps in two ways, but do not prove it, because the check inspects only the text right before `</think>`:
241
+
242
+ 1. **Fewer answers end their thinking in a loop:** Normal `<think>` closes jump more than sixfold, from `56` in `dev/base` to `344` in `dev/p50c`. Normal closes are usually right: `56 / 56` (`100%`) in `dev/base` and `286 / 344` (`83.1%`) in `dev/p50c`. The simplest reading is that fewer loops start.
243
+ 2. **More answers escape from a loop:** Answers that repeat verbatim and then emit `</think>` ("closed out of a loop") rise from `3` in `dev/base` to `172` at `alpha = 4` and `96` at `alpha = 6`. Loop escapes are noisier than normal closes: in `dev/p50c`, `29 of 96` (`30.2%`) are correct, adding `29` to the `286` normal-close successes (`286 + 29 = 315`).
244
+
245
+ Comparing `dev/l15only` and `dev/l19only` in the endings table:
246
+
247
+ - **Layer 15 alone mostly adds normal closes:** `dev/l15only` scores `60.4%` (`309` correct, near `61.5%` for both layers). Normal closes rise from `56` to `295` (`261` correct), cut answers fall from `453` to `81`, and `136` answers close out of a loop (`47` correct).
248
+ - **Layer 19 alone leaves more answers cut and gets more closes from loops:** `dev/l19only` scores `46.9%` (`240` correct), with `196` cut answers, `165` normal closes (`156` correct), and `151` closes out of a loop (`82` correct).
249
+
250
+ [Lab 6](lab6_steering.md) offers a hint, though not a proof, for why the layers differ. Across 48 real loops, it measures the trigger `s(t)` (how hard the 64 chosen neurons push along `u` on each token). Relative to its in-loop level, layer 15's trigger builds earlier: in the last 512 tokens before the loop it reaches `42%` of its in-loop value (`0.178` of `0.424`), compared with `32%` at layer 19 (`0.241` of `0.758`). Inside the loop, layer 19's trigger is larger (`0.758` vs `0.424`). This fits layer 15 acting earlier and layer 19 pushing harder once a loop starts, though we did not test that directly.
251
+
252
+ ### 4. The `alpha = -4` surprise: why you must read the answers
253
+
254
+ At `alpha = -4` (`dev/a-4`), the edit multiplies the 64 neurons' push along `u` by `1 - (-4) = +5`, pushing 5 times harder in the loop direction. On headline accuracy alone, `dev/a-4` scores `19.7 +/- 1.9%`, or `+7.6 +/- 2.2` points above the unedited model (`12.1%`).
255
+
256
+ The endings table and `labs/peek.py` show what happened:
257
+
258
+ ```mermaid
259
+ flowchart TD
260
+ Neg["alpha = -4 (dev/a-4):<br/>pushes 5x harder toward loops"] --> Collapse["Normal closes collapse:<br/>56 to 13 answers (10 correct)<br/>24 previously solved problems lost"]
261
+ Neg --> Stop["Mid-thinking stops spike:<br/>0 to 47 answers end with no answer"]
262
+ Neg --> Crash["Loops intensify and crash out of thinking:<br/>3 to 197 answers close out of a loop<br/>(87 of the 101 correct answers)"]
263
+ ```
264
+
265
+ - **Normal reasoning collapses:** Normal `<think>` closes drop from `56` in `dev/base` to `13` in `dev/a-4` (`10` correct). `dev/a-4` loses `24` problems the base model solved (`54` rescued, `24` lost, while `a4`, `p50c`, `a8`, and `a12` lose `0`).
266
+ - **Mid-thinking stops jump to 47:** `47` answers emit the end token inside `<think>` with no final answer, and WikiText perplexity worsens by `+5.39%`.
267
+ - **Most correct answers close their thinking right after repeated text:** `197` answers close this way; of `101` correct answers in `dev/a-4`, `87` came from `closed out of a loop`.
268
+
269
+ One problem shows the pattern (`gsm8k_train_5628`: *"Mr. Desmond bought three times as many toys for his younger son as he bought for his elder son. If the elder son received 60 toys, how many toys did Mr Desmond buy?"*, truth `240`):
270
+
271
+ - **Unedited (`quick/base`, answer 0):** Computes `60 + 180 = 240` early, then enters an 83-token verbatim loop at token `8,568` of `32,768` (*"Let's check if the answer is 240. / Let's check if the answer is 180..."*) that repeats about `292` times to the cap.
272
+ - **Released edit (`quick/p50c`, answer 0):** Finishes in `5,825` tokens, closes `</think>`, and writes `\boxed{240}`.
273
+ - **Alpha -4 (`dev/a-4`, answer 0, on `sampler`):** Run `python labs/peek.py dev/a-4 --id gsm8k_train_5628` on `sampler`. The model repeats *"Wait, is it possible $A$ and $C$ is the same?"* over and over, breaks off mid-line with a stray backtick and `</think>`, and writes `240` (`4,830` tokens total).
274
+
275
+ Pushing harder in the loop direction cut normal closes from `56` to `13`, lost `24` previously solved problems, and dropped finished-answer accuracy to `55.4%`. Yet `87` correct answers closed their thinking right after repeated text, and total accuracy rose by `+7.6` points.
276
+
277
+ ## Check yourself
278
+
279
+ 1. Look at the dose table between `alpha = 6` (`61.5%` accuracy, `19.7%` cut) and `alpha = 12` (`51.8%` accuracy, `12.3%` cut). Fewer answers are cut at `alpha = 12`, so why is accuracy `9.7` percentage points lower?
280
+ <details><summary>Answer</summary>
281
+
282
+ Two things go wrong at `alpha = 12`. First, `37` answers emit an end token inside `<think>` (`stopped while thinking`), against `0` at `alpha = 6`, producing no final answer. Second, the `-11` multiplier likely hurts reasoning: WikiText-2 perplexity rises `+14.34%`, and finished-answer accuracy falls from `76.6%` at `alpha = 6` to `59.0%` at `alpha = 12` (normal closes fall from `286 / 344 = 83.1%` to `262 / 413 = 63.4%`), though selection may contribute because the two runs finish different problems.
283
+ </details>
284
+
285
+ 2. Why do `randdir` and `randcols` both use `"norm_match": true` in `configs/variants.json`? What would happen if `randcols` used raw `alpha = 6` without `"norm_match": true`?
286
+ <details><summary>Answer</summary>
287
+
288
+ In Lab 1, the 64 chosen neurons aligned with `u` much more strongly than typical `down_proj` columns (median `|cos(u, column)|` is `0.095` on the 64 columns in layer 15 vs `0.018` on the rest). Without rescaling, plugging 64 random columns into `W' = W - 6 u (u^T W_S)` gives a Frobenius norm for `W' - W` several times smaller than the released edit's, so the control could fail from a smaller weight change alone. Setting `"norm_match": true` multiplies `alpha` by `||u_rel^T W[:, cols_rel]|| / ||u^T W[:, cols]||` so `W' - W` matches `p50c` in Frobenius norm.
289
+ </details>
290
+
291
+ 3. Compare `dev/l15only` (`60.4%` accuracy, `+0.05%` perplexity change) and `dev/l19only` (`46.9%` accuracy, `+1.65%` perplexity change). Which single layer carries most of the benefit on `dev256`, and how do their answer endings differ?
292
+ <details><summary>Answer</summary>
293
+
294
+ Layer 15 alone (`60.4%`) nearly matches editing both layers (`61.5%`) with almost no WikiText perplexity change (`+0.05%`). In the endings table, `dev/l15only` mostly adds normal closes (`295` normal, `81` cut while thinking), while `dev/l19only` leaves `196` cut, gets `165` normal closes, and closes out of a loop more often (`151`).
295
+ </details>
296
+
297
+ 4. In `dev/a-4`, `101` of `512` answers are correct (`19.7%`), beating `dev/base` (`62` of `512`, `12.1%`). Give three numbers from the tables that show `dev/a-4` damages the model instead of fixing the loop bug cleanly.
298
+ <details><summary>Answer</summary>
299
+
300
+ Any three of these confirm the damage:
301
+ 1. Normal closes fall from `56` (`56` correct) in `dev/base` to `13` (`10` correct) in `dev/a-4`.
302
+ 2. `dev/a-4` loses `24` problems that the unedited model solved (`54 / 24` rescued/lost), whereas `dev/p50c` loses `0` (`125 / 0`).
303
+ 3. `47` answers stop dead in the middle of thinking (`stopped while thinking`), compared with `0` in `dev/base` and `0` in `dev/p50c`.
304
+ 4. Accuracy among finished answers drops from `100.0%` in `dev/base` (`76.6%` in `dev/p50c`) to `55.4%`, and `87` of the `101` correct answers come from crashing out of a verbatim loop (`closed out of a loop`).
305
+ 5. WikiText-2 perplexity rises by `+5.39%` (compared with `+0.79%` for `p50c`).
306
+ </details>
307
+
308
+ ## Try this next
309
+
310
+ We did not run the experiments below. Write down your prediction first, then test on a 16-problem slice (`--limit 16`, about 3 to 5 minutes on 1 GPU) before running all 256 problems.
311
+
312
+ 1. **Test `alpha = 3` or `alpha = 5` on the dose curve (not run).** Between `alpha = 2` (`35.2%`) and `alpha = 4` (`59.2%`), accuracy jumps `24.0` points. From the project root on this machine, add `"a3"` or `"a5"` to a new file `configs/try_dose.json`:
313
+ ```json
314
+ {
315
+ "a3": {"about": "flip and double the loop component (1 - alpha = -2)",
316
+ "layers": {"15": {"alpha": 3}, "19": {"alpha": 3}}},
317
+ "a5": {"about": "between a4 and p50c (1 - alpha = -4)",
318
+ "layers": {"15": {"alpha": 5}, "19": {"alpha": 5}}}
319
+ }
320
+ ```
321
+ On this machine, create `plans/try_dose_quick.json` in the format of `plans/quick.json` (`--limit 16` runs the first 16 GSM8K problems of `dev256`, 2 answers each = 32 answers):
322
+ ```json
323
+ {
324
+ "about": "Quick 16-problem check of alpha 3 and alpha 5",
325
+ "runs": [
326
+ {"name": "quick/a3", "model": "qwen35-a3", "data": "dev256", "n": 2, "shards": 1, "est": 1, "args": ["--limit", "16"]},
327
+ {"name": "quick/a5", "model": "qwen35-a5", "data": "dev256", "n": 2, "shards": 1, "est": 1, "args": ["--limit", "16"]}
328
+ ]
329
+ }
330
+ ```
331
+ Sync both files:
332
+ ```bash
333
+ scripts/sync_vm.sh sampler
334
+ ```
335
+ Pick a free GPU with `nvidia-smi`. Connect to the VM, activate the environment, build both models on CPU, and run the plan (replace `7` with your free GPU):
336
+ ```bash
337
+ ssh sampler
338
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
339
+ CUDA_VISIBLE_DEVICES="" python scripts/build_models.py --variants configs/try_dose.json
340
+ python scripts/run_jobs.py plans/try_dose_quick.json --gpus 7
341
+ ```
342
+ **What to predict:** In the agent's 20th pass (`fwd_29`), raising `alpha = 2` to `alpha = 3` on all columns added about 5 points (`+34.77` to `+39.84`), about a third of the way to the roughly `+50` points its 64-neuron edit later reached. Our 64-neuron curve climbs faster, from `a2` at `35.2%` to `a4` at `59.2%`, so a fair guess for `a3` on full `dev256` is near the middle, around `45% to 50%`. Predict `a5` on full `dev256` to land around `60% to 61%` (within noise of `a4` at `59.2%` and `p50c` at `61.5%`). On these `16` problems alone, our full-run answers score `a2` at `37.5%` (`12/32` correct) and `a4`, `p50c`, and `a8` at `65.6%` (`21/32` each). With 16 problems, the standard error is roughly 9 points or more, so the quick check gives only a rough placement on the curve.
343
+
344
+ 2. **Edit only a subset of the 64 neurons at `alpha = 6` (not run).** In `wsp/edits.py`, `build_from_spec` accepts a list of integer column indices for `"cols"`. In `results/lab3/rebuild.json`, each layer stores `"cols"` (all 64 chosen neurons) and `"top12"` (the 12 highest-scoring neurons by loop score). From the project root on this machine, write a variant file with 32 of the 64 neurons (`"cols"[:32]`, the 32 lowest-numbered of the 64, since `cols` is sorted by index, not score) or with the top 12 (`"top12"`):
345
+ ```bash
346
+ python3 -c '
347
+ import json
348
+ r = json.load(open("results/lab3/rebuild.json"))["layers"]
349
+ spec = {
350
+ "halfcols-a6": {"about": "alpha 6 on 32 of the 64 neurons per layer",
351
+ "layers": {L: {"alpha": 6, "cols": r[L]["cols"][:32]} for L in ("15", "19")}},
352
+ "top12-a6": {"about": "alpha 6 on the top 12 loop-score neurons per layer",
353
+ "layers": {L: {"alpha": 6, "cols": r[L]["top12"]} for L in ("15", "19")}}
354
+ }
355
+ json.dump(spec, open("configs/try_subset.json", "w"), indent=1)
356
+ '
357
+ ```
358
+ Copy the quick plan from Try 1 to `plans/try_subset_quick.json`, replacing the two run names with `quick/halfcols-a6` and `quick/top12-a6` and the model names with `qwen35-halfcols-a6` and `qwen35-top12-a6`. Keep `"args": ["--limit", "16"]` and sync:
359
+ ```bash
360
+ scripts/sync_vm.sh sampler
361
+ ```
362
+ Build on CPU and test on a free GPU (replace `7` as needed):
363
+ ```bash
364
+ ssh sampler
365
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
366
+ CUDA_VISIBLE_DEVICES="" python scripts/build_models.py --variants configs/try_subset.json
367
+ python scripts/run_jobs.py plans/try_subset_quick.json --gpus 7
368
+ ```
369
+ **What to predict:** We did not measure the counter-push of each subset, so use Lab 3's loop scores as a rough guide. The 32 lowest-numbered columns are not an even half by score. At layer 19 they include the two highest scorers, neurons 1 and 11. Neuron 11 alone holds `24%` of layer 19's positive score, so this half probably carries well over half of layer 19's push. At layer 15 no neuron dominates (the top holds `5.6%`). If that half carried exactly half the push, `alpha = 6` on 32 columns would act like `alpha = 3` or `4` on all 64. Predict roughly `45% to 55%` on full `dev256` with under `+0.79%` WikiText perplexity change, and somewhat higher if layer 19's half carries more. The top 12 neurons hold more positive score than the top 8 (`19.3%` at layer 15, `39.7%` at layer 19) and less than the top 32 (`38.9%`, `49.8%`), compared with `49.8%` and `55.5%` for all 64. That share is large, especially at layer 19, so `top12-a6` could land anywhere from near `a2` (`35.2%`) to near `a4` (`59.2%`) on full `dev256`. The `16`-problem slice is not the full-set comparison: our full-run answers on these problems score `a2` at `37.5%` and `a4`, `p50c`, and `a8` at `65.6%`.
code/labs/lab4_endings.py ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ """Lab 4 (also used in Labs 2 and 5): how did each answer end? Reads the full answer texts of finished runs.
3
+
4
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
5
+ export CUDA_VISIBLE_DEVICES="" # CPU only, a few seconds per run
6
+ # sampler VM
7
+ python labs/lab4_endings.py # Lab 4, the alpha sweep -> results/lab4/endings.json
8
+ python labs/lab4_endings.py dev/base dev/p50c dev/l15only dev/l19only dev/all1 dev/all2 dev/randdir dev/randcols \
9
+ --out lab4/endings-controls # Lab 4, which parts matter
10
+ python labs/lab4_endings.py dev/base dev/base+temp1.0 dev/base+pp0 dev/base+pp2 dev/base+rep1.1 dev/base+max4k \
11
+ dev/base+nothink dev/p50c+nothink --out lab5/endings # Lab 5 (the two thinking-off runs are skipped)
12
+ python labs/lab4_endings.py dev/base dev/p50c dev/rebuilt --out lab3/endings # Lab 3
13
+ python labs/lab4_endings.py dev/base dev/p50c dev/capA-a6 dev/capB-a6 --out lab8/endings # Lab 8
14
+ # trainer VM
15
+ python labs/lab4_endings.py bench/base/math500 bench/p50c/math500 bench/base/gsm8k bench/p50c/gsm8k \
16
+ bench/base/ifeval bench/p50c/ifeval --out lab2/endings # Lab 2
17
+ python labs/lab4_endings.py dev/rebuilt-pairs2 dev/rebuilt-windows36-36 dev/rebuilt-layers15-19-21 \
18
+ --out lab3/endings-tries # Lab 3's experiments
19
+ # either VM
20
+ python labs/lab4_endings.py dev/a-4 --show 2 # also print where 2 answers closed their thinking
21
+
22
+ Without --out, any run list other than the sweep is written to results/lab4/try-<names>/endings.json, so the main
23
+ results stay as they are.
24
+
25
+ It runs on the VM that made the runs, on the CPU, because the answer texts stay there (scripts/pull_runs.sh copies
26
+ only the per-answer numbers to the laptop). Every answer lands in exactly one of four groups:
27
+ cut while thinking it hit the 32,768-token cap before it ever wrote </think>
28
+ stopped while thinking the model emitted its end token before it ever wrote </think>, so there is no final answer
29
+ closed out of a loop it wrote </think> right after a stretch of verbatim repetition
30
+ closed normally it wrote </think> after ordinary reasoning
31
+ "Closed out of a loop" is a rough text check: the 60 characters that end 100 characters before </think> appear
32
+ at least 3 times in the 6,000 characters before </think>. An answer that closed its thinking can still be cut
33
+ later, while it writes the final answer; it stays in its "closed" group.
34
+ """
35
+ import argparse
36
+ import glob
37
+ import hashlib
38
+ import json
39
+ import os
40
+ import sys
41
+
42
+ ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
43
+ sys.path.insert(0, ROOT)
44
+ from wsp import hf, loops, protocol # noqa: E402
45
+
46
+ SWEEP = ["dev/base", "dev/a-4", "dev/a1", "dev/a2", "dev/a4", "dev/p50c", "dev/a8", "dev/a12"]
47
+ GROUPS = ["cut while thinking", "stopped while thinking", "closed out of a loop", "closed normally"]
48
+ SNIP, GAP, LOOK, MIN_COUNT = 60, 100, 6000, 3
49
+
50
+ ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
51
+ ap.add_argument("runs", nargs="*", default=SWEEP, help="run names under runs/ (default: the alpha sweep)")
52
+ ap.add_argument("--show", type=int, default=0, metavar="N",
53
+ help="print the text around </think> for N correct answers that closed out of a loop, per run")
54
+ ap.add_argument("--out", metavar="PATH",
55
+ help="write results/PATH.json instead (e.g. lab5/endings)")
56
+ a = ap.parse_args()
57
+
58
+
59
+ def closed_out_of_loop(text, i):
60
+ """i = where </think> starts. True if the text just before it is a verbatim loop (rough check)."""
61
+ if i < LOOK:
62
+ return False
63
+ snip = text[i - GAP - SNIP:i - GAP]
64
+ return text[i - LOOK:i].count(snip) >= MIN_COUNT
65
+
66
+
67
+ def group(sample, text):
68
+ i = text.find("</think>")
69
+ if i < 0:
70
+ return GROUPS[0] if sample["cut"] else GROUPS[1]
71
+ return GROUPS[2] if closed_out_of_loop(text, i) else GROUPS[3]
72
+
73
+
74
+ res = {}
75
+ for run in a.runs:
76
+ run_dir = os.path.join(hf.LAB, "runs", run)
77
+ done = sorted(glob.glob(os.path.join(run_dir, "shard*.done.json")))
78
+ if not done:
79
+ sys.exit(f"no finished shards in {run_dir}; is this the VM that ran {run}?")
80
+ if not protocol.PRESETS[json.load(open(done[0]))["preset"]]["thinking"]:
81
+ print(f"skipping {run}: thinking is off, so its answers have no </think> to look for")
82
+ continue
83
+ counts = {g: {"answers": 0, "correct": 0} for g in GROUPS}
84
+ shown = 0
85
+ for rec, j, s, text in loops.iter_samples(run_dir):
86
+ g = group(s, text)
87
+ counts[g]["answers"] += 1
88
+ counts[g]["correct"] += bool(s["correct"])
89
+ if g == GROUPS[2] and s["correct"] and shown < a.show:
90
+ shown += 1
91
+ i = text.find("</think>")
92
+ print(f"--- {run}: {rec['id']} answer {j}, {s['tokens']:,} tokens, closed out of a loop and correct")
93
+ print(text[i - 600:i + 300].rstrip() + "\n")
94
+ n = sum(c["answers"] for c in counts.values())
95
+ if n == 0:
96
+ sys.exit(f"no answer texts found in {run_dir}; is this the VM that ran {run}?")
97
+ res[run] = {"answers": n, "correct": sum(c["correct"] for c in counts.values()), "groups": counts}
98
+
99
+ w = max(len(r) for r in res)
100
+ print(f"{'run':{w}s} answers correct | " + " | ".join(GROUPS) + " (each: answers, of which correct)")
101
+ for run, r in res.items():
102
+ cells = [f"{r['groups'][g]['answers']:>{len(g) - 6}d} ({r['groups'][g]['correct']:3d})" for g in GROUPS]
103
+ print(f"{run:{w}s} {r['answers']:7d} {r['correct']:7d} | " + " | ".join(cells))
104
+
105
+ if not res:
106
+ sys.exit("nothing to report")
107
+ if a.out:
108
+ path = os.path.join(ROOT, "results", a.out.removesuffix(".json") + ".json")
109
+ elif a.runs != SWEEP:
110
+ name = "-".join(r.replace("/", "_") for r in a.runs)
111
+ if len(name) > 60:
112
+ name = name[:50] + "-" + hashlib.md5(name.encode()).hexdigest()[:6]
113
+ path = os.path.join(ROOT, "results", "lab4", "try-" + name, "endings.json")
114
+ else:
115
+ path = os.path.join(ROOT, "results", "lab4", "endings.json")
116
+ os.makedirs(os.path.dirname(path), exist_ok=True)
117
+ json.dump({"check": {"snippet": SNIP, "gap": GAP, "look_back": LOOK, "min_count": MIN_COUNT}, "runs": res},
118
+ open(path, "w"), indent=1)
119
+ print(f"\nwrote {os.path.relpath(path, ROOT)}")
code/labs/lab5_decoding.md ADDED
@@ -0,0 +1,320 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Lab 5: can decoding settings do the same job?
2
+
3
+ **Big thing:** We compare the 131,072-weight edit against six decoding changes on unedited Qwen3.5-0.8B (temperature `1.0`, presence penalty `0` and `2.0`, repetition penalty `1.1`, a shorter `4,096`-token cap, and `<think>` mode off) across all 512 `dev256` answers. Penalties and temperature raise accuracy at most from `12.1%` to `34.4%`, turning thinking off reaches `52.1%` in 13 times fewer tokens than the unedited thinking baseline, and the weight edit with thinking on reaches `61.5%` (`74.6%` on the GSM8K half).
4
+
5
+ **Why it matters:** Always test whether a weight edit beats simpler decoding changes that leave the weights alone. Ending counts suggest penalties mostly add normal closes, while the weight edit adds both normal closes and loop escapes. These counts do not prove which loops were prevented, because the check reads only the text right before `</think>`.
6
+
7
+ **Time and hardware:** Already run on `sampler` (`results/summary.md`, `results/lab5/endings.json`, and `results/figures/lab5_decoding.png`). Re-running one unedited `dev256` job on 1 NVIDIA A100 GPU takes `34` to `49` minutes with thinking on at the `32,768`-token cap, or about `3` minutes with thinking off or a `4,096`-token cap.
8
+
9
+ **Theory guide:** Section 13.2.
10
+
11
+ ## Before you run: predict
12
+
13
+ Write down a guess for each question before looking at the table.
14
+
15
+ 1. The paper's baseline (`"think35"`) uses `presence_penalty = 1.5`, which subtracts `1.5` from the score of any token that has already appeared in the answer. If you turn that penalty off (`presence_penalty = 0`), what happens to the share of answers cut at the 32,768-token cap (now `88.7%`)?
16
+ 2. The unedited model's correct `dev256` answers have a median length of `5,600` tokens, while looping answers run to `32,768` tokens. What happens to accuracy if you lower the token cap to `4,096` tokens?
17
+ 3. What if you turn `<think>` mode off (`enable_thinking=False`), so the model writes a direct step-by-step solution instead of an open-ended internal scratchpad? Will it beat the unedited thinking model (`12.1%`), and will the weight edit (`p50c`) still help when thinking is off?
18
+
19
+ ## Run it
20
+
21
+ All seven decoding runs are at the bottom of `plans/sampler.json` and ran on `sampler` alongside the Lab 4 variants (`dev/base` and `dev/p50c` at the top of `plans/sampler.json` are the baselines).
22
+
23
+ Open `plans/sampler.json` and check the last seven entries:
24
+
25
+ ```json
26
+ {"name": "dev/base+temp1.0", "model": "base", "data": "dev256", "n": 2, "shards": 1, "est": 15, "set": ["temperature=1.0"]},
27
+ {"name": "dev/base+pp0", "model": "base", "data": "dev256", "n": 2, "shards": 1, "est": 15, "set": ["presence_penalty=0"]},
28
+ {"name": "dev/base+pp2", "model": "base", "data": "dev256", "n": 2, "shards": 1, "est": 15, "set": ["presence_penalty=2.0"]},
29
+ {"name": "dev/base+rep1.1", "model": "base", "data": "dev256", "n": 2, "shards": 1, "est": 14, "set": ["repetition_penalty=1.1"]},
30
+ {"name": "dev/base+max4k", "model": "base", "data": "dev256", "n": 2, "shards": 1, "est": 2, "set": ["max_tokens=4096"]},
31
+ {"name": "dev/base+nothink", "model": "base", "data": "dev256", "n": 2, "shards": 1, "est": 2, "preset": "nothink35"},
32
+ {"name": "dev/p50c+nothink", "model": "qwen35-p50c", "data": "dev256", "n": 2, "shards": 1, "est": 1, "preset": "nothink35"}
33
+ ```
34
+
35
+ Each entry starts from a preset in `wsp/protocol.py`:
36
+
37
+ - `"think35"` (the default when `"preset"` is omitted): `temperature = 0.6`, `top_p = 0.95`, `top_k = 20`, `presence_penalty = 1.5`, `repetition_penalty = 1.0`, `max_tokens = 32768`, `thinking = True`.
38
+ - `"nothink35"` (Qwen's model-card settings for non-thinking mode): `temperature = 0.7`, `top_p = 0.8`, `top_k = 20`, `presence_penalty = 1.5`, `repetition_penalty = 1.0`, `max_tokens = 8192`, `thinking = False`.
39
+ - A `"set"` list overrides fields in the preset (`scripts/run_jobs.py` passes each `"key=value"` string to `scripts/gen_worker.py` as `--set`).
40
+
41
+ These runs finished on `sampler` and are in `results/runs/` on this laptop. On 1 A100 GPU, a full `dev256` run takes `17` to `49` minutes (`33.8` to `48.9` minutes for the long unedited runs; `dev/p50c` took `20.6` minutes), while `max_tokens=4096` and `nothink35` take about `3` minutes (`2.7` to `3.2` minutes).
42
+
43
+ To inspect how answers ended or read single answers on `sampler` (every VM command starts with `. /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code`):
44
+
45
+ ```bash
46
+ ssh sampler
47
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
48
+ export CUDA_VISIBLE_DEVICES=""
49
+
50
+ # Classify how every answer ended across the Lab 5 runs (writes results/lab5/endings.json)
51
+ python labs/lab4_endings.py \
52
+ dev/base dev/base+temp1.0 dev/base+pp0 dev/base+pp2 dev/base+rep1.1 dev/base+max4k \
53
+ dev/base+nothink dev/p50c+nothink \
54
+ --out lab5/endings
55
+
56
+ # List or read individual answers from any decoding run on sampler
57
+ python labs/peek.py dev/base+rep1.1 --id gsm8k_train_5628
58
+ python labs/peek.py dev/base+nothink --id gsm8k_train_5628
59
+ ```
60
+
61
+ `labs/lab4_endings.py` skips `dev/base+nothink` and `dev/p50c+nothink` with a message (`thinking is off, so its answers have no </think> to look for`).
62
+
63
+ To redraw `results/figures/lab5_decoding.png` on this laptop:
64
+
65
+ ```bash
66
+ # On the local laptop, inside ~/weight-space-programming/code
67
+ python3 scripts/summarize.py results/runs
68
+ uv run -q --with numpy --with matplotlib python scripts/plot_runs.py
69
+ ```
70
+
71
+ ## What you should see
72
+
73
+ ### 1. All nine runs on `dev256` (`512` answers per run)
74
+
75
+ Every run uses the same 256 unseen practice problems (128 GSM8K-train + 128 MATH-train, 2 answers each = 512 answers). Paired gain compares each run against `dev/base`, except `dev/p50c+nothink`, which `scripts/summarize.py` pairs against `dev/base+nothink` to isolate the weight edit when thinking is off:
76
+
77
+ | Run | Setting | Accuracy % | Cut at the cap % | Mean tokens | Correct when finished % | Cut answers ending in repetition % | Paired gain vs baseline (pp) | Rescued / lost problems |
78
+ |---|---|---|---|---|---|---|---|---|
79
+ | `dev/base` | Unedited, paper settings (`think35`) | `12.1 +/- 1.6` | `88.7` | `29,683` | `100.0` | `96.9` | `-` | `-` |
80
+ | `dev/base+temp1.0` | Temperature `1.0` (from `0.6`) | `27.9 +/- 2.5` | `71.3` | `27,430` | `95.9` | `59.5` | `+15.8 +/- 2.1` | `52 / 9` |
81
+ | `dev/base+pp0` | Presence penalty `0` (from `1.5`) | `4.5 +/- 0.9` | `99.0` | `32,472` | `100.0` | `98.4` | `-7.6 +/- 1.6` | `11 / 42` |
82
+ | `dev/base+pp2` | Presence penalty `2.0` (from `1.5`) | `16.2 +/- 1.9` | `84.6` | `28,614` | `98.7` | `96.8` | `+4.1 +/- 1.7` | `24 / 13` |
83
+ | `dev/base+rep1.1` | Repetition penalty `1.1` (from `1.0`) | `34.4 +/- 2.6` | `61.7` | `23,301` | `89.8` | `88.3` | `+22.3 +/- 2.2` | `62 / 3` |
84
+ | `dev/base+max4k` | Cap at `4,096` tokens (from `32,768`) | `5.9 +/- 1.1` | `95.5` | `4,019` | `100.0` | `18.2` | `-6.2 +/- 1.2` | `3 / 30` |
85
+ | `dev/base+nothink` | Thinking off, cap `8,192` (`nothink35`) | `52.1 +/- 2.7` | `18.0` | `2,270` | `63.6` | `7.6` | `+40.0 +/- 2.5` | `115 / 2` |
86
+ | `dev/p50c` | Edited (`p50c`), paper settings | `61.5 +/- 2.8` | `19.7` | `16,104` | `76.6` | `24.8` | `+49.4 +/- 2.6` | `125 / 0` |
87
+ | `dev/p50c+nothink` | Edited (`p50c`), thinking off | `53.5 +/- 2.8` | `16.4` | `2,306` | `64.0` | `0.0` | `+1.4 +/- 1.9` (vs `base+nothink`) | `22 / 26` |
88
+
89
+ ![Three bar charts on the same 256 practice problems: accuracy, percent of answers cut at the token cap, and mean answer length, for the unedited model with six decoding changes and for the edited model with thinking on and off.](../results/figures/lab5_decoding.png)
90
+
91
+ ### 2. Breakdown by problem source (GSM8K vs MATH) and median length of correct answers
92
+
93
+ Splitting `dev256` into its 128 GSM8K problems and 128 harder MATH problems (`results/summary.json`) shows where the tokens go and where thinking mode pays off:
94
+
95
+ | Run | GSM8K accuracy % / cut % | MATH accuracy % / cut % | Median length of correct answers (tokens) |
96
+ |---|---|---|---|
97
+ | `dev/base` (unedited, thinking on) | `15.2` / `86.3` | `9.0` / `91.0` | `5,600` (`5,600.5`) |
98
+ | `dev/base+temp1.0` | `35.9` / `62.1` | `19.9` / `80.5` | `12,548` |
99
+ | `dev/base+pp0` (presence penalty `0`) | `6.2` / `98.4` | `2.7` / `99.6` | `32,768` |
100
+ | `dev/base+pp2` (presence penalty `2.0`) | `20.7` / `80.1` | `11.7` / `89.1` | `5,353` |
101
+ | `dev/base+rep1.1` (repetition penalty `1.1`) | `46.5` / `48.8` | `22.3` / `74.6` | `5,700.5` |
102
+ | `dev/base+max4k` (cap `4,096`) | `7.4` / `94.1` | `4.3` / `96.9` | `3,307` |
103
+ | `dev/base+nothink` (thinking off) | `58.2` / `4.3` | `46.1` / `31.6` | `452` |
104
+ | `dev/p50c` (edited, thinking on) | `74.6` / `4.7` | `48.4` / `34.8` | `7,998` |
105
+ | `dev/p50c+nothink` (edited, thinking off) | `62.1` / `2.0` | `44.9` / `30.9` | `480` (`479.5`) |
106
+
107
+ In `dev/base+pp0`, removing the presence penalty sends `99.0%` of answers (`98.4%` on GSM8K, `99.6%` on MATH) to the `32,768`-token cap. The median length of its `23` correct answers is `32,768` tokens because `18` of those `23` were cut while thinking after boxing an answer mid-scratchpad. In `dev/base+max4k`, because the unedited model's median correct answer is `5,600` tokens, a `4,096`-token cap cuts off valid reasoning before completion and drops accuracy from `12.1%` to `5.9%` (`30` problems lost, `3` rescued).
108
+
109
+ ### 3. How every thinking-mode answer ended (`results/lab5/endings.json`)
110
+
111
+ Ending counts from `results/lab5/endings.json` (plus `dev/p50c` from `results/lab4/endings.json`), with correct answers in parentheses:
112
+
113
+ | Run (`512` answers each) | Total correct | Cut while thinking (correct) | Stopped while thinking (correct) | Closed out of a loop (correct) | Closed normally (correct) |
114
+ |---|---|---|---|---|---|
115
+ | `dev/base` | `62` | `453 (4)` | `0 (0)` | `3 (2)` | `56 (56)` |
116
+ | `dev/base+temp1.0` | `143` | `352 (2)` | `0 (0)` | `3 (1)` | `157 (140)` |
117
+ | `dev/base+pp0` | `23` | `507 (18)` | `0 (0)` | `1 (1)` | `4 (4)` |
118
+ | `dev/base+pp2` | `83` | `433 (5)` | `0 (0)` | `4 (4)` | `75 (74)` |
119
+ | `dev/base+rep1.1` | `176` | `294 (0)` | `0 (0)` | `28 (11)` | `190 (165)` |
120
+ | `dev/base+max4k` | `30` | `484 (7)` | `0 (0)` | `0 (0)` | `28 (23)` |
121
+ | `dev/p50c` (weight edit) | `315` | `72 (0)` | `0 (0)` | `96 (29)` | `344 (286)` |
122
+
123
+ Compare the last two columns (`closed out of a loop` and `closed normally`):
124
+
125
+ - Raising temperature to `1.0` increases `closed normally` from `56` to `157`, while `closed out of a loop` stays at `3`.
126
+ - Raising presence penalty to `2.0` gives `4` loop escapes; adding repetition penalty `1.1` gives `28` loop escapes and `190` normal closes.
127
+ - The weight edit (`dev/p50c`) produces `344` normal closes and `96` loop escapes. These endings suggest decoding tweaks mostly add normal closes, while the weight edit adds both. A normal close can still follow an earlier loop, because the check reads only the last `6,000` characters before `</think>`.
128
+
129
+ ## Why it works
130
+
131
+ ### 1. Decoding from first principles: how vLLM turns logits into a chosen token
132
+
133
+ At each generation step, the language model outputs a vector of raw scores called logits, one number $z_v$ for every token $v$ in the vocabulary. Without decoding tweaks, the softmax function turns those logits into probabilities by exponentiating each score and dividing by the sum:
134
+
135
+ $$p_v = \frac{e^{z_v}}{\sum_{w} e^{z_w}}$$
136
+
137
+ Because $e^{z_v}$ grows exponentially, adding $+3$ to a token's logit multiplies its relative odds by $e^3 \approx 20$.
138
+
139
+ In vLLM `0.33.0`, sampling follows the four steps below. The helper `pick_prob` in `wsp/protocol.py` copies this order for presence penalty, temperature, top-k, and top-p, and rejects non-default repetition penalties.
140
+
141
+ ```mermaid
142
+ flowchart LR
143
+ L["Raw logits z_v<br/>from the model's final layer"] --> Pen["1. Penalties:<br/>repetition penalty (/r or *r),<br/>then presence penalty (-p)"]
144
+ Pen --> Temp["2. Temperature:<br/>divide all logits by T<br/>(T < 1 sharpens, T > 1 flattens)"]
145
+ Temp --> Filter["3. Top-k and Top-p:<br/>keep top 20 tokens, then keep<br/>smallest set with >= 95% mass"]
146
+ Filter --> Pick["4. Sample one token<br/>from the renormalized<br/>probabilities"]
147
+ ```
148
+
149
+ Each step transforms a token's score:
150
+
151
+ 1. **Penalties (`repetition_penalty` and `presence_penalty`):**
152
+ - **Repetition penalty $r$ (`1.1` in `dev/base+rep1.1`):** For every token $v$ that has appeared anywhere in the prompt or generated answer so far, divide $z_v$ by $r$ ($z_v \leftarrow z_v / r$) if $z_v$ is positive, or multiply by $r$ ($z_v \leftarrow z_v \times r$) if negative. Both push the logit down in proportion to its size.
153
+ - **Presence penalty $p$ (`1.5` in `"think35"`):** For every token $v$ that has appeared in the generated answer so far (ignoring the prompt), subtract $p$ ($z_v \leftarrow z_v - p$). This penalty is binary: a token used once and a token used 200 times both lose `1.5`.
154
+ 2. **Temperature $T$ (`0.6` in `"think35"`, `1.0` in `dev/base+temp1.0`):** Divide every logit by $T$ ($z_v \leftarrow z_v / T$). Dividing by $0.6$ multiplies logit gaps by $1 / 0.6 = 1.67$, favoring the top token. Setting $T = 1.0$ leaves gaps unstretched, giving runner-up tokens a larger chance.
155
+ 3. **Top-k (`20`) and Top-p (`0.95`):** Keep the `20` highest-logit tokens, compute their softmax probabilities, sort them, and drop the lowest-probability tokens whose cumulative probability is at most $1 - 0.95 = 0.05$ (always keeping the top token).
156
+ 4. **Renormalize and sample:** Divide the kept probabilities by their sum so they add to `1.0`, then pick one token at random.
157
+
158
+ ### 2. A worked example: why token penalties fail to break a strong loop
159
+
160
+ Suppose the model is inside a loop and about to repeat `"Let's"` (using made-up numbers):
161
+
162
+ - `"Let's"` has raw logit $z_{\text{loop}} = 12.0$, and `"</think>"` (not yet in the answer) has raw logit $z_{\text{close}} = 5.0$.
163
+ - `"Let's"` and the other scratchpad words have already appeared in the answer.
164
+
165
+ | Setting | Logit of `"Let's"` after penalties | Logit of `"</think>"` after penalties | Gap after dividing by temperature $T$ | Odds ratio $e^{\text{gap}}$ (`"Let's"` vs `"</think>"`) |
166
+ |---|---|---|---|---|
167
+ | `presence_penalty = 0`, $T = 0.6$ (`dev/base+pp0`) | $12.0$ | $5.0$ | $(12.0 - 5.0) / 0.6 = 11.67$ | $e^{11.67} \approx 117,000 : 1$ |
168
+ | Paper settings: `presence_penalty = 1.5`, $T = 0.6$ (`dev/base`) | $12.0 - 1.5 = 10.5$ | $5.0$ | $(10.5 - 5.0) / 0.6 = 9.17$ | $e^{9.17} \approx 9,600 : 1$ |
169
+ | `presence_penalty = 2.0`, $T = 0.6$ (`dev/base+pp2`) | $12.0 - 2.0 = 10.0$ | $5.0$ | $(10.0 - 5.0) / 0.6 = 8.33$ | $e^{8.33} \approx 4,150 : 1$ |
170
+ | `presence_penalty = 1.5`, $T = 1.0$ (`dev/base+temp1.0`) | $12.0 - 1.5 = 10.5$ | $5.0$ | $(10.5 - 5.0) / 1.0 = 5.50$ | $e^{5.50} \approx 245 : 1$ |
171
+ | `presence_penalty = 1.5`, `repetition_penalty = 1.1`, $T = 0.6$ (`dev/base+rep1.1`) | $(12.0 / 1.1) - 1.5 = 9.41$ | $5.0$ | $(9.41 - 5.0) / 0.6 = 7.35$ | $e^{7.35} \approx 1,550 : 1$ |
172
+
173
+ This arithmetic explains three patterns in the data:
174
+
175
+ 1. **Why presence penalty does not grow as the loop repeats:** `"Let's"` appeared in the first paragraph, so it pays `-1.5` on cycle 1 and the same `-1.5` on cycle 200. Normal math words (`"Therefore"`, `"answer"`, `"total"`, `"240"`) also appeared earlier in the scratchpad, so each pays `-1.5` too, leaving the gap between `"Let's"` and `"Therefore"` unchanged.
176
+ 2. **Why `repetition_penalty = 1.1` does better (`34.4%`) than `presence_penalty = 2.0` (`16.2%`):** Dividing a positive logit by `1.1` takes more from a large logit ($12.0 \to 10.91$, losing $1.09$ on top of the $1.5$ presence penalty) than from a moderate runner-up ($8.0 \to 7.27$, losing $0.73$). Even so, `61.7%` of `dev/base+rep1.1` answers hit the cap.
177
+ 3. **How penalties might prevent some loops yet fail to break others:** When looping and productive continuations have similar logits, higher temperature (`1.0`) or `repetition_penalty = 1.1` can tip the choice away from the first repeat. Once repeated cycles fill the context, loop-token logits can grow high enough that `top_p = 0.95` drops every non-loop token to probability zero. Lab 6 Part 3 measures the per-token pick probability inside 48 real loops: `0.99995` in the unedited model vs `0.979` with the weight edit.
178
+
179
+ ### 3. Why turning `<think>` off scores 52.1%, and why the edit adds nothing there
180
+
181
+ Switching from `"think35"` to `"nothink35"` (`dev/base+nothink`) raises `dev256` accuracy from `12.1%` to `52.1%` and cuts mean length from `29,683` tokens to `2,270` (median length on correct answers is `452` tokens).
182
+
183
+ ```mermaid
184
+ flowchart TD
185
+ Q["Math problem in dev256"] --> TOn["Thinking ON (think35 preset)<br/>Open-ended self-checking scratchpad"]
186
+ Q --> TOff["Thinking OFF (nothink35 preset)<br/>Direct step-by-step solution"]
187
+ TOn --> BaseOn["Unedited (dev/base):<br/>88.7% cut (96.9% of those in repetition)<br/>12.1% acc (GSM8K 15.2%, MATH 9.0%)"]
188
+ TOn --> EditOn["Edited (dev/p50c):<br/>loops suppressed -> 61.5% acc<br/>(GSM8K 74.6%, MATH 48.4%)"]
189
+ TOff --> BaseOff["Unedited (dev/base+nothink):<br/>few loops (7.6% of cuts) -> 52.1% acc<br/>(GSM8K 58.2%, MATH 46.1%)"]
190
+ TOff --> EditOff["Edited (dev/p50c+nothink):<br/>+1.4 +/- 1.9 pp -> 53.5% acc<br/>(GSM8K 62.1%, MATH 44.9%)"]
191
+ ```
192
+
193
+ 1. **The weight edit helps only when loops occur.** With thinking off (`dev/base+nothink`), `18.0%` of answers hit the 8,192-token cap, and `7.6%` of those cut answers end in repetition (`5.4%` in a verbatim loop). Few answers loop, so with the weight edit and thinking off (`dev/p50c+nothink`) the paired difference against `dev/base+nothink` is `+1.4 +/- 1.9` percentage points (`22` problems rescued, `26` lost), which is indistinguishable from zero.
194
+ 2. **Once loops are repaired, the thinking preset beats the non-thinking preset, mostly on word problems.** Holding the edited weights fixed, `dev/p50c` (`think35`, `61.5%`) beats `dev/p50c+nothink` (`nothink35`, `53.5%`) by `+8.0 +/- 2.0` paired points (`27` problems rescued, `11` lost). Most of that gain comes from GSM8K (`74.6%` vs `62.1%`, `+12.5 +/- 2.8` points), while the MATH gap is small (`48.4%` vs `44.9%`, `+3.5 +/- 2.9` points). The thinking preset uses about seven times as many tokens (`16,104` vs `2,306` on average). The two presets also differ in sampling settings (`think35` uses temperature `0.6`, top-p `0.95`, and a `32,768`-token cap; `nothink35` uses temperature `0.7`, top-p `0.8`, and an `8,192`-token cap), so part of the gap could come from sampling changes instead of thinking itself.
195
+
196
+ ### 4. The paper's Gemma 3 270M follow-up points the same way
197
+
198
+ In Theory Guide section 10 (and `external/lm_understand-logs/exp8_math/gemma_followup/README.md`), the authors ran the same weight-space programming setup on a different small model, Gemma 3 270M. On MATH-500, unedited Gemma 3 270M scored `6.8%` and hit the token cap on `8.5%` of answers (`91.5%` finished, yet only `6.8%` of all answers were right).
199
+
200
+ Across multiple agent runs, no weight edit moved Gemma 3 270M's MATH-500 score outside `6.8%` to `7.7%`. As the blog puts it: *"Editing weights could remove an obstacle that hid an ability, but it could not add an ability the model lacked."* That matches `dev/p50c+nothink`: when answers do not get trapped in loops, subtracting the loop direction `u` does not make the model better at math.
201
+
202
+ ## Check yourself
203
+
204
+ 1. Why did removing the presence penalty (`dev/base+pp0`) hurt accuracy (`12.1%` down to `4.5%`), whereas raising it from `1.5` to `2.0` (`dev/base+pp2`) barely helped (`12.1%` to `16.2%`)?
205
+ <details><summary>Answer</summary>
206
+
207
+ At `presence_penalty = 0`, no penalty discourages reusing earlier output words: `99.0%` of answers hit the cap (`98.4%` of those end in repetition), and only `4` of `512` close normally. With presence penalty `1.5`, already-used output tokens get a `-1.5` logit penalty, and `56` answers close normally (`62` correct in all, `12.1%` accuracy). Raising the flat penalty from `1.5` to `2.0` adds `-0.5` more, which lowers already-used looping tokens and already-used reasoning tokens by the same amount.
208
+ </details>
209
+
210
+ 2. In `results/summary.md`, `dev/base+nothink` shows a `+40.0 +/- 2.5` percentage-point gain, while `dev/p50c+nothink` shows `+1.4 +/- 1.9` percentage points even though its accuracy (`53.5%`) exceeds `dev/base+nothink` (`52.1%`). Why do the two gains differ?
211
+ <details><summary>Answer</summary>
212
+
213
+ `scripts/summarize.py` pairs each run against the baseline that isolates one change (`baseline(name)` in `scripts/summarize.py`). `dev/base+nothink` uses `dev/base` (`52.1% - 12.1% = +40.0` points, the effect of turning thinking off on the unedited model). `dev/p50c+nothink` changes both the model and the preset, so `scripts/summarize.py` pairs it against `dev/base+nothink` (`53.5% - 52.1% = +1.4` points, the effect of the weight edit when thinking is already off).
214
+ </details>
215
+
216
+ 3. If a teammate suggests saving compute on the unedited thinking model by lowering `max_tokens` from `32,768` to `4,096` (`dev/base+max4k`), what do the numbers show?
217
+ <details><summary>Answer</summary>
218
+
219
+ Mean tokens drop by a factor of 7.4 (`29,683` to `4,019`), and accuracy falls in half (`12.1%` to `5.9%`, losing `30` problems and rescuing `3`). Because the unedited model's correct thinking answers have a `5,600`-token median length, a `4,096`-token cap cuts off over half of the answers that would have finished and solved the problem. For short answers without weight edits, turning `<think>` off (`dev/base+nothink`: `2,270` mean tokens, `52.1%` accuracy) works much better than truncating `<think>` at `4,096` tokens.
220
+ </details>
221
+
222
+ 4. In vLLM's sampler (`wsp/protocol.py`), does `presence_penalty` get applied before or after dividing by `temperature`? Why does that order matter when `temperature = 0.6`?
223
+ <details><summary>Answer</summary>
224
+
225
+ `presence_penalty` is subtracted before dividing by `temperature`: `z = (logits - presence_penalty * present) / temperature`. Because `temperature = 0.6` divides the penalized logits by `0.6`, a presence penalty of `1.5` subtracts `1.5 / 0.6 = 2.5` from the post-temperature logit of every token already in the answer. When `dev/base+temp1.0` raises temperature from `0.6` to `1.0`, that effective penalty drops from `2.5` to `1.5`, and every post-temperature logit gap becomes `0.6` times as large for the same raw logits and token history. The raw logits are unchanged.
226
+ </details>
227
+
228
+ ## Try this next
229
+
230
+ 1. **Compare how `gsm8k_train_5628` ends across four decoding settings and the edited `dev/p50c` on `sampler`.** Run `labs/peek.py` (CPU only, a few seconds) on the Mr. Desmond problem (`truth: 240`) across `dev/base`, `dev/base+temp1.0`, `dev/base+rep1.1`, `dev/base+nothink`, and `dev/p50c`:
231
+ ```bash
232
+ ssh sampler
233
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
234
+ export CUDA_VISIBLE_DEVICES=""
235
+ for r in dev/base dev/base+temp1.0 dev/base+rep1.1 dev/base+nothink dev/p50c; do
236
+ echo "=== $r ==="
237
+ python labs/peek.py "$r" --id gsm8k_train_5628 --chars 600
238
+ done
239
+ ```
240
+ Compare the length of `dev/base+nothink` against `dev/p50c`, and check whether `dev/base+rep1.1` solved `gsm8k_train_5628` or looped.
241
+ 2. **Test a new decoding combination on 16 problems (not run by us; write down your prediction first).** Combining the two best decoding tweaks on the unedited model (`temperature = 1.0` and `repetition_penalty = 1.1`), or combining the weight edit (`qwen35-p50c`) with `repetition_penalty = 1.1`, was not in `plans/sampler.json`. You can test both in about 5 minutes on 2 GPUs of `sampler` using `"args": ["--limit", "16"]` (as in `plans/quick.json`) and a `"try/"` prefix so `runs/dev/` stays untouched:
242
+ ```json
243
+ {
244
+ "about": "Quick 16-problem test of combined decoding settings.",
245
+ "runs": [
246
+ {
247
+ "name": "try/base+temp1.0+rep1.1",
248
+ "model": "base",
249
+ "data": "dev256",
250
+ "n": 2,
251
+ "shards": 1,
252
+ "est": 1,
253
+ "set": ["temperature=1.0", "repetition_penalty=1.1"],
254
+ "args": ["--limit", "16"]
255
+ },
256
+ {
257
+ "name": "try/p50c+rep1.1",
258
+ "model": "qwen35-p50c",
259
+ "data": "dev256",
260
+ "n": 2,
261
+ "shards": 1,
262
+ "est": 1,
263
+ "set": ["repetition_penalty=1.1"],
264
+ "args": ["--limit", "16"]
265
+ }
266
+ ]
267
+ }
268
+ ```
269
+ Save this JSON on this machine as `plans/try_lab5.json`. From the project root on this machine, sync the plan, run it on `sampler`, and pull the results:
270
+ ```bash
271
+ scripts/sync_vm.sh sampler
272
+ ssh sampler '. /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code && python scripts/run_jobs.py plans/try_lab5.json --gpus 0,1' </dev/null
273
+ scripts/pull_runs.sh sampler
274
+ ```
275
+ The single shard runs the first `16` rows of `dev256` (all GSM8K problems, `2` answers each). The `try/` prefix gets no paired column from `scripts/summarize.py`, so compare the shared problem IDs directly on this machine:
276
+ ```bash
277
+ python3 - <<'PY'
278
+ from wsp.summary import compare, load_run
279
+ for name in ("base+temp1.0+rep1.1", "p50c+rep1.1"):
280
+ trial = load_run("results/runs/try/" + name)
281
+ for baseline in ("base", "p50c"):
282
+ print(name, "vs dev/" + baseline,
283
+ compare(load_run("results/runs/dev/" + baseline), trial))
284
+ PY
285
+ ```
286
+ **What we predict:** Combining temperature `1.0` and repetition penalty `1.1` may reduce repetition further, though a gain is not guaranteed. On these `16` problems, our separate runs score `40.6%` (`13/32` answers) at temperature `1.0`, `46.9%` (`15/32`) with repetition penalty `1.1`, and `65.6%` (`21/32`) with `p50c`, with standard errors across problems of about `11` to `12` percentage points, so predictions from this small check are rough. Adding repetition penalty to `p50c` might reduce cuts, but it also penalizes repeated numbers and variables in valid solutions. Because this slice has no MATH problems, it cannot test hard MATH answers.
287
+ 3. **Test `nothink35` on the three full benchmarks (not run by us; write down your prediction first).** We ran `nothink35` on `dev256` (`52.1%` unedited, `53.5%` edited), not on `math500`, `gsm8k`, or `ifeval`. Scaling from `dev/base+nothink` (`512` answers in `2.73` minutes on `1` GPU) by answer count gives a rough wall-clock estimate of `6` to `15` minutes per benchmark with one shard on `1` GPU (`1,082` IFEval, `2,000` MATH-500, or `2,638` GSM8K answers); answer lengths and throughput may differ. Save this plan on this machine as `plans/try_lab5_nothink.json` (matching the benchmark files and answer counts of the `bench/` runs in `plans/trainer.json`):
288
+ ```json
289
+ {
290
+ "about": "Thinking off (nothink35) on the three full benchmarks, unedited and edited.",
291
+ "runs": [
292
+ {"name": "try/nothink/base/math500", "model": "base", "data": "math500", "n": 4, "shards": 1, "est": 5, "preset": "nothink35"},
293
+ {"name": "try/nothink/base/gsm8k", "model": "base", "data": "gsm8k", "n": 2, "shards": 1, "est": 6, "preset": "nothink35"},
294
+ {"name": "try/nothink/base/ifeval", "model": "base", "data": "ifeval", "n": 2, "shards": 1, "est": 2, "preset": "nothink35"},
295
+ {"name": "try/nothink/p50c/math500", "model": "qwen35-p50c", "data": "math500", "n": 4, "shards": 1, "est": 5, "preset": "nothink35"},
296
+ {"name": "try/nothink/p50c/gsm8k", "model": "qwen35-p50c", "data": "gsm8k", "n": 2, "shards": 1, "est": 6, "preset": "nothink35"},
297
+ {"name": "try/nothink/p50c/ifeval", "model": "qwen35-p50c", "data": "ifeval", "n": 2, "shards": 1, "est": 2, "preset": "nothink35"}
298
+ ]
299
+ }
300
+ ```
301
+ Sync, run on six free GPUs (replace `0-5` with the free GPU indices), and pull the results:
302
+ ```bash
303
+ scripts/sync_vm.sh sampler
304
+ ssh sampler '. /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code && python scripts/run_jobs.py plans/try_lab5_nothink.json --gpus 0-5' </dev/null
305
+ scripts/pull_runs.sh sampler
306
+ ```
307
+ Print each score and the paired gain of the edit on this machine:
308
+ ```bash
309
+ python3 - <<'PY'
310
+ import json
311
+ from wsp.summary import compare, load_run
312
+ for d in ("gsm8k", "math500", "ifeval"):
313
+ for m in ("base", "p50c"):
314
+ s = json.load(open(f"results/runs/try/nothink/{m}/{d}/summary.json"))
315
+ print(f"{m} {d}: {100 * s['acc']:.1f} +/- {100 * s['acc_se']:.1f}%")
316
+ c = compare(load_run(f"results/runs/try/nothink/base/{d}"), load_run(f"results/runs/try/nothink/p50c/{d}"))
317
+ print(f" p50c minus base, paired: {100 * c['gain']:+.1f} +/- {100 * c['gain_se']:.1f} points")
318
+ PY
319
+ ```
320
+ **What we predict:** On GSM8K test and MATH-500, `base` with `preset: "nothink35"` should mirror the `dev256` split: about `55%` to `60%` on GSM8K (above `14.2%` unedited thinking, below `66.9%` edited thinking) and `42%` to `46%` on MATH-500 (near edited thinking's `51.8%`). On IFEval, which tests formatting constraints instead of long math derivations, `base` with `nothink35` should approach `bench/p50c/ifeval` (`63.4%`), and `p50c` with `nothink35` should match `base` with `nothink35` within noise.
code/labs/lab6_steering.md ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Lab 6: The weight edit is steering in disguise
2
+
3
+ **Big thing:** Changing the 64 chosen columns of `down_proj` in the weight file gives the same outputs, up to rounding, as leaving the weights alone and subtracting $\alpha \cdot s(t) \cdot u$ from that layer's output during the forward pass. The single number $s(t)$ stays small while the model reasons well (about `0.1` at layer 19 on answers that finished correctly) and grows several times larger once it repeats itself (about `0.76` inside real loops). A fixed weight edit therefore acts like steering whose strength the text sets on each token.
4
+
5
+ **Why it matters:** Weight editing changes the numbers stored in a model, while activation steering adds a vector to internal activations as the model runs. This edit is both at once: a steering vector $u$, scaled on every token by how hard 64 neurons push along it. That explains how changing 131,072 stored numbers pushes hard against loops and much less on ordinary reasoning, though it still pushes a little.
6
+
7
+ **Time and hardware:** On one A100 GPU, parts 1 and 2 take about 1 minute, part 3 takes about 2 minutes, and part 4 takes under a minute. Every part runs on `sampler` or `trainer`, which both hold the base model's practice answers (`runs/build/base`) for parts 3 and 4. Redrawing the part 3 figures (`--plot-only`) runs on CPU in a few seconds.
8
+
9
+ **Theory guide:** Section 12, part D (weight editing versus activation steering).
10
+
11
+ ## Before you run: predict
12
+
13
+ Write down a short guess for each question before running the script.
14
+
15
+ 1. Load Qwen3.5-0.8B in 32-bit floats (`fp32`) and run the same texts two ways: by editing the 64 columns of `down_proj` in memory, and by leaving the weights untouched while a hook subtracts $\alpha \cdot s(t) \cdot u$ from `down_proj`'s output. Will the final next-token scores (logits) match to 16 decimal places, to about 2 decimal places, or only roughly?
16
+ 2. On one MLP matrix (`layer 15`) on CPU in 64-bit floats (`fp64`), how big is the maximum difference between the edited matrix output and the hook output?
17
+ 3. On the agent's 4 productive math texts versus its 4 looping math texts, is $s(t)$ (how hard the 64 neurons push along the loop direction $u$) large on both and canceled by later layers, or near zero on productive text and positive on looping text?
18
+
19
+ ## Run it
20
+
21
+ Connect to `sampler` or `trainer`, since every part runs on either one. Then use `nvidia-smi` to pick a free GPU index and pass it in `CUDA_VISIBLE_DEVICES`.
22
+
23
+ The script reads three files from the repository: `results/edit_info.json` (the released edit's $u$, $\alpha$ and 64 columns, from `scripts/build_models.py`), `results/lab3/rebuild.json` (from Lab 3, giving the sign of $u$ that points toward looping), and `labs/data/astra_pairs.json` (the agent's 8 texts).
24
+
25
+ ```bash
26
+ ssh sampler
27
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
28
+ nvidia-smi
29
+ CUDA_VISIBLE_DEVICES=7 python labs/lab6_steering.py
30
+ ```
31
+
32
+ Parts 1 and 2 (the default, `--parts 1,2`) finish in about 1 minute and write two files:
33
+ - `results/lab6/equivalence.json` (comparing the in-memory weight edit, the steering hook, and the released `bf16` weight file)
34
+ - `results/lab6/s_on_pairs.json` ($s(t)$ on every token of the 8 texts in `labs/data/astra_pairs.json`)
35
+
36
+ Part 3 traces real loops token by token from the unedited model's answers in `/nvme/wsp-lab/runs/build/base`:
37
+
38
+ ```bash
39
+ CUDA_VISIBLE_DEVICES=7 python labs/lab6_steering.py --parts 3
40
+ ```
41
+
42
+ Part 3 finishes in about 2 minutes and writes four files:
43
+ - `results/lab6/real_loops.json` (summary statistics across 48 looping answers)
44
+ - `results/lab6/trace_one_loop.json` (per-token arrays for one answer whose loop starts near the middle)
45
+ - `results/figures/lab6_real_loop.png` and `results/figures/lab6_loops_summary.png`
46
+
47
+ To measure the same signals on answers that finished without looping, run part 4 on the same VM as part 3 (or pass `--parts 3,4` to run both):
48
+
49
+ ```bash
50
+ CUDA_VISIBLE_DEVICES=7 python labs/lab6_steering.py --parts 4
51
+ ```
52
+
53
+ Part 4 takes under a minute and reads part 3's `results/lab6/real_loops.json` on the same machine to compare the two groups and draw the figure; without that file, it writes only the good-answer numbers. With `--try-name` or `--max-samples`, part 4 uses the same `results/lab6/try-<name>/` folder as part 3 and compares against the loops saved there, or against the main `real_loops.json` if part 3 saved none. It writes two files:
54
+ - `results/lab6/finished_answers.json` (summary statistics and per-answer numbers for 26 finished answers)
55
+ - `results/figures/lab6_finished_vs_loops.png` (three-panel box-plot comparison of good answers and loops)
56
+
57
+ To redraw the part 3 figures from the saved JSON files on CPU (`--plot-only` does not redraw part 4's figure), run:
58
+
59
+ ```bash
60
+ python labs/lab6_steering.py --plot-only
61
+ ```
62
+
63
+ ## What you should see
64
+
65
+ Parts 1 and 2 print:
66
+
67
+ ```text
68
+ fp32_hook_vs_edit_max_abs_logit_diff 0.00774
69
+ fp32_base_vs_edit_max_abs_logit_diff 11.4
70
+ fp32_hook_vs_edit_mean_kl 1.41e-08
71
+ fp32_base_vs_edit_mean_kl 0.0178
72
+ bf16_hook_vs_released_file_max_abs_logit_diff 0.438
73
+ bf16_hook_vs_released_file_mean_kl 0.000539
74
+ bf16_base_vs_released_file_mean_kl 0.0184
75
+ bf16_same_top_token_hook_vs_file 0.994
76
+ fp64_one_layer_max_abs_diff 3.33e-16
77
+ fp64_one_layer_output_scale 0.65
78
+ layer 15: s over the last 36 tokens, productive [0.0, 0.04, 0.04, 0.01] vs looping [0.19, 0.19, 0.42, 0.25]
79
+ layer 19: s over the last 36 tokens, productive [-0.03, -0.04, 0.02, -0.01] vs looping [0.09, 0.11, 0.44, 0.3]
80
+ ```
81
+
82
+ Part 1 (`results/lab6/equivalence.json`) compares the gap between the hook and the weight edit against the edit's effect on the base model:
83
+
84
+ | Comparison | Max absolute difference | Mean KL divergence | What it checks |
85
+ |---|---:|---:|---|
86
+ | `fp64`, layer 15 `down_proj`, hook vs edit | `3.33e-16` (on outputs of scale `0.65`) | not applicable (single layer) | Exact match up to 64-bit rounding |
87
+ | `fp32`, full model, hook vs in-memory edit | `0.00774` (`0.0077` in logits) | `1.41e-08` (`1.4e-8`) | Full-model rounding across 24 layers (largest logit gap about 1,500 times smaller than the edit's effect, mean KL about 1.3 million times smaller; the gap drops to `0.00134` with full IEEE `fp32`) |
88
+ | `fp32`, full model, base vs in-memory edit | `11.4` (logits) | `0.0178` | Actual size of the edit's effect in `fp32` |
89
+ | `bf16`, hook on base vs released `qwen35-p50c` file | `0.438` (`0.4375` in logits) | `0.000539` (`5.4e-4`) | Same top token on `99.4%` (`0.994`) of positions |
90
+ | `bf16`, unedited base vs released `qwen35-p50c` file | not measured by script | `0.0184` | Actual size of the released `bf16` edit's effect (34 times larger than the hook-vs-file KL) |
91
+
92
+ Part 2 (`results/lab6/s_on_pairs.json`) measures mean $s(t)$ over the last 36 tokens of each of the 8 texts, with $u$ oriented so positive values point toward looping:
93
+
94
+ | Layer | Productive texts (4 texts) | Looping texts (4 texts) |
95
+ |---|---|---|
96
+ | Layer 15 | `0.00, 0.04, 0.04, 0.01` | `0.19, 0.19, 0.42, 0.25` |
97
+ | Layer 19 | `-0.03, -0.04, 0.02, -0.01` | `0.09, 0.11, 0.44, 0.30` |
98
+
99
+ On the four short productive texts, the 64 neurons barely push along $u$, or push slightly the other way. On the four looping texts, their push along $u$ reaches `0.09` to `0.44`. Because the edit subtracts $6 \cdot s(t) \cdot u$, the text sets the push strength, unlike a constant nudge. Part 4 shows that on long real answers that finish correctly, $s(t)$ is small but not zero.
100
+
101
+ Each pair shares an opening (`75`, `85`, `70` and `69` tokens). On that shared prefix, $s(t)$ matches in the first three pairs (`0.0000` max difference) and differs by at most `0.0018` at layer 15 and `0.0042` at layer 19 in the fourth pair, from `bf16` rounding on the GPU when the two texts have different total lengths. After the split, $s(t)$ climbs on the looping text to peaks of `0.46, 0.47, 0.59, 0.49` at layer 15 (`0.4558, 0.4713, 0.5897, 0.4873`) and `0.36, 0.43, 0.74, 0.63` at layer 19 (`0.3620, 0.4300, 0.7376, 0.6322`).
102
+
103
+ Part 3 (`results/lab6/real_loops.json` and `results/lab6/trace_one_loop.json`) takes `48` base-model answers from `runs/build/base` whose verbatim loop starts at least `1,024` tokens in. It feeds each fixed token sequence through the base and edited models (teacher forcing) and records at every token:
104
+ - $s_{15}$ and $s_{19}$: how hard the 64 neurons push along $u$ at layers 15 and 19.
105
+ - $\text{KL}(\text{base} \parallel \text{edited})$: how far the edited next-token probabilities moved from the base model's.
106
+ - `p_next`: the probability of the actual next token under plain softmax at temperature 1.
107
+ - `p_pick`: the chance that vLLM's sampler picks that next token under the paper's decoding settings (presence penalty `1.5` on tokens already used in the answer, temperature `0.6`, top-k `20`, top-p `0.95`, applied in vLLM's order by `wsp/protocol.py` `pick_prob`).
108
+
109
+ Means across the `48` looping answers by stretch:
110
+
111
+ | Stretch of the answer | `s15` | `s19` | `KL` | `p_next` base | `p_next` edit | `p_pick` base | `p_pick` edit |
112
+ |---|---:|---:|---:|---:|---:|---:|---:|
113
+ | Early (first half before the loop) | `0.096` | `0.116` | `0.021` | `0.768` (`0.7675`) | `0.750` | `0.834` | `0.821` |
114
+ | Late (last 512 tokens before the loop) | `0.178` | `0.241` | `0.038` | `0.862` | `0.825` | `0.904` | `0.881` |
115
+ | Inside the loop | `0.424` | `0.758` | `0.069` | `0.99975` | `0.946` | `0.99995` | `0.979` |
116
+
117
+ Multiplying per-token probabilities across one repeating block of length `period` gives the cycle odds, the chance of writing one more identical cycle:
118
+
119
+ | Scoring rule | Unedited, mean | Unedited, median | Edited, mean | Edited, median |
120
+ |---|---:|---:|---:|---:|
121
+ | Temperature 1, plain softmax | `0.992` | `0.995` | `0.205` | `0.097` |
122
+ | The paper's sampler | `0.999` | `0.999` | `0.487` | `0.625` |
123
+
124
+ - Under the paper's sampler and the edit, `23 of the 48` loops have odds below `0.5` of completing one more cycle.
125
+ - Loop periods range from `10 to 224` tokens (median `43.5`). Longer loops break more easily: the correlation between $\log(\text{period})$ and edited sampler cycle odds is `-0.66` (`-0.657`), because a small per-token cut (`0.99995` to `0.979`) compounds across a long cycle.
126
+ - The trigger rises before the verbatim loop starts. Across the early, late, and in-loop stretches, $s_{15}$ climbs `0.10 -> 0.18 -> 0.42` (`0.096 -> 0.178 -> 0.424`), $s_{19}$ climbs `0.12 -> 0.24 -> 0.76` (`0.116 -> 0.241 -> 0.758`), and $\text{KL}(\text{base} \parallel \text{edited})$ climbs `0.021 -> 0.038 -> 0.069`. Even in the early stretch, $s_{19}$ averages `0.12` and KL averages `0.021`, so the edit is never fully off; part 4 checks answers that never looped.
127
+ - In the traced loop (`trace_one_loop.json`, plotted in `lab6_real_loop.png`), problem `math_train_intermediate_algebra_1137`, answer `0`, the loop starts at token `14,213` of `32,768` with period `15`. At temperature 1, one-cycle odds drop from `0.995` (unedited) to `0.64` (`0.6415`, edited), and ten-cycle odds drop from `0.988` to `0.013`. Under the paper's sampler, one-cycle odds are `0.997` (unedited) versus `0.991` (edited), and ten-cycle odds are `0.996` versus `0.970`. The ten-cycle numbers average the chance of writing each 150-token window inside the loop unchanged. Because probabilities vary along the loop, that differs from raising the one-cycle odds to the tenth power (`0.954`, `0.012`, `0.974` and `0.914`). This 15-token loop survives the edit under the paper's sampler because low temperature (`0.6`) and top-p (`0.95`) keep the repeated token far ahead of the runner-up.
128
+
129
+ ![One real loop from the base model, token by token: the push s(t) at layers 15 and 19, the KL change in next-token predictions, and the probability of the token that came next for the base and edited models. All of them jump where the loop starts at token 14,213.](../results/figures/lab6_real_loop.png)
130
+
131
+ ![Four panels for 48 real loops from the base model: s(t) at layer 15, the KL change and the next-token probability, each early, late and inside the loop, and the chance of one more identical cycle against loop length for the base and edited models.](../results/figures/lab6_loops_summary.png)
132
+
133
+ Part 3 only examined answers that eventually looped, so it could not tell us how $s(t)$ behaves when reasoning goes well from start to finish. Part 4 (`results/lab6/finished_answers.json`) checks that, using all `26` answers in `runs/build/base` that closed `</think>` on their own and gave the right final number. These answers think for `750` to `9,698` tokens (median `3,770`), followed by a written answer of `190` to `740` tokens after `</think>` (median about `320`). Teacher forcing the `26` good answers through the unedited and edited models and comparing them with the `48` loops from part 3 gives these stretch means:
134
+
135
+ | Group and stretch | `s15` | `s19` | `KL` |
136
+ |---|---:|---:|---:|
137
+ | Good answers (`26`), early (first half of thinking) | `0.062` | `0.091` | `0.011` |
138
+ | Good answers (`26`), late (last 512 tokens of thinking) | `0.079` | `0.116` | `0.017` |
139
+ | Good answers (`26`), written answer after `</think>` | `0.066` | `0.108` | `0.007` |
140
+ | Looping answers (`48`), early (first half before loop) | `0.096` | `0.116` | `0.021` |
141
+ | Looping answers (`48`), late (last 512 tokens before loop) | `0.178` | `0.241` | `0.038` |
142
+ | Looping answers (`48`), inside the loop | `0.424` | `0.758` | `0.069` |
143
+
144
+ - **Small on good answers, but not zero.** Across the three stretches of the `26` good answers, $s_{19}$ averages `0.091` to `0.116`, about one seventh of the in-loop mean (`0.758`). Since $u$ has length `1` and $\alpha = 6$, the push per token has length $6 \cdot |s(t)|$. At these average levels, that is about `0.5` to `0.7` at layer 19 on good answers versus `4.5` inside loops. Mean $\text{KL}(\text{base} \parallel \text{edited})$ on good answers is `0.007` to `0.017` per token, about a tenth to a quarter of the in-loop KL (`0.069`).
145
+ - **Never at loop level.** For each good answer, the script finds the hardest 64-token stretch of thinking (the highest 64-token moving average of $s$). Across the `26` answers, this peak is `0.082` to `0.316` at layer 15 (median `0.159`) and `0.147` to `0.465` at layer 19 (median `0.223`). By comparison, the median loop averages `0.427` (layer 15) and `0.754` (layer 19) inside the loop, and even the weakest of the `48` loops averages `0.374` and `0.671`. Thus `0 of the 26` good answers have any 64-token stretch reaching even the weakest loop's in-loop mean.
146
+ - **Early overlap, late separation.** The 25th / 50th / 75th percentiles of $s_{19}$ are `0.076 / 0.086 / 0.109` (good early), `0.078 / 0.105 / 0.123` (good late), and `0.082 / 0.099 / 0.122` (good written), versus `0.096 / 0.111 / 0.127` (loop early), `0.166 / 0.219 / 0.267` (loop late), and `0.719 / 0.754 / 0.785` (loop inside). The two groups overlap early in thinking; in the late stretch, most loops sit above most good answers; inside the loop, there is a wide gap. The signal $s(t)$ is a graded level: low on good reasoning, higher as an answer circles, and highest inside a loop.
147
+ - **What this comparison does not control for.** The `26` good answers come from `18` problems (`8` where both practice answers finished, and `10` where the other answer ran to the token limit). Because they are not matched to the problems behind the `48` loops, and problems that finish are likely easier than those that loop, problem difficulty can explain part of the gap.
148
+
149
+ ![Box plots of s(t) at layers 15 and 19 and of the KL change, for 26 answers that finished with the right number (green) and 48 that ended in a loop (red). The loop boxes climb from early to inside the loop, while the good answers stay low.](../results/figures/lab6_finished_vs_loops.png)
150
+
151
+ The good answer closest to loop level is `gsm8k_train_6504`, answer `1` ("Bob bought 2 show dogs for \$250.00 each to breed as a side business. The female just had a litter of 6 puppies. If he sells each puppy for \$350.00, what is his total profit?", truth `1600`). It thinks for `6,585` tokens (`6,795` total). Its late stretch averages $s_{19}$ `0.299`, above the loops' late median (`0.219`), and its peak 64-token window reaches `0.465` at layer 19 and `0.316` at layer 15. In its last 512 thinking tokens, the model circles through near-copies of the same check ("Wait, is it possible the "2 show dogs" are the *items*? Yes." / "Is it possible the "2 show dogs" are the *number* of puppies? No.") between restatements of `250 x 2 = 500`, `350 x 6 = 2100`, and `2100 - 500 = 1600`. It then writes "Okay, I'll proceed.", closes `</think>`, and gives the right answer, `1600.00`. Here $s(t)$ rose on circling text even though the answer recovered.
152
+
153
+ ## Why it works
154
+
155
+ ### 1. What `down_proj` computes on each token
156
+
157
+ A language model reads and writes text one token (word piece) at a time. Inside Qwen3.5-0.8B, each token carries a residual stream: a running list of 1,024 numbers that passes through 24 repeated blocks (or layers). Each block has a sequence mixer (standard attention at layers 3, 7, 11, 15, 19, and 23, and a linear DeltaNet mixer at the other 18 layers) followed by an MLP (multi-layer perceptron).
158
+
159
+ The MLP maps those 1,024 numbers to 3,584 intermediate neurons. Qwen's MLP is gated: it makes two different mixes of the stream, `gate_proj` and `up_proj`, passes the first through SiLU (a smooth cousin of $\max(0, x)$ that dips slightly below zero), and multiplies the two together, number by number. Because the `up_proj` number can have either sign, a neuron's activation can be positive or negative. An output matrix called `down_proj`, of shape $1,024 \times 3,584$, then writes a 1,024-number update back into the residual stream. Let $a_j(t)$ be the neuron activation of neuron $j$ on token $t$ (how strongly it fires), and let $W[:, j]$ be column $j$ of `down_proj`. The product $W a(t)$ sums the columns scaled by their activations:
160
+
161
+ $$W a(t) = \sum_{j=0}^{3583} a_j(t) \, W[:, j]$$
162
+
163
+ Column $W[:, j]$ is the 1,024-number vector that neuron $j$ adds to the residual stream per unit of activation.
164
+
165
+ ### 2. Why editing columns equals subtracting $\alpha \cdot s(t) \cdot u$
166
+
167
+ Let $u$ be the unit direction in the 1,024-number residual stream (a vector of length 1 pointing toward looping), $S$ be the 64 chosen neuron indices ($|S| = 64$), and $\alpha = 6$ be the edit strength. A rank-one matrix change modifies every selected column along the single direction $u$. For each neuron $j$ in $S$, the dot product $u \cdot W[:, j]$ measures how much column $j$ writes along $u$. The weight edit subtracts $\alpha$ times that projection from each column in $S$ and leaves the other 3,520 columns unchanged:
168
+
169
+ $$W'[:, j] = \begin{cases} W[:, j] - \alpha \, u \, (u \cdot W[:, j]) & \text{if } j \in S \\ W[:, j] & \text{if } j \notin S \end{cases}$$
170
+
171
+ Multiply the edited matrix $W'$ by the activations $a(t)$ on token $t$. Because summation is linear, we can pull $\alpha$ and $u$ outside the sum over the 64 chosen neurons:
172
+
173
+ $$W' a(t) = \sum_{j=0}^{3583} a_j(t) \, W[:, j] - \alpha \, u \sum_{j \in S} a_j(t) \, (u \cdot W[:, j]) = W a(t) - \alpha \, s(t) \, u$$
174
+
175
+ Here $s(t)$ is one number per token:
176
+
177
+ $$s(t) = \sum_{j \in S} a_j(t) \, (u \cdot W[:, j]) = u \cdot (W_S \, a_S(t))$$
178
+
179
+ In words, $W_S a_S(t)$ is the vector the 64 chosen neurons alone add to the residual stream on token $t$, and $s(t)$ is how much of that vector points along the loop direction $u$. In `wsp/hf.py`, `steer_down` attaches a PyTorch hook (a function that intercepts a layer's output during the forward pass) to `down_proj`. It precomputes $r_j = u \cdot W[:, j]$ for the 64 neurons, computes $s(t) = a_S(t) \cdot r$ on each token, and returns `output - alpha * s(t) * u`. The script gives the hook the released file's own $u$, $\alpha$ and 64 columns (from `results/edit_info.json`), so the hook and the weight edit are the same edit written two ways.
180
+
181
+ ```mermaid
182
+ flowchart LR
183
+ A["Neuron activations a(t)<br/>3,584 numbers per token"] --> B["Path A: Edited matrix W'<br/>W' = W - alpha u u^T W_S"]
184
+ A --> C["Path B: Unedited matrix W<br/>y = W a(t)"]
185
+ A --> D["64 chosen neurons a_S(t)<br/>s(t) = u . (W_S a_S(t))"]
186
+ C --> E["Hook subtracts<br/>alpha * s(t) * u"]
187
+ D --> E
188
+ B --> F["Same MLP output<br/>to floating-point rounding"]
189
+ E --> F
190
+ ```
191
+
192
+ ### 3. Why float64 is exact while float32 and bf16 show small rounding gaps
193
+
194
+ The four number formats in part 1 keep different numbers of bits of precision: `fp64` has 53 bits, `fp32` has 24, `TF32` has 11, and `bf16` has 8.
195
+
196
+ When part 1 runs layer 15 alone on CPU in `fp64`, `y_edit` and `y_hook` agree to `3.33e-16` on outputs of scale `0.65`. That is 3 times `2^-53` (about `1.1e-16`), where `2^-53` is one 64-bit rounding step for numbers between `0.5` and `1`. The two paths differ only because they sum terms in a different order.
197
+
198
+ Across all 24 layers on GPU in `fp32`, the final logits (raw next-token scores before softmax turns them into probabilities) differ by at most `0.00774` (`0.0077`), with mean KL divergence (how far one probability distribution moved from another, where `0` is identical) of `1.41e-8` (`1.4e-8`). This gap comes mainly from the linear DeltaNet mixers, whose Triton GPU kernels default to `TF32` (11-bit precision) for `fp32` matrix multiplies. Small differences after layer 15 then pass through layers 16 to 23. Forcing full 24-bit IEEE `fp32` in Triton (`try-ieee` below) cuts the max logit gap `5.8`-fold, to `0.00134` (`0.0013`). Even with default `TF32`, the `0.00774` gap is about `1,500` times smaller than the edit's effect on the base model (`11.4`), and the `1.4e-8` mean KL is about 1.3 million times smaller than the edit's (`0.0178`).
199
+
200
+ The released `qwen35-p50c` file stores weights in bf16 (16-bit brain floating point, with 8 bits of precision, or about 3 significant decimal digits). Rounding $W'$ to `bf16` on disk introduces one rounding step of up to `0.00049` per weight entry (Lab 1). Against the released `bf16` file, the `bf16` hook on the base model has a max logit gap of `0.438` (`0.4375`), mean KL `0.000539` (`5.4e-4`), and the same top token on `99.4%` of positions; the mean KL between the base model and the released file in `bf16` (`0.0184`) is `34` times larger.
201
+
202
+ ### 4. How static weights act like a token-dependent trigger
203
+
204
+ Adding a constant steering vector $-c \cdot u$ at layers 15 and 19 on every token would push the residual stream equally hard during clean algebra and during a loop. Because the weight edit multiplies $u$ by $-\alpha \cdot s(t)$, the token itself sets the steering dose through the activations $a_S(t)$ of the 64 chosen neurons.
205
+
206
+ ```mermaid
207
+ flowchart LR
208
+ T["Current token t"] --> N["64 chosen MLP neurons<br/>fire with activations a_S(t)"]
209
+ N --> P["Finished real answers<br/>s(t) small (layer 19: 0.09 to 0.12)<br/>Edit push -6 s(t) u is small"]
210
+ N --> L["Inside real loops<br/>s(t) large (0.42 L15, 0.76 L19)<br/>Edit push -6 s(t) u opposes u"]
211
+ ```
212
+
213
+ Part 2 shows this on the 8 paired texts in `labs/data/astra_pairs.json`. Each pair shares its problem prompt and opening calculation before splitting into a productive check or a repeated sentence. In `results/lab6/s_on_pairs.json`, the `per_token` arrays match on the shared opening, apart from tiny `bf16` rounding differences in the fourth pair, then $s(t)$ stays near zero on the productive branch and climbs on the looping branch. Over the last 36 tokens of these short hand-picked texts, $s(t)$ is near zero on the productive continuations and large on the loops; on long real answers in part 4, productive $s(t)$ is higher, though still far below loop level.
214
+
215
+ Part 3 tests this trigger on long base-model answers (`runs/build/base` on `build128`). It selects 48 answers that were cut at the 32,768-token limit and end in a verbatim loop (the same token block repeated at least 3 times across at least 64 tokens, where one block's length is the period) starting after at least 1,024 tokens, to focus on answers that reasoned normally before getting stuck. Using teacher forcing, which feeds a fixed token sequence through the model instead of generating token by token, it runs each answer once with the edit hooks off and once with them on. That lets us line up every token position $t$ and compare $s(t)$, $\text{KL}(\text{base} \parallel \text{edited})$, `p_next`, and `p_pick` across the early answer, the last 512 tokens before the loop starts, and the tokens inside the loop. Part 4 runs the same check on the `26` base-model answers that closed `</think>` and got the right answer, where $s_{19}$ averages `0.09` to `0.12` per stretch and `0 of 26` reach even the weakest loop's in-loop mean (`0.671`) on any 64-token window.
216
+
217
+ **Lesson:** The weight edit and subtracting $\alpha \cdot s(t) \cdot u$ at layers 15 and 19 are the same computation. The text sets the dose through $s(t)$: small on ordinary reasoning (about one seventh of the in-loop mean at layer 19), rising as the model starts to circle, and largest inside a loop, where a small per-token cut adds up to a large chance of breaking the cycle. Because $s(t)$ stays above zero on good answers, the edit still nudges them a little; [Lab 7](lab7_side_effects.md) measures that cost.
218
+
219
+ ## Check yourself
220
+
221
+ 1. In Lab 3, the released file's singular vector $u$ at layer 19 had the opposite sign from the mean-difference vector $u_{\text{loop}}$. Why does replacing $u$ with $-u$ flip the sign of $s(t)$ without changing the steering push $-\alpha s(t) u$ or the weight edit $W'$?
222
+ <details>
223
+ <summary>Answer</summary>
224
+
225
+ Both $s(t) = u \cdot (W_S a_S(t))$ and $u$ appear once in $s(t) u$. Replacing $u$ with $-u$ turns $s(t)$ into $-s(t)$, and the two minus signs cancel in $(-s(t))(-u) = s(t) u$. In $W' = W - \alpha u u^T W_S$, $u$ multiplies its transpose $u^T$, and $(-u)(-u)^T = u u^T$.
226
+
227
+ </details>
228
+
229
+ 2. In part 1, the maximum `fp32` logit difference between the hook and the in-memory edit is `0.00774` (`0.0077`). Which numbers in `equivalence.json` and `try-ieee/equivalence.json` show this comes from floating-point kernel rounding and not from a mistake in the hook formula?
230
+ <details>
231
+ <summary>Answer</summary>
232
+
233
+ Running layer 15's `down_proj` alone in `fp64` on CPU, where no GPU kernel rounds intermediate sums, gives `fp64_one_layer_max_abs_diff` of `3.33e-16` on outputs of scale `0.65`. Forcing full 24-bit IEEE `fp32` in the DeltaNet Triton kernels (`results/lab6/try-ieee/equivalence.json`) cuts `fp32_hook_vs_edit_max_abs_logit_diff` `5.8`-fold from `0.00774` to `0.00134`. Across the full `fp32` model, the edit itself shifts base logits by `11.4` (`fp32_base_vs_edit_max_abs_logit_diff`) with mean KL `0.0178`, while the hook and in-memory edit differ by `0.00774` (`1,500` times smaller) with mean KL `1.41e-8` (1.3 million times smaller).
234
+
235
+ </details>
236
+
237
+ 3. On the four productive texts at layer 15, mean $s(t)$ over the last 36 tokens stays between `0.00` and `0.04`, versus `0.19`, `0.19`, `0.42`, and `0.25` on the four looping texts. How does $-\alpha \cdot s(t) \cdot u$ explain why the edit pushes much less on productive steps?
238
+ <details>
239
+ <summary>Answer</summary>
240
+
241
+ Since $u$ has length `1` and $\alpha = 6$, the vector subtracted on token $t$ has length $|6 \cdot s(t)|$: at most about `0.24` (`6` times `0.04`) on the productive texts versus about `1.1` to `2.5` (`6` times `0.19` to `6` times `0.42`) on the looping texts. As the 64 neurons fire together in a loop, the push grows with $s(t)$ against $u$. Part 4 finds a smaller gap in the same direction on long real answers, so the push on good answers is small but not zero.
242
+
243
+ </details>
244
+
245
+ 4. Why use teacher forcing in part 3 instead of letting the edited model generate freely from the prompt?
246
+ <details>
247
+ <summary>Answer</summary>
248
+
249
+ Free generation diverges as soon as the edit changes one sampled token, leaving the base and edited models on different sentences. Teacher forcing feeds the same base-model loop into both models so we can compare $s(t)$, KL divergence, and loop-continuation odds at identical token positions.
250
+
251
+ </details>
252
+
253
+ 5. Part 4 finds that `0 of 26` good answers have any 64-token stretch where $s(t)$ reaches loop level. Why does that not show that the edit leaves good answers unchanged?
254
+ <details>
255
+ <summary>Answer</summary>
256
+
257
+ The push is proportional to $s(t)$, which on good answers is about one seventh of the in-loop mean (`0.091` to `0.116` at layer 19 versus `0.758`), not zero. That shifts next-token probabilities by a mean KL of `0.007` to `0.017` per token on good answers, about a tenth to a quarter of the in-loop KL (`0.069`). Across thousands of thinking tokens, those small shifts add up and can change which tokens get sampled; [Lab 7](lab7_side_effects.md) measures those side effects directly.
258
+
259
+ </details>
260
+
261
+ ## Try this next
262
+
263
+ Non-default flags write to `results/lab6/try-<name>/` and leave the main result files and figures untouched.
264
+
265
+ 1. **Inspect where $s(t)$ splits inside the 4 pairs.** Run this one-liner on the VM or locally to check the shared openings (`75`, `85`, `70` and `69` tokens) and post-split peaks in `results/lab6/s_on_pairs.json`:
266
+ ```bash
267
+ python3 -c 'import json; d=json.load(open("results/lab6/s_on_pairs.json")); k="per_token"; [print(f"L{L} pair {p}: shared diff={max(abs(a-b) for a,b in zip(d[L][k][2*p][:sh], d[L][k][2*p+1][:sh])):.4f}, loop peak={max(d[L][k][2*p+1]):.4f}") for L in ("15","19") for p,sh in enumerate((75,85,70,69))]'
268
+ ```
269
+ This prints `0.0000` on the first three shared openings, `0.0018` (layer 15) and `0.0042` (layer 19) on the fourth from `bf16` rounding, and loop peaks of `0.4558, 0.4713, 0.5897, 0.4873` at layer 15 and `0.3620, 0.4300, 0.7376, 0.6322` at layer 19.
270
+
271
+ 2. **Double the edit strength to $\alpha = 12$ with `--alpha-scale 2`.** The `--alpha-scale` flag scales $\alpha$ in both the hook and the in-memory edit (skipping the `bf16` file comparison because the file on disk was saved at $\alpha = 6$) and writes `results/lab6/try-alpha-scale-2/equivalence.json`:
272
+ ```bash
273
+ CUDA_VISIBLE_DEVICES=7 python labs/lab6_steering.py --parts 1 --alpha-scale 2
274
+ ```
275
+ In `results/lab6/try-alpha-scale-2/equivalence.json`, the hook and in-memory edit still agree (`0.00916` max logit diff, mean KL `2.71e-8`). Against the base model, the max logit diff rises to `15.9` (`15.93`) and mean KL rises to `0.0745`, or `4.2` times the $\alpha = 6$ KL (`0.0178`). Doubling a small change roughly quadruples the KL divergence, because KL grows like the square of a small change.
276
+
277
+ 3. **Force full IEEE `fp32` in the Triton DeltaNet kernels (`try-ieee`).** Triton defaults to 11-bit `TF32` for `fp32` matrix multiplies on A100 Tensor Cores. Pass `TRITON_F32_DEFAULT=ieee` with a fresh cache directory to recompile in 24-bit `fp32` and write `results/lab6/try-ieee/equivalence.json`:
278
+ ```bash
279
+ TRITON_F32_DEFAULT=ieee TRITON_CACHE_DIR=/nvme/wsp-lab/tmp/triton-ieee \
280
+ CUDA_VISIBLE_DEVICES=7 python labs/lab6_steering.py --parts 1 --try-name ieee
281
+ ```
282
+ The max `fp32` logit gap drops from `0.00774` to `0.00134` (`0.0013`), `5.8` times smaller, showing that `TF32` rounding in the DeltaNet mixers caused most of the `fp32` gap.
283
+
284
+ 4. **Run part 3 on 10 loops (`--max-samples 10`) and redraw with less smoothing (`--smooth 16`).** Passing `--max-samples 10` runs part 3 in about a minute on 10 loops and writes `real_loops.json`, `trace_one_loop.json`, and both figures to `results/lab6/try-max-samples-10/`:
285
+ ```bash
286
+ CUDA_VISIBLE_DEVICES=7 python labs/lab6_steering.py --parts 3 --max-samples 10
287
+ ```
288
+ Across those 10 loops, edited `p_pick` inside the loop averages `0.984`, mean cycle odds at temperature 1 drop from `0.991` to `0.150`, and under the paper's sampler cycle odds drop from `0.999` to `0.511`. To redraw the 48-loop figure with a 16-token moving average instead of 64 (CPU only, writing to `results/lab6/try-smooth-16/`):
289
+ ```bash
290
+ python labs/lab6_steering.py --plot-only --smooth 16
291
+ ```
292
+ Comparing `results/lab6/try-smooth-16/lab6_real_loop.png` with `results/figures/lab6_real_loop.png` shows the token-by-token oscillations inside each cycle that the 64-token window smooths out.
293
+
294
+ 5. **Read the good answer that came closest to loop level.** Sort `samples` in `results/lab6/finished_answers.json` by `max_s19_64_tokens`:
295
+ ```bash
296
+ python3 -c 'import json; d=json.load(open("results/lab6/finished_answers.json")); [print(s["id"], s["j"], round(s["max_s19_64_tokens"], 3)) for s in sorted(d["samples"], key=lambda s: -s["max_s19_64_tokens"])[:3]]'
297
+ ```
298
+ This prints `gsm8k_train_6504 1 0.465`, `math_train_intermediate_algebra_605 0 0.353`, and `math_train_prealgebra_141 1 0.31`. Inspect the last 512 thinking tokens of `gsm8k_train_6504` answer `1` (tokens `6073` to `6585` from the start of the answer, on `sampler`, CPU only):
299
+ ```bash
300
+ CUDA_VISIBLE_DEVICES="" python labs/peek.py build/base --id gsm8k_train_6504 --answer 1 --tokens 6073 6585
301
+ ```
302
+ The header `answer 1: 6,796 tokens, finished` shows the generator's token count (`6,795` when re-tokenized from saved text). On this stretch, where $s_{19}$ averages `0.299` (this answer's hardest 64-token window of thinking reaches `0.465` at layer 19 and `0.316` at layer 15), the model circles through the repeated check and arithmetic described in part 4 before closing `</think>` and giving the right answer `1600.00`. Does $s(t)$ respond only to fatal infinite loops, or to circling text even when the answer recovers?
code/labs/lab6_steering.py ADDED
@@ -0,0 +1,431 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ """Lab 6: the weight edit is steering in disguise.
3
+
4
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
5
+ CUDA_VISIBLE_DEVICES=7 python labs/lab6_steering.py # parts 1 and 2, about a minute
6
+ CUDA_VISIBLE_DEVICES=7 python labs/lab6_steering.py --parts 3 # part 3, about 2 minutes, needs a finished base run
7
+ CUDA_VISIBLE_DEVICES=7 python labs/lab6_steering.py --parts 4 # part 4, about a minute, needs part 3's results
8
+ python labs/lab6_steering.py --plot-only # redraw part 3's figures, no GPU
9
+
10
+ Experiments (each writes to its own folder, results/lab6/try-*, and leaves the main results alone):
11
+ CUDA_VISIBLE_DEVICES=7 python labs/lab6_steering.py --parts 1 --alpha-scale 2 # the same check at alpha 12
12
+ CUDA_VISIBLE_DEVICES=7 python labs/lab6_steering.py --parts 3 --max-samples 10 # part 3 on 10 loops
13
+ python labs/lab6_steering.py --plot-only --smooth 16 # less smoothing, no GPU
14
+ TRITON_F32_DEFAULT=ieee TRITON_CACHE_DIR=/nvme/wsp-lab/tmp/triton-ieee \
15
+ CUDA_VISIBLE_DEVICES=7 python labs/lab6_steering.py --parts 1 --try-name ieee # DeltaNet kernels in full fp32
16
+
17
+ Part 1. Same math, two ways. In float32, compare the logits of
18
+ (a) the model with down_proj edited in memory: W' = W - alpha u u^T W[:, cols]
19
+ (b) the unedited model plus a hook that subtracts alpha * s(t) * u from down_proj's output,
20
+ where s(t) = u . (W[:, cols] a[cols](t)) is one number per token.
21
+ They agree to float32 rounding. Then compare the released bf16 file with (b) run in bf16.
22
+ Part 2. s(t) on the agent's 8 texts: how hard the 64 neurons push along the loop direction.
23
+ Part 3. Real loops. Teacher-force looping answers from the base model and follow, token by token:
24
+ s(t), how much the edit changes the next-token prediction (KL), and the probability of the token that
25
+ actually came next, both straight from the model (temperature 1) and as the chance that the paper's
26
+ sampler settings pick it (wsp/protocol.py, pick_prob). Inside the loop it also works out the chance
27
+ of writing one more full cycle unchanged.
28
+ Part 4. Answers that went well. The same s(t) and KL on every answer in the run that finished on its own with
29
+ the right number, side by side with part 3's loops (from part 3's folder for the same flags, or the main results
30
+ if that folder has none). If the edit only reacted to loops, both would stay near zero here.
31
+ """
32
+ import argparse
33
+ import contextlib
34
+ import json
35
+ import os
36
+ import sys
37
+
38
+ import numpy as np
39
+ import torch
40
+
41
+ ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
42
+ sys.path.insert(0, ROOT)
43
+ from wsp import hf, loops, protocol # noqa: E402
44
+ from wsp.edits import projection_edit # noqa: E402
45
+
46
+ ap = argparse.ArgumentParser()
47
+ ap.add_argument("--parts", default="1,2")
48
+ ap.add_argument("--run", default=os.path.join(hf.LAB, "runs", "build", "base"))
49
+ ap.add_argument("--data", default="build128")
50
+ ap.add_argument("--max-samples", type=int, default=48, help="part 3: how many looping answers to teacher-force")
51
+ ap.add_argument("--alpha-scale", type=float, default=1.0,
52
+ help="part 1: multiply alpha by this in both the hook and the in-memory edit (2 means alpha 12)")
53
+ ap.add_argument("--plot-only", action="store_true", help="redraw the part 3 figures from results/lab6/*.json")
54
+ ap.add_argument("--smooth", type=int, default=64, help="moving-average window for the one-loop figure, in tokens")
55
+ ap.add_argument("--try-name", help="write this run's results to results/lab6/try-<name>/ instead of the main files")
56
+ a = ap.parse_args()
57
+ parts = {int(x) for x in a.parts.split(",")}
58
+
59
+ OUT = os.path.join(ROOT, "results", "lab6")
60
+ FIG = os.path.join(ROOT, "results", "figures")
61
+ # Experiments write to results/lab6/try-*/, so the main results and figures are never overwritten.
62
+ # Changing --alpha-scale, --max-samples or --smooth picks a folder name for you.
63
+ name1 = a.try_name or (f"alpha-scale-{a.alpha_scale:g}" if a.alpha_scale != 1.0 else None)
64
+ name3 = a.try_name or (f"max-samples-{a.max_samples}" if a.max_samples != 48 else None)
65
+ OUT1 = os.path.join(OUT, f"try-{name1}") if name1 else OUT # part 1
66
+ OUT2 = os.path.join(OUT, f"try-{a.try_name}") if a.try_name else OUT # part 2
67
+ OUT3 = os.path.join(OUT, f"try-{name3}") if name3 else OUT # part 3, and where --plot-only reads it
68
+ FIG3 = OUT3 if name3 else FIG
69
+ if a.smooth != 64:
70
+ FIG3 = os.path.join(OUT, f"try-{name3 + '-' if name3 else ''}smooth-{a.smooth}")
71
+ for d in (OUT, FIG, OUT1, OUT2, OUT3, FIG3):
72
+ os.makedirs(d, exist_ok=True)
73
+ LAYERS = (15, 19)
74
+ rel = hf.released_edit()
75
+ lab3 = json.load(open(os.path.join(ROOT, "results", "lab3", "rebuild.json")))
76
+ # the released direction u, signed so that +u points toward looping (Lab 3 measured which sign that is)
77
+ u_loop = {}
78
+ for L in LAYERS:
79
+ u_rel = rel[L]["u"] / rel[L]["u"].norm()
80
+ u_loop[L] = u_rel if float(u_rel @ torch.tensor(lab3["layers"][str(L)]["u_loop_sign"])) > 0 else -u_rel
81
+ pairs = json.load(open(os.path.join(ROOT, "labs", "data", "astra_pairs.json")))
82
+
83
+
84
+ @contextlib.contextmanager
85
+ def steered(m, alpha_scale=1.0, record=None):
86
+ """The released edit as hooks at both layers. alpha_scale=0 only records s(t)."""
87
+ with contextlib.ExitStack() as st:
88
+ for L in LAYERS:
89
+ st.enter_context(hf.steer_down(m, L, u_loop[L], alpha_scale * rel[L]["alpha"], rel[L]["cols"],
90
+ record=None if record is None else record[L]))
91
+ yield
92
+
93
+
94
+ def kl_rows(lp, lq): # KL(p || q) per row, from log-probs
95
+ return (lp.exp() * (lp - lq)).sum(-1)
96
+
97
+
98
+ def plot_part3():
99
+ """Draws part 3's two figures from real_loops.json and trace_one_loop.json in OUT3, into FIG3."""
100
+ import matplotlib
101
+ matplotlib.use("Agg")
102
+ import matplotlib.pyplot as plt
103
+
104
+ real = json.load(open(os.path.join(OUT3, "real_loops.json")))
105
+ trace = json.load(open(os.path.join(OUT3, "trace_one_loop.json")))
106
+ stats, names = real["samples"], list(real["mean"])
107
+
108
+ def smooth(v, w=a.smooth): # moving average that stays honest at both ends of the answer
109
+ v, k = np.asarray(v, dtype=float), np.ones(w)
110
+ return np.convolve(v, k, mode="same") / np.convolve(np.ones_like(v), k, mode="same")
111
+
112
+ tm = trace["meta"]
113
+ xs = np.arange(len(trace["s15"])) # s(t): one value per answer token
114
+ xk = np.arange(len(trace["kl"])) # KL and p(next): one fewer, since position t predicts token t+1
115
+ has_pick = "p_pick_edit" in trace # traces written before the sampler measurement have 3 panels
116
+ fig, ax = plt.subplots(4 if has_pick else 3, 1, figsize=(13, 11.5 if has_pick else 9), sharex=True)
117
+ ax[0].plot(xs, smooth(trace["s15"]), label="layer 15", color="#0969da")
118
+ ax[0].plot(xs, smooth(trace["s19"]), label="layer 19", color="#8250df")
119
+ ax[0].set_ylabel("s(t): push along\nthe loop direction")
120
+ ax[0].legend(loc="upper left")
121
+ ax[1].plot(xk, smooth(trace["kl"]), color="#cf222e")
122
+ ax[1].set_ylabel("KL(base || edited)\nnext-token change")
123
+ ax[2].plot(xk, smooth(trace["p_base"]), label="base model", color="#57606a")
124
+ ax[2].plot(xk, smooth(trace["p_edit"]), label="edited model", color="#1a7f37")
125
+ ax[2].set_ylabel("probability of the\ntoken that came next\n(temperature 1)")
126
+ ax[2].legend(loc="lower left")
127
+ if has_pick:
128
+ ax[3].plot(xk, smooth(trace["p_pick_base"]), label="base model", color="#57606a")
129
+ ax[3].plot(xk, smooth(trace["p_pick_edit"]), label="edited model", color="#1a7f37")
130
+ ax[3].set_ylabel("chance the paper's\nsampler picks it")
131
+ ax[3].legend(loc="lower left")
132
+ ax[-1].set_xlabel("token position in the answer")
133
+ for axi in ax:
134
+ axi.axvline(tm["loop_starts_at"], color="black", ls="--", lw=1)
135
+ ax[0].set_title(f"One real loop from the base model ({tm['id']})\nthe loop starts at token "
136
+ f"{tm['loop_starts_at']} and repeats every {tm['period']} tokens; lines are "
137
+ f"{a.smooth}-token moving averages")
138
+ fig.tight_layout()
139
+ fig.savefig(os.path.join(FIG3, "lab6_real_loop.png"), dpi=130)
140
+
141
+ has_cycle = "cycle_pick_edit" in stats[0][names[2]]
142
+ fig, ax = plt.subplots(1, 4 if has_cycle else 3, figsize=(18.5 if has_cycle else 14, 3.8))
143
+ short = ["early", "late", "inside loop"]
144
+ for i, (k, lab) in enumerate([("s15", "s(t) at layer 15"), ("kl", "KL(base || edited)"),
145
+ ("p_next_base", "p(next token), temperature 1")]):
146
+ vals = np.array([[s[z][k] for z in names] for s in stats])
147
+ ax[i].boxplot([vals[:, j] for j in range(3)], tick_labels=short, showfliers=False)
148
+ if k == "p_next_base":
149
+ ve = np.array([[s[z]["p_next_edit"] for z in names] for s in stats])
150
+ ax[i].plot(range(1, 4), ve.mean(0), "o-", color="#1a7f37", label="edited model (mean)")
151
+ ax[i].plot(range(1, 4), vals.mean(0), "o-", color="#57606a", label="base model (mean)")
152
+ ax[i].legend()
153
+ ax[i].set_title(lab)
154
+ if has_cycle:
155
+ per = [s["period"] for s in stats]
156
+ ax[3].scatter(per, [s[names[2]]["cycle_pick_base"] for s in stats], color="#57606a", s=18, label="base model")
157
+ ax[3].scatter(per, [s[names[2]]["cycle_pick_edit"] for s in stats], color="#1a7f37", s=18,
158
+ label="edited model")
159
+ ax[3].set_xscale("log")
160
+ ax[3].set_ylim(-0.03, 1.03)
161
+ ax[3].set_xlabel("loop length (tokens per cycle)")
162
+ ax[3].set_title("chance of one more identical cycle\n(paper's sampler settings)")
163
+ ax[3].legend(loc="center left")
164
+ fig.suptitle(f"{len(stats)} real loops from the base model, three stretches of each answer")
165
+ fig.tight_layout()
166
+ fig.savefig(os.path.join(FIG3, "lab6_loops_summary.png"), dpi=130)
167
+ print("wrote lab6_real_loop.png and lab6_loops_summary.png in", FIG3)
168
+
169
+
170
+ if a.plot_only:
171
+ plot_part3()
172
+ sys.exit(0)
173
+
174
+
175
+ # ------------------------------------------------------------------ part 1
176
+ if 1 in parts:
177
+ res = {}
178
+ m, tok = hf.load("base", dtype=torch.float32)
179
+ ids = [hf.encode(tok, t) for t in pairs["texts"]]
180
+ with torch.no_grad():
181
+ base = [m(x).logits[0].float() for x in ids]
182
+ with steered(m, a.alpha_scale):
183
+ hook = [m(x).logits[0].float() for x in ids]
184
+ saved = {}
185
+ for L in LAYERS:
186
+ W = hf.down_proj(m, L).weight
187
+ saved[L] = W.data.clone()
188
+ W.data = projection_edit(W.data, u_loop[L].to(W.device), a.alpha_scale * rel[L]["alpha"], rel[L]["cols"])
189
+ edit = [m(x).logits[0].float() for x in ids]
190
+ for L in LAYERS:
191
+ hf.down_proj(m, L).weight.data = saved[L]
192
+ res["fp32_hook_vs_edit_max_abs_logit_diff"] = max(float((h - e).abs().max()) for h, e in zip(hook, edit))
193
+ res["fp32_base_vs_edit_max_abs_logit_diff"] = max(float((b - e).abs().max()) for b, e in zip(base, edit))
194
+ res["fp32_hook_vs_edit_mean_kl"] = float(torch.cat([kl_rows(e.log_softmax(-1), h.log_softmax(-1))
195
+ for h, e in zip(hook, edit)]).mean())
196
+ res["fp32_base_vs_edit_mean_kl"] = float(torch.cat([kl_rows(b.log_softmax(-1), e.log_softmax(-1))
197
+ for b, e in zip(base, edit)]).mean())
198
+ del m
199
+ torch.cuda.empty_cache()
200
+
201
+ if 1 in parts and a.alpha_scale == 1.0: # the released file has alpha 6, so this check only makes sense at scale 1
202
+ m16, tok = hf.load("base")
203
+ ids = [hf.encode(tok, t) for t in pairs["texts"]]
204
+ p16, _ = hf.load("qwen35-p50c")
205
+ with torch.no_grad():
206
+ b16 = [m16(x).logits[0].float() for x in ids]
207
+ with steered(m16):
208
+ h16 = [m16(x).logits[0].float() for x in ids]
209
+ r16 = [p16(x).logits[0].float() for x in ids]
210
+ res["bf16_hook_vs_released_file_max_abs_logit_diff"] = max(float((h - r).abs().max()) for h, r in zip(h16, r16))
211
+ res["bf16_hook_vs_released_file_mean_kl"] = float(torch.cat([kl_rows(r.log_softmax(-1), h.log_softmax(-1))
212
+ for h, r in zip(h16, r16)]).mean())
213
+ res["bf16_base_vs_released_file_mean_kl"] = float(torch.cat([kl_rows(b.log_softmax(-1), r.log_softmax(-1))
214
+ for b, r in zip(b16, r16)]).mean())
215
+ res["bf16_same_top_token_hook_vs_file"] = float(torch.cat([(h.argmax(-1) == r.argmax(-1)).float()
216
+ for h, r in zip(h16, r16)]).mean())
217
+ # one layer in float64, where no GPU kernel rounds anything: the identity holds to machine precision
218
+ with torch.no_grad(), hf.record_mlp_acts(m16, [15]) as acts:
219
+ m16(ids[1])
220
+ A64 = acts[15][0].double() # [tokens, 3584] neuron activations
221
+ W64 = hf.down_proj(m16, 15).weight.detach().double().cpu()
222
+ u64 = u_loop[15].double() / u_loop[15].double().norm()
223
+ al, cols = rel[15]["alpha"], rel[15]["cols"]
224
+ mask = torch.zeros(W64.shape[1], dtype=torch.float64)
225
+ mask[cols] = 1
226
+ W_edit = W64 - al * torch.outer(u64, mask * (u64 @ W64)) # the weight edit
227
+ s = A64[:, cols] @ (W64[:, cols].T @ u64) # one number per token
228
+ y_edit = A64 @ W_edit.T
229
+ y_hook = A64 @ W64.T - al * s[:, None] * u64[None, :]
230
+ res["fp64_one_layer_max_abs_diff"] = float((y_edit - y_hook).abs().max())
231
+ res["fp64_one_layer_output_scale"] = float(y_edit.abs().max())
232
+ del p16, m16
233
+ torch.cuda.empty_cache()
234
+ if 1 in parts:
235
+ json.dump(res, open(os.path.join(OUT1, "equivalence.json"), "w"), indent=1)
236
+ for k, v in res.items():
237
+ print(f"{k:52s} {v:.3g}")
238
+ if a.alpha_scale != 1.0:
239
+ print("skipped the bf16 and fp64 checks against the released file, which was saved with alpha 6; wrote", OUT1)
240
+
241
+ # ------------------------------------------------------------------ part 2
242
+ if 2 in parts:
243
+ m, tok = hf.load("base")
244
+ rec = {L: [] for L in LAYERS}
245
+ with torch.no_grad(), steered(m, 0.0, rec):
246
+ for t in pairs["texts"]:
247
+ m(hf.encode(tok, t))
248
+ out = {}
249
+ for L in LAYERS:
250
+ tails = [float(s[-36:].mean()) for s in rec[L]]
251
+ out[str(L)] = {"productive_last36": tails[0::2], "looping_last36": tails[1::2],
252
+ "per_token": [[round(float(x), 4) for x in s] for s in rec[L]]}
253
+ print(f"layer {L}: s over the last 36 tokens, productive {np.round(tails[0::2], 2).tolist()} "
254
+ f"vs looping {np.round(tails[1::2], 2).tolist()}")
255
+ json.dump(out, open(os.path.join(OUT2, "s_on_pairs.json"), "w"), indent=1)
256
+ del m
257
+ torch.cuda.empty_cache()
258
+
259
+ # ------------------------------------------------------------------ part 3
260
+ if 3 in parts:
261
+ m, tok = hf.load("base")
262
+ rows = loops.read_rows(hf.LAB, a.data)
263
+ found = loops.looping_samples(tok, a.run, rows)
264
+ print(f"{len(found)} samples in {a.run} end in a verbatim loop")
265
+ # keep loops with a real run-up before them, spread over the run
266
+ found = [f for f in found if f["onset"] - f["n_prompt"] >= 1024]
267
+ rng = np.random.default_rng(0)
268
+ pick = sorted(rng.choice(len(found), size=min(a.max_samples, len(found)), replace=False).tolist())
269
+ lm_head = m.lm_head
270
+ stats, best, trace = [], None, None
271
+ for k in pick:
272
+ f = found[k]
273
+ x = torch.tensor([f["ids"]], device="cuda")
274
+ rec_b = {L: [] for L in LAYERS}
275
+ rec_e = {L: [] for L in LAYERS}
276
+ n0, on = f["n_prompt"], f["onset"]
277
+ with torch.no_grad():
278
+ with steered(m, 0.0, rec_b):
279
+ hb = m.model(x, use_cache=False).last_hidden_state[0]
280
+ with steered(m, 1.0, rec_e):
281
+ he = m.model(x, use_cache=False).last_hidden_state[0]
282
+ T = x.shape[1]
283
+ # first[v] = where token v first shows up in the answer. The sampler's presence penalty
284
+ # hits token v at position t once first[v] <= t.
285
+ first = torch.full((lm_head.weight.shape[0],), T, device=x.device)
286
+ first.scatter_reduce_(0, x[0, n0:], torch.arange(n0, T, device=x.device), reduce="amin")
287
+ kl, pb, pe, qb, qe = [], [], [], [], []
288
+ nxt = x[0, 1:]
289
+ for c in range(0, T - 1, 1024):
290
+ zb = lm_head(hb[c:min(c + 1024, T - 1)]).float()
291
+ ze = lm_head(he[c:min(c + 1024, T - 1)]).float()
292
+ lb, le = zb.log_softmax(-1), ze.log_softmax(-1)
293
+ kl.append(kl_rows(lb, le).cpu())
294
+ ix = nxt[c:c + lb.shape[0]]
295
+ pb.append(lb.gather(1, ix[:, None])[:, 0].exp().cpu())
296
+ pe.append(le.gather(1, ix[:, None])[:, 0].exp().cpu())
297
+ present = first[None, :] <= torch.arange(c, c + lb.shape[0], device=x.device)[:, None]
298
+ qb.append(protocol.pick_prob(zb, present, ix).cpu())
299
+ qe.append(protocol.pick_prob(ze, present, ix).cpu())
300
+ del zb, ze, lb, le, present
301
+ kl, pb, pe = torch.cat(kl).numpy(), torch.cat(pb).numpy(), torch.cat(pe).numpy()
302
+ qb, qe = torch.cat(qb).numpy(), torch.cat(qe).numpy()
303
+ sb = {L: rec_b[L][0].numpy() for L in LAYERS}
304
+ # positions t predict token t+1; the three stretches of the answer
305
+ zones = {"early (first half before the loop)": slice(n0, n0 + (on - n0) // 2),
306
+ "late (last 512 tokens before the loop)": slice(on - 512, on),
307
+ "inside the loop": slice(on + f["period"], T - 1)}
308
+ st = {"id": f["id"], "j": f["j"], "answer_tokens": T - n0, "loop_starts_at": on - n0, "period": f["period"]}
309
+ for z, sl in zones.items():
310
+ st[z] = {"s15": float(sb[15][sl].mean()), "s19": float(sb[19][sl].mean()), "kl": float(kl[sl].mean()),
311
+ "p_next_base": float(pb[sl].mean()), "p_next_edit": float(pe[sl].mean()),
312
+ "p_pick_base": float(qb[sl].mean()), "p_pick_edit": float(qe[sl].mean())}
313
+ # Inside the loop the text repeats every `period` tokens. The chance of writing one more full
314
+ # cycle unchanged is the product of `period` token probabilities; average it over every start.
315
+ ins = zones["inside the loop"]
316
+ for n, v in (("base", pb), ("edit", pe), ("pick_base", qb), ("pick_edit", qe)):
317
+ lp = np.concatenate([[0.0], np.cumsum(np.log(np.maximum(v[ins].astype(np.float64), 1e-300)))])
318
+ st["inside the loop"][f"cycle_{n}"] = float(np.exp(lp[f["period"]:] - lp[:-f["period"]]).mean())
319
+ stats.append(st)
320
+ off_middle = abs(st["loop_starts_at"] / st["answer_tokens"] - 0.5) # plot a loop that starts mid-answer
321
+ if best is None or off_middle < best:
322
+ best = off_middle
323
+ trace = {"meta": st, "s15": sb[15][n0:].tolist(), "s19": sb[19][n0:].tolist(), "kl": kl[n0:].tolist(),
324
+ "p_base": pb[n0:].tolist(), "p_edit": pe[n0:].tolist(),
325
+ "p_pick_base": qb[n0:].tolist(), "p_pick_edit": qe[n0:].tolist(), "text": f["text"]}
326
+ print(f"{f['id']} #{f['j']}: loop at token {on - n0} of {T - n0}, period {f['period']}; "
327
+ f"KL early/late/inside {[round(st[z]['kl'], 3) for z in zones]}")
328
+ agg = {z: {k: float(np.mean([s[z][k] for s in stats])) for k in stats[0][z]} for z in zones}
329
+ json.dump({"n": len(stats), "mean": agg, "samples": stats}, open(os.path.join(OUT3, "real_loops.json"), "w"),
330
+ indent=1)
331
+ json.dump(trace, open(os.path.join(OUT3, "trace_one_loop.json"), "w"))
332
+ print(json.dumps(agg, indent=1))
333
+
334
+ plot_part3()
335
+ print("wrote", OUT3)
336
+ del m
337
+ torch.cuda.empty_cache()
338
+
339
+ # ------------------------------------------------------------------ part 4
340
+ if 4 in parts:
341
+ m, tok = hf.load("base")
342
+ rows = loops.read_rows(hf.LAB, a.data)
343
+ end_think = tok.convert_tokens_to_ids("</think>")
344
+ done = []
345
+ for rec, j, s, text in loops.iter_samples(a.run):
346
+ if s.get("cut") or not s.get("correct"):
347
+ continue
348
+ prompt = tok(protocol.render(tok, rows[rec["id"]], True), add_special_tokens=False).input_ids
349
+ ans = tok(text, add_special_tokens=False).input_ids
350
+ if end_think in ans:
351
+ done.append({"id": rec["id"], "j": j, "ids": prompt + ans, "n_prompt": len(prompt),
352
+ "think_end": len(prompt) + ans.index(end_think)})
353
+ print(f"{len(done)} answers in {a.run} finished on their own with the right number")
354
+ if not done:
355
+ sys.exit("part 4 needs at least one answer that finished on its own with the right number; this run has none")
356
+ box64 = np.ones(64) / 64
357
+ stats = []
358
+ for f in done:
359
+ x = torch.tensor([f["ids"]], device="cuda")
360
+ rec_b = {L: [] for L in LAYERS}
361
+ with torch.no_grad():
362
+ with steered(m, 0.0, rec_b):
363
+ hb = m.model(x, use_cache=False).last_hidden_state[0]
364
+ with steered(m, 1.0):
365
+ he = m.model(x, use_cache=False).last_hidden_state[0]
366
+ T = x.shape[1]
367
+ kl = []
368
+ for c in range(0, T - 1, 1024):
369
+ lb = m.lm_head(hb[c:min(c + 1024, T - 1)]).float().log_softmax(-1)
370
+ le = m.lm_head(he[c:min(c + 1024, T - 1)]).float().log_softmax(-1)
371
+ kl.append(kl_rows(lb, le).cpu())
372
+ del lb, le
373
+ kl = torch.cat(kl).numpy()
374
+ sb = {L: rec_b[L][0].numpy() for L in LAYERS}
375
+ n0, te = f["n_prompt"], f["think_end"]
376
+ zones = {"early (first half of the thinking)": slice(n0, n0 + (te - n0) // 2),
377
+ "late (last 512 tokens of the thinking)": slice(max(n0, te - 512), te),
378
+ "the written answer after </think>": slice(te, T - 1)}
379
+ st = {"id": f["id"], "j": f["j"], "answer_tokens": T - n0, "thinking_tokens": te - n0}
380
+ for z, sl in zones.items():
381
+ st[z] = {"s15": float(sb[15][sl].mean()), "s19": float(sb[19][sl].mean()), "kl": float(kl[sl].mean())}
382
+ for L in LAYERS: # the hardest 64-token stretch of the thinking
383
+ st[f"max_s{L}_64_tokens"] = float(np.convolve(sb[L][n0:te], box64, mode="valid").max())
384
+ stats.append(st)
385
+ print(f"{f['id']} #{f['j']}: {te - n0} thinking tokens; s19 early/late/answer "
386
+ f"{[round(st[z]['s19'], 3) for z in zones]}; KL {[round(st[z]['kl'], 4) for z in zones]}")
387
+ agg = {z: {k: float(np.mean([s[z][k] for s in stats])) for k in stats[0][z]} for z in zones}
388
+ out = {"n": len(stats), "mean": agg, "samples": stats}
389
+ # Part 4 writes where part 3 does, and compares with that experiment's loops if part 3 wrote any there.
390
+ OUT4, FIG4 = OUT3, (OUT3 if name3 else FIG)
391
+ loops_file = next((p for p in (os.path.join(OUT3, "real_loops.json"), os.path.join(OUT, "real_loops.json"))
392
+ if os.path.exists(p)), None)
393
+ if loops_file: # line the answers up against part 3's loops
394
+ print("comparing with the loops in", os.path.relpath(loops_file, ROOT))
395
+ real = json.load(open(loops_file))
396
+ inside = list(real["mean"])[2]
397
+ out["loops_for_comparison"] = {"n": real["n"], "mean": real["mean"]}
398
+ for L in LAYERS:
399
+ med = float(np.median([s[inside][f"s{L}"] for s in real["samples"]]))
400
+ hit = sum(s[f"max_s{L}_64_tokens"] > med for s in stats)
401
+ out[f"layer {L}"] = {"median loop, mean s inside the loop": med,
402
+ "answers whose hardest 64-token stretch beats it": hit}
403
+ print(f"layer {L}: {hit} of {len(stats)} answers have a 64-token stretch with s above the median "
404
+ f"loop's in-loop mean ({med:.3f})")
405
+ os.makedirs(OUT4, exist_ok=True)
406
+ json.dump(out, open(os.path.join(OUT4, "finished_answers.json"), "w"), indent=1)
407
+ print(json.dumps(agg, indent=1))
408
+ if "loops_for_comparison" in out:
409
+ import matplotlib
410
+ matplotlib.use("Agg")
411
+ import matplotlib.pyplot as plt
412
+
413
+ fz, lz = list(agg), list(real["mean"])
414
+ fig, ax = plt.subplots(1, 3, figsize=(16, 4.2))
415
+ for i, (k, lab) in enumerate((("s15", "s(t) at layer 15"), ("s19", "s(t) at layer 19"),
416
+ ("kl", "KL(base || edited)"))):
417
+ data = [[s[z][k] for s in stats] for z in fz] + [[s[z][k] for s in real["samples"]] for z in lz]
418
+ bp = ax[i].boxplot(data, tick_labels=["right answer\nearly", "right answer\nlate",
419
+ "right answer\nwritten", "loop\nearly", "loop\nlate",
420
+ "loop\ninside"], showfliers=False, patch_artist=True)
421
+ for b, col in zip(bp["boxes"], ["#1a7f37"] * 3 + ["#cf222e"] * 3):
422
+ b.set_facecolor(col)
423
+ b.set_alpha(0.35)
424
+ ax[i].set_title(lab)
425
+ ax[i].tick_params(axis="x", labelsize=8)
426
+ fig.suptitle(f"The edit's trigger on {len(stats)} answers that finished with the right number (green) "
427
+ f"and {real['n']} answers that ended in a loop (red), mean per stretch of each answer")
428
+ fig.tight_layout()
429
+ fig.savefig(os.path.join(FIG4, "lab6_finished_vs_loops.png"), dpi=130)
430
+ print("wrote lab6_finished_vs_loops.png in", FIG4)
431
+ print("wrote", OUT4)
code/labs/lab7_side_effects.md ADDED
@@ -0,0 +1,287 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Lab 7: What else did the edit change
2
+
3
+ **Big thing:** You will score the base model and 14 edited versions on 65,536 tokens of Wikipedia articles (WikiText-2) and on the agent's 8 short math texts (4 productive, 4 looping). For each model, you measure how well it predicts the text (perplexity) and how far its next-token probabilities move from the base model's (KL divergence). The released edit (`p50c`) moves predictions 2.5 times more on looping texts than on productive ones (`0.0241` vs `0.0097`) and raises WikiText-2 perplexity by only `0.79%`.
4
+
5
+ **Why it matters:** A loop fix is useless if it ruins ordinary language, since the model still has to read questions and write normal sentences. The 14 versions cover weaker and stronger edits ($\alpha$ from `-4` to `12`), single-layer edits, edits across all 3,584 neurons, and two random controls with the same weight-change size as the released edit. Comparing them shows what makes the edit act on loops and leave other text alone: the direction $u$, the 64 chosen neurons, or both.
6
+
7
+ **Time and hardware:** About 1.5 minutes on one A100 GPU (78 seconds in our run), plus a few seconds on the first run to download WikiText-2.
8
+
9
+ **Theory guide:** Section 13.4 (measure more than accuracy).
10
+
11
+ ## Before you run: predict
12
+
13
+ Write down a short guess for each question before running the script.
14
+
15
+ 1. The released edit (`p50c`, $\alpha = 6$) flips the loop push of 64 neurons in layers 15 and 19 and makes it 5 times larger. On plain Wikipedia text (no chat template, no thinking), will perplexity get much worse (say `+15%` or more), change by less than `1%`, or improve?
16
+ 2. Two random controls test whether any change of the same size works. `randdir` edits the same 64 neurons along a random direction at right angles to $u$. `randcols` edits 64 random neurons along $u$. Both match `p50c`'s weight-change size (matched before `bf16` rounding, so they agree closely but not exactly; the build script raised their $\alpha$ to between about 20 and 31). On the agent's 8 texts, will they move predictions more on looping texts than on productive ones, or move both about equally?
17
+ 3. Compare how far WikiText-2 predictions move when editing only layer 15 (`l15only`) versus both layers 15 and 19 (`p50c`). Must the two-layer edit move WikiText-2 predictions more than the single-layer edit?
18
+
19
+ ## Run it
20
+
21
+ Connect to `sampler` (or `trainer`). Then use `nvidia-smi` to pick a free GPU index and pass it in `CUDA_VISIBLE_DEVICES`.
22
+
23
+ ```bash
24
+ ssh sampler
25
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
26
+ nvidia-smi
27
+ CUDA_VISIBLE_DEVICES=7 python labs/lab7_side_effects.py
28
+ ```
29
+
30
+ The script loads the base model from `/nvme/wsp-lab/models/Qwen3.5-0.8B`, reads `edit_note.json` from each `/nvme/wsp-lab/models/qwen35-*` folder (built by `scripts/build_models.py`), and loads the `down_proj` matrices at the union of edited layers (layers 15 and 19 for the 14 standard edits, plus any extra edited layers from models you built in Labs 3 and 8). Pass `--models` to score a subset. It evaluates each model on `Salesforce/wikitext` (`wikitext-2-raw-v1`, `test` split) and `labs/data/astra_pairs.json`, then writes:
31
+ - `results/lab7/side_effects.json` (perplexity, percent perplexity change, and KL divergences for `base` and each edited model)
32
+ - `results/figures/lab7_side_effects.png` (two-panel bar chart of WikiText-2 perplexity change and productive-versus-looping KL divergence)
33
+
34
+ ## What you should see
35
+
36
+ The script lists the 15 models in the default run (`base` plus 14 standard edits) and the swapped `down_proj` layers, then prints one row per model and the output path and runtime (yours will differ a little):
37
+
38
+ ```text
39
+ models: ['base', 'a-4', 'a1', 'a12', 'a2', 'a4', 'a8', 'all1', 'all2', 'l15only', 'l19only', 'p50c', 'randcols', 'randdir', 'rebuilt'] | down_proj swapped at layers [15, 19]
40
+ model ppl ppl +% KL wiki KL prod KL loop
41
+ base 20.009 0.00 0.00000 0.00000 0.00000
42
+ a-4 21.088 5.39 0.02058 0.00558 0.01466
43
+ a1 19.912 -0.49 0.00160 0.00095 0.00138
44
+ a12 22.878 14.34 0.16430 0.03661 0.10268
45
+ a2 19.871 -0.69 0.00445 0.00178 0.00346
46
+ a4 19.916 -0.47 0.01516 0.00485 0.01120
47
+ a8 20.675 3.33 0.06168 0.01645 0.04416
48
+ all1 19.925 -0.42 0.00248 0.00227 0.00289
49
+ all2 19.937 -0.36 0.00782 0.00634 0.00919
50
+ l15only 20.020 0.05 0.03476 0.00550 0.01938
51
+ l19only 20.340 1.65 0.01025 0.00464 0.00755
52
+ p50c 20.167 0.79 0.03327 0.00967 0.02405
53
+ randcols 19.989 -0.10 0.00346 0.00446 0.00383
54
+ randdir 20.070 0.30 0.00462 0.00479 0.00387
55
+ rebuilt 20.167 0.79 0.03326 0.00961 0.02384
56
+ wrote /nvme/wsp-lab/code/results/lab7 in 78 s
57
+ ```
58
+
59
+ Grouping the 14 edited variants by what they test (rounded to 4 decimal places for KL, with base WikiText-2 perplexity `20.009`):
60
+
61
+ | Model | What it is | Perplexity change | KL WikiText | KL productive texts | KL looping texts |
62
+ |---|---|---:|---:|---:|---:|
63
+ | `p50c` | Released edit ($\alpha = 6$, 64 neurons, layers 15 and 19) | `+0.79%` | `0.0333` | `0.0097` | `0.0241` |
64
+ | `rebuilt` | Lab 3's rebuild from the agent's 8 texts | `+0.79%` | `0.0333` | `0.0096` | `0.0238` |
65
+ | `a-4` | $\alpha = -4$ (amplifies 5x toward loops) | `+5.39%` | `0.0206` | `0.0056` | `0.0147` |
66
+ | `a1` | $\alpha = 1$ (erase loop component on 64 neurons) | `-0.49%` | `0.0016` | `0.0010` | `0.0014` |
67
+ | `a2` | $\alpha = 2$ (mirror loop component on 64 neurons) | `-0.69%` | `0.0044` | `0.0018` | `0.0035` |
68
+ | `a4` | $\alpha = 4$ (flip and triple on 64 neurons) | `-0.47%` | `0.0152` | `0.0048` | `0.0112` |
69
+ | `a8` | $\alpha = 8$ (flip and multiply by 7) | `+3.33%` | `0.0617` | `0.0164` | `0.0442` |
70
+ | `a12` | $\alpha = 12$ (flip and multiply by 11) | `+14.34%` | `0.1643` | `0.0366` | `0.1027` |
71
+ | `all1` | $\alpha = 1$ on all 3,584 neurons | `-0.42%` | `0.0025` | `0.0023` | `0.0029` |
72
+ | `all2` | $\alpha = 2$ on all 3,584 neurons | `-0.36%` | `0.0078` | `0.0063` | `0.0092` |
73
+ | `l15only` | Released edit ($\alpha = 6$, 64 neurons) on layer 15 only | `+0.05%` | `0.0348` | `0.0055` | `0.0194` |
74
+ | `l19only` | Released edit ($\alpha = 6$, 64 neurons) on layer 19 only | `+1.65%` | `0.0102` | `0.0046` | `0.0075` |
75
+ | `randcols` | Control: direction $u$, 64 random neurons, matched change size | `-0.10%` | `0.0035` | `0.0045` | `0.0038` |
76
+ | `randdir` | Control: same 64 neurons, random orthogonal direction, matched size | `+0.30%` | `0.0046` | `0.0048` | `0.0039` |
77
+
78
+ The script also saves `results/figures/lab7_side_effects.png`:
79
+
80
+ ![Left: WikiText-2 perplexity change for each edit, highest for a12. Right: KL change on productive texts (green) and looping texts (red) for each edit, on a log scale.](../results/figures/lab7_side_effects.png)
81
+
82
+ ## Why it works
83
+
84
+ ### 1. Testing 15 models (the base model plus 14 edits) by swapping matrices in memory
85
+
86
+ A language model processes text one token (word piece) at a time through 24 blocks (or layers), each reading from and adding to the 1,024-number residual stream. Inside each block, the MLP (multi-layer perceptron) expands the stream to 3,584 neurons and projects them back through `down_proj` (a $1,024 \times 3,584$ matrix stored in bf16, 16-bit floating point).
87
+
88
+ The base model has 488 weight tensors. In the default 15-model run (`base` plus 14 edits in `results/lab7/side_effects.json`), each edited model differs from the base model in at most two tensors: `layers.15.mlp.down_proj.weight` and `layers.19.mlp.down_proj.weight` (single-layer edits change only one). Instead of loading 15 separate 1.7 GB models from disk, `labs/lab7_side_effects.py` loads the base model onto the GPU once. It reads `edit_note.json` in each model folder, takes the union of edited layers (`LAYERS`), and swaps `down_proj` at those layers with `set_weights(w)` (printing `down_proj swapped at layers ...`). Any layer a model leaves untouched gets the base weights, which is how Lab 3's layer-21 experiment (`rebuilt-layers15-19-21`, layers 15, 19, and 21) is scored alongside 2-layer models.
89
+
90
+ For each chunk of text, the script uses teacher forcing: it feeds a fixed sequence of $1,024$ tokens in one forward pass and reads next-token predictions at positions $0$ through $1,022$ (predicting tokens $1$ through $1,023$). On WikiText-2 test, 64 chunks of 1,024 tokens ($65,536$ tokens total) yield 65,472 predictions, since the first token of each chunk has no prior context.
91
+
92
+ ```mermaid
93
+ flowchart LR
94
+ B["Load base model once<br/>save base W_15 and W_19"] --> C["Feed 64 x 1,024 WikiText tokens<br/>and the 8 math texts"]
95
+ V["14 variant folders<br/>load only W_15 and W_19"] --> S["Swap W_15 and W_19<br/>in GPU memory"]
96
+ S --> C
97
+ C --> M["Compare base and edited log-probs<br/>compute perplexity and KL"]
98
+ ```
99
+
100
+ ### 2. Perplexity and KL divergence from scratch
101
+
102
+ At each token position, the model outputs logits (one raw score per vocabulary token), and softmax converts those scores into probabilities that sum to `1`. We compute two metrics from those probabilities.
103
+
104
+ Imagine a two-token vocabulary, `"38"` and `"But"`, at a step where `"38"` is the true next token:
105
+
106
+ - The base model predicts $p_{\text{base}}(\text{"38"}) = 0.80$ and $p_{\text{base}}(\text{"But"}) = 0.20$.
107
+ - An edited model predicts $p_{\text{edit}}(\text{"38"}) = 0.50$ and $p_{\text{edit}}(\text{"But"}) = 0.50$.
108
+
109
+ Perplexity measures how well a model predicts the true next token in ground-truth text. First take the negative natural log of the true token's probability (`"38"`), called the negative log-likelihood (NLL):
110
+ - Base model: $\text{NLL} = -\ln(0.80) = 0.2231$.
111
+ - Edited model: $\text{NLL} = -\ln(0.50) = 0.6931$.
112
+
113
+ Average the NLL across all tokens and raise $e$ to that average: $\text{perplexity} = e^{\text{mean NLL}}$. On this single token, perplexity is $e^{0.2231} = 1 / 0.80 = 1.25$ for the base model and $e^{0.6931} = 1 / 0.50 = 2.00$ for the edited model. A model as uncertain as a fair $K$-sided die at every token (assigning probability $1/K$ to the true token) has perplexity $K$. Lower perplexity means better prediction. On the 65,536 WikiText-2 tokens, the base model's perplexity is `20.009`.
114
+
115
+ KL divergence, written $\text{KL}(\text{base} \parallel \text{edited})$, measures how far the edited model's full probability distribution moved from the base model's, regardless of which token came next. At one position, it averages $\ln(p_{\text{base}}(v) / p_{\text{edit}}(v))$ weighted by the base probabilities:
116
+
117
+ $$\text{KL}(p_{\text{base}} \parallel p_{\text{edit}}) = \sum_{v} p_{\text{base}}(v) \left(\ln p_{\text{base}}(v) - \ln p_{\text{edit}}(v)\right)$$
118
+
119
+ In our two-token example:
120
+
121
+ $$\text{KL} = 0.80 \ln\left(\frac{0.80}{0.50}\right) + 0.20 \ln\left(\frac{0.20}{0.50}\right) = 0.80(0.4700) + 0.20(-0.9163) = 0.3760 - 0.1833 = 0.1927$$
122
+
123
+ Identical distributions have KL divergence `0`. Even when perplexity barely changes because gains on some tokens offset losses on others, KL divergence is strictly positive whenever predictions move.
124
+
125
+ ### 3. Reading the four findings in the table
126
+
127
+ First, compare selectivity on looping versus productive text. Each variant in `configs/variants.json` modifies `down_proj` along a direction $u$ (a 1,024-number unit vector) with edit strength $\alpha$ (scaling the chosen neurons' push along $u$ by $1 - \alpha$). In the right panel of `lab7_side_effects.png`, compare the red bars (`kl_looping_texts`) with the green bars (`kl_productive_texts`). The released edit `p50c` ($\alpha = 6$) moves next-token predictions about 2.5 times more on looping texts (`0.0241`) than on productive ones (`0.0097`). Lab 3's rebuild (`rebuilt`) matches `p50c` closely (`0.0238` vs `0.0096`, `+0.79%` perplexity).
128
+
129
+ Now compare `p50c` with the random controls `randdir` and `randcols`. Both set `norm_match: true`, rescaling $\alpha$ so the Frobenius size of the weight change $\|W' - W\|_F$ matches `p50c`'s change size before `bf16` rounding (`10.6%` of the matrix at layer 15 and `8.1%` at layer 19, from Lab 1). Despite equal change size, `randdir` (`0.0039` looping vs `0.0048` productive) and `randcols` (`0.0038` looping vs `0.0045` productive) move both text types by nearly the same small amount, shifting productive texts slightly more. Editing all 3,584 neurons (`all1`, `all2`) is also less selective than editing only the 64 chosen ones. At $\alpha = 2$, `a2` (64 neurons) moves productive text by `0.0018` and looping text by `0.0035` (ratio about 1.9), whereas `all2` (3,584 neurons) moves them by `0.0063` and `0.0092` (ratio about 1.5), shifting productive text 3.5 times more than `a2`.
130
+
131
+ ```mermaid
132
+ flowchart TD
133
+ E["Same Frobenius weight change size<br/>at layers 15 and 19"] --> T["Targeted edit (p50c)<br/>top 64 loop neurons + loop direction u"]
134
+ E --> R1["Random direction control (randdir)<br/>same 64 neurons + random orthogonal u"]
135
+ E --> R2["Random columns control (randcols)<br/>64 other neurons + loop direction u"]
136
+ T --> TO["Selective shift:<br/>KL looping 0.0241 vs productive 0.0097 (about 2.5x)"]
137
+ R1 --> RO1["Unselective shift:<br/>KL looping 0.0039 vs productive 0.0048"]
138
+ R2 --> RO2["Unselective shift:<br/>KL looping 0.0038 vs productive 0.0045"]
139
+ ```
140
+
141
+ KL has no sign: it measures how far predictions move, not which way. `a-4` pushes the 64 neurons toward loops instead of away, yet it is selective too (looping texts move 2.6 times more than productive ones) because it acts in the same place as `p50c`: where those 64 neurons push hard along $u$, which happens on looping text. Selectivity shows where an edit acts; Lab 4's accuracy runs show whether it helps or hurts.
142
+
143
+ Second, trace the WikiText-2 perplexity dose curve across $\alpha$ (`a-4`, `a1`, `a2`, `a4`, `p50c` at $\alpha = 6$, `a8`, `a12`) in the left panel of `lab7_side_effects.png`:
144
+ - Setting $\alpha$ to `-4` (`a-4`, pushing 5 times harder in the loop direction) raises perplexity by `+5.39%` (`21.088`).
145
+ - Small positive edits (`a1` `-0.49%`, `a2` `-0.69%`, `a4` `-0.47%`, `all1` `-0.42%`, `all2` `-0.36%`) lower WikiText-2 perplexity slightly below the base model's `20.009`, reaching `19.871` at `a2`.
146
+ - At the released strength $\alpha = 6$ (`p50c`), flipping and amplifying 5x costs `+0.79%` perplexity (`20.167`).
147
+ - Above $\alpha = 6$, perplexity climbs fast: `a8` ($\alpha = 8$) adds `+3.33%` (`20.675`), and `a12` ($\alpha = 12$) adds `+14.34%` (`22.878`) with WikiText KL `0.1643`.
148
+
149
+ Third, the single-layer rows `l15only` and `l19only` show that KL does not add across edits. On WikiText-2, editing layer 15 alone moves predictions slightly more (`wikitext_kl = 0.0348`) than editing both layers 15 and 19 (`p50c`, `0.0333`). Yet `l15only` barely changes perplexity (`+0.05%`), while `l19only` moves predictions less (KL `0.0102`) and raises perplexity more (`+1.65%`). This table cannot say why. One plausible story: layer 19 sits four blocks after layer 15, so the layer-15 edit changes the input reaching layer 19's 64 neurons. Because the two loop directions point along similar lines (absolute cosine similarity `0.849` in Lab 1, where `1` means the same direction and `0` means at right angles), the layer-19 edit may undo part of the layer-15 shift on ordinary text. `l15only` is also the most selective edit in the table, moving looping texts 3.5 times more than productive ones, and in Lab 4 it fixes almost as many problems as the full edit (`60.4%` correct on the dev set vs `61.5%` for `p50c`).
150
+
151
+ Fourth, WikiText-2 is plain encyclopedia text scored with no chat template and no thinking. This table measures damage to ordinary language modeling, not to math reasoning or following instructions. Lab 4's dose runs measure the math side. Here, $\alpha = 12$ hurts ordinary text a lot and $\alpha = 6$ barely touches it.
152
+
153
+ ## Check yourself
154
+
155
+ 1. Suppose a model assigns probability `0.25` to the true next token at every position of a test passage. What is its average negative log-likelihood (NLL), and what is its perplexity?
156
+ <details>
157
+ <summary>Answer</summary>
158
+
159
+ At every token, $\text{NLL} = -\ln(0.25) = \ln(4) \approx 1.3863$. Raising $e$ to the mean NLL gives $\text{perplexity} = e^{\ln(4)} = 4.0$. A constant true-token probability of $1/K$ always gives perplexity $K$.
160
+
161
+ </details>
162
+
163
+ 2. How can `a2` ($\alpha = 2$) have a positive KL divergence on WikiText-2 (`0.0044`) while its WikiText-2 perplexity change is negative (`-0.69%`)?
164
+ <details>
165
+ <summary>Answer</summary>
166
+
167
+ KL divergence measures how far the edited probabilities moved from the base probabilities in either direction, so any change makes KL positive. Perplexity measures how much probability lands on the actual next token in WikiText-2. For `a2`, the small shift puts slightly more probability on the true next token on average, so perplexity drops from `20.009` to `19.871` (`-0.69%`) while KL records the `0.0044` shift.
168
+
169
+ </details>
170
+
171
+ 3. What specific alternative explanation does `randdir` rule out, and what specific alternative explanation does `randcols` rule out?
172
+ <details>
173
+ <summary>Answer</summary>
174
+
175
+ `randdir` edits the same 64 neurons as `p50c` with the same change norm along a random direction orthogonal to $u$; its flat response (`0.0039` looping vs `0.0048` productive) argues against the idea that perturbing those 64 neurons in any direction would target loops. `randcols` uses the loop direction $u$ and the same change norm on 64 random neurons; its flat response (`0.0038` looping vs `0.0045` productive) argues against the idea that projecting $u$ out of any 64 neurons works just as well. In these tests, selectivity required both the loop direction $u$ and the neurons that fire during loops. Each control is a single random draw, so treat this as good evidence rather than proof.
176
+
177
+ </details>
178
+
179
+ 4. Why can `l15only` have a larger `wikitext_kl` (`0.0348`) than `p50c` (`0.0333`), even though `p50c` includes the exact same layer-15 edit plus the layer-19 edit?
180
+ <details>
181
+ <summary>Answer</summary>
182
+
183
+ KL doesn't add across edits. The layer-19 edit acts on a stream that the layer-15 edit already changed, so the two shifts can partly cancel instead of stacking. On WikiText-2 they seem to cancel a little, bringing predictions slightly back toward the base model's. This table shows that it happens, not how. Measuring the KL between `l15only` and `p50c` directly would show how much the layer-19 edit moves predictions once the layer-15 edit is in place.
184
+
185
+ </details>
186
+
187
+ ## Try this next
188
+
189
+ Each experiment writes its JSON file and figure to `results/lab7/try-<name>/`, leaving `results/lab7/side_effects.json` untouched. Our try folders are committed so you can compare your run with ours.
190
+
191
+ 1. **Rank the edits by how selective they are.** For each model, divide its KL on the looping texts by its KL on the productive texts, and sort:
192
+ ```bash
193
+ python -c 'import json; m=json.load(open("results/lab7/side_effects.json"))["models"]; rows=[(v["kl_looping_texts"]/v["kl_productive_texts"], k, v["ppl_change_pct"]) for k,v in m.items() if k != "base"]; print("\n".join(f"{k:10s} ratio={r:.2f}x ppl_change={p:+.2f}%" for r,k,p in sorted(rows, reverse=True)))'
194
+ ```
195
+ Our output:
196
+ ```text
197
+ l15only ratio=3.52x ppl_change=+0.05%
198
+ a12 ratio=2.81x ppl_change=+14.34%
199
+ a8 ratio=2.68x ppl_change=+3.33%
200
+ a-4 ratio=2.63x ppl_change=+5.39%
201
+ p50c ratio=2.49x ppl_change=+0.79%
202
+ rebuilt ratio=2.48x ppl_change=+0.79%
203
+ a4 ratio=2.31x ppl_change=-0.47%
204
+ a2 ratio=1.95x ppl_change=-0.69%
205
+ l19only ratio=1.63x ppl_change=+1.65%
206
+ all2 ratio=1.45x ppl_change=-0.36%
207
+ a1 ratio=1.44x ppl_change=-0.49%
208
+ all1 ratio=1.28x ppl_change=-0.42%
209
+ randcols ratio=0.86x ppl_change=-0.10%
210
+ randdir ratio=0.81x ppl_change=+0.30%
211
+ ```
212
+ The layer-15-only edit ranks first, ahead of the released edit. The `a-4` edit, which pushes toward loops, also ranks above `p50c` because KL measures how far predictions move, not which way. Both random controls rank last, below 1, moving productive text slightly more than looping text.
213
+
214
+ 2. **Score only the last 36 tokens of each text.** In each pair, the productive and looping texts share their prompt and opening lines before the looping text starts repeating. Those shared tokens get the same KL, pulling the two KL columns together. `--last 36` scores only the last 36 tokens of each of the 8 texts (the shortest has 98 tokens):
215
+ ```bash
216
+ CUDA_VISIBLE_DEVICES=7 python labs/lab7_side_effects.py --last 36
217
+ ```
218
+ This writes `results/lab7/try-last-36/`, with WikiText-2 columns unchanged. Point the ranking one-liner at `results/lab7/try-last-36/side_effects.json` to compare:
219
+
220
+ | Model | Ratio over all tokens | Ratio over the last 36 tokens |
221
+ |---|---:|---:|
222
+ | `l15only` | `3.52` | `12.96` |
223
+ | `p50c` | `2.49` | `6.31` |
224
+ | `rebuilt` | `2.48` | `6.18` |
225
+ | `all1` | `1.28` | `2.07` |
226
+ | `randcols` | `0.86` | `0.49` |
227
+ | `randdir` | `0.81` | `0.34` |
228
+
229
+ Every edit along $u$ gets more selective, while the random controls get less selective. For `p50c`, productive KL barely moves (`0.0097` to `0.0093`), while looping KL more than doubles (`0.0241` to `0.0584`). This fits Lab 6, where the trigger $s(t)$ rises in the last 512 tokens before a loop and peaks inside the repetition ($s_{15}$ goes `0.096 -> 0.178 -> 0.424` from early in a looping answer, to just before the loop, to inside it).
230
+
231
+ 3. **Score less WikiText-2.** `--chunks 8` scores the first 8 chunks (8,192 tokens) instead of all 64:
232
+ ```bash
233
+ CUDA_VISIBLE_DEVICES=7 python labs/lab7_side_effects.py --chunks 8
234
+ ```
235
+ This writes `results/lab7/try-chunks-8/`. Base perplexity is `22.804` on these 8,192 tokens versus `20.009` on all 65,536, which is fine because every model is compared with the base model on the same tokens. The WikiText KL ranking matches the 64-chunk run except that `p50c` and `rebuilt` (tied to 4 decimal places) swap places. Perplexity changes are less stable: the four largest keep their order (`a12` `+12.21%`, `a-4` `+5.16%`, `a8` `+2.43%`, `l19only` `+1.47%`), while shifts under 1% shuffle (`p50c` drops from `+0.79%` to `+0.20%`, and `l15only` flips from `+0.05%` to `-0.54%`). Because KL averages non-negative per-position values, positions cannot cancel out, which helps explain why its ranking held up. A perplexity change subtracts two nearly equal averages, so when it is under 1%, even its sign depends on which text you score. The run took 70 seconds versus 78 for 64 chunks: scoring the text is the fast part.
236
+
237
+ 4. **Score Lab 3's rebuild experiments (`try-lab3-tries`).** After building the three Lab 3 experiment models (`rebuilt-pairs2`, `rebuilt-windows36-36`, and `rebuilt-layers15-19-21`), compare them against the main 4-pair rebuild:
238
+ ```bash
239
+ CUDA_VISIBLE_DEVICES=7 python labs/lab7_side_effects.py --models rebuilt rebuilt-pairs2 rebuilt-windows36-36 rebuilt-layers15-19-21 --try-name lab3-tries
240
+ ```
241
+ Because `rebuilt-layers15-19-21` also edits layer 21, the script swaps `down_proj` at layers `[15, 19, 21]` and writes `results/lab7/try-lab3-tries/side_effects.json` (base perplexity `20.009`):
242
+
243
+ | Model | Perplexity | Change | KL WikiText | KL productive | KL looping |
244
+ |---|---:|---:|---:|---:|---:|
245
+ | `rebuilt` | `20.167` | `+0.79%` | `0.0333` | `0.0096` | `0.0238` |
246
+ | `rebuilt-pairs2` | `20.096` | `+0.43%` | `0.0194` | `0.0075` | `0.0109` |
247
+ | `rebuilt-windows36-36` | `20.151` | `+0.71%` | `0.0326` | `0.0096` | `0.0241` |
248
+ | `rebuilt-layers15-19-21` | `20.307` | `+1.49%` | `0.0394` | `0.0114` | `0.0280` |
249
+
250
+ All four models score between `61.3%` and `62.3%` on `dev256`, yet their side effects separate cleanly. Using only 2 pairs (`rebuilt-pairs2`) produces a gentler, less selective edit (`+0.43%` perplexity, looping-to-productive KL ratio `1.46` vs `2.48`), while widening the neuron window to 36 tokens (`rebuilt-windows36-36`) changes almost nothing. Adding layer 21 (`rebuilt-layers15-19-21`) nearly doubles the WikiText-2 perplexity cost (`+1.49%` vs `+0.79%`) without helping accuracy (`61.3%` vs `61.5%`).
251
+
252
+ 5. **Score your main capstone edits (after Lab 8).** Lab 8 adds two models built from the unedited model's 225 loops at $\alpha = 6$: `capA-a6` (loop minus just before) and `capB-a6` (loop minus well before). Compare them with the released edit and the most selective edit:
253
+ ```bash
254
+ CUDA_VISIBLE_DEVICES=7 python labs/lab7_side_effects.py --models p50c l15only capA-a6 capB-a6
255
+ ```
256
+ This writes `results/lab7/try-models-p50c-l15only-capA-a6-capB-a6/`. Our output:
257
+ ```text
258
+ models: ['base', 'p50c', 'l15only', 'capA-a6', 'capB-a6']
259
+ model ppl ppl +% KL wiki KL prod KL loop
260
+ base 20.009 0.00 0.00000 0.00000 0.00000
261
+ p50c 20.167 0.79 0.03327 0.00967 0.02405
262
+ l15only 20.020 0.05 0.03476 0.00550 0.01938
263
+ capA-a6 20.111 0.51 0.01778 0.01025 0.01624
264
+ capB-a6 20.194 0.92 0.02711 0.01166 0.02078
265
+ wrote /nvme/wsp-lab/code/results/lab7/try-models-p50c-l15only-capA-a6-capB-a6 in 34 s
266
+ ```
267
+ Both capstone edits move productive texts more than the released edit does, and looping texts less. Their selectivity ratios are `1.58` and `1.78` versus `2.49` for `p50c`. In Lab 8, `capA-a6` also helps less: it scores `43.0%` on the dev set with `52.5%` of answers cut off, versus `61.5%` accuracy and `19.7%` cut for `p50c` (`capB-a6` reaches `58.2%` accuracy with `30.7%` cut).
268
+
269
+ 6. **Score Lab 8's capstone experiments (`try-lab8-tries`).** Lab 8 tests building the capstone edits from only the first 16 loops (`capA-max-samples-16`, `capB-max-samples-16`) or raising the strength from $\alpha = 6$ to $\alpha = 8$ on all 225 loops (`capA-alpha-8`, `capB-alpha-8`):
270
+ ```bash
271
+ CUDA_VISIBLE_DEVICES=7 python labs/lab7_side_effects.py --models capA-max-samples-16 capB-max-samples-16 capA-alpha-8 capB-alpha-8 --try-name lab8-tries
272
+ ```
273
+ This writes `results/lab7/try-lab8-tries/side_effects.json` in about 33 seconds. Here are those four models alongside the main $\alpha = 6$ capstone edits (`capA-a6` and `capB-a6`, base perplexity `20.009`):
274
+
275
+ | Model | Loops used | $\alpha$ | `dev256` accuracy % | Perplexity | Change | KL WikiText | KL productive | KL looping |
276
+ |---|---:|---:|---|---:|---:|---:|---:|---:|
277
+ | `capA-a6` (main) | `225` | `6` | `43.0 +/- 2.6` | `20.111` | `+0.51%` | `0.0178` | `0.0103` | `0.0162` |
278
+ | `capA-max-samples-16` | `16` | `6` | `17.6 +/- 1.9` | `20.029` | `+0.10%` | `0.0081` | `0.0063` | `0.0086` |
279
+ | `capA-alpha-8` | `225` | `8` | `42.8 +/- 2.8` | `20.331` | `+1.61%` | `0.0298` | `0.0183` | `0.0265` |
280
+ | `capB-a6` (main) | `225` | `6` | `58.2 +/- 2.9` | `20.194` | `+0.92%` | `0.0271` | `0.0117` | `0.0208` |
281
+ | `capB-max-samples-16` | `16` | `6` | `53.7 +/- 2.8` | `20.021` | `+0.06%` | `0.0204` | `0.0102` | `0.0173` |
282
+ | `capB-alpha-8` | `225` | `8` | `54.5 +/- 2.9` | `20.562` | `+2.76%` | `0.0468` | `0.0206` | `0.0340` |
283
+
284
+ Raising $\alpha$ from `6` to `8` roughly triples the WikiText-2 perplexity cost (`+0.51%` to `+1.61%` for recipe A, `+0.92%` to `+2.76%` for recipe B) and nearly doubles the KL shifts (`0.0178` to `0.0298` and `0.0271` to `0.0468` on WikiText; `0.0103` to `0.0183` and `0.0117` to `0.0206` on productive texts). Yet `dev256` accuracy does not improve: recipe A scores `42.8%` vs `43.0%` (paired change `-0.2 +/- 2.3` points), and recipe B scores `54.5%` vs `58.2%` (paired drop `-3.7 +/- 1.6` points). Pushing harder along an imperfect direction scales the off-target part of $u$ along with the useful part. Using only 16 loops (`capB-max-samples-16`) keeps ordinary-text side effects tiny (`+0.06%` perplexity, `0.0204` WikiText KL) while reaching `53.7%` on `dev256` (`+41.6` points over the unedited model).
285
+
286
+ `capA-max-samples-16` shows why you should not read this table on its own. It has some of the smallest side effects here (`+0.10%` perplexity, `0.0081` WikiText KL), yet it reaches only `17.6%` on `dev256` (`5.5 +/- 1.9` points above the unedited model) with `82.6%` of answers still cut off. By comparison, `all2` has a similar WikiText KL (`0.0078`) and reaches `49.4%`. Small side effects are good news only when the edit also does its job. Read side effects next to the accuracy column. [Lab 8](lab8_capstone.md) explains why this edit fails.
287
+
code/labs/lab7_side_effects.py ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ """Lab 7: what else does the edit change?
3
+
4
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
5
+ CUDA_VISIBLE_DEVICES=7 python labs/lab7_side_effects.py # a few minutes on one GPU
6
+
7
+ For the base model and every edited model built in models/:
8
+ 1. Ordinary text. Perplexity on the first 64 x 1,024 tokens of WikiText-2 (test), and KL(base || edited):
9
+ how far the edited model's next-token prediction moves from the base model's, averaged over tokens.
10
+ 2. The agent's 8 texts. KL on its productive texts vs its looping texts. A good edit moves predictions a lot
11
+ where the model loops and barely anywhere else.
12
+ Every edited model differs from the base only in the down_proj weights of the layers its edit_note.json lists
13
+ (15 and 19 for most of them), so the script loads the base once and swaps those matrices in and out.
14
+
15
+ Experiments. Any flag other than the defaults writes to results/lab7/try-NAME/ (figure included), so the main
16
+ results stay as they are:
17
+ python labs/lab7_side_effects.py --chunks 8 # quick check on 8 x 1,024 WikiText tokens
18
+ python labs/lab7_side_effects.py --last 36 # KL on only the last 36 tokens of the 8 texts
19
+ python labs/lab7_side_effects.py --models p50c capA-a6 # only some models (after Lab 8: your own edit)
20
+ """
21
+ import argparse
22
+ import json
23
+ import os
24
+ import sys
25
+ import time
26
+
27
+ import numpy as np
28
+ import torch
29
+
30
+ ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
31
+ sys.path.insert(0, ROOT)
32
+ from wsp import hf # noqa: E402
33
+ from wsp.edits import NAME, load # noqa: E402
34
+
35
+ ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
36
+ ap.add_argument("--models", nargs="+", metavar="NAME",
37
+ help="only these edited models, named like their folders without 'qwen35-' (default: all of them)")
38
+ ap.add_argument("--chunks", type=int, default=64, help="how many 1,024-token WikiText-2 chunks to score (default 64)")
39
+ ap.add_argument("--last", type=int, default=0,
40
+ help="measure KL on the 8 texts over only their last N tokens (default 0: every token)")
41
+ ap.add_argument("--try-name", help="folder name for an experiment; results go to results/lab7/try-NAME/")
42
+ a = ap.parse_args()
43
+
44
+ t_start = time.time()
45
+ CHUNK = 1024
46
+ parts = []
47
+ if a.chunks != 64:
48
+ parts.append(f"chunks-{a.chunks}")
49
+ if a.last:
50
+ parts.append(f"last-{a.last}")
51
+ if a.models:
52
+ parts.append("models-" + ("-".join(a.models) if len(a.models) <= 4 else str(len(a.models))))
53
+ try_name = a.try_name or "-".join(parts)
54
+ OUT = os.path.join(ROOT, "results", "lab7", f"try-{try_name}" if try_name else "")
55
+ FIG = OUT if try_name else os.path.join(ROOT, "results", "figures")
56
+ os.makedirs(OUT, exist_ok=True)
57
+ os.makedirs(FIG, exist_ok=True)
58
+
59
+ MODELS = os.path.join(hf.LAB, "models")
60
+ found = sorted(d[len("qwen35-"):] for d in os.listdir(MODELS)
61
+ if d.startswith("qwen35-") and os.path.exists(os.path.join(MODELS, d, "edit_note.json")))
62
+ missing = [n for n in a.models or [] if n not in found]
63
+ if missing:
64
+ sys.exit(f"no edited model folder for {missing}; found {found}")
65
+
66
+
67
+ def edited_layers(n):
68
+ note = json.load(open(os.path.join(MODELS, "qwen35-" + n, "edit_note.json")))
69
+ return {int(L) for L in note["spec"]["layers"]} if "spec" in note else {15, 19} # p50c: the released edit
70
+
71
+
72
+ # swap every layer that any chosen model edits; a model that leaves one of them alone just carries the base weights
73
+ LAYERS = sorted(set().union(*(edited_layers(n) for n in a.models or found)))
74
+ m, tok = hf.load("base")
75
+ mods = {L: hf.down_proj(m, L) for L in LAYERS}
76
+ base_w = {L: mods[L].weight.detach().clone() for L in LAYERS}
77
+ variants = {}
78
+ for n in a.models or found:
79
+ t = load(os.path.join(MODELS, "qwen35-" + n), [NAME.format(L) for L in LAYERS])
80
+ variants[n] = {L: t[NAME.format(L)].to("cuda") for L in LAYERS}
81
+ print("models:", ["base"] + list(variants), "| down_proj swapped at layers", LAYERS)
82
+
83
+
84
+ def set_weights(w):
85
+ for L in LAYERS:
86
+ mods[L].weight.data.copy_(w[L])
87
+
88
+
89
+ from datasets import load_dataset # noqa: E402
90
+
91
+ wiki = load_dataset("Salesforce/wikitext", "wikitext-2-raw-v1", split="test")
92
+ ids = tok("".join(wiki["text"]), add_special_tokens=False).input_ids
93
+ if a.chunks > len(ids) // CHUNK:
94
+ sys.exit(f"WikiText-2 test has only {len(ids) // CHUNK} chunks of {CHUNK} tokens")
95
+ chunks = torch.tensor(ids[:CHUNK * a.chunks]).view(a.chunks, CHUNK)
96
+ pairs = json.load(open(os.path.join(ROOT, "labs", "data", "astra_pairs.json")))
97
+ pair_ids = [hf.encode(tok, t) for t in pairs["texts"]]
98
+
99
+ names = ["base"] + list(variants)
100
+ nll = {n: [] for n in names}
101
+ kl = {n: [] for n in names}
102
+ kl_pairs = {n: {"productive": [], "looping": []} for n in names}
103
+
104
+
105
+ def logprobs(x):
106
+ return m(x).logits[0, :-1].float().log_softmax(-1)
107
+
108
+
109
+ def kl_text(lb, lv):
110
+ k = (lb.exp() * (lb - lv)).sum(-1)
111
+ return (k[-a.last:] if a.last else k).mean().item()
112
+
113
+
114
+ with torch.no_grad():
115
+ for c in range(a.chunks):
116
+ x = chunks[c:c + 1].cuda()
117
+ set_weights(base_w)
118
+ lb = logprobs(x)
119
+ tgt = x[0, 1:, None]
120
+ nll["base"].append(-lb.gather(1, tgt).mean().item())
121
+ kl["base"].append(0.0)
122
+ for n, w in variants.items():
123
+ set_weights(w)
124
+ lv = logprobs(x)
125
+ nll[n].append(-lv.gather(1, tgt).mean().item())
126
+ kl[n].append((lb.exp() * (lb - lv)).sum(-1).mean().item())
127
+ for i, x in enumerate(pair_ids):
128
+ kind = "looping" if i % 2 else "productive"
129
+ set_weights(base_w)
130
+ lb = logprobs(x)
131
+ kl_pairs["base"][kind].append(0.0)
132
+ for n, w in variants.items():
133
+ set_weights(w)
134
+ lv = logprobs(x)
135
+ kl_pairs[n][kind].append(kl_text(lb, lv))
136
+ set_weights(base_w)
137
+
138
+ res = {}
139
+ for n in names:
140
+ res[n] = {"wikitext_ppl": float(np.exp(np.mean(nll[n]))),
141
+ "wikitext_kl": float(np.mean(kl[n])),
142
+ "kl_productive_texts": float(np.mean(kl_pairs[n]["productive"])),
143
+ "kl_looping_texts": float(np.mean(kl_pairs[n]["looping"]))}
144
+ for n in names:
145
+ res[n]["ppl_change_pct"] = 100 * (res[n]["wikitext_ppl"] / res["base"]["wikitext_ppl"] - 1)
146
+ json.dump({"tokens": CHUNK * a.chunks, "last": a.last, "models": res},
147
+ open(os.path.join(OUT, "side_effects.json"), "w"), indent=1)
148
+ print(f"{'model':10s} {'ppl':>8s} {'ppl +%':>7s} {'KL wiki':>9s} {'KL prod':>9s} {'KL loop':>9s}")
149
+ for n in names:
150
+ r = res[n]
151
+ print(f"{n:10s} {r['wikitext_ppl']:8.3f} {r['ppl_change_pct']:7.2f} {r['wikitext_kl']:9.5f} "
152
+ f"{r['kl_productive_texts']:9.5f} {r['kl_looping_texts']:9.5f}")
153
+
154
+ import matplotlib # noqa: E402
155
+ matplotlib.use("Agg")
156
+ import matplotlib.pyplot as plt # noqa: E402
157
+
158
+ order = [n for n in names if n != "base"]
159
+ fig, ax = plt.subplots(1, 2, figsize=(14, 4.5))
160
+ ax[0].bar(order, [res[n]["ppl_change_pct"] for n in order], color="#0969da")
161
+ ax[0].set_ylabel("WikiText-2 perplexity change vs base (%)")
162
+ ax[0].set_title("Damage to ordinary text")
163
+ xi = np.arange(len(order))
164
+ ax[1].bar(xi - 0.2, [res[n]["kl_productive_texts"] for n in order], 0.4, label="productive texts", color="#1a7f37")
165
+ ax[1].bar(xi + 0.2, [res[n]["kl_looping_texts"] for n in order], 0.4, label="looping texts", color="#cf222e")
166
+ ax[1].set_xticks(xi, order)
167
+ ax[1].set_yscale("log")
168
+ ax[1].set_ylabel("KL(base || edited), mean per token")
169
+ ax[1].set_title("Where the edit moves predictions")
170
+ ax[1].legend()
171
+ for a_ in ax:
172
+ a_.tick_params(axis="x", rotation=60)
173
+ fig.tight_layout()
174
+ fig.savefig(os.path.join(FIG, "lab7_side_effects.png"), dpi=130)
175
+ print(f"wrote {OUT} in {time.time() - t_start:.0f} s")
code/labs/lab8_capstone.md ADDED
@@ -0,0 +1,336 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Lab 8 (capstone): build your own edit from the model's own loops
2
+
3
+ **Big thing:** With no hand-written texts, the unedited model's own looping answers give a loop direction close to the agent's. A weight edit built from them ("well before the loop", recipe B) reaches `58.2%` accuracy on 256 unseen practice problems, against `61.5%` for the released edit.
4
+
5
+ **Why it matters:** In Lab 3, we rebuilt the released edit from 8 short math scratchpads that the AI agent wrote and labeled by hand. Here you extract the direction and the 64 neurons per layer from the base model's failed answers on 128 practice problems, check your direction with four diagnostics before editing any weights, and see why the comparison window inside each answer changes unseen accuracy by `15.2` percentage points.
6
+
7
+ **Time and hardware:** About 5 minutes on 1 NVIDIA A100 GPU to extract the edits, 1 minute on CPU to build the edited models, and 30 minutes on 2 A100 GPUs (one run per GPU) to test both on `dev256`.
8
+
9
+ **Theory guide:** Sections 9 and 13.3.
10
+
11
+ ## Before you run: predict
12
+
13
+ Write down a short guess for each question before you run the script.
14
+
15
+ 1. For each looping answer, we compare a 36-token window (a token is a word piece) inside the loop against an earlier 36-token window from the same answer. Recipe A ("just before the loop") ends 32 tokens before verbatim repetition begins. Recipe B ("well before the loop") sits halfway between the start of the answer and the start of the loop. Which recipe will produce a stronger edit on unseen problems, and why?
16
+ 2. The AI agent needed only 4 pairs of hand-written texts to find its direction. Cosine similarity measures how closely two directions align. With the model's own noisy, long answers, roughly how many random looping answers must we average before the direction $u$ settles down (say, cosine similarity above `0.95` with the direction from a held-out set of loops, kept separate from those used to estimate the direction)? Will recipe A need more answers than recipe B?
17
+ 3. In Lab 3, neuron 11 of layer 19 had a score nearly five times larger than any other neuron on the agent's 8 short texts. When we score all 3,584 neurons on 225 long math answers from the model itself, will neuron 11 of layer 19 still rank first?
18
+
19
+ ## Run it
20
+
21
+ Connect to `sampler`, which holds the answer texts for `runs/build/base` and the `dev/*` runs.
22
+
23
+ Run the four main commands from `labs/lab8_capstone.py`:
24
+
25
+ ```bash
26
+ ssh sampler
27
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
28
+ nvidia-smi
29
+ CUDA_VISIBLE_DEVICES=7 python labs/lab8_capstone.py # about 5 minutes on one GPU
30
+ CUDA_VISIBLE_DEVICES="" python scripts/build_models.py --variants configs/capstone.json # CPU, about a minute
31
+ python scripts/run_jobs.py plans/extra.json --gpus 6,7 --only 'dev/cap*' # about 30 minutes, one run per GPU
32
+ python scripts/summarize.py /nvme/wsp-lab/runs # writes /nvme/wsp-lab/summary.md
33
+ ```
34
+
35
+ `scripts/run_jobs.py` skips any shard with a `.done.json` marker. To generate `dev/capA-a6` and `dev/capB-a6` fresh instead of keeping our finished runs, move `/nvme/wsp-lab/runs/dev/capA-a6` and `/nvme/wsp-lab/runs/dev/capB-a6` aside first.
36
+
37
+ What each step writes:
38
+
39
+ - `labs/lab8_capstone.py` reads the base model's answers in `/nvme/wsp-lab/runs/build/base` (`build128`, 128 practice problems never used for testing). It writes `results/lab8/capstone.json`, `results/lab8/example_answer.json`, `configs/capstone.json` (defining `capA-a6` and `capB-a6`), and three figures: `results/figures/lab8_loop_onset.png`, `results/figures/lab8_data_needed.png`, and `results/figures/lab8_one_answer.png`.
40
+ - `scripts/build_models.py --variants configs/capstone.json` writes two edited model folders in `/nvme/wsp-lab/models/`: `qwen35-capA-a6` and `qwen35-capB-a6`.
41
+ - `scripts/run_jobs.py plans/extra.json --gpus 6,7 --only 'dev/cap*'` generates 2 answers per problem across the 256 unseen problems of `dev256` (`512` answers per model; `dev/capA-a6` took `31.8` minutes and `dev/capB-a6` took `26.1` minutes) and writes `merged.jsonl` and `summary.json` under `/nvme/wsp-lab/runs/dev/capA-a6` and `/nvme/wsp-lab/runs/dev/capB-a6`.
42
+ - `scripts/summarize.py` updates the summary table.
43
+
44
+ Two more scripts fill in the rest of the section 3 table. `labs/lab4_endings.py` sorts answers by how they ended ([Lab 4](lab4_dose.md) explains the four groups), and `labs/lab7_side_effects.py` measures side effects on WikiText-2 and the agent's 8 texts ([Lab 7](lab7_side_effects.md)). Run both on `sampler`:
45
+
46
+ ```bash
47
+ CUDA_VISIBLE_DEVICES="" python labs/lab4_endings.py dev/base dev/p50c dev/capA-a6 dev/capB-a6 --out lab8/endings # CPU, a few seconds per run
48
+ CUDA_VISIBLE_DEVICES=7 python labs/lab7_side_effects.py --models p50c l15only capA-a6 capB-a6 # under a minute on one GPU
49
+ ```
50
+
51
+ `--out lab8/endings` writes `results/lab8/endings.json`, and the side-effects run writes `results/lab7/try-models-p50c-l15only-capA-a6-capB-a6/`.
52
+
53
+ Passing any non-default flag to `labs/lab8_capstone.py` (such as `--max-samples 16`, `--min-before 2048`, or `--alpha 8`) writes to `results/lab8/try-<flags>/` (`capstone.json`, `example_answer.json`, `variant.json`, `plan.json`, and three figures) without touching the main files or `configs/capstone.json`. Full workflow for an experiment:
54
+
55
+ ```bash
56
+ CUDA_VISIBLE_DEVICES=7 python labs/lab8_capstone.py --max-samples 16 # about 5 minutes on one GPU
57
+ CUDA_VISIBLE_DEVICES="" python scripts/build_models.py --variants results/lab8/try-max-samples-16/variant.json # CPU
58
+ python scripts/run_jobs.py results/lab8/try-max-samples-16/plan.json --gpus 0,1 # about 30 minutes, one run per GPU
59
+ CUDA_VISIBLE_DEVICES="" python labs/lab4_endings.py dev/capA-max-samples-16 dev/capB-max-samples-16 --out lab8/try-max-samples-16/endings
60
+ CUDA_VISIBLE_DEVICES=7 python labs/lab7_side_effects.py --models capA-max-samples-16 capB-max-samples-16 capA-alpha-8 capB-alpha-8 --try-name lab8-tries
61
+ ```
62
+
63
+ (`--alpha 8` works the same way in `try-alpha-8`.) We ran both `lab8_capstone.py` tries side by side on GPUs 6 and 7, then built both models and ran each plan on two GPUs. The `dev256` runs took `28 to 33` minutes each, except `dev/capA-max-samples-16`, which took `43.5` minutes because most answers ran to the 32,768-token cap (mean `28,525` tokens). The Lab 7 check of all four models took `33` seconds (`results/lab7/try-lab8-tries/`).
64
+
65
+ ## What you should see
66
+
67
+ ### 1. Loops found in `build128` and the extracted directions
68
+
69
+ Running `labs/lab8_capstone.py` prints the loop counts, the percentiles of loop period and onset token, the diagnostics for both recipes, and the onset profile:
70
+
71
+ ```text
72
+ 256 answers in /nvme/wsp-lab/runs/build/base: 230 hit the token cap, 225 of those end in a verbatim loop
73
+ using 225 loops (loop starts at least 512 answer tokens in)
74
+ loop period, 10/25/50/75/90th percentile: [18.0, 27.0, 44.0, 78.0, 144.0] tokens
75
+ loop starts at answer token, same percentiles: [2813.4, 4021.0, 5822.0, 8013.0, 10953.8]
76
+ recipe A (just before the loop), layer 15: cos with released u +0.722; released neurons picked 38/64; split-half cos 0.978; agent's 4 pairs, looping minus productive along u: [0.78, 0.88, 1.69, 1.16]
77
+ answers needed: [(1, 0.6), (2, 0.628), (4, 0.712), (8, 0.841), (16, 0.919), (32, 0.952), (64, 0.97), (112, 0.978)]
78
+ recipe A (just before the loop), layer 19: cos with released u +0.724; released neurons picked 28/64; split-half cos 0.978; agent's 4 pairs, looping minus productive along u: [1.15, 1.27, 2.4, 1.73]
79
+ recipe B (well before the loop), layer 15: cos with released u +0.782; released neurons picked 42/64; split-half cos 0.988; agent's 4 pairs, looping minus productive along u: [0.9, 1.04, 1.7, 1.25]
80
+ answers needed: [(1, 0.676), (2, 0.781), (4, 0.853), (8, 0.912), (16, 0.958), (32, 0.97), (64, 0.984), (112, 0.988)]
81
+ recipe B (well before the loop), layer 19: cos with released u +0.773; released neurons picked 28/64; split-half cos 0.989; agent's 4 pairs, looping minus productive along u: [1.32, 1.5, 2.35, 1.83]
82
+ cos between recipe A's u and recipe B's u: {'15': 0.951, '19': 0.959}
83
+ ```
84
+
85
+ The loop finder works on tokens: an answer counts if its tokens end in one block repeated back to back at least 3 times over at least 64 tokens. It finds `225` of the `230` cut answers; the summary table's word-level check counts `222` of `230` (`96.5%`), a 3-answer difference. In `results/lab8/capstone.json`, all `225` loops start at least 512 tokens into the answer, so all `225` are used. The loop period (the length in tokens of one repeated block) has 10th, 25th, 50th, 75th, and 90th percentiles of `18, 27, 44, 78, 144` tokens. The answer token where the loop starts has percentiles of `2,813; 4,021; 5,822; 8,013; 10,954`.
86
+
87
+ How the two window recipes compare against the released edit (`p50c`) and each other:
88
+
89
+ | Recipe (comparison window) and layer | Cosine with released $u$ | Released neurons picked (of 64) | Split-half cosine (stability) | Gap size ($\|\overline{\Delta h}\|$) | Agent's 4 pairs: looping minus productive along $u$ | Top neuron (score, next score) |
90
+ |---|---|---|---|---|---|---|
91
+ | Recipe A (just before the loop), layer 15 | `0.722` | `38 / 64` | `0.978` | `0.689` | `+0.78, +0.88, +1.69, +1.16` | `46` (`0.0120`, next `294` at `0.0108`) |
92
+ | Recipe A (just before the loop), layer 19 | `0.724` | `28 / 64` | `0.978` | `1.037` | `+1.15, +1.27, +2.40, +1.73` | `11` (`0.121`, next `171` at `0.024`) |
93
+ | Recipe B (well before the loop), layer 15 | `0.782` | `42 / 64` | `0.988` | `1.418` | `+0.90, +1.04, +1.70, +1.25` | `46` (`0.0261`, next `0` at `0.0187`) |
94
+ | Recipe B (well before the loop), layer 19 | `0.773` | `28 / 64` | `0.989` | `2.234` | `+1.32, +1.50, +2.35, +1.83` | `11` (`0.210`, next `1` at `0.045`) |
95
+
96
+ Four patterns in these numbers:
97
+
98
+ - **Agreement with each other and with the released edit:** Both recipes give nearly the same unit direction (a length-1 vector in the 1,024-number stream; cosine similarity is the dot product of two unit vectors, where `1` means the same direction and `0` means at right angles). Their mutual cosine `cos_A_B` is `0.951` at layer 15 and `0.959` at layer 19, yet recipe B has more than double the raw gap size (the length of the average stream difference) and sits closer to the released edit's direction. The table above lists the gap sizes and cosines for each layer.
99
+ - **Top neuron:** At layer 19, neuron `11` ranks first in both recipes with a score nearly five times the next neuron's. The table above lists both scores for each recipe. The AI agent switched off this same neuron in its 4th forward pass (request file `fwd_5` in `external/lm_understand-logs/exp8_math/runs/p50c/requests/`; an earlier failed request did not count), taking 32 practice problems from 3 to 5 correct (`+6.25` points, standard error `4.35`, which its notes call "weak positive, not confirmed"). At layer 15, both recipes' top three neurons (`46, 294, 0`) are among Lab 3's top four (`294, 371, 0, 46`).
100
+ - **Transfer to the agent's 8 hand-written texts:** Both recipes were built only from the base model's long math loops, yet projecting the last 36 tokens of all 4 hand-written pairs (`labs/data/astra_pairs.json`) onto $u$ still separates the looping text from the productive text in every pair. The table above lists the gaps along the direction for each pair.
101
+ - **Data needed:** At layer 15, the average cosine similarity between a direction from $N$ random answers and a direction from a held-out half (`113` answers) climbs fast:
102
+
103
+ | Answers used ($N$) | `1` | `2` | `4` | `8` | `16` | `32` | `64` | `112` |
104
+ |---|---:|---:|---:|---:|---:|---:|---:|---:|
105
+ | Recipe A (just before the loop), layer 15 | `0.60` | `0.63` | `0.71` | `0.84` | `0.92` | `0.95` | `0.97` | `0.978` |
106
+ | Recipe B (well before the loop), layer 15 | `0.68` | `0.78` | `0.85` | `0.91` | `0.96` | `0.97` | `0.98` | `0.988` |
107
+
108
+ ![Cosine against the number of answers used to build u, at layers 15 and 19, for recipes A (red) and B (purple). Solid lines compare with the other half of the answers and dashed lines with the released u.](../results/figures/lab8_data_needed.png)
109
+
110
+ ### 2. When the loop starts in the residual stream
111
+
112
+ Aligning all `225` loops at the token where verbatim repetition begins (`token 0`) and averaging across answers shows when the residual stream and the 64 released neurons start drifting:
113
+
114
+ | Quantity (mean over 225 loops) | `2,048 to 1,024` tokens before | `512 to 128` tokens before | Last `128` tokens before (`-128..0`) | First `128` tokens inside (`0..128`) | `128 to 256` tokens inside |
115
+ |---|---:|---:|---:|---:|---:|
116
+ | Stream along released $u$, layer 15 | `1.14` | `1.39` | `1.58` | `1.95` | `2.20` |
117
+ | Stream along released $u$, layer 19 | `2.27` | `2.66` | `2.95` | `3.50` | `3.96` |
118
+ | Trigger $s(t)$ (released 64 neurons), layer 15 | `0.135` | `0.173` | `0.206` | `0.288` | `0.366` |
119
+ | Trigger $s(t)$ (released 64 neurons), layer 19 | `0.183` | `0.237` | `0.277` | `0.376` | `0.512` |
120
+
121
+ Both the stream projection along $u$ and the 64 neurons' push $s(t) = u \cdot (W_S a_S(t))$ (the Lab 6 trigger measuring how hard those 64 neurons push along $u$ at token $t$) rise hundreds of tokens before repetition starts, then jump at token `0`.
122
+
123
+ ![Median and middle half of 225 loops lined up at the token where each loop starts. The stream along the released u and the 64 neurons' push s(t) rise slowly at layers 15 and 19, then jump once the loop starts.](../results/figures/lab8_loop_onset.png)
124
+
125
+ In `results/lab8/example_answer.json` and `lab8_one_answer.png`, the median-onset answer (`math_train_algebra_1625`, sample `1`) runs to `32,768` tokens, and its loop starts at answer token `5,822` with period `51` tokens. The three shaded 36-token windows show where each recipe reads the stream: window B covers tokens `2,893 to 2,929`, window A covers `5,754 to 5,790`, and the loop window covers `5,924 to 5,960` (starting two periods after token `5,822`).
126
+
127
+ ![One answer that loops from token 5,822: the stream along the released u and the push s(t) at layers 15 and 19, with shaded strips for window B, window A and the loop window.](../results/figures/lab8_one_answer.png)
128
+
129
+ ### 3. Test results on the 256 unseen problems (`dev256`)
130
+
131
+ The table below combines `dev256` results (`256` unseen problems, `2` answers each = `512` answers per run), answer endings from `results/lab8/endings.json`, and side effects from `results/lab7/try-models-p50c-l15only-capA-a6-capB-a6/side_effects.json`. From Lab 7, perplexity ($e$ to the mean negative log-probability of the true next token; base model `20.009` on WikiText-2, lower is better) and KL divergence ($\text{KL}(\text{base} \parallel \text{edited})$, how far next-token probabilities shift from the unedited model, where `0` means identical) measure how much an edit disturbs ordinary text and the agent's 8 texts:
132
+
133
+ | Model / edit | Accuracy % | Cut at cap % | Mean tokens | Gain vs unedited (pp), rescued / lost | Correct when finished % | Endings: cut / stopped / escaped loop (correct) / normal (correct) | Perplexity change | KL: WikiText / productive / looping |
134
+ |---|---:|---:|---:|---|---:|---|---:|---|
135
+ | `dev/base` (unedited) | `12.1 +/- 1.6` | `88.7` | `29,683` | baseline | `100.0` | `453 (4) / 0 / 3 (2) / 56 (56)` | `0%` (`20.009`) | `0 / 0 / 0` |
136
+ | `capA-a6` (just before the loop, recipe A) | `43.0 +/- 2.6` | `52.5` | `22,699` | `+30.9 +/- 2.3`, `95 / 1` | `89.3` | `237 (3) / 1 (0) / 141 (94) / 133 (123)` | `+0.51%` | `0.0178 / 0.0103 / 0.0162` |
137
+ | `capB-a6` (well before the loop, recipe B) | `58.2 +/- 2.9` | `30.7` | `19,899` | `+46.1 +/- 2.6`, `113 / 0` | `83.7` | `108 (0) / 0 / 145 (67) / 259 (231)` | `+0.92%` | `0.0271 / 0.0117 / 0.0208` |
138
+ | `p50c` (released edit, alpha 6) | `61.5 +/- 2.8` | `19.7` | `16,104` | `+49.4 +/- 2.6`, `125 / 0` | `76.6` | `72 (0) / 0 / 96 (29) / 344 (286)` | `+0.79%` | `0.0333 / 0.0097 / 0.0241` |
139
+
140
+ Paired per-problem differences across the 256 problems:
141
+
142
+ - `capB-a6` beats `capA-a6` by `+15.2 +/- 2.2` percentage points.
143
+ - `capB-a6` trails the released edit `p50c` by `-3.3 +/- 1.5` percentage points.
144
+
145
+ An answer closed normally if it wrote `</think>` with no repeated snippet in the 6,000 characters before it ([Lab 4](lab4_dose.md)). Both capstone edits escape a running loop about equally often (`141` for recipe A, `145` for recipe B), while `capB-a6` nearly doubles normal endings (`259` vs `133`) and more than halves answers trapped at the cap (`108` vs `237`). A likely reading, though these counts do not prove it: recipe B also seems to stop more loops from starting. Rescued / lost in the table counts problems, relative to the unedited model.
146
+
147
+ ## Why it works
148
+
149
+ ### 1. From the model's own failures to a rank-one weight edit
150
+
151
+ A language model reads and writes one token (word piece) at a time. At each token, it updates a 1,024-number residual stream across 24 layers (blocks). Each block has a token mixer and an MLP (multi-layer perceptron) whose output matrix `down_proj` has 3,584 columns, one per neuron. In Lab 3, each of the agent's 4 text pairs shared the same prompt and opening lines, so subtracting the productive text from the looping text canceled the problem topic.
152
+
153
+ In `runs/build/base`, each answer cut at the 32,768-token cap starts with productive reasoning before falling into a verbatim loop (the same token block repeating to the cap). Comparing a window inside the loop against an earlier window from the same answer via teacher forcing (feeding the saved tokens through the model in one pass and recording activations with PyTorch hooks) cancels the problem topic within each answer.
154
+
155
+ ```mermaid
156
+ flowchart LR
157
+ S["Start of answer<br/>(token n0)"] --> WB["Well before the loop (recipe B)<br/>(36 tokens at midpoint of n0..onset)"]
158
+ WB --> WA["Just before the loop (recipe A)<br/>(36 tokens ending 32 tokens before onset)"]
159
+ WA --> ON["Loop starts<br/>(token onset, period p)"]
160
+ ON --> WL["Loop window: inside the loop<br/>(36 tokens starting at onset + 2p)"]
161
+ ```
162
+
163
+ For every looping answer whose loop starts at least `512` tokens into the answer (`on - n0 >= 512`), `labs/lab8_capstone.py` slices three 36-token windows (`WIN = 36`):
164
+
165
+ - **Inside the loop (`loop`):** 36 tokens starting two periods after loop onset (`on + 2 * p`), once verbatim repetition is underway.
166
+ - **Just before the loop (recipe A):** 36 tokens ending `GAP = 32` tokens before loop onset (`on - 68` to `on - 32`).
167
+ - **Well before the loop (recipe B):** 36 tokens centered halfway between the start of the answer and loop onset (`n0 + (on - n0) // 2 - 18` to `+ 18`).
168
+
169
+ Because the model is causal (each token reads only earlier tokens), the script truncates each forward pass right after the needed windows (`end = max(w["loop"][1], min(on + 256, len(ids)))`) instead of running all `32,768` tokens. That finishes all 225 answers in about 5 minutes on one GPU.
170
+
171
+ At layers $L \in \{15, 19\}$, the remaining steps follow Lab 3:
172
+
173
+ 1. **Direction $u$:** For each answer $i$ and recipe $k \in \{A, B\}$, subtract the mean residual stream leaving block $L$ over window $k$ from the mean over the loop window: $\Delta h_{i, L}^{(k)} = \bar{h}_{i, L}^{\text{loop}} - \bar{h}_{i, L}^{(k)}$. Average $\Delta h_{i, L}^{(k)}$ across all 225 answers and scale to unit length:
174
+ $$u_L^{(k)} = \frac{\overline{\Delta h}_L^{(k)}}{\|\overline{\Delta h}_L^{(k)}\|}$$
175
+ 2. **Neuron scores:** Over the last `AWIN = 24` tokens of each window, find the mean activation difference $\overline{\Delta a}_L^{(k)}[j]$ (loop window minus comparison window) for each of the 3,584 MLP neurons. Multiply by how much column $j$ of `down_proj` writes along $u_L^{(k)}$:
176
+ $$\text{score}_j = \left(u_L^{(k)} \cdot W_{\text{down}, L}[:, j]\right) \times \overline{\Delta a}_L^{(k)}[j]$$
177
+ 3. **Rank-one edit on the top 64 columns:** Sort the 3,584 neurons by $\text{score}_j$ descending, keep the top 64 indices $S$, and apply the rank-one update (one column vector $u$ times one row vector $u^T W_S$) $W' = W - \alpha \, u \, (u^T W_S)$ with edit strength $\alpha = 6$. This scales those 64 neurons' push along $u$ by $1 - \alpha = -5$.
178
+
179
+ ```mermaid
180
+ flowchart LR
181
+ R["225 looping answers<br/>from build128 (runs/build/base)"] --> W["Take 36-token windows:<br/>loop minus window A or B"]
182
+ W --> U["Average stream difference<br/>across answers -> unit vector u"]
183
+ U --> N["Score 3,584 neurons:<br/>(u . W[:, j]) * mean(da_24)<br/>Keep top 64 at alpha = 6"]
184
+ N --> T["Build edited models and test<br/>on 256 unseen problems (dev256)"]
185
+ ```
186
+
187
+ ### 2. Why "well before the loop" beats "just before the loop"
188
+
189
+ Recipe A and recipe B have cosine similarity `0.951` at layer 15 and `0.959` at layer 19, and both pick `28 of 64` released neurons at layer 19 (`38` vs `42` at layer 15). Yet recipe B beats recipe A on `dev256` by `15.2` percentage points (`58.2%` vs `43.0%`).
190
+
191
+ In `lab8_loop_onset.png` and Lab 6 Part 3, hundreds of tokens before exact repetition starts at token `0`, the model is already re-checking the same numbers and drifting toward the loop:
192
+
193
+ - At layer 15, the stream projection along the released $u$ climbs from `1.14` (`2,048 to 1,024` tokens before onset) to `1.58` in the last `128` tokens before onset, then jumps to `2.20` inside the loop.
194
+ - In Lab 6 Part 3, the 64 neurons' push $s_{15}(t)$ nearly doubled from `0.096` in the early half of the pre-loop answer to `0.178` in the last 512 tokens before onset, then jumped to `0.424` inside the loop.
195
+
196
+ Window A (`-68` to `-32` tokens before onset) sits inside that late drift zone, where the stream has already moved about halfway to the loop. When recipe A computes `stream(in the loop) - stream(just before the loop)`, it subtracts out the early drift and keeps mostly the final jump into verbatim repetition. Three results reflect this:
197
+
198
+ 1. **The raw gap vector is less than half as long:** Recipe A's $\|\overline{\Delta h}\|$ is `0.689` vs `1.418` for recipe B at layer 15 (`1.037` vs `2.234` at layer 19). Because part of the loop signal cancels out, background token differences between the two windows make up a larger share of the normalized unit vector $u_A$, lowering its cosine with the released $u$ (`0.722` vs `0.782`) and its layer-15 neuron overlap (`38` vs `42`).
199
+ 2. **Smaller neuron activation gaps:** Because loop neurons are already firing harder in the last 68 pre-loop tokens, subtracting window A's activations shrinks $\overline{\Delta a}_{24}$ on the neurons that build up the drift (the `score` of neuron 11 at layer 19 drops from `0.210` in B to `0.121` in A, and neuron 46 at layer 15 drops from `0.0261` to `0.0120`).
200
+ 3. **Fewer loops versus more escapes on `dev256`:** Both edits escape a loop about equally often (`141` for recipe A, `145` for recipe B), while recipe B nearly doubles normal endings (`259` vs `133`). A likely reading, which the endings counts fit but do not prove, is that recipe B's direction includes the pre-loop drift, so its edit pushes back before repetition starts, whereas recipe A seems to act mainly once a loop is underway.
201
+
202
+ ### 3. What split-half stability and the data-needed curve measure
203
+
204
+ To check that the direction reflects a shared property across problems instead of sample noise, the script runs 20 random split-half checks. Each trial shuffles the 225 loops into two non-overlapping halves of `112` and `113` answers ($H_1$ and $H_2$), computes a unit direction from each half, and takes their cosine similarity. Recipe B's halves agree at `0.988 +/- 0.003` (layer 15) and `0.989 +/- 0.003` (layer 19), and recipe A's agree at `0.978` at both layers. Different loops point along nearly the same direction in the 1,024-dimensional stream.
205
+
206
+ To measure how many answers are needed, the script draws $N \in \{1, 2, 4, 8, 16, 32, 64, 112\}$ answers from $H_1$ and measures the cosine of their direction with the full held-out half $H_2$:
207
+
208
+ ```mermaid
209
+ xychart-beta
210
+ title "Layer 15: recipe B (upper line) vs recipe A (lower line)"
211
+ x-axis "Answers used" ["1", "2", "4", "8", "16", "32", "64", "112"]
212
+ y-axis "Cosine with held-out half" 0.5 --> 1.0
213
+ line [0.600, 0.628, 0.712, 0.841, 0.919, 0.952, 0.970, 0.978]
214
+ line [0.676, 0.781, 0.853, 0.912, 0.958, 0.970, 0.984, 0.988]
215
+ ```
216
+
217
+ At $N = 1$ (one looping answer), recipe B already has a `0.68` cosine with the held-out half. By $N = 16$, it reaches `0.958` (`0.919` for recipe A), and past `32` answers the curve is nearly flat (`0.970` to `0.988`). In `lab8_data_needed.png`, the dashed curve (cosine with the released $u$) levels off near `0.78` for recipe B and `0.72` for recipe A. Averaging more answers reduces sampling noise around the natural-loop direction, while the gap between `0.78` and `1.00` is a systematic difference between windows of long natural answers and the agent's 4 hand-written pairs.
218
+
219
+ Because each `build128` problem has two answers, splitting by answer can put two answers to the same problem in opposite halves. To check whether that inflates agreement, we reran with `--split-by problem`, keeping both answers of each problem in the same half:
220
+
221
+ ```bash
222
+ CUDA_VISIBLE_DEVICES=7 python labs/lab8_capstone.py --split-by problem # writes results/lab8/try-split-by-problem/
223
+ ```
224
+
225
+ The 225 loops come from 120 problems (105 with two loops), giving halves of 108 to 117 loops. The split-half cosines barely change: `0.977 / 0.978` for recipe A at layers 15 / 19 (vs `0.978 / 0.978` by answer) and `0.988 / 0.987` for recipe B (vs `0.988 / 0.989`). Both data-needed curves shift by at most `0.068` at $N = 1$. From $N = 16$ on, held-out agreement shifts by under `0.006` and released-$u$ agreement by under `0.009`. When the half that the $N$ answers come from holds fewer than 112 loops, the final point uses all of them (`108` to `112` in `answers_used_min_max` in `capstone.json`). The edits themselves are unchanged because the split affects only these two checks.
226
+
227
+ ### 4. Why we keep `build128` separate from `dev256` and use paired differences
228
+
229
+ Section 13.3 of the theory guide states a basic evaluation rule: test an edit on problems not used to build it. Here `build128` (`128` problems, `256` answers) is used only to extract $u$ and the 64 neurons, and `dev256` (`256` separate problems, `512` answers) is used only for testing.
230
+
231
+ On `dev256`, `capB-a6` scores `58.2% +/- 2.9%` and `p50c` scores `61.5% +/- 2.8%`, where the standard error (SE) is the typical random error from testing a finite sample of problems (a gap of 2 SE or more is unlikely to be luck). Judging from the standalone SEs (`2.9%` and `2.8%`), the `3.3`-point gap looks like it could be chance variation. Because both models run on the same 256 problems, ranging from easy GSM8K arithmetic to hard competition algebra, problem-to-problem difficulty inflates both standalone SEs. Taking the paired difference on each problem $p$ (the fraction of sample answers solved by model 1 minus the fraction solved by model 2 on problem $p$) and computing the SE across those 256 differences cancels out shared problem difficulty:
232
+
233
+ - `capB-a6` minus `p50c`: `-3.3 +/- 1.5` percentage points (more than 2 SE below `p50c`, so `capB-a6` is close to the released edit and measurably a little weaker).
234
+ - `capB-a6` minus `capA-a6`: `+15.2 +/- 2.2` percentage points (nearly seven standard errors).
235
+
236
+ ## Experiments: sample count and edit strength
237
+
238
+ We ran two experiment flags on `sampler` to test how the capstone edit behaves with fewer loops (`--max-samples 16`) or a stronger edit (`--alpha 8`).
239
+
240
+ ### 1. How the directions and weight changes compare
241
+
242
+ - `--max-samples 16` (`results/lab8/try-max-samples-16/`) keeps the first `16` usable loops in file order (`used[:16]`), a file prefix instead of a random draw across all 128 problems. Because `build128` lists its 64 GSM8K problems before its 64 MATH problems, all 16 loops come from `9` GSM8K problems (`gsm8k_train_3030`, `3613`, `4033`, `1318`, `4077`, `6248`, `6350`, `6176`, `7193`). With 16 loops, the data-needed curve stops at `8` answers, and the split-half cosine compares two random halves of `8` loops each (vs `112` and `113` in the main run).
243
+ - `--alpha 8` (`results/lab8/try-alpha-8/`) uses all `225` loops, so it keeps the main run's unit directions $u$ and 64 neurons and raises edit strength from $\alpha = 6$ to $\alpha = 8$. The relative weight change (Frobenius norm (the overall size) of $W' - W$ divided by Frobenius norm of $W$, layer 15 / layer 19) is `0.078 / 0.075` for `capA-max-samples-16`, `0.097 / 0.092` for `capB-max-samples-16`, `0.141 / 0.129` for `capA-alpha-8`, and `0.149 / 0.147` for `capB-alpha-8`.
244
+
245
+ | Edit variant | Loops used | Alpha | Cosine with released $u$ (L15 / L19) | Split-half cosine (L15 / L19) | Released neurons picked (L15 / L19) |
246
+ |---|---:|---:|---|---|---|
247
+ | `capA-a6` (just before, main) | `225` | `6` | `0.722 / 0.724` | `0.978 / 0.978` | `38 / 28` |
248
+ | `capA-max-samples-16` (just before, 16 loops) | `16` | `6` | `0.574 / 0.596` | `0.632 / 0.622` | `27 / 21` |
249
+ | `capB-a6` (well before, main) | `225` | `6` | `0.782 / 0.773` | `0.988 / 0.989` | `42 / 28` |
250
+ | `capB-max-samples-16` (well before, 16 loops) | `16` | `6` | `0.687 / 0.682` | `0.828 / 0.864` | `34 / 20` |
251
+
252
+ With 16 loops from 9 GSM8K problems, recipes A and B still agree (`0.873` at layer 15 and `0.902` at layer 19, vs `0.951` and `0.959` on 225 loops), neuron 11 of layer 19 still ranks first in both, and both directions separate the agent's 8 texts in all 4 pairs (`+0.69, +0.83, +1.67, +1.10` for recipe B at layer 15).
253
+
254
+ ### 2. Evaluation on `dev256`, endings, and breakdown by source
255
+
256
+ | Edit variant | Accuracy % | Cut % | Mean tokens | Correct when finished % | Gain vs unedited (pp), rescued / lost | Paired change vs same recipe at alpha 6 with all 225 loops | Perplexity change | KL: WikiText / productive / looping |
257
+ |---|---:|---:|---:|---:|---|---|---:|---|
258
+ | `capA-a6` (just before, main) | `43.0 +/- 2.6` | `52.5` | `22,699` | `89.3` | `+30.9 +/- 2.3`, `95 / 1` | baseline for recipe A | `+0.51%` | `0.0178 / 0.0103 / 0.0162` |
259
+ | `capA-alpha-8` (just before, alpha 8) | `42.8 +/- 2.8` | `53.5` | `22,772` | `89.9` | `+30.7 +/- 2.5`, `87 / 4` | `-0.2 +/- 2.3` | `+1.61%` | `0.0298 / 0.0183 / 0.0265` |
260
+ | `capA-max-samples-16` (just before, 16 loops) | `17.6 +/- 1.9` | `82.6` | `28,525` | `96.6` | `+5.5 +/- 1.9`, `34 / 17` | `-25.4 +/- 2.3` | `+0.10%` | `0.0081 / 0.0063 / 0.0086` |
261
+ | `capB-a6` (well before, main) | `58.2 +/- 2.9` | `30.7` | `19,899` | `83.7` | `+46.1 +/- 2.6`, `113 / 0` | baseline for recipe B | `+0.92%` | `0.0271 / 0.0117 / 0.0208` |
262
+ | `capB-alpha-8` (well before, alpha 8) | `54.5 +/- 2.9` | `34.0` | `20,870` | `81.7` | `+42.4 +/- 2.6`, `107 / 1` | `-3.7 +/- 1.6` | `+2.76%` | `0.0468 / 0.0206 / 0.0340` |
263
+ | `capB-max-samples-16` (well before, 16 loops) | `53.7 +/- 2.8` | `39.6` | `20,725` | `88.7` | `+41.6 +/- 2.6`, `110 / 0` | `-4.5 +/- 1.8` | `+0.06%` | `0.0204 / 0.0102 / 0.0173` |
264
+ | `p50c` (released edit, alpha 6) | `61.5 +/- 2.8` | `19.7` | `16,104` | `76.6` | `+49.4 +/- 2.6`, `125 / 0` | (alpha 8 vs 6: `-0.8 +/- 1.6`) | `+0.79%` | `0.0333 / 0.0097 / 0.0241` |
265
+
266
+ Answer endings in `results/lab8/try-alpha-8/endings.json` and `results/lab8/try-max-samples-16/endings.json` (out of 512 answers, with correct counts in parentheses):
267
+
268
+ - `capA-alpha-8`: `242 (5)` cut while thinking, `0` stopped while thinking, `130 (87)` escaped a loop, `140 (127)` closed normally (vs `capA-a6`: `237 (3)` cut, `1 (0)` stopped, `141 (94)` escaped a loop, `133 (123)` closed normally).
269
+ - `capB-alpha-8`: `133 (1)` cut while thinking, `1 (0)` stopped while thinking, `96 (44)` escaped a loop, `282 (234)` closed normally (vs `capB-a6`: `108 (0)` cut, `0` stopped, `145 (67)` escaped a loop, `259 (231)` closed normally).
270
+ - `capA-max-samples-16`: `422 (4)` cut while thinking (`96.0%` of its cut answers end in repetition, `94.8%` in a verbatim loop), `0` stopped, `38 (34)` escaped a loop, `52 (52)` closed normally.
271
+ - `capB-max-samples-16`: `150 (1)` cut while thinking (`76.8%` of its cut answers end in repetition, `36.9%` in a verbatim loop), `0` stopped, `183 (107)` escaped a loop, `179 (167)` closed normally.
272
+
273
+ Breakdown by problem source on `dev256` (`128` GSM8K and `128` MATH problems, `256` answers each):
274
+
275
+ | Edit variant | GSM8K accuracy % / cut % | MATH accuracy % / cut % |
276
+ |---|---|---|
277
+ | `capA-a6` (just before, main) | `55.5 / 37.5` | `30.5 / 67.6` |
278
+ | `capA-alpha-8` (just before, alpha 8) | `51.2 / 41.8` | `34.4 / 65.2` |
279
+ | `capA-max-samples-16` (just before, 16 loops) | `23.8 / 76.2` | `11.3 / 89.1` |
280
+ | `capB-a6` (well before, main) | `71.5 / 13.3` | `44.9 / 48.0` |
281
+ | `capB-alpha-8` (well before, alpha 8) | `68.0 / 15.6` | `41.0 / 52.3` |
282
+ | `capB-max-samples-16` (well before, 16 loops) | `69.1 / 20.3` | `38.3 / 59.0` |
283
+ | `p50c` (released edit, alpha 6) | `74.6 / 4.7` | `48.4 / 34.8` |
284
+
285
+ Using 16 loops instead of 225 costs recipe A `-31.6 +/- 3.4` points on GSM8K and `-19.1 +/- 2.9` on MATH (paired vs `capA-a6`), and costs recipe B `-2.3 +/- 2.7` on GSM8K and `-6.6 +/- 2.5` on MATH (paired vs `capB-a6`). All 16 loops came from GSM8K, which may explain why recipe B loses slightly more on MATH, though the difference between sources (about 4 points) is within noise.
286
+
287
+ Two lessons from these runs:
288
+
289
+ 1. **Turning up $\alpha$ did not help either recipe.** Raising $\alpha$ from `6` to `8` leaves `capA` flat (`-0.2 +/- 2.3` points) and drops `capB` by `-3.7 +/- 1.6` points, while WikiText-2 perplexity damage grows from `+0.51%` and `+0.92%` at $\alpha = 6$ to `+1.61%` and `+2.76%` at $\alpha = 8$. Even the released edit gains nothing at $\alpha = 8$ (`-0.8 +/- 1.6` points, perplexity `+3.33%`). Raising $\alpha$ scales the whole update, including any part of $u$ that points the wrong way. With one stronger setting per recipe, we cannot tell whether a partly wrong direction or a strength already past its peak explains the drop.
290
+ 2. **A few loops are enough for recipe B ("well before the loop"), but not for recipe A ("just before the loop").** From 16 loops (9 problems), recipe B still reaches `53.7%` accuracy (`+41.6` points over the unedited model), `4.5 +/- 1.8` points below 225-loop `capB-a6` (`58.2%`), even though its direction is noisier (two halves of 8 loops agree at cosine `0.828` and `0.864`, against `0.988` and `0.989` with halves of 112 and 113 loops) and sits further from the released $u$. The direction table above lists its cosines with the released direction for both layers. Recipe A on the same 16 loops collapses to `17.6%` (`-25.4 +/- 2.3` points vs `capA-a6`), and `82.6%` of its answers are still cut. Its direction is the noisiest of all: two halves of 8 loops agree at only `0.632` and `0.622` at layers 15 and 19, and the direction table above lists its lower cosines with the released $u$. Its WikiText KL is `0.0081` (vs `0.0204` for `capB-max-samples-16` and `0.0178` for `capA-a6`), but [Lab 4](lab4_dose.md) and [Lab 7](lab7_side_effects.md) suggest that KL size does not explain the accuracy gap: random-direction control `randdir` (same 64 neurons, same size of weight change, random direction) has WikiText KL `0.0046` and gains `+2.3` points, while applying the agent's direction to all 3,584 neurons (`all2`) at $\alpha = 2$ has almost the same KL as `capA-max-samples-16` (`0.0078` vs `0.0081`) and gains `+37.3` points. This suggests that, in this setup, the gain depends on where the change points instead of how far it shifts WikiText predictions. Recipe B on 16 loops beats recipe A on 225 loops by `+10.7 +/- 2.1` points (paired), suggesting that window choice matters more than loop count here. One caveat: `--max-samples 16` takes the first 16 loops in file order (all from 9 GSM8K problems). In `data_needed` (`results/lab8/capstone.json`), random 16-loop draws agreed better with the released $u$ at layers `15 / 19` (`0.666 / 0.683` for A and `0.757 / 0.746` for B, vs `0.574 / 0.596` and `0.687 / 0.682` on the first 16), so that narrow sample may explain part of the drop. We did not build or test edits from random 16-loop sets. Data quality, window placement, and sample count trade off against one another.
291
+
292
+ ## Check yourself
293
+
294
+ 1. Why does `labs/lab8_capstone.py` take both the loop window and the baseline window (window A or window B) from the same answer, instead of averaging all looping answers and subtracting the average of the 26 non-looping answers in `build128`?
295
+ <details><summary>Answer</summary>
296
+
297
+ The 26 finished answers come from 18 problems, and 215 of the 225 loops come from other problems where neither answer finished. Subtracting the finished answers' average would mix problem differences (topic, numbers, problem type) into $u$ along with the loop signal. Taking both windows from the same answer cancels the prompt and topic within each example, as the shared opening did in Lab 3.
298
+ </details>
299
+
300
+ 2. At layer 15, recipe A and recipe B have a cosine similarity of `0.951`, yet recipe B scores `58.2%` on `dev256` and recipe A scores `43.0%`. What happens in the residual stream during the last few hundred tokens before a loop that makes window A a worse baseline than window B?
301
+ <details><summary>Answer</summary>
302
+
303
+ The residual stream along $u$ and the 64 neurons' push $s(t)$ are already climbing hundreds of tokens before the verbatim loop starts (`stream15` rises from `1.14` well before the loop to `1.58` in the last 128 tokens before onset, and `2.20` inside). Window A sits 32 to 68 tokens before loop onset, so subtracting it removes much of the loop buildup, halves the gap norm $\|\overline{\Delta h}\|$ (`0.689` vs `1.418`), and likely yields an edit that reacts mainly after a loop starts instead of stopping the drift into a loop.
304
+ </details>
305
+
306
+ 3. When you run `labs/lab8_capstone.py --max-samples 16`, recipe B keeps most of its accuracy (`53.7%` vs `58.2%`), while recipe A drops from `43.0%` to `17.6%`. Which diagnostic numbers from the 16-loop extraction and the main run's `data_needed` table warn you before running `dev256` that recipe A will suffer much more than recipe B, and why did the first 16 loops fall below the random-16 curve?
307
+ <details><summary>Answer</summary>
308
+
309
+ First, `--max-samples 16` splits the 16 loops into two halves of `8` loops. Recipe B's two halves still agree at cosine `0.828` (layer 15) and `0.864` (layer 19), with released-$u$ cosines `0.687` and `0.682`. In recipe A, subtracting window A cancels half the loop signal (`gap_size` is half as large), so sample noise dominates: its two halves agree at `0.632` and `0.622`, and its released-$u$ cosines drop to `0.574` and `0.596`. Second, `--max-samples 16` takes `used[:16]` in file order (all from 9 GSM8K problems) instead of 16 random loops across all 128 problems; in `data_needed`, random 16-loop sets had released-$u$ cosines of `0.666 / 0.683` (recipe A) and `0.757 / 0.746` (recipe B).
310
+ </details>
311
+
312
+ 4. Why is the standard error on the paired difference between `capB-a6` and `p50c` (`-3.3 +/- 1.5` percentage points) nearly half as large as the standalone standard error on either run (`58.2 +/- 2.9%` and `61.5 +/- 2.8%`)?
313
+ <details><summary>Answer</summary>
314
+
315
+ The standalone standard error (`2.8%` or `2.9%`) includes the wide difficulty variation across the 256 problems in `dev256`. Because both edits are tested on the same 256 problems, pairing the scores problem by problem subtracts each problem's baseline difficulty and leaves only the variance of the difference between the two edits.
316
+ </details>
317
+
318
+ ## Try this next
319
+
320
+ 1. **Restrict to loops that start late (`--min-before 2048`).** We did not run this experiment, so write down your prediction first. Out of the `225` verbatim loops in `build128`, `218` start at least `2,048` tokens into the answer (10th percentile `2,813` tokens). We expect `218` loops to be used and the directions $u$ and neuron overlaps to barely move, because Lab 6 Part 3 and the onset profile show the same gradual pre-loop drift regardless of onset position. Run the extraction (about 5 minutes on one GPU):
321
+ ```bash
322
+ CUDA_VISIBLE_DEVICES=7 python labs/lab8_capstone.py --min-before 2048
323
+ ```
324
+ Compare `cos_with_released_u`, `overlap_with_released_64`, and `split_half_cos_mean_sd` in `results/lab8/try-min-before-2048/capstone.json` against the default `--min-before 512` run.
325
+ 2. **Move window A further back from the loop onset (`GAP = 32` to `GAP = 512`).** Near the top of `labs/lab8_capstone.py`, `WIN, AWIN, GAP = 36, 24, 32` sets how many tokens before loop onset window A ends (`GAP = 32`). Change `GAP` from `32` to `512` and pass `--try-name gap-512`:
326
+ ```bash
327
+ CUDA_VISIBLE_DEVICES=7 python labs/lab8_capstone.py --try-name gap-512
328
+ ```
329
+ Check `results/lab8/try-gap-512/capstone.json` (and reset `GAP` to `32` afterward). Does moving window A 512 tokens before onset raise recipe A's `gap_size` (`0.689` at layer 15 when `GAP = 32`) and bring its cosine with the released $u$ closer to recipe B's `0.782`?
330
+ 3. **Predict what recipe A would do with 16 random loops instead of the first 16 (`used[:16]`).** We did not run this experiment. In `labs/lab8_capstone.py`, `used = used[:a.max_samples]` takes the first 16 loops in file order (9 GSM8K problems), giving recipe A a cosine of `0.574 / 0.596` with the released $u$ and `17.6%` accuracy on `dev256`. In `results/lab8/capstone.json`, the `answers = 16` entry of `data_needed` shows that across random 16-loop draws from all 225 loops, recipe A averages `0.666` (layer 15) and `0.683` (layer 19) with the released $u$, and `0.919` and `0.914` with the held-out half (`113` answers). Predict before editing: if you replace that line with `used = [used[i] for i in rng.choice(len(used), a.max_samples, replace=False)]` (`rng` is defined earlier) and run `--max-samples 16 --try-name rand16`, how much closer will `cos_with_released_u` and `overlap_with_released_64` move toward `capA-a6` (`0.722 / 0.724`), and will recipe A still trail recipe B at 16 random loops?
331
+ 4. **Read windows A and B of one loop yourself with `labs/peek.py --tokens`.** Under `"loops"` in `results/lab8/capstone.json`, each of the 225 loops records `id`, answer index `j`, `loop_starts_at`, `period`, and the token ranges of `A`, `B`, and `loop` from the start of the answer. In the median-onset answer `math_train_algebra_1625` (answer `1`), window B covers tokens `2,893` to `2,929`, window A covers `5,754` to `5,790`, and the 51-token loop starts at `5,822`. Print window B and the run-up to the loop on `sampler`:
332
+ ```bash
333
+ CUDA_VISIBLE_DEVICES="" python labs/peek.py build/base --id math_train_algebra_1625 --answer 1 --tokens 2893 2929
334
+ CUDA_VISIBLE_DEVICES="" python labs/peek.py build/base --id math_train_algebra_1625 --answer 1 --tokens 5200 5822
335
+ ```
336
+ Each command prints where the loop starts, one cycle, the last 1,500 characters, and the requested tokens. Window B is ordinary algebra (*"The equation is $b + n = 17.8$. So $b = 17.8 -$"*). Soon after token `5,200` the model checks $b = 8.8$ (the right answer), then circles through near-copies of one check (*"Wait, if $b = 8.99$, then $b \le 9$."* alternating with *"... then $b < 9$."*) for a few hundred tokens before the 51-token cycle locks in. Window A sits inside that circling. What does that show about what recipe A subtracts out when using window A as its baseline?
code/labs/lab8_capstone.py ADDED
@@ -0,0 +1,348 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ """Lab 8 (capstone): build your own edit from the model's own loops, then test it on problems it never saw.
3
+
4
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
5
+ CUDA_VISIBLE_DEVICES=7 python labs/lab8_capstone.py # about 5 minutes on one GPU
6
+ CUDA_VISIBLE_DEVICES="" python scripts/build_models.py --variants configs/capstone.json # CPU, about a minute
7
+ python scripts/run_jobs.py plans/extra.json --gpus 6,7 --only 'dev/cap*' # about 30 minutes, one run per GPU
8
+ python scripts/summarize.py /nvme/wsp-lab/runs # writes /nvme/wsp-lab/summary.md
9
+
10
+ Experiments (each writes to its own folder, results/lab8/try-*, and leaves the main results, the figures and
11
+ configs/capstone.json alone; the script prints the commands that build and test the try's two edits):
12
+ CUDA_VISIBLE_DEVICES=7 python labs/lab8_capstone.py --max-samples 16 # u from only 16 loops
13
+ CUDA_VISIBLE_DEVICES=7 python labs/lab8_capstone.py --min-before 2048 # only loops that start late
14
+ CUDA_VISIBLE_DEVICES=7 python labs/lab8_capstone.py --alpha 8 # same u and neurons, stronger edit
15
+ CUDA_VISIBLE_DEVICES=7 python labs/lab8_capstone.py --split-by problem # stability checks with no problem in both halves
16
+
17
+ The paper's agent built its edit from 8 short texts that it wrote and labeled itself. Here the data is the
18
+ unedited model's own answers to 128 practice problems (runs/build/base; none of them is used for testing).
19
+ For every answer that ends in a verbatim loop, take three 36-token windows:
20
+ in the loop starts two periods after the loop starts, when the model is surely repeating
21
+ just before ends 32 tokens before the loop starts (recipe A)
22
+ well before the middle of the text before the loop (recipe B)
23
+ u = mean over answers of (stream in the loop - stream before), after blocks 15 and 19, scaled to length 1.
24
+ Neuron score = (how much neuron j writes along u) x (how much more neuron j fires in the loop; last 24 tokens of
25
+ each window). The 64 highest-scoring neurons get the edit with alpha 6: the agent's recipe, your data.
26
+
27
+ Before you trust any direction you extract, measure:
28
+ agreement cos with the released u, and how many of the released 64 neurons you picked too
29
+ stability split the answers in half at random: do the two halves give the same u?
30
+ data needed how many answers you need before u stops changing (the agent used 4 pairs)
31
+ transfer does your u, built from the model's loops, separate the agent's 8 hand-written texts?
32
+ and one question about the loops themselves: lined up at the token where each loop starts, when does the stream
33
+ move along the released u, and when do the released 64 neurons start pushing (s(t), the edit's trigger)?
34
+ """
35
+ import argparse
36
+ import json
37
+ import os
38
+ import sys
39
+
40
+ import numpy as np
41
+ import torch
42
+
43
+ ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
44
+ sys.path.insert(0, ROOT)
45
+ from wsp import hf, loops # noqa: E402
46
+
47
+ ap = argparse.ArgumentParser()
48
+ ap.add_argument("--run", default=os.path.join(hf.LAB, "runs", "build", "base"))
49
+ ap.add_argument("--data", default="build128")
50
+ ap.add_argument("--min-before", type=int, default=512, help="skip loops that start fewer answer tokens in")
51
+ ap.add_argument("--max-samples", type=int, default=0, help="use at most this many loops (0 = all)")
52
+ ap.add_argument("--alpha", type=float, default=6)
53
+ ap.add_argument("--split-by", choices=("answer", "problem"), default="answer",
54
+ help="stability checks: split the loops at random by answer, or keep both answers to a problem "
55
+ "in the same half (the edits themselves do not change)")
56
+ ap.add_argument("--try-name", help="write to results/lab8/try-<name>/ (a name is picked for you when a flag changes)")
57
+ a = ap.parse_args()
58
+
59
+ # Experiments write to results/lab8/try-*/ and name their edits capA-<try>/capB-<try>, so the main results, the
60
+ # figures, configs/capstone.json and the models/qwen35-cap*-a6 folders are never overwritten.
61
+ changed = [f"{k.replace('_', '-')}-{getattr(a, k):g}" for k in ("min_before", "max_samples", "alpha")
62
+ if getattr(a, k) != ap.get_default(k)]
63
+ if a.run != ap.get_default("run") or a.data != ap.get_default("data"):
64
+ changed.append(f"{os.path.basename(os.path.normpath(a.run))}-{a.data}")
65
+ # --split-by on its own changes only the stability checks, so those edits are the main run's. Every other experiment,
66
+ # including a --try-name run after you change the code (GAP, say), writes edits you can build.
67
+ new_edits = bool(changed) or a.split_by == "answer"
68
+ if a.split_by != "answer":
69
+ changed.append(f"split-by-{a.split_by}")
70
+ TRY = a.try_name or "-".join(changed) or None
71
+ OUT = os.path.join(ROOT, "results", "lab8", f"try-{TRY}") if TRY else os.path.join(ROOT, "results", "lab8")
72
+ FIG = OUT if TRY else os.path.join(ROOT, "results", "figures")
73
+ os.makedirs(OUT, exist_ok=True)
74
+ os.makedirs(FIG, exist_ok=True)
75
+ LAYERS = (15, 19)
76
+ WIN, AWIN, GAP = 36, 24, 32 # window sizes as in the agent's recipe; GAP = distance of window A from onset
77
+ PRE, POST = 2048, 256 # the onset profile covers this many tokens before and after each loop starts
78
+ RECIPES = {"A": "just before the loop", "B": "well before the loop"}
79
+ rng = np.random.default_rng(0)
80
+
81
+
82
+ def unit(v):
83
+ return v / v.norm()
84
+
85
+
86
+ rel = hf.released_edit()
87
+ lab3 = json.load(open(os.path.join(ROOT, "results", "lab3", "rebuild.json")))
88
+ # the released u, signed to point toward looping (Lab 3 found which way it points)
89
+ u_rel = {L: unit(rel[L]["u"].float()) * float(np.sign(lab3["layers"][str(L)]["cos_with_released_u"]))
90
+ for L in LAYERS}
91
+
92
+ # 1. find the loops
93
+ rows = loops.read_rows(hf.LAB, a.data)
94
+ m, tok = hf.load("base")
95
+ n_total = n_cut = 0
96
+ for rec, j, s, text in loops.iter_samples(a.run):
97
+ n_total += 1
98
+ n_cut += bool(s.get("cut"))
99
+ found = loops.looping_samples(tok, a.run, rows)
100
+ print(f"{n_total} answers in {a.run}: {n_cut} hit the token cap, {len(found)} of those end in a verbatim loop")
101
+
102
+
103
+ def windows(f):
104
+ n0, on, p, T = f["n_prompt"], f["onset"], f["period"], len(f["ids"])
105
+ r0 = min(on + 2 * p, T - 1 - WIN) # the last token is not part of the checked loop
106
+ if on - n0 < a.min_before or r0 < on:
107
+ return None
108
+ b0 = n0 + (on - n0) // 2 - WIN // 2
109
+ return {"loop": (r0, r0 + WIN), "A": (on - GAP - WIN, on - GAP), "B": (b0, b0 + WIN)}
110
+
111
+
112
+ used = [(f, w) for f in found if (w := windows(f))]
113
+ if a.max_samples:
114
+ used = used[:a.max_samples]
115
+ N = len(used)
116
+ print(f"using {N} loops (loop starts at least {a.min_before} answer tokens in)")
117
+ assert N >= 8, "too few loops to build a direction"
118
+ Wd = {L: hf.down_proj(m, L).weight.detach().float().cpu() for L in LAYERS}
119
+ r_rel = {L: Wd[L][:, rel[L]["cols"]].T @ u_rel[L] for L in LAYERS} # per released neuron: its push along u
120
+
121
+ # 2. one forward pass per answer, cut where nothing later is needed (the model is causal)
122
+ dh = {k: {L: [] for L in LAYERS} for k in RECIPES}
123
+ da = {k: {L: [] for L in LAYERS} for k in RECIPES}
124
+ prof = {q: {L: [] for L in LAYERS} for q in ("stream", "s")}
125
+ meta, example = [], None
126
+ order_by_onset = sorted(range(N), key=lambda i: used[i][0]["onset"] - used[i][0]["n_prompt"])
127
+ example_i = order_by_onset[N // 2]
128
+ for i, (f, w) in enumerate(used):
129
+ n0, on = f["n_prompt"], f["onset"]
130
+ end = max(w["loop"][1], min(on + POST, len(f["ids"])))
131
+ x = torch.tensor([f["ids"][:end]], device="cuda")
132
+ with torch.no_grad(), hf.record_outputs(m, LAYERS) as H, hf.record_mlp_acts(m, LAYERS) as A:
133
+ m.model(x, use_cache=False)
134
+ pos = np.arange(end) - on
135
+ keep = (pos >= -PRE) & (pos < POST) & (np.arange(end) >= n0)
136
+ ex = {} if i == example_i else None
137
+ for L in LAYERS:
138
+ h, act = H[L][0], A[L][0]
139
+ hw = {k: h[s:e].mean(0) for k, (s, e) in w.items()}
140
+ aw = {k: act[e - AWIN:e].mean(0) for k, (s, e) in w.items()}
141
+ for k in RECIPES:
142
+ dh[k][L].append(hw["loop"] - hw[k])
143
+ da[k][L].append(aw["loop"] - aw[k])
144
+ for q, v in (("stream", h @ u_rel[L]), ("s", act[:, rel[L]["cols"]] @ r_rel[L])):
145
+ row = np.full(PRE + POST, np.nan, dtype=np.float32)
146
+ row[pos[keep] + PRE] = v.numpy()[keep]
147
+ prof[q][L].append(row)
148
+ if ex is not None:
149
+ ex[f"stream{L}"] = (h[n0:] @ u_rel[L]).tolist()
150
+ ex[f"s{L}"] = (act[n0:, rel[L]["cols"]] @ r_rel[L]).tolist()
151
+ meta.append({"id": f["id"], "j": f["j"], "loop_starts_at": on - n0, "period": f["period"],
152
+ "answer_tokens": len(f["ids"]) - n0, "windows": {k: [s - n0, e - n0] for k, (s, e) in w.items()}})
153
+ if ex is not None:
154
+ example = {"meta": meta[-1], **ex}
155
+ del H, A
156
+ if (i + 1) % 25 == 0:
157
+ print(f" {i + 1}/{N} answers")
158
+
159
+ # 3. directions, neurons, and the checks
160
+ res = {"answers": n_total, "cut": n_cut, "verbatim_loops": len(found), "used": N, "min_before": a.min_before,
161
+ "periods": np.percentile([x["period"] for x in meta], [10, 25, 50, 75, 90]).tolist(),
162
+ "loop_starts_at": np.percentile([x["loop_starts_at"] for x in meta], [10, 25, 50, 75, 90]).tolist(),
163
+ "recipes": {}, "loops": meta}
164
+ print(f"loop period, 10/25/50/75/90th percentile: {res['periods']} tokens")
165
+ print(f"loop starts at answer token, same percentiles: {res['loop_starts_at']}")
166
+ pairs = json.load(open(os.path.join(ROOT, "labs", "data", "astra_pairs.json")))
167
+ with torch.no_grad(), hf.record_outputs(m, LAYERS) as H:
168
+ for t in pairs["texts"]:
169
+ m.model(hf.encode(tok, t), use_cache=False)
170
+ astra_tail = {L: torch.stack([h[-WIN:].mean(0) for h in H[L]]) for L in LAYERS} # [8 texts, 1024]
171
+ SIZES = sorted({n for n in (1, 2, 4, 8, 16, 32, 64, 128) if n < N // 2} | {N // 2})
172
+ problem_of = np.unique([f["id"] for f, _ in used], return_inverse=True)[1] # which problem each loop came from
173
+ n_problems = int(problem_of.max()) + 1
174
+ half_sizes = []
175
+
176
+
177
+ def split():
178
+ """Two halves of the loops for the stability checks."""
179
+ if a.split_by == "answer": # two answers to the same problem can land in opposite halves
180
+ perm = rng.permutation(N)
181
+ h1, h2 = perm[:N // 2], perm[N // 2:]
182
+ else: # every problem's loops stay together, so the halves share no problem
183
+ first = rng.permutation(n_problems)[:n_problems // 2]
184
+ in1 = np.isin(problem_of, first)
185
+ h1, h2 = np.flatnonzero(in1), np.flatnonzero(~in1)
186
+ half_sizes.extend((len(h1), len(h2)))
187
+ return h1, h2
188
+
189
+
190
+ capstone = {}
191
+ for k, desc in RECIPES.items():
192
+ res["recipes"][k] = {"windows": desc, "layers": {}}
193
+ spec = {}
194
+ for L in LAYERS:
195
+ D, DA = torch.stack(dh[k][L]), torch.stack(da[k][L])
196
+ u = unit(D.mean(0))
197
+ score = (u @ Wd[L]) * DA.mean(0)
198
+ top_order = torch.argsort(score, descending=True)
199
+ top = sorted(top_order[:64].tolist())
200
+ halves, curve = [], {n: {"held_out_half": [], "released": [], "used": []} for n in SIZES}
201
+ for _ in range(20):
202
+ h1, h2 = split()
203
+ u2 = unit(D[h2].mean(0))
204
+ halves.append(float(unit(D[h1].mean(0)) @ u2))
205
+ for n in SIZES: # a problem split can leave fewer than N // 2 loops in h1; then the last point uses all
206
+ used_n = min(n, len(h1))
207
+ un = unit(D[rng.choice(h1, used_n, replace=False)].mean(0))
208
+ curve[n]["held_out_half"].append(float(un @ u2))
209
+ curve[n]["released"].append(float(un @ u_rel[L]))
210
+ curve[n]["used"].append(used_n)
211
+ proj = astra_tail[L] @ u
212
+ gaps = (proj[1::2] - proj[0::2]).tolist() # looping minus productive, one per pair
213
+ r = {"cos_with_released_u": float(u @ u_rel[L]),
214
+ "overlap_with_released_64": len(set(top) & set(rel[L]["cols"])),
215
+ "split_half_cos_mean_sd": [float(np.mean(halves)), float(np.std(halves))],
216
+ "data_needed": [{"answers": n, "cos_with_held_out_half": float(np.mean(c["held_out_half"])),
217
+ "cos_with_released_u": float(np.mean(c["released"])),
218
+ **({"answers_used_min_max": [min(c["used"]), max(c["used"])]} if min(c["used"]) < n
219
+ else {})} for n, c in curve.items()],
220
+ "astra_pairs_looping_minus_productive": gaps,
221
+ "gap_size": float(D.mean(0).norm()),
222
+ "top12": top_order[:12].tolist(), "cols": top,
223
+ "scores_top200": [round(float(x), 6) for x in score[top_order[:200]]]}
224
+ res["recipes"][k]["layers"][str(L)] = r
225
+ spec[str(L)] = {"alpha": a.alpha, "dir": [round(float(x), 8) for x in u], "cols": top}
226
+ print(f"recipe {k} ({desc}), layer {L}: cos with released u {r['cos_with_released_u']:+.3f}; "
227
+ f"released neurons picked {r['overlap_with_released_64']}/64; split-half cos "
228
+ f"{r['split_half_cos_mean_sd'][0]:.3f}; agent's 4 pairs, looping minus productive along u: "
229
+ f"{np.round(gaps, 2).tolist()}")
230
+ capstone[f"cap{k}-{TRY}" if TRY else f"cap{k}-a{a.alpha:g}"] = {
231
+ "about": f"Lab 8 recipe {k}: u and 64 neurons from the base model's own loops (windows {desc}), "
232
+ f"alpha {a.alpha:g}",
233
+ "layers": spec}
234
+ print(" answers needed:", [(d["answers"], round(d["cos_with_held_out_half"], 3))
235
+ for d in res["recipes"][k]["layers"]["15"]["data_needed"]])
236
+ res["cos_A_B"] = {}
237
+ for L in LAYERS:
238
+ uA, uB = (torch.tensor(spec_k["layers"][str(L)]["dir"]) for spec_k in capstone.values())
239
+ res["cos_A_B"][str(L)] = float(unit(uA) @ unit(uB))
240
+ print("cos between recipe A's u and recipe B's u:", {L: round(v, 3) for L, v in res["cos_A_B"].items()})
241
+ res["split_by"] = a.split_by
242
+ res["problems"] = n_problems
243
+ res["problems_with_two_loops"] = int((np.bincount(problem_of) > 1).sum())
244
+ res["half_sizes_min_max"] = [min(half_sizes), max(half_sizes)]
245
+ print(f"{N} loops from {n_problems} problems ({res['problems_with_two_loops']} with both answers looping); "
246
+ f"split by {a.split_by}: each half held {min(half_sizes)} to {max(half_sizes)} loops")
247
+
248
+ # 4. the onset profile: mean over loops, lined up at the loop's first token
249
+ res["onset_profile"] = {}
250
+ for q in prof:
251
+ for L in LAYERS:
252
+ P = np.stack(prof[q][L])
253
+ mean = np.nanmean(P, 0)
254
+
255
+ def avg(lo, hi):
256
+ return float(np.nanmean(mean[lo + PRE:hi + PRE]))
257
+ res["onset_profile"][f"{q}{L}"] = {"-2048..-1024": avg(-2048, -1024), "-512..-128": avg(-512, -128),
258
+ "-128..0": avg(-128, 0), "0..128": avg(0, 128), "128..256": avg(128, 256),
259
+ "mean": [round(float(x), 4) for x in mean]}
260
+ print(f"{'stream along u' if q == 'stream' else 's(t), the trigger'} at layer {L}, mean over loops: "
261
+ + ", ".join(f"{z} {v:.2f}" for z, v in res["onset_profile"][f"{q}{L}"].items() if z != "mean"))
262
+ json.dump(res, open(os.path.join(OUT, "capstone.json"), "w"), indent=1)
263
+ json.dump(example, open(os.path.join(OUT, "example_answer.json"), "w"))
264
+ if TRY and not new_edits:
265
+ variants = plan = None
266
+ elif TRY:
267
+ variants, plan = os.path.join(OUT, "variant.json"), os.path.join(OUT, "plan.json")
268
+ json.dump(capstone, open(variants, "w"), indent=1)
269
+ json.dump({"about": f"Lab 8 experiment {TRY}: both recipes' edits on the 256 unseen practice problems",
270
+ "runs": [{"name": f"dev/{v}", "model": f"qwen35-{v}", "data": "dev256", "n": 2, "shards": 1, "est": 8}
271
+ for v in capstone]}, open(plan, "w"), indent=1)
272
+ else:
273
+ variants, plan = os.path.join(ROOT, "configs", "capstone.json"), os.path.join(ROOT, "plans", "extra.json")
274
+ json.dump(capstone, open(variants, "w"), indent=1)
275
+
276
+ # figures
277
+ import matplotlib # noqa: E402
278
+ matplotlib.use("Agg")
279
+ import matplotlib.pyplot as plt # noqa: E402
280
+
281
+
282
+ def smooth(v, w=32):
283
+ v = np.asarray(v, dtype=float)
284
+ return np.convolve(v, np.ones(w) / w, mode="valid")
285
+
286
+
287
+ fig, ax = plt.subplots(2, 2, figsize=(13, 6.5), sharex=True)
288
+ xs = np.arange(-PRE, POST)
289
+ for c, L in enumerate(LAYERS):
290
+ for r_, (q, lab) in enumerate((("stream", "stream along the released u"), ("s", "s(t): the 64 neurons' push"))):
291
+ P = np.stack(prof[q][L])
292
+ lo, mid, hi = np.nanpercentile(P, [25, 50, 75], axis=0)
293
+ ax[r_, c].fill_between(xs, lo, hi, color="#0969da", alpha=0.15, label="middle half of loops")
294
+ ax[r_, c].plot(xs, mid, color="#0969da", lw=1, label="median")
295
+ ax[r_, c].axvline(0, color="black", ls="--", lw=1)
296
+ ax[r_, c].set_title(f"layer {L}: {lab}")
297
+ ax[r_, c].set_xlabel("tokens from the start of the loop")
298
+ ax[0, 0].legend(loc="upper left")
299
+ fig.suptitle(f"{N} loops from the unedited model, lined up at the token where each loop starts")
300
+ fig.tight_layout()
301
+ fig.savefig(os.path.join(FIG, "lab8_loop_onset.png"), dpi=130)
302
+
303
+ fig, ax = plt.subplots(1, 2, figsize=(13, 4))
304
+ for c, L in enumerate(LAYERS):
305
+ for k, col in (("A", "#cf222e"), ("B", "#8250df")):
306
+ dn = res["recipes"][k]["layers"][str(L)]["data_needed"]
307
+ n = [d["answers"] for d in dn]
308
+ ax[c].plot(n, [d["cos_with_held_out_half"] for d in dn], "o-", color=col,
309
+ label=f"recipe {k} vs the other half of the answers")
310
+ ax[c].plot(n, [d["cos_with_released_u"] for d in dn], "o--", color=col, alpha=0.6,
311
+ label=f"recipe {k} vs the released u")
312
+ ax[c].axvline(4, color="#57606a", lw=0.8, ls=":")
313
+ ax[c].annotate("the agent used 4 pairs", (4, 0.05), xytext=(4, 0), textcoords="offset points", fontsize=8)
314
+ ax[c].set_xscale("log", base=2)
315
+ ax[c].set_ylim(0, 1)
316
+ ax[c].set_xlabel("answers used to build u")
317
+ ax[c].set_ylabel("cos")
318
+ ax[c].set_title(f"layer {L}: how many loops does a direction need?")
319
+ ax[0].legend(fontsize=8, loc="lower right")
320
+ fig.tight_layout()
321
+ fig.savefig(os.path.join(FIG, "lab8_data_needed.png"), dpi=130)
322
+
323
+ em = example["meta"]
324
+ fig, ax = plt.subplots(2, 1, figsize=(13, 6), sharex=True)
325
+ for r_, (q, lab) in enumerate((("stream", "stream along the released u"), ("s", "s(t): the 64 neurons' push"))):
326
+ for L, col in zip(LAYERS, ("#0969da", "#8250df")):
327
+ v = smooth(example[f"{q}{L}"])
328
+ ax[r_].plot(np.arange(len(v)) + 16, v, color=col, label=f"layer {L}")
329
+ for k, col in (("B", "#8250df"), ("A", "#cf222e"), ("loop", "#1a7f37")):
330
+ ax[r_].axvspan(*em["windows"][k], color=col, alpha=0.25)
331
+ ax[r_].axvline(em["loop_starts_at"], color="black", ls="--", lw=1)
332
+ ax[r_].set_ylabel(lab)
333
+ ax[0].legend(loc="upper left")
334
+ ax[1].set_xlabel("token position in the answer (32-token moving average)")
335
+ ax[0].set_title(f"One answer ({em['id']}): the loop starts at token {em['loop_starts_at']} and repeats every "
336
+ f"{em['period']} tokens. Shaded: window B, window A, loop window")
337
+ fig.tight_layout()
338
+ fig.savefig(os.path.join(FIG, "lab8_one_answer.png"), dpi=130)
339
+ rp = lambda p: os.path.relpath(p, ROOT) # noqa: E731
340
+ if variants is None:
341
+ print("wrote", rp(OUT), "and three figures in", rp(FIG))
342
+ print("--split-by changes only the stability checks, so the edits match the main run's (configs/capstone.json) and "
343
+ "there is nothing new to build or test (if you also changed the code, run again without --split-by)")
344
+ sys.exit(0)
345
+ print("wrote", rp(OUT), rp(variants), "and three figures in", rp(FIG))
346
+ print("next, build both edits and test them on the 256 unseen practice problems (one GPU per run):")
347
+ print(f" CUDA_VISIBLE_DEVICES=\"\" python scripts/build_models.py --variants {rp(variants)}")
348
+ print(f" python scripts/run_jobs.py {rp(plan)} --gpus 6,7 --only " + " ".join(f"dev/{v}" for v in capstone))
code/labs/peek.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ """Read the model's answers yourself: how each one ended, and the text where it looped or stopped.
3
+
4
+ . /nvme/wsp-lab/code/scripts/env.sh && cd /nvme/wsp-lab/code
5
+ export CUDA_VISIBLE_DEVICES="" # CPU only, a few seconds
6
+ python labs/peek.py quick/base # one line per answer
7
+ python labs/peek.py quick/base --id gsm8k_train_5628 # the end of answer 0 of that problem
8
+ python labs/peek.py dev/p50c --id gsm8k_train_5628 --answer 1 --chars 3000
9
+ python labs/peek.py build/base --id math_train_algebra_1625 --answer 1 --tokens 2893 2929 # one window
10
+
11
+ Run it on the VM that made the run: the answer texts stay there (scripts/pull_runs.sh copies only the per-answer
12
+ numbers to the laptop). For an answer that ends in a verbatim loop, it also finds the loop in tokens: where the
13
+ repetition starts, how many tokens one cycle has, and the text of one cycle. --tokens prints any stretch of the
14
+ answer by token position, counted from the start of the answer as in Lab 8's windows (results/lab8/capstone.json).
15
+ """
16
+ import argparse
17
+ import glob
18
+ import json
19
+ import os
20
+ import sys
21
+
22
+ ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
23
+ sys.path.insert(0, ROOT)
24
+ from wsp import hf, loops # noqa: E402
25
+
26
+ ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
27
+ ap.add_argument("run", help="run name under runs/, e.g. quick/base or dev/p50c")
28
+ ap.add_argument("--id", help="problem id to print (from the list); without it, list every answer")
29
+ ap.add_argument("--answer", type=int, default=0, help="which of the problem's answers (0 = the first)")
30
+ ap.add_argument("--chars", type=int, default=1500, help="how many characters to print from the end")
31
+ ap.add_argument("--tokens", type=int, nargs=2, metavar=("START", "END"),
32
+ help="also print answer tokens START to END (e.g. a window from results/lab8/capstone.json)")
33
+ a = ap.parse_args()
34
+
35
+ run_dir = os.path.join(hf.LAB, "runs", a.run)
36
+ samples = list(loops.iter_samples(run_dir))
37
+ if not samples:
38
+ sys.exit(f"no answer texts in {run_dir}; is this the VM that ran {a.run}?")
39
+
40
+
41
+ def ending(s):
42
+ if s["cut"]:
43
+ return "cut at the cap " + ("after thinking" if s["closed"] else "while thinking")
44
+ return "finished" if s["closed"] else "stopped while thinking"
45
+
46
+
47
+ if not a.id:
48
+ print(f"{'problem':36s} {'answer':>6s} {'tokens':>7s} {'how it ended':29s} {'loops at the end':16s} "
49
+ f"{'correct':7s} model said / truth")
50
+ for rec, j, s, text in samples:
51
+ loop = "yes" if s["verbatim_loop_end"] else ""
52
+ print(f"{rec['id']:36s} {j:6d} {s['tokens']:7,d} {ending(s):29s} {loop:16s} {'yes' if s['correct'] else '':7s}"
53
+ f" {str(s['pred'])[:20]} / {str(rec.get('answer', '-'))[:20]}")
54
+ n, cut = len(samples), sum(s["cut"] for _, _, s, _ in samples)
55
+ print(f"\n{n} answers: {sum(s['correct'] for _, _, s, _ in samples)} correct, {cut} cut at the cap, "
56
+ f"{sum(s['cut'] and s['verbatim_loop_end'] for _, _, s, _ in samples)} of the cut ones end in a verbatim loop")
57
+ print(f"read one: python labs/peek.py {a.run} --id <problem> [--answer N]")
58
+ sys.exit()
59
+
60
+ hit = [(rec, j, s, text) for rec, j, s, text in samples if rec["id"] == a.id and j == a.answer]
61
+ if not hit:
62
+ sys.exit(f"no answer {a.answer} for problem {a.id} in {a.run}; list them with: python labs/peek.py {a.run}")
63
+ rec, j, s, text = hit[0]
64
+ done = json.load(open(sorted(glob.glob(os.path.join(run_dir, "shard*.done.json")))[0]))
65
+ problem = loops.read_rows(hf.LAB, os.path.basename(done["data"]).removesuffix(".jsonl")).get(rec["id"], {})
66
+ print(f"problem {rec['id']}: {problem.get('problem', problem.get('prompt', '?'))}")
67
+ print(f"truth: {rec.get('answer', '-')} model said: {s['pred']} correct: {'yes' if s['correct'] else 'no'}")
68
+ print(f"answer {j}: {s['tokens']:,} tokens, {ending(s)}")
69
+
70
+ if s["verbatim_loop_end"] or a.tokens:
71
+ from transformers import AutoTokenizer
72
+ tok = AutoTokenizer.from_pretrained(hf.model_dir("base"))
73
+ ids = tok(text, add_special_tokens=False).input_ids
74
+ found = loops.find_loop(ids) if s["verbatim_loop_end"] else None
75
+ if found:
76
+ onset, period = found
77
+ print(f"it loops: from token {onset:,} of {len(ids):,}, one block of {period} tokens repeats about "
78
+ f"{(len(ids) - 1 - onset) / period:.0f} times. One cycle:")
79
+ print(" " + repr(tok.decode(ids[onset:onset + period])))
80
+ i = text.find("</think>")
81
+ if i >= 0:
82
+ print(f"\n--- where it closed its thinking (character {i:,} of {len(text):,}) ---")
83
+ print(text[max(0, i - 400):i + 200])
84
+ print(f"\n--- the last {a.chars:,} characters ---")
85
+ print(text[-a.chars:])
86
+ if a.tokens:
87
+ t0, t1 = max(0, a.tokens[0]), min(len(ids), a.tokens[1])
88
+ print(f"\n--- answer tokens {t0:,} to {t1:,} (of {len(ids):,}) ---")
89
+ print(tok.decode(ids[t0:t1]))
code/plans/extra.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "about": "Labs 3 and 8 on unseen problems: the edit rebuilt from the agent's 8 texts, and the capstone edits built from the unedited model's own loops. Same 256 practice problems and settings as plans/sampler.json.",
3
+ "runs": [
4
+ {"name": "dev/rebuilt", "model": "qwen35-rebuilt", "data": "dev256", "n": 2, "shards": 1, "est": 8},
5
+ {"name": "dev/capA-a6", "model": "qwen35-capA-a6", "data": "dev256", "n": 2, "shards": 1, "est": 8},
6
+ {"name": "dev/capB-a6", "model": "qwen35-capB-a6", "data": "dev256", "n": 2, "shards": 1, "est": 8}
7
+ ]
8
+ }
code/plans/lab3-tries.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "about": "Lab 3's three experiments on unseen problems: the edit rebuilt from only the agent's first 2 pairs, with a 36-token neuron window, and with a third layer (21). Same 256 practice problems and settings as plans/sampler.json. Build the three models first: run labs/lab3_rebuild.py with --pairs 2, --windows 36 36 and --layers 15 19 21, then build each results/lab3/try-*/variant.json (labs/lab3_rebuild.md, Try this next).",
3
+ "runs": [
4
+ {
5
+ "name": "dev/rebuilt-pairs2",
6
+ "model": "qwen35-rebuilt-pairs2",
7
+ "data": "dev256",
8
+ "n": 2,
9
+ "shards": 2,
10
+ "est": 8
11
+ },
12
+ {
13
+ "name": "dev/rebuilt-windows36-36",
14
+ "model": "qwen35-rebuilt-windows36-36",
15
+ "data": "dev256",
16
+ "n": 2,
17
+ "shards": 2,
18
+ "est": 8
19
+ },
20
+ {
21
+ "name": "dev/rebuilt-layers15-19-21",
22
+ "model": "qwen35-rebuilt-layers15-19-21",
23
+ "data": "dev256",
24
+ "n": 2,
25
+ "shards": 2,
26
+ "est": 8
27
+ }
28
+ ]
29
+ }
code/plans/quick.json ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "about": "Quick start: the unedited model and the released edit on the first 16 practice problems of dev256 (all from GSM8K), 2 answers each, the paper's settings. About 5 minutes on 2 GPUs. Then read the answers with labs/peek.py.",
3
+ "runs": [
4
+ {
5
+ "name": "quick/base",
6
+ "model": "base",
7
+ "data": "dev256",
8
+ "n": 2,
9
+ "shards": 1,
10
+ "est": 1,
11
+ "args": ["--limit", "16"]
12
+ },
13
+ {
14
+ "name": "quick/p50c",
15
+ "model": "qwen35-p50c",
16
+ "data": "dev256",
17
+ "n": 2,
18
+ "shards": 1,
19
+ "est": 1,
20
+ "args": ["--limit", "16"]
21
+ }
22
+ ]
23
+ }
code/plans/sampler.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "about": "Labs 4, 5 and 8: every edit variant and every decoding fix on 256 unseen practice problems (2 answers each), plus the unedited model's answers on 128 more practice problems for the capstone.",
3
+ "runs": [
4
+ {"name": "build/base", "model": "base", "data": "build128", "n": 2, "shards": 1, "est": 8, "priority": 2},
5
+ {"name": "dev/base", "model": "base", "data": "dev256", "n": 2, "shards": 1, "est": 15, "priority": 1},
6
+ {"name": "dev/p50c", "model": "qwen35-p50c", "data": "dev256", "n": 2, "shards": 1, "est": 8, "priority": 1},
7
+
8
+ {"name": "dev/a-4", "model": "qwen35-a-4", "data": "dev256", "n": 2, "shards": 1, "est": 17},
9
+ {"name": "dev/a1", "model": "qwen35-a1", "data": "dev256", "n": 2, "shards": 1, "est": 13},
10
+ {"name": "dev/a2", "model": "qwen35-a2", "data": "dev256", "n": 2, "shards": 1, "est": 10},
11
+ {"name": "dev/a4", "model": "qwen35-a4", "data": "dev256", "n": 2, "shards": 1, "est": 8},
12
+ {"name": "dev/a8", "model": "qwen35-a8", "data": "dev256", "n": 2, "shards": 1, "est": 8},
13
+ {"name": "dev/a12", "model": "qwen35-a12", "data": "dev256", "n": 2, "shards": 1, "est": 8},
14
+ {"name": "dev/l15only", "model": "qwen35-l15only", "data": "dev256", "n": 2, "shards": 1, "est": 11},
15
+ {"name": "dev/l19only", "model": "qwen35-l19only", "data": "dev256", "n": 2, "shards": 1, "est": 11},
16
+ {"name": "dev/randdir", "model": "qwen35-randdir", "data": "dev256", "n": 2, "shards": 1, "est": 15},
17
+ {"name": "dev/randcols", "model": "qwen35-randcols", "data": "dev256", "n": 2, "shards": 1, "est": 15},
18
+ {"name": "dev/all1", "model": "qwen35-all1", "data": "dev256", "n": 2, "shards": 1, "est": 12},
19
+ {"name": "dev/all2", "model": "qwen35-all2", "data": "dev256", "n": 2, "shards": 1, "est": 10},
20
+
21
+ {"name": "dev/base+temp1.0", "model": "base", "data": "dev256", "n": 2, "shards": 1, "est": 15, "set": ["temperature=1.0"]},
22
+ {"name": "dev/base+pp0", "model": "base", "data": "dev256", "n": 2, "shards": 1, "est": 15, "set": ["presence_penalty=0"]},
23
+ {"name": "dev/base+pp2", "model": "base", "data": "dev256", "n": 2, "shards": 1, "est": 15, "set": ["presence_penalty=2.0"]},
24
+ {"name": "dev/base+rep1.1", "model": "base", "data": "dev256", "n": 2, "shards": 1, "est": 14, "set": ["repetition_penalty=1.1"]},
25
+ {"name": "dev/base+max4k", "model": "base", "data": "dev256", "n": 2, "shards": 1, "est": 2, "set": ["max_tokens=4096"]},
26
+ {"name": "dev/base+nothink", "model": "base", "data": "dev256", "n": 2, "shards": 1, "est": 2, "preset": "nothink35"},
27
+ {"name": "dev/p50c+nothink", "model": "qwen35-p50c", "data": "dev256", "n": 2, "shards": 1, "est": 1, "preset": "nothink35"}
28
+ ]
29
+ }
code/plans/trainer.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "about": "Lab 2: the paper's three benchmarks, unedited model vs the released edit, same settings as the paper (thinking on, 32,768-token cap).",
3
+ "runs": [
4
+ {"name": "bench/base/math500", "model": "base", "data": "math500", "n": 4, "shards": 4, "est": 62},
5
+ {"name": "bench/base/gsm8k", "model": "base", "data": "gsm8k", "n": 2, "shards": 5, "est": 77},
6
+ {"name": "bench/base/ifeval", "model": "base", "data": "ifeval", "n": 2, "shards": 2, "est": 27},
7
+ {"name": "bench/p50c/math500", "model": "qwen35-p50c", "data": "math500", "n": 4, "shards": 3, "est": 44},
8
+ {"name": "bench/p50c/gsm8k", "model": "qwen35-p50c", "data": "gsm8k", "n": 2, "shards": 2, "est": 32},
9
+ {"name": "bench/p50c/ifeval", "model": "qwen35-p50c", "data": "ifeval", "n": 2, "shards": 1, "est": 5}
10
+ ]
11
+ }
code/reference/theory-guide.md ADDED
@@ -0,0 +1,1791 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Weight-space programming: from the basics to the actual research
2
+
3
+ The central idea is that a neural network is a numerical program, and a capable LLM may be able to inspect, build, or repair parts of that program by editing its weights directly.
4
+
5
+ Each lab in this project tests a section of this guide.
6
+
7
+ | Guide section | Lab that makes it hands-on |
8
+ |---|---|
9
+ | 1 to 5: weights, architecture, training vs editing, the language-model machinery | Background. Read before [Lab 0](../labs/lab0_toy.md). |
10
+ | 6: each MLP column writes a vector | [Lab 0](../labs/lab0_toy.md) |
11
+ | 7: what went wrong with Qwen | [Lab 2](../labs/lab2_reproduce.md) |
12
+ | 8.1 to 8.4: directions, dot products, removing a component | [Lab 0](../labs/lab0_toy.md) |
13
+ | 8.5: from removal to counteraction (the strength alpha) | [Lab 0](../labs/lab0_toy.md), then [Lab 4](../labs/lab4_dose.md) |
14
+ | 9, steps 1 to 3: contexts, the direction, the neurons, the columns | [Lab 3](../labs/lab3_rebuild.md), then [Lab 8](../labs/lab8_capstone.md) |
15
+ | 9, step 4: the matrix operation | [Lab 1](../labs/lab1_diff.md) |
16
+ | 10: why believe this repaired repetition | [Lab 2](../labs/lab2_reproduce.md) and the controls in [Lab 4](../labs/lab4_dose.md) |
17
+ | 12C: low rank versus LoRA | [Lab 1](../labs/lab1_diff.md) (the change is rank one) |
18
+ | 12D: weight editing versus activation steering | [Lab 6](../labs/lab6_steering.md) |
19
+ | 13.2: compare against a strong practical baseline | [Lab 5](../labs/lab5_decoding.md) |
20
+ | 13.3: keep development data separate | [Lab 8](../labs/lab8_capstone.md) |
21
+ | 13.4: measure more than accuracy | [Lab 7](../labs/lab7_side_effects.md) |
22
+ | 11, 12A, 12B, 12E, 12F, 13.1, 13.5, 13.6, 14 | No lab. Read them in the guide. |
23
+
24
+ We usually find weights by training. This study asks whether an LLM can instead read what weights compute, repair a broken network, or write a network from scratch. [rikiyat.github.io](https://rikiyat.github.io/blog/weight-space-programming/)
25
+
26
+ In the main language-model experiment, a small Qwen model often got stuck in repetitive reasoning, and a direct weight edit helped it finish many more answers. Understanding that repair requires five pieces:
27
+
28
+ 1. How weights turn numbers into a computation.
29
+ 2. How direct editing differs from training.
30
+ 3. What attention heads and MLPs do inside a language model.
31
+ 4. How vectors, projections, and small matrix updates change behavior.
32
+ 5. What these experiments show and what stays open.
33
+
34
+ Every exercise has its worked solution right below "Check after trying." Cover the solution and work each one on paper first.
35
+
36
+ ---
37
+
38
+ ## 1. Start here: what is a neural-network weight?
39
+
40
+ A paddle moves left or right to stay under a ball. Its input has two coordinates:
41
+
42
+ $$
43
+ x=
44
+ \begin{pmatrix}
45
+ p\\
46
+ b
47
+ \end{pmatrix},
48
+ $$
49
+
50
+ where:
51
+
52
+ - $p$ is the paddle's horizontal position.
53
+ - $b$ is the ball's horizontal position.
54
+
55
+ With the paddle at $3$ and the ball at $8$:
56
+
57
+ $$
58
+ x=
59
+ \begin{pmatrix}
60
+ 3\\
61
+ 8
62
+ \end{pmatrix}.
63
+ $$
64
+
65
+ The ball is to the right, so we subtract:
66
+
67
+ $$
68
+ b-p.
69
+ $$
70
+
71
+ $$
72
+ 8-3=5.
73
+ $$
74
+
75
+ A positive difference means the ball is to the right; a negative difference means it is to the left. Written with weights:
76
+
77
+ $$
78
+ (-1)p+(1)b.
79
+ $$
80
+
81
+ The weights are $-1$ and $1$, which set how much each input contributes. A weight is a numerical coefficient in a computation; when we know the rule we want, we can choose the weights ourselves instead of training them.
82
+
83
+ ### Build a tiny network yourself
84
+
85
+ We want two hidden quantities that measure how far the ball is to the right or left:
86
+
87
+ $$
88
+ h_{\text{right}}=\max(0,b-p),
89
+ $$
90
+
91
+ $$
92
+ h_{\text{left}}=\max(0,p-b).
93
+ $$
94
+
95
+ The operation
96
+
97
+ $$
98
+ \operatorname{ReLU}(t)=\max(0,t)
99
+ $$
100
+
101
+ is pronounced "ree-loo." It keeps positive values and replaces negative values with zero.
102
+
103
+ **Try first:** What $2\times2$ matrix computes $b-p$ in its first row and $p-b$ in its second row?
104
+
105
+ *Hint: each row specifies one weighted sum of the input coordinates.*
106
+
107
+ ### Check after trying
108
+
109
+ $$
110
+ W_1=
111
+ \begin{pmatrix}
112
+ -1&1\\
113
+ 1&-1
114
+ \end{pmatrix}.
115
+ $$
116
+
117
+ Now multiply:
118
+
119
+ $$
120
+ W_1x
121
+ =
122
+ \begin{pmatrix}
123
+ -1&1\\
124
+ 1&-1
125
+ \end{pmatrix}
126
+ \begin{pmatrix}
127
+ 3\\
128
+ 8
129
+ \end{pmatrix}
130
+ =
131
+ \begin{pmatrix}
132
+ (-1)(3)+(1)(8)\\
133
+ (1)(3)+(-1)(8)
134
+ \end{pmatrix}
135
+ =
136
+ \begin{pmatrix}
137
+ 5\\
138
+ -5
139
+ \end{pmatrix}.
140
+ $$
141
+
142
+ Apply ReLU to each entry:
143
+
144
+ $$
145
+ h=
146
+ \operatorname{ReLU}(W_1x)
147
+ =
148
+ \begin{pmatrix}
149
+ 5\\
150
+ 0
151
+ \end{pmatrix}.
152
+ $$
153
+
154
+ Interpretation:
155
+
156
+ - Rightward distance: $5$.
157
+ - Leftward distance: $0$.
158
+
159
+ ### Turn those hidden quantities into actions
160
+
161
+ Our three output scores will be ordered:
162
+
163
+ $$
164
+ \text{left},\quad \text{stay},\quad \text{right}.
165
+ $$
166
+
167
+ Define:
168
+
169
+ $$
170
+ z=
171
+ W_2h+c,
172
+ $$
173
+
174
+ with
175
+
176
+ $$
177
+ W_2=
178
+ \begin{pmatrix}
179
+ 0&1\\
180
+ 0&0\\
181
+ 1&0
182
+ \end{pmatrix},
183
+ \qquad
184
+ c=
185
+ \begin{pmatrix}
186
+ 0\\
187
+ 1\\
188
+ 0
189
+ \end{pmatrix}.
190
+ $$
191
+
192
+ The vector $c$ is a bias, an offset added after multiplication.
193
+
194
+ Calculate:
195
+
196
+ $$
197
+ z=
198
+ \begin{pmatrix}
199
+ 0&1\\
200
+ 0&0\\
201
+ 1&0
202
+ \end{pmatrix}
203
+ \begin{pmatrix}
204
+ 5\\
205
+ 0
206
+ \end{pmatrix}
207
+ +
208
+ \begin{pmatrix}
209
+ 0\\
210
+ 1\\
211
+ 0
212
+ \end{pmatrix}
213
+ =
214
+ \begin{pmatrix}
215
+ 0\\
216
+ 1\\
217
+ 5
218
+ \end{pmatrix}.
219
+ $$
220
+
221
+ The highest score is "right." Because we knew the rule in advance, we could program the network by choosing its weights without a dataset.
222
+
223
+ ### What did the bias accomplish?
224
+
225
+ If the ball and paddle have the same position:
226
+
227
+ $$
228
+ h=
229
+ \begin{pmatrix}
230
+ 0\\
231
+ 0
232
+ \end{pmatrix},
233
+ \qquad
234
+ z=
235
+ \begin{pmatrix}
236
+ 0\\
237
+ 1\\
238
+ 0
239
+ \end{pmatrix}.
240
+ $$
241
+
242
+ The paddle stays still. "Stay" wins whenever the position difference is smaller than $1$; a tie needs a tie-breaking rule.
243
+
244
+ ### Exercise: run it in the other direction
245
+
246
+ Now let:
247
+
248
+ $$
249
+ x=
250
+ \begin{pmatrix}
251
+ 8\\
252
+ 3
253
+ \end{pmatrix}.
254
+ $$
255
+
256
+ Compute the hidden vector and action scores.
257
+
258
+ ### Check after trying
259
+
260
+ $$
261
+ W_1x=
262
+ \begin{pmatrix}
263
+ -5\\
264
+ 5
265
+ \end{pmatrix},
266
+ \qquad
267
+ h=
268
+ \begin{pmatrix}
269
+ 0\\
270
+ 5
271
+ \end{pmatrix}.
272
+ $$
273
+
274
+ Therefore:
275
+
276
+ $$
277
+ z=
278
+ \begin{pmatrix}
279
+ 5\\
280
+ 1\\
281
+ 0
282
+ \end{pmatrix}.
283
+ $$
284
+
285
+ The paddle moves left.
286
+
287
+ **What you should be able to explain:** the first layer computes relationships between inputs, and the second layer turns those relationships into action scores.
288
+
289
+ ---
290
+
291
+ ## 2. Architecture, weights, activations, and weight space
292
+
293
+ Keep these concepts separate:
294
+
295
+ | Term | Meaning | In our paddle network |
296
+ |---|---|---|
297
+ | **Architecture** | Which operations happen and how they connect | Matrix multiplication, ReLU, another matrix multiplication |
298
+ | **Weights** | Stored coefficients specifying the computation | Entries of $W_1$ and $W_2$ |
299
+ | **Biases** | Stored offsets | The $1$ that favors staying still |
300
+ | **Activations** | Intermediate values produced for a particular input | $h=(5,0)^T$ |
301
+ | **Logits** | Raw output scores before conversion to probabilities | $z=(0,1,5)^T$ |
302
+ | **Parameters** | Collective name for adjustable stored values | All weights and biases |
303
+
304
+ During inference, inputs and activations change from example to example while the stored parameters stay fixed. Collecting every parameter into one vector gives
305
+
306
+ $$
307
+ \theta,
308
+ $$
309
+
310
+ pronounced "theta." The network is the function:
311
+
312
+ $$
313
+ f_\theta(x).
314
+ $$
315
+
316
+ Read this as "the function applied to input $x$ with parameters $\theta$."
317
+
318
+ ### What does "weight space" mean?
319
+
320
+ If a model has two weights:
321
+
322
+ $$
323
+ \theta=(w_1,w_2).
324
+ $$
325
+
326
+ Every setting is a point on a two-dimensional plane. A model with a million parameters lives in a million-dimensional space, with one coordinate per stored parameter. Editing weights moves the model to another point in that space.
327
+
328
+ Weight space differs from activation space. An activation vector holds the intermediate numbers produced on one input; a weight vector holds part of the stored program.
329
+
330
+ ### A useful limitation of the "program" analogy
331
+
332
+ In ordinary code, a line might say:
333
+
334
+ ```python
335
+ if ball_is_right:
336
+ move_right()
337
+ ```
338
+
339
+ Trained networks rarely separate into clean lines like this. One concept can spread across many neurons and matrices, and one neuron can take part in several behaviors. Our paddle network is easy to read because we designed each coordinate by hand; networks shaped by training are much harder to read.
340
+
341
+ ---
342
+
343
+ ## 3. How ordinary training differs from direct weight editing
344
+
345
+ Start with a one-weight model:
346
+
347
+ $$
348
+ \hat y=wx.
349
+ $$
350
+
351
+ Here:
352
+
353
+ - $x$: input.
354
+ - $w$: weight.
355
+ - $\hat y$, pronounced "y-hat": predicted output.
356
+
357
+ Suppose:
358
+
359
+ $$
360
+ x=2,\qquad y=6,\qquad w=1.
361
+ $$
362
+
363
+ The target output is $6$, and the model predicts:
364
+
365
+ $$
366
+ \hat y=1\cdot2=2.
367
+ $$
368
+
369
+ ### Forward pass
370
+
371
+ A forward pass runs the input through the current weights to produce an output, here $2$.
372
+
373
+ ### Loss
374
+
375
+ A loss function scores how far the prediction is from the target:
376
+
377
+ $$
378
+ L(w)=\frac12(wx-y)^2.
379
+ $$
380
+
381
+ At $w=1$:
382
+
383
+ $$
384
+ L(1)=\frac12(2-6)^2=8.
385
+ $$
386
+
387
+ ### Backward pass
388
+
389
+ A backward pass computes how the loss changes when each weight changes (the derivative of the loss with respect to that weight):
390
+
391
+ $$
392
+ \frac{dL}{dw}=(wx-y)x.
393
+ $$
394
+
395
+ Substituting:
396
+
397
+ $$
398
+ \frac{dL}{dw}=(2-6)\cdot2=-8.
399
+ $$
400
+
401
+ Because the derivative is negative, nudging $w$ upward lowers the loss.
402
+
403
+ ### Gradient-descent update
404
+
405
+ With learning rate $\eta=0.1$:
406
+
407
+ $$
408
+ w_{\text{new}}
409
+ =
410
+ w-\eta\frac{dL}{dw}
411
+ =
412
+ 1-0.1(-8)
413
+ =
414
+ 1.8.
415
+ $$
416
+
417
+ The new prediction is closer to $6$:
418
+
419
+ $$
420
+ 1.8\cdot2=3.6.
421
+ $$
422
+
423
+ ### Direct programming
424
+
425
+ When we know the exact relationship, we can solve directly:
426
+
427
+ $$
428
+ 2w=6,
429
+ $$
430
+
431
+ so:
432
+
433
+ $$
434
+ w=3.
435
+ $$
436
+
437
+ **Try first:** Why does this example not prove that gradient descent is unnecessary for large networks?
438
+
439
+ ### Check after trying
440
+
441
+ Here the specification gave the equation directly. In a large nonlinear network:
442
+
443
+ - We may not know the exact internal formula the task needs.
444
+ - Many weights interact across layers.
445
+ - Many parameter settings produce the same output on a given input.
446
+ - Fixing one behavior can break another.
447
+
448
+ Direct editing works when we can isolate the mechanism we want to change; training works when the mechanism has to be discovered from examples.
449
+
450
+ ### Where weight-space programming fits
451
+
452
+ | Approach | How the target model is changed |
453
+ |---|---|
454
+ | Prompting | Change the input; keep its weights fixed |
455
+ | Ordinary fine-tuning | Compute gradients and update weights |
456
+ | An LLM writing a training script | The LLM designs the procedure; a training algorithm updates weights |
457
+ | Random or evolutionary search | Try parameter changes and retain promising ones |
458
+ | Weight-space programming in this work | The LLM reasons about the target and directly constructs parameter changes |
459
+
460
+ Keep two distinctions in mind:
461
+
462
+ $$
463
+ \boxed{
464
+ \text{No gradients does not imply no data, no feedback, or no computation.}
465
+ }
466
+ $$
467
+
468
+ Some experiments in this study are one-shot constructions, while others test and revise edits over many trials. Every experiment also involves two separate models:
469
+
470
+ - The editor: a frontier pretrained LLM.
471
+ - The target: the network being inspected or changed.
472
+
473
+ "No training" means no gradient updates on the target during the edit. Both the editor and any pretrained target were already trained beforehand.
474
+
475
+ ---
476
+
477
+ ## 4. How could anyone infer behavior from weights?
478
+
479
+ Return to a single weighted sum:
480
+
481
+ $$
482
+ z=-p+b.
483
+ $$
484
+
485
+ When we know what $p$ and $b$ mean, we can see that $z$ measures relative position. From the weight row alone,
486
+
487
+ $$
488
+ (-1,1),
489
+ $$
490
+
491
+ we cannot conclude "paddle controller." It could compute:
492
+
493
+ - Revenue minus cost.
494
+ - Target position minus robot position.
495
+ - Tomorrow's temperature minus today's temperature.
496
+
497
+ Weights get their meaning from the architecture and from the input and output coordinates they connect. In the game-identification experiments, the authors gave the editor the architecture and the input and output descriptions while withholding the game names, so the task was to infer the game from how the learned weights used those known inputs. [rikiyat.github.io](https://rikiyat.github.io/blog/weight-space-programming/)
498
+
499
+ ### Why boundary layers can be revealing
500
+
501
+ Suppose the input is an image grid. First-layer weights that concentrate on the bottom row of the "agent" channel suggest an agent constrained near the bottom of the screen. Output weights that mainly separate left from right add another clue. Together, those clues can suggest a paddle game. That is meaningful inference, but it is a different achievement from explaining every internal computation.
502
+
503
+ ### Exercise: the same numbers, a changed interface
504
+
505
+ A robot controller uses:
506
+
507
+ $$
508
+ z=x_1-x_2,
509
+ $$
510
+
511
+ where $x_1$ is the target position and $x_2$ is the robot position.
512
+
513
+ A new sensor interface reverses the order:
514
+
515
+ $$
516
+ x'=
517
+ \begin{pmatrix}
518
+ \text{robot position}\\
519
+ \text{target position}
520
+ \end{pmatrix}.
521
+ $$
522
+
523
+ What should the new weight row be?
524
+
525
+ ### Check after trying
526
+
527
+ The original weight row was:
528
+
529
+ $$
530
+ (1,-1).
531
+ $$
532
+
533
+ The reversed interface needs:
534
+
535
+ $$
536
+ (-1,1).
537
+ $$
538
+
539
+ The behavior is unchanged, while the weights flip to match the coordinate order. This is why reading weights requires knowing what the coordinates mean.
540
+
541
+ ### Another complication: different weights can implement the same function
542
+
543
+ Suppose:
544
+
545
+ $$
546
+ h_1=\operatorname{ReLU}(x_1),\qquad
547
+ h_2=\operatorname{ReLU}(x_2),
548
+ $$
549
+
550
+ and:
551
+
552
+ $$
553
+ y=2h_1+3h_2.
554
+ $$
555
+
556
+ Now swap the hidden neurons:
557
+
558
+ $$
559
+ h'_1=\operatorname{ReLU}(x_2),\qquad
560
+ h'_2=\operatorname{ReLU}(x_1).
561
+ $$
562
+
563
+ Use:
564
+
565
+ $$
566
+ y=3h'_1+2h'_2.
567
+ $$
568
+
569
+ Both versions compute the same function. For $x=(4,5)$, both produce:
570
+
571
+ $$
572
+ 2(4)+3(5)=23.
573
+ $$
574
+
575
+ Because hidden neurons can be swapped without changing the output, "neuron 1" has no fixed meaning across networks; what matters is how a neuron connects to the layers around it.
576
+
577
+ ---
578
+
579
+ ## 5. The language-model machinery you need
580
+
581
+ ### Tokens become vectors
582
+
583
+ A tokenizer splits text into tokens (words, subwords, or punctuation), and an embedding maps each token to a vector. At each token position, the model updates an internal vector:
584
+
585
+ $$
586
+ h.
587
+ $$
588
+
589
+ ### The residual stream
590
+
591
+ As a useful approximation, each component adds its update to the running vector, and the existing vector continues forward:
592
+
593
+ $$
594
+ h_{\text{next}}=h+\text{update}.
595
+ $$
596
+
597
+ This running vector is the residual stream. Attention heads and MLPs read from it and add their outputs back into it, like a shared workspace made of numerical features rather than readable text.
598
+
599
+ ### Attention moves information between positions
600
+
601
+ For a simplified attention head:
602
+
603
+ $$
604
+ q_i=W_Qh_i,
605
+ \qquad
606
+ k_j=W_Kh_j,
607
+ \qquad
608
+ v_j=W_Vh_j.
609
+ $$
610
+
611
+ Here:
612
+
613
+ - $i$ is the token position receiving information.
614
+ - $j$ is a candidate position to read from.
615
+ - $q_i$, the query, specifies what position $i$ looks for.
616
+ - $k_j$, the key, specifies what position $j$ offers.
617
+ - $v_j$, the value, holds the information that can be copied.
618
+
619
+ The matching score includes:
620
+
621
+ $$
622
+ q_i^Tk_j.
623
+ $$
624
+
625
+ The superscript $T$ means transpose, which turns a column vector into a row so the product is one number. Expanding the query and key shows why multiplying weight matrices together is informative:
626
+
627
+ $$
628
+ q_i^Tk_j
629
+ =
630
+ h_i^TW_Q^TW_Kh_j.
631
+ $$
632
+
633
+ The product
634
+
635
+ $$
636
+ W_Q^TW_K
637
+ $$
638
+
639
+ describes which token features match which, and the output path uses the product
640
+
641
+ $$
642
+ W_OW_V.
643
+ $$
644
+
645
+ In the transformer-circuits framework (shown here without positional encodings or normalization): [transformer-circuits.pub](https://transformer-circuits.pub/2021/framework/index.html)
646
+
647
+ - **QK:** selects where to read information from.
648
+ - **OV:** determines what is written back into the residual stream.
649
+
650
+ ### What is an induction head?
651
+
652
+ When a sequence contains
653
+
654
+ > A B ... A
655
+
656
+ an induction circuit predicts
657
+
658
+ > B
659
+
660
+ by matching the current token to its earlier occurrence and copying what followed it.
661
+
662
+ **Try first:** In the pattern "red apple ... red," should a copying circuit copy the previous word "red"?
663
+
664
+ ### Check after trying
665
+
666
+ No, the continuation is "apple." The circuit must link the earlier "red" to the token after it, which is why the researchers inspected products of matrices across heads rather than looking only for large single weights.
667
+
668
+ ### MLPs compute and write features
669
+
670
+ An MLP (multilayer perceptron) transforms the representation at one position:
671
+
672
+ $$
673
+ a=\phi(W_{\text{up}}h),
674
+ $$
675
+
676
+ $$
677
+ m=W_{\text{down}}a,
678
+ $$
679
+
680
+ $$
681
+ h_{\text{next}}=h+m.
682
+ $$
683
+
684
+ Here $a$ holds the intermediate feature activations, and $W_{\text{down}}$ turns those activations into an update for the residual stream (real models may also use gating and normalization). For the Qwen target, the dimensions are:
685
+
686
+ $$
687
+ h\in\mathbb R^{1024},
688
+ \qquad
689
+ a\in\mathbb R^{3584},
690
+ \qquad
691
+ W_{\text{down}}\in\mathbb R^{1024\times3584}.
692
+ $$
693
+
694
+ The notation $\mathbb R^{1024}$ means "a vector of 1,024 real numbers." This Qwen model mixes Gated DeltaNet and gated-attention blocks, so standard attention does not describe every layer. [Hugging Face](https://huggingface.co/Qwen/Qwen3.5-0.8B)
695
+
696
+ ---
697
+
698
+ ## 6. The key insight: each MLP column writes a vector
699
+
700
+ Let the columns of $W_{\text{down}}$ be:
701
+
702
+ $$
703
+ w_1,w_2,\ldots,w_{3584}.
704
+ $$
705
+
706
+ Then matrix multiplication can be rewritten:
707
+
708
+ $$
709
+ \boxed{
710
+ W_{\text{down}}a
711
+ =
712
+ a_1w_1+a_2w_2+\cdots+a_{3584}w_{3584}.
713
+ }
714
+ $$
715
+
716
+ This is the most useful equation for understanding the edit. Each intermediate activation $a_j$ controls how much of column $w_j$ gets added:
717
+
718
+ - If $a_j=0$, that feature contributes nothing.
719
+ - If $a_j=2$, it contributes $2w_j$.
720
+ - Editing $w_j$ changes the vector that feature writes whenever it activates.
721
+
722
+ ### Calculate it
723
+
724
+ Suppose:
725
+
726
+ $$
727
+ W=
728
+ \begin{pmatrix}
729
+ 1&2\\
730
+ 3&4
731
+ \end{pmatrix},
732
+ \qquad
733
+ a=
734
+ \begin{pmatrix}
735
+ 2\\
736
+ 0
737
+ \end{pmatrix}.
738
+ $$
739
+
740
+ **Try first:** Compute $Wa$ by thinking in columns.
741
+
742
+ ### Check after trying
743
+
744
+ The columns are:
745
+
746
+ $$
747
+ w_1=
748
+ \begin{pmatrix}
749
+ 1\\
750
+ 3
751
+ \end{pmatrix},
752
+ \qquad
753
+ w_2=
754
+ \begin{pmatrix}
755
+ 2\\
756
+ 4
757
+ \end{pmatrix}.
758
+ $$
759
+
760
+ Therefore:
761
+
762
+ $$
763
+ Wa
764
+ =
765
+ 2w_1+0w_2
766
+ =
767
+ \begin{pmatrix}
768
+ 2\\
769
+ 6
770
+ \end{pmatrix}.
771
+ $$
772
+
773
+ Even if the second column held huge numbers, it would contribute nothing on this input because its activation is zero. Looking only for the largest weights is not enough to find important behavior: a neuron's effect depends on both its stored vector and when it activates.
774
+
775
+ ---
776
+
777
+ ## 7. What went wrong with Qwen?
778
+
779
+ Language models generate text autoregressively:
780
+
781
+ 1. Read the current context.
782
+ 2. Predict the next token.
783
+ 3. Append it.
784
+ 4. Repeat.
785
+
786
+ During chain-of-thought reasoning, each forward pass is finite, yet the generated tokens can steer the context into repeating the same material until generation hits the token cap without a final answer.
787
+
788
+ ### Why fixing repetition can improve "math accuracy"
789
+
790
+ Consider an invented illustration, not a decomposition measured in this study:
791
+
792
+ - A model finishes 10% of problems.
793
+ - Among the answers it finishes, 80% are correct.
794
+
795
+ The fraction of problems with a correct finished answer is:
796
+
797
+ $$
798
+ 0.10\cdot0.80=0.08.
799
+ $$
800
+
801
+ If a repair raises completion to 60% while accuracy on finished answers stays 80%:
802
+
803
+ $$
804
+ 0.60\cdot0.80=0.48.
805
+ $$
806
+
807
+ Overall accuracy rises from 8% to 48% without adding mathematical knowledge, because:
808
+
809
+ $$
810
+ P(\text{correct and finished})
811
+ =
812
+ P(\text{finished})
813
+ P(\text{correct}\mid\text{finished}).
814
+ $$
815
+
816
+ ### The reported results
817
+
818
+ Under the authors' evaluation settings:
819
+
820
+ | Measurement | Original Qwen | Edited Qwen |
821
+ |---|---:|---:|
822
+ | MATH-500 accuracy | 7.9% | **48.1%** |
823
+ | GSM8K accuracy | 13.8% | **67.1%** |
824
+ | IFEval accuracy | 21.0% | **62.3%** |
825
+ | MATH-500 responses reaching the token cap | 93.0% | **33.1%** |
826
+
827
+ MATH-500 and GSM8K test mathematical problem solving; IFEval tests instruction compliance. The gain on IFEval fits a repair that reduces a shared generation failure across tasks. [Hugging Face](https://huggingface.co/RikiyaT/Qwen3.5-0.8B-weight-space-programming)
828
+
829
+ Reported scores average correctness across sampled responses (four responses per MATH-500 problem), rather than counting a problem solved if any of the four attempts succeeds. Evaluation used thinking mode, a 32,768-token limit, temperature $0.6$, top-p $0.95$, top-k $20$, and presence penalty $1.5$. [raw.githubusercontent.com](https://raw.githubusercontent.com/RikiyaT/lm_understand-logs/main/exp8_math/README.md)
830
+
831
+ ### Exercise: distinguish two evaluation methods
832
+
833
+ A model answers one problem four times:
834
+
835
+ $$
836
+ \text{correct},\ \text{wrong},\ \text{wrong},\ \text{correct}.
837
+ $$
838
+
839
+ What is its average sample accuracy on that problem? What is its "at least one success among four" result?
840
+
841
+ ### Check after trying
842
+
843
+ Average sample accuracy:
844
+
845
+ $$
846
+ \frac24=50\%.
847
+ $$
848
+
849
+ At least one success:
850
+
851
+ $$
852
+ 100\%.
853
+ $$
854
+
855
+ Confusing these two metrics can make a model look much stronger than the experiment shows.
856
+
857
+ ---
858
+
859
+ ## 8. The linear algebra behind the repair
860
+
861
+ Changing a vector's contribution along one direction uses three operations: dot product, projection, and scaled subtraction.
862
+
863
+ ### 8.1 A direction is a pattern across coordinates
864
+
865
+ Suppose:
866
+
867
+ $$
868
+ u=
869
+ \begin{pmatrix}
870
+ 1\\
871
+ 0
872
+ \end{pmatrix}.
873
+ $$
874
+
875
+ This points along the first coordinate.
876
+
877
+ Let:
878
+
879
+ $$
880
+ w=
881
+ \begin{pmatrix}
882
+ 1\\
883
+ 3
884
+ \end{pmatrix}.
885
+ $$
886
+
887
+ It contains:
888
+
889
+ - $1$ unit along $u$.
890
+ - $3$ units perpendicular to $u$.
891
+
892
+ In a real model, a direction typically spans many coordinates and need not align with a single neuron or coordinate axis.
893
+
894
+ ### 8.2 The dot product measures the component
895
+
896
+ For a unit-length vector $u$, the scalar
897
+
898
+ $$
899
+ u^Tw
900
+ $$
901
+
902
+ gives the signed component of $w$ along $u$.
903
+
904
+ Here:
905
+
906
+ $$
907
+ u^Tw
908
+ =
909
+ \begin{pmatrix}
910
+ 1&0
911
+ \end{pmatrix}
912
+ \begin{pmatrix}
913
+ 1\\
914
+ 3
915
+ \end{pmatrix}
916
+ =
917
+ 1.
918
+ $$
919
+
920
+ Multiply that scalar by $u$:
921
+
922
+ $$
923
+ u(u^Tw)
924
+ =
925
+ \begin{pmatrix}
926
+ 1\\
927
+ 0
928
+ \end{pmatrix}.
929
+ $$
930
+
931
+ This is the vector component along $u$.
932
+
933
+ ### 8.3 Remove that component
934
+
935
+ Subtract it:
936
+
937
+ $$
938
+ w'
939
+ =
940
+ w-u(u^Tw).
941
+ $$
942
+
943
+ Therefore:
944
+
945
+ $$
946
+ w'
947
+ =
948
+ \begin{pmatrix}
949
+ 1\\
950
+ 3
951
+ \end{pmatrix}
952
+ -
953
+ \begin{pmatrix}
954
+ 1\\
955
+ 0
956
+ \end{pmatrix}
957
+ =
958
+ \begin{pmatrix}
959
+ 0\\
960
+ 3
961
+ \end{pmatrix}.
962
+ $$
963
+
964
+ The component along $u$ is now zero while the perpendicular component remains. Geometrically, this is orthogonal projection onto the space perpendicular to $u$.
965
+
966
+ ### 8.4 What if the direction is not unit length?
967
+
968
+ For any nonzero direction $d$, the denominator accounts for its length:
969
+
970
+ $$
971
+ \operatorname{proj}_d(w)
972
+ =
973
+ d\frac{d^Tw}{d^Td}.
974
+ $$
975
+
976
+ Or normalize $d$ to unit length first, where $\|d\|$ denotes its length:
977
+
978
+ $$
979
+ u=\frac{d}{\|d\|}.
980
+ $$
981
+
982
+ For:
983
+
984
+ $$
985
+ d=
986
+ \begin{pmatrix}
987
+ 3\\
988
+ 4
989
+ \end{pmatrix},
990
+ $$
991
+
992
+ we have:
993
+
994
+ $$
995
+ \|d\|=\sqrt{3^2+4^2}=5,
996
+ \qquad
997
+ u=
998
+ \begin{pmatrix}
999
+ 3/5\\
1000
+ 4/5
1001
+ \end{pmatrix}.
1002
+ $$
1003
+
1004
+ ### Exercise: remove a diagonal component
1005
+
1006
+ Let:
1007
+
1008
+ $$
1009
+ d=
1010
+ \begin{pmatrix}
1011
+ 1\\
1012
+ 1
1013
+ \end{pmatrix},
1014
+ \qquad
1015
+ w=
1016
+ \begin{pmatrix}
1017
+ 3\\
1018
+ 1
1019
+ \end{pmatrix}.
1020
+ $$
1021
+
1022
+ Find the part of $w$ along $d$, then subtract it.
1023
+
1024
+ *Hint: compute $d^Tw$ and $d^Td$ separately.*
1025
+
1026
+ ### Check after trying
1027
+
1028
+ $$
1029
+ d^Tw=3+1=4,
1030
+ $$
1031
+
1032
+ $$
1033
+ d^Td=1+1=2.
1034
+ $$
1035
+
1036
+ Thus:
1037
+
1038
+ $$
1039
+ \operatorname{proj}_d(w)
1040
+ =
1041
+ \begin{pmatrix}
1042
+ 1\\
1043
+ 1
1044
+ \end{pmatrix}
1045
+ \frac42
1046
+ =
1047
+ \begin{pmatrix}
1048
+ 2\\
1049
+ 2
1050
+ \end{pmatrix}.
1051
+ $$
1052
+
1053
+ Subtract:
1054
+
1055
+ $$
1056
+ w'
1057
+ =
1058
+ \begin{pmatrix}
1059
+ 3\\
1060
+ 1
1061
+ \end{pmatrix}
1062
+ -
1063
+ \begin{pmatrix}
1064
+ 2\\
1065
+ 2
1066
+ \end{pmatrix}
1067
+ =
1068
+ \begin{pmatrix}
1069
+ 1\\
1070
+ -1
1071
+ \end{pmatrix}.
1072
+ $$
1073
+
1074
+ Check perpendicularity:
1075
+
1076
+ $$
1077
+ d^Tw'=1-1=0.
1078
+ $$
1079
+
1080
+ Omitting the denominator and subtracting $(4,4)^T$ would overcorrect because $d$ is not normalized.
1081
+
1082
+ ### 8.5 Generalize from removal to counteraction
1083
+
1084
+ Introduce an edit strength $\alpha$, pronounced "alpha":
1085
+
1086
+ $$
1087
+ \boxed{
1088
+ w'=w-\alpha u(u^Tw).
1089
+ }
1090
+ $$
1091
+
1092
+ For unit $u$:
1093
+
1094
+ $$
1095
+ u^Tw'=(1-\alpha)u^Tw.
1096
+ $$
1097
+
1098
+ Therefore:
1099
+
1100
+ | Strength | Effect on the component along $u$ |
1101
+ |---|---|
1102
+ | $\alpha=0$ | Leave it unchanged |
1103
+ | $\alpha=0.5$ | Halve it |
1104
+ | $\alpha=1$ | Remove it |
1105
+ | $\alpha=2$ | Reverse it with equal magnitude |
1106
+ | $\alpha=6$ | Reverse it with five times the original magnitude |
1107
+
1108
+ On the toy vector $w=(1,3)^T$ with the horizontal direction selected, raising the strength from $0$ to $1$ and then to $6$ changes only the horizontal component. The Qwen patch used strength $6$, reversing the selected component more strongly than projection removal.
1109
+
1110
+ ---
1111
+
1112
+ ## 9. What the successful Qwen edit actually did
1113
+
1114
+ ### Step 1: compare productive and repetitive contexts
1115
+
1116
+ The editor built four matched pairs of math traces where one continuation made progress and the other repeated. It ran these fixed traces through the original model to record internal activations (teacher forcing, where the model reads a supplied sequence rather than sampling each next token).
1117
+
1118
+ At each relevant layer, it averaged the representations of the last 36 tokens of each trace and took a difference:
1119
+
1120
+ $$
1121
+ d
1122
+ =
1123
+ \text{average representation during repetition}
1124
+ -
1125
+ \text{average representation during progress}.
1126
+ $$
1127
+
1128
+ Then it normalized that difference:
1129
+
1130
+ $$
1131
+ u=\frac{d}{\|d\|}.
1132
+ $$
1133
+
1134
+ Each edited layer used its own direction vector rather than sharing one vector across the model. [Source: executed experiment code](https://raw.githubusercontent.com/RikiyaT/lm_understand-logs/main/exp8_math/runs/p50c/tool_calls.jsonl)
1135
+
1136
+ **Interpretation:** $u$ is an activation pattern correlated with repetition. Correlation alone does not prove causation; weight interventions test whether changing it alters generation.
1137
+
1138
+ ### Step 2: identify which features write that pattern
1139
+
1140
+ Recall:
1141
+
1142
+ $$
1143
+ Wa=\sum_j a_jw_j.
1144
+ $$
1145
+
1146
+ For feature $j$, two things matter:
1147
+
1148
+ 1. Does it activate more strongly during repetition?
1149
+ 2. Does its output vector point along the repetition direction?
1150
+
1151
+ The selection score multiplied both terms:
1152
+
1153
+ $$
1154
+ s_j
1155
+ =
1156
+ \underbrace{u^Tw_j}_{\text{writing along the direction}}
1157
+ \underbrace{
1158
+ \left(
1159
+ \overline{a_j^{\text{repeat}}}
1160
+ -
1161
+ \overline{a_j^{\text{productive}}}
1162
+ \right)
1163
+ }_{\text{extra activation during repetition}}.
1164
+ $$
1165
+
1166
+ The code selected the 64 highest-scoring neurons in each of two layers using the activation averages from the matched traces. [Source: neuron-selection code](https://raw.githubusercontent.com/RikiyaT/lm_understand-logs/main/exp8_math/runs/p50c/tool_calls.jsonl)
1167
+
1168
+ ### Exercise: choose the stronger candidate
1169
+
1170
+ Suppose:
1171
+
1172
+ | Neuron | Writing component $u^Tw_j$ | Extra activation during repetition |
1173
+ |---|---:|---:|
1174
+ | A | 3 | 2 |
1175
+ | B | 100 | 0 |
1176
+ | C | $-2$ | 4 |
1177
+
1178
+ Which receives the largest score?
1179
+
1180
+ ### Check after trying
1181
+
1182
+ $$
1183
+ s_A=3\cdot2=6,
1184
+ $$
1185
+
1186
+ $$
1187
+ s_B=100\cdot0=0,
1188
+ $$
1189
+
1190
+ $$
1191
+ s_C=(-2)\cdot4=-8.
1192
+ $$
1193
+
1194
+ Neuron A gets the highest score. Neuron B aligns with the direction but shows no extra activation during repetition, and Neuron C pushes in the opposite direction.
1195
+
1196
+ ### Step 3: edit selected output columns
1197
+
1198
+ The final patch changed 64 columns in each of:
1199
+
1200
+ ```text
1201
+ layers.15.mlp.down_proj.weight
1202
+ layers.19.mlp.down_proj.weight
1203
+ ```
1204
+
1205
+ For each selected column:
1206
+
1207
+ $$
1208
+ \boxed{
1209
+ w_j'=w_j-6u(u^Tw_j).
1210
+ }
1211
+ $$
1212
+
1213
+ The successful run's [final report](https://raw.githubusercontent.com/RikiyaT/lm_understand-logs/main/exp8_math/runs/p50c/work_notes/final_report.md) specifies that strength and those two matrices. Each column has 1,024 entries, so the edit touches:
1214
+
1215
+ $$
1216
+ 2\cdot64\cdot1024=131{,}072.
1217
+ $$
1218
+
1219
+ Out of roughly 800 million parameters, that is:
1220
+
1221
+ $$
1222
+ \frac{131{,}072}{800{,}000{,}000}\times100
1223
+ \approx0.0164\%.
1224
+ $$
1225
+
1226
+ That is the origin of the headline percentage. [Hugging Face](https://huggingface.co/RikiyaT/Qwen3.5-0.8B-weight-space-programming)
1227
+
1228
+ ### Step 4: understand the matrix operation
1229
+
1230
+ Let $m$ be a mask: $1$ for a selected column, $0$ otherwise.
1231
+
1232
+ The implementation has the form:
1233
+
1234
+ ```python
1235
+ projection = u @ W
1236
+ correction = np.outer(u, mask * projection)
1237
+ W_edited = W - 6 * correction
1238
+ ```
1239
+
1240
+ An outer product multiplies a column vector by a row vector to build a matrix:
1241
+
1242
+ $$
1243
+ \begin{pmatrix}
1244
+ 1\\
1245
+ 2
1246
+ \end{pmatrix}
1247
+ \begin{pmatrix}
1248
+ 3&4
1249
+ \end{pmatrix}
1250
+ =
1251
+ \begin{pmatrix}
1252
+ 3&4\\
1253
+ 6&8
1254
+ \end{pmatrix}.
1255
+ $$
1256
+
1257
+ Every column of this matrix is a multiple of $(1,2)^T$. That makes the correction matrix rank at most one before rounding: it modifies only one output direction per edited matrix. Only the correction has this structure, not the entire model.
1258
+
1259
+ ### A subtle but essential distinction
1260
+
1261
+ The edit does not subtract a fixed constant vector from every token. For an intermediate activation vector $a$:
1262
+
1263
+ $$
1264
+ W'a-Wa
1265
+ =
1266
+ -6u
1267
+ \sum_{j\in S}a_j(u^Tw_j),
1268
+ $$
1269
+
1270
+ where $S$ is the selected neuron set. The push scales with how strongly those selected neurons fire on the current token. Preserving each selected column's perpendicular component in the edited matrix also does not freeze perpendicular activations in later layers, because downstream nonlinearities and future tokens respond to the updated stream.
1271
+
1272
+ ---
1273
+
1274
+ ## 10. Why believe this repaired repetition?
1275
+
1276
+ To check whether the edit worked by cutting repetition, the authors tested several controls:
1277
+
1278
+ - Sweeping edit strengths.
1279
+ - Editing each layer on its own.
1280
+ - Comparing against a random direction with matched edit norm.
1281
+ - Evaluating on held-out practice problems after freezing the edit.
1282
+
1283
+ On a 1,152-problem held-out confirmation set, 579 problems flipped from wrong to right, and 576 of them had originally been truncated at the token cap. Aggregate accuracy on outputs that already finished before the edit did not change. [Source: successful run's final assessment](https://raw.githubusercontent.com/RikiyaT/lm_understand-logs/main/exp8_math/runs/p50c/final_answer.md)
1284
+
1285
+ That strongly supports the interpretation that the intervention rescued many answers by reducing a generation failure. It still does not prove that every form of repetition shares one mechanism, or that this direction represents "looping" in every context.
1286
+
1287
+ ### The unsuccessful follow-up is informative
1288
+
1289
+ The authors also tried Gemma 3 270M, which rarely hit its token cap. Its MATH-500 score was 6.8% before editing and 7.0 to 7.7% across editing runs, showing no clear gain. [raw.githubusercontent.com](https://raw.githubusercontent.com/RikiyaT/lm_understand-logs/main/exp8_math/gemma_followup/README.md)
1290
+
1291
+ That contrast separates two failure modes:
1292
+
1293
+ | Failure type | What may be needed |
1294
+ |---|---|
1295
+ | The model often knows enough but gets trapped in repetitive generation | A targeted execution repair may help |
1296
+ | The model finishes but makes mathematical mistakes | It may need additional learning or a different intervention |
1297
+
1298
+ The Gemma follow-up does not show that direct editing can never add capabilities, only that these experiments did not demonstrate it.
1299
+
1300
+ ---
1301
+
1302
+ ## 11. What the other experiments demonstrate
1303
+
1304
+ Each experiment gave the editor different inputs and tools. Keep their conditions separate to avoid overgeneralizing:
1305
+
1306
+ | Experiment | What the editor could inspect or use | Reported outcome and its scope |
1307
+ |---|---|---|
1308
+ | **1. Identify a game** | Small policy weights, architecture, input/action descriptions; no tools | Correct game identification for four selected networks. [rikiyat.github.io](https://rikiyat.github.io/blog/weight-space-programming/) |
1309
+ | **2. Identify generated shapes** | Small diffusion-network weights; no target forward passes | Correct coarse shape categories, but it described outlines when the models generated filled shapes. [Experiment 2](https://github.com/RikiyaT/lm_understand-logs/blob/main/exp2_diffusion/README.md) |
1310
+ | **3. Identify training text** | Middle transformer blocks and Python linear algebra; no target forward passes | Correct choice among Python, romanized Japanese, and English in two attempts per model. [Experiment 3](https://github.com/RikiyaT/lm_understand-logs/blob/main/exp3_language/README.md) |
1311
+ | **4. Repair a car controller** | Weights, scene, rules, baseline failures, one trajectory | A single edit succeeded in 1,024 tested episodes after the original policy failed. [raw.githubusercontent.com](https://raw.githubusercontent.com/RikiyaT/lm_understand-logs/main/exp4_fix_car/README.md) |
1312
+ | **5. Write game policies** | Game rules, source code, network specification; no trial gameplay | Written networks beat the reported 100-million-step PPO baselines in three games. [GitHub](https://github.com/RikiyaT/lm_understand-logs/blob/main/exp5_write_from_scratch/README.md) |
1313
+ | **6. Write a Go policy** | Rules, Python, and varying feedback budgets | Zero-feedback policies were useful; beating the tuned equal-sized PPO baselines required the feedback-assisted run. [Experiment 6](https://github.com/RikiyaT/lm_understand-logs/blob/main/exp6_go_from_scratch/README.md) |
1314
+ | **7. Identify sycophancy** | Initially weights alone; later attention patterns on supplied texts | Weights-only investigations failed to distinguish reliably; attention probes enabled three correct pair classifications. [Experiment 7](https://github.com/RikiyaT/lm_understand-logs/blob/main/exp7_sycophancy/README.md) |
1315
+
1316
+ ### Why the car example is interesting
1317
+
1318
+ To clear a ramp, the car had to back up before accelerating. Where the original policy kept repeating an unsuccessful approach, the editor wired a control pathway through selected units that used position and velocity to create a run-up before handing control back to the trained network. That edit changed about 3.5% of the actor's parameters. The result applies to the tested task distribution, without guaranteeing success in arbitrary driving situations. [raw.githubusercontent.com](https://raw.githubusercontent.com/RikiyaT/lm_understand-logs/main/exp4_fix_car/README.md) This resembles our paddle example at a more sophisticated level: devise a controller, then encode it into a network.
1319
+
1320
+ ### Why the game results need their full comparison
1321
+
1322
+ The reported scores were:
1323
+
1324
+ | Game | PPO: 100M steps | PPO: 500M steps | Directly written network |
1325
+ |---|---:|---:|---:|
1326
+ | Breakout | 15.8 | 25.3 | **517.4** |
1327
+ | Seaquest | 5.6 | **88.9** | 53.1 |
1328
+ | Asterix | 25.8 | **55.2** | 53.2 |
1329
+
1330
+ The written networks beat all three 100M-step baselines, but only on Breakout did they also beat the 500M-step baseline. [GitHub](https://github.com/RikiyaT/lm_understand-logs/blob/main/exp5_write_from_scratch/README.md) Because the editor received the game source code and knew the games, it could design heuristics and compile them into weights.
1331
+
1332
+ ### What is the minimum diffusion background?
1333
+
1334
+ A diffusion generator repeatedly applies a learned denoiser to turn random noise into structured output. In the shape experiment, the editor identified the coarse shape category from the denoiser's weights, yet mistook filled shapes for outlines: spotting a structural signature in weights is weaker than reconstructing a generator's full output behavior.
1335
+
1336
+ ### What is the minimum PPO background?
1337
+
1338
+ A policy maps a state to action probabilities. PPO (proximal policy optimization) is a reinforcement-learning algorithm that updates a policy from interaction rewards while limiting how sharply its action probabilities shift on each step. One environment step is a single interaction with the simulator, so it is not directly comparable to an LLM token, a full game, or a batched evaluation call.
1339
+
1340
+ ### Why the sycophancy result matters
1341
+
1342
+ Sycophancy means following the user's suggested answer even when that suggestion is wrong. Seeing attention shift toward the suggestion shows where a head reads from, not whether that head causes the behavior: a head can attend strongly to a token while writing almost nothing through its value-output transformation. Causal interventions are stronger evidence than attention maps alone.
1343
+
1344
+ ---
1345
+
1346
+ ## 12. The distinctions you need for mastery
1347
+
1348
+ ### A. Gradient-free versus feedback-free
1349
+
1350
+ The Qwen editor inspected practice problems, activations, and candidate outcomes. Its budget of "50 forward passes" meant 50 forward-tool requests, where one request could generate up to 512 multi-token responses or inspect up to 1,024 supplied texts. The winning run used 158 turns and 80 Python executions over 20.3 hours of elapsed time, including 4.7 hours of service unavailability. [raw.githubusercontent.com](https://raw.githubusercontent.com/RikiyaT/lm_understand-logs/main/exp8_math/README.md) This is gradient-free editing, yet it still involved a substantial experimental process.
1351
+
1352
+ ### B. Sparse versus small
1353
+
1354
+ Sparse counts how many coordinates change; small measures how much the transformation or output changes. Changing one branch condition in a program can redirect nearly every run. Likewise, editing a tiny fraction of parameters can produce a large behavioral shift and does not guarantee that all other capabilities stay intact:
1355
+
1356
+ $$
1357
+ \boxed{
1358
+ \text{0.016\% of parameters changed}
1359
+ \not\Rightarrow
1360
+ \text{0.016\% of behavior changed}.
1361
+ }
1362
+ $$
1363
+
1364
+ ### C. Low rank versus LoRA
1365
+
1366
+ Both the Qwen patch and LoRA write an update using low-rank factors:
1367
+
1368
+ $$
1369
+ W'=W+BA.
1370
+ $$
1371
+
1372
+ Ordinary LoRA learns those factors by gradient descent, whereas the Qwen patch constructed its rank-one update directly from activation contrasts. Low rank describes the shape of an update, not how it was found. [arxiv.org](https://arxiv.org/abs/2106.09685)
1373
+
1374
+ ### D. Weight editing versus activation steering
1375
+
1376
+ Activation steering modifies temporary internal activations during an inference run; weight editing modifies the stored matrices that produce future activations. They can be related mathematically, while intervening at different places.
1377
+
1378
+ ### E. Direct construction versus reverse engineering
1379
+
1380
+ Writing our paddle policy was straightforward because we chose the algorithm and coordinate meanings up front. Reading a trained network is harder because its computation can be distributed and redundant across many units. Building a network from scratch does not imply an ability to reverse engineer arbitrary trained networks.
1381
+
1382
+ ### F. Novel combination versus entirely new ingredients
1383
+
1384
+ Each ingredient has prior work behind it:
1385
+
1386
+ - Mechanistic interpretability analyzes internal circuits.
1387
+ - Model editing patches targeted behaviors.
1388
+ - Tracr compiles human-readable programs into transformer weights, and it predates this work. [arxiv.org](https://arxiv.org/abs/2301.05062)
1389
+ - Low-rank updates represent structured parameter deltas.
1390
+
1391
+ The interesting claim here is that a general-purpose LLM can perform several parts of this work itself, without specialized training as a weight generator.
1392
+
1393
+ ---
1394
+
1395
+ ## 13. How to evaluate the claims like a researcher
1396
+
1397
+ ### 13.1 Separate three levels of understanding
1398
+
1399
+ Ask which level of claim the evidence supports:
1400
+
1401
+ 1. **Identification:** "This network probably plays a paddle game."
1402
+ 2. **Prediction:** "On this input, it will choose right."
1403
+ 3. **Causal explanation:** "This pathway computes the position error, and changing it produces the predicted behavioral shift."
1404
+
1405
+ A correct label alone does not establish a causal explanation.
1406
+
1407
+ ### 13.2 Compare against a strong practical baseline
1408
+
1409
+ Original and edited Qwen were tested under matched decoding settings, which supports a comparison under those conditions. However, the official model card recommends temperature $1.0$ for thinking text tasks, whereas the experiment used $0.6$, and notes that this small model can still enter thinking loops under recommended settings. [Hugging Face](https://huggingface.co/Qwen/Qwen3.5-0.8B)
1410
+
1411
+ To judge whether weight editing is the best practical fix, also compare against:
1412
+
1413
+ - Tuned decoding parameters.
1414
+ - Thinking versus non-thinking modes.
1415
+ - Repetition detection or other simple generation controls.
1416
+ - Gradient-based fine-tuning under a similar resource budget.
1417
+
1418
+ This does not invalidate the measured edit effect. It determines whether editing is the best available intervention.
1419
+
1420
+ ### 13.3 Keep development data separate from final evaluation
1421
+
1422
+ A sound workflow is:
1423
+
1424
+ ```mermaid
1425
+ flowchart TD
1426
+ A["Inspect failures and internals"] --> B["Propose structured edit"]
1427
+ B --> C["Test on development cases"]
1428
+ C -->|"Revise"| A
1429
+ C -->|"Select"| D["Freeze the edit"]
1430
+ D --> E["Evaluate on held-out tasks"]
1431
+ ```
1432
+
1433
+ Repeatedly checking the evaluation set while choosing layers, directions, or alpha turns that test set into development data. Final evaluation must run on held-out tasks after the edit is frozen.
1434
+
1435
+ ### 13.4 Measure more than accuracy
1436
+
1437
+ For a repetition repair, track metrics that test the proposed mechanism alongside accuracy:
1438
+
1439
+ - Correct-answer rate and fraction hitting the token cap.
1440
+ - Repetition rate, output length, and latency.
1441
+ - Regressions on problems the unedited model solved.
1442
+ - Performance on unrelated tasks.
1443
+
1444
+ ### 13.5 Count the whole cost
1445
+
1446
+ A full cost comparison includes:
1447
+
1448
+ $$
1449
+ \text{editor inference}
1450
+ +
1451
+ \text{target inference}
1452
+ +
1453
+ \text{analysis}
1454
+ +
1455
+ \text{development attempts}
1456
+ +
1457
+ \text{verification}.
1458
+ $$
1459
+
1460
+ Skipping backpropagation does not automatically mean lower total cost.
1461
+
1462
+ ### 13.6 Understand the current evidence boundary
1463
+
1464
+ The repository notes that earlier prompt trials are not all included in the released logs, making these runs exploratory demonstrations rather than a measure of success rate across models and tasks. [GitHub](https://github.com/RikiyaT/lm_understand-logs)
1465
+
1466
+ Our assessment is that the clearest near-term uses are:
1467
+
1468
+ - Targeted repairs of localized failure modes.
1469
+ - Constructing small controllers from known rules.
1470
+ - Proposing interpretable weight initializations.
1471
+ - Automating parts of mechanistic investigation.
1472
+
1473
+ Installing broad new capabilities, auditing models reliably from weights alone, and replacing gradient-based training remain open research problems.
1474
+
1475
+ ---
1476
+
1477
+ ## 14. Use the demo as an active-learning experiment
1478
+
1479
+ Before opening the demo, solve its example yourself:
1480
+
1481
+ > Positive integers $x,y$ satisfy $x+y=20$ and $xy=91$. Find $x^2+y^2$.
1482
+
1483
+ **Try first:** Can you solve it without finding $x$ and $y$ separately?
1484
+
1485
+ *Hint: expand $(x+y)^2$.*
1486
+
1487
+ ### Check after trying
1488
+
1489
+ $$
1490
+ (x+y)^2=x^2+2xy+y^2.
1491
+ $$
1492
+
1493
+ Rearrange:
1494
+
1495
+ $$
1496
+ x^2+y^2=(x+y)^2-2xy.
1497
+ $$
1498
+
1499
+ Substitute:
1500
+
1501
+ $$
1502
+ x^2+y^2=20^2-2(91)=400-182=\boxed{218}.
1503
+ $$
1504
+
1505
+ Now test both checkpoints in the [live comparison](https://huggingface.co/spaces/ML-Intern-lab/weight-space-programming-demo) and check:
1506
+
1507
+ 1. Does each model reach a final answer?
1508
+ 2. Does it repeat a sentence or reasoning pattern?
1509
+ 3. Is the final answer correct?
1510
+ 4. Does a repeated run behave differently?
1511
+
1512
+ The edited model need not win on every individual prompt. The live app defaults to 4,096 tokens (the benchmark used 32,768) and omits the benchmark's presence penalty of $1.5$; the recorded-runs tab shows full-protocol examples. [Source: demo implementation](https://huggingface.co/spaces/ML-Intern-lab/weight-space-programming-demo/raw/main/app.py)
1513
+
1514
+ The model card also reports an independent full MATH-500 rerun of the edited checkpoint at 51.0%, paired with a 25-problem smoke test on the original model rather than a second full baseline evaluation. [Hugging Face](https://huggingface.co/RikiyaT/Qwen3.5-0.8B-weight-space-programming)
1515
+
1516
+ ### How to read the paper's log repository
1517
+
1518
+ For the Qwen experiment, read in this order:
1519
+
1520
+ | File or folder | What it helps you understand |
1521
+ |---|---|
1522
+ | `exp8_math/README.md` | Rules, tools, evaluation protocol, and run mapping |
1523
+ | `runs/p50c/final_answer.md` | Final choice and the main supporting evidence |
1524
+ | `runs/p50c/work_notes/final_report.md` | Exact intervention and confirmation results |
1525
+ | `runs/p50c/tool_calls.jsonl` | Executed computations behind the method |
1526
+ | `runs/p50c/edit/` | The released parameter difference |
1527
+ | `exp8_math/code/apply_edit.py` | Reconstructing and verifying the edited tensors |
1528
+ | `exp8_math/eval/` | Benchmark scores and individual outcomes |
1529
+
1530
+ The reconstruction script verifies tensor hashes so you can confirm that the rebuilt weights match the released checkpoint. [raw.githubusercontent.com](https://raw.githubusercontent.com/RikiyaT/lm_understand-logs/main/exp8_math/README.md) For understanding, the final report plus the edit script are more useful at first than the full conversation log.
1531
+
1532
+ ---
1533
+
1534
+ ## 15. Transfer exercise: predict collateral effects
1535
+
1536
+ Start with:
1537
+
1538
+ $$
1539
+ W=
1540
+ \begin{pmatrix}
1541
+ 1&0\\
1542
+ 0&1
1543
+ \end{pmatrix}.
1544
+ $$
1545
+
1546
+ This is the identity matrix:
1547
+
1548
+ $$
1549
+ Wx=x.
1550
+ $$
1551
+
1552
+ We want the input
1553
+
1554
+ $$
1555
+ v=
1556
+ \begin{pmatrix}
1557
+ 1\\
1558
+ 1
1559
+ \end{pmatrix}
1560
+ $$
1561
+
1562
+ to produce:
1563
+
1564
+ $$
1565
+ \begin{pmatrix}
1566
+ 3\\
1567
+ 1
1568
+ \end{pmatrix}
1569
+ $$
1570
+
1571
+ instead of $(1,1)^T$.
1572
+
1573
+ One possible edit is:
1574
+
1575
+ $$
1576
+ \Delta W=
1577
+ \begin{pmatrix}
1578
+ 1&1\\
1579
+ 0&0
1580
+ \end{pmatrix}.
1581
+ $$
1582
+
1583
+ Then:
1584
+
1585
+ $$
1586
+ W'=W+\Delta W
1587
+ =
1588
+ \begin{pmatrix}
1589
+ 2&1\\
1590
+ 0&1
1591
+ \end{pmatrix}.
1592
+ $$
1593
+
1594
+ Indeed:
1595
+
1596
+ $$
1597
+ W'v=
1598
+ \begin{pmatrix}
1599
+ 3\\
1600
+ 1
1601
+ \end{pmatrix}.
1602
+ $$
1603
+
1604
+ **Try first:** Does the edit also change the outputs for:
1605
+
1606
+ $$
1607
+ x_A=
1608
+ \begin{pmatrix}
1609
+ 1\\
1610
+ -1
1611
+ \end{pmatrix},
1612
+ \qquad
1613
+ x_B=
1614
+ \begin{pmatrix}
1615
+ 1\\
1616
+ 0
1617
+ \end{pmatrix}?
1618
+ $$
1619
+
1620
+ ### Check after trying
1621
+
1622
+ For $x_A$:
1623
+
1624
+ $$
1625
+ \Delta Wx_A
1626
+ =
1627
+ \begin{pmatrix}
1628
+ 1&1\\
1629
+ 0&0
1630
+ \end{pmatrix}
1631
+ \begin{pmatrix}
1632
+ 1\\
1633
+ -1
1634
+ \end{pmatrix}
1635
+ =
1636
+ \begin{pmatrix}
1637
+ 0\\
1638
+ 0
1639
+ \end{pmatrix}.
1640
+ $$
1641
+
1642
+ Its output is unchanged.
1643
+
1644
+ For $x_B$:
1645
+
1646
+ $$
1647
+ \Delta Wx_B
1648
+ =
1649
+ \begin{pmatrix}
1650
+ 1\\
1651
+ 0
1652
+ \end{pmatrix}.
1653
+ $$
1654
+
1655
+ Its output changes.
1656
+
1657
+ An edit designed for one target behavior also changes other inputs. That shared reach is the source of both useful generalization and possible collateral effects, and you need evaluation to determine which one you obtained.
1658
+
1659
+ ---
1660
+
1661
+ # Compact mastery check
1662
+
1663
+ Test yourself on these questions before reading the answers.
1664
+
1665
+ ## 1. Recall: explain weight-space programming to a beginner
1666
+
1667
+ What does the editor do, and what does "no training" mean in this study?
1668
+
1669
+ ### Check after trying
1670
+
1671
+ > A neural network computes a function defined by its architecture and stored numerical weights. Weight-space programming asks a general-purpose LLM to inspect those weights and directly construct targeted updates, without running gradient-based training on the target model. Several experiments still used practice inputs, internal activation probes, and iterative testing, and both the editor and any pretrained targets were trained beforehand.
1672
+
1673
+ Your answer should separate the editor from the target and distinguish "no gradients" from "no feedback."
1674
+
1675
+ ## 2. Intuition: why could a math repair improve instruction following?
1676
+
1677
+ ### Check after trying
1678
+
1679
+ Repetitive looping can cut off generation on any task before the model emits its final answer. Suppressing that shared generation failure lets instruction-following responses finish too, without teaching each instruction separately. This is a mechanism-based prediction: improvement should transfer to other tasks affected by the same failure.
1680
+
1681
+ ## 3. Calculation: compute the directional edit
1682
+
1683
+ Let:
1684
+
1685
+ $$
1686
+ u=
1687
+ \begin{pmatrix}
1688
+ 1\\
1689
+ 0
1690
+ \end{pmatrix},
1691
+ \qquad
1692
+ w=
1693
+ \begin{pmatrix}
1694
+ 2\\
1695
+ 5
1696
+ \end{pmatrix}.
1697
+ $$
1698
+
1699
+ Calculate:
1700
+
1701
+ $$
1702
+ w'=w-\alpha u(u^Tw)
1703
+ $$
1704
+
1705
+ for $\alpha=1$ and $\alpha=6$.
1706
+
1707
+ ### Check after trying
1708
+
1709
+ First:
1710
+
1711
+ $$
1712
+ u^Tw=2.
1713
+ $$
1714
+
1715
+ For $\alpha=1$:
1716
+
1717
+ $$
1718
+ w'=
1719
+ \begin{pmatrix}
1720
+ 2\\
1721
+ 5
1722
+ \end{pmatrix}
1723
+ -
1724
+ \begin{pmatrix}
1725
+ 2\\
1726
+ 0
1727
+ \end{pmatrix}
1728
+ =
1729
+ \boxed{
1730
+ \begin{pmatrix}
1731
+ 0\\
1732
+ 5
1733
+ \end{pmatrix}
1734
+ }.
1735
+ $$
1736
+
1737
+ For $\alpha=6$:
1738
+
1739
+ $$
1740
+ w'=
1741
+ \begin{pmatrix}
1742
+ 2\\
1743
+ 5
1744
+ \end{pmatrix}
1745
+ -
1746
+ 6
1747
+ \begin{pmatrix}
1748
+ 2\\
1749
+ 0
1750
+ \end{pmatrix}
1751
+ =
1752
+ \boxed{
1753
+ \begin{pmatrix}
1754
+ -10\\
1755
+ 5
1756
+ \end{pmatrix}
1757
+ }.
1758
+ $$
1759
+
1760
+ The original component $2$ becomes $-10$, which is $-5$ times the original. The perpendicular component remains $5$.
1761
+
1762
+ ## 4. Discrimination: which claim is justified?
1763
+
1764
+ Someone says:
1765
+
1766
+ > "Only 0.016% of the weights changed, so all other capabilities must be preserved."
1767
+
1768
+ What is wrong with the argument?
1769
+
1770
+ ### Check after trying
1771
+
1772
+ The percentage counts how many parameter coordinates changed, not how many behaviors depend on them. A small set of edited columns can sit on a pathway used across many tasks, so capability preservation must be tested on benchmarks rather than inferred from parameter count. A small edit count is useful evidence that the intervention is localized, but it is not a preservation guarantee.
1773
+
1774
+ ## 5. Transfer: design a convincing experiment
1775
+
1776
+ You have a small model that often loops during tool-use planning. You propose editing a repetition-associated direction. What would make the result convincing?
1777
+
1778
+ ### Check after trying
1779
+
1780
+ 1. Measure the baseline failure under fixed decoding settings.
1781
+ 2. Use a development split to find candidate directions and choose the edit.
1782
+ 3. Check whether the proposed mechanism predicts the change in repetition rate.
1783
+ 4. Compare against controls, including a norm-matched random direction and simple decoding adjustments.
1784
+ 5. Freeze the edit before evaluating on held-out tasks.
1785
+ 6. Report task completion, accuracy, token usage, and regressions on previously solved cases.
1786
+ 7. Evaluate unrelated tasks for collateral side effects.
1787
+ 8. Include both editor and target compute when comparing total cost.
1788
+
1789
+ The explanation you should now be able to defend is:
1790
+
1791
+ > The promising result is that a frontier LLM can sometimes inspect a network's weights and activations, isolate a structured failure mode, construct a targeted matrix edit, and validate it. In the Qwen experiment, a rank-one update to two MLP matrices substantially reduced repetitive looping under the tested decoding protocol. Whether direct weight editing can install broad new capabilities or replace gradient training remains an open question.
code/results/edit_info.json ADDED
@@ -0,0 +1,2223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "layers": {
3
+ "15": {
4
+ "u": [
5
+ -0.24895589,
6
+ 0.02042487,
7
+ 0.04845341,
8
+ 0.04011886,
9
+ 0.01447522,
10
+ -0.22025995,
11
+ -0.03074205,
12
+ -0.13840941,
13
+ -0.06866153,
14
+ 0.09642404,
15
+ 0.0353046,
16
+ 0.0226175,
17
+ -0.0256597,
18
+ 0.14359753,
19
+ 0.02338508,
20
+ 0.1498878,
21
+ 0.08304029,
22
+ 0.08268246,
23
+ -0.19874601,
24
+ -0.09147045,
25
+ 0.07413888,
26
+ 0.06162012,
27
+ 0.10411097,
28
+ 0.10747174,
29
+ -0.03906542,
30
+ -0.00092564,
31
+ 0.05868161,
32
+ 0.03840519,
33
+ -0.01002132,
34
+ -0.01528517,
35
+ 0.02351971,
36
+ -0.10380503,
37
+ 0.04659568,
38
+ -0.05271685,
39
+ -0.11021212,
40
+ -0.02139628,
41
+ 0.05885201,
42
+ 0.27632716,
43
+ -0.07798066,
44
+ -0.00321422,
45
+ -0.0532624,
46
+ 0.02187534,
47
+ -0.10555955,
48
+ 0.03803927,
49
+ -0.02107798,
50
+ -0.01953006,
51
+ 0.11633734,
52
+ -0.02078464,
53
+ -0.01075826,
54
+ -0.03035684,
55
+ -0.0609631,
56
+ 0.04406511,
57
+ -0.03191375,
58
+ 0.01976214,
59
+ -0.0230045,
60
+ 0.10779605,
61
+ 0.02044537,
62
+ 0.03443716,
63
+ -0.00222996,
64
+ 0.04737126,
65
+ 0.02329854,
66
+ -0.06319241,
67
+ -0.03822986,
68
+ 0.02501477,
69
+ 0.03194892,
70
+ -0.01586943,
71
+ 0.11077951,
72
+ -0.01513172,
73
+ 0.01495523,
74
+ -0.02717557,
75
+ 0.00209524,
76
+ 0.02339578,
77
+ -0.05470583,
78
+ -0.01732864,
79
+ -0.13191094,
80
+ 0.00083009,
81
+ -0.03853427,
82
+ 0.00820126,
83
+ -0.08445728,
84
+ -0.01159446,
85
+ 0.04487658,
86
+ 0.09761336,
87
+ 0.01805925,
88
+ -0.0599457,
89
+ -0.00016578,
90
+ 0.12068176,
91
+ 0.07060459,
92
+ -0.01015618,
93
+ 0.00422971,
94
+ 0.00676035,
95
+ -0.03283456,
96
+ -0.04117064,
97
+ -0.03248039,
98
+ 0.01022426,
99
+ 0.04163227,
100
+ 0.02309724,
101
+ -0.06292485,
102
+ 0.04230608,
103
+ -0.03557596,
104
+ -0.00750133,
105
+ -0.01376741,
106
+ -0.0576286,
107
+ -0.01923029,
108
+ 0.01580647,
109
+ -0.03461713,
110
+ -0.02074857,
111
+ -0.00220167,
112
+ 0.02275244,
113
+ -0.05322073,
114
+ -0.02142559,
115
+ 0.00997804,
116
+ 0.06086341,
117
+ 0.01686513,
118
+ -0.01301738,
119
+ -0.0158516,
120
+ 0.08372771,
121
+ -2.635e-05,
122
+ -0.03974802,
123
+ -0.08011906,
124
+ 0.05203765,
125
+ 0.02601849,
126
+ -0.00085876,
127
+ -0.01701879,
128
+ -0.01993006,
129
+ -0.03135775,
130
+ 0.02044019,
131
+ -0.00596576,
132
+ -0.04512589,
133
+ 0.02383405,
134
+ 0.0689159,
135
+ 0.00962629,
136
+ -0.02303838,
137
+ 0.02315754,
138
+ 0.01817906,
139
+ 0.00915363,
140
+ -0.02060907,
141
+ -0.00802063,
142
+ -0.06477001,
143
+ 0.0903992,
144
+ 0.00209243,
145
+ 0.02819165,
146
+ 0.04408683,
147
+ -0.00083157,
148
+ 0.00915311,
149
+ -0.02369876,
150
+ 0.00748562,
151
+ 0.0616214,
152
+ 0.02288214,
153
+ -0.01104823,
154
+ -0.01828848,
155
+ 0.00216472,
156
+ 0.00094484,
157
+ 0.01034287,
158
+ 0.01963162,
159
+ -0.02591415,
160
+ 0.0176215,
161
+ -0.04005237,
162
+ 0.03766043,
163
+ -0.00072997,
164
+ 0.008385,
165
+ 0.04705599,
166
+ -0.02323771,
167
+ 0.03808962,
168
+ 0.01465922,
169
+ -0.02919623,
170
+ 0.01405872,
171
+ 0.09836871,
172
+ 0.03246114,
173
+ -0.03860031,
174
+ 0.02028195,
175
+ -0.03454779,
176
+ -0.04695908,
177
+ -0.01388437,
178
+ 0.01351815,
179
+ -0.00994505,
180
+ -0.00924062,
181
+ 0.01713493,
182
+ -0.02566713,
183
+ 0.01801864,
184
+ -0.00727941,
185
+ -0.03246095,
186
+ -0.00945351,
187
+ 0.02356717,
188
+ -0.01617906,
189
+ 0.0002673,
190
+ 0.0152512,
191
+ -0.05930668,
192
+ -0.02066182,
193
+ 0.03876992,
194
+ -0.01206058,
195
+ -0.0229992,
196
+ -0.01304931,
197
+ 0.03253681,
198
+ 0.01845163,
199
+ -0.02612325,
200
+ 0.03437841,
201
+ 0.00318938,
202
+ -0.02345755,
203
+ 0.0103406,
204
+ -0.0547941,
205
+ -0.01196721,
206
+ 0.03946429,
207
+ -0.01932701,
208
+ -0.02480044,
209
+ -0.03023697,
210
+ 0.01206645,
211
+ 0.01655773,
212
+ -0.03254522,
213
+ -0.06123636,
214
+ -9.718e-05,
215
+ 0.04361208,
216
+ 0.01765037,
217
+ 0.00690927,
218
+ -0.01908739,
219
+ 0.00710973,
220
+ -0.01635341,
221
+ -0.01807397,
222
+ 0.03195731,
223
+ -0.00062558,
224
+ -0.07525031,
225
+ -0.0073614,
226
+ 0.02113813,
227
+ 0.01171023,
228
+ -0.02401062,
229
+ 0.00238248,
230
+ -0.07541478,
231
+ 0.01359196,
232
+ 0.00859964,
233
+ -0.03433002,
234
+ -0.0139362,
235
+ -0.0203502,
236
+ 0.04897985,
237
+ 0.0189726,
238
+ 0.03786045,
239
+ 0.00315449,
240
+ 0.02159249,
241
+ 0.00791335,
242
+ -0.0041802,
243
+ -0.04222211,
244
+ -0.00197757,
245
+ -0.01045728,
246
+ 0.01888887,
247
+ 0.00323757,
248
+ 0.0024898,
249
+ 0.01738279,
250
+ 0.01731246,
251
+ -0.00074183,
252
+ -0.04513367,
253
+ 0.01699476,
254
+ 0.00029007,
255
+ 0.0313411,
256
+ -0.00078065,
257
+ -0.04166787,
258
+ 0.00046865,
259
+ 0.01072607,
260
+ 0.04978328,
261
+ -0.00921632,
262
+ -0.0318539,
263
+ -0.01703876,
264
+ -0.00962165,
265
+ 0.0084379,
266
+ 0.01526213,
267
+ -0.00175763,
268
+ -0.00346911,
269
+ -0.01330116,
270
+ 0.0110462,
271
+ 0.00858184,
272
+ 0.00793702,
273
+ -0.02276208,
274
+ 0.00776517,
275
+ 0.01489741,
276
+ -0.03487925,
277
+ -0.00485283,
278
+ -0.01072808,
279
+ 0.01804194,
280
+ 0.00069952,
281
+ -0.0350399,
282
+ -0.00891272,
283
+ -0.05034607,
284
+ -0.02013264,
285
+ 0.02382344,
286
+ 0.00452687,
287
+ -0.03417827,
288
+ 0.01371371,
289
+ 0.01013853,
290
+ 0.02402873,
291
+ 0.00687579,
292
+ -0.00293787,
293
+ -0.04200112,
294
+ 0.0155979,
295
+ 0.02103346,
296
+ 0.00102457,
297
+ -0.01553039,
298
+ 0.0048782,
299
+ 0.01342523,
300
+ -0.00700563,
301
+ -0.0008517,
302
+ -0.00815003,
303
+ 0.022058,
304
+ -0.01499639,
305
+ 0.08941843,
306
+ 0.00295805,
307
+ 0.01554664,
308
+ 0.04637756,
309
+ -0.01786274,
310
+ 0.00893744,
311
+ 0.00528128,
312
+ 0.01970699,
313
+ 0.03162822,
314
+ 0.01654248,
315
+ -0.00447435,
316
+ 0.02589581,
317
+ 0.0091261,
318
+ 0.02967603,
319
+ -0.05372572,
320
+ 0.0408803,
321
+ -0.06336509,
322
+ -0.06271089,
323
+ -0.06071565,
324
+ 0.0155491,
325
+ 0.00989458,
326
+ 0.00055541,
327
+ 0.06561002,
328
+ 0.02666396,
329
+ -0.00034945,
330
+ -0.00383269,
331
+ 0.06482472,
332
+ -0.01433675,
333
+ 0.0087758,
334
+ 0.0072566,
335
+ -0.01086841,
336
+ -0.01884942,
337
+ 0.00414664,
338
+ 0.03651181,
339
+ 0.00429372,
340
+ 0.00615434,
341
+ 0.02905573,
342
+ 0.00150529,
343
+ -0.01203159,
344
+ -0.00446294,
345
+ -0.00850888,
346
+ 0.02013037,
347
+ -0.01540696,
348
+ 0.00676713,
349
+ -0.03000972,
350
+ 0.02548919,
351
+ 0.0193899,
352
+ -0.00388361,
353
+ -0.0120254,
354
+ 0.03926307,
355
+ -0.01654343,
356
+ -0.0079602,
357
+ -0.03867238,
358
+ -0.00353636,
359
+ -0.06303667,
360
+ -0.01678802,
361
+ 0.00676866,
362
+ -0.02049406,
363
+ 0.01812037,
364
+ -0.01209067,
365
+ 0.01619302,
366
+ -0.00095294,
367
+ 0.0351405,
368
+ -0.01859815,
369
+ -0.00705459,
370
+ 0.00921238,
371
+ 0.00619499,
372
+ -0.01505822,
373
+ 0.00492557,
374
+ -0.02152072,
375
+ -0.00978784,
376
+ -0.01185161,
377
+ -0.01378092,
378
+ 0.01576162,
379
+ 0.00231337,
380
+ 0.00048437,
381
+ -0.00383284,
382
+ 0.01836542,
383
+ 0.00245513,
384
+ -0.00395904,
385
+ -0.02486439,
386
+ -0.00779246,
387
+ -0.03026413,
388
+ 0.00707892,
389
+ -0.00066427,
390
+ 0.02269584,
391
+ 0.00932425,
392
+ 0.00880448,
393
+ -0.01355996,
394
+ -0.0213621,
395
+ -0.02409151,
396
+ 0.00435197,
397
+ -0.00831101,
398
+ -0.00868758,
399
+ -0.00613131,
400
+ 0.0235176,
401
+ 0.00874971,
402
+ -0.00766997,
403
+ -0.00574366,
404
+ 0.00216369,
405
+ -0.02224782,
406
+ 0.03144755,
407
+ 0.02889329,
408
+ 0.02490773,
409
+ -0.02212773,
410
+ -0.03703013,
411
+ -0.004282,
412
+ -0.0073097,
413
+ 0.04372522,
414
+ -0.00034812,
415
+ 0.02630935,
416
+ -0.01894524,
417
+ 0.02313965,
418
+ -0.02358787,
419
+ 0.02153209,
420
+ -0.01355532,
421
+ -0.02822855,
422
+ 0.00959931,
423
+ -0.02350557,
424
+ 0.00530226,
425
+ -0.03639463,
426
+ -0.00146062,
427
+ 0.01056713,
428
+ -0.00041511,
429
+ -0.06991735,
430
+ 0.01254784,
431
+ -0.00036259,
432
+ 0.00870194,
433
+ -0.00938099,
434
+ 0.00348201,
435
+ -0.02274709,
436
+ 0.00077291,
437
+ -0.00150898,
438
+ 0.02153555,
439
+ 0.00189581,
440
+ 0.04270721,
441
+ -0.02096639,
442
+ -0.00875471,
443
+ 0.01324256,
444
+ 0.03579133,
445
+ -0.00585621,
446
+ -0.00067577,
447
+ 0.02695031,
448
+ 0.01527341,
449
+ 0.01530647,
450
+ 0.01833474,
451
+ 0.03444716,
452
+ 0.01472223,
453
+ 0.01547363,
454
+ -0.02332466,
455
+ -0.01141694,
456
+ 0.0147395,
457
+ -0.0099107,
458
+ 0.00653573,
459
+ 0.01798838,
460
+ -0.00588708,
461
+ -0.00307174,
462
+ -0.01062604,
463
+ 0.01664033,
464
+ 0.0183456,
465
+ 0.02847986,
466
+ -0.01004799,
467
+ 0.01069096,
468
+ -0.01371973,
469
+ -0.02210832,
470
+ 0.03123316,
471
+ 0.02425844,
472
+ 0.00473939,
473
+ 0.01202199,
474
+ 0.01749886,
475
+ -0.02504295,
476
+ 0.03103473,
477
+ -0.01667802,
478
+ -0.00476564,
479
+ 0.00896814,
480
+ 0.01514297,
481
+ -0.00828517,
482
+ 0.0351658,
483
+ 0.00495074,
484
+ -0.0195084,
485
+ -0.03341911,
486
+ -0.00168051,
487
+ -0.02753034,
488
+ 0.01903882,
489
+ -0.0075416,
490
+ -0.0049103,
491
+ 0.01581883,
492
+ 0.0321242,
493
+ -0.00483631,
494
+ -0.00991875,
495
+ -0.00546838,
496
+ -0.01638026,
497
+ -0.02765951,
498
+ 0.00011725,
499
+ 0.00141833,
500
+ 0.00129717,
501
+ 0.02686252,
502
+ 0.0125603,
503
+ 0.04140906,
504
+ -0.03283318,
505
+ -0.00511294,
506
+ -0.01852764,
507
+ 0.0173699,
508
+ 0.02363337,
509
+ 0.01911985,
510
+ 0.02551599,
511
+ -0.00080893,
512
+ -0.00387747,
513
+ -0.01336184,
514
+ -0.00042963,
515
+ 0.01777469,
516
+ -0.01526318,
517
+ -0.02027697,
518
+ -0.00069285,
519
+ -0.0191701,
520
+ 0.02119613,
521
+ -0.01990354,
522
+ 0.01281992,
523
+ -0.00026741,
524
+ -0.00895518,
525
+ 0.04394981,
526
+ 0.0006412,
527
+ 0.00957361,
528
+ 0.01212802,
529
+ 0.00837842,
530
+ 0.01858075,
531
+ -0.0227863,
532
+ -0.00424773,
533
+ 0.00491972,
534
+ -0.01974545,
535
+ 0.01062466,
536
+ 0.00607882,
537
+ 0.0155143,
538
+ -0.00043194,
539
+ -0.03623306,
540
+ -0.00583369,
541
+ 0.01177452,
542
+ 0.03419334,
543
+ 0.02956153,
544
+ 0.0052018,
545
+ 0.01841628,
546
+ -0.01298493,
547
+ 0.00708795,
548
+ -0.00819775,
549
+ 0.01528713,
550
+ 0.01948304,
551
+ -0.02305606,
552
+ 0.02498595,
553
+ 0.02640811,
554
+ 0.01192026,
555
+ -0.01046528,
556
+ -0.05339457,
557
+ 0.01436175,
558
+ -0.02520583,
559
+ 0.02034942,
560
+ -0.00458668,
561
+ 0.02091778,
562
+ -0.00933316,
563
+ 0.00395203,
564
+ 0.03259778,
565
+ -0.00922393,
566
+ 0.02764433,
567
+ 0.00442787,
568
+ 0.03087782,
569
+ -0.0116332,
570
+ -0.00695784,
571
+ 0.01182763,
572
+ -0.00538845,
573
+ 0.00021123,
574
+ 0.01527191,
575
+ 0.00304669,
576
+ -0.03362828,
577
+ 0.00619225,
578
+ -0.01377127,
579
+ 0.00983315,
580
+ -0.00093796,
581
+ -0.03062665,
582
+ -0.02714591,
583
+ 0.03252864,
584
+ 0.0116705,
585
+ 0.0046911,
586
+ 0.02099744,
587
+ 0.00110952,
588
+ -0.01673126,
589
+ -0.00478365,
590
+ -0.0029851,
591
+ -0.01008971,
592
+ 0.01333155,
593
+ 0.01805684,
594
+ -0.02784364,
595
+ -0.00718621,
596
+ 0.00725112,
597
+ 0.01517552,
598
+ -0.00338144,
599
+ -0.00584107,
600
+ -0.02987582,
601
+ 0.00158235,
602
+ -0.00127262,
603
+ 0.00509386,
604
+ 0.03241724,
605
+ 0.0053058,
606
+ -0.0192148,
607
+ 0.03831203,
608
+ 0.0062505,
609
+ -0.01712746,
610
+ 0.00881974,
611
+ -0.00132802,
612
+ 0.01592885,
613
+ -0.02010931,
614
+ -0.01624735,
615
+ 0.01509205,
616
+ 0.00294987,
617
+ 0.00105078,
618
+ -0.00484741,
619
+ 0.00536887,
620
+ -0.00379959,
621
+ 0.01152481,
622
+ 0.00751099,
623
+ 0.02359054,
624
+ 0.03136535,
625
+ -0.0063684,
626
+ -0.0170561,
627
+ -0.0185177,
628
+ -0.00116783,
629
+ 0.02391142,
630
+ -0.00098122,
631
+ 0.0231038,
632
+ -0.00058802,
633
+ -0.01934259,
634
+ 0.01925419,
635
+ -0.00160873,
636
+ -0.00343772,
637
+ 0.00204482,
638
+ -0.00612924,
639
+ 0.00664135,
640
+ -0.00410643,
641
+ 0.02248072,
642
+ -0.01219042,
643
+ -0.005031,
644
+ 0.01700245,
645
+ 0.04766306,
646
+ -0.00623854,
647
+ 0.00205189,
648
+ -0.01746178,
649
+ 0.01765306,
650
+ -0.00321955,
651
+ 0.01712283,
652
+ 0.03226326,
653
+ -0.01496384,
654
+ -0.02296406,
655
+ 0.01896325,
656
+ -0.00347675,
657
+ -0.00667849,
658
+ -0.00348551,
659
+ 0.00290251,
660
+ 0.00737752,
661
+ 0.00164614,
662
+ 0.02763323,
663
+ 0.00416791,
664
+ 0.04870329,
665
+ -0.00788404,
666
+ 0.02007534,
667
+ 0.03416662,
668
+ -0.01308093,
669
+ 0.02324471,
670
+ -0.00844425,
671
+ -0.01111874,
672
+ -0.00479347,
673
+ 0.0400974,
674
+ -0.02121527,
675
+ -0.00885901,
676
+ -0.02882532,
677
+ -0.01977198,
678
+ 0.00859949,
679
+ 0.01935566,
680
+ -0.03194553,
681
+ -0.03194808,
682
+ -0.00403009,
683
+ -0.02349184,
684
+ -0.03737707,
685
+ -0.03581585,
686
+ -0.01526899,
687
+ -0.01812003,
688
+ -0.03087662,
689
+ -0.01981069,
690
+ 0.00603969,
691
+ 0.0297529,
692
+ 0.00296423,
693
+ -0.02692392,
694
+ -0.01962237,
695
+ 0.01595629,
696
+ 0.0133781,
697
+ -0.00838766,
698
+ 0.00202173,
699
+ -0.00669928,
700
+ 0.01277655,
701
+ 0.00910131,
702
+ -0.01616032,
703
+ 0.00190905,
704
+ -0.00290908,
705
+ -0.00844915,
706
+ 0.02247931,
707
+ 0.00278589,
708
+ 0.0074408,
709
+ 0.01545245,
710
+ -0.00589412,
711
+ 0.00073301,
712
+ 0.0002124,
713
+ -0.02050213,
714
+ 0.00781624,
715
+ -0.02782231,
716
+ -0.02541454,
717
+ -0.02580804,
718
+ 0.00299428,
719
+ -0.0064333,
720
+ -0.01027469,
721
+ -0.00159922,
722
+ -0.0115659,
723
+ 0.01340889,
724
+ 0.00064927,
725
+ -0.01072332,
726
+ 0.02129249,
727
+ 0.00946642,
728
+ -0.01493452,
729
+ 0.02495065,
730
+ 0.01947329,
731
+ 0.02000644,
732
+ 0.02259371,
733
+ 0.0192214,
734
+ 0.03618272,
735
+ 0.0010455,
736
+ -0.02290383,
737
+ 0.00989413,
738
+ 0.02508756,
739
+ 0.01118085,
740
+ -0.00993386,
741
+ -0.01107015,
742
+ 0.00862738,
743
+ -0.02019879,
744
+ -0.01283852,
745
+ -0.0022925,
746
+ 0.00202893,
747
+ -0.02117793,
748
+ -0.00277756,
749
+ 0.01195571,
750
+ -0.01994682,
751
+ -0.0079788,
752
+ 0.01083683,
753
+ 0.0089177,
754
+ -0.02156102,
755
+ 0.01252366,
756
+ 0.01663527,
757
+ -0.00813946,
758
+ 0.01951447,
759
+ 0.00973816,
760
+ -0.02092359,
761
+ -0.0287288,
762
+ -0.00458775,
763
+ -0.01974544,
764
+ -0.03533125,
765
+ -0.00473243,
766
+ 0.00108676,
767
+ -0.00633175,
768
+ 0.0253578,
769
+ 0.00719606,
770
+ -0.00228621,
771
+ -0.00385442,
772
+ -0.01694687,
773
+ -0.00414391,
774
+ 0.02107211,
775
+ 0.00458527,
776
+ -0.02927144,
777
+ -0.00728856,
778
+ 0.0214509,
779
+ -0.01268997,
780
+ -0.01285534,
781
+ -0.01931156,
782
+ -0.0241033,
783
+ -0.00974043,
784
+ 0.0114543,
785
+ 0.02551264,
786
+ -0.0184916,
787
+ -0.02875029,
788
+ -0.03214873,
789
+ 0.01575397,
790
+ 0.02314444,
791
+ -0.01654227,
792
+ -0.00960274,
793
+ -0.01798794,
794
+ -0.01836254,
795
+ 0.02789288,
796
+ 0.01121621,
797
+ 0.02051398,
798
+ 0.01267839,
799
+ 0.03018006,
800
+ -0.00046684,
801
+ -0.0016793,
802
+ 0.00511237,
803
+ -0.0284223,
804
+ -0.00453048,
805
+ 0.00387164,
806
+ 0.00151989,
807
+ 0.00119731,
808
+ -0.01721905,
809
+ 0.04307334,
810
+ 0.00410935,
811
+ -0.02953692,
812
+ -0.01089871,
813
+ 0.02228069,
814
+ 0.00585313,
815
+ -0.00188712,
816
+ 0.00644261,
817
+ 0.00592251,
818
+ 0.00384603,
819
+ 0.03889028,
820
+ -0.02183412,
821
+ 0.01400458,
822
+ -0.00418775,
823
+ 0.00452603,
824
+ -0.00569817,
825
+ 0.00766993,
826
+ 0.02411799,
827
+ -0.01340204,
828
+ 0.02915989,
829
+ 0.02999854,
830
+ -0.00952892,
831
+ 0.03975626,
832
+ 0.00725433,
833
+ 0.00375535,
834
+ -0.01060731,
835
+ 0.01101369,
836
+ -0.00209802,
837
+ -0.00799715,
838
+ 0.00572794,
839
+ 0.00746325,
840
+ 0.0195495,
841
+ 0.03768567,
842
+ 0.02324522,
843
+ -0.00865699,
844
+ 0.01903305,
845
+ -0.0080013,
846
+ 0.00185655,
847
+ -0.02940235,
848
+ 0.00213212,
849
+ 0.01456472,
850
+ 0.00119804,
851
+ -0.01697334,
852
+ -0.02465601,
853
+ 0.02958135,
854
+ -0.03395347,
855
+ 0.02159254,
856
+ -0.02046661,
857
+ -0.02458588,
858
+ 0.00049309,
859
+ -0.00332874,
860
+ -0.00102587,
861
+ -0.00399199,
862
+ -0.02822484,
863
+ 0.00208327,
864
+ 0.00223905,
865
+ -0.01686649,
866
+ 0.01564872,
867
+ 0.00826747,
868
+ -0.03714826,
869
+ 0.02550041,
870
+ -0.00482738,
871
+ -0.00063024,
872
+ 0.02527108,
873
+ -0.00863101,
874
+ 0.0155461,
875
+ 0.00435747,
876
+ -0.02645369,
877
+ 0.00606709,
878
+ 0.01549895,
879
+ -0.00731538,
880
+ 0.00661131,
881
+ -0.02080153,
882
+ -0.01456788,
883
+ -0.01408948,
884
+ 0.04520504,
885
+ 0.00120645,
886
+ -0.03185837,
887
+ -0.00196832,
888
+ -0.03367321,
889
+ -0.00950671,
890
+ 0.00269519,
891
+ -0.01255799,
892
+ 0.01798061,
893
+ -0.0206966,
894
+ 0.0053775,
895
+ -0.01866919,
896
+ -0.0157447,
897
+ 0.02510054,
898
+ -0.00183053,
899
+ -0.02721807,
900
+ -0.00336808,
901
+ 0.00617041,
902
+ -0.0037063,
903
+ -0.00847081,
904
+ 0.01475876,
905
+ 0.00360406,
906
+ -0.00919089,
907
+ 0.00846259,
908
+ -0.00986784,
909
+ 0.03616112,
910
+ -0.01348828,
911
+ -0.03773517,
912
+ 0.01366153,
913
+ 0.01557916,
914
+ 0.00253959,
915
+ 0.01761871,
916
+ 0.02746272,
917
+ -2.889e-05,
918
+ -0.02247486,
919
+ -0.00598504,
920
+ 0.00305881,
921
+ -0.0189243,
922
+ 0.00307659,
923
+ -0.00270951,
924
+ -0.01400889,
925
+ 0.00048907,
926
+ -0.0141398,
927
+ 0.01238399,
928
+ -0.01219304,
929
+ -0.01345699,
930
+ -0.02739621,
931
+ 0.00530776,
932
+ -0.00869406,
933
+ 0.01128332,
934
+ -0.00857811,
935
+ 0.00681696,
936
+ -0.02160553,
937
+ 0.00656677,
938
+ -0.00244236,
939
+ -0.01392634,
940
+ 0.02499205,
941
+ -0.01085414,
942
+ -0.0210641,
943
+ -0.01952154,
944
+ -0.00241756,
945
+ 0.02450087,
946
+ 0.00031882,
947
+ 0.01047522,
948
+ -0.00239012,
949
+ -0.01098445,
950
+ 0.00337774,
951
+ 0.02112963,
952
+ 0.02175324,
953
+ -0.00375673,
954
+ -0.0055648,
955
+ -0.04740867,
956
+ 0.00146061,
957
+ 0.00249884,
958
+ -0.00492785,
959
+ 0.0217794,
960
+ 0.0009986,
961
+ 0.00053267,
962
+ 0.02192291,
963
+ -0.04000758,
964
+ -0.02102595,
965
+ -0.01386886,
966
+ 0.00854084,
967
+ -0.00585625,
968
+ -0.01580242,
969
+ 0.00548004,
970
+ 0.01337633,
971
+ -0.01481773,
972
+ -0.00783593,
973
+ 0.01119454,
974
+ -0.02044921,
975
+ 0.00733242,
976
+ -0.03132246,
977
+ 0.01873356,
978
+ -0.00689313,
979
+ 0.01189136,
980
+ 0.00341645,
981
+ 0.00868533,
982
+ 0.01298623,
983
+ -0.02415536,
984
+ -0.00280589,
985
+ 0.00276073,
986
+ -0.00506605,
987
+ 0.00613886,
988
+ 0.00331544,
989
+ 0.01548592,
990
+ 0.00503579,
991
+ -0.00113607,
992
+ 0.0292991,
993
+ 0.00820742,
994
+ 0.03085042,
995
+ 0.00137113,
996
+ -0.00751863,
997
+ -0.00376906,
998
+ -0.0054176,
999
+ -0.02161988,
1000
+ -0.02108021,
1001
+ 0.01517477,
1002
+ -0.01694685,
1003
+ -0.0144569,
1004
+ -0.0112995,
1005
+ -0.00250447,
1006
+ -0.01745691,
1007
+ -0.03337117,
1008
+ -0.00301234,
1009
+ -0.02143885,
1010
+ 0.00391685,
1011
+ 0.02673941,
1012
+ 0.00387426,
1013
+ 0.00849906,
1014
+ -0.03743575,
1015
+ -0.00248575,
1016
+ 0.01569809,
1017
+ 0.02312513,
1018
+ -0.01871841,
1019
+ -0.02665746,
1020
+ -0.02359086,
1021
+ -0.00458622,
1022
+ -0.02799702,
1023
+ -0.01016588,
1024
+ -0.00041483,
1025
+ -0.01132115,
1026
+ 0.01678916,
1027
+ 0.02570533,
1028
+ -0.00215934
1029
+ ],
1030
+ "alpha": 6.000321105305686,
1031
+ "cols": [
1032
+ 0,
1033
+ 2,
1034
+ 11,
1035
+ 25,
1036
+ 34,
1037
+ 35,
1038
+ 38,
1039
+ 39,
1040
+ 46,
1041
+ 50,
1042
+ 53,
1043
+ 56,
1044
+ 61,
1045
+ 62,
1046
+ 63,
1047
+ 64,
1048
+ 67,
1049
+ 73,
1050
+ 100,
1051
+ 108,
1052
+ 129,
1053
+ 135,
1054
+ 156,
1055
+ 169,
1056
+ 209,
1057
+ 215,
1058
+ 264,
1059
+ 266,
1060
+ 294,
1061
+ 327,
1062
+ 328,
1063
+ 357,
1064
+ 371,
1065
+ 376,
1066
+ 412,
1067
+ 414,
1068
+ 442,
1069
+ 504,
1070
+ 505,
1071
+ 509,
1072
+ 516,
1073
+ 580,
1074
+ 590,
1075
+ 601,
1076
+ 638,
1077
+ 682,
1078
+ 738,
1079
+ 882,
1080
+ 926,
1081
+ 930,
1082
+ 1047,
1083
+ 1060,
1084
+ 1087,
1085
+ 1130,
1086
+ 1155,
1087
+ 1184,
1088
+ 1460,
1089
+ 1481,
1090
+ 1787,
1091
+ 1943,
1092
+ 2005,
1093
+ 2066,
1094
+ 2738,
1095
+ 3003
1096
+ ],
1097
+ "singular_values_top5": [
1098
+ 1.7488828897476196,
1099
+ 0.0009874117095023394,
1100
+ 0.0009485556511208415,
1101
+ 0.0008693150011822581,
1102
+ 0.0008093058713711798
1103
+ ],
1104
+ "rank1_share": 0.9999944567680359,
1105
+ "relative_change": 0.10635697096586227,
1106
+ "changed_entries": 65260,
1107
+ "rebuild_alpha_6.0003_entries_differing": 5875,
1108
+ "rebuild_alpha_6.0003_max_abs_diff": 0.00048828125,
1109
+ "rebuild_alpha_6.0000_entries_differing": 5924,
1110
+ "rebuild_alpha_6.0000_max_abs_diff": 0.00048828125
1111
+ },
1112
+ "19": {
1113
+ "u": [
1114
+ 0.33559576,
1115
+ -0.03464305,
1116
+ -0.06492217,
1117
+ -0.0758407,
1118
+ -0.04330086,
1119
+ 0.20503135,
1120
+ -0.01100612,
1121
+ 0.07837953,
1122
+ 0.02964944,
1123
+ -0.07836166,
1124
+ -0.02681363,
1125
+ 0.00668682,
1126
+ -0.02350798,
1127
+ -0.10140933,
1128
+ -0.03288105,
1129
+ -0.14028722,
1130
+ -0.09824702,
1131
+ -0.11770061,
1132
+ 0.20775542,
1133
+ 0.10649866,
1134
+ -0.09821087,
1135
+ -0.0250454,
1136
+ -0.11596692,
1137
+ -0.12496462,
1138
+ 0.02373243,
1139
+ 0.01544948,
1140
+ -0.05152019,
1141
+ -0.07280387,
1142
+ 0.0386979,
1143
+ 0.04272014,
1144
+ -0.0081986,
1145
+ 0.10454679,
1146
+ -0.06094285,
1147
+ 0.01446683,
1148
+ 0.10608368,
1149
+ -0.00541141,
1150
+ -0.08207095,
1151
+ -0.21935181,
1152
+ 0.13905452,
1153
+ 0.0108749,
1154
+ 0.05618587,
1155
+ -0.02780561,
1156
+ 0.06047707,
1157
+ -0.01481501,
1158
+ 0.03299749,
1159
+ 0.06129911,
1160
+ -0.07319687,
1161
+ 0.01751008,
1162
+ -0.0128193,
1163
+ -0.02267084,
1164
+ 0.04299525,
1165
+ -0.06126266,
1166
+ 0.03324774,
1167
+ -0.01272442,
1168
+ 0.02234195,
1169
+ -0.10648298,
1170
+ -0.02969377,
1171
+ -0.02647679,
1172
+ -0.01312392,
1173
+ -0.03706729,
1174
+ -0.02219274,
1175
+ 0.08834642,
1176
+ 0.04794782,
1177
+ -0.02925058,
1178
+ -0.04679199,
1179
+ 0.02374988,
1180
+ -0.06003199,
1181
+ 0.02554107,
1182
+ -0.00404207,
1183
+ 0.04579034,
1184
+ 0.01502077,
1185
+ -0.0341815,
1186
+ 0.05213764,
1187
+ -0.00307369,
1188
+ 0.11000572,
1189
+ 0.01039833,
1190
+ 0.05742714,
1191
+ 0.01005035,
1192
+ 0.09028663,
1193
+ -0.00666727,
1194
+ -0.03628933,
1195
+ -0.07142809,
1196
+ -0.00386145,
1197
+ 0.06643536,
1198
+ -0.02378962,
1199
+ -0.11098496,
1200
+ -0.08980843,
1201
+ 0.01918232,
1202
+ -0.01326295,
1203
+ 0.00460524,
1204
+ 0.03124605,
1205
+ 0.02860291,
1206
+ 0.04878333,
1207
+ 0.00629162,
1208
+ -0.0320451,
1209
+ -0.01327783,
1210
+ 0.04441041,
1211
+ -0.03540138,
1212
+ 0.02303142,
1213
+ -0.00700999,
1214
+ 0.02519903,
1215
+ 0.07456323,
1216
+ 0.01754859,
1217
+ -0.03160535,
1218
+ 0.02420698,
1219
+ 0.01294643,
1220
+ 0.00773049,
1221
+ -0.00283266,
1222
+ 0.02386064,
1223
+ 0.03280374,
1224
+ -0.02897603,
1225
+ -0.06339945,
1226
+ -0.01799321,
1227
+ -0.00071628,
1228
+ 0.01139896,
1229
+ -0.07300469,
1230
+ 0.00444034,
1231
+ 0.06423035,
1232
+ 0.06576908,
1233
+ -0.04132114,
1234
+ -0.01963111,
1235
+ -0.03308957,
1236
+ 0.02335624,
1237
+ -0.00031846,
1238
+ 0.01106018,
1239
+ -0.04028105,
1240
+ -0.00064796,
1241
+ 0.0194139,
1242
+ -0.00128262,
1243
+ -0.06438185,
1244
+ -0.0089235,
1245
+ 0.04541202,
1246
+ -0.01789016,
1247
+ -0.01187054,
1248
+ -0.00504768,
1249
+ 0.00589341,
1250
+ 0.01914849,
1251
+ 0.06267631,
1252
+ -0.05639358,
1253
+ 0.02688798,
1254
+ -0.02004607,
1255
+ -0.02166805,
1256
+ -0.01570235,
1257
+ -0.01208901,
1258
+ 0.03124614,
1259
+ -0.0246713,
1260
+ -0.0409742,
1261
+ -0.01053298,
1262
+ 0.02573625,
1263
+ 0.00536171,
1264
+ -0.01311869,
1265
+ -0.0021853,
1266
+ -0.00545144,
1267
+ -0.02813631,
1268
+ 0.00454908,
1269
+ -0.02800255,
1270
+ 0.05073616,
1271
+ -0.02977879,
1272
+ -0.00736543,
1273
+ -0.00806641,
1274
+ -0.039063,
1275
+ 0.01411793,
1276
+ -0.03575376,
1277
+ -0.02516236,
1278
+ 0.01975346,
1279
+ 0.00354834,
1280
+ -0.04622909,
1281
+ 0.01877856,
1282
+ 0.03596357,
1283
+ -0.02185726,
1284
+ 0.01092272,
1285
+ 0.03444305,
1286
+ 0.00924869,
1287
+ 0.01236634,
1288
+ 0.01875189,
1289
+ 0.00123886,
1290
+ 0.00015365,
1291
+ 0.02428256,
1292
+ -0.03664358,
1293
+ -0.03418206,
1294
+ 0.02913373,
1295
+ 0.01644926,
1296
+ -0.00766343,
1297
+ 0.01605212,
1298
+ 0.00822296,
1299
+ 0.01068919,
1300
+ 0.04207898,
1301
+ 0.04286939,
1302
+ -0.02778637,
1303
+ 0.01277927,
1304
+ 0.03045678,
1305
+ 0.00630897,
1306
+ -0.02207148,
1307
+ -0.03986497,
1308
+ 0.0315614,
1309
+ -0.03530436,
1310
+ 0.01030708,
1311
+ 0.02566668,
1312
+ 0.00233784,
1313
+ 0.0426428,
1314
+ 0.02157422,
1315
+ -0.04566223,
1316
+ 0.00993582,
1317
+ 0.01377577,
1318
+ 0.05212355,
1319
+ -0.03241672,
1320
+ 0.00040995,
1321
+ 0.02884942,
1322
+ 0.05572666,
1323
+ -0.01136362,
1324
+ -0.02396954,
1325
+ -0.0074198,
1326
+ -0.01548682,
1327
+ 0.02242211,
1328
+ 0.00626146,
1329
+ 0.02337368,
1330
+ 0.00290542,
1331
+ -0.03092874,
1332
+ -0.0145256,
1333
+ 0.0860513,
1334
+ 0.01966276,
1335
+ -0.04058313,
1336
+ 0.01011636,
1337
+ 0.02531578,
1338
+ -0.02338178,
1339
+ 0.06847191,
1340
+ -0.00949347,
1341
+ 0.00520009,
1342
+ 0.01857924,
1343
+ 0.00234352,
1344
+ 0.04116894,
1345
+ -0.03657442,
1346
+ -0.01991575,
1347
+ -0.0411019,
1348
+ -0.01821601,
1349
+ -0.0151482,
1350
+ -0.00906486,
1351
+ -0.0120324,
1352
+ 0.04747478,
1353
+ 0.00392497,
1354
+ 0.00323421,
1355
+ -0.01088273,
1356
+ -0.00134013,
1357
+ 0.00170012,
1358
+ -0.02111938,
1359
+ -0.02722598,
1360
+ -0.01383084,
1361
+ 0.00617009,
1362
+ 0.00610261,
1363
+ -0.01101309,
1364
+ -0.02752119,
1365
+ 0.02427548,
1366
+ 0.02947209,
1367
+ 0.01694234,
1368
+ -0.00122762,
1369
+ -0.05191525,
1370
+ 0.01423117,
1371
+ 0.04006915,
1372
+ 0.0091782,
1373
+ 0.02908517,
1374
+ 0.00436178,
1375
+ 0.00128976,
1376
+ -0.02698803,
1377
+ -0.01225181,
1378
+ -0.00978529,
1379
+ -0.0362439,
1380
+ -0.02339351,
1381
+ -0.02572778,
1382
+ 0.02842867,
1383
+ -0.0092746,
1384
+ -0.01647593,
1385
+ 0.04589776,
1386
+ -0.01857447,
1387
+ 0.02361508,
1388
+ -0.00518944,
1389
+ -0.001908,
1390
+ 0.01267422,
1391
+ -0.02436761,
1392
+ 0.02689295,
1393
+ 0.0360607,
1394
+ -0.01325329,
1395
+ 0.00222294,
1396
+ 0.02398918,
1397
+ -0.02143449,
1398
+ -0.00039261,
1399
+ -0.0127418,
1400
+ -0.00827601,
1401
+ 0.02336513,
1402
+ 0.0375852,
1403
+ 0.01680375,
1404
+ -0.00802757,
1405
+ -0.01239895,
1406
+ 0.01229273,
1407
+ -0.02017267,
1408
+ -0.0171816,
1409
+ 0.0323827,
1410
+ -0.00800342,
1411
+ 0.00295632,
1412
+ 0.0116492,
1413
+ 0.01432486,
1414
+ -0.03085296,
1415
+ 0.0109554,
1416
+ 0.02080563,
1417
+ -0.03709523,
1418
+ 0.03610288,
1419
+ 0.00124742,
1420
+ 0.00432386,
1421
+ -0.01376396,
1422
+ -0.03764868,
1423
+ -0.02849224,
1424
+ 0.01595173,
1425
+ 0.01317301,
1426
+ -0.01462442,
1427
+ -0.02009147,
1428
+ 0.03663041,
1429
+ -0.02031766,
1430
+ 0.07009073,
1431
+ 0.05813979,
1432
+ 0.03254977,
1433
+ -0.00911946,
1434
+ -0.03866731,
1435
+ 0.00168065,
1436
+ -0.02967888,
1437
+ -0.00748741,
1438
+ 0.00997796,
1439
+ 0.006374,
1440
+ -0.06434561,
1441
+ 0.00840214,
1442
+ 0.00192905,
1443
+ 0.01429112,
1444
+ -0.02104565,
1445
+ 0.01245597,
1446
+ -0.03688834,
1447
+ -0.05348125,
1448
+ -0.01398059,
1449
+ -0.01013292,
1450
+ -0.01436885,
1451
+ -0.00795575,
1452
+ 0.01192914,
1453
+ -0.00317343,
1454
+ 0.00442111,
1455
+ 0.01588613,
1456
+ 0.01969983,
1457
+ 0.00358299,
1458
+ 0.00843091,
1459
+ 0.01197292,
1460
+ -0.00808269,
1461
+ -0.0120325,
1462
+ 0.01489386,
1463
+ -0.0229412,
1464
+ 0.03391672,
1465
+ 0.00041582,
1466
+ 0.0444411,
1467
+ 0.00166488,
1468
+ 0.04418352,
1469
+ -0.0045545,
1470
+ 0.0105409,
1471
+ 0.0129758,
1472
+ -0.01058341,
1473
+ 0.01379588,
1474
+ -0.05057723,
1475
+ 0.01058852,
1476
+ -0.01741165,
1477
+ 0.03224251,
1478
+ -0.00865988,
1479
+ -0.00366896,
1480
+ -0.01064463,
1481
+ 0.01051221,
1482
+ -0.00587606,
1483
+ -0.00057805,
1484
+ -0.00040366,
1485
+ 0.00980105,
1486
+ -0.01242636,
1487
+ 0.00816267,
1488
+ -0.00530881,
1489
+ 0.0107462,
1490
+ 0.00933116,
1491
+ -0.01976871,
1492
+ 0.00267897,
1493
+ 0.01366714,
1494
+ 5.26e-05,
1495
+ -0.02362413,
1496
+ 0.0266993,
1497
+ 0.00520968,
1498
+ -0.00263139,
1499
+ -0.01041333,
1500
+ -0.00524971,
1501
+ -0.01508682,
1502
+ 0.0208301,
1503
+ -0.00231445,
1504
+ 0.01323305,
1505
+ 0.0148322,
1506
+ 0.0298044,
1507
+ 0.00122306,
1508
+ -0.0082885,
1509
+ -0.0325029,
1510
+ -0.01031271,
1511
+ 0.00835443,
1512
+ 0.01340494,
1513
+ -0.02087971,
1514
+ 0.024467,
1515
+ -0.01740892,
1516
+ -0.00783929,
1517
+ -0.02822377,
1518
+ -0.00948129,
1519
+ 0.03420782,
1520
+ -0.0123644,
1521
+ -0.00093461,
1522
+ -0.00221112,
1523
+ -0.00794372,
1524
+ -0.02958393,
1525
+ 0.01561154,
1526
+ -0.02206564,
1527
+ 0.02932473,
1528
+ -0.01317207,
1529
+ 0.01216475,
1530
+ 0.05258676,
1531
+ -0.04038979,
1532
+ 0.03514089,
1533
+ 0.01140578,
1534
+ 0.04206885,
1535
+ 0.00339565,
1536
+ -0.01667423,
1537
+ 0.01111635,
1538
+ 0.1091687,
1539
+ -0.02007846,
1540
+ 8.865e-05,
1541
+ -0.01171773,
1542
+ -0.01453419,
1543
+ -0.00614797,
1544
+ 0.00426437,
1545
+ -0.01244039,
1546
+ 0.00979596,
1547
+ -0.01847589,
1548
+ -0.01246628,
1549
+ -0.06025706,
1550
+ 0.00985852,
1551
+ 0.0059323,
1552
+ -0.01498602,
1553
+ -0.02150767,
1554
+ -0.00260376,
1555
+ 0.00529287,
1556
+ 0.02987313,
1557
+ -0.01029432,
1558
+ -0.01696572,
1559
+ -0.02138881,
1560
+ -0.01986272,
1561
+ -0.04967582,
1562
+ -0.01098283,
1563
+ 0.01132601,
1564
+ -0.00342707,
1565
+ -0.02904738,
1566
+ 0.01663383,
1567
+ 0.00752067,
1568
+ 0.00172083,
1569
+ 0.02616538,
1570
+ 0.014528,
1571
+ 0.02282346,
1572
+ -0.01403882,
1573
+ -0.03293377,
1574
+ -0.03202629,
1575
+ 0.00279875,
1576
+ -0.02190205,
1577
+ 0.01519247,
1578
+ 0.01755691,
1579
+ -0.01623092,
1580
+ -0.01870362,
1581
+ -0.02548813,
1582
+ -0.0095768,
1583
+ -0.01465815,
1584
+ 0.02063545,
1585
+ -0.02311257,
1586
+ 0.01527115,
1587
+ -0.00958146,
1588
+ -0.01391149,
1589
+ 0.00278968,
1590
+ -0.00244607,
1591
+ -0.01574637,
1592
+ -0.02303971,
1593
+ 0.05559954,
1594
+ 0.04239292,
1595
+ -0.00063367,
1596
+ 0.02805366,
1597
+ -0.01336559,
1598
+ 0.00476259,
1599
+ 0.02709932,
1600
+ 0.0059806,
1601
+ -0.00172392,
1602
+ -0.01144029,
1603
+ 0.00935203,
1604
+ 0.0347795,
1605
+ 0.03206396,
1606
+ 0.00100924,
1607
+ 0.00740879,
1608
+ 0.01276771,
1609
+ -0.02085232,
1610
+ -0.0040931,
1611
+ 0.00163903,
1612
+ -0.01503349,
1613
+ 0.02570495,
1614
+ 0.00230727,
1615
+ 0.00422022,
1616
+ -0.00537709,
1617
+ -0.00793678,
1618
+ -0.01116326,
1619
+ -0.00440048,
1620
+ 0.02106057,
1621
+ 0.00996581,
1622
+ -0.01476111,
1623
+ -0.03709668,
1624
+ -0.0181785,
1625
+ 0.01074154,
1626
+ 0.02489132,
1627
+ 0.01092589,
1628
+ 0.02339229,
1629
+ -0.00799304,
1630
+ 0.0194321,
1631
+ -0.01218181,
1632
+ -0.00301016,
1633
+ 0.00131753,
1634
+ -0.04913622,
1635
+ -0.00534636,
1636
+ 0.00865552,
1637
+ -0.00328565,
1638
+ -0.01685773,
1639
+ -0.01348882,
1640
+ 0.01474129,
1641
+ 0.03932326,
1642
+ -0.0140139,
1643
+ 0.00828108,
1644
+ 0.00203812,
1645
+ -0.0016696,
1646
+ -0.02406231,
1647
+ 0.01888967,
1648
+ 0.02036756,
1649
+ -0.00563243,
1650
+ -0.00151437,
1651
+ -0.01412627,
1652
+ 0.0138975,
1653
+ 0.0254507,
1654
+ -0.00512592,
1655
+ 0.02180942,
1656
+ 0.00169177,
1657
+ -0.00658894,
1658
+ -0.01424513,
1659
+ -0.01952751,
1660
+ 0.00987476,
1661
+ -0.04215077,
1662
+ -0.03103609,
1663
+ -0.03450365,
1664
+ -0.00404743,
1665
+ 0.02796279,
1666
+ -0.01409377,
1667
+ 0.02256705,
1668
+ 0.00011281,
1669
+ 0.00330633,
1670
+ 0.00917419,
1671
+ 0.02237586,
1672
+ -0.00486028,
1673
+ -0.02149612,
1674
+ 0.00931693,
1675
+ -0.02292099,
1676
+ 0.00794967,
1677
+ -0.03169209,
1678
+ -0.00961952,
1679
+ 0.01466525,
1680
+ -0.01301782,
1681
+ -0.02313516,
1682
+ -0.01651174,
1683
+ -0.01917735,
1684
+ -0.00666742,
1685
+ 0.01564164,
1686
+ -0.01308908,
1687
+ 0.01639422,
1688
+ -0.02955538,
1689
+ 0.0093139,
1690
+ 0.01910305,
1691
+ -0.04276742,
1692
+ -0.02866714,
1693
+ -0.0257399,
1694
+ 0.00713598,
1695
+ -0.02067339,
1696
+ -0.01301082,
1697
+ 0.00712061,
1698
+ -0.00678727,
1699
+ 0.0025609,
1700
+ 0.0082221,
1701
+ 0.0205679,
1702
+ 0.00185209,
1703
+ 0.00875445,
1704
+ 0.03590834,
1705
+ -0.00312236,
1706
+ -0.05863045,
1707
+ -0.02253641,
1708
+ 0.03322073,
1709
+ 0.03223033,
1710
+ 0.00666336,
1711
+ -0.02435645,
1712
+ 0.00948125,
1713
+ -0.01510644,
1714
+ -0.01706491,
1715
+ 0.00094003,
1716
+ -0.02911649,
1717
+ -0.02057935,
1718
+ 0.00863724,
1719
+ 0.01242663,
1720
+ -0.00044811,
1721
+ -0.0096365,
1722
+ 0.0145297,
1723
+ -0.00646989,
1724
+ -0.01096002,
1725
+ -0.02550274,
1726
+ 0.01688749,
1727
+ 0.01177412,
1728
+ -0.0141668,
1729
+ 0.01863014,
1730
+ 0.00935381,
1731
+ 0.03003733,
1732
+ -0.02493563,
1733
+ -0.02605457,
1734
+ -0.00891686,
1735
+ 0.0150456,
1736
+ 0.01764304,
1737
+ -0.00095473,
1738
+ -0.02690967,
1739
+ 0.01516299,
1740
+ -0.01976084,
1741
+ 0.0176523,
1742
+ 6.914e-05,
1743
+ -0.01932689,
1744
+ -0.00354382,
1745
+ -0.00172482,
1746
+ -0.00987696,
1747
+ -0.00907846,
1748
+ 0.01484565,
1749
+ -0.014577,
1750
+ -0.01390697,
1751
+ 0.04038126,
1752
+ -0.0048265,
1753
+ -0.00247458,
1754
+ -0.01462614,
1755
+ 0.00847769,
1756
+ 0.0153567,
1757
+ 0.00848636,
1758
+ -0.00389406,
1759
+ -0.00768076,
1760
+ -0.01452379,
1761
+ -0.02445431,
1762
+ 0.01986887,
1763
+ 0.01502052,
1764
+ -0.0102555,
1765
+ 0.03740228,
1766
+ 0.00843638,
1767
+ 0.0082063,
1768
+ -0.00148864,
1769
+ -0.00990654,
1770
+ -0.0164611,
1771
+ -0.03292083,
1772
+ 0.0217579,
1773
+ -0.05537551,
1774
+ -0.00516081,
1775
+ -0.00284331,
1776
+ -0.00884788,
1777
+ -0.00121732,
1778
+ -0.00610297,
1779
+ 0.00762023,
1780
+ 0.0217163,
1781
+ 0.00473982,
1782
+ -0.01743882,
1783
+ -0.00036,
1784
+ -0.01559782,
1785
+ -0.00327546,
1786
+ 0.03106499,
1787
+ -0.00088182,
1788
+ -0.01079302,
1789
+ 0.02392566,
1790
+ 0.03034966,
1791
+ -0.01150686,
1792
+ 0.00968921,
1793
+ 0.03213762,
1794
+ 0.04562483,
1795
+ 0.01939051,
1796
+ 0.00593721,
1797
+ 0.03199087,
1798
+ 0.00499116,
1799
+ -0.0,
1800
+ -0.00065295,
1801
+ -0.0139709,
1802
+ 0.04694964,
1803
+ 0.01491039,
1804
+ -0.011949,
1805
+ -0.01931378,
1806
+ 0.00424174,
1807
+ -0.00083657,
1808
+ 0.01842102,
1809
+ -0.03574473,
1810
+ 0.01963529,
1811
+ 0.00709899,
1812
+ 0.02524782,
1813
+ -0.01975095,
1814
+ -0.00502984,
1815
+ -0.02291168,
1816
+ -0.0025806,
1817
+ 0.0093552,
1818
+ -0.00500408,
1819
+ 0.01239402,
1820
+ -0.00599165,
1821
+ -0.01745293,
1822
+ 0.0153375,
1823
+ -0.00978259,
1824
+ -0.00413711,
1825
+ -0.00162416,
1826
+ 0.02107732,
1827
+ 0.00189593,
1828
+ -0.01225992,
1829
+ -0.00017994,
1830
+ 0.0193435,
1831
+ -0.01469106,
1832
+ 0.00198856,
1833
+ 0.0195028,
1834
+ 0.0110589,
1835
+ -0.0039253,
1836
+ -0.03639412,
1837
+ 0.02734545,
1838
+ -0.00599648,
1839
+ 0.00229398,
1840
+ -0.00796998,
1841
+ -0.00644312,
1842
+ -0.02087018,
1843
+ -0.02181302,
1844
+ -0.00862427,
1845
+ -0.00391108,
1846
+ 0.02068237,
1847
+ 0.026373,
1848
+ 0.00668544,
1849
+ 0.01560128,
1850
+ -0.00049076,
1851
+ 0.0115634,
1852
+ 0.00455035,
1853
+ 0.0356213,
1854
+ -0.00194795,
1855
+ 8.843e-05,
1856
+ -0.00874462,
1857
+ -0.00446164,
1858
+ 0.00585578,
1859
+ 0.02350405,
1860
+ -0.00209728,
1861
+ -0.02649821,
1862
+ -0.02575775,
1863
+ -0.00397179,
1864
+ -0.01277832,
1865
+ -0.01572774,
1866
+ 0.01737997,
1867
+ 0.01299238,
1868
+ 0.01312802,
1869
+ 0.01565149,
1870
+ 0.0065699,
1871
+ 0.00885125,
1872
+ 0.03822977,
1873
+ 0.01536098,
1874
+ 0.02513214,
1875
+ -0.03427662,
1876
+ 0.00189558,
1877
+ -0.01710075,
1878
+ -0.0016079,
1879
+ 0.03475442,
1880
+ 0.00088984,
1881
+ 0.01690382,
1882
+ 0.00949159,
1883
+ -0.04899343,
1884
+ -0.00457814,
1885
+ 0.00797072,
1886
+ 0.01603921,
1887
+ -0.00151148,
1888
+ 0.00165931,
1889
+ 0.02781364,
1890
+ 0.00976236,
1891
+ 0.00831935,
1892
+ -0.00147864,
1893
+ -0.01809784,
1894
+ -0.0110757,
1895
+ 0.01264199,
1896
+ 0.01910636,
1897
+ 0.0324349,
1898
+ -0.0100819,
1899
+ 0.00105819,
1900
+ -0.00291676,
1901
+ 0.03201768,
1902
+ -0.00393973,
1903
+ 0.03793667,
1904
+ -0.02129277,
1905
+ -0.00139829,
1906
+ -0.03023741,
1907
+ 0.01313352,
1908
+ -0.02107666,
1909
+ -0.01446228,
1910
+ 0.03014615,
1911
+ 0.00998374,
1912
+ 0.02206057,
1913
+ 0.00608394,
1914
+ 0.01368592,
1915
+ 0.02053929,
1916
+ 0.00695174,
1917
+ 0.00442718,
1918
+ -0.03535806,
1919
+ -0.03074319,
1920
+ 0.00144798,
1921
+ 0.01089423,
1922
+ -0.01115825,
1923
+ 0.01397229,
1924
+ 0.01757357,
1925
+ -0.01300421,
1926
+ -0.02727049,
1927
+ 0.00731468,
1928
+ -0.0159276,
1929
+ 0.00104553,
1930
+ 0.00665206,
1931
+ 0.01975397,
1932
+ -0.02238113,
1933
+ 0.01083192,
1934
+ -0.0046235,
1935
+ 0.00074646,
1936
+ 0.00504114,
1937
+ -0.01104437,
1938
+ -0.00610299,
1939
+ 0.01871506,
1940
+ -0.02166009,
1941
+ 0.0016235,
1942
+ -0.01377717,
1943
+ 0.01032949,
1944
+ -0.01005503,
1945
+ -0.0014602,
1946
+ -0.01458238,
1947
+ 0.00213626,
1948
+ -0.00465989,
1949
+ -0.03355782,
1950
+ -0.01548542,
1951
+ -0.02482919,
1952
+ 0.00147339,
1953
+ -0.01707004,
1954
+ 0.01612629,
1955
+ -0.00786161,
1956
+ 0.00012219,
1957
+ -0.00965089,
1958
+ 0.00940183,
1959
+ 0.00342041,
1960
+ 0.01054248,
1961
+ 0.0242572,
1962
+ -0.03094187,
1963
+ 0.01119379,
1964
+ -0.01056487,
1965
+ 0.00866525,
1966
+ 0.02372767,
1967
+ -0.00327202,
1968
+ 0.00686602,
1969
+ -0.02390694,
1970
+ 0.02468651,
1971
+ 0.01500709,
1972
+ 0.00445921,
1973
+ 0.01888491,
1974
+ 0.01410272,
1975
+ -0.02463877,
1976
+ 0.00823074,
1977
+ 0.03660055,
1978
+ -0.00133192,
1979
+ 0.01779861,
1980
+ -0.01875987,
1981
+ 0.01344969,
1982
+ -0.01102422,
1983
+ -0.01044747,
1984
+ -0.01006741,
1985
+ 0.0323382,
1986
+ -0.01484594,
1987
+ 0.00146564,
1988
+ 0.00804006,
1989
+ 0.00426122,
1990
+ 0.01966095,
1991
+ -0.00127307,
1992
+ -0.00580236,
1993
+ -0.01033067,
1994
+ 0.01667609,
1995
+ 0.02310249,
1996
+ 0.01413857,
1997
+ 0.02454613,
1998
+ 0.01059382,
1999
+ 0.0026291,
2000
+ 0.00943861,
2001
+ -3.41e-06,
2002
+ 0.0033934,
2003
+ -0.01704926,
2004
+ 0.01335935,
2005
+ 0.00020378,
2006
+ 0.00195949,
2007
+ -0.00776831,
2008
+ 0.01629458,
2009
+ -0.00759811,
2010
+ -0.00645715,
2011
+ 0.01794815,
2012
+ -0.00121991,
2013
+ 0.01632476,
2014
+ 0.01075443,
2015
+ -0.00039715,
2016
+ -0.01542044,
2017
+ -0.00214471,
2018
+ -0.03306837,
2019
+ -0.03733351,
2020
+ 0.02991378,
2021
+ -0.01045495,
2022
+ -0.02377043,
2023
+ -0.01897853,
2024
+ -0.00408702,
2025
+ -0.00167294,
2026
+ -0.00295758,
2027
+ 0.02123102,
2028
+ -0.01130867,
2029
+ -0.00295642,
2030
+ -0.01521876,
2031
+ 0.02204927,
2032
+ 0.00140542,
2033
+ 0.04342286,
2034
+ 0.02072281,
2035
+ 0.03388473,
2036
+ -0.00877712,
2037
+ -0.00512505,
2038
+ 0.0151763,
2039
+ 0.02042603,
2040
+ 0.00843577,
2041
+ 0.01931063,
2042
+ 0.00979518,
2043
+ 0.00592844,
2044
+ -0.02599867,
2045
+ 0.01489202,
2046
+ -0.00266025,
2047
+ -0.00813517,
2048
+ -0.01534928,
2049
+ -0.01736461,
2050
+ 0.01320925,
2051
+ 0.0355656,
2052
+ 0.02181039,
2053
+ 0.00512136,
2054
+ -0.02044921,
2055
+ -0.02641958,
2056
+ 0.01625668,
2057
+ 0.00261996,
2058
+ 0.01905341,
2059
+ -0.00689309,
2060
+ -0.02259504,
2061
+ -0.01227041,
2062
+ 0.02103539,
2063
+ 0.00072978,
2064
+ 0.05127988,
2065
+ -0.0009185,
2066
+ 0.02334338,
2067
+ 0.0002852,
2068
+ -0.01754911,
2069
+ -0.02641877,
2070
+ -0.01193665,
2071
+ -0.02183538,
2072
+ -0.00192791,
2073
+ 0.01890938,
2074
+ 0.02368859,
2075
+ 0.0052784,
2076
+ 0.00044876,
2077
+ -0.00013281,
2078
+ 0.0180367,
2079
+ -0.01584567,
2080
+ 0.03249414,
2081
+ 0.00945242,
2082
+ -0.01653406,
2083
+ 0.01701443,
2084
+ -0.00781736,
2085
+ 0.01829203,
2086
+ -0.01258584,
2087
+ -0.00042628,
2088
+ -0.00774671,
2089
+ -0.00815235,
2090
+ 0.00224324,
2091
+ 0.0114774,
2092
+ 0.02551686,
2093
+ 0.01460277,
2094
+ -0.00243856,
2095
+ -0.00373503,
2096
+ 0.02294976,
2097
+ -0.00273832,
2098
+ -0.01014692,
2099
+ -0.00593784,
2100
+ -0.00318341,
2101
+ -0.01724492,
2102
+ -0.00022391,
2103
+ -0.01840212,
2104
+ -0.01111254,
2105
+ 0.01020701,
2106
+ 0.01218268,
2107
+ -0.0004537,
2108
+ 0.02419603,
2109
+ 0.046826,
2110
+ -0.01415514,
2111
+ -0.00445342,
2112
+ 0.0266875,
2113
+ 0.02994117,
2114
+ -0.00343018,
2115
+ 0.02188344,
2116
+ -0.0069619,
2117
+ 0.00566721,
2118
+ 0.02015941,
2119
+ 0.02477168,
2120
+ -0.03093816,
2121
+ 0.01301537,
2122
+ 0.02665879,
2123
+ 0.02318588,
2124
+ -2.81e-06,
2125
+ -0.01443277,
2126
+ -0.02519409,
2127
+ 0.00244334,
2128
+ 0.00380622,
2129
+ -0.00973464,
2130
+ 0.00215472,
2131
+ 0.00762649,
2132
+ -0.01932207,
2133
+ -0.00900411,
2134
+ 0.03000734,
2135
+ -0.01070951,
2136
+ -0.00942091,
2137
+ -0.00686134
2138
+ ],
2139
+ "alpha": 6.0001732486833035,
2140
+ "cols": [
2141
+ 1,
2142
+ 5,
2143
+ 8,
2144
+ 11,
2145
+ 13,
2146
+ 15,
2147
+ 17,
2148
+ 18,
2149
+ 34,
2150
+ 36,
2151
+ 44,
2152
+ 55,
2153
+ 63,
2154
+ 66,
2155
+ 69,
2156
+ 92,
2157
+ 93,
2158
+ 95,
2159
+ 98,
2160
+ 114,
2161
+ 119,
2162
+ 127,
2163
+ 145,
2164
+ 152,
2165
+ 161,
2166
+ 168,
2167
+ 171,
2168
+ 195,
2169
+ 246,
2170
+ 259,
2171
+ 265,
2172
+ 343,
2173
+ 364,
2174
+ 445,
2175
+ 506,
2176
+ 544,
2177
+ 621,
2178
+ 625,
2179
+ 636,
2180
+ 744,
2181
+ 766,
2182
+ 770,
2183
+ 799,
2184
+ 877,
2185
+ 965,
2186
+ 1003,
2187
+ 1346,
2188
+ 1410,
2189
+ 1505,
2190
+ 1704,
2191
+ 1799,
2192
+ 2072,
2193
+ 2135,
2194
+ 2175,
2195
+ 2248,
2196
+ 2314,
2197
+ 2376,
2198
+ 2437,
2199
+ 2514,
2200
+ 2595,
2201
+ 2664,
2202
+ 2684,
2203
+ 2816,
2204
+ 3343
2205
+ ],
2206
+ "singular_values_top5": [
2207
+ 1.3987572193145752,
2208
+ 0.0009012575028464198,
2209
+ 0.0008135843672789633,
2210
+ 0.0007443478680215776,
2211
+ 0.000705311365891248
2212
+ ],
2213
+ "rank1_share": 0.9999914765357971,
2214
+ "relative_change": 0.08105870336294174,
2215
+ "changed_entries": 64928,
2216
+ "rebuild_alpha_6.0002_entries_differing": 6158,
2217
+ "rebuild_alpha_6.0002_max_abs_diff": 0.00048828125,
2218
+ "rebuild_alpha_6.0000_entries_differing": 6166,
2219
+ "rebuild_alpha_6.0000_max_abs_diff": 0.00048828125
2220
+ }
2221
+ },
2222
+ "cos_u15_u19": -0.8490049839019775
2223
+ }
code/results/figures/lab0_toy.png ADDED
code/results/figures/lab1_diff.png ADDED
code/results/figures/lab2_benchmarks.png ADDED
code/results/figures/lab2_lengths.png ADDED
code/results/figures/lab3_neuron_scores.png ADDED
code/results/figures/lab3_where_the_loop_grows.png ADDED
code/results/figures/lab4_controls.png ADDED
code/results/figures/lab4_dose.png ADDED

Git LFS Details

  • SHA256: 78b0d1fd460b06b3298cd2dbc15f70b801dc9ca9724294ab5e3eae4f77b6fd89
  • Pointer size: 131 Bytes
  • Size of remote file: 155 kB
code/results/figures/lab5_decoding.png ADDED
code/results/figures/lab6_finished_vs_loops.png ADDED
code/results/figures/lab6_loops_summary.png ADDED

Git LFS Details

  • SHA256: 950742fca268f4115995649c7f79c5dc32b200f3c453b98af80ab3dac1ad82a4
  • Pointer size: 131 Bytes
  • Size of remote file: 107 kB
code/results/figures/lab6_real_loop.png ADDED

Git LFS Details

  • SHA256: c43c7fa0c345b568c8165528f6748b32ba52c3a78e411314cf5284b9ac80df2a
  • Pointer size: 131 Bytes
  • Size of remote file: 352 kB
code/results/figures/lab7_side_effects.png ADDED
code/results/figures/lab8_data_needed.png ADDED

Git LFS Details

  • SHA256: 680737fc8a23954c5920b2579a117832f301e31e00e197334ec2d0d566a431d7
  • Pointer size: 131 Bytes
  • Size of remote file: 120 kB
code/results/figures/lab8_loop_onset.png ADDED

Git LFS Details

  • SHA256: aca0904a28485b0f08f9bb60f88ae046200bc948d22da5b6e743b42c1ad6a256
  • Pointer size: 131 Bytes
  • Size of remote file: 357 kB