Skip to content

Control arms, objective-family ablation, and store hygiene - #36

Merged
SimonCrouzet merged 6 commits into
mainfrom
feat/benchmark-controls
Sep 1, 2026
Merged

Control arms, objective-family ablation, and store hygiene#36
SimonCrouzet merged 6 commits into
mainfrom
feat/benchmark-controls

Conversation

@SimonCrouzet

Copy link
Copy Markdown
Owner

Adds control arms, an objective ablation, and a store-hygiene tool. results/ is gitignored, so no data is in the diff.

What it adds

  1. Mask-aware GA (genetic-masked arm): a variation operator that constructs feasible offspring through the environment's own forward_mask — one legal substitution at a time — instead of rejection-sampling whole offspring and discarding infeasible ones.
  2. Second predicate family (width2 tier): runs feasibility under a width-2 contact predicate in place of its Ehrlich-adjacency rule.
  3. Rounds/budget rung: adds the 6×64 rung to the fixed-384-budget rounds curve, so it has three points at equal total budget rather than two.
  4. label_noise_std on the ensemble: a knob that adds Gaussian noise to the surrogate's fit targets, guarded to be a no-op at the default 0.0.
  5. Objective-family tier: runs the training objectives (tb / contrastive / subtb / db / fldb) plus a learned-P_B arm on the constrained tasks. learn_backward is threaded through the policy factory (previously only on SequencePolicy); the tier reuses the existing gfn-tb/gfn-subtb cells so only the four new arms run.
  6. experiments/bless_merge.py: restamps records staled by these edits' fingerprint changes, each of which is a guarded no-op or a new-arm-only path for existing arms.

The arm-set test is updated to include genetic-masked. ruff and mypy-strict pass.

🤖 Generated with Claude Code

SimonCrouzet and others added 6 commits August 31, 2026 12:02
width2_tasks()/width2_tier() re-run `feasibility` under a width-2 contact
predicate (ContactPredicate.TRACTABLE_WIDTH) in place of its Ehrlich-adjacency
rule, so the headline mask-vs-rejection comparison exists on a second
predicate family, not only in support_study's diagnostic instance.

protocol-alde is left out: EhrlichLandscape._evaluate zeroes reward by its own
transition matrix independent of Task.feasibility, and an unmasked arm that
doesn't consult the injected predicate crashes re-anchoring the moment it
picks an infeasible best. support_study avoids this by being reanchor=False
everywhere; feasibility-w2 inherits that safety. Fixing re-anchoring against
an injected predicate is out of scope here.

Per-contact density is calibrated by Monte Carlo (not matched to the source
task's own transition_density, which leaves ~0 of the local neighbourhood
feasible at width 2) so genetic and genetic-feasible both have something to
search from every round.
GeneticAlgorithm gains construct_feasible, a variation operator that walks
each bred offspring toward its target through the environment's own
forward_mask -- the identical mask a masked GFlowNet policy is scored
against -- rather than accepting or rejecting the whole target the way
feasible_only does. An edit the mask forbids at its turn is dropped;
nothing is redrawn and no whole offspring is discarded. Registered as the
genetic-masked arm.

This is the second control for the feasibility claim, alongside
genetic-feasible (rejection): construction is available to a GA too, so a
GFlowNet's margin over a GA that is *also* masked is attributable to
learning rather than to rejection's mechanics. Also adds
construction_attempted/construction_dropped to the result schema, the
construction-side analogue of draws_attempted/draws_rejected.

Campaign run at 100 seeds on feasibility, protocol-alde and
protocol-evolvepro (results/, gitignored, not part of this commit).
round_sweep halves batch size from 96, which lands exactly on 4x96 and
8x48 at the 384-assay budget and skips every split between them -- a
two-point curve standing in for a trend. 6x64 is the one integer split
the halving cannot reach, so it is added explicitly in rounds_curve
rather than by changing what round_sweep enumerates for its other
callers.

Ran all three rungs at 50 seeds (results/rounds-{4x96,6x64,8x48}). The
third point does not confirm a trend -- it breaks the monotonicity a
two-point read would have implied. GFlowNet regret is 0.383 / 0.345 /
0.381 across 4x96 / 6x64 / 8x48, and the margin over every baseline
(genetic, cmaes, genetic-feasible) peaks at the middle rung rather than
growing or shrinking with rounds count.
The mask-aware construction control adds an arm, and the ladder test pins
the arm set exactly so that adding one is a deliberate edit rather than a
silent drift. Record it in the 'ours, not published' group.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013WkACTq6CTiwkqmuv6Mhet
Carries the training-objective comparison from the diagnostic landscape onto
the tasks whose graph actually constrains construction, and adds a learned-P_B
rung. Threads learn_backward through the policy factory (it was on
SequencePolicy but never exposed), registers gfn-tb-learnpb, and adds an
objective-family tier over feasibility/protocol-alde/protocol-evolvepro that
reuses the headline gfn-tb/gfn-subtb cells so only the four new arms cost a
campaign.

Also adds experiments/bless_merge.py, the merge-hygiene tool that restamps the
records the solidify-paper-controls edits staled but provably could not alter.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013WkACTq6CTiwkqmuv6Mhet
@SimonCrouzet
SimonCrouzet merged commit 4fc4cb8 into main Sep 1, 2026
13 checks passed
@SimonCrouzet
SimonCrouzet deleted the feat/benchmark-controls branch September 1, 2026 12:41
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