Skip to content

Experiments: what breaks a PH W-oscillation cycle (sizes)#787

Open
DLWoodruff wants to merge 4 commits into
Pyomo:mainfrom
DLWoodruff:w-oscillation-experiments
Open

Experiments: what breaks a PH W-oscillation cycle (sizes)#787
DLWoodruff wants to merge 4 commits into
Pyomo:mainfrom
DLWoodruff:w-oscillation-experiments

Conversation

@DLWoodruff

Copy link
Copy Markdown
Collaborator

Self-contained research experiments (not a product feature, not wired into
CI) establishing which PH interruption actually breaks a W-vector oscillation on
the sizes model. This is the empirical evidence behind scaling #780 back to
slamming as the only interruption remedy.

What

examples/sizes/w_oscillation_experiments/ — a harness that reuses the merged
detection primitives (PR1, #778) and scores each would-be remedy by two metrics:

  • zc — how many nonants the zero-crossings detector still flags. Scale-free
    (counts sign changes), so it can be fooled by anything that freezes W.
  • primal gap sum_s p_s |x_s - xbar| — the ground truth: low only when the
    scenarios actually agree.

Run (needs a MIP solver; gurobi_persistent by default):

python run_experiments.py

Findings (sizes-3, 60 iters; see results/summary.md)

lever outcome
plain PH / W-damping / W-reset / rho jitter still cycling
rho reduction (geometric) decoupled — primal gap explodes to ~24,000 while W freezes
fix (slam analogue) converges (gap → 0 @ iter 35)
uniform rho ≥ 0.1 converges (rho ≤ 0.01 cycles)
  • Only fix (changing the problem structure) and a larger rho converge the
    primal gap. Every state-perturbing move leaves the cycle intact, decouples the
    scenarios, or worsens it.
  • Geometric rho reduction is the cautionary case: zc drops while the gap
    explodes — a detector false positive (driving rho → 0 freezes W).
  • Root cause: the cycle is a small-rho artifact; sizes's _rho_setter
    uses rho = cost × 0.001, and rho ≥ 0.1 converges with no interruption at all.
  • Metric lesson: a W-only oscillation detector can be gamed by anything that
    freezes W; confirming an interruption converged (vs decoupled) needs a
    primal-side check like the gap used here.

Relationship to #780

#780 ships the W-oscillation interruption with slamming as the only remedy
these experiments are why (they showed the dual-side remedies do not break the
cycle). The extension's design doc (§9) points here for the evidence. Kept out
of CI because it needs a MIP solver.

🤖 Generated with Claude Code

A self-contained research harness (not a product feature, not CI-wired)
that establishes which PH interruption actually breaks a W-vector
oscillation on the sizes model. This is the empirical evidence behind
shipping slamming as the only interruption remedy (see PR Pyomo#780 and
doc/designs/w_oscillation_design.md §9).

Reuses the merged PR1 detection primitives (zero_crossings_detect, etc.)
and scores every arm by two metrics: the detector's scale-free zc count
and the PH primal gap sum_s p_s |x_s - xbar| (the ground truth).

Findings (results/summary.md): only fixing a variable (the slam analogue)
and using a larger rho converge the primal gap. W-damping, W-reset, rho
reduction, and rho jitter all leave the cycle intact, decouple the
scenarios (gap explodes while W freezes -- a detector false positive), or
worsen it. Root cause: the cycle is a small-rho artifact; sizes'
_rho_setter uses cost x 0.001, and rho >= 0.1 converges with no
interruption at all.

- w_osc_experiment_ext.py: PH extension (detect / intervene / record)
- _run_one_arm.py: worker (PH-only hub on sizes, one arm)
- run_experiments.py: orchestrator (all arms -> summary + per-iter CSV)
- README.md: writeup; results/: captured summary + per-iteration CSV

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 76.16%. Comparing base (a49f6f3) to head (f0b2ebb).
⚠️ Report is 6 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #787      +/-   ##
==========================================
+ Coverage   76.00%   76.16%   +0.16%     
==========================================
  Files         169      169              
  Lines       22096    22224     +128     
==========================================
+ Hits        16794    16928     +134     
+ Misses       5302     5296       -6     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

DLWoodruff and others added 2 commits July 8, 2026 17:49
Extend the sizes cycle-breaking harness with a prox-only lever and a
solution-quality axis, answering not just "does it stop the cycle" but
"is the answer any good, in x and in W".

New intervention `prox_boost` scales only the quadratic penalty (the
`prox_on` coefficient on ProxExpr), leaving rho -- and hence the dual
update W += rho*(x-xbar) -- untouched. Four schedules:
  - one-shot / re-firing: a temporary window (revert after N iters, or
    re-open on each re-detected cycle). Transient only: the small-rho
    cycle snaps back the moment the penalty relaxes.
  - held: near-permanent boost; damps the cycle to a residual but plateaus.
  - escalating: hold and ramp the multiplier (x10 -> x160) while the gap
    persists. The only prox-only schedule that converges outright
    (primal gap -> 0, PH self-terminates ~iter 32).

New section 4 measures quality vs the monolithic EF optimum z*: the inner
bound (expected cost of committing to the consensus xbar; x-quality) via
fix-to-xbar + solve + Eobjective, and the outer/Lagrangian bound from the
final W (W-quality) via post_solve_bound. Per-iteration CSV also logs the
live prox multiplier.

Finding: convergence and solution quality are different axes. A larger
uniform rho converges fastest but lands the worst decision (x-gap ~3%) and
an overshot bound; the native small-rho cycle never "converges" yet orbits
the optimum (near-optimal x, tightest W). Escalating prox is the sweet
spot for the decision (x-gap ~0.3%, keeps the small dual step) but freezes
W off-optimum -> loose dual certificate. So: for a good decision,
escalating prox beats raising rho; for a tight bound, nothing beats
letting the cycle run.

Default budget bumped to 80 iters so `python run_experiments.py`
reproduces the committed summary. README and summary rewritten to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…break it)

Test mpi-sppy's built-in smoothing (cfg.smoothing: smoothed PH, penalty
p = smoothing_rho_ratio * rho anchoring each scenario's x to its own EMA
trajectory z, memory beta) as a cycle-breaker on sizes. Wired through the
worker (set on cfg before ph_hub) and added as a group-1 arm.

Finding: smoothing does NOT break the cycle -- it AMPLIFIES it. At native
(small) rho the primal gap grows to ~968 at ratio 0.1 (vs plain PH ~34),
worse as the ratio rises, and numerically catastrophic by ratio 100. It
anchors each scenario to its own lagged EMA, a per-scenario momentum that
fights consensus, so zc stays high AND the gap explodes -- distinct from
the "decoupled" mode (rho reduction: zc low, gap high). The center still
orbits the optimum (rounded average is a fine decision, x-gap ~0.65%), so
it is strictly worse than plain PH: bigger swings, no convergence.

Refine the >500-gap reading to split "decoupled (gap exploded)" (zc low)
from "cycle amplified" (zc high) so both failure modes read correctly.
README/summary updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@DLWoodruff DLWoodruff marked this pull request as ready for review July 9, 2026 17:54
Add a `w_average` intervention: on a detected cycle, replace each cycling
nonant's W, in every scenario, with that scenario's mean W over the captured
cycle (a warm restart to the cycle's centre rather than to zero, the
counterpart to w_reset). Averaging is a per-scenario linear combination of
past iterates, so it trivially preserves PH's dual invariant sum_s p_s W_s = 0:
every buffered W already has expectation zero and expectation is linear.

Empirically it does not break the sizes cycle: restarting W at the centre kicks
the primal gap ~3 -> ~5200, then slowly re-damps but re-enters the same
oscillation (last-10 gap ~113) -- another fixed-magnitude perturbation that
keeps the cycle's near-optimal profile (x-gap +0.27%, W-gap +0.7%). README §1/§4
tables, a "why it is a legal PH move" section, and conclusion #1 updated;
results/summary.md and interventions_by_iteration.csv regenerated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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