quantum-cortex 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cortex_c2/__init__.py +161 -0
- cortex_c2/consolidation.py +146 -0
- cortex_c2/router_ablation.py +254 -0
- cortex_c2/router_v2.py +109 -0
- cortex_c2b/__init__.py +724 -0
- cortex_c2b/companion_demo.py +203 -0
- cortex_c2b/crypto.py +102 -0
- cortex_c2b/external_arm.py +591 -0
- cortex_c2b/hm_lm.py +983 -0
- cortex_c2b/hm_protocol.py +308 -0
- cortex_c2b/lifecycle.py +454 -0
- cortex_c2b/lm_bridge.py +338 -0
- cortex_c2b/organ_use.py +254 -0
- cortex_c2b/read_path.py +218 -0
- cortex_c2b/reconstruction_probe.py +471 -0
- cortex_c2b/write_path.py +176 -0
- cortex_data/__init__.py +439 -0
- cortex_data/relay.py +174 -0
- cortex_eval/__init__.py +20 -0
- cortex_eval/ablation_mqar.py +187 -0
- cortex_eval/confirmation_run.py +133 -0
- cortex_eval/domain_map.py +127 -0
- cortex_eval/external_baseline.py +148 -0
- cortex_eval/ingest_confirmation.py +194 -0
- cortex_eval/mqar.py +122 -0
- cortex_eval/multiseed.py +148 -0
- cortex_eval/recurrent_base.py +197 -0
- cortex_eval/recurrent_ladder.py +159 -0
- cortex_eval/resumable_ladder.py +231 -0
- cortex_eval/resumable_rb.py +207 -0
- cortex_eval/run_mqar.py +127 -0
- cortex_eval/serial_position.py +116 -0
- quantum_cortex-1.0.0.dist-info/METADATA +209 -0
- quantum_cortex-1.0.0.dist-info/RECORD +41 -0
- quantum_cortex-1.0.0.dist-info/WHEEL +5 -0
- quantum_cortex-1.0.0.dist-info/licenses/AUTHORS.md +30 -0
- quantum_cortex-1.0.0.dist-info/licenses/LICENSE +73 -0
- quantum_cortex-1.0.0.dist-info/licenses/LICENSE-DOCS.md +34 -0
- quantum_cortex-1.0.0.dist-info/licenses/NOTICE +50 -0
- quantum_cortex-1.0.0.dist-info/top_level.txt +5 -0
- train.py +885 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""cortex_eval.recurrent_ladder — the recurrent-family ladder (D19 Level 1c, refined).
|
|
2
|
+
|
|
3
|
+
The founder's question: "custom GLA vs official Mamba — isn't there an
|
|
4
|
+
intermediate?" — and the answer: the space between them is CONTINUOUS. Mamba is
|
|
5
|
+
the canonical gated linear attention PLUS a stack of documented refinements.
|
|
6
|
+
So instead of one custom GLA or one official Mamba, we build a LADDER: the
|
|
7
|
+
canonical GLA, then each refinement added one at a time behind a flag, each
|
|
8
|
+
re-implemented from its paper (zero borrowed code), each measured. Our own
|
|
9
|
+
ablation discipline (one variable at a time, equal at everything) applied to
|
|
10
|
+
the construction of the reference itself.
|
|
11
|
+
|
|
12
|
+
The rungs (each credited to the paper that introduced the idea):
|
|
13
|
+
L0 canonical GLA S_t = g ⊙ S_{t-1} + k vᵀ ; o = q S (linear attention lineage)
|
|
14
|
+
L1 + input-dependent gate g_t = σ(W_g x_t) — Mamba's "selectivity" (Gu & Dao 2023)
|
|
15
|
+
L2 + channel-wise decay per-channel forget instead of per-head (Kimi Linear's KDA finding)
|
|
16
|
+
L3 + short local conv a depthwise conv on the input before mixing (Mamba / RWKV lineage)
|
|
17
|
+
L4 + delta-rule write S += β k (v − Sᵀk)ᵀ with L2-normalised keys (DeltaNet / KDA)
|
|
18
|
+
|
|
19
|
+
Reading rule (the founder's curve principle): read the CURVE of MQAR accuracy
|
|
20
|
+
vs rung, not one point. Note L4 is exactly our own DeltaMemory — so the top of
|
|
21
|
+
the ladder places our component INSIDE the recurrent family, and "L4 pure" vs
|
|
22
|
+
"our hybrid (L4 + attention)" isolates the contribution of hybridisation.
|
|
23
|
+
|
|
24
|
+
Claim discipline: we compare to "GLA + <named rungs>", never to "Mamba" — an
|
|
25
|
+
official pre-trained Mamba/RWKV remains a separate, non-equalised external
|
|
26
|
+
reference (a later job, license-checked at ingestion).
|
|
27
|
+
"""
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
from dataclasses import dataclass
|
|
31
|
+
from datetime import datetime, timezone
|
|
32
|
+
|
|
33
|
+
import numpy as np
|
|
34
|
+
|
|
35
|
+
from cortex_eval.mqar import MQARTier, make_batch
|
|
36
|
+
|
|
37
|
+
VOCAB = 256 + 8
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class Rung:
|
|
42
|
+
name: str
|
|
43
|
+
selective_gate: bool = False # L1
|
|
44
|
+
channel_decay: bool = False # L2
|
|
45
|
+
local_conv: bool = False # L3
|
|
46
|
+
delta_rule: bool = False # L4
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
LADDER = [
|
|
50
|
+
Rung("L0-canonical-gla"),
|
|
51
|
+
Rung("L1-+selective-gate", selective_gate=True),
|
|
52
|
+
Rung("L2-+channel-decay", selective_gate=True, channel_decay=True),
|
|
53
|
+
Rung("L3-+local-conv", selective_gate=True, channel_decay=True, local_conv=True),
|
|
54
|
+
Rung("L4-+delta-rule", selective_gate=True, channel_decay=True, local_conv=True, delta_rule=True),
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _build(rung: Rung, d_model: int, n_head: int):
|
|
59
|
+
import torch, torch.nn as nn
|
|
60
|
+
dk = d_model // n_head
|
|
61
|
+
|
|
62
|
+
class RecurrentRung(nn.Module):
|
|
63
|
+
def __init__(self):
|
|
64
|
+
super().__init__()
|
|
65
|
+
self.emb = nn.Embedding(VOCAB, d_model)
|
|
66
|
+
self.ln = nn.LayerNorm(d_model)
|
|
67
|
+
self.conv = (nn.Conv1d(d_model, d_model, kernel_size=3, padding=2, groups=d_model)
|
|
68
|
+
if rung.local_conv else None) # L3: depthwise, causal (crop)
|
|
69
|
+
self.qkv = nn.Linear(d_model, 3 * d_model, bias=False)
|
|
70
|
+
# gate: constant-per-head (L0) or input-dependent (L1); per-head or per-channel (L2)
|
|
71
|
+
gate_out = n_head * dk if rung.channel_decay else n_head
|
|
72
|
+
self.gate = (nn.Linear(d_model, gate_out, bias=True) if rung.selective_gate
|
|
73
|
+
else nn.Parameter(torch.zeros(gate_out))) # L0: learned constant
|
|
74
|
+
self.beta = nn.Linear(d_model, n_head, bias=True) if rung.delta_rule else None # L4
|
|
75
|
+
self.out = nn.Linear(d_model, d_model, bias=False)
|
|
76
|
+
self.ln2 = nn.LayerNorm(d_model)
|
|
77
|
+
self.mlp = nn.Sequential(nn.Linear(d_model, 4 * d_model), nn.GELU(),
|
|
78
|
+
nn.Linear(4 * d_model, d_model))
|
|
79
|
+
self.head = nn.Linear(d_model, VOCAB, bias=False)
|
|
80
|
+
|
|
81
|
+
def forward(self, idx):
|
|
82
|
+
x = self.emb(idx)
|
|
83
|
+
b, T, c = x.shape
|
|
84
|
+
z = self.ln(x)
|
|
85
|
+
if self.conv is not None: # L3
|
|
86
|
+
z = self.conv(z.transpose(1, 2))[:, :, :T].transpose(1, 2) # causal crop
|
|
87
|
+
q, k, v = self.qkv(z).split(c, dim=2)
|
|
88
|
+
q = torch.nn.functional.normalize(q.view(b, T, n_head, dk), dim=-1)
|
|
89
|
+
k = torch.nn.functional.normalize(k.view(b, T, n_head, dk), dim=-1)
|
|
90
|
+
v = v.view(b, T, n_head, dk)
|
|
91
|
+
if rung.selective_gate: # L1
|
|
92
|
+
g_raw = torch.sigmoid(self.gate(z))
|
|
93
|
+
else: # L0
|
|
94
|
+
g_raw = torch.sigmoid(self.gate).expand(b, T, -1)
|
|
95
|
+
if rung.channel_decay: # L2
|
|
96
|
+
g = g_raw.view(b, T, n_head, dk)
|
|
97
|
+
else:
|
|
98
|
+
g = g_raw.view(b, T, n_head, 1).expand(b, T, n_head, dk)
|
|
99
|
+
beta = torch.sigmoid(self.beta(z)).view(b, T, n_head, 1) if rung.delta_rule else None
|
|
100
|
+
S = torch.zeros(b, n_head, dk, dk, device=x.device)
|
|
101
|
+
outs = []
|
|
102
|
+
for i in range(T):
|
|
103
|
+
S = S * g[:, i].unsqueeze(2) # forget (columns)
|
|
104
|
+
if rung.delta_rule: # L4
|
|
105
|
+
u = torch.einsum('bhij,bhi->bhj', S, k[:, i])
|
|
106
|
+
S = S + beta[:, i].unsqueeze(-1) * torch.einsum('bhi,bhj->bhij', k[:, i], v[:, i] - u)
|
|
107
|
+
else: # L0–L3: plain write
|
|
108
|
+
S = S + torch.einsum('bhi,bhj->bhij', k[:, i], v[:, i])
|
|
109
|
+
outs.append(torch.einsum('bhi,bhij->bhj', q[:, i], S).reshape(b, c))
|
|
110
|
+
o = torch.stack(outs, 1)
|
|
111
|
+
x = x + self.out(o)
|
|
112
|
+
x = x + self.mlp(self.ln2(x))
|
|
113
|
+
return self.head(x)
|
|
114
|
+
|
|
115
|
+
return RecurrentRung()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def run_rung(rung: Rung, tier: MQARTier, steps: int, seed: int, d_model: int = 64,
|
|
119
|
+
n_head: int = 2, batch: int = 32, lr: float = 3e-3, eval_batch: int = 64) -> float:
|
|
120
|
+
import torch, torch.nn as nn
|
|
121
|
+
torch.manual_seed(seed)
|
|
122
|
+
model = _build(rung, d_model, n_head)
|
|
123
|
+
opt = torch.optim.AdamW(model.parameters(), lr=lr)
|
|
124
|
+
for step in range(steps):
|
|
125
|
+
X, Y = make_batch(tier, batch, seed=seed + step)
|
|
126
|
+
logits = model(torch.from_numpy(X))
|
|
127
|
+
loss = nn.functional.cross_entropy(logits.view(-1, VOCAB),
|
|
128
|
+
torch.from_numpy(Y).view(-1), ignore_index=-100)
|
|
129
|
+
if not torch.isfinite(loss):
|
|
130
|
+
return float("nan") # circuit-breaker spirit
|
|
131
|
+
opt.zero_grad(); loss.backward()
|
|
132
|
+
nn.utils.clip_grad_norm_(model.parameters(), 1.0); opt.step()
|
|
133
|
+
Xe, Ye = make_batch(tier, eval_batch, seed=seed + 10_000)
|
|
134
|
+
with torch.no_grad():
|
|
135
|
+
preds = model(torch.from_numpy(Xe)).argmax(-1).numpy()
|
|
136
|
+
mask = Ye != -100
|
|
137
|
+
return float((preds[mask] == Ye[mask]).mean())
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def run_ladder(tiers: list[MQARTier] | None = None, steps: int = 1500, seed: int = 1337,
|
|
141
|
+
rungs: list[Rung] | None = None, **kw) -> dict:
|
|
142
|
+
tiers = tiers or [MQARTier(kv_pairs=4, seq_len=128), MQARTier(kv_pairs=8, seq_len=128),
|
|
143
|
+
MQARTier(kv_pairs=16, seq_len=128)]
|
|
144
|
+
rungs = rungs or LADDER
|
|
145
|
+
curve = []
|
|
146
|
+
for rung in rungs:
|
|
147
|
+
accs = [run_rung(rung, t, steps, seed, **kw) for t in tiers]
|
|
148
|
+
auc = float(np.nanmean(accs)) if any(a == a for a in accs) else float("nan")
|
|
149
|
+
curve.append({"rung": rung.name, "per_tier": accs, "auc": round(auc, 4)})
|
|
150
|
+
print(f"[ladder] {rung.name:22s} -> auc={auc:.4f}")
|
|
151
|
+
return {
|
|
152
|
+
"benchmark": "recurrent_ladder",
|
|
153
|
+
"timestamp_utc": datetime.now(timezone.utc).isoformat(),
|
|
154
|
+
"regime": {"tiers": [(t.kv_pairs, t.seq_len) for t in tiers], "steps": steps, "seed": seed},
|
|
155
|
+
"curve": curve,
|
|
156
|
+
"reading": "MQAR AUC vs recurrent refinement rung. L4 (delta-rule, normalised keys) is our own "
|
|
157
|
+
"DeltaMemory — the top of the ladder places our component inside the recurrent family; "
|
|
158
|
+
"'L4 pure' vs our hybrid isolates hybridisation. Compare to 'GLA + named rungs', never to 'Mamba'.",
|
|
159
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""cortex_eval.resumable_ladder — the 8-seed ladder that survives a quota cut.
|
|
2
|
+
|
|
3
|
+
The lesson of run B (2026-09-09): a run that writes only at the end loses
|
|
4
|
+
everything when Kaggle kills the session. This runner makes the ladder
|
|
5
|
+
**resumable at the unit level**:
|
|
6
|
+
|
|
7
|
+
unit = (rung, seed, tier) → one small JSON file, written the moment the unit
|
|
8
|
+
finishes, in a checkpoint directory.
|
|
9
|
+
|
|
10
|
+
On start, the runner scans the checkpoint directory (and any *previous*
|
|
11
|
+
checkpoint directories mounted as Kaggle Inputs), loads every finished unit,
|
|
12
|
+
and skips them. A session cut at unit 37 of 80 restarts at unit 38. Nothing is
|
|
13
|
+
ever recomputed; nothing is ever lost.
|
|
14
|
+
|
|
15
|
+
Kaggle specifics: a killed session loses /kaggle/working, but the Output of the
|
|
16
|
+
previous *version* is retained and can be mounted as an Input of the next
|
|
17
|
+
version. So we READ from every directory in `resume_from` (the mounted Inputs)
|
|
18
|
+
and WRITE to `ckpt_dir` (the current Output). Same pattern as the N1 trainer's
|
|
19
|
+
--resume from an Input-mounted checkpoint.
|
|
20
|
+
|
|
21
|
+
Time budget: `--time-budget-min` stops cleanly before the quota wall (units
|
|
22
|
+
are small, ~6-10 min each, so the loss at the wall is at most one unit).
|
|
23
|
+
|
|
24
|
+
When every unit exists, the aggregate (per-rung stats, CIs, paired tests) is
|
|
25
|
+
computed from the unit files — deterministic, reproducible from the units alone.
|
|
26
|
+
"""
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import argparse
|
|
30
|
+
import json
|
|
31
|
+
import math
|
|
32
|
+
import time
|
|
33
|
+
from datetime import datetime, timezone
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
|
|
36
|
+
import numpy as np
|
|
37
|
+
|
|
38
|
+
from cortex_eval.mqar import MQARTier
|
|
39
|
+
from cortex_eval.recurrent_ladder import LADDER, Rung, run_rung
|
|
40
|
+
from cortex_eval.multiseed import _mean_ci, _paired_test
|
|
41
|
+
from cortex_eval.run_mqar import run_curriculum
|
|
42
|
+
|
|
43
|
+
# The three ARCHITECTURE paths measured alongside the pure ladder, same seeds, same
|
|
44
|
+
# tiers, same steps — so every comparison below is a PAIRED test on identical data.
|
|
45
|
+
ARCH = ("none", "hopfield", "delta") # control / Hopfield hybrid / delta hybrid
|
|
46
|
+
ARCH_EXTRA = ("none+local-conv",) # the control plus a local convolution (Rev60): a declared reference, --paths only,
|
|
47
|
+
# never part of the full ladder's 128 units nor of its aggregate
|
|
48
|
+
|
|
49
|
+
DEFAULT_SEEDS = (1337, 2024, 7, 42, 99, 3, 11, 2026) # 8 seeds → df 7, t_crit 2.365
|
|
50
|
+
DEFAULT_TIERS = [MQARTier(kv_pairs=16, seq_len=128), MQARTier(kv_pairs=8, seq_len=128)]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def unit_id(rung, seed: int, tier: MQARTier) -> str:
|
|
54
|
+
"""rung is a Rung (pure ladder) or a str in ARCH (control / hybrids)."""
|
|
55
|
+
name = rung.name if isinstance(rung, Rung) else f"ARCH-{rung}"
|
|
56
|
+
return f"{name}__s{seed}__kv{tier.kv_pairs}_seq{tier.seq_len}"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def run_unit(rung, tier: MQARTier, steps: int, seed: int, **kw) -> float:
|
|
60
|
+
if isinstance(rung, Rung):
|
|
61
|
+
return run_rung(rung, tier, steps, seed, **kw)
|
|
62
|
+
if rung == "none+local-conv": # Rev60: the control, one variable added
|
|
63
|
+
res = run_curriculum(c2_variant="none", attn_local_conv=True, steps=steps, tiers=[tier], seed=seed, **kw)
|
|
64
|
+
return float(res.per_tier[0]["accuracy"])
|
|
65
|
+
res = run_curriculum(c2_variant=rung, steps=steps, tiers=[tier], seed=seed, **kw)
|
|
66
|
+
return float(res.per_tier[0]["accuracy"])
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def load_done(dirs: list[Path]) -> dict[str, dict]:
|
|
70
|
+
"""Every finished unit across all given directories (later dirs win on duplicates)."""
|
|
71
|
+
done = {}
|
|
72
|
+
for d in dirs:
|
|
73
|
+
if not d or not d.exists():
|
|
74
|
+
continue
|
|
75
|
+
for p in sorted(d.glob("unit-*.json")):
|
|
76
|
+
try:
|
|
77
|
+
u = json.loads(p.read_text())
|
|
78
|
+
if u.get("status") == "done" and u.get("accuracy") == u.get("accuracy"):
|
|
79
|
+
done[u["unit_id"]] = u
|
|
80
|
+
except Exception:
|
|
81
|
+
continue
|
|
82
|
+
return done
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def run_resumable_ladder(seeds=DEFAULT_SEEDS, tiers=None, steps: int = 1500,
|
|
86
|
+
ckpt_dir: str | Path = "metrics/mqar/ladder8-ckpt",
|
|
87
|
+
resume_from: list[str | Path] | None = None,
|
|
88
|
+
time_budget_min: float | None = None, paths: list[str] | None = None,
|
|
89
|
+
relay: str | None = None, **kw) -> dict:
|
|
90
|
+
"""`paths` (optional): restrict the run to these path names (e.g. the sigma run of
|
|
91
|
+
the convergence, L3 alone at 3000 steps on the seeds that stayed at the floor).
|
|
92
|
+
A subset writes its units and a SUBSET aggregate (per path stats, no paired
|
|
93
|
+
tests against absent paths); the full readout needs all eight paths."""
|
|
94
|
+
tiers = tiers or DEFAULT_TIERS
|
|
95
|
+
ckpt = Path(ckpt_dir); ckpt.mkdir(parents=True, exist_ok=True)
|
|
96
|
+
if relay: # the checkpoint relay: the last push, before anything else
|
|
97
|
+
import tempfile
|
|
98
|
+
from cortex_data import relay as _relay
|
|
99
|
+
pulled = Path(tempfile.mkdtemp(prefix="relay-")) / ckpt.name
|
|
100
|
+
if _relay.pull(relay, pulled):
|
|
101
|
+
resume_from = list(resume_from or []) + [pulled]
|
|
102
|
+
dirs = [Path(p) for p in (resume_from or [])] + [ckpt]
|
|
103
|
+
done = load_done(dirs)
|
|
104
|
+
all_paths = list(LADDER) + list(ARCH)
|
|
105
|
+
if paths:
|
|
106
|
+
names = {(r.name if isinstance(r, Rung) else f"ARCH-{r}"): r for r in all_paths + list(ARCH_EXTRA)}
|
|
107
|
+
unknown = [n for n in paths if n not in names]
|
|
108
|
+
if unknown:
|
|
109
|
+
raise SystemExit(f"unknown paths {unknown}; known: {sorted(names)}")
|
|
110
|
+
all_paths = [names[n] for n in paths]
|
|
111
|
+
units = [(r, s, t) for r in all_paths for s in seeds for t in tiers]
|
|
112
|
+
todo = [u for u in units if unit_id(*u) not in done]
|
|
113
|
+
print(f"[resumable] {len(units)} units total, {len(done)} already done, {len(todo)} to run")
|
|
114
|
+
t0 = time.time()
|
|
115
|
+
stopped_early = False
|
|
116
|
+
for rung, seed, tier in todo:
|
|
117
|
+
if time_budget_min is not None and (time.time() - t0) / 60.0 > time_budget_min:
|
|
118
|
+
print(f"[resumable] time budget reached — stopping cleanly; rerun to resume")
|
|
119
|
+
stopped_early = True
|
|
120
|
+
break
|
|
121
|
+
uid = unit_id(rung, seed, tier)
|
|
122
|
+
t1 = time.time()
|
|
123
|
+
acc = run_unit(rung, tier, steps, seed, **kw)
|
|
124
|
+
rec = {"unit_id": uid, "rung": rung.name if isinstance(rung, Rung) else f"ARCH-{rung}", "seed": seed, "kv_pairs": tier.kv_pairs,
|
|
125
|
+
"seq_len": tier.seq_len, "steps": steps, "accuracy": acc,
|
|
126
|
+
"status": "done" if acc == acc else "diverged",
|
|
127
|
+
"wall_s": round(time.time() - t1, 1), "written_utc": datetime.now(timezone.utc).isoformat()}
|
|
128
|
+
(ckpt / f"unit-{uid}.json").write_text(json.dumps(rec) + "\n") # written THE MOMENT it finishes
|
|
129
|
+
done[uid] = rec
|
|
130
|
+
if relay:
|
|
131
|
+
from cortex_data import relay as _relay
|
|
132
|
+
_relay.push(ckpt, relay, uid) # small files: a blocking push, seconds
|
|
133
|
+
print(f"[unit] {uid} -> acc={acc:.4f} ({rec['wall_s']}s) [{len(done)}/{len(units)}]")
|
|
134
|
+
complete = all(unit_id(*u) in done for u in units)
|
|
135
|
+
result = {"benchmark": "recurrent_ladder_8seed_resumable",
|
|
136
|
+
"timestamp_utc": datetime.now(timezone.utc).isoformat(),
|
|
137
|
+
"regime": {"seeds": list(seeds), "n_seeds": len(seeds), "steps": steps,
|
|
138
|
+
"tiers": [(t.kv_pairs, t.seq_len) for t in tiers]},
|
|
139
|
+
"units_done": len(done), "units_total": len(units), "complete": complete,
|
|
140
|
+
"stopped_early": stopped_early}
|
|
141
|
+
if complete and paths:
|
|
142
|
+
result["subset"] = list(paths)
|
|
143
|
+
result["stats"] = {}
|
|
144
|
+
for r in all_paths:
|
|
145
|
+
name = r.name if isinstance(r, Rung) else f"ARCH-{r}"
|
|
146
|
+
vals = [float(np.mean([done[unit_id(r, s, t)]["accuracy"] for t in tiers])) for s in seeds]
|
|
147
|
+
per_tier = {f"kv{t.kv_pairs}": [done[unit_id(r, s, t)]["accuracy"] for s in seeds] for t in tiers}
|
|
148
|
+
result["stats"][name] = {**_mean_ci(vals), "per_seed": vals, "per_tier": per_tier}
|
|
149
|
+
tag = "-".join(paths).replace("+", "").replace("/", "")[:40]
|
|
150
|
+
(ckpt / f"SUBSET-{tag}-{steps}steps.json").write_text(json.dumps(result, indent=2) + "\n")
|
|
151
|
+
print(f"[resumable] COMPLETE (subset {paths}) — per path stats written; no paired tests on a subset")
|
|
152
|
+
elif complete:
|
|
153
|
+
result.update(aggregate(done, seeds, tiers))
|
|
154
|
+
(ckpt / "LATEST-ladder8.json").write_text(json.dumps(result, indent=2) + "\n")
|
|
155
|
+
print("[resumable] COMPLETE — aggregate written")
|
|
156
|
+
else:
|
|
157
|
+
(ckpt / "PROGRESS-ladder8.json").write_text(json.dumps(result, indent=2) + "\n")
|
|
158
|
+
print(f"[resumable] incomplete ({len(done)}/{len(units)}) — progress written; rerun with this dir as Input to resume")
|
|
159
|
+
return result
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def aggregate(done: dict, seeds, tiers) -> dict:
|
|
163
|
+
"""Per-rung AUC per seed (mean over tiers), then stats + paired tests. Deterministic
|
|
164
|
+
from the unit files alone — re-runnable offline on the committed units."""
|
|
165
|
+
per_rung: dict[str, list[float]] = {}
|
|
166
|
+
for r in LADDER:
|
|
167
|
+
vals = []
|
|
168
|
+
for s in seeds:
|
|
169
|
+
accs = [done[unit_id(r, s, t)]["accuracy"] for t in tiers]
|
|
170
|
+
vals.append(float(np.mean(accs)))
|
|
171
|
+
per_rung[r.name] = vals
|
|
172
|
+
per_arch: dict[str, list[float]] = {}
|
|
173
|
+
for a in ARCH:
|
|
174
|
+
vals = []
|
|
175
|
+
for s in seeds:
|
|
176
|
+
accs = [done[unit_id(a, s, t)]["accuracy"] for t in tiers]
|
|
177
|
+
vals.append(float(np.mean(accs)))
|
|
178
|
+
per_arch[a] = vals
|
|
179
|
+
stats = {r: _mean_ci(v) for r, v in {**per_rung, **{f"ARCH-{a}": v for a, v in per_arch.items()}}.items()}
|
|
180
|
+
best_pure = max(per_rung, key=lambda r: np.mean(per_rung[r]))
|
|
181
|
+
n = len(seeds)
|
|
182
|
+
tests = {
|
|
183
|
+
"hopfield_hybrid_vs_best_pure": _paired_test(per_arch["hopfield"], per_rung[best_pure]),
|
|
184
|
+
"delta_hybrid_vs_best_pure": _paired_test(per_arch["delta"], per_rung[best_pure]),
|
|
185
|
+
"control_vs_best_pure": _paired_test(per_arch["none"], per_rung[best_pure]),
|
|
186
|
+
"hopfield_vs_control": _paired_test(per_arch["hopfield"], per_arch["none"]),
|
|
187
|
+
"delta_vs_control": _paired_test(per_arch["delta"], per_arch["none"]),
|
|
188
|
+
"L4_vs_L3": _paired_test(per_rung["L4-+delta-rule"], per_rung["L3-+local-conv"]),
|
|
189
|
+
}
|
|
190
|
+
gate = {k: bool(v.get("significant_95")) and n >= 3 for k, v in tests.items()}
|
|
191
|
+
return {"per_rung_per_seed": per_rung, "per_arch_per_seed": per_arch, "stats": stats,
|
|
192
|
+
"best_pure_rung": best_pure, "paired_tests": tests, "gate_readout": gate,
|
|
193
|
+
"note": "every test is paired on seed at identical steps/tiers; gated = significant at 95% with >= 3 seeds."}
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _cli():
|
|
197
|
+
ap = argparse.ArgumentParser(description="Resumable 8-seed recurrent ladder")
|
|
198
|
+
ap.add_argument("--steps", type=int, default=1500)
|
|
199
|
+
ap.add_argument("--seeds", type=int, nargs="+", default=list(DEFAULT_SEEDS))
|
|
200
|
+
ap.add_argument("--ckpt-dir", default="metrics/mqar/ladder8-ckpt")
|
|
201
|
+
ap.add_argument("--resume-from", nargs="*", default=[],
|
|
202
|
+
help="previous checkpoint dirs (e.g. Kaggle Inputs) whose finished units are reused")
|
|
203
|
+
ap.add_argument("--time-budget-min", type=float, default=None)
|
|
204
|
+
ap.add_argument("--paths", nargs="*", default=None,
|
|
205
|
+
help="restrict to these path names (e.g. L3-+local-conv for the sigma run)")
|
|
206
|
+
ap.add_argument("--tiers", nargs="*", default=None, help="restrict to these tiers as KVxSEQ (e.g. 8x128 for the tier where L3 takes off); a subset aggregate, never a paired claim across tiers")
|
|
207
|
+
ap.add_argument("--relay", default=None, help="checkpoint relay: a private Kaggle dataset slug pulled before the resume lookup and pushed after every unit")
|
|
208
|
+
ap.add_argument("--quick", action="store_true")
|
|
209
|
+
args = ap.parse_args()
|
|
210
|
+
tiers = parse_tiers(args.tiers)
|
|
211
|
+
if args.quick:
|
|
212
|
+
run_resumable_ladder(seeds=(1, 2), tiers=tiers or [MQARTier(kv_pairs=4, seq_len=32)], steps=40,
|
|
213
|
+
ckpt_dir=args.ckpt_dir, resume_from=args.resume_from, d_model=32, paths=args.paths, relay=args.relay)
|
|
214
|
+
else:
|
|
215
|
+
run_resumable_ladder(seeds=tuple(args.seeds), tiers=tiers, steps=args.steps, ckpt_dir=args.ckpt_dir,
|
|
216
|
+
resume_from=args.resume_from, time_budget_min=args.time_budget_min, paths=args.paths, relay=args.relay)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def parse_tiers(spec):
|
|
220
|
+
"""'8x128 16x128' -> [MQARTier(8, 128), MQARTier(16, 128)]; None -> None (the default tiers)."""
|
|
221
|
+
if not spec:
|
|
222
|
+
return None
|
|
223
|
+
out = []
|
|
224
|
+
for s in spec:
|
|
225
|
+
kv, seq = s.lower().split("x")
|
|
226
|
+
out.append(MQARTier(kv_pairs=int(kv), seq_len=int(seq)))
|
|
227
|
+
return out
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
if __name__ == "__main__":
|
|
231
|
+
_cli()
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"""cortex_eval.resumable_rb -- the recurrent base hybrid run, resumable (Rev38; declared before the run).
|
|
2
|
+
|
|
3
|
+
64 units = 4 arms (bare, critical, cls, cls-phi) x 8 seeds x 2 tiers, at the
|
|
4
|
+
ladder's 1500 steps, each written the moment it finishes. The references are
|
|
5
|
+
NOT retrained: the 8 seed ladder's units (`metrics/mqar/ladder8-ckpt/`) give L3
|
|
6
|
+
pure and the transformer control at the same seeds, tiers and steps, so every
|
|
7
|
+
comparison is paired on seed on identical data.
|
|
8
|
+
|
|
9
|
+
The readout, declared 2026-09-12 before any unit ran:
|
|
10
|
+
per arm: mean full accuracy (mean over tiers per seed), trunk only accuracy,
|
|
11
|
+
take off rate (trunk only at 8 pairs >= 0.3), the 16 pair mean, the
|
|
12
|
+
consolidation gap, the gate at the end;
|
|
13
|
+
paired tests (95 %, n >= 3): each arm's full model vs L3 pure; each arm's full
|
|
14
|
+
model vs the control; each arm's trunk only vs L3 pure (did the base
|
|
15
|
+
keep or gain its take off?); cls-phi vs cls (phi against 1/2).
|
|
16
|
+
gated = significant at 95 %; a bimodal comparator gets its reserve written
|
|
17
|
+
next to the number, as for the ladder.
|
|
18
|
+
No number here is a result until the units exist and the log is read cold.
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import argparse
|
|
23
|
+
import json
|
|
24
|
+
import time
|
|
25
|
+
from datetime import datetime, timezone
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
|
|
28
|
+
import numpy as np
|
|
29
|
+
|
|
30
|
+
from cortex_eval.mqar import MQARTier
|
|
31
|
+
from cortex_eval.multiseed import _mean_ci, _paired_test
|
|
32
|
+
from cortex_eval.recurrent_base import ARMS, ARM_BY_NAME, RBArm, run_rb_unit
|
|
33
|
+
from cortex_eval.resumable_ladder import DEFAULT_SEEDS, DEFAULT_TIERS, load_done
|
|
34
|
+
|
|
35
|
+
REF_L3, REF_CONTROL = "L3-+local-conv", "ARCH-none"
|
|
36
|
+
REF_CONTROL_CONV = "ARCH-none+local-conv" # the control plus a local convolution (Rev60), a declared reference
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def rb_unit_id(arm: RBArm, seed: int, tier: MQARTier) -> str:
|
|
40
|
+
return f"{arm.name}__s{seed}__kv{tier.kv_pairs}_seq{tier.seq_len}"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def ref_unit_id(name: str, seed: int, tier: MQARTier) -> str:
|
|
44
|
+
return f"{name}__s{seed}__kv{tier.kv_pairs}_seq{tier.seq_len}"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def run_resumable_rb(seeds=DEFAULT_SEEDS, tiers=None, steps: int = 1500, relay: str | None = None, latest_name: str = "LATEST-rb.json",
|
|
48
|
+
ckpt_dir: str | Path = "metrics/mqar/rb-ckpt",
|
|
49
|
+
resume_from: list[str | Path] | None = None,
|
|
50
|
+
reference_from: list[str | Path] | None = None,
|
|
51
|
+
time_budget_min: float | None = None, arms: list[str] | None = None, **kw) -> dict:
|
|
52
|
+
tiers = tiers or DEFAULT_TIERS
|
|
53
|
+
ckpt = Path(ckpt_dir); ckpt.mkdir(parents=True, exist_ok=True)
|
|
54
|
+
if relay: # the checkpoint relay: the last push, before anything else
|
|
55
|
+
import tempfile
|
|
56
|
+
from cortex_data import relay as _relay
|
|
57
|
+
pulled = Path(tempfile.mkdtemp(prefix="relay-")) / ckpt.name
|
|
58
|
+
if _relay.pull(relay, pulled):
|
|
59
|
+
resume_from = list(resume_from or []) + [pulled]
|
|
60
|
+
done = load_done([Path(p) for p in (resume_from or [])] + [ckpt])
|
|
61
|
+
refs = load_done([Path(p) for p in (reference_from or [])])
|
|
62
|
+
chosen = [ARM_BY_NAME[a] for a in arms] if arms else list(ARMS)
|
|
63
|
+
units = [(a, s, t) for a in chosen for s in seeds for t in tiers]
|
|
64
|
+
todo = [u for u in units if rb_unit_id(*u) not in done]
|
|
65
|
+
print(f"[resumable-rb] {len(units)} units total, {len(done)} already done, {len(todo)} to run; "
|
|
66
|
+
f"{len(refs)} reference units loaded")
|
|
67
|
+
t0 = time.time(); stopped_early = False
|
|
68
|
+
for arm, seed, tier in todo:
|
|
69
|
+
if time_budget_min is not None and (time.time() - t0) / 60.0 > time_budget_min:
|
|
70
|
+
print("[resumable-rb] time budget reached \u2014 stopping cleanly; rerun to resume")
|
|
71
|
+
stopped_early = True
|
|
72
|
+
break
|
|
73
|
+
uid = rb_unit_id(arm, seed, tier)
|
|
74
|
+
t1 = time.time()
|
|
75
|
+
r = run_rb_unit(arm, tier, steps, seed, **kw)
|
|
76
|
+
rec = {"unit_id": uid, "rung": arm.name, "arm": arm.name, "seed": seed, "kv_pairs": tier.kv_pairs,
|
|
77
|
+
"seq_len": tier.seq_len, "steps": steps, **r,
|
|
78
|
+
"wall_s": round(time.time() - t1, 1), "written_utc": datetime.now(timezone.utc).isoformat()}
|
|
79
|
+
(ckpt / f"unit-{uid}.json").write_text(json.dumps(rec) + "\n") # written the moment it finishes
|
|
80
|
+
done[uid] = rec
|
|
81
|
+
if relay:
|
|
82
|
+
from cortex_data import relay as _relay
|
|
83
|
+
_relay.push(ckpt, relay, uid)
|
|
84
|
+
print(f"[unit] {uid} -> acc={r['accuracy']:.4f} trunk={r.get('accuracy_trunk_only', float('nan')):.4f} "
|
|
85
|
+
f"gate={r.get('gate_final')} ({rec['wall_s']}s) [{len(done)}/{len(units)}]")
|
|
86
|
+
complete = all(rb_unit_id(*u) in done for u in units)
|
|
87
|
+
result = {"benchmark": "recurrent_base_hybrid_resumable", "timestamp_utc": datetime.now(timezone.utc).isoformat(),
|
|
88
|
+
"regime": {"seeds": list(seeds), "n_seeds": len(seeds), "steps": steps,
|
|
89
|
+
"tiers": [(t.kv_pairs, t.seq_len) for t in tiers], "arms": [a.name for a in chosen],
|
|
90
|
+
"arm_hashes": {a.name: a.config_hash() for a in chosen}},
|
|
91
|
+
"units_done": len(done), "units_total": len(units), "complete": complete, "stopped_early": stopped_early}
|
|
92
|
+
if complete:
|
|
93
|
+
result.update(aggregate(done, refs, chosen, seeds, tiers))
|
|
94
|
+
(ckpt / latest_name).write_text(json.dumps(result, indent=2) + "\n")
|
|
95
|
+
print(f"[resumable-rb] COMPLETE \u2014 aggregate written ({latest_name})")
|
|
96
|
+
else:
|
|
97
|
+
(ckpt / "PROGRESS-rb.json").write_text(json.dumps(result, indent=2) + "\n")
|
|
98
|
+
print(f"[resumable-rb] incomplete ({len(done)}/{len(units)}) \u2014 progress written; rerun with this dir as Input")
|
|
99
|
+
return result
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def aggregate(done: dict, refs: dict, arms, seeds, tiers) -> dict:
|
|
103
|
+
"""Deterministic from the unit files alone; references from the ladder8 units."""
|
|
104
|
+
kv8 = [t for t in tiers if t.kv_pairs == 8]
|
|
105
|
+
kv16 = [t for t in tiers if t.kv_pairs == 16]
|
|
106
|
+
per_full, per_trunk, stats = {}, {}, {}
|
|
107
|
+
for a in arms:
|
|
108
|
+
full = [float(np.mean([done[rb_unit_id(a, s, t)]["accuracy"] for t in tiers])) for s in seeds]
|
|
109
|
+
trunk = [float(np.mean([done[rb_unit_id(a, s, t)]["accuracy_trunk_only"] for t in tiers])) for s in seeds]
|
|
110
|
+
per_full[a.name], per_trunk[a.name] = full, trunk
|
|
111
|
+
st = {"full": _mean_ci(full), "trunk_only": _mean_ci(trunk),
|
|
112
|
+
"take_off_rate": (float(np.mean([done[rb_unit_id(a, s, t)]["took_off"] for s in seeds for t in kv8])) if kv8 else None),
|
|
113
|
+
"kv16_mean": (float(np.mean([done[rb_unit_id(a, s, t)]["accuracy"] for s in seeds for t in kv16])) if kv16 else None),
|
|
114
|
+
"gap_mean": float(np.mean([done[rb_unit_id(a, s, t)]["consolidation_gap"] for s in seeds for t in tiers])),
|
|
115
|
+
"gate_final_mean": float(np.mean([done[rb_unit_id(a, s, t)]["gate_final"] for s in seeds for t in tiers]))}
|
|
116
|
+
stats[a.name] = st
|
|
117
|
+
tests, missing = {}, []
|
|
118
|
+
def ref_values(name):
|
|
119
|
+
try:
|
|
120
|
+
return [float(np.mean([refs[ref_unit_id(name, s, t)]["accuracy"] for t in tiers])) for s in seeds]
|
|
121
|
+
except KeyError:
|
|
122
|
+
missing.append(name); return None
|
|
123
|
+
l3, ctrl = ref_values(REF_L3), ref_values(REF_CONTROL)
|
|
124
|
+
for a in arms:
|
|
125
|
+
if l3 is not None:
|
|
126
|
+
tests[f"{a.name}_full_vs_L3"] = _paired_test(per_full[a.name], l3)
|
|
127
|
+
tests[f"{a.name}_trunk_vs_L3"] = _paired_test(per_trunk[a.name], l3)
|
|
128
|
+
if ctrl is not None:
|
|
129
|
+
tests[f"{a.name}_full_vs_control"] = _paired_test(per_full[a.name], ctrl)
|
|
130
|
+
if "RB-cls-phi" in per_full and "RB-cls" in per_full:
|
|
131
|
+
tests["RB-cls-phi_vs_RB-cls"] = _paired_test(per_full["RB-cls-phi"], per_full["RB-cls"])
|
|
132
|
+
gate = {k: ("gated" if (v.get("significant_95") and len(seeds) >= 3) else "held") for k, v in tests.items()}
|
|
133
|
+
return {"stats": stats, "per_seed": {"full": per_full, "trunk_only": per_trunk},
|
|
134
|
+
"references": {"L3": l3, "control": ctrl, "missing": sorted(set(missing))},
|
|
135
|
+
"paired_tests": tests, "gate_readout": gate,
|
|
136
|
+
"note": "paired on seed at identical steps/tiers against the ladder8 units; gated = significant at 95% with >= 3 seeds; "
|
|
137
|
+
"a bimodal comparator (L3) gets its reserve written next to the number."}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def control_conv_readout(refs: dict, done: dict, arms, seeds, tiers) -> dict:
|
|
141
|
+
"""Rev60, declared before the run: the control plus a local convolution against
|
|
142
|
+
the control (does the convolution lift the two layer transformer), against L3
|
|
143
|
+
pure (does it reach the recurrent trunk's level) and against each measured arm's
|
|
144
|
+
full accuracy (does the arm add anything beyond a convolved transformer). Paired
|
|
145
|
+
on seed at identical steps and tier; gated = significant at 95 % with >= 3 seeds.
|
|
146
|
+
Deterministic from the unit files alone."""
|
|
147
|
+
def ref_values(name):
|
|
148
|
+
return [float(np.mean([refs[ref_unit_id(name, s, t)]["accuracy"] for t in tiers])) for s in seeds]
|
|
149
|
+
cc, ctrl, l3 = ref_values(REF_CONTROL_CONV), ref_values(REF_CONTROL), ref_values(REF_L3)
|
|
150
|
+
tests = {"control_conv_vs_control": _paired_test(cc, ctrl), "control_conv_vs_L3": _paired_test(cc, l3)}
|
|
151
|
+
per_full = {}
|
|
152
|
+
for a in arms:
|
|
153
|
+
try:
|
|
154
|
+
per_full[a.name] = [float(np.mean([done[rb_unit_id(a, s, t)]["accuracy"] for t in tiers])) for s in seeds]
|
|
155
|
+
except KeyError:
|
|
156
|
+
continue
|
|
157
|
+
tests[f"{a.name}_full_vs_control_conv"] = _paired_test(per_full[a.name], cc)
|
|
158
|
+
gate = {k: ("gated" if (v.get("significant_95") and len(seeds) >= 3) else "held") for k, v in tests.items()}
|
|
159
|
+
return {"benchmark": "control_plus_local_conv_readout", "timestamp_utc": datetime.now(timezone.utc).isoformat(),
|
|
160
|
+
"regime": {"seeds": list(seeds), "n_seeds": len(seeds), "tiers": [(t.kv_pairs, t.seq_len) for t in tiers]},
|
|
161
|
+
"stats": {REF_CONTROL_CONV: _mean_ci(cc), REF_CONTROL: _mean_ci(ctrl), REF_L3: _mean_ci(l3)},
|
|
162
|
+
"per_seed": {REF_CONTROL_CONV: cc, REF_CONTROL: ctrl, REF_L3: l3, "arms_full": per_full},
|
|
163
|
+
"paired_tests": tests, "gate_readout": gate,
|
|
164
|
+
"note": "Rev60: the readings were declared before the run; a subset readout, paired on seed at identical steps and tier"}
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _cli():
|
|
168
|
+
ap = argparse.ArgumentParser(description="Resumable recurrent base hybrid run (4 arms)")
|
|
169
|
+
ap.add_argument("--steps", type=int, default=1500)
|
|
170
|
+
ap.add_argument("--seeds", type=int, nargs="+", default=list(DEFAULT_SEEDS))
|
|
171
|
+
ap.add_argument("--arms", nargs="*", default=None, help=f"subset of {[a.name for a in ARMS]}")
|
|
172
|
+
ap.add_argument("--ckpt-dir", default="metrics/mqar/rb-ckpt")
|
|
173
|
+
ap.add_argument("--resume-from", nargs="*", default=[])
|
|
174
|
+
ap.add_argument("--reference-from", nargs="*", default=["metrics/mqar/ladder8-ckpt"],
|
|
175
|
+
help="dirs holding the ladder8 units (L3 pure and the control at the same seeds/tiers/steps)")
|
|
176
|
+
ap.add_argument("--time-budget-min", type=float, default=None)
|
|
177
|
+
ap.add_argument("--tiers", nargs="*", default=None, help="restrict to these tiers as KVxSEQ (e.g. 8x128 for the tier where L3 takes off); a subset aggregate, never a paired claim across tiers")
|
|
178
|
+
ap.add_argument("--relay", default=None, help="checkpoint relay: a private Kaggle dataset slug pulled before the resume lookup and pushed after every unit")
|
|
179
|
+
ap.add_argument("--quick", action="store_true")
|
|
180
|
+
ap.add_argument("--latest-name", default="LATEST-rb.json",
|
|
181
|
+
help="the aggregate's file name in --ckpt-dir (Rev62: a 16 seed aggregate next to the 8 seed one, never over it)")
|
|
182
|
+
ap.add_argument("--control-conv-readout", default=None, metavar="OUT",
|
|
183
|
+
help="Rev60: no training; read the control plus convolution units from --reference-from and the arms from "
|
|
184
|
+
"--ckpt-dir, compute the declared paired tests, write them to OUT")
|
|
185
|
+
args = ap.parse_args()
|
|
186
|
+
from cortex_eval.resumable_ladder import parse_tiers
|
|
187
|
+
tiers = parse_tiers(args.tiers)
|
|
188
|
+
if args.control_conv_readout:
|
|
189
|
+
refs = load_done([Path(p) for p in args.reference_from])
|
|
190
|
+
done = load_done([Path(p) for p in args.resume_from] + [Path(args.ckpt_dir)])
|
|
191
|
+
arms = [ARM_BY_NAME[n] for n in (args.arms or [a.name for a in ARMS])]
|
|
192
|
+
r = control_conv_readout(refs, done, arms, tuple(args.seeds), tiers or list(DEFAULT_TIERS))
|
|
193
|
+
out = Path(args.control_conv_readout); out.parent.mkdir(parents=True, exist_ok=True)
|
|
194
|
+
out.write_text(json.dumps(r, indent=2) + "\n")
|
|
195
|
+
print(out); print(json.dumps({k: r[k] for k in ("stats", "paired_tests", "gate_readout")}, indent=1))
|
|
196
|
+
return
|
|
197
|
+
if args.quick:
|
|
198
|
+
run_resumable_rb(seeds=(1, 2), tiers=tiers or [MQARTier(kv_pairs=4, seq_len=32)], steps=40, ckpt_dir=args.ckpt_dir,
|
|
199
|
+
resume_from=args.resume_from, reference_from=args.reference_from, arms=args.arms, d_model=32, relay=args.relay)
|
|
200
|
+
else:
|
|
201
|
+
run_resumable_rb(seeds=tuple(args.seeds), tiers=tiers, steps=args.steps, ckpt_dir=args.ckpt_dir, resume_from=args.resume_from,
|
|
202
|
+
reference_from=args.reference_from, time_budget_min=args.time_budget_min, arms=args.arms, relay=args.relay,
|
|
203
|
+
latest_name=args.latest_name)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
if __name__ == "__main__":
|
|
207
|
+
_cli()
|