Skip to content

Fix benchmark validity: penalty bound, nontrivial instances + raw metrics, embedder refinement - #10

Merged
jaewonyun1234 merged 4 commits into
mainfrom
fixes/benchmark-validity
Aug 6, 2026
Merged

jaewonyun1234 merged 4 commits into
mainfrom
fixes/benchmark-validity

Conversation

@jaewonyun1234

Copy link
Copy Markdown
Owner

Three scientific problems found when running the minimal pipeline, fixed in dependency order (Fix 2 → Fix 1 → Fix 3). All measured numbers below are reproducible with the seeds shown.

Fix 2 — penalty bound was mathematically wrong

A = B = 2·(1 + (c_max − c_min)) claimed feasible ground states, but skipping a flight saves its full option cost, not the spread. Verified: make_scenario(4, 3, seed, alpha=0.0) gave infeasible brute-force ground states in 40/40 seeds. With the corrected bound A = B = 2·(1 + c_max): 0/40. Regression test test_ground_state_feasible_small_spread covers the exact broken case.

Fix 1 — benchmark could not distinguish solvers

1a. Trivial instances. Generator tuned (5 blobs spanning 2–3 levels, up to 4 columns wide, routes drawn from the central row band). Conflict-edge counts at F=4, K=3, seeds 0..19:

distribution median zero-conflict seeds
before [0,0,1,10,1,2,4,3,3,2,2,6,6,0,5,12,6,2,3,8] 3.0 3/20
after [4,3,4,14,6,11,2,12,8,3,3,6,12,1,6,15,6,10,3,10] 6.0 0/20

Target (median ≥ 4, ≤ 1/20 zeros) met. run.py --min-conflicts N (default 1) additionally skips sub-threshold scenarios with a printed notice and deterministic seed+1000 substitution recorded in the CSV.

1b. Repair masked all quality differences. Raw QUBO energy of unrepaired samples is now the primary metric (raw_metrics, CSV columns raw_best_E/raw_mean_E); the repair heuristic is exposed as the named solver greedy. The denser instances also exposed that the one-pass repair could leave conflicts standing; it gained a deterministic promote-and-retry (0/2000 failures at the new density).

python run.py --flights 5 --options 3 --seeds 10 --shots 1000 now shows a spread:

RAW QUBO ENERGY (primary)          raw_best_E          raw_mean_E
random                        452.739 ± 74.062    2724.454 ± 336.915
pasqal-analog                 375.360 ± 69.863     391.984 ±  72.127
(E_min ref)                   375.360 ± 69.863

REPAIRED COST (secondary)     approx_ratio   feasibility
brute-force                  1.000 ± 0.000   1.000
greedy                       0.959 ± 0.122   1.000
random                       1.000 ± 0.000   0.002
pasqal-analog                1.000 ± 0.000   0.977

Solvers no longer all tie at 1.000 (greedy misses the optimum on 2/10 seeds), and the raw columns carry the real signal: the analog sampler's best raw shot equals E_min on every seed with a raw mean of ~392 vs random's ~2724. Reported as measured — nothing was tuned to favor the quantum solver.

Fix 3 — embedder strengthened; failures now attributed

embed() (greedy init → vectorized force-directed refinement → 8 seeded jittered restarts, deterministic per seed) replaces the one-pass placer everywhere (solver register builder + study). Refinement fixes found on the way: per-node displacement cap (uncapped spring sums between far clusters diverged to inf — and NaN layouts previously passed check_embedding as "valid", which is why old large-F numbers looked plausible), antisymmetric force directions for coincident nodes, seeded annealing kicks for tug-of-war equilibria, stagnation early-out. max_degree is now computed on the independence graph actually embedded; n_nodes_deg_ge_6, n_all_level_pairs, n_restarts_used, refine_iters added to the CSV.

Fit-fraction ladder, seeds 0..19 (old = one-pass greedy, new = embed(); both on the tuned generator):

F old new new-failure attribution
4 2/20 5/20 12 prism-only, 3 prism-family, 0 deg≥6
6 0/20 1/20 12 deg≥6, 6 prism-only, 1 prism-family
8 0/20 0/20 19 deg≥6, 1 prism-only
12–96 0/20 0/20 20/20 deg≥6

Deviation from the acceptance criterion, reported as-is: "F=4 must embed at ~100%" is not reachable, and the reason is geometry, not the embedder. The tuned generator (1a) routinely makes two flights conflict at all K levels; their one-hot triangles plus the level-matching edges form a triangular prism, which cannot satisfy the margin rules (edges ≤ 0.95·R_b, non-edges ≥ 1.05·R_b, 5 µm floor) in 2D — provable for the aligned case (it would need a triangle side of ≥ 11.8 µm against a 7.6 µm ceiling) and confirmed by 500-restart searches (never below 6 violations). The 3 "unexplained" F=4 failures are cross-flight conflict triangles at shared levels — same obstruction family; each also resisted 200-restart deep searches. The acceptance criterion's premise ("max degree ~5 ⇒ failures are embedder bugs") is therefore false on this ensemble; per instruction 5, nothing was tuned to make the numbers look better. The study CSV now attributes every failure (deg≥6 / prism / other), which was the criterion's actual intent.

Verification

  • ruff check ., mypy contrail_env, pytest (29 tests) — all green
  • python run.py --flights 5 --options 3 --seeds 10 --shots 1000 — spread shown above
  • python -m contrail_env.embedding_study --csv embedding.csv --seeds 20 — ladder above, 7 min 44 s total

… drop flights

Dropping a flight saves its full option cost (~c_max), not the cost
spread, so A = 2*(1 + c_max - c_min) made infeasible ground states
optimal whenever spread << cost (alpha=0: 40/40 seeds infeasible).
Regression test covers the exact broken case.
…tric

- problem.py: 5 blobs spanning 2-3 levels, central-band routes, wider
  blobs. F=4 conflict counts: median 3->6, zero-conflict seeds 3/20->0/20.
- exact.py: raw_metrics (unrepaired QUBO energies — repair cannot game
  them), solve_greedy names the repair heuristic as an honest competitor,
  repair gains deterministic promote-and-retry (0/2000 failures at the
  new density; single pass failed on dense seeds).
- run.py: --min-conflicts substitution (visible, deterministic, seed+1000),
  raw_best_E/raw_mean_E columns, primary raw-energy + secondary
  repaired-cost summary tables.
…to geometry

- refine_embedding: vectorized force-directed repair (springs on missing
  edges, repulsion on spurious/crowded pairs), per-node displacement cap
  (uncapped sums diverged to inf — and NaN layouts passed check_embedding
  as 'valid'), antisymmetric directions for coincident nodes, seeded
  annealing kicks to escape tug-of-war equilibria, stagnation early-out.
- embed(): greedy init + 8 seeded jittered restarts; single entry point
  for both the solver register builder and the study.
- check_embedding: vectorized; non-finite coordinates now report invalid.
- Study: degree computed on the independence graph (the graph actually
  embedded), logs n_nodes_deg_ge_6, n_restarts_used, refine_iters, and
  n_all_level_pairs — two flights conflicting at every level form a
  triangular prism that provably cannot satisfy the margin rules in 2D,
  a low-degree geometric obstruction the degree diagnostic misses.
- Tests: 5-leaf star embeds, 7-leaf star still fails (geometry), embed
  deterministic per seed, refinement strictly reduces violations.
@jaewonyun1234
jaewonyun1234 merged commit c5feb48 into main Aug 6, 2026
1 check failed
@jaewonyun1234
jaewonyun1234 deleted the fixes/benchmark-validity branch August 6, 2026 15:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant