Skip to content

Commit c0a8e39

Browse files
avrabeclaude
andauthored
feat(nc): PMOO/LUDB bound via good_lp for tree-shaped multiplexing (v0.9.3 NC tightness #2) (#203)
Closes the external reviewer's NC top-5 #2 (credibility gap with RTaW-Pegase): adds an opt-in Pay-Multiplexing-Only-Once / Bisti-LUDB ("Linear Upper Delay Bound") path in `spar-network::pmoo` that the WCTT pass invokes when the topology is tree-shaped (one tagged flow, ≥ 2 competing flows on contiguous sub-paths of the tagged tandem, all hops FIFO/Priority). The LP is set up via `good_lp` with the HiGHS backend already vendored for the deployment solver. On the canonical zonal/automotive multiplexing pattern (one switch, many sources to one sink) the bound is 23-27% tighter than today's per-hop SFA chain on the in-tree fixtures, with 30-60% reported in the literature for larger fan-ins. On LP infeasibility (`Σ ρ ≥ R_h` at any hop) the WCTT pass silently falls back to the existing SFA chain — bound never worsens. Numerical comparison from the in-tree fixtures: 3-hop / 3-competing: PMOO = 98_571_429 ps SFA = 135_843_430 ps tightening = 27.4% Zonal 5-source: PMOO = 159_000_000 ps SFA = 208_488_000 ps tightening = 23.7% API surface in `spar-network::pmoo`: - `TaggedFlow { alpha, path }`, `CompetingFlow { alpha, path }` - `PmooBound { delay_ps, model_size, solve_time_us }` - `LpError::{Infeasible, OutOfRange, EmptyPath, NonContiguous, SolverFailed}` - `ludb_bound(tagged, competing, services) -> Result<PmooBound, LpError>` Wiring: - `WcttAnalysis` becomes a configurable struct with `pmoo: bool` (default `false` → byte-identical to v0.9.2). `WcttAnalysis::with_pmoo()` enables the opt-in path. - New `pmoo_or_sfa(stream, all_streams, switch_type, service_for_bus)` helper in wctt.rs validates eligibility (all-FIFO chain, ≥ 2 competitors, contiguous sub-paths) and dispatches to `ludb_bound`. - New `WcttPmooBound` Info diagnostic reports method=ludb, PMOO delay, SFA delay for comparison, tightening %, and LP solve time. - New `--pmoo` CLI flag on `spar analyze` opts in. `AnalysisRunner:: register_all_except_wctt()` lets the CLI swap in the PMOO-configured `WcttAnalysis` without duplicating the SFA pass. Tests: - 10 new unit tests in `spar-network/src/pmoo.rs`: single-hop / no-competing baseline (PMOO ≡ SFA), 2-hop tandem 1 competing flow (PMOO ≤ SFA), 3-hop / 3-competing (PMOO ≪ SFA, ≥ 5% tightening asserted), LP infeasibility, validation errors, model-size and solve-time reporting, single-flow tandem reduces to pay-burst-once, canonical zonal 5-source reference comparison. - 2 new round-trip tests in `spar-analysis/src/wctt.rs`: `pmoo_flag_off_emits_no_pmoo_diagnostic` (default v0.9.2 byte- identical), `pmoo_flag_on_emits_pmoo_diagnostic_for_eligible_streams` (≥ 1 `WcttPmooBound` with method=ludb). Quality gates: - cargo build --workspace: clean - cargo test --workspace: 851 spar-analysis tests pass (was 849; +2 round-trip), 87 spar-network tests pass (was 77; +10 PMOO), no regressions - cargo clippy --workspace --all-targets -- -D warnings: clean - cargo fmt --all -- --check: clean - rivet validate: PASS Artifacts: - REQ-NETWORK-014 in artifacts/requirements.yaml - TEST-NC-PMOO in artifacts/verification.yaml (links: satisfies REQ-NETWORK-014) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 6fbcada commit c0a8e39

11 files changed

Lines changed: 1190 additions & 29 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

artifacts/requirements.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1700,6 +1700,29 @@ artifacts:
17001700
status: implemented
17011701
tags: [network, wctt, rta, coupling, v092]
17021702

1703+
- id: REQ-NETWORK-014
1704+
type: requirement
1705+
title: PMOO/LUDB bound for tree-shaped multiplexing
1706+
description: >
1707+
v0.9.3 adds an opt-in Pay-Multiplexing-Only-Once / Bisti-LUDB
1708+
("Linear Upper Delay Bound") path in `spar-network::pmoo` that
1709+
the `WcttAnalysis` pass invokes when (a) the stream's tandem is
1710+
composed entirely of FIFO/Priority switches, (b) every other
1711+
stream that crosses any hop on the tandem does so on a contiguous
1712+
sub-path (PMOO precondition), and (c) ≥ 2 such competing flows
1713+
exist (otherwise PMOO ≡ SFA). The LP is set up via `good_lp` with
1714+
the HiGHS backend already vendored for the deployment solver;
1715+
on infeasibility (`Σ ρ ≥ R_h` at any hop) we silently fall back
1716+
to per-hop SFA. New `WcttPmooBound` Info diagnostic reports the
1717+
method (=ludb), the PMOO delay, the SFA delay for comparison,
1718+
the percentage tightening, and the LP solve time. Closes the
1719+
external reviewer's NC top-5 #2 (credibility gap with RTaW-Pegase):
1720+
typical 30-60% tightening on automotive zonal aggregations,
1721+
observed 23-27% on the in-tree fixtures. CLI `--pmoo` flag opts
1722+
in; default off → byte-identical to v0.9.2.
1723+
status: implemented
1724+
tags: [network, wctt, pmoo, ludb, lp, v093]
1725+
17031726
# ── Track G: spar-insight discrepancy assistant (v0.9.0) ──────────
17041727

17051728
- id: REQ-INSIGHT-001

artifacts/verification.yaml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2247,6 +2247,37 @@ artifacts:
22472247
- type: satisfies
22482248
target: REQ-NETWORK-011
22492249

2250+
- id: TEST-NC-PMOO
2251+
type: feature
2252+
title: PMOO/LUDB bound is tighter than SFA for tree-shaped multiplexing
2253+
description: >
2254+
Ten unit tests in `spar-network/src/pmoo.rs` plus two CLI-flag
2255+
round-trip tests in `spar-analysis/src/wctt.rs` exercise the
2256+
v0.9.3 PMOO/LUDB path. The PMOO unit tests cover: (a) single-hop
2257+
no-competing baseline (PMOO ≡ SFA), (b) 2-hop tandem 1 competing
2258+
flow joining at hop 2 (PMOO ≤ SFA, strictly tighter on the
2259+
fixture), (c) 3-hop / 3-competing convergent fixture (PMOO ≪ SFA,
2260+
≥ 5 % tightening asserted; observed 27.4 %), (d) LP infeasibility
2261+
surfaces as `Err(LpError::Infeasible)` for SFA fallback,
2262+
(e) empty-path / out-of-range / non-contiguous validation, (f) LP
2263+
model size and solve-time reporting, (g) single-flow tandem
2264+
reduces to "pay-burst-once" closed form, (h) the canonical
2265+
automotive-zonal 5-source pattern (observed 23.7 % tightening).
2266+
CLI round-trip tests confirm `WcttPmooBound` only fires with the
2267+
`--pmoo` flag (`pmoo_flag_off_emits_no_pmoo_diagnostic` and
2268+
`pmoo_flag_on_emits_pmoo_diagnostic_for_eligible_streams`),
2269+
preserving v0.9.2 byte-identical default output.
2270+
fields:
2271+
method: automated-test
2272+
steps:
2273+
- run: cargo test -p spar-network --lib -- pmoo
2274+
- run: cargo test -p spar-analysis --lib -- pmoo_flag
2275+
status: passing
2276+
tags: [v0.9.3, network, wctt, pmoo, ludb, lp]
2277+
links:
2278+
- type: satisfies
2279+
target: REQ-NETWORK-014
2280+
22502281
- id: TEST-INSIGHT-DISCREPANCY
22512282
type: feature
22522283
title: spar-insight CTF parser + 5-kind discrepancy detection

crates/spar-analysis/src/lib.rs

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,77 @@ impl AnalysisRunner {
195195
self.register(Box::new(WeightPowerAnalysis));
196196
self.register(Box::new(BusBandwidthAnalysis));
197197
self.register(Box::new(FeatureGroupCheckAnalysis));
198-
self.register(Box::new(WcttAnalysis));
198+
self.register(Box::new(WcttAnalysis::default()));
199+
}
200+
201+
/// Register all instance-level analyses **except** [`wctt::WcttAnalysis`].
202+
///
203+
/// Used by the CLI's `--pmoo` flag path to register a custom-
204+
/// configured `WcttAnalysis` (with PMOO/LUDB enabled) without
205+
/// duplicating the v0.9.2 SFA pass. See
206+
/// `spar-cli/src/main.rs::run_all_analyses_with_pmoo`.
207+
pub fn register_all_except_wctt(&mut self) {
208+
use ai_ml::AiMlAnalysis;
209+
use arinc653::Arinc653Analysis;
210+
use binding_check::BindingCheckAnalysis;
211+
use binding_rules::BindingRuleAnalysis;
212+
use bus_bandwidth::BusBandwidthAnalysis;
213+
use classifier_match::ClassifierMatchAnalysis;
214+
use completeness::CompletenessAnalysis;
215+
use connection_rules::ConnectionRuleAnalysis;
216+
use connectivity::ConnectivityAnalysis;
217+
use direction_rules::DirectionRuleAnalysis;
218+
use emv2_analysis::Emv2Analysis;
219+
use emv2_stpa_bridge::Emv2StpaBridgeAnalysis;
220+
use feature_group_check::FeatureGroupCheckAnalysis;
221+
use flow_check::FlowCheckAnalysis;
222+
use flow_rules::FlowRuleAnalysis;
223+
use hierarchy::HierarchyAnalysis;
224+
use latency::LatencyAnalysis;
225+
use memory_budget::MemoryBudgetAnalysis;
226+
use modal_rules::ModalRuleAnalysis;
227+
use mode_check::ModeCheckAnalysis;
228+
use mode_reachability::ModeReachabilityAnalysis;
229+
use mode_rules::ModeRuleAnalysis;
230+
use property_rules::PropertyRuleAnalysis;
231+
use resource_budget::ResourceBudgetAnalysis;
232+
use rta::RtaAnalysis;
233+
use scheduling::SchedulingAnalysis;
234+
use subcomponent_rules::SubcomponentRuleAnalysis;
235+
use weight_power::WeightPowerAnalysis;
236+
use wrpc_binding::WrpcBindingAnalysis;
237+
238+
self.register(Box::new(AiMlAnalysis));
239+
self.register(Box::new(ConnectivityAnalysis));
240+
self.register(Box::new(HierarchyAnalysis));
241+
self.register(Box::new(CompletenessAnalysis));
242+
self.register(Box::new(DirectionRuleAnalysis));
243+
self.register(Box::new(ClassifierMatchAnalysis));
244+
self.register(Box::new(BindingCheckAnalysis));
245+
self.register(Box::new(BindingRuleAnalysis));
246+
self.register(Box::new(FlowCheckAnalysis));
247+
self.register(Box::new(FlowRuleAnalysis));
248+
self.register(Box::new(ModeCheckAnalysis));
249+
self.register(Box::new(ModeRuleAnalysis));
250+
self.register(Box::new(ModalRuleAnalysis));
251+
self.register(Box::new(PropertyRuleAnalysis));
252+
self.register(Box::new(ConnectionRuleAnalysis));
253+
self.register(Box::new(SubcomponentRuleAnalysis));
254+
self.register(Box::new(SchedulingAnalysis));
255+
self.register(Box::new(RtaAnalysis));
256+
self.register(Box::new(LatencyAnalysis));
257+
self.register(Box::new(MemoryBudgetAnalysis));
258+
self.register(Box::new(ResourceBudgetAnalysis));
259+
self.register(Box::new(Emv2Analysis));
260+
self.register(Box::new(Emv2StpaBridgeAnalysis));
261+
self.register(Box::new(Arinc653Analysis));
262+
self.register(Box::new(WrpcBindingAnalysis));
263+
self.register(Box::new(ModeReachabilityAnalysis));
264+
self.register(Box::new(WeightPowerAnalysis));
265+
self.register(Box::new(BusBandwidthAnalysis));
266+
self.register(Box::new(FeatureGroupCheckAnalysis));
267+
// WcttAnalysis intentionally omitted — caller registers a
268+
// PMOO-configured variant.
199269
}
200270

201271
/// Return the number of registered analyses.

0 commit comments

Comments
 (0)