Repository navigation
feat(adr-001): sparse solve witness — per-entry residual audit - #41
Merged
Merged
Conversation
Closes ADR-001 open question #3: "does solve_on_change need a witness?" YES, but a full A·x matvec costs O(nnz(A)) and would dominate the SubLinear orchestrator's own cost. This module's twist: verify ONLY the closure entries. Restricted residual: r_i = b[i] - Σ_j A[i,j] · x_new[j] for each entry row i where x_new[j] uses the orchestrator's value if j is in the closure, and prev_solution[j] otherwise (the closure boundary contract). Audit passes iff max_i |r_i| ≤ tolerance · max(1, ‖b‖_∞). Cost is O(|entries| · avg_row_nnz) — same complexity class as the orchestrator, independent of n. Lands: src/witness.rs (new, 304 LOC) pub fn verify_sparse_solution(matrix, prev, b, entries, tolerance) -> Result<WitnessReport> pub struct WitnessReport { max_residual: Precision, threshold: Precision, ok: bool, worst_row: Option<usize>, } pub struct VerifySparseSolutionOp; // SubLinear class marker src/lib.rs Re-exports verify_sparse_solution + WitnessReport + VerifySparseSolutionOp. Use cases: - Audit mode in development: catch real solver bugs (witness failure on strict-DD input = bug, not just tolerance miss). - Trust-but-verify in production: gate downstream agent actions on witness success at essentially the same cost as the solve. - Regression guards: property-test fuzz deltas + assert witness. Tests: 7 covering: - Op marker SubLinear class - Pass on genuine SubLinear output (strong-DD ring, depth=8) - Fail on a deliberately corrupted entry - Empty entries trivially pass - DimensionMismatch on wrong-sized prev / b - OOB indices silently dropped (no panic) Co-Authored-By: claude-flow <ruv@ruv.net>
The witness PR added an Op marker (VerifySparseSolutionOp = SubLinear) without updating .github/complexity-baseline.txt — the regression-guard CI job correctly tripped. Regenerated via scripts/extract_complexity_classes.sh. Also picks up PlanBudget-adjacent declarations that landed in #40 if the script discovers them (none in this iteration — PlanBudget's class lives on individual ComplexityClass instances passed by callers, not on the budget struct itself). Co-Authored-By: claude-flow <ruv@ruv.net>
This was referenced May 19, 2026
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
Closes ADR-001 open question #3: "does `solve_on_change` need a witness?" YES — but a full `A·x` matvec costs `O(nnz(A))` and would dominate the SubLinear orchestrator's own cost. This PR's refinement: verify ONLY the closure entries.
Math
For each entry row `i` returned by the SubLinear orchestrator:
```
r_i = b[i] - Σ_j A[i,j] · x_new[j]
```
where `x_new[j]` uses the orchestrator's value if `j` is in the closure, and `prev_solution[j]` otherwise (the closure-boundary contract). Audit passes iff `max_i |r_i| ≤ tolerance · max(1, ‖b‖_∞)`. Cost is `O(|entries| · avg_row_nnz)` — same complexity class as the orchestrator.
API
```rust
pub fn verify_sparse_solution(
matrix: &dyn Matrix,
prev_solution: &[Precision],
b: &[Precision],
entries: &[(usize, Precision)],
tolerance: Precision,
) -> Result
pub struct WitnessReport {
pub max_residual: Precision,
pub threshold: Precision,
pub ok: bool,
pub worst_row: Option,
}
pub struct VerifySparseSolutionOp; // SubLinear class marker
```
Use cases
Test plan
🤖 Generated with claude-flow