main: keep only what reproduces the manuscript; everything else lives on dev

Removed from main (all preserved on the dev branch): the arXiv build and
its sources, design documents (blueprint, results summary, review responses,
essay drafts), tasks/ and CLAUDE.md, the cover letter and reference tooling,
two unused manuscript figures, and every experiment that feeds no figure or
number in the paper: the collapse null, the sexual-vs-asexual lineage, the
NK speciation variant, the 0.5B single-seed LLM prototypes, the compose and
society experiments with their calibration and pilot runs, and their
configs, runners, tests, figure scripts and PBS jobs. Their result bundles
are moved to results/_archive/ (ignored) so the parquets stay on disk.

Also: plot_llm_speciation reads the s{seed}/ layout; the mating-breadth
plot writes under its bundle name; Makefile targets reduced to the kept
experiments; REPRODUCING.md and README point to dev for the rest.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y64o8FKP7rCuXzC48pxpMm
This commit is contained in:
Giorgio Gilestro 2026-09-13 17:07:23 +01:00
parent ab3dc10587
commit 6f8cef1ac5
292 changed files with 26 additions and 15590 deletions

Binary file not shown.

Binary file not shown.

View file

@ -1,32 +0,0 @@
# E1 — Distillation without grounding collapses, tail-first
**Claim tested:** if a model is trained only on the previous model's output, generation after
generation, does it lose knowledge — and does the *rare* knowledge go first?
**Setup (Layer 1, pure math).** A "population" of `K = 500` items with a fixed true frequency
`p*` shaped like a Zipf curve (a few common items, a long tail of rare ones). Each generation we
draw `n = 100` samples from the current model and refit — **no real data is ever added** (`g = 0`).
Run for 600 generations, averaged over 100 independent repeats.
### Symbols
- **`p*`** — the true frequencies (fixed reality). **`p_t`** — the model's frequencies at generation *t* (drifts).
- **`H`** heterozygosity = diversity (1 = everything equally likely, 0 = one item left). **`H*`** = diversity of the truth.
- **forward-KL** `D(p*‖p_t)` — how far the model has drifted from truth (0 = perfect, grows without bound as the tail is forgotten).
- **support** = how many items still have any probability. **head/tail** = common/rare items.
### The three panels
1. **Geometric decay.** Blue = the simulated diversity `H`; black dashed = the exact textbook law
`H₀·(1 1/n)^t`. They sit on top of each other — the loss of diversity is *exactly* the
population-genetics drift law, not an approximation. (This is the validation gate: if these two
curves disagreed, the simulator would be wrong.)
2. **Tail dies first** (log axis). Red = fraction of *rare* (tail) items still alive; green =
fraction of *common* (head) items still alive. The red curve plunges far faster — rare knowledge
is lost roughly an order of magnitude sooner than common knowledge.
3. **Collapse.** Purple (left axis, log) = number of distinct items surviving, falling from 500
toward ~1 (everything collapses onto a single dominant item). Orange (right axis) = forward-KL to
truth, diverging as the tail vanishes.
### Takeaway
Unchecked model-on-model training is a ratchet: diversity decays on a precise mathematical schedule,
and the rare tail is destroyed first. **Falsifier (not triggered):** if `H` had stayed flat, the
whole thesis would fail. It didn't.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

View file

@ -1,14 +0,0 @@
{
"experiment": "E1_reproduce_collapse",
"master_seed": 20260704,
"git_commit": "a6eb9b75124779375fa1a0b3a64115ecd705b218",
"python": "3.14.5",
"libraries": {
"numpy": "2.5.0",
"scipy": "1.18.0",
"pandas": "3.0.3",
"pyarrow": "24.0.0"
},
"rows": 60100,
"results_sha256": "038bf62046d593a61d0177f988f76897c9af5d9c370dc4d19bf37dc68d47afde"
}

View file

@ -1,68 +0,0 @@
experiment: E1_reproduce_collapse
seed: 20260704
n_replicates: 100
source_config:
experiment: E1_reproduce_collapse
seed: 20260704
n_replicates: 100
generations: 600
truth:
K: 500
R: 1
tail: zipf
zipf_s: 1.1
tail_frac: 0.5
tail_threshold: 0.001
init: truth
dynamics:
n: 100
teachers:
K_T: 1
rho: 0.0
q: 1.0
grounding:
m: 0
policy: proportional
selection:
mode: none
novelty_alpha: 0.0
remint:
enabled: false
period: null
H_gate: null
metrics:
kl_floor: 1.0e-09
support_eps: 1.0e-09
output:
dir: results/collapse_null
grid:
- label: {}
lineage_cfg:
truth:
K: 500
R: 1
tail: zipf
zipf_s: 1.1
tail_frac: 0.5
tail_threshold: 0.001
init: truth
dynamics:
n: 100
teachers:
K_T: 1
rho: 0.0
q: 1.0
grounding:
m: 0
policy: proportional
selection:
mode: none
novelty_alpha: 0.0
remint:
enabled: false
period: null
H_gate: null
generations: 600
metrics:
kl_floor: 1.0e-09
support_eps: 1.0e-09

View file

@ -15,7 +15,7 @@ simulator, whose separate RNG would break that guarantee). Two landscapes:
set of loci (each parent adaptive, neither carrying an incompatibility); a fraction `ρ` of
cross-lineage locus pairs are BDMIs (penalty `s`), biting only when a hybrid inherits *both* derived
alleles. Sweep divergence `d` (substitutions) for several `ρ`; `L=20`, 15 reps.
- **NK** (`configs/inheritance/speciation_bdm_nk.yaml`): parents are *local optima* (hill-climbed) on a Kauffman NK
- **NK** (`speciation_bdm_nk`, on the dev branch): parents are *local optima* (hill-climbed) on a Kauffman NK
landscape; sweep ruggedness `K`. The emergent version.
### Results

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 KiB

After

Width:  |  Height:  |  Size: 171 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 368 KiB

View file

@ -1,40 +0,0 @@
{
"experiment": "llm_compose",
"master_seed": 1,
"git_commit": "e4804adabcdce6d928c5e6b1e85429b2a6acf2fe",
"python": "3.14.7",
"libraries": {
"numpy": "2.5.0",
"scipy": "1.18.0",
"pandas": "3.0.3",
"pyarrow": "24.0.0",
"torch": "2.12.1",
"transformers": "5.16.1",
"peft": "0.20.0"
},
"rows": 366,
"results_sha256": "92ea7965e13685f77a79b85393685a1e7b53e8c821778f2368c4a68097292818",
"layer": "2",
"tier": "llm",
"base_model": "Qwen/Qwen2.5-1.5B",
"hard": false,
"compose": {
"arms": [
"dry",
"grounded",
"dry_cat"
],
"generations": 6,
"g": 0.1,
"k_inherit": 300,
"conf_gate": 0.85,
"epochs": 3,
"n_hard": 150,
"n_gsm8k": 150,
"n_mbpp": 100,
"lora": {
"r": 16,
"alpha": 32
}
}
}

View file

@ -1,49 +0,0 @@
experiment: llm_compose
seed: 1
n_replicates: 1
source_config:
experiment: llm_compose
kind: llm_compose
base_model: Qwen/Qwen2.5-1.5B
seed: 1
generations: 6
arms:
- dry
- grounded
- dry_cat
g: 0.1
n_hard: 150
n_gsm8k: 150
n_mbpp: 100
n_probe: 60
k_inherit: 300
epochs: 3
conf_gate: 0.85
spec_train: 1200
spec_epochs: 3
max_new_tokens: 320
batch_size: 16
score_batch_size: 4
train_batch_size: 2
train_max_len: 448
resume: true
lora:
r: 16
alpha: 32
output:
dir: results/llm_compose/s1
arm_ops:
dry: linear
grounded: linear
dry_cat: cat
n_hard_val: 60
merge_weights:
- - 0.5
- 0.5
- - 0.3
- 0.7
- - 0.2
- 0.8
- - 0.1
- 0.9
n_replicates: 1

View file

@ -1,38 +0,0 @@
{
"experiment": "llm_compose",
"master_seed": 2,
"git_commit": null,
"python": "3.11.7",
"libraries": {
"numpy": "2.4.6",
"scipy": "1.17.1",
"pandas": "3.0.3",
"pyarrow": "24.0.0",
"torch": "2.12.1",
"transformers": "5.16.1",
"peft": "0.20.0"
},
"rows": 122,
"results_sha256": "fb747c86d164496f423a83ba09bbdf79eb85723fefbc3c2ccd28bba7c1f7086e",
"layer": "2",
"tier": "llm",
"base_model": "Qwen/Qwen2.5-1.5B",
"hard": false,
"compose": {
"arms": [
"dry"
],
"generations": 6,
"g": 0.1,
"k_inherit": 300,
"conf_gate": 0.85,
"epochs": 3,
"n_hard": 150,
"n_gsm8k": 150,
"n_mbpp": 100,
"lora": {
"r": 16,
"alpha": 32
}
}
}

View file

@ -1,47 +0,0 @@
experiment: llm_compose
seed: 2
n_replicates: 1
source_config:
experiment: llm_compose
kind: llm_compose
base_model: Qwen/Qwen2.5-1.5B
seed: 2
generations: 6
arms:
- dry
g: 0.1
n_hard: 150
n_gsm8k: 150
n_mbpp: 100
n_probe: 60
k_inherit: 300
epochs: 3
conf_gate: 0.85
spec_train: 1200
spec_epochs: 3
max_new_tokens: 320
batch_size: 32
score_batch_size: 16
train_batch_size: 2
train_max_len: 448
resume: true
lora:
r: 16
alpha: 32
output:
dir: results/llm_compose/s2_dry
arm_ops:
dry: linear
grounded: linear
dry_cat: cat
n_hard_val: 60
merge_weights:
- - 0.5
- 0.5
- - 0.3
- 0.7
- - 0.2
- 0.8
- - 0.1
- 0.9
n_replicates: 1

View file

@ -1,38 +0,0 @@
{
"experiment": "llm_compose",
"master_seed": 2,
"git_commit": null,
"python": "3.11.7",
"libraries": {
"numpy": "2.4.6",
"scipy": "1.17.1",
"pandas": "3.0.3",
"pyarrow": "24.0.0",
"torch": "2.12.1",
"transformers": "5.16.1",
"peft": "0.20.0"
},
"rows": 122,
"results_sha256": "e0a1c452c8ae05f0830f97c3faf53530bda42e6420824dfb9e15a7b09399f11d",
"layer": "2",
"tier": "llm",
"base_model": "Qwen/Qwen2.5-1.5B",
"hard": false,
"compose": {
"arms": [
"dry_cat"
],
"generations": 6,
"g": 0.1,
"k_inherit": 300,
"conf_gate": 0.85,
"epochs": 3,
"n_hard": 150,
"n_gsm8k": 150,
"n_mbpp": 100,
"lora": {
"r": 16,
"alpha": 32
}
}
}

View file

@ -1,47 +0,0 @@
experiment: llm_compose
seed: 2
n_replicates: 1
source_config:
experiment: llm_compose
kind: llm_compose
base_model: Qwen/Qwen2.5-1.5B
seed: 2
generations: 6
arms:
- dry_cat
g: 0.1
n_hard: 150
n_gsm8k: 150
n_mbpp: 100
n_probe: 60
k_inherit: 300
epochs: 3
conf_gate: 0.85
spec_train: 1200
spec_epochs: 3
max_new_tokens: 320
batch_size: 32
score_batch_size: 16
train_batch_size: 2
train_max_len: 448
resume: true
lora:
r: 16
alpha: 32
output:
dir: results/llm_compose/s2_dry_cat
arm_ops:
dry: linear
grounded: linear
dry_cat: cat
n_hard_val: 60
merge_weights:
- - 0.5
- 0.5
- - 0.3
- 0.7
- - 0.2
- 0.8
- - 0.1
- 0.9
n_replicates: 1

View file

@ -1,38 +0,0 @@
{
"experiment": "llm_compose",
"master_seed": 2,
"git_commit": null,
"python": "3.11.7",
"libraries": {
"numpy": "2.4.6",
"scipy": "1.17.1",
"pandas": "3.0.3",
"pyarrow": "24.0.0",
"torch": "2.12.1",
"transformers": "5.16.1",
"peft": "0.20.0"
},
"rows": 122,
"results_sha256": "a4355506daf356e216915d10e451ba314667e5e50e4b2e96ab38f0985d20e01b",
"layer": "2",
"tier": "llm",
"base_model": "Qwen/Qwen2.5-1.5B",
"hard": false,
"compose": {
"arms": [
"grounded"
],
"generations": 6,
"g": 0.1,
"k_inherit": 300,
"conf_gate": 0.85,
"epochs": 3,
"n_hard": 150,
"n_gsm8k": 150,
"n_mbpp": 100,
"lora": {
"r": 16,
"alpha": 32
}
}
}

View file

@ -1,47 +0,0 @@
experiment: llm_compose
seed: 2
n_replicates: 1
source_config:
experiment: llm_compose
kind: llm_compose
base_model: Qwen/Qwen2.5-1.5B
seed: 2
generations: 6
arms:
- grounded
g: 0.1
n_hard: 150
n_gsm8k: 150
n_mbpp: 100
n_probe: 60
k_inherit: 300
epochs: 3
conf_gate: 0.85
spec_train: 1200
spec_epochs: 3
max_new_tokens: 320
batch_size: 32
score_batch_size: 16
train_batch_size: 2
train_max_len: 448
resume: true
lora:
r: 16
alpha: 32
output:
dir: results/llm_compose/s2_grounded
arm_ops:
dry: linear
grounded: linear
dry_cat: cat
n_hard_val: 60
merge_weights:
- - 0.5
- 0.5
- - 0.3
- 0.7
- - 0.2
- 0.8
- - 0.1
- 0.9
n_replicates: 1

View file

@ -1,38 +0,0 @@
{
"experiment": "llm_compose",
"master_seed": 3,
"git_commit": null,
"python": "3.11.7",
"libraries": {
"numpy": "2.4.6",
"scipy": "1.17.1",
"pandas": "3.0.3",
"pyarrow": "24.0.0",
"torch": "2.12.1",
"transformers": "5.16.1",
"peft": "0.20.0"
},
"rows": 122,
"results_sha256": "d9be9eacf3014ab9341090c18961297d97a0e68d1ca20b229a7fb0713c3b5ff5",
"layer": "2",
"tier": "llm",
"base_model": "Qwen/Qwen2.5-1.5B",
"hard": false,
"compose": {
"arms": [
"dry"
],
"generations": 6,
"g": 0.1,
"k_inherit": 300,
"conf_gate": 0.85,
"epochs": 3,
"n_hard": 150,
"n_gsm8k": 150,
"n_mbpp": 100,
"lora": {
"r": 16,
"alpha": 32
}
}
}

View file

@ -1,47 +0,0 @@
experiment: llm_compose
seed: 3
n_replicates: 1
source_config:
experiment: llm_compose
kind: llm_compose
base_model: Qwen/Qwen2.5-1.5B
seed: 3
generations: 6
arms:
- dry
g: 0.1
n_hard: 150
n_gsm8k: 150
n_mbpp: 100
n_probe: 60
k_inherit: 300
epochs: 3
conf_gate: 0.85
spec_train: 1200
spec_epochs: 3
max_new_tokens: 320
batch_size: 32
score_batch_size: 16
train_batch_size: 2
train_max_len: 448
resume: true
lora:
r: 16
alpha: 32
output:
dir: results/llm_compose/s3_dry
arm_ops:
dry: linear
grounded: linear
dry_cat: cat
n_hard_val: 60
merge_weights:
- - 0.5
- 0.5
- - 0.3
- 0.7
- - 0.2
- 0.8
- - 0.1
- 0.9
n_replicates: 1

View file

@ -1,38 +0,0 @@
{
"experiment": "llm_compose",
"master_seed": 3,
"git_commit": null,
"python": "3.11.7",
"libraries": {
"numpy": "2.4.6",
"scipy": "1.17.1",
"pandas": "3.0.3",
"pyarrow": "24.0.0",
"torch": "2.12.1",
"transformers": "5.16.1",
"peft": "0.20.0"
},
"rows": 122,
"results_sha256": "c9d6d1408cb87b1bf71b3f85a5f58384fe5f3678350d10a32ed5e639cb63d48d",
"layer": "2",
"tier": "llm",
"base_model": "Qwen/Qwen2.5-1.5B",
"hard": false,
"compose": {
"arms": [
"dry_cat"
],
"generations": 6,
"g": 0.1,
"k_inherit": 300,
"conf_gate": 0.85,
"epochs": 3,
"n_hard": 150,
"n_gsm8k": 150,
"n_mbpp": 100,
"lora": {
"r": 16,
"alpha": 32
}
}
}

View file

@ -1,47 +0,0 @@
experiment: llm_compose
seed: 3
n_replicates: 1
source_config:
experiment: llm_compose
kind: llm_compose
base_model: Qwen/Qwen2.5-1.5B
seed: 3
generations: 6
arms:
- dry_cat
g: 0.1
n_hard: 150
n_gsm8k: 150
n_mbpp: 100
n_probe: 60
k_inherit: 300
epochs: 3
conf_gate: 0.85
spec_train: 1200
spec_epochs: 3
max_new_tokens: 320
batch_size: 32
score_batch_size: 16
train_batch_size: 2
train_max_len: 448
resume: true
lora:
r: 16
alpha: 32
output:
dir: results/llm_compose/s3_dry_cat
arm_ops:
dry: linear
grounded: linear
dry_cat: cat
n_hard_val: 60
merge_weights:
- - 0.5
- 0.5
- - 0.3
- 0.7
- - 0.2
- 0.8
- - 0.1
- 0.9
n_replicates: 1

View file

@ -1,38 +0,0 @@
{
"experiment": "llm_compose",
"master_seed": 3,
"git_commit": null,
"python": "3.11.7",
"libraries": {
"numpy": "2.4.6",
"scipy": "1.17.1",
"pandas": "3.0.3",
"pyarrow": "24.0.0",
"torch": "2.12.1",
"transformers": "5.16.1",
"peft": "0.20.0"
},
"rows": 122,
"results_sha256": "52d7b0c456160ea828e7adc55341ad13358c214715b3be80a31834bb7ef4ea6e",
"layer": "2",
"tier": "llm",
"base_model": "Qwen/Qwen2.5-1.5B",
"hard": false,
"compose": {
"arms": [
"grounded"
],
"generations": 6,
"g": 0.1,
"k_inherit": 300,
"conf_gate": 0.85,
"epochs": 3,
"n_hard": 150,
"n_gsm8k": 150,
"n_mbpp": 100,
"lora": {
"r": 16,
"alpha": 32
}
}
}

View file

@ -1,47 +0,0 @@
experiment: llm_compose
seed: 3
n_replicates: 1
source_config:
experiment: llm_compose
kind: llm_compose
base_model: Qwen/Qwen2.5-1.5B
seed: 3
generations: 6
arms:
- grounded
g: 0.1
n_hard: 150
n_gsm8k: 150
n_mbpp: 100
n_probe: 60
k_inherit: 300
epochs: 3
conf_gate: 0.85
spec_train: 1200
spec_epochs: 3
max_new_tokens: 320
batch_size: 32
score_batch_size: 16
train_batch_size: 2
train_max_len: 448
resume: true
lora:
r: 16
alpha: 32
output:
dir: results/llm_compose/s3_grounded
arm_ops:
dry: linear
grounded: linear
dry_cat: cat
n_hard_val: 60
merge_weights:
- - 0.5
- 0.5
- - 0.3
- 0.7
- - 0.2
- 0.8
- - 0.1
- 0.9
n_replicates: 1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 KiB

View file

@ -1,49 +0,0 @@
experiment: llm_compose
seed: 1
n_replicates: 1
source_config:
experiment: llm_compose
kind: llm_compose
base_model: Qwen/Qwen2.5-1.5B
seed: 1
generations: 6
arms:
- dry
- grounded
- dry_cat
g: 0.1
n_hard: 150
n_gsm8k: 150
n_mbpp: 100
n_probe: 60
k_inherit: 300
epochs: 3
conf_gate: 0.85
spec_train: 1200
spec_epochs: 3
max_new_tokens: 320
batch_size: 16
score_batch_size: 4
train_batch_size: 2
train_max_len: 448
resume: true
lora:
r: 16
alpha: 32
output:
dir: results/llm_compose/s1
arm_ops:
dry: linear
grounded: linear
dry_cat: cat
n_hard_val: 60
merge_weights:
- - 0.5
- 0.5
- - 0.3
- 0.7
- - 0.2
- 0.8
- - 0.1
- 0.9
n_replicates: 1

View file

@ -1,47 +0,0 @@
experiment: llm_compose
seed: 2
n_replicates: 1
source_config:
experiment: llm_compose
kind: llm_compose
base_model: Qwen/Qwen2.5-1.5B
seed: 2
generations: 6
arms:
- dry
g: 0.1
n_hard: 150
n_gsm8k: 150
n_mbpp: 100
n_probe: 60
k_inherit: 300
epochs: 3
conf_gate: 0.85
spec_train: 1200
spec_epochs: 3
max_new_tokens: 320
batch_size: 32
score_batch_size: 16
train_batch_size: 2
train_max_len: 448
resume: true
lora:
r: 16
alpha: 32
output:
dir: results/llm_compose/s2_dry
arm_ops:
dry: linear
grounded: linear
dry_cat: cat
n_hard_val: 60
merge_weights:
- - 0.5
- 0.5
- - 0.3
- 0.7
- - 0.2
- 0.8
- - 0.1
- 0.9
n_replicates: 1

View file

@ -1,47 +0,0 @@
experiment: llm_compose
seed: 2
n_replicates: 1
source_config:
experiment: llm_compose
kind: llm_compose
base_model: Qwen/Qwen2.5-1.5B
seed: 2
generations: 6
arms:
- dry_cat
g: 0.1
n_hard: 150
n_gsm8k: 150
n_mbpp: 100
n_probe: 60
k_inherit: 300
epochs: 3
conf_gate: 0.85
spec_train: 1200
spec_epochs: 3
max_new_tokens: 320
batch_size: 32
score_batch_size: 16
train_batch_size: 2
train_max_len: 448
resume: true
lora:
r: 16
alpha: 32
output:
dir: results/llm_compose/s2_dry_cat
arm_ops:
dry: linear
grounded: linear
dry_cat: cat
n_hard_val: 60
merge_weights:
- - 0.5
- 0.5
- - 0.3
- 0.7
- - 0.2
- 0.8
- - 0.1
- 0.9
n_replicates: 1

View file

@ -1,47 +0,0 @@
experiment: llm_compose
seed: 2
n_replicates: 1
source_config:
experiment: llm_compose
kind: llm_compose
base_model: Qwen/Qwen2.5-1.5B
seed: 2
generations: 6
arms:
- grounded
g: 0.1
n_hard: 150
n_gsm8k: 150
n_mbpp: 100
n_probe: 60
k_inherit: 300
epochs: 3
conf_gate: 0.85
spec_train: 1200
spec_epochs: 3
max_new_tokens: 320
batch_size: 32
score_batch_size: 16
train_batch_size: 2
train_max_len: 448
resume: true
lora:
r: 16
alpha: 32
output:
dir: results/llm_compose/s2_grounded
arm_ops:
dry: linear
grounded: linear
dry_cat: cat
n_hard_val: 60
merge_weights:
- - 0.5
- 0.5
- - 0.3
- 0.7
- - 0.2
- 0.8
- - 0.1
- 0.9
n_replicates: 1

View file

@ -1,47 +0,0 @@
experiment: llm_compose
seed: 3
n_replicates: 1
source_config:
experiment: llm_compose
kind: llm_compose
base_model: Qwen/Qwen2.5-1.5B
seed: 3
generations: 6
arms:
- dry
g: 0.1
n_hard: 150
n_gsm8k: 150
n_mbpp: 100
n_probe: 60
k_inherit: 300
epochs: 3
conf_gate: 0.85
spec_train: 1200
spec_epochs: 3
max_new_tokens: 320
batch_size: 32
score_batch_size: 16
train_batch_size: 2
train_max_len: 448
resume: true
lora:
r: 16
alpha: 32
output:
dir: results/llm_compose/s3_dry
arm_ops:
dry: linear
grounded: linear
dry_cat: cat
n_hard_val: 60
merge_weights:
- - 0.5
- 0.5
- - 0.3
- 0.7
- - 0.2
- 0.8
- - 0.1
- 0.9
n_replicates: 1

View file

@ -1,47 +0,0 @@
experiment: llm_compose
seed: 3
n_replicates: 1
source_config:
experiment: llm_compose
kind: llm_compose
base_model: Qwen/Qwen2.5-1.5B
seed: 3
generations: 6
arms:
- dry_cat
g: 0.1
n_hard: 150
n_gsm8k: 150
n_mbpp: 100
n_probe: 60
k_inherit: 300
epochs: 3
conf_gate: 0.85
spec_train: 1200
spec_epochs: 3
max_new_tokens: 320
batch_size: 32
score_batch_size: 16
train_batch_size: 2
train_max_len: 448
resume: true
lora:
r: 16
alpha: 32
output:
dir: results/llm_compose/s3_dry_cat
arm_ops:
dry: linear
grounded: linear
dry_cat: cat
n_hard_val: 60
merge_weights:
- - 0.5
- 0.5
- - 0.3
- 0.7
- - 0.2
- 0.8
- - 0.1
- 0.9
n_replicates: 1

View file

@ -1,47 +0,0 @@
experiment: llm_compose
seed: 3
n_replicates: 1
source_config:
experiment: llm_compose
kind: llm_compose
base_model: Qwen/Qwen2.5-1.5B
seed: 3
generations: 6
arms:
- grounded
g: 0.1
n_hard: 150
n_gsm8k: 150
n_mbpp: 100
n_probe: 60
k_inherit: 300
epochs: 3
conf_gate: 0.85
spec_train: 1200
spec_epochs: 3
max_new_tokens: 320
batch_size: 32
score_batch_size: 16
train_batch_size: 2
train_max_len: 448
resume: true
lora:
r: 16
alpha: 32
output:
dir: results/llm_compose/s3_grounded
arm_ops:
dry: linear
grounded: linear
dry_cat: cat
n_hard_val: 60
merge_weights:
- - 0.5
- 0.5
- - 0.3
- 0.7
- - 0.2
- 0.8
- - 0.1
- 0.9
n_replicates: 1

View file

@ -1,38 +0,0 @@
{
"experiment": "llm_compose_gate",
"master_seed": 1,
"git_commit": "e4804adabcdce6d928c5e6b1e85429b2a6acf2fe",
"python": "3.14.7",
"libraries": {
"numpy": "2.5.0",
"scipy": "1.18.0",
"pandas": "3.0.3",
"pyarrow": "24.0.0",
"torch": "2.12.1",
"transformers": "5.16.1",
"peft": "0.20.0"
},
"rows": 13,
"results_sha256": "3c22a286420de512c6b3a114bb8d928072bc8750f861405252c8e563405d7b39",
"layer": "2",
"tier": "llm",
"base_model": "Qwen/Qwen2.5-1.5B",
"hard": false,
"compose": {
"arms": [
"dry"
],
"generations": 0,
"g": 0.1,
"k_inherit": 300,
"conf_gate": 0.85,
"epochs": 3,
"n_hard": 150,
"n_gsm8k": 100,
"n_mbpp": 80,
"lora": {
"r": 16,
"alpha": 32
}
}
}

View file

@ -1,32 +0,0 @@
experiment: llm_compose_gate
seed: 1
n_replicates: 1
source_config:
experiment: llm_compose_gate
kind: llm_compose
base_model: Qwen/Qwen2.5-1.5B
seed: 1
generations: 0
arms:
- dry
n_hard: 150
n_gsm8k: 100
n_mbpp: 80
n_probe: 40
k_inherit: 300
epochs: 3
conf_gate: 0.85
g: 0.1
spec_train: 1200
spec_epochs: 3
max_new_tokens: 320
batch_size: 16
score_batch_size: 8
train_batch_size: 2
train_max_len: 448
lora:
r: 16
alpha: 32
output:
dir: results/llm_compose_gate
n_replicates: 1

View file

@ -1,38 +0,0 @@
{
"experiment": "llm_compose_gate",
"master_seed": 1,
"git_commit": "e4804adabcdce6d928c5e6b1e85429b2a6acf2fe",
"python": "3.14.7",
"libraries": {
"numpy": "2.5.0",
"scipy": "1.18.0",
"pandas": "3.0.3",
"pyarrow": "24.0.0",
"torch": "2.12.1",
"transformers": "5.16.1",
"peft": "0.20.0"
},
"rows": 14,
"results_sha256": "cc1c6f5ac13eda7c26eecb563b3fcd22b034027dfec432e80cb071a71bf2a7c6",
"layer": "2",
"tier": "llm",
"base_model": "Qwen/Qwen2.5-1.5B",
"hard": false,
"compose": {
"arms": [
"dry"
],
"generations": 0,
"g": 0.1,
"k_inherit": 300,
"conf_gate": 0.85,
"epochs": 3,
"n_hard": 120,
"n_gsm8k": 100,
"n_mbpp": 80,
"lora": {
"r": 16,
"alpha": 32
}
}
}

View file

@ -1,41 +0,0 @@
experiment: llm_compose_gate
seed: 1
n_replicates: 1
source_config:
experiment: llm_compose_gate
kind: llm_compose
base_model: Qwen/Qwen2.5-1.5B
seed: 1
generations: 0
arms:
- dry
target: math500
n_hard: 120
n_hard_val: 50
merge_weights:
- - 0.5
- 0.5
- - 0.3
- 0.7
- - 0.2
- 0.8
n_gsm8k: 100
n_mbpp: 80
n_probe: 40
k_inherit: 300
epochs: 3
conf_gate: 0.85
g: 0.1
spec_train: 1200
spec_epochs: 3
max_new_tokens: 320
batch_size: 16
score_batch_size: 8
train_batch_size: 2
train_max_len: 448
lora:
r: 16
alpha: 32
output:
dir: results/llm_compose_gate_math500
n_replicates: 1

View file

@ -1,48 +0,0 @@
{
"experiment": "llm_curriculum_g2",
"master_seed": 1,
"git_commit": "e4804adabcdce6d928c5e6b1e85429b2a6acf2fe",
"python": "3.14.7",
"libraries": {
"numpy": "2.5.0",
"scipy": "1.18.0",
"pandas": "3.0.3",
"pyarrow": "24.0.0",
"torch": "2.12.1",
"transformers": "5.16.1",
"peft": "0.20.0"
},
"rows": 136,
"results_sha256": "0ead4632190886ae91cefc2d95b8318a4330adc42d25c7cc8e5e5e422ea08275",
"layer": "2",
"tier": "llm",
"base_model": "Qwen/Qwen2.5-1.5B",
"hard": false,
"curriculum": {
"families": [
"strings",
"setops",
"numtheory",
"mixedtoken",
"digits",
"alphabet",
"prime",
"wordlen",
"roman"
],
"lineages": 1,
"generations": 9,
"arms": [
"isolated"
],
"baselines": [],
"n_new": 300,
"n_replay": 0,
"operator": null,
"ancestor_depth": null,
"lora": {
"r": 16,
"alpha": 32
}
}
}

View file

@ -1,39 +0,0 @@
experiment: llm_curriculum_g2
seed: 1
n_replicates: 1
source_config:
experiment: llm_curriculum_g2
kind: llm_curriculum
base_model: Qwen/Qwen2.5-1.5B
seed: 1
families:
- strings
- setops
- numtheory
- mixedtoken
- digits
- alphabet
- prime
- wordlen
- roman
lineages: 1
generations: 9
arms:
- isolated
baselines: []
n_new: 300
n_replay: 0
n_test: 60
n_val: 30
epochs: 3
lr: 0.0001
max_new_tokens: 32
batch_size: 32
train_batch_size: 2
train_max_len: 448
lora:
r: 16
alpha: 32
output:
dir: results/llm_curriculum_g2
n_replicates: 1

View file

@ -1,43 +0,0 @@
{
"experiment": "llm_curriculum_gate",
"master_seed": 1,
"git_commit": "e4804adabcdce6d928c5e6b1e85429b2a6acf2fe",
"python": "3.14.7",
"libraries": {
"numpy": "2.5.0",
"scipy": "1.18.0",
"pandas": "3.0.3",
"pyarrow": "24.0.0",
"torch": "2.12.1",
"transformers": "5.16.1",
"peft": "0.20.0"
},
"rows": 144,
"results_sha256": "c910942d8071f802cca5c01a8d50710622b4c921e4b8f5dfce67f19674016e8f",
"layer": "2",
"tier": "llm",
"base_model": "Qwen/Qwen2.5-1.5B",
"hard": false,
"curriculum": {
"families": [
"strings",
"setops",
"numtheory"
],
"lineages": 3,
"generations": 3,
"arms": [
"isolated",
"society"
],
"baselines": [],
"n_new": 300,
"n_replay": 120,
"operator": "linear",
"ancestor_depth": null,
"lora": {
"r": 16,
"alpha": 32
}
}
}

View file

@ -1,42 +0,0 @@
experiment: llm_curriculum_gate
seed: 1
n_replicates: 1
source_config:
experiment: llm_curriculum_gate
kind: llm_curriculum
base_model: Qwen/Qwen2.5-1.5B
seed: 1
families:
- strings
- setops
- numtheory
lineages: 3
generations: 3
arms:
- isolated
- society
baselines: []
n_new: 300
n_replay: 120
n_test: 60
n_val: 30
epochs: 3
lr: 0.0001
operator: linear
merge_weights:
- - 0.5
- 0.5
- - 0.3
- 0.7
- - 0.7
- 0.3
max_new_tokens: 32
batch_size: 32
train_batch_size: 2
train_max_len: 448
lora:
r: 16
alpha: 32
output:
dir: results/llm_curriculum_gate
n_replicates: 1

View file

@ -1,50 +0,0 @@
{
"experiment": "llm_curriculum_v5_calib",
"master_seed": 1,
"git_commit": "e4804adabcdce6d928c5e6b1e85429b2a6acf2fe",
"python": "3.14.7",
"libraries": {
"numpy": "2.5.0",
"scipy": "1.18.0",
"pandas": "3.0.3",
"pyarrow": "24.0.0",
"torch": "2.12.1",
"transformers": "5.16.1",
"peft": "0.20.0"
},
"rows": 188,
"results_sha256": "a585ee6a11a47ab65732abcd5e263b885a6cfa74fbcae0bec6b5c71bf2827fd8",
"layer": "2",
"tier": "llm",
"base_model": "Qwen/Qwen2.5-1.5B",
"hard": false,
"curriculum": {
"families": [
"gsm8k",
"mbpp",
"boolq",
"mnli",
"sst2",
"csqa",
"arc",
"winogrande",
"squad",
"nq_open",
"hellaswag"
],
"lineages": 11,
"generations": 1,
"arms": [
"isolated"
],
"baselines": [],
"n_new": 300,
"n_replay": 0,
"operator": null,
"ancestor_depth": null,
"lora": {
"r": 16,
"alpha": 32
}
}
}

View file

@ -1,41 +0,0 @@
experiment: llm_curriculum_v5_calib
seed: 1
n_replicates: 1
source_config:
experiment: llm_curriculum_v5_calib
kind: llm_curriculum
base_model: Qwen/Qwen2.5-1.5B
seed: 1
families:
- gsm8k
- mbpp
- boolq
- mnli
- sst2
- csqa
- arc
- winogrande
- squad
- nq_open
- hellaswag
lineages: 11
generations: 1
arms:
- isolated
baselines: []
n_new: 300
n_replay: 0
n_test: 60
n_val: 20
epochs: 3
lr: 0.0001
max_new_tokens: 48
batch_size: 24
train_batch_size: 2
train_max_len: 512
lora:
r: 16
alpha: 32
output:
dir: results/llm_curriculum_v5_calib
n_replicates: 1

View file

@ -1,45 +0,0 @@
{
"experiment": "llm_curriculum_v5_calib_b",
"master_seed": 1,
"git_commit": "e4804adabcdce6d928c5e6b1e85429b2a6acf2fe",
"python": "3.14.7",
"libraries": {
"numpy": "2.5.0",
"scipy": "1.18.0",
"pandas": "3.0.3",
"pyarrow": "24.0.0",
"torch": "2.12.1",
"transformers": "5.16.1",
"peft": "0.20.0"
},
"rows": 73,
"results_sha256": "dfd9abe18530d6999399962bce0347f35e6e9151b09e87664ce9c94eddcbd0d7",
"layer": "2",
"tier": "llm",
"base_model": "Qwen/Qwen2.5-1.5B",
"hard": false,
"curriculum": {
"families": [
"gsm8k",
"mbpp",
"sst2",
"csqa",
"winogrande",
"nq_open"
],
"lineages": 6,
"generations": 1,
"arms": [
"isolated"
],
"baselines": [],
"n_new": 1200,
"n_replay": 0,
"operator": null,
"ancestor_depth": null,
"lora": {
"r": 16,
"alpha": 32
}
}
}

View file

@ -1,36 +0,0 @@
experiment: llm_curriculum_v5_calib_b
seed: 1
n_replicates: 1
source_config:
experiment: llm_curriculum_v5_calib_b
kind: llm_curriculum
base_model: Qwen/Qwen2.5-1.5B
seed: 1
families:
- gsm8k
- mbpp
- sst2
- csqa
- winogrande
- nq_open
lineages: 6
generations: 1
arms:
- isolated
baselines: []
n_new: 1200
n_replay: 0
n_test: 60
n_val: 20
epochs: 3
lr: 0.0001
max_new_tokens: 48
batch_size: 24
train_batch_size: 2
train_max_len: 512
lora:
r: 16
alpha: 32
output:
dir: results/llm_curriculum_v5_calib_b
n_replicates: 1

View file

@ -5,7 +5,7 @@ families, Qwen2.5-1.5B, rank-16 adapters continued from the parent) but every li
mnli, then diverges maximally, then converges, so mean pairwise complementarity by generation is
0.00, 0.67, 0.70, 0.58, 0.33, 0.00 (Latin square: 1.00, 1.00, 0.80, 0.67, 0.33, 0.00). Arms:
`isolated` (never merge) and `society` with `allow_veto: true` (declinable merge). Pre-registered in
`tasks/prereg-llm-society-v4.md` §8h before running. Seed 1 local; seeds 23 on CX3
`tasks/prereg-llm-society-v4.md on the dev branch` §8h before running. Seed 1 local; seeds 23 on CX3
(`hpc/llm_curriculum_controls.pbs`). Layout: `results.parquet` (seed 1), `s2/`, `s3/`.
Why: in the Latin square, complementarity and generation number are collinear, so the veto's

View file

@ -1,45 +0,0 @@
{
"experiment": "llm_curriculum_v5_g2",
"master_seed": 1,
"git_commit": "e4804adabcdce6d928c5e6b1e85429b2a6acf2fe",
"python": "3.14.7",
"libraries": {
"numpy": "2.5.0",
"scipy": "1.18.0",
"pandas": "3.0.3",
"pyarrow": "24.0.0",
"torch": "2.12.1",
"transformers": "5.16.1",
"peft": "0.20.0"
},
"rows": 73,
"results_sha256": "27493600f7086998e911d87c98c5106eacec2092ff7ff9d6912844dd5c8b88bd",
"layer": "2",
"tier": "llm",
"base_model": "Qwen/Qwen2.5-1.5B",
"hard": false,
"curriculum": {
"families": [
"mnli",
"arc",
"hellaswag",
"squad",
"boolq",
"winogrande"
],
"lineages": 1,
"generations": 6,
"arms": [
"isolated"
],
"baselines": [],
"n_new": 300,
"n_replay": 0,
"operator": null,
"ancestor_depth": null,
"lora": {
"r": 16,
"alpha": 32
}
}
}

View file

@ -1,36 +0,0 @@
experiment: llm_curriculum_v5_g2
seed: 1
n_replicates: 1
source_config:
experiment: llm_curriculum_v5_g2
kind: llm_curriculum
base_model: Qwen/Qwen2.5-1.5B
seed: 1
families:
- mnli
- arc
- hellaswag
- squad
- boolq
- winogrande
lineages: 1
generations: 6
arms:
- isolated
baselines: []
n_new: 300
n_replay: 0
n_test: 60
n_val: 20
epochs: 3
lr: 0.0001
max_new_tokens: 48
batch_size: 24
train_batch_size: 2
train_max_len: 512
lora:
r: 16
alpha: 32
output:
dir: results/llm_curriculum_v5_g2
n_replicates: 1

View file

@ -3,7 +3,7 @@
Config `configs/llm/curriculum_v5_stop3.yaml`: the v5 `society` arm (three lineages, six real-dataset
families in a Latin square, Qwen2.5-1.5B base, rank-16 adapters continued from the parent) with
recombination switched off from generation 3 (`merge_until: 3`, no veto). Generations 02 merge
obligately; 35 never. Pre-registered in `tasks/prereg-llm-society-v4.md` §8h before running.
obligately; 35 never. Pre-registered in `tasks/prereg-llm-society-v4.md on the dev branch` §8h before running.
Seed 1 local (batch 24 / train batch 2); seeds 23 on CX3 via `hpc/llm_curriculum_controls.pbs`
(batch 48 / train batch 4), as in the v5 campaign. Layout: `results.parquet` (seed 1), `s2/`, `s3/`.

View file

@ -1,50 +0,0 @@
# llm_directed — directed sex in weight space: breed offspring + select on the verifier (E10, 0.5B)
**Claim tested.** `llm_moe` left a clean gap: fusion can *compose* beyond the parents but the right
blend is unknown and base-dependent, while pure routing is capped at the best parent. E10's answer is
**directed sex** — biology can't preview offspring, an AI can: breed a *population* of recombinant
offspring (the specialists merged at many different weights), score each against the verifier
("reality") on a held-out validation split, and keep the fittest. Selection replaces betting on one
a-priori blend. Two breeding objectives: best validation **overall**, and best validation
**worst-family** (raw capability vs the balanced generalist).
**Setup.** Base **Qwen2.5-0.5B-Instruct**, the three cached `llm_merge` specialists, **16 offspring**
(Dirichlet-weighted merges, concentration 0.5, pinning candidate 0 = uniform soup for reference),
scored on an **80-task/family validation** split, winners reported on a **fresh 100-task/family test**
split (no selection-on-test leakage). Seed 1.
### Results (test accuracy)
| model | lists | strings | arith | overall | worst-family |
|---|---|---|---|---|---|
| best specialist (strings) | 0.08 | 1.00 | 0.80 | 0.63 | 0.08 |
| merge_soup (uniform, candidate 0) | 0.26 | 0.74 | 0.91 | 0.64 | 0.26 |
| **directed_overall** (bred for overall) | 0.17 | 0.99 | 0.92 | **0.69** | 0.17 |
| **directed_balanced** (bred for worst-family) | 0.37 | 0.37 | 0.79 | 0.51 | **0.37** |
### What holds, and the honest cost
- **Offspring selection beats the single a-priori blend — on the objective you breed for.**
`directed_overall` reaches **0.69 overall > soup 0.64** (and > best parent 0.63); `directed_balanced`
reaches **0.37 worst-family > soup 0.26**. Searching the recombination-weight space and letting the
verifier choose beats committing to uniform averaging — the E10 "preview and keep the fittest" claim,
in real weights.
- **Single-objective selection trades off the other axis (honest).** Breeding for *overall* on
lexically-imbalanced families finds a strings+arith-heavy blend that sacrifices the rare `lists`
skill (0.17, below soup's 0.26); breeding for *balance* lifts worst-family to 0.37 but costs overall.
Directed sex gives *control* over what you breed for — it does not hand you both for free.
- **A global blend still trails per-input routing at a weak base.** At 0.5B the best directed *global*
merge (0.69 / 0.43-max) does not beat `llm_moe`'s per-input **routing** (0.74 / 0.43): when the base
is weak, adapting the recombination *per input* beats any one fixed blend, however well selected. So
directed sex over blends beats *averaging*, not *routing* — combining the two (route, then select
among routed+blended offspring) is the natural next operator.
### Takeaway
Directed sex — breed a population, select on the verifier — is confirmed in real LLM weights: it beats
the single uniform soup on whichever objective it optimises, the distinctly-AI advantage (offspring
preview + unbounded candidates) that biology lacks. The honest scope at 0.5B: selection buys one axis
at the other's expense, and a single global blend can't yet beat per-input routing. Whether searching
blends + selection can exceed even the *strong* 7B soup (which routing could not) is answered by
**`results/llm_directed_hpc/`: it can't — directed ≈ soup (0.868 ≈ 0.873)** because the 7B soup already
composes to the ceiling on these near-saturated families, leaving no fitter offspring to breed. So
directed sex helps exactly when the default blend is *suboptimal* (0.5B), and is inert when it is
already near-optimal (7B). **Falsifier (not triggered at 0.5B):** directed offspring ≤ uniform soup on
their bred objective — instead each beat it.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

View file

@ -1,25 +0,0 @@
{
"experiment": "llm_directed",
"master_seed": 1,
"git_commit": "8da0dac00713fb9708804b4696a847a3767758d5",
"python": "3.14.5",
"libraries": {
"numpy": "2.5.0",
"scipy": "1.18.0",
"pandas": "3.0.3",
"pyarrow": "24.0.0",
"torch": "2.12.1",
"transformers": "5.13.0",
"peft": "0.19.1"
},
"rows": 35,
"results_sha256": "143b86833cb320c7c6c693f0b5394f381a94a69277cc31ba5e0977e5ea6ffd78",
"layer": "2",
"tier": "llm",
"base_model": "Qwen/Qwen2.5-0.5B-Instruct",
"directed": {
"n_candidates": 16,
"concentration": 0.5,
"n_val": 80
}
}

View file

@ -1,24 +0,0 @@
experiment: llm_directed
seed: 1
n_replicates: 1
source_config:
experiment: llm_directed
kind: llm_directed
seed: 1
n_replicates: 1
base_model: Qwen/Qwen2.5-0.5B-Instruct
families:
- lists
- strings
- arith
n_train: 700
n_val: 80
n_test: 100
n_candidates: 16
concentration: 0.5
epochs: 3
lora:
r: 16
alpha: 32
output:
dir: results/llm_directed

View file

@ -1,48 +0,0 @@
# llm_merge — recombining specialist LLMs (the first real-LLM prototype; blueprint C2/C4)
**Claim tested.** The first step from toy models toward real language models: does the sexual-
reproduction result — recombining decorrelated specialists yields a model that exceeds/retains what
any single parent has (E8) — appear in real LoRA-adapted LLM weights? This is a **prototype**, run
on a single 16 GB consumer GPU, not the full society.
**Setup.** Base model **Qwen2.5-0.5B-Instruct** (Apache-2.0). Three *disjoint*, procedurally-generated
task families with an **exact-match verifier** (the "reality that says no"): `lists` (list ops),
`strings` (string ops), `arith` (integer arithmetic), deliberately made hard so specialists
decorrelate. One **LoRA specialist** is fine-tuned per family (~90 s for all three), then the base,
each specialist, and two weight-space **merges**`soup` (averaged LoRA deltas) and `ties`
(sign-reconciled union) — are evaluated on a held-out mixed test set. Seed 1, 100 test tasks/family.
### Results (accuracy)
| model | lists | strings | arith | overall | **worst family** |
|---|---|---|---|---|---|
| base | 0.15 | 0.15 | 0.53 | 0.28 | 0.15 |
| spec: lists | 0.43 | 0.16 | 0.71 | 0.43 | 0.16 |
| spec: strings | 0.08 | **1.00** | 0.80 | 0.63 | 0.08 |
| spec: arith | 0.11 | 0.22 | 0.78 | 0.37 | 0.11 |
| **merge: soup** | 0.26 | 0.74 | 0.91 | 0.64 | **0.26** |
| **merge: ties** | 0.23 | 0.71 | 0.90 | 0.61 | **0.23** |
### What holds, and what doesn't (honest)
- **Strong and robust — balance / "retains all specialties".** The merges are the *only* models
competent across **all** families: worst-family ≈ **0.25**, versus **< 0.16** for every single
specialist (the best specialist, strings, is at 0.08 on its worst family). Each specialist spikes on
its own family and is weak elsewhere; the merge is decent everywhere. This is the Fisher-Muller
"a generalist assembled from specialists" signature, in real LLM weights.
- **Marginal / noisy — "exceeds any parent overall".** On *overall* accuracy the merge only *matches*
the best specialist (soup 0.64 vs strings-specialist 0.63; ties 0.61 is slightly below). At this
scale (a 0.5 B model, 3 families, one seed) the strict "offspring exceed every parent" claim is not
cleanly established.
- **The dilution caveat, visible in the flesh.** On `lists`, the lists-specialist alone scores 0.43
but the merge only 0.230.26 — weight-averaging *diluted* that specialist's contribution. This is
exactly Layer-1's "merge, don't average" concern (E4) appearing in real weights; the finer
soup-vs-ties advantage is not resolved at K=3.
### Takeaway
The pipeline runs end-to-end on real LLMs on a 16 GB GPU (specialise → verify → merge → evaluate), and
the **balance/retention** half of the sexual-reproduction claim reproduces clearly. The stronger
"exceeds every parent" claim is marginal at this toy scale and is the thing a larger run should firm
up — more, cleaner-decorrelated families; a bigger base; multiple seeds; and a merge that resists
dilution (e.g. per-task-family weighting, or the offspring-selection of "directed sex"). That scaling
is the natural HPC step; this prototype de-risks the machinery and shows the first sign in real
weights. **Falsifier (partially triggered — reported honestly):** a single specialist matches the
merge on *overall* here; the merge's advantage is currently specific to cross-family *balance*.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

View file

@ -1,20 +0,0 @@
{
"experiment": "llm_merge",
"master_seed": 1,
"git_commit": "6bca1db61e1130ac6899308cc18e520cd9872839",
"python": "3.14.5",
"libraries": {
"numpy": "2.5.0",
"scipy": "1.18.0",
"pandas": "3.0.3",
"pyarrow": "24.0.0",
"torch": "2.12.1",
"transformers": "5.13.0",
"peft": "0.19.1"
},
"rows": 30,
"results_sha256": "bbc13776970c9bc1e2779b1abe0ff5dbee85ef2e1380eba6fe6e5c9ba7e012aa",
"layer": "2",
"tier": "llm",
"base_model": "Qwen/Qwen2.5-0.5B-Instruct"
}

View file

@ -1,24 +0,0 @@
experiment: llm_merge
seed: 1
n_replicates: 1
source_config:
experiment: llm_merge
kind: llm_merge
seed: 1
n_replicates: 1
base_model: Qwen/Qwen2.5-0.5B-Instruct
families:
- lists
- strings
- arith
n_train: 700
n_test: 100
epochs: 3
lora:
r: 16
alpha: 32
merges:
- soup
- ties
output:
dir: results/llm_merge

View file

@ -1,58 +0,0 @@
# llm_moe — union-preserving recombination (route / max-merge) vs fusion (the real-weight E8 *max*)
**Claim tested.** E8 (analytic) found that the recombination *operator* matters: a **union** over
parents (`max`) assembles a child fitter than any parent, while an **average** (`mean`, the model
soup) conserves collapse. `llm_merge` showed fusion (soup/ties, which *average* the LoRA deltas). This
experiment adds the **union** operator to real LLM weights — never average the parents, keep each
specialist intact and **select** the right one per input (a Mixture-of-Experts *router*) or per module
(winner-take-all) — and asks whether union beats fusion, and *where*.
**Setup.** Base **Qwen2.5-0.5B-Instruct**, reusing the three cached LoRA specialists from `llm_merge`
(disjoint families `lists`/`strings`/`arith`, exact-match verifier), 100 test tasks/family, seed 1.
Five recombination operators on the same test set:
- **Fusion** (blend the deltas): `soup` = mean(Δₖ); `ties` = sign-reconciled union.
- **Union** (never average): `route:oracle`/`route:learned` keep all adapters live and route each
prompt to one specialist (MoE); `max-merge` builds one adapter taking, per module, the specialist
with the largest-norm delta. The learned router is training-free — nearest-centroid over the *base*
model's own prompt embeddings; its routing accuracy is reported.
### Results (accuracy)
| operator | lists | strings | arith | overall | worst-family | router |
|---|---|---|---|---|---|---|
| base | 0.15 | 0.15 | 0.53 | 0.28 | 0.15 | — |
| best specialist (strings) | 0.08 | 1.00 | 0.80 | 0.63 | 0.08 | — |
| fuse: soup | 0.26 | 0.74 | 0.91 | 0.64 | 0.26 | — |
| fuse: ties | 0.23 | 0.71 | 0.90 | 0.61 | 0.23 | — |
| **route: oracle** | 0.43 | 1.00 | 0.78 | **0.74** | **0.43** | 1.00 |
| **route: learned** | 0.43 | 1.00 | 0.78 | **0.74** | **0.43** | **1.00** |
| max-merge | 0.18 | 0.34 | 0.87 | 0.46 | 0.18 | — |
### What holds
- **Union (routing) beats fusion at a weak base — decisively.** Routing reaches **0.74 overall /
0.43 worst-family**, above both fusion merges (soup 0.64/0.26) and every specialist. It recovers
*each* specialist's own-family peak exactly (lists 0.43, strings 1.00, arith 0.78) because it *is*
that specialist there — **no dilution**. This is E8's `max` (union) beating `mean` (average) in real
LLM weights: exactly where fusion diluted the lists-specialist (0.43→0.26), routing keeps 0.43.
- **The learned router is perfect here (1.00) — stated as a caveat, not a triumph.** The three families
are lexically distinct, so nearest-centroid routing over base embeddings is trivially easy;
`route:learned` equals `route:oracle`. Routing's advantage on *these* tasks therefore rests partly on
the routing problem being easy — the honest scope. On overlapping/ambiguous skills the router would
be the bottleneck, and that is the interesting failure mode to probe next.
- **Static per-module `max-merge` is a poor union (0.46) — an informative negative.** Picking, per
module, the largest-norm specialist delta is *not* input-adaptive: it collapses toward whichever
specialist dominates the weight norms (arith 0.87, but lists 0.18, strings 0.34). The union benefit
needs **routing** (input-adaptive selection), not weight-space surgery — "keep the parents whole"
only pays off if you also *choose* the right parent per input.
### Takeaway
Adds the union half of E8's operator dichotomy to real LLM weights and confirms its sign at a weak
base: **route-don't-average > average**, with no dilution, mirroring the analytic `max > mean`. Two
honest riders — the learned router is trivially good because the families are lexically separable, and
the router-free `max-merge` union fails because it isn't input-adaptive. The regime question the 0.5B
result raises — *does routing still help once a capable base lets fusion **compose** rather than
dilute?* (`llm_merge_hpc` showed 7B soup already beats its specialists with no dilution) — is answered
by **`results/llm_moe_hpc/`: the ordering flips.** At 7B fusion wins (soup 0.87 > routing 0.84),
because routing is capped at the best parent while fusion composes beyond it. So "merge, don't average"
is a **weak-base law**: union wins here (0.5B, dilution regime), fusion wins there (7B, composition
regime). **Falsifier (not triggered at 0.5B):** fusion matching the routing ceiling, i.e. averaging
never diluting.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

View file

@ -1,27 +0,0 @@
{
"experiment": "llm_moe",
"master_seed": 1,
"git_commit": "585264d0b42f0e829229611bd83b08f5a5e418b7",
"python": "3.14.5",
"libraries": {
"numpy": "2.5.0",
"scipy": "1.18.0",
"pandas": "3.0.3",
"pyarrow": "24.0.0",
"torch": "2.12.1",
"transformers": "5.13.0",
"peft": "0.19.1"
},
"rows": 47,
"results_sha256": "3e73caaeae3b5d77ded3ba956af4b767c0d4025a41a7ae1841806ce74b78045e",
"layer": "2",
"tier": "llm",
"base_model": "Qwen/Qwen2.5-0.5B-Instruct",
"operators": [
"soup",
"ties",
"moe_oracle",
"moe_learned",
"max_merge"
]
}

Some files were not shown because too many files have changed in this diff Show more