# Sublinear-Time Solver: DDD Tactical Design **Version**: 1.0 **Date**: 2026-02-20 **Status**: Proposed --- ## 1. Aggregate Design ### 1.1 SolverSession Aggregate (Root) The SolverSession is the primary aggregate root, encapsulating the lifecycle of a solve operation. ```rust /// Aggregate root for solver operations pub struct SolverSession { // Identity id: SessionId, // Configuration (set at creation, immutable during solve) config: SolverConfig, budget: ComputeBudget, // State (mutated during solve lifecycle) state: SessionState, current_algorithm: Algorithm, // Event sourcing history: Vec, version: u64, // Timing created_at: Timestamp, started_at: Option, completed_at: Option, } /// Session state machine #[derive(Debug, Clone, PartialEq)] pub enum SessionState { /// Created but not yet started Idle, /// Preprocessing (TRUE: JL, sparsification) Preprocessing { phase: PreprocessPhase, progress: f64 }, /// Active solving Solving { iteration: usize, residual: f64 }, /// Successfully converged Converged { result: SolverResult }, /// Failed with error Failed { error: SolverError, best_effort: Option> }, /// Cancelled by user or budget enforcement Cancelled { reason: String }, } impl SolverSession { // === Invariants === /// Budget is never exceeded fn check_budget(&self) -> Result<(), SolverError> { if let Some(started) = self.started_at { let elapsed = Timestamp::now() - started; if elapsed > self.budget.max_wall_time { return Err(SolverError::BudgetExhausted { budget: self.budget.clone(), progress: self.progress(), }); } } if let SessionState::Solving { iteration, .. } = &self.state { if *iteration > self.budget.max_iterations as usize { return Err(SolverError::BudgetExhausted { budget: self.budget.clone(), progress: self.progress(), }); } } Ok(()) } /// State transitions are valid fn transition(&mut self, new_state: SessionState) -> Result<(), SolverError> { let valid = match (&self.state, &new_state) { (SessionState::Idle, SessionState::Preprocessing { .. }) => true, (SessionState::Idle, SessionState::Solving { .. }) => true, (SessionState::Preprocessing { .. }, SessionState::Solving { .. }) => true, (SessionState::Solving { .. }, SessionState::Solving { .. }) => true, (SessionState::Solving { .. }, SessionState::Converged { .. }) => true, (SessionState::Solving { .. }, SessionState::Failed { .. }) => true, (_, SessionState::Cancelled { .. }) => true, // Always cancellable _ => false, }; if !valid { return Err(SolverError::InvalidStateTransition { from: format!("{:?}", self.state), to: format!("{:?}", new_state), }); } self.state = new_state; self.version += 1; Ok(()) } // === Commands === pub fn start_solve(&mut self, system: &SparseSystem) -> Result<(), SolverError> { self.check_budget()?; self.started_at = Some(Timestamp::now()); self.history.push(SolverEvent::SolveRequested { request_id: self.id, algorithm: self.current_algorithm, input_dimensions: (system.matrix.rows, system.matrix.cols, system.matrix.nnz()), timestamp: Timestamp::now(), }); self.transition(SessionState::Solving { iteration: 0, residual: f64::INFINITY }) } pub fn record_iteration(&mut self, iteration: usize, residual: f64) -> Result<(), SolverError> { self.check_budget()?; self.history.push(SolverEvent::IterationCompleted { request_id: self.id, iteration, residual_norm: residual, wall_time_us: self.elapsed_us(), timestamp: Timestamp::now(), }); if residual < self.config.tolerance { self.transition(SessionState::Converged { result: SolverResult { iterations: iteration, final_residual: residual, ..Default::default() }, }) } else { self.transition(SessionState::Solving { iteration, residual }) } } pub fn fail_and_fallback(&mut self, error: SolverError) -> Option { let fallback = self.next_fallback(); self.history.push(SolverEvent::AlgorithmFallback { request_id: self.id, from_algorithm: self.current_algorithm, to_algorithm: fallback, reason: error.to_string(), timestamp: Timestamp::now(), }); if let Some(next) = fallback { self.current_algorithm = next; self.state = SessionState::Idle; // Reset for retry Some(next) } else { let _ = self.transition(SessionState::Failed { error, best_effort: None, }); None } } fn next_fallback(&self) -> Option { match self.current_algorithm { Algorithm::Neumann | Algorithm::ForwardPush | Algorithm::BackwardPush | Algorithm::HybridRandomWalk | Algorithm::TRUE | Algorithm::BMSSP => Some(Algorithm::CG), Algorithm::CG => Some(Algorithm::DenseDirect), Algorithm::DenseDirect => None, // No further fallback } } } ``` ### 1.2 SparseSystem Aggregate ```rust /// Immutable representation of a sparse linear system Ax = b pub struct SparseSystem { id: SystemId, matrix: CsrMatrix, rhs: Vec, metadata: SystemMetadata, } pub struct SystemMetadata { pub sparsity: SparsityProfile, pub is_spd: bool, pub is_laplacian: bool, pub condition_estimate: Option, pub source_context: SourceContext, } pub enum SourceContext { CoherenceLaplacian { graph_id: String }, GnnAdjacency { layer: usize, node_count: usize }, GraphAnalytics { query_type: String }, SpectralFilter { filter_degree: usize }, UserProvided, } impl SparseSystem { // === Invariants === pub fn validate(&self) -> Result<(), ValidationError> { // Matrix dimensions match RHS if self.matrix.rows != self.rhs.len() { return Err(ValidationError::DimensionMismatch { expected: self.matrix.rows, actual: self.rhs.len(), }); } // All values finite for v in self.matrix.values.iter() { if !v.is_finite() { return Err(ValidationError::InvalidNumber { field: "matrix_values", index: 0, reason: "non-finite", }); } } for v in self.rhs.iter() { if !v.is_finite() { return Err(ValidationError::InvalidNumber { field: "rhs", index: 0, reason: "non-finite", }); } } // Sparsity > 0 if self.matrix.nnz() == 0 { return Err(ValidationError::EmptyMatrix); } Ok(()) } } ``` ### 1.3 GraphProblem Aggregate ```rust /// Graph-based problem for Push algorithms and random walks pub struct GraphProblem { id: ProblemId, graph: SparseAdjacency, query: GraphQuery, parameters: PushParameters, } pub struct SparseAdjacency { pub adj: CsrMatrix, pub directed: bool, pub weighted: bool, } pub enum GraphQuery { SingleSource { source: usize }, SingleTarget { target: usize }, Pairwise { source: usize, target: usize }, BatchSources { sources: Vec }, AllNodes, } pub struct PushParameters { pub alpha: f64, // Damping factor (default: 0.85) pub epsilon: f64, // Push threshold pub max_iterations: u64, // Safety bound } ``` --- ## 2. Entity Design ### 2.1 SolverResult Entity ```rust #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SolverResult { pub id: ResultId, pub session_id: SessionId, pub algorithm_used: Algorithm, pub solution: Vec, pub iterations: usize, pub residual_norm: f64, pub wall_time_us: u64, pub convergence: ConvergenceInfo, pub error_bounds: ErrorBounds, pub audit_entry: SolverAuditEntry, } ``` ### 2.2 ComputeBudget Entity ```rust #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ComputeBudget { pub max_wall_time: Duration, pub max_iterations: u64, pub max_memory_bytes: usize, pub lane: ComputeLane, } #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] pub enum ComputeLane { Reflex, // < 1ms — cached results, trivial problems Retrieval, // ~ 10ms — simple solves (small n, well-conditioned) Heavy, // ~ 100ms — full solver pipeline Deliberate, // unbounded — streaming progress, complex problems } impl ComputeBudget { pub fn for_lane(lane: ComputeLane) -> Self { match lane { ComputeLane::Reflex => Self { max_wall_time: Duration::from_millis(1), max_iterations: 10, max_memory_bytes: 1 << 20, // 1MB lane, }, ComputeLane::Retrieval => Self { max_wall_time: Duration::from_millis(10), max_iterations: 100, max_memory_bytes: 16 << 20, // 16MB lane, }, ComputeLane::Heavy => Self { max_wall_time: Duration::from_millis(100), max_iterations: 10_000, max_memory_bytes: 256 << 20, // 256MB lane, }, ComputeLane::Deliberate => Self { max_wall_time: Duration::from_secs(300), max_iterations: 1_000_000, max_memory_bytes: 2 << 30, // 2GB lane, }, } } } ``` ### 2.3 AlgorithmProfile Entity ```rust #[derive(Debug, Clone)] pub struct AlgorithmProfile { pub algorithm: Algorithm, pub complexity_class: ComplexityClass, pub sparsity_range: (f64, f64), // (min_density, max_density) pub size_range: (usize, usize), // (min_n, max_n) pub deterministic: bool, pub parallelizable: bool, pub wasm_compatible: bool, pub numerical_stability: Stability, pub convergence_guarantee: ConvergenceGuarantee, } pub enum ComplexityClass { Logarithmic, // O(log n) SquareRoot, // O(√n) NearLinear, // O(n · polylog(n)) Linear, // O(n) Quadratic, // O(n²) } pub enum ConvergenceGuarantee { Guaranteed { max_iterations: usize }, Probabilistic { confidence: f64 }, Conditional { requirement: &'static str }, } ``` --- ## 3. Value Objects ### 3.1 CsrMatrix ```rust /// Immutable value object — equality by content #[derive(Clone)] pub struct CsrMatrix { pub values: AlignedVec, pub col_indices: AlignedVec, pub row_ptrs: Vec, pub rows: usize, pub cols: usize, } impl CsrMatrix { pub fn nnz(&self) -> usize { self.values.len() } pub fn density(&self) -> f64 { self.nnz() as f64 / (self.rows * self.cols) as f64 } pub fn memory_bytes(&self) -> usize { self.values.len() * size_of::() + self.col_indices.len() * size_of::() + self.row_ptrs.len() * size_of::() } } ``` ### 3.2 ConvergenceInfo ```rust #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ConvergenceInfo { pub converged: bool, pub iterations: usize, pub residual_history: Vec, pub final_residual: f64, pub convergence_rate: f64, // ratio of consecutive residuals } ``` ### 3.3 SparsityProfile ```rust #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SparsityProfile { pub nonzero_count: usize, pub total_elements: usize, pub density: f64, pub diagonal_dominance: f64, // fraction of rows that are diag. dominant pub bandwidth: usize, // max |i - j| for nonzero a_ij pub symmetry: f64, // fraction of entries with a_ij == a_ji pub avg_row_nnz: f64, pub max_row_nnz: usize, } ``` ### 3.4 ComplexityEstimate ```rust #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ComplexityEstimate { pub estimated_flops: u64, pub estimated_memory_bytes: u64, pub estimated_wall_time_us: u64, pub recommended_algorithm: Algorithm, pub recommended_lane: ComputeLane, pub confidence: f64, } ``` ### 3.5 ErrorBounds ```rust #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ErrorBounds { pub absolute_error: f64, // ||x_approx - x_exact|| pub relative_error: f64, // ||x_approx - x_exact|| / ||x_exact|| pub residual_norm: f64, // ||A*x_approx - b|| pub confidence: f64, // Statistical confidence (for randomized algorithms) } ``` --- ## 4. Domain Events ```rust #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "type")] pub enum SolverEvent { SolveRequested { request_id: SessionId, algorithm: Algorithm, input_dimensions: (usize, usize, usize), // rows, cols, nnz timestamp: Timestamp, }, IterationCompleted { request_id: SessionId, iteration: usize, residual_norm: f64, wall_time_us: u64, timestamp: Timestamp, }, SolveConverged { request_id: SessionId, total_iterations: usize, final_residual: f64, total_wall_time_us: u64, accuracy: ErrorBounds, timestamp: Timestamp, }, SolveFailed { request_id: SessionId, error: String, best_residual: f64, iterations_completed: usize, timestamp: Timestamp, }, AlgorithmFallback { request_id: SessionId, from_algorithm: Algorithm, to_algorithm: Option, reason: String, timestamp: Timestamp, }, BudgetExhausted { request_id: SessionId, budget: ComputeBudget, best_residual: f64, timestamp: Timestamp, }, ComplexityEstimated { request_id: SessionId, estimate: ComplexityEstimate, timestamp: Timestamp, }, SparsityDetected { system_id: SystemId, profile: SparsityProfile, recommended_path: Algorithm, timestamp: Timestamp, }, NumericalWarning { request_id: SessionId, warning_type: NumericalWarningType, detail: String, timestamp: Timestamp, }, } pub enum NumericalWarningType { NearSingular, SlowConvergence, OrthogonalityLoss, MassInvariantViolation, PrecisionLoss, } ``` --- ## 5. Domain Services ### 5.1 SolverOrchestrator ```rust /// Orchestrates: routing → validation → execution → fallback → result pub struct SolverOrchestrator { router: AlgorithmRouter, solvers: HashMap>, budget_enforcer: BudgetEnforcer, event_bus: broadcast::Sender, } impl SolverOrchestrator { pub async fn solve(&self, system: SparseSystem) -> Result { // 1. Analyze sparsity let profile = system.metadata.sparsity.clone(); self.event_bus.send(SolverEvent::SparsityDetected { .. }); // 2. Route to optimal algorithm let algorithm = self.router.select(&ProblemProfile::from(&system)); let estimate = self.estimate_complexity(&system); self.event_bus.send(SolverEvent::ComplexityEstimated { .. }); // 3. Create session let mut session = SolverSession::new(algorithm, estimate.recommended_lane); // 4. Execute with fallback chain loop { match self.execute_algorithm(&mut session, &system).await { Ok(result) => return Ok(result), Err(e) => { match session.fail_and_fallback(e) { Some(_next) => continue, // Retry with fallback None => return Err(SolverError::AllAlgorithmsFailed), } } } } } } ``` ### 5.2 SparsityAnalyzer ```rust /// Analyzes matrix properties for routing decisions pub struct SparsityAnalyzer; impl SparsityAnalyzer { pub fn analyze(matrix: &CsrMatrix) -> SparsityProfile { SparsityProfile { nonzero_count: matrix.nnz(), total_elements: matrix.rows * matrix.cols, density: matrix.density(), diagonal_dominance: Self::measure_diagonal_dominance(matrix), bandwidth: Self::estimate_bandwidth(matrix), symmetry: Self::measure_symmetry(matrix), avg_row_nnz: matrix.nnz() as f64 / matrix.rows as f64, max_row_nnz: Self::max_row_nnz(matrix), } } } ``` ### 5.3 ConvergenceMonitor ```rust /// Monitors convergence and triggers fallback pub struct ConvergenceMonitor { stagnation_window: usize, // Look back N iterations stagnation_threshold: f64, // Improvement < threshold → stagnant divergence_factor: f64, // Residual growth > factor → diverging } impl ConvergenceMonitor { pub fn check(&self, history: &[f64]) -> ConvergenceStatus { if history.len() < 2 { return ConvergenceStatus::Progressing; } let latest = *history.last().unwrap(); let previous = history[history.len() - 2]; // Divergence check if latest > previous * self.divergence_factor { return ConvergenceStatus::Diverging; } // Stagnation check if history.len() >= self.stagnation_window { let window_start = history[history.len() - self.stagnation_window]; let improvement = (window_start - latest) / window_start; if improvement < self.stagnation_threshold { return ConvergenceStatus::Stagnant; } } ConvergenceStatus::Progressing } } ``` --- ## 6. Repositories ### 6.1 SolverSessionRepository ```rust pub trait SolverSessionRepository: Send + Sync { fn save(&self, session: &SolverSession) -> Result<(), RepositoryError>; fn find_by_id(&self, id: &SessionId) -> Result, RepositoryError>; fn find_active(&self) -> Result, RepositoryError>; fn delete(&self, id: &SessionId) -> Result<(), RepositoryError>; } /// In-memory implementation (server, WASM) pub struct InMemorySessionRepo { sessions: DashMap, } ``` --- ## 7. Factories ### 7.1 SolverFactory ```rust pub struct SolverFactory; impl SolverFactory { pub fn create(algorithm: Algorithm, config: &SolverConfig) -> Box { match algorithm { Algorithm::Neumann => Box::new(NeumannSolver::from_config(config)), Algorithm::ForwardPush => Box::new(ForwardPushSolver::from_config(config)), Algorithm::BackwardPush => Box::new(BackwardPushSolver::from_config(config)), Algorithm::HybridRandomWalk => Box::new(HybridRandomWalkSolver::from_config(config)), Algorithm::TRUE => Box::new(TrueSolver::from_config(config)), Algorithm::CG => Box::new(ConjugateGradientSolver::from_config(config)), Algorithm::BMSSP => Box::new(BmsspSolver::from_config(config)), Algorithm::DenseDirect => Box::new(DenseDirectSolver::from_config(config)), } } } ``` ### 7.2 SparseSystemFactory ```rust pub struct SparseSystemFactory; impl SparseSystemFactory { pub fn from_hnsw(hnsw: &HnswIndex, level: usize) -> SparseSystem { ... } pub fn from_adjacency_list(edges: &[(usize, usize, f32)], n: usize) -> SparseSystem { ... } pub fn from_dense(matrix: &[Vec], threshold: f32) -> SparseSystem { ... } pub fn laplacian_from_graph(graph: &SparseAdjacency) -> SparseSystem { ... } } ``` --- ## 8. Module Structure ``` crates/ruvector-solver/src/ ├── lib.rs # Public API surface ├── domain/ │ ├── mod.rs │ ├── aggregates/ │ │ ├── session.rs # SolverSession aggregate │ │ ├── sparse_system.rs # SparseSystem aggregate │ │ └── graph_problem.rs # GraphProblem aggregate │ ├── entities/ │ │ ├── result.rs # SolverResult entity │ │ ├── budget.rs # ComputeBudget entity │ │ └── profile.rs # AlgorithmProfile entity │ ├── values/ │ │ ├── csr_matrix.rs # CsrMatrix value object │ │ ├── convergence.rs # ConvergenceInfo value object │ │ ├── sparsity.rs # SparsityProfile value object │ │ └── estimate.rs # ComplexityEstimate value object │ └── events.rs # SolverEvent enum ├── services/ │ ├── orchestrator.rs # SolverOrchestrator │ ├── sparsity_analyzer.rs # SparsityAnalyzer │ ├── convergence_monitor.rs # ConvergenceMonitor │ └── budget_enforcer.rs # BudgetEnforcer ├── algorithms/ │ ├── neumann.rs │ ├── forward_push.rs │ ├── backward_push.rs │ ├── hybrid_random_walk.rs │ ├── true_solver.rs │ ├── conjugate_gradient.rs │ ├── bmssp.rs │ └── dense_direct.rs ├── routing/ │ ├── router.rs # AlgorithmRouter │ ├── heuristic.rs # Tier 2 rules │ └── adaptive.rs # Tier 3 SONA ├── infrastructure/ │ ├── arena.rs # Arena allocator integration │ ├── simd.rs # SIMD dispatch │ ├── repository.rs # Session repository │ └── factory.rs # SolverFactory, SparseSystemFactory └── traits.rs # SolverEngine, NumericBackend, etc. ``` --- ## 9. State Machine ``` ┌─────────┐ │ IDLE │ └────┬────┘ │ start_solve() ┌────▼────┐ ┌─────│PREPROC. │──────┐ │ └────┬────┘ │ │ │ done │ cancel │ ┌────▼────┐ │ │ │ SOLVING │◀────┤ (back to SOLVING on retry) │ └──┬──┬───┘ │ │ │ │ │ │ converge fail │ │ │ │ │ │ ┌────▼┐ ┌▼────┐ │ │ │CONV.│ │FAIL │ │ │ └─────┘ └──┬──┘ │ │ │ │ │ fallback? │ │ Y N │ │ │ │ │ │ ┌────▼┐ │ ┌──▼──────┐ └────▶│IDLE │ └─▶│CANCELLED│ └─────┘ └─────────┘ ```