Skip to content

Releases: ruvnet/sublinear-time-solver

v1.7.2 — ADR-001 phase-2 complete

Choose a tag to compare

@ruvnet ruvnet released this 19 May 01:56
b3c6d30

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) in src/contrastive.rs. Drops the phase-1 O(n log k) full scan to O(|candidates| log k) by limiting the scan to a caller-supplied candidate set. Combined with the existing SublinearNeumannSolver's single-entry primitive (O(log n) per query), the total contrastive top-k cost becomes O(|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-guard job (.github/workflows/ci.yml) + scripts/extract_complexity_classes.sh + frozen .github/complexity-baseline.txt. Diffs the live Complexity impls against a checked-in snapshot on every PR; a silent class downgrade fails the build the same way safe-path regressions do. Uses LC_ALL=C so 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 Complexity impls that silently degrade.
    • Sub-linear contrastive primitive (this release): find_anomalous_rows_in_subset lets callers pay only for the rows they care about.

Tests

  • 5 new unit tests in src/contrastive.rs pinning 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

Choose a tag to compare

@ruvnet ruvnet released this 19 May 01:37
df75552

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). solve and solveTrueSublinear JSON schemas now carry an x-complexity extension with class, default/worst for Adaptive solvers, detail, and edgeSafe. Clients can read it at tools/list time.
  • max_complexity_class input arg + server-side enforcement. Both solver handlers reject the call with a structured InvalidRequest when 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 Rust Complexity impls.
  • New estimateComplexityClass MCP 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 sets panic = "unwind" so criterion's transitive deps (regex_syntax, aho_corasick, memchr, log, humantime, is_terminal, termcolor) can link. Without this, criterion's catch_unwind-using measurement loop fails the CI bench-smoke job after a recent dep drift tightened panic-strategy requirements.
  • Cargo.toml gains an explicit include list 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

Choose a tag to compare

@ruvnet ruvnet released this 19 May 01:19
9a57816

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

  • ComplexityClass enum + Complexity trait (src/complexity.rs).
    Twelve-tier taxonomy (LogarithmicDoubleExponential +
    Adaptive { default, worst }) with PartialOrd/Ord for budget
    comparison, an object-safe ComplexityIntrospect trait blanket-
    impl'd for any T: Complexity, and is_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 as Complexity,
    ComplexityClass, ComplexityIntrospect.

  • Complexity impls for the headline solvers:
    NeumannSolverLinear,
    OptimizedConjugateGradientSolverLinear,
    SublinearNeumannSolverAdaptive { default: Logarithmic, worst: Linear },
    JLEmbeddingLinear. Adaptive solvers carry both bounds so
    callers budget against the safe worst case.

  • Coherence gate (src/coherence.rs).
    coherence_score(&dyn Matrix) -> f64 returns 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_threshold defaults to 0.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 trait IncrementalSolver blanket-
    impl'd for every SolverAlgorithm, so the entry point is available
    on every solver in the crate. Uses the residual-correction pattern
    (A·dx = delta, then x_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 with apply_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 5m cron a3644c7d.

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

Choose a tag to compare

@ruvnet ruvnet released this 18 May 23:18
f31a206

TL;DR — should you upgrade?

Yes, immediately, if any of these apply to you:

  1. 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) and saveVectorToFile (main package). Both are fixed in 1.6.0.
  2. 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.
  3. 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 × n matrix A and a vector b, and you want to find x such that A·x = b.
  • Classical solvers (Gaussian elimination, LU, conjugate gradient on the full matrix) all touch every nonzero in A at least once, so they're O(nnz) at best.
  • Sublinear solvers can compute individual entries of the solution x (or estimates of b · x, or other reductions) without ever materialising the full solution. For diagonally-dominant matrices this can be O(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/state by 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.relative after path.resolve (defence against platform-specific path-canonicalisation quirks).
  • Opens with O_NOFOLLOW | O_CLOEXEC mode 0o600 so a symlink planted at the final path component cannot redirect the I/O.
  • Creates the state directory at mode 0o700 if 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_residual admitted it was comparing A·x against the scaled RHS D⁻¹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 stores original_rhs and computes r = A·x − b correctly. 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. solution was initialised to D⁻¹b and compute_next_term immediately 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_case was hard-coded to 10 Jacobi iters, ~30 short of the typical convergence point on 2×2 test systems. Now driven by max_recursion_depth with a 64-iter floor.
  • Conjugate gradient: instrumentation correctness. Hot loops inlined every dot product and AXPY directly, so performance_stats.dot_product_count and axpy_count stayed at 0 the whole run. Routed through the existing instrumented helpers; SIMD/scalar dispatch is unchanged.
  • JL embedding: target_dim capped at original_dim − 1. For tight ε and modest n, the raw k = O(log n / ε²) could exceed n itself — 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 unconditionally core::arch::x86_64::_rdtsc(), so cargo build failed on Apple Silicon. Three gated paths: x86_64 → RDTSC; aarch64 → inline-asm mrs 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_rate now scales with temperature (was hardcoded to 1 GHz, so 10 mK cryogenic and 300 K room-temp reported identical coherence times).
  • EntanglementValidator gains three previously-stub methods: analyze_consciousness_time_scales, model_consciousness_network, calculate_quantum_fisher_information.

🏗 Infrastructure

  • New .github/workflows/ci.yml with 4 gating jobs: cargo test on Ubuntu + macOS, fmt + clippy, safe-path regression (the #19 test suite), cargo bench --quick (proves the bench corpus compiles + runs).
  • New BENCHMARK.md with 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 under benches/.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 = b with 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...
Read more