Skip to content

Flex ranks: coherence read-outcome diagnostic (#742) - #816

Merged
DLWoodruff merged 6 commits into
Pyomo:mainfrom
DLWoodruff:flex-layout-exchange-coherence-diag
Aug 10, 2026
Merged

Flex ranks: coherence read-outcome diagnostic (#742)#816
DLWoodruff merged 6 commits into
Pyomo:mainfrom
DLWoodruff:flex-layout-exchange-coherence-diag

Conversation

@DLWoodruff

@DLWoodruff DLWoodruff commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Coherence read-outcome diagnostic for the unequal-rank (flexible-ranks) path, localized to the multi-source reader.

Closes #742.

(This PR originally also replaced the unequal-rank layout exchange with a two-level scheme (#726). That change has been dropped: the flat fullcomm.allgather runs once at startup, its latency is O(log N), and its O(N) per-rank data volume is the result itself — every rank needs every peer's layout, so any replacement still delivers O(N) data per rank and only changes the collective's participant pattern. At even 10,000 total ranks the flat exchange moves a few MB per rank, milliseconds against hours of solves. #726 is closed won't-fix with that rationale, and the design doc's Option D note, Phase-2 bullet, and release-gate list now record it.)

Coherence read-outcome diagnostic (#742)

The unequal-rank multi-source reader now counts every multi-source read (>= 2 sources; a single source cannot straddle a publish) into one of five buckets at the reduce_source_write_ids call site:

  • new_accepted — coherent, advanced, used
  • not_new — coherent, but the sender has not published (slow sender)
  • rejected_incoherent — this rank's sources disagreed (the fundamental coherence miss: the read straddled a publish)
  • rejected_cross_reader — this rank's sources agreed, the collective cross-reader check rejected (typically the shadow of some other reader rank's straddle)
  • accepted_mixed — relaxed field, blended assembly used

The reported miss rate is every read a straddled publish cost — rejected_incoherent + rejected_cross_reader + accepted_mixed, equivalently everything that was neither a clean accept nor a clean nothing-to-take. Counting only the locally-detected misses would divide the rate by the reader-rank count, since one straddle on an R-rank reader records one rejected_incoherent and R-1 rejected_cross_reader.

so an infrequently-reporting bounds cylinder can be diagnosed as coherence problem (rejected_incoherent/accepted_mixed dominate) vs. slow upstream sender (not_new dominates). Counting is always-on (two integer increments per multi-source read); each cylinder prints a per-field summary at finalization (aggregated across its ranks with one gather, rank-0-gated, only for fields that did multi-source reads — equal-rank runs print nothing). An opt-in periodic line (coherence_diagnostics_period in the cylinder's opt_kwargs options) supports live debugging, and the counters are exposed as SPCommunicator.coherence_counters. Sample from the farmer 4+2 integration test:

coherence diagnostic [LagrangianOuterBound] DUALS: total=1812, new_accepted=38, not_new=1664, rejected_incoherent=50, rejected_cross_reader=60, accepted_mixed=0, miss rate=6.07%

Tests

  • test_flex_coherence_policy.py: the deterministic stub reader now also pins the counter buckets (strict mixed → rejected_incoherent, relaxed mixed → accepted_mixed, coherent no-advance → not_new, buckets partition the total).
  • test_flexible_rank_duals.py: after both split directions, asserts the buckets partition the total, that the strict DUALS field never accepts a blended read, and that the 4+2 bound came from accepted multi-source reads (in 2+4 the spoke's slices can nest inside the hub's halves, leaving every read single-source, so counter presence is only guaranteed in 4+2). All existing flex MPI tests (cylinders, duals, xhat, xfeas, xhat_multistage, extension_fields, spwindow_multisource) pass locally at -np 6.

The design doc moves the #742 diagnostic from "possible future work" into the coherence section.

Deliberately not in this PR: #749 (reduced_costs spoke at flexible ratios, Phase 7) — behavioral work on the spoke itself, better reviewed separately.

🤖 Generated with Claude Code

…outcome diagnostic (Pyomo#742)

Replace the unequal-rank path's interim fullcomm.allgather buffer-layout
exchange with a scalable two-level scheme (allgather within each cylinder,
allgather across one anchor rank per cylinder, broadcast within each
cylinder), chosen over local-compute because extensions register send
fields dynamically. Localized to how strata_buffer_layouts is populated
at startup; the multi-source reader is untouched, and the equal-rank
path keeps its strata_comm allgather verbatim.

Add an always-on per-field read-outcome counter to the unequal-rank
multi-source reader (new_accepted / not_new / rejected_incoherent /
rejected_cross_reader / accepted_mixed), so an infrequently-reporting
bounds cylinder can be diagnosed as a coherence problem vs. a slow
upstream sender. Each cylinder prints a per-field summary at
finalization (aggregated across its ranks, rank-0-gated, only for
fields that did multi-source reads); an opt-in periodic line
(coherence_diagnostics_period in the spcomm options) supports live
debugging; counters are exposed as SPCommunicator.coherence_counters.

Tests: new test_flex_layout_exchange.py pins the two-level exchange
against fullcomm.allgather across several cylinder partitions (wired
into CI and run_coverage.bash); the deterministic coherence-policy stub
now also pins the counter buckets; test_flexible_rank_duals.py asserts
the counters partition the total and that the strict DUALS field never
accepts a blended read.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.34884% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.83%. Comparing base (99cd706) to head (d2edf5c).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
mpisppy/cylinders/spcommunicator.py 95.23% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #816      +/-   ##
==========================================
+ Coverage   76.79%   76.83%   +0.04%     
==========================================
  Files         177      177              
  Lines       23456    23498      +42     
==========================================
+ Hits        18012    18054      +42     
  Misses       5444     5444              

☔ 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.

Local-compute of remote layouts is not impossible in principle; the real
obstacle is that the library has no static declaration surface for
fields (layouts arise only from runtime register_send_field calls, and
custom drivers need not use Config), so it would require a new
mandatory declare-your-fields API plus re-deriving remote scenario
slices.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@DLWoodruff
DLWoodruff requested a review from bknueven August 5, 2026 21:19
The scaling analysis does not support replacing the flat
fullcomm.allgather layout exchange: it runs once at startup, allgather
latency is O(log N), and the O(N) per-rank data is the result itself
(every rank needs all N layouts), so the two-level scheme has the same
asymptotics and only changes the collective's participant pattern -- a
constant-factor effect on a cold path, at even 10,000 ranks a few MB
per rank against hours of solves.  Pyomo#726 will be closed won't-fix; the
design doc's Option D note, Phase-2 bullet, and release-gate list now
record that rationale.  The coherence read-outcome diagnostic (Pyomo#742)
is unchanged and remains this branch's content.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@DLWoodruff DLWoodruff changed the title Flex ranks: scalable two-level layout exchange (#726) and coherence read-outcome diagnostic (#742) Flex ranks: coherence read-outcome diagnostic (#742) Aug 8, 2026
@DLWoodruff
DLWoodruff marked this pull request as ready for review August 8, 2026 01:36
DLWoodruff and others added 3 commits August 7, 2026 18:48
Two review findings on the read-outcome diagnostic.

`coherence_diagnostics_period` could never take effect on a bound spoke
-- the cylinders whose infrequent reporting the diagnostic exists to
explain. `_BoundSpoke.__init__` and `InnerBoundSpoke.__init__` take no
`communicators` parameter, so the cylinder list WheelSpinner passes
positionally binds to their `options` parameter and is forwarded into
`communicators`, leaving `SPCommunicator.options` empty on every spoke
(this diagnostic was its first consumer, so nothing had noticed). Read
the period from `opt.options` instead, which is set from `opt_kwargs`
for every cylinder and already houses the sibling cylinder-wide debug
switches `trace_prefix` and `inspect_buffers_on_shutdown`.

The printed `miss rate` counted only `rejected_incoherent` and
`accepted_mixed`. One straddled publish on an R-rank reader records one
`rejected_incoherent` and R-1 `rejected_cross_reader`, so the reported
rate was the truth divided by the reader-rank count (a 2-rank spoke
showed 2.58% where 5.25% of read attempts had been rejected). The rate
now comes from a `coherence_miss_rate` helper that counts all three
non-clean outcomes -- equivalently, every read that was neither a clean
accept nor a clean nothing-to-take.

Relatedly, the two rejection buckets now split on whether this rank's
own sources disagreed rather than on the field's coherence policy. A
relaxed field can straddle a publish too: its floor then differs from a
peer reader's and the collective check rejects. That is this rank's
coherence miss, but `rejected_incoherent` was gated on `strict and
mixed` and so was unreachable for every relaxed field, which sent those
misses to `rejected_cross_reader` in contradiction of that bucket's
documented meaning.

Tests: a stub cylinder_comm lets the serial reader tests drive the
collective cross-reader check, pinning both the relaxed-field local
straddle and the peer-caused rejection to their buckets; the miss-rate
formula is pinned directly. Both new assertions fail against the old
behavior.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@DLWoodruff
DLWoodruff enabled auto-merge (squash) August 10, 2026 20:22
@DLWoodruff
DLWoodruff merged commit ebc0d78 into Pyomo:main Aug 10, 2026
30 checks passed
@DLWoodruff
DLWoodruff deleted the flex-layout-exchange-coherence-diag branch August 10, 2026 20:35
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.

Flexible ranks: per-field coherence read-outcome diagnostic (miss rate / why a bounds cylinder reports infrequently)

1 participant