results tooling + solve-orth knob + results-by-question doc

- scripts/results.py + `just results`: aggregate logs/*.log into last-5
  hack_s and gt_s (solve) tables, sorted-by-time + grouped-by-config, with
  full argv provenance column. Filters smoke/probe runs.
- extract_vhack_grad: solve_orth_m knob — strip top-m known-solve subspace
  (SVD of clean-side grads) from D before SVD, so projection doesn't ablate
  the solve signal. No grader/oracle, off by default.
- docs/results.md: every experiment grouped by the question it answers
  (feasibility, H1, gate_mode, basis, refresh, mix, noise-floor, pair-set)
  with comparison tables and answers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wassname
2026-05-29 07:21:05 +00:00
co-authored by Claude Opus 4.8
parent 826b2aa83e
commit 4464f9d312
4 changed files with 318 additions and 0 deletions
+23
View File
@@ -79,6 +79,12 @@ class Config:
# outlier pairs and doesn't waste rank on noise. Saved with k=1 -- train.py
# load_v_hack reads it the same way as SVD output.
mean_diff: bool = False
# solve_orth_m: if >0, strip the top-m "solve" directions (SVD of the clean-
# side gradients G_c, = grads toward our known-good hand-written solutions)
# out of D before extracting v_hack. 0 = off. Aims to keep the projection
# from ablating the legitimate solve signal (pass-rate selectivity). No
# grader/oracle is read — only the clean solutions we wrote.
solve_orth_m: int = 0
def resolve_dtype(s: str) -> torch.dtype:
@@ -110,6 +116,7 @@ def extract_v_hack(
n_heldout: int,
device,
mean_diff: bool = False,
solve_orth_m: int = 0,
) -> tuple[
dict[str, Float[torch.Tensor, "k r"]],
dict[str, Float[torch.Tensor, "k"]],
@@ -177,6 +184,21 @@ def extract_v_hack(
G_c = torch.stack(grads_clean[name])
D = G_h - G_c
if solve_orth_m > 0:
# Strip the known-solve subspace from D before extracting hack
# directions. B = top-m right singular vectors of G_c (the gradient
# toward our hand-written *correct* clean solutions = the "solve"
# direction; no grader/oracle used, just known-good solutions).
# D = G_h - G_c already carries -G_c, so the solve directions have
# real energy in D; removing them keeps projection from also
# ablating the solve signal (pass-rate selectivity). The SVD below
# then returns hack directions orthogonal to solve, still
# orthonormal, so S/orientation/noise-floor logic is unchanged.
m = min(solve_orth_m, G_c.shape[0])
_, _, Bh = torch.linalg.svd(G_c, full_matrices=False)
B = Bh[:m] # [m, r], orthonormal solve basis
D = D - (D @ B.T) @ B # D_perp
if mean_diff:
# Rank-1 mean-diff direction. Honest under small N: SVD axes 2..k on
# N=12 pairs fit noise; mean-diff regularizes to the only direction
@@ -276,6 +298,7 @@ def main(cfg: Config) -> int:
top_k=cfg.top_k, tau_axis=cfg.tau_axis,
n_heldout=cfg.n_heldout, device=device,
mean_diff=cfg.mean_diff,
solve_orth_m=cfg.solve_orth_m,
)
n_zero = sum(1 for v in v_hack.values() if v.norm() < 1e-12)
k = 1 if cfg.mean_diff else min(cfg.top_k, len(train_pairs))