Report BpTrellis no-path outcomes with a cause and run the escalation ladder on one model - #839
Merged
Merged
Conversation
… ladder on one model
…get, and render the mutant diagnostics verbatim
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Makes a BpTrellis no-path a per-shot outcome with a cause instead of only an error, and runs the escalation ladder as
(k, delta)rungs on one engine model with one BP refresh per shot.Before this change a no-path aborted a whole batch with
DecodingFailed, andescalation_ksbuilt a complete engine model per rung and re-ran the untouched-detector precheck and BP scoring for every rung.Engine (
pecos-trellis)TrellisDecoder::prepare(syndrome): dimension check, residual precheck (reports the lowest detector whose residual no probabilistic mechanism can change), one BP refresh into scratch. Readiness is cleared at entry and published only on completion, so a failed prepare can never decode the previous shot.TrellisDecoder::attempt(PruneParams { k, delta }): runs the DP on the prepared shot with the given pruning parameters. Parameters are validated against the decoder's metric mode first; a model configured with BP scoring but holding no BP graph (built exact, or a genuine N-ary kernel) rejects a pruned override rather than silently scoring prior-only; callingattemptwithout a ready prepare is a panic.decode_attemptis nowpreparefollowed byattemptat the configured parameters. The byte-frozen parity snapshots from Add byte-frozen bitwise parity snapshots for the trellis engine and BpTrellis facade #832 pass unchanged in both crates (regeneration was forbidden during this work), so the engine's numbers are bit-identical, including the streaming decoder, which passes its configured values through the same range function.TrellisDecodeAttempt::NoPathgainsdropped_states;TrellisResultgainsbp_runs; newbp_refreshes(),forced_observables(),prune_params().Facade (
pecos-bp-trellis)BpTrellisConfig::escalation: Vec<EscalationRung { k, delta }>replacesescalation_ks. Rungs are validated individually; there is no ordering or dominance rule between them (a narrower rung can succeed where a wider one fails, and a test pins that case). A non-empty ladder on an exact base is rejected, since an exact base never drops a state.BpTrellisDecoderholds oneTrellisDecoder.decode_outcomereturnsDecoded(TrellisResult)orNoPath(NoPathReport)with a cause:Residual { detector }(no attempt is run),Infeasible(an attempt found an empty column with nothing pruned, which proves the syndrome is unexplainable under the DEM; the ladder stops there, whether at the base or at a rung), orExhausted(every attempt ran out of states after pruning). The report carries the placeholder mask (the initial forced contribution of probability-one mechanisms, not a correction),rungs_tried,transitionssummed over attempts, andbp_runs/bp_secondscounted once per shot.decodekeeps its error contract; the message now names the cause.decode_batchis unchanged;decode_batch_outcomesis its per-shot-outcome twin. TheObservableDecoderimplementation stays strict, so theDecoderSpecandbatch.decode()routes are unchanged.Python (
pecos_rslib_exp)BpTrellisDecoder.from_dem(...)andbp_trellis(...)acceptescalation=[(k, delta), ...]alongside the keptescalation_ks=[...](rungs at the base delta). Supplying both raisesValueError. Spec equality is on the resolved rungs;reprround-trips, printing whichever form reproduces the ladder.decode_syndrome,decode_from_defectsanddecode_batch(shots, workers=1)takeon_no_path="raise"(default, unchanged behaviour) or"report", which returns aBpTrellisNoPathin place for a no-path shot (cause,detector,placeholder_flips,rungs_tried,transitions,bp_runs,bp_seconds,no_path=True). The mask getter is deliberately not namedobservable_flips, so code written for a decoded correction raisesAttributeErroron a report instead of silently consuming a placeholder. Other errors still raise with their shot index.BpTrellisResultgainsno_path=Falseandbp_runs.on_no_path; the spec route stays strict.Docs
The user guide carries the full description; the workflow page, the experimental guide and the crate README point to it.
Verification
cargo test --locked -p pecos-trellis -p pecos-bp-trellis -p pecos-frontier --no-fail-fast, both snapshot tests with the committed JSON untouched, coldcargo clippy --locked --workspace --all-targets -- -D warnings,cargo fmt --all -- --check, thepecos-rslib-expextension rebuild, the BpTrellis and Frontier Python suites (199 tests), andpre-commit run --all-filestwice: all clean.MUTANTS.mdwith its exact edit and killer; 71 mutants were applied in a workspace copy and all fail their named test. Two independent review arms (engine parity; API, Python surface and docs) found no correctness defect; the surviving mutants they found (a half-exact capability probe, a residual shot with BP enabled, and Python no-path getters asserted only against zeros) now have killers.Review follow-ups
Applied after review, each verified by execution rather than asserted:
Exhaustedmessage is pluralized, so a one-rung ladder readsafter 1 escalation rung. Its three pins and the two mutation rows whose recorded output embedded the old wording were re-run, not hand-edited.exhausted_ladder_propagates_the_final_rung_erroris renamed toexhausted_ladder_reports_the_attempted_rung_count: it pins the synthesizedExhaustedmessage and no longer propagates a rung's own error.placeholder_flips, matching the Rust field name. The suite now pins the separation in both directions withassert not hasattr(...).exp/pecos-trellis/tests/MUTANTS.mdwere missing theFirst failing line verbatimcolumn the file's own contract requires; both mutants were applied and re-run to fill them.exhausted_message_wrong's edit anchor was refreshed for the new format string, and two new guards (exhausted_message_always_plural,python_no_path_aliases_observable_flips) were added, applied and killed.Re-verification after these changes:
cargo fmt --all -- --check, coldcargo clippy --locked -p pecos-rslib-exp -p pecos-trellis -p pecos-bp-trellis --all-targets -- -D warnings,cargo test --locked -p pecos-trellis -p pecos-bp-trellis -p pecos-frontier --no-fail-fast(21 test binaries), the BpTrellis Python suites (48 tests) against a rebuilt extension, andpre-commit run --all-files: all clean, with the frozen snapshot JSON untouched.Independent cross-review of the follow-ups
The review follow-ups above were themselves cross-reviewed by a separate arm, scoped to those commits only. It returned REVISE with three guard defects, all confirmed by execution here and all fixed:
match="after 1 escalation rung"is a regex search, so it also acceptedrungs— a live always-plural mutant passed that test. It is nowr"after 1 escalation rung\\Z", and the same mutant fails it:Regex pattern did not match. Expected regex: 'after 1 escalation rung\\Z'.exhausted_message_always_pluralrow named an integration killer, but the file's documented command is--lib NAME, which runs 0 tests and exits 0 for that name (verified:0 passed; 0 failed; 13 filtered out). The killer is now listed among the--test bp_trellisexceptions, and the row records both its Rust and Python killers.`inside single-backtick code spans, which Markdown renders as the literal entity rather than a backtick, so the recorded output did not match the real compiler diagnostic. All 30 such cells across both files now use double-backtick delimiters; only delimiters changed, no quoted content.The reviewing arm found no defect in the runtime rename or the pluralization themselves, and confirmed rename completeness across bindings, the spec route, docs, tests, stubs and examples.
Not in this PR
(64, 100.0); turning it on is a separate decision.batch.decode()through the spec route needs a per-shot channel in the provider protocol (v2) and is not attempted here.