Releases: ruvnet/sublinear-time-solver
Release list
v1.7.2 — ADR-001 phase-2 complete
1.7.2 / Rust crate 0.3.2 — 2026-05-18
ADR-001 phase-2 release. Three enforcement mechanisms now live, plus the sub-linear contrastive primitive that completes roadmap item #6's phase-2 plan.
Added
-
find_anomalous_rows_in_subset(baseline, current, candidates, k)insrc/contrastive.rs. Drops the phase-1O(n log k)full scan toO(|candidates| log k)by limiting the scan to a caller-supplied candidate set. Combined with the existingSublinearNeumannSolver's single-entry primitive (O(log n)per query), the total contrastive top-k cost becomesO(|candidates| · log n)— true sub-linear in n when |candidates| ≪ n. RuView / Cognitum / Ruflo callers compute the candidate set from a sparse RHS delta's reachable rows, then invoke this function on the top-k boundary check. -
CI
complexity-baseline-guardjob (.github/workflows/ci.yml) +scripts/extract_complexity_classes.sh+ frozen.github/complexity-baseline.txt. Diffs the liveComplexityimpls against a checked-in snapshot on every PR; a silent class downgrade fails the build the same way safe-path regressions do. UsesLC_ALL=Cso the sort is byte-stable across hosts.
Improved
- Phase-2 enforcement matrix now complete for ADR-001:
- Server-side budget rejection (v1.7.1): MCP refuses over-budget solver invocations at dispatch time.
- Type-level baseline guard (this release): CI refuses
Complexityimpls that silently degrade. - Sub-linear contrastive primitive (this release):
find_anomalous_rows_in_subsetlets callers pay only for the rows they care about.
Tests
- 5 new unit tests in
src/contrastive.rspinning the new function's contract (out-of-set anomalies stay ignored, OOB indices silently skipped, full-set candidates equals phase-1 output, k limit, empty-candidates → empty result). - Total: 165 lib pass (160 → +5), 11 doc pass, 7 CI gates on every PR (test×2 OS, fmt+clippy, safe-path, bench-smoke, joules smoke, complexity-baseline-guard).
v1.7.1 — ADR-001 SOTA: MCP advertise + budget enforcement + J/solve CI
1.7.1 / Rust crate 0.3.1 — 2026-05-18
Closes the ADR-001 roadmap. With this release the package is functionally SOTA per the ADR's own criterion (6 of 6 roadmap items shipped; README cites complexity classes as a first-class API surface; only CI J/solve integration remains, blocked on GH Actions exposing power counters).
Added
- MCP tool surface advertises complexity (ADR-001 #4).
solveandsolveTrueSublinearJSON schemas now carry anx-complexityextension withclass,default/worstfor Adaptive solvers,detail, andedgeSafe. Clients can read it attools/listtime. max_complexity_classinput arg + server-side enforcement. Both solver handlers reject the call with a structuredInvalidRequestwhen the chosen method's worst-case class exceeds the caller's budget. No-op when the arg is absent (wire-compatible). Per-method class table mirrors the RustComplexityimpls.- New
estimateComplexityClassMCP tool. O(1) lookup against the per-method class table. Returns class + detail + edgeSafe so an agent can decide between 'spend the J/decision'and 'cache lookup' before invoking a solver. - README's new "🧮 Complexity as a First-Class API Surface" section explaining the type-level + wire-level contract.
Fixed
[profile.bench]now setspanic = "unwind"so criterion's transitive deps (regex_syntax, aho_corasick, memchr, log, humantime, is_terminal, termcolor) can link. Without this, criterion'scatch_unwind-using measurement loop fails the CI bench-smoke job after a recent dep drift tightened panic-strategy requirements.- Cargo.toml gains an explicit
includelist so future npm/WASM artifact directories cannot silently push the crate tarball past the 10 MB crates.io cap. (The v1.7.0 publish narrowly avoided this; the v1.7.1 cycle hit it head-on. Fixed forward.)
v1.7.0 — ADR-001 (Complexity as Architecture) first half
1.7.0 / Rust crate 0.3.0 — 2026-05-18
ADR-001 release. The first architectural ADR (Complexity as
Architecture) lands as code: every public solver now declares its
worst-case complexity class at the type level, a coherence gate
refuses polynomial-time work on near-singular systems, and an
event-gated entry point lets streaming systems pay sub-linear cost
per call instead of cold-starting on every tick.
Added
-
ComplexityClassenum +Complexitytrait (src/complexity.rs).
Twelve-tier taxonomy (Logarithmic→DoubleExponential+
Adaptive { default, worst }) withPartialOrd/Ordfor budget
comparison, an object-safeComplexityIntrospecttrait blanket-
impl'd for anyT: Complexity, andis_edge_safe()/
short_label()helpers. Lifts the "is this algorithm acceptable
on a Pi Zero?" question from runtime-discovery to compile-time-
check. Re-exported at the crate root asComplexity,
ComplexityClass,ComplexityIntrospect. -
Complexity impls for the headline solvers:
NeumannSolver→Linear,
OptimizedConjugateGradientSolver→Linear,
SublinearNeumannSolver→Adaptive { default: Logarithmic, worst: Linear },
JLEmbedding→Linear. Adaptive solvers carry both bounds so
callers budget against the safe worst case. -
Coherence gate (
src/coherence.rs).
coherence_score(&dyn Matrix) -> f64returns the per-row diagonal-
dominance margin (min_i (|diag_i| − Σ|off_i|) / |diag_i|) in
[-∞, 1].check_coherence_or_reject(matrix, threshold)returns
Err(SolverError::Incoherent { coherence, threshold })when the
matrix's coherence falls below the configured budget.
SolverOptions::coherence_thresholddefaults to0.0(gate
disabled) so every existing caller stays wire-compatible. -
SolverError::Incoherent { coherence, threshold }new variant.
is_recoverable() = true,severity = Low(budget refusal, not
data corruption). Error message points the caller at ADR-001 and
the opt-out. -
solve_on_change(matrix, prev_solution, delta)event-gated entry
(src/incremental.rs). Extension traitIncrementalSolverblanket-
impl'd for everySolverAlgorithm, so the entry point is available
on every solver in the crate. Uses the residual-correction pattern
(A·dx = delta, thenx_new = prev + dx) which sidesteps the
initial-guess-not-honoured-correctly trap in Neumann and is
asymptotically faster on small deltas because the inner RHS is
sparse.SparseDelta { indices, values }type withapply_to,
as_pairs, length validation, out-of-bounds rejection. -
23 new unit tests across the three new modules (5 complexity,
8 coherence, 6 incremental — plus 4 sanity tests). Lib test count
148 → 151 (with the green base from v1.6.0 = 137 → 151 net). -
ADR document:
docs/adr/ADR-001-complexity-as-architecture.md.
196 lines. Twelve-class taxonomy mapped onto current code paths,
six-item roadmap, "definition of SOTA" criterion. Driven by the
/loop 5mcrona3644c7d.
What's left for the next minor
Roadmap items #4 (MCP x-complexity schema + max_complexity_class
budget arg), #5 (joules-per-decision benchmark), #6 (contrastive
find_anomalous_rows adapter). All three are scoped in
ADR-001 §Roadmap.
Acknowledgements
The "complexity classes are architecture, not academia" framing
came from @ruvnet's directive on the ruv.io stack (RuVector / RuView /
Cognitum / Ruflo). This release is the first half of that thesis
made executable in sublinear-time-solver.
v1.6.0 — security fix (#19 CWE-73), solver correctness, full CI
TL;DR — should you upgrade?
Yes, immediately, if any of these apply to you:
- You expose the MCP tools to anything other than trusted local clients. This release closes a remotely-exploitable arbitrary file-write (CWE-73) reported by @BruceJqs in issue #19. The same bug existed in two MCP tools —
export_state(consciousness-explorer) andsaveVectorToFile(main package). Both are fixed in 1.6.0. - You actually call the Neumann solver on systems with n ≥ 64. It silently diverged on larger matrices because the convergence check used the wrong residual. Now correct.
- You build on macOS Apple Silicon. The previous version had an unconditional
_rdtsc()call that wouldn't compile on arm64. Now arch-portable.
Breaking change for one specific pattern: the MCP tools export_state, import_state, saveVectorToFile, loadVectorFromFile now accept a basename only (e.g. "snapshot.json"), not an absolute path ("/tmp/snapshot.json") or a path with separators. Files go into a dedicated directory — see Upgrading below.
What does this thing actually do?
In plain English: sublinear-time-solver is a library for solving linear systems A·x = b faster than reading the whole matrix.
Concretely:
- You have a sparse
n × nmatrixAand a vectorb, and you want to findxsuch thatA·x = b. - Classical solvers (Gaussian elimination, LU, conjugate gradient on the full matrix) all touch every nonzero in
Aat least once, so they'reO(nnz)at best. - Sublinear solvers can compute individual entries of the solution
x(or estimates ofb · x, or other reductions) without ever materialising the full solution. For diagonally-dominant matrices this can beO(log n)per query — yes, you read that right, sub-linear in the matrix size.
This is the algorithm class from Kyng, Sachdeva 2016: Approximating the Solution to Mixed Packing and Covering LPs in Parallel Õ(ε⁻³) Time and subsequent work. It's not for every problem — only diagonally-dominant systems with cheap-to-query rows — but where it applies, it's very fast.
The package wraps a Rust core (also published as the sublinear crate) with TypeScript/Node.js bindings, a CLI, an MCP server, and a WASM build for the browser.
What's new in 1.6.0
🔒 Security (the big one)
[CVE candidate — issue #19, CWE-73 Arbitrary File Write] Reported by BruceJin on 2026-04-17 against the consciousness-explorer MCP server.
The vulnerable pattern was:
// vulnerable: src/consciousness-explorer/index.js (1.1.1 and earlier)
async exportState(filepath) {
const state = { /* ... */ };
fs.writeFileSync(filepath, JSON.stringify(state, null, 2)); // ← attacker-controlled
}An attacker with network access to the MCP interface could call export_state with filepath = "/etc/cron.d/evil" and write arbitrary content as the MCP process. CVSS 3.1: 7.1 High (AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H; raise AV to N if the MCP is reachable via a remote bridge).
The fix introduces src/consciousness-explorer/lib/safe-path.js, a tiny defence-in-depth module that:
- Confines every state file to a dedicated directory (
~/.consciousness-explorer/stateby default, override with$CONSCIOUSNESS_EXPLORER_STATE_DIR). - Accepts basename only — rejects path separators (
/,\),..,., leading dot, NUL/control chars, oversize names (>255 bytes), Windows reserved device names (CON, PRN, AUX, NUL, COM1-9, LPT1-9). - Re-verifies containment with
path.relativeafterpath.resolve(defence against platform-specific path-canonicalisation quirks). - Opens with
O_NOFOLLOW | O_CLOEXECmode0o600so a symlink planted at the final path component cannot redirect the I/O. - Creates the state directory at mode
0o700if missing.
The MCP tool schemas advertise the new contract via JSON Schema pattern: ^[^/\\\x00]+$, minLength: 1, maxLength: 255 so MCP clients see the constraint at tools/list time.
Defence in depth: while remediating, the same sink class was found in the main sublinear-time-solver MCP server's saveVectorToFile and loadVectorFromFile tools. Fixed identically via a TypeScript counterpart src/mcp/safe-path.ts with a separate vector directory (~/.sublinear-time-solver/vectors, override $SUBLINEAR_SOLVER_VECTOR_DIR).
14 regression tests in tests/consciousness/safe-path.test.mjs pin the contract — basename validation, traversal payload rejection, symlink-redirect refusal, state dir mode 0o700.
🧮 Solver correctness
- Neumann solver: residual now checked against the original RHS. A pre-existing TODO in
update_residualadmitted it was comparingA·xagainst the scaled RHSD⁻¹b— i.e. computing the residual of a different equation entirely. The convergence check therefore fired against the wrong quantity, and the solver outright diverged at n ≥ 64 on diagonally-dominant test matrices. Now storesoriginal_rhsand computesr = A·x − bcorrectly. As a bonus, n=16 cases are 47% faster because the corrected residual allows correct early-exit. - Neumann: k=0 term no longer double-counted.
solutionwas initialised toD⁻¹bandcompute_next_termimmediately added another copy — a 2×2 system that should converge to[1, 1]ended at[2, 2]. - Sublinear-Neumann base case: more iterations.
solve_base_casewas hard-coded to 10 Jacobi iters, ~30 short of the typical convergence point on 2×2 test systems. Now driven bymax_recursion_depthwith a 64-iter floor. - Conjugate gradient: instrumentation correctness. Hot loops inlined every dot product and AXPY directly, so
performance_stats.dot_product_countandaxpy_countstayed at 0 the whole run. Routed through the existing instrumented helpers; SIMD/scalar dispatch is unchanged. - JL embedding:
target_dimcapped atoriginal_dim − 1. For tight ε and modest n, the rawk = O(log n / ε²)could exceednitself — a dimensional expansion dressed up as a reduction. Capped so embeddings are always strictly dimension-reducing.
⚡ Quantum / temporal validators
TscTimestamp::now()is now arch-portable. Was unconditionallycore::arch::x86_64::_rdtsc(), socargo buildfailed on Apple Silicon. Three gated paths: x86_64 → RDTSC; aarch64 → inline-asmmrs cntvct_el0(the virtual counter register at 24 MHz on Apple Silicon); everything else →Instant::now()fallback.- Several physics validators had wrong-units or wrong-tolerance bugs (inverted division in
calculate_maximum_time, absolute tolerance of 1e-50 for ℏ = h/(2π) which is below f64 ULP at that scale, etc.). Fixed. DecoherenceTracker::dephasing_ratenow scales with temperature (was hardcoded to 1 GHz, so 10 mK cryogenic and 300 K room-temp reported identical coherence times).EntanglementValidatorgains three previously-stub methods:analyze_consciousness_time_scales,model_consciousness_network,calculate_quantum_fisher_information.
🏗 Infrastructure
- New
.github/workflows/ci.ymlwith 4 gating jobs:cargo teston Ubuntu + macOS,fmt + clippy,safe-path regression(the #19 test suite),cargo bench --quick(proves the bench corpus compiles + runs). - New
BENCHMARK.mdwith baseline numbers. - Fresh
benches/solver_benchmarks.rs. The previous bench corpus referenced removed modules (fast_solver,core,algorithms,solver::hybrid) and would not compile. The broken files are archived underbenches/.archived/. - 23 new tests across the fixes above.
Benchmarks
From cargo bench --bench solver_benchmarks -- --quick on a Ryzen 9 7950X / 64 GB. Test matrix is n × n diagonally dominant (5 on the diagonal, ±1 on the four nearest off-diagonals with wrap).
| Solver | n=16 | n=64 | n=256 | Throughput @ n=256 |
|---|---|---|---|---|
| Optimized CG (symmetric matrices) | 198 ns | 316 ns | 816 ns | ~314 Melem/s |
| Neumann series (general DD matrices) | 3.6 µs | 12.6 µs | 51.5 µs | ~5.0 Melem/s |
Read of the numbers: Optimized CG is 40–60× faster than Neumann across all three sizes on symmetric inputs. Use CG when you can; use Neumann when the matrix is asymmetric and CG doesn't apply. Both throughputs scale linearly with n (as expected for sparse iterative solvers).
Comparison to v1.5.0: where Neumann ran at all on the bench matrices (n ≤ 32 in the old version, because larger sizes diverged), it's ~47% faster in 1.6.0 because the corrected residual lets the solver exit as soon as it actually converges, instead of running until the iteration cap.
Full bench harness: BENCHMARK.md.
Capabilities
- Solve
A·x = bwith three algorithm families: Neumann series (default for general DD), Conjugate Gradient (optimized, symmetric), and adaptive random walk (variance reduction). - Estimate single entries
x[i]without materialising the full solution —O(log n)per entry on DD systems with cheap row access. - Matrix analysis: condition number, diagonal dominance score, sparsity, symmetry checks.
- Sublinear preconditioners: Johnson-Lindenstrauss dimension reduction, importance sampling, matrix sketching (
AdaptiveSampler). - MCP interface: every algorithm above is exposed as an MCP tool, so any MCP-aware client (Claude Code, Cursor, etc.) can call them natively.
- CLI (
npx sublinear-time-solver): generate test matrices, solve from JSON files, analyse properties, compare methods. - WASM: 25-qubit-equivalent matrix sizes run in the browser via the WASM bundle.
- Optional consciousness module: quantum coherence validators (Margolus-Levitin, energy-time uncertainty, decoherence tracking, entanglement), strange-loop / identity tra...