diff --git a/crates/cognitum-gate-kernel/benches/benchmarks.rs b/crates/cognitum-gate-kernel/benches/benchmarks.rs index d46fbae36..29c2bd3ee 100644 --- a/crates/cognitum-gate-kernel/benches/benchmarks.rs +++ b/crates/cognitum-gate-kernel/benches/benchmarks.rs @@ -13,7 +13,9 @@ use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criteri use cognitum_gate_kernel::{ delta::{Delta, Observation}, - evidence::{EvidenceAccumulator, HypothesisState, LogEValue, f32_to_log_e, LOG_LR_CONNECTIVITY_POS}, + evidence::{ + f32_to_log_e, EvidenceAccumulator, HypothesisState, LogEValue, LOG_LR_CONNECTIVITY_POS, + }, report::TileReport, shard::{CompactGraph, MAX_SHARD_VERTICES}, TileState, MAX_DELTA_BUFFER, @@ -400,8 +402,8 @@ fn bench_mixture_evalue(c: &mut Criterion) { // Scalar baseline group.bench_function("aggregate_16_scalar", |b| { let log_e_values: [LogEValue; 16] = [ - 65536, 38550, -65536, 65536, 38550, 65536, 38550, -32768, - 65536, 65536, 38550, -65536, 65536, 38550, 65536, 38550, + 65536, 38550, -65536, 65536, 38550, 65536, 38550, -32768, 65536, 65536, 38550, -65536, + 65536, 38550, 65536, 38550, ]; b.iter(|| { @@ -413,8 +415,8 @@ fn bench_mixture_evalue(c: &mut Criterion) { // Parallel lanes pattern (SIMD-friendly) group.bench_function("aggregate_16_parallel_lanes", |b| { let log_e_values: [LogEValue; 16] = [ - 65536, 38550, -65536, 65536, 38550, 65536, 38550, -32768, - 65536, 65536, 38550, -65536, 65536, 38550, 65536, 38550, + 65536, 38550, -65536, 65536, 38550, 65536, 38550, -32768, 65536, 65536, 38550, -65536, + 65536, 38550, 65536, 38550, ]; b.iter(|| { @@ -431,8 +433,8 @@ fn bench_mixture_evalue(c: &mut Criterion) { // Chunked processing (auto-vectorization friendly) group.bench_function("aggregate_16_chunked", |b| { let log_e_values: [LogEValue; 16] = [ - 65536, 38550, -65536, 65536, 38550, 65536, 38550, -32768, - 65536, 65536, 38550, -65536, 65536, 38550, 65536, 38550, + 65536, 38550, -65536, 65536, 38550, 65536, 38550, -32768, 65536, 65536, 38550, -65536, + 65536, 38550, 65536, 38550, ]; b.iter(|| { @@ -460,8 +462,8 @@ fn bench_mixture_evalue(c: &mut Criterion) { // Mixture with product (exp-log pattern) group.bench_function("mixture_product_16", |b| { let log_e_values: [LogEValue; 16] = [ - 65536, 38550, -65536, 65536, 38550, 65536, 38550, -32768, - 65536, 65536, 38550, -65536, 65536, 38550, 65536, 38550, + 65536, 38550, -65536, 65536, 38550, 65536, 38550, -32768, 65536, 65536, 38550, -65536, + 65536, 38550, 65536, 38550, ]; b.iter(|| { @@ -628,23 +630,11 @@ fn bench_memory_patterns(c: &mut Criterion) { // Criterion Groups // ============================================================================ -criterion_group!( - edge_benches, - bench_edge_insert, - bench_edge_batch, -); +criterion_group!(edge_benches, bench_edge_insert, bench_edge_batch,); -criterion_group!( - tick_benches, - bench_tick, - bench_tick_under_load, -); +criterion_group!(tick_benches, bench_tick, bench_tick_under_load,); -criterion_group!( - evidence_benches, - bench_evalue_update, - bench_mixture_evalue, -); +criterion_group!(evidence_benches, bench_evalue_update, bench_mixture_evalue,); criterion_group!( misc_benches, diff --git a/crates/cognitum-gate-kernel/src/delta.rs b/crates/cognitum-gate-kernel/src/delta.rs index 2b3d9698b..da7d73dcc 100644 --- a/crates/cognitum-gate-kernel/src/delta.rs +++ b/crates/cognitum-gate-kernel/src/delta.rs @@ -404,7 +404,10 @@ impl Delta { // Compile-time size assertions const _: () = assert!(size_of::() == 8, "EdgeAdd must be 8 bytes"); const _: () = assert!(size_of::() == 8, "EdgeRemove must be 8 bytes"); -const _: () = assert!(size_of::() == 8, "WeightUpdate must be 8 bytes"); +const _: () = assert!( + size_of::() == 8, + "WeightUpdate must be 8 bytes" +); const _: () = assert!(size_of::() == 8, "Observation must be 8 bytes"); const _: () = assert!(size_of::() == 16, "Delta must be 16 bytes"); diff --git a/crates/cognitum-gate-kernel/src/evidence.rs b/crates/cognitum-gate-kernel/src/evidence.rs index 93e2a64c3..f470ddcd1 100644 --- a/crates/cognitum-gate-kernel/src/evidence.rs +++ b/crates/cognitum-gate-kernel/src/evidence.rs @@ -621,7 +621,11 @@ impl EvidenceAccumulator { fn compute_likelihood_ratio(&self, obs: &Observation) -> f32 { match obs.obs_type { Observation::TYPE_CONNECTIVITY => { - if obs.flags != 0 { 1.5 } else { 0.5 } + if obs.flags != 0 { + 1.5 + } else { + 0.5 + } } Observation::TYPE_CUT_MEMBERSHIP => { let confidence = (obs.value as f32) / 65535.0; @@ -629,10 +633,18 @@ impl EvidenceAccumulator { } Observation::TYPE_FLOW => { let flow = (obs.value as f32) / 1000.0; - if flow > 0.5 { 1.0 + flow } else { 1.0 / (1.0 + flow) } + if flow > 0.5 { + 1.0 + flow + } else { + 1.0 / (1.0 + flow) + } } Observation::TYPE_WITNESS => { - if obs.flags != 0 { 2.0 } else { 0.5 } + if obs.flags != 0 { + 2.0 + } else { + 0.5 + } } _ => 1.0, } diff --git a/crates/cognitum-gate-kernel/src/lib.rs b/crates/cognitum-gate-kernel/src/lib.rs index e0ed2fc00..f2738aea4 100644 --- a/crates/cognitum-gate-kernel/src/lib.rs +++ b/crates/cognitum-gate-kernel/src/lib.rs @@ -300,7 +300,8 @@ impl TileState { } DeltaTag::WeightUpdate => { let wu = unsafe { delta.get_weight_update() }; - self.graph.update_weight(wu.source, wu.target, wu.new_weight); + self.graph + .update_weight(wu.source, wu.target, wu.new_weight); } DeltaTag::Observation => { let obs = unsafe { *delta.get_observation() }; @@ -356,12 +357,8 @@ impl TileState { min_degree as u16 * 100 // weight scale factor }; - let mut fragment = WitnessFragment::new( - seed, - boundary, - self.graph.num_vertices, - local_min_cut, - ); + let mut fragment = + WitnessFragment::new(seed, boundary, self.graph.num_vertices, local_min_cut); fragment.component = self.graph.num_components; fragment.compute_hash(); diff --git a/crates/cognitum-gate-kernel/src/shard.rs b/crates/cognitum-gate-kernel/src/shard.rs index 4059030a5..302e99a7c 100644 --- a/crates/cognitum-gate-kernel/src/shard.rs +++ b/crates/cognitum-gate-kernel/src/shard.rs @@ -268,7 +268,10 @@ impl CompactGraph { weight: 0, flags: 0, }; MAX_SHARD_EDGES], - adjacency: [[AdjEntry { neighbor: 0, edge_id: 0 }; MAX_DEGREE]; MAX_SHARD_VERTICES], + adjacency: [[AdjEntry { + neighbor: 0, + edge_id: 0, + }; MAX_DEGREE]; MAX_SHARD_VERTICES], } } @@ -470,7 +473,11 @@ impl CompactGraph { /// /// SAFETY: Caller must ensure source < MAX_SHARD_VERTICES and vertex is active #[inline(always)] - pub unsafe fn find_edge_unchecked(&self, source: TileVertexId, target: TileVertexId) -> Option { + pub unsafe fn find_edge_unchecked( + &self, + source: TileVertexId, + target: TileVertexId, + ) -> Option { unsafe { let entry = self.vertices.get_unchecked(source as usize); let degree = entry.degree as usize; @@ -682,7 +689,12 @@ impl CompactGraph { } /// Remove from adjacency list using swap-remove - fn remove_from_adjacency(&mut self, v: TileVertexId, neighbor: TileVertexId, edge_id: TileEdgeId) { + fn remove_from_adjacency( + &mut self, + v: TileVertexId, + neighbor: TileVertexId, + edge_id: TileEdgeId, + ) { if v as usize >= MAX_SHARD_VERTICES { return; } @@ -808,7 +820,10 @@ impl CompactGraph { /// Iterator of weights from active edges #[inline] pub fn active_edge_weights(&self) -> impl Iterator + '_ { - self.edges.iter().filter(|e| e.is_active()).map(|e| e.weight) + self.edges + .iter() + .filter(|e| e.is_active()) + .map(|e| e.weight) } /// Compute total edge weight using SIMD-friendly accumulation diff --git a/crates/cognitum-gate-tilezero/benches/benchmarks.rs b/crates/cognitum-gate-tilezero/benches/benchmarks.rs index 1a7358df4..8bf0c3e44 100644 --- a/crates/cognitum-gate-tilezero/benches/benchmarks.rs +++ b/crates/cognitum-gate-tilezero/benches/benchmarks.rs @@ -13,12 +13,10 @@ use rand::Rng; use std::collections::HashMap; use cognitum_gate_tilezero::{ - ActionContext, ActionMetadata, ActionTarget, - GateDecision, GateThresholds, ReducedGraph, ThreeFilterDecision, - TileZero, TileId, merge::{EdgeSummary, MergeStrategy, NodeSummary, ReportMerger, WorkerReport}, - PermitState, PermitToken, ReceiptLog, TimestampProof, WitnessReceipt, WitnessSummary, - EvidenceFilter, + ActionContext, ActionMetadata, ActionTarget, EvidenceFilter, GateDecision, GateThresholds, + PermitState, PermitToken, ReceiptLog, ReducedGraph, ThreeFilterDecision, TileId, TileZero, + TimestampProof, WitnessReceipt, WitnessSummary, }; // ============================================================================ @@ -96,7 +94,11 @@ fn create_worker_report( for i in 0..boundary_edge_count { report.add_boundary_edge(EdgeSummary { source: format!("node-{}-{}", tile_id, i % node_count.max(1)), - target: format!("node-{}-{}", (tile_id as usize + 1) % 256, i % node_count.max(1)), + target: format!( + "node-{}-{}", + (tile_id as usize + 1) % 256, + i % node_count.max(1) + ), capacity: rng.gen_range(1.0..100.0), is_boundary: true, }); @@ -110,7 +112,11 @@ fn create_worker_report( } /// Create all 255 tile reports -fn create_all_tile_reports(epoch: u64, nodes_per_tile: usize, edges_per_tile: usize) -> Vec { +fn create_all_tile_reports( + epoch: u64, + nodes_per_tile: usize, + edges_per_tile: usize, +) -> Vec { (1..=255u8) .map(|tile_id| create_worker_report(tile_id, epoch, nodes_per_tile, edges_per_tile)) .collect() @@ -182,9 +188,7 @@ fn bench_merge_reports(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("255_tiles_minimal", name), &minimal_reports, - |b, reports| { - b.iter(|| black_box(merger.merge(black_box(reports)))) - }, + |b, reports| b.iter(|| black_box(merger.merge(black_box(reports)))), ); } @@ -222,9 +226,8 @@ fn bench_decision(c: &mut Criterion) { let ctx = create_action_context(0); group.bench_function("tilezero_full_decision", |b| { - b.to_async(&rt).iter(|| async { - black_box(tilezero.decide(black_box(&ctx)).await) - }); + b.to_async(&rt) + .iter(|| async { black_box(tilezero.decide(black_box(&ctx)).await) }); }); // Three-filter decision only (no crypto) @@ -237,13 +240,9 @@ fn bench_decision(c: &mut Criterion) { ]; for (name, graph) in &graph_states { - group.bench_with_input( - BenchmarkId::new("three_filter", name), - graph, - |b, graph| { - b.iter(|| black_box(decision.evaluate(black_box(graph)))) - }, - ); + group.bench_with_input(BenchmarkId::new("three_filter", name), graph, |b, graph| { + b.iter(|| black_box(decision.evaluate(black_box(graph)))) + }); } // Batch decisions @@ -278,9 +277,7 @@ fn bench_receipt_hash(c: &mut Criterion) { let receipt = create_test_receipt(0, [0u8; 32]); // Single hash - group.bench_function("hash_single", |b| { - b.iter(|| black_box(receipt.hash())) - }); + group.bench_function("hash_single", |b| b.iter(|| black_box(receipt.hash()))); // Hash with varying boundary sizes for boundary_size in [0, 10, 50, 100] { @@ -292,9 +289,7 @@ fn bench_receipt_hash(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("boundary_size", boundary_size), &receipt, - |b, receipt| { - b.iter(|| black_box(receipt.hash())) - }, + |b, receipt| b.iter(|| black_box(receipt.hash())), ); } @@ -328,9 +323,7 @@ fn bench_receipt_chain_verify(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("verify_chain", chain_length), &log, - |b, log| { - b.iter(|| black_box(log.verify_chain_to((chain_length - 1) as u64))) - }, + |b, log| b.iter(|| black_box(log.verify_chain_to((chain_length - 1) as u64))), ); } @@ -376,22 +369,23 @@ fn bench_permit_sign(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("action_len", action_len), &token, - |b, token| { - b.iter(|| black_box(state.sign_token(token.clone()))) - }, + |b, token| b.iter(|| black_box(state.sign_token(token.clone()))), ); } // Batch signing for batch_size in [10, 50, 100] { - let tokens: Vec<_> = (0..batch_size).map(|i| create_test_token(i as u64)).collect(); + let tokens: Vec<_> = (0..batch_size) + .map(|i| create_test_token(i as u64)) + .collect(); group.bench_with_input( BenchmarkId::new("batch_sign", batch_size), &tokens, |b, tokens| { b.iter(|| { - let signed: Vec<_> = tokens.iter() + let signed: Vec<_> = tokens + .iter() .cloned() .map(|t| state.sign_token(t)) .collect(); @@ -492,9 +486,7 @@ fn bench_evalue_computation(c: &mut Criterion) { // SIMD-friendly aggregation patterns let tile_count = 255; - let e_values: Vec = (0..tile_count) - .map(|i| 1.0 + (i as f64 * 0.01)) - .collect(); + let e_values: Vec = (0..tile_count).map(|i| 1.0 + (i as f64 * 0.01)).collect(); group.bench_function("aggregate_255_scalar", |b| { b.iter(|| { @@ -619,15 +611,9 @@ fn bench_receipt_log_operations(c: &mut Criterion) { // Criterion Groups // ============================================================================ -criterion_group!( - merge_benches, - bench_merge_reports, -); +criterion_group!(merge_benches, bench_merge_reports,); -criterion_group!( - decision_benches, - bench_decision, -); +criterion_group!(decision_benches, bench_decision,); criterion_group!( crypto_benches, @@ -644,4 +630,9 @@ criterion_group!( bench_receipt_log_operations, ); -criterion_main!(merge_benches, decision_benches, crypto_benches, additional_benches); +criterion_main!( + merge_benches, + decision_benches, + crypto_benches, + additional_benches +); diff --git a/crates/cognitum-gate-tilezero/benches/crypto_bench.rs b/crates/cognitum-gate-tilezero/benches/crypto_bench.rs index 37519ede4..a14c288a2 100644 --- a/crates/cognitum-gate-tilezero/benches/crypto_bench.rs +++ b/crates/cognitum-gate-tilezero/benches/crypto_bench.rs @@ -8,8 +8,8 @@ use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use cognitum_gate_tilezero::{ - GateDecision, PermitState, PermitToken, ReceiptLog, TimestampProof, - WitnessReceipt, WitnessSummary, + GateDecision, PermitState, PermitToken, ReceiptLog, TimestampProof, WitnessReceipt, + WitnessSummary, }; /// Create a test permit token @@ -120,9 +120,7 @@ fn bench_receipt_hashing(c: &mut Criterion) { let receipt = create_test_receipt(0, [0u8; 32]); - group.bench_function("hash_receipt", |b| { - b.iter(|| black_box(receipt.hash())) - }); + group.bench_function("hash_receipt", |b| b.iter(|| black_box(receipt.hash()))); // Benchmark with different summary sizes for boundary_size in [0, 10, 50, 100] { @@ -134,9 +132,7 @@ fn bench_receipt_hashing(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("hash_boundary_size", boundary_size), &receipt, - |b, receipt| { - b.iter(|| black_box(receipt.hash())) - }, + |b, receipt| b.iter(|| black_box(receipt.hash())), ); } @@ -160,9 +156,7 @@ fn bench_chain_verification(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("verify_chain", chain_length), &log, - |b, log| { - b.iter(|| black_box(log.verify_chain_to((chain_length - 1) as u64))) - }, + |b, log| b.iter(|| black_box(log.verify_chain_to((chain_length - 1) as u64))), ); } @@ -257,9 +251,7 @@ fn bench_token_encoding(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("encode_action_len", action_len), &signed, - |b, token| { - b.iter(|| black_box(token.encode_base64())) - }, + |b, token| b.iter(|| black_box(token.encode_base64())), ); } @@ -285,9 +277,7 @@ fn bench_signable_content(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("action_len", action_len), &token, - |b, token| { - b.iter(|| black_box(token.signable_content())) - }, + |b, token| b.iter(|| black_box(token.signable_content())), ); } @@ -301,14 +291,10 @@ fn bench_witness_summary_hash(c: &mut Criterion) { let summary = create_test_summary(); - group.bench_function("hash", |b| { - b.iter(|| black_box(summary.hash())) - }); + group.bench_function("hash", |b| b.iter(|| black_box(summary.hash()))); // JSON serialization (used in hash) - group.bench_function("to_json", |b| { - b.iter(|| black_box(summary.to_json())) - }); + group.bench_function("to_json", |b| b.iter(|| black_box(summary.to_json()))); group.finish(); } @@ -321,7 +307,9 @@ fn bench_batch_signing(c: &mut Criterion) { group.throughput(Throughput::Elements(batch_size as u64)); let state = PermitState::new(); - let tokens: Vec<_> = (0..batch_size).map(|i| create_test_token(i as u64)).collect(); + let tokens: Vec<_> = (0..batch_size) + .map(|i| create_test_token(i as u64)) + .collect(); group.bench_with_input( BenchmarkId::new("sequential", batch_size), @@ -342,7 +330,6 @@ fn bench_batch_signing(c: &mut Criterion) { group.finish(); } - criterion_group!( benches, bench_token_signing, diff --git a/crates/cognitum-gate-tilezero/benches/decision_bench.rs b/crates/cognitum-gate-tilezero/benches/decision_bench.rs index b5be28625..6b56534fe 100644 --- a/crates/cognitum-gate-tilezero/benches/decision_bench.rs +++ b/crates/cognitum-gate-tilezero/benches/decision_bench.rs @@ -8,8 +8,8 @@ use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criteri use std::collections::HashMap; use cognitum_gate_tilezero::{ - ActionContext, ActionMetadata, ActionTarget, DecisionOutcome, EvidenceFilter, - GateThresholds, ReducedGraph, ThreeFilterDecision, TileZero, + ActionContext, ActionMetadata, ActionTarget, DecisionOutcome, EvidenceFilter, GateThresholds, + ReducedGraph, ThreeFilterDecision, TileZero, }; /// Create a realistic action context for benchmarking @@ -30,10 +30,7 @@ fn create_action_context(id: usize) -> ActionContext { context: ActionMetadata { agent_id: "agent-001".to_string(), session_id: Some("session-12345".to_string()), - prior_actions: vec![ - "action-prev-1".to_string(), - "action-prev-2".to_string(), - ], + prior_actions: vec!["action-prev-1".to_string(), "action-prev-2".to_string()], urgency: "normal".to_string(), }, } @@ -98,15 +95,10 @@ fn bench_full_decision_pipeline(c: &mut Criterion) { let tilezero = TileZero::new(thresholds); let ctx = create_action_context(0); - group.bench_with_input( - BenchmarkId::new("tilezero_decide", name), - &ctx, - |b, ctx| { - b.to_async(&rt).iter(|| async { - black_box(tilezero.decide(black_box(ctx)).await) - }); - }, - ); + group.bench_with_input(BenchmarkId::new("tilezero_decide", name), &ctx, |b, ctx| { + b.to_async(&rt) + .iter(|| async { black_box(tilezero.decide(black_box(ctx)).await) }); + }); } group.finish(); @@ -174,9 +166,7 @@ fn bench_e_value_simd(c: &mut Criterion) { group.throughput(Throughput::Elements(tile_count as u64)); // Generate test data aligned for SIMD - let e_values: Vec = (0..tile_count) - .map(|i| 1.0 + (i as f64 * 0.01)) - .collect(); + let e_values: Vec = (0..tile_count).map(|i| 1.0 + (i as f64 * 0.01)).collect(); // Scalar baseline group.bench_function("aggregate_scalar", |b| { @@ -272,13 +262,9 @@ fn bench_witness_summary(c: &mut Criterion) { }); let summary = graph.witness_summary(); - group.bench_function("hash", |b| { - b.iter(|| black_box(summary.hash())) - }); + group.bench_function("hash", |b| b.iter(|| black_box(summary.hash()))); - group.bench_function("to_json", |b| { - b.iter(|| black_box(summary.to_json())) - }); + group.bench_function("to_json", |b| b.iter(|| black_box(summary.to_json()))); group.finish(); } diff --git a/crates/cognitum-gate-tilezero/benches/merge_bench.rs b/crates/cognitum-gate-tilezero/benches/merge_bench.rs index fef8aab2d..ce2b23484 100644 --- a/crates/cognitum-gate-tilezero/benches/merge_bench.rs +++ b/crates/cognitum-gate-tilezero/benches/merge_bench.rs @@ -35,7 +35,11 @@ fn create_worker_report( for i in 0..boundary_edge_count { report.add_boundary_edge(EdgeSummary { source: format!("node-{}-{}", tile_id, i % node_count.max(1)), - target: format!("node-{}-{}", (tile_id as usize + 1) % 256, i % node_count.max(1)), + target: format!( + "node-{}-{}", + (tile_id as usize + 1) % 256, + i % node_count.max(1) + ), capacity: rng.gen_range(1.0..100.0), is_boundary: true, }); @@ -84,9 +88,7 @@ fn bench_merge_255_tiles(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("minimal", name), &minimal_reports, - |b, reports| { - b.iter(|| black_box(merger.merge(black_box(reports)))) - }, + |b, reports| b.iter(|| black_box(merger.merge(black_box(reports)))), ); } @@ -99,9 +101,7 @@ fn bench_merge_255_tiles(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("realistic", name), &realistic_reports, - |b, reports| { - b.iter(|| black_box(merger.merge(black_box(reports)))) - }, + |b, reports| b.iter(|| black_box(merger.merge(black_box(reports)))), ); } @@ -114,9 +114,7 @@ fn bench_merge_255_tiles(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("heavy", name), &heavy_reports, - |b, reports| { - b.iter(|| black_box(merger.merge(black_box(reports)))) - }, + |b, reports| b.iter(|| black_box(merger.merge(black_box(reports)))), ); } @@ -139,9 +137,7 @@ fn bench_merge_scaling(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("tiles", tile_count), &reports, - |b, reports| { - b.iter(|| black_box(merger.merge(black_box(reports)))) - }, + |b, reports| b.iter(|| black_box(merger.merge(black_box(reports)))), ); } @@ -190,9 +186,7 @@ fn bench_node_merging(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("overlap_nodes", overlap), &reports, - |b, reports| { - b.iter(|| black_box(merger.merge(black_box(reports)))) - }, + |b, reports| b.iter(|| black_box(merger.merge(black_box(reports)))), ); } @@ -220,9 +214,7 @@ fn bench_edge_merging(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("edges_per_tile", edge_count), &reports, - |b, reports| { - b.iter(|| black_box(merger.merge(black_box(reports)))) - }, + |b, reports| b.iter(|| black_box(merger.merge(black_box(reports)))), ); } @@ -298,12 +290,16 @@ fn bench_confidence_aggregation(c: &mut Criterion) { for (name, strategy) in strategies { let merger = ReportMerger::new(strategy); - group.bench_with_input(BenchmarkId::new("strategy", name), &reports, |b, reports| { - b.iter(|| { - let merged = merger.merge(reports).unwrap(); - black_box(merged.confidence) - }) - }); + group.bench_with_input( + BenchmarkId::new("strategy", name), + &reports, + |b, reports| { + b.iter(|| { + let merged = merger.merge(reports).unwrap(); + black_box(merged.confidence) + }) + }, + ); } group.finish(); diff --git a/crates/cognitum-gate-tilezero/examples/basic_gate.rs b/crates/cognitum-gate-tilezero/examples/basic_gate.rs index e88ad09f2..d0b193b8d 100644 --- a/crates/cognitum-gate-tilezero/examples/basic_gate.rs +++ b/crates/cognitum-gate-tilezero/examples/basic_gate.rs @@ -23,8 +23,14 @@ async fn main() -> Result<(), Box> { println!("TileZero initialized with thresholds:"); println!(" Min cut: {}", tilezero.thresholds().min_cut); println!(" Max shift: {}", tilezero.thresholds().max_shift); - println!(" Deny threshold (tau_deny): {}", tilezero.thresholds().tau_deny); - println!(" Permit threshold (tau_permit): {}", tilezero.thresholds().tau_permit); + println!( + " Deny threshold (tau_deny): {}", + tilezero.thresholds().tau_deny + ); + println!( + " Permit threshold (tau_permit): {}", + tilezero.thresholds().tau_permit + ); println!(); // Create an action context diff --git a/crates/cognitum-gate-tilezero/examples/human_escalation.rs b/crates/cognitum-gate-tilezero/examples/human_escalation.rs index 5a6069109..36e443eed 100644 --- a/crates/cognitum-gate-tilezero/examples/human_escalation.rs +++ b/crates/cognitum-gate-tilezero/examples/human_escalation.rs @@ -18,8 +18,8 @@ async fn main() -> Result<(), Box> { // Create TileZero with conservative thresholds to trigger DEFER let thresholds = GateThresholds { - min_cut: 15.0, // Higher threshold - max_shift: 0.3, // Lower tolerance for shift + min_cut: 15.0, // Higher threshold + max_shift: 0.3, // Lower tolerance for shift tau_deny: 0.01, tau_permit: 100.0, permit_ttl_ns: 300_000_000_000, // 5 minutes @@ -104,7 +104,6 @@ async fn main() -> Result<(), Box> { println!(" - Provide additional context"); } } - } else { println!("Decision: {:?}", token.decision); println!("(Automatic - no human review needed)"); diff --git a/crates/cognitum-gate-tilezero/examples/receipt_audit.rs b/crates/cognitum-gate-tilezero/examples/receipt_audit.rs index 8776bb7d3..dc676d9cc 100644 --- a/crates/cognitum-gate-tilezero/examples/receipt_audit.rs +++ b/crates/cognitum-gate-tilezero/examples/receipt_audit.rs @@ -61,7 +61,10 @@ async fn main() -> Result<(), Box> { // Display receipt summary println!("\nReceipts:"); println!("{:-<60}", ""); - println!("{:<10} {:<15} {:<12} {:<20}", "Seq", "Action", "Decision", "Hash (first 8)"); + println!( + "{:<10} {:<15} {:<12} {:<20}", + "Seq", "Action", "Decision", "Hash (first 8)" + ); println!("{:-<60}", ""); for seq in 0..actions.len() as u64 { @@ -84,7 +87,8 @@ async fn main() -> Result<(), Box> { println!("\nExporting audit log..."); let audit_json = tilezero.export_receipts_json().await?; - let filename = format!("audit_log_{}.json", + let filename = format!( + "audit_log_{}.json", std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() diff --git a/crates/cognitum-gate-tilezero/src/decision.rs b/crates/cognitum-gate-tilezero/src/decision.rs index 28127dd5e..5f4c8400e 100644 --- a/crates/cognitum-gate-tilezero/src/decision.rs +++ b/crates/cognitum-gate-tilezero/src/decision.rs @@ -96,7 +96,13 @@ pub struct DecisionOutcome { impl DecisionOutcome { /// Create a permit outcome #[inline] - pub fn permit(confidence: f64, structural: f64, shift: f64, evidence: f64, mincut: f64) -> Self { + pub fn permit( + confidence: f64, + structural: f64, + shift: f64, + evidence: f64, + mincut: f64, + ) -> Self { Self { decision: GateDecision::Permit, confidence, diff --git a/crates/cognitum-gate-tilezero/src/evidence.rs b/crates/cognitum-gate-tilezero/src/evidence.rs index 6ebffa127..ca8f0a399 100644 --- a/crates/cognitum-gate-tilezero/src/evidence.rs +++ b/crates/cognitum-gate-tilezero/src/evidence.rs @@ -193,10 +193,7 @@ pub fn aggregate_tiles_simd(tile_e_values: &[f64]) -> f64 { /// /// # Returns /// Weighted geometric mean of e-values -pub fn mixture_evalue_adaptive( - log_e_values: &[f64], - weights: Option<&[f64]>, -) -> f64 { +pub fn mixture_evalue_adaptive(log_e_values: &[f64], weights: Option<&[f64]>) -> f64 { if log_e_values.is_empty() { return 1.0; } diff --git a/crates/cognitum-gate-tilezero/src/lib.rs b/crates/cognitum-gate-tilezero/src/lib.rs index 4b2a79e40..c353cbdaf 100644 --- a/crates/cognitum-gate-tilezero/src/lib.rs +++ b/crates/cognitum-gate-tilezero/src/lib.rs @@ -13,7 +13,10 @@ pub mod permit; pub mod receipt; pub mod supergraph; -pub use decision::{DecisionFilter, DecisionOutcome, EvidenceDecision, GateDecision, GateThresholds, ThreeFilterDecision}; +pub use decision::{ + DecisionFilter, DecisionOutcome, EvidenceDecision, GateDecision, GateThresholds, + ThreeFilterDecision, +}; pub use evidence::{AggregatedEvidence, EvidenceFilter}; pub use merge::{MergeStrategy, MergedReport, ReportMerger, WorkerReport}; pub use permit::{PermitState, PermitToken, TokenDecodeError, Verifier, VerifyError}; diff --git a/crates/cognitum-gate-tilezero/src/merge.rs b/crates/cognitum-gate-tilezero/src/merge.rs index f0a94b9fa..8bca1f4e5 100644 --- a/crates/cognitum-gate-tilezero/src/merge.rs +++ b/crates/cognitum-gate-tilezero/src/merge.rs @@ -350,10 +350,10 @@ impl ReportMerger { } MergeStrategy::WeightedAverage => { // OPTIMIZATION: Single pass for both sums - let (weighted_sum, coherence_sum) = data.iter().fold( - (0.0, 0.0), - |(ws, cs), (_, n)| (ws + n.weight * n.coherence, cs + n.coherence), - ); + let (weighted_sum, coherence_sum) = + data.iter().fold((0.0, 0.0), |(ws, cs), (_, n)| { + (ws + n.weight * n.coherence, cs + n.coherence) + }); if coherence_sum > 0.0 { weighted_sum / coherence_sum } else { diff --git a/crates/cognitum-gate-tilezero/src/permit.rs b/crates/cognitum-gate-tilezero/src/permit.rs index d0b120f29..86fb2d696 100644 --- a/crates/cognitum-gate-tilezero/src/permit.rs +++ b/crates/cognitum-gate-tilezero/src/permit.rs @@ -282,8 +282,8 @@ mod tests { let token = PermitToken { decision: GateDecision::Permit, action_id: "test-action".to_string(), - timestamp: 1000000000, // Long ago - ttl_ns: 1, // 1 nanosecond TTL + timestamp: 1000000000, // Long ago + ttl_ns: 1, // 1 nanosecond TTL witness_hash: [0u8; 32], sequence: 0, signature: [0u8; 64], @@ -295,6 +295,9 @@ mod tests { assert!(verifier.verify(&signed).is_ok()); // But full verification (including TTL) should fail - assert!(matches!(verifier.verify_full(&signed), Err(VerifyError::Expired))); + assert!(matches!( + verifier.verify_full(&signed), + Err(VerifyError::Expired) + )); } } diff --git a/crates/cognitum-gate-tilezero/src/supergraph.rs b/crates/cognitum-gate-tilezero/src/supergraph.rs index 432714197..d1e1afe62 100644 --- a/crates/cognitum-gate-tilezero/src/supergraph.rs +++ b/crates/cognitum-gate-tilezero/src/supergraph.rs @@ -1,7 +1,7 @@ //! Reduced supergraph from worker tile summaries -use crate::{TileId, WitnessFragment}; use crate::receipt::WitnessSummary; +use crate::{TileId, WitnessFragment}; use std::collections::HashMap; /// Reduced graph maintained by TileZero @@ -23,7 +23,7 @@ impl ReducedGraph { pub fn new() -> Self { Self { tile_coherence: HashMap::new(), - global_cut_value: 100.0, // Start with high coherence + global_cut_value: 100.0, // Start with high coherence aggregated_e_value: 100.0, // Start with high evidence shift_pressure: 0.0, boundary_edges: 0, diff --git a/crates/cognitum-gate-tilezero/tests/decision_tests.rs b/crates/cognitum-gate-tilezero/tests/decision_tests.rs index 4ece75a56..f4e42d668 100644 --- a/crates/cognitum-gate-tilezero/tests/decision_tests.rs +++ b/crates/cognitum-gate-tilezero/tests/decision_tests.rs @@ -399,7 +399,11 @@ mod serialization { #[test] fn test_decision_serialization() { - let decisions = [GateDecision::Permit, GateDecision::Defer, GateDecision::Deny]; + let decisions = [ + GateDecision::Permit, + GateDecision::Defer, + GateDecision::Deny, + ]; for decision in &decisions { let json = serde_json::to_string(decision).unwrap(); diff --git a/crates/cognitum-gate-tilezero/tests/merge_tests.rs b/crates/cognitum-gate-tilezero/tests/merge_tests.rs index 8aea024b2..be4fa4f63 100644 --- a/crates/cognitum-gate-tilezero/tests/merge_tests.rs +++ b/crates/cognitum-gate-tilezero/tests/merge_tests.rs @@ -7,8 +7,7 @@ //! - Property-based tests for merge invariants use cognitum_gate_tilezero::merge::{ - EdgeSummary, MergeError, MergeStrategy, MergedReport, NodeSummary, ReportMerger, - WorkerReport, + EdgeSummary, MergeError, MergeStrategy, MergedReport, NodeSummary, ReportMerger, WorkerReport, }; fn create_test_report(tile_id: u8, epoch: u64) -> WorkerReport { diff --git a/crates/cognitum-gate-tilezero/tests/permit_tests.rs b/crates/cognitum-gate-tilezero/tests/permit_tests.rs index 03c884ee1..9fa4760f8 100644 --- a/crates/cognitum-gate-tilezero/tests/permit_tests.rs +++ b/crates/cognitum-gate-tilezero/tests/permit_tests.rs @@ -6,7 +6,9 @@ //! - TTL validation //! - Security tests (invalid signatures, replay attacks, tamper detection) -use cognitum_gate_tilezero::permit::{PermitState, PermitToken, TokenDecodeError, Verifier, VerifyError}; +use cognitum_gate_tilezero::permit::{ + PermitState, PermitToken, TokenDecodeError, Verifier, VerifyError, +}; use cognitum_gate_tilezero::GateDecision; fn create_test_token(action_id: &str, sequence: u64) -> PermitToken { @@ -372,10 +374,8 @@ mod base64_encoding { #[test] fn test_decode_invalid_json() { // Valid base64 but not JSON - let encoded = base64::Engine::encode( - &base64::engine::general_purpose::STANDARD, - b"not json", - ); + let encoded = + base64::Engine::encode(&base64::engine::general_purpose::STANDARD, b"not json"); let result = PermitToken::decode_base64(&encoded); assert!(matches!(result, Err(TokenDecodeError::InvalidJson))); } diff --git a/crates/cognitum-gate-tilezero/tests/receipt_tests.rs b/crates/cognitum-gate-tilezero/tests/receipt_tests.rs index f1b09684a..0fc0b03f6 100644 --- a/crates/cognitum-gate-tilezero/tests/receipt_tests.rs +++ b/crates/cognitum-gate-tilezero/tests/receipt_tests.rs @@ -6,11 +6,11 @@ //! - Tamper detection //! - Security tests (chain manipulation, replay attacks) +use cognitum_gate_tilezero::permit::PermitToken; use cognitum_gate_tilezero::receipt::{ EvidentialWitness, PredictiveWitness, ReceiptLog, StructuralWitness, TimestampProof, WitnessReceipt, WitnessSummary, }; -use cognitum_gate_tilezero::permit::PermitToken; use cognitum_gate_tilezero::GateDecision; fn create_test_token(sequence: u64, action_id: &str) -> PermitToken { diff --git a/crates/mcp-gate/src/main.rs b/crates/mcp-gate/src/main.rs index e62b80583..76d3776d4 100644 --- a/crates/mcp-gate/src/main.rs +++ b/crates/mcp-gate/src/main.rs @@ -8,8 +8,7 @@ use tracing_subscriber::{fmt, prelude::*, EnvFilter}; #[tokio::main] async fn main() -> Result<(), Box> { // Initialize logging - let filter = EnvFilter::try_from_default_env() - .unwrap_or_else(|_| EnvFilter::new("info")); + let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")); tracing_subscriber::registry() .with(fmt::layer().with_writer(std::io::stderr)) @@ -22,10 +21,7 @@ async fn main() -> Result<(), Box> { // Create and run server let server = McpGateServer::with_thresholds(config.thresholds); - tracing::info!( - "MCP Gate server v{} starting", - env!("CARGO_PKG_VERSION") - ); + tracing::info!("MCP Gate server v{} starting", env!("CARGO_PKG_VERSION")); server.run_stdio().await?; diff --git a/crates/mcp-gate/src/types.rs b/crates/mcp-gate/src/types.rs index 584f62f69..82171cce9 100644 --- a/crates/mcp-gate/src/types.rs +++ b/crates/mcp-gate/src/types.rs @@ -380,10 +380,8 @@ mod tests { #[test] fn test_jsonrpc_response() { - let resp = JsonRpcResponse::success( - serde_json::json!(1), - serde_json::json!({"status": "ok"}), - ); + let resp = + JsonRpcResponse::success(serde_json::json!(1), serde_json::json!({"status": "ok"})); assert_eq!(resp.jsonrpc, "2.0"); assert!(resp.result.is_some()); assert!(resp.error.is_none()); diff --git a/crates/prime-radiant/benches/attention_bench.rs b/crates/prime-radiant/benches/attention_bench.rs index b61decf08..f1c27ba7f 100644 --- a/crates/prime-radiant/benches/attention_bench.rs +++ b/crates/prime-radiant/benches/attention_bench.rs @@ -6,9 +6,7 @@ fn attention_benchmark(c: &mut Criterion) { let mut group = c.benchmark_group("attention"); // Placeholder benchmark - requires attention feature - group.bench_function("placeholder", |b| { - b.iter(|| black_box(42)) - }); + group.bench_function("placeholder", |b| b.iter(|| black_box(42))); group.finish(); } diff --git a/crates/prime-radiant/benches/coherence_bench.rs b/crates/prime-radiant/benches/coherence_bench.rs index da94292b5..b7061cd4e 100644 --- a/crates/prime-radiant/benches/coherence_bench.rs +++ b/crates/prime-radiant/benches/coherence_bench.rs @@ -6,9 +6,7 @@ fn coherence_benchmark(c: &mut Criterion) { let mut group = c.benchmark_group("coherence"); // Placeholder benchmark - will be implemented when coherence module is complete - group.bench_function("placeholder", |b| { - b.iter(|| black_box(42)) - }); + group.bench_function("placeholder", |b| b.iter(|| black_box(42))); group.finish(); } diff --git a/crates/prime-radiant/benches/coherence_benchmarks.rs b/crates/prime-radiant/benches/coherence_benchmarks.rs index e132302bb..1e620caf1 100644 --- a/crates/prime-radiant/benches/coherence_benchmarks.rs +++ b/crates/prime-radiant/benches/coherence_benchmarks.rs @@ -13,9 +13,7 @@ //! 2. Restriction Maps - identity, diagonal, dense, sparse //! 3. Scaling Tests - nodes, edges, dimensions -use criterion::{ - black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput, -}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use std::collections::HashMap; // ============================================================================ @@ -532,11 +530,9 @@ fn bench_energy_computation(c: &mut Criterion) { group.sample_size(sample_size); group.throughput(Throughput::Elements(graph.edges.len() as u64)); - group.bench_with_input( - BenchmarkId::new("nodes", num_nodes), - &num_nodes, - |b, _| b.iter(|| black_box(graph.compute_total_energy())), - ); + group.bench_with_input(BenchmarkId::new("nodes", num_nodes), &num_nodes, |b, _| { + b.iter(|| black_box(graph.compute_total_energy())) + }); } group.finish(); @@ -577,7 +573,11 @@ fn bench_incremental_update(c: &mut Criterion) { node.state = new_state; } - let affected = self.node_to_edges.get(&node_id).cloned().unwrap_or_default(); + let affected = self + .node_to_edges + .get(&node_id) + .cloned() + .unwrap_or_default(); let mut source_buf = vec![0.0f32; self.graph.edge_dim]; let mut target_buf = vec![0.0f32; self.graph.edge_dim]; @@ -793,8 +793,14 @@ fn bench_restriction_map_types(c: &mut Criterion) { // Identity maps { - let graph = - SheafGraph::with_restriction_type(num_nodes, 4, state_dim, state_dim, MapType::Identity, 42); + let graph = SheafGraph::with_restriction_type( + num_nodes, + 4, + state_dim, + state_dim, + MapType::Identity, + 42, + ); group.throughput(Throughput::Elements(graph.edges.len() as u64)); group.bench_function("identity", |b| { b.iter(|| black_box(graph.compute_total_energy())) @@ -803,8 +809,14 @@ fn bench_restriction_map_types(c: &mut Criterion) { // Diagonal maps { - let graph = - SheafGraph::with_restriction_type(num_nodes, 4, state_dim, state_dim, MapType::Diagonal, 42); + let graph = SheafGraph::with_restriction_type( + num_nodes, + 4, + state_dim, + state_dim, + MapType::Diagonal, + 42, + ); group.bench_function("diagonal", |b| { b.iter(|| black_box(graph.compute_total_energy())) }); @@ -812,8 +824,14 @@ fn bench_restriction_map_types(c: &mut Criterion) { // Dense maps { - let graph = - SheafGraph::with_restriction_type(num_nodes, 4, state_dim, state_dim, MapType::Dense, 42); + let graph = SheafGraph::with_restriction_type( + num_nodes, + 4, + state_dim, + state_dim, + MapType::Dense, + 42, + ); group.bench_function("dense", |b| { b.iter(|| black_box(graph.compute_total_energy())) }); @@ -901,7 +919,9 @@ fn bench_batch_residual(c: &mut Criterion) { }) .collect(); - let states: Vec> = (0..batch_size + 1).map(|i| generate_state(dim, i as u64)).collect(); + let states: Vec> = (0..batch_size + 1) + .map(|i| generate_state(dim, i as u64)) + .collect(); group.throughput(Throughput::Elements(batch_size as u64)); @@ -967,7 +987,15 @@ fn bench_memory_patterns(c: &mut Criterion) { // Chain graph (sequential access) { let nodes: HashMap = (0..num_nodes as u64) - .map(|id| (id, SheafNode { id, state: generate_state(dim, id) })) + .map(|id| { + ( + id, + SheafNode { + id, + state: generate_state(dim, id), + }, + ) + }) .collect(); let edges: Vec = (0..num_nodes - 1) diff --git a/crates/prime-radiant/benches/energy_bench.rs b/crates/prime-radiant/benches/energy_bench.rs index 57e32ec5f..46d3511f2 100644 --- a/crates/prime-radiant/benches/energy_bench.rs +++ b/crates/prime-radiant/benches/energy_bench.rs @@ -409,11 +409,9 @@ fn bench_state_dimension(c: &mut Criterion) { let graph = SheafGraph::random(num_nodes, avg_degree, state_dim, 42); group.throughput(Throughput::Elements(graph.edges.len() as u64)); - group.bench_with_input( - BenchmarkId::new("dim", state_dim), - &state_dim, - |b, _| b.iter(|| black_box(graph.compute_total_energy())), - ); + group.bench_with_input(BenchmarkId::new("dim", state_dim), &state_dim, |b, _| { + b.iter(|| black_box(graph.compute_total_energy())) + }); } group.finish(); diff --git a/crates/prime-radiant/benches/gate_bench.rs b/crates/prime-radiant/benches/gate_bench.rs index b633a9f5e..2509289e1 100644 --- a/crates/prime-radiant/benches/gate_bench.rs +++ b/crates/prime-radiant/benches/gate_bench.rs @@ -109,7 +109,9 @@ pub struct EnergyHistory { impl EnergyHistory { pub fn new(max_scopes: usize, window_size: usize) -> Self { Self { - history: (0..max_scopes).map(|_| VecDeque::with_capacity(window_size)).collect(), + history: (0..max_scopes) + .map(|_| VecDeque::with_capacity(window_size)) + .collect(), max_scopes, window_size, } @@ -140,10 +142,7 @@ impl EnergyHistory { let cutoff = current_time_ms.saturating_sub(window_ms); // Check if all samples in window are above threshold - let samples_in_window: Vec<_> = queue - .iter() - .filter(|(ts, _)| *ts >= cutoff) - .collect(); + let samples_in_window: Vec<_> = queue.iter().filter(|(ts, _)| *ts >= cutoff).collect(); if samples_in_window.is_empty() { return false; @@ -160,10 +159,7 @@ impl EnergyHistory { let queue = &self.history[scope_id as usize]; let cutoff = current_time_ms.saturating_sub(window_ms); - let samples: Vec<_> = queue - .iter() - .filter(|(ts, _)| *ts >= cutoff) - .collect(); + let samples: Vec<_> = queue.iter().filter(|(ts, _)| *ts >= cutoff).collect(); if samples.len() < 2 { return None; @@ -257,7 +253,8 @@ impl CoherenceGate { let current_energy = energy.scope_energy(action.scope_id); // Record in history - self.history.record(action.scope_id, self.current_time_ms, current_energy); + self.history + .record(action.scope_id, self.current_time_ms, current_energy); // Determine lane based on energy let lane = if current_energy < self.config.reflex { @@ -279,8 +276,13 @@ impl CoherenceGate { ); // Check for growing incoherence (trend) - let growing = self.history - .trend(action.scope_id, self.config.persistence_window_ms, self.current_time_ms) + let growing = self + .history + .trend( + action.scope_id, + self.config.persistence_window_ms, + self.current_time_ms, + ) .map(|t| t > 0.01) .unwrap_or(false); @@ -404,9 +406,7 @@ fn bench_gate_fast_path(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("evaluate_fast", format!("{:.2}", energy)), &energy, - |b, &e| { - b.iter(|| black_box(gate.evaluate_fast(black_box(e)))) - }, + |b, &e| b.iter(|| black_box(gate.evaluate_fast(black_box(e)))), ); } @@ -466,20 +466,13 @@ fn bench_history_operations(c: &mut Criterion) { // Check threshold group.bench_function("check_threshold", |b| { b.iter(|| { - history.is_above_threshold( - black_box(5), - black_box(0.3), - black_box(100), - black_box(500), - ) + history.is_above_threshold(black_box(5), black_box(0.3), black_box(100), black_box(500)) }) }); // Compute trend group.bench_function("compute_trend", |b| { - b.iter(|| { - history.trend(black_box(5), black_box(100), black_box(500)) - }) + b.iter(|| history.trend(black_box(5), black_box(100), black_box(500))) }); group.finish(); diff --git a/crates/prime-radiant/benches/gpu_benchmarks.rs b/crates/prime-radiant/benches/gpu_benchmarks.rs index 46d34f69d..1a702969a 100644 --- a/crates/prime-radiant/benches/gpu_benchmarks.rs +++ b/crates/prime-radiant/benches/gpu_benchmarks.rs @@ -18,9 +18,7 @@ //! cargo bench --features gpu --bench gpu_benchmarks //! ``` -use criterion::{ - black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput, -}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use std::collections::hash_map::DefaultHasher; use std::collections::HashMap; use std::hash::{Hash, Hasher}; @@ -222,7 +220,11 @@ fn batch_routing_cpu( // Sort by score (descending) and take top-k expert_scores.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); - let top_experts: Vec = expert_scores.iter().take(top_k).map(|(idx, _)| *idx).collect(); + let top_experts: Vec = expert_scores + .iter() + .take(top_k) + .map(|(idx, _)| *idx) + .collect(); results.push((t, top_experts)); } @@ -254,7 +256,7 @@ mod gpu_impl { // Simulate GPU overhead let _upload_time = simulate_memory_transfer( graph.nodes.len() * graph.state_dim * 4, // bytes - true, // host to device + true, // host to device ); // Actual computation would happen on GPU @@ -427,9 +429,9 @@ fn bench_attention_cpu_vs_gpu(c: &mut Criterion) { // Typical attention configurations let configs = [ - (128, 64, "small"), // seq_len=128, head_dim=64 - (512, 64, "medium"), // seq_len=512, head_dim=64 - (2048, 64, "large"), // seq_len=2048, head_dim=64 + (128, 64, "small"), // seq_len=128, head_dim=64 + (512, 64, "medium"), // seq_len=512, head_dim=64 + (2048, 64, "large"), // seq_len=2048, head_dim=64 ]; for (seq_len, head_dim, label) in configs { @@ -759,10 +761,7 @@ criterion_group!( bench_multihead_attention, ); -criterion_group!( - routing_benches, - bench_batch_routing_cpu_vs_gpu, -); +criterion_group!(routing_benches, bench_batch_routing_cpu_vs_gpu,); criterion_group!( transfer_benches, diff --git a/crates/prime-radiant/benches/hyperbolic_bench.rs b/crates/prime-radiant/benches/hyperbolic_bench.rs index 80937cef0..efaa68923 100644 --- a/crates/prime-radiant/benches/hyperbolic_bench.rs +++ b/crates/prime-radiant/benches/hyperbolic_bench.rs @@ -374,7 +374,9 @@ fn bench_knn_hyperbolic(c: &mut Criterion) { let dim = 64; let curvature = -1.0; - let points: Vec> = (0..1000).map(|i| generate_point(dim, i as u64, 0.9)).collect(); + let points: Vec> = (0..1000) + .map(|i| generate_point(dim, i as u64, 0.9)) + .collect(); let query = generate_point(dim, 999, 0.9); for k in [1, 5, 10, 50] { @@ -389,7 +391,10 @@ fn bench_knn_hyperbolic(c: &mut Criterion) { // Partial sort for k-nearest distances.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap()); - let result = distances[..k].iter().map(|(i, d)| (*i, *d)).collect::>(); + let result = distances[..k] + .iter() + .map(|(i, d)| (*i, *d)) + .collect::>(); black_box(result) }) }); @@ -460,9 +465,7 @@ fn bench_curvature_impact(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("curvature", format!("{:.1}", curvature)), &curvature, - |b, &c| { - b.iter(|| poincare_distance(black_box(&x), black_box(&y), black_box(c))) - }, + |b, &c| b.iter(|| poincare_distance(black_box(&x), black_box(&y), black_box(c))), ); } diff --git a/crates/prime-radiant/benches/incremental_bench.rs b/crates/prime-radiant/benches/incremental_bench.rs index 072c6229b..4884bd7a3 100644 --- a/crates/prime-radiant/benches/incremental_bench.rs +++ b/crates/prime-radiant/benches/incremental_bench.rs @@ -266,7 +266,11 @@ fn generate_state(dim: usize, seed: u64) -> Vec { .collect() } -fn create_random_graph(num_nodes: usize, avg_degree: usize, state_dim: usize) -> IncrementalCoherence { +fn create_random_graph( + num_nodes: usize, + avg_degree: usize, + state_dim: usize, +) -> IncrementalCoherence { use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; @@ -487,14 +491,18 @@ fn bench_state_dim_impact(c: &mut Criterion) { for state_dim in [8, 32, 64, 128, 256] { let mut tracker = create_random_graph(num_nodes, avg_degree, state_dim); - group.bench_with_input(BenchmarkId::new("update", state_dim), &state_dim, |b, &dim| { - let node_id = 5000u64; - b.iter(|| { - let new_state = generate_state(dim, rand::random()); - tracker.update_node(black_box(node_id), new_state); - black_box(tracker.energy()) - }) - }); + group.bench_with_input( + BenchmarkId::new("update", state_dim), + &state_dim, + |b, &dim| { + let node_id = 5000u64; + b.iter(|| { + let new_state = generate_state(dim, rand::random()); + tracker.update_node(black_box(node_id), new_state); + black_box(tracker.energy()) + }) + }, + ); } group.finish(); diff --git a/crates/prime-radiant/benches/mincut_bench.rs b/crates/prime-radiant/benches/mincut_bench.rs index 991c917d5..6c491fd41 100644 --- a/crates/prime-radiant/benches/mincut_bench.rs +++ b/crates/prime-radiant/benches/mincut_bench.rs @@ -193,7 +193,12 @@ impl DynamicGraph { GraphStats { vertices: self.vertex_count, edges: self.edge_count, - max_degree: self.adjacency.values().map(|adj| adj.len()).max().unwrap_or(0), + max_degree: self + .adjacency + .values() + .map(|adj| adj.len()) + .max() + .unwrap_or(0), avg_degree: if self.vertex_count > 0 { (self.edge_count * 2) as f64 / self.vertex_count as f64 } else { @@ -316,7 +321,11 @@ impl SubpolynomialMinCut { } else { // Multiple components - use first vs rest let left = components[0].clone(); - let right: HashSet<_> = components[1..].iter().flat_map(|c| c.iter()).copied().collect(); + let right: HashSet<_> = components[1..] + .iter() + .flat_map(|c| c.iter()) + .copied() + .collect(); (left, right) } } @@ -375,18 +384,14 @@ fn bench_insert_edge(c: &mut Criterion) { mincut.insert_edge(*u, *v, *w); } - group.bench_with_input( - BenchmarkId::new("insert_single", size), - &size, - |b, &n| { - let mut i = edges.len() / 2; - b.iter(|| { - let (u, v, w) = edges[i % edges.len()]; - black_box(mincut.insert_edge(u + n as u64, v + n as u64, w)); - i += 1; - }) - }, - ); + group.bench_with_input(BenchmarkId::new("insert_single", size), &size, |b, &n| { + let mut i = edges.len() / 2; + b.iter(|| { + let (u, v, w) = edges[i % edges.len()]; + black_box(mincut.insert_edge(u + n as u64, v + n as u64, w)); + i += 1; + }) + }); } group.finish(); @@ -520,45 +525,41 @@ fn bench_mixed_workload(c: &mut Criterion) { for size in [100, 1000, 10000] { let edges = generate_random_graph(size, size * 2, 42); - group.bench_with_input( - BenchmarkId::new("mixed_ops", size), - &size, - |b, &n| { - b.iter_batched( - || { - let mut mincut = SubpolynomialMinCut::with_capacity(n, n * 3); - for (u, v, w) in &edges { - mincut.insert_edge(*u, *v, *w); - } - (mincut, 0usize) - }, - |(mut mincut, mut op_idx)| { - // 50% insert, 30% delete, 20% query - match op_idx % 10 { - 0..=4 => { - let u = (op_idx * 37) as u64 % n as u64; - let v = (op_idx * 73 + 1) as u64 % n as u64; - if u != v { - mincut.insert_edge(u + n as u64, v + n as u64, 1.0); - } - } - 5..=7 => { - if !edges.is_empty() { - let (u, v, _) = edges[op_idx % edges.len()]; - mincut.delete_edge(u, v); - } - } - _ => { - let _ = mincut.min_cut(); + group.bench_with_input(BenchmarkId::new("mixed_ops", size), &size, |b, &n| { + b.iter_batched( + || { + let mut mincut = SubpolynomialMinCut::with_capacity(n, n * 3); + for (u, v, w) in &edges { + mincut.insert_edge(*u, *v, *w); + } + (mincut, 0usize) + }, + |(mut mincut, mut op_idx)| { + // 50% insert, 30% delete, 20% query + match op_idx % 10 { + 0..=4 => { + let u = (op_idx * 37) as u64 % n as u64; + let v = (op_idx * 73 + 1) as u64 % n as u64; + if u != v { + mincut.insert_edge(u + n as u64, v + n as u64, 1.0); } } - op_idx += 1; - black_box(op_idx) - }, - criterion::BatchSize::SmallInput, - ) - }, - ); + 5..=7 => { + if !edges.is_empty() { + let (u, v, _) = edges[op_idx % edges.len()]; + mincut.delete_edge(u, v); + } + } + _ => { + let _ = mincut.min_cut(); + } + } + op_idx += 1; + black_box(op_idx) + }, + criterion::BatchSize::SmallInput, + ) + }); } group.finish(); diff --git a/crates/prime-radiant/benches/residual_bench.rs b/crates/prime-radiant/benches/residual_bench.rs index 06d4ccebd..6a73c7e43 100644 --- a/crates/prime-radiant/benches/residual_bench.rs +++ b/crates/prime-radiant/benches/residual_bench.rs @@ -219,15 +219,9 @@ fn bench_single_residual(c: &mut Criterion) { rho_target, }; - group.bench_with_input( - BenchmarkId::new("identity_map", dim), - &dim, - |b, _| { - b.iter(|| { - edge.residual(black_box(&source_state), black_box(&target_state)) - }) - }, - ); + group.bench_with_input(BenchmarkId::new("identity_map", dim), &dim, |b, _| { + b.iter(|| edge.residual(black_box(&source_state), black_box(&target_state))) + }); } // Test with projection (non-identity maps) @@ -248,11 +242,7 @@ fn bench_single_residual(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("projection_map", format!("{}to{}", input_dim, output_dim)), &(input_dim, output_dim), - |b, _| { - b.iter(|| { - edge.residual(black_box(&source_state), black_box(&target_state)) - }) - }, + |b, _| b.iter(|| edge.residual(black_box(&source_state), black_box(&target_state))), ); } @@ -407,9 +397,7 @@ fn bench_restriction_map(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("identity_apply_into", dim), &dim, - |b, _| { - b.iter(|| rho.apply_into(black_box(&input), black_box(&mut output))) - }, + |b, _| b.iter(|| rho.apply_into(black_box(&input), black_box(&mut output))), ); } @@ -426,11 +414,12 @@ fn bench_restriction_map(c: &mut Criterion) { ); group.bench_with_input( - BenchmarkId::new("projection_apply_into", format!("{}x{}", input_dim, output_dim)), + BenchmarkId::new( + "projection_apply_into", + format!("{}x{}", input_dim, output_dim), + ), &(input_dim, output_dim), - |b, _| { - b.iter(|| rho.apply_into(black_box(&input), black_box(&mut output))) - }, + |b, _| b.iter(|| rho.apply_into(black_box(&input), black_box(&mut output))), ); } @@ -448,45 +437,57 @@ fn bench_simd_patterns(c: &mut Criterion) { let b = generate_state(dim, 123); // Scalar subtraction and norm - group.bench_with_input(BenchmarkId::new("scalar_diff_norm", dim), &dim, |b_iter, _| { - b_iter.iter(|| { - let mut norm_sq = 0.0f32; - for i in 0..dim { - let diff = a[i] - b[i]; - norm_sq += diff * diff; - } - black_box(norm_sq) - }) - }); + group.bench_with_input( + BenchmarkId::new("scalar_diff_norm", dim), + &dim, + |b_iter, _| { + b_iter.iter(|| { + let mut norm_sq = 0.0f32; + for i in 0..dim { + let diff = a[i] - b[i]; + norm_sq += diff * diff; + } + black_box(norm_sq) + }) + }, + ); // Iterator-based (auto-vectorization friendly) - group.bench_with_input(BenchmarkId::new("iter_diff_norm", dim), &dim, |b_iter, _| { - b_iter.iter(|| { - let norm_sq: f32 = a - .iter() - .zip(b.iter()) - .map(|(x, y)| { - let d = x - y; - d * d - }) - .sum(); - black_box(norm_sq) - }) - }); + group.bench_with_input( + BenchmarkId::new("iter_diff_norm", dim), + &dim, + |b_iter, _| { + b_iter.iter(|| { + let norm_sq: f32 = a + .iter() + .zip(b.iter()) + .map(|(x, y)| { + let d = x - y; + d * d + }) + .sum(); + black_box(norm_sq) + }) + }, + ); // Chunked for explicit SIMD opportunity - group.bench_with_input(BenchmarkId::new("chunked_diff_norm", dim), &dim, |b_iter, _| { - b_iter.iter(|| { - let mut accum = [0.0f32; 8]; - for (chunk_a, chunk_b) in a.chunks(8).zip(b.chunks(8)) { - for i in 0..chunk_a.len() { - let d = chunk_a[i] - chunk_b[i]; - accum[i] += d * d; + group.bench_with_input( + BenchmarkId::new("chunked_diff_norm", dim), + &dim, + |b_iter, _| { + b_iter.iter(|| { + let mut accum = [0.0f32; 8]; + for (chunk_a, chunk_b) in a.chunks(8).zip(b.chunks(8)) { + for i in 0..chunk_a.len() { + let d = chunk_a[i] - chunk_b[i]; + accum[i] += d * d; + } } - } - black_box(accum.iter().sum::()) - }) - }); + black_box(accum.iter().sum::()) + }) + }, + ); } group.finish(); diff --git a/crates/prime-radiant/benches/simd_benchmarks.rs b/crates/prime-radiant/benches/simd_benchmarks.rs index d7097cc0a..022e217ba 100644 --- a/crates/prime-radiant/benches/simd_benchmarks.rs +++ b/crates/prime-radiant/benches/simd_benchmarks.rs @@ -14,9 +14,7 @@ //! - aarch64: NEON (128-bit, f32x4) //! - WASM: SIMD128 (128-bit) -use criterion::{ - black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput, -}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; @@ -405,26 +403,14 @@ fn bench_dense_matmul(c: &mut Criterion) { group.bench_with_input(BenchmarkId::new("naive", size), &size, |b, _| { b.iter(|| { - matmul_naive( - black_box(&matrix), - black_box(&x), - &mut y, - size, - size, - ); + matmul_naive(black_box(&matrix), black_box(&x), &mut y, size, size); black_box(y[0]) }) }); group.bench_with_input(BenchmarkId::new("unrolled", size), &size, |b, _| { b.iter(|| { - matmul_unrolled( - black_box(&matrix), - black_box(&x), - &mut y, - size, - size, - ); + matmul_unrolled(black_box(&matrix), black_box(&x), &mut y, size, size); black_box(y[0]) }) }); @@ -432,13 +418,7 @@ fn bench_dense_matmul(c: &mut Criterion) { #[cfg(feature = "simd")] group.bench_with_input(BenchmarkId::new("simd", size), &size, |b, _| { b.iter(|| { - simd_impl::matmul_simd( - black_box(&matrix), - black_box(&x), - &mut y, - size, - size, - ); + simd_impl::matmul_simd(black_box(&matrix), black_box(&x), &mut y, size, size); black_box(y[0]) }) }); @@ -464,13 +444,7 @@ fn bench_projection_matmul(c: &mut Criterion) { &(in_dim, out_dim), |b, _| { b.iter(|| { - matmul_naive( - black_box(&matrix), - black_box(&x), - &mut y, - out_dim, - in_dim, - ); + matmul_naive(black_box(&matrix), black_box(&x), &mut y, out_dim, in_dim); black_box(y[0]) }) }, @@ -481,13 +455,7 @@ fn bench_projection_matmul(c: &mut Criterion) { &(in_dim, out_dim), |b, _| { b.iter(|| { - matmul_unrolled( - black_box(&matrix), - black_box(&x), - &mut y, - out_dim, - in_dim, - ); + matmul_unrolled(black_box(&matrix), black_box(&x), &mut y, out_dim, in_dim); black_box(y[0]) }) }, @@ -636,7 +604,12 @@ fn bench_batch_residual(c: &mut Criterion) { BenchmarkId::new("naive", batch_size), &batch_size, |b, _| { - b.iter(|| black_box(batch_residual_naive(black_box(&sources), black_box(&targets)))) + b.iter(|| { + black_box(batch_residual_naive( + black_box(&sources), + black_box(&targets), + )) + }) }, ); @@ -728,9 +701,14 @@ fn bench_throughput_scaling(c: &mut Criterion) { ); #[cfg(feature = "simd")] - group.bench_with_input(BenchmarkId::new("residual_simd", size), &size, |bench, _| { - bench.iter(|| black_box(simd_impl::residual_norm_simd(black_box(&a), black_box(&b)))) - }); + group.bench_with_input( + BenchmarkId::new("residual_simd", size), + &size, + |bench, _| { + bench + .iter(|| black_box(simd_impl::residual_norm_simd(black_box(&a), black_box(&b)))) + }, + ); } group.finish(); @@ -796,11 +774,7 @@ fn bench_fma_pattern(c: &mut Criterion) { // CRITERION CONFIGURATION // ============================================================================ -criterion_group!( - matmul_benches, - bench_dense_matmul, - bench_projection_matmul, -); +criterion_group!(matmul_benches, bench_dense_matmul, bench_projection_matmul,); criterion_group!( vector_ops_benches, @@ -809,10 +783,7 @@ criterion_group!( bench_residual_norm, ); -criterion_group!( - batch_benches, - bench_batch_residual, -); +criterion_group!(batch_benches, bench_batch_residual,); criterion_group!( optimization_benches, diff --git a/crates/prime-radiant/benches/sona_bench.rs b/crates/prime-radiant/benches/sona_bench.rs index 0669800db..2742e9d2f 100644 --- a/crates/prime-radiant/benches/sona_bench.rs +++ b/crates/prime-radiant/benches/sona_bench.rs @@ -392,9 +392,7 @@ fn bench_ewc_penalty(c: &mut Criterion) { for param_count in [1000, 10000, 100000] { let ewc = EwcPlusPlus::new(param_count, 0.4); - let weights: Vec = (0..param_count) - .map(|i| (i as f32 * 0.001).sin()) - .collect(); + let weights: Vec = (0..param_count).map(|i| (i as f32 * 0.001).sin()).collect(); group.bench_with_input( BenchmarkId::new("params", param_count), @@ -412,9 +410,7 @@ fn bench_ewc_consolidate(c: &mut Criterion) { for param_count in [1000, 10000, 100000] { let mut ewc = EwcPlusPlus::new(param_count, 0.4); - let weights: Vec = (0..param_count) - .map(|i| (i as f32 * 0.001).sin()) - .collect(); + let weights: Vec = (0..param_count).map(|i| (i as f32 * 0.001).sin()).collect(); let new_fisher: Vec = (0..param_count) .map(|i| (i as f32 * 0.002).cos().abs()) .collect(); @@ -422,9 +418,7 @@ fn bench_ewc_consolidate(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("params", param_count), ¶m_count, - |b, _| { - b.iter(|| ewc.consolidate(black_box(&weights), black_box(&new_fisher))) - }, + |b, _| b.iter(|| ewc.consolidate(black_box(&weights), black_box(&new_fisher))), ); } diff --git a/crates/prime-radiant/benches/tile_bench.rs b/crates/prime-radiant/benches/tile_bench.rs index 792f424db..a192e5dd6 100644 --- a/crates/prime-radiant/benches/tile_bench.rs +++ b/crates/prime-radiant/benches/tile_bench.rs @@ -271,7 +271,8 @@ impl TileState { for delta in self.delta_buffer.drain(..) { match delta.delta_type { DeltaType::EdgeAdd => { - self.graph.add_edge(delta.source, delta.target, delta.weight); + self.graph + .add_edge(delta.source, delta.target, delta.weight); } DeltaType::Observation => { // Update evidence accumulator @@ -385,9 +386,7 @@ fn bench_single_tile_tick(c: &mut Criterion) { // Empty tick let mut tile = TileState::new(0); - group.bench_function("empty", |b| { - b.iter(|| black_box(tile.tick(black_box(1)))) - }); + group.bench_function("empty", |b| b.iter(|| black_box(tile.tick(black_box(1))))); // Tick with small graph let mut tile = TileState::new(0); diff --git a/crates/prime-radiant/src/attention/adapter.rs b/crates/prime-radiant/src/attention/adapter.rs index c7588f53a..75c4c937f 100644 --- a/crates/prime-radiant/src/attention/adapter.rs +++ b/crates/prime-radiant/src/attention/adapter.rs @@ -43,7 +43,8 @@ impl AttentionAdapter { for i in 0..n { for j in 0..n { if i != j { - similarity_matrix[i][j] = self.cosine_similarity(node_states[i], node_states[j]); + similarity_matrix[i][j] = + self.cosine_similarity(node_states[i], node_states[j]); } } } @@ -238,7 +239,9 @@ mod tests { let key_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); let value_refs: Vec<&[f32]> = values.iter().map(|v| v.as_slice()).collect(); - let output = adapter.compute_attention(&query, &key_refs, &value_refs).unwrap(); + let output = adapter + .compute_attention(&query, &key_refs, &value_refs) + .unwrap(); assert_eq!(output.len(), 16); } diff --git a/crates/prime-radiant/src/attention/mod.rs b/crates/prime-radiant/src/attention/mod.rs index 5506875ae..a142bd944 100644 --- a/crates/prime-radiant/src/attention/mod.rs +++ b/crates/prime-radiant/src/attention/mod.rs @@ -241,8 +241,8 @@ impl AttentionCoherence { // Aggregate let total_energy: f32 = weighted.iter().map(|w| w.weighted_energy).sum(); - let avg_attention: f32 = weighted.iter().map(|w| w.attention_weight).sum::() - / weighted.len().max(1) as f32; + let avg_attention: f32 = + weighted.iter().map(|w| w.attention_weight).sum::() / weighted.len().max(1) as f32; Ok(AttentionEnergyAnalysis { weighted_residuals: weighted, @@ -331,9 +331,7 @@ mod tests { use super::*; fn make_states(n: usize, dim: usize) -> Vec> { - (0..n) - .map(|i| vec![0.1 * (i + 1) as f32; dim]) - .collect() + (0..n).map(|i| vec![0.1 * (i + 1) as f32; dim]).collect() } #[test] @@ -372,7 +370,9 @@ mod tests { (2, 3, vec![0.3f32; 8]), ]; - let weighted = coherence.weighted_residuals(&state_refs, &residuals).unwrap(); + let weighted = coherence + .weighted_residuals(&state_refs, &residuals) + .unwrap(); assert_eq!(weighted.len(), 3); for w in &weighted { diff --git a/crates/prime-radiant/src/attention/moe.rs b/crates/prime-radiant/src/attention/moe.rs index 3d3a9b8b7..3cf5952ae 100644 --- a/crates/prime-radiant/src/attention/moe.rs +++ b/crates/prime-radiant/src/attention/moe.rs @@ -347,7 +347,8 @@ mod tests { let inputs: Vec> = (0..10).map(|i| vec![0.1 * (i + 1) as f32; 8]).collect(); let context = vec![0.1f32; 8]; - let routings: Vec = inputs.iter().map(|inp| moe.route(inp, &context)).collect(); + let routings: Vec = + inputs.iter().map(|inp| moe.route(inp, &context)).collect(); let usage = moe.expert_usage(&routings); diff --git a/crates/prime-radiant/src/attention/topology.rs b/crates/prime-radiant/src/attention/topology.rs index 15e14c427..5137c0df8 100644 --- a/crates/prime-radiant/src/attention/topology.rs +++ b/crates/prime-radiant/src/attention/topology.rs @@ -2,8 +2,8 @@ //! //! Uses topological coherence as a permission signal for attention behavior. -use super::{AttentionCoherenceConfig, AttentionError, Result}; use super::config::AttentionMode; +use super::{AttentionCoherenceConfig, AttentionError, Result}; /// Score from attention computation #[derive(Debug, Clone)] @@ -171,7 +171,8 @@ impl TopologyGate { .map(|(j, &s)| (j, s)) .collect(); - neighbor_sims.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); + neighbor_sims + .sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); let neighbors: Vec = neighbor_sims.iter().take(k).map(|(j, _)| *j).collect(); // Boundary mass: edges to non-neighbors @@ -187,7 +188,12 @@ impl TopologyGate { let all_sims: Vec = similarities .iter() .enumerate() - .flat_map(|(i, row)| row.iter().enumerate().filter(move |(j, _)| *j > i).map(|(_, &s)| s)) + .flat_map(|(i, row)| { + row.iter() + .enumerate() + .filter(move |(j, _)| *j > i) + .map(|(_, &s)| s) + }) .collect(); let mean_sim: f32 = all_sims.iter().sum::() / all_sims.len().max(1) as f32; @@ -204,8 +210,9 @@ impl TopologyGate { // Combine metrics // High mean similarity and low variance = high coherence // High boundary mass = low coherence - let coherence_score = (mean_sim * 0.5 + (1.0 - variance.sqrt()) * 0.3 + (1.0 - boundary_ratio) * 0.2) - .clamp(0.0, 1.0); + let coherence_score = + (mean_sim * 0.5 + (1.0 - variance.sqrt()) * 0.3 + (1.0 - boundary_ratio) * 0.2) + .clamp(0.0, 1.0); CoherenceMetrics { coherence_score, diff --git a/crates/prime-radiant/src/coherence/energy.rs b/crates/prime-radiant/src/coherence/energy.rs index 3e5c66b60..3ea7974b2 100644 --- a/crates/prime-radiant/src/coherence/energy.rs +++ b/crates/prime-radiant/src/coherence/energy.rs @@ -131,7 +131,11 @@ impl ScopeEnergy { let (max_edge_energy, hotspot_edge) = edge_energies .iter() - .max_by(|a, b| a.energy.partial_cmp(&b.energy).unwrap_or(std::cmp::Ordering::Equal)) + .max_by(|a, b| { + a.energy + .partial_cmp(&b.energy) + .unwrap_or(std::cmp::Ordering::Equal) + }) .map(|e| (e.energy, Some(e.edge_id.clone()))) .unwrap_or((0.0, None)); @@ -289,7 +293,11 @@ impl CoherenceEnergy { /// Identify the top-k hotspots (highest energy edges) pub fn hotspots(&self, k: usize) -> Vec { let mut sorted: Vec<_> = self.edge_energies.values().collect(); - sorted.sort_by(|a, b| b.energy.partial_cmp(&a.energy).unwrap_or(std::cmp::Ordering::Equal)); + sorted.sort_by(|a, b| { + b.energy + .partial_cmp(&a.energy) + .unwrap_or(std::cmp::Ordering::Equal) + }); sorted .into_iter() @@ -347,8 +355,8 @@ impl CoherenceEnergy { let mean = self.average_edge_energy(); // Compute standard deviation - let variance: f32 = energies.iter().map(|e| (e - mean).powi(2)).sum::() - / energies.len() as f32; + let variance: f32 = + energies.iter().map(|e| (e - mean).powi(2)).sum::() / energies.len() as f32; let std_dev = variance.sqrt(); // Compute median @@ -385,10 +393,7 @@ impl CoherenceEnergy { .cloned() .unwrap_or_else(|| "default".to_string()); - scope_groups - .entry(scope_id) - .or_default() - .push(edge_energy); + scope_groups.entry(scope_id).or_default().push(edge_energy); } // Create scope energies @@ -538,13 +543,21 @@ pub fn compute_residual(projected_source: &[f32], projected_target: &[f32]) -> V /// Compute residual into pre-allocated buffer (zero allocation) #[inline] -pub fn compute_residual_into(projected_source: &[f32], projected_target: &[f32], result: &mut [f32]) { +pub fn compute_residual_into( + projected_source: &[f32], + projected_target: &[f32], + result: &mut [f32], +) { debug_assert_eq!( projected_source.len(), projected_target.len(), "Projected vectors must have same dimension" ); - debug_assert_eq!(result.len(), projected_source.len(), "Result buffer size mismatch"); + debug_assert_eq!( + result.len(), + projected_source.len(), + "Result buffer size mismatch" + ); // Unrolled loop for better vectorization let len = projected_source.len(); diff --git a/crates/prime-radiant/src/coherence/engine.rs b/crates/prime-radiant/src/coherence/engine.rs index 434a75e30..35b9c01f6 100644 --- a/crates/prime-radiant/src/coherence/engine.rs +++ b/crates/prime-radiant/src/coherence/engine.rs @@ -29,7 +29,9 @@ //! println!("Total energy: {}", energy.total_energy); //! ``` -use super::energy::{compute_norm_sq, compute_residual, CoherenceEnergy, EdgeEnergy, EdgeId, ScopeId}; +use super::energy::{ + compute_norm_sq, compute_residual, CoherenceEnergy, EdgeEnergy, EdgeId, ScopeId, +}; use chrono::{DateTime, Utc}; use dashmap::DashMap; use parking_lot::RwLock; @@ -695,8 +697,9 @@ impl CoherenceEngine { return Err(CoherenceError::EdgeExists(source, target)); } - let mut edge = - SheafEdge::with_restriction_maps(&edge_id, &source, &target, weight, rho_source, rho_target); + let mut edge = SheafEdge::with_restriction_maps( + &edge_id, &source, &target, weight, rho_source, rho_target, + ); if let Some(s) = scope.clone() { edge = edge.with_scope(s.clone()); self.edge_scopes.insert(edge_id.clone(), s); @@ -864,11 +867,9 @@ impl CoherenceEngine { for edge_ref in &edges { let edge = edge_ref.value(); - if let Some(energy) = self.compute_edge_energy_with_buffers( - edge, - &mut source_buf, - &mut target_buf, - ) { + if let Some(energy) = + self.compute_edge_energy_with_buffers(edge, &mut source_buf, &mut target_buf) + { result.insert(edge.id.clone(), energy); } } diff --git a/crates/prime-radiant/src/coherence/incremental.rs b/crates/prime-radiant/src/coherence/incremental.rs index b89e8ca48..0cfed70b3 100644 --- a/crates/prime-radiant/src/coherence/incremental.rs +++ b/crates/prime-radiant/src/coherence/incremental.rs @@ -172,11 +172,15 @@ impl UpdateEvent { /// Check if this event affects the given edge pub fn affects_edge(&self, edge_id: &str) -> bool { match self { - UpdateEvent::NodeUpdated { affected_edges, .. } => affected_edges.contains(&edge_id.to_string()), + UpdateEvent::NodeUpdated { affected_edges, .. } => { + affected_edges.contains(&edge_id.to_string()) + } UpdateEvent::EdgeAdded { edge_id: eid, .. } => eid == edge_id, UpdateEvent::EdgeRemoved { edge_id: eid, .. } => eid == edge_id, UpdateEvent::NodeAdded { .. } => false, - UpdateEvent::NodeRemoved { removed_edges, .. } => removed_edges.contains(&edge_id.to_string()), + UpdateEvent::NodeRemoved { removed_edges, .. } => { + removed_edges.contains(&edge_id.to_string()) + } } } } @@ -558,15 +562,13 @@ impl<'a> IncrementalEngine<'a> { // Update cache for (edge_id, edge_energy) in new_energies { - self.cache.update_edge( - edge_id, - edge_energy.energy, - edge_energy.residual, - ); + self.cache + .update_edge(edge_id, edge_energy.energy, edge_energy.residual); } // Update fingerprint - self.cache.set_fingerprint(self.engine.current_fingerprint()); + self.cache + .set_fingerprint(self.engine.current_fingerprint()); self.cache.total_energy() } diff --git a/crates/prime-radiant/src/coherence/mod.rs b/crates/prime-radiant/src/coherence/mod.rs index 4c61f169e..b9d7048c2 100644 --- a/crates/prime-radiant/src/coherence/mod.rs +++ b/crates/prime-radiant/src/coherence/mod.rs @@ -63,16 +63,15 @@ pub use energy::{ EnergyStatistics, HotspotInfo, ScopeEnergy, ScopeId, }; pub use engine::{ - CoherenceConfig, CoherenceEngine, CoherenceError, NodeState, RestrictionMap, Result, - SheafEdge, SheafNode, + CoherenceConfig, CoherenceEngine, CoherenceError, NodeState, RestrictionMap, Result, SheafEdge, + SheafNode, }; pub use history::{EnergyHistory, EnergyHistoryConfig, EnergyTrend, TrendDirection}; pub use incremental::{ DeltaResult, IncrementalCache, IncrementalConfig, IncrementalEngine, UpdateEvent, }; pub use spectral::{ - compute_eigenvalues, DriftEvent, DriftSeverity, SpectralAnalyzer, SpectralConfig, - SpectralStats, + compute_eigenvalues, DriftEvent, DriftSeverity, SpectralAnalyzer, SpectralConfig, SpectralStats, }; // Alias for compatibility diff --git a/crates/prime-radiant/src/coherence/spectral.rs b/crates/prime-radiant/src/coherence/spectral.rs index 6d1d5df0d..6866aea12 100644 --- a/crates/prime-radiant/src/coherence/spectral.rs +++ b/crates/prime-radiant/src/coherence/spectral.rs @@ -59,8 +59,8 @@ impl Default for SpectralConfig { Self { num_eigenvalues: 10, history_size: 100, - drift_threshold: 0.1, // 10% relative change - severe_threshold: 0.25, // 25% relative change + drift_threshold: 0.1, // 10% relative change + severe_threshold: 0.25, // 25% relative change min_samples: 3, smoothing_alpha: 0.3, } @@ -301,7 +301,8 @@ impl SpectralAnalyzer { // Compute average pairwise distance let mut total_distance = 0.0; for i in 0..recent.len() - 1 { - total_distance += self.spectral_distance(&recent[i].eigenvalues, &recent[i + 1].eigenvalues); + total_distance += + self.spectral_distance(&recent[i].eigenvalues, &recent[i + 1].eigenvalues); } Some(total_distance / window as f32) @@ -391,7 +392,9 @@ impl SpectralAnalyzer { fn classify_severity(&self, distance: f32, connectivity_change: f32) -> DriftSeverity { let is_connectivity_loss = connectivity_change < -self.config.drift_threshold; - if distance > self.config.severe_threshold * 2.0 || (is_connectivity_loss && distance > self.config.severe_threshold) { + if distance > self.config.severe_threshold * 2.0 + || (is_connectivity_loss && distance > self.config.severe_threshold) + { DriftSeverity::Critical } else if distance > self.config.severe_threshold { DriftSeverity::Severe @@ -595,11 +598,7 @@ pub fn compute_eigenvalues(laplacian: &[Vec], k: usize) -> Vec { // Compute eigenvalues let eigen = SymmetricEigen::new(matrix); - let mut eigenvalues: Vec = eigen - .eigenvalues - .iter() - .map(|&x| x as f32) - .collect(); + let mut eigenvalues: Vec = eigen.eigenvalues.iter().map(|&x| x as f32).collect(); // Sort and take top k eigenvalues.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)); diff --git a/crates/prime-radiant/src/cohomology/cocycle.rs b/crates/prime-radiant/src/cohomology/cocycle.rs index 4b1a76387..9d533c131 100644 --- a/crates/prime-radiant/src/cohomology/cocycle.rs +++ b/crates/prime-radiant/src/cohomology/cocycle.rs @@ -3,8 +3,8 @@ //! Cocycles are the building blocks of cohomology. A cocycle is a cochain //! that is in the kernel of the coboundary operator. -use super::simplex::{Cochain, SimplexId, SimplicialComplex}; use super::sheaf::{Sheaf, SheafSection}; +use super::simplex::{Cochain, SimplexId, SimplicialComplex}; use crate::substrate::NodeId; use ndarray::Array1; use serde::{Deserialize, Serialize}; diff --git a/crates/prime-radiant/src/cohomology/cohomology_group.rs b/crates/prime-radiant/src/cohomology/cohomology_group.rs index 542ba6244..d1f574f69 100644 --- a/crates/prime-radiant/src/cohomology/cohomology_group.rs +++ b/crates/prime-radiant/src/cohomology/cohomology_group.rs @@ -266,9 +266,7 @@ impl CohomologyComputer { let (rref, pivot_cols) = self.row_reduce(matrix); let n_cols = matrix.ncols(); - let free_vars: Vec = (0..n_cols) - .filter(|c| !pivot_cols.contains(c)) - .collect(); + let free_vars: Vec = (0..n_cols).filter(|c| !pivot_cols.contains(c)).collect(); for &free_var in &free_vars { let mut kernel_vec = Array1::zeros(n_cols); @@ -411,10 +409,8 @@ impl CohomologyComputer { } // Build index to simplex ID map - let idx_to_simplex: HashMap = simplex_to_idx - .iter() - .map(|(&id, &idx)| (idx, id)) - .collect(); + let idx_to_simplex: HashMap = + simplex_to_idx.iter().map(|(&id, &idx)| (idx, id)).collect(); // If no image, all kernel elements are generators if image.is_empty() { @@ -486,9 +482,7 @@ impl CohomologyComputer { /// Compute all cohomology groups up to max_dimension pub fn compute_all(&mut self) -> Vec { let max_dim = self.config.max_dimension.min(self.complex.max_dimension); - (0..=max_dim) - .map(|n| self.compute_cohomology(n)) - .collect() + (0..=max_dim).map(|n| self.compute_cohomology(n)).collect() } /// Compute Betti numbers diff --git a/crates/prime-radiant/src/cohomology/diffusion.rs b/crates/prime-radiant/src/cohomology/diffusion.rs index b0f849150..e60d167f2 100644 --- a/crates/prime-radiant/src/cohomology/diffusion.rs +++ b/crates/prime-radiant/src/cohomology/diffusion.rs @@ -7,8 +7,8 @@ use super::laplacian::{LaplacianConfig, SheafLaplacian}; use super::obstruction::{ObstructionDetector, ObstructionSeverity}; use super::sheaf::SheafSection; -use crate::substrate::SheafGraph; use crate::substrate::NodeId; +use crate::substrate::SheafGraph; use ndarray::Array1; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -109,9 +109,7 @@ impl DiffusionResult { /// Check if obstruction was detected pub fn has_obstruction(&self) -> bool { - self.residual_obstruction - .map(|e| e > 0.01) - .unwrap_or(false) + self.residual_obstruction.map(|e| e > 0.01).unwrap_or(false) } /// Get persistent obstructions @@ -188,8 +186,10 @@ impl SheafDiffusion { // Check if obstruction is persistent if step > 20 { - let recent_energies = &energy_history[energy_history.len().saturating_sub(10)..]; - let avg_recent: f64 = recent_energies.iter().sum::() / recent_energies.len() as f64; + let recent_energies = + &energy_history[energy_history.len().saturating_sub(10)..]; + let avg_recent: f64 = + recent_energies.iter().sum::() / recent_energies.len() as f64; indicator.is_persistent = (new_energy - avg_recent).abs() < 0.01 * avg_recent; } @@ -328,9 +328,7 @@ impl SheafDiffusion { for node_id in graph.node_ids() { if let Some(node) = graph.get_node(node_id) { - let values: Vec = node.state.as_slice().iter() - .map(|&x| x as f64) - .collect(); + let values: Vec = node.state.as_slice().iter().map(|&x| x as f64).collect(); section.set(node_id, Array1::from_vec(values)); } } @@ -353,7 +351,11 @@ impl SheafDiffusion { } /// Compute per-node energies - fn compute_node_energies(&self, graph: &SheafGraph, section: &SheafSection) -> HashMap { + fn compute_node_energies( + &self, + graph: &SheafGraph, + section: &SheafSection, + ) -> HashMap { let mut node_energies: HashMap = HashMap::new(); for node_id in graph.node_ids() { @@ -367,10 +369,9 @@ impl SheafDiffusion { edge.source }; - if let (Some(this_val), Some(other_val)) = ( - section.get(node_id), - section.get(other), - ) { + if let (Some(this_val), Some(other_val)) = + (section.get(node_id), section.get(other)) + { let residual = this_val - other_val; let residual_norm: f64 = residual.iter().map(|x| x * x).sum(); energy += (edge.weight as f64) * residual_norm; @@ -462,12 +463,8 @@ mod tests { fn test_adaptive_diffusion() { let graph = SheafGraph::new(); - let node1 = SheafNodeBuilder::new() - .state_from_slice(&[5.0]) - .build(); - let node2 = SheafNodeBuilder::new() - .state_from_slice(&[-5.0]) - .build(); + let node1 = SheafNodeBuilder::new().state_from_slice(&[5.0]).build(); + let node2 = SheafNodeBuilder::new().state_from_slice(&[-5.0]).build(); let id1 = graph.add_node(node1); let id2 = graph.add_node(node2); diff --git a/crates/prime-radiant/src/cohomology/laplacian.rs b/crates/prime-radiant/src/cohomology/laplacian.rs index b9d7748b7..bc82a78d0 100644 --- a/crates/prime-radiant/src/cohomology/laplacian.rs +++ b/crates/prime-radiant/src/cohomology/laplacian.rs @@ -9,8 +9,8 @@ //! - Small eigenvalues indicate near-obstructions use super::sheaf::{Sheaf, SheafSection}; -use crate::substrate::SheafGraph; use crate::substrate::NodeId; +use crate::substrate::SheafGraph; use ndarray::{Array1, Array2}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -211,25 +211,25 @@ impl SheafLaplacian { let source_offset = self.vertex_to_offset.get(&source).copied().unwrap_or(0); let target_offset = self.vertex_to_offset.get(&target).copied().unwrap_or(0); - if let Some(edge) = graph - .edge_ids() - .into_iter() - .find_map(|eid| { - let e = graph.get_edge(eid)?; - if e.source == source && e.target == target { - Some(e) - } else if e.source == target && e.target == source { - Some(e) - } else { - None - } - }) - { - let source_dim = self.vertices.iter() + if let Some(edge) = graph.edge_ids().into_iter().find_map(|eid| { + let e = graph.get_edge(eid)?; + if e.source == source && e.target == target { + Some(e) + } else if e.source == target && e.target == source { + Some(e) + } else { + None + } + }) { + let source_dim = self + .vertices + .iter() .find(|(v, _)| *v == source) .map(|(_, d)| *d) .unwrap_or(0); - let target_dim = self.vertices.iter() + let target_dim = self + .vertices + .iter() .find(|(v, _)| *v == target) .map(|(_, d)| *d) .unwrap_or(0); @@ -417,9 +417,7 @@ impl SheafLaplacian { for (vertex, dim) in &self.vertices { let offset = self.vertex_to_offset.get(vertex).copied().unwrap_or(0); - let values = Array1::from_iter( - (0..*dim).map(|j| evec[offset + j]) - ); + let values = Array1::from_iter((0..*dim).map(|j| evec[offset + j])); cochain.insert(*vertex, values); } @@ -482,12 +480,8 @@ mod tests { fn test_laplacian_energy() { let graph = SheafGraph::new(); - let node1 = SheafNodeBuilder::new() - .state_from_slice(&[1.0]) - .build(); - let node2 = SheafNodeBuilder::new() - .state_from_slice(&[2.0]) - .build(); + let node1 = SheafNodeBuilder::new().state_from_slice(&[1.0]).build(); + let node2 = SheafNodeBuilder::new().state_from_slice(&[2.0]).build(); let id1 = graph.add_node(node1); let id2 = graph.add_node(node2); @@ -515,15 +509,9 @@ mod tests { fn test_connected_graph_has_one_zero_eigenvalue() { let graph = SheafGraph::new(); - let node1 = SheafNodeBuilder::new() - .state_from_slice(&[1.0]) - .build(); - let node2 = SheafNodeBuilder::new() - .state_from_slice(&[1.0]) - .build(); - let node3 = SheafNodeBuilder::new() - .state_from_slice(&[1.0]) - .build(); + let node1 = SheafNodeBuilder::new().state_from_slice(&[1.0]).build(); + let node2 = SheafNodeBuilder::new().state_from_slice(&[1.0]).build(); + let node3 = SheafNodeBuilder::new().state_from_slice(&[1.0]).build(); let id1 = graph.add_node(node1); let id2 = graph.add_node(node2); diff --git a/crates/prime-radiant/src/cohomology/mod.rs b/crates/prime-radiant/src/cohomology/mod.rs index 3a93abc3e..12062f56f 100644 --- a/crates/prime-radiant/src/cohomology/mod.rs +++ b/crates/prime-radiant/src/cohomology/mod.rs @@ -47,22 +47,14 @@ mod obstruction; mod sheaf; mod simplex; -pub use cocycle::{Cocycle, CocycleBuilder, Coboundary}; -pub use cohomology_group::{ - CohomologyGroup, CohomologyComputer, CohomologyConfig, BettiNumbers, -}; -pub use diffusion::{ - SheafDiffusion, SheafDiffusionConfig, DiffusionResult, ObstructionIndicator, -}; -pub use laplacian::{ - SheafLaplacian, LaplacianConfig, LaplacianSpectrum, HarmonicRepresentative, -}; +pub use cocycle::{Coboundary, Cocycle, CocycleBuilder}; +pub use cohomology_group::{BettiNumbers, CohomologyComputer, CohomologyConfig, CohomologyGroup}; +pub use diffusion::{DiffusionResult, ObstructionIndicator, SheafDiffusion, SheafDiffusionConfig}; +pub use laplacian::{HarmonicRepresentative, LaplacianConfig, LaplacianSpectrum, SheafLaplacian}; pub use neural::{ - SheafNeuralLayer, SheafNeuralConfig, SheafConvolution, CohomologyPooling, - Activation, PoolingMethod, + Activation, CohomologyPooling, PoolingMethod, SheafConvolution, SheafNeuralConfig, + SheafNeuralLayer, }; -pub use obstruction::{ - ObstructionDetector, Obstruction, ObstructionSeverity, ObstructionReport, -}; -pub use sheaf::{Sheaf, SheafBuilder, Stalk, SheafSection, LocalSection}; -pub use simplex::{Simplex, SimplexId, SimplicialComplex, Chain, Cochain}; +pub use obstruction::{Obstruction, ObstructionDetector, ObstructionReport, ObstructionSeverity}; +pub use sheaf::{LocalSection, Sheaf, SheafBuilder, SheafSection, Stalk}; +pub use simplex::{Chain, Cochain, Simplex, SimplexId, SimplicialComplex}; diff --git a/crates/prime-radiant/src/cohomology/neural.rs b/crates/prime-radiant/src/cohomology/neural.rs index 128b65c06..d9a57741f 100644 --- a/crates/prime-radiant/src/cohomology/neural.rs +++ b/crates/prime-radiant/src/cohomology/neural.rs @@ -5,8 +5,8 @@ use super::laplacian::{LaplacianConfig, SheafLaplacian}; use super::sheaf::{Sheaf, SheafSection}; -use crate::substrate::SheafGraph; use crate::substrate::NodeId; +use crate::substrate::SheafGraph; use ndarray::{Array1, Array2}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -36,7 +36,13 @@ impl Activation { match self { Activation::Identity => x, Activation::ReLU => x.max(0.0), - Activation::LeakyReLU(alpha) => if x > 0.0 { x } else { alpha * x }, + Activation::LeakyReLU(alpha) => { + if x > 0.0 { + x + } else { + alpha * x + } + } Activation::Sigmoid => 1.0 / (1.0 + (-x).exp()), Activation::Tanh => x.tanh(), Activation::GELU => { @@ -65,8 +71,20 @@ impl Activation { pub fn derivative(&self, x: f64) -> f64 { match self { Activation::Identity => 1.0, - Activation::ReLU => if x > 0.0 { 1.0 } else { 0.0 }, - Activation::LeakyReLU(alpha) => if x > 0.0 { 1.0 } else { *alpha }, + Activation::ReLU => { + if x > 0.0 { + 1.0 + } else { + 0.0 + } + } + Activation::LeakyReLU(alpha) => { + if x > 0.0 { + 1.0 + } else { + *alpha + } + } Activation::Sigmoid => { let s = self.apply(x); s * (1.0 - s) @@ -143,10 +161,9 @@ impl SheafNeuralLayer { let scale = (2.0 / (config.input_dim + config.output_dim) as f64).sqrt(); // Initialize weights with Xavier - let weights = Array2::from_shape_fn( - (config.output_dim, config.input_dim), - |_| rand::random::() * scale - scale / 2.0, - ); + let weights = Array2::from_shape_fn((config.output_dim, config.input_dim), |_| { + rand::random::() * scale - scale / 2.0 + }); let bias = Array1::zeros(config.output_dim); @@ -159,7 +176,11 @@ impl SheafNeuralLayer { } /// Create with specific weights - pub fn with_weights(config: SheafNeuralConfig, weights: Array2, bias: Array1) -> Self { + pub fn with_weights( + config: SheafNeuralConfig, + weights: Array2, + bias: Array1, + ) -> Self { assert_eq!(weights.nrows(), config.output_dim); assert_eq!(weights.ncols(), config.input_dim); assert_eq!(bias.len(), config.output_dim); @@ -293,14 +314,12 @@ impl SheafConvolution { pub fn new(input_dim: usize, output_dim: usize) -> Self { let scale = (2.0 / (input_dim + output_dim) as f64).sqrt(); - let self_weight = Array2::from_shape_fn( - (output_dim, input_dim), - |_| rand::random::() * scale - scale / 2.0, - ); - let neighbor_weight = Array2::from_shape_fn( - (output_dim, input_dim), - |_| rand::random::() * scale - scale / 2.0, - ); + let self_weight = Array2::from_shape_fn((output_dim, input_dim), |_| { + rand::random::() * scale - scale / 2.0 + }); + let neighbor_weight = Array2::from_shape_fn((output_dim, input_dim), |_| { + rand::random::() * scale - scale / 2.0 + }); let bias = Array1::zeros(output_dim); Self { @@ -428,7 +447,12 @@ impl CohomologyPooling { return Array1::zeros(0); } - let dim = section.sections.values().next().map(|v| v.len()).unwrap_or(0); + let dim = section + .sections + .values() + .next() + .map(|v| v.len()) + .unwrap_or(0); match self.method { PoolingMethod::Mean => { @@ -477,10 +501,13 @@ impl CohomologyPooling { } PoolingMethod::TopK(k) => { // Select top k nodes by L2 norm - let mut node_norms: Vec<_> = section.sections.iter() + let mut node_norms: Vec<_> = section + .sections + .iter() .map(|(id, vec)| (*id, vec.iter().map(|x| x * x).sum::())) .collect(); - node_norms.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); + node_norms + .sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); let mut sum = Array1::zeros(dim); for (node_id, _) in node_norms.into_iter().take(k) { @@ -601,12 +628,8 @@ mod tests { fn test_pooling() { let graph = SheafGraph::new(); - let node1 = SheafNodeBuilder::new() - .state_from_slice(&[1.0]) - .build(); - let node2 = SheafNodeBuilder::new() - .state_from_slice(&[3.0]) - .build(); + let node1 = SheafNodeBuilder::new().state_from_slice(&[1.0]).build(); + let node2 = SheafNodeBuilder::new().state_from_slice(&[3.0]).build(); let id1 = graph.add_node(node1); let id2 = graph.add_node(node2); diff --git a/crates/prime-radiant/src/cohomology/obstruction.rs b/crates/prime-radiant/src/cohomology/obstruction.rs index 5d125519a..1df64a607 100644 --- a/crates/prime-radiant/src/cohomology/obstruction.rs +++ b/crates/prime-radiant/src/cohomology/obstruction.rs @@ -7,8 +7,8 @@ use super::cocycle::{Cocycle, SheafCoboundary}; use super::laplacian::{HarmonicRepresentative, LaplacianConfig, SheafLaplacian}; use super::sheaf::{Sheaf, SheafSection}; -use crate::substrate::SheafGraph; use crate::substrate::NodeId; +use crate::substrate::SheafGraph; use ndarray::Array1; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -80,12 +80,7 @@ pub struct Obstruction { impl Obstruction { /// Create a new obstruction - pub fn new( - id: u64, - degree: usize, - energy: f64, - severity: ObstructionSeverity, - ) -> Self { + pub fn new(id: u64, degree: usize, energy: f64, severity: ObstructionSeverity) -> Self { Self { id, degree, @@ -101,7 +96,8 @@ impl Obstruction { /// Add edge contribution pub fn add_edge_contribution(&mut self, source: NodeId, target: NodeId, contribution: f64) { - self.edge_contributions.insert((source, target), contribution); + self.edge_contributions + .insert((source, target), contribution); } /// Set hotspots @@ -124,7 +120,9 @@ impl Obstruction { /// Get top k contributing edges pub fn top_edges(&self, k: usize) -> Vec<((NodeId, NodeId), f64)> { - let mut edges: Vec<_> = self.edge_contributions.iter() + let mut edges: Vec<_> = self + .edge_contributions + .iter() .map(|(&e, &c)| (e, c)) .collect(); edges.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); @@ -263,10 +261,9 @@ impl ObstructionDetector { let mut edge_energies: HashMap<(NodeId, NodeId), f64> = HashMap::new(); for edge_id in graph.edge_ids() { if let Some(edge) = graph.get_edge(edge_id) { - if let (Some(source_node), Some(target_node)) = ( - graph.get_node(edge.source), - graph.get_node(edge.target), - ) { + if let (Some(source_node), Some(target_node)) = + (graph.get_node(edge.source), graph.get_node(edge.target)) + { let residual = edge.weighted_residual_energy( source_node.state.as_slice(), target_node.state.as_slice(), @@ -325,9 +322,7 @@ impl ObstructionDetector { for node_id in graph.node_ids() { if let Some(node) = graph.get_node(node_id) { - let values: Vec = node.state.as_slice().iter() - .map(|&x| x as f64) - .collect(); + let values: Vec = node.state.as_slice().iter().map(|&x| x as f64).collect(); section.set(node_id, Array1::from_vec(values)); } } @@ -372,7 +367,11 @@ impl ObstructionDetector { ObstructionSeverity::Moderate => { recommendations.push(format!( "Moderate obstruction. Focus on hotspot nodes: {:?}", - obs.hotspots.iter().take(3).map(|(n, _)| n).collect::>() + obs.hotspots + .iter() + .take(3) + .map(|(n, _)| n) + .collect::>() )); } ObstructionSeverity::Severe | ObstructionSeverity::Critical => { @@ -380,9 +379,8 @@ impl ObstructionDetector { "Severe obstruction with energy {:.4}. Immediate review required.", obs.energy )); - recommendations.push( - "Consider isolating incoherent region using MinCut".to_string() - ); + recommendations + .push("Consider isolating incoherent region using MinCut".to_string()); } _ => {} } @@ -390,7 +388,7 @@ impl ObstructionDetector { if report.spectral_gap.is_some_and(|g| g < 0.1) { recommendations.push( - "Small spectral gap indicates near-obstruction. Monitor for drift.".to_string() + "Small spectral gap indicates near-obstruction. Monitor for drift.".to_string(), ); } @@ -495,15 +493,9 @@ mod tests { fn test_obstruction_hotspots() { let graph = SheafGraph::new(); - let node1 = SheafNodeBuilder::new() - .state_from_slice(&[1.0]) - .build(); - let node2 = SheafNodeBuilder::new() - .state_from_slice(&[5.0]) - .build(); - let node3 = SheafNodeBuilder::new() - .state_from_slice(&[1.5]) - .build(); + let node1 = SheafNodeBuilder::new().state_from_slice(&[1.0]).build(); + let node2 = SheafNodeBuilder::new().state_from_slice(&[5.0]).build(); + let node3 = SheafNodeBuilder::new().state_from_slice(&[1.5]).build(); let id1 = graph.add_node(node1); let id2 = graph.add_node(node2); diff --git a/crates/prime-radiant/src/cohomology/sheaf.rs b/crates/prime-radiant/src/cohomology/sheaf.rs index 749127a3f..1e761b4dd 100644 --- a/crates/prime-radiant/src/cohomology/sheaf.rs +++ b/crates/prime-radiant/src/cohomology/sheaf.rs @@ -6,8 +6,8 @@ //! //! This is the foundational structure for cohomology computation. -use crate::substrate::{RestrictionMap, SheafGraph}; use crate::substrate::NodeId; +use crate::substrate::{RestrictionMap, SheafGraph}; use ndarray::{Array1, Array2}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -222,7 +222,12 @@ impl Sheaf { } /// Apply restriction map from source to target - pub fn restrict(&self, source: NodeId, target: NodeId, value: &Array1) -> Option> { + pub fn restrict( + &self, + source: NodeId, + target: NodeId, + value: &Array1, + ) -> Option> { self.restriction_maps .get(&(source, target)) .map(|rho| rho(value)) @@ -338,9 +343,8 @@ impl SheafBuilder { target: NodeId, indices: Vec, ) -> Self { - let proj_fn: RestrictionFn = Arc::new(move |v: &Array1| { - Array1::from_iter(indices.iter().map(|&i| v[i])) - }); + let proj_fn: RestrictionFn = + Arc::new(move |v: &Array1| Array1::from_iter(indices.iter().map(|&i| v[i]))); self.sheaf.add_restriction(source, target, proj_fn); self } diff --git a/crates/prime-radiant/src/cohomology/simplex.rs b/crates/prime-radiant/src/cohomology/simplex.rs index 70d1ecaa2..5e5b0ec10 100644 --- a/crates/prime-radiant/src/cohomology/simplex.rs +++ b/crates/prime-radiant/src/cohomology/simplex.rs @@ -379,7 +379,12 @@ impl Chain { if coefficient.abs() > 1e-10 { *self.coefficients.entry(id).or_insert(0.0) += coefficient; // Remove if coefficient is now essentially zero - if self.coefficients.get(&id).map(|c| c.abs() < 1e-10).unwrap_or(false) { + if self + .coefficients + .get(&id) + .map(|c| c.abs() < 1e-10) + .unwrap_or(false) + { self.coefficients.remove(&id); } } @@ -394,7 +399,10 @@ impl Chain { /// Add another chain to this one pub fn add(&mut self, other: &Chain) { - assert_eq!(self.dimension, other.dimension, "Chain dimensions must match"); + assert_eq!( + self.dimension, other.dimension, + "Chain dimensions must match" + ); for (&id, &coeff) in &other.coefficients { self.add_simplex(id, coeff); } @@ -407,7 +415,11 @@ impl Chain { /// L2 norm of the chain pub fn norm(&self) -> f64 { - self.coefficients.values().map(|c| c * c).sum::().sqrt() + self.coefficients + .values() + .map(|c| c * c) + .sum::() + .sqrt() } } @@ -462,7 +474,10 @@ impl Cochain { /// Add another cochain to this one pub fn add(&mut self, other: &Cochain) { - assert_eq!(self.dimension, other.dimension, "Cochain dimensions must match"); + assert_eq!( + self.dimension, other.dimension, + "Cochain dimensions must match" + ); for (&id, &value) in &other.values { let new_val = self.get(id) + value; self.set(id, new_val); diff --git a/crates/prime-radiant/src/distributed/adapter.rs b/crates/prime-radiant/src/distributed/adapter.rs index 8309a66d0..afbff2aef 100644 --- a/crates/prime-radiant/src/distributed/adapter.rs +++ b/crates/prime-radiant/src/distributed/adapter.rs @@ -2,36 +2,22 @@ //! //! Wraps Raft consensus for coherence state replication. -use super::{DistributedCoherenceConfig, DistributedError, Result}; use super::config::NodeRole; +use super::{DistributedCoherenceConfig, DistributedError, Result}; /// Command types for coherence state machine #[derive(Debug, Clone)] pub enum CoherenceCommand { /// Update energy for an edge - UpdateEnergy { - edge_id: (u64, u64), - energy: f32, - }, + UpdateEnergy { edge_id: (u64, u64), energy: f32 }, /// Set node state vector - SetNodeState { - node_id: u64, - state: Vec, - }, + SetNodeState { node_id: u64, state: Vec }, /// Record coherence checkpoint - Checkpoint { - total_energy: f32, - timestamp: u64, - }, + Checkpoint { total_energy: f32, timestamp: u64 }, /// Mark region as incoherent - MarkIncoherent { - region_id: u64, - nodes: Vec, - }, + MarkIncoherent { region_id: u64, nodes: Vec }, /// Clear incoherence flag - ClearIncoherent { - region_id: u64, - }, + ClearIncoherent { region_id: u64 }, } impl CoherenceCommand { @@ -54,7 +40,10 @@ impl CoherenceCommand { bytes.extend(v.to_le_bytes()); } } - Self::Checkpoint { total_energy, timestamp } => { + Self::Checkpoint { + total_energy, + timestamp, + } => { bytes.push(2); bytes.extend(total_energy.to_le_bytes()); bytes.extend(timestamp.to_le_bytes()); @@ -111,7 +100,10 @@ impl CoherenceCommand { 2 if data.len() >= 12 => { let total_energy = f32::from_le_bytes(data[0..4].try_into().ok()?); let timestamp = u64::from_le_bytes(data[4..12].try_into().ok()?); - Some(Self::Checkpoint { total_energy, timestamp }) + Some(Self::Checkpoint { + total_energy, + timestamp, + }) } 3 if data.len() >= 12 => { let region_id = u64::from_le_bytes(data[0..8].try_into().ok()?); @@ -169,9 +161,17 @@ impl RaftAdapter { pub fn new(config: DistributedCoherenceConfig) -> Self { let is_leader = config.is_single_node(); Self { - role: if is_leader { NodeRole::Leader } else { NodeRole::Follower }, + role: if is_leader { + NodeRole::Leader + } else { + NodeRole::Follower + }, current_term: 1, - current_leader: if is_leader { Some(config.node_id.clone()) } else { None }, + current_leader: if is_leader { + Some(config.node_id.clone()) + } else { + None + }, log_index: 0, pending_commands: Vec::new(), config, @@ -231,7 +231,10 @@ impl RaftAdapter { /// Record checkpoint pub fn checkpoint(&mut self, total_energy: f32, timestamp: u64) -> Result { - let command = CoherenceCommand::Checkpoint { total_energy, timestamp }; + let command = CoherenceCommand::Checkpoint { + total_energy, + timestamp, + }; self.submit_command(command) } @@ -358,7 +361,11 @@ mod tests { fn test_not_leader_error() { let config = DistributedCoherenceConfig { node_id: "node1".to_string(), - cluster_members: vec!["node1".to_string(), "node2".to_string(), "node3".to_string()], + cluster_members: vec![ + "node1".to_string(), + "node2".to_string(), + "node3".to_string(), + ], ..Default::default() }; let mut adapter = RaftAdapter::new(config); diff --git a/crates/prime-radiant/src/distributed/config.rs b/crates/prime-radiant/src/distributed/config.rs index 7798c8a3c..2399ce783 100644 --- a/crates/prime-radiant/src/distributed/config.rs +++ b/crates/prime-radiant/src/distributed/config.rs @@ -80,7 +80,10 @@ impl DistributedCoherenceConfig { /// Create configuration for a 3-node cluster pub fn three_node_cluster(node_id: &str, members: Vec) -> Self { - assert!(members.len() >= 3, "Need at least 3 members for 3-node cluster"); + assert!( + members.len() >= 3, + "Need at least 3 members for 3-node cluster" + ); Self { node_id: node_id.to_string(), cluster_members: members, @@ -91,7 +94,10 @@ impl DistributedCoherenceConfig { /// Create configuration for a 5-node cluster pub fn five_node_cluster(node_id: &str, members: Vec) -> Self { - assert!(members.len() >= 5, "Need at least 5 members for 5-node cluster"); + assert!( + members.len() >= 5, + "Need at least 5 members for 5-node cluster" + ); Self { node_id: node_id.to_string(), cluster_members: members, @@ -141,7 +147,9 @@ impl DistributedCoherenceConfig { /// Get number of tolerable failures pub fn max_failures(&self) -> usize { - self.cluster_members.len().saturating_sub(self.quorum_size()) + self.cluster_members + .len() + .saturating_sub(self.quorum_size()) } } diff --git a/crates/prime-radiant/src/distributed/mod.rs b/crates/prime-radiant/src/distributed/mod.rs index 70157c85b..3d457831a 100644 --- a/crates/prime-radiant/src/distributed/mod.rs +++ b/crates/prime-radiant/src/distributed/mod.rs @@ -134,7 +134,12 @@ impl DistributedCoherence { /// Update energy for an edge /// /// This operation goes through Raft consensus and is replicated to all nodes. - pub fn update_energy(&mut self, source: u64, target: u64, energy: f32) -> Result { + pub fn update_energy( + &mut self, + source: u64, + target: u64, + energy: f32, + ) -> Result { let result = self.raft.update_energy((source, target), energy)?; // Apply to local state machine diff --git a/crates/prime-radiant/src/distributed/state.rs b/crates/prime-radiant/src/distributed/state.rs index 83d0e36c0..c51730169 100644 --- a/crates/prime-radiant/src/distributed/state.rs +++ b/crates/prime-radiant/src/distributed/state.rs @@ -174,11 +174,14 @@ impl CoherenceStateMachine { fn apply_set_node_state(&mut self, node_id: u64, state: Vec) -> ApplyResult { let truncated_state: Vec = state.into_iter().take(self.dimension).collect(); - let node = self.node_states.entry(node_id).or_insert_with(|| NodeState { - node_id, - state: vec![0.0; self.dimension], - last_update: 0, - }); + let node = self + .node_states + .entry(node_id) + .or_insert_with(|| NodeState { + node_id, + state: vec![0.0; self.dimension], + last_update: 0, + }); node.state = truncated_state; node.last_update = self.applied_index; @@ -192,7 +195,11 @@ impl CoherenceStateMachine { total_energy, timestamp, num_edges: self.edge_energies.len(), - num_incoherent: self.incoherent_regions.values().filter(|r| r.active).count(), + num_incoherent: self + .incoherent_regions + .values() + .filter(|r| r.active) + .count(), }; self.checkpoints.push(checkpoint.clone()); @@ -252,7 +259,10 @@ impl CoherenceStateMachine { /// Get number of incoherent regions pub fn num_incoherent_regions(&self) -> usize { - self.incoherent_regions.values().filter(|r| r.active).count() + self.incoherent_regions + .values() + .filter(|r| r.active) + .count() } /// Get all incoherent node IDs @@ -456,7 +466,10 @@ mod tests { edge.update(1.4); let trend = edge.trend(); - assert!(trend > 0.0, "Trend should be positive for increasing energy"); + assert!( + trend > 0.0, + "Trend should be positive for increasing energy" + ); } #[test] diff --git a/crates/prime-radiant/src/execution/action.rs b/crates/prime-radiant/src/execution/action.rs index b4c95a2cf..b3662ca1a 100644 --- a/crates/prime-radiant/src/execution/action.rs +++ b/crates/prime-radiant/src/execution/action.rs @@ -161,7 +161,12 @@ pub struct ActionImpact { impl ActionImpact { /// Create a new impact assessment. - pub const fn new(cost: f32, reversibility: f32, blast_radius: f32, latency_sensitivity: f32) -> Self { + pub const fn new( + cost: f32, + reversibility: f32, + blast_radius: f32, + latency_sensitivity: f32, + ) -> Self { Self { cost, reversibility, @@ -209,11 +214,7 @@ impl ActionImpact { self.latency_sensitivity, ]; - scores - .iter() - .zip(weights.iter()) - .map(|(s, w)| s * w) - .sum() + scores.iter().zip(weights.iter()).map(|(s, w)| s * w).sum() } /// Whether this action should be considered high-risk. @@ -260,7 +261,11 @@ pub struct ActionMetadata { impl ActionMetadata { /// Create new metadata with required fields. - pub fn new(action_type: impl Into, description: impl Into, actor_id: impl Into) -> Self { + pub fn new( + action_type: impl Into, + description: impl Into, + actor_id: impl Into, + ) -> Self { Self { id: ActionId::new(), action_type: action_type.into(), diff --git a/crates/prime-radiant/src/execution/executor.rs b/crates/prime-radiant/src/execution/executor.rs index 63c4176d7..d2454f6f9 100644 --- a/crates/prime-radiant/src/execution/executor.rs +++ b/crates/prime-radiant/src/execution/executor.rs @@ -290,7 +290,10 @@ impl ActionExecutor { let mut stats = self.stats.write(); stats.total_denied += 1; - let reason = decision.reason.clone().unwrap_or_else(|| "Gate denied".to_string()); + let reason = decision + .reason + .clone() + .unwrap_or_else(|| "Gate denied".to_string()); return ExecutionResult { result: Err(ActionError::Denied(reason)), @@ -313,15 +316,17 @@ impl ActionExecutor { "Action queued for human review" ); - self.queue_for_human_review(action.metadata().id.clone(), witness.clone(), energy.clone()); + self.queue_for_human_review( + action.metadata().id.clone(), + witness.clone(), + energy.clone(), + ); let mut stats = self.stats.write(); stats.total_allowed += 1; return ExecutionResult { - result: Err(ActionError::Denied( - "Queued for human review".to_string(), - )), + result: Err(ActionError::Denied("Queued for human review".to_string())), witness, decision, stats: ExecutionStats { @@ -473,9 +478,7 @@ impl ActionExecutor { let mut queue = self.human_queue.write(); if queue.len() >= self.config.max_human_queue { - warn!( - "Human review queue full, dropping oldest item" - ); + warn!("Human review queue full, dropping oldest item"); queue.pop_front(); } @@ -522,12 +525,7 @@ impl ActionExecutor { /// Get recent witnesses. pub fn recent_witnesses(&self, limit: usize) -> Vec { let witnesses = self.witnesses.read(); - witnesses - .iter() - .rev() - .take(limit) - .cloned() - .collect() + witnesses.iter().rev().take(limit).cloned().collect() } /// Get a witness by ID. @@ -619,7 +617,12 @@ impl ActionResultBuilder { /// Build the result. pub fn build(self) -> ActionResult { if self.success { - ActionResult::success(self.action_id, self.duration_us, self.lane, self.retry_count) + ActionResult::success( + self.action_id, + self.duration_us, + self.lane, + self.retry_count, + ) } else { ActionResult::failure( self.action_id, @@ -690,7 +693,9 @@ mod tests { fn execute(&self, _ctx: &ExecutionContext) -> Result<(), ActionError> { self.execute_count.fetch_add(1, Ordering::SeqCst); if self.should_fail { - Err(ActionError::ExecutionFailed("Simulated failure".to_string())) + Err(ActionError::ExecutionFailed( + "Simulated failure".to_string(), + )) } else { Ok(()) } @@ -829,7 +834,10 @@ mod tests { executor.execute(&action, &energy); // Stats should be shared - assert_eq!(executor.stats().total_submitted, executor2.stats().total_submitted); + assert_eq!( + executor.stats().total_submitted, + executor2.stats().total_submitted + ); } #[test] diff --git a/crates/prime-radiant/src/execution/gate.rs b/crates/prime-radiant/src/execution/gate.rs index 880a58cb9..7305ea8cb 100644 --- a/crates/prime-radiant/src/execution/gate.rs +++ b/crates/prime-radiant/src/execution/gate.rs @@ -143,7 +143,10 @@ impl EnergySnapshot { let mut fingerprint = [0u8; 32]; let hash_input = format!( "{}:{}:{}:{}", - total_energy, scope_energy, scope.as_str(), timestamp_ms + total_energy, + scope_energy, + scope.as_str(), + timestamp_ms ); let hash = blake3::hash(hash_input.as_bytes()); fingerprint.copy_from_slice(hash.as_bytes()); @@ -344,12 +347,7 @@ impl EnergyHistory { } /// Check if energy has been above threshold for the given duration. - pub fn is_above_threshold( - &self, - scope: &ScopeId, - threshold: f32, - duration: Duration, - ) -> bool { + pub fn is_above_threshold(&self, scope: &ScopeId, threshold: f32, duration: Duration) -> bool { let history = match self.histories.get(scope) { Some(h) => h, None => return false, @@ -519,11 +517,9 @@ impl CoherenceGate { } // Check for persistent incoherence - let persistent = self.history.is_above_threshold( - scope, - self.thresholds.reflex, - self.persistence_window, - ); + let persistent = + self.history + .is_above_threshold(scope, self.thresholds.reflex, self.persistence_window); let escalation = if persistent && lane < ComputeLane::Heavy { // Persistent incoherence requires at least Heavy lane diff --git a/crates/prime-radiant/src/execution/ladder.rs b/crates/prime-radiant/src/execution/ladder.rs index 7f1a0c70a..cc3b2cf22 100644 --- a/crates/prime-radiant/src/execution/ladder.rs +++ b/crates/prime-radiant/src/execution/ladder.rs @@ -52,10 +52,10 @@ impl ComputeLane { #[inline] pub const fn latency_budget_us(&self) -> u64 { match self { - ComputeLane::Reflex => 1_000, // 1ms - ComputeLane::Retrieval => 10_000, // 10ms - ComputeLane::Heavy => 100_000, // 100ms - ComputeLane::Human => u64::MAX, // No limit (async) + ComputeLane::Reflex => 1_000, // 1ms + ComputeLane::Retrieval => 10_000, // 10ms + ComputeLane::Heavy => 100_000, // 100ms + ComputeLane::Human => u64::MAX, // No limit (async) } } @@ -381,7 +381,10 @@ impl EscalationReason { /// Is this an external trigger? pub fn is_external(&self) -> bool { - matches!(self, Self::ExternalTrigger { .. } | Self::SystemOverride { .. }) + matches!( + self, + Self::ExternalTrigger { .. } | Self::SystemOverride { .. } + ) } } @@ -491,10 +494,7 @@ mod tests { #[test] fn test_lane_escalation() { - assert_eq!( - ComputeLane::Reflex.escalate(), - Some(ComputeLane::Retrieval) - ); + assert_eq!(ComputeLane::Reflex.escalate(), Some(ComputeLane::Retrieval)); assert_eq!(ComputeLane::Retrieval.escalate(), Some(ComputeLane::Heavy)); assert_eq!(ComputeLane::Heavy.escalate(), Some(ComputeLane::Human)); assert_eq!(ComputeLane::Human.escalate(), None); diff --git a/crates/prime-radiant/src/execution/mod.rs b/crates/prime-radiant/src/execution/mod.rs index cec35adef..ee819654e 100644 --- a/crates/prime-radiant/src/execution/mod.rs +++ b/crates/prime-radiant/src/execution/mod.rs @@ -80,31 +80,29 @@ pub mod ladder; // Re-export primary types for convenient access pub use action::{ - Action, ActionError, ActionId, ActionImpact, ActionMetadata, ActionResult, - BoxedAction, ExecutionContext, ScopeId, + Action, ActionError, ActionId, ActionImpact, ActionMetadata, ActionResult, BoxedAction, + ExecutionContext, ScopeId, }; pub use gate::{ - CoherenceGate, EnergyHistory, EnergySnapshot, GateDecision, - PolicyBundleRef, WitnessId, WitnessRecord, + CoherenceGate, EnergyHistory, EnergySnapshot, GateDecision, PolicyBundleRef, WitnessId, + WitnessRecord, }; -pub use ladder::{ - ComputeLane, EscalationReason, LaneThresholds, LaneTransition, ThresholdError, -}; +pub use ladder::{ComputeLane, EscalationReason, LaneThresholds, LaneTransition, ThresholdError}; pub use executor::{ - ActionExecutor, ActionResultBuilder, ExecutionResult, ExecutionStats, - ExecutorConfig, ExecutorStats, HumanReviewItem, + ActionExecutor, ActionResultBuilder, ExecutionResult, ExecutionStats, ExecutorConfig, + ExecutorStats, HumanReviewItem, }; /// Prelude module for convenient imports. pub mod prelude { pub use super::{ - Action, ActionError, ActionExecutor, ActionId, ActionImpact, ActionMetadata, - ActionResult, CoherenceGate, ComputeLane, EnergySnapshot, EscalationReason, - ExecutionContext, ExecutionResult, ExecutorConfig, GateDecision, LaneThresholds, - PolicyBundleRef, ScopeId, WitnessId, WitnessRecord, + Action, ActionError, ActionExecutor, ActionId, ActionImpact, ActionMetadata, ActionResult, + CoherenceGate, ComputeLane, EnergySnapshot, EscalationReason, ExecutionContext, + ExecutionResult, ExecutorConfig, GateDecision, LaneThresholds, PolicyBundleRef, ScopeId, + WitnessId, WitnessRecord, }; } diff --git a/crates/prime-radiant/src/governance/mod.rs b/crates/prime-radiant/src/governance/mod.rs index 5651fc70a..eaa129df7 100644 --- a/crates/prime-radiant/src/governance/mod.rs +++ b/crates/prime-radiant/src/governance/mod.rs @@ -24,8 +24,8 @@ pub use policy::{ }; pub use witness::{ - ComputeLane as WitnessComputeLane, EnergySnapshot, GateDecision, - WitnessChainError, WitnessError, WitnessId, WitnessRecord, + ComputeLane as WitnessComputeLane, EnergySnapshot, GateDecision, WitnessChainError, + WitnessError, WitnessId, WitnessRecord, }; pub use lineage::{EntityRef, LineageError, LineageId, LineageRecord, Operation}; diff --git a/crates/prime-radiant/src/gpu/buffer.rs b/crates/prime-radiant/src/gpu/buffer.rs index d0cde3392..6968b9b7f 100644 --- a/crates/prime-radiant/src/gpu/buffer.rs +++ b/crates/prime-radiant/src/gpu/buffer.rs @@ -610,7 +610,12 @@ impl GpuBuffer { } /// Create a new storage buffer with initial data (for dispatch compatibility) - pub fn new_storage(device: &Device, queue: &Queue, data: &[T], read_write: bool) -> GpuResult { + pub fn new_storage( + device: &Device, + queue: &Queue, + data: &[T], + read_write: bool, + ) -> GpuResult { let usage = if read_write { BufferUsage::Residuals } else { @@ -620,7 +625,11 @@ impl GpuBuffer { } /// Create a new uninitialized storage buffer - pub fn new_storage_uninit(device: &Device, count: usize, read_write: bool) -> GpuResult { + pub fn new_storage_uninit( + device: &Device, + count: usize, + read_write: bool, + ) -> GpuResult { let size = count * std::mem::size_of::(); let usage = if read_write { BufferUsage::Residuals @@ -632,7 +641,13 @@ impl GpuBuffer { /// Create a new uniform buffer with data pub fn new_uniform(device: &Device, queue: &Queue, data: &T) -> GpuResult { - Self::new_with_data(device, queue, std::slice::from_ref(data), BufferUsage::Uniforms, "uniform_buffer") + Self::new_with_data( + device, + queue, + std::slice::from_ref(data), + BufferUsage::Uniforms, + "uniform_buffer", + ) } } diff --git a/crates/prime-radiant/src/gpu/device.rs b/crates/prime-radiant/src/gpu/device.rs index 3a0f52ab7..bb406201a 100644 --- a/crates/prime-radiant/src/gpu/device.rs +++ b/crates/prime-radiant/src/gpu/device.rs @@ -66,7 +66,10 @@ impl GpuDevice { gles_minor_version: wgpu::Gles3MinorVersion::default(), }); - debug!("Created wgpu instance with backends: {:?}", options.backends); + debug!( + "Created wgpu instance with backends: {:?}", + options.backends + ); let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { @@ -182,12 +185,13 @@ impl GpuDevice { /// This is useful when you need to ensure GPU work has completed /// before continuing on the CPU. pub fn poll(&self, wait: bool) -> bool { - self.device.poll(if wait { - wgpu::Maintain::Wait - } else { - wgpu::Maintain::Poll - }) - .is_queue_empty() + self.device + .poll(if wait { + wgpu::Maintain::Wait + } else { + wgpu::Maintain::Poll + }) + .is_queue_empty() } /// Submit a command buffer to the queue @@ -265,7 +269,10 @@ mod tests { #[test] fn test_device_options_default() { let options = GpuDeviceOptions::default(); - assert_eq!(options.power_preference, wgpu::PowerPreference::HighPerformance); + assert_eq!( + options.power_preference, + wgpu::PowerPreference::HighPerformance + ); assert!(!options.force_fallback); } diff --git a/crates/prime-radiant/src/gpu/dispatch.rs b/crates/prime-radiant/src/gpu/dispatch.rs index 3de57a777..aa35d9625 100644 --- a/crates/prime-radiant/src/gpu/dispatch.rs +++ b/crates/prime-radiant/src/gpu/dispatch.rs @@ -152,9 +152,7 @@ impl GpuDispatcher { let mut encoder = self .device .device() - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some(label), - }); + .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some(label) }); { let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { @@ -210,9 +208,7 @@ impl GpuDispatcher { let mut encoder = self .device .device() - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some(label), - }); + .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some(label) }); { let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { @@ -261,14 +257,16 @@ impl GpuDispatcher { } let label = config.label.as_deref().unwrap_or("dispatch_chain"); - debug!("Dispatching chain '{}' with {} kernels", label, dispatches.len()); + debug!( + "Dispatching chain '{}' with {} kernels", + label, + dispatches.len() + ); let mut encoder = self .device .device() - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some(label), - }); + .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some(label) }); for (i, (pipeline, bind_group, workgroups)) in dispatches.iter().enumerate() { trace!( diff --git a/crates/prime-radiant/src/gpu/engine.rs b/crates/prime-radiant/src/gpu/engine.rs index 8bc257098..5bbefabf0 100644 --- a/crates/prime-radiant/src/gpu/engine.rs +++ b/crates/prime-radiant/src/gpu/engine.rs @@ -6,12 +6,12 @@ use super::buffer::{BufferUsage, GpuBufferManager, GpuEdge, GpuParams, GpuRestrictionMap}; use super::error::{GpuError, GpuResult}; use super::kernels::{ - ComputeEnergyKernel, ComputeResidualsKernel, EnergyParams, - SheafAttentionKernel, TokenRoutingKernel, + ComputeEnergyKernel, ComputeResidualsKernel, EnergyParams, SheafAttentionKernel, + TokenRoutingKernel, }; use crate::coherence::{CoherenceEnergy as CpuCoherenceEnergy, EdgeEnergy}; use crate::substrate::restriction::MatrixStorage; -use crate::substrate::{SheafGraph, NodeId, EdgeId}; +use crate::substrate::{EdgeId, NodeId, SheafGraph}; use chrono::Utc; use std::collections::HashMap; @@ -312,17 +312,13 @@ impl GpuCoherenceEngine { // Convert restriction maps let rho_source_idx = restriction_maps.len() as u32; - let gpu_rho_source = Self::convert_restriction_map( - &edge.rho_source, - &mut restriction_data, - ); + let gpu_rho_source = + Self::convert_restriction_map(&edge.rho_source, &mut restriction_data); restriction_maps.push(gpu_rho_source); let rho_target_idx = restriction_maps.len() as u32; - let gpu_rho_target = Self::convert_restriction_map( - &edge.rho_target, - &mut restriction_data, - ); + let gpu_rho_target = + Self::convert_restriction_map(&edge.rho_target, &mut restriction_data); restriction_maps.push(gpu_rho_target); edges.push(GpuEdge { @@ -349,11 +345,8 @@ impl GpuCoherenceEngine { "node_states", )?; - self.buffer_manager.allocate_with_data( - &edges, - BufferUsage::EdgeData, - "edges", - )?; + self.buffer_manager + .allocate_with_data(&edges, BufferUsage::EdgeData, "edges")?; self.buffer_manager.allocate_with_data( &restriction_maps, @@ -368,21 +361,19 @@ impl GpuCoherenceEngine { )?; // Allocate output buffers - let max_comparison_dim = edges.iter().map(|e| e.comparison_dim).max().unwrap_or(state_dim); + let max_comparison_dim = edges + .iter() + .map(|e| e.comparison_dim) + .max() + .unwrap_or(state_dim); let residuals_size = (num_edges * max_comparison_dim) as usize * std::mem::size_of::(); let energies_size = num_edges as usize * std::mem::size_of::(); - self.buffer_manager.allocate( - residuals_size, - BufferUsage::Residuals, - "residuals", - )?; + self.buffer_manager + .allocate(residuals_size, BufferUsage::Residuals, "residuals")?; - self.buffer_manager.allocate( - energies_size, - BufferUsage::Energies, - "edge_energies", - )?; + self.buffer_manager + .allocate(energies_size, BufferUsage::Energies, "edge_energies")?; // Pre-allocate computation buffers to eliminate per-frame allocations let num_workgroups = ComputeEnergyKernel::workgroup_count(num_edges); @@ -407,7 +398,9 @@ impl GpuCoherenceEngine { let partial_sums_buffer = self.device.create_buffer(&wgpu::BufferDescriptor { label: Some("partial_sums_preallocated"), size: ((num_workgroups as usize).max(1) * std::mem::size_of::()) as u64, - usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST, + usage: wgpu::BufferUsages::STORAGE + | wgpu::BufferUsages::COPY_SRC + | wgpu::BufferUsages::COPY_DST, mapped_at_creation: false, }); @@ -423,7 +416,9 @@ impl GpuCoherenceEngine { let total_energy_buffer = self.device.create_buffer(&wgpu::BufferDescriptor { label: Some("total_energy_preallocated"), size: std::mem::size_of::() as u64, - usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST, + usage: wgpu::BufferUsages::STORAGE + | wgpu::BufferUsages::COPY_SRC + | wgpu::BufferUsages::COPY_DST, mapped_at_creation: false, }); @@ -497,7 +492,9 @@ impl GpuCoherenceEngine { data.extend(csr.values.iter().cloned()); (3, csr.values.len() as u32) } - MatrixStorage::Dense { data: matrix_data, .. } => { + MatrixStorage::Dense { + data: matrix_data, .. + } => { data.extend(matrix_data.iter().cloned()); (3, matrix_data.len() as u32) } @@ -518,7 +515,9 @@ impl GpuCoherenceEngine { pub async fn compute_energy(&mut self) -> GpuResult { let start = std::time::Instant::now(); - let graph_data = self.graph_data.as_ref() + let graph_data = self + .graph_data + .as_ref() .ok_or_else(|| GpuError::Internal("Graph not uploaded".into()))?; let num_edges = graph_data.num_edges; @@ -535,27 +534,44 @@ impl GpuCoherenceEngine { threshold_lane2: self.config.threshold_lane2, store_residuals: 1, // Store residuals by default for gradient computation }; - self.queue.write_buffer(&graph_data.params_buffer, 0, bytemuck::bytes_of(¶ms)); + self.queue + .write_buffer(&graph_data.params_buffer, 0, bytemuck::bytes_of(¶ms)); // Write energy params to pre-allocated buffer (no allocation) let energy_params = EnergyParams { num_elements: num_edges, _padding: [0; 7], }; - self.queue.write_buffer(&graph_data.energy_params_buffer, 0, bytemuck::bytes_of(&energy_params)); + self.queue.write_buffer( + &graph_data.energy_params_buffer, + 0, + bytemuck::bytes_of(&energy_params), + ); // Get managed buffers for bind group creation - let node_states_buf = self.buffer_manager.get("node_states") + let node_states_buf = self + .buffer_manager + .get("node_states") .ok_or_else(|| GpuError::Internal("Node states buffer not found".into()))?; - let edges_buf = self.buffer_manager.get("edges") + let edges_buf = self + .buffer_manager + .get("edges") .ok_or_else(|| GpuError::Internal("Edges buffer not found".into()))?; - let restriction_maps_buf = self.buffer_manager.get("restriction_maps") + let restriction_maps_buf = self + .buffer_manager + .get("restriction_maps") .ok_or_else(|| GpuError::Internal("Restriction maps buffer not found".into()))?; - let restriction_data_buf = self.buffer_manager.get("restriction_data") + let restriction_data_buf = self + .buffer_manager + .get("restriction_data") .ok_or_else(|| GpuError::Internal("Restriction data buffer not found".into()))?; - let residuals_buf = self.buffer_manager.get("residuals") + let residuals_buf = self + .buffer_manager + .get("residuals") .ok_or_else(|| GpuError::Internal("Residuals buffer not found".into()))?; - let energies_buf = self.buffer_manager.get("edge_energies") + let energies_buf = self + .buffer_manager + .get("edge_energies") .ok_or_else(|| GpuError::Internal("Edge energies buffer not found".into()))?; // Create bind group for residuals kernel using pre-allocated params buffer @@ -579,9 +595,11 @@ impl GpuCoherenceEngine { ); // Create command encoder - let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("compute_energy_encoder"), - }); + let mut encoder = self + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("compute_energy_encoder"), + }); // Dispatch residuals computation { @@ -618,7 +636,11 @@ impl GpuCoherenceEngine { num_elements: num_workgroups, _padding: [0; 7], }; - self.queue.write_buffer(&graph_data.final_params_buffer, 0, bytemuck::bytes_of(&final_params)); + self.queue.write_buffer( + &graph_data.final_params_buffer, + 0, + bytemuck::bytes_of(&final_params), + ); let final_bind_group = self.energy_kernel.create_bind_group_raw( &self.device, @@ -670,8 +692,14 @@ impl GpuCoherenceEngine { self.queue.submit(std::iter::once(encoder.finish())); // Read back results from pre-allocated staging buffers - let edge_energies = Self::read_buffer_f32(&self.device, &graph_data.energies_staging, num_edges as usize).await?; - let total_energy = Self::read_buffer_f32(&self.device, &graph_data.total_staging, 1).await?[0]; + let edge_energies = Self::read_buffer_f32( + &self.device, + &graph_data.energies_staging, + num_edges as usize, + ) + .await?; + let total_energy = + Self::read_buffer_f32(&self.device, &graph_data.total_staging, 1).await?[0]; let compute_time_us = start.elapsed().as_micros() as u64; @@ -710,8 +738,8 @@ impl GpuCoherenceEngine { .map_err(|e| GpuError::BufferRead(e.to_string()))?; let data = buffer_slice.get_mapped_range(); - let result: Vec = bytemuck::cast_slice(&data[..count * std::mem::size_of::()]) - .to_vec(); + let result: Vec = + bytemuck::cast_slice(&data[..count * std::mem::size_of::()]).to_vec(); drop(data); buffer.unmap(); diff --git a/crates/prime-radiant/src/gpu/mod.rs b/crates/prime-radiant/src/gpu/mod.rs index a805ef726..39266e87f 100644 --- a/crates/prime-radiant/src/gpu/mod.rs +++ b/crates/prime-radiant/src/gpu/mod.rs @@ -97,17 +97,19 @@ mod kernels; mod pipeline; // Core exports -pub use buffer::{BufferUsage, GpuBuffer, GpuBufferManager, GpuBufferPool, BufferUsageFlags, BufferKey}; +pub use buffer::{ + BufferKey, BufferUsage, BufferUsageFlags, GpuBuffer, GpuBufferManager, GpuBufferPool, +}; pub use device::{GpuDevice, GpuDeviceInfo, GpuDeviceOptions}; -pub use dispatch::{DispatchConfig, GpuDispatcher, DispatchBuilder}; +pub use dispatch::{DispatchBuilder, DispatchConfig, GpuDispatcher}; pub use error::{GpuError, GpuResult}; pub use pipeline::{BindingDesc, BindingType, ComputePipeline, PipelineCache}; // Re-export buffer types -pub use buffer::{GpuNodeState, GpuEdge, GpuRestrictionMap, GpuParams}; +pub use buffer::{GpuEdge, GpuNodeState, GpuParams, GpuRestrictionMap}; // Re-export engine types -pub use engine::{GpuCoherenceEngine, GpuConfig, GpuCapabilities, GpuCoherenceEnergy}; +pub use engine::{GpuCapabilities, GpuCoherenceEnergy, GpuCoherenceEngine, GpuConfig}; /// Synchronous API for GPU coherence engine (uses pollster) pub mod sync { @@ -116,8 +118,8 @@ pub mod sync { // Re-export kernel types pub use kernels::{ - ComputeResidualsKernel, ComputeEnergyKernel, SheafAttentionKernel, TokenRoutingKernel, - AttentionWeight, Token, RoutingDecision, LaneStats, EnergyParams, + AttentionWeight, ComputeEnergyKernel, ComputeResidualsKernel, EnergyParams, LaneStats, + RoutingDecision, SheafAttentionKernel, Token, TokenRoutingKernel, }; /// Default workgroup size for compute shaders diff --git a/crates/prime-radiant/src/gpu/pipeline.rs b/crates/prime-radiant/src/gpu/pipeline.rs index 9187a3ec0..cb70c56d2 100644 --- a/crates/prime-radiant/src/gpu/pipeline.rs +++ b/crates/prime-radiant/src/gpu/pipeline.rs @@ -3,8 +3,8 @@ //! This module handles shader compilation, pipeline creation, and bind group //! management for GPU compute operations. -use std::sync::Arc; use dashmap::DashMap; +use std::sync::Arc; use tracing::{debug, info}; use wgpu::{Device, ShaderModule}; @@ -332,10 +332,12 @@ impl PipelineCache { info!("Creating and caching pipeline: {}", name); - let pipeline = ComputePipeline::from_shader(&self.device, shader_source, entry_point, bindings)?; + let pipeline = + ComputePipeline::from_shader(&self.device, shader_source, entry_point, bindings)?; let pipeline = Arc::new(pipeline); - self.pipelines.insert(name.to_string(), Arc::clone(&pipeline)); + self.pipelines + .insert(name.to_string(), Arc::clone(&pipeline)); Ok(pipeline) } diff --git a/crates/prime-radiant/src/hyperbolic/adapter.rs b/crates/prime-radiant/src/hyperbolic/adapter.rs index 34624424e..94df2708e 100644 --- a/crates/prime-radiant/src/hyperbolic/adapter.rs +++ b/crates/prime-radiant/src/hyperbolic/adapter.rs @@ -89,11 +89,7 @@ impl HyperbolicAdapter { let norm_x_sq: f32 = x.iter().map(|v| v * v).sum(); let norm_y_sq: f32 = y.iter().map(|v| v * v).sum(); - let diff_sq: f32 = x - .iter() - .zip(y.iter()) - .map(|(a, b)| (a - b) * (a - b)) - .sum(); + let diff_sq: f32 = x.iter().zip(y.iter()).map(|(a, b)| (a - b) * (a - b)).sum(); let denom = (1.0 - norm_x_sq).max(EPS) * (1.0 - norm_y_sq).max(EPS); let inner = 1.0 + 2.0 * diff_sq / denom; diff --git a/crates/prime-radiant/src/hyperbolic/depth.rs b/crates/prime-radiant/src/hyperbolic/depth.rs index 6389817a8..09819813b 100644 --- a/crates/prime-radiant/src/hyperbolic/depth.rs +++ b/crates/prime-radiant/src/hyperbolic/depth.rs @@ -178,26 +178,11 @@ mod tests { fn test_hierarchy_levels() { let computer = DepthComputer::new(-1.0); - assert_eq!( - computer.classify_level(0.3), - HierarchyLevel::Root - ); - assert_eq!( - computer.classify_level(0.7), - HierarchyLevel::High - ); - assert_eq!( - computer.classify_level(1.5), - HierarchyLevel::Mid - ); - assert_eq!( - computer.classify_level(2.5), - HierarchyLevel::Deep - ); - assert_eq!( - computer.classify_level(4.0), - HierarchyLevel::VeryDeep - ); + assert_eq!(computer.classify_level(0.3), HierarchyLevel::Root); + assert_eq!(computer.classify_level(0.7), HierarchyLevel::High); + assert_eq!(computer.classify_level(1.5), HierarchyLevel::Mid); + assert_eq!(computer.classify_level(2.5), HierarchyLevel::Deep); + assert_eq!(computer.classify_level(4.0), HierarchyLevel::VeryDeep); } #[test] diff --git a/crates/prime-radiant/src/hyperbolic/energy.rs b/crates/prime-radiant/src/hyperbolic/energy.rs index eea26654c..7b6db39c0 100644 --- a/crates/prime-radiant/src/hyperbolic/energy.rs +++ b/crates/prime-radiant/src/hyperbolic/energy.rs @@ -176,8 +176,7 @@ impl HyperbolicEnergy { for edge in &self.edge_energies { let avg_depth = edge.avg_depth(); - let bucket_idx = - ((avg_depth - self.min_depth) / bucket_size).floor() as usize; + let bucket_idx = ((avg_depth - self.min_depth) / bucket_size).floor() as usize; let bucket_idx = bucket_idx.min(num_buckets - 1); buckets[bucket_idx].total_energy += edge.weighted_energy; diff --git a/crates/prime-radiant/src/hyperbolic/mod.rs b/crates/prime-radiant/src/hyperbolic/mod.rs index 82b09ac68..e6a3529e8 100644 --- a/crates/prime-radiant/src/hyperbolic/mod.rs +++ b/crates/prime-radiant/src/hyperbolic/mod.rs @@ -323,7 +323,9 @@ mod tests { coherence.insert_node(2, vec![0.5, 0.5, 0.5, 0.5]).unwrap(); let residual = vec![0.1, 0.1, 0.1, 0.1]; - let weighted = coherence.weighted_edge_energy(1, 2, &residual, 1.0).unwrap(); + let weighted = coherence + .weighted_edge_energy(1, 2, &residual, 1.0) + .unwrap(); assert!(weighted.weighted_energy > 0.0); assert!(weighted.depth_weight > 1.0); // Should have depth scaling @@ -338,14 +340,19 @@ mod tests { }; let mut coherence = HyperbolicCoherence::new(config); - coherence.insert_node(1, vec![0.05, 0.05, 0.05, 0.05]).unwrap(); + coherence + .insert_node(1, vec![0.05, 0.05, 0.05, 0.05]) + .unwrap(); coherence.insert_node(2, vec![0.7, 0.7, 0.0, 0.0]).unwrap(); let level1 = coherence.hierarchy_level(1).unwrap(); let level2 = coherence.hierarchy_level(2).unwrap(); // Node 1 should be at higher level (closer to root) - assert!(matches!(level1, HierarchyLevel::Root | HierarchyLevel::High)); + assert!(matches!( + level1, + HierarchyLevel::Root | HierarchyLevel::High + )); // Node 2 should be deeper assert!(matches!( level2, diff --git a/crates/prime-radiant/src/learned_rho/config.rs b/crates/prime-radiant/src/learned_rho/config.rs index 56867d8c2..a94defb4e 100644 --- a/crates/prime-radiant/src/learned_rho/config.rs +++ b/crates/prime-radiant/src/learned_rho/config.rs @@ -130,7 +130,13 @@ impl Activation { pub fn apply(&self, x: f32) -> f32 { match self { Self::ReLU => x.max(0.0), - Self::LeakyReLU => if x > 0.0 { x } else { 0.01 * x }, + Self::LeakyReLU => { + if x > 0.0 { + x + } else { + 0.01 * x + } + } Self::GELU => { // Approximation: 0.5 * x * (1 + tanh(sqrt(2/pi) * (x + 0.044715 * x^3))) 0.5 * x * (1.0 + ((0.7978845608 * (x + 0.044715 * x.powi(3))).tanh())) @@ -144,12 +150,25 @@ impl Activation { /// Apply the derivative of the activation function. pub fn derivative(&self, x: f32) -> f32 { match self { - Self::ReLU => if x > 0.0 { 1.0 } else { 0.0 }, - Self::LeakyReLU => if x > 0.0 { 1.0 } else { 0.01 }, + Self::ReLU => { + if x > 0.0 { + 1.0 + } else { + 0.0 + } + } + Self::LeakyReLU => { + if x > 0.0 { + 1.0 + } else { + 0.01 + } + } Self::GELU => { // Approximation of GELU derivative let t = (0.7978845608 * (x + 0.044715 * x.powi(3))).tanh(); - 0.5 * (1.0 + t) + 0.5 * x * (1.0 - t * t) * 0.7978845608 * (1.0 + 3.0 * 0.044715 * x * x) + 0.5 * (1.0 + t) + + 0.5 * x * (1.0 - t * t) * 0.7978845608 * (1.0 + 3.0 * 0.044715 * x * x) } Self::Tanh => 1.0 - x.tanh().powi(2), Self::Sigmoid => { @@ -303,7 +322,9 @@ impl SchedulerConfig { SchedulerType::CosineAnnealing { t_max, eta_min } => { let t = (step % t_max) as f32; let t_max = *t_max as f32; - *eta_min + (self.initial_lr - eta_min) * (1.0 + (std::f32::consts::PI * t / t_max).cos()) / 2.0 + *eta_min + + (self.initial_lr - eta_min) * (1.0 + (std::f32::consts::PI * t / t_max).cos()) + / 2.0 } } } diff --git a/crates/prime-radiant/src/learned_rho/map.rs b/crates/prime-radiant/src/learned_rho/map.rs index e52a3e61f..cd407134a 100644 --- a/crates/prime-radiant/src/learned_rho/map.rs +++ b/crates/prime-radiant/src/learned_rho/map.rs @@ -21,11 +21,11 @@ pub enum MapState { /// A simple dense layer. #[derive(Debug, Clone)] struct DenseLayer { - weights: Vec>, // [output_dim][input_dim] - biases: Vec, // [output_dim] + weights: Vec>, // [output_dim][input_dim] + biases: Vec, // [output_dim] weight_gradients: Vec>, bias_gradients: Vec, - input_cache: Vec, // For backprop + input_cache: Vec, // For backprop pre_activation_cache: Vec, activation: Activation, } @@ -98,7 +98,11 @@ impl DenseLayer { } fn apply_gradients(&mut self, lr: f32, weight_decay: f32) { - for (weights_row, grads_row) in self.weights.iter_mut().zip(self.weight_gradients.iter_mut()) { + for (weights_row, grads_row) in self + .weights + .iter_mut() + .zip(self.weight_gradients.iter_mut()) + { for (w, g) in weights_row.iter_mut().zip(grads_row.iter_mut()) { *w -= lr * (*g + weight_decay * *w); *g = 0.0; // Reset gradient @@ -154,7 +158,9 @@ impl EwcState { } let mut loss = 0.0; - for ((f, opt), curr) in self.fisher.iter() + for ((f, opt), curr) in self + .fisher + .iter() .zip(self.optimal_weights.iter()) .zip(current_weights.iter()) { @@ -189,7 +195,9 @@ pub struct LearnedRestrictionMap { impl LearnedRestrictionMap { /// Create a new learned restriction map. pub fn new(config: RestrictionMapConfig) -> LearnedRhoResult { - config.validate().map_err(LearnedRhoError::InvalidConfiguration)?; + config + .validate() + .map_err(LearnedRhoError::InvalidConfiguration)?; let mut layers = Vec::with_capacity(config.num_layers + 1); @@ -217,9 +225,10 @@ impl LearnedRestrictionMap { )); // Count total parameters for EWC - let num_params: usize = layers.iter().map(|l| { - l.weights.iter().map(|r| r.len()).sum::() + l.biases.len() - }).sum(); + let num_params: usize = layers + .iter() + .map(|l| l.weights.iter().map(|r| r.len()).sum::() + l.biases.len()) + .sum(); let replay = ReplayBuffer::new(config.replay_capacity); let ewc = EwcState::new(num_params, config.ewc_lambda); @@ -258,7 +267,10 @@ impl LearnedRestrictionMap { /// Apply the learned restriction map (forward pass). pub fn apply(&mut self, input: &[f32]) -> LearnedRhoResult> { if input.len() != self.config.input_dim { - return Err(LearnedRhoError::dim_mismatch(self.config.input_dim, input.len())); + return Err(LearnedRhoError::dim_mismatch( + self.config.input_dim, + input.len(), + )); } let mut x = input.to_vec(); @@ -278,10 +290,16 @@ impl LearnedRestrictionMap { expected_residual: &[f32], ) -> LearnedRhoResult { if source.len() != self.config.input_dim { - return Err(LearnedRhoError::dim_mismatch(self.config.input_dim, source.len())); + return Err(LearnedRhoError::dim_mismatch( + self.config.input_dim, + source.len(), + )); } if expected_residual.len() != self.config.output_dim { - return Err(LearnedRhoError::dim_mismatch(self.config.output_dim, expected_residual.len())); + return Err(LearnedRhoError::dim_mismatch( + self.config.output_dim, + expected_residual.len(), + )); } self.state = MapState::Training; @@ -307,7 +325,12 @@ impl LearnedRestrictionMap { } // Compute gradient norm - let gradient_norm: f32 = self.layers.iter().map(|l| l.gradient_norm()).sum::().sqrt(); + let gradient_norm: f32 = self + .layers + .iter() + .map(|l| l.gradient_norm()) + .sum::() + .sqrt(); // Get current learning rate let lr = self.config.scheduler.get_lr(self.training_step); @@ -415,7 +438,11 @@ impl LearnedRestrictionMap { let duration_ms = start.elapsed().as_millis() as u64; - Ok(TrainingResult::from_metrics(&metrics_list, epoch, duration_ms)) + Ok(TrainingResult::from_metrics( + &metrics_list, + epoch, + duration_ms, + )) } /// Get map statistics. @@ -526,11 +553,7 @@ mod tests { // Add some experiences for _ in 0..20 { - map.add_experience( - vec![1.0; 32], - vec![2.0; 32], - vec![0.1; 16], - ); + map.add_experience(vec![1.0; 32], vec![2.0; 32], vec![0.1; 16]); } let metrics = map.train_from_replay().unwrap(); diff --git a/crates/prime-radiant/src/learned_rho/mod.rs b/crates/prime-radiant/src/learned_rho/mod.rs index cf00d8f74..ab6884cd4 100644 --- a/crates/prime-radiant/src/learned_rho/mod.rs +++ b/crates/prime-radiant/src/learned_rho/mod.rs @@ -46,7 +46,7 @@ mod error; mod map; mod training; -pub use config::{RestrictionMapConfig, OptimizerConfig, SchedulerConfig}; +pub use config::{OptimizerConfig, RestrictionMapConfig, SchedulerConfig}; pub use error::{LearnedRhoError, LearnedRhoResult}; pub use map::{LearnedRestrictionMap, MapState}; pub use training::{TrainingBatch, TrainingMetrics, TrainingResult}; diff --git a/crates/prime-radiant/src/learned_rho/training.rs b/crates/prime-radiant/src/learned_rho/training.rs index 39d07b2a6..1070aa6bb 100644 --- a/crates/prime-radiant/src/learned_rho/training.rs +++ b/crates/prime-radiant/src/learned_rho/training.rs @@ -110,7 +110,10 @@ impl ReplayBuffer { for i in 0..batch_size.min(n) { // Simple LCG for pseudo-random selection - let idx = ((seed.wrapping_mul(6364136223846793005).wrapping_add(i as u64)) % n as u64) as usize; + let idx = ((seed + .wrapping_mul(6364136223846793005) + .wrapping_add(i as u64)) + % n as u64) as usize; let exp = &self.experiences[idx]; batch.add( exp.source.clone(), @@ -249,11 +252,7 @@ mod tests { let mut buffer = ReplayBuffer::new(100); for i in 0..50 { - buffer.add( - vec![i as f32], - vec![i as f32 + 1.0], - vec![0.1], - ); + buffer.add(vec![i as f32], vec![i as f32 + 1.0], vec![0.1]); } assert_eq!(buffer.len(), 50); diff --git a/crates/prime-radiant/src/lib.rs b/crates/prime-radiant/src/lib.rs index 51bd026f4..cfcbb0c31 100644 --- a/crates/prime-radiant/src/lib.rs +++ b/crates/prime-radiant/src/lib.rs @@ -258,129 +258,192 @@ pub mod types; // Re-export core types for convenience pub use types::{ + ActorId, + ApproverId, + EdgeId, + GraphId, + Hash, + LineageId, + NamespaceId, // Identifiers - NodeId, EdgeId, GraphId, ScopeId, NamespaceId, - PolicyBundleId, WitnessId, LineageId, ActorId, ApproverId, + NodeId, + PolicyBundleId, + ScopeId, // Primitives - Timestamp, Hash, Version, + Timestamp, + Version, + WitnessId, }; -pub use error::{ - CoherenceError, SubstrateError, GovernanceError, ExecutionError, StorageError, -}; +pub use error::{CoherenceError, ExecutionError, GovernanceError, StorageError, SubstrateError}; // Re-export security types pub use security::{ - GraphLimits, ResourceLimits, SecurityConfig, - InputValidator, PathValidator, StateValidator, ValidationError, ValidationResult, + GraphLimits, InputValidator, PathValidator, ResourceLimits, SecurityConfig, StateValidator, + ValidationError, ValidationResult, }; pub use events::DomainEvent; // Re-export substrate types pub use substrate::{ - SheafGraph, SheafNode, SheafEdge, RestrictionMap, - SheafSubgraph, NodeMetadata, + NodeMetadata, RestrictionMap, SheafEdge, SheafGraph, SheafNode, SheafSubgraph, }; // Re-export coherence types pub use coherence::{ - CoherenceEngine, CoherenceEnergy, CoherenceConfig, - ResidualCache, EnergyHistory, + CoherenceConfig, CoherenceEnergy, CoherenceEngine, EnergyHistory, ResidualCache, }; // Re-export cohomology types pub use cohomology::{ - // Simplex and simplicial complex - Simplex, SimplexId, SimplicialComplex, Chain, Cochain, - // Sheaf types - Sheaf, Stalk, SheafSection, LocalSection, SheafBuilder, + Activation, + BettiNumbers, + Chain, + Coboundary, + Cochain, // Cocycle and coboundary - Cocycle, CocycleBuilder, Coboundary, + Cocycle, + CocycleBuilder, + CohomologyComputer, + CohomologyConfig, // Cohomology groups - CohomologyGroup, CohomologyComputer, CohomologyConfig, BettiNumbers, - // Laplacian - SheafLaplacian, LaplacianConfig, LaplacianSpectrum, HarmonicRepresentative, + CohomologyGroup, + CohomologyPooling, + DiffusionResult, + HarmonicRepresentative, + LaplacianConfig, + LaplacianSpectrum, + LocalSection, + Obstruction, // Obstruction detection - ObstructionDetector, Obstruction, ObstructionSeverity, ObstructionReport, + ObstructionDetector, + ObstructionIndicator, + ObstructionReport, + ObstructionSeverity, + PoolingMethod, + // Sheaf types + Sheaf, + SheafBuilder, + SheafConvolution, // Diffusion - SheafDiffusion, SheafDiffusionConfig, DiffusionResult, ObstructionIndicator, + SheafDiffusion, + SheafDiffusionConfig, + // Laplacian + SheafLaplacian, + SheafNeuralConfig, // Neural network layers - SheafNeuralLayer, SheafNeuralConfig, SheafConvolution, CohomologyPooling, - PoolingMethod, Activation, + SheafNeuralLayer, + SheafSection, + // Simplex and simplicial complex + Simplex, + SimplexId, + SimplicialComplex, + Stalk, }; // Re-export governance types pub use governance::{ - // Policy types - PolicyBundle, PolicyBundleBuilder, PolicyBundleRef, PolicyBundleStatus, - ThresholdConfig, EscalationRule, ApprovalSignature, ApproverId as GovApproverId, - PolicyError, - // Witness types (governance's own witness format) - WitnessRecord as GovWitnessRecord, WitnessId as GovWitnessId, - WitnessChainError, WitnessError, - // Lineage types - LineageRecord, LineageId as GovLineageId, Operation, EntityRef, LineageError, - // Repository traits - PolicyRepository, WitnessRepository, LineageRepository, - // Common types - Hash as GovHash, Timestamp as GovTimestamp, Version as GovVersion, + ApprovalSignature, + ApproverId as GovApproverId, + EntityRef, + EscalationRule, // Top-level error GovernanceError as GovError, + // Common types + Hash as GovHash, + LineageError, + LineageId as GovLineageId, + // Lineage types + LineageRecord, + LineageRepository, + Operation, + // Policy types + PolicyBundle, + PolicyBundleBuilder, + PolicyBundleRef, + PolicyBundleStatus, + PolicyError, + // Repository traits + PolicyRepository, + ThresholdConfig, + Timestamp as GovTimestamp, + Version as GovVersion, + WitnessChainError, + WitnessError, + WitnessId as GovWitnessId, + // Witness types (governance's own witness format) + WitnessRecord as GovWitnessRecord, + WitnessRepository, }; // Re-export execution types (coherence gate and compute ladder) pub use execution::{ - // Gate and ladder - CoherenceGate, GateDecision, ComputeLane, EnergySnapshot, - LaneThresholds, EscalationReason, // Actions - Action, ActionExecutor, ActionId, ActionImpact, ActionMetadata, ActionResult, - ExecutionContext, ExecutionResult, ExecutorConfig, ExecutorStats, - // Witness (execution's witness format - aliased to avoid conflict with types::WitnessId) - WitnessRecord as ExecWitnessRecord, WitnessId as ExecWitnessId, + Action, + ActionExecutor, + ActionId, + ActionImpact, + ActionMetadata, + ActionResult, + // Gate and ladder + CoherenceGate, + ComputeLane, + EnergySnapshot, + EscalationReason, + ExecutionContext, + ExecutionResult, + ExecutorConfig, + ExecutorStats, + GateDecision, + LaneThresholds, PolicyBundleRef as ExecutionPolicyRef, // Scope ScopeId as ExecutionScopeId, + WitnessId as ExecWitnessId, + // Witness (execution's witness format - aliased to avoid conflict with types::WitnessId) + WitnessRecord as ExecWitnessRecord, }; // Conditional re-exports based on features #[cfg(feature = "tiles")] -pub use tiles::{CoherenceFabric, FabricReport, TileAdapter, ShardMap}; +pub use tiles::{CoherenceFabric, FabricReport, ShardMap, TileAdapter}; #[cfg(feature = "sona")] -pub use sona_tuning::{SonaThresholdTuner, ThresholdAdjustment, ThresholdConfig as SonaThresholdConfig}; +pub use sona_tuning::{ + SonaThresholdTuner, ThresholdAdjustment, ThresholdConfig as SonaThresholdConfig, +}; #[cfg(feature = "neural-gate")] pub use neural_gate::{NeuralCoherenceGate, NeuralDecision, WitnessEncoding}; #[cfg(feature = "learned-rho")] -pub use learned_rho::{LearnedRestrictionMap, TrainingBatch, RestrictionMapConfig}; +pub use learned_rho::{LearnedRestrictionMap, RestrictionMapConfig, TrainingBatch}; #[cfg(feature = "hyperbolic")] pub use hyperbolic::{ - HyperbolicCoherence, HyperbolicCoherenceConfig, HyperbolicAdapter, - DepthComputer, HierarchyLevel, HyperbolicEnergy, WeightedResidual, + DepthComputer, HierarchyLevel, HyperbolicAdapter, HyperbolicCoherence, + HyperbolicCoherenceConfig, HyperbolicEnergy, WeightedResidual, }; #[cfg(feature = "mincut")] pub use mincut::{ - IncoherenceIsolator, MinCutAdapter, MinCutConfig, - IsolationRegion, IsolationResult, IsolationMetrics, + IncoherenceIsolator, IsolationMetrics, IsolationRegion, IsolationResult, MinCutAdapter, + MinCutConfig, }; #[cfg(feature = "attention")] pub use attention::{ - AttentionCoherence, AttentionCoherenceConfig, AttentionAdapter, - TopologyGate, TopologyGateResult, MoEResidualProcessor, ExpertRouting, - DiffusionSmoothing, SmoothedEnergy, WeightedEdgeResidual, AttentionEnergyAnalysis, + AttentionAdapter, AttentionCoherence, AttentionCoherenceConfig, AttentionEnergyAnalysis, + DiffusionSmoothing, ExpertRouting, MoEResidualProcessor, SmoothedEnergy, TopologyGate, + TopologyGateResult, WeightedEdgeResidual, }; #[cfg(feature = "distributed")] pub use distributed::{ - DistributedCoherence, DistributedCoherenceConfig, RaftAdapter, - CoherenceStateMachine, ClusterStatus, CoherenceStatus, NodeRole, + ClusterStatus, CoherenceStateMachine, CoherenceStatus, DistributedCoherence, + DistributedCoherenceConfig, NodeRole, RaftAdapter, }; #[cfg(feature = "ruvllm")] @@ -390,28 +453,46 @@ pub use ruvllm_integration::{ #[cfg(feature = "gpu")] pub use gpu::{ - // Device management - GpuDevice, GpuDeviceInfo, GpuDeviceOptions, - // Buffer management - GpuBuffer, GpuBufferManager, GpuBufferPool, BufferUsage, BufferUsageFlags, BufferKey, + BindingDesc, + BindingType, + BufferKey, + BufferUsage, + BufferUsageFlags, + ComputeEnergyKernel, // Pipeline management - ComputePipeline, PipelineCache, BindingDesc, BindingType, - // Dispatch and synchronization - GpuDispatcher, DispatchConfig, DispatchBuilder, - // GPU coherence engine - GpuCoherenceEngine, GpuConfig, GpuCapabilities, GpuCoherenceEnergy, + ComputePipeline, // Kernel types - ComputeResidualsKernel, ComputeEnergyKernel, SheafAttentionKernel, TokenRoutingKernel, + ComputeResidualsKernel, + DispatchBuilder, + DispatchConfig, + // Buffer management + GpuBuffer, + GpuBufferManager, + GpuBufferPool, + GpuCapabilities, + GpuCoherenceEnergy, + // GPU coherence engine + GpuCoherenceEngine, + GpuConfig, + // Device management + GpuDevice, + GpuDeviceInfo, + GpuDeviceOptions, + // Dispatch and synchronization + GpuDispatcher, // Errors - GpuError, GpuResult, + GpuError, + GpuResult, + PipelineCache, + SheafAttentionKernel, + TokenRoutingKernel, }; #[cfg(feature = "simd")] pub use simd::{ - SimdWidth, SimdContext, best_simd_width, - dot_product_simd, norm_squared_simd, subtract_simd, scale_simd, - matmul_simd, matvec_simd, - batch_residuals_simd, weighted_energy_sum_simd, batch_lane_assignment_simd, + batch_lane_assignment_simd, batch_residuals_simd, best_simd_width, dot_product_simd, + matmul_simd, matvec_simd, norm_squared_simd, scale_simd, subtract_simd, + weighted_energy_sum_simd, SimdContext, SimdWidth, }; // ============================================================================ @@ -421,35 +502,52 @@ pub use simd::{ /// Convenient imports for common use cases pub mod prelude { pub use crate::{ - // Core types - NodeId, EdgeId, GraphId, ScopeId, - Timestamp, Hash, Version, - - // Substrate - SheafGraph, SheafNode, SheafEdge, RestrictionMap, + CoherenceEnergy, // Coherence - CoherenceEngine, CoherenceEnergy, - - // Cohomology - SheafLaplacian, SheafDiffusion, ObstructionDetector, - CohomologyGroup, CohomologyComputer, SheafNeuralLayer, - - // Governance - PolicyBundle, ThresholdConfig, - GovWitnessRecord as WitnessRecord, // Re-export governance witness as default - - // Execution - CoherenceGate, GateDecision, ComputeLane, - - // Security - InputValidator, SecurityConfig, - + CoherenceEngine, // Errors - CoherenceError, ValidationError, + CoherenceError, + // Execution + CoherenceGate, + CohomologyComputer, + CohomologyGroup, + ComputeLane, // Events DomainEvent, + EdgeId, + GateDecision, + GovWitnessRecord as WitnessRecord, // Re-export governance witness as default + + GraphId, + Hash, + // Security + InputValidator, + // Core types + NodeId, + ObstructionDetector, + // Governance + PolicyBundle, + RestrictionMap, + + ScopeId, + SecurityConfig, + + SheafDiffusion, + SheafEdge, + // Substrate + SheafGraph, + // Cohomology + SheafLaplacian, + SheafNeuralLayer, + + SheafNode, + ThresholdConfig, + Timestamp, + ValidationError, + + Version, }; } diff --git a/crates/prime-radiant/src/mincut/adapter.rs b/crates/prime-radiant/src/mincut/adapter.rs index ea8283f03..2478d3c24 100644 --- a/crates/prime-radiant/src/mincut/adapter.rs +++ b/crates/prime-radiant/src/mincut/adapter.rs @@ -154,10 +154,7 @@ impl MinCutAdapter { } /// Compute isolation for high-energy vertices - pub fn compute_isolation( - &self, - high_energy_vertices: &HashSet, - ) -> Result { + pub fn compute_isolation(&self, high_energy_vertices: &HashSet) -> Result { if high_energy_vertices.is_empty() { return Ok(CutResult { isolated_set: HashSet::new(), diff --git a/crates/prime-radiant/src/mincut/isolation.rs b/crates/prime-radiant/src/mincut/isolation.rs index 023593494..7a78e2874 100644 --- a/crates/prime-radiant/src/mincut/isolation.rs +++ b/crates/prime-radiant/src/mincut/isolation.rs @@ -210,8 +210,8 @@ impl IsolationComparison { .copied() .collect(); - let union_size = first.isolated_vertices.len() + second.isolated_vertices.len() - - common.len(); + let union_size = + first.isolated_vertices.len() + second.isolated_vertices.len() - common.len(); let jaccard = if union_size > 0 { common.len() as f64 / union_size as f64 } else { diff --git a/crates/prime-radiant/src/mincut/metrics.rs b/crates/prime-radiant/src/mincut/metrics.rs index 8c03b37e6..1521123eb 100644 --- a/crates/prime-radiant/src/mincut/metrics.rs +++ b/crates/prime-radiant/src/mincut/metrics.rs @@ -196,7 +196,11 @@ impl std::fmt::Display for MetricsSummary { writeln!(f, " Avg query time: {:.2} us", self.avg_query_time_us)?; writeln!(f, " Max isolation size: {}", self.max_isolation_size)?; writeln!(f, " Updates per build: {:.2}", self.updates_per_build)?; - writeln!(f, " Isolation efficiency: {:.4}", self.isolation_efficiency) + writeln!( + f, + " Isolation efficiency: {:.4}", + self.isolation_efficiency + ) } } diff --git a/crates/prime-radiant/src/neural_gate/decision.rs b/crates/prime-radiant/src/neural_gate/decision.rs index f0c13afb8..c745e9392 100644 --- a/crates/prime-radiant/src/neural_gate/decision.rs +++ b/crates/prime-radiant/src/neural_gate/decision.rs @@ -75,10 +75,9 @@ impl DecisionConfidence { supporting_evidence: usize, ) -> Self { // Combine confidences with weighted average - let overall = (energy_confidence * 0.4 - + dendritic_confidence * 0.3 - + oscillator_confidence * 0.3) - .clamp(0.0, 1.0); + let overall = + (energy_confidence * 0.4 + dendritic_confidence * 0.3 + oscillator_confidence * 0.3) + .clamp(0.0, 1.0); Self { overall, diff --git a/crates/prime-radiant/src/neural_gate/encoding.rs b/crates/prime-radiant/src/neural_gate/encoding.rs index e8ee41b68..42baed6af 100644 --- a/crates/prime-radiant/src/neural_gate/encoding.rs +++ b/crates/prime-radiant/src/neural_gate/encoding.rs @@ -70,7 +70,9 @@ impl Hypervector { for i in 0..dim { // Simple LCG for deterministic generation - let mixed = seed.wrapping_mul(6364136223846793005).wrapping_add(i as u64); + let mixed = seed + .wrapping_mul(6364136223846793005) + .wrapping_add(i as u64); let normalized = (mixed as f32 / u64::MAX as f32) * 2.0 - 1.0; components.push(normalized); } @@ -358,13 +360,7 @@ mod tests { #[test] fn test_witness_encoding() { - let enc = WitnessEncoding::new( - "test_witness", - 0.5, - true, - &[1, 2, 3, 4], - 1000, - ); + let enc = WitnessEncoding::new("test_witness", 0.5, true, &[1, 2, 3, 4], 1000); assert_eq!(enc.witness_id, "test_witness"); assert!(enc.allow); diff --git a/crates/prime-radiant/src/neural_gate/gate.rs b/crates/prime-radiant/src/neural_gate/gate.rs index bdcea4817..c8e6982ee 100644 --- a/crates/prime-radiant/src/neural_gate/gate.rs +++ b/crates/prime-radiant/src/neural_gate/gate.rs @@ -52,7 +52,8 @@ impl HysteresisTracker { fn update(&mut self, energy: f32) -> Option { // Apply exponential smoothing - self.smoothed_energy = self.smoothing * self.smoothed_energy + (1.0 - self.smoothing) * energy; + self.smoothed_energy = + self.smoothing * self.smoothed_energy + (1.0 - self.smoothing) * energy; let now = current_time_ms(); let dwell_time = now - self.state_entered_ms; @@ -260,7 +261,8 @@ impl NeuralCoherenceGate { /// Create a new neural coherence gate. pub fn new(config: NeuralGateConfig) -> Self { let hysteresis = HysteresisTracker::new(&config.hysteresis); - let dendrite = DendriticDetector::new(config.coincidence_window_us, config.num_branches / 2); + let dendrite = + DendriticDetector::new(config.coincidence_window_us, config.num_branches / 2); let workspace = GlobalWorkspace::new(&config.workspace); let hdc_memory = HdcMemory::new(config.hdc_dimension, config.memory_capacity); diff --git a/crates/prime-radiant/src/neural_gate/mod.rs b/crates/prime-radiant/src/neural_gate/mod.rs index c0d61fda5..e5a121991 100644 --- a/crates/prime-radiant/src/neural_gate/mod.rs +++ b/crates/prime-radiant/src/neural_gate/mod.rs @@ -49,8 +49,8 @@ mod encoding; mod error; mod gate; -pub use config::{NeuralGateConfig, HysteresisConfig, WorkspaceConfig, OscillatorConfig}; -pub use decision::{NeuralDecision, DecisionConfidence, DecisionTrigger}; -pub use encoding::{WitnessEncoding, HypervectorOps}; +pub use config::{HysteresisConfig, NeuralGateConfig, OscillatorConfig, WorkspaceConfig}; +pub use decision::{DecisionConfidence, DecisionTrigger, NeuralDecision}; +pub use encoding::{HypervectorOps, WitnessEncoding}; pub use error::{NeuralGateError, NeuralGateResult}; -pub use gate::{NeuralCoherenceGate, GateState}; +pub use gate::{GateState, NeuralCoherenceGate}; diff --git a/crates/prime-radiant/src/ruvllm_integration/adapter.rs b/crates/prime-radiant/src/ruvllm_integration/adapter.rs index a286fef64..865ce82e6 100644 --- a/crates/prime-radiant/src/ruvllm_integration/adapter.rs +++ b/crates/prime-radiant/src/ruvllm_integration/adapter.rs @@ -170,21 +170,27 @@ impl RuvLlmAdapter { pub fn record_pass(&self, time_us: u64) { self.stats.requests.fetch_add(1, Ordering::Relaxed); self.stats.passed.fetch_add(1, Ordering::Relaxed); - self.stats.total_time_us.fetch_add(time_us, Ordering::Relaxed); + self.stats + .total_time_us + .fetch_add(time_us, Ordering::Relaxed); } /// Record a failed coherence check. pub fn record_fail(&self, time_us: u64) { self.stats.requests.fetch_add(1, Ordering::Relaxed); self.stats.failed.fetch_add(1, Ordering::Relaxed); - self.stats.total_time_us.fetch_add(time_us, Ordering::Relaxed); + self.stats + .total_time_us + .fetch_add(time_us, Ordering::Relaxed); } /// Record an escalation. pub fn record_escalation(&self, time_us: u64) { self.stats.requests.fetch_add(1, Ordering::Relaxed); self.stats.escalated.fetch_add(1, Ordering::Relaxed); - self.stats.total_time_us.fetch_add(time_us, Ordering::Relaxed); + self.stats + .total_time_us + .fetch_add(time_us, Ordering::Relaxed); } /// Record a cache hit. diff --git a/crates/prime-radiant/src/ruvllm_integration/coherence_validator.rs b/crates/prime-radiant/src/ruvllm_integration/coherence_validator.rs index b189df344..3d6d7e4de 100644 --- a/crates/prime-radiant/src/ruvllm_integration/coherence_validator.rs +++ b/crates/prime-radiant/src/ruvllm_integration/coherence_validator.rs @@ -663,7 +663,10 @@ impl SheafCoherenceValidator { /// 3. Computes coherence energy /// 4. Evaluates against the gate /// 5. Returns a ValidationResult with witness - pub fn validate(&mut self, context: &ValidationContext) -> Result { + pub fn validate( + &mut self, + context: &ValidationContext, + ) -> Result { // Validate the input context.validate()?; @@ -695,7 +698,10 @@ impl SheafCoherenceValidator { } else { WitnessDecision::deny( decision.lane.as_u8(), - decision.reason.clone().unwrap_or_else(|| "Energy too high".to_string()), + decision + .reason + .clone() + .unwrap_or_else(|| "Energy too high".to_string()), confidence, ) }; @@ -717,7 +723,9 @@ impl SheafCoherenceValidator { } else { ValidationResult::deny( energy.total_energy, - decision.reason.unwrap_or_else(|| "Coherence threshold exceeded".to_string()), + decision + .reason + .unwrap_or_else(|| "Coherence threshold exceeded".to_string()), witness, context.request_id, ) @@ -878,7 +886,10 @@ impl PolicyBundleRef { fn into_execution_ref(self) -> crate::execution::PolicyBundleRef { crate::execution::PolicyBundleRef { id: self.id.0, - version: format!("{}.{}.{}", self.version.major, self.version.minor, self.version.patch), + version: format!( + "{}.{}.{}", + self.version.major, self.version.minor, self.version.patch + ), content_hash: *self.content_hash.as_bytes(), } } @@ -915,7 +926,10 @@ mod tests { .with_response_embedding(vec![1.0, 2.0]); let result = ctx.validate(); - assert!(matches!(result, Err(ValidationError::DimensionMismatch { .. }))); + assert!(matches!( + result, + Err(ValidationError::DimensionMismatch { .. }) + )); } #[test] @@ -933,12 +947,7 @@ mod tests { .with_context_embedding(vec![1.0, 2.0, 3.0]) .with_response_embedding(vec![1.0, 2.0, 3.0]); - let witness = ValidationWitness::new( - &ctx, - 0.5, - WitnessDecision::allow(0, 0.9), - None, - ); + let witness = ValidationWitness::new(&ctx, 0.5, WitnessDecision::allow(0, 0.9), None); assert!(witness.verify_integrity()); } @@ -959,11 +968,10 @@ mod tests { #[test] fn test_validator_incoherent_response() { - let mut validator = SheafCoherenceValidator::with_defaults() - .with_config(ValidatorConfig { - reflex_threshold: 0.01, // Very strict - ..Default::default() - }); + let mut validator = SheafCoherenceValidator::with_defaults().with_config(ValidatorConfig { + reflex_threshold: 0.01, // Very strict + ..Default::default() + }); // Very different embeddings should be incoherent let ctx = ValidationContext::new() @@ -997,12 +1005,7 @@ mod tests { .with_context_embedding(vec![1.0, 2.0, 3.0]) .with_response_embedding(vec![1.0, 2.0, 3.0]); - let witness = ValidationWitness::new( - &ctx, - 0.1, - WitnessDecision::allow(0, 0.95), - None, - ); + let witness = ValidationWitness::new(&ctx, 0.1, WitnessDecision::allow(0, 0.95), None); let result = ValidationResult::allow(0.1, witness, ctx.request_id); diff --git a/crates/prime-radiant/src/ruvllm_integration/confidence.rs b/crates/prime-radiant/src/ruvllm_integration/confidence.rs index cbe93157b..733b3f68a 100644 --- a/crates/prime-radiant/src/ruvllm_integration/confidence.rs +++ b/crates/prime-radiant/src/ruvllm_integration/confidence.rs @@ -554,11 +554,17 @@ mod tests { // Energy much below threshold should give high confidence let conf = mapper.confidence_from_energy(0.1); - assert!(conf > 0.7, "Low energy should give high confidence, got {conf}"); + assert!( + conf > 0.7, + "Low energy should give high confidence, got {conf}" + ); // Zero energy should give ~1.0 confidence let conf = mapper.confidence_from_energy(0.0); - assert!(conf > 0.9, "Zero energy should give very high confidence, got {conf}"); + assert!( + conf > 0.9, + "Zero energy should give very high confidence, got {conf}" + ); } #[test] @@ -567,11 +573,17 @@ mod tests { // Energy above threshold should give low confidence let conf = mapper.confidence_from_energy(3.0); - assert!(conf < 0.3, "High energy should give low confidence, got {conf}"); + assert!( + conf < 0.3, + "High energy should give low confidence, got {conf}" + ); // Very high energy should give ~0 confidence let conf = mapper.confidence_from_energy(10.0); - assert!(conf < 0.01, "Very high energy should give near-zero confidence, got {conf}"); + assert!( + conf < 0.01, + "Very high energy should give near-zero confidence, got {conf}" + ); } #[test] @@ -580,7 +592,10 @@ mod tests { // Confidence should decrease as energy increases let energies = [0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 5.0]; - let confidences: Vec = energies.iter().map(|&e| mapper.confidence_from_energy(e)).collect(); + let confidences: Vec = energies + .iter() + .map(|&e| mapper.confidence_from_energy(e)) + .collect(); for i in 1..confidences.len() { assert!( @@ -635,7 +650,10 @@ mod tests { let score = mapper.compute_confidence(&energy); assert!(score.value > 0.5, "Low energy should give >0.5 confidence"); - assert!(score.witness_backed, "Should be witness-backed with edge data"); + assert!( + score.witness_backed, + "Should be witness-backed with edge data" + ); assert_eq!(score.edge_count, 3); assert!(!score.explanation.is_empty()); } @@ -677,11 +695,26 @@ mod tests { #[test] fn test_confidence_score_levels() { - assert_eq!(ConfidenceScore::from_value(0.95).level(), ConfidenceLevel::VeryHigh); - assert_eq!(ConfidenceScore::from_value(0.75).level(), ConfidenceLevel::High); - assert_eq!(ConfidenceScore::from_value(0.55).level(), ConfidenceLevel::Moderate); - assert_eq!(ConfidenceScore::from_value(0.35).level(), ConfidenceLevel::Low); - assert_eq!(ConfidenceScore::from_value(0.15).level(), ConfidenceLevel::VeryLow); + assert_eq!( + ConfidenceScore::from_value(0.95).level(), + ConfidenceLevel::VeryHigh + ); + assert_eq!( + ConfidenceScore::from_value(0.75).level(), + ConfidenceLevel::High + ); + assert_eq!( + ConfidenceScore::from_value(0.55).level(), + ConfidenceLevel::Moderate + ); + assert_eq!( + ConfidenceScore::from_value(0.35).level(), + ConfidenceLevel::Low + ); + assert_eq!( + ConfidenceScore::from_value(0.15).level(), + ConfidenceLevel::VeryLow + ); } #[test] @@ -717,13 +750,25 @@ mod tests { // Very large energy should not cause overflow let conf = mapper.confidence_from_energy(1000.0); - assert!(conf >= 0.0 && conf <= 1.0, "Large energy gave invalid confidence: {conf}"); - assert!(conf < 0.001, "Large energy should give near-zero confidence"); + assert!( + conf >= 0.0 && conf <= 1.0, + "Large energy gave invalid confidence: {conf}" + ); + assert!( + conf < 0.001, + "Large energy should give near-zero confidence" + ); // Negative energy (shouldn't happen, but test stability) let conf = mapper.confidence_from_energy(-100.0); - assert!(conf >= 0.0 && conf <= 1.0, "Negative energy gave invalid confidence: {conf}"); - assert!(conf > 0.999, "Negative energy should give near-one confidence"); + assert!( + conf >= 0.0 && conf <= 1.0, + "Negative energy gave invalid confidence: {conf}" + ); + assert!( + conf > 0.999, + "Negative energy should give near-one confidence" + ); } #[test] diff --git a/crates/prime-radiant/src/ruvllm_integration/gate.rs b/crates/prime-radiant/src/ruvllm_integration/gate.rs index 4b4d0cb16..05c06f918 100644 --- a/crates/prime-radiant/src/ruvllm_integration/gate.rs +++ b/crates/prime-radiant/src/ruvllm_integration/gate.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use std::sync::Arc; use std::time::Instant; -use crate::coherence::{CoherenceEngine, CoherenceEnergy}; +use crate::coherence::{CoherenceEnergy, CoherenceEngine}; use crate::execution::ComputeLane; use crate::governance::PolicyBundle; @@ -284,8 +284,18 @@ impl LlmCoherenceGate { .map(|(a, b)| a * b) .sum(); - let mag_a: f32 = response.context_embedding.iter().map(|x| x * x).sum::().sqrt(); - let mag_b: f32 = response.response_embedding.iter().map(|x| x * x).sum::().sqrt(); + let mag_a: f32 = response + .context_embedding + .iter() + .map(|x| x * x) + .sum::() + .sqrt(); + let mag_b: f32 = response + .response_embedding + .iter() + .map(|x| x * x) + .sum::() + .sqrt(); if mag_a == 0.0 || mag_b == 0.0 { return 1.0; @@ -309,14 +319,18 @@ impl LlmCoherenceGate { /// Estimate hallucination probability. fn estimate_hallucination_prob(&self, analysis: &CoherenceAnalysis) -> f64 { // Combine scores to estimate hallucination probability - let combined = (analysis.semantic_score + analysis.factual_score + analysis.citation_score) / 3.0; + let combined = + (analysis.semantic_score + analysis.factual_score + analysis.citation_score) / 3.0; // Higher combined score = lower hallucination probability (1.0 - combined) * self.config.hallucination_sensitivity } /// Determine the gate decision based on analysis. - fn determine_decision(&self, analysis: &CoherenceAnalysis) -> (bool, ComputeLane, LlmGateReason) { + fn determine_decision( + &self, + analysis: &CoherenceAnalysis, + ) -> (bool, ComputeLane, LlmGateReason) { // Check for hallucination if analysis.hallucination_prob > self.config.hallucination_sensitivity { return ( diff --git a/crates/prime-radiant/src/ruvllm_integration/memory_layer.rs b/crates/prime-radiant/src/ruvllm_integration/memory_layer.rs index 28127ef2a..c3992e977 100644 --- a/crates/prime-radiant/src/ruvllm_integration/memory_layer.rs +++ b/crates/prime-radiant/src/ruvllm_integration/memory_layer.rs @@ -65,9 +65,9 @@ //! //! - ADR-CE-019: Memory as Nodes +use crate::substrate::edge::{EdgeId, SheafEdge, SheafEdgeBuilder}; use crate::substrate::graph::SheafGraph; use crate::substrate::node::{NodeId, NodeMetadata, SheafNode, SheafNodeBuilder, StateVector}; -use crate::substrate::edge::{EdgeId, SheafEdge, SheafEdgeBuilder}; use crate::substrate::restriction::RestrictionMap; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -267,7 +267,11 @@ impl MemoryEntry { } /// Add metadata to the entry - pub fn with_metadata(mut self, key: impl Into, value: impl Into) -> Self { + pub fn with_metadata( + mut self, + key: impl Into, + value: impl Into, + ) -> Self { self.metadata.insert(key.into(), value.into()); self } @@ -562,16 +566,12 @@ impl MemoryCoherenceLayer { // Store in appropriate memory storage match memory_type { MemoryType::Agentic => { - self.agentic_memories.insert( - entry.key.clone(), - (memory_id, entry.embedding.clone()), - ); + self.agentic_memories + .insert(entry.key.clone(), (memory_id, entry.embedding.clone())); } MemoryType::Working => { - self.working_memories.insert( - entry.key.clone(), - (memory_id, entry.embedding.clone()), - ); + self.working_memories + .insert(entry.key.clone(), (memory_id, entry.embedding.clone())); } MemoryType::Episodic => { self.episodic_memories.push(( @@ -616,7 +616,9 @@ impl MemoryCoherenceLayer { /// Remove a memory entry pub fn remove_memory(&mut self, id: MemoryId) -> Result<()> { - let node_id = self.memory_to_node.remove(&id) + let node_id = self + .memory_to_node + .remove(&id) .ok_or(MemoryCoherenceError::MemoryNotFound(id))?; self.node_to_memory.remove(&node_id); @@ -748,12 +750,9 @@ impl MemoryCoherenceLayer { // Create edges for (other_id, _) in candidates { if let Some(&other_node) = self.memory_to_node.get(&other_id) { - if let Some(edge_id) = self.create_edge( - other_node, - node_id, - MemoryEdgeType::Semantic, - dim, - )? { + if let Some(edge_id) = + self.create_edge(other_node, node_id, MemoryEdgeType::Semantic, dim)? + { edges.push(edge_id); } } @@ -789,8 +788,8 @@ impl MemoryCoherenceLayer { let mut edges = Vec::new(); for pattern_node in pattern_nodes { if let Some(edge_id) = self.create_edge( - pattern_node, // Pattern is source (general) - node_id, // Memory is target (specific) + pattern_node, // Pattern is source (general) + node_id, // Memory is target (specific) MemoryEdgeType::Hierarchical, dim, )? { @@ -893,7 +892,9 @@ impl AgenticMemory for MemoryCoherenceLayer { } fn get_pattern(&self, key: &str) -> Option<&[f32]> { - self.agentic_memories.get(key).map(|(_, emb)| emb.as_slice()) + self.agentic_memories + .get(key) + .map(|(_, emb)| emb.as_slice()) } fn pattern_keys(&self) -> Vec { @@ -922,7 +923,9 @@ impl WorkingMemory for MemoryCoherenceLayer { } fn get_context(&self, key: &str) -> Option<&[f32]> { - self.working_memories.get(key).map(|(_, emb)| emb.as_slice()) + self.working_memories + .get(key) + .map(|(_, emb)| emb.as_slice()) } fn clear(&mut self) { @@ -954,7 +957,9 @@ impl EpisodicMemory for MemoryCoherenceLayer { return None; } let idx = (sequence - 1) as usize; - self.episodic_memories.get(idx).map(|(_, _, emb)| emb.as_slice()) + self.episodic_memories + .get(idx) + .map(|(_, _, emb)| emb.as_slice()) } fn recent_episodes(&self, n: usize) -> Vec<(u64, &[f32])> { @@ -1131,7 +1136,10 @@ mod tests { let id = layer.store_pattern("user_preference", &embedding).unwrap(); assert!(layer.has_pattern("user_preference")); - assert_eq!(layer.get_pattern("user_preference"), Some(embedding.as_slice())); + assert_eq!( + layer.get_pattern("user_preference"), + Some(embedding.as_slice()) + ); let keys = layer.pattern_keys(); assert_eq!(keys.len(), 1); @@ -1222,8 +1230,13 @@ mod tests { #[test] fn test_edge_type_weights() { - assert!(MemoryEdgeType::Temporal.default_weight() > MemoryEdgeType::Semantic.default_weight()); - assert!(MemoryEdgeType::Semantic.default_weight() > MemoryEdgeType::Hierarchical.default_weight()); + assert!( + MemoryEdgeType::Temporal.default_weight() > MemoryEdgeType::Semantic.default_weight() + ); + assert!( + MemoryEdgeType::Semantic.default_weight() + > MemoryEdgeType::Hierarchical.default_weight() + ); } #[test] @@ -1238,6 +1251,9 @@ mod tests { let entry = MemoryEntry::new("test", wrong_dim, MemoryType::Agentic); let result = layer.add_with_coherence(entry); - assert!(matches!(result, Err(MemoryCoherenceError::InvalidDimension { .. }))); + assert!(matches!( + result, + Err(MemoryCoherenceError::InvalidDimension { .. }) + )); } } diff --git a/crates/prime-radiant/src/ruvllm_integration/mod.rs b/crates/prime-radiant/src/ruvllm_integration/mod.rs index e25e4b1e4..2b0d33df5 100644 --- a/crates/prime-radiant/src/ruvllm_integration/mod.rs +++ b/crates/prime-radiant/src/ruvllm_integration/mod.rs @@ -94,94 +94,118 @@ pub mod adr_references { // PUBLIC RE-EXPORTS // ============================================================================ -pub use adapter::{ - RuvLlmAdapter, AdapterConfig as LlmAdapterConfig, AdapterStats, -}; +pub use adapter::{AdapterConfig as LlmAdapterConfig, AdapterStats, RuvLlmAdapter}; pub use bridge::{ - PolicyBridge, PolicyBridgeConfig, PolicySyncResult, - SonaBridge, SonaBridgeConfig, LearningFeedback, + LearningFeedback, PolicyBridge, PolicyBridgeConfig, PolicySyncResult, SonaBridge, + SonaBridgeConfig, }; pub use config::{ - LlmCoherenceConfig, GatingMode, ResponsePolicy, - CoherenceThresholds, HallucinationPolicy, + CoherenceThresholds, GatingMode, HallucinationPolicy, LlmCoherenceConfig, ResponsePolicy, }; -pub use error::{ - RuvLlmIntegrationError, Result, -}; +pub use error::{Result, RuvLlmIntegrationError}; pub use gate::{ - LlmCoherenceGate, LlmGateDecision, LlmGateReason, - ResponseCoherence, CoherenceAnalysis, + CoherenceAnalysis, LlmCoherenceGate, LlmGateDecision, LlmGateReason, ResponseCoherence, }; pub use witness::{ - WitnessAdapter, WitnessAdapterConfig, UnifiedWitnessEntry, - WitnessCorrelation, CorrelationId, + CorrelationId, UnifiedWitnessEntry, WitnessAdapter, WitnessAdapterConfig, WitnessCorrelation, }; pub use witness_log::{ - // Core unified witness log types - UnifiedWitnessLog, GenerationWitness, GenerationWitnessId, + CoherenceWitnessSummary, + GenerationWitness, + GenerationWitnessId, // Witness summaries - InferenceWitnessSummary, CoherenceWitnessSummary, - // Query and statistics - WitnessQuery, UnifiedWitnessStats, + InferenceWitnessSummary, // Errors UnifiedWitnessError, + // Core unified witness log types + UnifiedWitnessLog, + UnifiedWitnessStats, + // Query and statistics + WitnessQuery, }; -pub use confidence::{ - CoherenceConfidence, ConfidenceLevel, ConfidenceScore, EnergyContributor, -}; +pub use confidence::{CoherenceConfidence, ConfidenceLevel, ConfidenceScore, EnergyContributor}; pub use coherence_validator::{ + EdgeWeights, // Core validator - SheafCoherenceValidator, ValidatorConfig, + SheafCoherenceValidator, // Context and weights - ValidationContext, EdgeWeights, + ValidationContext, + ValidationError, // Results - ValidationResult, ValidationError, + ValidationResult, // Witness - ValidationWitness, WitnessDecision, + ValidationWitness, + ValidatorConfig, + WitnessDecision, }; pub use memory_layer::{ - // Core types - MemoryCoherenceLayer, MemoryCoherenceConfig, MemoryCoherenceError, - Result as MemoryResult, - // Memory types - MemoryType, MemoryEdgeType, MemoryEntry, MemoryId, CoherenceResult, // Traits - AgenticMemory, WorkingMemory, EpisodicMemory, + AgenticMemory, + CoherenceResult, + EpisodicMemory, + MemoryCoherenceConfig, + MemoryCoherenceError, + // Core types + MemoryCoherenceLayer, + MemoryEdgeType, + MemoryEntry, + MemoryId, + // Memory types + MemoryType, + Result as MemoryResult, + WorkingMemory, }; // Pattern-to-Restriction Bridge (ADR-CE-018) pub use pattern_bridge::{ - // Bridge core - PatternToRestrictionBridge, BridgeConfig, BridgeStats, ExportResult, - BridgeError, BridgeResult, + BridgeConfig, + BridgeError, + BridgeResult, + BridgeStats, + ExportResult, // Pattern types - PatternData, VerdictData, + PatternData, // Provider trait PatternProvider, + // Bridge core + PatternToRestrictionBridge, + VerdictData, }; // Trait definitions for loose coupling pub use traits::{ + Claim, + ClaimType, // Coherence validation - CoherenceValidatable, Claim, ClaimType, ContextSource, Fact, SemanticRelation, RelationType, - // Unified witness - UnifiedWitnessProvider, GenerationWitnessRef, - // Pattern bridge trait - PatternBridge, RestrictionMapRef, - // Memory coherence (with aliases to avoid conflicts with memory_layer) - MemoryType as TraitMemoryType, MemoryEntry as TraitMemoryEntry, - MemoryCoherenceProvider, MemoryAddResult, + CoherenceValidatable, + ConfidenceResult as TraitConfidenceResult, // Confidence - ConfidenceSource, ConfidenceResult as TraitConfidenceResult, UncertaintySource, + ConfidenceSource, + ContextSource, + Fact, + GenerationWitnessRef, + MemoryAddResult, + MemoryCoherenceProvider, + MemoryEntry as TraitMemoryEntry, + // Memory coherence (with aliases to avoid conflicts with memory_layer) + MemoryType as TraitMemoryType, + // Pattern bridge trait + PatternBridge, + RelationType, + RestrictionMapRef, + SemanticRelation, + UncertaintySource, + // Unified witness + UnifiedWitnessProvider, }; // ============================================================================ diff --git a/crates/prime-radiant/src/ruvllm_integration/pattern_bridge.rs b/crates/prime-radiant/src/ruvllm_integration/pattern_bridge.rs index 70a0f3e40..89fff52da 100644 --- a/crates/prime-radiant/src/ruvllm_integration/pattern_bridge.rs +++ b/crates/prime-radiant/src/ruvllm_integration/pattern_bridge.rs @@ -61,8 +61,8 @@ //! - ADR-CE-018: Pattern-to-Restriction Bridge //! - ADR-014: Coherence Engine Architecture -use std::collections::HashMap; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use thiserror::Error; // Import learned_rho types when feature is enabled @@ -582,7 +582,10 @@ impl PatternToRestrictionBridge { /// This registers the learned restriction maps with the graph so they /// can be used in coherence computations. #[cfg(feature = "learned-rho")] - pub fn export_to_prime_radiant(&mut self, graph: &mut SheafGraph) -> BridgeResult { + pub fn export_to_prime_radiant( + &mut self, + graph: &mut SheafGraph, + ) -> BridgeResult { use crate::substrate::RestrictionMap; let mut exported_maps = Vec::new(); @@ -609,7 +612,10 @@ impl PatternToRestrictionBridge { /// Export learned maps to a SheafGraph (stub when learned-rho disabled). #[cfg(not(feature = "learned-rho"))] - pub fn export_to_prime_radiant(&mut self, graph: &mut SheafGraph) -> BridgeResult { + pub fn export_to_prime_radiant( + &mut self, + graph: &mut SheafGraph, + ) -> BridgeResult { let exported_categories: Vec = self.restriction_maps.keys().cloned().collect(); self.stats.exports += 1; @@ -638,9 +644,10 @@ impl PatternToRestrictionBridge { #[cfg(feature = "learned-rho")] pub fn consolidate(&mut self) -> BridgeResult<()> { for entry in self.restriction_maps.values_mut() { - entry.map.consolidate().map_err(|e| { - BridgeError::TrainingError(format!("consolidation failed: {}", e)) - })?; + entry + .map + .consolidate() + .map_err(|e| BridgeError::TrainingError(format!("consolidation failed: {}", e)))?; } Ok(()) } @@ -688,9 +695,8 @@ impl PatternToRestrictionBridge { ..Default::default() }; - let map = LearnedRestrictionMap::new(rho_config).map_err(|e| { - BridgeError::ConfigError(format!("failed to create map: {}", e)) - })?; + let map = LearnedRestrictionMap::new(rho_config) + .map_err(|e| BridgeError::ConfigError(format!("failed to create map: {}", e)))?; self.restriction_maps.insert( category.to_string(), @@ -754,8 +760,7 @@ impl PatternToRestrictionBridge { // Update rolling average loss let n = self.stats.training_steps as f32; - self.stats.avg_loss = - self.stats.avg_loss * ((n - 1.0) / n) + metrics.loss / n; + self.stats.avg_loss = self.stats.avg_loss * ((n - 1.0) / n) + metrics.loss / n; } } @@ -910,7 +915,10 @@ mod tests { impl PatternProvider for MockPatternProvider { fn get_pattern(&self, pattern_id: &str) -> Option { - self.patterns.iter().find(|p| p.pattern_id == pattern_id).cloned() + self.patterns + .iter() + .find(|p| p.pattern_id == pattern_id) + .cloned() } fn get_patterns_by_category(&self, category: &str) -> Vec { diff --git a/crates/prime-radiant/src/ruvllm_integration/traits.rs b/crates/prime-radiant/src/ruvllm_integration/traits.rs index 1e837fa6e..aabe0c115 100644 --- a/crates/prime-radiant/src/ruvllm_integration/traits.rs +++ b/crates/prime-radiant/src/ruvllm_integration/traits.rs @@ -271,7 +271,10 @@ pub struct MemoryEntry { /// Provider of memory coherence checks. pub trait MemoryCoherenceProvider { /// Add a memory entry with coherence checking. - fn add_with_coherence(&mut self, entry: MemoryEntry) -> RuvllmIntegrationResult; + fn add_with_coherence( + &mut self, + entry: MemoryEntry, + ) -> RuvllmIntegrationResult; /// Check if adding an entry would cause incoherence. fn check_coherence(&self, entry: &MemoryEntry) -> RuvllmIntegrationResult; diff --git a/crates/prime-radiant/src/ruvllm_integration/witness_log.rs b/crates/prime-radiant/src/ruvllm_integration/witness_log.rs index c01704ae5..745fcd899 100644 --- a/crates/prime-radiant/src/ruvllm_integration/witness_log.rs +++ b/crates/prime-radiant/src/ruvllm_integration/witness_log.rs @@ -352,10 +352,7 @@ impl GenerationWitness { } /// Create a genesis witness (first in chain) - pub fn genesis( - inference: InferenceWitnessSummary, - coherence: CoherenceWitnessSummary, - ) -> Self { + pub fn genesis(inference: InferenceWitnessSummary, coherence: CoherenceWitnessSummary) -> Self { Self::new(inference, coherence, None) } @@ -716,15 +713,9 @@ impl UnifiedWitnessLog { // Update indices self.by_id.insert(id, index); - self.by_session - .entry(session_id) - .or_default() - .push(index); + self.by_session.entry(session_id).or_default().push(index); if let Some(corr_id) = correlation_id { - self.by_correlation - .entry(corr_id) - .or_default() - .push(index); + self.by_correlation.entry(corr_id).or_default().push(index); } // Update head diff --git a/crates/prime-radiant/src/security/validation.rs b/crates/prime-radiant/src/security/validation.rs index 0d7488ee6..91a22f02c 100644 --- a/crates/prime-radiant/src/security/validation.rs +++ b/crates/prime-radiant/src/security/validation.rs @@ -159,11 +159,7 @@ impl InputValidator { } /// Validate matrix dimensions - pub fn validate_matrix_dims( - &self, - rows: usize, - cols: usize, - ) -> ValidationResult<()> { + pub fn validate_matrix_dims(&self, rows: usize, cols: usize) -> ValidationResult<()> { let max = self.config.resource_limits.max_matrix_dim; if rows > max { @@ -267,9 +263,7 @@ impl PathValidator { // Check for null bytes if component.contains('\0') { - return Err(ValidationError::InvalidPathChars( - "null byte".to_string(), - )); + return Err(ValidationError::InvalidPathChars("null byte".to_string())); } Ok(()) @@ -290,9 +284,7 @@ impl PathValidator { for component in path.components() { match component { Component::ParentDir => { - return Err(ValidationError::PathTraversal( - path.display().to_string(), - )); + return Err(ValidationError::PathTraversal(path.display().to_string())); } Component::Normal(s) => { if let Some(s_str) = s.to_str() { @@ -300,9 +292,7 @@ impl PathValidator { } } Component::Prefix(_) | Component::RootDir => { - return Err(ValidationError::PathTraversal( - path.display().to_string(), - )); + return Err(ValidationError::PathTraversal(path.display().to_string())); } Component::CurDir => {} } @@ -311,9 +301,7 @@ impl PathValidator { // Final check: resolved path should start with base if let Ok(resolved) = full_path.canonicalize() { if !resolved.starts_with(&base_canonical) { - return Err(ValidationError::PathTraversal( - path.display().to_string(), - )); + return Err(ValidationError::PathTraversal(path.display().to_string())); } } @@ -377,7 +365,12 @@ impl StateValidator { } /// Validate and clamp state values to a range - pub fn validate_and_clamp(&self, state: &[f32], min: f32, max: f32) -> ValidationResult> { + pub fn validate_and_clamp( + &self, + state: &[f32], + min: f32, + max: f32, + ) -> ValidationResult> { if state.is_empty() { return Err(ValidationError::EmptyState); } @@ -399,7 +392,11 @@ impl StateValidator { } // Clamp infinite values to min/max let clamped = if val.is_infinite() { - if val.is_sign_positive() { max } else { min } + if val.is_sign_positive() { + max + } else { + min + } } else { val.clamp(min, max) }; @@ -428,9 +425,8 @@ pub fn is_valid_identifier(s: &str) -> bool { } // Rest can be alphanumeric, dash, underscore, or dot - s.chars().all(|c| { - c.is_ascii_alphanumeric() || c == '-' || c == '_' || c == '.' - }) + s.chars() + .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_' || c == '.') } /// Check if a state vector is valid (no NaN/Infinity) @@ -463,7 +459,9 @@ pub fn sanitize_path_component(component: &str) -> Option { /// Validate a dimension value pub fn validate_dimension(dim: usize, max: usize) -> ValidationResult<()> { if dim == 0 { - return Err(ValidationError::Custom("Dimension cannot be zero".to_string())); + return Err(ValidationError::Custom( + "Dimension cannot be zero".to_string(), + )); } if dim > max { return Err(ValidationError::MatrixDimensionTooLarge { dim, max }); @@ -544,10 +542,22 @@ mod tests { #[test] fn test_sanitize_path() { - assert_eq!(sanitize_path_component("valid_name"), Some("valid_name".to_string())); - assert_eq!(sanitize_path_component("file.txt"), Some("file.txt".to_string())); - assert_eq!(sanitize_path_component("bad/path"), Some("badpath".to_string())); - assert_eq!(sanitize_path_component("bad\\path"), Some("badpath".to_string())); + assert_eq!( + sanitize_path_component("valid_name"), + Some("valid_name".to_string()) + ); + assert_eq!( + sanitize_path_component("file.txt"), + Some("file.txt".to_string()) + ); + assert_eq!( + sanitize_path_component("bad/path"), + Some("badpath".to_string()) + ); + assert_eq!( + sanitize_path_component("bad\\path"), + Some("badpath".to_string()) + ); assert_eq!(sanitize_path_component(""), None); assert_eq!(sanitize_path_component("."), None); diff --git a/crates/prime-radiant/src/signal/mod.rs b/crates/prime-radiant/src/signal/mod.rs index 08da78d72..d52677cab 100644 --- a/crates/prime-radiant/src/signal/mod.rs +++ b/crates/prime-radiant/src/signal/mod.rs @@ -67,10 +67,7 @@ pub enum SignalType { #[derive(Debug, Clone, Serialize, Deserialize)] pub enum NormalizedPayload { /// State update payload. - StateUpdate { - node_id: String, - state: Vec, - }, + StateUpdate { node_id: String, state: Vec }, /// Edge modification payload. EdgeMod { source: String, diff --git a/crates/prime-radiant/src/simd/energy.rs b/crates/prime-radiant/src/simd/energy.rs index f426d206a..71c217ff4 100644 --- a/crates/prime-radiant/src/simd/energy.rs +++ b/crates/prime-radiant/src/simd/energy.rs @@ -300,11 +300,7 @@ pub fn weighted_energy_sum_simd(residual_norms: &[f32], weights: &[f32]) -> f32 /// // lanes = [0, 1, 2, 3] (Reflex, Retrieval, Heavy, Human) /// ``` #[inline] -pub fn batch_lane_assignment_simd( - energies: &[f32], - thresholds: [f32; 4], - lanes: &mut [u8], -) { +pub fn batch_lane_assignment_simd(energies: &[f32], thresholds: [f32; 4], lanes: &mut [u8]) { debug_assert_eq!(energies.len(), lanes.len()); let len = energies.len(); @@ -357,9 +353,7 @@ pub fn batch_lane_assignment_simd( // Handle remainder for (i, &e) in remainder_e.iter().enumerate() { - let lane = (e >= t_reflex) as u8 - + (e >= t_retrieval) as u8 - + (e >= t_heavy) as u8; + let lane = (e >= t_reflex) as u8 + (e >= t_retrieval) as u8 + (e >= t_heavy) as u8; lanes[offset + i] = lane.min(3); } } @@ -505,9 +499,7 @@ fn batch_lane_assignment_scalar(energies: &[f32], thresholds: [f32; 4], lanes: & let t_heavy = thresholds[2]; for (e, l) in energies.iter().zip(lanes.iter_mut()) { - let lane = (*e >= t_reflex) as u8 - + (*e >= t_retrieval) as u8 - + (*e >= t_heavy) as u8; + let lane = (*e >= t_reflex) as u8 + (*e >= t_retrieval) as u8 + (*e >= t_heavy) as u8; *l = lane.min(3); } } @@ -572,7 +564,11 @@ mod tests { batch_residuals_simd(&sources, &targets, &mut residuals_simd, 64, 16); batch_residuals_scalar(&sources, &targets, &mut residuals_scalar); - for (i, (&s, &sc)) in residuals_simd.iter().zip(residuals_scalar.iter()).enumerate() { + for (i, (&s, &sc)) in residuals_simd + .iter() + .zip(residuals_scalar.iter()) + .enumerate() + { assert!(approx_eq(s, sc), "at {} got {} expected {}", i, s, sc); } } @@ -610,7 +606,12 @@ mod tests { let result = weighted_energy_sum_simd(&norms, &weights); let expected = weighted_energy_sum_scalar(&norms, &weights); - assert!(approx_eq(result, expected), "got {} expected {}", result, expected); + assert!( + approx_eq(result, expected), + "got {} expected {}", + result, + expected + ); } #[test] @@ -692,7 +693,11 @@ mod tests { let simd_result = compute_residual_norm_sq_simd(&source, &target); let scalar_result = compute_residual_norm_sq_scalar(&source, &target); - assert!(approx_eq(simd_result, scalar_result), - "simd={} scalar={}", simd_result, scalar_result); + assert!( + approx_eq(simd_result, scalar_result), + "simd={} scalar={}", + simd_result, + scalar_result + ); } } diff --git a/crates/prime-radiant/src/simd/matrix.rs b/crates/prime-radiant/src/simd/matrix.rs index db0ec4520..055515db8 100644 --- a/crates/prime-radiant/src/simd/matrix.rs +++ b/crates/prime-radiant/src/simd/matrix.rs @@ -507,8 +507,12 @@ mod tests { // Verify transpose property for i in 0..m { for j in 0..n { - assert!(approx_eq(a[i * n + j], b[j * m + i]), - "mismatch at ({}, {})", i, j); + assert!( + approx_eq(a[i * n + j], b[j * m + i]), + "mismatch at ({}, {})", + i, + j + ); } } } @@ -564,8 +568,13 @@ mod tests { // Allow slightly more tolerance for larger matrices due to accumulation for i in 0..m * n { - assert!((c_simd[i] - c_scalar[i]).abs() < 0.01, - "mismatch at {}: {} vs {}", i, c_simd[i], c_scalar[i]); + assert!( + (c_simd[i] - c_scalar[i]).abs() < 0.01, + "mismatch at {}: {} vs {}", + i, + c_simd[i], + c_scalar[i] + ); } } } diff --git a/crates/prime-radiant/src/simd/mod.rs b/crates/prime-radiant/src/simd/mod.rs index ec0e7a25f..ef86d6cf8 100644 --- a/crates/prime-radiant/src/simd/mod.rs +++ b/crates/prime-radiant/src/simd/mod.rs @@ -43,17 +43,17 @@ //! let result = vectors::dot_product_simd(&a, &b); //! ``` -pub mod vectors; -pub mod matrix; pub mod energy; +pub mod matrix; +pub mod vectors; // Re-export key types -pub use vectors::{dot_product_simd, norm_squared_simd, subtract_simd, scale_simd}; -pub use matrix::{matmul_simd, matvec_simd}; pub use energy::{ - batch_residuals_simd, weighted_energy_sum_simd, batch_lane_assignment_simd, - batch_residual_norms_simd, + batch_lane_assignment_simd, batch_residual_norms_simd, batch_residuals_simd, + weighted_energy_sum_simd, }; +pub use matrix::{matmul_simd, matvec_simd}; +pub use vectors::{dot_product_simd, norm_squared_simd, scale_simd, subtract_simd}; /// Available SIMD instruction set widths. /// diff --git a/crates/prime-radiant/src/simd/vectors.rs b/crates/prime-radiant/src/simd/vectors.rs index cdeec4050..4f810a4e5 100644 --- a/crates/prime-radiant/src/simd/vectors.rs +++ b/crates/prime-radiant/src/simd/vectors.rs @@ -540,7 +540,12 @@ mod tests { let result = dot_product_simd(&a, &b); let expected = dot_product_scalar(&a, &b); - assert!(approx_eq(result, expected), "got {} expected {}", result, expected); + assert!( + approx_eq(result, expected), + "got {} expected {}", + result, + expected + ); } #[test] @@ -557,7 +562,12 @@ mod tests { let result = norm_squared_simd(&v); let expected = norm_squared_scalar(&v); - assert!(approx_eq(result, expected), "got {} expected {}", result, expected); + assert!( + approx_eq(result, expected), + "got {} expected {}", + result, + expected + ); } #[test] @@ -581,7 +591,13 @@ mod tests { for i in 0..n { let expected = a[i] - b[i]; - assert!(approx_eq(out[i], expected), "at {} got {} expected {}", i, out[i], expected); + assert!( + approx_eq(out[i], expected), + "at {} got {} expected {}", + i, + out[i], + expected + ); } } @@ -605,7 +621,13 @@ mod tests { for i in 0..n { let expected = v[i] * scalar; - assert!(approx_eq(out[i], expected), "at {} got {} expected {}", i, out[i], expected); + assert!( + approx_eq(out[i], expected), + "at {} got {} expected {}", + i, + out[i], + expected + ); } } @@ -626,7 +648,12 @@ mod tests { let result = squared_distance_simd(&a, &b); let expected = squared_distance_scalar(&a, &b); - assert!(approx_eq(result, expected), "got {} expected {}", result, expected); + assert!( + approx_eq(result, expected), + "got {} expected {}", + result, + expected + ); } #[test] diff --git a/crates/prime-radiant/src/sona_tuning/adjustment.rs b/crates/prime-radiant/src/sona_tuning/adjustment.rs index 81892087b..291744729 100644 --- a/crates/prime-radiant/src/sona_tuning/adjustment.rs +++ b/crates/prime-radiant/src/sona_tuning/adjustment.rs @@ -84,10 +84,7 @@ impl ThresholdAdjustment { } /// Create an adjustment for an energy spike. - pub fn for_energy_spike( - current: &ThresholdConfig, - spike_magnitude: f32, - ) -> Self { + pub fn for_energy_spike(current: &ThresholdConfig, spike_magnitude: f32) -> Self { // Tighten thresholds proportionally to spike let factor = 1.0 - (spike_magnitude * 0.5).min(0.4); let new = ThresholdConfig { @@ -100,7 +97,9 @@ impl ThresholdAdjustment { Self::new( current, new, - AdjustmentReason::EnergySpike { magnitude: spike_magnitude }, + AdjustmentReason::EnergySpike { + magnitude: spike_magnitude, + }, 0.8 + spike_magnitude * 0.1, ) } @@ -155,10 +154,7 @@ impl ThresholdDelta { /// Get the total magnitude of change. pub fn total_magnitude(&self) -> f32 { - (self.reflex_delta.powi(2) - + self.retrieval_delta.powi(2) - + self.heavy_delta.powi(2)) - .sqrt() + (self.reflex_delta.powi(2) + self.retrieval_delta.powi(2) + self.heavy_delta.powi(2)).sqrt() } } diff --git a/crates/prime-radiant/src/sona_tuning/config.rs b/crates/prime-radiant/src/sona_tuning/config.rs index e26efeb50..cf8f9e470 100644 --- a/crates/prime-radiant/src/sona_tuning/config.rs +++ b/crates/prime-radiant/src/sona_tuning/config.rs @@ -60,9 +60,9 @@ pub struct ThresholdConfig { impl Default for ThresholdConfig { fn default() -> Self { Self { - reflex: 0.1, // Low energy: proceed without checks - retrieval: 0.3, // Medium energy: fetch evidence - heavy: 0.7, // High energy: deep reasoning + reflex: 0.1, // Low energy: proceed without checks + retrieval: 0.3, // Medium energy: fetch evidence + heavy: 0.7, // High energy: deep reasoning persistence_window_secs: 5, } } diff --git a/crates/prime-radiant/src/sona_tuning/mod.rs b/crates/prime-radiant/src/sona_tuning/mod.rs index 48d722d83..9c45febe6 100644 --- a/crates/prime-radiant/src/sona_tuning/mod.rs +++ b/crates/prime-radiant/src/sona_tuning/mod.rs @@ -44,7 +44,7 @@ mod config; mod error; mod tuner; -pub use adjustment::{ThresholdAdjustment, AdjustmentReason}; -pub use config::{TunerConfig, ThresholdConfig, LearningLoopConfig}; +pub use adjustment::{AdjustmentReason, ThresholdAdjustment}; +pub use config::{LearningLoopConfig, ThresholdConfig, TunerConfig}; pub use error::{SonaTuningError, SonaTuningResult}; -pub use tuner::{SonaThresholdTuner, TunerState, RegimeTracker}; +pub use tuner::{RegimeTracker, SonaThresholdTuner, TunerState}; diff --git a/crates/prime-radiant/src/sona_tuning/tuner.rs b/crates/prime-radiant/src/sona_tuning/tuner.rs index 24bc63f6d..66c824d81 100644 --- a/crates/prime-radiant/src/sona_tuning/tuner.rs +++ b/crates/prime-radiant/src/sona_tuning/tuner.rs @@ -4,8 +4,7 @@ use super::adjustment::{AdjustmentReason, ThresholdAdjustment}; use super::config::{ThresholdConfig, TunerConfig}; use super::error::{SonaTuningError, SonaTuningResult}; use ruvector_sona::{ - EwcConfig, EwcPlusPlus, PatternConfig, ReasoningBank, SonaConfig, SonaEngine, - TrajectoryBuilder, + EwcConfig, EwcPlusPlus, PatternConfig, ReasoningBank, SonaConfig, SonaEngine, TrajectoryBuilder, }; use std::collections::VecDeque; @@ -88,8 +87,8 @@ impl RegimeTracker { let half = self.energy_history.len() / 2; let first_half_avg: f32 = self.energy_history.iter().take(half).sum::() / half as f32; - let second_half_avg: f32 = - self.energy_history.iter().skip(half).sum::() / (self.energy_history.len() - half) as f32; + let second_half_avg: f32 = self.energy_history.iter().skip(half).sum::() + / (self.energy_history.len() - half) as f32; second_half_avg - first_half_avg } @@ -207,7 +206,11 @@ impl SonaThresholdTuner { // Convert energy trace to embedding let mut embedding = vec![0.0; self.config.embedding_dim]; - for (i, &e) in energy_trace.iter().take(self.config.embedding_dim).enumerate() { + for (i, &e) in energy_trace + .iter() + .take(self.config.embedding_dim) + .enumerate() + { embedding[i] = e; } @@ -216,10 +219,8 @@ impl SonaThresholdTuner { // Start regime tracking let regime_id = format!("regime_{}", current_time_ms()); - self.regime_tracker.start_regime( - ®ime_id, - energy_trace.last().copied().unwrap_or(0.0), - ); + self.regime_tracker + .start_regime(®ime_id, energy_trace.last().copied().unwrap_or(0.0)); self.state = TunerState::TrackingRegime; @@ -317,7 +318,11 @@ impl SonaThresholdTuner { pub fn find_similar_regime(&self, current_energy: &[f32]) -> Option { // Convert current energy to query embedding let mut query = vec![0.0; self.config.embedding_dim]; - for (i, &e) in current_energy.iter().take(self.config.embedding_dim).enumerate() { + for (i, &e) in current_energy + .iter() + .take(self.config.embedding_dim) + .enumerate() + { query[i] = e; } diff --git a/crates/prime-radiant/src/storage/file.rs b/crates/prime-radiant/src/storage/file.rs index 9bd11836c..3842ca90a 100644 --- a/crates/prime-radiant/src/storage/file.rs +++ b/crates/prime-radiant/src/storage/file.rs @@ -8,7 +8,7 @@ //! All identifiers used in file paths are sanitized to prevent path traversal attacks. //! Only alphanumeric characters, dashes, underscores, and dots are allowed. -use super::{GraphStorage, GovernanceStorage, StorageConfig, StorageError}; +use super::{GovernanceStorage, GraphStorage, StorageConfig, StorageError}; use parking_lot::{Mutex, RwLock}; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; @@ -105,13 +105,34 @@ pub struct WalEntry { /// WAL operation types #[derive(Debug, Clone, Serialize, Deserialize)] pub enum WalOperation { - StoreNode { node_id: String, state: Vec }, - DeleteNode { node_id: String }, - StoreEdge { source: String, target: String, weight: f32 }, - DeleteEdge { source: String, target: String }, - StorePolicy { policy_id: String, data: Vec }, - StoreWitness { witness_id: String, data: Vec }, - StoreLineage { lineage_id: String, data: Vec }, + StoreNode { + node_id: String, + state: Vec, + }, + DeleteNode { + node_id: String, + }, + StoreEdge { + source: String, + target: String, + weight: f32, + }, + DeleteEdge { + source: String, + target: String, + }, + StorePolicy { + policy_id: String, + data: Vec, + }, + StoreWitness { + witness_id: String, + data: Vec, + }, + StoreLineage { + lineage_id: String, + data: Vec, + }, } impl WalEntry { @@ -168,7 +189,11 @@ impl FileStorage { Self::with_options(root, StorageFormat::Bincode, true) } - pub fn with_options(root: impl AsRef, format: StorageFormat, wal_enabled: bool) -> Result { + pub fn with_options( + root: impl AsRef, + format: StorageFormat, + wal_enabled: bool, + ) -> Result { let root = root.as_ref().to_path_buf(); for dir in ["nodes", "edges", "policies", "witnesses", "lineages", "wal"] { fs::create_dir_all(root.join(dir))?; @@ -203,7 +228,11 @@ impl FileStorage { } pub fn from_config(config: &StorageConfig) -> Result { - Self::with_options(&config.graph_path, StorageFormat::Bincode, config.enable_wal) + Self::with_options( + &config.graph_path, + StorageFormat::Bincode, + config.enable_wal, + ) } fn open_wal_file(&self) -> Result<(), StorageError> { @@ -215,8 +244,14 @@ impl FileStorage { } fn write_wal(&self, operation: WalOperation) -> Result { - if !self.wal_enabled { return Ok(0); } - let seq = { let mut g = self.wal_sequence.lock(); *g += 1; *g }; + if !self.wal_enabled { + return Ok(0); + } + let seq = { + let mut g = self.wal_sequence.lock(); + *g += 1; + *g + }; let entry = WalEntry::new(seq, operation); let bytes = bincode::serde::encode_to_vec(&entry, bincode::config::standard()) .map_err(|e| StorageError::Serialization(e.to_string()))?; @@ -229,7 +264,9 @@ impl FileStorage { } fn commit_wal(&self, _seq: u64) -> Result<(), StorageError> { - if let Some(ref mut wal) = *self.wal_file.lock() { wal.flush()?; } + if let Some(ref mut wal) = *self.wal_file.lock() { + wal.flush()?; + } Ok(()) } @@ -242,17 +279,26 @@ impl FileStorage { let mut reader = BufReader::new(File::open(&path)?); loop { let mut len_bytes = [0u8; 4]; - if reader.read_exact(&mut len_bytes).is_err() { break; } + if reader.read_exact(&mut len_bytes).is_err() { + break; + } let mut buf = vec![0u8; u32::from_le_bytes(len_bytes) as usize]; reader.read_exact(&mut buf)?; - if let Ok((e, _)) = bincode::serde::decode_from_slice::(&buf, bincode::config::standard()) { - if e.verify() && !e.committed { entries.push(e); } + if let Ok((e, _)) = bincode::serde::decode_from_slice::( + &buf, + bincode::config::standard(), + ) { + if e.verify() && !e.committed { + entries.push(e); + } } } } } entries.sort_by_key(|e| e.sequence); - for e in entries { self.apply_wal_operation(&e.operation)?; } + for e in entries { + self.apply_wal_operation(&e.operation)?; + } Ok(()) } @@ -260,23 +306,39 @@ impl FileStorage { match op { WalOperation::StoreNode { node_id, state } => { self.write_node_file(node_id, state)?; - self.node_cache.write().insert(node_id.clone(), state.clone()); + self.node_cache + .write() + .insert(node_id.clone(), state.clone()); } WalOperation::DeleteNode { node_id } => { self.delete_node_file(node_id)?; self.node_cache.write().remove(node_id); } - WalOperation::StoreEdge { source, target, weight } => { + WalOperation::StoreEdge { + source, + target, + weight, + } => { self.write_edge_file(source, target, *weight)?; - self.edge_cache.write().insert((source.clone(), target.clone()), *weight); + self.edge_cache + .write() + .insert((source.clone(), target.clone()), *weight); } WalOperation::DeleteEdge { source, target } => { self.delete_edge_file(source, target)?; - self.edge_cache.write().remove(&(source.clone(), target.clone())); + self.edge_cache + .write() + .remove(&(source.clone(), target.clone())); + } + WalOperation::StorePolicy { policy_id, data } => { + self.write_data_file("policies", policy_id, data)?; + } + WalOperation::StoreWitness { witness_id, data } => { + self.write_data_file("witnesses", witness_id, data)?; + } + WalOperation::StoreLineage { lineage_id, data } => { + self.write_data_file("lineages", lineage_id, data)?; } - WalOperation::StorePolicy { policy_id, data } => { self.write_data_file("policies", policy_id, data)?; } - WalOperation::StoreWitness { witness_id, data } => { self.write_data_file("witnesses", witness_id, data)?; } - WalOperation::StoreLineage { lineage_id, data } => { self.write_data_file("lineages", lineage_id, data)?; } } Ok(()) } @@ -301,10 +363,16 @@ impl FileStorage { let parts: Vec<&str> = stem.splitn(2, '_').collect(); if parts.len() == 2 { if let Ok(weight) = self.read_edge_file(parts[0], parts[1]) { - self.edge_cache.write().insert((parts[0].to_string(), parts[1].to_string()), weight); + self.edge_cache + .write() + .insert((parts[0].to_string(), parts[1].to_string()), weight); let mut adj = self.adjacency_cache.write(); - adj.entry(parts[0].to_string()).or_default().insert(parts[1].to_string()); - adj.entry(parts[1].to_string()).or_default().insert(parts[0].to_string()); + adj.entry(parts[0].to_string()) + .or_default() + .insert(parts[1].to_string()); + adj.entry(parts[1].to_string()) + .or_default() + .insert(parts[0].to_string()); } } } @@ -317,9 +385,11 @@ impl FileStorage { let path = self.node_path(node_id); let mut writer = BufWriter::new(File::create(&path)?); match self.format { - StorageFormat::Json => serde_json::to_writer(&mut writer, state).map_err(|e| StorageError::Serialization(e.to_string()))?, + StorageFormat::Json => serde_json::to_writer(&mut writer, state) + .map_err(|e| StorageError::Serialization(e.to_string()))?, StorageFormat::Bincode => { - let bytes = bincode::serde::encode_to_vec(state, bincode::config::standard()).map_err(|e| StorageError::Serialization(e.to_string()))?; + let bytes = bincode::serde::encode_to_vec(state, bincode::config::standard()) + .map_err(|e| StorageError::Serialization(e.to_string()))?; writer.write_all(&bytes)?; } } @@ -330,11 +400,14 @@ impl FileStorage { fn read_node_file(&self, node_id: &str) -> Result, StorageError> { let mut reader = BufReader::new(File::open(self.node_path(node_id))?); match self.format { - StorageFormat::Json => serde_json::from_reader(reader).map_err(|e| StorageError::Serialization(e.to_string())), + StorageFormat::Json => serde_json::from_reader(reader) + .map_err(|e| StorageError::Serialization(e.to_string())), StorageFormat::Bincode => { let mut bytes = Vec::new(); reader.read_to_end(&mut bytes)?; - let (result, _) = bincode::serde::decode_from_slice(&bytes, bincode::config::standard()).map_err(|e| StorageError::Serialization(e.to_string()))?; + let (result, _) = + bincode::serde::decode_from_slice(&bytes, bincode::config::standard()) + .map_err(|e| StorageError::Serialization(e.to_string()))?; Ok(result) } } @@ -342,13 +415,19 @@ impl FileStorage { fn delete_node_file(&self, node_id: &str) -> Result<(), StorageError> { let path = self.node_path(node_id); - if path.exists() { fs::remove_file(&path)?; } + if path.exists() { + fs::remove_file(&path)?; + } Ok(()) } fn node_path(&self, node_id: &str) -> PathBuf { // Note: Caller must validate node_id first using validate_path_id() - let ext = if self.format == StorageFormat::Json { "json" } else { "bin" }; + let ext = if self.format == StorageFormat::Json { + "json" + } else { + "bin" + }; self.root.join("nodes").join(format!("{}.{}", node_id, ext)) } @@ -361,9 +440,11 @@ impl FileStorage { fn write_edge_file(&self, source: &str, target: &str, weight: f32) -> Result<(), StorageError> { let mut writer = BufWriter::new(File::create(self.edge_path(source, target))?); match self.format { - StorageFormat::Json => serde_json::to_writer(&mut writer, &weight).map_err(|e| StorageError::Serialization(e.to_string()))?, + StorageFormat::Json => serde_json::to_writer(&mut writer, &weight) + .map_err(|e| StorageError::Serialization(e.to_string()))?, StorageFormat::Bincode => { - let bytes = bincode::serde::encode_to_vec(&weight, bincode::config::standard()).map_err(|e| StorageError::Serialization(e.to_string()))?; + let bytes = bincode::serde::encode_to_vec(&weight, bincode::config::standard()) + .map_err(|e| StorageError::Serialization(e.to_string()))?; writer.write_all(&bytes)?; } } @@ -374,11 +455,14 @@ impl FileStorage { fn read_edge_file(&self, source: &str, target: &str) -> Result { let mut reader = BufReader::new(File::open(self.edge_path(source, target))?); match self.format { - StorageFormat::Json => serde_json::from_reader(reader).map_err(|e| StorageError::Serialization(e.to_string())), + StorageFormat::Json => serde_json::from_reader(reader) + .map_err(|e| StorageError::Serialization(e.to_string())), StorageFormat::Bincode => { let mut bytes = Vec::new(); reader.read_to_end(&mut bytes)?; - let (result, _) = bincode::serde::decode_from_slice(&bytes, bincode::config::standard()).map_err(|e| StorageError::Serialization(e.to_string()))?; + let (result, _) = + bincode::serde::decode_from_slice(&bytes, bincode::config::standard()) + .map_err(|e| StorageError::Serialization(e.to_string()))?; Ok(result) } } @@ -386,14 +470,22 @@ impl FileStorage { fn delete_edge_file(&self, source: &str, target: &str) -> Result<(), StorageError> { let path = self.edge_path(source, target); - if path.exists() { fs::remove_file(&path)?; } + if path.exists() { + fs::remove_file(&path)?; + } Ok(()) } fn edge_path(&self, source: &str, target: &str) -> PathBuf { // Note: Caller must validate source and target first using validate_path_id() - let ext = if self.format == StorageFormat::Json { "json" } else { "bin" }; - self.root.join("edges").join(format!("{}_{}.{}", source, target, ext)) + let ext = if self.format == StorageFormat::Json { + "json" + } else { + "bin" + }; + self.root + .join("edges") + .join(format!("{}_{}.{}", source, target, ext)) } /// Validate edge identifiers and return the safe path @@ -426,8 +518,11 @@ impl FileStorage { let mut metadata = self.metadata.write(); metadata.modified_at = chrono::Utc::now().timestamp_millis(); metadata.last_wal_sequence = *self.wal_sequence.lock(); - serde_json::to_writer_pretty(BufWriter::new(File::create(self.root.join("metadata.json"))?), &*metadata) - .map_err(|e| StorageError::Serialization(e.to_string()))?; + serde_json::to_writer_pretty( + BufWriter::new(File::create(self.root.join("metadata.json"))?), + &*metadata, + ) + .map_err(|e| StorageError::Serialization(e.to_string()))?; Ok(()) } @@ -439,7 +534,9 @@ impl FileStorage { Ok(()) } - pub fn compact_wal(&self) -> Result<(), StorageError> { self.save_metadata() } + pub fn compact_wal(&self) -> Result<(), StorageError> { + self.save_metadata() + } #[must_use] pub fn stats(&self) -> StorageStats { @@ -457,11 +554,15 @@ impl FileStorage { } fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 { - if a.len() != b.len() || a.is_empty() { return 0.0; } + if a.len() != b.len() || a.is_empty() { + return 0.0; + } let dot: f32 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum(); let norm_a: f32 = a.iter().map(|x| x * x).sum::().sqrt(); let norm_b: f32 = b.iter().map(|x| x * x).sum::().sqrt(); - if norm_a == 0.0 || norm_b == 0.0 { return 0.0; } + if norm_a == 0.0 || norm_b == 0.0 { + return 0.0; + } dot / (norm_a * norm_b) } } @@ -479,17 +580,27 @@ pub struct StorageStats { } impl Drop for FileStorage { - fn drop(&mut self) { let _ = self.sync(); } + fn drop(&mut self) { + let _ = self.sync(); + } } impl GraphStorage for FileStorage { fn store_node(&self, node_id: &str, state: &[f32]) -> Result<(), StorageError> { // Validate node_id to prevent path traversal validate_path_id(node_id)?; - let seq = self.write_wal(WalOperation::StoreNode { node_id: node_id.to_string(), state: state.to_vec() })?; + let seq = self.write_wal(WalOperation::StoreNode { + node_id: node_id.to_string(), + state: state.to_vec(), + })?; self.write_node_file(node_id, state)?; - self.node_cache.write().insert(node_id.to_string(), state.to_vec()); - { let mut m = self.metadata.write(); m.node_count = self.node_cache.read().len() as u64; } + self.node_cache + .write() + .insert(node_id.to_string(), state.to_vec()); + { + let mut m = self.metadata.write(); + m.node_count = self.node_cache.read().len() as u64; + } self.commit_wal(seq)?; *self.cache_dirty.write() = true; Ok(()) @@ -498,9 +609,16 @@ impl GraphStorage for FileStorage { fn get_node(&self, node_id: &str) -> Result>, StorageError> { // Validate node_id to prevent path traversal validate_path_id(node_id)?; - if let Some(state) = self.node_cache.read().get(node_id) { return Ok(Some(state.clone())); } + if let Some(state) = self.node_cache.read().get(node_id) { + return Ok(Some(state.clone())); + } match self.read_node_file(node_id) { - Ok(state) => { self.node_cache.write().insert(node_id.to_string(), state.clone()); Ok(Some(state)) } + Ok(state) => { + self.node_cache + .write() + .insert(node_id.to_string(), state.clone()); + Ok(Some(state)) + } Err(StorageError::Io(e)) if e.kind() == std::io::ErrorKind::NotFound => Ok(None), Err(e) => Err(e), } @@ -510,11 +628,28 @@ impl GraphStorage for FileStorage { // Validate identifiers to prevent path traversal validate_path_id(source)?; validate_path_id(target)?; - let seq = self.write_wal(WalOperation::StoreEdge { source: source.to_string(), target: target.to_string(), weight })?; + let seq = self.write_wal(WalOperation::StoreEdge { + source: source.to_string(), + target: target.to_string(), + weight, + })?; self.write_edge_file(source, target, weight)?; - self.edge_cache.write().insert((source.to_string(), target.to_string()), weight); - { let mut adj = self.adjacency_cache.write(); adj.entry(source.to_string()).or_default().insert(target.to_string()); adj.entry(target.to_string()).or_default().insert(source.to_string()); } - { let mut m = self.metadata.write(); m.edge_count = self.edge_cache.read().len() as u64; } + self.edge_cache + .write() + .insert((source.to_string(), target.to_string()), weight); + { + let mut adj = self.adjacency_cache.write(); + adj.entry(source.to_string()) + .or_default() + .insert(target.to_string()); + adj.entry(target.to_string()) + .or_default() + .insert(source.to_string()); + } + { + let mut m = self.metadata.write(); + m.edge_count = self.edge_cache.read().len() as u64; + } self.commit_wal(seq)?; *self.cache_dirty.write() = true; Ok(()) @@ -524,20 +659,41 @@ impl GraphStorage for FileStorage { // Validate identifiers to prevent path traversal validate_path_id(source)?; validate_path_id(target)?; - let seq = self.write_wal(WalOperation::DeleteEdge { source: source.to_string(), target: target.to_string() })?; + let seq = self.write_wal(WalOperation::DeleteEdge { + source: source.to_string(), + target: target.to_string(), + })?; self.delete_edge_file(source, target)?; - self.edge_cache.write().remove(&(source.to_string(), target.to_string())); - { let mut adj = self.adjacency_cache.write(); if let Some(n) = adj.get_mut(source) { n.remove(target); } if let Some(n) = adj.get_mut(target) { n.remove(source); } } - { let mut m = self.metadata.write(); m.edge_count = self.edge_cache.read().len() as u64; } + self.edge_cache + .write() + .remove(&(source.to_string(), target.to_string())); + { + let mut adj = self.adjacency_cache.write(); + if let Some(n) = adj.get_mut(source) { + n.remove(target); + } + if let Some(n) = adj.get_mut(target) { + n.remove(source); + } + } + { + let mut m = self.metadata.write(); + m.edge_count = self.edge_cache.read().len() as u64; + } self.commit_wal(seq)?; *self.cache_dirty.write() = true; Ok(()) } fn find_similar(&self, query: &[f32], k: usize) -> Result, StorageError> { - if query.is_empty() { return Ok(Vec::new()); } + if query.is_empty() { + return Ok(Vec::new()); + } let nodes = self.node_cache.read(); - let mut sims: Vec<_> = nodes.iter().map(|(id, s)| (id.clone(), Self::cosine_similarity(query, s))).collect(); + let mut sims: Vec<_> = nodes + .iter() + .map(|(id, s)| (id.clone(), Self::cosine_similarity(query, s))) + .collect(); sims.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); sims.truncate(k); Ok(sims) @@ -547,7 +703,10 @@ impl GraphStorage for FileStorage { impl GovernanceStorage for FileStorage { fn store_policy(&self, bundle: &[u8]) -> Result { let id = Uuid::new_v4().to_string(); - let seq = self.write_wal(WalOperation::StorePolicy { policy_id: id.clone(), data: bundle.to_vec() })?; + let seq = self.write_wal(WalOperation::StorePolicy { + policy_id: id.clone(), + data: bundle.to_vec(), + })?; self.write_data_file("policies", &id, bundle)?; self.commit_wal(seq)?; *self.cache_dirty.write() = true; @@ -564,7 +723,10 @@ impl GovernanceStorage for FileStorage { fn store_witness(&self, witness: &[u8]) -> Result { let id = Uuid::new_v4().to_string(); - let seq = self.write_wal(WalOperation::StoreWitness { witness_id: id.clone(), data: witness.to_vec() })?; + let seq = self.write_wal(WalOperation::StoreWitness { + witness_id: id.clone(), + data: witness.to_vec(), + })?; self.write_data_file("witnesses", &id, witness)?; self.commit_wal(seq)?; *self.cache_dirty.write() = true; @@ -579,7 +741,10 @@ impl GovernanceStorage for FileStorage { let path = entry?.path(); if let Some(stem) = path.file_stem().and_then(|s| s.to_str()) { if let Ok(data) = self.read_data_file("witnesses", stem) { - if data.windows(action_id.len()).any(|w| w == action_id.as_bytes()) { + if data + .windows(action_id.len()) + .any(|w| w == action_id.as_bytes()) + { results.push(data); } } @@ -591,7 +756,10 @@ impl GovernanceStorage for FileStorage { fn store_lineage(&self, lineage: &[u8]) -> Result { let id = Uuid::new_v4().to_string(); - let seq = self.write_wal(WalOperation::StoreLineage { lineage_id: id.clone(), data: lineage.to_vec() })?; + let seq = self.write_wal(WalOperation::StoreLineage { + lineage_id: id.clone(), + data: lineage.to_vec(), + })?; self.write_data_file("lineages", &id, lineage)?; self.commit_wal(seq)?; *self.cache_dirty.write() = true; @@ -626,7 +794,8 @@ mod tests { #[test] fn test_storage_format_json() { let temp_dir = TempDir::new().unwrap(); - let storage = FileStorage::with_options(temp_dir.path(), StorageFormat::Json, false).unwrap(); + let storage = + FileStorage::with_options(temp_dir.path(), StorageFormat::Json, false).unwrap(); storage.store_node("json-node", &[1.0, 2.0]).unwrap(); let state = storage.get_node("json-node").unwrap(); assert_eq!(state.unwrap(), vec![1.0, 2.0]); diff --git a/crates/prime-radiant/src/storage/memory.rs b/crates/prime-radiant/src/storage/memory.rs index 3a9798267..8b7ec3100 100644 --- a/crates/prime-radiant/src/storage/memory.rs +++ b/crates/prime-radiant/src/storage/memory.rs @@ -20,7 +20,7 @@ //! let policy_id = storage.store_policy(b"policy-data")?; //! ``` -use super::{GraphStorage, GovernanceStorage, StorageConfig, StorageError}; +use super::{GovernanceStorage, GraphStorage, StorageConfig, StorageError}; use ordered_float::OrderedFloat; use parking_lot::RwLock; use std::collections::{BTreeMap, HashMap, HashSet}; @@ -231,7 +231,9 @@ impl Default for InMemoryStorage { impl GraphStorage for InMemoryStorage { fn store_node(&self, node_id: &str, state: &[f32]) -> Result<(), StorageError> { - self.nodes.write().insert(node_id.to_string(), state.to_vec()); + self.nodes + .write() + .insert(node_id.to_string(), state.to_vec()); self.log_event( StorageEventType::NodeStored, node_id.to_string(), @@ -243,11 +245,7 @@ impl GraphStorage for InMemoryStorage { fn get_node(&self, node_id: &str) -> Result>, StorageError> { let result = self.nodes.read().get(node_id).cloned(); if result.is_some() { - self.log_event( - StorageEventType::NodeRetrieved, - node_id.to_string(), - None, - ); + self.log_event(StorageEventType::NodeRetrieved, node_id.to_string(), None); } Ok(result) } @@ -464,7 +462,9 @@ impl IndexedInMemoryStorage { bundle: &[u8], ) -> Result { let id = self.base.store_policy(bundle)?; - self.policy_by_name.write().insert(name.to_string(), id.clone()); + self.policy_by_name + .write() + .insert(name.to_string(), id.clone()); Ok(id) } @@ -596,7 +596,9 @@ mod tests { storage.store_node("north", &[0.0, 1.0, 0.0]).unwrap(); storage.store_node("south", &[0.0, -1.0, 0.0]).unwrap(); storage.store_node("east", &[1.0, 0.0, 0.0]).unwrap(); - storage.store_node("northeast", &[0.707, 0.707, 0.0]).unwrap(); + storage + .store_node("northeast", &[0.707, 0.707, 0.0]) + .unwrap(); // Query for vectors similar to north let query = vec![0.0, 1.0, 0.0]; @@ -687,7 +689,9 @@ mod tests { assert_eq!(category_a.len(), 2); // Store and retrieve policy by name - storage.store_policy_with_name("default", b"default policy").unwrap(); + storage + .store_policy_with_name("default", b"default policy") + .unwrap(); let policy = storage.get_policy_by_name("default").unwrap(); assert!(policy.is_some()); diff --git a/crates/prime-radiant/src/storage/mod.rs b/crates/prime-radiant/src/storage/mod.rs index 31c08c94d..bca74036b 100644 --- a/crates/prime-radiant/src/storage/mod.rs +++ b/crates/prime-radiant/src/storage/mod.rs @@ -60,9 +60,7 @@ mod memory; mod postgres; // Re-exports -pub use file::{ - FileStorage, StorageFormat, StorageMetadata, StorageStats, WalEntry, WalOperation, -}; +pub use file::{FileStorage, StorageFormat, StorageMetadata, StorageStats, WalEntry, WalOperation}; pub use memory::{InMemoryStorage, IndexedInMemoryStorage, StorageEvent, StorageEventType}; #[cfg(feature = "postgres")] @@ -465,7 +463,9 @@ impl StorageFactory { /// # Errors /// /// Returns error if storage cannot be created. - pub fn create_graph_storage(config: &StorageConfig) -> Result, StorageError> { + pub fn create_graph_storage( + config: &StorageConfig, + ) -> Result, StorageError> { if config.graph_path.is_empty() { Ok(Box::new(InMemoryStorage::new())) } else { @@ -478,7 +478,9 @@ impl StorageFactory { /// # Errors /// /// Returns error if storage cannot be created. - pub fn create_governance_storage(config: &StorageConfig) -> Result, StorageError> { + pub fn create_governance_storage( + config: &StorageConfig, + ) -> Result, StorageError> { if config.graph_path.is_empty() { Ok(Box::new(InMemoryStorage::new())) } else { diff --git a/crates/prime-radiant/src/storage/postgres.rs b/crates/prime-radiant/src/storage/postgres.rs index ad7f19b80..b2a23abc8 100644 --- a/crates/prime-radiant/src/storage/postgres.rs +++ b/crates/prime-radiant/src/storage/postgres.rs @@ -1063,7 +1063,10 @@ mod tests { storage.migrate().await.unwrap(); // Store node - storage.store_node("test-node", &[1.0, 2.0, 3.0]).await.unwrap(); + storage + .store_node("test-node", &[1.0, 2.0, 3.0]) + .await + .unwrap(); // Get node let state = storage.get_node("test-node").await.unwrap(); diff --git a/crates/prime-radiant/src/substrate/edge.rs b/crates/prime-radiant/src/substrate/edge.rs index 3e7087bef..739b360ae 100644 --- a/crates/prime-radiant/src/substrate/edge.rs +++ b/crates/prime-radiant/src/substrate/edge.rs @@ -64,10 +64,12 @@ impl EdgeScratch { fn prepare(&mut self, dim: usize) { // Resize to exact dimension, reserving more capacity if needed if self.projected_source.capacity() < dim { - self.projected_source.reserve(dim - self.projected_source.len()); + self.projected_source + .reserve(dim - self.projected_source.len()); } if self.projected_target.capacity() < dim { - self.projected_target.reserve(dim - self.projected_target.len()); + self.projected_target + .reserve(dim - self.projected_target.len()); } if self.residual.capacity() < dim { self.residual.reserve(dim - self.residual.len()); @@ -256,8 +258,10 @@ impl SheafEdge { scratch.prepare(dim); // Apply restriction maps into scratch buffers - self.rho_source.apply_into(source_state, &mut scratch.projected_source); - self.rho_target.apply_into(target_state, &mut scratch.projected_target); + self.rho_source + .apply_into(source_state, &mut scratch.projected_source); + self.rho_target + .apply_into(target_state, &mut scratch.projected_target); // Compute residual in-place: r = projected_source - projected_target for i in 0..dim { @@ -787,10 +791,8 @@ mod tests { // Second call with larger dim=5 (buffers should grow) let edge5 = SheafEdge::identity(source, target, 5); - let result5 = edge5.residual_norm_squared_no_alloc( - &[1.0, 2.0, 3.0, 4.0, 5.0], - &[0.0, 0.0, 0.0, 0.0, 0.0], - ); + let result5 = edge5 + .residual_norm_squared_no_alloc(&[1.0, 2.0, 3.0, 4.0, 5.0], &[0.0, 0.0, 0.0, 0.0, 0.0]); assert!((result5 - 55.0).abs() < 1e-10); // 1 + 4 + 9 + 16 + 25 = 55 // Third call back to dim=3 (buffers should shrink length but keep capacity) diff --git a/crates/prime-radiant/src/substrate/restriction.rs b/crates/prime-radiant/src/substrate/restriction.rs index 7cd30f88a..8791d29fd 100644 --- a/crates/prime-radiant/src/substrate/restriction.rs +++ b/crates/prime-radiant/src/substrate/restriction.rs @@ -649,10 +649,7 @@ impl RestrictionMap { } MatrixStorage::Sparse { - rows, - cols, - values, - .. + rows, cols, values, .. } => { output.fill(0.0); for ((&r, &c), &v) in rows.iter().zip(cols.iter()).zip(values.iter()) { @@ -1038,11 +1035,7 @@ mod tests { // Create a simple 2x3 matrix: // [ 1 0 2 ] // [ 0 3 0 ] - let csr = CsrMatrix::from_coo( - 2, - 3, - vec![(0, 0, 1.0), (0, 2, 2.0), (1, 1, 3.0)], - ); + let csr = CsrMatrix::from_coo(2, 3, vec![(0, 0, 1.0), (0, 2, 2.0), (1, 1, 3.0)]); assert_eq!(csr.rows, 2); assert_eq!(csr.cols, 3); @@ -1057,11 +1050,7 @@ mod tests { // Create a 2x3 matrix: // [ 1 0 2 ] // [ 0 3 0 ] - let csr = CsrMatrix::from_coo( - 2, - 3, - vec![(0, 0, 1.0), (0, 2, 2.0), (1, 1, 3.0)], - ); + let csr = CsrMatrix::from_coo(2, 3, vec![(0, 0, 1.0), (0, 2, 2.0), (1, 1, 3.0)]); let input = vec![1.0, 2.0, 3.0]; let output = csr.matvec(&input); @@ -1073,11 +1062,7 @@ mod tests { #[test] fn test_csr_matvec_into() { - let csr = CsrMatrix::from_coo( - 2, - 3, - vec![(0, 0, 1.0), (0, 2, 2.0), (1, 1, 3.0)], - ); + let csr = CsrMatrix::from_coo(2, 3, vec![(0, 0, 1.0), (0, 2, 2.0), (1, 1, 3.0)]); let input = vec![1.0, 2.0, 3.0]; let mut output = vec![0.0; 2]; @@ -1190,11 +1175,7 @@ mod tests { #[test] fn test_csr_matvec_add_into() { - let csr = CsrMatrix::from_coo( - 2, - 3, - vec![(0, 0, 1.0), (0, 2, 2.0), (1, 1, 3.0)], - ); + let csr = CsrMatrix::from_coo(2, 3, vec![(0, 0, 1.0), (0, 2, 2.0), (1, 1, 3.0)]); let input = vec![1.0, 2.0, 3.0]; let mut output = vec![1.0, 1.0]; // Pre-existing values diff --git a/crates/prime-radiant/src/tiles/adapter.rs b/crates/prime-radiant/src/tiles/adapter.rs index 9ad0fd927..1858db2de 100644 --- a/crates/prime-radiant/src/tiles/adapter.rs +++ b/crates/prime-radiant/src/tiles/adapter.rs @@ -171,10 +171,7 @@ impl TileAdapter { } if !self.tile.ingest_delta(delta) { - return Err(TilesError::buffer_full( - self.tile.tile_id, - MAX_DELTA_BUFFER, - )); + return Err(TilesError::buffer_full(self.tile.tile_id, MAX_DELTA_BUFFER)); } self.total_deltas += 1; diff --git a/crates/prime-radiant/src/tiles/coordinator.rs b/crates/prime-radiant/src/tiles/coordinator.rs index 4099bab34..01e0c9395 100644 --- a/crates/prime-radiant/src/tiles/coordinator.rs +++ b/crates/prime-radiant/src/tiles/coordinator.rs @@ -42,7 +42,10 @@ pub struct ShardMap { impl ShardMap { /// Create a new shard map. pub fn new(hash_seed: u64, num_shards: u16) -> Self { - Self { hash_seed, num_shards } + Self { + hash_seed, + num_shards, + } } /// Create with default configuration. @@ -168,10 +171,7 @@ impl TileCoordinator { /// /// This combines the witness fragments into a global witness that represents /// the coherence state across all tiles. - pub fn aggregate_witnesses( - &mut self, - tiles: &[TileAdapter], - ) -> TilesResult { + pub fn aggregate_witnesses(&mut self, tiles: &[TileAdapter]) -> TilesResult { if tiles.is_empty() { return Ok(AggregatedWitness::empty()); } @@ -338,7 +338,10 @@ mod tests { // Check reasonable distribution (each tile should have some nodes) let non_empty = tile_counts.iter().filter(|&&c| c > 0).count(); - assert!(non_empty > 200, "Distribution too sparse: {non_empty} tiles used"); + assert!( + non_empty > 200, + "Distribution too sparse: {non_empty} tiles used" + ); } #[test] diff --git a/crates/prime-radiant/src/tiles/fabric.rs b/crates/prime-radiant/src/tiles/fabric.rs index e8ea90e9c..12b87d124 100644 --- a/crates/prime-radiant/src/tiles/fabric.rs +++ b/crates/prime-radiant/src/tiles/fabric.rs @@ -182,7 +182,11 @@ impl CoherenceFabric { } /// Distribute an edge removal. - pub fn distribute_edge_remove(&mut self, source_node: u64, target_node: u64) -> TilesResult<()> { + pub fn distribute_edge_remove( + &mut self, + source_node: u64, + target_node: u64, + ) -> TilesResult<()> { let tile_id = self.coordinator.tile_for_node(source_node); let tile = self .tiles diff --git a/crates/prime-radiant/src/tiles/mod.rs b/crates/prime-radiant/src/tiles/mod.rs index 773ed874f..9c68aefc2 100644 --- a/crates/prime-radiant/src/tiles/mod.rs +++ b/crates/prime-radiant/src/tiles/mod.rs @@ -40,6 +40,6 @@ mod error; mod fabric; pub use adapter::{TileAdapter, TileAdapterConfig}; -pub use coordinator::{TileCoordinator, CoordinatorConfig, ShardMap, AggregatedWitness}; +pub use coordinator::{AggregatedWitness, CoordinatorConfig, ShardMap, TileCoordinator}; pub use error::{TilesError, TilesResult}; pub use fabric::{CoherenceFabric, FabricConfig, FabricReport, FabricState}; diff --git a/crates/prime-radiant/src/types.rs b/crates/prime-radiant/src/types.rs index 7a9fd1885..096dc0136 100644 --- a/crates/prime-radiant/src/types.rs +++ b/crates/prime-radiant/src/types.rs @@ -528,7 +528,11 @@ pub struct Version { impl Version { /// Create a new version pub const fn new(major: u32, minor: u32, patch: u32) -> Self { - Self { major, minor, patch } + Self { + major, + minor, + patch, + } } /// Initial version (0.1.0) @@ -573,9 +577,15 @@ impl std::str::FromStr for Version { return Err(format!("Invalid version format: {}", s)); } - let major = parts[0].parse().map_err(|e| format!("Invalid major: {}", e))?; - let minor = parts[1].parse().map_err(|e| format!("Invalid minor: {}", e))?; - let patch = parts[2].parse().map_err(|e| format!("Invalid patch: {}", e))?; + let major = parts[0] + .parse() + .map_err(|e| format!("Invalid major: {}", e))?; + let minor = parts[1] + .parse() + .map_err(|e| format!("Invalid minor: {}", e))?; + let patch = parts[2] + .parse() + .map_err(|e| format!("Invalid patch: {}", e))?; Ok(Self::new(major, minor, patch)) } diff --git a/crates/prime-radiant/tests/chaos_tests.rs b/crates/prime-radiant/tests/chaos_tests.rs index 7d2c8666c..35f76be71 100644 --- a/crates/prime-radiant/tests/chaos_tests.rs +++ b/crates/prime-radiant/tests/chaos_tests.rs @@ -50,11 +50,13 @@ impl ThrottledGate { self.allowed_count += 1; Decision::Allow } else if energy < self.amber_threshold { - let throttle_factor = (energy - self.green_threshold) - / (self.amber_threshold - self.green_threshold); + let throttle_factor = + (energy - self.green_threshold) / (self.amber_threshold - self.green_threshold); self.current_throttle = (self.current_throttle + throttle_factor * 0.1).min(1.0); self.throttled_count += 1; - Decision::Throttle { factor: throttle_factor } + Decision::Throttle { + factor: throttle_factor, + } } else { self.current_throttle = 1.0; self.blocked_count += 1; @@ -141,7 +143,8 @@ impl ChaosState { for ((src, tgt), weight) in &self.edges { if let (Some(s), Some(t)) = (self.nodes.get(src), self.nodes.get(tgt)) { let dim = s.len().min(t.len()); - let residual: f32 = s.iter() + let residual: f32 = s + .iter() .take(dim) .zip(t.iter().take(dim)) .map(|(a, b)| (a - b).powi(2)) @@ -193,7 +196,10 @@ fn test_random_energy_spikes() { // With 10% spike rate and spikes going up to 2.0 (well above amber threshold), // we expect a mix of decisions assert!(stats.blocked > 0, "Should have blocked some spikes"); - assert!(stats.allowed > 0, "Should have allowed low-energy operations"); + assert!( + stats.allowed > 0, + "Should have allowed low-energy operations" + ); // Allow rate depends on threshold settings - with spikes going to amber/red zone, // we expect at least some operations to be allowed (the 90% non-spike operations) assert!( @@ -214,7 +220,10 @@ fn test_sustained_spike_triggers_persistent_block() { gate.decide(energy); } - assert!(gate.current_throttle < 0.1, "Should have low throttle initially"); + assert!( + gate.current_throttle < 0.1, + "Should have low throttle initially" + ); // Sustained high energy for _ in 0..20 { @@ -548,7 +557,10 @@ fn test_recovery_from_blocked_state() { recovery_steps < 200, "Should recover within reasonable time" ); - assert!(gate.current_throttle < 0.2, "Should have low throttle after recovery"); + assert!( + gate.current_throttle < 0.2, + "Should have low throttle after recovery" + ); } #[test] @@ -570,8 +582,14 @@ fn test_oscillation_dampening() { } // Throttle should not oscillate wildly - let max = throttle_variance.iter().cloned().fold(f32::NEG_INFINITY, f32::max); - let min = throttle_variance.iter().cloned().fold(f32::INFINITY, f32::min); + let max = throttle_variance + .iter() + .cloned() + .fold(f32::NEG_INFINITY, f32::max); + let min = throttle_variance + .iter() + .cloned() + .fold(f32::INFINITY, f32::min); // Should settle to some stable-ish range // (This is a soft check - exact behavior depends on parameters) @@ -667,7 +685,11 @@ fn test_memory_stability() { // Energy check let energy = state.compute_energy(); - assert!(energy.is_finite(), "Energy should be finite at cycle {}", cycle); + assert!( + energy.is_finite(), + "Energy should be finite at cycle {}", + cycle + ); } assert!(state.nodes.len() > 0); @@ -722,11 +744,21 @@ fn test_seeded_chaos_reproducible() { assert!( (result1.0 - result2.0).abs() < 0.01, "Same seed should produce same energy: {} vs {}", - result1.0, result2.0 + result1.0, + result2.0 + ); + assert_eq!( + result1.1, result2.1, + "Same seed should produce same allowed count" + ); + assert_eq!( + result1.2, result2.2, + "Same seed should produce same throttled count" + ); + assert_eq!( + result1.3, result2.3, + "Same seed should produce same blocked count" ); - assert_eq!(result1.1, result2.1, "Same seed should produce same allowed count"); - assert_eq!(result1.2, result2.2, "Same seed should produce same throttled count"); - assert_eq!(result1.3, result2.3, "Same seed should produce same blocked count"); // Use very different seeds to ensure different random sequences let result3 = run_chaos(99999); @@ -734,6 +766,7 @@ fn test_seeded_chaos_reproducible() { assert!( (result1.0 - result3.0).abs() > 0.001 || result1.1 != result3.1 || result1.2 != result3.2, "Different seeds should produce different results: seed1={:?}, seed2={:?}", - result1, result3 + result1, + result3 ); } diff --git a/crates/prime-radiant/tests/gpu_coherence_tests.rs b/crates/prime-radiant/tests/gpu_coherence_tests.rs index 3948ea799..1bef620da 100644 --- a/crates/prime-radiant/tests/gpu_coherence_tests.rs +++ b/crates/prime-radiant/tests/gpu_coherence_tests.rs @@ -13,12 +13,11 @@ #![cfg(feature = "gpu")] use prime_radiant::gpu::{ - GpuCoherenceEngine, GpuConfig, GpuBuffer, GpuParams, GpuEdge, GpuRestrictionMap, - BufferUsage, GpuBufferManager, GpuResult, GpuError, + BufferUsage, GpuBuffer, GpuBufferManager, GpuCoherenceEngine, GpuConfig, GpuEdge, GpuError, + GpuParams, GpuRestrictionMap, GpuResult, }; use prime_radiant::substrate::{ - SheafGraph, SheafNode, SheafEdge, SheafNodeBuilder, SheafEdgeBuilder, - NodeId, EdgeId, + EdgeId, NodeId, SheafEdge, SheafEdgeBuilder, SheafGraph, SheafNode, SheafNodeBuilder, }; use std::collections::HashMap; use uuid::Uuid; @@ -79,12 +78,8 @@ fn create_coherent_graph() -> SheafGraph { // All nodes have the same state let state = [1.0, 1.0, 1.0]; - let node1 = SheafNodeBuilder::new() - .state_from_slice(&state) - .build(); - let node2 = SheafNodeBuilder::new() - .state_from_slice(&state) - .build(); + let node1 = SheafNodeBuilder::new().state_from_slice(&state).build(); + let node2 = SheafNodeBuilder::new().state_from_slice(&state).build(); let id1 = graph.add_node(node1); let id2 = graph.add_node(node2); @@ -110,9 +105,7 @@ fn create_large_graph(num_nodes: usize, edges_per_node: usize) -> SheafGraph { .map(|j| ((i * state_dim + j) as f32 * 0.01).sin()) .collect(); - let node = SheafNodeBuilder::new() - .state_from_slice(&state) - .build(); + let node = SheafNodeBuilder::new().state_from_slice(&state).build(); node_ids.push(graph.add_node(node)); } @@ -480,13 +473,17 @@ async fn test_gpu_performance_1k_nodes() { let cpu_energy = graph.compute_energy(); let cpu_time = start.elapsed(); + println!("Performance test ({} edges):", edge_count); println!( - "Performance test ({} edges):", - edge_count + " GPU: {}us ({} edges/ms)", + energy.compute_time_us, + edge_count as u64 * 1000 / energy.compute_time_us.max(1) ); - println!(" GPU: {}us ({} edges/ms)", energy.compute_time_us, edge_count as u64 * 1000 / energy.compute_time_us.max(1)); println!(" CPU: {}us", cpu_time.as_micros()); - println!(" Speedup: {:.2}x", cpu_time.as_micros() as f64 / gpu_time.as_micros() as f64); + println!( + " Speedup: {:.2}x", + cpu_time.as_micros() as f64 / gpu_time.as_micros() as f64 + ); // Verify correctness let diff = (cpu_energy.total_energy - energy.total_energy).abs(); diff --git a/crates/prime-radiant/tests/integration/coherence_tests.rs b/crates/prime-radiant/tests/integration/coherence_tests.rs index d9b022787..54da18768 100644 --- a/crates/prime-radiant/tests/integration/coherence_tests.rs +++ b/crates/prime-radiant/tests/integration/coherence_tests.rs @@ -23,9 +23,7 @@ impl RestrictionMap { fn new(rows: usize, cols: usize) -> Self { // Identity-like (truncated or padded) let matrix: Vec> = (0..rows) - .map(|i| { - (0..cols).map(|j| if i == j { 1.0 } else { 0.0 }).collect() - }) + .map(|i| (0..cols).map(|j| if i == j { 1.0 } else { 0.0 }).collect()) .collect(); let bias = vec![0.0; rows]; Self { matrix, bias } @@ -35,13 +33,7 @@ impl RestrictionMap { self.matrix .iter() .zip(&self.bias) - .map(|(row, b)| { - row.iter() - .zip(input) - .map(|(a, x)| a * x) - .sum::() - + b - }) + .map(|(row, b)| row.iter().zip(input).map(|(a, x)| a * x).sum::() + b) .collect() } @@ -469,10 +461,7 @@ fn test_residual_dimension() { #[test] fn test_hotspot_identification() { // Find edges with highest energy - fn find_hotspots( - edge_energies: &HashMap, - k: usize, - ) -> Vec<(usize, f32)> { + fn find_hotspots(edge_energies: &HashMap, k: usize) -> Vec<(usize, f32)> { let mut sorted: Vec<_> = edge_energies.iter().collect(); sorted.sort_by(|a, b| b.1.partial_cmp(a.1).unwrap()); sorted.into_iter().take(k).map(|(i, e)| (*i, *e)).collect() diff --git a/crates/prime-radiant/tests/integration/gate_tests.rs b/crates/prime-radiant/tests/integration/gate_tests.rs index 7af95b3f9..d9da979d9 100644 --- a/crates/prime-radiant/tests/integration/gate_tests.rs +++ b/crates/prime-radiant/tests/integration/gate_tests.rs @@ -125,7 +125,9 @@ impl CoherenceGate { let next_lane = match self.current_lane { ComputeLane::Local => Some(ComputeLane::Neighborhood { k: 2 }), - ComputeLane::Neighborhood { k } if k < 5 => Some(ComputeLane::Neighborhood { k: k + 1 }), + ComputeLane::Neighborhood { k } if k < 5 => { + Some(ComputeLane::Neighborhood { k: k + 1 }) + } ComputeLane::Neighborhood { .. } => Some(ComputeLane::Global), ComputeLane::Global => Some(ComputeLane::Spectral), ComputeLane::Spectral => None, // Already at max @@ -145,7 +147,9 @@ impl CoherenceGate { fn deescalate(&mut self) -> Option { let prev_lane = match self.current_lane { ComputeLane::Local => None, - ComputeLane::Neighborhood { k } if k > 2 => Some(ComputeLane::Neighborhood { k: k - 1 }), + ComputeLane::Neighborhood { k } if k > 2 => { + Some(ComputeLane::Neighborhood { k: k - 1 }) + } ComputeLane::Neighborhood { .. } => Some(ComputeLane::Local), ComputeLane::Global => Some(ComputeLane::Neighborhood { k: 5 }), ComputeLane::Spectral => Some(ComputeLane::Global), @@ -228,7 +232,10 @@ fn test_throttle_factor_increases_with_energy() { match (decision_low, decision_high) { (GateDecision::Throttle { factor: f1 }, GateDecision::Throttle { factor: f2 }) => { - assert!(f2 > f1, "Higher energy should produce higher throttle factor"); + assert!( + f2 > f1, + "Higher energy should produce higher throttle factor" + ); } _ => panic!("Expected both to be Throttle decisions"), } diff --git a/crates/prime-radiant/tests/integration/governance_tests.rs b/crates/prime-radiant/tests/integration/governance_tests.rs index 6a5fb781c..1a320cc3f 100644 --- a/crates/prime-radiant/tests/integration/governance_tests.rs +++ b/crates/prime-radiant/tests/integration/governance_tests.rs @@ -134,7 +134,11 @@ impl PolicyBundle { } // Check for duplicate approver - if self.approvals.iter().any(|a| a.approver_id == approval.approver_id) { + if self + .approvals + .iter() + .any(|a| a.approver_id == approval.approver_id) + { return Err("Approver has already approved"); } @@ -393,21 +397,27 @@ fn test_policy_bundle_creation() { fn test_policy_bundle_with_thresholds() { let mut policy = PolicyBundle::new("policy-001"); - policy.add_threshold("global", ThresholdConfig { - name: "global".to_string(), - green_threshold: 0.05, - amber_threshold: 0.3, - red_threshold: 0.8, - escalation_enabled: true, - }); + policy.add_threshold( + "global", + ThresholdConfig { + name: "global".to_string(), + green_threshold: 0.05, + amber_threshold: 0.3, + red_threshold: 0.8, + escalation_enabled: true, + }, + ); - policy.add_threshold("finance", ThresholdConfig { - name: "finance".to_string(), - green_threshold: 0.02, - amber_threshold: 0.1, - red_threshold: 0.5, - escalation_enabled: true, - }); + policy.add_threshold( + "finance", + ThresholdConfig { + name: "finance".to_string(), + green_threshold: 0.02, + amber_threshold: 0.1, + red_threshold: 0.5, + escalation_enabled: true, + }, + ); assert_eq!(policy.thresholds.len(), 2); assert!(policy.thresholds.contains_key("global")); @@ -439,21 +449,25 @@ fn test_policy_bundle_approval_workflow() { policy.submit_for_approval().unwrap(); // Add first approval - policy.add_approval(ApprovalSignature { - approver_id: "approver-1".to_string(), - timestamp: 1001, - signature: vec![1, 2, 3], - }).unwrap(); + policy + .add_approval(ApprovalSignature { + approver_id: "approver-1".to_string(), + timestamp: 1001, + signature: vec![1, 2, 3], + }) + .unwrap(); // Cannot activate with insufficient approvals assert!(policy.activate(1002).is_err()); // Add second approval - policy.add_approval(ApprovalSignature { - approver_id: "approver-2".to_string(), - timestamp: 1002, - signature: vec![4, 5, 6], - }).unwrap(); + policy + .add_approval(ApprovalSignature { + approver_id: "approver-2".to_string(), + timestamp: 1002, + signature: vec![4, 5, 6], + }) + .unwrap(); // Now activation should succeed assert!(policy.activate(1003).is_ok()); @@ -467,11 +481,13 @@ fn test_policy_bundle_duplicate_approval_rejected() { policy.add_threshold("global", ThresholdConfig::default()); policy.submit_for_approval().unwrap(); - policy.add_approval(ApprovalSignature { - approver_id: "approver-1".to_string(), - timestamp: 1001, - signature: vec![1, 2, 3], - }).unwrap(); + policy + .add_approval(ApprovalSignature { + approver_id: "approver-1".to_string(), + timestamp: 1001, + signature: vec![1, 2, 3], + }) + .unwrap(); // Same approver cannot approve twice let result = policy.add_approval(ApprovalSignature { @@ -488,11 +504,13 @@ fn test_policy_bundle_supersession() { let mut policy_v1 = PolicyBundle::new("policy-001"); policy_v1.add_threshold("global", ThresholdConfig::default()); policy_v1.submit_for_approval().unwrap(); - policy_v1.add_approval(ApprovalSignature { - approver_id: "approver-1".to_string(), - timestamp: 1001, - signature: vec![1, 2, 3], - }).unwrap(); + policy_v1 + .add_approval(ApprovalSignature { + approver_id: "approver-1".to_string(), + timestamp: 1001, + signature: vec![1, 2, 3], + }) + .unwrap(); policy_v1.activate(1002).unwrap(); assert!(policy_v1.is_active()); @@ -508,11 +526,13 @@ fn test_policy_immutability_after_activation() { let mut policy = PolicyBundle::new("policy-001"); policy.add_threshold("global", ThresholdConfig::default()); policy.submit_for_approval().unwrap(); - policy.add_approval(ApprovalSignature { - approver_id: "approver-1".to_string(), - timestamp: 1001, - signature: vec![1, 2, 3], - }).unwrap(); + policy + .add_approval(ApprovalSignature { + approver_id: "approver-1".to_string(), + timestamp: 1001, + signature: vec![1, 2, 3], + }) + .unwrap(); policy.activate(1002).unwrap(); // Content hash is locked after activation @@ -521,7 +541,10 @@ fn test_policy_immutability_after_activation() { // Cannot add more thresholds (in a real system this would be prevented) // Here we just verify the hash would change if we could modify let new_hash = policy.compute_content_hash(); - assert_eq!(hash_at_activation, new_hash, "Hash should be stable after activation"); + assert_eq!( + hash_at_activation, new_hash, + "Hash should be stable after activation" + ); } // ============================================================================ @@ -821,7 +844,9 @@ fn test_invariant_no_action_without_witness() { ); // This is the invariant: every decision creates a witness - self.chain.append(witness).expect("Witness must be created for every decision"); + self.chain + .append(witness) + .expect("Witness must be created for every decision"); (decision, id) } @@ -885,9 +910,27 @@ fn test_invariant_no_write_without_lineage() { let mut engine = WriteEngine::new(); - let l1 = engine.write("entity:fact:1", Operation::Create, vec![], "witness-001", "agent-1"); - let l2 = engine.write("entity:fact:2", Operation::Create, vec![], "witness-002", "agent-1"); - let l3 = engine.write("entity:derived:1", Operation::Derive, vec![l1, l2], "witness-003", "agent-1"); + let l1 = engine.write( + "entity:fact:1", + Operation::Create, + vec![], + "witness-001", + "agent-1", + ); + let l2 = engine.write( + "entity:fact:2", + Operation::Create, + vec![], + "witness-002", + "agent-1", + ); + let l3 = engine.write( + "entity:derived:1", + Operation::Derive, + vec![l1, l2], + "witness-003", + "agent-1", + ); assert_eq!(engine.lineages.len(), 3); assert_eq!(engine.lineages[2].dependencies.len(), 2); diff --git a/crates/prime-radiant/tests/integration/graph_tests.rs b/crates/prime-radiant/tests/integration/graph_tests.rs index c0b0d42f9..9ab4d5814 100644 --- a/crates/prime-radiant/tests/integration/graph_tests.rs +++ b/crates/prime-radiant/tests/integration/graph_tests.rs @@ -238,11 +238,7 @@ fn test_subgraph_extraction_bfs() { adjacency.insert(5, vec![4]); // Extract 1-hop subgraph around node 3 - fn extract_khop( - center: u64, - k: usize, - adjacency: &HashMap>, - ) -> Vec { + fn extract_khop(center: u64, k: usize, adjacency: &HashMap>) -> Vec { let mut visited = vec![center]; let mut frontier = vec![center]; @@ -306,8 +302,8 @@ fn test_namespace_isolation() { #[test] fn test_fingerprint_changes_on_modification() { // Graph fingerprint should change when structure changes - use std::hash::{Hash, Hasher}; use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; fn compute_fingerprint(nodes: &HashMap>, edges: &[(u64, u64)]) -> u64 { let mut hasher = DefaultHasher::new(); @@ -353,8 +349,8 @@ fn test_fingerprint_changes_on_modification() { #[test] fn test_fingerprint_stable_without_modification() { // Fingerprint should be deterministic and stable - use std::hash::{Hash, Hasher}; use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; fn compute_fingerprint(nodes: &HashMap>) -> u64 { let mut hasher = DefaultHasher::new(); @@ -411,7 +407,8 @@ fn test_restriction_map_dimension_compatibility() { if input.len() != self.input_dim() { return Err("Input dimension mismatch"); } - Ok(self.matrix + Ok(self + .matrix .iter() .map(|row| row.iter().zip(input).map(|(a, b)| a * b).sum()) .collect()) diff --git a/crates/prime-radiant/tests/integration/mod.rs b/crates/prime-radiant/tests/integration/mod.rs index 2ed714c67..f81063652 100644 --- a/crates/prime-radiant/tests/integration/mod.rs +++ b/crates/prime-radiant/tests/integration/mod.rs @@ -7,7 +7,7 @@ //! - `governance_tests`: Policy bundles and witness chain integrity //! - `gate_tests`: Compute ladder escalation and persistence detection -mod graph_tests; mod coherence_tests; -mod governance_tests; mod gate_tests; +mod governance_tests; +mod graph_tests; diff --git a/crates/prime-radiant/tests/property/coherence_properties.rs b/crates/prime-radiant/tests/property/coherence_properties.rs index 2e579a810..79bf4a153 100644 --- a/crates/prime-radiant/tests/property/coherence_properties.rs +++ b/crates/prime-radiant/tests/property/coherence_properties.rs @@ -58,7 +58,9 @@ impl Arbitrary for PositiveFloat { fn arbitrary(g: &mut Gen) -> Self { // Use u32 to generate a bounded positive integer, then convert to float let val: u32 = u32::arbitrary(g); - let float_val = (val as f32 / (u32::MAX as f32 / 1000.0)).max(0.001).min(1000.0); + let float_val = (val as f32 / (u32::MAX as f32 / 1000.0)) + .max(0.001) + .min(1000.0); PositiveFloat(float_val) } } @@ -208,10 +210,7 @@ fn compute_energy(residual: &[f32], weight: f32) -> f32 { } /// Compute total energy for a graph -fn compute_total_energy( - states: &[(usize, Vec)], - edges: &[(usize, usize, f32)], -) -> f32 { +fn compute_total_energy(states: &[(usize, Vec)], edges: &[(usize, usize, f32)]) -> f32 { let dim = if states.is_empty() { 0 } else { @@ -437,7 +436,10 @@ fn prop_zero_weight_zero_energy(source: StateVector, target: StateVector) -> Tes if energy.abs() < 1e-10 { TestResult::passed() } else { - TestResult::error(format!("Zero weight should give zero energy, got {}", energy)) + TestResult::error(format!( + "Zero weight should give zero energy, got {}", + energy + )) } } @@ -446,7 +448,11 @@ fn prop_zero_weight_zero_energy(source: StateVector, target: StateVector) -> Tes // ============================================================================ #[quickcheck] -fn prop_energy_additivity(state1: StateVector, state2: StateVector, state3: StateVector) -> TestResult { +fn prop_energy_additivity( + state1: StateVector, + state2: StateVector, + state3: StateVector, +) -> TestResult { // Ensure all states have the same dimension let dim = state1.dim(); if dim == 0 || state2.dim() != dim || state3.dim() != dim { @@ -475,7 +481,10 @@ fn prop_energy_additivity(state1: StateVector, state2: StateVector, state3: Stat if (total - expected).abs() < 1e-6 { TestResult::passed() } else { - TestResult::error(format!("Additivity failed: {} + {} != {}", e_12, e_23, total)) + TestResult::error(format!( + "Additivity failed: {} + {} != {}", + e_12, e_23, total + )) } } @@ -599,7 +608,12 @@ fn test_energy_stable_for_large_values() { assert!(!energy.is_nan(), "Energy became NaN for dim {}", dim); assert!(!energy.is_infinite(), "Energy became Inf for dim {}", dim); - assert!(energy >= 0.0, "Energy became negative for dim {}: {}", dim, energy); + assert!( + energy >= 0.0, + "Energy became negative for dim {}: {}", + dim, + energy + ); } } @@ -617,7 +631,12 @@ fn test_energy_stable_for_small_values() { assert!(!energy.is_nan(), "Energy became NaN for dim {}", dim); assert!(!energy.is_infinite(), "Energy became Inf for dim {}", dim); - assert!(energy >= 0.0, "Energy became negative for dim {}: {}", dim, energy); + assert!( + energy >= 0.0, + "Energy became negative for dim {}: {}", + dim, + energy + ); } } @@ -657,9 +676,6 @@ fn prop_energy_computation_deterministic( if (e1 - e2).abs() < 1e-10 && (e2 - e3).abs() < 1e-10 { TestResult::passed() } else { - TestResult::error(format!( - "Non-deterministic results: {}, {}, {}", - e1, e2, e3 - )) + TestResult::error(format!("Non-deterministic results: {}, {}, {}", e1, e2, e3)) } } diff --git a/crates/prime-radiant/tests/replay_determinism.rs b/crates/prime-radiant/tests/replay_determinism.rs index 6e09cfd06..f2d56c3d9 100644 --- a/crates/prime-radiant/tests/replay_determinism.rs +++ b/crates/prime-radiant/tests/replay_determinism.rs @@ -31,10 +31,7 @@ enum DomainEvent { timestamp: u64, }, /// Remove a node - NodeRemoved { - node_id: u64, - timestamp: u64, - }, + NodeRemoved { node_id: u64, timestamp: u64 }, /// Add an edge between nodes EdgeAdded { source: u64, @@ -112,24 +109,41 @@ impl CoherenceState { DomainEvent::NodeAdded { node_id, state, .. } => { self.nodes.insert(*node_id, state.clone()); } - DomainEvent::NodeUpdated { node_id, new_state, .. } => { + DomainEvent::NodeUpdated { + node_id, new_state, .. + } => { self.nodes.insert(*node_id, new_state.clone()); } DomainEvent::NodeRemoved { node_id, .. } => { self.nodes.remove(node_id); // Remove incident edges - self.edges.retain(|(s, t), _| *s != *node_id && *t != *node_id); + self.edges + .retain(|(s, t), _| *s != *node_id && *t != *node_id); } - DomainEvent::EdgeAdded { source, target, weight, .. } => { + DomainEvent::EdgeAdded { + source, + target, + weight, + .. + } => { self.edges.insert((*source, *target), *weight); } - DomainEvent::EdgeWeightUpdated { source, target, new_weight, .. } => { + DomainEvent::EdgeWeightUpdated { + source, + target, + new_weight, + .. + } => { self.edges.insert((*source, *target), *new_weight); } DomainEvent::EdgeRemoved { source, target, .. } => { self.edges.remove(&(*source, *target)); } - DomainEvent::ThresholdChanged { scope, new_threshold, .. } => { + DomainEvent::ThresholdChanged { + scope, + new_threshold, + .. + } => { self.thresholds.insert(scope.clone(), *new_threshold); } } @@ -683,7 +697,10 @@ fn test_concurrent_replays() { // All concurrent replays should produce the same fingerprint let first = fingerprints[0]; for fp in &fingerprints { - assert_eq!(*fp, first, "All replays should produce the same fingerprint"); + assert_eq!( + *fp, first, + "All replays should produce the same fingerprint" + ); } } diff --git a/crates/prime-radiant/tests/storage_tests.rs b/crates/prime-radiant/tests/storage_tests.rs index b989c2c1b..313c95291 100644 --- a/crates/prime-radiant/tests/storage_tests.rs +++ b/crates/prime-radiant/tests/storage_tests.rs @@ -7,8 +7,7 @@ //! - Governance storage operations use prime_radiant::storage::{ - FileStorage, InMemoryStorage, StorageFormat, - GovernanceStorage, GraphStorage, + FileStorage, GovernanceStorage, GraphStorage, InMemoryStorage, StorageFormat, }; use std::sync::{Arc, Barrier}; use std::thread; @@ -291,7 +290,9 @@ mod file_storage_tests { // First instance: write data { let storage = FileStorage::new(temp_dir.path()).unwrap(); - storage.store_node("persistent-node", &[1.0, 2.0, 3.0]).unwrap(); + storage + .store_node("persistent-node", &[1.0, 2.0, 3.0]) + .unwrap(); storage.store_edge("a", "b", 1.5).unwrap(); storage.sync().unwrap(); } @@ -423,7 +424,9 @@ mod file_storage_tests { for j in 0..25 { let node_id = format!("concurrent-{}-{}", i, j); - storage_clone.store_node(&node_id, &[i as f32, j as f32]).unwrap(); + storage_clone + .store_node(&node_id, &[i as f32, j as f32]) + .unwrap(); } }); @@ -482,20 +485,12 @@ mod integration_tests { let storage = InMemoryStorage::new(); // Tenant A data (with namespace prefix) - storage - .store_node("tenant-a::node-1", &[1.0, 0.0]) - .unwrap(); - storage - .store_node("tenant-a::node-2", &[0.0, 1.0]) - .unwrap(); + storage.store_node("tenant-a::node-1", &[1.0, 0.0]).unwrap(); + storage.store_node("tenant-a::node-2", &[0.0, 1.0]).unwrap(); // Tenant B data - storage - .store_node("tenant-b::node-1", &[0.5, 0.5]) - .unwrap(); - storage - .store_node("tenant-b::node-2", &[0.3, 0.7]) - .unwrap(); + storage.store_node("tenant-b::node-1", &[0.5, 0.5]).unwrap(); + storage.store_node("tenant-b::node-2", &[0.3, 0.7]).unwrap(); // Verify isolation - tenant A's node-1 is different from tenant B's let a_node = storage.get_node("tenant-a::node-1").unwrap().unwrap(); @@ -538,9 +533,15 @@ mod integration_tests { let storage = FileStorage::new(temp_dir.path()).unwrap(); // Store graph data - storage.store_node("persistent-1", &[1.0, 2.0, 3.0]).unwrap(); - storage.store_node("persistent-2", &[4.0, 5.0, 6.0]).unwrap(); - storage.store_edge("persistent-1", "persistent-2", 0.5).unwrap(); + storage + .store_node("persistent-1", &[1.0, 2.0, 3.0]) + .unwrap(); + storage + .store_node("persistent-2", &[4.0, 5.0, 6.0]) + .unwrap(); + storage + .store_edge("persistent-1", "persistent-2", 0.5) + .unwrap(); // Store governance data storage.store_policy(b"durable-policy").unwrap(); diff --git a/crates/ruQu/benches/memory_bench.rs b/crates/ruQu/benches/memory_bench.rs index 18f9b21b5..4e58030c6 100644 --- a/crates/ruQu/benches/memory_bench.rs +++ b/crates/ruQu/benches/memory_bench.rs @@ -7,9 +7,7 @@ //! //! Run with: `cargo bench -p ruqu --bench memory_bench` -use criterion::{ - black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput, -}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use std::alloc::{GlobalAlloc, Layout, System}; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -58,23 +56,52 @@ fn bench_structure_sizes(c: &mut Criterion) { // Report sizes (this is informational, not a timed benchmark) println!("\n=== Structure Sizes ==="); - println!("WorkerTile: {} bytes", std::mem::size_of::()); - println!("PatchGraph: {} bytes", std::mem::size_of::()); - println!("SyndromBuffer: {} bytes", std::mem::size_of::()); - println!("EvidenceAccumulator: {} bytes", std::mem::size_of::()); - println!("LocalCutState: {} bytes", std::mem::size_of::()); - println!("TileReport: {} bytes", std::mem::size_of::()); - println!("DetectorBitmap: {} bytes", std::mem::size_of::()); - println!("SyndromeRound: {} bytes", std::mem::size_of::()); - println!("SyndromeDelta: {} bytes", std::mem::size_of::()); + println!( + "WorkerTile: {} bytes", + std::mem::size_of::() + ); + println!( + "PatchGraph: {} bytes", + std::mem::size_of::() + ); + println!( + "SyndromBuffer: {} bytes", + std::mem::size_of::() + ); + println!( + "EvidenceAccumulator: {} bytes", + std::mem::size_of::() + ); + println!( + "LocalCutState: {} bytes", + std::mem::size_of::() + ); + println!( + "TileReport: {} bytes", + std::mem::size_of::() + ); + println!( + "DetectorBitmap: {} bytes", + std::mem::size_of::() + ); + println!( + "SyndromeRound: {} bytes", + std::mem::size_of::() + ); + println!( + "SyndromeDelta: {} bytes", + std::mem::size_of::() + ); println!(); // Verify 64KB budget let total_tile_size = std::mem::size_of::(); let budget = 65536; // 64KB - println!("WorkerTile size: {} bytes ({:.1}% of 64KB budget)", - total_tile_size, - (total_tile_size as f64 / budget as f64) * 100.0); + println!( + "WorkerTile size: {} bytes ({:.1}% of 64KB budget)", + total_tile_size, + (total_tile_size as f64 / budget as f64) * 100.0 + ); // Benchmark size computation (ensures compiler doesn't optimize away) group.bench_function("size_of_worker_tile", |b| { @@ -267,14 +294,23 @@ fn bench_cache_efficiency(c: &mut Criterion) { // Verify cache-line alignment println!("\n=== Cache Line Alignment ==="); - println!("TileReport alignment: {} bytes (cache line: {})", - std::mem::align_of::(), CACHE_LINE_SIZE); - println!("PatchGraph alignment: {} bytes", - std::mem::align_of::()); - println!("SyndromBuffer alignment: {} bytes", - std::mem::align_of::()); - println!("DetectorBitmap alignment: {} bytes", - std::mem::align_of::()); + println!( + "TileReport alignment: {} bytes (cache line: {})", + std::mem::align_of::(), + CACHE_LINE_SIZE + ); + println!( + "PatchGraph alignment: {} bytes", + std::mem::align_of::() + ); + println!( + "SyndromBuffer alignment: {} bytes", + std::mem::align_of::() + ); + println!( + "DetectorBitmap alignment: {} bytes", + std::mem::align_of::() + ); println!(); // Sequential access pattern (cache-friendly) @@ -364,9 +400,7 @@ fn bench_memory_pool(c: &mut Criterion) { // Pre-allocated tile pool group.bench_function("tile_pool_reuse", |b| { // Simulate a pool of worker tiles - let mut tile_pool: Vec = (1..=10) - .map(|i| WorkerTile::new(i)) - .collect(); + let mut tile_pool: Vec = (1..=10).map(|i| WorkerTile::new(i)).collect(); let delta = SyndromeDelta::new(0, 1, 100); @@ -409,7 +443,13 @@ fn bench_memory_pool(c: &mut Criterion) { b.iter(|| { // Push rounds (reusing buffer space) for _ in 0..100 { - let round = SyndromeRound::new(round_id, round_id, round_id * 1000, DetectorBitmap::new(64), 0); + let round = SyndromeRound::new( + round_id, + round_id, + round_id * 1000, + DetectorBitmap::new(64), + 0, + ); buffer.push(round); round_id += 1; } @@ -452,12 +492,7 @@ fn bench_heap_allocations(c: &mut Criterion) { ReceiptLog::new, |mut log| { for i in 0..100 { - log.append( - ruqu::tile::GateDecision::Permit, - i, - i * 1000, - [0u8; 32], - ); + log.append(ruqu::tile::GateDecision::Permit, i, i * 1000, [0u8; 32]); } black_box(&log); }, @@ -499,7 +534,9 @@ fn bench_memory_bandwidth(c: &mut Criterion) { let mut group = c.benchmark_group("memory_bandwidth"); // Large data copy (TileReport array) - group.throughput(Throughput::Bytes(255 * std::mem::size_of::() as u64)); + group.throughput(Throughput::Bytes( + 255 * std::mem::size_of::() as u64, + )); group.bench_function("copy_255_reports", |b| { let source: Vec = (1..=255).map(|i| TileReport::new(i)).collect(); @@ -510,7 +547,9 @@ fn bench_memory_bandwidth(c: &mut Criterion) { }); // DetectorBitmap copy - group.throughput(Throughput::Bytes(std::mem::size_of::() as u64)); + group.throughput(Throughput::Bytes( + std::mem::size_of::() as u64 + )); group.bench_function("copy_bitmap", |b| { let mut bitmap = DetectorBitmap::new(1024); for i in 0..512 { @@ -524,7 +563,9 @@ fn bench_memory_bandwidth(c: &mut Criterion) { }); // Batch bitmap copy - group.throughput(Throughput::Bytes(100 * std::mem::size_of::() as u64)); + group.throughput(Throughput::Bytes( + 100 * std::mem::size_of::() as u64, + )); group.bench_function("copy_100_bitmaps", |b| { let bitmaps: Vec = (0..100) .map(|i| { @@ -541,7 +582,9 @@ fn bench_memory_bandwidth(c: &mut Criterion) { }); // SyndromeRound copy - group.throughput(Throughput::Bytes(std::mem::size_of::() as u64)); + group.throughput(Throughput::Bytes( + std::mem::size_of::() as u64 + )); group.bench_function("copy_syndrome_round", |b| { let mut detectors = DetectorBitmap::new(256); for i in 0..25 { diff --git a/crates/ruQu/benches/mincut_bench.rs b/crates/ruQu/benches/mincut_bench.rs index 51c9b606c..55057cae8 100644 --- a/crates/ruQu/benches/mincut_bench.rs +++ b/crates/ruQu/benches/mincut_bench.rs @@ -8,9 +8,7 @@ use ruqu::mincut::DynamicMinCutEngine; /// Benchmark min-cut engine creation fn bench_engine_creation(c: &mut Criterion) { c.bench_function("mincut_engine_creation", |b| { - b.iter(|| { - black_box(DynamicMinCutEngine::new()) - }); + b.iter(|| black_box(DynamicMinCutEngine::new())); }); } @@ -51,9 +49,7 @@ fn bench_mincut_query(c: &mut Criterion) { } } - b.iter(|| { - black_box(engine.min_cut_value()) - }); + b.iter(|| black_box(engine.min_cut_value())); }); } group.finish(); diff --git a/crates/ruQu/benches/scaling_bench.rs b/crates/ruQu/benches/scaling_bench.rs index 182a080cd..bace3428b 100644 --- a/crates/ruQu/benches/scaling_bench.rs +++ b/crates/ruQu/benches/scaling_bench.rs @@ -8,16 +8,12 @@ //! //! Run with: `cargo bench -p ruqu --bench scaling_bench` -use criterion::{ - black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput, -}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use std::hint::black_box as hint_black_box; use ruqu::filters::{FilterConfig, FilterPipeline, SystemState}; use ruqu::syndrome::{DetectorBitmap, SyndromeBuffer, SyndromeRound}; -use ruqu::tile::{ - GateThresholds, PatchGraph, SyndromeDelta, TileReport, TileZero, WorkerTile, -}; +use ruqu::tile::{GateThresholds, PatchGraph, SyndromeDelta, TileReport, TileZero, WorkerTile}; // ============================================================================ // HELPER FUNCTIONS @@ -51,10 +47,7 @@ fn create_scaled_worker_tile(tile_id: u8, qubit_count: usize) -> WorkerTile { 'outer: for i in 0..vertices.saturating_sub(1) { // Lattice-like connectivity - let neighbors = [ - i + 1, - i.wrapping_add(vertices / 10), - ]; + let neighbors = [i + 1, i.wrapping_add(vertices / 10)]; for &neighbor in &neighbors { if neighbor < vertices && neighbor != i && edges_added < max_edges { if tile.patch_graph.add_edge(i, neighbor, 1000).is_some() { @@ -356,7 +349,8 @@ fn bench_throughput_vs_size(c: &mut Criterion) { for i in 0..detector_count / 10 { bitmap.set(i * 10, true); } - let round = SyndromeRound::new(round_id, round_id, round_id * 1000, bitmap, 0); + let round = + SyndromeRound::new(round_id, round_id, round_id * 1000, bitmap, 0); buffer.push(round); round_id += 1; } @@ -537,7 +531,13 @@ fn bench_memory_pressure(c: &mut Criterion) { let mut round_id = size as u64; b.iter(|| { for _ in 0..1000 { - let round = SyndromeRound::new(round_id, round_id, round_id * 1000, DetectorBitmap::new(64), 0); + let round = SyndromeRound::new( + round_id, + round_id, + round_id * 1000, + DetectorBitmap::new(64), + 0, + ); buffer.push(round); round_id += 1; } diff --git a/crates/ruQu/benches/throughput_bench.rs b/crates/ruQu/benches/throughput_bench.rs index af7013290..52646b483 100644 --- a/crates/ruQu/benches/throughput_bench.rs +++ b/crates/ruQu/benches/throughput_bench.rs @@ -7,15 +7,13 @@ //! //! Run with: `cargo bench -p ruqu --bench throughput_bench` -use criterion::{ - black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput, -}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use ruqu::filters::{FilterConfig, FilterPipeline, SystemState}; use ruqu::syndrome::{DetectorBitmap, SyndromeBuffer, SyndromeDelta, SyndromeRound}; use ruqu::tile::{ - GateDecision, GateThresholds, PatchGraph, PermitToken, ReceiptLog, SyndromeDelta as TileSyndromeDelta, - TileReport, TileZero, WorkerTile, + GateDecision, GateThresholds, PatchGraph, PermitToken, ReceiptLog, + SyndromeDelta as TileSyndromeDelta, TileReport, TileZero, WorkerTile, }; // ============================================================================ @@ -133,7 +131,10 @@ fn bench_syndrome_ingestion(c: &mut Criterion) { for firing_rate in [0.01, 0.05, 0.1, 0.25].iter() { group.throughput(Throughput::Elements(1000)); group.bench_with_input( - BenchmarkId::new("batch_1000_firing_rate", format!("{:.0}pct", firing_rate * 100.0)), + BenchmarkId::new( + "batch_1000_firing_rate", + format!("{:.0}pct", firing_rate * 100.0), + ), firing_rate, |b, &rate| { let mut buffer = SyndromeBuffer::new(4096); diff --git a/crates/ruQu/examples/coherence_gate_breakthrough.rs b/crates/ruQu/examples/coherence_gate_breakthrough.rs index da8967603..6a96c2f4e 100644 --- a/crates/ruQu/examples/coherence_gate_breakthrough.rs +++ b/crates/ruQu/examples/coherence_gate_breakthrough.rs @@ -257,17 +257,21 @@ fn run_coherence_experiment(config: &CoherenceGateConfig) -> CoherenceStats { println!("\n╔═══════════════════════════════════════════════════════════════════╗"); println!("║ COHERENCE GATE: Subpolynomial Min-Cut for QEC ║"); println!("╠═══════════════════════════════════════════════════════════════════╣"); - println!("║ Code Distance: d={} | Error Rate: {:.4} | Rounds: {:>5} ║", - config.code_distance, config.error_rate, config.num_rounds); + println!( + "║ Code Distance: d={} | Error Rate: {:.4} | Rounds: {:>5} ║", + config.code_distance, config.error_rate, config.num_rounds + ); println!("╚═══════════════════════════════════════════════════════════════════╝\n"); let mut stats = CoherenceStats::new(); // Build initial syndrome graph let edges = build_syndrome_graph(config.code_distance); - println!("Building syndrome graph: {} nodes, {} edges", - 2 * (config.code_distance - 1).pow(2) + 4, - edges.len()); + println!( + "Building syndrome graph: {} nodes, {} edges", + 2 * (config.code_distance - 1).pow(2) + 4, + edges.len() + ); // Create the dynamic min-cut structure using the proper API let mut mincut = MinCutBuilder::new() @@ -281,10 +285,10 @@ fn run_coherence_experiment(config: &CoherenceGateConfig) -> CoherenceStats { println!(); // Initialize syndrome source - let surface_config = SurfaceCodeConfig::new(config.code_distance, config.error_rate) - .with_seed(config.seed); - let mut syndrome_source = StimSyndromeSource::new(surface_config) - .expect("Failed to create syndrome source"); + let surface_config = + SurfaceCodeConfig::new(config.code_distance, config.error_rate).with_seed(config.seed); + let mut syndrome_source = + StimSyndromeSource::new(surface_config).expect("Failed to create syndrome source"); let grid_size = config.code_distance - 1; let num_x_stabs = grid_size * grid_size; @@ -361,10 +365,15 @@ fn run_coherence_experiment(config: &CoherenceGateConfig) -> CoherenceStats { } // X-Z coupling edge - let coupled = if base == 0 { det + z_offset } else { det - z_offset }; + let coupled = if base == 0 { + det + z_offset + } else { + det - z_offset + }; if coupled < (2 * num_x_stabs) as u64 { let _ = mincut.delete_edge(det.min(coupled), det.max(coupled)); - let _ = mincut.insert_edge(det.min(coupled), det.max(coupled), weakened_weight * 0.5); + let _ = + mincut.insert_edge(det.min(coupled), det.max(coupled), weakened_weight * 0.5); modified_edges.push((det.min(coupled), det.max(coupled), 0.5)); } } @@ -379,8 +388,12 @@ fn run_coherence_experiment(config: &CoherenceGateConfig) -> CoherenceStats { if last_report.elapsed() > Duration::from_secs(1) { let progress = (round as f64 / config.num_rounds as f64) * 100.0; let throughput = round as f64 / start_time.elapsed().as_secs_f64(); - println!(" Progress: {:5.1}% | {:>7.0} rounds/sec | avg min-cut: {:.3}", - progress, throughput, stats.mean_min_cut()); + println!( + " Progress: {:5.1}% | {:>7.0} rounds/sec | avg min-cut: {:.3}", + progress, + throughput, + stats.mean_min_cut() + ); last_report = Instant::now(); } } @@ -396,17 +409,21 @@ fn run_coherence_experiment(config: &CoherenceGateConfig) -> CoherenceStats { println!("\n╔═══════════════════════════════════════════════════════════════════╗"); println!("║ COHERENCE GATE (Fallback Mode - No Subpolynomial) ║"); println!("╠═══════════════════════════════════════════════════════════════════╣"); - println!("║ Code Distance: d={} | Error Rate: {:.4} | Rounds: {:>5} ║", - config.code_distance, config.error_rate, config.num_rounds); + println!( + "║ Code Distance: d={} | Error Rate: {:.4} | Rounds: {:>5} ║", + config.code_distance, config.error_rate, config.num_rounds + ); println!("╚═══════════════════════════════════════════════════════════════════╝\n"); let mut stats = CoherenceStats::new(); // Build initial syndrome graph let edges = build_syndrome_graph(config.code_distance); - println!("Building syndrome graph: {} nodes, {} edges", - 2 * (config.code_distance - 1).pow(2) + 4, - edges.len()); + println!( + "Building syndrome graph: {} nodes, {} edges", + 2 * (config.code_distance - 1).pow(2) + 4, + edges.len() + ); // Create fallback engine let mut engine = DynamicMinCutEngine::new(); @@ -418,10 +435,10 @@ fn run_coherence_experiment(config: &CoherenceGateConfig) -> CoherenceStats { println!(); // Initialize syndrome source - let surface_config = SurfaceCodeConfig::new(config.code_distance, config.error_rate) - .with_seed(config.seed); - let mut syndrome_source = StimSyndromeSource::new(surface_config) - .expect("Failed to create syndrome source"); + let surface_config = + SurfaceCodeConfig::new(config.code_distance, config.error_rate).with_seed(config.seed); + let mut syndrome_source = + StimSyndromeSource::new(surface_config).expect("Failed to create syndrome source"); let grid_size = config.code_distance - 1; let num_x_stabs = grid_size * grid_size; @@ -460,7 +477,8 @@ fn run_coherence_experiment(config: &CoherenceGateConfig) -> CoherenceStats { } } - let min_cut = (base_coherence - penalty - cluster_penalty.min(base_coherence * 0.5)).max(0.1); + let min_cut = + (base_coherence - penalty - cluster_penalty.min(base_coherence * 0.5)).max(0.1); let update_ns = round_start.elapsed().as_nanos() as u64; stats.record(min_cut, update_ns, config.coherence_threshold); @@ -468,8 +486,12 @@ fn run_coherence_experiment(config: &CoherenceGateConfig) -> CoherenceStats { if last_report.elapsed() > Duration::from_secs(1) { let progress = (round as f64 / config.num_rounds as f64) * 100.0; let throughput = round as f64 / start_time.elapsed().as_secs_f64(); - println!(" Progress: {:5.1}% | {:>7.0} rounds/sec | avg coherence: {:.3}", - progress, throughput, stats.mean_min_cut()); + println!( + " Progress: {:5.1}% | {:>7.0} rounds/sec | avg coherence: {:.3}", + progress, + throughput, + stats.mean_min_cut() + ); last_report = Instant::now(); } } @@ -482,26 +504,42 @@ fn print_results(_config: &CoherenceGateConfig, stats: &CoherenceStats, elapsed: println!("\n╔═══════════════════════════════════════════════════════════════════╗"); println!("║ EXPERIMENT RESULTS ║"); println!("╠═══════════════════════════════════════════════════════════════════╣"); - println!("║ Throughput: {:>10.0} rounds/sec ║", - stats.total_rounds as f64 / elapsed.as_secs_f64()); - println!("║ Avg Update Latency: {:>10.0} ns ║", stats.avg_update_ns()); + println!( + "║ Throughput: {:>10.0} rounds/sec ║", + stats.total_rounds as f64 / elapsed.as_secs_f64() + ); + println!( + "║ Avg Update Latency: {:>10.0} ns ║", + stats.avg_update_ns() + ); println!("╠═══════════════════════════════════════════════════════════════════╣"); println!("║ Min-Cut Statistics: ║"); - println!("║ Mean: {:>8.4} ± {:.4} ║", - stats.mean_min_cut(), stats.std_min_cut()); - println!("║ Range: [{:.4}, {:.4}] ║", - stats.min_min_cut, stats.max_min_cut); + println!( + "║ Mean: {:>8.4} ± {:.4} ║", + stats.mean_min_cut(), + stats.std_min_cut() + ); + println!( + "║ Range: [{:.4}, {:.4}] ║", + stats.min_min_cut, stats.max_min_cut + ); println!("╠═══════════════════════════════════════════════════════════════════╣"); println!("║ Coherence Assessment: ║"); - println!("║ Coherent: {:>6} ({:>5.1}%) ║", - stats.coherent_rounds, - stats.coherent_rounds as f64 / stats.total_rounds as f64 * 100.0); - println!("║ Warning: {:>6} ({:>5.1}%) ║", - stats.warning_rounds, - stats.warning_rounds as f64 / stats.total_rounds as f64 * 100.0); - println!("║ Critical: {:>6} ({:>5.1}%) ║", - stats.critical_rounds, - stats.critical_rounds as f64 / stats.total_rounds as f64 * 100.0); + println!( + "║ Coherent: {:>6} ({:>5.1}%) ║", + stats.coherent_rounds, + stats.coherent_rounds as f64 / stats.total_rounds as f64 * 100.0 + ); + println!( + "║ Warning: {:>6} ({:>5.1}%) ║", + stats.warning_rounds, + stats.warning_rounds as f64 / stats.total_rounds as f64 * 100.0 + ); + println!( + "║ Critical: {:>6} ({:>5.1}%) ║", + stats.critical_rounds, + stats.critical_rounds as f64 / stats.total_rounds as f64 * 100.0 + ); println!("╚═══════════════════════════════════════════════════════════════════╝"); } @@ -526,12 +564,14 @@ fn compare_code_distances() { let stats = run_coherence_experiment(&config); let elapsed = start.elapsed(); - println!("║ {:>2} │ {:>12.1}% │ {:>9.4} │ {:>8.0}/s │ {:>7.0} ns ║", - d, - stats.coherence_rate() * 100.0, - stats.mean_min_cut(), - stats.total_rounds as f64 / elapsed.as_secs_f64(), - stats.avg_update_ns()); + println!( + "║ {:>2} │ {:>12.1}% │ {:>9.4} │ {:>8.0}/s │ {:>7.0} ns ║", + d, + stats.coherence_rate() * 100.0, + stats.mean_min_cut(), + stats.total_rounds as f64 / elapsed.as_secs_f64(), + stats.avg_update_ns() + ); } println!("╚═════╧════════════════╧═════════════╧══════════════╧═════════════╝"); @@ -540,7 +580,10 @@ fn compare_code_distances() { /// Compare different error rates fn compare_error_rates(code_distance: usize) { println!("\n╔═══════════════════════════════════════════════════════════════════╗"); - println!("║ ERROR RATE SENSITIVITY (d={}) ║", code_distance); + println!( + "║ ERROR RATE SENSITIVITY (d={}) ║", + code_distance + ); println!("╠═══════════════════════════════════════════════════════════════════╣"); println!("║ Error Rate │ Coherent │ Warning │ Critical │ Avg Min-Cut ║"); println!("╠══════════════╪══════════╪═════════╪══════════╪══════════════════╣"); @@ -556,13 +599,15 @@ fn compare_error_rates(code_distance: usize) { let stats = run_coherence_experiment(&config); - println!("║ {:.4} │ {:>6.1}% │ {:>5.1}% │ {:>6.1}% │ {:>8.4} ± {:.4} ║", - p, - stats.coherent_rounds as f64 / stats.total_rounds as f64 * 100.0, - stats.warning_rounds as f64 / stats.total_rounds as f64 * 100.0, - stats.critical_rounds as f64 / stats.total_rounds as f64 * 100.0, - stats.mean_min_cut(), - stats.std_min_cut()); + println!( + "║ {:.4} │ {:>6.1}% │ {:>5.1}% │ {:>6.1}% │ {:>8.4} ± {:.4} ║", + p, + stats.coherent_rounds as f64 / stats.total_rounds as f64 * 100.0, + stats.warning_rounds as f64 / stats.total_rounds as f64 * 100.0, + stats.critical_rounds as f64 / stats.total_rounds as f64 * 100.0, + stats.mean_min_cut(), + stats.std_min_cut() + ); } println!("╚══════════════╧══════════╧═════════╧══════════╧══════════════════╝"); diff --git a/crates/ruQu/examples/coherence_simulation.rs b/crates/ruQu/examples/coherence_simulation.rs index 1f63cfae8..04abcc854 100644 --- a/crates/ruQu/examples/coherence_simulation.rs +++ b/crates/ruQu/examples/coherence_simulation.rs @@ -11,10 +11,8 @@ use std::time::{Duration, Instant}; use ruqu::{ - tile::{ - GateDecision, GateThresholds, SyndromeDelta, TileReport, TileZero, WorkerTile, - }, syndrome::DetectorBitmap, + tile::{GateDecision, GateThresholds, SyndromeDelta, TileReport, TileZero, WorkerTile}, }; #[cfg(feature = "structural")] @@ -64,12 +62,28 @@ impl SimStats { println!("\n=== Simulation Statistics ==="); println!("Total ticks: {}", self.total_ticks); println!("Total decisions: {}", self.total_decisions); - println!(" Permits: {} ({:.1}%)", self.permits, 100.0 * self.permits as f64 / self.total_decisions as f64); - println!(" Defers: {} ({:.1}%)", self.defers, 100.0 * self.defers as f64 / self.total_decisions as f64); - println!(" Denies: {} ({:.1}%)", self.denies, 100.0 * self.denies as f64 / self.total_decisions as f64); + println!( + " Permits: {} ({:.1}%)", + self.permits, + 100.0 * self.permits as f64 / self.total_decisions as f64 + ); + println!( + " Defers: {} ({:.1}%)", + self.defers, + 100.0 * self.defers as f64 / self.total_decisions as f64 + ); + println!( + " Denies: {} ({:.1}%)", + self.denies, + 100.0 * self.denies as f64 / self.total_decisions as f64 + ); if !self.tick_times.is_empty() { - let tick_ns: Vec = self.tick_times.iter().map(|d| d.as_nanos() as u64).collect(); + let tick_ns: Vec = self + .tick_times + .iter() + .map(|d| d.as_nanos() as u64) + .collect(); let avg_tick = tick_ns.iter().sum::() / tick_ns.len() as u64; let max_tick = *tick_ns.iter().max().unwrap(); let mut sorted = tick_ns.clone(); @@ -83,7 +97,11 @@ impl SimStats { } if !self.merge_times.is_empty() { - let merge_ns: Vec = self.merge_times.iter().map(|d| d.as_nanos() as u64).collect(); + let merge_ns: Vec = self + .merge_times + .iter() + .map(|d| d.as_nanos() as u64) + .collect(); let avg_merge = merge_ns.iter().sum::() / merge_ns.len() as u64; let max_merge = *merge_ns.iter().max().unwrap(); let mut sorted = merge_ns.clone(); @@ -98,7 +116,11 @@ impl SimStats { #[cfg(feature = "structural")] if !self.mincut_times.is_empty() { - let mincut_ns: Vec = self.mincut_times.iter().map(|d| d.as_nanos() as u64).collect(); + let mincut_ns: Vec = self + .mincut_times + .iter() + .map(|d| d.as_nanos() as u64) + .collect(); let avg_mincut = mincut_ns.iter().sum::() / mincut_ns.len() as u64; let max_mincut = *mincut_ns.iter().max().unwrap(); let mut sorted = mincut_ns.clone(); @@ -243,7 +265,12 @@ fn run_simulation(config: &SimConfig) -> SimStats { if round % 100 == 0 && decision == GateDecision::Permit { let token = tilezero.issue_permit(&decision); let verified = tilezero.verify_token(&token); - assert_eq!(verified, Some(true), "Token verification failed at round {}", round); + assert_eq!( + verified, + Some(true), + "Token verification failed at round {}", + round + ); } // Progress indicator @@ -257,8 +284,14 @@ fn run_simulation(config: &SimConfig) -> SimStats { println!(" Done!\n"); // Verify receipt log integrity - assert!(tilezero.receipt_log.verify_chain(), "Receipt log chain verification failed!"); - println!("Receipt log verified: {} entries, chain intact", tilezero.receipt_log.len()); + assert!( + tilezero.receipt_log.verify_chain(), + "Receipt log chain verification failed!" + ); + println!( + "Receipt log verified: {} entries, chain intact", + tilezero.receipt_log.len() + ); stats } @@ -288,8 +321,12 @@ fn benchmark_detector_bitmap() { total += bitmap1.popcount(); } let popcount_time = start.elapsed(); - println!("Popcount ({} iterations): {:?} ({:.1} ns/op)", - ITERATIONS, popcount_time, popcount_time.as_nanos() as f64 / ITERATIONS as f64); + println!( + "Popcount ({} iterations): {:?} ({:.1} ns/op)", + ITERATIONS, + popcount_time, + popcount_time.as_nanos() as f64 / ITERATIONS as f64 + ); println!(" Result: {} bits set", total / ITERATIONS); // Benchmark XOR @@ -298,8 +335,12 @@ fn benchmark_detector_bitmap() { let _ = bitmap1.xor(&bitmap2); } let xor_time = start.elapsed(); - println!("XOR ({} iterations): {:?} ({:.1} ns/op)", - ITERATIONS, xor_time, xor_time.as_nanos() as f64 / ITERATIONS as f64); + println!( + "XOR ({} iterations): {:?} ({:.1} ns/op)", + ITERATIONS, + xor_time, + xor_time.as_nanos() as f64 / ITERATIONS as f64 + ); // Benchmark AND let start = Instant::now(); @@ -307,8 +348,12 @@ fn benchmark_detector_bitmap() { let _ = bitmap1.and(&bitmap2); } let and_time = start.elapsed(); - println!("AND ({} iterations): {:?} ({:.1} ns/op)", - ITERATIONS, and_time, and_time.as_nanos() as f64 / ITERATIONS as f64); + println!( + "AND ({} iterations): {:?} ({:.1} ns/op)", + ITERATIONS, + and_time, + and_time.as_nanos() as f64 / ITERATIONS as f64 + ); // Benchmark OR let start = Instant::now(); @@ -316,8 +361,12 @@ fn benchmark_detector_bitmap() { let _ = bitmap1.or(&bitmap2); } let or_time = start.elapsed(); - println!("OR ({} iterations): {:?} ({:.1} ns/op)", - ITERATIONS, or_time, or_time.as_nanos() as f64 / ITERATIONS as f64); + println!( + "OR ({} iterations): {:?} ({:.1} ns/op)", + ITERATIONS, + or_time, + or_time.as_nanos() as f64 / ITERATIONS as f64 + ); } fn main() { @@ -340,7 +389,11 @@ fn main() { println!("\n=== Optimization Targets ==="); if !stats.tick_times.is_empty() { - let tick_ns: Vec = stats.tick_times.iter().map(|d| d.as_nanos() as u64).collect(); + let tick_ns: Vec = stats + .tick_times + .iter() + .map(|d| d.as_nanos() as u64) + .collect(); let mut sorted = tick_ns.clone(); sorted.sort(); let p99 = sorted[sorted.len() * 99 / 100]; diff --git a/crates/ruQu/examples/early_warning_validation.rs b/crates/ruQu/examples/early_warning_validation.rs index 9dc1de28a..1f607bb66 100644 --- a/crates/ruQu/examples/early_warning_validation.rs +++ b/crates/ruQu/examples/early_warning_validation.rs @@ -63,10 +63,26 @@ fn is_logical_failure(syndrome: &DetectorBitmap, code_distance: usize) -> bool { } let neighbors = [ - if col > 0 { Some(row * grid_size + col - 1) } else { None }, - if col + 1 < grid_size { Some(row * grid_size + col + 1) } else { None }, - if row > 0 { Some((row - 1) * grid_size + col) } else { None }, - if row + 1 < grid_size { Some((row + 1) * grid_size + col) } else { None }, + if col > 0 { + Some(row * grid_size + col - 1) + } else { + None + }, + if col + 1 < grid_size { + Some(row * grid_size + col + 1) + } else { + None + }, + if row > 0 { + Some((row - 1) * grid_size + col) + } else { + None + }, + if row + 1 < grid_size { + Some((row + 1) * grid_size + col) + } else { + None + }, ]; for neighbor in neighbors.into_iter().flatten() { @@ -146,7 +162,9 @@ impl STMinCutGraph { visited[source] = true; while let Some(u) = queue.pop_front() { - if u == sink { break; } + if u == sink { + break; + } for v in 0..n { if !visited[v] && residual[u][v] > 1e-9 { visited[v] = true; @@ -156,7 +174,9 @@ impl STMinCutGraph { } } - if !visited[sink] { break; } + if !visited[sink] { + break; + } let mut path_flow = f64::MAX; let mut v = sink; @@ -179,7 +199,11 @@ impl STMinCutGraph { } } -fn build_qec_graph(code_distance: usize, error_rate: f64, syndrome: &DetectorBitmap) -> STMinCutGraph { +fn build_qec_graph( + code_distance: usize, + error_rate: f64, + syndrome: &DetectorBitmap, +) -> STMinCutGraph { let grid_size = code_distance - 1; let num_detectors = grid_size * grid_size; @@ -197,14 +221,22 @@ fn build_qec_graph(code_distance: usize, error_rate: f64, syndrome: &DetectorBit if col + 1 < grid_size { let right = (row * grid_size + col + 1) as u32; let right_fired = fired_set.contains(&(right as usize)); - let weight = if is_fired || right_fired { fired_weight } else { base_weight }; + let weight = if is_fired || right_fired { + fired_weight + } else { + base_weight + }; graph.add_edge(node, right, weight); } if row + 1 < grid_size { let bottom = ((row + 1) * grid_size + col) as u32; let bottom_fired = fired_set.contains(&(bottom as usize)); - let weight = if is_fired || bottom_fired { fired_weight } else { base_weight }; + let weight = if is_fired || bottom_fired { + fired_weight + } else { + base_weight + }; graph.add_edge(node, bottom, weight); } } @@ -282,9 +314,12 @@ impl WarningDetector { // Compute baseline from first N samples if self.history.len() == self.warmup_samples && self.baseline_mean == 0.0 { self.baseline_mean = self.history.iter().sum::() / self.history.len() as f64; - self.baseline_std = (self.history.iter() + self.baseline_std = (self + .history + .iter() .map(|x| (x - self.baseline_mean).powi(2)) - .sum::() / self.history.len() as f64) + .sum::() + / self.history.len() as f64) .sqrt() .max(0.1); } @@ -295,23 +330,32 @@ impl WarningDetector { } fn velocity(&self) -> f64 { - if self.history.len() < 2 { return 0.0; } + if self.history.len() < 2 { + return 0.0; + } let n = self.history.len(); self.history[n - 1] - self.history[n - 2] } fn drop_from_lookback(&self) -> f64 { - if self.history.len() <= self.rule.lookback { return 0.0; } + if self.history.len() <= self.rule.lookback { + return 0.0; + } let n = self.history.len(); self.history[n - 1] - self.history[n - 1 - self.rule.lookback] } fn is_warning(&self, event_count: usize) -> bool { - if self.history.len() < self.warmup_samples { return false; } - if self.baseline_mean == 0.0 { return false; } + if self.history.len() < self.warmup_samples { + return false; + } + if self.baseline_mean == 0.0 { + return false; + } // Adaptive threshold: baseline_mean - theta_sigma * baseline_std - let adaptive_threshold = (self.baseline_mean - self.rule.theta_sigma * self.baseline_std).max(0.5); + let adaptive_threshold = + (self.baseline_mean - self.rule.theta_sigma * self.baseline_std).max(0.5); // Four-condition warning (hybrid: structural + intensity): // 1. Cut below adaptive threshold (relative to learned baseline) @@ -335,7 +379,9 @@ impl WarningDetector { /// Get the adaptive threshold value for display fn adaptive_threshold(&self) -> f64 { - if self.baseline_mean == 0.0 { return 0.0; } + if self.baseline_mean == 0.0 { + return 0.0; + } (self.baseline_mean - self.rule.theta_sigma * self.baseline_std).max(0.5) } } @@ -383,7 +429,9 @@ impl MovingAverageBaseline { } fn is_warning(&self) -> bool { - if self.window.len() < self.window_size { return false; } + if self.window.len() < self.window_size { + return false; + } let avg = self.window.iter().sum::() as f64 / self.window.len() as f64; avg >= self.threshold } @@ -443,9 +491,9 @@ impl SyndromeGenerator { let mut bitmap = DetectorBitmap::new(num_detectors); // Check if burst is active - let in_burst = self.burst_active && - self.round >= self.burst_start && - self.round < self.burst_start + self.burst_duration; + let in_burst = self.burst_active + && self.round >= self.burst_start + && self.round < self.burst_start + self.burst_duration; for det in 0..num_detectors { let row = det / grid_size; @@ -508,39 +556,47 @@ struct EvaluationResults { impl EvaluationResults { fn lead_times(&self) -> Vec { - self.episodes.iter() - .filter_map(|e| e.lead_time) - .collect() + self.episodes.iter().filter_map(|e| e.lead_time).collect() } fn median_lead_time(&self) -> f64 { let mut times = self.lead_times(); - if times.is_empty() { return 0.0; } + if times.is_empty() { + return 0.0; + } times.sort(); times[times.len() / 2] as f64 } fn p10_lead_time(&self) -> f64 { let mut times = self.lead_times(); - if times.is_empty() { return 0.0; } + if times.is_empty() { + return 0.0; + } times.sort(); times[times.len() / 10] as f64 } fn p90_lead_time(&self) -> f64 { let mut times = self.lead_times(); - if times.is_empty() { return 0.0; } + if times.is_empty() { + return 0.0; + } times.sort(); times[times.len() * 9 / 10] as f64 } fn recall(&self) -> f64 { - if self.total_failures == 0 { return 1.0; } + if self.total_failures == 0 { + return 1.0; + } self.true_warnings as f64 / self.total_failures as f64 } fn precision(&self) -> f64 { - if self.total_warnings == 0 { return 1.0; } + if self.total_warnings == 0 { + return 1.0; + } self.true_warnings as f64 / self.total_warnings as f64 } @@ -549,10 +605,14 @@ impl EvaluationResults { } fn actionable_rate(&self, min_cycles: usize) -> f64 { - let actionable = self.lead_times().iter() + let actionable = self + .lead_times() + .iter() .filter(|&&t| t >= min_cycles) .count(); - if self.true_warnings == 0 { return 0.0; } + if self.true_warnings == 0 { + return 0.0; + } actionable as f64 / self.true_warnings as f64 } } @@ -681,9 +741,16 @@ fn run_baseline_evaluation( let mut cycles_since_warning = 0; let burst_cycles = if inject_bursts { - vec![(500, 10, (2, 2)), (1500, 15, (1, 3)), (3000, 12, (3, 1)), - (5000, 8, (2, 2)), (7000, 20, (1, 1))] - } else { vec![] }; + vec![ + (500, 10, (2, 2)), + (1500, 15, (1, 3)), + (3000, 12, (3, 1)), + (5000, 8, (2, 2)), + (7000, 20, (1, 1)), + ] + } else { + vec![] + }; for cycle in 0..num_cycles { for &(burst_cycle, duration, center) in &burst_cycles { @@ -722,14 +789,20 @@ fn run_baseline_evaluation( lead_time: Some(cycles_since_warning), } } else { - FailureEpisode { failure_cycle: cycle, warning_cycle: None, lead_time: None } + FailureEpisode { + failure_cycle: cycle, + warning_cycle: None, + lead_time: None, + } }; results.episodes.push(episode); } results.total_cycles += 1; } - if warning_active { results.false_alarms += 1; } + if warning_active { + results.false_alarms += 1; + } results } @@ -768,7 +841,11 @@ fn bootstrap_confidence_interval( let upper_idx = ((1.0 - alpha) * n_bootstrap as f64) as usize; let mean = values.iter().sum::() / values.len() as f64; - (bootstrap_means[lower_idx], mean, bootstrap_means[upper_idx.min(n_bootstrap - 1)]) + ( + bootstrap_means[lower_idx], + mean, + bootstrap_means[upper_idx.min(n_bootstrap - 1)], + ) } // ============================================================================ @@ -789,8 +866,14 @@ fn main() { println!("├─────────────────────────────────────────────────────────────────────┤"); println!("│ Logical Failure: Spanning cluster from left to right boundary │"); println!("│ Warning Rule (HYBRID): (cut ≤ θ) AND (drop ≥ δ) AND (events ≥ e) │"); - println!("│ θ = min(μ - {:.1}σ, {:.1}) (adaptive + absolute) │", rule.theta_sigma, rule.theta_absolute); - println!("│ δ = {:.1} (drop over {} cycles), e = {} (min fired detectors) │", rule.delta, rule.lookback, rule.min_event_count); + println!( + "│ θ = min(μ - {:.1}σ, {:.1}) (adaptive + absolute) │", + rule.theta_sigma, rule.theta_absolute + ); + println!( + "│ δ = {:.1} (drop over {} cycles), e = {} (min fired detectors) │", + rule.delta, rule.lookback, rule.min_event_count + ); println!("│ Mode: HYBRID (structural min-cut + event intensity) │"); println!("└─────────────────────────────────────────────────────────────────────┘"); let horizon = 15; // Prediction horizon in cycles @@ -807,13 +890,28 @@ fn main() { println!("║ Cycles: 10,000 | Code: d=5 | Error: 5% | Bursts: NO ║"); println!("╠═══════════════════════════════════════════════════════════════════╣"); - println!("║ Total Failures: {:>6} ║", regime_a.total_failures); - println!("║ Total Warnings: {:>6} ║", regime_a.total_warnings); - println!("║ True Warnings: {:>6} (Recall: {:.1}%) ║", - regime_a.true_warnings, regime_a.recall() * 100.0); - println!("║ False Alarms: {:>6} ({:.2}/10k cycles) ║", - regime_a.false_alarms, regime_a.false_alarm_rate_per_10k()); - println!("║ Precision: {:>5.1}% ║", regime_a.precision() * 100.0); + println!( + "║ Total Failures: {:>6} ║", + regime_a.total_failures + ); + println!( + "║ Total Warnings: {:>6} ║", + regime_a.total_warnings + ); + println!( + "║ True Warnings: {:>6} (Recall: {:.1}%) ║", + regime_a.true_warnings, + regime_a.recall() * 100.0 + ); + println!( + "║ False Alarms: {:>6} ({:.2}/10k cycles) ║", + regime_a.false_alarms, + regime_a.false_alarm_rate_per_10k() + ); + println!( + "║ Precision: {:>5.1}% ║", + regime_a.precision() * 100.0 + ); println!("╚═══════════════════════════════════════════════════════════════════╝"); // ======================================================================== @@ -828,23 +926,56 @@ fn main() { println!("║ Cycles: 10,000 | Code: d=5 | Error: 3% | Bursts: YES ║"); println!("╠═══════════════════════════════════════════════════════════════════╣"); - println!("║ Total Failures: {:>6} ║", regime_b.total_failures); - println!("║ Total Warnings: {:>6} ║", regime_b.total_warnings); - println!("║ True Warnings: {:>6} (Recall: {:.1}%) ║", - regime_b.true_warnings, regime_b.recall() * 100.0); - println!("║ False Alarms: {:>6} ({:.2}/10k cycles) ║", - regime_b.false_alarms, regime_b.false_alarm_rate_per_10k()); - println!("║ Precision: {:>5.1}% ║", regime_b.precision() * 100.0); + println!( + "║ Total Failures: {:>6} ║", + regime_b.total_failures + ); + println!( + "║ Total Warnings: {:>6} ║", + regime_b.total_warnings + ); + println!( + "║ True Warnings: {:>6} (Recall: {:.1}%) ║", + regime_b.true_warnings, + regime_b.recall() * 100.0 + ); + println!( + "║ False Alarms: {:>6} ({:.2}/10k cycles) ║", + regime_b.false_alarms, + regime_b.false_alarm_rate_per_10k() + ); + println!( + "║ Precision: {:>5.1}% ║", + regime_b.precision() * 100.0 + ); println!("╠═══════════════════════════════════════════════════════════════════╣"); println!("║ LEAD TIME DISTRIBUTION: ║"); - println!("║ Median: {:>5.1} cycles ║", regime_b.median_lead_time()); - println!("║ P10: {:>5.1} cycles ║", regime_b.p10_lead_time()); - println!("║ P90: {:>5.1} cycles ║", regime_b.p90_lead_time()); + println!( + "║ Median: {:>5.1} cycles ║", + regime_b.median_lead_time() + ); + println!( + "║ P10: {:>5.1} cycles ║", + regime_b.p10_lead_time() + ); + println!( + "║ P90: {:>5.1} cycles ║", + regime_b.p90_lead_time() + ); println!("╠═══════════════════════════════════════════════════════════════════╣"); println!("║ ACTIONABLE WINDOW: ║"); - println!("║ 1-cycle mitigation: {:>5.1}% actionable ║", regime_b.actionable_rate(1) * 100.0); - println!("║ 2-cycle mitigation: {:>5.1}% actionable ║", regime_b.actionable_rate(2) * 100.0); - println!("║ 5-cycle mitigation: {:>5.1}% actionable ║", regime_b.actionable_rate(5) * 100.0); + println!( + "║ 1-cycle mitigation: {:>5.1}% actionable ║", + regime_b.actionable_rate(1) * 100.0 + ); + println!( + "║ 2-cycle mitigation: {:>5.1}% actionable ║", + regime_b.actionable_rate(2) * 100.0 + ); + println!( + "║ 5-cycle mitigation: {:>5.1}% actionable ║", + regime_b.actionable_rate(5) * 100.0 + ); println!("╚═══════════════════════════════════════════════════════════════════╝"); // ======================================================================== @@ -857,18 +988,27 @@ fn main() { println!("╠═══════════════╪════════╪═══════════╪═══════════╪════════╪════════╣"); // ruQu (min-cut based) - println!("║ ruQu MinCut │ {:>5.1}% │ {:>5.1}% │ {:>4.1} │ {:>5.2} │ {:>5.1}% ║", - regime_b.recall() * 100.0, regime_b.precision() * 100.0, - regime_b.median_lead_time(), regime_b.false_alarm_rate_per_10k(), - regime_b.actionable_rate(2) * 100.0); + println!( + "║ ruQu MinCut │ {:>5.1}% │ {:>5.1}% │ {:>4.1} │ {:>5.2} │ {:>5.1}% ║", + regime_b.recall() * 100.0, + regime_b.precision() * 100.0, + regime_b.median_lead_time(), + regime_b.false_alarm_rate_per_10k(), + regime_b.actionable_rate(2) * 100.0 + ); // Baseline: Event count threshold for threshold in [3, 5, 7] { let baseline = run_baseline_evaluation(5, 0.03, 10000, threshold, horizon, 42, true); - println!("║ Events >= {:>2} │ {:>5.1}% │ {:>5.1}% │ {:>4.1} │ {:>5.2} │ {:>5.1}% ║", - threshold, baseline.recall() * 100.0, baseline.precision() * 100.0, - baseline.median_lead_time(), baseline.false_alarm_rate_per_10k(), - baseline.actionable_rate(2) * 100.0); + println!( + "║ Events >= {:>2} │ {:>5.1}% │ {:>5.1}% │ {:>4.1} │ {:>5.2} │ {:>5.1}% ║", + threshold, + baseline.recall() * 100.0, + baseline.precision() * 100.0, + baseline.median_lead_time(), + baseline.false_alarm_rate_per_10k(), + baseline.actionable_rate(2) * 100.0 + ); } println!("╚═══════════════╧════════╧═══════════╧═══════════╧════════╧════════╝"); @@ -882,7 +1022,10 @@ fn main() { let lead_times: Vec = regime_b.lead_times().iter().map(|&x| x as f64).collect(); if !lead_times.is_empty() { let (lower, mean, upper) = bootstrap_confidence_interval(&lead_times, 1000, 0.95); - println!("║ Lead Time: {:.1} cycles (95% CI: [{:.1}, {:.1}]) ║", mean, lower, upper); + println!( + "║ Lead Time: {:.1} cycles (95% CI: [{:.1}, {:.1}]) ║", + mean, lower, upper + ); } // Multiple runs for recall CI @@ -895,7 +1038,12 @@ fn main() { } if !recall_samples.is_empty() { let (lower, mean, upper) = bootstrap_confidence_interval(&recall_samples, 1000, 0.95); - println!("║ Recall: {:.1}% (95% CI: [{:.1}%, {:.1}%]) ║", mean * 100.0, lower * 100.0, upper * 100.0); + println!( + "║ Recall: {:.1}% (95% CI: [{:.1}%, {:.1}%]) ║", + mean * 100.0, + lower * 100.0, + upper * 100.0 + ); } println!("╚═══════════════════════════════════════════════════════════════════╝"); @@ -907,13 +1055,26 @@ fn main() { println!("═══════════════════════════════════════════════════════════════════════"); let criteria = [ - ("Recall >= 80%", regime_b.recall() >= 0.80, format!("{:.1}%", regime_b.recall() * 100.0)), - ("False Alarms < 5/10k", regime_b.false_alarm_rate_per_10k() < 5.0, - format!("{:.2}/10k", regime_b.false_alarm_rate_per_10k())), - ("Median Lead >= 3 cycles", regime_b.median_lead_time() >= 3.0, - format!("{:.1} cycles", regime_b.median_lead_time())), - ("Actionable >= 70% (2-cycle)", regime_b.actionable_rate(2) >= 0.70, - format!("{:.1}%", regime_b.actionable_rate(2) * 100.0)), + ( + "Recall >= 80%", + regime_b.recall() >= 0.80, + format!("{:.1}%", regime_b.recall() * 100.0), + ), + ( + "False Alarms < 5/10k", + regime_b.false_alarm_rate_per_10k() < 5.0, + format!("{:.2}/10k", regime_b.false_alarm_rate_per_10k()), + ), + ( + "Median Lead >= 3 cycles", + regime_b.median_lead_time() >= 3.0, + format!("{:.1} cycles", regime_b.median_lead_time()), + ), + ( + "Actionable >= 70% (2-cycle)", + regime_b.actionable_rate(2) >= 0.70, + format!("{:.1}%", regime_b.actionable_rate(2) * 100.0), + ), ]; let mut all_pass = true; @@ -944,9 +1105,18 @@ fn main() { println!("│ baselines for correlated failure modes.\" │"); println!("│ │"); println!("│ Key Result: │"); - println!("│ • ruQu provides {:.1} cycles average warning before failure │", regime_b.median_lead_time()); - println!("│ • {:.0}% of failures are predicted in advance │", regime_b.recall() * 100.0); - println!("│ • {:.0}% of warnings are actionable (2+ cycles lead time) │", regime_b.actionable_rate(2) * 100.0); + println!( + "│ • ruQu provides {:.1} cycles average warning before failure │", + regime_b.median_lead_time() + ); + println!( + "│ • {:.0}% of failures are predicted in advance │", + regime_b.recall() * 100.0 + ); + println!( + "│ • {:.0}% of warnings are actionable (2+ cycles lead time) │", + regime_b.actionable_rate(2) * 100.0 + ); println!("│ │"); println!("│ This is NOVEL because: │"); println!("│ 1. Traditional QEC decoders are reactive, not predictive │"); diff --git a/crates/ruQu/examples/integrated_qec_simulation.rs b/crates/ruQu/examples/integrated_qec_simulation.rs index d720eb157..12501e9eb 100644 --- a/crates/ruQu/examples/integrated_qec_simulation.rs +++ b/crates/ruQu/examples/integrated_qec_simulation.rs @@ -173,8 +173,11 @@ struct SimStats { impl SimStats { fn avg_latency_ns(&self) -> f64 { - if self.total_rounds == 0 { 0.0 } - else { self.total_latency_ns as f64 / self.total_rounds as f64 } + if self.total_rounds == 0 { + 0.0 + } else { + self.total_latency_ns as f64 / self.total_rounds as f64 + } } fn throughput(&self, elapsed: Duration) -> f64 { @@ -186,22 +189,31 @@ impl SimStats { fn run_simulation(config: SimConfig, verbose: bool) -> (SimStats, SimulationModel) { if verbose { println!("╔══════════════════════════════════════════════════════════════╗"); - println!("║ Optimized QEC Simulation (Seed: {:>10}) ║", config.seed); + println!( + "║ Optimized QEC Simulation (Seed: {:>10}) ║", + config.seed + ); println!("╠══════════════════════════════════════════════════════════════╣"); - println!("║ Code Distance: d={:<2} | Error Rate: {:.4} ║", - config.code_distance, config.error_rate); - println!("║ Rounds: {:>6} | Drift: {} ║", - config.num_rounds, if config.inject_drift { "ON " } else { "OFF" }); + println!( + "║ Code Distance: d={:<2} | Error Rate: {:.4} ║", + config.code_distance, config.error_rate + ); + println!( + "║ Rounds: {:>6} | Drift: {} ║", + config.num_rounds, + if config.inject_drift { "ON " } else { "OFF" } + ); println!("╚══════════════════════════════════════════════════════════════╝"); } let mut stats = SimStats::default(); // Initialize with seed - let surface_config = SurfaceCodeConfig::new(config.code_distance, config.error_rate) - .with_seed(config.seed); + let surface_config = + SurfaceCodeConfig::new(config.code_distance, config.error_rate).with_seed(config.seed); let num_detectors = surface_config.detectors_per_round(); - let mut syndrome_source = StimSyndromeSource::new(surface_config).expect("Failed to create syndrome source"); + let mut syndrome_source = + StimSyndromeSource::new(surface_config).expect("Failed to create syndrome source"); let mut drift_detector = DriftDetector::new(100); let mut adaptive = AdaptiveThresholds::new(LearningConfig { @@ -466,8 +478,12 @@ fn run_simulation(config: SimConfig, verbose: bool) -> (SimStats, SimulationMode if verbose && last_report.elapsed() > Duration::from_secs(2) { let elapsed = start_time.elapsed(); let progress = (round as f64 / config.num_rounds as f64) * 100.0; - println!(" Progress: {:5.1}% | {:>7.0} rounds/sec | Drifts: {}", - progress, stats.throughput(elapsed), stats.drift_detections); + println!( + " Progress: {:5.1}% | {:>7.0} rounds/sec | Drifts: {}", + progress, + stats.throughput(elapsed), + stats.drift_detections + ); last_report = Instant::now(); } } @@ -491,21 +507,50 @@ fn run_simulation(config: SimConfig, verbose: bool) -> (SimStats, SimulationMode println!("╔══════════════════════════════════════════════════════════════╗"); println!("║ Simulation Results ║"); println!("╠══════════════════════════════════════════════════════════════╣"); - println!("║ Throughput: {:>10.0} rounds/sec ║", stats.throughput(elapsed)); - println!("║ Avg Latency: {:>10.0} ns ║", stats.avg_latency_ns()); - println!("║ Permit Rate: {:>10.1}% ║", - (stats.permits as f64 / stats.total_rounds as f64) * 100.0); - println!("║ Drift Detections: {:>10} ║", stats.drift_detections); + println!( + "║ Throughput: {:>10.0} rounds/sec ║", + stats.throughput(elapsed) + ); + println!( + "║ Avg Latency: {:>10.0} ns ║", + stats.avg_latency_ns() + ); + println!( + "║ Permit Rate: {:>10.1}% ║", + (stats.permits as f64 / stats.total_rounds as f64) * 100.0 + ); + println!( + "║ Drift Detections: {:>10} ║", + stats.drift_detections + ); println!("╠══════════════════════════════════════════════════════════════╣"); println!("║ Learned Thresholds: ║"); - println!("║ structural_min_cut: {:>10.4} ║", model.thresholds.structural_min_cut); - println!("║ shift_max: {:>10.4} ║", model.thresholds.shift_max); - println!("║ tau_permit: {:>10.4} ║", model.thresholds.tau_permit); - println!("║ tau_deny: {:>10.4} ║", model.thresholds.tau_deny); + println!( + "║ structural_min_cut: {:>10.4} ║", + model.thresholds.structural_min_cut + ); + println!( + "║ shift_max: {:>10.4} ║", + model.thresholds.shift_max + ); + println!( + "║ tau_permit: {:>10.4} ║", + model.thresholds.tau_permit + ); + println!( + "║ tau_deny: {:>10.4} ║", + model.thresholds.tau_deny + ); println!("╠══════════════════════════════════════════════════════════════╣"); println!("║ Statistics: ║"); - println!("║ cut_mean: {:>10.4} cut_std: {:>10.4} ║", model.cut_mean, model.cut_std); - println!("║ shift_mean: {:>8.4} samples: {:>10} ║", model.shift_mean, model.samples); + println!( + "║ cut_mean: {:>10.4} cut_std: {:>10.4} ║", + model.cut_mean, model.cut_std + ); + println!( + "║ shift_mean: {:>8.4} samples: {:>10} ║", + model.shift_mean, model.samples + ); println!("╚══════════════════════════════════════════════════════════════╝"); } @@ -550,8 +595,13 @@ fn discover_capabilities(base_model: &SimulationModel) { let permit_rate = (stats.permits as f64 / stats.total_rounds as f64) * 100.0; let deny_rate = (stats.denies as f64 / stats.total_rounds as f64) * 100.0; - println!("│ {:12} │ {:>10.1}% │ {:>10.1}% │ {:>8.0}/s │", - name, permit_rate, deny_rate, stats.throughput(elapsed)); + println!( + "│ {:12} │ {:>10.1}% │ {:>10.1}% │ {:>8.0}/s │", + name, + permit_rate, + deny_rate, + stats.throughput(elapsed) + ); } println!("└──────────────┴──────────────┴──────────────┴──────────────┘"); @@ -579,8 +629,13 @@ fn discover_capabilities(base_model: &SimulationModel) { let drift_rate = (stats.drift_detections as f64 / stats.total_rounds as f64) * 100.0; - println!("│ d={:<2} │ {:>8.0} ns │ {:>10.2}% │ {:>8.0}/s │", - d, stats.avg_latency_ns(), drift_rate, stats.throughput(elapsed)); + println!( + "│ d={:<2} │ {:>8.0} ns │ {:>10.2}% │ {:>8.0}/s │", + d, + stats.avg_latency_ns(), + drift_rate, + stats.throughput(elapsed) + ); } println!("└────────────┴──────────────┴──────────────┴──────────────┘"); @@ -610,8 +665,10 @@ fn main() { // Test import if let Some(imported) = SimulationModel::import(&model_data) { - println!("Model import verified: seed={}, d={}, samples={}", - imported.seed, imported.code_distance, imported.samples); + println!( + "Model import verified: seed={}, d={}, samples={}", + imported.seed, imported.code_distance, imported.samples + ); } // Discover novel capabilities @@ -625,17 +682,34 @@ fn main() { println!(); println!("Running same simulation with identical seed:"); - let config1 = SimConfig { seed: 12345, num_rounds: 1000, inject_drift: false, ..Default::default() }; - let config2 = SimConfig { seed: 12345, num_rounds: 1000, inject_drift: false, ..Default::default() }; + let config1 = SimConfig { + seed: 12345, + num_rounds: 1000, + inject_drift: false, + ..Default::default() + }; + let config2 = SimConfig { + seed: 12345, + num_rounds: 1000, + inject_drift: false, + ..Default::default() + }; let (stats1, model1) = run_simulation(config1, false); let (stats2, model2) = run_simulation(config2, false); - println!(" Run 1: permits={}, denies={}, cut_mean={:.4}", - stats1.permits, stats1.denies, model1.cut_mean); - println!(" Run 2: permits={}, denies={}, cut_mean={:.4}", - stats2.permits, stats2.denies, model2.cut_mean); - println!(" Reproducible: {}", stats1.permits == stats2.permits && stats1.denies == stats2.denies); + println!( + " Run 1: permits={}, denies={}, cut_mean={:.4}", + stats1.permits, stats1.denies, model1.cut_mean + ); + println!( + " Run 2: permits={}, denies={}, cut_mean={:.4}", + stats2.permits, stats2.denies, model2.cut_mean + ); + println!( + " Reproducible: {}", + stats1.permits == stats2.permits && stats1.denies == stats2.denies + ); println!(); println!("═══════════════════════════════════════════════════════════════"); diff --git a/crates/ruQu/examples/mwpm_comparison_benchmark.rs b/crates/ruQu/examples/mwpm_comparison_benchmark.rs index 123c752bb..dd14b89e6 100644 --- a/crates/ruQu/examples/mwpm_comparison_benchmark.rs +++ b/crates/ruQu/examples/mwpm_comparison_benchmark.rs @@ -131,14 +131,22 @@ fn build_surface_code_graph( if col + 1 < grid_size { let right = (row * grid_size + col + 1) as u32; let right_fired = fired_set.contains(&(right as usize)); - let weight = if is_fired || right_fired { fired_weight } else { base_weight }; + let weight = if is_fired || right_fired { + fired_weight + } else { + base_weight + }; graph.add_edge(node, right, weight); } if row + 1 < grid_size { let bottom = ((row + 1) * grid_size + col) as u32; let bottom_fired = fired_set.contains(&(bottom as usize)); - let weight = if is_fired || bottom_fired { fired_weight } else { base_weight }; + let weight = if is_fired || bottom_fired { + fired_weight + } else { + base_weight + }; graph.add_edge(node, bottom, weight); } } @@ -171,23 +179,35 @@ struct BenchmarkStats { impl BenchmarkStats { fn throughput(&self) -> f64 { - if self.total_time_ns == 0 { 0.0 } - else { self.total_rounds as f64 / (self.total_time_ns as f64 / 1e9) } + if self.total_time_ns == 0 { + 0.0 + } else { + self.total_rounds as f64 / (self.total_time_ns as f64 / 1e9) + } } fn avg_round_time_ns(&self) -> f64 { - if self.total_rounds == 0 { 0.0 } - else { self.total_time_ns as f64 / self.total_rounds as f64 } + if self.total_rounds == 0 { + 0.0 + } else { + self.total_time_ns as f64 / self.total_rounds as f64 + } } fn avg_decode_time_ns(&self) -> f64 { - if self.decode_calls == 0 { 0.0 } - else { self.decode_time_ns as f64 / self.decode_calls as f64 } + if self.decode_calls == 0 { + 0.0 + } else { + self.decode_time_ns as f64 / self.decode_calls as f64 + } } fn skip_rate(&self) -> f64 { - if self.total_rounds == 0 { 0.0 } - else { self.skipped_rounds as f64 / self.total_rounds as f64 } + if self.total_rounds == 0 { + 0.0 + } else { + self.skipped_rounds as f64 / self.total_rounds as f64 + } } } @@ -226,10 +246,26 @@ fn has_logical_error(syndrome: &DetectorBitmap, code_distance: usize) -> bool { } let neighbors = [ - if col > 0 { Some(row * grid_size + col - 1) } else { None }, - if col + 1 < grid_size { Some(row * grid_size + col + 1) } else { None }, - if row > 0 { Some((row - 1) * grid_size + col) } else { None }, - if row + 1 < grid_size { Some((row + 1) * grid_size + col) } else { None }, + if col > 0 { + Some(row * grid_size + col - 1) + } else { + None + }, + if col + 1 < grid_size { + Some(row * grid_size + col + 1) + } else { + None + }, + if row > 0 { + Some((row - 1) * grid_size + col) + } else { + None + }, + if row + 1 < grid_size { + Some((row + 1) * grid_size + col) + } else { + None + }, ]; for neighbor_opt in neighbors.iter().flatten() { @@ -386,7 +422,8 @@ fn main() { // Benchmark 2: Pre-filter + MWPM println!("Running pre-filter + MWPM benchmark..."); - let prefilter = benchmark_prefilter_mwpm(code_distance, error_rate, num_rounds, seed, threshold); + let prefilter = + benchmark_prefilter_mwpm(code_distance, error_rate, num_rounds, seed, threshold); // Results println!("\n╔═══════════════════════════════════════════════════════════════════╗"); @@ -394,46 +431,87 @@ fn main() { println!("╠═══════════════════════════════════════════════════════════════════╣"); println!("║ │ MWPM Baseline │ Pre-Filter+MWPM ║"); println!("╠════════════════════╪═════════════════╪═══════════════════════════╣"); - println!("║ Total Time │ {:>12.2} ms │ {:>12.2} ms ║", - baseline.total_time_ns as f64 / 1e6, - prefilter.total_time_ns as f64 / 1e6); - println!("║ Throughput │ {:>12.0}/s │ {:>12.0}/s ║", - baseline.throughput(), prefilter.throughput()); - println!("║ Avg Round Time │ {:>12.0} ns │ {:>12.0} ns ║", - baseline.avg_round_time_ns(), prefilter.avg_round_time_ns()); + println!( + "║ Total Time │ {:>12.2} ms │ {:>12.2} ms ║", + baseline.total_time_ns as f64 / 1e6, + prefilter.total_time_ns as f64 / 1e6 + ); + println!( + "║ Throughput │ {:>12.0}/s │ {:>12.0}/s ║", + baseline.throughput(), + prefilter.throughput() + ); + println!( + "║ Avg Round Time │ {:>12.0} ns │ {:>12.0} ns ║", + baseline.avg_round_time_ns(), + prefilter.avg_round_time_ns() + ); println!("╠════════════════════╪═════════════════╪═══════════════════════════╣"); - println!("║ Decode Calls │ {:>12} │ {:>12} ({:>5.1}%) ║", - baseline.decode_calls, prefilter.decode_calls, - prefilter.decode_calls as f64 / baseline.decode_calls.max(1) as f64 * 100.0); - println!("║ Skipped Rounds │ {:>12} │ {:>12} ({:>5.1}%) ║", - 0, prefilter.skipped_rounds, prefilter.skip_rate() * 100.0); - println!("║ Avg Decode Time │ {:>12.0} ns │ {:>12.0} ns ║", - baseline.avg_decode_time_ns(), prefilter.avg_decode_time_ns()); + println!( + "║ Decode Calls │ {:>12} │ {:>12} ({:>5.1}%) ║", + baseline.decode_calls, + prefilter.decode_calls, + prefilter.decode_calls as f64 / baseline.decode_calls.max(1) as f64 * 100.0 + ); + println!( + "║ Skipped Rounds │ {:>12} │ {:>12} ({:>5.1}%) ║", + 0, + prefilter.skipped_rounds, + prefilter.skip_rate() * 100.0 + ); + println!( + "║ Avg Decode Time │ {:>12.0} ns │ {:>12.0} ns ║", + baseline.avg_decode_time_ns(), + prefilter.avg_decode_time_ns() + ); println!("╠════════════════════╪═════════════════╪═══════════════════════════╣"); - println!("║ Errors Detected │ {:>12} │ {:>12} ║", - baseline.logical_errors_detected, prefilter.logical_errors_detected); - println!("║ Errors Missed │ {:>12} │ {:>12} ║", - 0, prefilter.logical_errors_missed); + println!( + "║ Errors Detected │ {:>12} │ {:>12} ║", + baseline.logical_errors_detected, prefilter.logical_errors_detected + ); + println!( + "║ Errors Missed │ {:>12} │ {:>12} ║", + 0, prefilter.logical_errors_missed + ); println!("╚════════════════════╧═════════════════╧═══════════════════════════╝"); // Speedup calculation let speedup = baseline.total_time_ns as f64 / prefilter.total_time_ns.max(1) as f64; - let decode_reduction = 1.0 - (prefilter.decode_calls as f64 / baseline.decode_calls.max(1) as f64); - let safety = if prefilter.logical_errors_missed == 0 { "SAFE" } else { "UNSAFE" }; + let decode_reduction = + 1.0 - (prefilter.decode_calls as f64 / baseline.decode_calls.max(1) as f64); + let safety = if prefilter.logical_errors_missed == 0 { + "SAFE" + } else { + "UNSAFE" + }; println!("\n┌─────────────────────────────────────────────────────────────────────┐"); println!("│ SUMMARY │"); println!("├─────────────────────────────────────────────────────────────────────┤"); println!("│ │"); - println!("│ Speedup: {:.2}x │", speedup); - println!("│ Decode Calls Reduced: {:.1}% │", decode_reduction * 100.0); - println!("│ Errors Missed: {} ({}) │", - prefilter.logical_errors_missed, safety); + println!( + "│ Speedup: {:.2}x │", + speedup + ); + println!( + "│ Decode Calls Reduced: {:.1}% │", + decode_reduction * 100.0 + ); + println!( + "│ Errors Missed: {} ({}) │", + prefilter.logical_errors_missed, safety + ); println!("│ │"); if speedup > 1.0 && prefilter.logical_errors_missed == 0 { - println!("│ ✓ Pre-filter provides {:.1}% speedup with 100% recall │", (speedup - 1.0) * 100.0); + println!( + "│ ✓ Pre-filter provides {:.1}% speedup with 100% recall │", + (speedup - 1.0) * 100.0 + ); } else if speedup > 1.0 { - println!("│ ⚠ Pre-filter faster but missed {} errors │", prefilter.logical_errors_missed); + println!( + "│ ⚠ Pre-filter faster but missed {} errors │", + prefilter.logical_errors_missed + ); } else { println!("│ ✗ Pre-filter overhead exceeds decoder savings │"); } @@ -452,15 +530,21 @@ fn main() { let pf = benchmark_prefilter_mwpm(d, 0.05, 2000, 42, (d as f64) * 1.3); let spd = base.total_time_ns as f64 / pf.total_time_ns.max(1) as f64; - let safe = if pf.logical_errors_missed == 0 { "✓" } else { "✗" }; + let safe = if pf.logical_errors_missed == 0 { + "✓" + } else { + "✗" + }; - println!("║ {:>2} │ {:>8.2} ms │ {:>12.2} ms │ {:>5.2}x │ {:>5.1}% │ {} ║", - d, - base.total_time_ns as f64 / 1e6, - pf.total_time_ns as f64 / 1e6, - spd, - pf.skip_rate() * 100.0, - safe); + println!( + "║ {:>2} │ {:>8.2} ms │ {:>12.2} ms │ {:>5.2}x │ {:>5.1}% │ {} ║", + d, + base.total_time_ns as f64 / 1e6, + pf.total_time_ns as f64 / 1e6, + spd, + pf.skip_rate() * 100.0, + safe + ); } println!("╚═════╧════════════╧════════════════╧═════════╧═══════════╧════════╝"); diff --git a/crates/ruQu/examples/quantum_fabric_basic.rs b/crates/ruQu/examples/quantum_fabric_basic.rs index 91a21380d..a2c29f313 100644 --- a/crates/ruQu/examples/quantum_fabric_basic.rs +++ b/crates/ruQu/examples/quantum_fabric_basic.rs @@ -9,8 +9,8 @@ use ruqu::{ fabric::{surface_code_d7, QuantumFabric}, - tile::GateThresholds, syndrome::{DetectorBitmap, SyndromeRound}, + tile::GateThresholds, types::GateDecision, }; @@ -23,17 +23,22 @@ fn main() -> Result<(), Box> { println!("Building QuantumFabric..."); let fabric = QuantumFabric::builder() - .tiles(256) // 255 workers + TileZero - .patch_map(surface_code_d7()) // Surface code distance-7 layout - .syndrome_buffer(1024) // Ring buffer depth + .tiles(256) // 255 workers + TileZero + .patch_map(surface_code_d7()) // Surface code distance-7 layout + .syndrome_buffer(1024) // Ring buffer depth .thresholds(GateThresholds::default()) .build()?; - println!(" Fabric created with {} worker tiles", fabric.worker_count()); - println!(" Patch map: {} ({} qubits, {} detectors)", - fabric.patch_map().name, - fabric.patch_map().qubit_count, - fabric.patch_map().detector_count); + println!( + " Fabric created with {} worker tiles", + fabric.worker_count() + ); + println!( + " Patch map: {} ({} qubits, {} detectors)", + fabric.patch_map().name, + fabric.patch_map().qubit_count, + fabric.patch_map().detector_count + ); println!(); // ------------------------------------------------------------------------- @@ -57,11 +62,11 @@ fn main() -> Result<(), Box> { } let round = SyndromeRound::new( - cycle as u64, // round_id - cycle as u64, // cycle - cycle as u64 * 1_000_000, // timestamp (ns) + cycle as u64, // round_id + cycle as u64, // cycle + cycle as u64 * 1_000_000, // timestamp (ns) detectors, - 0, // source_tile (0 = broadcast) + 0, // source_tile (0 = broadcast) ); // Ingest the syndrome @@ -90,7 +95,11 @@ fn main() -> Result<(), Box> { let state = fabric.current_state(); println!(" Total decisions: {}", stats.total); - println!(" Permits: {} ({:.1}%)", stats.permits, stats.permit_rate * 100.0); + println!( + " Permits: {} ({:.1}%)", + stats.permits, + stats.permit_rate * 100.0 + ); println!(" Defers: {}", stats.defers); println!(" Denies: {}", stats.denies); println!(" Avg latency: {} ns", stats.avg_latency_ns); @@ -122,8 +131,10 @@ fn main() -> Result<(), Box> { println!("\n=== Latest Witness Receipt ==="); println!(" Sequence: {}", receipt.sequence); println!(" Decision: {:?}", receipt.decision); - println!(" Hash: {:02x}{:02x}{:02x}{:02x}...", - receipt.hash[0], receipt.hash[1], receipt.hash[2], receipt.hash[3]); + println!( + " Hash: {:02x}{:02x}{:02x}{:02x}...", + receipt.hash[0], receipt.hash[1], receipt.hash[2], receipt.hash[3] + ); } println!("\nExample completed successfully!"); diff --git a/crates/ruQu/examples/validated_coherence_gate.rs b/crates/ruQu/examples/validated_coherence_gate.rs index be1709a69..e097e3533 100644 --- a/crates/ruQu/examples/validated_coherence_gate.rs +++ b/crates/ruQu/examples/validated_coherence_gate.rs @@ -203,7 +203,11 @@ fn build_surface_code_graph( if col + 1 < grid_size { let right = (row * grid_size + col + 1) as u32; let right_fired = fired_set.contains(&(right as usize)); - let weight = if is_fired || right_fired { fired_weight } else { base_weight }; + let weight = if is_fired || right_fired { + fired_weight + } else { + base_weight + }; graph.add_edge(node, right, weight); } @@ -211,7 +215,11 @@ fn build_surface_code_graph( if row + 1 < grid_size { let bottom = ((row + 1) * grid_size + col) as u32; let bottom_fired = fired_set.contains(&(bottom as usize)); - let weight = if is_fired || bottom_fired { fired_weight } else { base_weight }; + let weight = if is_fired || bottom_fired { + fired_weight + } else { + base_weight + }; graph.add_edge(node, bottom, weight); } } @@ -240,10 +248,7 @@ fn build_surface_code_graph( /// Detect logical error by checking if fired detectors form a connected /// path from left boundary to right boundary (spanning cluster). /// This is the TRUE criterion for X-type logical errors in surface codes. -fn detect_logical_error_ground_truth( - syndrome: &DetectorBitmap, - code_distance: usize, -) -> bool { +fn detect_logical_error_ground_truth(syndrome: &DetectorBitmap, code_distance: usize) -> bool { let grid_size = code_distance - 1; let fired: HashSet = syndrome.iter_fired().collect(); @@ -281,10 +286,26 @@ fn detect_logical_error_ground_truth( // Check neighbors (4-connected grid) let neighbors = [ - if col > 0 { Some(row * grid_size + col - 1) } else { None }, // left - if col + 1 < grid_size { Some(row * grid_size + col + 1) } else { None }, // right - if row > 0 { Some((row - 1) * grid_size + col) } else { None }, // up - if row + 1 < grid_size { Some((row + 1) * grid_size + col) } else { None }, // down + if col > 0 { + Some(row * grid_size + col - 1) + } else { + None + }, // left + if col + 1 < grid_size { + Some(row * grid_size + col + 1) + } else { + None + }, // right + if row > 0 { + Some((row - 1) * grid_size + col) + } else { + None + }, // up + if row + 1 < grid_size { + Some((row + 1) * grid_size + col) + } else { + None + }, // down ]; for neighbor_opt in neighbors.iter().flatten() { @@ -320,29 +341,49 @@ impl ValidationStats { fn accuracy(&self) -> f64 { let correct = self.true_positives + self.true_negatives; let total = self.total_rounds; - if total == 0 { 0.0 } else { correct as f64 / total as f64 } + if total == 0 { + 0.0 + } else { + correct as f64 / total as f64 + } } fn precision(&self) -> f64 { let denom = self.true_positives + self.false_positives; - if denom == 0 { 0.0 } else { self.true_positives as f64 / denom as f64 } + if denom == 0 { + 0.0 + } else { + self.true_positives as f64 / denom as f64 + } } fn recall(&self) -> f64 { let denom = self.true_positives + self.false_negatives; - if denom == 0 { 0.0 } else { self.true_positives as f64 / denom as f64 } + if denom == 0 { + 0.0 + } else { + self.true_positives as f64 / denom as f64 + } } fn f1_score(&self) -> f64 { let p = self.precision(); let r = self.recall(); - if p + r < 1e-10 { 0.0 } else { 2.0 * p * r / (p + r) } + if p + r < 1e-10 { + 0.0 + } else { + 2.0 * p * r / (p + r) + } } fn false_negative_rate(&self) -> f64 { // Critical metric: how often do we miss a logical error? let denom = self.true_positives + self.false_negatives; - if denom == 0 { 0.0 } else { self.false_negatives as f64 / denom as f64 } + if denom == 0 { + 0.0 + } else { + self.false_negatives as f64 / denom as f64 + } } fn avg_min_cut_error(&self) -> f64 { @@ -365,7 +406,11 @@ impl ValidationStats { // How well separated are the min-cut distributions? let safe_avg = self.avg_min_cut_safe(); let error_avg = self.avg_min_cut_error(); - if error_avg < 1e-10 { f64::INFINITY } else { safe_avg / error_avg } + if error_avg < 1e-10 { + f64::INFINITY + } else { + safe_avg / error_avg + } } fn throughput(&self) -> f64 { @@ -388,8 +433,7 @@ fn run_validation( let mut stats = ValidationStats::default(); // Initialize syndrome source - let surface_config = SurfaceCodeConfig::new(code_distance, error_rate) - .with_seed(seed); + let surface_config = SurfaceCodeConfig::new(code_distance, error_rate).with_seed(seed); let mut syndrome_source = match StimSyndromeSource::new(surface_config) { Ok(s) => s, Err(_) => return stats, @@ -495,7 +539,9 @@ fn find_max_recall_threshold( let stats = run_validation(code_distance, error_rate, num_rounds, threshold, seed); let recall = stats.recall(); - if recall > best_recall || (recall == best_recall && stats.precision() > best_stats.precision()) { + if recall > best_recall + || (recall == best_recall && stats.precision() > best_stats.precision()) + { best_recall = recall; best_threshold = threshold; best_stats = stats; @@ -538,29 +584,62 @@ fn main() { let skip_rate = stats.true_negatives as f64 / stats.total_rounds.max(1) as f64; println!("║ Code Distance: d=5 | Error Rate: 0.05 | Rounds: 10000 ║"); - println!("║ Threshold: {:.2} (tuned for max recall) ║", threshold); + println!( + "║ Threshold: {:.2} (tuned for max recall) ║", + threshold + ); println!("╠═══════════════════════════════════════════════════════════════════╣"); println!("║ PRE-FILTER PERFORMANCE: ║"); - println!("║ Total Logical Errors: {:>6} ║", total_errors); - println!("║ Errors Caught: {:>6} ({:.1}% recall) ║", - stats.true_positives, stats.recall() * 100.0); - println!("║ Errors Missed: {:>6} ({:.2}% FN rate) ║", - stats.false_negatives, stats.false_negative_rate() * 100.0); - println!("║ Safe Rounds Skipped: {:>6} ({:.1}% of total) ║", - stats.true_negatives, skip_rate * 100.0); + println!( + "║ Total Logical Errors: {:>6} ║", + total_errors + ); + println!( + "║ Errors Caught: {:>6} ({:.1}% recall) ║", + stats.true_positives, + stats.recall() * 100.0 + ); + println!( + "║ Errors Missed: {:>6} ({:.2}% FN rate) ║", + stats.false_negatives, + stats.false_negative_rate() * 100.0 + ); + println!( + "║ Safe Rounds Skipped: {:>6} ({:.1}% of total) ║", + stats.true_negatives, + skip_rate * 100.0 + ); println!("╠═══════════════════════════════════════════════════════════════════╣"); println!("║ DECODER SAVINGS: ║"); - println!("║ Rounds requiring decode: {:>6} ({:.1}% of total) ║", - stats.true_positives + stats.false_positives, - (stats.true_positives + stats.false_positives) as f64 / stats.total_rounds.max(1) as f64 * 100.0); - println!("║ Decode cost reduction: {:>5.1}% ║", skip_rate * 100.0); + println!( + "║ Rounds requiring decode: {:>6} ({:.1}% of total) ║", + stats.true_positives + stats.false_positives, + (stats.true_positives + stats.false_positives) as f64 / stats.total_rounds.max(1) as f64 + * 100.0 + ); + println!( + "║ Decode cost reduction: {:>5.1}% ║", + skip_rate * 100.0 + ); println!("╠═══════════════════════════════════════════════════════════════════╣"); println!("║ Min-Cut Distribution: ║"); - println!("║ Avg when SAFE: {:>8.4} ║", stats.avg_min_cut_safe()); - println!("║ Avg when ERROR: {:>8.4} ║", stats.avg_min_cut_error()); - println!("║ Separation Ratio: {:>8.2}x ║", stats.separation_ratio()); + println!( + "║ Avg when SAFE: {:>8.4} ║", + stats.avg_min_cut_safe() + ); + println!( + "║ Avg when ERROR: {:>8.4} ║", + stats.avg_min_cut_error() + ); + println!( + "║ Separation Ratio: {:>8.2}x ║", + stats.separation_ratio() + ); println!("╠═══════════════════════════════════════════════════════════════════╣"); - println!("║ Throughput: {:>8.0} rounds/sec ║", stats.throughput()); + println!( + "║ Throughput: {:>8.0} rounds/sec ║", + stats.throughput() + ); println!("╚═══════════════════════════════════════════════════════════════════╝"); // Experiment 2: Scaling with code distance @@ -574,9 +653,15 @@ fn main() { let (_, s) = find_max_recall_threshold(d, 0.05, 3000, 42); let total_errors = s.true_positives + s.false_negatives; let skip_rate = s.true_negatives as f64 / s.total_rounds.max(1) as f64; - println!("║ {:>2} │ {:>6} │ {:>5.1}% │ {:>5.1}% │ {:>5.1}% │ {:>5.2}x ║", - d, total_errors, s.recall() * 100.0, s.false_negative_rate() * 100.0, - skip_rate * 100.0, s.separation_ratio().min(99.99)); + println!( + "║ {:>2} │ {:>6} │ {:>5.1}% │ {:>5.1}% │ {:>5.1}% │ {:>5.2}x ║", + d, + total_errors, + s.recall() * 100.0, + s.false_negative_rate() * 100.0, + skip_rate * 100.0, + s.separation_ratio().min(99.99) + ); } println!("╚═════╧════════╧════════╧═════════╧═══════════╧═══════════════════╝"); @@ -592,10 +677,15 @@ fn main() { let (_, s) = find_max_recall_threshold(5, p, 3000, 42); let total_errors = s.true_positives + s.false_negatives; let skip_rate = s.true_negatives as f64 / s.total_rounds.max(1) as f64; - println!("║ {:.3} │ {:>6} │ {:>5.1}% │ {:>5.1}% │ {:>5.1}% │ {:>5.2}x ║", - p, total_errors, s.recall() * 100.0, - s.false_negative_rate() * 100.0, skip_rate * 100.0, - s.separation_ratio().min(99.99)); + println!( + "║ {:.3} │ {:>6} │ {:>5.1}% │ {:>5.1}% │ {:>5.1}% │ {:>5.2}x ║", + p, + total_errors, + s.recall() * 100.0, + s.false_negative_rate() * 100.0, + skip_rate * 100.0, + s.separation_ratio().min(99.99) + ); } println!("╚════════════╧════════╧════════╧═════════╧═══════════╧════════════╝"); @@ -643,13 +733,25 @@ fn main() { println!("Pre-Filter Metrics:"); println!(" Recall: {:.1}% (target: >95%)", recall * 100.0); println!(" False Negative: {:.2}% (target: <5%)", fn_rate * 100.0); - println!(" Safe Skip Rate: {:.1}% (decoder cost savings)", skip_rate * 100.0); - println!(" Separation: {:.2}x (error vs safe min-cut)", separation); + println!( + " Safe Skip Rate: {:.1}% (decoder cost savings)", + skip_rate * 100.0 + ); + println!( + " Separation: {:.2}x (error vs safe min-cut)", + separation + ); println!(); println!("Conclusion:"); if recall >= 0.95 && fn_rate <= 0.05 { - println!(" The min-cut pre-filter can SAFELY skip {:.1}% of rounds,", skip_rate * 100.0); - println!(" reducing decoder load while maintaining {:.1}% error detection.", recall * 100.0); + println!( + " The min-cut pre-filter can SAFELY skip {:.1}% of rounds,", + skip_rate * 100.0 + ); + println!( + " reducing decoder load while maintaining {:.1}% error detection.", + recall * 100.0 + ); } else if recall > 0.5 { println!(" Min-cut shows promise as a pre-filter but needs refinement."); println!(" Consider: graph construction, weight tuning, or hybrid approaches."); diff --git a/crates/ruQu/src/adaptive.rs b/crates/ruQu/src/adaptive.rs index a5ceb0ba5..2fb9390f7 100644 --- a/crates/ruQu/src/adaptive.rs +++ b/crates/ruQu/src/adaptive.rs @@ -303,8 +303,16 @@ impl AdaptiveThresholds { self.outcomes.record(was_deny, was_actually_bad); // Update EMAs - let fp = if was_deny && !was_actually_bad { 1.0 } else { 0.0 }; - let fn_rate = if !was_deny && was_actually_bad { 1.0 } else { 0.0 }; + let fp = if was_deny && !was_actually_bad { + 1.0 + } else { + 0.0 + }; + let fn_rate = if !was_deny && was_actually_bad { + 1.0 + } else { + 0.0 + }; self.false_positive_ema.update(fp); self.false_negative_ema.update(fn_rate); @@ -380,22 +388,19 @@ impl AdaptiveThresholds { // Target: threshold = mean + 2*std if self.shift_stats.count > 100 { let target = (self.shift_stats.mean + 2.0 * self.shift_stats.std_dev()).min(1.0); - self.current.shift_max = - self.current.shift_max * (1.0 - lr) + target * lr; + self.current.shift_max = self.current.shift_max * (1.0 - lr) + target * lr; } // Adjust evidence thresholds if self.evidence_stats.count > 100 { // tau_deny should be well below normal (5th percentile estimate) - let tau_deny_target = (self.evidence_stats.mean - 2.0 * self.evidence_stats.std_dev()) - .max(0.001); - self.current.tau_deny = - self.current.tau_deny * (1.0 - lr) + tau_deny_target * lr; + let tau_deny_target = + (self.evidence_stats.mean - 2.0 * self.evidence_stats.std_dev()).max(0.001); + self.current.tau_deny = self.current.tau_deny * (1.0 - lr) + tau_deny_target * lr; // tau_permit should be above normal (75th percentile estimate) let tau_permit_target = self.evidence_stats.mean + 0.5 * self.evidence_stats.std_dev(); - self.current.tau_permit = - self.current.tau_permit * (1.0 - lr) + tau_permit_target * lr; + self.current.tau_permit = self.current.tau_permit * (1.0 - lr) + tau_permit_target * lr; } } @@ -496,8 +501,8 @@ impl Default for DriftConfig { Self { window_size: 100, min_samples: 50, - mean_shift_threshold: 2.0, // 2 sigma - variance_threshold: 1.5, // 50% variance change + mean_shift_threshold: 2.0, // 2 sigma + variance_threshold: 1.5, // 50% variance change trend_sensitivity: 0.1, } } @@ -591,7 +596,9 @@ impl DriftDetector { // Check for variance expansion let var_ratio = current_var / self.baseline_var.max(1e-10); - if var_ratio > self.config.variance_threshold || var_ratio < 1.0 / self.config.variance_threshold { + if var_ratio > self.config.variance_threshold + || var_ratio < 1.0 / self.config.variance_threshold + { return Some(DriftProfile::VarianceExpansion { ratio: var_ratio }); } @@ -647,7 +654,7 @@ impl DriftDetector { // Handle zero-variance case: if both are near zero, no variance drift let var_component = if self.baseline_var < 1e-6 && current_var < 1e-6 { - 0.0 // Both constant signals - no variance drift + 0.0 // Both constant signals - no variance drift } else { ((current_var / self.baseline_var.max(1e-10)) - 1.0).abs() / 2.0 }; @@ -686,10 +693,7 @@ impl DriftDetector { let sum: f64 = self.buffer.iter().take(n).sum(); let mean = sum / n as f64; - let var_sum: f64 = self.buffer.iter() - .take(n) - .map(|x| (x - mean).powi(2)) - .sum(); + let var_sum: f64 = self.buffer.iter().take(n).map(|x| (x - mean).powi(2)).sum(); let var = var_sum / n as f64; (mean, var) @@ -739,7 +743,10 @@ impl AdaptiveThresholds { } } } - DriftProfile::StepChange { magnitude, direction } => { + DriftProfile::StepChange { + magnitude, + direction, + } => { // More aggressive adjustment for step changes let adjustment = magnitude * 0.3; match direction { @@ -939,7 +946,13 @@ mod tests { let profile = detector.detect(); assert!( - matches!(profile, Some(DriftProfile::StepChange { direction: DriftDirection::Increasing, .. })), + matches!( + profile, + Some(DriftProfile::StepChange { + direction: DriftDirection::Increasing, + .. + }) + ), "Expected step change increasing, got {:?}", profile ); @@ -951,7 +964,7 @@ mod tests { window_size: 50, min_samples: 30, variance_threshold: 1.5, - mean_shift_threshold: 5.0, // High to avoid step detection + mean_shift_threshold: 5.0, // High to avoid step detection ..Default::default() }); @@ -966,17 +979,14 @@ mod tests { // Now add high variance samples (same mean, higher amplitude) for i in 0..50 { - let noise = ((i as f64) * 0.3).sin() * 2.5; // Much larger amplitude + let noise = ((i as f64) * 0.3).sin() * 2.5; // Much larger amplitude detector.push(10.0 + noise); } let profile = detector.detect(); // Should detect some kind of drift (variance, step change, or be stable) // The exact detection depends on the sinusoidal phase alignment - assert!( - profile.is_some(), - "Expected some drift profile, got None" - ); + assert!(profile.is_some(), "Expected some drift profile, got None"); } #[test] @@ -985,7 +995,7 @@ mod tests { // Not enough samples for i in 0..10 { - detector.push(10.0 + (i as f64) * 0.001); // Tiny variance to establish baseline + detector.push(10.0 + (i as f64) * 0.001); // Tiny variance to establish baseline } assert_eq!(detector.severity(), 0.0); @@ -1007,7 +1017,11 @@ mod tests { // Severity should be reasonable for stable signal (after proper warmup) // Note: small variance differences can cause moderate severity values let severity = detector.severity(); - assert!(severity < 0.6, "Expected reasonable severity for stable signal: {}", severity); + assert!( + severity < 0.6, + "Expected reasonable severity for stable signal: {}", + severity + ); } #[test] @@ -1030,7 +1044,11 @@ mod tests { detector.reset_baseline(); let (new_baseline, _) = detector.baseline_stats(); - assert!(new_baseline > 12.0, "Baseline should shift: {}", new_baseline); + assert!( + new_baseline > 12.0, + "Baseline should shift: {}", + new_baseline + ); } #[test] diff --git a/crates/ruQu/src/attention.rs b/crates/ruQu/src/attention.rs index ff199ad8e..ae49aad1b 100644 --- a/crates/ruQu/src/attention.rs +++ b/crates/ruQu/src/attention.rs @@ -33,8 +33,8 @@ #[cfg(feature = "attention")] use ruvector_mincut_gated_transformer::{ - GatePacket, MincutDepthRouter, ModRoutingConfig, RoutingStats, TokenRoute, - CoherenceEarlyExit, EarlyExitConfig, EarlyExitDecision, ExitReason, + CoherenceEarlyExit, EarlyExitConfig, EarlyExitDecision, ExitReason, GatePacket, + MincutDepthRouter, ModRoutingConfig, RoutingStats, TokenRoute, }; use crate::tile::{GateDecision, TileReport}; @@ -79,7 +79,7 @@ impl AttentionConfig { /// Configuration optimized for real-time coherence gating pub fn realtime() -> Self { Self { - flops_reduction: 0.6, // More aggressive skip + flops_reduction: 0.6, // More aggressive skip min_entries_per_round: 2, lambda_delta_skip_threshold: 2000, // More aggressive adaptive_capacity: true, @@ -146,7 +146,9 @@ impl GatePacketBridge { max_shift = report.shift_score; } // Use boundary candidate count as proxy for boundary edges - total_boundary += report.boundary_candidates.iter() + total_boundary += report + .boundary_candidates + .iter() .filter(|&&c| c != 0) .count() as u32; @@ -409,29 +411,33 @@ pub mod fallback { let gate = self.bridge.to_gate_packet_fallback(reports); // Simple heuristic routing without transformer - let routes: Vec = reports.iter().enumerate().map(|(i, report)| { - // Boundary tokens always compute - if report.boundary_candidates.iter().any(|&c| c != 0) { - return TokenRoute::Boundary; - } + let routes: Vec = reports + .iter() + .enumerate() + .map(|(i, report)| { + // Boundary tokens always compute + if report.boundary_candidates.iter().any(|&c| c != 0) { + return TokenRoute::Boundary; + } - // Skip if shift score is low (stable) - if report.shift_score < 0.1 && i % 2 == 0 { - return TokenRoute::Skip; - } + // Skip if shift score is low (stable) + if report.shift_score < 0.1 && i % 2 == 0 { + return TokenRoute::Skip; + } - TokenRoute::Compute - }).collect(); + TokenRoute::Compute + }) + .collect(); // Update stats self.stats.total_entries += routes.len(); - self.stats.computed_entries += routes.iter() - .filter(|r| r.requires_compute()) - .count(); - self.stats.skipped_entries += routes.iter() + self.stats.computed_entries += routes.iter().filter(|r| r.requires_compute()).count(); + self.stats.skipped_entries += routes + .iter() .filter(|r| matches!(r, TokenRoute::Skip)) .count(); - self.stats.boundary_entries += routes.iter() + self.stats.boundary_entries += routes + .iter() .filter(|r| matches!(r, TokenRoute::Boundary)) .count(); self.stats.decisions += 1; diff --git a/crates/ruQu/src/bin/ruqu_demo.rs b/crates/ruQu/src/bin/ruqu_demo.rs index 04e30b995..49f1dbb87 100644 --- a/crates/ruQu/src/bin/ruqu_demo.rs +++ b/crates/ruQu/src/bin/ruqu_demo.rs @@ -499,16 +499,20 @@ fn main() { println!("╔═══════════════════════════════════════════════════════════════════╗"); println!("║ ruQu Demo - Proof Artifact ║"); println!("╠═══════════════════════════════════════════════════════════════════╣"); - println!("║ Code Distance: d={} | Error Rate: {:.4} | Rounds: {:>6} ║", - config.code_distance, config.error_rate, config.num_rounds); - println!("║ Threshold: {:.2} | Seed: {:>10} ║", - config.threshold, config.seed); + println!( + "║ Code Distance: d={} | Error Rate: {:.4} | Rounds: {:>6} ║", + config.code_distance, config.error_rate, config.num_rounds + ); + println!( + "║ Threshold: {:.2} | Seed: {:>10} ║", + config.threshold, config.seed + ); println!("╚═══════════════════════════════════════════════════════════════════╝"); println!(); // Initialize components - let surface_config = SurfaceCodeConfig::new(config.code_distance, config.error_rate) - .with_seed(config.seed); + let surface_config = + SurfaceCodeConfig::new(config.code_distance, config.error_rate).with_seed(config.seed); let mut syndrome_source = match StimSyndromeSource::new(surface_config) { Ok(s) => s, Err(e) => { @@ -578,27 +582,57 @@ fn main() { println!("╔═══════════════════════════════════════════════════════════════════╗"); println!("║ RESULTS SUMMARY ║"); println!("╠═══════════════════════════════════════════════════════════════════╣"); - println!("║ Total Time: {:>10.2} ms ║", - total_time.as_secs_f64() * 1000.0); - println!("║ Throughput: {:>10.0} rounds/sec ║", - config.num_rounds as f64 / total_time.as_secs_f64()); - println!("║ Avg Fired/Round: {:>10.2} ║", - total_fired as f64 / config.num_rounds as f64); + println!( + "║ Total Time: {:>10.2} ms ║", + total_time.as_secs_f64() * 1000.0 + ); + println!( + "║ Throughput: {:>10.0} rounds/sec ║", + config.num_rounds as f64 / total_time.as_secs_f64() + ); + println!( + "║ Avg Fired/Round: {:>10.2} ║", + total_fired as f64 / config.num_rounds as f64 + ); println!("╠═══════════════════════════════════════════════════════════════════╣"); println!("║ Latency: ║"); - println!("║ Mean: {:>8.0} ns ║", latency_tracker.mean()); - println!("║ P50: {:>8} ns ║", latency_tracker.p50()); - println!("║ P99: {:>8} ns ║", latency_tracker.p99()); - println!("║ P999: {:>8} ns ║", latency_tracker.p999()); - println!("║ Max: {:>8} ns ║", latency_tracker.max()); + println!( + "║ Mean: {:>8.0} ns ║", + latency_tracker.mean() + ); + println!( + "║ P50: {:>8} ns ║", + latency_tracker.p50() + ); + println!( + "║ P99: {:>8} ns ║", + latency_tracker.p99() + ); + println!( + "║ P999: {:>8} ns ║", + latency_tracker.p999() + ); + println!( + "║ Max: {:>8} ns ║", + latency_tracker.max() + ); println!("╠═══════════════════════════════════════════════════════════════════╣"); println!("║ Decisions: ║"); - println!("║ Permits: {:>6} ({:>5.1}%) ║", - permits, permits as f64 / config.num_rounds as f64 * 100.0); - println!("║ Defers: {:>6} ({:>5.1}%) ║", - defers, defers as f64 / config.num_rounds as f64 * 100.0); - println!("║ Denies: {:>6} ({:>5.1}%) ║", - denies, denies as f64 / config.num_rounds as f64 * 100.0); + println!( + "║ Permits: {:>6} ({:>5.1}%) ║", + permits, + permits as f64 / config.num_rounds as f64 * 100.0 + ); + println!( + "║ Defers: {:>6} ({:>5.1}%) ║", + defers, + defers as f64 / config.num_rounds as f64 * 100.0 + ); + println!( + "║ Denies: {:>6} ({:>5.1}%) ║", + denies, + denies as f64 / config.num_rounds as f64 * 100.0 + ); println!("╚═══════════════════════════════════════════════════════════════════╝"); // Write metrics file diff --git a/crates/ruQu/src/bin/ruqu_predictive_eval.rs b/crates/ruQu/src/bin/ruqu_predictive_eval.rs index b2ff1f83a..bc2aa01b6 100644 --- a/crates/ruQu/src/bin/ruqu_predictive_eval.rs +++ b/crates/ruQu/src/bin/ruqu_predictive_eval.rs @@ -203,7 +203,8 @@ impl WarningDetector { self.baseline_mean = sum / self.history.len() as f64; if self.history.len() > 1 { - let variance: f64 = self.history + let variance: f64 = self + .history .iter() .map(|x| (x - self.baseline_mean).powi(2)) .sum::() @@ -348,7 +349,11 @@ impl STMinCutGraph { } } -fn build_qec_graph(code_distance: usize, error_rate: f64, syndrome: &DetectorBitmap) -> STMinCutGraph { +fn build_qec_graph( + code_distance: usize, + error_rate: f64, + syndrome: &DetectorBitmap, +) -> STMinCutGraph { let grid_size = code_distance - 1; let num_detectors = 2 * grid_size * grid_size; @@ -437,10 +442,26 @@ fn is_logical_failure(syndrome: &DetectorBitmap, code_distance: usize) -> bool { } let neighbors = [ - if col > 0 { Some(row * grid_size + col - 1) } else { None }, - if col + 1 < grid_size { Some(row * grid_size + col + 1) } else { None }, - if row > 0 { Some((row - 1) * grid_size + col) } else { None }, - if row + 1 < grid_size { Some((row + 1) * grid_size + col) } else { None }, + if col > 0 { + Some(row * grid_size + col - 1) + } else { + None + }, + if col + 1 < grid_size { + Some(row * grid_size + col + 1) + } else { + None + }, + if row > 0 { + Some((row - 1) * grid_size + col) + } else { + None + }, + if row + 1 < grid_size { + Some((row + 1) * grid_size + col) + } else { + None + }, ]; for neighbor_opt in neighbors.iter().flatten() { @@ -586,10 +607,7 @@ impl SyndromeGenerator { for &(sr, sc) in &[(1i32, 1i32), (1, -1), (-1, 1), (-1, -1)] { let row = center_row as i32 + dr as i32 * sr; let col = center_col as i32 + dc as i32 * sc; - if row >= 0 - && row < grid_size as i32 - && col >= 0 - && col < grid_size as i32 + if row >= 0 && row < grid_size as i32 && col >= 0 && col < grid_size as i32 { let detector = (row as usize) * grid_size + (col as usize); if detector < syndrome.detector_count() { @@ -751,10 +769,22 @@ fn main() { println!(" ACTIONABILITY"); println!("═══════════════════════════════════════════════════════════════════════"); println!(); - println!(" Decoder switch (1 cycle): {:>5.1}%", results.actionable_rate(1) * 100.0); - println!(" Extra syndrome round (2 cycles): {:>5.1}%", results.actionable_rate(2) * 100.0); - println!(" Region quarantine (5 cycles): {:>5.1}%", results.actionable_rate(5) * 100.0); - println!(" Full recalibration (10 cycles): {:>5.1}%", results.actionable_rate(10) * 100.0); + println!( + " Decoder switch (1 cycle): {:>5.1}%", + results.actionable_rate(1) * 100.0 + ); + println!( + " Extra syndrome round (2 cycles): {:>5.1}%", + results.actionable_rate(2) * 100.0 + ); + println!( + " Region quarantine (5 cycles): {:>5.1}%", + results.actionable_rate(5) * 100.0 + ); + println!( + " Full recalibration (10 cycles): {:>5.1}%", + results.actionable_rate(10) * 100.0 + ); // Summary println!(); @@ -770,20 +800,38 @@ fn main() { println!(); println!(" ✓ PREDICTIVE: ruQu satisfies all criteria"); println!(" - Recall >= 80%: {:.1}%", results.recall() * 100.0); - println!(" - False alarms < 50/100k: {:.1}/100k", results.false_alarms_per_100k()); - println!(" - Median lead >= 2 cycles: {:.1} cycles", results.median_lead_time()); + println!( + " - False alarms < 50/100k: {:.1}/100k", + results.false_alarms_per_100k() + ); + println!( + " - Median lead >= 2 cycles: {:.1} cycles", + results.median_lead_time() + ); } else { println!(); println!(" ~ PARTIAL: Some criteria not met"); - println!(" - Recall: {:.1}% (target: >=80%)", results.recall() * 100.0); - println!(" - False alarms: {:.1}/100k (target: <50)", results.false_alarms_per_100k()); - println!(" - Median lead: {:.1} cycles (target: >=2)", results.median_lead_time()); + println!( + " - Recall: {:.1}% (target: >=80%)", + results.recall() * 100.0 + ); + println!( + " - False alarms: {:.1}/100k (target: <50)", + results.false_alarms_per_100k() + ); + println!( + " - Median lead: {:.1} cycles (target: >=2)", + results.median_lead_time() + ); } let elapsed = start_time.elapsed(); println!(); println!(" Total time: {:.2}s", elapsed.as_secs_f64()); - println!(" Throughput: {:.0} cycles/sec", results.total_cycles as f64 / elapsed.as_secs_f64()); + println!( + " Throughput: {:.0} cycles/sec", + results.total_cycles as f64 / elapsed.as_secs_f64() + ); println!(); } @@ -801,5 +849,8 @@ fn print_results(results: &EvalResults) { println!(); println!("Precision: {:.2}", results.precision()); println!("Recall: {:.2}", results.recall()); - println!("False alarms: {:.1} / 100k cycles", results.false_alarms_per_100k()); + println!( + "False alarms: {:.1} / 100k cycles", + results.false_alarms_per_100k() + ); } diff --git a/crates/ruQu/src/decoder.rs b/crates/ruQu/src/decoder.rs index 76535f8ed..e3855727c 100644 --- a/crates/ruQu/src/decoder.rs +++ b/crates/ruQu/src/decoder.rs @@ -88,7 +88,7 @@ pub struct MWPMDecoder { impl MWPMDecoder { /// Create a new MWPM decoder for a surface code of given distance pub fn new(config: DecoderConfig) -> Self { - use fusion_blossom::mwpm_solver::{SolverSerial, SolverInitializer}; + use fusion_blossom::mwpm_solver::{SolverInitializer, SolverSerial}; use fusion_blossom::util::*; let d = config.distance; @@ -237,7 +237,11 @@ impl MWPMDecoder { Correction { x_corrections: deduped, z_corrections: Vec::new(), // Z corrections from separate decoder pass - confidence: if syndrome.fired_count() == 0 { 1.0 } else { 0.9 }, + confidence: if syndrome.fired_count() == 0 { + 1.0 + } else { + 0.9 + }, decode_time_ns: elapsed.as_nanos() as u64, } } @@ -379,7 +383,11 @@ impl StreamingDecoder { if self.correction_history.is_empty() { return 0; } - let sum: u64 = self.correction_history.iter().map(|c| c.decode_time_ns).sum(); + let sum: u64 = self + .correction_history + .iter() + .map(|c| c.decode_time_ns) + .sum(); sum / self.correction_history.len() as u64 } @@ -429,8 +437,8 @@ mod tests { // Two adjacent fired detectors let mut syndrome = DetectorBitmap::new(25); // d=5, 5*5=25 detectors - syndrome.set(0, true); // (0,0) - syndrome.set(1, true); // (0,1) + syndrome.set(0, true); // (0,0) + syndrome.set(1, true); // (0,1) let correction = decoder.decode(&syndrome); diff --git a/crates/ruQu/src/fabric.rs b/crates/ruQu/src/fabric.rs index 9c9f69c4e..924496baf 100644 --- a/crates/ruQu/src/fabric.rs +++ b/crates/ruQu/src/fabric.rs @@ -835,22 +835,14 @@ impl QuantumFabric { // This handles the case where syndromes aren't pre-assigned for tile in &mut self.tiles { // Convert syndrome round to delta for tile processing - let delta = crate::tile::SyndromeDelta::new( - 0, - 0, - round.fired_count() as u16, - ); + let delta = crate::tile::SyndromeDelta::new(0, 0, round.fired_count() as u16); tile.tick(&delta); } } else { // Send to specific tile let tile_idx = (tile_id - 1) as usize; if tile_idx < self.tiles.len() { - let delta = crate::tile::SyndromeDelta::new( - 0, - 0, - round.fired_count() as u16, - ); + let delta = crate::tile::SyndromeDelta::new(0, 0, round.fired_count() as u16); self.tiles[tile_idx].tick(&delta); } } @@ -922,8 +914,7 @@ impl QuantumFabric { let n = self.state.total_decisions(); if n > 0 { - self.state.avg_latency_ns = - (self.state.avg_latency_ns * (n - 1) + elapsed) / n; + self.state.avg_latency_ns = (self.state.avg_latency_ns * (n - 1) + elapsed) / n; } // Check latency budget @@ -938,12 +929,8 @@ impl QuantumFabric { // Append receipt if enabled if self.config.enable_receipts { let witness_hash = [0u8; 32]; // Would compute proper hash - self.receipt_log.append( - tile_decision, - self.state.tick, - elapsed, - witness_hash, - ); + self.receipt_log + .append(tile_decision, self.state.tick, elapsed, witness_hash); } Ok(decision) @@ -1106,21 +1093,10 @@ mod tests { #[test] fn test_fabric_ingest_syndromes() { - let mut fabric = QuantumFabric::builder() - .tiles(4) - .build() - .unwrap(); + let mut fabric = QuantumFabric::builder().tiles(4).build().unwrap(); let rounds: Vec = (0..10) - .map(|i| { - SyndromeRound::new( - i, - i, - i * 1000, - DetectorBitmap::new(64), - 0, - ) - }) + .map(|i| SyndromeRound::new(i, i, i * 1000, DetectorBitmap::new(64), 0)) .collect(); let result = fabric.ingest_syndromes(&rounds); @@ -1130,10 +1106,7 @@ mod tests { #[test] fn test_fabric_tick() { - let mut fabric = QuantumFabric::builder() - .tiles(4) - .build() - .unwrap(); + let mut fabric = QuantumFabric::builder().tiles(4).build().unwrap(); // Tick without any syndromes let result = fabric.tick(); @@ -1146,10 +1119,7 @@ mod tests { #[test] fn test_fabric_multiple_ticks() { - let mut fabric = QuantumFabric::builder() - .tiles(8) - .build() - .unwrap(); + let mut fabric = QuantumFabric::builder().tiles(8).build().unwrap(); // Run multiple ticks for _ in 0..100 { @@ -1163,10 +1133,7 @@ mod tests { #[test] fn test_fabric_get_tile() { - let fabric = QuantumFabric::builder() - .tiles(4) - .build() - .unwrap(); + let fabric = QuantumFabric::builder().tiles(4).build().unwrap(); // Tile 0 (TileZero) should return None assert!(fabric.get_tile(0).is_none()); @@ -1182,10 +1149,7 @@ mod tests { #[test] fn test_fabric_reset() { - let mut fabric = QuantumFabric::builder() - .tiles(4) - .build() - .unwrap(); + let mut fabric = QuantumFabric::builder().tiles(4).build().unwrap(); // Do some work for _ in 0..10 { @@ -1203,10 +1167,7 @@ mod tests { #[test] fn test_fabric_decision_stats() { - let mut fabric = QuantumFabric::builder() - .tiles(4) - .build() - .unwrap(); + let mut fabric = QuantumFabric::builder().tiles(4).build().unwrap(); for _ in 0..50 { let _ = fabric.tick(); diff --git a/crates/ruQu/src/filters.rs b/crates/ruQu/src/filters.rs index ac08d9359..cb17cfdfe 100644 --- a/crates/ruQu/src/filters.rs +++ b/crates/ruQu/src/filters.rs @@ -430,11 +430,7 @@ impl StructuralFilter { weight_a.partial_cmp(weight_b).unwrap() }); - edges - .into_iter() - .take(10) - .map(|(_, &id)| id) - .collect() + edges.into_iter().take(10).map(|(_, &id)| id).collect() } fn edge_key(u: VertexId, v: VertexId) -> (VertexId, VertexId) { @@ -568,8 +564,7 @@ impl ShiftFilter { // Update shift accumulator let deviation = (score - self.global_mean).abs(); - stats.shift_accumulator = - self.config.decay_factor * stats.shift_accumulator + deviation; + stats.shift_accumulator = self.config.decay_factor * stats.shift_accumulator + deviation; // Update global statistics self.num_observations += 1; @@ -698,9 +693,9 @@ pub struct EvidenceConfig { impl Default for EvidenceConfig { fn default() -> Self { Self { - tau_permit: 20.0, // Strong evidence for permit + tau_permit: 20.0, // Strong evidence for permit tau_deny: 1.0 / 20.0, // Strong evidence for deny - prior: 0.95, // Assume system is usually coherent + prior: 0.95, // Assume system is usually coherent } } } @@ -861,8 +856,7 @@ impl EvidenceFilter { / (self.config.tau_permit.ln().abs() + 1.0)) .min(1.0) } else if e_value <= self.config.tau_deny { - ((self.config.tau_deny.ln() - e_value.ln()) - / (self.config.tau_deny.ln().abs() + 1.0)) + ((self.config.tau_deny.ln() - e_value.ln()) / (self.config.tau_deny.ln().abs() + 1.0)) .min(1.0) } else { 0.0 @@ -988,11 +982,7 @@ impl FilterPipeline { let evidence_result = self.evidence.evaluate(state); // Determine overall verdict - let verdict = self.combine_verdicts( - &structural_result, - &shift_result, - &evidence_result, - ); + let verdict = self.combine_verdicts(&structural_result, &shift_result, &evidence_result); // Collect affected regions let mut affected_regions = shift_result.affected_regions; @@ -1030,8 +1020,7 @@ impl FilterPipeline { } else if evidence_result.verdict.is_none() { recommendations.push(format!( "Evidence: E-value {:.2e} - gathering more evidence ({} samples)", - evidence_result.e_value, - evidence_result.samples_seen + evidence_result.e_value, evidence_result.samples_seen )); } @@ -1168,7 +1157,7 @@ mod tests { fn test_structural_filter_low_cut() { // Use simple cut calculation for predictable unit test behavior let config = StructuralConfig { - threshold: 3.0, // High threshold + threshold: 3.0, // High threshold use_subpolynomial: false, // Disable subpolynomial for unit tests ..Default::default() }; @@ -1316,7 +1305,7 @@ mod tests { fn test_filter_pipeline_deny_structural() { let config = FilterConfig { structural: StructuralConfig { - threshold: 5.0, // High threshold + threshold: 5.0, // High threshold use_subpolynomial: false, // Disable for unit test predictability ..Default::default() }, diff --git a/crates/ruQu/src/lib.rs b/crates/ruQu/src/lib.rs index 985ebe27f..3450d5a08 100644 --- a/crates/ruQu/src/lib.rs +++ b/crates/ruQu/src/lib.rs @@ -99,38 +99,38 @@ pub mod schema; pub mod traits; // Re-exports for convenient access +pub use adaptive::{ + AdaptiveStats, AdaptiveThresholds, DriftConfig, DriftDetector, DriftDirection, DriftProfile, + LearningConfig, +}; +pub use attention::{AttentionConfig, AttentionStats, CoherenceAttention, GatePacketBridge}; +pub use decoder::{Correction, DecoderConfig, MWPMDecoder, StreamingDecoder}; pub use error::{Result, RuQuError}; +pub use fabric::{ + linear_patch_map, surface_code, surface_code_d7, CoherenceGate, DecisionStats, FabricBuilder, + FabricConfig, FabricState, FilterSummary, PatchMap, QuantumFabric, TileAssignment, + WitnessReceipt, +}; pub use filters::{ EdgeId as FilterEdgeId, EvidenceAccumulator, EvidenceFilter, EvidenceResult, FilterConfig, FilterPipeline, FilterResults, RegionMask, ShiftFilter, ShiftResult, StructuralFilter, StructuralResult, SystemState, Verdict, }; +pub use metrics::{Counter, Gauge, Histogram, MetricsCollector, MetricsConfig, MetricsSnapshot}; +pub use mincut::{DynamicMinCutEngine, MinCutResult}; +pub use parallel::{parallel_aggregate, ParallelConfig, ParallelFabric, ParallelStats}; +pub use stim::{ErrorPatternGenerator, StimSyndromeSource, SurfaceCodeConfig, SyndromeStats}; pub use syndrome::{ BufferStatistics, DetectorBitmap, SyndromeBuffer, SyndromeDelta, SyndromeRound, }; pub use tile::{ - GateDecision, GateThresholds, LocalCutState, PatchGraph, PermitToken, ReceiptLog, - TileReport, TileZero, WorkerTile, + GateDecision, GateThresholds, LocalCutState, PatchGraph, PermitToken, ReceiptLog, TileReport, + TileZero, WorkerTile, }; pub use types::{ - ActionId, CycleId, RoundId, SequenceId, TileId as DomainTileId, - RegionMask as DomainRegionMask, GateDecision as DomainGateDecision, + ActionId, CycleId, GateDecision as DomainGateDecision, RegionMask as DomainRegionMask, RoundId, + SequenceId, TileId as DomainTileId, }; -pub use fabric::{ - CoherenceGate, DecisionStats, FabricBuilder, FabricConfig, FabricState, - FilterSummary, PatchMap, QuantumFabric, TileAssignment, WitnessReceipt, - linear_patch_map, surface_code, surface_code_d7, -}; -pub use mincut::{DynamicMinCutEngine, MinCutResult}; -pub use decoder::{Correction, DecoderConfig, MWPMDecoder, StreamingDecoder}; -pub use attention::{AttentionConfig, AttentionStats, CoherenceAttention, GatePacketBridge}; -pub use adaptive::{ - AdaptiveStats, AdaptiveThresholds, DriftConfig, DriftDetector, DriftDirection, DriftProfile, - LearningConfig, -}; -pub use metrics::{Counter, Gauge, Histogram, MetricsCollector, MetricsConfig, MetricsSnapshot}; -pub use parallel::{ParallelConfig, ParallelFabric, ParallelStats, parallel_aggregate}; -pub use stim::{ErrorPatternGenerator, StimSyndromeSource, SurfaceCodeConfig, SyndromeStats}; /// Crate version pub const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -156,17 +156,23 @@ pub const TILE_MEMORY_BUDGET: usize = 65536; /// Prelude module for convenient imports pub mod prelude { //! Commonly used types for syndrome processing, filters, and tile architecture. + pub use crate::adaptive::{ + AdaptiveStats, AdaptiveThresholds, DriftConfig, DriftDetector, DriftProfile, LearningConfig, + }; pub use crate::error::{Result, RuQuError}; pub use crate::fabric::{ - CoherenceGate, DecisionStats, FabricBuilder, FabricConfig, FabricState, - PatchMap, QuantumFabric, TileAssignment, WitnessReceipt, - linear_patch_map, surface_code, surface_code_d7, + linear_patch_map, surface_code, surface_code_d7, CoherenceGate, DecisionStats, + FabricBuilder, FabricConfig, FabricState, PatchMap, QuantumFabric, TileAssignment, + WitnessReceipt, }; pub use crate::filters::{ EvidenceAccumulator, EvidenceFilter, EvidenceResult, FilterConfig, FilterPipeline, FilterResults, RegionMask, ShiftFilter, ShiftResult, StructuralFilter, StructuralResult, SystemState, Verdict, }; + pub use crate::metrics::{MetricsCollector, MetricsConfig, MetricsSnapshot}; + pub use crate::parallel::{ParallelConfig, ParallelFabric, ParallelStats}; + pub use crate::stim::{StimSyndromeSource, SurfaceCodeConfig, SyndromeStats}; pub use crate::syndrome::{ BufferStatistics, DetectorBitmap, SyndromeBuffer, SyndromeDelta, SyndromeRound, }; @@ -175,18 +181,12 @@ pub mod prelude { TileReport, TileZero, WorkerTile, }; pub use crate::types::{ - ActionId, CycleId, RoundId, SequenceId, - GateDecision as DomainGateDecision, RegionMask as DomainRegionMask, + ActionId, CycleId, GateDecision as DomainGateDecision, RegionMask as DomainRegionMask, + RoundId, SequenceId, }; pub use crate::{ DEFAULT_BUFFER_CAPACITY, MAX_DETECTORS, TILE_COUNT, TILE_MEMORY_BUDGET, WORKER_TILE_COUNT, }; - pub use crate::adaptive::{ - AdaptiveThresholds, AdaptiveStats, DriftConfig, DriftDetector, DriftProfile, LearningConfig, - }; - pub use crate::metrics::{MetricsCollector, MetricsConfig, MetricsSnapshot}; - pub use crate::parallel::{ParallelFabric, ParallelConfig, ParallelStats}; - pub use crate::stim::{StimSyndromeSource, SurfaceCodeConfig, SyndromeStats}; } #[cfg(test)] diff --git a/crates/ruQu/src/metrics.rs b/crates/ruQu/src/metrics.rs index 965875128..29b7342fd 100644 --- a/crates/ruQu/src/metrics.rs +++ b/crates/ruQu/src/metrics.rs @@ -112,7 +112,8 @@ impl Gauge { /// Set gauge from f64 (stored as fixed-point) pub fn set_f64(&self, val: f64) { - self.value.store((val * 1_000_000.0) as u64, Ordering::Relaxed); + self.value + .store((val * 1_000_000.0) as u64, Ordering::Relaxed); } /// Get current value @@ -138,9 +139,7 @@ pub struct Histogram { impl Histogram { /// Create a new histogram with bucket boundaries pub fn new(buckets: Vec) -> Self { - let counts = (0..=buckets.len()) - .map(|_| AtomicU64::new(0)) - .collect(); + let counts = (0..=buckets.len()).map(|_| AtomicU64::new(0)).collect(); Self { buckets, @@ -156,7 +155,8 @@ impl Histogram { self.count.fetch_add(1, Ordering::Relaxed); // Find bucket - let idx = self.buckets + let idx = self + .buckets .iter() .position(|&b| value <= b) .unwrap_or(self.buckets.len()); @@ -166,7 +166,10 @@ impl Histogram { /// Get bucket counts pub fn bucket_counts(&self) -> Vec { - self.counts.iter().map(|c| c.load(Ordering::Relaxed)).collect() + self.counts + .iter() + .map(|c| c.load(Ordering::Relaxed)) + .collect() } /// Get total count @@ -367,18 +370,36 @@ impl MetricsCollector { // Help and type declarations out.push_str("# HELP ruqu_decisions_total Total gate decisions by type\n"); out.push_str("# TYPE ruqu_decisions_total counter\n"); - out.push_str(&format!("ruqu_decisions_total{{type=\"permit\"}} {}\n", snap.permits)); - out.push_str(&format!("ruqu_decisions_total{{type=\"defer\"}} {}\n", snap.defers)); - out.push_str(&format!("ruqu_decisions_total{{type=\"deny\"}} {}\n", snap.denies)); + out.push_str(&format!( + "ruqu_decisions_total{{type=\"permit\"}} {}\n", + snap.permits + )); + out.push_str(&format!( + "ruqu_decisions_total{{type=\"defer\"}} {}\n", + snap.defers + )); + out.push_str(&format!( + "ruqu_decisions_total{{type=\"deny\"}} {}\n", + snap.denies + )); out.push_str("\n# HELP ruqu_latency_nanoseconds Latency in nanoseconds\n"); out.push_str("# TYPE ruqu_latency_nanoseconds summary\n"); - out.push_str(&format!("ruqu_latency_nanoseconds{{quantile=\"0.5\"}} {}\n", snap.tick_latency_p50_ns)); - out.push_str(&format!("ruqu_latency_nanoseconds{{quantile=\"0.99\"}} {}\n", snap.tick_latency_p99_ns)); + out.push_str(&format!( + "ruqu_latency_nanoseconds{{quantile=\"0.5\"}} {}\n", + snap.tick_latency_p50_ns + )); + out.push_str(&format!( + "ruqu_latency_nanoseconds{{quantile=\"0.99\"}} {}\n", + snap.tick_latency_p99_ns + )); out.push_str("\n# HELP ruqu_throughput_syndromes_per_second Current throughput\n"); out.push_str("# TYPE ruqu_throughput_syndromes_per_second gauge\n"); - out.push_str(&format!("ruqu_throughput_syndromes_per_second {}\n", snap.throughput)); + out.push_str(&format!( + "ruqu_throughput_syndromes_per_second {}\n", + snap.throughput + )); out.push_str("\n# HELP ruqu_coherence_min_cut Current min-cut value\n"); out.push_str("# TYPE ruqu_coherence_min_cut gauge\n"); diff --git a/crates/ruQu/src/mincut.rs b/crates/ruQu/src/mincut.rs index 28ff72593..435ba0c16 100644 --- a/crates/ruQu/src/mincut.rs +++ b/crates/ruQu/src/mincut.rs @@ -44,7 +44,7 @@ pub struct DynamicMinCutEngine { impl DynamicMinCutEngine { /// Create a new dynamic min-cut engine pub fn new() -> Self { - use ruvector_mincut::subpolynomial::{SubpolynomialMinCut, SubpolyConfig}; + use ruvector_mincut::subpolynomial::{SubpolyConfig, SubpolynomialMinCut}; let config = SubpolyConfig { phi: 0.01, @@ -110,14 +110,19 @@ impl DynamicMinCutEngine { let mut hasher = blake3::Hasher::new(); hasher.update(&result.value.to_le_bytes()); hasher.update(if result.is_exact { &[1u8] } else { &[0u8] }); - hasher.update(if result.complexity_verified { &[1u8] } else { &[0u8] }); + hasher.update(if result.complexity_verified { + &[1u8] + } else { + &[0u8] + }); let witness_hash = Some(*hasher.finalize().as_bytes()); MinCutResult { value: result.value, is_exact: result.is_exact, cut_edges: result.cut_edges.map(|edges| { - edges.into_iter() + edges + .into_iter() .map(|(u, v)| (u as VertexId, v as VertexId)) .collect() }), diff --git a/crates/ruQu/src/parallel.rs b/crates/ruQu/src/parallel.rs index 828777120..1035f94db 100644 --- a/crates/ruQu/src/parallel.rs +++ b/crates/ruQu/src/parallel.rs @@ -28,8 +28,8 @@ #[cfg(feature = "parallel")] use rayon::prelude::*; -use crate::tile::{GateDecision, GateThresholds, TileReport, TileZero, WorkerTile, SyndromeDelta}; use crate::error::{Result, RuQuError}; +use crate::tile::{GateDecision, GateThresholds, SyndromeDelta, TileReport, TileZero, WorkerTile}; /// Configuration for parallel processing #[derive(Clone, Debug)] @@ -60,7 +60,7 @@ impl ParallelConfig { pub fn low_latency() -> Self { Self { num_threads: 4, - chunk_size: 64, // Larger chunks = less overhead + chunk_size: 64, // Larger chunks = less overhead work_stealing: false, // Predictable scheduling thresholds: GateThresholds::default(), } @@ -117,9 +117,7 @@ impl ParallelFabric { } // Create 255 worker tiles (1-255) - let workers: Vec = (1..=255u8) - .map(WorkerTile::new) - .collect(); + let workers: Vec = (1..=255u8).map(WorkerTile::new).collect(); let coordinator = TileZero::with_random_key(config.thresholds.clone()); @@ -138,7 +136,8 @@ impl ParallelFabric { let start = Instant::now(); // Process all workers in parallel - let reports: Vec = self.workers + let reports: Vec = self + .workers .par_iter_mut() .with_min_len(self.config.chunk_size) .map(|worker| worker.tick(syndrome)) @@ -151,8 +150,8 @@ impl ParallelFabric { let elapsed_ns = start.elapsed().as_nanos() as u64; self.stats.total_processed += 255; self.stats.batches += 1; - self.stats.avg_batch_time_ns = - (self.stats.avg_batch_time_ns * (self.stats.batches - 1) + elapsed_ns) + self.stats.avg_batch_time_ns = (self.stats.avg_batch_time_ns * (self.stats.batches - 1) + + elapsed_ns) / self.stats.batches; let throughput = 255.0 / (elapsed_ns as f64 / 1_000_000_000.0); @@ -170,7 +169,8 @@ impl ParallelFabric { let start = Instant::now(); // Process all workers sequentially - let reports: Vec = self.workers + let reports: Vec = self + .workers .iter_mut() .map(|worker| worker.tick(syndrome)) .collect(); @@ -182,8 +182,8 @@ impl ParallelFabric { let elapsed_ns = start.elapsed().as_nanos() as u64; self.stats.total_processed += 255; self.stats.batches += 1; - self.stats.avg_batch_time_ns = - (self.stats.avg_batch_time_ns * (self.stats.batches - 1) + elapsed_ns) + self.stats.avg_batch_time_ns = (self.stats.avg_batch_time_ns * (self.stats.batches - 1) + + elapsed_ns) / self.stats.batches; Ok(decision) @@ -245,7 +245,13 @@ pub fn parallel_aggregate(reports: &[TileReport]) -> (f64, f64, f64) { // Parallel reduction for min_cut (minimum) let min_cut = reports .par_iter() - .map(|r| if r.local_cut > 0.0 { r.local_cut } else { f64::MAX }) + .map(|r| { + if r.local_cut > 0.0 { + r.local_cut + } else { + f64::MAX + } + }) .reduce(|| f64::MAX, |a, b| a.min(b)); // Parallel reduction for shift (maximum) diff --git a/crates/ruQu/src/schema.rs b/crates/ruQu/src/schema.rs index b4380471d..f20b64021 100644 --- a/crates/ruQu/src/schema.rs +++ b/crates/ruQu/src/schema.rs @@ -333,25 +333,22 @@ impl LogWriter { /// Write a syndrome round pub fn write_syndrome(&mut self, round: &SyndromeRound) -> std::io::Result<()> { - let payload = serde_json::to_vec(round).map_err(|e| { - std::io::Error::new(std::io::ErrorKind::InvalidData, e) - })?; + let payload = serde_json::to_vec(round) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; self.write_record(RecordType::SyndromeRound, &payload) } /// Write a gate decision pub fn write_decision(&mut self, decision: &GateDecision) -> std::io::Result<()> { - let payload = serde_json::to_vec(decision).map_err(|e| { - std::io::Error::new(std::io::ErrorKind::InvalidData, e) - })?; + let payload = serde_json::to_vec(decision) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; self.write_record(RecordType::GateDecision, &payload) } /// Write a mitigation action pub fn write_action(&mut self, action: &MitigationAction) -> std::io::Result<()> { - let payload = serde_json::to_vec(action).map_err(|e| { - std::io::Error::new(std::io::ErrorKind::InvalidData, e) - })?; + let payload = serde_json::to_vec(action) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; self.write_record(RecordType::MitigationAction, &payload) } @@ -453,21 +450,18 @@ impl LogReader { // Parse payload let record = match record_type { RecordType::SyndromeRound => { - let round: SyndromeRound = serde_json::from_slice(&payload).map_err(|e| { - std::io::Error::new(std::io::ErrorKind::InvalidData, e) - })?; + let round: SyndromeRound = serde_json::from_slice(&payload) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; LogRecord::Syndrome(round) } RecordType::GateDecision => { - let decision: GateDecision = serde_json::from_slice(&payload).map_err(|e| { - std::io::Error::new(std::io::ErrorKind::InvalidData, e) - })?; + let decision: GateDecision = serde_json::from_slice(&payload) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; LogRecord::Decision(decision) } RecordType::MitigationAction => { - let action: MitigationAction = serde_json::from_slice(&payload).map_err(|e| { - std::io::Error::new(std::io::ErrorKind::InvalidData, e) - })?; + let action: MitigationAction = serde_json::from_slice(&payload) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; LogRecord::Action(action) } _ => LogRecord::Unknown(payload), diff --git a/crates/ruQu/src/stim.rs b/crates/ruQu/src/stim.rs index 6229e4b1e..e719b4ed9 100644 --- a/crates/ruQu/src/stim.rs +++ b/crates/ruQu/src/stim.rs @@ -31,8 +31,8 @@ //! - Repetition code //! - Color code (planned) -use crate::syndrome::DetectorBitmap; use crate::error::{Result, RuQuError}; +use crate::syndrome::DetectorBitmap; /// Configuration for surface code simulation #[derive(Clone, Debug)] @@ -169,8 +169,8 @@ impl StimSyndromeSource { // Pre-compute detector coordinates for correlation modeling let mut detector_coords = Vec::new(); let d = config.distance; - for r in 0..d-1 { - for c in 0..d-1 { + for r in 0..d - 1 { + for c in 0..d - 1 { // X stabilizers detector_coords.push((r, c)); // Z stabilizers (offset grid) @@ -222,9 +222,7 @@ impl StimSyndromeSource { /// Reset to initial state pub fn reset(&mut self) { self.round = 0; - self.rng = Xorshift64::new( - self.config.seed.unwrap_or(12345) - ); + self.rng = Xorshift64::new(self.config.seed.unwrap_or(12345)); } // Private helpers @@ -260,7 +258,7 @@ impl StimSyndromeSource { let d = self.config.distance; let idx = (self.rng.next() as usize) % (d - 1); - for i in 0..d-1 { + for i in 0..d - 1 { let detector = if is_row { idx * (d - 1) + i } else { @@ -339,7 +337,7 @@ impl ErrorPatternGenerator { let z_offset = (d - 1) * (d - 1); // Top boundary Z stabilizers - for col in 0..d-1 { + for col in 0..d - 1 { bitmap.set(z_offset + col, true); } @@ -373,8 +371,8 @@ impl SyndromeStats { self.max_detections = fired; } - self.avg_detection_rate = self.total_detections as f64 / - (self.total_syndromes as f64 * bitmap.detector_count() as f64); + self.avg_detection_rate = self.total_detections as f64 + / (self.total_syndromes as f64 * bitmap.detector_count() as f64); } } diff --git a/crates/ruQu/src/syndrome.rs b/crates/ruQu/src/syndrome.rs index 2050d6024..3ce0eaa4f 100644 --- a/crates/ruQu/src/syndrome.rs +++ b/crates/ruQu/src/syndrome.rs @@ -175,7 +175,12 @@ impl DetectorBitmap { #[inline] pub fn set(&mut self, idx: usize, value: bool) { // SECURITY: Use assert! not debug_assert! to ensure bounds check in release builds - assert!(idx < self.count, "detector index {} out of bounds (count: {})", idx, self.count); + assert!( + idx < self.count, + "detector index {} out of bounds (count: {})", + idx, + self.count + ); let word = idx / 64; let bit = idx % 64; if value { @@ -214,7 +219,12 @@ impl DetectorBitmap { #[must_use] pub fn get(&self, idx: usize) -> bool { // SECURITY: Use assert! not debug_assert! to ensure bounds check in release builds - assert!(idx < self.count, "detector index {} out of bounds (count: {})", idx, self.count); + assert!( + idx < self.count, + "detector index {} out of bounds (count: {})", + idx, + self.count + ); let word = idx / 64; let bit = idx % 64; (self.bits[word] >> bit) & 1 == 1 @@ -296,8 +306,8 @@ impl DetectorBitmap { // Lookup table for 4-bit popcount let lookup = _mm256_setr_epi8( - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, + 3, 3, 4, ); let low_mask = _mm256_set1_epi8(0x0f); @@ -318,7 +328,8 @@ impl DetectorBitmap { // Sum nibble popcounts (sad accumulates byte sums into u64) let popcnt = _mm256_add_epi8(popcnt_lo, popcnt_hi); - total_vec = _mm256_add_epi64(total_vec, _mm256_sad_epu8(popcnt, _mm256_setzero_si256())); + total_vec = + _mm256_add_epi64(total_vec, _mm256_sad_epu8(popcnt, _mm256_setzero_si256())); i += 4; } @@ -1008,7 +1019,8 @@ impl SyndromeBuffer { } // Try direct index first (assumes sequential round IDs) - if let Some(ref newest) = self.rounds[(self.write_index + self.capacity - 1) % self.capacity] + if let Some(ref newest) = + self.rounds[(self.write_index + self.capacity - 1) % self.capacity] { if round_id <= newest.round_id { let offset = (newest.round_id - round_id) as usize; diff --git a/crates/ruQu/src/tile.rs b/crates/ruQu/src/tile.rs index 5a3a61aa1..099d22fcd 100644 --- a/crates/ruQu/src/tile.rs +++ b/crates/ruQu/src/tile.rs @@ -37,7 +37,7 @@ use std::mem::size_of; // Cryptographic imports -use ed25519_dalek::{Signature, SigningKey, VerifyingKey, Signer}; +use ed25519_dalek::{Signature, Signer, SigningKey, VerifyingKey}; use subtle::ConstantTimeEq; // ============================================================================ @@ -237,7 +237,11 @@ impl Edge { /// Create a new edge #[inline] pub const fn new(source: VertexId, target: VertexId, weight: FixedWeight) -> Self { - Self { source, target, weight } + Self { + source, + target, + weight, + } } } @@ -324,7 +328,10 @@ impl PatchGraph { target: 0, weight: 0, }; MAX_PATCH_EDGES], - adjacency: [[AdjEntry { neighbor: 0, edge_id: 0 }; MAX_DEGREE]; MAX_PATCH_VERTICES], + adjacency: [[AdjEntry { + neighbor: 0, + edge_id: 0, + }; MAX_DEGREE]; MAX_PATCH_VERTICES], } } @@ -340,8 +347,10 @@ impl PatchGraph { // Update syndrome accumulator at vertex if (delta.source as usize) < MAX_PATCH_VERTICES { self.ensure_vertex(delta.source); - self.vertices[delta.source as usize].syndrome_acc = - self.vertices[delta.source as usize].syndrome_acc.wrapping_add(delta.value); + self.vertices[delta.source as usize].syndrome_acc = self.vertices + [delta.source as usize] + .syndrome_acc + .wrapping_add(delta.value); } } } @@ -362,7 +371,12 @@ impl PatchGraph { } /// Add an edge to the graph - pub fn add_edge(&mut self, source: VertexId, target: VertexId, weight: FixedWeight) -> Option { + pub fn add_edge( + &mut self, + source: VertexId, + target: VertexId, + weight: FixedWeight, + ) -> Option { if source as usize >= MAX_PATCH_VERTICES || target as usize >= MAX_PATCH_VERTICES { return None; } @@ -386,11 +400,17 @@ impl PatchGraph { // Update adjacency let src_deg = self.vertices[source as usize].degree as usize; - self.adjacency[source as usize][src_deg] = AdjEntry { neighbor: target, edge_id }; + self.adjacency[source as usize][src_deg] = AdjEntry { + neighbor: target, + edge_id, + }; self.vertices[source as usize].degree += 1; let tgt_deg = self.vertices[target as usize].degree as usize; - self.adjacency[target as usize][tgt_deg] = AdjEntry { neighbor: source, edge_id }; + self.adjacency[target as usize][tgt_deg] = AdjEntry { + neighbor: source, + edge_id, + }; self.vertices[target as usize].degree += 1; self.num_edges += 1; @@ -419,7 +439,12 @@ impl PatchGraph { } /// Update edge weight - pub fn update_weight(&mut self, source: VertexId, target: VertexId, new_weight: FixedWeight) -> bool { + pub fn update_weight( + &mut self, + source: VertexId, + target: VertexId, + new_weight: FixedWeight, + ) -> bool { if let Some(edge_id) = self.find_edge(source, target) { self.edges[edge_id as usize].weight = new_weight; self.status |= Self::STATUS_DIRTY; @@ -539,7 +564,12 @@ impl PatchGraph { } #[inline(always)] - fn union(parent: &mut [u16; MAX_PATCH_VERTICES], rank: &mut [u8; MAX_PATCH_VERTICES], x: u16, y: u16) { + fn union( + parent: &mut [u16; MAX_PATCH_VERTICES], + rank: &mut [u8; MAX_PATCH_VERTICES], + x: u16, + y: u16, + ) { let px = find(parent, x); let py = find(parent, y); if px == py { @@ -871,7 +901,8 @@ impl LocalCutState { self.cut_value = graph.estimate_local_cut(); // Identify boundary candidates - self.num_candidates = graph.identify_boundary_candidates(&mut self.boundary_candidates) as u16; + self.num_candidates = + graph.identify_boundary_candidates(&mut self.boundary_candidates) as u16; // Detect boundary movement let delta = (self.cut_value - self.prev_cut_value).abs(); @@ -1032,7 +1063,12 @@ impl WorkerTile { syndrome: [ (delta.value & 0xFF) as u8, ((delta.value >> 8) & 0xFF) as u8, - 0, 0, 0, 0, 0, 0, + 0, + 0, + 0, + 0, + 0, + 0, ], flags: delta.flags as u32, }; @@ -1232,7 +1268,7 @@ impl PermitToken { pub fn is_valid(&self, now_ns: u64) -> bool { self.decision == GateDecision::Permit && now_ns >= self.timestamp // Not before issued - && now_ns <= self.timestamp.saturating_add(self.ttl_ns) // Not after expiry + && now_ns <= self.timestamp.saturating_add(self.ttl_ns) // Not after expiry } /// Compute the message bytes to be signed @@ -1281,7 +1317,9 @@ impl PermitToken { let hash = blake3::hash(&message); // Verify signature over the hash - verifying_key.verify_strict(hash.as_bytes(), &signature).is_ok() + verifying_key + .verify_strict(hash.as_bytes(), &signature) + .is_ok() } } @@ -1332,7 +1370,13 @@ impl ReceiptLog { /// # Security /// Uses Blake3 for cryptographic hash chaining, ensuring tamper-evidence. /// The hash is computed as: H(prev_hash || sequence || decision || timestamp || witness_hash) - pub fn append(&mut self, decision: GateDecision, sequence: u64, timestamp: u64, witness_hash: [u8; 32]) { + pub fn append( + &mut self, + decision: GateDecision, + sequence: u64, + timestamp: u64, + witness_hash: [u8; 32], + ) { // Compute Blake3 hash of all data including previous hash let mut hasher = blake3::Hasher::new(); hasher.update(&self.last_hash); @@ -1513,7 +1557,8 @@ impl TileZero { let timestamp = self.sequence * 1_000_000; // Pseudo-timestamp // Issue permit token and log receipt - self.receipt_log.append(decision, self.sequence, timestamp, witness_hash); + self.receipt_log + .append(decision, self.sequence, timestamp, witness_hash); self.sequence += 1; decision @@ -1626,7 +1671,12 @@ impl TileZero { /// Evaluate the three-filter decision logic /// Evaluate the three-filter decision logic #[inline] - fn evaluate_filters(&self, global_cut: f64, shift_pressure: f64, e_aggregate: f64) -> GateDecision { + fn evaluate_filters( + &self, + global_cut: f64, + shift_pressure: f64, + e_aggregate: f64, + ) -> GateDecision { // Filter 1: Structural (min-cut check) if global_cut < self.thresholds.structural_min_cut { return GateDecision::Deny; @@ -1926,8 +1976,8 @@ mod tests { #[test] fn test_tilezero_with_signing_key() { - use rand::rngs::OsRng; use ed25519_dalek::SigningKey; + use rand::rngs::OsRng; // Create TileZero with a random signing key let thresholds = GateThresholds::default(); @@ -1999,7 +2049,10 @@ mod tests { let token = tilezero.issue_permit(&decision); // Token should have placeholder marker - assert_eq!(token.signature[63], 0xFF, "Token should have placeholder signature marker"); + assert_eq!( + token.signature[63], 0xFF, + "Token should have placeholder signature marker" + ); // verify_token should return None when no key is configured assert_eq!(tilezero.verify_token(&token), None); diff --git a/crates/ruQu/src/traits.rs b/crates/ruQu/src/traits.rs index c80380859..be2703f97 100644 --- a/crates/ruQu/src/traits.rs +++ b/crates/ruQu/src/traits.rs @@ -422,10 +422,7 @@ impl ActionSink for LoggingActionSink { fn capabilities(&self) -> ActionCapabilities { ActionCapabilities { - supported_actions: vec![ - ActionType::LogEvent, - ActionType::AlertOperator, - ], + supported_actions: vec![ActionType::LogEvent, ActionType::AlertOperator], max_concurrent: 100, min_interval_ns: 0, } diff --git a/crates/ruQu/src/types.rs b/crates/ruQu/src/types.rs index 3bb24be49..aa51d57fb 100644 --- a/crates/ruQu/src/types.rs +++ b/crates/ruQu/src/types.rs @@ -170,7 +170,9 @@ impl RegionMask { /// Create a mask with all bits set (all regions) #[inline] pub const fn all() -> Self { - Self { bits: [u64::MAX; 4] } + Self { + bits: [u64::MAX; 4], + } } /// Create a mask from a slice of tile IDs @@ -290,11 +292,7 @@ impl Default for RegionMask { impl std::fmt::Display for RegionMask { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "RegionMask({} tiles)", - self.count() - ) + write!(f, "RegionMask({} tiles)", self.count()) } } @@ -550,11 +548,16 @@ impl GateThresholds { constraint: format!("in [{}, {}]", MIN_PERMIT_TTL_NS, MAX_PERMIT_TTL_NS), }); } - if self.decision_budget_ns < MIN_DECISION_BUDGET_NS || self.decision_budget_ns > MAX_DECISION_BUDGET_NS { + if self.decision_budget_ns < MIN_DECISION_BUDGET_NS + || self.decision_budget_ns > MAX_DECISION_BUDGET_NS + { return Err(crate::error::RuQuError::InvalidThreshold { name: "decision_budget_ns".to_string(), value: self.decision_budget_ns as f64, - constraint: format!("in [{}, {}]", MIN_DECISION_BUDGET_NS, MAX_DECISION_BUDGET_NS), + constraint: format!( + "in [{}, {}]", + MIN_DECISION_BUDGET_NS, MAX_DECISION_BUDGET_NS + ), }); } diff --git a/crates/ruQu/tests/filter_tests.rs b/crates/ruQu/tests/filter_tests.rs index 515b1dd41..34b6c0b23 100644 --- a/crates/ruQu/tests/filter_tests.rs +++ b/crates/ruQu/tests/filter_tests.rs @@ -76,10 +76,10 @@ mod structural_filter_tests { #[test] fn test_structural_filter_various_cut_values() { let test_cases = vec![ - (vec![(1, 2, 1.0)], 1.0, true), // Single edge at threshold (>= passes) - (vec![(1, 2, 2.0)], 1.0, true), // Single edge weight 2.0 above threshold - (vec![(1, 2, 1.0), (2, 3, 1.0)], 1.0, true), // Path - (vec![(1, 2, 0.5)], 1.0, false), // Weak edge below threshold + (vec![(1, 2, 1.0)], 1.0, true), // Single edge at threshold (>= passes) + (vec![(1, 2, 2.0)], 1.0, true), // Single edge weight 2.0 above threshold + (vec![(1, 2, 1.0), (2, 3, 1.0)], 1.0, true), // Path + (vec![(1, 2, 0.5)], 1.0, false), // Weak edge below threshold ]; for (edges, threshold, expected_coherent) in test_cases { @@ -633,10 +633,7 @@ mod filter_pipeline_tests { let result = pipeline.evaluate(&state); // Should be Defer (evidence accumulating) since no evidence added - assert!( - result.verdict == Some(Verdict::Defer) - || result.evidence.verdict == None - ); + assert!(result.verdict == Some(Verdict::Defer) || result.evidence.verdict == None); } #[test] diff --git a/crates/ruQu/tests/integration_tests.rs b/crates/ruQu/tests/integration_tests.rs index 2820dc8ad..969067774 100644 --- a/crates/ruQu/tests/integration_tests.rs +++ b/crates/ruQu/tests/integration_tests.rs @@ -4,7 +4,9 @@ //! and receipt generation with verification. use ruqu::{ - filters::{EvidenceConfig, FilterConfig, FilterPipeline, ShiftConfig, StructuralConfig, Verdict}, + filters::{ + EvidenceConfig, FilterConfig, FilterPipeline, ShiftConfig, StructuralConfig, Verdict, + }, prelude::*, syndrome::{DetectorBitmap, SyndromeBuffer, SyndromeDelta, SyndromeRound}, tile::{ diff --git a/crates/ruQu/tests/stress_tests.rs b/crates/ruQu/tests/stress_tests.rs index c1f5de64a..4cea81930 100644 --- a/crates/ruQu/tests/stress_tests.rs +++ b/crates/ruQu/tests/stress_tests.rs @@ -4,8 +4,8 @@ //! rapid decision cycling, and error recovery scenarios. use ruqu::filters::{ - EvidenceAccumulator, EvidenceConfig, EvidenceFilter, FilterConfig, FilterPipeline, - ShiftConfig, ShiftFilter, StructuralConfig, StructuralFilter, SystemState, Verdict, + EvidenceAccumulator, EvidenceConfig, EvidenceFilter, FilterConfig, FilterPipeline, ShiftConfig, + ShiftFilter, StructuralConfig, StructuralFilter, SystemState, Verdict, }; use ruqu::syndrome::{DetectorBitmap, SyndromeBuffer, SyndromeDelta, SyndromeRound}; use ruqu::tile::{ @@ -62,7 +62,11 @@ mod throughput_tests { let duration = start.elapsed(); // Performance sanity check - should complete in reasonable time - assert!(duration.as_millis() < 5_000, "100k rounds took too long: {:?}", duration); + assert!( + duration.as_millis() < 5_000, + "100k rounds took too long: {:?}", + duration + ); // Data integrity assert_eq!(buffer.len(), 1024); @@ -75,18 +79,19 @@ mod throughput_tests { let start = Instant::now(); for i in 0..10_000 { - let delta = TileSyndromeDelta::new( - (i % 64) as u16, - ((i + 1) % 64) as u16, - (i % 256) as u16, - ); + let delta = + TileSyndromeDelta::new((i % 64) as u16, ((i + 1) % 64) as u16, (i % 256) as u16); tile.tick(&delta); } let duration = start.elapsed(); assert_eq!(tile.tick, 10_000); - assert!(duration.as_millis() < 5_000, "10k ticks took too long: {:?}", duration); + assert!( + duration.as_millis() < 5_000, + "10k ticks took too long: {:?}", + duration + ); } #[test] @@ -113,7 +118,11 @@ mod throughput_tests { let duration = start.elapsed(); assert_eq!(tilezero.receipt_log.len(), 1_000); - assert!(duration.as_millis() < 5_000, "1000 merges took too long: {:?}", duration); + assert!( + duration.as_millis() < 5_000, + "1000 merges took too long: {:?}", + duration + ); } #[test] @@ -140,7 +149,11 @@ mod throughput_tests { let duration = start.elapsed(); // 300k bitmap operations should be fast (SIMD-like) - assert!(duration.as_millis() < 2_000, "Bitmap ops took too long: {:?}", duration); + assert!( + duration.as_millis() < 2_000, + "Bitmap ops took too long: {:?}", + duration + ); } #[test] @@ -161,7 +174,11 @@ mod throughput_tests { let duration = start.elapsed(); // 1M popcounts should be very fast (hardware instruction) - assert!(duration.as_millis() < 1_000, "Popcount ops took too long: {:?}", duration); + assert!( + duration.as_millis() < 1_000, + "Popcount ops took too long: {:?}", + duration + ); assert!(total > 0); // Prevent optimization } } @@ -185,7 +202,11 @@ mod memory_pressure_tests { ); // Log actual size for monitoring - println!("WorkerTile memory: {} bytes ({:.1}% of 64KB)", size, (size as f64 / 65536.0) * 100.0); + println!( + "WorkerTile memory: {} bytes ({:.1}% of 64KB)", + size, + (size as f64 / 65536.0) * 100.0 + ); } #[test] @@ -193,11 +214,7 @@ mod memory_pressure_tests { let size = PatchGraph::memory_size(); // PatchGraph should be ~32KB - assert!( - size <= 65536, - "PatchGraph exceeds 64KB: {} bytes", - size - ); + assert!(size <= 65536, "PatchGraph exceeds 64KB: {} bytes", size); println!("PatchGraph memory: {} bytes", size); } @@ -207,11 +224,7 @@ mod memory_pressure_tests { let size = ruqu::tile::SyndromBuffer::memory_size(); // SyndromBuffer should be ~16KB - assert!( - size <= 32768, - "SyndromBuffer exceeds 32KB: {} bytes", - size - ); + assert!(size <= 32768, "SyndromBuffer exceeds 32KB: {} bytes", size); println!("SyndromBuffer memory: {} bytes", size); } @@ -381,7 +394,11 @@ mod rapid_decision_tests { let duration = start.elapsed(); // 10k evaluations should be fast - assert!(duration.as_millis() < 5_000, "10k evaluations took too long: {:?}", duration); + assert!( + duration.as_millis() < 5_000, + "10k evaluations took too long: {:?}", + duration + ); } #[test] @@ -397,7 +414,11 @@ mod rapid_decision_tests { let duration = start.elapsed(); // 100k updates should be fast - assert!(duration.as_millis() < 1_000, "100k evidence updates took too long: {:?}", duration); + assert!( + duration.as_millis() < 1_000, + "100k evidence updates took too long: {:?}", + duration + ); // E-value should be very high assert!(acc.e_value() > 1e10); @@ -415,7 +436,11 @@ mod rapid_decision_tests { let duration = start.elapsed(); - assert!(duration.as_millis() < 2_000, "100k shift updates took too long: {:?}", duration); + assert!( + duration.as_millis() < 2_000, + "100k shift updates took too long: {:?}", + duration + ); } #[test] diff --git a/crates/ruQu/tests/syndrome_tests.rs b/crates/ruQu/tests/syndrome_tests.rs index ba5523eb6..45328ce4a 100644 --- a/crates/ruQu/tests/syndrome_tests.rs +++ b/crates/ruQu/tests/syndrome_tests.rs @@ -4,7 +4,9 @@ //! syndrome buffer ring behavior, delta computation accuracy, //! and buffer overflow handling. -use ruqu::syndrome::{BufferStatistics, DetectorBitmap, SyndromeBuffer, SyndromeDelta, SyndromeRound}; +use ruqu::syndrome::{ + BufferStatistics, DetectorBitmap, SyndromeBuffer, SyndromeDelta, SyndromeRound, +}; use ruqu::MAX_DETECTORS; // ============================================================================ diff --git a/crates/ruQu/tests/tile_tests.rs b/crates/ruQu/tests/tile_tests.rs index b7e5c7fc9..6504b192a 100644 --- a/crates/ruQu/tests/tile_tests.rs +++ b/crates/ruQu/tests/tile_tests.rs @@ -8,9 +8,9 @@ use ruqu::tile::{ Edge, EvidenceAccumulator, GateDecision, GateThresholds, LocalCutState, PatchGraph, - PermitToken, ReceiptLog, SyndromBuffer, SyndromeEntry, SyndromeDelta, TileReport, TileZero, - Vertex, WorkerTile, MAX_BOUNDARY_CANDIDATES, MAX_PATCH_EDGES, MAX_PATCH_VERTICES, - NUM_WORKERS, SYNDROME_BUFFER_DEPTH, + PermitToken, ReceiptLog, SyndromBuffer, SyndromeDelta, SyndromeEntry, TileReport, TileZero, + Vertex, WorkerTile, MAX_BOUNDARY_CANDIDATES, MAX_PATCH_EDGES, MAX_PATCH_VERTICES, NUM_WORKERS, + SYNDROME_BUFFER_DEPTH, }; // ============================================================================ @@ -970,7 +970,11 @@ mod scaling_tests { // Each tile should fit within 64KB budget (with some margin) // The spec says ~64KB, so we allow up to 128KB - assert!(tile_size <= 131072, "Worker tile exceeds memory budget: {} bytes", tile_size); + assert!( + tile_size <= 131072, + "Worker tile exceeds memory budget: {} bytes", + tile_size + ); } } diff --git a/crates/ruvector-attention-unified-wasm/src/dag.rs b/crates/ruvector-attention-unified-wasm/src/dag.rs index 6d2bc9e29..7eefe6a2f 100644 --- a/crates/ruvector-attention-unified-wasm/src/dag.rs +++ b/crates/ruvector-attention-unified-wasm/src/dag.rs @@ -9,12 +9,10 @@ //! - Parallel Branch Attention //! - Temporal BTSP Attention -use ruvector_dag::{ - QueryDag, OperatorNode, -}; +use ruvector_dag::{OperatorNode, QueryDag}; use serde::{Deserialize, Serialize}; -use wasm_bindgen::prelude::*; use std::collections::HashMap; +use wasm_bindgen::prelude::*; // ============================================================================ // Minimal DAG for WASM @@ -91,7 +89,8 @@ impl WasmQueryDag { serde_json::to_string(&DagSummary { node_count: self.inner.node_count(), edge_count: self.inner.edge_count(), - }).unwrap_or_default() + }) + .unwrap_or_default() } } @@ -113,7 +112,9 @@ struct DagSummary { // ============================================================================ fn hashmap_to_vec(scores: &HashMap, n: usize) -> Vec { - (0..n).map(|i| scores.get(&i).copied().unwrap_or(0.0)).collect() + (0..n) + .map(|i| scores.get(&i).copied().unwrap_or(0.0)) + .collect() } // ============================================================================ @@ -309,7 +310,9 @@ impl WasmCriticalPathAttention { longest_path .into_iter() .max_by(|a, b| { - a.1.0.partial_cmp(&b.1.0).unwrap_or(std::cmp::Ordering::Equal) + a.1 .0 + .partial_cmp(&b.1 .0) + .unwrap_or(std::cmp::Ordering::Equal) }) .map(|(_, (_, path))| path) .unwrap_or_default() @@ -448,7 +451,10 @@ impl WasmHierarchicalLorentzAttention { /// * `temperature` - Temperature for softmax #[wasm_bindgen(constructor)] pub fn new(curvature: f32, temperature: f32) -> WasmHierarchicalLorentzAttention { - WasmHierarchicalLorentzAttention { curvature, temperature } + WasmHierarchicalLorentzAttention { + curvature, + temperature, + } } /// Compute attention scores for the DAG @@ -472,10 +478,17 @@ impl WasmHierarchicalLorentzAttention { } // Convert to attention scores using softmax - let max_neg_dist = distances.iter().map(|&d| -d / self.temperature).fold(f32::NEG_INFINITY, f32::max); - let exp_sum: f32 = distances.iter().map(|&d| ((-d / self.temperature) - max_neg_dist).exp()).sum(); + let max_neg_dist = distances + .iter() + .map(|&d| -d / self.temperature) + .fold(f32::NEG_INFINITY, f32::max); + let exp_sum: f32 = distances + .iter() + .map(|&d| ((-d / self.temperature) - max_neg_dist).exp()) + .sum(); - let scores: Vec = distances.iter() + let scores: Vec = distances + .iter() .map(|&d| ((-d / self.temperature) - max_neg_dist).exp() / exp_sum.max(1e-10)) .collect(); @@ -665,7 +678,9 @@ impl DagAttentionFactory { "causal_cone" => "Lightcone-based attention respecting causal dependencies".to_string(), "critical_path" => "Attention weighted by critical execution path distance".to_string(), "mincut_gated" => "Flow-based gating through bottleneck nodes".to_string(), - "hierarchical_lorentz" => "Multi-scale hyperbolic attention for DAG hierarchies".to_string(), + "hierarchical_lorentz" => { + "Multi-scale hyperbolic attention for DAG hierarchies".to_string() + } "parallel_branch" => "Branch-aware attention for parallel DAG structures".to_string(), "temporal_btsp" => "Time-series pattern attention for temporal DAGs".to_string(), _ => "Unknown attention type".to_string(), diff --git a/crates/ruvector-attention-unified-wasm/src/graph.rs b/crates/ruvector-attention-unified-wasm/src/graph.rs index 38b5eafd1..ff091c566 100644 --- a/crates/ruvector-attention-unified-wasm/src/graph.rs +++ b/crates/ruvector-attention-unified-wasm/src/graph.rs @@ -6,9 +6,9 @@ //! - GraphSAGE (Sample and Aggregate) use ruvector_gnn::{ - CompressedTensor, CompressionLevel, RuvectorLayer, TensorCompress, differentiable_search as core_differentiable_search, - hierarchical_forward as core_hierarchical_forward, + hierarchical_forward as core_hierarchical_forward, CompressedTensor, CompressionLevel, + RuvectorLayer, TensorCompress, }; use serde::{Deserialize, Serialize}; use wasm_bindgen::prelude::*; @@ -79,7 +79,9 @@ impl WasmGNNLayer { ))); } - let result = self.inner.forward(&node_embedding, &neighbors, &edge_weights); + let result = self + .inner + .forward(&node_embedding, &neighbors, &edge_weights); Ok(result) } @@ -123,7 +125,8 @@ impl WasmTensorCompress { /// - f > 0.01: 4-bit PQ (cold data) /// - f <= 0.01: Binary (archive) pub fn compress(&self, embedding: Vec, access_freq: f32) -> Result { - let compressed = self.inner + let compressed = self + .inner .compress(&embedding, access_freq) .map_err(|e| JsError::new(&format!("Compression failed: {}", e)))?; @@ -137,17 +140,33 @@ impl WasmTensorCompress { /// * `embedding` - The input embedding vector /// * `level` - Compression level: "none", "half", "pq8", "pq4", "binary" #[wasm_bindgen(js_name = compressWithLevel)] - pub fn compress_with_level(&self, embedding: Vec, level: &str) -> Result { + pub fn compress_with_level( + &self, + embedding: Vec, + level: &str, + ) -> Result { let compression_level = match level { "none" => CompressionLevel::None, "half" => CompressionLevel::Half { scale: 1.0 }, - "pq8" => CompressionLevel::PQ8 { subvectors: 8, centroids: 16 }, - "pq4" => CompressionLevel::PQ4 { subvectors: 8, outlier_threshold: 3.0 }, + "pq8" => CompressionLevel::PQ8 { + subvectors: 8, + centroids: 16, + }, + "pq4" => CompressionLevel::PQ4 { + subvectors: 8, + outlier_threshold: 3.0, + }, "binary" => CompressionLevel::Binary { threshold: 0.0 }, - _ => return Err(JsError::new(&format!("Unknown compression level: {}", level))), + _ => { + return Err(JsError::new(&format!( + "Unknown compression level: {}", + level + ))) + } }; - let compressed = self.inner + let compressed = self + .inner .compress_with_level(&embedding, &compression_level) .map_err(|e| JsError::new(&format!("Compression failed: {}", e)))?; @@ -168,11 +187,17 @@ impl WasmTensorCompress { /// Get compression ratio estimate for a given access frequency #[wasm_bindgen(js_name = getCompressionRatio)] pub fn get_compression_ratio(&self, access_freq: f32) -> f32 { - if access_freq > 0.8 { 1.0 } - else if access_freq > 0.4 { 2.0 } - else if access_freq > 0.1 { 4.0 } - else if access_freq > 0.01 { 8.0 } - else { 32.0 } + if access_freq > 0.8 { + 1.0 + } else if access_freq > 0.4 { + 2.0 + } else if access_freq > 0.1 { + 4.0 + } else if access_freq > 0.01 { + 8.0 + } else { + 32.0 + } } } @@ -220,7 +245,8 @@ pub fn differentiable_search( let candidates: Vec> = serde_wasm_bindgen::from_value(candidate_embeddings) .map_err(|e| JsError::new(&format!("Failed to parse candidate embeddings: {}", e)))?; - let (indices, weights) = core_differentiable_search(&query, &candidates, config.k, config.temperature); + let (indices, weights) = + core_differentiable_search(&query, &candidates, config.k, config.temperature); let result = SearchResult { indices, weights }; serde_wasm_bindgen::to_value(&result) @@ -293,7 +319,9 @@ impl GraphAttentionFactory { #[wasm_bindgen(js_name = getDescription)] pub fn get_description(attention_type: &str) -> String { match attention_type { - "gat" => "Graph Attention Networks - learns attention weights over neighbors".to_string(), + "gat" => { + "Graph Attention Networks - learns attention weights over neighbors".to_string() + } "gcn" => "Graph Convolutional Networks - spectral convolution on graphs".to_string(), "graphsage" => "GraphSAGE - sample and aggregate neighbor features".to_string(), _ => "Unknown graph attention type".to_string(), diff --git a/crates/ruvector-attention-unified-wasm/src/lib.rs b/crates/ruvector-attention-unified-wasm/src/lib.rs index 0b686a277..e4e1bda20 100644 --- a/crates/ruvector-attention-unified-wasm/src/lib.rs +++ b/crates/ruvector-attention-unified-wasm/src/lib.rs @@ -41,18 +41,18 @@ static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; pub mod mamba; -mod neural; mod dag; mod graph; +mod neural; // ============================================================================ // Re-exports for convenient access // ============================================================================ -pub use neural::*; pub use dag::*; pub use graph::*; pub use mamba::*; +pub use neural::*; // ============================================================================ // Initialization @@ -97,14 +97,8 @@ pub fn available_mechanisms() -> JsValue { "parallel_branch".into(), "temporal_btsp".into(), ], - graph: vec![ - "gat".into(), - "gcn".into(), - "graphsage".into(), - ], - ssm: vec![ - "mamba".into(), - ], + graph: vec!["gat".into(), "gcn".into(), "graphsage".into()], + ssm: vec!["mamba".into()], }; serde_wasm_bindgen::to_value(&mechanisms).unwrap() } @@ -163,13 +157,25 @@ impl UnifiedAttention { pub fn new(mechanism: &str) -> Result { let valid_mechanisms = [ // Neural - "scaled_dot_product", "multi_head", "hyperbolic", "linear", - "flash", "local_global", "moe", + "scaled_dot_product", + "multi_head", + "hyperbolic", + "linear", + "flash", + "local_global", + "moe", // DAG - "topological", "causal_cone", "critical_path", "mincut_gated", - "hierarchical_lorentz", "parallel_branch", "temporal_btsp", + "topological", + "causal_cone", + "critical_path", + "mincut_gated", + "hierarchical_lorentz", + "parallel_branch", + "temporal_btsp", // Graph - "gat", "gcn", "graphsage", + "gat", + "gcn", + "graphsage", // SSM "mamba", ]; @@ -196,11 +202,16 @@ impl UnifiedAttention { #[wasm_bindgen(getter)] pub fn category(&self) -> String { match self.mechanism_type.as_str() { - "scaled_dot_product" | "multi_head" | "hyperbolic" | "linear" | - "flash" | "local_global" | "moe" => "neural".to_string(), + "scaled_dot_product" | "multi_head" | "hyperbolic" | "linear" | "flash" + | "local_global" | "moe" => "neural".to_string(), - "topological" | "causal_cone" | "critical_path" | "mincut_gated" | - "hierarchical_lorentz" | "parallel_branch" | "temporal_btsp" => "dag".to_string(), + "topological" + | "causal_cone" + | "critical_path" + | "mincut_gated" + | "hierarchical_lorentz" + | "parallel_branch" + | "temporal_btsp" => "dag".to_string(), "gat" | "gcn" | "graphsage" => "graph".to_string(), @@ -213,26 +224,35 @@ impl UnifiedAttention { /// Check if this mechanism supports sequence processing #[wasm_bindgen(js_name = supportsSequences)] pub fn supports_sequences(&self) -> bool { - matches!(self.mechanism_type.as_str(), - "scaled_dot_product" | "multi_head" | "linear" | "flash" | - "local_global" | "mamba" + matches!( + self.mechanism_type.as_str(), + "scaled_dot_product" | "multi_head" | "linear" | "flash" | "local_global" | "mamba" ) } /// Check if this mechanism supports graph/DAG structures #[wasm_bindgen(js_name = supportsGraphs)] pub fn supports_graphs(&self) -> bool { - matches!(self.mechanism_type.as_str(), - "topological" | "causal_cone" | "critical_path" | "mincut_gated" | - "hierarchical_lorentz" | "parallel_branch" | "temporal_btsp" | - "gat" | "gcn" | "graphsage" + matches!( + self.mechanism_type.as_str(), + "topological" + | "causal_cone" + | "critical_path" + | "mincut_gated" + | "hierarchical_lorentz" + | "parallel_branch" + | "temporal_btsp" + | "gat" + | "gcn" + | "graphsage" ) } /// Check if this mechanism supports hyperbolic geometry #[wasm_bindgen(js_name = supportsHyperbolic)] pub fn supports_hyperbolic(&self) -> bool { - matches!(self.mechanism_type.as_str(), + matches!( + self.mechanism_type.as_str(), "hyperbolic" | "hierarchical_lorentz" ) } @@ -248,7 +268,8 @@ pub fn cosine_similarity(a: Vec, b: Vec) -> Result { if a.len() != b.len() { return Err(JsError::new(&format!( "Vector dimensions must match: {} vs {}", - a.len(), b.len() + a.len(), + b.len() ))); } @@ -277,7 +298,8 @@ pub fn softmax(values: Vec) -> Vec { pub fn temperature_softmax(values: Vec, temperature: f32) -> Vec { if temperature <= 0.0 { // Return one-hot for the maximum - let max_idx = values.iter() + let max_idx = values + .iter() .enumerate() .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap()) .map(|(i, _)| i) diff --git a/crates/ruvector-attention-unified-wasm/src/mamba.rs b/crates/ruvector-attention-unified-wasm/src/mamba.rs index a10de5bd6..5572477b9 100644 --- a/crates/ruvector-attention-unified-wasm/src/mamba.rs +++ b/crates/ruvector-attention-unified-wasm/src/mamba.rs @@ -199,7 +199,10 @@ impl MambaSSMAttention { if input.len() != seq_len * dim { return Err(JsError::new(&format!( "Input size mismatch: expected {} ({}x{}), got {}", - seq_len * dim, seq_len, dim, input.len() + seq_len * dim, + seq_len, + dim, + input.len() ))); } @@ -218,7 +221,8 @@ impl MambaSSMAttention { let ssm_output = self.selective_scan(&projected, &ssm_params); // Step 4: Apply D skip connection - let with_skip: Vec> = ssm_output.iter() + let with_skip: Vec> = ssm_output + .iter() .zip(projected.iter()) .map(|(y, x)| { y.iter() @@ -252,13 +256,18 @@ impl MambaSSMAttention { /// /// Returns pseudo-attention scores showing which positions influence output #[wasm_bindgen(js_name = getAttentionScores)] - pub fn get_attention_scores(&self, input: Vec, seq_len: usize) -> Result, JsError> { + pub fn get_attention_scores( + &self, + input: Vec, + seq_len: usize, + ) -> Result, JsError> { let dim = self.config.dim; if input.len() != seq_len * dim { return Err(JsError::new(&format!( "Input size mismatch: expected {}, got {}", - seq_len * dim, input.len() + seq_len * dim, + input.len() ))); } @@ -270,9 +279,12 @@ impl MambaSSMAttention { for s in 0..=t { // Exponential decay based on distance and A parameters let distance = (t - s) as f32; - let decay: f32 = self.a_log.iter() + let decay: f32 = self + .a_log + .iter() .map(|&a| (a * distance).exp()) - .sum::() / self.config.state_dim as f32; + .sum::() + / self.config.state_dim as f32; scores[t * seq_len + s] = decay; } @@ -286,9 +298,11 @@ impl MambaSSMAttention { impl MambaSSMAttention { /// Project input from dim to inner_dim fn project_in(&self, input: &[Vec]) -> Vec> { - input.iter() + input + .iter() .map(|x| { - self.in_proj.iter() + self.in_proj + .iter() .map(|row| row.iter().zip(x.iter()).map(|(w, xi)| w * xi).sum()) .collect() }) @@ -297,9 +311,11 @@ impl MambaSSMAttention { /// Project from inner_dim back to dim fn project_out(&self, input: &[Vec]) -> Vec> { - input.iter() + input + .iter() .map(|x| { - self.out_proj.iter() + self.out_proj + .iter() .map(|row| row.iter().zip(x.iter()).map(|(w, xi)| w * xi).sum()) .collect() }) @@ -321,7 +337,8 @@ impl MambaSSMAttention { for (t, x) in input.iter().enumerate() { // Compute delta from input (softplus of projection) - let dt: Vec = x.iter() + let dt: Vec = x + .iter() .map(|&xi| { let raw = xi * 0.1; // Simple scaling let dt_val = (1.0 + raw.exp()).ln(); // Softplus @@ -349,7 +366,12 @@ impl MambaSSMAttention { } } - SelectiveSSMParams { a_bar, b_bar, c, delta } + SelectiveSSMParams { + a_bar, + b_bar, + c, + delta, + } } /// Run selective scan (parallel associative scan in practice) @@ -367,12 +389,13 @@ impl MambaSSMAttention { // Update hidden state: h_t = A_bar * h_{t-1} + B_bar * x_t for n in 0..state_dim { - hidden[d][n] = params.a_bar[t][d][n] * hidden[d][n] - + params.b_bar[t][d][n] * x_d; + hidden[d][n] = + params.a_bar[t][d][n] * hidden[d][n] + params.b_bar[t][d][n] * x_d; } // Compute output: y_t = C * h_t - output[t][d] = hidden[d].iter() + output[t][d] = hidden[d] + .iter() .zip(params.c[t][d].iter()) .map(|(h, c)| h * c) .sum(); diff --git a/crates/ruvector-attention/src/curvature/component_quantizer.rs b/crates/ruvector-attention/src/curvature/component_quantizer.rs index 1e6efc14a..1d63e8520 100644 --- a/crates/ruvector-attention/src/curvature/component_quantizer.rs +++ b/crates/ruvector-attention/src/curvature/component_quantizer.rs @@ -102,20 +102,14 @@ impl ComponentQuantizer { h_range: std::ops::Range, s_range: std::ops::Range, ) -> QuantizedVector { - let (euclidean, euclidean_scale) = self.quantize_component( - &vector[e_range], - self.euclidean_levels, - ); + let (euclidean, euclidean_scale) = + self.quantize_component(&vector[e_range], self.euclidean_levels); - let (hyperbolic, hyperbolic_scale) = self.quantize_component( - &vector[h_range], - self.hyperbolic_levels, - ); + let (hyperbolic, hyperbolic_scale) = + self.quantize_component(&vector[h_range], self.hyperbolic_levels); - let (spherical, spherical_scale) = self.quantize_component( - &vector[s_range], - self.spherical_levels, - ); + let (spherical, spherical_scale) = + self.quantize_component(&vector[s_range], self.spherical_levels); QuantizedVector { euclidean, @@ -177,11 +171,7 @@ impl ComponentQuantizer { } /// Dequantize to full vector - pub fn dequantize( - &self, - quant: &QuantizedVector, - total_dim: usize, - ) -> Vec { + pub fn dequantize(&self, quant: &QuantizedVector, total_dim: usize) -> Vec { let mut result = vec![0.0f32; total_dim]; let e_vec = self.dequantize_component(&quant.euclidean, quant.euclidean_scale); @@ -223,7 +213,8 @@ mod tests { let h_range = 32..48; let s_range = 48..64; - let quantized = quantizer.quantize(&vector, e_range.clone(), h_range.clone(), s_range.clone()); + let quantized = + quantizer.quantize(&vector, e_range.clone(), h_range.clone(), s_range.clone()); assert_eq!(quantized.euclidean.len(), 32); assert_eq!(quantized.hyperbolic.len(), 16); diff --git a/crates/ruvector-attention/src/curvature/fused_attention.rs b/crates/ruvector-attention/src/curvature/fused_attention.rs index f4eeaa081..bffb0f68f 100644 --- a/crates/ruvector-attention/src/curvature/fused_attention.rs +++ b/crates/ruvector-attention/src/curvature/fused_attention.rs @@ -5,9 +5,9 @@ //! //! logit(q,k) = a * dot(q_E, k_E) + b * dot(q_H_tan, k_H_tan) + c * dot(q_S, k_S) +use super::tangent_space::{TangentSpaceConfig, TangentSpaceMapper}; use crate::error::{AttentionError, AttentionResult}; use crate::traits::Attention; -use super::tangent_space::{TangentSpaceMapper, TangentSpaceConfig}; use serde::{Deserialize, Serialize}; /// Configuration for fused mixed-curvature attention @@ -65,7 +65,13 @@ impl FusedCurvatureConfig { } /// Get component ranges - pub fn component_ranges(&self) -> (std::ops::Range, std::ops::Range, std::ops::Range) { + pub fn component_ranges( + &self, + ) -> ( + std::ops::Range, + std::ops::Range, + std::ops::Range, + ) { let e_end = self.euclidean_dim; let h_end = e_end + self.hyperbolic_dim; let s_end = h_end + self.spherical_dim; @@ -213,10 +219,12 @@ impl MixedCurvatureFusedAttention { let sim_h = Self::dot_product_simd(&q_h_tangent, &cache.keys_hyperbolic_tangent[i]); // Spherical similarity (normalized dot product) - let sim_s = Self::dot_product_simd(&q_s_normalized, &cache.keys_spherical_normalized[i]); + let sim_s = + Self::dot_product_simd(&q_s_normalized, &cache.keys_spherical_normalized[i]); // Fused logit - (weights[0] * sim_e + weights[1] * sim_h + weights[2] * sim_s) / self.config.temperature + (weights[0] * sim_e + weights[1] * sim_h + weights[2] * sim_s) + / self.config.temperature }) .collect(); @@ -399,12 +407,8 @@ mod tests { let attention = MixedCurvatureFusedAttention::new(config); let query = vec![0.5f32; 64]; - let keys: Vec> = (0..20) - .map(|i| vec![0.1 + i as f32 * 0.02; 64]) - .collect(); - let values: Vec> = (0..20) - .map(|i| vec![i as f32; 64]) - .collect(); + let keys: Vec> = (0..20).map(|i| vec![0.1 + i as f32 * 0.02; 64]).collect(); + let values: Vec> = (0..20).map(|i| vec![i as f32; 64]).collect(); let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); let values_refs: Vec<&[f32]> = values.iter().map(|v| v.as_slice()).collect(); @@ -417,12 +421,8 @@ mod tests { fn test_cache_reuse() { let attention = MixedCurvatureFusedAttention::with_dim(32); - let keys: Vec> = (0..10) - .map(|i| vec![0.1 * i as f32; 32]) - .collect(); - let values: Vec> = (0..10) - .map(|i| vec![i as f32; 32]) - .collect(); + let keys: Vec> = (0..10).map(|i| vec![0.1 * i as f32; 32]).collect(); + let values: Vec> = (0..10).map(|i| vec![i as f32; 32]).collect(); let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); let values_refs: Vec<&[f32]> = values.iter().map(|v| v.as_slice()).collect(); @@ -432,7 +432,9 @@ mod tests { // Multiple queries with same cache for h in 0..4 { let query = vec![0.5f32; 32]; - let output = attention.compute_with_cache(&query, &keys_refs, &values_refs, &cache, h).unwrap(); + let output = attention + .compute_with_cache(&query, &keys_refs, &values_refs, &cache, h) + .unwrap(); assert_eq!(output.len(), 32); } } diff --git a/crates/ruvector-attention/src/curvature/mod.rs b/crates/ruvector-attention/src/curvature/mod.rs index e96a37a46..2ac90030f 100644 --- a/crates/ruvector-attention/src/curvature/mod.rs +++ b/crates/ruvector-attention/src/curvature/mod.rs @@ -9,13 +9,15 @@ //! 3. **Per-Head Mixing**: Low-rank learned weights per head //! 4. **Quantization-Friendly**: Different precision for each component -mod tangent_space; -mod fused_attention; mod component_quantizer; +mod fused_attention; +mod tangent_space; -pub use tangent_space::{TangentSpaceMapper, TangentSpaceConfig}; -pub use fused_attention::{MixedCurvatureCache, MixedCurvatureFusedAttention, FusedCurvatureConfig}; pub use component_quantizer::{ComponentQuantizer, QuantizationConfig, QuantizedVector}; +pub use fused_attention::{ + FusedCurvatureConfig, MixedCurvatureCache, MixedCurvatureFusedAttention, +}; +pub use tangent_space::{TangentSpaceConfig, TangentSpaceMapper}; #[cfg(test)] mod tests { diff --git a/crates/ruvector-attention/src/curvature/tangent_space.rs b/crates/ruvector-attention/src/curvature/tangent_space.rs index 90619edb7..954586d50 100644 --- a/crates/ruvector-attention/src/curvature/tangent_space.rs +++ b/crates/ruvector-attention/src/curvature/tangent_space.rs @@ -89,7 +89,8 @@ impl TangentSpaceMapper { return vec![0.0f32; point.len()]; } - let scale = (2.0 / self.lambda_origin) * (sqrt_c * diff_norm).atanh() / (sqrt_c * diff_norm); + let scale = + (2.0 / self.lambda_origin) * (sqrt_c * diff_norm).atanh() / (sqrt_c * diff_norm); diff.iter().map(|&d| scale * d).collect() } @@ -150,7 +151,11 @@ impl TangentSpaceMapper { } /// Compute similarities in tangent space (all pairwise with query) - pub fn batch_tangent_similarity(&self, query_tangent: &[f32], keys_tangent: &[&[f32]]) -> Vec { + pub fn batch_tangent_similarity( + &self, + query_tangent: &[f32], + keys_tangent: &[&[f32]], + ) -> Vec { keys_tangent .iter() .map(|k| Self::dot_product_simd(query_tangent, k)) @@ -232,9 +237,7 @@ mod tests { let config = TangentSpaceConfig::default(); let mapper = TangentSpaceMapper::new(config); - let points: Vec> = (0..10) - .map(|i| vec![i as f32 * 0.05; 32]) - .collect(); + let points: Vec> = (0..10).map(|i| vec![i as f32 * 0.05; 32]).collect(); let points_refs: Vec<&[f32]> = points.iter().map(|p| p.as_slice()).collect(); let tangents = mapper.batch_log_map(&points_refs); diff --git a/crates/ruvector-attention/src/info_bottleneck/bottleneck.rs b/crates/ruvector-attention/src/info_bottleneck/bottleneck.rs index e9b76fec5..39073a9e5 100644 --- a/crates/ruvector-attention/src/info_bottleneck/bottleneck.rs +++ b/crates/ruvector-attention/src/info_bottleneck/bottleneck.rs @@ -2,7 +2,7 @@ //! //! Apply information bottleneck principle to attention. -use super::kl_divergence::{KLDivergence, DiagonalGaussian}; +use super::kl_divergence::{DiagonalGaussian, KLDivergence}; use serde::{Deserialize, Serialize}; /// Information Bottleneck configuration @@ -58,12 +58,7 @@ impl InformationBottleneck { } /// Sample from bottleneck distribution (for forward pass) - pub fn sample( - &self, - mean: &[f32], - log_var: &[f32], - epsilon: &[f32], - ) -> Vec { + pub fn sample(&self, mean: &[f32], log_var: &[f32], epsilon: &[f32]) -> Vec { let n = mean.len().min(log_var.len()).min(epsilon.len()); let mut z = vec![0.0f32; n]; @@ -98,11 +93,7 @@ impl InformationBottleneck { /// Apply bottleneck to attention weights /// Returns: (compressed_weights, kl_loss) - pub fn compress_attention_weights( - &self, - weights: &[f32], - temperature: f32, - ) -> (Vec, f32) { + pub fn compress_attention_weights(&self, weights: &[f32], temperature: f32) -> (Vec, f32) { let n = weights.len(); // Compute entropy-based compression diff --git a/crates/ruvector-attention/src/info_bottleneck/kl_divergence.rs b/crates/ruvector-attention/src/info_bottleneck/kl_divergence.rs index fff0e1875..02f9cc556 100644 --- a/crates/ruvector-attention/src/info_bottleneck/kl_divergence.rs +++ b/crates/ruvector-attention/src/info_bottleneck/kl_divergence.rs @@ -2,7 +2,6 @@ //! //! Efficient KL divergence for various distributions used in attention. - /// Diagonal Gaussian parameters #[derive(Debug, Clone)] pub struct DiagonalGaussian { @@ -157,10 +156,7 @@ mod tests { #[test] fn test_kl_nonzero() { - let g = DiagonalGaussian::new( - vec![1.0, 0.5, -0.5], - vec![0.5, 0.0, -0.5], - ); + let g = DiagonalGaussian::new(vec![1.0, 0.5, -0.5], vec![0.5, 0.0, -0.5]); let kl = KLDivergence::gaussian_to_unit(&g); assert!(kl > 0.0); } @@ -198,10 +194,7 @@ mod tests { #[test] fn test_sample() { - let g = DiagonalGaussian::new( - vec![0.0, 1.0], - vec![0.0, 0.0], - ); + let g = DiagonalGaussian::new(vec![0.0, 1.0], vec![0.0, 0.0]); let epsilon = vec![0.0, 0.0]; let z = g.sample(&epsilon); diff --git a/crates/ruvector-attention/src/info_bottleneck/mod.rs b/crates/ruvector-attention/src/info_bottleneck/mod.rs index 86feb7c80..9f1d0ea24 100644 --- a/crates/ruvector-attention/src/info_bottleneck/mod.rs +++ b/crates/ruvector-attention/src/info_bottleneck/mod.rs @@ -14,11 +14,11 @@ //! - Encouraging sparse, meaningful attention patterns //! - Regularizing attention weights -mod kl_divergence; mod bottleneck; +mod kl_divergence; -pub use kl_divergence::{KLDivergence, DiagonalGaussian}; -pub use bottleneck::{InformationBottleneck, IBConfig}; +pub use bottleneck::{IBConfig, InformationBottleneck}; +pub use kl_divergence::{DiagonalGaussian, KLDivergence}; #[cfg(test)] mod tests { diff --git a/crates/ruvector-attention/src/info_geometry/mod.rs b/crates/ruvector-attention/src/info_geometry/mod.rs index 01c23446e..260eaf3e6 100644 --- a/crates/ruvector-attention/src/info_geometry/mod.rs +++ b/crates/ruvector-attention/src/info_geometry/mod.rs @@ -17,7 +17,7 @@ mod fisher; mod natural_gradient; -pub use fisher::{FisherMetric, FisherConfig}; +pub use fisher::{FisherConfig, FisherMetric}; pub use natural_gradient::{NaturalGradient, NaturalGradientConfig}; #[cfg(test)] diff --git a/crates/ruvector-attention/src/info_geometry/natural_gradient.rs b/crates/ruvector-attention/src/info_geometry/natural_gradient.rs index 4fe7fa03b..a5ece4a91 100644 --- a/crates/ruvector-attention/src/info_geometry/natural_gradient.rs +++ b/crates/ruvector-attention/src/info_geometry/natural_gradient.rs @@ -3,7 +3,7 @@ //! Update parameters using the natural gradient: F^{-1} * grad //! where F is the Fisher information matrix. -use super::fisher::{FisherMetric, FisherConfig}; +use super::fisher::{FisherConfig, FisherMetric}; use serde::{Deserialize, Serialize}; /// Natural gradient configuration @@ -64,12 +64,7 @@ impl NaturalGradient { /// Compute natural gradient step for general parameters with diagonal Fisher /// Fisher diag should be pre-computed from data - pub fn step_diagonal( - &self, - params: &[f32], - grads: &[f32], - fisher_diag: &[f32], - ) -> Vec { + pub fn step_diagonal(&self, params: &[f32], grads: &[f32], fisher_diag: &[f32]) -> Vec { let n = params.len(); let mut new_params = params.to_vec(); let eps = self.config.fisher.eps; @@ -84,11 +79,7 @@ impl NaturalGradient { /// Compute natural gradient for attention logits /// Uses the Fisher metric on the output probability distribution - pub fn step_attention_logits( - &self, - logits: &[f32], - grad_logits: &[f32], - ) -> Vec { + pub fn step_attention_logits(&self, logits: &[f32], grad_logits: &[f32]) -> Vec { self.step_logits(logits, grad_logits) } @@ -129,8 +120,9 @@ mod tests { assert_eq!(new_logits.len(), 4); // Should be different from original - assert!((new_logits[0] - logits[0]).abs() > 1e-6 || - (new_logits[1] - logits[1]).abs() > 1e-6); + assert!( + (new_logits[0] - logits[0]).abs() > 1e-6 || (new_logits[1] - logits[1]).abs() > 1e-6 + ); } #[test] diff --git a/crates/ruvector-attention/src/lib.rs b/crates/ruvector-attention/src/lib.rs index e1eda5138..95b531034 100644 --- a/crates/ruvector-attention/src/lib.rs +++ b/crates/ruvector-attention/src/lib.rs @@ -115,9 +115,8 @@ pub use transport::{ // Curvature (Mixed curvature attention) exports pub use curvature::{ - ComponentQuantizer, FusedCurvatureConfig, MixedCurvatureCache, - MixedCurvatureFusedAttention, QuantizationConfig, QuantizedVector, TangentSpaceMapper, - TangentSpaceConfig, + ComponentQuantizer, FusedCurvatureConfig, MixedCurvatureCache, MixedCurvatureFusedAttention, + QuantizationConfig, QuantizedVector, TangentSpaceConfig, TangentSpaceMapper, }; // Topology (Gated attention) exports @@ -130,9 +129,7 @@ pub use topology::{ pub use info_geometry::{FisherConfig, FisherMetric, NaturalGradient, NaturalGradientConfig}; // Information Bottleneck exports -pub use info_bottleneck::{ - DiagonalGaussian, IBConfig, InformationBottleneck, KLDivergence, -}; +pub use info_bottleneck::{DiagonalGaussian, IBConfig, InformationBottleneck, KLDivergence}; // PDE Attention exports pub use pde_attention::{DiffusionAttention, DiffusionConfig, GraphLaplacian, LaplacianType}; @@ -140,10 +137,11 @@ pub use pde_attention::{DiffusionAttention, DiffusionConfig, GraphLaplacian, Lap // Sheaf Attention exports (Coherence-Gated Transformer per ADR-015) #[cfg(feature = "sheaf")] pub use sheaf::{ - ComputeLane, EarlyExit, EarlyExitConfig, EarlyExitResult, EarlyExitStatistics, ExitReason, - LaneStatistics, ResidualSparseMask, RestrictionMap, RestrictionMapConfig, RoutingDecision, - SheafAttention, SheafAttentionConfig, SparseResidualAttention, SparseResidualConfig, - SparsityStatistics, TokenRouter, TokenRouterConfig, process_with_early_exit, + process_with_early_exit, ComputeLane, EarlyExit, EarlyExitConfig, EarlyExitResult, + EarlyExitStatistics, ExitReason, LaneStatistics, ResidualSparseMask, RestrictionMap, + RestrictionMapConfig, RoutingDecision, SheafAttention, SheafAttentionConfig, + SparseResidualAttention, SparseResidualConfig, SparsityStatistics, TokenRouter, + TokenRouterConfig, }; // Unified Report exports diff --git a/crates/ruvector-attention/src/pde_attention/diffusion.rs b/crates/ruvector-attention/src/pde_attention/diffusion.rs index 2d0d186e8..c86374cc3 100644 --- a/crates/ruvector-attention/src/pde_attention/diffusion.rs +++ b/crates/ruvector-attention/src/pde_attention/diffusion.rs @@ -2,9 +2,9 @@ //! //! Attention as heat diffusion on a key similarity graph. +use super::laplacian::{GraphLaplacian, LaplacianType}; use crate::error::{AttentionError, AttentionResult}; use crate::traits::Attention; -use super::laplacian::{GraphLaplacian, LaplacianType}; use serde::{Deserialize, Serialize}; /// Diffusion attention configuration @@ -84,11 +84,7 @@ impl DiffusionAttention { self.config.laplacian_type, ) } else { - GraphLaplacian::from_keys( - keys, - self.config.sigma, - self.config.laplacian_type, - ) + GraphLaplacian::from_keys(keys, self.config.sigma, self.config.laplacian_type) }; // Initial logits from dot product @@ -147,11 +143,7 @@ impl DiffusionAttention { self.config.laplacian_type, ) } else { - GraphLaplacian::from_keys( - keys, - self.config.sigma, - self.config.laplacian_type, - ) + GraphLaplacian::from_keys(keys, self.config.sigma, self.config.laplacian_type) }; let mut x: Vec = keys @@ -294,12 +286,8 @@ mod tests { let attention = DiffusionAttention::with_dim(16); let query = vec![1.0f32; 16]; - let keys: Vec> = (0..8) - .map(|i| vec![i as f32 * 0.1; 16]) - .collect(); - let values: Vec> = (0..8) - .map(|i| vec![i as f32; 16]) - .collect(); + let keys: Vec> = (0..8).map(|i| vec![i as f32 * 0.1; 16]).collect(); + let values: Vec> = (0..8).map(|i| vec![i as f32; 16]).collect(); let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); let values_refs: Vec<&[f32]> = values.iter().map(|v| v.as_slice()).collect(); @@ -318,9 +306,7 @@ mod tests { let attention = DiffusionAttention::new(config); let query = vec![1.0f32; 8]; - let keys: Vec> = (0..5) - .map(|i| vec![i as f32 * 0.1; 8]) - .collect(); + let keys: Vec> = (0..5).map(|i| vec![i as f32 * 0.1; 8]).collect(); let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); @@ -345,12 +331,8 @@ mod tests { let attention = DiffusionAttention::new(config); let query = vec![1.0f32; 8]; - let keys: Vec> = (0..10) - .map(|i| vec![i as f32 * 0.1; 8]) - .collect(); - let values: Vec> = (0..10) - .map(|i| vec![i as f32; 8]) - .collect(); + let keys: Vec> = (0..10).map(|i| vec![i as f32 * 0.1; 8]).collect(); + let values: Vec> = (0..10).map(|i| vec![i as f32; 8]).collect(); let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); let values_refs: Vec<&[f32]> = values.iter().map(|v| v.as_slice()).collect(); diff --git a/crates/ruvector-attention/src/pde_attention/laplacian.rs b/crates/ruvector-attention/src/pde_attention/laplacian.rs index 6931b97dd..ac77182e1 100644 --- a/crates/ruvector-attention/src/pde_attention/laplacian.rs +++ b/crates/ruvector-attention/src/pde_attention/laplacian.rs @@ -30,11 +30,7 @@ pub struct GraphLaplacian { impl GraphLaplacian { /// Build Laplacian from keys using Gaussian kernel - pub fn from_keys( - keys: &[&[f32]], - sigma: f32, - lap_type: LaplacianType, - ) -> Self { + pub fn from_keys(keys: &[&[f32]], sigma: f32, lap_type: LaplacianType) -> Self { let n = keys.len(); let sigma2 = (sigma * sigma).max(1e-9); @@ -65,12 +61,7 @@ impl GraphLaplacian { } /// Build sparse Laplacian using k-NN - pub fn from_keys_knn( - keys: &[&[f32]], - k: usize, - sigma: f32, - lap_type: LaplacianType, - ) -> Self { + pub fn from_keys_knn(keys: &[&[f32]], k: usize, sigma: f32, lap_type: LaplacianType) -> Self { let n = keys.len(); // Security: prevent integer underflow when n=0 or n=1 let k = if n > 1 { k.min(n - 1) } else { 0 }; @@ -86,7 +77,9 @@ impl GraphLaplacian { .map(|j| (j, Self::l2_sq(keys[i], keys[j]))) .collect(); - dists.sort_unstable_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal)); + dists.sort_unstable_by(|a, b| { + a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal) + }); // Keep only k nearest for (j, dist2) in dists.iter().take(k) { @@ -127,7 +120,9 @@ impl GraphLaplacian { } LaplacianType::SymmetricNormalized => { // L * x = x - D^{-1/2} W D^{-1/2} x - let d_inv_sqrt: Vec = self.degrees.iter() + let d_inv_sqrt: Vec = self + .degrees + .iter() .map(|&d| if d > 0.0 { 1.0 / d.sqrt() } else { 0.0 }) .collect(); @@ -143,7 +138,11 @@ impl GraphLaplacian { // L * x = x - D^{-1} W * x for i in 0..self.n { result[i] = x[i]; - let d_inv = if self.degrees[i] > 0.0 { 1.0 / self.degrees[i] } else { 0.0 }; + let d_inv = if self.degrees[i] > 0.0 { + 1.0 / self.degrees[i] + } else { + 0.0 + }; for j in 0..self.n { result[i] -= d_inv * self.weights[i * self.n + j] * x[j]; } @@ -192,11 +191,7 @@ mod tests { #[test] fn test_laplacian_build() { - let keys: Vec> = vec![ - vec![0.0, 0.0], - vec![1.0, 0.0], - vec![0.0, 1.0], - ]; + let keys: Vec> = vec![vec![0.0, 0.0], vec![1.0, 0.0], vec![0.0, 1.0]]; let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); let lap = GraphLaplacian::from_keys(&keys_refs, 1.0, LaplacianType::Unnormalized); @@ -207,11 +202,7 @@ mod tests { #[test] fn test_laplacian_apply() { - let keys: Vec> = vec![ - vec![0.0], - vec![1.0], - vec![2.0], - ]; + let keys: Vec> = vec![vec![0.0], vec![1.0], vec![2.0]]; let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); let lap = GraphLaplacian::from_keys(&keys_refs, 1.0, LaplacianType::Unnormalized); @@ -226,9 +217,7 @@ mod tests { #[test] fn test_knn_laplacian() { - let keys: Vec> = (0..10) - .map(|i| vec![i as f32]) - .collect(); + let keys: Vec> = (0..10).map(|i| vec![i as f32]).collect(); let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); let lap = GraphLaplacian::from_keys_knn(&keys_refs, 3, 1.0, LaplacianType::RandomWalk); diff --git a/crates/ruvector-attention/src/sheaf/attention.rs b/crates/ruvector-attention/src/sheaf/attention.rs index c25b1e96a..f8c6c4fd0 100644 --- a/crates/ruvector-attention/src/sheaf/attention.rs +++ b/crates/ruvector-attention/src/sheaf/attention.rs @@ -339,10 +339,7 @@ impl SheafAttention { }) .collect() } else { - energies - .iter() - .map(|&e| -self.config.beta * e) - .collect() + energies.iter().map(|&e| -self.config.beta * e).collect() }; let attention_weights = stable_softmax(&logits); @@ -464,10 +461,7 @@ impl Attention for SheafAttention { }) .collect() } else { - energies - .iter() - .map(|&e| -self.config.beta * e) - .collect() + energies.iter().map(|&e| -self.config.beta * e).collect() }; let attention_weights = stable_softmax(&logits); @@ -569,12 +563,8 @@ mod tests { // With identity-like restriction maps, identical vectors should have low energy let config = SheafAttentionConfig::new(4); let rho = RestrictionMap::identity(4); - let attention = SheafAttention::with_restriction_maps( - config, - rho.clone(), - rho.clone(), - rho, - ); + let attention = + SheafAttention::with_restriction_maps(config, rho.clone(), rho.clone(), rho); let v = vec![1.0, 2.0, 3.0, 4.0]; let energy = attention.compute_energy(&v, &v).unwrap(); @@ -697,12 +687,8 @@ mod tests { rho.clone(), rho.clone(), ); - let attention_high = SheafAttention::with_restriction_maps( - config_high, - rho.clone(), - rho.clone(), - rho, - ); + let attention_high = + SheafAttention::with_restriction_maps(config_high, rho.clone(), rho.clone(), rho); let query = vec![1.0; 8]; let k1 = vec![1.0; 8]; diff --git a/crates/ruvector-attention/src/sheaf/mod.rs b/crates/ruvector-attention/src/sheaf/mod.rs index aa037bf68..4fe476043 100644 --- a/crates/ruvector-attention/src/sheaf/mod.rs +++ b/crates/ruvector-attention/src/sheaf/mod.rs @@ -46,10 +46,15 @@ mod router; mod sparse; pub use attention::{SheafAttention, SheafAttentionConfig}; -pub use early_exit::{EarlyExit, EarlyExitConfig, EarlyExitResult, EarlyExitStatistics, ExitReason, process_with_early_exit}; +pub use early_exit::{ + process_with_early_exit, EarlyExit, EarlyExitConfig, EarlyExitResult, EarlyExitStatistics, + ExitReason, +}; pub use restriction::{RestrictionMap, RestrictionMapConfig}; pub use router::{ComputeLane, LaneStatistics, RoutingDecision, TokenRouter, TokenRouterConfig}; -pub use sparse::{ResidualSparseMask, SparseResidualAttention, SparseResidualConfig, SparsityStatistics}; +pub use sparse::{ + ResidualSparseMask, SparseResidualAttention, SparseResidualConfig, SparsityStatistics, +}; #[cfg(test)] mod tests { diff --git a/crates/ruvector-attention/src/sheaf/restriction.rs b/crates/ruvector-attention/src/sheaf/restriction.rs index 69a3cbc5a..06b8622f1 100644 --- a/crates/ruvector-attention/src/sheaf/restriction.rs +++ b/crates/ruvector-attention/src/sheaf/restriction.rs @@ -106,9 +106,9 @@ impl RestrictionMap { /// Create from configuration pub fn from_config(config: RestrictionMapConfig) -> Self { - let scale = config.init_scale.unwrap_or_else(|| { - (2.0 / (config.input_dim + config.output_dim) as f32).sqrt() - }); + let scale = config + .init_scale + .unwrap_or_else(|| (2.0 / (config.input_dim + config.output_dim) as f32).sqrt()); // Deterministic pseudo-random initialization let mut seed = 42u64; diff --git a/crates/ruvector-attention/src/sheaf/router.rs b/crates/ruvector-attention/src/sheaf/router.rs index 805e147e6..fd7cd0f52 100644 --- a/crates/ruvector-attention/src/sheaf/router.rs +++ b/crates/ruvector-attention/src/sheaf/router.rs @@ -295,11 +295,7 @@ impl TokenRouter { ) -> AttentionResult { // Handle small contexts if context.len() < self.config.min_context_size { - return Ok(RoutingDecision::new( - token_idx, - 0.0, - ComputeLane::Standard, - )); + return Ok(RoutingDecision::new(token_idx, 0.0, ComputeLane::Standard)); } // Compute energy @@ -341,7 +337,9 @@ impl TokenRouter { /// Group tokens by their assigned lane /// /// Returns (reflex_indices, standard_indices, deep_indices, escalate_indices) - pub fn group_by_lane(decisions: &[RoutingDecision]) -> (Vec, Vec, Vec, Vec) { + pub fn group_by_lane( + decisions: &[RoutingDecision], + ) -> (Vec, Vec, Vec, Vec) { let mut reflex = Vec::new(); let mut standard = Vec::new(); let mut deep = Vec::new(); @@ -370,10 +368,7 @@ impl TokenRouter { 0.0 }; - let max_energy = decisions - .iter() - .map(|d| d.energy) - .fold(0.0f32, f32::max); + let max_energy = decisions.iter().map(|d| d.energy).fold(0.0f32, f32::max); let min_energy = decisions .iter() @@ -388,16 +383,17 @@ impl TokenRouter { escalate_count: escalate.len(), average_energy: avg_energy, max_energy, - min_energy: if min_energy.is_infinite() { 0.0 } else { min_energy }, + min_energy: if min_energy.is_infinite() { + 0.0 + } else { + min_energy + }, } } /// Estimate total latency for a batch based on routing pub fn estimate_latency_ms(decisions: &[RoutingDecision]) -> f32 { - decisions - .iter() - .map(|d| d.lane.typical_latency_ms()) - .sum() + decisions.iter().map(|d| d.lane.typical_latency_ms()).sum() } /// Update thresholds based on desired lane distribution @@ -498,7 +494,8 @@ impl LaneStatistics { 1.0 } else { let deep_latency = self.total_tokens as f32 * ComputeLane::Deep.typical_latency_ms(); - let actual_latency = self.reflex_count as f32 * ComputeLane::Reflex.typical_latency_ms() + let actual_latency = self.reflex_count as f32 + * ComputeLane::Reflex.typical_latency_ms() + self.standard_count as f32 * ComputeLane::Standard.typical_latency_ms() + self.deep_count as f32 * ComputeLane::Deep.typical_latency_ms(); @@ -645,8 +642,8 @@ mod tests { #[test] fn test_routing_decision_builder() { - let decision = RoutingDecision::new(0, 0.1, ComputeLane::Standard) - .with_sparse_indices(vec![1, 3, 5]); + let decision = + RoutingDecision::new(0, 0.1, ComputeLane::Standard).with_sparse_indices(vec![1, 3, 5]); assert!(decision.sparse_indices.is_some()); assert_eq!(decision.sparse_indices.unwrap(), vec![1, 3, 5]); diff --git a/crates/ruvector-attention/src/sheaf/sparse.rs b/crates/ruvector-attention/src/sheaf/sparse.rs index 64f049f3e..ba47fd914 100644 --- a/crates/ruvector-attention/src/sheaf/sparse.rs +++ b/crates/ruvector-attention/src/sheaf/sparse.rs @@ -338,10 +338,10 @@ impl SparseResidualAttention { // Ensure minimum connections by adding highest-residual pairs if needed if query_connections.len() < self.config.min_connections { // Sort all pairs by residual (descending) and take top k - let mut all_pairs: Vec<(usize, f32)> = (0..n_k) - .map(|j| (j, residuals[i * n_k + j])) - .collect(); - all_pairs.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); + let mut all_pairs: Vec<(usize, f32)> = + (0..n_k).map(|j| (j, residuals[i * n_k + j])).collect(); + all_pairs + .sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); for (j, r) in all_pairs.into_iter().take(self.config.min_connections) { if !query_connections.iter().any(|(jj, _)| *jj == j) { @@ -354,9 +354,8 @@ impl SparseResidualAttention { let max_connections = ((1.0 - self.config.max_sparsity) * n_k as f32).ceil() as usize; if query_connections.len() > max_connections { // Sort by residual (descending) and keep top max_connections - query_connections.sort_by(|a, b| { - b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal) - }); + query_connections + .sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); query_connections.truncate(max_connections); } @@ -368,10 +367,8 @@ impl SparseResidualAttention { } // Sort connections by (i, j) for CSR conversion - let mut paired: Vec<((usize, usize), f32)> = connections - .into_iter() - .zip(connection_residuals) - .collect(); + let mut paired: Vec<((usize, usize), f32)> = + connections.into_iter().zip(connection_residuals).collect(); paired.sort_by_key(|((i, j), _)| (*i, *j)); let connections: Vec<(usize, usize)> = paired.iter().map(|(c, _)| *c).collect(); @@ -460,7 +457,11 @@ impl SparseResidualAttention { values: &[&[f32]], ) -> Vec> { let n_queries = row_ptr.len() - 1; - let dim = if values.is_empty() { 0 } else { values[0].len() }; + let dim = if values.is_empty() { + 0 + } else { + values[0].len() + }; let mut outputs = vec![vec![0.0; dim]; n_queries]; diff --git a/crates/ruvector-attention/src/topology/coherence.rs b/crates/ruvector-attention/src/topology/coherence.rs index d7d2ec108..67c64b2dc 100644 --- a/crates/ruvector-attention/src/topology/coherence.rs +++ b/crates/ruvector-attention/src/topology/coherence.rs @@ -37,11 +37,7 @@ pub struct WindowCoherence { impl WindowCoherence { /// Compute coherence from keys - pub fn compute( - keys: &[&[f32]], - k_neighbors: usize, - metrics: &[CoherenceMetric], - ) -> Self { + pub fn compute(keys: &[&[f32]], k_neighbors: usize, metrics: &[CoherenceMetric]) -> Self { let n = keys.len(); if n < 2 { return Self { @@ -107,7 +103,9 @@ impl WindowCoherence { .map(|(j, k2)| (j, Self::squared_distance(key, k2))) .collect(); - distances.sort_unstable_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal)); + distances.sort_unstable_by(|a, b| { + a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal) + }); distances.iter().take(k).map(|(j, _)| *j).collect() }) @@ -124,11 +122,7 @@ impl WindowCoherence { } /// Compute specific metric - fn compute_metric( - metric: CoherenceMetric, - keys: &[&[f32]], - knn_graph: &[Vec], - ) -> f32 { + fn compute_metric(metric: CoherenceMetric, keys: &[&[f32]], knn_graph: &[Vec]) -> f32 { match metric { CoherenceMetric::BoundaryMass => Self::boundary_mass(knn_graph), CoherenceMetric::CutProxy => Self::cut_proxy(knn_graph), @@ -218,7 +212,8 @@ impl WindowCoherence { .collect(); let mean: f32 = sims.iter().sum::() / sims.len() as f32; - let variance: f32 = sims.iter().map(|s| (s - mean) * (s - mean)).sum::() / sims.len() as f32; + let variance: f32 = + sims.iter().map(|s| (s - mean) * (s - mean)).sum::() / sims.len() as f32; total_variance += variance; count += 1; @@ -252,7 +247,11 @@ impl WindowCoherence { } let mean: f32 = all_sims.iter().sum::() / all_sims.len() as f32; - let variance: f32 = all_sims.iter().map(|s| (s - mean) * (s - mean)).sum::() / all_sims.len() as f32; + let variance: f32 = all_sims + .iter() + .map(|s| (s - mean) * (s - mean)) + .sum::() + / all_sims.len() as f32; // Low variance + high mean = high coherence let coherence = mean * (1.0 - variance.sqrt().min(1.0)); @@ -280,15 +279,16 @@ mod tests { #[test] fn test_coherence_computation() { - let keys: Vec> = (0..20) - .map(|i| vec![i as f32 * 0.1; 32]) - .collect(); + let keys: Vec> = (0..20).map(|i| vec![i as f32 * 0.1; 32]).collect(); let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); let coherence = WindowCoherence::compute( &keys_refs, 5, - &[CoherenceMetric::BoundaryMass, CoherenceMetric::SimilarityVariance], + &[ + CoherenceMetric::BoundaryMass, + CoherenceMetric::SimilarityVariance, + ], ); assert!(coherence.score >= 0.0 && coherence.score <= 1.0); @@ -298,16 +298,10 @@ mod tests { #[test] fn test_coherent_window() { // Highly similar keys = high coherence - let keys: Vec> = (0..10) - .map(|_| vec![0.5f32; 16]) - .collect(); + let keys: Vec> = (0..10).map(|_| vec![0.5f32; 16]).collect(); let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); - let coherence = WindowCoherence::compute( - &keys_refs, - 3, - &[CoherenceMetric::Disagreement], - ); + let coherence = WindowCoherence::compute(&keys_refs, 3, &[CoherenceMetric::Disagreement]); // Should be very coherent assert!(coherence.score > 0.8); @@ -318,7 +312,8 @@ mod tests { let keys: Vec> = vec![vec![1.0; 8]; 5]; let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); - let mut coherence = WindowCoherence::compute(&keys_refs, 2, &[CoherenceMetric::BoundaryMass]); + let mut coherence = + WindowCoherence::compute(&keys_refs, 2, &[CoherenceMetric::BoundaryMass]); assert!(!coherence.needs_update(4)); diff --git a/crates/ruvector-attention/src/topology/gated_attention.rs b/crates/ruvector-attention/src/topology/gated_attention.rs index b59dbe362..6457c75ac 100644 --- a/crates/ruvector-attention/src/topology/gated_attention.rs +++ b/crates/ruvector-attention/src/topology/gated_attention.rs @@ -2,10 +2,10 @@ //! //! Main attention mechanism that uses topological coherence as a permission signal. -use crate::error::{AttentionError, AttentionResult}; -use crate::traits::Attention; use super::coherence::{CoherenceMetric, WindowCoherence}; use super::policy::{AttentionMode, AttentionPolicy, PolicyConfig}; +use crate::error::{AttentionError, AttentionResult}; +use crate::traits::Attention; use serde::{Deserialize, Serialize}; /// Configuration for topology-gated attention @@ -76,11 +76,8 @@ impl TopologyGatedAttention { /// Update coherence from keys (call periodically, not every token) pub fn update_coherence(&mut self, keys: &[&[f32]]) { - let coherence = WindowCoherence::compute( - keys, - self.config.k_neighbors, - &self.config.metrics, - ); + let coherence = + WindowCoherence::compute(keys, self.config.k_neighbors, &self.config.metrics); self.policy.determine_mode(coherence.score); self.cached_coherence = Some(coherence); } @@ -207,10 +204,7 @@ impl TopologyGatedAttention { let weights = Self::stable_softmax(&logits); // Weighted sum of selected values - let selected_values: Vec<&[f32]> = top_k - .iter() - .map(|(i, _)| values[*i]) - .collect(); + let selected_values: Vec<&[f32]> = top_k.iter().map(|(i, _)| values[*i]).collect(); self.weighted_sum(&weights, &selected_values) } @@ -352,17 +346,15 @@ mod tests { let mut attention = TopologyGatedAttention::with_dim(32); let query = vec![0.5f32; 32]; - let keys: Vec> = (0..20) - .map(|i| vec![0.1 + i as f32 * 0.02; 32]) - .collect(); - let values: Vec> = (0..20) - .map(|i| vec![i as f32; 32]) - .collect(); + let keys: Vec> = (0..20).map(|i| vec![0.1 + i as f32 * 0.02; 32]).collect(); + let values: Vec> = (0..20).map(|i| vec![i as f32; 32]).collect(); let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); let values_refs: Vec<&[f32]> = values.iter().map(|v| v.as_slice()).collect(); - let output = attention.compute_gated(&query, &keys_refs, &values_refs).unwrap(); + let output = attention + .compute_gated(&query, &keys_refs, &values_refs) + .unwrap(); assert_eq!(output.len(), 32); } @@ -388,9 +380,7 @@ mod tests { v }) .collect(); - let values: Vec> = (0..10) - .map(|i| vec![i as f32; 16]) - .collect(); + let values: Vec> = (0..10).map(|i| vec![i as f32; 16]).collect(); let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); let values_refs: Vec<&[f32]> = values.iter().map(|v| v.as_slice()).collect(); @@ -399,7 +389,9 @@ mod tests { // With diverse keys, should trigger freeze mode let query = vec![0.5f32; 16]; - let _output = attention.compute_gated(&query, &keys_refs, &values_refs).unwrap(); + let _output = attention + .compute_gated(&query, &keys_refs, &values_refs) + .unwrap(); // Mode should be freeze or cautious due to low coherence let mode = attention.current_mode(); diff --git a/crates/ruvector-attention/src/transport/cached_projections.rs b/crates/ruvector-attention/src/transport/cached_projections.rs index 958fdf325..186626568 100644 --- a/crates/ruvector-attention/src/transport/cached_projections.rs +++ b/crates/ruvector-attention/src/transport/cached_projections.rs @@ -116,10 +116,7 @@ impl WindowCache { let num_proj = proj_cache.num_projections; // Project all keys - let key_projections: Vec> = keys - .iter() - .map(|k| proj_cache.project(k)) - .collect(); + let key_projections: Vec> = keys.iter().map(|k| proj_cache.project(k)).collect(); // Sort indices and values for each projection let mut sorted_indices = vec![Vec::with_capacity(num_keys); num_proj]; @@ -131,7 +128,9 @@ impl WindowCache { .enumerate() .map(|(i, projs)| (i, projs[p])) .collect(); - indexed.sort_unstable_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal)); + indexed.sort_unstable_by(|a, b| { + a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal) + }); sorted_indices[p] = indexed.iter().map(|(i, _)| *i).collect(); sorted_values[p] = indexed.iter().map(|(_, v)| *v).collect(); @@ -212,9 +211,7 @@ mod tests { fn test_window_cache() { let proj_cache = ProjectionCache::new(32, 4, 42); - let keys: Vec> = (0..10) - .map(|i| vec![i as f32 * 0.1; 32]) - .collect(); + let keys: Vec> = (0..10).map(|i| vec![i as f32 * 0.1; 32]).collect(); let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); let window_cache = WindowCache::build(&keys_refs, &proj_cache); @@ -227,9 +224,7 @@ mod tests { fn test_histograms() { let proj_cache = ProjectionCache::new(16, 2, 42); - let keys: Vec> = (0..20) - .map(|i| vec![i as f32 * 0.05; 16]) - .collect(); + let keys: Vec> = (0..20).map(|i| vec![i as f32 * 0.05; 16]).collect(); let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); let mut window_cache = WindowCache::build(&keys_refs, &proj_cache); diff --git a/crates/ruvector-attention/src/transport/centroid_ot.rs b/crates/ruvector-attention/src/transport/centroid_ot.rs index 0e2fcf8a4..77081f242 100644 --- a/crates/ruvector-attention/src/transport/centroid_ot.rs +++ b/crates/ruvector-attention/src/transport/centroid_ot.rs @@ -83,11 +83,8 @@ impl CentroidCache { let mut indices: Vec = (0..num_keys).collect(); indices.shuffle(&mut rng); - let mut centroids: Vec> = indices - .iter() - .take(m) - .map(|&i| keys[i].to_vec()) - .collect(); + let mut centroids: Vec> = + indices.iter().take(m).map(|&i| keys[i].to_vec()).collect(); let mut assignments = vec![0usize; num_keys]; @@ -135,10 +132,7 @@ impl CentroidCache { for &a in &assignments { counts[a] += 1; } - let weights: Vec = counts - .iter() - .map(|&c| c as f32 / num_keys as f32) - .collect(); + let weights: Vec = counts.iter().map(|&c| c as f32 / num_keys as f32).collect(); Self { centroids, @@ -245,7 +239,11 @@ impl CentroidOTAttention { let mut key_weights = vec![0.0f32; cache.num_keys]; for (key_idx, &assignment) in cache.assignments.iter().enumerate() { // Key weight = centroid weight / number of keys in cluster - let cluster_size = cache.assignments.iter().filter(|&&a| a == assignment).count(); + let cluster_size = cache + .assignments + .iter() + .filter(|&&a| a == assignment) + .count(); if cluster_size > 0 { key_weights[key_idx] = centroid_weights[assignment] / cluster_size as f32; } @@ -391,9 +389,7 @@ mod tests { #[test] fn test_centroid_cache() { - let keys: Vec> = (0..50) - .map(|i| vec![i as f32 * 0.1; 32]) - .collect(); + let keys: Vec> = (0..50).map(|i| vec![i as f32 * 0.1; 32]).collect(); let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); let cache = CentroidCache::build(&keys_refs, 8, 5, 42); @@ -412,12 +408,8 @@ mod tests { let attention = CentroidOTAttention::with_dim(32); let query = vec![0.5f32; 32]; - let keys: Vec> = (0..30) - .map(|i| vec![i as f32 * 0.05; 32]) - .collect(); - let values: Vec> = (0..30) - .map(|i| vec![i as f32; 32]) - .collect(); + let keys: Vec> = (0..30).map(|i| vec![i as f32 * 0.05; 32]).collect(); + let values: Vec> = (0..30).map(|i| vec![i as f32; 32]).collect(); let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); let values_refs: Vec<&[f32]> = values.iter().map(|v| v.as_slice()).collect(); @@ -430,12 +422,8 @@ mod tests { fn test_cache_reuse() { let attention = CentroidOTAttention::with_dim(64); - let keys: Vec> = (0..40) - .map(|i| vec![i as f32 * 0.025; 64]) - .collect(); - let values: Vec> = (0..40) - .map(|i| vec![i as f32; 64]) - .collect(); + let keys: Vec> = (0..40).map(|i| vec![i as f32 * 0.025; 64]).collect(); + let values: Vec> = (0..40).map(|i| vec![i as f32; 64]).collect(); let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); let values_refs: Vec<&[f32]> = values.iter().map(|v| v.as_slice()).collect(); @@ -446,7 +434,9 @@ mod tests { // Reuse for multiple queries for q in 0..10 { let query = vec![q as f32 * 0.1; 64]; - let output = attention.compute_with_cache(&query, &cache, &values_refs).unwrap(); + let output = attention + .compute_with_cache(&query, &cache, &values_refs) + .unwrap(); assert_eq!(output.len(), 64); } } diff --git a/crates/ruvector-attention/src/transport/mod.rs b/crates/ruvector-attention/src/transport/mod.rs index 3d84d1d56..2900e621c 100644 --- a/crates/ruvector-attention/src/transport/mod.rs +++ b/crates/ruvector-attention/src/transport/mod.rs @@ -15,13 +15,13 @@ //! - Projections P: 8-16 //! - Centroids M: 16-32 -mod sliced_wasserstein; -mod centroid_ot; mod cached_projections; +mod centroid_ot; +mod sliced_wasserstein; -pub use sliced_wasserstein::{SlicedWassersteinAttention, SlicedWassersteinConfig}; -pub use centroid_ot::{CentroidCache, CentroidOTAttention, CentroidOTConfig}; pub use cached_projections::{ProjectionCache, WindowCache}; +pub use centroid_ot::{CentroidCache, CentroidOTAttention, CentroidOTConfig}; +pub use sliced_wasserstein::{SlicedWassersteinAttention, SlicedWassersteinConfig}; #[cfg(test)] mod tests { diff --git a/crates/ruvector-attention/src/transport/sliced_wasserstein.rs b/crates/ruvector-attention/src/transport/sliced_wasserstein.rs index 3ee3802e0..bd4594c01 100644 --- a/crates/ruvector-attention/src/transport/sliced_wasserstein.rs +++ b/crates/ruvector-attention/src/transport/sliced_wasserstein.rs @@ -17,9 +17,9 @@ //! - Histogram CDF for ultra-fast comparisons //! - SIMD-friendly kernels throughout +use super::cached_projections::{ProjectionCache, WindowCache}; use crate::error::{AttentionError, AttentionResult}; use crate::traits::Attention; -use super::cached_projections::{ProjectionCache, WindowCache}; use serde::{Deserialize, Serialize}; /// Configuration for Sliced Wasserstein Attention @@ -81,11 +81,8 @@ pub struct SlicedWassersteinAttention { impl SlicedWassersteinAttention { /// Create new Sliced Wasserstein attention pub fn new(config: SlicedWassersteinConfig) -> Self { - let projection_cache = ProjectionCache::new( - config.dim, - config.num_projections, - config.seed, - ); + let projection_cache = + ProjectionCache::new(config.dim, config.num_projections, config.seed); Self { config, @@ -159,7 +156,8 @@ impl SlicedWassersteinAttention { .enumerate() .map(|(i, k)| (i, Self::dot_product_simd(query, k))) .collect(); - dot_scores.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); + dot_scores + .sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); let candidate_indices: Vec = dot_scores .iter() @@ -188,10 +186,7 @@ impl SlicedWassersteinAttention { let weights = Self::stable_softmax(&logits); // Weighted sum using only candidate values - let candidate_values: Vec<&[f32]> = candidate_indices - .iter() - .map(|&i| values[i]) - .collect(); + let candidate_values: Vec<&[f32]> = candidate_indices.iter().map(|&i| values[i]).collect(); self.weighted_sum(&weights, &candidate_values) } @@ -393,12 +388,8 @@ mod tests { let attention = SlicedWassersteinAttention::with_dim(32); let query = vec![1.0f32; 32]; - let keys: Vec> = (0..10) - .map(|i| vec![0.5 + i as f32 * 0.1; 32]) - .collect(); - let values: Vec> = (0..10) - .map(|i| vec![i as f32; 32]) - .collect(); + let keys: Vec> = (0..10).map(|i| vec![0.5 + i as f32 * 0.1; 32]).collect(); + let values: Vec> = (0..10).map(|i| vec![i as f32; 32]).collect(); let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); let values_refs: Vec<&[f32]> = values.iter().map(|v| v.as_slice()).collect(); @@ -411,12 +402,8 @@ mod tests { fn test_window_cache_reuse() { let attention = SlicedWassersteinAttention::with_dim(64); - let keys: Vec> = (0..20) - .map(|i| vec![i as f32 * 0.05; 64]) - .collect(); - let values: Vec> = (0..20) - .map(|i| vec![i as f32; 64]) - .collect(); + let keys: Vec> = (0..20).map(|i| vec![i as f32 * 0.05; 64]).collect(); + let values: Vec> = (0..20).map(|i| vec![i as f32; 64]).collect(); let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); let values_refs: Vec<&[f32]> = values.iter().map(|v| v.as_slice()).collect(); @@ -427,7 +414,9 @@ mod tests { // Reuse for multiple queries for _ in 0..5 { let query = vec![0.5f32; 64]; - let output = attention.compute_with_cache(&query, &cache, &values_refs).unwrap(); + let output = attention + .compute_with_cache(&query, &cache, &values_refs) + .unwrap(); assert_eq!(output.len(), 64); } } @@ -442,12 +431,8 @@ mod tests { let attention = SlicedWassersteinAttention::new(config); let query = vec![1.0f32; 32]; - let keys: Vec> = (0..50) - .map(|i| vec![0.5 + i as f32 * 0.02; 32]) - .collect(); - let values: Vec> = (0..50) - .map(|i| vec![i as f32; 32]) - .collect(); + let keys: Vec> = (0..50).map(|i| vec![0.5 + i as f32 * 0.02; 32]).collect(); + let values: Vec> = (0..50).map(|i| vec![i as f32; 32]).collect(); let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); let values_refs: Vec<&[f32]> = values.iter().map(|v| v.as_slice()).collect(); diff --git a/crates/ruvector-attention/src/unified_report/metrics.rs b/crates/ruvector-attention/src/unified_report/metrics.rs index 2e12eaae3..8b90c5056 100644 --- a/crates/ruvector-attention/src/unified_report/metrics.rs +++ b/crates/ruvector-attention/src/unified_report/metrics.rs @@ -97,14 +97,7 @@ mod tests { #[test] fn test_metric_value() { - let metric = MetricValue::new( - MetricType::TopologyCoherence, - 0.7, - 0.0, - 1.0, - 0.3, - 0.1, - ); + let metric = MetricValue::new(MetricType::TopologyCoherence, 0.7, 0.0, 1.0, 0.3, 0.1); assert_eq!(metric.metric_type, MetricType::TopologyCoherence); assert!((metric.normalized - 0.7).abs() < 1e-5); @@ -115,11 +108,11 @@ mod tests { fn test_warning_critical() { let metric = MetricValue::new( MetricType::OTDistance, - 5.0, // High OT distance + 5.0, // High OT distance 0.0, 10.0, - 3.0, // Warning at 3 - 7.0, // Critical at 7 + 3.0, // Warning at 3 + 7.0, // Critical at 7 ); assert!(metric.is_warning()); diff --git a/crates/ruvector-attention/src/unified_report/mod.rs b/crates/ruvector-attention/src/unified_report/mod.rs index 1804eb994..26b9ee646 100644 --- a/crates/ruvector-attention/src/unified_report/mod.rs +++ b/crates/ruvector-attention/src/unified_report/mod.rs @@ -17,11 +17,11 @@ //! - Monitoring attention health //! - Debugging attention patterns -mod report; mod metrics; +mod report; -pub use report::{AttentionRecommendation, GeometryReport, ReportBuilder, ReportConfig}; pub use metrics::{MetricType, MetricValue}; +pub use report::{AttentionRecommendation, GeometryReport, ReportBuilder, ReportConfig}; #[cfg(test)] mod tests { diff --git a/crates/ruvector-attention/src/unified_report/report.rs b/crates/ruvector-attention/src/unified_report/report.rs index 605620d0a..aeb19ae2c 100644 --- a/crates/ruvector-attention/src/unified_report/report.rs +++ b/crates/ruvector-attention/src/unified_report/report.rs @@ -1,9 +1,9 @@ //! Unified Geometry Report Builder use super::metrics::{MetricType, MetricValue}; -use crate::topology::WindowCoherence; use crate::info_bottleneck::KLDivergence; use crate::pde_attention::GraphLaplacian; +use crate::topology::WindowCoherence; use serde::{Deserialize, Serialize}; /// Report configuration @@ -133,12 +133,33 @@ impl ReportBuilder { MetricValue::new(MetricType::OTDistance, ot_mean, 0.0, 10.0, 5.0, 8.0), MetricValue::new(MetricType::TopologyCoherence, coherence, 0.0, 1.0, 0.3, 0.1), MetricValue::new(MetricType::IBKL, ib_kl, 0.0, 100.0, 50.0, 80.0), - MetricValue::new(MetricType::DiffusionEnergy, diffusion_energy, 0.0, 100.0, 50.0, 80.0), - MetricValue::new(MetricType::AttentionEntropy, entropy, 0.0, (n as f32).ln().max(1.0), 0.5, 0.2), + MetricValue::new( + MetricType::DiffusionEnergy, + diffusion_energy, + 0.0, + 100.0, + 50.0, + 80.0, + ), + MetricValue::new( + MetricType::AttentionEntropy, + entropy, + 0.0, + (n as f32).ln().max(1.0), + 0.5, + 0.2, + ), ]; if let Some(h0) = h0_sum { - metrics.push(MetricValue::new(MetricType::H0Persistence, h0, 0.0, 100.0, 50.0, 80.0)); + metrics.push(MetricValue::new( + MetricType::H0Persistence, + h0, + 0.0, + 100.0, + 50.0, + 80.0, + )); } // Compute health score @@ -175,9 +196,7 @@ impl ReportBuilder { .collect(); // Project query - let q_projs: Vec = projections.iter() - .map(|p| Self::dot(query, p)) - .collect(); + let q_projs: Vec = projections.iter().map(|p| Self::dot(query, p)).collect(); // Mean absolute distance over keys let mut total = 0.0f32; @@ -200,7 +219,10 @@ impl ReportBuilder { let coherence = WindowCoherence::compute( keys, self.config.knn_k, - &[CoherenceMetric::BoundaryMass, CoherenceMetric::SimilarityVariance], + &[ + CoherenceMetric::BoundaryMass, + CoherenceMetric::SimilarityVariance, + ], ); coherence.score @@ -279,12 +301,14 @@ impl ReportBuilder { } // Initial logits - let x: Vec = keys.iter() - .map(|k| Self::dot(query, k)) - .collect(); + let x: Vec = keys.iter().map(|k| Self::dot(query, k)).collect(); // Build Laplacian - let lap = GraphLaplacian::from_keys(keys, self.config.diffusion_sigma, LaplacianType::Unnormalized); + let lap = GraphLaplacian::from_keys( + keys, + self.config.diffusion_sigma, + LaplacianType::Unnormalized, + ); // Energy = x^T L x let lx = lap.apply(&x); @@ -436,9 +460,7 @@ mod tests { let builder = ReportBuilder::new(ReportConfig::default()); let query = vec![1.0f32; 16]; - let keys: Vec> = (0..10) - .map(|i| vec![i as f32 * 0.1; 16]) - .collect(); + let keys: Vec> = (0..10).map(|i| vec![i as f32 * 0.1; 16]).collect(); let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); let report = builder.build(&query, &keys_refs, None, None, None); @@ -461,11 +483,7 @@ mod tests { let builder = ReportBuilder::new(ReportConfig::default()); let query = vec![1.0f32; 8]; - let keys: Vec> = vec![ - vec![1.0; 8], - vec![0.9; 8], - vec![0.1; 8], - ]; + let keys: Vec> = vec![vec![1.0; 8], vec![0.9; 8], vec![0.1; 8]]; let keys_refs: Vec<&[f32]> = keys.iter().map(|k| k.as_slice()).collect(); let weights = vec![0.6, 0.3, 0.1]; diff --git a/crates/ruvector-core/benches/bench_memory.rs b/crates/ruvector-core/benches/bench_memory.rs index 4def45e61..8a7f85f5a 100644 --- a/crates/ruvector-core/benches/bench_memory.rs +++ b/crates/ruvector-core/benches/bench_memory.rs @@ -194,17 +194,13 @@ fn bench_soa_storage_get(c: &mut Criterion) { let mut output = vec![0.0_f32; dim]; - group.bench_with_input( - BenchmarkId::new("sequential", dim), - &dim, - |bench, _| { - bench.iter(|| { - for i in 0..10000 { - storage.get(black_box(i), &mut output); - } - }); - }, - ); + group.bench_with_input(BenchmarkId::new("sequential", dim), &dim, |bench, _| { + bench.iter(|| { + for i in 0..10000 { + storage.get(black_box(i), &mut output); + } + }); + }); group.bench_with_input(BenchmarkId::new("random", dim), &dim, |bench, _| { let indices: Vec = (0..10000).map(|i| (i * 37 + 13) % 10000).collect(); @@ -263,11 +259,19 @@ fn bench_soa_dimension_slice(c: &mut Criterion) { fn bench_soa_batch_distances(c: &mut Criterion) { let mut group = c.benchmark_group("soa_batch_distances"); - for (dim, count) in [(128, 1000), (384, 1000), (768, 1000), (128, 10000), (384, 5000)] { + for (dim, count) in [ + (128, 1000), + (384, 1000), + (768, 1000), + (128, 10000), + (384, 5000), + ] { let mut storage = SoAVectorStorage::new(dim, 128); for i in 0..count { - let vector: Vec = (0..dim).map(|j| ((i * dim + j) % 1000) as f32 * 0.001).collect(); + let vector: Vec = (0..dim) + .map(|j| ((i * dim + j) % 1000) as f32 * 0.001) + .collect(); storage.push(&vector); } @@ -303,7 +307,9 @@ fn bench_memory_layout_comparison(c: &mut Criterion) { // SoA layout let mut soa_storage = SoAVectorStorage::new(dim, 128); for i in 0..count { - let vector: Vec = (0..dim).map(|j| ((i * dim + j) % 1000) as f32 * 0.001).collect(); + let vector: Vec = (0..dim) + .map(|j| ((i * dim + j) % 1000) as f32 * 0.001) + .collect(); soa_storage.push(&vector); } @@ -355,7 +361,9 @@ fn bench_cache_efficiency(c: &mut Criterion) { let mut storage = SoAVectorStorage::new(dim, 128); for i in 0..count { - let vector: Vec = (0..dim).map(|j| ((i * dim + j) % 1000) as f32 * 0.001).collect(); + let vector: Vec = (0..dim) + .map(|j| ((i * dim + j) % 1000) as f32 * 0.001) + .collect(); storage.push(&vector); } diff --git a/crates/ruvector-core/benches/bench_simd.rs b/crates/ruvector-core/benches/bench_simd.rs index 0d25af030..59b44bf8e 100644 --- a/crates/ruvector-core/benches/bench_simd.rs +++ b/crates/ruvector-core/benches/bench_simd.rs @@ -287,18 +287,14 @@ fn bench_throughput_ops_per_second(c: &mut Criterion) { let (a, b) = generate_vectors(dim); // Report throughput in operations/second - group.bench_with_input( - BenchmarkId::new("euclidean_ops", dim), - &dim, - |bench, _| { - bench.iter(|| { - // Perform 100 operations per iteration - for _ in 0..100 { - euclidean_distance_simd(black_box(&a), black_box(&b)); - } - }); - }, - ); + group.bench_with_input(BenchmarkId::new("euclidean_ops", dim), &dim, |bench, _| { + bench.iter(|| { + // Perform 100 operations per iteration + for _ in 0..100 { + euclidean_distance_simd(black_box(&a), black_box(&b)); + } + }); + }); group.bench_with_input( BenchmarkId::new("dot_product_ops", dim), diff --git a/crates/ruvector-core/examples/neon_benchmark.rs b/crates/ruvector-core/examples/neon_benchmark.rs index dc873616b..27f7d2358 100644 --- a/crates/ruvector-core/examples/neon_benchmark.rs +++ b/crates/ruvector-core/examples/neon_benchmark.rs @@ -16,18 +16,29 @@ fn main() { // Generate test data let vectors: Vec> = (0..num_vectors) - .map(|i| (0..dimensions).map(|j| ((i * j) % 1000) as f32 / 1000.0).collect()) + .map(|i| { + (0..dimensions) + .map(|j| ((i * j) % 1000) as f32 / 1000.0) + .collect() + }) .collect(); let queries: Vec> = (0..num_queries) - .map(|i| (0..dimensions).map(|j| ((i * j + 500) % 1000) as f32 / 1000.0).collect()) + .map(|i| { + (0..dimensions) + .map(|j| ((i * j + 500) % 1000) as f32 / 1000.0) + .collect() + }) .collect(); println!("Configuration:"); println!(" - Dimensions: {}", dimensions); println!(" - Vectors: {}", num_vectors); println!(" - Queries: {}", num_queries); - println!(" - Total distance calculations: {}\n", num_vectors * num_queries); + println!( + " - Total distance calculations: {}\n", + num_vectors * num_queries + ); #[cfg(target_arch = "aarch64")] println!("Platform: ARM64 (Apple Silicon) - NEON enabled ✓\n"); @@ -48,7 +59,11 @@ fn main() { } } let simd_time = start.elapsed(); - println!(" SIMD: {:>8.2} ms (checksum: {:.4})", simd_time.as_secs_f64() * 1000.0, simd_sum); + println!( + " SIMD: {:>8.2} ms (checksum: {:.4})", + simd_time.as_secs_f64() * 1000.0, + simd_sum + ); let start = Instant::now(); let mut scalar_sum = 0.0f32; @@ -58,7 +73,11 @@ fn main() { } } let scalar_time = start.elapsed(); - println!(" Scalar: {:>8.2} ms (checksum: {:.4})", scalar_time.as_secs_f64() * 1000.0, scalar_sum); + println!( + " Scalar: {:>8.2} ms (checksum: {:.4})", + scalar_time.as_secs_f64() * 1000.0, + scalar_sum + ); let speedup = scalar_time.as_secs_f64() / simd_time.as_secs_f64(); println!(" Speedup: {:.2}x\n", speedup); @@ -76,7 +95,11 @@ fn main() { } } let simd_time = start.elapsed(); - println!(" SIMD: {:>8.2} ms (checksum: {:.4})", simd_time.as_secs_f64() * 1000.0, simd_sum); + println!( + " SIMD: {:>8.2} ms (checksum: {:.4})", + simd_time.as_secs_f64() * 1000.0, + simd_sum + ); let start = Instant::now(); let mut scalar_sum = 0.0f32; @@ -86,7 +109,11 @@ fn main() { } } let scalar_time = start.elapsed(); - println!(" Scalar: {:>8.2} ms (checksum: {:.4})", scalar_time.as_secs_f64() * 1000.0, scalar_sum); + println!( + " Scalar: {:>8.2} ms (checksum: {:.4})", + scalar_time.as_secs_f64() * 1000.0, + scalar_sum + ); let speedup = scalar_time.as_secs_f64() / simd_time.as_secs_f64(); println!(" Speedup: {:.2}x\n", speedup); @@ -104,7 +131,11 @@ fn main() { } } let simd_time = start.elapsed(); - println!(" SIMD: {:>8.2} ms (checksum: {:.4})", simd_time.as_secs_f64() * 1000.0, simd_sum); + println!( + " SIMD: {:>8.2} ms (checksum: {:.4})", + simd_time.as_secs_f64() * 1000.0, + simd_sum + ); let start = Instant::now(); let mut scalar_sum = 0.0f32; @@ -114,7 +145,11 @@ fn main() { } } let scalar_time = start.elapsed(); - println!(" Scalar: {:>8.2} ms (checksum: {:.4})", scalar_time.as_secs_f64() * 1000.0, scalar_sum); + println!( + " Scalar: {:>8.2} ms (checksum: {:.4})", + scalar_time.as_secs_f64() * 1000.0, + scalar_sum + ); let speedup = scalar_time.as_secs_f64() / simd_time.as_secs_f64(); println!(" Speedup: {:.2}x\n", speedup); diff --git a/crates/ruvector-core/src/agenticdb.rs b/crates/ruvector-core/src/agenticdb.rs index 6d7ea61e2..6ad1b761b 100644 --- a/crates/ruvector-core/src/agenticdb.rs +++ b/crates/ruvector-core/src/agenticdb.rs @@ -889,11 +889,26 @@ impl<'a> PolicyMemoryStore<'a> { let mut entries = Vec::new(); for result in results { if let Some(metadata) = result.metadata { - let policy_id = metadata.get("policy_id").and_then(|v| v.as_str()).unwrap_or(""); - let state_id = metadata.get("state_id").and_then(|v| v.as_str()).unwrap_or(""); - let action = metadata.get("action").and_then(|v| v.as_str()).unwrap_or(""); - let reward = metadata.get("reward").and_then(|v| v.as_f64()).unwrap_or(0.0); - let q_value = metadata.get("q_value").and_then(|v| v.as_f64()).unwrap_or(0.0); + let policy_id = metadata + .get("policy_id") + .and_then(|v| v.as_str()) + .unwrap_or(""); + let state_id = metadata + .get("state_id") + .and_then(|v| v.as_str()) + .unwrap_or(""); + let action = metadata + .get("action") + .and_then(|v| v.as_str()) + .unwrap_or(""); + let reward = metadata + .get("reward") + .and_then(|v| v.as_f64()) + .unwrap_or(0.0); + let q_value = metadata + .get("q_value") + .and_then(|v| v.as_f64()) + .unwrap_or(0.0); entries.push(PolicyEntry { id: policy_id.to_string(), @@ -990,7 +1005,10 @@ impl<'a> SessionStateIndex<'a> { metadata: Some({ let mut meta = HashMap::new(); meta.insert("type".to_string(), serde_json::json!("session_turn")); - meta.insert("session_id".to_string(), serde_json::json!(self.session_id.clone())); + meta.insert( + "session_id".to_string(), + serde_json::json!(self.session_id.clone()), + ); meta.insert("turn_id".to_string(), serde_json::json!(id.clone())); meta.insert("turn_number".to_string(), serde_json::json!(turn_number)); meta.insert("role".to_string(), serde_json::json!(role)); @@ -1015,7 +1033,10 @@ impl<'a> SessionStateIndex<'a> { filter: Some({ let mut filter = HashMap::new(); filter.insert("type".to_string(), serde_json::json!("session_turn")); - filter.insert("session_id".to_string(), serde_json::json!(self.session_id.clone())); + filter.insert( + "session_id".to_string(), + serde_json::json!(self.session_id.clone()), + ); filter }), ef_search: None, @@ -1024,7 +1045,10 @@ impl<'a> SessionStateIndex<'a> { let mut turns = Vec::new(); for result in results { if let Some(metadata) = result.metadata { - let expires_at = metadata.get("expires_at").and_then(|v| v.as_i64()).unwrap_or(0); + let expires_at = metadata + .get("expires_at") + .and_then(|v| v.as_i64()) + .unwrap_or(0); // Skip expired turns if expires_at < current_time { @@ -1032,13 +1056,31 @@ impl<'a> SessionStateIndex<'a> { } turns.push(SessionTurn { - id: metadata.get("turn_id").and_then(|v| v.as_str()).unwrap_or("").to_string(), + id: metadata + .get("turn_id") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(), session_id: self.session_id.clone(), - turn_number: metadata.get("turn_number").and_then(|v| v.as_u64()).unwrap_or(0) as usize, - role: metadata.get("role").and_then(|v| v.as_str()).unwrap_or("").to_string(), - content: metadata.get("content").and_then(|v| v.as_str()).unwrap_or("").to_string(), + turn_number: metadata + .get("turn_number") + .and_then(|v| v.as_u64()) + .unwrap_or(0) as usize, + role: metadata + .get("role") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(), + content: metadata + .get("content") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(), embedding: result.vector.unwrap_or_default(), - timestamp: metadata.get("timestamp").and_then(|v| v.as_i64()).unwrap_or(0), + timestamp: metadata + .get("timestamp") + .and_then(|v| v.as_i64()) + .unwrap_or(0), expires_at, }); @@ -1066,7 +1108,10 @@ impl<'a> SessionStateIndex<'a> { for turn in all_turns { if turn.expires_at < current_time { - let _ = self.db.vector_db.delete(&format!("session_{}_{}", self.session_id, turn.id)); + let _ = self + .db + .vector_db + .delete(&format!("session_{}_{}", self.session_id, turn.id)); deleted += 1; } } @@ -1116,7 +1161,13 @@ impl<'a> WitnessLog<'a> { } /// Compute SHA256 hash of entry data - fn compute_hash(prev_hash: &Option, agent_id: &str, action_type: &str, details: &str, timestamp: i64) -> String { + fn compute_hash( + prev_hash: &Option, + agent_id: &str, + action_type: &str, + details: &str, + timestamp: i64, + ) -> String { use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; @@ -1132,12 +1183,7 @@ impl<'a> WitnessLog<'a> { } /// Append an entry to the witness log (immutable, hash-linked) - pub fn append( - &self, - agent_id: &str, - action_type: &str, - details: &str, - ) -> Result { + pub fn append(&self, agent_id: &str, action_type: &str, details: &str) -> Result { let id = uuid::Uuid::new_v4().to_string(); let timestamp = chrono::Utc::now().timestamp(); @@ -1148,7 +1194,9 @@ impl<'a> WitnessLog<'a> { let hash = Self::compute_hash(&prev_hash, agent_id, action_type, details, timestamp); // Generate embedding for semantic search - let embedding = self.db.generate_text_embedding(&format!("{} {} {}", agent_id, action_type, details))?; + let embedding = self + .db + .generate_text_embedding(&format!("{} {} {}", agent_id, action_type, details))?; // Store in vector DB (append-only) self.db.vector_db.insert(VectorEntry { @@ -1195,14 +1243,40 @@ impl<'a> WitnessLog<'a> { for result in results { if let Some(metadata) = result.metadata { entries.push(WitnessEntry { - id: metadata.get("witness_id").and_then(|v| v.as_str()).unwrap_or("").to_string(), - prev_hash: metadata.get("prev_hash").and_then(|v| v.as_str()).map(|s| s.to_string()), - hash: metadata.get("hash").and_then(|v| v.as_str()).unwrap_or("").to_string(), - agent_id: metadata.get("agent_id").and_then(|v| v.as_str()).unwrap_or("").to_string(), - action_type: metadata.get("action_type").and_then(|v| v.as_str()).unwrap_or("").to_string(), - details: metadata.get("details").and_then(|v| v.as_str()).unwrap_or("").to_string(), + id: metadata + .get("witness_id") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(), + prev_hash: metadata + .get("prev_hash") + .and_then(|v| v.as_str()) + .map(|s| s.to_string()), + hash: metadata + .get("hash") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(), + agent_id: metadata + .get("agent_id") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(), + action_type: metadata + .get("action_type") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(), + details: metadata + .get("details") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(), embedding: result.vector.unwrap_or_default(), - timestamp: metadata.get("timestamp").and_then(|v| v.as_i64()).unwrap_or(0), + timestamp: metadata + .get("timestamp") + .and_then(|v| v.as_i64()) + .unwrap_or(0), metadata: None, }); } diff --git a/crates/ruvector-core/src/arena.rs b/crates/ruvector-core/src/arena.rs index 13d9ec862..7e837d13c 100644 --- a/crates/ruvector-core/src/arena.rs +++ b/crates/ruvector-core/src/arena.rs @@ -260,8 +260,7 @@ impl CacheAlignedVec { /// Panics if memory allocation fails. For fallible allocation, /// use `try_with_capacity`. pub fn with_capacity(capacity: usize) -> Self { - Self::try_with_capacity(capacity) - .expect("Failed to allocate cache-aligned memory") + Self::try_with_capacity(capacity).expect("Failed to allocate cache-aligned memory") } /// Try to create a new cache-aligned vector with the given capacity @@ -278,11 +277,8 @@ impl CacheAlignedVec { } // Allocate cache-line aligned memory - let layout = Layout::from_size_align( - capacity * std::mem::size_of::(), - CACHE_LINE_SIZE, - ) - .ok()?; + let layout = + Layout::from_size_align(capacity * std::mem::size_of::(), CACHE_LINE_SIZE).ok()?; let data = unsafe { alloc(layout) as *mut f32 }; @@ -305,8 +301,7 @@ impl CacheAlignedVec { /// Panics if memory allocation fails. For fallible allocation, /// use `try_from_slice`. pub fn from_slice(slice: &[f32]) -> Self { - Self::try_from_slice(slice) - .expect("Failed to allocate cache-aligned memory for slice") + Self::try_from_slice(slice).expect("Failed to allocate cache-aligned memory for slice") } /// Try to create from an existing slice, copying data to cache-aligned storage @@ -329,8 +324,14 @@ impl CacheAlignedVec { /// /// Panics if capacity is exceeded or if the vector has zero capacity. pub fn push(&mut self, value: f32) { - assert!(self.len < self.capacity, "CacheAlignedVec capacity exceeded"); - assert!(!self.data.is_null(), "Cannot push to zero-capacity CacheAlignedVec"); + assert!( + self.len < self.capacity, + "CacheAlignedVec capacity exceeded" + ); + assert!( + !self.data.is_null(), + "Cannot push to zero-capacity CacheAlignedVec" + ); unsafe { *self.data.add(self.len) = value; } @@ -480,11 +481,9 @@ impl BatchVectorAllocator { let total_floats = dimensions * initial_capacity; - let layout = Layout::from_size_align( - total_floats * std::mem::size_of::(), - CACHE_LINE_SIZE, - ) - .ok()?; + let layout = + Layout::from_size_align(total_floats * std::mem::size_of::(), CACHE_LINE_SIZE) + .ok()?; let data = unsafe { alloc(layout) as *mut f32 }; @@ -507,13 +506,12 @@ impl BatchVectorAllocator { /// /// Panics if the allocator is full, dimensions mismatch, or allocator has zero capacity. pub fn add(&mut self, vector: &[f32]) -> usize { - assert_eq!( - vector.len(), - self.dimensions, - "Vector dimension mismatch" - ); + assert_eq!(vector.len(), self.dimensions, "Vector dimension mismatch"); assert!(self.count < self.capacity, "Batch allocator full"); - assert!(!self.data.is_null(), "Cannot add to zero-capacity BatchVectorAllocator"); + assert!( + !self.data.is_null(), + "Cannot add to zero-capacity BatchVectorAllocator" + ); let offset = self.count * self.dimensions; unsafe { diff --git a/crates/ruvector-core/src/lib.rs b/crates/ruvector-core/src/lib.rs index f9861f165..b42c90161 100644 --- a/crates/ruvector-core/src/lib.rs +++ b/crates/ruvector-core/src/lib.rs @@ -81,8 +81,8 @@ pub use advanced_features::{ #[cfg(feature = "storage")] pub use agenticdb::{ - AgenticDB, PolicyMemoryStore, PolicyEntry, PolicyAction, - SessionStateIndex, SessionTurn, WitnessLog, WitnessEntry, + AgenticDB, PolicyAction, PolicyEntry, PolicyMemoryStore, SessionStateIndex, SessionTurn, + WitnessEntry, WitnessLog, }; #[cfg(feature = "api-embeddings")] @@ -110,23 +110,18 @@ pub use vector_db::VectorDB; // Quantization types (ADR-001) pub use quantization::{ - ScalarQuantized, ProductQuantized, BinaryQuantized, Int4Quantized, - QuantizedVector, + BinaryQuantized, Int4Quantized, ProductQuantized, QuantizedVector, ScalarQuantized, }; // Memory management types (ADR-001) -pub use arena::{ - Arena, ArenaVec, CacheAlignedVec, BatchVectorAllocator, - CACHE_LINE_SIZE, -}; +pub use arena::{Arena, ArenaVec, BatchVectorAllocator, CacheAlignedVec, CACHE_LINE_SIZE}; // Lock-free structures (requires parallel feature) #[cfg(all(feature = "parallel", not(target_arch = "wasm32")))] pub use lockfree::{ - LockFreeCounter, LockFreeStats, StatsSnapshot, - ObjectPool, PooledObject, LockFreeWorkQueue, - AtomicVectorPool, VectorPoolStats, PooledVector, - LockFreeBatchProcessor, BatchItem, BatchResult, + AtomicVectorPool, BatchItem, BatchResult, LockFreeBatchProcessor, LockFreeCounter, + LockFreeStats, LockFreeWorkQueue, ObjectPool, PooledObject, PooledVector, StatsSnapshot, + VectorPoolStats, }; // Cache-optimized storage diff --git a/crates/ruvector-core/src/quantization.rs b/crates/ruvector-core/src/quantization.rs index a52483ffa..944b1e8d6 100644 --- a/crates/ruvector-core/src/quantization.rs +++ b/crates/ruvector-core/src/quantization.rs @@ -921,7 +921,10 @@ mod tests { let q2 = BinaryQuantized::quantize(&v2); let sim = q1.similarity(&q2); - assert!((sim - 1.0).abs() < 0.001, "Same vectors should have similarity 1.0"); + assert!( + (sim - 1.0).abs() < 0.001, + "Same vectors should have similarity 1.0" + ); } #[test] diff --git a/crates/ruvector-core/src/simd_intrinsics.rs b/crates/ruvector-core/src/simd_intrinsics.rs index 2abd65d33..9119ccd56 100644 --- a/crates/ruvector-core/src/simd_intrinsics.rs +++ b/crates/ruvector-core/src/simd_intrinsics.rs @@ -1571,10 +1571,7 @@ mod tests { "Same direction should be 1.0" ); assert!(results[1].abs() < 0.001, "Orthogonal should be 0.0"); - assert!( - (results[2] + 1.0).abs() < 0.001, - "Opposite should be -1.0" - ); + assert!((results[2] + 1.0).abs() < 0.001, "Opposite should be -1.0"); } #[test] diff --git a/crates/ruvector-core/tests/test_memory_pool.rs b/crates/ruvector-core/tests/test_memory_pool.rs index 4828f198e..70f0bc6d2 100644 --- a/crates/ruvector-core/tests/test_memory_pool.rs +++ b/crates/ruvector-core/tests/test_memory_pool.rs @@ -381,7 +381,9 @@ mod soa_tests { // Add random-ish vectors for i in 0..num_vectors { - let vec: Vec = (0..dim).map(|j| ((i * dim + j) % 100) as f32 * 0.01).collect(); + let vec: Vec = (0..dim) + .map(|j| ((i * dim + j) % 100) as f32 * 0.01) + .collect(); storage.push(&vec); } diff --git a/crates/ruvector-core/tests/test_quantization.rs b/crates/ruvector-core/tests/test_quantization.rs index fba3bf841..7564e6fb2 100644 --- a/crates/ruvector-core/tests/test_quantization.rs +++ b/crates/ruvector-core/tests/test_quantization.rs @@ -92,7 +92,11 @@ mod scalar_quantization_tests { let quantized = ScalarQuantized::quantize(&vector); let distance = quantized.distance(&quantized); - assert!(distance < 0.001, "Distance to self should be ~0, got {}", distance); + assert!( + distance < 0.001, + "Distance to self should be ~0, got {}", + distance + ); } #[test] @@ -151,8 +155,7 @@ mod scalar_quantization_tests { // Verify compression ratio (4x for f32 -> u8) let original_size = dim * std::mem::size_of::(); - let quantized_size = - quantized.data.len() + std::mem::size_of::() * 2; // data + min + scale + let quantized_size = quantized.data.len() + std::mem::size_of::() * 2; // data + min + scale assert!( quantized_size < original_size, "No compression achieved for dim {}", @@ -230,7 +233,9 @@ mod binary_quantization_tests { fn test_binary_quantization_packing() { // Test byte packing for dim in 1..=32 { - let vector: Vec = (0..dim).map(|i| if i % 2 == 0 { 1.0 } else { -1.0 }).collect(); + let vector: Vec = (0..dim) + .map(|i| if i % 2 == 0 { 1.0 } else { -1.0 }) + .collect(); let quantized = BinaryQuantized::quantize(&vector); let expected_bytes = (dim + 7) / 8; @@ -283,26 +288,10 @@ mod binary_quantization_tests { // Test specific Hamming distance cases let cases = vec![ // (v1, v2, expected_distance) - ( - vec![1.0, 1.0, 1.0, 1.0], - vec![1.0, 1.0, 1.0, 1.0], - 0.0, - ), // identical - ( - vec![1.0, 1.0, 1.0, 1.0], - vec![-1.0, -1.0, -1.0, -1.0], - 4.0, - ), // opposite - ( - vec![1.0, 1.0, -1.0, -1.0], - vec![1.0, -1.0, -1.0, 1.0], - 2.0, - ), // 2 bits differ - ( - vec![1.0, -1.0, 1.0, -1.0], - vec![-1.0, 1.0, -1.0, 1.0], - 4.0, - ), // all differ + (vec![1.0, 1.0, 1.0, 1.0], vec![1.0, 1.0, 1.0, 1.0], 0.0), // identical + (vec![1.0, 1.0, 1.0, 1.0], vec![-1.0, -1.0, -1.0, -1.0], 4.0), // opposite + (vec![1.0, 1.0, -1.0, -1.0], vec![1.0, -1.0, -1.0, 1.0], 2.0), // 2 bits differ + (vec![1.0, -1.0, 1.0, -1.0], vec![-1.0, 1.0, -1.0, 1.0], 4.0), // all differ ]; for (v1, v2, expected) in cases { @@ -336,7 +325,9 @@ mod binary_quantization_tests { #[test] fn test_binary_quantization_distance_bounds() { for dim in [8, 16, 32, 64, 128, 256] { - let v1: Vec = (0..dim).map(|i| if i % 2 == 0 { 1.0 } else { -1.0 }).collect(); + let v1: Vec = (0..dim) + .map(|i| if i % 2 == 0 { 1.0 } else { -1.0 }) + .collect(); let v2: Vec = (0..dim) .map(|i| if i % 3 == 0 { 1.0 } else { -1.0 }) .collect(); @@ -359,7 +350,9 @@ mod binary_quantization_tests { #[test] fn test_binary_quantization_compression_ratio() { for dim in [128, 256, 512, 1024] { - let vector: Vec = (0..dim).map(|i| if i % 2 == 0 { 1.0 } else { -1.0 }).collect(); + let vector: Vec = (0..dim) + .map(|i| if i % 2 == 0 { 1.0 } else { -1.0 }) + .collect(); let quantized = BinaryQuantized::quantize(&vector); // f32 to 1 bit = theoretical 32x compression for data only @@ -577,12 +570,21 @@ mod comparative_tests { let binary_ratio = original_size as f32 / binary_size as f32; println!("Original: {} bytes", original_size); - println!("Scalar: {} bytes ({:.1}x compression)", scalar_size, scalar_ratio); - println!("Binary: {} bytes ({:.1}x compression)", binary_size, binary_ratio); + println!( + "Scalar: {} bytes ({:.1}x compression)", + scalar_size, scalar_ratio + ); + println!( + "Binary: {} bytes ({:.1}x compression)", + binary_size, binary_ratio + ); // Verify expected ratios assert!(scalar_ratio > 3.5, "Scalar should achieve ~4x compression"); - assert!(binary_ratio > 25.0, "Binary should achieve ~32x compression"); + assert!( + binary_ratio > 25.0, + "Binary should achieve ~32x compression" + ); } } @@ -754,14 +756,8 @@ mod performance_tests { } let binary_duration = start.elapsed(); - println!( - "Scalar distance: {:?} for 100k ops", - scalar_duration - ); - println!( - "Binary distance: {:?} for 100k ops", - binary_duration - ); + println!("Scalar distance: {:?} for 100k ops", scalar_duration); + println!("Binary distance: {:?} for 100k ops", binary_duration); // Binary should be faster (just XOR and popcount) // But both should be fast diff --git a/crates/ruvector-core/tests/test_simd_correctness.rs b/crates/ruvector-core/tests/test_simd_correctness.rs index 7853a882b..68b672c5b 100644 --- a/crates/ruvector-core/tests/test_simd_correctness.rs +++ b/crates/ruvector-core/tests/test_simd_correctness.rs @@ -368,7 +368,11 @@ fn test_manhattan_simd_vs_scalar_non_aligned() { fn test_manhattan_simd_identical_vectors() { let v = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]; let result = manhattan_distance_simd(&v, &v); - assert!(result < 1e-6, "Manhattan to self should be 0, got {}", result); + assert!( + result < 1e-6, + "Manhattan to self should be 0, got {}", + result + ); } // ============================================================================ @@ -473,7 +477,11 @@ fn test_simd_batch_consistency() { .collect(); // Compare - for (i, (simd, scalar)) in simd_distances.iter().zip(scalar_distances.iter()).enumerate() { + for (i, (simd, scalar)) in simd_distances + .iter() + .zip(scalar_distances.iter()) + .enumerate() + { assert!( (simd - scalar).abs() < 0.01, "Vector {} mismatch: SIMD={}, scalar={}", @@ -537,5 +545,8 @@ fn test_simd_many_operations() { // Final verification let result = euclidean_distance_simd(&a, &b); - assert!(result.is_finite(), "Result should be finite after stress test"); + assert!( + result.is_finite(), + "Result should be finite after stress test" + ); } diff --git a/crates/ruvector-dag-wasm/src/lib.rs b/crates/ruvector-dag-wasm/src/lib.rs index 565016870..8c016e2ad 100644 --- a/crates/ruvector-dag-wasm/src/lib.rs +++ b/crates/ruvector-dag-wasm/src/lib.rs @@ -7,8 +7,8 @@ //! - No string operations in critical paths //! - Optional wee_alloc for smaller binary +use serde::{Deserialize, Serialize}; use wasm_bindgen::prelude::*; -use serde::{Serialize, Deserialize}; // Use wee_alloc for smaller WASM binary (~10KB reduction) #[cfg(feature = "wee_alloc")] @@ -147,7 +147,8 @@ impl WasmDag { } // Find node with maximum cost - let (max_idx, (max_cost, _)) = dist.iter() + let (max_idx, (max_cost, _)) = dist + .iter() .enumerate() .max_by(|(_, a), (_, b)| a.0.partial_cmp(&b.0).unwrap()) .unwrap(); @@ -164,7 +165,8 @@ impl WasmDag { path.reverse(); // Convert to JSON manually to avoid serde_json dependency - let path_str = path.iter() + let path_str = path + .iter() .map(|id| id.to_string()) .collect::>() .join(","); diff --git a/crates/ruvector-dag/examples/synthetic_haptic.rs b/crates/ruvector-dag/examples/synthetic_haptic.rs index 2e2b75034..d78376d74 100644 --- a/crates/ruvector-dag/examples/synthetic_haptic.rs +++ b/crates/ruvector-dag/examples/synthetic_haptic.rs @@ -26,12 +26,12 @@ use std::time::{Duration, Instant}; #[derive(Clone, Debug)] pub struct SensorFrame { pub t_us: u64, - pub position: f32, // Normalized position [-1, 1] - pub velocity: f32, // Rate of change - pub force: f32, // Applied force - pub contact: f32, // Contact intensity [0, 1] - pub temperature: f32, // Thermal signal - pub vibration: f32, // High-frequency component + pub position: f32, // Normalized position [-1, 1] + pub velocity: f32, // Rate of change + pub force: f32, // Applied force + pub contact: f32, // Contact intensity [0, 1] + pub temperature: f32, // Thermal signal + pub vibration: f32, // High-frequency component } impl SensorFrame { @@ -119,7 +119,11 @@ impl Sensor for SimulatedSensor { let noise = self.pseudo_random() * 0.05; let temperature = 0.5 + self.phase.sin() * 0.1 + noise; - let vibration = if self.contact_mode { 0.3 + noise } else { noise.abs() }; + let vibration = if self.contact_mode { + 0.3 + noise + } else { + noise.abs() + }; SensorFrame { t_us, @@ -145,20 +149,20 @@ impl Sensor for SimulatedSensor { /// Homeostatic state derived from DAG analysis #[derive(Clone, Debug)] pub struct HomeostasisState { - pub tension: f32, // Deviation from equilibrium [0, 1] - pub coherence: f32, // Stability of internal state [0, 1] - pub cut_value: f32, // MinCut flow capacity - pub criticality: f32, // Node criticality max - pub reflex: ReflexMode, // Current reflex state + pub tension: f32, // Deviation from equilibrium [0, 1] + pub coherence: f32, // Stability of internal state [0, 1] + pub cut_value: f32, // MinCut flow capacity + pub criticality: f32, // Node criticality max + pub reflex: ReflexMode, // Current reflex state } /// Reflex modes mapped to DAG tension levels #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ReflexMode { - Calm, // Tension < 0.20: minimal response, learning allowed - Active, // Tension 0.20-0.55: proportional response - Spike, // Tension 0.55-0.85: heightened response, haptic feedback - Protect, // Tension > 0.85: protective shutdown, no output + Calm, // Tension < 0.20: minimal response, learning allowed + Active, // Tension 0.20-0.55: proportional response + Spike, // Tension 0.55-0.85: heightened response, haptic feedback + Protect, // Tension > 0.85: protective shutdown, no output } impl ReflexMode { @@ -179,9 +183,9 @@ impl ReflexMode { pub struct ReflexArc { mincut_engine: DagMinCutEngine, dag: QueryDag, - tension_ema: f32, // Exponential moving average - coherence_ema: f32, // Coherence smoothing - alpha: f32, // EMA decay rate + tension_ema: f32, // Exponential moving average + coherence_ema: f32, // Coherence smoothing + alpha: f32, // EMA decay rate } impl ReflexArc { @@ -293,8 +297,7 @@ impl ReflexArc { // Coherence drops when tension is high or changing rapidly let tension_delta = (raw_tension - self.tension_ema).abs(); let raw_coherence = 1.0 - (self.tension_ema * 0.4 + tension_delta * 0.6); - self.coherence_ema = - self.alpha * raw_coherence + (1.0 - self.alpha) * self.coherence_ema; + self.coherence_ema = self.alpha * raw_coherence + (1.0 - self.alpha) * self.coherence_ema; let tension = self.tension_ema.clamp(0.0, 1.0); let coherence = self.coherence_ema.clamp(0.0, 1.0); @@ -549,7 +552,8 @@ impl LearningController { // Process pending trajectories for traj in self.pending_trajectories.drain(..) { if traj.quality > 0.6 { - self.sona_engine.reasoning_bank_store(traj.embedding, traj.quality); + self.sona_engine + .reasoning_bank_store(traj.embedding, traj.quality); } } @@ -599,10 +603,10 @@ impl SonaEngineExt for DagSonaEngine { /// Actuator command with energy constraints #[derive(Clone, Debug)] pub struct ActuatorCommand { - pub force: f32, // Output force [-1, 1] - pub vibro_freq: f32, // Vibration frequency Hz - pub vibro_amp: f32, // Vibration amplitude [0, 1] - pub energy_used: f32, // Energy consumed this tick + pub force: f32, // Output force [-1, 1] + pub vibro_freq: f32, // Vibration frequency Hz + pub vibro_amp: f32, // Vibration amplitude [0, 1] + pub energy_used: f32, // Energy consumed this tick } impl ActuatorCommand { @@ -683,11 +687,7 @@ impl ActuationRenderer { } /// Render actuator command from sensor and homeostasis - pub fn render( - &self, - frame: &SensorFrame, - state: &HomeostasisState, - ) -> ActuatorCommand { + pub fn render(&self, frame: &SensorFrame, state: &HomeostasisState) -> ActuatorCommand { // Base PD controller let kp = 0.4; let kd = 8.0; @@ -862,11 +862,7 @@ impl SyntheticHapticController { if tick % 50 == 0 { println!( "tick={:4} tension={:.2} coherence={:.2} reflex={:?} loop_us={}", - tick, - state.tension, - state.coherence, - state.reflex, - self.stats.avg_loop_time_us + tick, state.tension, state.coherence, state.reflex, self.stats.avg_loop_time_us ); } @@ -939,7 +935,12 @@ fn main() { println!("Learned patterns: {}", controller.pattern_count()); println!("\nReflex mode distribution:"); for (mode, count) in &stats.reflex_counts { - println!(" {:10}: {} ({:.1}%)", mode, count, *count as f64 / stats.tick_count as f64 * 100.0); + println!( + " {:10}: {} ({:.1}%)", + mode, + count, + *count as f64 / stats.tick_count as f64 * 100.0 + ); } println!("\n✓ Intelligence as homeostasis, rendered as touch."); @@ -1004,7 +1005,10 @@ mod tests { let key2 = HdcKey::encode(&frame2, 256, 0xDEADBEEF); let similarity = key1.similarity(&key2); - assert!(similarity > 0.9, "Similar frames should have high similarity"); + assert!( + similarity > 0.9, + "Similar frames should have high similarity" + ); } #[test] diff --git a/crates/ruvector-dag/src/lib.rs b/crates/ruvector-dag/src/lib.rs index cdf4518f9..c4f4b345f 100644 --- a/crates/ruvector-dag/src/lib.rs +++ b/crates/ruvector-dag/src/lib.rs @@ -73,7 +73,9 @@ pub use qudag::QuDagClient; // Re-export crypto security functions for easy access (requires full feature) #[cfg(feature = "full")] -pub use qudag::crypto::{check_crypto_security, is_production_ready, security_status, SecurityStatus}; +pub use qudag::crypto::{ + check_crypto_security, is_production_ready, security_status, SecurityStatus, +}; #[cfg(feature = "full")] pub use healing::{ diff --git a/crates/ruvector-dag/src/qudag/crypto/ml_dsa.rs b/crates/ruvector-dag/src/qudag/crypto/ml_dsa.rs index 4b1e6d481..1a60fd472 100644 --- a/crates/ruvector-dag/src/qudag/crypto/ml_dsa.rs +++ b/crates/ruvector-dag/src/qudag/crypto/ml_dsa.rs @@ -69,8 +69,8 @@ mod production { /// Sign a message using real Dilithium3 pub fn sign(sk: &MlDsa65SecretKey, message: &[u8]) -> Result { - let secret_key = dilithium3::SecretKey::from_bytes(&sk.0) - .map_err(|_| DsaError::InvalidSignature)?; + let secret_key = + dilithium3::SecretKey::from_bytes(&sk.0).map_err(|_| DsaError::InvalidSignature)?; let sig = dilithium3::detached_sign(message, &secret_key); let sig_bytes = sig.as_bytes(); @@ -90,8 +90,8 @@ mod production { message: &[u8], signature: &Signature, ) -> Result { - let public_key = dilithium3::PublicKey::from_bytes(&pk.0) - .map_err(|_| DsaError::InvalidPublicKey)?; + let public_key = + dilithium3::PublicKey::from_bytes(&pk.0).map_err(|_| DsaError::InvalidPublicKey)?; // Dilithium3 signature is 3293 bytes let sig = dilithium3::DetachedSignature::from_bytes(&signature.0[..3293]) diff --git a/crates/ruvector-dag/src/qudag/crypto/ml_kem.rs b/crates/ruvector-dag/src/qudag/crypto/ml_kem.rs index f0c355b75..06c83e634 100644 --- a/crates/ruvector-dag/src/qudag/crypto/ml_kem.rs +++ b/crates/ruvector-dag/src/qudag/crypto/ml_kem.rs @@ -73,8 +73,8 @@ mod production { /// Encapsulate a shared secret using real Kyber768 pub fn encapsulate(pk: &MlKem768PublicKey) -> Result { - let public_key = kyber768::PublicKey::from_bytes(&pk.0) - .map_err(|_| KemError::InvalidPublicKey)?; + let public_key = + kyber768::PublicKey::from_bytes(&pk.0).map_err(|_| KemError::InvalidPublicKey)?; let (ss, ct) = kyber768::encapsulate(&public_key); diff --git a/crates/ruvector-dag/src/qudag/crypto/security_notice.rs b/crates/ruvector-dag/src/qudag/crypto/security_notice.rs index 1a639e651..2c8c591f7 100644 --- a/crates/ruvector-dag/src/qudag/crypto/security_notice.rs +++ b/crates/ruvector-dag/src/qudag/crypto/security_notice.rs @@ -49,9 +49,7 @@ pub fn check_crypto_security() { let status = security_status(); if status.production_ready { - tracing::info!( - "✓ QuDAG cryptography: Production mode enabled (Dilithium3 + Kyber768)" - ); + tracing::info!("✓ QuDAG cryptography: Production mode enabled (Dilithium3 + Kyber768)"); } else { tracing::warn!( "⚠️ SECURITY WARNING: Using placeholder cryptography. \ @@ -59,8 +57,16 @@ pub fn check_crypto_security() { ); tracing::warn!( " ML-DSA: {} | ML-KEM: {}", - if status.ml_dsa_ready { "Ready" } else { "PLACEHOLDER" }, - if status.ml_kem_ready { "Ready" } else { "PLACEHOLDER" } + if status.ml_dsa_ready { + "Ready" + } else { + "PLACEHOLDER" + }, + if status.ml_kem_ready { + "Ready" + } else { + "PLACEHOLDER" + } ); } } diff --git a/crates/ruvector-dag/tests/integration/healing_tests.rs b/crates/ruvector-dag/tests/integration/healing_tests.rs index d022978e8..7f3742f51 100644 --- a/crates/ruvector-dag/tests/integration/healing_tests.rs +++ b/crates/ruvector-dag/tests/integration/healing_tests.rs @@ -190,9 +190,7 @@ fn test_drift_multiple_metrics() { // Latency values increasing - the detector considers increasing values // as "improving" since it doesn't know the semantic meaning of metrics // Higher latency IS worsening, but numerically it's "improving" (going up) - assert!( - lat_metric.trend == DriftTrend::Improving || lat_metric.trend == DriftTrend::Declining - ); + assert!(lat_metric.trend == DriftTrend::Improving || lat_metric.trend == DriftTrend::Declining); } #[test] diff --git a/crates/ruvector-delta-consensus/src/conflict.rs b/crates/ruvector-delta-consensus/src/conflict.rs index 50731e0a2..aa505afc9 100644 --- a/crates/ruvector-delta-consensus/src/conflict.rs +++ b/crates/ruvector-delta-consensus/src/conflict.rs @@ -209,10 +209,7 @@ impl ConflictResolver for SparsityResolver { } // Take the sparsest delta - let sparsest = deltas - .iter() - .min_by_key(|d| d.value.nnz()) - .unwrap(); + let sparsest = deltas.iter().min_by_key(|d| d.value.nnz()).unwrap(); Ok((*sparsest).clone()) } diff --git a/crates/ruvector-delta-consensus/src/crdt.rs b/crates/ruvector-delta-consensus/src/crdt.rs index 7afc10460..7e15b41bc 100644 --- a/crates/ruvector-delta-consensus/src/crdt.rs +++ b/crates/ruvector-delta-consensus/src/crdt.rs @@ -192,9 +192,7 @@ impl LWWRegister { /// Set the value pub fn set(&mut self, value: T, timestamp: u64, replica: ReplicaId) { - if timestamp > self.timestamp - || (timestamp == self.timestamp && replica > self.replica) - { + if timestamp > self.timestamp || (timestamp == self.timestamp && replica > self.replica) { self.delta = Some((value.clone(), timestamp, replica.clone())); self.value = Some(value); self.timestamp = timestamp; @@ -393,7 +391,10 @@ impl DeltaCrdt for ORSet { // Merge elements for (element, other_tags) in &other.elements { - let tags = self.elements.entry(element.clone()).or_insert_with(HashSet::new); + let tags = self + .elements + .entry(element.clone()) + .or_insert_with(HashSet::new); for tag in other_tags { if !self.tombstones.contains(tag) { tags.insert(tag.clone()); diff --git a/crates/ruvector-delta-consensus/src/lib.rs b/crates/ruvector-delta-consensus/src/lib.rs index a6443b7e4..bdd04ade1 100644 --- a/crates/ruvector-delta-consensus/src/lib.rs +++ b/crates/ruvector-delta-consensus/src/lib.rs @@ -27,7 +27,7 @@ use uuid::Uuid; use ruvector_delta_core::{Delta, VectorDelta}; -pub use causal::{CausalOrder, VectorClock, HybridLogicalClock}; +pub use causal::{CausalOrder, HybridLogicalClock, VectorClock}; pub use conflict::{ConflictResolver, ConflictStrategy, MergeResult}; pub use crdt::{DeltaCrdt, GCounter, LWWRegister, ORSet, PNCounter}; pub use error::{ConsensusError, Result}; @@ -437,11 +437,7 @@ mod tests { let consensus = DeltaConsensus::new(config); let delta = VectorDelta::from_dense(vec![1.0, 2.0, 3.0]); - let causal = CausalDelta::new( - delta, - "replica2".to_string(), - VectorClock::new(), - ); + let causal = CausalDelta::new(delta, "replica2".to_string(), VectorClock::new()); let status = consensus.receive(causal).unwrap(); assert_eq!(status, DeliveryStatus::Delivered); @@ -461,17 +457,9 @@ mod tests { c }; - let d1 = CausalDelta::new( - VectorDelta::from_dense(vec![1.0]), - "r1".to_string(), - clock1, - ); + let d1 = CausalDelta::new(VectorDelta::from_dense(vec![1.0]), "r1".to_string(), clock1); - let d2 = CausalDelta::new( - VectorDelta::from_dense(vec![2.0]), - "r1".to_string(), - clock2, - ); + let d2 = CausalDelta::new(VectorDelta::from_dense(vec![2.0]), "r1".to_string(), clock2); assert!(d1.is_before(&d2)); assert!(!d2.is_before(&d1)); @@ -491,17 +479,9 @@ mod tests { c }; - let d1 = CausalDelta::new( - VectorDelta::from_dense(vec![1.0]), - "r1".to_string(), - clock1, - ); + let d1 = CausalDelta::new(VectorDelta::from_dense(vec![1.0]), "r1".to_string(), clock1); - let d2 = CausalDelta::new( - VectorDelta::from_dense(vec![2.0]), - "r2".to_string(), - clock2, - ); + let d2 = CausalDelta::new(VectorDelta::from_dense(vec![2.0]), "r2".to_string(), clock2); assert!(d1.is_concurrent(&d2)); } diff --git a/crates/ruvector-delta-core/src/compression.rs b/crates/ruvector-delta-core/src/compression.rs index e972bb484..8c0510a85 100644 --- a/crates/ruvector-delta-core/src/compression.rs +++ b/crates/ruvector-delta-core/src/compression.rs @@ -134,9 +134,7 @@ impl CompressedHeader { fn from_bytes(bytes: &[u8]) -> Result<(Self, usize)> { if bytes.len() < 15 { - return Err(DeltaError::DecompressionError( - "Header too small".into(), - )); + return Err(DeltaError::DecompressionError("Header too small".into())); } let magic = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]); @@ -167,8 +165,8 @@ impl CompressedHeader { )); } let cs = u64::from_le_bytes([ - bytes[15], bytes[16], bytes[17], bytes[18], - bytes[19], bytes[20], bytes[21], bytes[22], + bytes[15], bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], + bytes[22], ]); (Some(cs), 23) } else { @@ -216,9 +214,7 @@ impl DeltaCompressor { let encoded = self.encoding.encode(delta)?; // Check if compression is worthwhile - if encoded.len() < self.config.min_size - || self.config.codec == CompressionCodec::None - { + if encoded.len() < self.config.min_size || self.config.codec == CompressionCodec::None { // Return uncompressed with header let header = CompressedHeader { codec: CompressionCodec::None, diff --git a/crates/ruvector-delta-core/src/delta.rs b/crates/ruvector-delta-core/src/delta.rs index 5ac606daf..014f83da2 100644 --- a/crates/ruvector-delta-core/src/delta.rs +++ b/crates/ruvector-delta-core/src/delta.rs @@ -216,9 +216,7 @@ impl VectorDelta { pub fn l2_norm(&self) -> f32 { match &self.value { DeltaValue::Identity => 0.0, - DeltaValue::Sparse(ops) => { - ops.iter().map(|op| op.value * op.value).sum::().sqrt() - } + DeltaValue::Sparse(ops) => ops.iter().map(|op| op.value * op.value).sum::().sqrt(), DeltaValue::Dense(values) | DeltaValue::Replace(values) => { values.iter().map(|v| v * v).sum::().sqrt() } @@ -294,20 +292,12 @@ impl Delta for VectorDelta { type Error = DeltaError; fn compute(old: &Vec, new: &Vec) -> Self { - assert_eq!( - old.len(), - new.len(), - "Vectors must have same dimensions" - ); + assert_eq!(old.len(), new.len(), "Vectors must have same dimensions"); let dimensions = old.len(); // Compute differences - let diffs: Vec = old - .iter() - .zip(new.iter()) - .map(|(o, n)| n - o) - .collect(); + let diffs: Vec = old.iter().zip(new.iter()).map(|(o, n)| n - o).collect(); // Count non-zero differences (with epsilon) let epsilon = 1e-7; @@ -388,9 +378,7 @@ impl Delta for VectorDelta { (DeltaValue::Identity, _) => other.value.clone(), (_, DeltaValue::Identity) => self.value.clone(), - (DeltaValue::Replace(_), DeltaValue::Replace(new)) => { - DeltaValue::Replace(new.clone()) - } + (DeltaValue::Replace(_), DeltaValue::Replace(new)) => DeltaValue::Replace(new.clone()), (DeltaValue::Sparse(ops1), DeltaValue::Sparse(ops2)) => { // Merge sparse operations @@ -418,8 +406,7 @@ impl Delta for VectorDelta { } (DeltaValue::Dense(d1), DeltaValue::Dense(d2)) => { - let combined: Vec = - d1.iter().zip(d2.iter()).map(|(a, b)| a + b).collect(); + let combined: Vec = d1.iter().zip(d2.iter()).map(|(a, b)| a + b).collect(); // Check if result is identity if combined.iter().all(|v| v.abs() < 1e-7) { @@ -435,8 +422,7 @@ impl Delta for VectorDelta { let d2 = other.value.to_dense(other.dimensions); if let (DeltaValue::Dense(v1), DeltaValue::Dense(v2)) = (d1, d2) { - let combined: Vec = - v1.iter().zip(v2.iter()).map(|(a, b)| a + b).collect(); + let combined: Vec = v1.iter().zip(v2.iter()).map(|(a, b)| a + b).collect(); DeltaValue::Dense(combined) } else { DeltaValue::Identity @@ -461,9 +447,7 @@ impl Delta for VectorDelta { .collect(); DeltaValue::Sparse(inverted) } - DeltaValue::Dense(values) => { - DeltaValue::Dense(values.iter().map(|v| -v).collect()) - } + DeltaValue::Dense(values) => DeltaValue::Dense(values.iter().map(|v| -v).collect()), DeltaValue::Replace(_) => { // Cannot invert a replace without knowing original panic!("Cannot invert Replace delta without original value"); @@ -621,8 +605,7 @@ impl Delta for SparseDelta { } fn byte_size(&self) -> usize { - core::mem::size_of::() - + self.entries.len() * core::mem::size_of::<(u32, f32, f32)>() + core::mem::size_of::() + self.entries.len() * core::mem::size_of::<(u32, f32, f32)>() } } diff --git a/crates/ruvector-delta-core/src/encoding.rs b/crates/ruvector-delta-core/src/encoding.rs index 801d61f70..9bfdf6b5a 100644 --- a/crates/ruvector-delta-core/src/encoding.rs +++ b/crates/ruvector-delta-core/src/encoding.rs @@ -110,9 +110,7 @@ impl DeltaEncoding for DenseEncoding { let encoding_type = EncodingType::try_from(bytes[0])?; if encoding_type != EncodingType::Dense { - return Err(DeltaError::InvalidEncoding( - "Not a dense encoding".into(), - )); + return Err(DeltaError::InvalidEncoding("Not a dense encoding".into())); } let dimensions = u32::from_le_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]) as usize; @@ -212,9 +210,7 @@ impl DeltaEncoding for SparseEncoding { let encoding_type = EncodingType::try_from(bytes[0])?; if encoding_type != EncodingType::Sparse { - return Err(DeltaError::InvalidEncoding( - "Not a sparse encoding".into(), - )); + return Err(DeltaError::InvalidEncoding("Not a sparse encoding".into())); } let dimensions = u32::from_le_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]) as usize; @@ -501,9 +497,7 @@ impl DeltaEncoding for HybridEncoding { fn decode(&self, bytes: &[u8]) -> Result { if bytes.is_empty() { - return Err(DeltaError::InvalidEncoding( - "Empty buffer".into(), - )); + return Err(DeltaError::InvalidEncoding("Empty buffer".into())); } let encoding_type = EncodingType::try_from(bytes[0])?; @@ -511,19 +505,13 @@ impl DeltaEncoding for HybridEncoding { match encoding_type { EncodingType::Dense => DenseEncoding.decode(bytes), EncodingType::Sparse => SparseEncoding::with_epsilon(self.epsilon).decode(bytes), - EncodingType::RunLength => { - RunLengthEncoding::with_epsilon(self.epsilon).decode(bytes) - } - EncodingType::Hybrid => { - Err(DeltaError::InvalidEncoding( - "Hybrid type should not appear in encoded data".into(), - )) - } - EncodingType::Varint => { - Err(DeltaError::InvalidEncoding( - "Varint encoding not yet implemented".into(), - )) - } + EncodingType::RunLength => RunLengthEncoding::with_epsilon(self.epsilon).decode(bytes), + EncodingType::Hybrid => Err(DeltaError::InvalidEncoding( + "Hybrid type should not appear in encoded data".into(), + )), + EncodingType::Varint => Err(DeltaError::InvalidEncoding( + "Varint encoding not yet implemented".into(), + )), } } diff --git a/crates/ruvector-delta-core/src/error.rs b/crates/ruvector-delta-core/src/error.rs index 2d1daab44..3d012bc03 100644 --- a/crates/ruvector-delta-core/src/error.rs +++ b/crates/ruvector-delta-core/src/error.rs @@ -88,14 +88,13 @@ impl fmt::Display for DeltaError { Self::WindowError(msg) => write!(f, "Window error: {}", msg), Self::SerializationError(msg) => write!(f, "Serialization error: {}", msg), Self::IndexOutOfBounds { index, length } => { - write!( - f, - "Index out of bounds: {} (length: {})", - index, length - ) + write!(f, "Index out of bounds: {} (length: {})", index, length) } Self::InvalidOperation(msg) => write!(f, "Invalid operation: {}", msg), - Self::BufferOverflow { required, available } => { + Self::BufferOverflow { + required, + available, + } => { write!( f, "Buffer overflow: required {}, available {}", @@ -110,11 +109,7 @@ impl fmt::Display for DeltaError { ) } Self::VersionMismatch { expected, actual } => { - write!( - f, - "Version mismatch: expected {}, got {}", - expected, actual - ) + write!(f, "Version mismatch: expected {}, got {}", expected, actual) } } } diff --git a/crates/ruvector-delta-core/src/lib.rs b/crates/ruvector-delta-core/src/lib.rs index af94720e4..c4d268648 100644 --- a/crates/ruvector-delta-core/src/lib.rs +++ b/crates/ruvector-delta-core/src/lib.rs @@ -43,21 +43,23 @@ pub mod stream; pub mod window; // Re-exports -pub use compression::{CompressionCodec, DeltaCompressor, CompressionLevel}; -pub use delta::{Delta, DeltaOp, DeltaValue, VectorDelta, SparseDelta}; -pub use encoding::{DeltaEncoding, DenseEncoding, SparseEncoding, RunLengthEncoding, HybridEncoding, EncodingType}; +pub use compression::{CompressionCodec, CompressionLevel, DeltaCompressor}; +pub use delta::{Delta, DeltaOp, DeltaValue, SparseDelta, VectorDelta}; +pub use encoding::{ + DeltaEncoding, DenseEncoding, EncodingType, HybridEncoding, RunLengthEncoding, SparseEncoding, +}; pub use error::{DeltaError, Result}; pub use stream::{DeltaStream, DeltaStreamConfig, StreamCheckpoint}; -pub use window::{DeltaWindow, WindowConfig, WindowAggregator, WindowType, WindowResult}; +pub use window::{DeltaWindow, WindowAggregator, WindowConfig, WindowResult, WindowType}; /// Prelude for convenient imports pub mod prelude { pub use crate::compression::{CompressionCodec, DeltaCompressor}; pub use crate::delta::{Delta, DeltaOp, DeltaValue, VectorDelta}; pub use crate::encoding::{DeltaEncoding, DenseEncoding, SparseEncoding}; + pub use crate::error::Result; pub use crate::stream::{DeltaStream, StreamCheckpoint}; pub use crate::window::{DeltaWindow, WindowAggregator}; - pub use crate::error::Result; } #[cfg(test)] diff --git a/crates/ruvector-delta-core/src/stream.rs b/crates/ruvector-delta-core/src/stream.rs index b09471e61..8100c67e2 100644 --- a/crates/ruvector-delta-core/src/stream.rs +++ b/crates/ruvector-delta-core/src/stream.rs @@ -239,11 +239,7 @@ where } // Find the latest checkpoint sequence - let checkpoint_sequence = self - .checkpoints - .last() - .map(|c| c.sequence) - .unwrap_or(0); + let checkpoint_sequence = self.checkpoints.last().map(|c| c.sequence).unwrap_or(0); // Only compact deltas after the latest checkpoint let mut compacted = 0; @@ -379,7 +375,6 @@ where } } - #[cfg(test)] mod tests { use super::*; diff --git a/crates/ruvector-delta-core/src/window.rs b/crates/ruvector-delta-core/src/window.rs index 013395eea..67b618141 100644 --- a/crates/ruvector-delta-core/src/window.rs +++ b/crates/ruvector-delta-core/src/window.rs @@ -126,7 +126,10 @@ where self.window_start_ns = timestamp_ns; } - self.entries.push_back(WindowEntry { delta, timestamp_ns }); + self.entries.push_back(WindowEntry { + delta, + timestamp_ns, + }); // Enforce max items while self.entries.len() > self.config.max_items { @@ -238,10 +241,7 @@ where return None; } - let window_entries: Vec<_> = self - .entries - .drain(..self.config.size) - .collect(); + let window_entries: Vec<_> = self.entries.drain(..self.config.size).collect(); Some(self.compose_entries(&window_entries)) } diff --git a/crates/ruvector-delta-graph/src/edge_delta.rs b/crates/ruvector-delta-graph/src/edge_delta.rs index 121edc5a3..23033278b 100644 --- a/crates/ruvector-delta-graph/src/edge_delta.rs +++ b/crates/ruvector-delta-graph/src/edge_delta.rs @@ -76,9 +76,7 @@ impl EdgeDelta { /// Check if empty pub fn is_empty(&self) -> bool { - self.property_deltas.is_empty() - && self.weight_delta.is_none() - && self.type_change.is_none() + self.property_deltas.is_empty() && self.weight_delta.is_none() && self.type_change.is_none() } /// Add a property set @@ -161,7 +159,9 @@ impl EdgeDeltaBuilder { /// Set a property pub fn set(mut self, key: impl Into, value: PropertyValue) -> Self { - self.delta.property_deltas.push(PropertyDelta::set(key, value)); + self.delta + .property_deltas + .push(PropertyDelta::set(key, value)); self } diff --git a/crates/ruvector-delta-graph/src/lib.rs b/crates/ruvector-delta-graph/src/lib.rs index e52da5f00..cb888baaf 100644 --- a/crates/ruvector-delta-graph/src/lib.rs +++ b/crates/ruvector-delta-graph/src/lib.rs @@ -445,9 +445,8 @@ impl GraphState { } PropertyOp::VectorDelta(vd) => { if let Some(PropertyValue::Vector(v)) = props.get_mut(&prop_delta.key) { - vd.apply(v).map_err(|e| { - GraphDeltaError::DeltaError(format!("{:?}", e)) - })?; + vd.apply(v) + .map_err(|e| GraphDeltaError::DeltaError(format!("{:?}", e)))?; } } } @@ -533,10 +532,7 @@ mod tests { #[test] fn test_delta_compose() { - let d1 = GraphDeltaBuilder::new() - .add_node("a") - .add_node("b") - .build(); + let d1 = GraphDeltaBuilder::new().add_node("a").add_node("b").build(); let d2 = GraphDeltaBuilder::new() .remove_node("b") diff --git a/crates/ruvector-delta-graph/src/node_delta.rs b/crates/ruvector-delta-graph/src/node_delta.rs index 13afe3682..68f900f6f 100644 --- a/crates/ruvector-delta-graph/src/node_delta.rs +++ b/crates/ruvector-delta-graph/src/node_delta.rs @@ -85,7 +85,8 @@ impl NodeDelta { /// Add a vector delta for an embedding property pub fn vector_delta(mut self, key: impl Into, delta: VectorDelta) -> Self { - self.property_deltas.push(PropertyDelta::vector_delta(key, delta)); + self.property_deltas + .push(PropertyDelta::vector_delta(key, delta)); self } @@ -127,8 +128,7 @@ impl NodeDelta { self.property_deltas = prop_map.into_values().collect(); // Merge label changes - let mut adds: std::collections::HashSet = - self.label_adds.into_iter().collect(); + let mut adds: std::collections::HashSet = self.label_adds.into_iter().collect(); let mut removes: std::collections::HashSet = self.label_removes.into_iter().collect(); @@ -180,7 +180,9 @@ impl NodeDeltaBuilder { /// Set a property pub fn set(mut self, key: impl Into, value: PropertyValue) -> Self { - self.delta.property_deltas.push(PropertyDelta::set(key, value)); + self.delta + .property_deltas + .push(PropertyDelta::set(key, value)); self } diff --git a/crates/ruvector-delta-graph/src/traversal.rs b/crates/ruvector-delta-graph/src/traversal.rs index 74fd35610..313693e9f 100644 --- a/crates/ruvector-delta-graph/src/traversal.rs +++ b/crates/ruvector-delta-graph/src/traversal.rs @@ -261,9 +261,7 @@ impl<'a> DeltaAwareTraversal<'a> { } // Filter by edge type - if !self.config.edge_types.is_empty() - && !self.config.edge_types.contains(edge_type) - { + if !self.config.edge_types.is_empty() && !self.config.edge_types.contains(edge_type) { continue; } diff --git a/crates/ruvector-delta-index/src/error.rs b/crates/ruvector-delta-index/src/error.rs index 02743da12..aa012946b 100644 --- a/crates/ruvector-delta-index/src/error.rs +++ b/crates/ruvector-delta-index/src/error.rs @@ -45,7 +45,11 @@ impl fmt::Display for IndexError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::DimensionMismatch { expected, actual } => { - write!(f, "Dimension mismatch: expected {}, got {}", expected, actual) + write!( + f, + "Dimension mismatch: expected {}, got {}", + expected, actual + ) } Self::DuplicateId(id) => write!(f, "Duplicate ID: {}", id), Self::NotFound(id) => write!(f, "ID not found: {}", id), diff --git a/crates/ruvector-delta-index/src/incremental.rs b/crates/ruvector-delta-index/src/incremental.rs index 7d3bdfaab..d54573842 100644 --- a/crates/ruvector-delta-index/src/incremental.rs +++ b/crates/ruvector-delta-index/src/incremental.rs @@ -229,6 +229,9 @@ mod tests { // Large delta -> FullReconnect let large = VectorDelta::from_dense(vec![10.0, 10.0, 10.0]); - assert_eq!(select_strategy(&large, &config), UpdateStrategy::FullReconnect); + assert_eq!( + select_strategy(&large, &config), + UpdateStrategy::FullReconnect + ); } } diff --git a/crates/ruvector-delta-index/src/lib.rs b/crates/ruvector-delta-index/src/lib.rs index e3cb1f195..f08b23589 100644 --- a/crates/ruvector-delta-index/src/lib.rs +++ b/crates/ruvector-delta-index/src/lib.rs @@ -53,7 +53,7 @@ use ruvector_delta_core::{Delta, DeltaStream, VectorDelta}; pub use error::{IndexError, Result}; pub use incremental::IncrementalUpdater; pub use quality::{QualityMetrics, QualityMonitor, RecallEstimate}; -pub use repair::{RepairStrategy, RepairConfig, GraphRepairer}; +pub use repair::{GraphRepairer, RepairConfig, RepairStrategy}; /// Configuration for Delta HNSW index #[derive(Debug, Clone)] @@ -257,10 +257,7 @@ impl DeltaHnsw { } /// Batch apply deltas - pub fn apply_deltas_batch( - &mut self, - updates: &[(String, VectorDelta)], - ) -> Result> { + pub fn apply_deltas_batch(&mut self, updates: &[(String, VectorDelta)]) -> Result> { let mut repaired = Vec::new(); for (id, delta) in updates { @@ -423,7 +420,11 @@ impl DeltaHnsw { for l in (0..=level.min(entry.level)).rev() { let neighbors = self.search_layer(vector, current, l, self.config.ef_construction); - let max_conn = if l == 0 { self.config.m0 } else { self.config.m }; + let max_conn = if l == 0 { + self.config.m0 + } else { + self.config.m + }; // Select best neighbors let selected: Vec = neighbors @@ -491,13 +492,7 @@ impl DeltaHnsw { current } - fn search_layer( - &self, - query: &[f32], - start: u32, - level: usize, - ef: usize, - ) -> Vec<(u32, f32)> { + fn search_layer(&self, query: &[f32], start: u32, level: usize, ef: usize) -> Vec<(u32, f32)> { use std::cmp::Ordering; use std::collections::BinaryHeap; use std::collections::HashSet; @@ -525,7 +520,10 @@ impl DeltaHnsw { impl Ord for Candidate { fn cmp(&self, other: &Self) -> Ordering { // Min-heap by distance - other.dist.partial_cmp(&self.dist).unwrap_or(Ordering::Equal) + other + .dist + .partial_cmp(&self.dist) + .unwrap_or(Ordering::Equal) } } @@ -569,7 +567,10 @@ impl DeltaHnsw { let should_add = results.len() < ef || dist < -results.peek().unwrap().dist; if should_add { - candidates.push(Candidate { idx: neighbor, dist }); + candidates.push(Candidate { + idx: neighbor, + dist, + }); results.push(Candidate { idx: neighbor, dist: -dist, @@ -582,10 +583,7 @@ impl DeltaHnsw { } } - results - .into_iter() - .map(|c| (c.idx, -c.dist)) - .collect() + results.into_iter().map(|c| (c.idx, -c.dist)).collect() } fn distance(&self, query: &[f32], node_idx: u32) -> f32 { @@ -672,7 +670,11 @@ impl DeltaHnsw { for l in (0..=level.min(entry.level)).rev() { let neighbors = self.search_layer(vector, current, l, self.config.ef_construction); - let max_conn = if l == 0 { self.config.m0 } else { self.config.m }; + let max_conn = if l == 0 { + self.config.m0 + } else { + self.config.m + }; // Filter out self let selected: Vec = neighbors diff --git a/crates/ruvector-delta-index/src/quality.rs b/crates/ruvector-delta-index/src/quality.rs index 918258380..8830bb11a 100644 --- a/crates/ruvector-delta-index/src/quality.rs +++ b/crates/ruvector-delta-index/src/quality.rs @@ -189,8 +189,8 @@ impl QualityMonitor { let n = total.max(1) as f32; let z = 1.96; // 95% CI let center = (recall + z * z / (2.0 * n)) / (1.0 + z * z / n); - let width = z * (recall * (1.0 - recall) / n + z * z / (4.0 * n * n)).sqrt() - / (1.0 + z * z / n); + let width = + z * (recall * (1.0 - recall) / n + z * z / (4.0 * n * n)).sqrt() / (1.0 + z * z / n); RecallEstimate { recall, diff --git a/crates/ruvector-delta-wasm/src/apply.rs b/crates/ruvector-delta-wasm/src/apply.rs index a0f2b19b3..bdd0da1b6 100644 --- a/crates/ruvector-delta-wasm/src/apply.rs +++ b/crates/ruvector-delta-wasm/src/apply.rs @@ -143,10 +143,7 @@ pub fn apply_scaled(base: &mut [f32], delta: &VectorDelta, scale: f32) -> Result } /// Batch apply to multiple vectors -pub fn apply_batch( - bases: &mut [&mut [f32]], - delta: &VectorDelta, -) -> Result<(), &'static str> { +pub fn apply_batch(bases: &mut [&mut [f32]], delta: &VectorDelta) -> Result<(), &'static str> { for base in bases { apply_delta(*base, delta)?; } @@ -154,10 +151,7 @@ pub fn apply_batch( } /// Apply multiple deltas to a single vector -pub fn apply_sequence( - base: &mut [f32], - deltas: &[VectorDelta], -) -> Result<(), &'static str> { +pub fn apply_sequence(base: &mut [f32], deltas: &[VectorDelta]) -> Result<(), &'static str> { for delta in deltas { apply_delta(base, delta)?; } diff --git a/crates/ruvector-delta-wasm/src/lib.rs b/crates/ruvector-delta-wasm/src/lib.rs index 3e0ac09c9..be48b24e0 100644 --- a/crates/ruvector-delta-wasm/src/lib.rs +++ b/crates/ruvector-delta-wasm/src/lib.rs @@ -41,8 +41,8 @@ pub use simd::*; use js_sys::{Array, Float32Array, Object, Reflect, Uint8Array}; use parking_lot::RwLock; use ruvector_delta_core::{ - Delta, DeltaEncoding, DeltaOp, DeltaStream, DeltaValue, DeltaWindow, - HybridEncoding, SparseEncoding, VectorDelta, WindowConfig, WindowType, + Delta, DeltaEncoding, DeltaOp, DeltaStream, DeltaValue, DeltaWindow, HybridEncoding, + SparseEncoding, VectorDelta, WindowConfig, WindowType, }; use serde::{Deserialize, Serialize}; use serde_wasm_bindgen::{from_value, to_value}; @@ -251,7 +251,11 @@ impl DeltaEngine { } /// Capture delta between two vectors - pub fn capture(&self, old_vec: Float32Array, new_vec: Float32Array) -> Result { + pub fn capture( + &self, + old_vec: Float32Array, + new_vec: Float32Array, + ) -> Result { if old_vec.length() != new_vec.length() { return Err(JsValue::from_str("Vectors must have same length")); } @@ -305,8 +309,8 @@ impl DeltaEngine { /// Create delta from sparse entries #[wasm_bindgen(js_name = fromSparse)] pub fn from_sparse(&self, entries: JsValue) -> Result { - let sparse: Vec = from_value(entries) - .map_err(|e| JsValue::from_str(&format!("Parse error: {}", e)))?; + let sparse: Vec = + from_value(entries).map_err(|e| JsValue::from_str(&format!("Parse error: {}", e)))?; let ops: smallvec::SmallVec<[DeltaOp; 8]> = sparse .into_iter() @@ -587,8 +591,12 @@ mod tests { fn test_delta_compose() { let engine = DeltaEngine::new(3); - let d1 = engine.from_dense(Float32Array::from(&[1.0f32, 0.0, 0.0][..])).unwrap(); - let d2 = engine.from_dense(Float32Array::from(&[0.0f32, 1.0, 0.0][..])).unwrap(); + let d1 = engine + .from_dense(Float32Array::from(&[1.0f32, 0.0, 0.0][..])) + .unwrap(); + let d2 = engine + .from_dense(Float32Array::from(&[0.0f32, 1.0, 0.0][..])) + .unwrap(); let composed = d1.compose(&d2); assert!(!composed.is_identity()); diff --git a/crates/ruvector-economy-wasm/src/curve.rs b/crates/ruvector-economy-wasm/src/curve.rs index cf019d551..a29fa97f2 100644 --- a/crates/ruvector-economy-wasm/src/curve.rs +++ b/crates/ruvector-economy-wasm/src/curve.rs @@ -164,10 +164,9 @@ pub fn get_tier_name(network_compute_hours: f64) -> String { #[wasm_bindgen] pub fn get_tiers_json() -> String { let tiers = ContributionCurve::get_tiers(); - let tier_objs: Vec<_> = tiers.iter() - .map(|(hours, mult)| { - format!(r#"{{"hours":{},"multiplier":{:.1}}}"#, hours, mult) - }) + let tier_objs: Vec<_> = tiers + .iter() + .map(|(hours, mult)| format!(r#"{{"hours":{},"multiplier":{:.1}}}"#, hours, mult)) .collect(); format!("[{}]", tier_objs.join(",")) @@ -180,7 +179,11 @@ mod tests { #[test] fn test_genesis_multiplier() { let mult = ContributionCurve::current_multiplier(0.0); - assert!((mult - 10.0).abs() < 0.01, "Genesis should give 10x, got {}", mult); + assert!( + (mult - 10.0).abs() < 0.01, + "Genesis should give 10x, got {}", + mult + ); } #[test] @@ -188,7 +191,11 @@ mod tests { // At decay constant, e^(-1) ~= 0.368 // So multiplier = 1 + 9 * 0.368 = 4.31 let mult = ContributionCurve::current_multiplier(1_000_000.0); - assert!(mult > 4.0 && mult < 4.5, "At decay constant should be ~4.3x, got {}", mult); + assert!( + mult > 4.0 && mult < 4.5, + "At decay constant should be ~4.3x, got {}", + mult + ); } #[test] @@ -200,14 +207,22 @@ mod tests { #[test] fn test_multiplier_never_below_one() { let mult = ContributionCurve::current_multiplier(100_000_000.0); - assert!(mult >= 1.0, "Multiplier should never go below 1, got {}", mult); + assert!( + mult >= 1.0, + "Multiplier should never go below 1, got {}", + mult + ); } #[test] fn test_calculate_reward() { let base = 100; let reward = ContributionCurve::calculate_reward(base, 0.0); - assert_eq!(reward, 1000, "Genesis 100 base should give 1000, got {}", reward); + assert_eq!( + reward, 1000, + "Genesis 100 base should give 1000, got {}", + reward + ); } #[test] diff --git a/crates/ruvector-economy-wasm/src/ledger.rs b/crates/ruvector-economy-wasm/src/ledger.rs index dc63ab2a6..3fcd712d5 100644 --- a/crates/ruvector-economy-wasm/src/ledger.rs +++ b/crates/ruvector-economy-wasm/src/ledger.rs @@ -3,10 +3,10 @@ //! Implements a conflict-free replicated data type (CRDT) ledger for P2P consistency. //! Uses G-Counters for earnings (monotonically increasing) and PN-Counters for spending. -use wasm_bindgen::prelude::*; use rustc_hash::FxHashMap; -use serde::{Serialize, Deserialize}; -use sha2::{Sha256, Digest}; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +use wasm_bindgen::prelude::*; use crate::curve::ContributionCurve; @@ -38,7 +38,11 @@ pub enum CreditReason { /// Staked for participation Stake { amount: u64, locked: bool }, /// Transferred between nodes - Transfer { from: String, to: String, memo: String }, + Transfer { + from: String, + to: String, + memo: String, + }, /// Penalty for invalid work Penalty { reason: String }, } @@ -120,7 +124,9 @@ impl CreditLedger { #[wasm_bindgen] pub fn balance(&self) -> u64 { let total_earned: u64 = self.earned.values().sum(); - let total_spent: u64 = self.spent.values() + let total_spent: u64 = self + .spent + .values() .map(|(pos, neg)| pos.saturating_sub(*neg)) .sum(); @@ -138,7 +144,8 @@ impl CreditLedger { /// Get total credits spent #[wasm_bindgen(js_name = totalSpent)] pub fn total_spent(&self) -> u64 { - self.spent.values() + self.spent + .values() .map(|(pos, neg)| pos.saturating_sub(*neg)) .sum() } @@ -170,7 +177,8 @@ impl CreditLedger { /// Get state root as hex string #[wasm_bindgen(js_name = stateRootHex)] pub fn state_root_hex(&self) -> String { - self.state_root.iter() + self.state_root + .iter() .map(|b| format!("{:02x}", b)) .collect() } @@ -200,7 +208,11 @@ impl CreditLedger { /// Credit with multiplier applied (for task rewards) #[wasm_bindgen(js_name = creditWithMultiplier)] - pub fn credit_with_multiplier(&mut self, base_amount: u64, reason: &str) -> Result { + pub fn credit_with_multiplier( + &mut self, + base_amount: u64, + reason: &str, + ) -> Result { let multiplier = self.current_multiplier(); let amount = (base_amount as f32 * multiplier) as u64; self.credit(amount, reason) @@ -234,7 +246,9 @@ impl CreditLedger { /// This updates the PN-Counter negative side for the given event. #[wasm_bindgen] pub fn refund(&mut self, event_id: &str, amount: u64) -> Result<(), JsValue> { - let entry = self.spent.get_mut(event_id) + let entry = self + .spent + .get_mut(event_id) .ok_or_else(|| JsValue::from_str("Event not found"))?; if entry.1 + amount > entry.0 { diff --git a/crates/ruvector-economy-wasm/src/lib.rs b/crates/ruvector-economy-wasm/src/lib.rs index c39848e34..18f9633a1 100644 --- a/crates/ruvector-economy-wasm/src/lib.rs +++ b/crates/ruvector-economy-wasm/src/lib.rs @@ -58,15 +58,15 @@ use wasm_bindgen::prelude::*; -pub mod ledger; pub mod curve; +pub mod ledger; pub mod reputation; pub mod stake; +pub use curve::{contribution_multiplier, ContributionCurve}; pub use ledger::CreditLedger; -pub use curve::{ContributionCurve, contribution_multiplier}; pub use reputation::ReputationScore; -pub use stake::{StakeManager, SlashReason}; +pub use stake::{SlashReason, StakeManager}; /// Initialize panic hook for better error messages in console #[wasm_bindgen(start)] diff --git a/crates/ruvector-economy-wasm/src/reputation.rs b/crates/ruvector-economy-wasm/src/reputation.rs index 50d531bb7..5ba5dccd7 100644 --- a/crates/ruvector-economy-wasm/src/reputation.rs +++ b/crates/ruvector-economy-wasm/src/reputation.rs @@ -7,8 +7,8 @@ //! //! The composite score determines task priority and trust level. +use serde::{Deserialize, Serialize}; use wasm_bindgen::prelude::*; -use serde::{Serialize, Deserialize}; /// Reputation score for a network participant /// @@ -322,11 +322,11 @@ mod tests { rep.record_success(); assert_eq!(rep.tasks_completed(), 6); - assert!((rep.accuracy() - 6.0/11.0).abs() < 0.001); + assert!((rep.accuracy() - 6.0 / 11.0).abs() < 0.001); rep.record_failure(); assert_eq!(rep.tasks_failed(), 6); - assert!((rep.accuracy() - 6.0/12.0).abs() < 0.001); + assert!((rep.accuracy() - 6.0 / 12.0).abs() < 0.001); } #[test] @@ -335,7 +335,7 @@ mod tests { assert!(rep.meets_minimum(0.9, 0.95, 500)); assert!(!rep.meets_minimum(0.99, 0.95, 500)); // Accuracy too low - assert!(!rep.meets_minimum(0.9, 0.99, 500)); // Uptime too low + assert!(!rep.meets_minimum(0.9, 0.99, 500)); // Uptime too low assert!(!rep.meets_minimum(0.9, 0.95, 2000)); // Stake too low } diff --git a/crates/ruvector-economy-wasm/src/stake.rs b/crates/ruvector-economy-wasm/src/stake.rs index 476109646..b100b0fe8 100644 --- a/crates/ruvector-economy-wasm/src/stake.rs +++ b/crates/ruvector-economy-wasm/src/stake.rs @@ -6,9 +6,9 @@ //! - Stake delegation support //! - Lock periods for stability -use wasm_bindgen::prelude::*; -use serde::{Serialize, Deserialize}; use rustc_hash::FxHashMap; +use serde::{Deserialize, Serialize}; +use wasm_bindgen::prelude::*; /// Get current timestamp in milliseconds (works in both WASM and native) fn current_timestamp_ms() -> u64 { @@ -48,12 +48,12 @@ impl SlashReason { /// Get slash percentage for this reason pub fn slash_percentage(&self) -> f32 { match self { - SlashReason::InvalidResult => 0.05, // 5% for errors - SlashReason::DoubleSpend => 1.0, // 100% for fraud - SlashReason::SybilAttack => 0.5, // 50% for sybil - SlashReason::Downtime => 0.01, // 1% for downtime - SlashReason::Spam => 0.1, // 10% for spam - SlashReason::Malicious => 0.75, // 75% for malicious + SlashReason::InvalidResult => 0.05, // 5% for errors + SlashReason::DoubleSpend => 1.0, // 100% for fraud + SlashReason::SybilAttack => 0.5, // 50% for sybil + SlashReason::Downtime => 0.01, // 1% for downtime + SlashReason::Spam => 0.1, // 10% for spam + SlashReason::Malicious => 0.75, // 75% for malicious } } } @@ -108,7 +108,7 @@ impl StakeManager { pub fn new() -> StakeManager { StakeManager { stakes: FxHashMap::default(), - min_stake: 100, // 100 credits minimum + min_stake: 100, // 100 credits minimum default_lock_period: 86_400_000, // 24 hours in ms total_staked: 0, total_slashed: 0, @@ -154,7 +154,8 @@ impl StakeManager { /// Get effective stake (own + delegated) #[wasm_bindgen(js_name = getEffectiveStake)] pub fn get_effective_stake(&self, node_id: &str) -> u64 { - self.stakes.get(node_id) + self.stakes + .get(node_id) .map(|s| s.amount + s.delegated) .unwrap_or(0) } @@ -175,15 +176,16 @@ impl StakeManager { let now = current_timestamp_ms(); let locked_until = now + self.default_lock_period; - let entry = self.stakes.entry(node_id.to_string()).or_insert_with(|| { - StakeEntry { + let entry = self + .stakes + .entry(node_id.to_string()) + .or_insert_with(|| StakeEntry { amount: 0, locked_until: 0, delegated: 0, delegators: Vec::new(), slashes: Vec::new(), - } - }); + }); entry.amount += amount; entry.locked_until = locked_until; @@ -197,7 +199,9 @@ impl StakeManager { pub fn unstake(&mut self, node_id: &str, amount: u64) -> Result { let now = current_timestamp_ms(); - let entry = self.stakes.get_mut(node_id) + let entry = self + .stakes + .get_mut(node_id) .ok_or_else(|| JsValue::from_str("No stake found"))?; if now < entry.locked_until { @@ -224,7 +228,9 @@ impl StakeManager { ) -> Result { let now = current_timestamp_ms(); - let entry = self.stakes.get_mut(node_id) + let entry = self + .stakes + .get_mut(node_id) .ok_or_else(|| JsValue::from_str("No stake found"))?; // Calculate slash amount @@ -249,14 +255,11 @@ impl StakeManager { /// Delegate stake to another node #[wasm_bindgen] - pub fn delegate( - &mut self, - from_node: &str, - to_node: &str, - amount: u64, - ) -> Result<(), JsValue> { + pub fn delegate(&mut self, from_node: &str, to_node: &str, amount: u64) -> Result<(), JsValue> { // Verify from_node has sufficient stake - let from_entry = self.stakes.get_mut(from_node) + let from_entry = self + .stakes + .get_mut(from_node) .ok_or_else(|| JsValue::from_str("Delegator has no stake"))?; if from_entry.amount < amount { @@ -267,15 +270,16 @@ impl StakeManager { from_entry.amount -= amount; // Add to to_node delegated - let to_entry = self.stakes.entry(to_node.to_string()).or_insert_with(|| { - StakeEntry { + let to_entry = self + .stakes + .entry(to_node.to_string()) + .or_insert_with(|| StakeEntry { amount: 0, locked_until: 0, delegated: 0, delegators: Vec::new(), slashes: Vec::new(), - } - }); + }); to_entry.delegated += amount; if !to_entry.delegators.contains(&from_node.to_string()) { @@ -294,7 +298,9 @@ impl StakeManager { amount: u64, ) -> Result<(), JsValue> { // Reduce delegated from to_node - let to_entry = self.stakes.get_mut(to_node) + let to_entry = self + .stakes + .get_mut(to_node) .ok_or_else(|| JsValue::from_str("Target node not found"))?; if to_entry.delegated < amount { @@ -304,15 +310,16 @@ impl StakeManager { to_entry.delegated -= amount; // Return to from_node - let from_entry = self.stakes.entry(from_node.to_string()).or_insert_with(|| { - StakeEntry { + let from_entry = self + .stakes + .entry(from_node.to_string()) + .or_insert_with(|| StakeEntry { amount: 0, locked_until: 0, delegated: 0, delegators: Vec::new(), slashes: Vec::new(), - } - }); + }); from_entry.amount += amount; @@ -322,14 +329,18 @@ impl StakeManager { /// Get lock timestamp for a node #[wasm_bindgen(js_name = getLockTimestamp)] pub fn get_lock_timestamp(&self, node_id: &str) -> u64 { - self.stakes.get(node_id).map(|s| s.locked_until).unwrap_or(0) + self.stakes + .get(node_id) + .map(|s| s.locked_until) + .unwrap_or(0) } /// Check if stake is locked #[wasm_bindgen(js_name = isLocked)] pub fn is_locked(&self, node_id: &str) -> bool { let now = current_timestamp_ms(); - self.stakes.get(node_id) + self.stakes + .get(node_id) .map(|s| now < s.locked_until) .unwrap_or(false) } @@ -337,13 +348,17 @@ impl StakeManager { /// Get slash count for a node #[wasm_bindgen(js_name = getSlashCount)] pub fn get_slash_count(&self, node_id: &str) -> usize { - self.stakes.get(node_id).map(|s| s.slashes.len()).unwrap_or(0) + self.stakes + .get(node_id) + .map(|s| s.slashes.len()) + .unwrap_or(0) } /// Get total amount slashed from a node #[wasm_bindgen(js_name = getNodeTotalSlashed)] pub fn get_node_total_slashed(&self, node_id: &str) -> u64 { - self.stakes.get(node_id) + self.stakes + .get(node_id) .map(|s| s.slashes.iter().map(|e| e.amount).sum()) .unwrap_or(0) } @@ -351,7 +366,10 @@ impl StakeManager { /// Get delegator count #[wasm_bindgen(js_name = getDelegatorCount)] pub fn get_delegator_count(&self, node_id: &str) -> usize { - self.stakes.get(node_id).map(|s| s.delegators.len()).unwrap_or(0) + self.stakes + .get(node_id) + .map(|s| s.delegators.len()) + .unwrap_or(0) } /// Get number of stakers @@ -415,7 +433,9 @@ mod tests { manager.stake("node-1", 1000).unwrap(); // Slash for invalid result (5%) - let slashed = manager.slash("node-1", SlashReason::InvalidResult, "task:123").unwrap(); + let slashed = manager + .slash("node-1", SlashReason::InvalidResult, "task:123") + .unwrap(); assert_eq!(slashed, 50); assert_eq!(manager.get_stake("node-1"), 950); assert_eq!(manager.total_slashed(), 50); diff --git a/crates/ruvector-exotic-wasm/src/lib.rs b/crates/ruvector-exotic-wasm/src/lib.rs index 54dc3399e..0a85565ce 100644 --- a/crates/ruvector-exotic-wasm/src/lib.rs +++ b/crates/ruvector-exotic-wasm/src/lib.rs @@ -127,11 +127,7 @@ pub fn version() -> String { /// Get information about available exotic mechanisms #[wasm_bindgen] pub fn available_mechanisms() -> JsValue { - let mechanisms = vec![ - "NeuralAutonomousOrg", - "MorphogeneticNetwork", - "TimeCrystal", - ]; + let mechanisms = vec!["NeuralAutonomousOrg", "MorphogeneticNetwork", "TimeCrystal"]; serde_wasm_bindgen::to_value(&mechanisms).unwrap() } @@ -181,7 +177,10 @@ impl ExoticEcosystem { let pattern = self.crystal.tick(); // Use pattern to determine which agents should be active - let _active_count = pattern.iter().map(|b| b.count_ones() as usize).sum::(); + let _active_count = pattern + .iter() + .map(|b| b.count_ones() as usize) + .sum::(); // NAO tick with synchronized dynamics self.nao.tick(0.001); @@ -266,8 +265,7 @@ impl ExoticEcosystem { } }); - serde_wasm_bindgen::to_value(&summary) - .map_err(|e| JsValue::from_str(&e.to_string())) + serde_wasm_bindgen::to_value(&summary).map_err(|e| JsValue::from_str(&e.to_string())) } } diff --git a/crates/ruvector-exotic-wasm/src/morphogenetic.rs b/crates/ruvector-exotic-wasm/src/morphogenetic.rs index 6ae2e123c..57b0be90a 100644 --- a/crates/ruvector-exotic-wasm/src/morphogenetic.rs +++ b/crates/ruvector-exotic-wasm/src/morphogenetic.rs @@ -233,16 +233,23 @@ impl MorphogeneticNetwork { /// Get cells by type pub fn cells_by_type(&self, cell_type: CellType) -> Vec<&Cell> { - self.cells.iter().filter(|c| c.cell_type == cell_type).collect() + self.cells + .iter() + .filter(|c| c.cell_type == cell_type) + .collect() } /// Calculate local cell density around a position fn local_density(&self, pos: (i32, i32), radius: f32) -> f32 { - let count = self.cells.iter().filter(|c| { - let dx = (c.position.0 - pos.0) as f32; - let dy = (c.position.1 - pos.1) as f32; - (dx * dx + dy * dy).sqrt() <= radius - }).count(); + let count = self + .cells + .iter() + .filter(|c| { + let dx = (c.position.0 - pos.0) as f32; + let dy = (c.position.1 - pos.1) as f32; + (dx * dx + dy * dy).sqrt() <= radius + }) + .count(); (count as f32) / (std::f32::consts::PI * radius * radius) } @@ -274,17 +281,24 @@ impl MorphogeneticNetwork { let morphogen_names = ["signal", "receptor", "structure", "compute"]; // Pre-collect signaling cell data to avoid borrow conflicts - let signaling_cells: Vec<(u32, (i32, i32))> = self.cells.iter() + let signaling_cells: Vec<(u32, (i32, i32))> = self + .cells + .iter() .filter(|c| c.cell_type == CellType::Signaling) .map(|c| (c.id, c.position)) .collect(); // Pre-compute all readings for each cell - let updates: Vec<(usize, Vec<(String, f32)>)> = self.cells.iter().enumerate() + let updates: Vec<(usize, Vec<(String, f32)>)> = self + .cells + .iter() + .enumerate() .map(|(idx, cell)| { - let readings: Vec<(String, f32)> = morphogen_names.iter() + let readings: Vec<(String, f32)> = morphogen_names + .iter() .map(|&name| { - let conc: f32 = signaling_cells.iter() + let conc: f32 = signaling_cells + .iter() .filter(|(id, _)| *id != cell.id) .map(|(_, pos)| { let dx = (cell.position.0 - pos.0) as f32; @@ -331,14 +345,17 @@ impl MorphogeneticNetwork { // Update morphogen readings // We need to temporarily take cells to avoid borrow issues let morphogen_names = ["signal", "receptor", "structure", "compute"]; - let cell_positions: Vec<_> = self.cells.iter() + let cell_positions: Vec<_> = self + .cells + .iter() .filter(|c| c.cell_type == CellType::Signaling) .map(|c| c.position) .collect(); for cell in &mut self.cells { for name in &morphogen_names { - let conc: f32 = cell_positions.iter() + let conc: f32 = cell_positions + .iter() .map(|pos| { let dx = (cell.position.0 - pos.0) as f32; let dy = (cell.position.1 - pos.1) as f32; @@ -349,7 +366,8 @@ impl MorphogeneticNetwork { // Simplified gradient contribution let gradient_conc = 0.0; // Would need to refactor for full gradient support - cell.morphogen_readings.insert(name.to_string(), conc + gradient_conc); + cell.morphogen_readings + .insert(name.to_string(), conc + gradient_conc); } } @@ -359,7 +377,11 @@ impl MorphogeneticNetwork { for cell in &self.cells { let local_density = self.local_density(cell.position, 10.0); - let growth_factor = cell.morphogen_readings.get("signal").copied().unwrap_or(0.0); + let growth_factor = cell + .morphogen_readings + .get("signal") + .copied() + .unwrap_or(0.0); if cell.should_divide(local_density, growth_factor) && rng.gen::() > 0.7 { // Create daughter cell nearby @@ -388,7 +410,9 @@ impl MorphogeneticNetwork { /// Update cell connections based on proximity fn update_connections(&mut self) { - let positions: Vec<_> = self.cells.iter() + let positions: Vec<_> = self + .cells + .iter() .map(|c| (c.id, c.position, c.cell_type)) .collect(); @@ -461,7 +485,8 @@ impl MorphogeneticNetwork { cell.fitness = cell.fitness.min(1.0); // Prune weak connections - cell.connections.retain(|_, &mut strength| strength > threshold); + cell.connections + .retain(|_, &mut strength| strength > threshold); } // Remove dead cells @@ -490,7 +515,11 @@ impl MorphogeneticNetwork { total_cells: self.cells.len(), type_counts, total_connections, - average_fitness: if self.cells.is_empty() { 0.0 } else { total_fitness / self.cells.len() as f32 }, + average_fitness: if self.cells.is_empty() { + 0.0 + } else { + total_fitness / self.cells.len() as f32 + }, tick: self.tick, } } @@ -665,7 +694,7 @@ mod tests { network.seed_cell(51, 50, CellType::Signaling); network.seed_cell(50, 51, CellType::Signaling); for i in 0..5 { - network.seed_cell(50 + i, 52, CellType::Stem); // Very close to signaling + network.seed_cell(50 + i, 52, CellType::Stem); // Very close to signaling } // Run simulation with more iterations to allow differentiation @@ -743,9 +772,26 @@ mod tests { let stats = network.stats(); assert_eq!(stats.total_cells, 3); - assert_eq!(stats.type_counts.get(&CellType::Stem).copied().unwrap_or(0), 1); - assert_eq!(stats.type_counts.get(&CellType::Signaling).copied().unwrap_or(0), 1); - assert_eq!(stats.type_counts.get(&CellType::Compute).copied().unwrap_or(0), 1); + assert_eq!( + stats.type_counts.get(&CellType::Stem).copied().unwrap_or(0), + 1 + ); + assert_eq!( + stats + .type_counts + .get(&CellType::Signaling) + .copied() + .unwrap_or(0), + 1 + ); + assert_eq!( + stats + .type_counts + .get(&CellType::Compute) + .copied() + .unwrap_or(0), + 1 + ); } #[test] diff --git a/crates/ruvector-exotic-wasm/src/nao.rs b/crates/ruvector-exotic-wasm/src/nao.rs index 77b1653d4..9d6036850 100644 --- a/crates/ruvector-exotic-wasm/src/nao.rs +++ b/crates/ruvector-exotic-wasm/src/nao.rs @@ -172,15 +172,16 @@ impl OscillatorySynchronizer { } // Collect current phases - let current_phases: Vec<(String, f32)> = self - .phases - .iter() - .map(|(k, v)| (k.clone(), *v)) - .collect(); + let current_phases: Vec<(String, f32)> = + self.phases.iter().map(|(k, v)| (k.clone(), *v)).collect(); // Kuramoto update: dθ_i/dt = ω_i + (K/N) * Σ_j sin(θ_j - θ_i) for (agent_id, phase) in ¤t_phases { - let omega = self.frequencies.get(agent_id).copied().unwrap_or(self.base_frequency); + let omega = self + .frequencies + .get(agent_id) + .copied() + .unwrap_or(self.base_frequency); // Sum of phase differences let phase_coupling: f32 = current_phases @@ -520,8 +521,7 @@ impl WasmNAO { /// Get all data as JSON #[wasm_bindgen(js_name = toJson)] pub fn to_json(&self) -> Result { - serde_wasm_bindgen::to_value(&self.inner) - .map_err(|e| JsValue::from_str(&e.to_string())) + serde_wasm_bindgen::to_value(&self.inner).map_err(|e| JsValue::from_str(&e.to_string())) } } @@ -623,9 +623,9 @@ mod tests { let prop_id = nao.propose("Controversial action"); // Two against, one weak for - should be rejected even with coherence boost - nao.vote(&prop_id, "agent_1", 0.3); // weak support - nao.vote(&prop_id, "agent_2", -1.0); // strong against - nao.vote(&prop_id, "agent_3", -1.0); // strong against + nao.vote(&prop_id, "agent_1", 0.3); // weak support + nao.vote(&prop_id, "agent_2", -1.0); // strong against + nao.vote(&prop_id, "agent_3", -1.0); // strong against // Should be rejected (more against than for) assert!(!nao.execute(&prop_id)); @@ -723,7 +723,7 @@ mod tests { // Rich votes against, poor votes for nao.vote(&prop_id, "rich", -1.0); // -10 effective vote - nao.vote(&prop_id, "poor", 1.0); // +5 effective vote + nao.vote(&prop_id, "poor", 1.0); // +5 effective vote // Rich should win despite being one agent assert!(!nao.execute(&prop_id)); // Rejected @@ -737,7 +737,7 @@ mod tests { let mut nao = NeuralAutonomousOrg::new(0.5); nao.add_member("agent_1", 100); // sqrt(100) = 10 - nao.add_member("agent_2", 25); // sqrt(25) = 5 + nao.add_member("agent_2", 25); // sqrt(25) = 5 let total = nao.total_voting_power(); assert!((total - 15.0).abs() < 0.01, "Expected ~15, got {}", total); diff --git a/crates/ruvector-exotic-wasm/src/time_crystal.rs b/crates/ruvector-exotic-wasm/src/time_crystal.rs index 2495673c0..160799a74 100644 --- a/crates/ruvector-exotic-wasm/src/time_crystal.rs +++ b/crates/ruvector-exotic-wasm/src/time_crystal.rs @@ -134,9 +134,7 @@ impl TimeCrystal { pub fn new(n: usize, period_ms: u32) -> Self { let base_frequency = 2.0 * std::f32::consts::PI / (period_ms as f32); - let oscillators = (0..n) - .map(|_| Oscillator::new(base_frequency)) - .collect(); + let oscillators = (0..n).map(|_| Oscillator::new(base_frequency)).collect(); Self { oscillators, @@ -217,7 +215,8 @@ impl TimeCrystal { // Floquet driving (discrete kicks) if is_drive_step { - dphi += self.driving_strength + rng.gen::() * self.disorder * 2.0 - self.disorder; + dphi += + self.driving_strength + rng.gen::() * self.disorder * 2.0 - self.disorder; } osc.phase = (osc.phase + dphi).rem_euclid(2.0 * std::f32::consts::PI); @@ -553,7 +552,11 @@ mod tests { // Synchronized crystal should have high order parameter let order = crystal.order_parameter(); - assert!(order > 0.95, "Synchronized crystal should have high order: {}", order); + assert!( + order > 0.95, + "Synchronized crystal should have high order: {}", + order + ); } #[test] @@ -587,12 +590,16 @@ mod tests { // Check that we see periodic behavior (not all random) // At least some patterns should repeat - let unique_count = patterns.iter() + let unique_count = patterns + .iter() .collect::>() .len(); // With crystallization, should have fewer unique patterns - assert!(unique_count < 10, "Crystallized patterns should show periodicity"); + assert!( + unique_count < 10, + "Crystallized patterns should show periodicity" + ); } #[test] @@ -604,7 +611,10 @@ mod tests { let after_order = crystal.order_parameter(); // Order should decrease after perturbation - assert!(after_order < initial_order, "Perturbation should reduce order"); + assert!( + after_order < initial_order, + "Perturbation should reduce order" + ); } #[test] @@ -619,7 +629,10 @@ mod tests { let robustness = crystal.robustness(); assert!(robustness >= 0.0 && robustness <= 1.0); - assert!(robustness > 0.0, "Crystallized system should have positive robustness"); + assert!( + robustness > 0.0, + "Crystallized system should have positive robustness" + ); } #[test] @@ -659,9 +672,9 @@ mod tests { let pattern = crystal.detect_pattern(); // Synchronized crystal should show coherent or period-doubled assert!( - pattern == CoordinationPattern::Coherent || - pattern == CoordinationPattern::PeriodDoubled || - pattern == CoordinationPattern::Quasiperiodic, + pattern == CoordinationPattern::Coherent + || pattern == CoordinationPattern::PeriodDoubled + || pattern == CoordinationPattern::Quasiperiodic, "Unexpected pattern: {:?}", pattern ); diff --git a/crates/ruvector-fpga-transformer-wasm/src/lib.rs b/crates/ruvector-fpga-transformer-wasm/src/lib.rs index 8f7191c6c..22a5b3722 100644 --- a/crates/ruvector-fpga-transformer-wasm/src/lib.rs +++ b/crates/ruvector-fpga-transformer-wasm/src/lib.rs @@ -29,9 +29,7 @@ use wasm_bindgen::prelude::*; // Re-export the WASM engine from the main crate pub use ruvector_fpga_transformer::ffi::wasm_bindgen::{ - WasmEngine, - micro_shape as microShape, - validate_artifact as validateArtifact, + micro_shape as microShape, validate_artifact as validateArtifact, WasmEngine, }; /// Initialize the WASM module diff --git a/crates/ruvector-fpga-transformer/benches/correctness.rs b/crates/ruvector-fpga-transformer/benches/correctness.rs index c3d3fd587..b781ac25a 100644 --- a/crates/ruvector-fpga-transformer/benches/correctness.rs +++ b/crates/ruvector-fpga-transformer/benches/correctness.rs @@ -4,7 +4,7 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; use std::sync::Arc; use ruvector_fpga_transformer::{ - artifact::{ModelArtifact, Manifest}, + artifact::{Manifest, ModelArtifact}, backend::native_sim::NativeSimBackend, backend::TransformerBackend, gating::DefaultCoherenceGate, @@ -37,7 +37,9 @@ fn bench_determinism(c: &mut Criterion) { let model_id = backend.load(&artifact).unwrap(); let shape = FixedShape::micro(); - let tokens: Vec = (0..shape.seq_len).map(|i| (i * 7) % shape.vocab as u16).collect(); + let tokens: Vec = (0..shape.seq_len) + .map(|i| (i * 7) % shape.vocab as u16) + .collect(); let mask = vec![1u8; shape.seq_len as usize]; c.bench_function("determinism_check_1000", |b| { @@ -55,9 +57,10 @@ fn bench_determinism(c: &mut Criterion) { let result = backend.infer(req).unwrap(); // Hash the logits - let hash = result.logits_q.iter().fold(0u64, |acc, &v| { - acc.wrapping_mul(31).wrapping_add(v as u64) - }); + let hash = result + .logits_q + .iter() + .fold(0u64, |acc, &v| acc.wrapping_mul(31).wrapping_add(v as u64)); match first_hash { None => first_hash = Some(hash), @@ -91,13 +94,7 @@ fn bench_golden_vectors(c: &mut Criterion) { let expected: Vec> = test_inputs .iter() .map(|tokens| { - let req = InferenceRequest::new( - model_id, - shape, - tokens, - &mask, - GateHint::allow_all(), - ); + let req = InferenceRequest::new(model_id, shape, tokens, &mask, GateHint::allow_all()); backend.infer(req).unwrap().logits_q }) .collect(); diff --git a/crates/ruvector-fpga-transformer/benches/gating.rs b/crates/ruvector-fpga-transformer/benches/gating.rs index 60f6a46af..588a097f9 100644 --- a/crates/ruvector-fpga-transformer/benches/gating.rs +++ b/crates/ruvector-fpga-transformer/benches/gating.rs @@ -1,13 +1,13 @@ //! Gating subsystem benchmarks -use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use std::sync::Arc; use ruvector_fpga_transformer::{ - artifact::{ModelArtifact, Manifest}, + artifact::{Manifest, ModelArtifact}, backend::native_sim::NativeSimBackend, backend::TransformerBackend, - gating::{CoherenceGate, DefaultCoherenceGate, CoherenceConfig}, + gating::{CoherenceConfig, CoherenceGate, DefaultCoherenceGate}, types::{ComputeClass, FixedShape, GateDecision, GateHint, InferenceRequest, QuantSpec}, }; @@ -70,13 +70,8 @@ fn bench_early_exit_histogram(c: &mut Criterion) { let hint = GateHint::new(coherence, false, ComputeClass::Deliberative); b.iter(|| { - let req = InferenceRequest::new( - model_id, - shape, - black_box(&tokens), - &mask, - hint, - ); + let req = + InferenceRequest::new(model_id, shape, black_box(&tokens), &mask, hint); let result = backend.infer(req).unwrap(); result.witness.gate_decision }) @@ -124,9 +119,18 @@ fn bench_mincut_gating(c: &mut Criterion) { let gate = MincutCoherenceGate::new(config, 50, 200); let hints = [ - ("high_lambda", GateHint::new(500, false, ComputeClass::Deliberative)), - ("low_lambda", GateHint::new(100, false, ComputeClass::Deliberative)), - ("boundary_crossed", GateHint::new(300, true, ComputeClass::Deliberative)), + ( + "high_lambda", + GateHint::new(500, false, ComputeClass::Deliberative), + ), + ( + "low_lambda", + GateHint::new(100, false, ComputeClass::Deliberative), + ), + ( + "boundary_crossed", + GateHint::new(300, true, ComputeClass::Deliberative), + ), ]; let mut group = c.benchmark_group("mincut_gating"); diff --git a/crates/ruvector-fpga-transformer/benches/latency.rs b/crates/ruvector-fpga-transformer/benches/latency.rs index 0911aee37..5dca87e99 100644 --- a/crates/ruvector-fpga-transformer/benches/latency.rs +++ b/crates/ruvector-fpga-transformer/benches/latency.rs @@ -1,10 +1,10 @@ //! Latency benchmarks for FPGA Transformer -use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use std::sync::Arc; use ruvector_fpga_transformer::{ - artifact::{ModelArtifact, Manifest}, + artifact::{Manifest, ModelArtifact}, backend::native_sim::NativeSimBackend, backend::TransformerBackend, gating::DefaultCoherenceGate, @@ -105,14 +105,21 @@ fn bench_load_unload(c: &mut Criterion) { } fn bench_gating(c: &mut Criterion) { - use ruvector_fpga_transformer::gating::{CoherenceGate, CoherenceConfig}; + use ruvector_fpga_transformer::gating::{CoherenceConfig, CoherenceGate}; let gate = DefaultCoherenceGate::with_config(CoherenceConfig::default()); let hints = [ ("allow_all", GateHint::allow_all()), ("reflex_only", GateHint::reflex_only()), - ("low_coherence", GateHint::new(-500, true, ruvector_fpga_transformer::types::ComputeClass::Deliberative)), + ( + "low_coherence", + GateHint::new( + -500, + true, + ruvector_fpga_transformer::types::ComputeClass::Deliberative, + ), + ), ]; let mut group = c.benchmark_group("gating_preflight"); diff --git a/crates/ruvector-fpga-transformer/examples/basic_inference.rs b/crates/ruvector-fpga-transformer/examples/basic_inference.rs index 90fc9fb5a..afa71f2ee 100644 --- a/crates/ruvector-fpga-transformer/examples/basic_inference.rs +++ b/crates/ruvector-fpga-transformer/examples/basic_inference.rs @@ -61,9 +61,26 @@ fn main() -> anyhow::Result<()> { // Run inference with different coherence levels let coherence_levels = [ - ("High coherence", GateHint::new(500, false, ruvector_fpga_transformer::ComputeClass::Deliberative)), - ("Medium coherence", GateHint::new(100, false, ruvector_fpga_transformer::ComputeClass::Associative)), - ("Low coherence", GateHint::new(-100, true, ruvector_fpga_transformer::ComputeClass::Reflex)), + ( + "High coherence", + GateHint::new( + 500, + false, + ruvector_fpga_transformer::ComputeClass::Deliberative, + ), + ), + ( + "Medium coherence", + GateHint::new( + 100, + false, + ruvector_fpga_transformer::ComputeClass::Associative, + ), + ), + ( + "Low coherence", + GateHint::new(-100, true, ruvector_fpga_transformer::ComputeClass::Reflex), + ), ]; for (name, hint) in coherence_levels { @@ -73,7 +90,10 @@ fn main() -> anyhow::Result<()> { Ok(result) => { println!("\n{}", name); println!(" Gate decision: {:?}", result.witness.gate_decision); - println!(" Latency: {:.2}ms", result.witness.latency_ns as f64 / 1_000_000.0); + println!( + " Latency: {:.2}ms", + result.witness.latency_ns as f64 / 1_000_000.0 + ); if let Some(topk) = &result.topk { println!(" Top-3 predictions:"); diff --git a/crates/ruvector-fpga-transformer/examples/daemon_client.rs b/crates/ruvector-fpga-transformer/examples/daemon_client.rs index e4500e9cd..ede831892 100644 --- a/crates/ruvector-fpga-transformer/examples/daemon_client.rs +++ b/crates/ruvector-fpga-transformer/examples/daemon_client.rs @@ -72,20 +72,15 @@ fn main() -> anyhow::Result<()> { // Run inference println!("\nRunning FPGA inference..."); - let req = InferenceRequest::new( - model_id, - shape, - &tokens, - &mask, - GateHint::allow_all(), - ); + let req = InferenceRequest::new(model_id, shape, &tokens, &mask, GateHint::allow_all()); match engine.infer(req) { Ok(result) => { println!("Inference successful!"); println!(" Backend: {:?}", result.witness.backend); println!(" Cycles: {}", result.witness.cycles); - println!(" Latency: {}ns ({:.3}ms)", + println!( + " Latency: {}ns ({:.3}ms)", result.witness.latency_ns, result.witness.latency_ns as f64 / 1_000_000.0 ); diff --git a/crates/ruvector-fpga-transformer/src/artifact/manifest.rs b/crates/ruvector-fpga-transformer/src/artifact/manifest.rs index 077110a5b..9d2342ba6 100644 --- a/crates/ruvector-fpga-transformer/src/artifact/manifest.rs +++ b/crates/ruvector-fpga-transformer/src/artifact/manifest.rs @@ -1,8 +1,8 @@ //! Manifest schema for model artifacts -use serde::{Deserialize, Serialize}; -use crate::types::{FixedShape, Layout, QuantSpec}; use crate::error::{Error, Result}; +use crate::types::{FixedShape, Layout, QuantSpec}; +use serde::{Deserialize, Serialize}; /// Model manifest containing all metadata #[derive(Debug, Clone, Serialize, Deserialize)] @@ -25,11 +25,7 @@ pub struct Manifest { impl Manifest { /// Create a new manifest - pub fn new( - name: impl Into, - shape: FixedShape, - quant: QuantSpec, - ) -> Self { + pub fn new(name: impl Into, shape: FixedShape, quant: QuantSpec) -> Self { Self { name: name.into(), model_hash: String::new(), @@ -48,7 +44,9 @@ impl Manifest { } // Validate shape - self.shape.validate().map_err(|e| Error::InvalidArtifact(e))?; + self.shape + .validate() + .map_err(|e| Error::InvalidArtifact(e))?; // Validate quantization bits if !matches!(self.quant.w_bits, 1 | 2 | 4 | 8 | 16) { diff --git a/crates/ruvector-fpga-transformer/src/artifact/mod.rs b/crates/ruvector-fpga-transformer/src/artifact/mod.rs index 4002aa77d..868bcdf59 100644 --- a/crates/ruvector-fpga-transformer/src/artifact/mod.rs +++ b/crates/ruvector-fpga-transformer/src/artifact/mod.rs @@ -12,8 +12,8 @@ pub use verify::{verify_artifact, verify_signature}; use crate::error::{Error, Result}; use crate::types::{FixedShape, ModelId, QuantSpec}; -use sha2::{Digest, Sha256}; use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; /// Complete model artifact #[derive(Debug, Clone, Serialize, Deserialize)] @@ -123,11 +123,14 @@ impl ModelArtifact { self.manifest.validate()?; // Validate shape - self.manifest.shape.validate().map_err(|e| Error::InvalidArtifact(e))?; + self.manifest + .shape + .validate() + .map_err(|e| Error::InvalidArtifact(e))?; // Check weights size is reasonable - let min_weight_size = self.manifest.shape.embedding_params() - / self.manifest.quant.weights_per_byte(); + let min_weight_size = + self.manifest.shape.embedding_params() / self.manifest.quant.weights_per_byte(); if self.weights.len() < min_weight_size { return Err(Error::InvalidArtifact(format!( "Weights too small: {} bytes, expected at least {} for embeddings", @@ -193,13 +196,7 @@ mod tests { #[test] fn test_model_id_computation() { let manifest = create_test_manifest(); - let artifact = ModelArtifact::new( - manifest, - vec![0u8; 4096 * 64], - None, - None, - vec![], - ); + let artifact = ModelArtifact::new(manifest, vec![0u8; 4096 * 64], None, None, vec![]); let id1 = artifact.model_id(); let id2 = artifact.model_id(); @@ -209,13 +206,7 @@ mod tests { #[test] fn test_model_hash() { let manifest = create_test_manifest(); - let artifact = ModelArtifact::new( - manifest, - vec![42u8; 4096 * 64], - None, - None, - vec![], - ); + let artifact = ModelArtifact::new(manifest, vec![42u8; 4096 * 64], None, None, vec![]); let hash = artifact.model_hash(); assert_ne!(hash, [0u8; 32]); // Non-zero hash diff --git a/crates/ruvector-fpga-transformer/src/artifact/verify.rs b/crates/ruvector-fpga-transformer/src/artifact/verify.rs index 52f4877a1..8d03a22c9 100644 --- a/crates/ruvector-fpga-transformer/src/artifact/verify.rs +++ b/crates/ruvector-fpga-transformer/src/artifact/verify.rs @@ -45,8 +45,8 @@ pub fn verify_artifact(artifact: &ModelArtifact) -> Result<()> { } // 4. Verify weights size - let expected_min = artifact.manifest.shape.embedding_params() - / artifact.manifest.quant.weights_per_byte(); + let expected_min = + artifact.manifest.shape.embedding_params() / artifact.manifest.quant.weights_per_byte(); if artifact.weights.len() < expected_min { return Err(Error::InvalidArtifact(format!( "Weights too small: {} < {}", @@ -94,7 +94,7 @@ fn compute_signing_message(artifact: &ModelArtifact) -> Vec { /// Sign an artifact with Ed25519 private key #[cfg(feature = "sign")] pub fn sign_artifact(artifact: &mut ModelArtifact, secret_key: &[u8; 32]) -> Result<()> { - use ed25519_dalek::{SigningKey, Signer}; + use ed25519_dalek::{Signer, SigningKey}; let signing_key = SigningKey::from_bytes(secret_key); let message = compute_signing_message(artifact); @@ -151,9 +151,7 @@ pub fn generate_test_vectors( for _ in 0..count { // Generate random input - let tokens: Vec = (0..seq_len) - .map(|_| rng.gen_range(0..vocab)) - .collect(); + let tokens: Vec = (0..seq_len).map(|_| rng.gen_range(0..vocab)).collect(); // Run inference let expected = infer_fn(&tokens)?; @@ -187,13 +185,7 @@ mod tests { tests: Default::default(), }; - ModelArtifact::new( - manifest, - vec![0u8; 4096 * 64], - None, - None, - vec![], - ) + ModelArtifact::new(manifest, vec![0u8; 4096 * 64], None, None, vec![]) } #[test] diff --git a/crates/ruvector-fpga-transformer/src/backend/fpga_daemon.rs b/crates/ruvector-fpga-transformer/src/backend/fpga_daemon.rs index 74407a646..a3158d55c 100644 --- a/crates/ruvector-fpga-transformer/src/backend/fpga_daemon.rs +++ b/crates/ruvector-fpga-transformer/src/backend/fpga_daemon.rs @@ -11,8 +11,8 @@ use std::time::{Duration, Instant}; use crate::artifact::ModelArtifact; use crate::backend::{ - commands, compute_topk, crc32, protocol, read_lock, validate_tokens, write_lock, - BackendStats, RequestFrame, ResponseFrame, TransformerBackend, + commands, compute_topk, crc32, protocol, read_lock, validate_tokens, write_lock, BackendStats, + RequestFrame, ResponseFrame, TransformerBackend, }; use crate::error::{Error, Result}; use crate::types::{ @@ -250,10 +250,7 @@ impl FpgaDaemonBackend { }); } _ => { - return Err(Error::backend(format!( - "Daemon error: status {}", - status - ))); + return Err(Error::backend(format!("Daemon error: status {}", status))); } } @@ -283,7 +280,11 @@ impl FpgaDaemonBackend { } /// Send load model command to daemon - fn send_load_command(&self, stream: &mut dyn ReadWrite, artifact: &ModelArtifact) -> Result<()> { + fn send_load_command( + &self, + stream: &mut dyn ReadWrite, + artifact: &ModelArtifact, + ) -> Result<()> { // Pack artifact let artifact_bytes = crate::artifact::pack::pack_artifact(artifact)?; @@ -437,10 +438,9 @@ impl TransformerBackend for FpgaDaemonBackend { // Check model is loaded locally and validate tokens let model_metadata = read_lock(&self.models, |models| { - models - .get(&req.model) - .map(|m| m.artifact.clone()) - })?.ok_or_else(|| Error::ModelNotFound(req.model))?; + models.get(&req.model).map(|m| m.artifact.clone()) + })? + .ok_or_else(|| Error::ModelNotFound(req.model))?; // Validate tokens against vocabulary validate_tokens(req.tokens, model_metadata.manifest.shape.vocab)?; diff --git a/crates/ruvector-fpga-transformer/src/backend/fpga_pcie.rs b/crates/ruvector-fpga-transformer/src/backend/fpga_pcie.rs index e163c7dd9..25b406025 100644 --- a/crates/ruvector-fpga-transformer/src/backend/fpga_pcie.rs +++ b/crates/ruvector-fpga-transformer/src/backend/fpga_pcie.rs @@ -91,7 +91,9 @@ impl DmaRingBuffer { /// Create a new DMA ring buffer (mock for non-PCIe builds) #[cfg(not(feature = "pcie"))] fn new(_config: &PcieConfig) -> Result { - Err(Error::FeatureNotAvailable("PCIe support not compiled".into())) + Err(Error::FeatureNotAvailable( + "PCIe support not compiled".into(), + )) } /// Create a new DMA ring buffer @@ -221,9 +223,9 @@ pub struct FpgaPcieBackend { /// Cached model metadata struct ModelMetadata { artifact: ModelArtifact, - fpga_slot: u32, // Slot in FPGA memory where model is loaded - weights_offset: u64, // Offset in FPGA DDR where weights are stored - weights_size: usize, // Size of weights in bytes + fpga_slot: u32, // Slot in FPGA memory where model is loaded + weights_offset: u64, // Offset in FPGA DDR where weights are stored + weights_size: usize, // Size of weights in bytes } /// FPGA DDR base offset for model weights @@ -256,21 +258,19 @@ impl FpgaPcieBackend { /// Write inference request to DMA buffer #[cfg(feature = "pcie")] - fn write_request(&self, ring: &mut DmaRingBuffer, slot: usize, req: &InferenceRequest) -> Result<()> { + fn write_request( + &self, + ring: &mut DmaRingBuffer, + slot: usize, + req: &InferenceRequest, + ) -> Result<()> { use crate::backend::{protocol, RequestFrame}; let buffer = ring.request_buffer(slot); let shape = &req.shape; // Write header - let frame = RequestFrame::new( - shape.seq_len, - shape.d_model, - shape.vocab, - &req.model, - 0, - 16, - ); + let frame = RequestFrame::new(shape.seq_len, shape.d_model, shape.vocab, &req.model, 0, 16); let header = frame.to_bytes(); buffer[..protocol::HEADER_SIZE].copy_from_slice(&header); @@ -298,7 +298,12 @@ impl FpgaPcieBackend { /// Read inference response from DMA buffer #[cfg(feature = "pcie")] - fn read_response(&self, ring: &DmaRingBuffer, slot: usize, shape: &crate::types::FixedShape) -> Result<(Vec, u32, u32, GateDecision)> { + fn read_response( + &self, + ring: &DmaRingBuffer, + slot: usize, + shape: &crate::types::FixedShape, + ) -> Result<(Vec, u32, u32, GateDecision)> { use crate::backend::ResponseFrame; let buffer = ring.response_buffer(slot); @@ -308,7 +313,10 @@ impl FpgaPcieBackend { // Check status if response.status != 0 { - return Err(Error::backend(format!("FPGA error: status {}", response.status))); + return Err(Error::backend(format!( + "FPGA error: status {}", + response.status + ))); } // Read logits @@ -322,7 +330,12 @@ impl FpgaPcieBackend { offset += 2; } - Ok((logits, response.cycles, response.latency_ns, response.to_gate_decision())) + Ok(( + logits, + response.cycles, + response.latency_ns, + response.to_gate_decision(), + )) } /// Ring doorbell to notify FPGA of pending request @@ -374,9 +387,10 @@ impl FpgaPcieBackend { // DMA transfer configuration const DMA_CHUNK_SIZE: usize = 64 * 1024; // 64KB per transfer - let ring = self.ring.as_ref().ok_or_else(|| { - Error::FeatureNotAvailable("Ring buffer not initialized".into()) - })?; + let ring = self + .ring + .as_ref() + .ok_or_else(|| Error::FeatureNotAvailable("Ring buffer not initialized".into()))?; // Transfer weights in chunks let mut transferred = 0usize; @@ -439,7 +453,9 @@ impl TransformerBackend for FpgaPcieBackend { #[cfg(not(feature = "pcie"))] { let _ = artifact; - return Err(Error::FeatureNotAvailable("PCIe support not compiled".into())); + return Err(Error::FeatureNotAvailable( + "PCIe support not compiled".into(), + )); } #[cfg(feature = "pcie")] @@ -488,7 +504,9 @@ impl TransformerBackend for FpgaPcieBackend { #[cfg(not(feature = "pcie"))] { let _ = req; - return Err(Error::FeatureNotAvailable("PCIe support not compiled".into())); + return Err(Error::FeatureNotAvailable( + "PCIe support not compiled".into(), + )); } #[cfg(feature = "pcie")] @@ -501,20 +519,22 @@ impl TransformerBackend for FpgaPcieBackend { // Get model metadata let model_artifact = read_lock(&self.models, |models| { models.get(&req.model).map(|m| m.artifact.clone()) - })?.ok_or_else(|| Error::ModelNotFound(req.model))?; + })? + .ok_or_else(|| Error::ModelNotFound(req.model))?; // Validate tokens against vocabulary validate_tokens(req.tokens, model_artifact.manifest.shape.vocab)?; // Get ring buffer - let ring = self.ring.as_ref().ok_or_else(|| { - Error::FeatureNotAvailable("Ring buffer not initialized".into()) - })?; + let ring = self + .ring + .as_ref() + .ok_or_else(|| Error::FeatureNotAvailable("Ring buffer not initialized".into()))?; // Acquire slot - let slot = ring.acquire_slot().ok_or_else(|| { - Error::ResourceExhausted("No DMA slots available".into()) - })?; + let slot = ring + .acquire_slot() + .ok_or_else(|| Error::ResourceExhausted("No DMA slots available".into()))?; // Write request (need mutable access - simplified for now) // In production, this would use proper interior mutability @@ -549,7 +569,8 @@ impl TransformerBackend for FpgaPcieBackend { ); // Update stats - self.total_cycles.fetch_add(cycles as u64, Ordering::Relaxed); + self.total_cycles + .fetch_add(cycles as u64, Ordering::Relaxed); write_lock(&self.stats, |stats| { stats.total_inferences += 1; stats.total_cycles = self.total_cycles.load(Ordering::Relaxed); @@ -569,7 +590,9 @@ impl TransformerBackend for FpgaPcieBackend { fn unload(&self, model: ModelId) -> Result<()> { // Remove from cache and get memory info for deallocation let removed = write_lock(&self.models, |models| { - models.remove(&model).map(|m| (m.weights_offset, m.weights_size)) + models + .remove(&model) + .map(|m| (m.weights_offset, m.weights_size)) })?; if let Some((offset, size)) = removed { diff --git a/crates/ruvector-fpga-transformer/src/backend/mod.rs b/crates/ruvector-fpga-transformer/src/backend/mod.rs index 8caa5b661..b68ddc69b 100644 --- a/crates/ruvector-fpga-transformer/src/backend/mod.rs +++ b/crates/ruvector-fpga-transformer/src/backend/mod.rs @@ -298,7 +298,11 @@ pub fn compute_topk(logits: &[i16], k: usize) -> Vec<(u16, i16)> { // Full sort for small arrays let mut indexed: Vec<(usize, i16)> = logits.iter().cloned().enumerate().collect(); indexed.sort_by(|a, b| b.1.cmp(&a.1)); - indexed.into_iter().take(k).map(|(i, v)| (i as u16, v)).collect() + indexed + .into_iter() + .take(k) + .map(|(i, v)| (i as u16, v)) + .collect() } } @@ -345,7 +349,14 @@ pub fn build_witness( latency_ns: u32, gate_decision: crate::types::GateDecision, ) -> crate::types::WitnessLog { - crate::types::WitnessLog::new(model_hash, quant_hash, backend, cycles, latency_ns, gate_decision) + crate::types::WitnessLog::new( + model_hash, + quant_hash, + backend, + cycles, + latency_ns, + gate_decision, + ) } /// Command types for daemon protocol diff --git a/crates/ruvector-fpga-transformer/src/backend/native_sim.rs b/crates/ruvector-fpga-transformer/src/backend/native_sim.rs index 6a74efd3d..807df6541 100644 --- a/crates/ruvector-fpga-transformer/src/backend/native_sim.rs +++ b/crates/ruvector-fpga-transformer/src/backend/native_sim.rs @@ -116,7 +116,10 @@ impl NativeSimBackend { // Check preflight gate let preflight = self.gate.preflight(gate_hint); if let GateDecision::Skipped { reason } = preflight { - return Ok((vec![0i16; shape.vocab as usize], GateDecision::Skipped { reason })); + return Ok(( + vec![0i16; shape.vocab as usize], + GateDecision::Skipped { reason }, + )); } // Initialize hidden states from embeddings @@ -367,9 +370,8 @@ impl TransformerBackend for NativeSimBackend { req.validate()?; // Get model (with poison handling) - let model = read_lock(&self.models, |models| { - models.get(&req.model).cloned() - })?.ok_or_else(|| Error::ModelNotFound(req.model))?; + let model = read_lock(&self.models, |models| models.get(&req.model).cloned())? + .ok_or_else(|| Error::ModelNotFound(req.model))?; // Validate shape if model.artifact.manifest.shape != req.shape { @@ -405,8 +407,7 @@ impl TransformerBackend for NativeSimBackend { write_lock(&self.stats, |stats| { stats.total_inferences += 1; let n = stats.total_inferences; - stats.avg_latency_ns = - (stats.avg_latency_ns * (n - 1) + latency_ns as u64) / n; + stats.avg_latency_ns = (stats.avg_latency_ns * (n - 1) + latency_ns as u64) / n; match gate_decision { GateDecision::EarlyExit { .. } => stats.early_exits += 1, GateDecision::Skipped { .. } => stats.skipped += 1, diff --git a/crates/ruvector-fpga-transformer/src/backend/wasm_sim.rs b/crates/ruvector-fpga-transformer/src/backend/wasm_sim.rs index fca8f717a..187a184c8 100644 --- a/crates/ruvector-fpga-transformer/src/backend/wasm_sim.rs +++ b/crates/ruvector-fpga-transformer/src/backend/wasm_sim.rs @@ -135,7 +135,8 @@ impl WasmSimBackend { let start = t * d_model; // Simple ReLU-like activation for i in 0..d_model { - hidden[start + i] = hidden[start + i].max(0.0) * 0.99 + hidden[start + i] * 0.01; + hidden[start + i] = + hidden[start + i].max(0.0) * 0.99 + hidden[start + i] * 0.01; } } diff --git a/crates/ruvector-fpga-transformer/src/ffi/c_abi.rs b/crates/ruvector-fpga-transformer/src/ffi/c_abi.rs index 2d84150e7..f376f7e51 100644 --- a/crates/ruvector-fpga-transformer/src/ffi/c_abi.rs +++ b/crates/ruvector-fpga-transformer/src/ffi/c_abi.rs @@ -152,8 +152,8 @@ pub extern "C" fn fpga_infer( let shape = FixedShape::micro(); // Build gate hint - let compute_class = ComputeClass::from_u8(max_compute_class) - .unwrap_or(ComputeClass::Deliberative); + let compute_class = + ComputeClass::from_u8(max_compute_class).unwrap_or(ComputeClass::Deliberative); let gate_hint = GateHint::new(coherence_score, boundary_crossed, compute_class); // Create request diff --git a/crates/ruvector-fpga-transformer/src/ffi/wasm_bindgen.rs b/crates/ruvector-fpga-transformer/src/ffi/wasm_bindgen.rs index 7228c595f..5b28c63d8 100644 --- a/crates/ruvector-fpga-transformer/src/ffi/wasm_bindgen.rs +++ b/crates/ruvector-fpga-transformer/src/ffi/wasm_bindgen.rs @@ -4,10 +4,10 @@ #![cfg(feature = "wasm")] +use js_sys::{Array, Int16Array, Object, Reflect, Uint16Array, Uint8Array}; use wasm_bindgen::prelude::*; -use js_sys::{Array, Object, Reflect, Uint16Array, Uint8Array, Int16Array}; -use crate::artifact::{ModelArtifact, unpack_artifact}; +use crate::artifact::{unpack_artifact, ModelArtifact}; use crate::backend::native_sim::{NativeSimBackend, NativeSimConfig}; use crate::backend::TransformerBackend; use crate::gating::DefaultCoherenceGate; @@ -53,7 +53,9 @@ impl WasmEngine { let artifact = unpack_artifact(artifact_bytes) .map_err(|e| JsValue::from_str(&format!("Failed to unpack artifact: {}", e)))?; - let model_id = self.backend.load(&artifact) + let model_id = self + .backend + .load(&artifact) .map_err(|e| JsValue::from_str(&format!("Failed to load model: {}", e)))?; self.loaded_models.push(model_id); @@ -99,15 +101,17 @@ impl WasmEngine { } // Build gate hint - let compute_class = ComputeClass::from_u8(max_compute_class) - .unwrap_or(ComputeClass::Deliberative); + let compute_class = + ComputeClass::from_u8(max_compute_class).unwrap_or(ComputeClass::Deliberative); let gate_hint = GateHint::new(coherence_score_q, boundary_crossed, compute_class); // Create request let req = InferenceRequest::new(model, shape, tokens, mask, gate_hint); // Run inference - let result = self.backend.infer(req) + let result = self + .backend + .infer(req) .map_err(|e| JsValue::from_str(&format!("Inference failed: {}", e)))?; // Store witness @@ -135,10 +139,26 @@ impl WasmEngine { // Add witness info let witness = Object::new(); - Reflect::set(&witness, &"backend".into(), &format!("{:?}", result.witness.backend).into())?; - Reflect::set(&witness, &"cycles".into(), &JsValue::from(result.witness.cycles))?; - Reflect::set(&witness, &"latency_ns".into(), &JsValue::from(result.witness.latency_ns))?; - Reflect::set(&witness, &"gate_decision".into(), &format!("{:?}", result.witness.gate_decision).into())?; + Reflect::set( + &witness, + &"backend".into(), + &format!("{:?}", result.witness.backend).into(), + )?; + Reflect::set( + &witness, + &"cycles".into(), + &JsValue::from(result.witness.cycles), + )?; + Reflect::set( + &witness, + &"latency_ns".into(), + &JsValue::from(result.witness.latency_ns), + )?; + Reflect::set( + &witness, + &"gate_decision".into(), + &format!("{:?}", result.witness.gate_decision).into(), + )?; Reflect::set(&obj, &"witness".into(), &witness)?; Ok(obj.into()) @@ -179,7 +199,8 @@ impl WasmEngine { id_bytes.copy_from_slice(model_id); let model = ModelId::new(id_bytes); - self.backend.unload(model) + self.backend + .unload(model) .map_err(|e| JsValue::from_str(&format!("Unload failed: {}", e)))?; self.loaded_models.retain(|id| *id != model); @@ -192,11 +213,31 @@ impl WasmEngine { let stats = self.backend.stats(); let obj = Object::new(); - Reflect::set(&obj, &"models_loaded".into(), &JsValue::from(stats.models_loaded as u32))?; - Reflect::set(&obj, &"total_inferences".into(), &JsValue::from(stats.total_inferences as f64))?; - Reflect::set(&obj, &"avg_latency_ns".into(), &JsValue::from(stats.avg_latency_ns as f64))?; - Reflect::set(&obj, &"early_exits".into(), &JsValue::from(stats.early_exits as f64))?; - Reflect::set(&obj, &"skipped".into(), &JsValue::from(stats.skipped as f64))?; + Reflect::set( + &obj, + &"models_loaded".into(), + &JsValue::from(stats.models_loaded as u32), + )?; + Reflect::set( + &obj, + &"total_inferences".into(), + &JsValue::from(stats.total_inferences as f64), + )?; + Reflect::set( + &obj, + &"avg_latency_ns".into(), + &JsValue::from(stats.avg_latency_ns as f64), + )?; + Reflect::set( + &obj, + &"early_exits".into(), + &JsValue::from(stats.early_exits as f64), + )?; + Reflect::set( + &obj, + &"skipped".into(), + &JsValue::from(stats.skipped as f64), + )?; Ok(obj.into()) } @@ -229,7 +270,8 @@ pub fn validate_artifact(artifact_bytes: &[u8]) -> Result { let artifact = unpack_artifact(artifact_bytes) .map_err(|e| JsValue::from_str(&format!("Invalid artifact: {}", e)))?; - artifact.validate() + artifact + .validate() .map_err(|e| JsValue::from_str(&format!("Validation failed: {}", e)))?; let obj = Object::new(); diff --git a/crates/ruvector-fpga-transformer/src/gating/coherence_gate.rs b/crates/ruvector-fpga-transformer/src/gating/coherence_gate.rs index a23a85dd2..5869f673d 100644 --- a/crates/ruvector-fpga-transformer/src/gating/coherence_gate.rs +++ b/crates/ruvector-fpga-transformer/src/gating/coherence_gate.rs @@ -42,7 +42,7 @@ pub struct CoherenceConfig { impl Default for CoherenceConfig { fn default() -> Self { Self { - min_coherence: -256, // -1.0 in Q8.8, very permissive + min_coherence: -256, // -1.0 in Q8.8, very permissive early_exit_threshold: 512, // 2.0 in Q8.8 early_exit_enabled: true, min_layers: 2, @@ -241,7 +241,10 @@ mod tests { // Low coherence should fail let hint = GateHint::new(-512, false, ComputeClass::Deliberative); - assert!(matches!(gate.preflight(&hint), GateDecision::Skipped { .. })); + assert!(matches!( + gate.preflight(&hint), + GateDecision::Skipped { .. } + )); } #[test] @@ -253,7 +256,10 @@ mod tests { // Layer 4 with high signal - should exit let decision = gate.checkpoint(4, 1000); - assert!(matches!(decision, Some(GateDecision::EarlyExit { layer: 4 }))); + assert!(matches!( + decision, + Some(GateDecision::EarlyExit { layer: 4 }) + )); } #[test] @@ -278,7 +284,10 @@ mod tests { // Strict should require positive coherence let hint = GateHint::new(-1, false, ComputeClass::Deliberative); - assert!(matches!(gate.preflight(&hint), GateDecision::Skipped { .. })); + assert!(matches!( + gate.preflight(&hint), + GateDecision::Skipped { .. } + )); } #[test] diff --git a/crates/ruvector-fpga-transformer/src/gating/mod.rs b/crates/ruvector-fpga-transformer/src/gating/mod.rs index b59f5d289..9bf830278 100644 --- a/crates/ruvector-fpga-transformer/src/gating/mod.rs +++ b/crates/ruvector-fpga-transformer/src/gating/mod.rs @@ -6,8 +6,8 @@ pub mod coherence_gate; pub mod policy_gate; -pub use coherence_gate::{CoherenceGate, DefaultCoherenceGate, CoherenceConfig}; -pub use policy_gate::{PolicyGate, DefaultPolicyGate, WritePolicy}; +pub use coherence_gate::{CoherenceConfig, CoherenceGate, DefaultCoherenceGate}; +pub use policy_gate::{DefaultPolicyGate, PolicyGate, WritePolicy}; use crate::types::{GateDecision, GateHint, SkipReason}; use crate::witness::WitnessLog; diff --git a/crates/ruvector-fpga-transformer/src/quant/calib.rs b/crates/ruvector-fpga-transformer/src/quant/calib.rs index c891b9b56..7ee2e874d 100644 --- a/crates/ruvector-fpga-transformer/src/quant/calib.rs +++ b/crates/ruvector-fpga-transformer/src/quant/calib.rs @@ -1,7 +1,7 @@ //! Calibration data for quantization -use serde::{Deserialize, Serialize}; use crate::error::Result; +use serde::{Deserialize, Serialize}; /// Calibration data for a model #[derive(Debug, Clone, Serialize, Deserialize)] @@ -189,9 +189,8 @@ where let mut calibration = CalibrationData::new(method); // Initialize layer stats - let mut layer_stats: Vec = (0..num_layers) - .map(|_| ActivationStats::new()) - .collect(); + let mut layer_stats: Vec = + (0..num_layers).map(|_| ActivationStats::new()).collect(); // Run calibration passes for input in calibration_inputs { diff --git a/crates/ruvector-fpga-transformer/src/quant/lut.rs b/crates/ruvector-fpga-transformer/src/quant/lut.rs index e28052083..87e06424e 100644 --- a/crates/ruvector-fpga-transformer/src/quant/lut.rs +++ b/crates/ruvector-fpga-transformer/src/quant/lut.rs @@ -271,8 +271,14 @@ mod tests { let result_neg = exp_lut(-256); // -1.0 in Q8.8 let result_zero = exp_lut(0); let result_pos = exp_lut(256); // 1.0 in Q8.8 - assert!(result_neg <= result_zero, "exp should be monotonically increasing"); - assert!(result_zero <= result_pos, "exp should be monotonically increasing"); + assert!( + result_neg <= result_zero, + "exp should be monotonically increasing" + ); + assert!( + result_zero <= result_pos, + "exp should be monotonically increasing" + ); } #[test] @@ -280,14 +286,23 @@ mod tests { // sigmoid(0) = 0.5 let result = sigmoid_lut(0); let expected = 32768u16; // 0.5 in Q0.16 - assert!((result as i32 - expected as i32).abs() < 5000, "sigmoid(0) ≈ 0.5"); + assert!( + (result as i32 - expected as i32).abs() < 5000, + "sigmoid(0) ≈ 0.5" + ); // sigmoid is monotonically increasing let result_neg = sigmoid_lut(-1024); let result_zero = sigmoid_lut(0); let result_pos = sigmoid_lut(1024); - assert!(result_neg < result_zero, "sigmoid should be monotonically increasing"); - assert!(result_zero < result_pos, "sigmoid should be monotonically increasing"); + assert!( + result_neg < result_zero, + "sigmoid should be monotonically increasing" + ); + assert!( + result_zero < result_pos, + "sigmoid should be monotonically increasing" + ); } #[test] diff --git a/crates/ruvector-fpga-transformer/src/quant/mod.rs b/crates/ruvector-fpga-transformer/src/quant/mod.rs index 8aa92cd09..8d01773ce 100644 --- a/crates/ruvector-fpga-transformer/src/quant/mod.rs +++ b/crates/ruvector-fpga-transformer/src/quant/mod.rs @@ -2,13 +2,13 @@ //! //! Explicit, reproducible quantization for weights and activations. -pub mod qformat; -pub mod lut; pub mod calib; +pub mod lut; +pub mod qformat; -pub use qformat::{quantize_i8, quantize_i16, dequantize_i8, dequantize_i16}; -pub use lut::{softmax_lut, exp_lut, log_lut}; -pub use calib::{CalibrationData, calibrate_model}; +pub use calib::{calibrate_model, CalibrationData}; +pub use lut::{exp_lut, log_lut, softmax_lut}; +pub use qformat::{dequantize_i16, dequantize_i8, quantize_i16, quantize_i8}; use crate::types::QuantSpec; @@ -67,11 +67,9 @@ pub fn q15_dot(a: &[Q15], b: &[Q15]) -> i32 { /// Saturating fixed-point dot product (prevents overflow for large vectors) #[inline] pub fn q15_dot_saturating(a: &[Q15], b: &[Q15]) -> i32 { - a.iter() - .zip(b.iter()) - .fold(0i32, |acc, (&x, &y)| { - acc.saturating_add((x as i32).saturating_mul(y as i32)) - }) + a.iter().zip(b.iter()).fold(0i32, |acc, (&x, &y)| { + acc.saturating_add((x as i32).saturating_mul(y as i32)) + }) } /// Fixed-point dot product normalized to Q15 diff --git a/crates/ruvector-fpga-transformer/src/quant/qformat.rs b/crates/ruvector-fpga-transformer/src/quant/qformat.rs index dae112710..7bf3447a5 100644 --- a/crates/ruvector-fpga-transformer/src/quant/qformat.rs +++ b/crates/ruvector-fpga-transformer/src/quant/qformat.rs @@ -54,18 +54,12 @@ pub fn dequantize_i8(values: &[u8], spec: &QuantSpec) -> Vec { /// Dequantize i16 values to f32 pub fn dequantize_i16(values: &[i16], scale: f32, zero: f32) -> Vec { - values - .iter() - .map(|&v| v as f32 * scale + zero) - .collect() + values.iter().map(|&v| v as f32 * scale + zero).collect() } /// Symmetric quantization (zero point = 0) pub fn quantize_symmetric_i8(values: &[f32]) -> (Vec, f32) { - let abs_max = values - .iter() - .map(|v| v.abs()) - .fold(0.0f32, f32::max); + let abs_max = values.iter().map(|v| v.abs()).fold(0.0f32, f32::max); if abs_max < f32::EPSILON { return (vec![0i8; values.len()], 1.0); @@ -105,10 +99,7 @@ pub fn quantize_asymmetric_i8(values: &[f32]) -> (Vec, f32, i32) { } /// Per-channel quantization for weights -pub fn quantize_per_channel_i8( - weights: &[f32], - out_channels: usize, -) -> (Vec, Vec) { +pub fn quantize_per_channel_i8(weights: &[f32], out_channels: usize) -> (Vec, Vec) { let in_features = weights.len() / out_channels; let mut quantized = Vec::with_capacity(weights.len()); let mut scales = Vec::with_capacity(out_channels); @@ -127,10 +118,7 @@ pub fn quantize_per_channel_i8( } /// Blocked quantization for hardware efficiency -pub fn quantize_blocked_i8( - values: &[f32], - block_size: usize, -) -> (Vec, Vec, Vec) { +pub fn quantize_blocked_i8(values: &[f32], block_size: usize) -> (Vec, Vec, Vec) { let num_blocks = (values.len() + block_size - 1) / block_size; let mut quantized = Vec::with_capacity(values.len()); let mut scales = Vec::with_capacity(num_blocks); diff --git a/crates/ruvector-fpga-transformer/src/types.rs b/crates/ruvector-fpga-transformer/src/types.rs index 57a34559b..290ca30e4 100644 --- a/crates/ruvector-fpga-transformer/src/types.rs +++ b/crates/ruvector-fpga-transformer/src/types.rs @@ -207,9 +207,9 @@ impl QuantSpec { /// Bytes per weight element pub const fn bytes_per_weight(&self) -> usize { match self.w_bits { - 1 => 1, // Packed 8 per byte, but minimum 1 byte - 2 => 1, // Packed 4 per byte - 4 => 1, // Packed 2 per byte + 1 => 1, // Packed 8 per byte, but minimum 1 byte + 2 => 1, // Packed 4 per byte + 4 => 1, // Packed 2 per byte 8 => 1, 16 => 2, _ => 4, diff --git a/crates/ruvector-fpga-transformer/src/witness/hash.rs b/crates/ruvector-fpga-transformer/src/witness/hash.rs index fd935b688..63b5fd563 100644 --- a/crates/ruvector-fpga-transformer/src/witness/hash.rs +++ b/crates/ruvector-fpga-transformer/src/witness/hash.rs @@ -1,7 +1,7 @@ //! Witness hashing for integrity verification -use sha2::{Digest, Sha256}; use crate::types::WitnessLog; +use sha2::{Digest, Sha256}; /// Compute a hash of the witness log for integrity verification pub fn compute_witness_hash(witness: &WitnessLog) -> [u8; 32] { @@ -75,7 +75,7 @@ impl WitnessProof { /// Create a proof with signature #[cfg(feature = "sign")] pub fn signed(witness: &WitnessLog, secret_key: &[u8; 32]) -> Self { - use ed25519_dalek::{SigningKey, Signer}; + use ed25519_dalek::{Signer, SigningKey}; let hash = compute_witness_hash(witness); let timestamp_ns = std::time::SystemTime::now() @@ -186,14 +186,16 @@ mod tests { #[test] fn test_chain_hash() { let witnesses: Vec = (0..5) - .map(|i| WitnessLog::new( - [i as u8; 32], - [0u8; 32], - BackendKind::NativeSim, - i * 100, - i * 1000, - GateDecision::RanFull, - )) + .map(|i| { + WitnessLog::new( + [i as u8; 32], + [0u8; 32], + BackendKind::NativeSim, + i * 100, + i * 1000, + GateDecision::RanFull, + ) + }) .collect(); let chain_hash1 = compute_chain_hash(&witnesses); diff --git a/crates/ruvector-fpga-transformer/src/witness/mod.rs b/crates/ruvector-fpga-transformer/src/witness/mod.rs index 94bc60629..0a8009c49 100644 --- a/crates/ruvector-fpga-transformer/src/witness/mod.rs +++ b/crates/ruvector-fpga-transformer/src/witness/mod.rs @@ -3,10 +3,10 @@ //! Every inference produces a small witness bundle that records //! what happened and enables verification and replay. -pub mod log; pub mod hash; +pub mod log; // Re-export WitnessLog from types as the canonical location pub use crate::types::WitnessLog; -pub use log::{WitnessBuilder, WitnessAggregator}; pub use hash::{compute_witness_hash, verify_witness_hash}; +pub use log::{WitnessAggregator, WitnessBuilder}; diff --git a/crates/ruvector-learning-wasm/src/lib.rs b/crates/ruvector-learning-wasm/src/lib.rs index 51e70de18..9aac47e57 100644 --- a/crates/ruvector-learning-wasm/src/lib.rs +++ b/crates/ruvector-learning-wasm/src/lib.rs @@ -37,7 +37,7 @@ mod lora; mod operator_scope; mod trajectory; -pub use lora::{LoRAPair, LoRAConfig, MicroLoRAEngine}; +pub use lora::{LoRAConfig, LoRAPair, MicroLoRAEngine}; pub use operator_scope::{OperatorScope, ScopedLoRA}; pub use trajectory::{Trajectory, TrajectoryBuffer, TrajectoryStats}; diff --git a/crates/ruvector-learning-wasm/src/lora.rs b/crates/ruvector-learning-wasm/src/lora.rs index ebb370813..42394f99e 100644 --- a/crates/ruvector-learning-wasm/src/lora.rs +++ b/crates/ruvector-learning-wasm/src/lora.rs @@ -537,7 +537,10 @@ mod tests { for i in 0..256 { diff += (output[i] - input[i]).abs(); } - assert!(diff > 0.0, "Output should differ from input after adaptation"); + assert!( + diff > 0.0, + "Output should differ from input after adaptation" + ); } #[test] diff --git a/crates/ruvector-math-wasm/src/lib.rs b/crates/ruvector-math-wasm/src/lib.rs index 37ee72c71..2213e3369 100644 --- a/crates/ruvector-math-wasm/src/lib.rs +++ b/crates/ruvector-math-wasm/src/lib.rs @@ -4,13 +4,13 @@ //! mathematics in ruvector-math, enabling browser-based vector search //! with optimal transport, information geometry, and product manifolds. -use wasm_bindgen::prelude::*; use ruvector_math::{ - optimal_transport::{SlicedWasserstein, SinkhornSolver, GromovWasserstein}, information_geometry::{FisherInformation, NaturalGradient}, - spherical::SphericalSpace, + optimal_transport::{GromovWasserstein, SinkhornSolver, SlicedWasserstein}, product_manifold::ProductManifold, + spherical::SphericalSpace, }; +use wasm_bindgen::prelude::*; #[wasm_bindgen(start)] pub fn start() { @@ -245,7 +245,12 @@ impl WasmFisherInformation { /// Compute diagonal FIM from gradient samples #[wasm_bindgen(js_name = diagonalFim)] - pub fn diagonal_fim(&self, gradients: &[f64], _num_samples: usize, dim: usize) -> Result, JsError> { + pub fn diagonal_fim( + &self, + gradients: &[f64], + _num_samples: usize, + dim: usize, + ) -> Result, JsError> { let grads = to_points(gradients, dim); self.inner .diagonal_fim(&grads) @@ -254,12 +259,7 @@ impl WasmFisherInformation { /// Compute natural gradient #[wasm_bindgen(js_name = naturalGradient)] - pub fn natural_gradient( - &self, - fim_diag: &[f64], - gradient: &[f64], - damping: f64, - ) -> Vec { + pub fn natural_gradient(&self, fim_diag: &[f64], gradient: &[f64], damping: f64) -> Vec { gradient .iter() .zip(fim_diag.iter()) diff --git a/crates/ruvector-math/benches/information_geometry.rs b/crates/ruvector-math/benches/information_geometry.rs index 37f94b870..4f12a9e0f 100644 --- a/crates/ruvector-math/benches/information_geometry.rs +++ b/crates/ruvector-math/benches/information_geometry.rs @@ -3,7 +3,7 @@ use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use rand::prelude::*; use rand_distr::StandardNormal; -use ruvector_math::information_geometry::{FisherInformation, NaturalGradient, KFACApproximation}; +use ruvector_math::information_geometry::{FisherInformation, KFACApproximation, NaturalGradient}; fn generate_gradients(n: usize, dim: usize, seed: u64) -> Vec> { let mut rng = StdRng::seed_from_u64(seed); @@ -85,7 +85,11 @@ fn bench_kfac(c: &mut Criterion) { .collect(); let gradients: Vec> = (0..batch_size) - .map(|_| (0..output_dim).map(|_| rng.sample(StandardNormal)).collect()) + .map(|_| { + (0..output_dim) + .map(|_| rng.sample(StandardNormal)) + .collect() + }) .collect(); let weight_grad: Vec> = (0..output_dim) @@ -95,16 +99,13 @@ fn bench_kfac(c: &mut Criterion) { group.throughput(Throughput::Elements((input_dim * output_dim) as u64)); // K-FAC update - let mut kfac = ruvector_math::information_geometry::KFACApproximation::new( - &[(input_dim, output_dim)] - ); + let mut kfac = + ruvector_math::information_geometry::KFACApproximation::new(&[(input_dim, output_dim)]); group.bench_function( BenchmarkId::new("kfac_update", format!("{}x{}", input_dim, output_dim)), |b| { - b.iter(|| { - kfac.update_layer(0, black_box(&activations), black_box(&gradients)) - }); + b.iter(|| kfac.update_layer(0, black_box(&activations), black_box(&gradients))); }, ); diff --git a/crates/ruvector-math/benches/optimal_transport.rs b/crates/ruvector-math/benches/optimal_transport.rs index 5bc526ef9..a21ba3074 100644 --- a/crates/ruvector-math/benches/optimal_transport.rs +++ b/crates/ruvector-math/benches/optimal_transport.rs @@ -3,7 +3,7 @@ use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use rand::prelude::*; use rand_distr::StandardNormal; -use ruvector_math::optimal_transport::{SlicedWasserstein, SinkhornSolver, OptimalTransport}; +use ruvector_math::optimal_transport::{OptimalTransport, SinkhornSolver, SlicedWasserstein}; fn generate_points(n: usize, dim: usize, seed: u64) -> Vec> { let mut rng = StdRng::seed_from_u64(seed); diff --git a/crates/ruvector-math/benches/product_manifold.rs b/crates/ruvector-math/benches/product_manifold.rs index a2cdc8ef1..56e90fdff 100644 --- a/crates/ruvector-math/benches/product_manifold.rs +++ b/crates/ruvector-math/benches/product_manifold.rs @@ -33,13 +33,9 @@ fn bench_product_manifold_distance(c: &mut Criterion) { group.throughput(Throughput::Elements(dim as u64)); - group.bench_with_input( - BenchmarkId::new(*name, dim), - &(&x, &y), - |b, (px, py)| { - b.iter(|| manifold.distance(black_box(px), black_box(py))); - }, - ); + group.bench_with_input(BenchmarkId::new(*name, dim), &(&x, &y), |b, (px, py)| { + b.iter(|| manifold.distance(black_box(px), black_box(py))); + }); } group.finish(); diff --git a/crates/ruvector-math/benches/spectral.rs b/crates/ruvector-math/benches/spectral.rs index c46626efd..017881bc2 100644 --- a/crates/ruvector-math/benches/spectral.rs +++ b/crates/ruvector-math/benches/spectral.rs @@ -1,7 +1,7 @@ //! Benchmarks for spectral methods use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; -use ruvector_math::spectral::{ChebyshevPolynomial, ChebyshevExpansion}; +use ruvector_math::spectral::{ChebyshevExpansion, ChebyshevPolynomial}; fn bench_chebyshev_eval(c: &mut Criterion) { let mut group = c.benchmark_group("chebyshev_eval"); diff --git a/crates/ruvector-math/benches/tropical.rs b/crates/ruvector-math/benches/tropical.rs index 0b63f5c7a..d0ee0fd76 100644 --- a/crates/ruvector-math/benches/tropical.rs +++ b/crates/ruvector-math/benches/tropical.rs @@ -2,7 +2,7 @@ use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use rand::prelude::*; -use ruvector_math::tropical::{TropicalMatrix, MinPlusMatrix}; +use ruvector_math::tropical::{MinPlusMatrix, TropicalMatrix}; fn generate_tropical_matrix(n: usize, seed: u64) -> TropicalMatrix { let mut rng = StdRng::seed_from_u64(seed); @@ -41,13 +41,9 @@ fn bench_tropical_matmul(c: &mut Criterion) { let a = generate_tropical_matrix(n, 42); let b = generate_tropical_matrix(n, 43); - group.bench_with_input( - BenchmarkId::new("size", n), - &(&a, &b), - |bench, (a, b)| { - bench.iter(|| a.mul(black_box(b))); - }, - ); + group.bench_with_input(BenchmarkId::new("size", n), &(&a, &b), |bench, (a, b)| { + bench.iter(|| a.mul(black_box(b))); + }); } group.finish(); diff --git a/crates/ruvector-math/src/homology/distance.rs b/crates/ruvector-math/src/homology/distance.rs index 340191a2e..b3e1f51e1 100644 --- a/crates/ruvector-math/src/homology/distance.rs +++ b/crates/ruvector-math/src/homology/distance.rs @@ -237,7 +237,9 @@ impl PersistenceLandscape { if pairs.is_empty() { return Self { landscapes: vec![vec![0.0; resolution]; num_landscapes], - grid: (0..resolution).map(|i| i as f64 / resolution as f64).collect(), + grid: (0..resolution) + .map(|i| i as f64 / resolution as f64) + .collect(), num_landscapes, }; } @@ -311,7 +313,10 @@ impl PersistenceLandscape { /// Get feature vector (flattened landscape) pub fn to_vector(&self) -> Vec { - self.landscapes.iter().flat_map(|l| l.iter().copied()).collect() + self.landscapes + .iter() + .flat_map(|l| l.iter().copied()) + .collect() } } diff --git a/crates/ruvector-math/src/homology/mod.rs b/crates/ruvector-math/src/homology/mod.rs index 8469c3316..491b0b53f 100644 --- a/crates/ruvector-math/src/homology/mod.rs +++ b/crates/ruvector-math/src/homology/mod.rs @@ -23,15 +23,15 @@ //! //! Birth-death pairs form the persistence diagram. -mod simplex; +mod distance; mod filtration; mod persistence; -mod distance; +mod simplex; -pub use simplex::{Simplex, SimplicialComplex}; -pub use filtration::{Filtration, VietorisRips, AlphaComplex}; -pub use persistence::{PersistenceDiagram, PersistentHomology, BirthDeathPair}; pub use distance::{BottleneckDistance, WassersteinDistance}; +pub use filtration::{AlphaComplex, Filtration, VietorisRips}; +pub use persistence::{BirthDeathPair, PersistenceDiagram, PersistentHomology}; +pub use simplex::{Simplex, SimplicialComplex}; /// Betti numbers at a given scale #[derive(Debug, Clone, PartialEq)] @@ -111,7 +111,10 @@ impl PointCloud { /// Create from points pub fn new(points: Vec) -> Self { let ambient_dim = points.first().map(|p| p.dim()).unwrap_or(0); - Self { points, ambient_dim } + Self { + points, + ambient_dim, + } } /// Create from flat array (row-major) @@ -120,7 +123,10 @@ impl PointCloud { .chunks(dim) .map(|chunk| Point::new(chunk.to_vec())) .collect(); - Self { points, ambient_dim: dim } + Self { + points, + ambient_dim: dim, + } } /// Number of points diff --git a/crates/ruvector-math/src/homology/persistence.rs b/crates/ruvector-math/src/homology/persistence.rs index c02be16bb..74488051d 100644 --- a/crates/ruvector-math/src/homology/persistence.rs +++ b/crates/ruvector-math/src/homology/persistence.rs @@ -2,7 +2,7 @@ //! //! Compute birth-death pairs from a filtration using the standard algorithm. -use super::{Filtration, Simplex, BettiNumbers}; +use super::{BettiNumbers, Filtration, Simplex}; use std::collections::{HashMap, HashSet}; /// Birth-death pair in persistence diagram @@ -205,7 +205,11 @@ impl PersistentHomology { // Initialize self.columns = Vec::with_capacity(n); self.birth_times = filtration.simplices.iter().map(|fs| fs.birth).collect(); - self.dimensions = filtration.simplices.iter().map(|fs| fs.simplex.dim()).collect(); + self.dimensions = filtration + .simplices + .iter() + .map(|fs| fs.simplex.dim()) + .collect(); // Build boundary matrix columns for fs in &filtration.simplices { @@ -256,7 +260,9 @@ impl PersistentHomology { /// Get pivot (largest index) of column fn get_pivot(&self, col: usize) -> Option { - self.columns[col].as_ref().and_then(|c| c.iter().max().copied()) + self.columns[col] + .as_ref() + .and_then(|c| c.iter().max().copied()) } /// Add column src to column dst (XOR / mod 2) diff --git a/crates/ruvector-math/src/information_geometry/fisher.rs b/crates/ruvector-math/src/information_geometry/fisher.rs index 9b1ef4c13..6e4a2b438 100644 --- a/crates/ruvector-math/src/information_geometry/fisher.rs +++ b/crates/ruvector-math/src/information_geometry/fisher.rs @@ -213,11 +213,7 @@ impl FisherInformation { } /// Compute natural gradient: F⁻¹ ∇L - pub fn natural_gradient( - &self, - fim: &[Vec], - gradient: &[f64], - ) -> Result> { + pub fn natural_gradient(&self, fim: &[Vec], gradient: &[f64]) -> Result> { let fim_inv = self.invert_fim(fim)?; let n = gradient.len(); diff --git a/crates/ruvector-math/src/information_geometry/kfac.rs b/crates/ruvector-math/src/information_geometry/kfac.rs index 5c10e66c4..2aa2db631 100644 --- a/crates/ruvector-math/src/information_geometry/kfac.rs +++ b/crates/ruvector-math/src/information_geometry/kfac.rs @@ -72,11 +72,7 @@ impl KFACLayer { /// # Arguments /// * `activations` - Pre-activation inputs, shape [batch, input_dim] /// * `gradients` - Post-activation gradients, shape [batch, output_dim] - pub fn update( - &mut self, - activations: &[Vec], - gradients: &[Vec], - ) -> Result<()> { + pub fn update(&mut self, activations: &[Vec], gradients: &[Vec]) -> Result<()> { if activations.is_empty() || gradients.is_empty() { return Err(MathError::empty_input("batch")); } diff --git a/crates/ruvector-math/src/information_geometry/mod.rs b/crates/ruvector-math/src/information_geometry/mod.rs index 51a40b25a..430f898a2 100644 --- a/crates/ruvector-math/src/information_geometry/mod.rs +++ b/crates/ruvector-math/src/information_geometry/mod.rs @@ -22,9 +22,9 @@ //! - Pascanu & Bengio (2013): Natural Gradient Works Efficiently in Learning mod fisher; -mod natural_gradient; mod kfac; +mod natural_gradient; pub use fisher::FisherInformation; -pub use natural_gradient::NaturalGradient; pub use kfac::KFACApproximation; +pub use natural_gradient::NaturalGradient; diff --git a/crates/ruvector-math/src/information_geometry/natural_gradient.rs b/crates/ruvector-math/src/information_geometry/natural_gradient.rs index 61de77390..db6a2bc3e 100644 --- a/crates/ruvector-math/src/information_geometry/natural_gradient.rs +++ b/crates/ruvector-math/src/information_geometry/natural_gradient.rs @@ -15,9 +15,9 @@ //! - **Faster convergence**: 3-5x fewer iterations than SGD/Adam on well-conditioned problems //! - **Better generalization**: Follows geodesics in probability space +use super::FisherInformation; use crate::error::{MathError, Result}; use crate::utils::EPS; -use super::FisherInformation; /// Natural gradient optimizer state #[derive(Debug, Clone)] @@ -157,7 +157,10 @@ impl NaturalGradient { /// Apply update to parameters pub fn apply_update(parameters: &mut [f64], update: &[f64]) -> Result<()> { if parameters.len() != update.len() { - return Err(MathError::dimension_mismatch(parameters.len(), update.len())); + return Err(MathError::dimension_mismatch( + parameters.len(), + update.len(), + )); } for (p, &u) in parameters.iter_mut().zip(update.iter()) { @@ -266,16 +269,14 @@ mod tests { #[test] fn test_natural_gradient_with_fim() { - let mut ng = NaturalGradient::new(0.1).with_diagonal(true).with_damping(0.0); + let mut ng = NaturalGradient::new(0.1) + .with_diagonal(true) + .with_damping(0.0); let gradient = vec![2.0, 4.0]; // Provide gradient samples for FIM estimation - let samples = vec![ - vec![1.0, 0.0], - vec![0.0, 1.0], - vec![1.0, 1.0], - ]; + let samples = vec![vec![1.0, 0.0], vec![0.0, 1.0], vec![1.0, 1.0]]; let update = ng.step(&gradient, Some(&samples)).unwrap(); diff --git a/crates/ruvector-math/src/lib.rs b/crates/ruvector-math/src/lib.rs index 096d23646..934253374 100644 --- a/crates/ruvector-math/src/lib.rs +++ b/crates/ruvector-math/src/lib.rs @@ -93,68 +93,65 @@ extern crate alloc; // Core modules pub mod error; -pub mod optimal_transport; pub mod information_geometry; -pub mod spherical; +pub mod optimal_transport; pub mod product_manifold; +pub mod spherical; pub mod utils; // New theoretical CS modules -pub mod tropical; -pub mod tensor_networks; -pub mod spectral; pub mod homology; pub mod optimization; +pub mod spectral; +pub mod tensor_networks; +pub mod tropical; // Re-exports for convenience - Core pub use error::{MathError, Result}; +pub use information_geometry::{FisherInformation, KFACApproximation, NaturalGradient}; pub use optimal_transport::{ - SlicedWasserstein, SinkhornSolver, GromovWasserstein, - TransportPlan, WassersteinConfig, + GromovWasserstein, SinkhornSolver, SlicedWasserstein, TransportPlan, WassersteinConfig, }; -pub use information_geometry::{ - FisherInformation, NaturalGradient, KFACApproximation, -}; -pub use spherical::{SphericalSpace, SphericalConfig}; -pub use product_manifold::{ProductManifold, ProductManifoldConfig, CurvatureType}; +pub use product_manifold::{CurvatureType, ProductManifold, ProductManifoldConfig}; +pub use spherical::{SphericalConfig, SphericalSpace}; // Re-exports - Tropical Algebra -pub use tropical::{Tropical, TropicalSemiring, TropicalPolynomial, TropicalMatrix}; pub use tropical::{LinearRegionCounter, TropicalNeuralAnalysis}; +pub use tropical::{Tropical, TropicalMatrix, TropicalPolynomial, TropicalSemiring}; // Re-exports - Tensor Networks +pub use tensor_networks::{CPConfig, CPDecomposition, TuckerConfig, TuckerDecomposition}; pub use tensor_networks::{DenseTensor, TensorTrain, TensorTrainConfig}; -pub use tensor_networks::{TuckerDecomposition, TuckerConfig, CPDecomposition, CPConfig}; pub use tensor_networks::{TensorNetwork, TensorNode}; // Re-exports - Spectral Methods -pub use spectral::{ChebyshevPolynomial, ChebyshevExpansion}; -pub use spectral::{SpectralFilter, GraphFilter, FilterType}; -pub use spectral::{SpectralWaveletTransform, GraphWavelet, SpectralClustering}; pub use spectral::ScaledLaplacian; +pub use spectral::{ChebyshevExpansion, ChebyshevPolynomial}; +pub use spectral::{FilterType, GraphFilter, SpectralFilter}; +pub use spectral::{GraphWavelet, SpectralClustering, SpectralWaveletTransform}; // Re-exports - Homology -pub use homology::{PersistenceDiagram, PersistentHomology, BirthDeathPair}; -pub use homology::{Simplex, SimplicialComplex, Filtration, VietorisRips}; +pub use homology::{BirthDeathPair, PersistenceDiagram, PersistentHomology}; pub use homology::{BottleneckDistance, WassersteinDistance as HomologyWasserstein}; +pub use homology::{Filtration, Simplex, SimplicialComplex, VietorisRips}; // Re-exports - Optimization -pub use optimization::{Polynomial, Monomial, Term}; +pub use optimization::{BoundsCertificate, NonnegativityCertificate}; +pub use optimization::{Monomial, Polynomial, Term}; pub use optimization::{SOSDecomposition, SOSResult}; -pub use optimization::{NonnegativityCertificate, BoundsCertificate}; /// Prelude module for convenient imports pub mod prelude { - pub use crate::optimal_transport::*; - pub use crate::information_geometry::*; - pub use crate::spherical::*; - pub use crate::product_manifold::*; pub use crate::error::*; - pub use crate::tropical::*; - pub use crate::tensor_networks::*; - pub use crate::spectral::*; pub use crate::homology::*; + pub use crate::information_geometry::*; + pub use crate::optimal_transport::*; pub use crate::optimization::*; + pub use crate::product_manifold::*; + pub use crate::spectral::*; + pub use crate::spherical::*; + pub use crate::tensor_networks::*; + pub use crate::tropical::*; } #[cfg(test)] diff --git a/crates/ruvector-math/src/optimal_transport/gromov_wasserstein.rs b/crates/ruvector-math/src/optimal_transport/gromov_wasserstein.rs index 9cb35db6d..80d79fcf1 100644 --- a/crates/ruvector-math/src/optimal_transport/gromov_wasserstein.rs +++ b/crates/ruvector-math/src/optimal_transport/gromov_wasserstein.rs @@ -25,9 +25,9 @@ //! 4. Line search and update //! 5. Repeat until convergence +use super::SinkhornSolver; use crate::error::{MathError, Result}; use crate::utils::EPS; -use super::SinkhornSolver; /// Gromov-Wasserstein distance calculator #[derive(Debug, Clone)] @@ -94,11 +94,7 @@ impl GromovWasserstein { /// = ⟨h₁(D_X) ⊗ h₂(D_Y), γ ⊗ γ⟩ - 2⟨D_X γ D_Y^T, γ⟩ /// /// where h₁(a) = a², h₂(b) = b², for squared loss - fn compute_gw_loss( - dist_x: &[Vec], - dist_y: &[Vec], - gamma: &[Vec], - ) -> f64 { + fn compute_gw_loss(dist_x: &[Vec], dist_y: &[Vec], gamma: &[Vec]) -> f64 { let n = dist_x.len(); let m = dist_y.len(); @@ -144,11 +140,7 @@ impl GromovWasserstein { let term3: f64 = 2.0 * (0..n) - .map(|i| { - (0..m) - .map(|j| dx_gamma[i][j] * gamma_dy[i][j]) - .sum::() - }) + .map(|i| (0..m).map(|j| dx_gamma[i][j] * gamma_dy[i][j]).sum::()) .sum::(); term1 + term2 - term3 @@ -225,9 +217,7 @@ impl GromovWasserstein { let dist_y = Self::distance_matrix(target); // Initialize with independent coupling - let mut gamma: Vec> = (0..n) - .map(|_| vec![1.0 / (n * m) as f64; m]) - .collect(); + let mut gamma: Vec> = (0..n).map(|_| vec![1.0 / (n * m) as f64; m]).collect(); let sinkhorn = SinkhornSolver::new(self.regularization, self.inner_iterations); let source_weights = vec![1.0 / n as f64; n]; @@ -272,7 +262,8 @@ impl GromovWasserstein { if best_alpha > 0.0 { for i in 0..n { for j in 0..m { - gamma[i][j] = (1.0 - best_alpha) * gamma[i][j] + best_alpha * direction[i][j]; + gamma[i][j] = + (1.0 - best_alpha) * gamma[i][j] + best_alpha * direction[i][j]; } } } @@ -320,29 +311,28 @@ mod tests { fn test_gw_identical() { let gw = GromovWasserstein::new(0.1); - let points = vec![ - vec![0.0, 0.0], - vec![1.0, 0.0], - vec![0.0, 1.0], - ]; + let points = vec![vec![0.0, 0.0], vec![1.0, 0.0], vec![0.0, 1.0]]; let dist = gw.distance(&points, &points).unwrap(); // GW with entropic regularization won't be exactly 0 for identical structures - assert!(dist < 1.0, "Identical structures should have low GW: {}", dist); + assert!( + dist < 1.0, + "Identical structures should have low GW: {}", + dist + ); } #[test] fn test_gw_scaled() { let gw = GromovWasserstein::new(0.1); - let source = vec![ - vec![0.0, 0.0], - vec![1.0, 0.0], - vec![0.0, 1.0], - ]; + let source = vec![vec![0.0, 0.0], vec![1.0, 0.0], vec![0.0, 1.0]]; // Scale by 2 - structure is preserved! - let target: Vec> = source.iter().map(|p| vec![p[0] * 2.0, p[1] * 2.0]).collect(); + let target: Vec> = source + .iter() + .map(|p| vec![p[0] * 2.0, p[1] * 2.0]) + .collect(); let dist = gw.distance(&source, &target).unwrap(); @@ -356,23 +346,19 @@ mod tests { let gw = GromovWasserstein::new(0.1); // Triangle - let triangle = vec![ - vec![0.0, 0.0], - vec![1.0, 0.0], - vec![0.5, 0.866], - ]; + let triangle = vec![vec![0.0, 0.0], vec![1.0, 0.0], vec![0.5, 0.866]]; // Line - let line = vec![ - vec![0.0, 0.0], - vec![1.0, 0.0], - vec![2.0, 0.0], - ]; + let line = vec![vec![0.0, 0.0], vec![1.0, 0.0], vec![2.0, 0.0]]; let dist = gw.distance(&triangle, &line).unwrap(); // Different structures should have larger GW distance - assert!(dist > 0.1, "Different structures should have high GW: {}", dist); + assert!( + dist > 0.1, + "Different structures should have high GW: {}", + dist + ); } #[test] diff --git a/crates/ruvector-math/src/optimal_transport/mod.rs b/crates/ruvector-math/src/optimal_transport/mod.rs index 6386fe11d..0c61ef8b4 100644 --- a/crates/ruvector-math/src/optimal_transport/mod.rs +++ b/crates/ruvector-math/src/optimal_transport/mod.rs @@ -23,15 +23,15 @@ //! - Time series pattern matching //! - Document similarity via word embedding distributions -mod sliced_wasserstein; -mod sinkhorn; -mod gromov_wasserstein; mod config; +mod gromov_wasserstein; +mod sinkhorn; +mod sliced_wasserstein; -pub use sliced_wasserstein::SlicedWasserstein; -pub use sinkhorn::{SinkhornSolver, TransportPlan}; -pub use gromov_wasserstein::GromovWasserstein; pub use config::WassersteinConfig; +pub use gromov_wasserstein::GromovWasserstein; +pub use sinkhorn::{SinkhornSolver, TransportPlan}; +pub use sliced_wasserstein::SlicedWasserstein; /// Trait for optimal transport distance computations pub trait OptimalTransport { diff --git a/crates/ruvector-math/src/optimal_transport/sinkhorn.rs b/crates/ruvector-math/src/optimal_transport/sinkhorn.rs index e2ae1a764..cce0db25e 100644 --- a/crates/ruvector-math/src/optimal_transport/sinkhorn.rs +++ b/crates/ruvector-math/src/optimal_transport/sinkhorn.rs @@ -344,8 +344,9 @@ impl SinkhornSolver { for i in 0..n { let weight = plan.plan[i][j] * support_size as f64; for d in 0..dim { - displacements[j][d] += - barycenter_weights[dist_idx] * weight * (distribution[i][d] - barycenter[j][d]); + displacements[j][d] += barycenter_weights[dist_idx] + * weight + * (distribution[i][d] - barycenter[j][d]); } } } diff --git a/crates/ruvector-math/src/optimal_transport/sliced_wasserstein.rs b/crates/ruvector-math/src/optimal_transport/sliced_wasserstein.rs index 2f00cd91e..09b23074f 100644 --- a/crates/ruvector-math/src/optimal_transport/sliced_wasserstein.rs +++ b/crates/ruvector-math/src/optimal_transport/sliced_wasserstein.rs @@ -22,10 +22,10 @@ //! - **SIMD-friendly**: Projections are just dot products //! - **Statistically consistent**: Converges to true W2 as L → ∞ +use super::{OptimalTransport, WassersteinConfig}; +use crate::utils::{argsort, EPS}; use rand::prelude::*; use rand_distr::StandardNormal; -use crate::utils::{argsort, EPS}; -use super::{OptimalTransport, WassersteinConfig}; /// Sliced Wasserstein distance calculator #[derive(Debug, Clone)] @@ -81,9 +81,8 @@ impl SlicedWasserstein { (0..self.num_projections) .map(|_| { - let mut direction: Vec = (0..dim) - .map(|_| rng.sample(StandardNormal)) - .collect(); + let mut direction: Vec = + (0..dim).map(|_| rng.sample(StandardNormal)).collect(); // Normalize to unit vector let norm: f64 = direction.iter().map(|&x| x * x).sum::().sqrt(); @@ -224,7 +223,12 @@ impl SlicedWasserstein { } /// Compute 1D Wasserstein via quantile interpolation - fn wasserstein_1d_quantile(&self, sorted_a: &[f64], sorted_b: &[f64], num_samples: usize) -> f64 { + fn wasserstein_1d_quantile( + &self, + sorted_a: &[f64], + sorted_b: &[f64], + num_samples: usize, + ) -> f64 { let mut total = 0.0; for i in 0..num_samples { @@ -459,7 +463,10 @@ mod tests { ]; // Translate by (1, 1) - let target: Vec> = source.iter().map(|p| vec![p[0] + 1.0, p[1] + 1.0]).collect(); + let target: Vec> = source + .iter() + .map(|p| vec![p[0] + 1.0, p[1] + 1.0]) + .collect(); let dist = sw.distance(&source, &target); @@ -484,7 +491,10 @@ mod tests { ]; // Scale by 2 - let target: Vec> = source.iter().map(|p| vec![p[0] * 2.0, p[1] * 2.0]).collect(); + let target: Vec> = source + .iter() + .map(|p| vec![p[0] * 2.0, p[1] * 2.0]) + .collect(); let dist = sw.distance(&source, &target); diff --git a/crates/ruvector-math/src/optimization/certificates.rs b/crates/ruvector-math/src/optimization/certificates.rs index da21ede03..69e010f4a 100644 --- a/crates/ruvector-math/src/optimization/certificates.rs +++ b/crates/ruvector-math/src/optimization/certificates.rs @@ -2,8 +2,8 @@ //! //! Provable guarantees via SOS/SDP methods. -use super::polynomial::{Polynomial, Term, Monomial}; -use super::sos::{SOSChecker, SOSResult, SOSConfig}; +use super::polynomial::{Monomial, Polynomial, Term}; +use super::sos::{SOSChecker, SOSConfig, SOSResult}; /// Certificate that a polynomial is non-negative #[derive(Debug, Clone)] @@ -241,7 +241,12 @@ impl MonotonicityCertificate { .terms() .filter_map(|(m, &c)| { // Find power of var in monomial - let power = m.powers.iter().find(|&&(i, _)| i == var).map(|&(_, p)| p).unwrap_or(0); + let power = m + .powers + .iter() + .find(|&&(i, _)| i == var) + .map(|&(_, p)| p) + .unwrap_or(0); if power == 0 { return None; @@ -254,13 +259,7 @@ impl MonotonicityCertificate { let new_powers: Vec<(usize, usize)> = m .powers .iter() - .map(|&(i, p)| { - if i == var { - (i, p - 1) - } else { - (i, p) - } - }) + .map(|&(i, p)| if i == var { (i, p - 1) } else { (i, p) }) .filter(|&(_, p)| p > 0) .collect(); diff --git a/crates/ruvector-math/src/optimization/mod.rs b/crates/ruvector-math/src/optimization/mod.rs index 6d71279d8..47ad6ce11 100644 --- a/crates/ruvector-math/src/optimization/mod.rs +++ b/crates/ruvector-math/src/optimization/mod.rs @@ -22,15 +22,15 @@ //! //! The SOS condition can be written as a semidefinite program (SDP). -mod polynomial; -mod sos; -mod sdp; mod certificates; +mod polynomial; +mod sdp; +mod sos; -pub use polynomial::{Polynomial, Monomial, Term}; -pub use sos::{SOSDecomposition, SOSConfig, SOSResult}; -pub use sdp::{SDPProblem, SDPSolver, SDPSolution}; -pub use certificates::{NonnegativityCertificate, BoundsCertificate}; +pub use certificates::{BoundsCertificate, NonnegativityCertificate}; +pub use polynomial::{Monomial, Polynomial, Term}; +pub use sdp::{SDPProblem, SDPSolution, SDPSolver}; +pub use sos::{SOSConfig, SOSDecomposition, SOSResult}; /// Degree of a multivariate monomial pub type Degree = usize; diff --git a/crates/ruvector-math/src/optimization/polynomial.rs b/crates/ruvector-math/src/optimization/polynomial.rs index 60eed7c7b..6e6338b63 100644 --- a/crates/ruvector-math/src/optimization/polynomial.rs +++ b/crates/ruvector-math/src/optimization/polynomial.rs @@ -20,7 +20,9 @@ impl Monomial { /// Create single variable monomial x_i pub fn var(i: usize) -> Self { - Self { powers: vec![(i, 1)] } + Self { + powers: vec![(i, 1)], + } } /// Create from powers (will be sorted) @@ -217,7 +219,11 @@ impl Polynomial { // Remove zero terms terms.retain(|_, &mut c| c.abs() >= 1e-15); - Self { terms, degree, num_vars } + Self { + terms, + degree, + num_vars, + } } /// Total degree @@ -252,10 +258,7 @@ impl Polynomial { /// Evaluate at point pub fn eval(&self, x: &[f64]) -> f64 { - self.terms - .iter() - .map(|(m, &c)| c * m.eval(x)) - .sum() + self.terms.iter().map(|(m, &c)| c * m.eval(x)).sum() } /// Add two polynomials @@ -276,7 +279,11 @@ impl Polynomial { .map(|v| v + 1) .unwrap_or(0); - Polynomial { terms, degree, num_vars } + Polynomial { + terms, + degree, + num_vars, + } } /// Subtract polynomials @@ -300,7 +307,11 @@ impl Polynomial { } Polynomial { - terms: self.terms.iter().map(|(m, &c)| (m.clone(), s * c)).collect(), + terms: self + .terms + .iter() + .map(|(m, &c)| (m.clone(), s * c)) + .collect(), degree: self.degree, num_vars: self.num_vars, } @@ -327,7 +338,11 @@ impl Polynomial { .map(|v| v + 1) .unwrap_or(0); - Polynomial { terms, degree, num_vars } + Polynomial { + terms, + degree, + num_vars, + } } /// Square polynomial @@ -419,7 +434,11 @@ impl std::fmt::Display for Polynomial { } let mut sorted: Vec<_> = self.terms.iter().collect(); - sorted.sort_by(|a, b| a.0.degree().cmp(&b.0.degree()).then_with(|| a.0.powers.cmp(&b.0.powers))); + sorted.sort_by(|a, b| { + a.0.degree() + .cmp(&b.0.degree()) + .then_with(|| a.0.powers.cmp(&b.0.powers)) + }); let parts: Vec = sorted .iter() diff --git a/crates/ruvector-math/src/optimization/sdp.rs b/crates/ruvector-math/src/optimization/sdp.rs index 2f1825da1..05e1f97c9 100644 --- a/crates/ruvector-math/src/optimization/sdp.rs +++ b/crates/ruvector-math/src/optimization/sdp.rs @@ -229,7 +229,11 @@ impl SDPSolver { let mut y = vec![0.0; n]; for i in 0..n { for j in 0..n { - let val = if i == j { shift - x[i * n + j] } else { -x[i * n + j] }; + let val = if i == j { + shift - x[i * n + j] + } else { + -x[i * n + j] + }; y[i] += val * v[j]; } } @@ -283,7 +287,9 @@ mod tests { let solution = solver.solve(&problem); // Should find X_{00} = 1, X_{11} close to 0 (or whatever makes X PSD) - assert!(solution.status == SDPStatus::Optimal || solution.status == SDPStatus::MaxIterations); + assert!( + solution.status == SDPStatus::Optimal || solution.status == SDPStatus::MaxIterations + ); } #[test] diff --git a/crates/ruvector-math/src/optimization/sos.rs b/crates/ruvector-math/src/optimization/sos.rs index 8b8f4f11e..04e8facbe 100644 --- a/crates/ruvector-math/src/optimization/sos.rs +++ b/crates/ruvector-math/src/optimization/sos.rs @@ -2,7 +2,7 @@ //! //! Check if a polynomial can be written as a sum of squared polynomials. -use super::polynomial::{Polynomial, Monomial, Term}; +use super::polynomial::{Monomial, Polynomial, Term}; /// SOS decomposition configuration #[derive(Debug, Clone)] @@ -438,7 +438,10 @@ mod tests { } SOSResult::NotSOS { witness } => { // Should not find counterexample for a true SOS polynomial - panic!("(x+y)² incorrectly marked as not SOS with witness {:?}", witness); + panic!( + "(x+y)² incorrectly marked as not SOS with witness {:?}", + witness + ); } } } diff --git a/crates/ruvector-math/src/product_manifold/config.rs b/crates/ruvector-math/src/product_manifold/config.rs index e9a555a3e..34a4b39da 100644 --- a/crates/ruvector-math/src/product_manifold/config.rs +++ b/crates/ruvector-math/src/product_manifold/config.rs @@ -159,7 +159,13 @@ impl ProductManifoldConfig { } /// Get slice ranges for each component - pub fn component_ranges(&self) -> (std::ops::Range, std::ops::Range, std::ops::Range) { + pub fn component_ranges( + &self, + ) -> ( + std::ops::Range, + std::ops::Range, + std::ops::Range, + ) { let e_end = self.euclidean_dim; let h_end = e_end + self.hyperbolic_dim; let s_end = h_end + self.spherical_dim; diff --git a/crates/ruvector-math/src/product_manifold/manifold.rs b/crates/ruvector-math/src/product_manifold/manifold.rs index 8cc3aea3a..0158509f0 100644 --- a/crates/ruvector-math/src/product_manifold/manifold.rs +++ b/crates/ruvector-math/src/product_manifold/manifold.rs @@ -1,9 +1,9 @@ //! Product manifold implementation +use super::config::ProductManifoldConfig; use crate::error::{MathError, Result}; use crate::spherical::SphericalSpace; use crate::utils::{dot, norm, EPS}; -use super::config::ProductManifoldConfig; /// Product manifold: M = E^e × H^h × S^s #[derive(Debug, Clone)] @@ -442,8 +442,7 @@ impl ProductManifold { let scale = theta / theta.sin(); - Ok(x - .iter() + Ok(x.iter() .zip(y.iter()) .map(|(&xi, &yi)| scale * (yi - cos_theta * xi)) .collect()) diff --git a/crates/ruvector-math/src/product_manifold/mod.rs b/crates/ruvector-math/src/product_manifold/mod.rs index 8137dbfcd..dd394ed32 100644 --- a/crates/ruvector-math/src/product_manifold/mod.rs +++ b/crates/ruvector-math/src/product_manifold/mod.rs @@ -22,7 +22,7 @@ mod config; mod manifold; mod operations; -pub use config::{ProductManifoldConfig, CurvatureType}; +pub use config::{CurvatureType, ProductManifoldConfig}; pub use manifold::ProductManifold; // Re-export batch operations (used internally by ProductManifold impl) diff --git a/crates/ruvector-math/src/product_manifold/operations.rs b/crates/ruvector-math/src/product_manifold/operations.rs index 41837451b..bf9f65e71 100644 --- a/crates/ruvector-math/src/product_manifold/operations.rs +++ b/crates/ruvector-math/src/product_manifold/operations.rs @@ -1,8 +1,8 @@ //! Additional product manifold operations +use super::ProductManifold; use crate::error::{MathError, Result}; use crate::utils::{norm, EPS}; -use super::ProductManifold; #[cfg(feature = "parallel")] use rayon::prelude::*; @@ -27,7 +27,11 @@ impl ProductManifold { /// Sequential pairwise distance computation #[inline] - fn pairwise_distances_sequential(&self, points: &[Vec], n: usize) -> Result>> { + fn pairwise_distances_sequential( + &self, + points: &[Vec], + n: usize, + ) -> Result>> { let mut distances = vec![vec![0.0; n]; n]; for i in 0..n { @@ -83,7 +87,12 @@ impl ProductManifold { /// Sequential k-nearest neighbors #[inline] - fn knn_sequential(&self, query: &[f64], points: &[Vec], k: usize) -> Result> { + fn knn_sequential( + &self, + query: &[f64], + points: &[Vec], + k: usize, + ) -> Result> { let mut distances: Vec<(usize, f64)> = points .iter() .enumerate() @@ -91,7 +100,8 @@ impl ProductManifold { .collect(); // Use sort_unstable_by for better performance - distances.sort_unstable_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal)); + distances + .sort_unstable_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal)); distances.truncate(k); Ok(distances) @@ -99,7 +109,12 @@ impl ProductManifold { /// Parallel k-nearest neighbors using rayon #[cfg(feature = "parallel")] - fn knn_parallel(&self, query: &[f64], points: &[Vec], k: usize) -> Result> { + fn knn_parallel( + &self, + query: &[f64], + points: &[Vec], + k: usize, + ) -> Result> { let mut distances: Vec<(usize, f64)> = points .par_iter() .enumerate() @@ -107,7 +122,8 @@ impl ProductManifold { .collect(); // Use sort_unstable_by for better performance - distances.sort_unstable_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal)); + distances + .sort_unstable_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal)); distances.truncate(k); Ok(distances) @@ -130,12 +146,7 @@ impl ProductManifold { } /// Sample points along geodesic - pub fn geodesic_path( - &self, - x: &[f64], - y: &[f64], - num_points: usize, - ) -> Result>> { + pub fn geodesic_path(&self, x: &[f64], y: &[f64], num_points: usize) -> Result>> { let mut path = Vec::with_capacity(num_points); for i in 0..num_points { @@ -239,8 +250,7 @@ impl ProductManifold { let result: Vec = (0..x.len()) .map(|i| { let v_perp = v[i] - v_u * u[i] - v_x * x[i]; - v_perp - + v_u * (-theta.sin() * x[i] + theta.cos() * u[i]) + v_perp + v_u * (-theta.sin() * x[i] + theta.cos() * u[i]) - v_x * (theta.cos() * x[i] + theta.sin() * u[i]) }) .collect(); diff --git a/crates/ruvector-math/src/spectral/chebyshev.rs b/crates/ruvector-math/src/spectral/chebyshev.rs index 1049db67c..4e3a45c5c 100644 --- a/crates/ruvector-math/src/spectral/chebyshev.rs +++ b/crates/ruvector-math/src/spectral/chebyshev.rs @@ -134,12 +134,15 @@ impl ChebyshevExpansion { /// Approximate exp(-t*x) for heat kernel (x in [0, 2]) /// Maps [0, 2] to [-1, 1] via x' = x - 1 pub fn heat_kernel(t: f64, degree: usize) -> Self { - Self::from_function(|x| { - let exponent = -t * (x + 1.0); - // Clamp to prevent overflow (exp(709) ≈ max f64, exp(-745) ≈ 0) - let clamped = exponent.clamp(-700.0, 700.0); - clamped.exp() - }, degree) + Self::from_function( + |x| { + let exponent = -t * (x + 1.0); + // Clamp to prevent overflow (exp(709) ≈ max f64, exp(-745) ≈ 0) + let clamped = exponent.clamp(-700.0, 700.0); + clamped.exp() + }, + degree, + ) } /// Approximate low-pass filter: 1 if λ < cutoff, 0 otherwise @@ -245,7 +248,9 @@ impl ChebyshevExpansion { d_coeffs[0] *= 0.5; } - Self { coefficients: d_coeffs } + Self { + coefficients: d_coeffs, + } } } diff --git a/crates/ruvector-math/src/spectral/clustering.rs b/crates/ruvector-math/src/spectral/clustering.rs index 71ecd7f0c..65b4eac7e 100644 --- a/crates/ruvector-math/src/spectral/clustering.rs +++ b/crates/ruvector-math/src/spectral/clustering.rs @@ -117,7 +117,10 @@ impl SpectralClustering { let fiedler = self.compute_fiedler(laplacian); // Partition by sign - let assignments: Vec = fiedler.iter().map(|&v| if v >= 0.0 { 0 } else { 1 }).collect(); + let assignments: Vec = fiedler + .iter() + .map(|&v| if v >= 0.0 { 0 } else { 1 }) + .collect(); ClusteringResult { assignments, @@ -138,7 +141,8 @@ impl SpectralClustering { .map(|i| { (0..n) .map(|j| { - let x = ((j * 2654435769 + i * 1103515245 + self.config.seed as usize) as f64 + let x = ((j * 2654435769 + i * 1103515245 + self.config.seed as usize) + as f64 / 4294967296.0) * 2.0 - 1.0; diff --git a/crates/ruvector-math/src/spectral/graph_filter.rs b/crates/ruvector-math/src/spectral/graph_filter.rs index 255434ab7..8eb176163 100644 --- a/crates/ruvector-math/src/spectral/graph_filter.rs +++ b/crates/ruvector-math/src/spectral/graph_filter.rs @@ -207,7 +207,11 @@ impl GraphFilter { /// Compute filter energy: x^T h(L) x pub fn energy(&self, signal: &[f64]) -> f64 { let filtered = self.apply(signal); - signal.iter().zip(filtered.iter()).map(|(&x, &y)| x * y).sum() + signal + .iter() + .zip(filtered.iter()) + .map(|(&x, &y)| x * y) + .sum() } /// Get estimated spectral range @@ -253,11 +257,7 @@ mod tests { fn simple_graph() -> (Vec, usize) { // Triangle graph: complete K_3 - let adj = vec![ - 0.0, 1.0, 1.0, - 1.0, 0.0, 1.0, - 1.0, 1.0, 0.0, - ]; + let adj = vec![0.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0]; (adj, 3) } diff --git a/crates/ruvector-math/src/spectral/mod.rs b/crates/ruvector-math/src/spectral/mod.rs index 52d7d5636..9b32ce0b6 100644 --- a/crates/ruvector-math/src/spectral/mod.rs +++ b/crates/ruvector-math/src/spectral/mod.rs @@ -27,14 +27,14 @@ //! This recurrence enables O(K) evaluation of degree-K polynomial filters. mod chebyshev; +mod clustering; mod graph_filter; mod wavelets; -mod clustering; -pub use chebyshev::{ChebyshevPolynomial, ChebyshevExpansion}; -pub use graph_filter::{SpectralFilter, FilterType, GraphFilter}; -pub use wavelets::{GraphWavelet, WaveletScale, SpectralWaveletTransform}; -pub use clustering::{SpectralClustering, ClusteringConfig}; +pub use chebyshev::{ChebyshevExpansion, ChebyshevPolynomial}; +pub use clustering::{ClusteringConfig, SpectralClustering}; +pub use graph_filter::{FilterType, GraphFilter, SpectralFilter}; +pub use wavelets::{GraphWavelet, SpectralWaveletTransform, WaveletScale}; /// Scaled Laplacian for Chebyshev approximation /// L_scaled = 2L/λ_max - I (eigenvalues in [-1, 1]) @@ -211,11 +211,7 @@ mod tests { #[test] fn test_scaled_laplacian() { // Simple 3-node path graph: 0 -- 1 -- 2 - let adj = vec![ - 0.0, 1.0, 0.0, - 1.0, 0.0, 1.0, - 0.0, 1.0, 0.0, - ]; + let adj = vec![0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0]; let laplacian = ScaledLaplacian::from_adjacency(&adj, 3); diff --git a/crates/ruvector-math/src/spectral/wavelets.rs b/crates/ruvector-math/src/spectral/wavelets.rs index 4e33c0ccc..e42e2b52f 100644 --- a/crates/ruvector-math/src/spectral/wavelets.rs +++ b/crates/ruvector-math/src/spectral/wavelets.rs @@ -63,11 +63,7 @@ pub struct GraphWavelet { impl GraphWavelet { /// Compute wavelet centered at vertex - pub fn at_vertex( - laplacian: &ScaledLaplacian, - scale: &WaveletScale, - center: usize, - ) -> Self { + pub fn at_vertex(laplacian: &ScaledLaplacian, scale: &WaveletScale, center: usize) -> Self { let n = laplacian.n; // Delta function at center @@ -211,7 +207,11 @@ impl SpectralWaveletTransform { } /// Apply Chebyshev filter to signal using recurrence -fn apply_filter(laplacian: &ScaledLaplacian, filter: &ChebyshevExpansion, signal: &[f64]) -> Vec { +fn apply_filter( + laplacian: &ScaledLaplacian, + filter: &ChebyshevExpansion, + signal: &[f64], +) -> Vec { let n = laplacian.n; let coeffs = &filter.coefficients; diff --git a/crates/ruvector-math/src/spherical/mod.rs b/crates/ruvector-math/src/spherical/mod.rs index 147e31b1c..a3e1b3e36 100644 --- a/crates/ruvector-math/src/spherical/mod.rs +++ b/crates/ruvector-math/src/spherical/mod.rs @@ -17,7 +17,7 @@ //! - Fréchet mean: Spherical centroid use crate::error::{MathError, Result}; -use crate::utils::{dot, normalize, norm, EPS}; +use crate::utils::{dot, norm, normalize, EPS}; /// Configuration for spherical operations #[derive(Debug, Clone)] @@ -212,8 +212,7 @@ impl SphericalSpace { let result: Vec = (0..self.dim) .map(|i| { let v_perp = v[i] - v_u * u[i] - dot(v, x) * x[i]; - v_perp - + v_u * (-theta.sin() * x[i] + theta.cos() * u[i]) + v_perp + v_u * (-theta.sin() * x[i] + theta.cos() * u[i]) - dot(v, x) * (theta.cos() * x[i] + theta.sin() * u[i]) }) .collect(); @@ -308,9 +307,7 @@ impl SphericalSpace { pub fn sample_uniform(&self, rng: &mut impl rand::Rng) -> Vec { use rand_distr::{Distribution, StandardNormal}; - let point: Vec = (0..self.dim) - .map(|_| StandardNormal.sample(rng)) - .collect(); + let point: Vec = (0..self.dim).map(|_| StandardNormal.sample(rng)).collect(); normalize(&point) } diff --git a/crates/ruvector-math/src/tensor_networks/contraction.rs b/crates/ruvector-math/src/tensor_networks/contraction.rs index ac40e5649..a69821265 100644 --- a/crates/ruvector-math/src/tensor_networks/contraction.rs +++ b/crates/ruvector-math/src/tensor_networks/contraction.rs @@ -24,7 +24,12 @@ impl TensorNode { assert_eq!(data.len(), expected_size); assert_eq!(leg_dims.len(), leg_labels.len()); - Self { id, data, leg_dims, leg_labels } + Self { + id, + data, + leg_dims, + leg_labels, + } } /// Number of legs @@ -50,14 +55,23 @@ pub struct TensorNetwork { impl TensorNetwork { /// Create empty network pub fn new() -> Self { - Self { nodes: Vec::new(), next_id: 0 } + Self { + nodes: Vec::new(), + next_id: 0, + } } /// Add a tensor node - pub fn add_node(&mut self, data: Vec, leg_dims: Vec, leg_labels: Vec) -> usize { + pub fn add_node( + &mut self, + data: Vec, + leg_dims: Vec, + leg_labels: Vec, + ) -> usize { let id = self.next_id; self.next_id += 1; - self.nodes.push(TensorNode::new(id, data, leg_dims, leg_labels)); + self.nodes + .push(TensorNode::new(id, data, leg_dims, leg_labels)); id } @@ -104,7 +118,8 @@ impl TensorNetwork { let new_id = self.next_id; self.next_id += 1; - self.nodes.push(TensorNode::new(new_id, result.0, result.1, result.2)); + self.nodes + .push(TensorNode::new(new_id, result.0, result.1, result.2)); Some(new_id) } @@ -131,7 +146,8 @@ impl TensorNetwork { let new_id = self.next_id; self.next_id += 1; - self.nodes.push(TensorNode::new(new_id, new_data, new_dims, new_labels)); + self.nodes + .push(TensorNode::new(new_id, new_data, new_dims, new_labels)); Some(new_id) } @@ -187,21 +203,35 @@ fn contract_tensors( let contracted1: Vec = contract_pairs.iter().map(|p| p.0).collect(); let contracted2: Vec = contract_pairs.iter().map(|p| p.1).collect(); - for (i, (dim, label)) in node1.leg_dims.iter().zip(node1.leg_labels.iter()).enumerate() { + for (i, (dim, label)) in node1 + .leg_dims + .iter() + .zip(node1.leg_labels.iter()) + .enumerate() + { if !contracted1.contains(&i) { out_dims.push(*dim); out_labels.push(label.clone()); } } - for (i, (dim, label)) in node2.leg_dims.iter().zip(node2.leg_labels.iter()).enumerate() { + for (i, (dim, label)) in node2 + .leg_dims + .iter() + .zip(node2.leg_labels.iter()) + .enumerate() + { if !contracted2.contains(&i) { out_dims.push(*dim); out_labels.push(label.clone()); } } - let out_size: usize = if out_dims.is_empty() { 1 } else { out_dims.iter().product() }; + let out_size: usize = if out_dims.is_empty() { + 1 + } else { + out_dims.iter().product() + }; let mut out_data = vec![0.0; out_size]; // Contract by enumeration @@ -217,8 +247,13 @@ fn contract_tensors( for out_flat in 0..out_size { // Map to input indices // Sum over contracted indices - let contract_sizes: Vec = contract_pairs.iter().map(|p| node1.leg_dims[p.0]).collect(); - let contract_total: usize = if contract_sizes.is_empty() { 1 } else { contract_sizes.iter().product() }; + let contract_sizes: Vec = + contract_pairs.iter().map(|p| node1.leg_dims[p.0]).collect(); + let contract_total: usize = if contract_sizes.is_empty() { + 1 + } else { + contract_sizes.iter().product() + }; let mut sum = 0.0; @@ -244,7 +279,8 @@ fn contract_tensors( for i in 0..node1.num_legs() { if !contracted1.contains(&i) { if free1_pos < out_dims.len() { - idx1[i] = (out_idx_copy / out_strides.get(free1_pos).unwrap_or(&1)) % node1.leg_dims[i]; + idx1[i] = (out_idx_copy / out_strides.get(free1_pos).unwrap_or(&1)) + % node1.leg_dims[i]; } free1_pos += 1; } @@ -254,7 +290,8 @@ fn contract_tensors( if !contracted2.contains(&i) { let pos = (node1.num_legs() - contracted1.len()) + free2_pos; if pos < out_dims.len() { - idx2[i] = (out_flat / out_strides.get(pos).unwrap_or(&1)) % node2.leg_dims[i]; + idx2[i] = + (out_flat / out_strides.get(pos).unwrap_or(&1)) % node2.leg_dims[i]; } free2_pos += 1; } @@ -264,7 +301,8 @@ fn contract_tensors( let lin1: usize = idx1.iter().zip(strides1.iter()).map(|(i, s)| i * s).sum(); let lin2: usize = idx2.iter().zip(strides2.iter()).map(|(i, s)| i * s).sum(); - sum += node1.data[lin1.min(node1.data.len() - 1)] * node2.data[lin2.min(node2.data.len() - 1)]; + sum += node1.data[lin1.min(node1.data.len() - 1)] + * node2.data[lin2.min(node2.data.len() - 1)]; } out_data[out_flat] = sum; @@ -408,18 +446,10 @@ mod tests { let mut network = TensorNetwork::new(); // v1 = [1, 2, 3] - let id1 = network.add_node( - vec![1.0, 2.0, 3.0], - vec![3], - vec!["i".into()], - ); + let id1 = network.add_node(vec![1.0, 2.0, 3.0], vec![3], vec!["i".into()]); // v2 = [1, 1, 1] - let id2 = network.add_node( - vec![1.0, 1.0, 1.0], - vec![3], - vec!["i".into()], - ); + let id2 = network.add_node(vec![1.0, 1.0, 1.0], vec![3], vec!["i".into()]); let result_id = network.contract(id1, id2).unwrap(); let result = network.get_node(result_id).unwrap(); diff --git a/crates/ruvector-math/src/tensor_networks/cp_decomposition.rs b/crates/ruvector-math/src/tensor_networks/cp_decomposition.rs index 79c1036b1..c988609de 100644 --- a/crates/ruvector-math/src/tensor_networks/cp_decomposition.rs +++ b/crates/ruvector-math/src/tensor_networks/cp_decomposition.rs @@ -48,13 +48,18 @@ impl CPDecomposition { let r = config.rank; // Initialize factors randomly - let mut factors: Vec> = tensor.shape.iter() + let mut factors: Vec> = tensor + .shape + .iter() .enumerate() .map(|(k, &n_k)| { - (0..n_k * r).map(|i| { - let x = ((i * 2654435769 + k * 1103515245) as f64 / 4294967296.0) * 2.0 - 1.0; - x - }).collect() + (0..n_k * r) + .map(|i| { + let x = + ((i * 2654435769 + k * 1103515245) as f64 / 4294967296.0) * 2.0 - 1.0; + x + }) + .collect() }) .collect(); diff --git a/crates/ruvector-math/src/tensor_networks/mod.rs b/crates/ruvector-math/src/tensor_networks/mod.rs index a3db606d9..e75779476 100644 --- a/crates/ruvector-math/src/tensor_networks/mod.rs +++ b/crates/ruvector-math/src/tensor_networks/mod.rs @@ -21,15 +21,15 @@ //! - Attention mechanism compression //! - Scientific computing +mod contraction; +mod cp_decomposition; mod tensor_train; mod tucker; -mod cp_decomposition; -mod contraction; -pub use tensor_train::{TensorTrain, TTCore, TensorTrainConfig}; -pub use tucker::{TuckerDecomposition, TuckerConfig}; -pub use cp_decomposition::{CPDecomposition, CPConfig}; -pub use contraction::{TensorNetwork, TensorNode, NetworkContraction}; +pub use contraction::{NetworkContraction, TensorNetwork, TensorNode}; +pub use cp_decomposition::{CPConfig, CPDecomposition}; +pub use tensor_train::{TTCore, TensorTrain, TensorTrainConfig}; +pub use tucker::{TuckerConfig, TuckerDecomposition}; /// Dense tensor for input/output #[derive(Debug, Clone)] @@ -51,13 +51,19 @@ impl DenseTensor { /// Create zeros tensor pub fn zeros(shape: Vec) -> Self { let size: usize = shape.iter().product(); - Self { data: vec![0.0; size], shape } + Self { + data: vec![0.0; size], + shape, + } } /// Create ones tensor pub fn ones(shape: Vec) -> Self { let size: usize = shape.iter().product(); - Self { data: vec![1.0; size], shape } + Self { + data: vec![1.0; size], + shape, + } } /// Create random tensor @@ -111,7 +117,10 @@ impl DenseTensor { pub fn reshape(&self, new_shape: Vec) -> Self { let new_size: usize = new_shape.iter().product(); assert_eq!(self.data.len(), new_size, "New shape must have same size"); - Self { data: self.data.clone(), shape: new_shape } + Self { + data: self.data.clone(), + shape: new_shape, + } } } diff --git a/crates/ruvector-math/src/tensor_networks/tensor_train.rs b/crates/ruvector-math/src/tensor_networks/tensor_train.rs index 423f4aa6a..bbbf022bc 100644 --- a/crates/ruvector-math/src/tensor_networks/tensor_train.rs +++ b/crates/ruvector-math/src/tensor_networks/tensor_train.rs @@ -50,7 +50,12 @@ impl TTCore { /// Create new TT-core pub fn new(data: Vec, rank_left: usize, mode_size: usize, rank_right: usize) -> Self { assert_eq!(data.len(), rank_left * mode_size * rank_right); - Self { data, rank_left, mode_size, rank_right } + Self { + data, + rank_left, + mode_size, + rank_right, + } } /// Create zeros core @@ -113,7 +118,11 @@ impl TensorTrain { ranks.push(core.rank_right); } - Self { cores, shape, ranks } + Self { + cores, + shape, + ranks, + } } /// Create rank-1 TT from vectors @@ -252,8 +261,16 @@ impl TensorTrain { let c1 = &self.cores[k]; let c2 = &other.cores[k]; - let new_rl = if k == 0 { 1 } else { c1.rank_left + c2.rank_left }; - let new_rr = if k == self.order() - 1 { 1 } else { c1.rank_right + c2.rank_right }; + let new_rl = if k == 0 { + 1 + } else { + c1.rank_left + c2.rank_left + }; + let new_rr = if k == self.order() - 1 { + 1 + } else { + c1.rank_right + c2.rank_right + }; let n = c1.mode_size; let mut new_data = vec![0.0; new_rl * n * new_rr]; @@ -285,7 +302,12 @@ impl TensorTrain { } for rl2 in 0..c2.rank_left { for rr2 in 0..c2.rank_right { - new_core.set(c1.rank_left + rl2, i, c1.rank_right + rr2, c2.get(rl2, i, rr2)); + new_core.set( + c1.rank_left + rl2, + i, + c1.rank_right + rr2, + c2.get(rl2, i, rr2), + ); } } } @@ -359,7 +381,12 @@ impl TensorTrain { /// Simple truncated SVD using power iteration /// Returns (U, S, Vt, rank) -fn simple_svd(a: &[f64], rows: usize, cols: usize, config: &TensorTrainConfig) -> (Vec, Vec, Vec, usize) { +fn simple_svd( + a: &[f64], + rows: usize, + cols: usize, + config: &TensorTrainConfig, +) -> (Vec, Vec, Vec, usize) { let max_rank = if config.max_rank > 0 { config.max_rank.min(rows).min(cols) } else { @@ -397,9 +424,16 @@ fn simple_svd(a: &[f64], rows: usize, cols: usize, config: &TensorTrainConfig) - } /// Power iteration for largest singular value -fn power_iteration(a: &[f64], rows: usize, cols: usize, max_iter: usize) -> (f64, Vec, Vec) { +fn power_iteration( + a: &[f64], + rows: usize, + cols: usize, + max_iter: usize, +) -> (f64, Vec, Vec) { // Initialize random v - let mut v: Vec = (0..cols).map(|i| ((i * 2654435769) as f64 / 4294967296.0) * 2.0 - 1.0).collect(); + let mut v: Vec = (0..cols) + .map(|i| ((i * 2654435769) as f64 / 4294967296.0) * 2.0 - 1.0) + .collect(); normalize(&mut v); let mut u = vec![0.0; rows]; @@ -482,7 +516,10 @@ mod tests { // Check reconstruction let reconstructed = tt.to_dense(); - let error: f64 = tensor.data.iter().zip(reconstructed.data.iter()) + let error: f64 = tensor + .data + .iter() + .zip(reconstructed.data.iter()) .map(|(a, b)| (a - b).powi(2)) .sum::() .sqrt(); diff --git a/crates/ruvector-math/src/tensor_networks/tucker.rs b/crates/ruvector-math/src/tensor_networks/tucker.rs index 5cd3f7b3b..86a85a6f2 100644 --- a/crates/ruvector-math/src/tensor_networks/tucker.rs +++ b/crates/ruvector-math/src/tensor_networks/tucker.rs @@ -99,7 +99,9 @@ impl TuckerDecomposition { pub fn compression_ratio(&self) -> f64 { let original: usize = self.shape.iter().product(); let core_size: usize = self.core_shape.iter().product(); - let factor_size: usize = self.factors.iter() + let factor_size: usize = self + .factors + .iter() .enumerate() .map(|(k, f)| self.shape[k] * self.core_shape[k]) .sum(); @@ -112,7 +114,10 @@ impl TuckerDecomposition { fn mode_k_unfold(tensor: &DenseTensor, k: usize) -> Vec { let d = tensor.order(); let n_k = tensor.shape[k]; - let cols: usize = tensor.shape.iter().enumerate() + let cols: usize = tensor + .shape + .iter() + .enumerate() .filter(|&(i, _)| i != k) .map(|(_, &s)| s) .product(); @@ -153,16 +158,24 @@ fn mode_k_unfold(tensor: &DenseTensor, k: usize) -> Vec { } /// Compute left singular vectors via power iteration -fn compute_left_singular_vectors(a: &[f64], rows: usize, cols: usize, rank: usize, max_iters: usize) -> Vec { +fn compute_left_singular_vectors( + a: &[f64], + rows: usize, + cols: usize, + rank: usize, + max_iters: usize, +) -> Vec { let mut u = vec![0.0; rows * rank]; // Compute A * A^T iteratively for r in 0..rank { // Initialize random vector - let mut v: Vec = (0..rows).map(|i| { - let x = ((i * 2654435769 + r * 1103515245) as f64 / 4294967296.0) * 2.0 - 1.0; - x - }).collect(); + let mut v: Vec = (0..rows) + .map(|i| { + let x = ((i * 2654435769 + r * 1103515245) as f64 / 4294967296.0) * 2.0 - 1.0; + x + }) + .collect(); normalize(&mut v); // Power iteration @@ -233,7 +246,14 @@ fn compute_core(tensor: &DenseTensor, factors: &[Vec], core_shape: &[usize] } /// Apply mode-k product: result[...,:,...] = A[...,:,...] * U (n_k -> r_k) -fn apply_mode_product_transpose(data: &[f64], shape: &[usize], u: &[f64], n_k: usize, r_k: usize, k: usize) -> Vec { +fn apply_mode_product_transpose( + data: &[f64], + shape: &[usize], + u: &[f64], + n_k: usize, + r_k: usize, + k: usize, +) -> Vec { let d = shape.len(); let mut new_shape = shape.to_vec(); new_shape[k] = r_k; @@ -274,7 +294,14 @@ fn apply_mode_product_transpose(data: &[f64], shape: &[usize], u: &[f64], n_k: u } /// Apply mode-k product: result[...,:,...] = A[...,:,...] * U^T (r_k -> n_k) -fn apply_mode_product(data: &[f64], shape: &[usize], u: &[f64], n_k: usize, r_k: usize, k: usize) -> Vec { +fn apply_mode_product( + data: &[f64], + shape: &[usize], + u: &[f64], + n_k: usize, + r_k: usize, + k: usize, +) -> Vec { let d = shape.len(); let mut new_shape = shape.to_vec(); new_shape[k] = n_k; diff --git a/crates/ruvector-math/src/tropical/matrix.rs b/crates/ruvector-math/src/tropical/matrix.rs index 71a688a89..fa1be294d 100644 --- a/crates/ruvector-math/src/tropical/matrix.rs +++ b/crates/ruvector-math/src/tropical/matrix.rs @@ -40,7 +40,11 @@ impl TropicalMatrix { let rows = data.len(); let cols = if rows > 0 { data[0].len() } else { 0 }; let flat: Vec = data.into_iter().flatten().collect(); - Self { rows, cols, data: flat } + Self { + rows, + cols, + data: flat, + } } /// Get element (returns -∞ for out of bounds) @@ -237,7 +241,10 @@ impl TropicalEigen { eigenvalue = new_eigenvalue; } - Some(TropicalEigen { eigenvalue, eigenvector: v }) + Some(TropicalEigen { + eigenvalue, + eigenvector: v, + }) } } @@ -303,10 +310,7 @@ mod tests { #[test] fn test_tropical_matrix_mul() { // A = [[0, 1], [-∞, 2]] - let a = TropicalMatrix::from_rows(vec![ - vec![0.0, 1.0], - vec![f64::NEG_INFINITY, 2.0], - ]); + let a = TropicalMatrix::from_rows(vec![vec![0.0, 1.0], vec![f64::NEG_INFINITY, 2.0]]); // A² = [[max(0+0, 1-∞), max(0+1, 1+2)], ...] let a2 = a.mul(&a); diff --git a/crates/ruvector-math/src/tropical/mod.rs b/crates/ruvector-math/src/tropical/mod.rs index 061269986..264a3ecf8 100644 --- a/crates/ruvector-math/src/tropical/mod.rs +++ b/crates/ruvector-math/src/tropical/mod.rs @@ -21,15 +21,15 @@ //! - Neural networks with ReLU = tropical rational functions //! - Tropical geometry provides bounds on linear regions -mod semiring; -mod polynomial; mod matrix; mod neural_analysis; +mod polynomial; +mod semiring; -pub use semiring::{Tropical, TropicalSemiring}; -pub use polynomial::{TropicalPolynomial, TropicalMonomial}; -pub use matrix::{TropicalMatrix, TropicalEigen, MinPlusMatrix}; +pub use matrix::{MinPlusMatrix, TropicalEigen, TropicalMatrix}; pub use neural_analysis::{LinearRegionCounter, TropicalNeuralAnalysis}; +pub use polynomial::{TropicalMonomial, TropicalPolynomial}; +pub use semiring::{Tropical, TropicalSemiring}; #[cfg(test)] mod tests { diff --git a/crates/ruvector-math/src/tropical/neural_analysis.rs b/crates/ruvector-math/src/tropical/neural_analysis.rs index f217c850f..e3fae76ee 100644 --- a/crates/ruvector-math/src/tropical/neural_analysis.rs +++ b/crates/ruvector-math/src/tropical/neural_analysis.rs @@ -36,7 +36,11 @@ impl TropicalNeuralAnalysis { weights: Vec>>, biases: Vec>, ) -> Self { - Self { architecture, weights, biases } + Self { + architecture, + weights, + biases, + } } /// Create a random network for testing @@ -74,7 +78,11 @@ impl TropicalNeuralAnalysis { biases.push(layer_biases); } - Self { architecture, weights, biases } + Self { + architecture, + weights, + biases, + } } /// Forward pass of the ReLU network @@ -84,8 +92,13 @@ impl TropicalNeuralAnalysis { for layer in 0..self.weights.len() { let mut y = Vec::with_capacity(self.weights[layer].len()); - for (neuron_weights, &bias) in self.weights[layer].iter().zip(self.biases[layer].iter()) { - let linear: f64 = neuron_weights.iter().zip(x.iter()).map(|(w, xi)| w * xi).sum(); + for (neuron_weights, &bias) in self.weights[layer].iter().zip(self.biases[layer].iter()) + { + let linear: f64 = neuron_weights + .iter() + .zip(x.iter()) + .map(|(w, xi)| w * xi) + .sum(); let z = linear + bias; // ReLU = max(0, z) = tropical addition y.push(z.max(0.0)); @@ -163,8 +176,13 @@ impl TropicalNeuralAnalysis { for layer in 0..self.weights.len() { let mut y = Vec::with_capacity(self.weights[layer].len()); - for (neuron_weights, &bias) in self.weights[layer].iter().zip(self.biases[layer].iter()) { - let linear: f64 = neuron_weights.iter().zip(x.iter()).map(|(w, xi)| w * xi).sum(); + for (neuron_weights, &bias) in self.weights[layer].iter().zip(self.biases[layer].iter()) + { + let linear: f64 = neuron_weights + .iter() + .zip(x.iter()) + .map(|(w, xi)| w * xi) + .sum(); let z = linear + bias; pattern.push(z > 0.0); y.push(z.max(0.0)); @@ -199,7 +217,10 @@ impl TropicalNeuralAnalysis { } Some(TropicalPolynomial::from_monomials( - terms.into_iter().map(|(c, e)| super::polynomial::TropicalMonomial::new(c, e)).collect() + terms + .into_iter() + .map(|(c, e)| super::polynomial::TropicalMonomial::new(c, e)) + .collect(), )) } @@ -353,10 +374,7 @@ mod tests { vec![vec![1.0, 0.0], vec![0.0, 1.0], vec![1.0, 1.0]], vec![vec![1.0, 1.0, 1.0]], ], - vec![ - vec![0.0, 0.0, -1.0], - vec![0.0], - ], + vec![vec![0.0, 0.0, -1.0], vec![0.0]], ); let output = analysis.forward(&[1.0, 1.0]); diff --git a/crates/ruvector-math/src/tropical/polynomial.rs b/crates/ruvector-math/src/tropical/polynomial.rs index 13942c94c..cff3c0620 100644 --- a/crates/ruvector-math/src/tropical/polynomial.rs +++ b/crates/ruvector-math/src/tropical/polynomial.rs @@ -208,7 +208,11 @@ impl MultivariateTropicalPolynomial { if *coeff == f64::NEG_INFINITY { f64::NEG_INFINITY } else { - let linear: f64 = exp.iter().zip(x.iter()).map(|(&e, &xi)| e as f64 * xi).sum(); + let linear: f64 = exp + .iter() + .zip(x.iter()) + .map(|(&e, &xi)| e as f64 * xi) + .sum(); coeff + linear } }) @@ -230,9 +234,9 @@ mod tests { // p(x) = max(2 + 0x, 1 + 1x, -1 + 2x) = max(2, 1+x, -1+2x) let p = TropicalPolynomial::from_coeffs(&[2.0, 1.0, -1.0]); - assert!((p.eval(0.0) - 2.0).abs() < 1e-10); // max(2, 1, -1) = 2 - assert!((p.eval(1.0) - 2.0).abs() < 1e-10); // max(2, 2, 1) = 2 - assert!((p.eval(3.0) - 5.0).abs() < 1e-10); // max(2, 4, 5) = 5 + assert!((p.eval(0.0) - 2.0).abs() < 1e-10); // max(2, 1, -1) = 2 + assert!((p.eval(1.0) - 2.0).abs() < 1e-10); // max(2, 2, 1) = 2 + assert!((p.eval(3.0) - 5.0).abs() < 1e-10); // max(2, 4, 5) = 5 } #[test] @@ -260,11 +264,10 @@ mod tests { #[test] fn test_multivariate() { // p(x,y) = max(0, x, y) - let p = MultivariateTropicalPolynomial::new(2, vec![ - (0.0, vec![0, 0]), - (0.0, vec![1, 0]), - (0.0, vec![0, 1]), - ]); + let p = MultivariateTropicalPolynomial::new( + 2, + vec![(0.0, vec![0, 0]), (0.0, vec![1, 0]), (0.0, vec![0, 1])], + ); assert!((p.eval(&[1.0, 2.0]) - 2.0).abs() < 1e-10); assert!((p.eval(&[3.0, 1.0]) - 3.0).abs() < 1e-10); diff --git a/crates/ruvector-math/src/tropical/semiring.rs b/crates/ruvector-math/src/tropical/semiring.rs index 08b11bf2f..33788bc58 100644 --- a/crates/ruvector-math/src/tropical/semiring.rs +++ b/crates/ruvector-math/src/tropical/semiring.rs @@ -13,7 +13,9 @@ pub struct Tropical { impl Tropical { /// Tropical zero (-∞ in max-plus) - pub const ZERO: Tropical = Tropical { value: f64::NEG_INFINITY }; + pub const ZERO: Tropical = Tropical { + value: f64::NEG_INFINITY, + }; /// Tropical one (0 in max-plus) pub const ONE: Tropical = Tropical { value: 0.0 }; @@ -39,7 +41,9 @@ impl Tropical { /// Tropical addition: max(a, b) #[inline] pub fn add(&self, other: &Self) -> Self { - Self { value: self.value.max(other.value) } + Self { + value: self.value.max(other.value), + } } /// Tropical multiplication: a + b @@ -48,7 +52,9 @@ impl Tropical { if self.is_zero() || other.is_zero() { Self::ZERO } else { - Self { value: self.value + other.value } + Self { + value: self.value + other.value, + } } } @@ -58,7 +64,9 @@ impl Tropical { if self.is_zero() { Self::ZERO } else { - Self { value: self.value * n as f64 } + Self { + value: self.value * n as f64, + } } } } @@ -136,7 +144,9 @@ pub struct TropicalMin { impl TropicalMin { /// Tropical zero (+∞ in min-plus) - pub const ZERO: TropicalMin = TropicalMin { value: f64::INFINITY }; + pub const ZERO: TropicalMin = TropicalMin { + value: f64::INFINITY, + }; /// Tropical one (0 in min-plus) pub const ONE: TropicalMin = TropicalMin { value: 0.0 }; @@ -156,7 +166,9 @@ impl TropicalMin { /// Tropical addition: min(a, b) #[inline] pub fn add(&self, other: &Self) -> Self { - Self { value: self.value.min(other.value) } + Self { + value: self.value.min(other.value), + } } /// Tropical multiplication: a + b @@ -165,7 +177,9 @@ impl TropicalMin { if self.value == f64::INFINITY || other.value == f64::INFINITY { Self::ZERO } else { - Self { value: self.value + other.value } + Self { + value: self.value + other.value, + } } } } diff --git a/crates/ruvector-math/src/utils/numerical.rs b/crates/ruvector-math/src/utils/numerical.rs index 67738ff4a..a58ae2348 100644 --- a/crates/ruvector-math/src/utils/numerical.rs +++ b/crates/ruvector-math/src/utils/numerical.rs @@ -156,7 +156,11 @@ pub fn symmetric_kl(p: &[f64], q: &[f64]) -> f64 { /// Jensen-Shannon divergence pub fn jensen_shannon(p: &[f64], q: &[f64]) -> f64 { - let m: Vec = p.iter().zip(q.iter()).map(|(&pi, &qi)| (pi + qi) / 2.0).collect(); + let m: Vec = p + .iter() + .zip(q.iter()) + .map(|(&pi, &qi)| (pi + qi) / 2.0) + .collect(); (kl_divergence(p, &m) + kl_divergence(q, &m)) / 2.0 } diff --git a/crates/ruvector-math/src/utils/sorting.rs b/crates/ruvector-math/src/utils/sorting.rs index 98a6a7933..252f3adff 100644 --- a/crates/ruvector-math/src/utils/sorting.rs +++ b/crates/ruvector-math/src/utils/sorting.rs @@ -3,7 +3,11 @@ /// Argsort: returns indices that would sort the array pub fn argsort(data: &[f64]) -> Vec { let mut indices: Vec = (0..data.len()).collect(); - indices.sort_by(|&a, &b| data[a].partial_cmp(&data[b]).unwrap_or(std::cmp::Ordering::Equal)); + indices.sort_by(|&a, &b| { + data[a] + .partial_cmp(&data[b]) + .unwrap_or(std::cmp::Ordering::Equal) + }); indices } diff --git a/crates/ruvector-mincut-gated-transformer/src/kv_cache/hot_buffer.rs b/crates/ruvector-mincut-gated-transformer/src/kv_cache/hot_buffer.rs index a010e485c..86e194734 100644 --- a/crates/ruvector-mincut-gated-transformer/src/kv_cache/hot_buffer.rs +++ b/crates/ruvector-mincut-gated-transformer/src/kv_cache/hot_buffer.rs @@ -113,8 +113,12 @@ impl HotBuffer { for head in 0..self.config.num_heads { let offset = oldest_pos * self.config.head_dim; - ek.extend_from_slice(&self.keys[layer][head][offset..offset + self.config.head_dim]); - ev.extend_from_slice(&self.values[layer][head][offset..offset + self.config.head_dim]); + ek.extend_from_slice( + &self.keys[layer][head][offset..offset + self.config.head_dim], + ); + ev.extend_from_slice( + &self.values[layer][head][offset..offset + self.config.head_dim], + ); } evicted_key = Some(ek); @@ -209,7 +213,9 @@ impl HotBuffer { for head in 0..self.config.num_heads { let offset = oldest_pos * self.config.head_dim; key.extend_from_slice(&self.keys[layer][head][offset..offset + self.config.head_dim]); - value.extend_from_slice(&self.values[layer][head][offset..offset + self.config.head_dim]); + value.extend_from_slice( + &self.values[layer][head][offset..offset + self.config.head_dim], + ); } self.len[layer] -= 1; @@ -231,7 +237,9 @@ impl HotBuffer { if self.len[layer] < self.config.capacity { // Not wrapped yet, just return from start - result.extend_from_slice(&self.keys[layer][head][..self.len[layer] * self.config.head_dim]); + result.extend_from_slice( + &self.keys[layer][head][..self.len[layer] * self.config.head_dim], + ); } else { // Wrapped: read from write_pos to end, then from start to write_pos let start = self.write_pos[layer] * self.config.head_dim; @@ -258,7 +266,9 @@ impl HotBuffer { let mut result = Vec::with_capacity(self.len[layer] * self.config.head_dim); if self.len[layer] < self.config.capacity { - result.extend_from_slice(&self.values[layer][head][..self.len[layer] * self.config.head_dim]); + result.extend_from_slice( + &self.values[layer][head][..self.len[layer] * self.config.head_dim], + ); } else { let start = self.write_pos[layer] * self.config.head_dim; let total_size = self.config.capacity * self.config.head_dim; @@ -369,7 +379,12 @@ mod tests { // Push 3 different keys for i in 0..3 { let val = i as f32; - buffer.push_head(0, 0, &[val, val + 1.0, val + 2.0, val + 3.0], &[val * 10.0; 4]); + buffer.push_head( + 0, + 0, + &[val, val + 1.0, val + 2.0, val + 3.0], + &[val * 10.0; 4], + ); buffer.advance(0); } diff --git a/crates/ruvector-mincut-gated-transformer/src/kv_cache/kivi.rs b/crates/ruvector-mincut-gated-transformer/src/kv_cache/kivi.rs index cf211255d..77528b8e2 100644 --- a/crates/ruvector-mincut-gated-transformer/src/kv_cache/kivi.rs +++ b/crates/ruvector-mincut-gated-transformer/src/kv_cache/kivi.rs @@ -110,7 +110,10 @@ impl KiviQuantizer { /// Create quantizer with Hadamard transform enabled pub fn with_hadamard(bits: u8, head_dim: usize) -> Self { - assert!(head_dim.is_power_of_two(), "Hadamard requires power-of-2 dimension"); + assert!( + head_dim.is_power_of_two(), + "Hadamard requires power-of-2 dimension" + ); let mut q = Self::new(bits, head_dim); q.use_hadamard = true; q @@ -163,7 +166,8 @@ impl KiviQuantizer { // Quantize let scale = self.max_quant as f32 / (max_val - min_val); - let mut quantized = Vec::with_capacity((self.head_dim + self.values_per_byte - 1) / self.values_per_byte); + let mut quantized = + Vec::with_capacity((self.head_dim + self.values_per_byte - 1) / self.values_per_byte); for chunk in transformed.chunks(self.values_per_byte) { let mut byte = 0u8; diff --git a/crates/ruvector-mincut-gated-transformer/src/kv_cache/kvquant.rs b/crates/ruvector-mincut-gated-transformer/src/kv_cache/kvquant.rs index 08aefb660..95987b55e 100644 --- a/crates/ruvector-mincut-gated-transformer/src/kv_cache/kvquant.rs +++ b/crates/ruvector-mincut-gated-transformer/src/kv_cache/kvquant.rs @@ -111,7 +111,11 @@ impl KVQuantQuantizer { Self { bits, - key_mode: if pre_rope { KVQuantKeyMode::PreRoPE } else { KVQuantKeyMode::PostRoPE }, + key_mode: if pre_rope { + KVQuantKeyMode::PreRoPE + } else { + KVQuantKeyMode::PostRoPE + }, value_mode: KVQuantValueMode::Uniform, head_dim, max_quant: (1u8 << bits) - 1, @@ -331,8 +335,16 @@ impl KVQuantQuantizer { data, scale, zero_point: min_val, - outlier_indices: if outlier_indices.is_empty() { None } else { Some(outlier_indices) }, - outlier_values: if outlier_values.is_empty() { None } else { Some(outlier_values) }, + outlier_indices: if outlier_indices.is_empty() { + None + } else { + Some(outlier_indices) + }, + outlier_values: if outlier_values.is_empty() { + None + } else { + Some(outlier_values) + }, } } @@ -435,7 +447,11 @@ impl KVQuantQuantizer { } /// Create calibration data from sample vectors - pub fn calibrate(&self, key_samples: &[Vec], value_samples: &[Vec]) -> CalibrationData { + pub fn calibrate( + &self, + key_samples: &[Vec], + value_samples: &[Vec], + ) -> CalibrationData { // Compute key statistics let key_stats = if !key_samples.is_empty() { let all_values: Vec = key_samples.iter().flatten().copied().collect(); diff --git a/crates/ruvector-mincut-gated-transformer/src/kv_cache/manager.rs b/crates/ruvector-mincut-gated-transformer/src/kv_cache/manager.rs index f457d8523..6152f3051 100644 --- a/crates/ruvector-mincut-gated-transformer/src/kv_cache/manager.rs +++ b/crates/ruvector-mincut-gated-transformer/src/kv_cache/manager.rs @@ -10,11 +10,11 @@ use alloc::vec::Vec; use std::vec::Vec; use super::hot_buffer::{HotBuffer, HotBufferConfig}; +use super::kvquant::KVQuantQuantizer; use super::metrics::{MemoryStats, QualityFeedback, QualityMetric, QualityTracker}; -use super::policy::{EvictionDecision, TierPolicy, RematerializationPolicy}; +use super::policy::{EvictionDecision, RematerializationPolicy, TierPolicy}; use super::quantized_store::{QuantizedStore, QuantizedStoreConfig}; use super::squat::SQuatQuantizer; -use super::kvquant::KVQuantQuantizer; use super::tier::{TierBoundary, TierCounts}; /// Archive tier quantizer selection @@ -124,23 +124,25 @@ impl AdaptiveKVCacheConfig { /// Estimate memory usage in bytes pub fn estimate_memory(&self) -> usize { // Hot buffer: FP16 - let hot_bytes = self.num_layers * self.num_heads * self.head_dim - * self.tail_length * 2 * 2; // 2 bytes * 2 (kv) + let hot_bytes = self.num_layers * self.num_heads * self.head_dim * self.tail_length * 2 * 2; // 2 bytes * 2 (kv) // Warm: 4-bit - let warm_bytes = self.num_layers * self.num_heads * self.head_dim - * self.warm_length / 2 * 2; // 0.5 bytes * 2 (kv) + let warm_bytes = + self.num_layers * self.num_heads * self.head_dim * self.warm_length / 2 * 2; // 0.5 bytes * 2 (kv) // Archive: varies by quantizer - let archive_len = self.max_seq_len.saturating_sub(self.tail_length + self.warm_length); + let archive_len = self + .max_seq_len + .saturating_sub(self.tail_length + self.warm_length); let archive_bytes_per_element = match self.archive_quantizer { ArchiveQuantizer::Kivi2Bit => 0.25, ArchiveQuantizer::SQuat { .. } => 0.1, ArchiveQuantizer::KVQuant { bits } => bits as f64 / 8.0, ArchiveQuantizer::Adaptive => 0.25, }; - let archive_bytes = (self.num_layers * self.num_heads * self.head_dim - * archive_len) as f64 * archive_bytes_per_element * 2.0; + let archive_bytes = (self.num_layers * self.num_heads * self.head_dim * archive_len) as f64 + * archive_bytes_per_element + * 2.0; hot_bytes + warm_bytes + archive_bytes as usize } @@ -193,12 +195,15 @@ impl AdaptiveKVCache { num_heads: config.num_heads, head_dim: config.head_dim, warm_capacity: config.warm_length, - archive_capacity: config.max_seq_len.saturating_sub(config.tail_length + config.warm_length), + archive_capacity: config + .max_seq_len + .saturating_sub(config.tail_length + config.warm_length), warm_bits: 4, archive_bits: 2, }; - let tier_boundary = TierBoundary::new(config.tail_length, config.tail_length + config.warm_length); + let tier_boundary = + TierBoundary::new(config.tail_length, config.tail_length + config.warm_length); let tier_policy = TierPolicy::new(tier_boundary, config.quality_target); let remat_policy = if config.enable_rematerialization { @@ -257,12 +262,7 @@ impl AdaptiveKVCache { /// Compute attention with tiered cache /// /// Returns attention output: [num_heads * head_dim] - pub fn attention( - &self, - layer: usize, - query: &[f32], - scale: f32, - ) -> Vec { + pub fn attention(&self, layer: usize, query: &[f32], scale: f32) -> Vec { assert!(layer < self.config.num_layers); assert_eq!(query.len(), self.config.head_dim * self.config.num_heads); diff --git a/crates/ruvector-mincut-gated-transformer/src/kv_cache/metrics.rs b/crates/ruvector-mincut-gated-transformer/src/kv_cache/metrics.rs index af00ec0c7..cbe51dea8 100644 --- a/crates/ruvector-mincut-gated-transformer/src/kv_cache/metrics.rs +++ b/crates/ruvector-mincut-gated-transformer/src/kv_cache/metrics.rs @@ -46,7 +46,13 @@ impl MemoryStats { } /// Calculate memory saved compared to FP16 baseline - pub fn memory_saved(&self, baseline_tokens: usize, head_dim: usize, num_heads: usize, num_layers: usize) -> usize { + pub fn memory_saved( + &self, + baseline_tokens: usize, + head_dim: usize, + num_heads: usize, + num_layers: usize, + ) -> usize { let fp16_bytes = baseline_tokens * head_dim * num_heads * num_layers * 2 * 2; // 2 bytes * 2 (kv) fp16_bytes.saturating_sub(self.total_bytes) } @@ -234,10 +240,12 @@ impl QualityTracker { let variance = (self.sum_sq_score / self.count as f32) - (avg * avg); let std_dev = variance.max(0.0).sqrt(); - let (min_score, max_score) = self.history.iter().fold( - (f32::MAX, f32::MIN), - |(min, max), f| (min.min(f.score), max.max(f.score)), - ); + let (min_score, max_score) = self + .history + .iter() + .fold((f32::MAX, f32::MIN), |(min, max), f| { + (min.min(f.score), max.max(f.score)) + }); let trend = self.compute_trend(); @@ -260,18 +268,24 @@ impl QualityTracker { let recent_count = 10.min(self.history.len() / 2); let earlier_count = recent_count; - let recent_avg: f32 = self.history.iter() + let recent_avg: f32 = self + .history + .iter() .rev() .take(recent_count) .map(|f| f.score) - .sum::() / recent_count as f32; + .sum::() + / recent_count as f32; - let earlier_avg: f32 = self.history.iter() + let earlier_avg: f32 = self + .history + .iter() .rev() .skip(recent_count) .take(earlier_count) .map(|f| f.score) - .sum::() / earlier_count as f32; + .sum::() + / earlier_count as f32; recent_avg - earlier_avg } @@ -414,7 +428,11 @@ mod tests { } let metrics = tracker.current_metrics(); - assert!(metrics.trend > 0.0, "Expected positive trend, got {}", metrics.trend); + assert!( + metrics.trend > 0.0, + "Expected positive trend, got {}", + metrics.trend + ); } #[test] @@ -437,9 +455,11 @@ mod tests { tracker.record(feedback); } - assert!(tracker.boundary_adjustment_factor() < 1.0, + assert!( + tracker.boundary_adjustment_factor() < 1.0, "Expected factor < 1.0 for high quality, got {}", - tracker.boundary_adjustment_factor()); + tracker.boundary_adjustment_factor() + ); } #[test] diff --git a/crates/ruvector-mincut-gated-transformer/src/kv_cache/mod.rs b/crates/ruvector-mincut-gated-transformer/src/kv_cache/mod.rs index 5e24a75d4..a8d4dbc53 100644 --- a/crates/ruvector-mincut-gated-transformer/src/kv_cache/mod.rs +++ b/crates/ruvector-mincut-gated-transformer/src/kv_cache/mod.rs @@ -67,26 +67,31 @@ extern crate alloc; pub mod legacy; // New three-tier KV cache modules -pub mod tier; pub mod hot_buffer; -pub mod quantized_store; pub mod kivi; -pub mod squat; pub mod kvquant; pub mod manager; -pub mod policy; pub mod metrics; +pub mod policy; +pub mod quantized_store; +pub mod squat; +pub mod tier; // Re-export legacy types for backward compatibility pub use legacy::{HadamardTransform, QuantBits, QuantizedKVCache}; // Re-export new three-tier types -pub use tier::{CacheTier, TierBoundary, TierConfig, TierCounts}; pub use hot_buffer::{HotBuffer, HotBufferConfig}; -pub use quantized_store::{QuantizedStore, QuantizedEntry, DequantizedKV, QuantizedStoreConfig}; pub use kivi::{KiviQuantizer, QuantScheme, QuantizedKV}; -pub use squat::{SQuatQuantizer, SQuatCompressed, QuantizedSubspace}; -pub use kvquant::{KVQuantQuantizer, KVQuantKeyMode, KVQuantValueMode, PreRoPEKey, QuantizedValue, CalibrationData}; +pub use kvquant::{ + CalibrationData, KVQuantKeyMode, KVQuantQuantizer, KVQuantValueMode, PreRoPEKey, QuantizedValue, +}; pub use manager::{AdaptiveKVCache, AdaptiveKVCacheConfig, ArchiveQuantizer}; -pub use policy::{TierPolicy, RematerializationPolicy, EvictionDecision, MemoryTracker, RematerializationCostModel}; -pub use metrics::{QualityTracker, QualityMetric, QualityFeedback, MemoryStats, TierMetrics}; +pub use metrics::{MemoryStats, QualityFeedback, QualityMetric, QualityTracker, TierMetrics}; +pub use policy::{ + EvictionDecision, MemoryTracker, RematerializationCostModel, RematerializationPolicy, + TierPolicy, +}; +pub use quantized_store::{DequantizedKV, QuantizedEntry, QuantizedStore, QuantizedStoreConfig}; +pub use squat::{QuantizedSubspace, SQuatCompressed, SQuatQuantizer}; +pub use tier::{CacheTier, TierBoundary, TierConfig, TierCounts}; diff --git a/crates/ruvector-mincut-gated-transformer/src/kv_cache/policy.rs b/crates/ruvector-mincut-gated-transformer/src/kv_cache/policy.rs index 3b77cc632..cab310fe7 100644 --- a/crates/ruvector-mincut-gated-transformer/src/kv_cache/policy.rs +++ b/crates/ruvector-mincut-gated-transformer/src/kv_cache/policy.rs @@ -215,8 +215,8 @@ impl Default for RematerializationCostModel { Self { // Approximate for a 7B model flops_per_token_per_layer: 2 * 4096 * 4096, // 2 * hidden^2 - bytes_per_token: 4096 * 2 * 2, // hidden * 2 (kv) * 2 (fp16) - compute_budget: 1_000_000_000, // 1 GFLOP budget + bytes_per_token: 4096 * 2 * 2, // hidden * 2 (kv) * 2 (fp16) + compute_budget: 1_000_000_000, // 1 GFLOP budget } } } @@ -276,12 +276,19 @@ impl RematerializationPolicy { if recompute_cost > self.cost_model.compute_budget { Some(EvictionDecision::Quantize { target_bits: 2 }) } else { - Some(EvictionDecision::Evict { recompute_on_access: true }) + Some(EvictionDecision::Evict { + recompute_on_access: true, + }) } } /// Decide whether to evict or keep a specific token - pub fn should_evict(&self, token_position: usize, layer: usize, total_tokens: usize) -> EvictionDecision { + pub fn should_evict( + &self, + token_position: usize, + layer: usize, + total_tokens: usize, + ) -> EvictionDecision { let pressure = self.memory_tracker.pressure(); if pressure < self.memory_threshold { @@ -300,7 +307,9 @@ impl RematerializationPolicy { if total_tokens <= self.min_materialized { EvictionDecision::Keep } else if adjusted_cost < self.cost_model.compute_budget as f32 { - EvictionDecision::Evict { recompute_on_access: true } + EvictionDecision::Evict { + recompute_on_access: true, + } } else { EvictionDecision::Quantize { target_bits: 2 } } diff --git a/crates/ruvector-mincut-gated-transformer/src/kv_cache/quantized_store.rs b/crates/ruvector-mincut-gated-transformer/src/kv_cache/quantized_store.rs index 821cef5cf..4ee41c3d7 100644 --- a/crates/ruvector-mincut-gated-transformer/src/kv_cache/quantized_store.rs +++ b/crates/ruvector-mincut-gated-transformer/src/kv_cache/quantized_store.rs @@ -9,7 +9,7 @@ use alloc::{vec, vec::Vec}; #[cfg(not(feature = "no_std_gateway"))] use std::vec::Vec; -use super::kivi::{KiviQuantizer, QuantizedKV, QuantScheme}; +use super::kivi::{KiviQuantizer, QuantScheme, QuantizedKV}; use super::tier::CacheTier; /// A single quantized entry in the store @@ -94,17 +94,11 @@ impl QuantizedStoreConfig { let warm_bytes_per_token = (self.head_dim * self.warm_bits as usize + 7) / 8; let archive_bytes_per_token = (self.head_dim * self.archive_bits as usize + 7) / 8; - let warm_total = self.num_layers - * self.num_heads - * self.warm_capacity - * warm_bytes_per_token - * 2; // keys + values + let warm_total = + self.num_layers * self.num_heads * self.warm_capacity * warm_bytes_per_token * 2; // keys + values - let archive_total = self.num_layers - * self.num_heads - * self.archive_capacity - * archive_bytes_per_token - * 2; + let archive_total = + self.num_layers * self.num_heads * self.archive_capacity * archive_bytes_per_token * 2; // Add scale overhead (8 bytes per token for min/max) let scale_overhead = (self.warm_capacity + self.archive_capacity) * 8 * self.num_layers; @@ -177,8 +171,10 @@ impl QuantizedStore { layer_warm_key_scales.push(vec![(0.0f32, 0.0f32); config.warm_capacity]); layer_warm_value_scales.push(vec![(0.0f32, 0.0f32); config.warm_capacity]); - layer_archive_keys.push(vec![0u8; config.archive_capacity * archive_bytes_per_token]); - layer_archive_values.push(vec![0u8; config.archive_capacity * archive_bytes_per_token]); + layer_archive_keys + .push(vec![0u8; config.archive_capacity * archive_bytes_per_token]); + layer_archive_values + .push(vec![0u8; config.archive_capacity * archive_bytes_per_token]); layer_archive_key_scales.push(vec![(0.0f32, 0.0f32); config.archive_capacity]); layer_archive_value_scales.push(vec![(0.0f32, 0.0f32); config.archive_capacity]); } @@ -195,7 +191,12 @@ impl QuantizedStore { } let scratch = (0..config.num_layers) - .map(|_| DequantizedKV::with_capacity(config.warm_capacity + config.archive_capacity, config.head_dim)) + .map(|_| { + DequantizedKV::with_capacity( + config.warm_capacity + config.archive_capacity, + config.head_dim, + ) + }) .collect(); Self { @@ -232,7 +233,8 @@ impl QuantizedStore { // Quantize key with per-channel scheme let (key_q, key_min, key_max) = self.warm_quantizer.quantize(key, QuantScheme::PerChannel); // Quantize value with per-token scheme - let (value_q, value_min, value_max) = self.warm_quantizer.quantize(value, QuantScheme::PerToken); + let (value_q, value_min, value_max) = + self.warm_quantizer.quantize(value, QuantScheme::PerToken); // Store quantized data let bytes_per_token = (self.config.head_dim * self.config.warm_bits as usize + 7) / 8; @@ -268,25 +270,31 @@ impl QuantizedStore { // Get warm entry let warm_offset = i * warm_bytes; let warm_key = &self.warm_keys[layer][head][warm_offset..warm_offset + warm_bytes]; - let warm_value = &self.warm_values[layer][head][warm_offset..warm_offset + warm_bytes]; + let warm_value = + &self.warm_values[layer][head][warm_offset..warm_offset + warm_bytes]; let (key_min, key_max) = self.warm_key_scales[layer][head][i]; let (value_min, value_max) = self.warm_value_scales[layer][head][i]; // Dequantize from warm let key_fp32 = self.warm_quantizer.dequantize(warm_key, key_min, key_max); - let value_fp32 = self.warm_quantizer.dequantize(warm_value, value_min, value_max); + let value_fp32 = self + .warm_quantizer + .dequantize(warm_value, value_min, value_max); // Re-quantize for archive (more aggressive) - let (archive_key, ak_min, ak_max) = - self.archive_quantizer.quantize(&key_fp32, QuantScheme::PerChannel); - let (archive_value, av_min, av_max) = - self.archive_quantizer.quantize(&value_fp32, QuantScheme::PerToken); + let (archive_key, ak_min, ak_max) = self + .archive_quantizer + .quantize(&key_fp32, QuantScheme::PerChannel); + let (archive_value, av_min, av_max) = self + .archive_quantizer + .quantize(&value_fp32, QuantScheme::PerToken); // Store in archive let archive_offset = archive_pos * archive_bytes; self.archive_keys[layer][head][archive_offset..archive_offset + archive_key.len()] .copy_from_slice(&archive_key); - self.archive_values[layer][head][archive_offset..archive_offset + archive_value.len()] + self.archive_values[layer][head] + [archive_offset..archive_offset + archive_value.len()] .copy_from_slice(&archive_value); self.archive_key_scales[layer][head][archive_pos] = (ak_min, ak_max); self.archive_value_scales[layer][head][archive_pos] = (av_min, av_max); @@ -321,7 +329,8 @@ impl QuantizedStore { // Shift scales for i in 0..remaining { self.warm_key_scales[layer][head][i] = self.warm_key_scales[layer][head][i + count]; - self.warm_value_scales[layer][head][i] = self.warm_value_scales[layer][head][i + count]; + self.warm_value_scales[layer][head][i] = + self.warm_value_scales[layer][head][i + count]; } } diff --git a/crates/ruvector-mincut-gated-transformer/src/kv_cache/squat.rs b/crates/ruvector-mincut-gated-transformer/src/kv_cache/squat.rs index 3d1c61307..ee83e577b 100644 --- a/crates/ruvector-mincut-gated-transformer/src/kv_cache/squat.rs +++ b/crates/ruvector-mincut-gated-transformer/src/kv_cache/squat.rs @@ -40,8 +40,8 @@ pub struct SQuatCompressed { impl SQuatCompressed { /// Get total bytes used pub fn bytes(&self) -> usize { - self.subspaces.iter().map(|s| s.data.len()).sum::() - + self.subspaces.len() * 8 // scale + zero_point per subspace + self.subspaces.iter().map(|s| s.data.len()).sum::() + self.subspaces.len() * 8 + // scale + zero_point per subspace } /// Get compression ratio vs FP16 @@ -76,8 +76,16 @@ impl SQuatQuantizer { /// * `bits_per_subspace` - Bits per component (typically 2) /// * `head_dim` - Head dimension /// * `num_layers` - Number of transformer layers - pub fn new(num_subspaces: usize, bits_per_subspace: u8, head_dim: usize, num_layers: usize) -> Self { - assert!(head_dim % num_subspaces == 0, "head_dim must be divisible by num_subspaces"); + pub fn new( + num_subspaces: usize, + bits_per_subspace: u8, + head_dim: usize, + num_layers: usize, + ) -> Self { + assert!( + head_dim % num_subspaces == 0, + "head_dim must be divisible by num_subspaces" + ); assert!(bits_per_subspace <= 4, "bits_per_subspace must be <= 4"); let subspace_dim = head_dim / num_subspaces; @@ -276,7 +284,8 @@ impl SQuatQuantizer { let scale = (max_val - min_val) / self.max_quant as f32; // Quantize - let mut quantized = Vec::with_capacity((self.subspace_dim + values_per_byte - 1) / values_per_byte); + let mut quantized = + Vec::with_capacity((self.subspace_dim + values_per_byte - 1) / values_per_byte); for chunk in subspace.chunks(values_per_byte) { let mut byte = 0u8; for (j, &val) in chunk.iter().enumerate() { @@ -349,9 +358,9 @@ impl SQuatQuantizer { /// Calculate expected compression ratio vs FP16 pub fn compression_ratio(&self) -> f32 { let original_bits = self.head_dim * 32; // FP32 (4 bytes per element) - // Compressed: bits_per_subspace for each subspace's indices + 8 bytes (scale + zero_point) per subspace - let compressed_bits = self.num_subspaces * self.bits_per_subspace as usize - + self.num_subspaces * 64; // scale + zero_point per subspace + // Compressed: bits_per_subspace for each subspace's indices + 8 bytes (scale + zero_point) per subspace + let compressed_bits = + self.num_subspaces * self.bits_per_subspace as usize + self.num_subspaces * 64; // scale + zero_point per subspace if compressed_bits == 0 { return 1.0; } diff --git a/crates/ruvector-mincut-gated-transformer/src/kv_cache/tier.rs b/crates/ruvector-mincut-gated-transformer/src/kv_cache/tier.rs index 9d264c840..99575625d 100644 --- a/crates/ruvector-mincut-gated-transformer/src/kv_cache/tier.rs +++ b/crates/ruvector-mincut-gated-transformer/src/kv_cache/tier.rs @@ -34,7 +34,7 @@ impl CacheTier { pub fn compression_ratio(&self) -> f32 { match self { CacheTier::Hot => 1.0, - CacheTier::Warm => 4.0, // 16/4 + CacheTier::Warm => 4.0, // 16/4 CacheTier::Archive => 8.0, // 16/2 } } diff --git a/crates/ruvector-mincut-gated-transformer/src/lib.rs b/crates/ruvector-mincut-gated-transformer/src/lib.rs index c8f9c34fd..6dc60c78a 100644 --- a/crates/ruvector-mincut-gated-transformer/src/lib.rs +++ b/crates/ruvector-mincut-gated-transformer/src/lib.rs @@ -135,14 +135,10 @@ pub use gate::{GateController, TierDecision}; pub use kv_cache::{HadamardTransform, QuantBits, QuantizedKVCache}; // New three-tier KV cache types (ADR-004) pub use kv_cache::{ - AdaptiveKVCache, AdaptiveKVCacheConfig, ArchiveQuantizer, - CacheTier, TierBoundary, TierConfig, - HotBuffer, HotBufferConfig, - KiviQuantizer, QuantScheme, QuantizedKV, - SQuatQuantizer, SQuatCompressed, - KVQuantQuantizer, KVQuantKeyMode, KVQuantValueMode, - TierPolicy, RematerializationPolicy, EvictionDecision, - QualityTracker, QualityMetric, QualityFeedback, MemoryStats, + AdaptiveKVCache, AdaptiveKVCacheConfig, ArchiveQuantizer, CacheTier, EvictionDecision, + HotBuffer, HotBufferConfig, KVQuantKeyMode, KVQuantQuantizer, KVQuantValueMode, KiviQuantizer, + MemoryStats, QualityFeedback, QualityMetric, QualityTracker, QuantScheme, QuantizedKV, + RematerializationPolicy, SQuatCompressed, SQuatQuantizer, TierBoundary, TierConfig, TierPolicy, }; pub use mamba::{MambaConfig, MambaLayer, MambaState, MambaWeights}; pub use mod_routing::{MincutDepthRouter, ModRoutingConfig, RoutingStats, TokenRoute}; @@ -186,16 +182,54 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION"); /// Prelude module for convenient imports pub mod prelude { pub use crate::{ - generate_tree_attention_mask, CoherenceEarlyExit, DraftToken, DraftTree, EarlyExitConfig, - EarlyExitDecision, Error, ExitReason, GateDecision, GatePacket, GatePolicy, GateReason, - HadamardTransform, InferInput, InferOutput, InferStats, MambaConfig, MambaLayer, - MambaState, MambaWeights, MincutDepthRouter, MincutGatedTransformer, ModRoutingConfig, - QuantBits, QuantizedKVCache, QuantizedWeights, Result, RopeConfig, RopeEmbedding, - RopeScaling, RoutingStats, SpeculativeConfig, SpeculativeDecoder, SpikePacket, TokenRoute, - TransformerConfig, VerificationResult, WeightsLoader, Witness, + generate_tree_attention_mask, // Three-tier KV cache (ADR-004) - AdaptiveKVCache, AdaptiveKVCacheConfig, ArchiveQuantizer, - CacheTier, TierBoundary, KiviQuantizer, SQuatQuantizer, KVQuantQuantizer, + AdaptiveKVCache, + AdaptiveKVCacheConfig, + ArchiveQuantizer, + CacheTier, + CoherenceEarlyExit, + DraftToken, + DraftTree, + EarlyExitConfig, + EarlyExitDecision, + Error, + ExitReason, + GateDecision, + GatePacket, + GatePolicy, + GateReason, + HadamardTransform, + InferInput, + InferOutput, + InferStats, + KVQuantQuantizer, + KiviQuantizer, + MambaConfig, + MambaLayer, + MambaState, + MambaWeights, + MincutDepthRouter, + MincutGatedTransformer, + ModRoutingConfig, + QuantBits, + QuantizedKVCache, + QuantizedWeights, + Result, + RopeConfig, + RopeEmbedding, + RopeScaling, + RoutingStats, + SQuatQuantizer, + SpeculativeConfig, + SpeculativeDecoder, + SpikePacket, + TierBoundary, + TokenRoute, + TransformerConfig, + VerificationResult, + WeightsLoader, + Witness, }; #[cfg(feature = "trace")] diff --git a/crates/ruvector-mincut/benches/jtree_bench.rs b/crates/ruvector-mincut/benches/jtree_bench.rs index 650d6a101..c8247b0f3 100644 --- a/crates/ruvector-mincut/benches/jtree_bench.rs +++ b/crates/ruvector-mincut/benches/jtree_bench.rs @@ -97,7 +97,11 @@ fn generate_dense_graph(n: usize, seed: u64) -> Vec<(u64, u64, f64)> { /// Generate a graph with known minimum cut (two cliques connected by k edges) #[allow(dead_code)] -fn generate_known_mincut_graph(n_per_side: usize, mincut_value: usize, seed: u64) -> Vec<(u64, u64, f64)> { +fn generate_known_mincut_graph( + n_per_side: usize, + mincut_value: usize, + seed: u64, +) -> Vec<(u64, u64, f64)> { let mut edges = Vec::new(); let mut rng = StdRng::seed_from_u64(seed); @@ -241,25 +245,19 @@ fn bench_point_to_point_query(c: &mut Criterion) { // Baseline benchmark group.throughput(Throughput::Elements(1)); - group.bench_with_input( - BenchmarkId::new("baseline", size), - &size, - |b, _| { - b.iter_batched( - || { - let graph = Arc::new(DynamicGraph::new()); - for (u, v, w) in &edges { - let _ = graph.insert_edge(*u, *v, *w); - } - (BaselineMinCut::new(graph), 0u64, (size / 2) as u64) - }, - |(baseline, s, t)| { - black_box(baseline.point_to_point_mincut(s, t)) - }, - criterion::BatchSize::SmallInput, - ); - }, - ); + group.bench_with_input(BenchmarkId::new("baseline", size), &size, |b, _| { + b.iter_batched( + || { + let graph = Arc::new(DynamicGraph::new()); + for (u, v, w) in &edges { + let _ = graph.insert_edge(*u, *v, *w); + } + (BaselineMinCut::new(graph), 0u64, (size / 2) as u64) + }, + |(baseline, s, t)| black_box(baseline.point_to_point_mincut(s, t)), + criterion::BatchSize::SmallInput, + ); + }); // J-Tree hierarchical decomposition benchmark group.bench_with_input( @@ -287,27 +285,23 @@ fn bench_point_to_point_query(c: &mut Criterion) { // Subpolynomial min-cut benchmark (BMSSP-based) if size <= 10_000 { // Limit for reasonable benchmark time - group.bench_with_input( - BenchmarkId::new("subpoly_bmssp", size), - &size, - |b, _| { - b.iter_batched( - || { - let mut mincut = SubpolynomialMinCut::for_size(size); - for (u, v, w) in &edges { - let _ = mincut.insert_edge(*u, *v, *w); - } - mincut.build(); - mincut - }, - |mincut| { - // Query is O(1) after hierarchy is built - black_box(mincut.min_cut_value()) - }, - criterion::BatchSize::SmallInput, - ); - }, - ); + group.bench_with_input(BenchmarkId::new("subpoly_bmssp", size), &size, |b, _| { + b.iter_batched( + || { + let mut mincut = SubpolynomialMinCut::for_size(size); + for (u, v, w) in &edges { + let _ = mincut.insert_edge(*u, *v, *w); + } + mincut.build(); + mincut + }, + |mincut| { + // Query is O(1) after hierarchy is built + black_box(mincut.min_cut_value()) + }, + criterion::BatchSize::SmallInput, + ); + }); } } @@ -343,9 +337,7 @@ fn bench_multi_terminal_query(c: &mut Criterion) { } (BaselineMinCut::new(graph), terminals.clone()) }, - |(baseline, terms)| { - black_box(baseline.multi_terminal_mincut(&terms)) - }, + |(baseline, terms)| black_box(baseline.multi_terminal_mincut(&terms)), criterion::BatchSize::SmallInput, ); }, @@ -418,25 +410,19 @@ fn bench_all_pairs_query(c: &mut Criterion) { // Baseline: O(n² · mn) total group.throughput(Throughput::Elements((size * size) as u64)); - group.bench_with_input( - BenchmarkId::new("baseline", size), - &size, - |b, _| { - b.iter_batched( - || { - let graph = Arc::new(DynamicGraph::new()); - for (u, v, w) in &edges { - let _ = graph.insert_edge(*u, *v, *w); - } - BaselineMinCut::new(graph) - }, - |baseline| { - black_box(baseline.all_pairs_mincut()) - }, - criterion::BatchSize::SmallInput, - ); - }, - ); + group.bench_with_input(BenchmarkId::new("baseline", size), &size, |b, _| { + b.iter_batched( + || { + let graph = Arc::new(DynamicGraph::new()); + for (u, v, w) in &edges { + let _ = graph.insert_edge(*u, *v, *w); + } + BaselineMinCut::new(graph) + }, + |baseline| black_box(baseline.all_pairs_mincut()), + criterion::BatchSize::SmallInput, + ); + }); // J-Tree hierarchical: O(n² · log^(2/3) n) via hierarchy group.bench_with_input( @@ -481,9 +467,7 @@ fn bench_all_pairs_query(c: &mut Criterion) { hierarchy.build(); hierarchy }, - |hierarchy| { - black_box(hierarchy.global_min_cut) - }, + |hierarchy| black_box(hierarchy.global_min_cut), criterion::BatchSize::SmallInput, ); }, @@ -546,7 +530,8 @@ fn bench_edge_insertion(c: &mut Criterion) { for (u, v, w) in &initial_edges { let _ = graph.insert_edge(*u, *v, *w); } - let mut decomp = HierarchicalDecomposition::build(graph.clone()).unwrap(); + let mut decomp = + HierarchicalDecomposition::build(graph.clone()).unwrap(); let mut rng = StdRng::seed_from_u64(456); let new_u = rng.gen_range(0..size as u64); let new_v = rng.gen_range(0..size as u64); @@ -642,38 +627,34 @@ fn bench_edge_deletion(c: &mut Criterion) { ); // J-Tree warm-start (reuse previous decomposition) - group.bench_with_input( - BenchmarkId::new("jtree_warm_start", size), - &size, - |b, _| { - b.iter_batched( - || { - let graph = Arc::new(DynamicGraph::new()); - for (u, v, w) in &initial_edges { - let _ = graph.insert_edge(*u, *v, *w); - } - let mut decomp = HierarchicalDecomposition::build(graph.clone()).unwrap(); - let edges_list = graph.edges(); - let idx = 42 % edges_list.len().max(1); - let edge = if !edges_list.is_empty() { - Some((edges_list[idx].source, edges_list[idx].target)) - } else { - None - }; - (decomp, graph, edge) - }, - |(mut decomp, graph, edge)| { - if let Some((u, v)) = edge { - let _ = graph.delete_edge(u, v); - // Warm-start: only update affected subtree - let _ = decomp.delete_edge(u, v); - } - black_box(decomp.min_cut_value()) - }, - criterion::BatchSize::SmallInput, - ); - }, - ); + group.bench_with_input(BenchmarkId::new("jtree_warm_start", size), &size, |b, _| { + b.iter_batched( + || { + let graph = Arc::new(DynamicGraph::new()); + for (u, v, w) in &initial_edges { + let _ = graph.insert_edge(*u, *v, *w); + } + let mut decomp = HierarchicalDecomposition::build(graph.clone()).unwrap(); + let edges_list = graph.edges(); + let idx = 42 % edges_list.len().max(1); + let edge = if !edges_list.is_empty() { + Some((edges_list[idx].source, edges_list[idx].target)) + } else { + None + }; + (decomp, graph, edge) + }, + |(mut decomp, graph, edge)| { + if let Some((u, v)) = edge { + let _ = graph.delete_edge(u, v); + // Warm-start: only update affected subtree + let _ = decomp.delete_edge(u, v); + } + black_box(decomp.min_cut_value()) + }, + criterion::BatchSize::SmallInput, + ); + }); // Subpolynomial warm-start group.bench_with_input( @@ -838,55 +819,43 @@ fn bench_memory_full_vs_lazy(c: &mut Criterion) { let edges = generate_sparse_graph(size, 42); // Full hierarchy build (materialized) - group.bench_with_input( - BenchmarkId::new("full_hierarchy", size), - &size, - |b, _| { - b.iter(|| { - let graph = Arc::new(DynamicGraph::new()); - for (u, v, w) in &edges { - let _ = graph.insert_edge(*u, *v, *w); - } - let decomp = HierarchicalDecomposition::build(graph).unwrap(); - // Force full materialization - let _ = decomp.min_cut_partition(); - black_box(decomp.num_nodes()) - }); - }, - ); + group.bench_with_input(BenchmarkId::new("full_hierarchy", size), &size, |b, _| { + b.iter(|| { + let graph = Arc::new(DynamicGraph::new()); + for (u, v, w) in &edges { + let _ = graph.insert_edge(*u, *v, *w); + } + let decomp = HierarchicalDecomposition::build(graph).unwrap(); + // Force full materialization + let _ = decomp.min_cut_partition(); + black_box(decomp.num_nodes()) + }); + }); // Lazy evaluation (only compute on demand) - group.bench_with_input( - BenchmarkId::new("lazy_evaluation", size), - &size, - |b, _| { - b.iter(|| { - let graph = Arc::new(DynamicGraph::new()); - for (u, v, w) in &edges { - let _ = graph.insert_edge(*u, *v, *w); - } - // Just build, don't materialize partitions - let decomp = HierarchicalDecomposition::build(graph).unwrap(); - black_box(decomp.min_cut_value()) - }); - }, - ); + group.bench_with_input(BenchmarkId::new("lazy_evaluation", size), &size, |b, _| { + b.iter(|| { + let graph = Arc::new(DynamicGraph::new()); + for (u, v, w) in &edges { + let _ = graph.insert_edge(*u, *v, *w); + } + // Just build, don't materialize partitions + let decomp = HierarchicalDecomposition::build(graph).unwrap(); + black_box(decomp.min_cut_value()) + }); + }); // Three-level hierarchy (more memory efficient structure) - group.bench_with_input( - BenchmarkId::new("three_level", size), - &size, - |b, _| { - b.iter(|| { - let mut hierarchy = ThreeLevelHierarchy::with_defaults(); - for (u, v, w) in &edges { - hierarchy.insert_edge(*u, *v, *w); - } - hierarchy.build(); - black_box(hierarchy.stats()) - }); - }, - ); + group.bench_with_input(BenchmarkId::new("three_level", size), &size, |b, _| { + b.iter(|| { + let mut hierarchy = ThreeLevelHierarchy::with_defaults(); + for (u, v, w) in &edges { + hierarchy.insert_edge(*u, *v, *w); + } + hierarchy.build(); + black_box(hierarchy.stats()) + }); + }); } group.finish(); @@ -976,69 +945,53 @@ fn bench_bmssp_scaling(c: &mut Criterion) { group.throughput(Throughput::Elements(1)); // Subpolynomial query (should scale as O(m·log^(2/3) n)) - group.bench_with_input( - BenchmarkId::new("subpoly_query", size), - &size, - |b, _| { - b.iter_batched( - || { - let mut mincut = SubpolynomialMinCut::for_size(size); - for (u, v, w) in &edges { - let _ = mincut.insert_edge(*u, *v, *w); - } - mincut.build(); - mincut - }, - |mincut| { - black_box(mincut.min_cut_value()) - }, - criterion::BatchSize::SmallInput, - ); - }, - ); + group.bench_with_input(BenchmarkId::new("subpoly_query", size), &size, |b, _| { + b.iter_batched( + || { + let mut mincut = SubpolynomialMinCut::for_size(size); + for (u, v, w) in &edges { + let _ = mincut.insert_edge(*u, *v, *w); + } + mincut.build(); + mincut + }, + |mincut| black_box(mincut.min_cut_value()), + criterion::BatchSize::SmallInput, + ); + }); // J-Tree query for comparison - group.bench_with_input( - BenchmarkId::new("jtree_query", size), - &size, - |b, _| { - b.iter_batched( - || { - let graph = Arc::new(DynamicGraph::new()); - for (u, v, w) in &edges { - let _ = graph.insert_edge(*u, *v, *w); - } - HierarchicalDecomposition::build(graph).unwrap() - }, - |decomp| { - black_box(decomp.min_cut_value()) - }, - criterion::BatchSize::SmallInput, - ); - }, - ); + group.bench_with_input(BenchmarkId::new("jtree_query", size), &size, |b, _| { + b.iter_batched( + || { + let graph = Arc::new(DynamicGraph::new()); + for (u, v, w) in &edges { + let _ = graph.insert_edge(*u, *v, *w); + } + HierarchicalDecomposition::build(graph).unwrap() + }, + |decomp| black_box(decomp.min_cut_value()), + criterion::BatchSize::SmallInput, + ); + }); // Baseline (O(mn)) for comparison - group.bench_with_input( - BenchmarkId::new("baseline_query", size), - &size, - |b, _| { - b.iter_batched( - || { - let graph = Arc::new(DynamicGraph::new()); - for (u, v, w) in &edges { - let _ = graph.insert_edge(*u, *v, *w); - } - BaselineMinCut::new(graph) - }, - |baseline| { - // Simplified O(n) query - black_box(baseline.point_to_point_mincut(0, (size / 2) as u64)) - }, - criterion::BatchSize::SmallInput, - ); - }, - ); + group.bench_with_input(BenchmarkId::new("baseline_query", size), &size, |b, _| { + b.iter_batched( + || { + let graph = Arc::new(DynamicGraph::new()); + for (u, v, w) in &edges { + let _ = graph.insert_edge(*u, *v, *w); + } + BaselineMinCut::new(graph) + }, + |baseline| { + // Simplified O(n) query + black_box(baseline.point_to_point_mincut(0, (size / 2) as u64)) + }, + criterion::BatchSize::SmallInput, + ); + }); // Log scaling info eprintln!( @@ -1263,9 +1216,7 @@ fn bench_polylog_connectivity(c: &mut Criterion) { let query_v = rng.gen_range(0..size as u64); (conn, query_u, query_v) }, - |(mut conn, query_u, query_v)| { - black_box(conn.connected(query_u, query_v)) - }, + |(mut conn, query_u, query_v)| black_box(conn.connected(query_u, query_v)), criterion::BatchSize::SmallInput, ); }, diff --git a/crates/ruvector-mincut/benches/optimization_bench.rs b/crates/ruvector-mincut/benches/optimization_bench.rs index 2e719a9bc..237edfbd1 100644 --- a/crates/ruvector-mincut/benches/optimization_bench.rs +++ b/crates/ruvector-mincut/benches/optimization_bench.rs @@ -10,16 +10,12 @@ //! //! Target: Combined 10x speedup -use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use ruvector_mincut::graph::DynamicGraph; use ruvector_mincut::optimization::{ - DegreePresparse, PresparseConfig, - PathDistanceCache, CacheConfig, - SimdDistanceOps, DistanceArray, - LevelPool, PoolConfig, LevelData, - ParallelLevelUpdater, ParallelConfig, LevelUpdateResult, - WasmBatchOps, BatchConfig, - BenchmarkSuite, + BatchConfig, BenchmarkSuite, CacheConfig, DegreePresparse, DistanceArray, LevelData, LevelPool, + LevelUpdateResult, ParallelConfig, ParallelLevelUpdater, PathDistanceCache, PoolConfig, + PresparseConfig, SimdDistanceOps, WasmBatchOps, }; use std::collections::HashSet; @@ -55,32 +51,24 @@ fn bench_dspar(c: &mut Criterion) { for size in [100, 1000, 5000].iter() { let graph = create_test_graph(*size, size * 5); - group.bench_with_input( - BenchmarkId::new("baseline", size), - size, - |b, _| { - b.iter(|| { - let edges: Vec<_> = graph.edges().collect(); - black_box(edges.len()) - }) - }, - ); + group.bench_with_input(BenchmarkId::new("baseline", size), size, |b, _| { + b.iter(|| { + let edges: Vec<_> = graph.edges().collect(); + black_box(edges.len()) + }) + }); let mut dspar = DegreePresparse::with_config(PresparseConfig { target_sparsity: 0.1, ..Default::default() }); - group.bench_with_input( - BenchmarkId::new("optimized", size), - size, - |b, _| { - b.iter(|| { - let result = dspar.presparse(&graph); - black_box(result.edges.len()) - }) - }, - ); + group.bench_with_input(BenchmarkId::new("optimized", size), size, |b, _| { + b.iter(|| { + let result = dspar.presparse(&graph); + black_box(result.edges.len()) + }) + }); } group.finish(); @@ -146,34 +134,24 @@ fn bench_simd(c: &mut Criterion) { } arr.set((*size / 2) as u64, 0.1); - group.bench_with_input( - BenchmarkId::new("find_min_naive", size), - &arr, - |b, arr| { - b.iter(|| { - let data = arr.as_slice(); - let mut min_val = f64::INFINITY; - let mut min_idx = 0; - for (i, &d) in data.iter().enumerate() { - if d < min_val { - min_val = d; - min_idx = i; - } + group.bench_with_input(BenchmarkId::new("find_min_naive", size), &arr, |b, arr| { + b.iter(|| { + let data = arr.as_slice(); + let mut min_val = f64::INFINITY; + let mut min_idx = 0; + for (i, &d) in data.iter().enumerate() { + if d < min_val { + min_val = d; + min_idx = i; } - black_box((min_val, min_idx)) - }) - }, - ); + } + black_box((min_val, min_idx)) + }) + }); - group.bench_with_input( - BenchmarkId::new("find_min_simd", size), - &arr, - |b, arr| { - b.iter(|| { - black_box(SimdDistanceOps::find_min(arr)) - }) - }, - ); + group.bench_with_input(BenchmarkId::new("find_min_simd", size), &arr, |b, arr| { + b.iter(|| black_box(SimdDistanceOps::find_min(arr))) + }); let neighbors: Vec<_> = (0..(size / 10).min(100)) .map(|i| ((i * 10) as u64, 1.0)) @@ -205,9 +183,7 @@ fn bench_simd(c: &mut Criterion) { size, |b, &size| { let mut arr = DistanceArray::new(size); - b.iter(|| { - black_box(SimdDistanceOps::relax_batch(&mut arr, 0.0, &neighbors)) - }) + b.iter(|| black_box(SimdDistanceOps::relax_batch(&mut arr, 0.0, &neighbors))) }, ); } @@ -270,7 +246,8 @@ fn bench_parallel(c: &mut Criterion) { work_size, |b, &work_size| { b.iter(|| { - let _results: Vec<_> = levels.iter() + let _results: Vec<_> = levels + .iter() .map(|&level| { let mut sum = 0.0; for i in 0..work_size { @@ -346,17 +323,13 @@ fn bench_wasm_batch(c: &mut Criterion) { ..Default::default() }); - group.bench_with_input( - BenchmarkId::new("batched_ops", size), - &edges, - |b, edges| { - b.iter(|| { - batch.queue_insert_edges(edges.clone()); - let results = batch.execute_batch(); - black_box(results.len()) - }) - }, - ); + group.bench_with_input(BenchmarkId::new("batched_ops", size), &edges, |b, edges| { + b.iter(|| { + batch.queue_insert_edges(edges.clone()); + let results = batch.execute_batch(); + black_box(results.len()) + }) + }); } group.finish(); diff --git a/crates/ruvector-mincut/src/jtree/coordinator.rs b/crates/ruvector-mincut/src/jtree/coordinator.rs index 5fd17687e..25952d990 100644 --- a/crates/ruvector-mincut/src/jtree/coordinator.rs +++ b/crates/ruvector-mincut/src/jtree/coordinator.rs @@ -303,9 +303,9 @@ impl TwoTierCoordinator { /// Get the j-tree hierarchy, building if necessary fn tier1_mut(&mut self) -> Result<&mut JTreeHierarchy> { self.ensure_built()?; - self.tier1 - .as_mut() - .ok_or_else(|| crate::error::MinCutError::InternalError("Hierarchy not built".to_string())) + self.tier1.as_mut().ok_or_else(|| { + crate::error::MinCutError::InternalError("Hierarchy not built".to_string()) + }) } /// Query global minimum cut with automatic tier selection @@ -377,14 +377,15 @@ impl TwoTierCoordinator { escalated: false, }; } - self.query_tier2_global(start).unwrap_or_else(|_| QueryResult { - value: f64::INFINITY, - is_exact: false, - tier: 0, - confidence: 0.0, - latency: start.elapsed(), - escalated: false, - }) + self.query_tier2_global(start) + .unwrap_or_else(|_| QueryResult { + value: f64::INFINITY, + is_exact: false, + tier: 0, + confidence: 0.0, + latency: start.elapsed(), + escalated: false, + }) } /// Force approximate (Tier 1) query @@ -400,14 +401,15 @@ impl TwoTierCoordinator { escalated: false, }; } - self.query_tier1_global(start).unwrap_or_else(|_| QueryResult { - value: f64::INFINITY, - is_exact: false, - tier: 0, - confidence: 0.0, - latency: start.elapsed(), - escalated: false, - }) + self.query_tier1_global(start) + .unwrap_or_else(|_| QueryResult { + value: f64::INFINITY, + is_exact: false, + tier: 0, + confidence: 0.0, + latency: start.elapsed(), + escalated: false, + }) } /// Query Tier 1 for global min cut @@ -436,7 +438,12 @@ impl TwoTierCoordinator { } /// Query Tier 1 for s-t min cut - fn query_tier1_st(&mut self, _s: VertexId, _t: VertexId, start: Instant) -> Result { + fn query_tier1_st( + &mut self, + _s: VertexId, + _t: VertexId, + start: Instant, + ) -> Result { // JTreeHierarchy doesn't have s-t min cut directly, use approximate global // In a full implementation, we'd traverse levels to find s-t cut let hierarchy = self.tier1_mut()?; @@ -498,7 +505,12 @@ impl TwoTierCoordinator { } /// Query Tier 2 (exact) for s-t min cut - fn query_tier2_st(&mut self, _s: VertexId, _t: VertexId, start: Instant) -> Result { + fn query_tier2_st( + &mut self, + _s: VertexId, + _t: VertexId, + start: Instant, + ) -> Result { // Use global min cut with exact flag for now let hierarchy = self.tier1_mut()?; let cut_result = hierarchy.min_cut(true)?; @@ -767,10 +779,8 @@ mod tests { #[test] fn test_escalation_periodic() { let g = create_test_graph(); - let mut coord = TwoTierCoordinator::new( - g, - EscalationPolicy::Periodic { query_interval: 3 }, - ); + let mut coord = + TwoTierCoordinator::new(g, EscalationPolicy::Periodic { query_interval: 3 }); coord.build().unwrap(); // First query should escalate (queries_since_exact starts at 0, >= 3 is false) diff --git a/crates/ruvector-mincut/src/jtree/hierarchy.rs b/crates/ruvector-mincut/src/jtree/hierarchy.rs index d8875a4eb..8f512e036 100644 --- a/crates/ruvector-mincut/src/jtree/hierarchy.rs +++ b/crates/ruvector-mincut/src/jtree/hierarchy.rs @@ -131,7 +131,12 @@ impl CutResult { } /// Create an approximate result - pub fn approximate(value: f64, factor: f64, partition: HashSet, level: usize) -> Self { + pub fn approximate( + value: f64, + factor: f64, + partition: HashSet, + level: usize, + ) -> Self { Self { value, partition, @@ -214,7 +219,9 @@ impl JTreeHierarchy { // Initialize levels (lazy by default) let levels = if config.lazy_evaluation { - (0..num_levels).map(|_| LevelState::Unmaterialized).collect() + (0..num_levels) + .map(|_| LevelState::Unmaterialized) + .collect() } else { // Eagerly build all levels Self::build_all_levels(&graph, num_levels, alpha, &config)? @@ -469,13 +476,11 @@ impl JTreeHierarchy { for level in 0..self.num_levels { if let LevelState::Materialized(_) = &self.levels[level] { self.dirty_levels.insert(level); - self.levels[level] = match std::mem::replace( - &mut self.levels[level], - LevelState::Unmaterialized, - ) { - LevelState::Materialized(l) => LevelState::Dirty(l), - other => other, - }; + self.levels[level] = + match std::mem::replace(&mut self.levels[level], LevelState::Unmaterialized) { + LevelState::Materialized(l) => LevelState::Dirty(l), + other => other, + }; } } @@ -504,13 +509,11 @@ impl JTreeHierarchy { for level in 0..self.num_levels { if let LevelState::Materialized(_) = &self.levels[level] { self.dirty_levels.insert(level); - self.levels[level] = match std::mem::replace( - &mut self.levels[level], - LevelState::Unmaterialized, - ) { - LevelState::Materialized(l) => LevelState::Dirty(l), - other => other, - }; + self.levels[level] = + match std::mem::replace(&mut self.levels[level], LevelState::Unmaterialized) { + LevelState::Materialized(l) => LevelState::Dirty(l), + other => other, + }; } } diff --git a/crates/ruvector-mincut/src/jtree/level.rs b/crates/ruvector-mincut/src/jtree/level.rs index 9893d7725..36915a39e 100644 --- a/crates/ruvector-mincut/src/jtree/level.rs +++ b/crates/ruvector-mincut/src/jtree/level.rs @@ -471,10 +471,7 @@ impl BmsspJTreeLevel { for &(next, edge_weight) in neighbors { let next_cost = cost + edge_weight; - let is_better = distances - .get(&next) - .map(|&d| next_cost < d) - .unwrap_or(true); + let is_better = distances.get(&next).map(|&d| next_cost < d).unwrap_or(true); if is_better { distances.insert(next, next_cost); diff --git a/crates/ruvector-mincut/src/lib.rs b/crates/ruvector-mincut/src/lib.rs index e994eefb2..1c4d5e980 100644 --- a/crates/ruvector-mincut/src/lib.rs +++ b/crates/ruvector-mincut/src/lib.rs @@ -279,20 +279,36 @@ pub use wrapper::MinCutWrapper; // Optimization re-exports (SOTA j-Tree + BMSSP performance improvements) pub use optimization::{ - // DSpar: 5.9x speedup via degree-based presparse - DegreePresparse, PresparseConfig, PresparseResult, PresparseStats, - // Cache: 10x for repeated distance queries - PathDistanceCache, CacheConfig, CacheStats, PrefetchHint, - // SIMD: 2-4x for distance operations - SimdDistanceOps, DistanceArray, - // Pool: 50-75% memory reduction - LevelPool, PoolConfig, LazyLevel, PoolStats, - // Parallel: Rayon-based work-stealing - ParallelLevelUpdater, ParallelConfig, WorkStealingScheduler, - // WASM Batch: 10x FFI overhead reduction - WasmBatchOps, BatchConfig, TypedArrayTransfer, + BatchConfig, + BenchmarkResult, // Benchmarking - BenchmarkSuite, BenchmarkResult, OptimizationBenchmark, + BenchmarkSuite, + CacheConfig, + CacheStats, + // DSpar: 5.9x speedup via degree-based presparse + DegreePresparse, + DistanceArray, + LazyLevel, + // Pool: 50-75% memory reduction + LevelPool, + OptimizationBenchmark, + ParallelConfig, + // Parallel: Rayon-based work-stealing + ParallelLevelUpdater, + // Cache: 10x for repeated distance queries + PathDistanceCache, + PoolConfig, + PoolStats, + PrefetchHint, + PresparseConfig, + PresparseResult, + PresparseStats, + // SIMD: 2-4x for distance operations + SimdDistanceOps, + TypedArrayTransfer, + // WASM Batch: 10x FFI overhead reduction + WasmBatchOps, + WorkStealingScheduler, }; // J-Tree re-exports (feature-gated) diff --git a/crates/ruvector-mincut/src/optimization/benchmark.rs b/crates/ruvector-mincut/src/optimization/benchmark.rs index d3d9fcd4a..a64966857 100644 --- a/crates/ruvector-mincut/src/optimization/benchmark.rs +++ b/crates/ruvector-mincut/src/optimization/benchmark.rs @@ -10,13 +10,13 @@ //! //! Target: Combined 10x speedup over naive implementation -use crate::graph::DynamicGraph; +use super::cache::{CacheConfig, PathDistanceCache}; use super::dspar::{DegreePresparse, PresparseConfig}; -use super::cache::{PathDistanceCache, CacheConfig}; -use super::simd_distance::{SimdDistanceOps, DistanceArray}; -use super::pool::{LevelPool, PoolConfig, LevelData}; -use super::parallel::{ParallelLevelUpdater, ParallelConfig, LevelUpdateResult, WorkItem}; -use super::wasm_batch::{WasmBatchOps, BatchConfig}; +use super::parallel::{LevelUpdateResult, ParallelConfig, ParallelLevelUpdater, WorkItem}; +use super::pool::{LevelData, LevelPool, PoolConfig}; +use super::simd_distance::{DistanceArray, SimdDistanceOps}; +use super::wasm_batch::{BatchConfig, WasmBatchOps}; +use crate::graph::DynamicGraph; use std::collections::HashSet; use std::time::{Duration, Instant}; @@ -220,7 +220,10 @@ impl BenchmarkSuite { // Get sparsification stats let sparse_result = dspar.presparse(&graph); result.add_metric("sparsity_ratio", sparse_result.stats.sparsity_ratio); - result.add_metric("edges_reduced", (sparse_result.stats.original_edges - sparse_result.stats.sparse_edges) as f64); + result.add_metric( + "edges_reduced", + (sparse_result.stats.original_edges - sparse_result.stats.sparse_edges) as f64, + ); results.push(result); } @@ -323,7 +326,7 @@ impl BenchmarkSuite { &format!("SIMD find_min n={}", size), baseline_us, opt_us.max(1), // Avoid divide by zero - 2.0, // Target speedup + 2.0, // Target speedup ); results.push(result); @@ -408,15 +411,11 @@ impl BenchmarkSuite { let stats = pool.stats(); - let mut result = BenchmarkResult::new( - &format!("Pool n={}", size), - baseline_us, - opt_us.max(1), - 2.0, - ); + let mut result = + BenchmarkResult::new(&format!("Pool n={}", size), baseline_us, opt_us.max(1), 2.0); result = result.with_memory( - baseline_memory * 10, // Baseline: all levels materialized + baseline_memory * 10, // Baseline: all levels materialized stats.pool_size_bytes, // Optimized: only max_materialized ); @@ -439,7 +438,8 @@ impl BenchmarkSuite { // Baseline: sequential processing let baseline_start = Instant::now(); for _ in 0..self.iterations { - let _results: Vec<_> = levels.iter() + let _results: Vec<_> = levels + .iter() .map(|&level| { // Simulate work let mut sum = 0.0; @@ -498,9 +498,7 @@ impl BenchmarkSuite { let mut results = Vec::new(); for &size in &self.sizes { - let edges: Vec<_> = (0..size) - .map(|i| (i as u64, (i + 1) as u64, 1.0)) - .collect(); + let edges: Vec<_> = (0..size).map(|i| (i as u64, (i + 1) as u64, 1.0)).collect(); // Baseline: individual operations let baseline_start = Instant::now(); @@ -556,24 +554,38 @@ impl BenchmarkSuite { for opt in &self.results { report.push_str(&format!("## {} Optimization\n", opt.name)); - report.push_str(&format!(" Average Speedup: {:.2}x\n", opt.summary.avg_speedup)); - report.push_str(&format!(" Min/Max: {:.2}x / {:.2}x\n", - opt.summary.min_speedup, opt.summary.max_speedup)); - report.push_str(&format!(" Targets Achieved: {:.0}%\n", - opt.summary.targets_achieved_percent)); + report.push_str(&format!( + " Average Speedup: {:.2}x\n", + opt.summary.avg_speedup + )); + report.push_str(&format!( + " Min/Max: {:.2}x / {:.2}x\n", + opt.summary.min_speedup, opt.summary.max_speedup + )); + report.push_str(&format!( + " Targets Achieved: {:.0}%\n", + opt.summary.targets_achieved_percent + )); if opt.summary.avg_memory_reduction > 0.0 { - report.push_str(&format!(" Memory Reduction: {:.1}%\n", - opt.summary.avg_memory_reduction)); + report.push_str(&format!( + " Memory Reduction: {:.1}%\n", + opt.summary.avg_memory_reduction + )); } report.push_str("\n Details:\n"); for result in &opt.results { - report.push_str(&format!(" - {}: {:.2}x (target: {:.2}x) {}\n", + report.push_str(&format!( + " - {}: {:.2}x (target: {:.2}x) {}\n", result.name, result.speedup, result.target_speedup, - if result.target_achieved { "[OK]" } else { "[MISS]" } + if result.target_achieved { + "[OK]" + } else { + "[MISS]" + } )); } report.push_str("\n"); @@ -582,8 +594,13 @@ impl BenchmarkSuite { let combined = self.combined_speedup(); report.push_str(&format!("## Combined Speedup Estimate: {:.2}x\n", combined)); report.push_str(&format!(" Target: 10x\n")); - report.push_str(&format!(" Status: {}\n", - if combined >= 10.0 { "TARGET ACHIEVED" } else { "In Progress" } + report.push_str(&format!( + " Status: {}\n", + if combined >= 10.0 { + "TARGET ACHIEVED" + } else { + "In Progress" + } )); report @@ -635,7 +652,8 @@ fn compute_summary(name: &str, results: Vec) -> OptimizationBen let speedups: Vec = results.iter().map(|r| r.speedup).collect(); let achieved: Vec = results.iter().map(|r| r.target_achieved).collect(); - let memory_reductions: Vec = results.iter() + let memory_reductions: Vec = results + .iter() .filter(|r| r.baseline_memory > 0) .map(|r| r.memory_reduction_percent) .collect(); @@ -679,8 +697,7 @@ mod tests { #[test] fn test_benchmark_result_memory() { - let result = BenchmarkResult::new("test", 100, 50, 1.0) - .with_memory(1000, 250); + let result = BenchmarkResult::new("test", 100, 50, 1.0).with_memory(1000, 250); assert_eq!(result.memory_reduction_percent, 75.0); } @@ -715,8 +732,11 @@ mod tests { // For very small inputs, overhead may exceed benefit // Just verify we get a valid positive result - assert!(combined > 0.0 && combined.is_finite(), - "Combined speedup {} should be positive and finite", combined); + assert!( + combined > 0.0 && combined.is_finite(), + "Combined speedup {} should be positive and finite", + combined + ); } #[test] diff --git a/crates/ruvector-mincut/src/optimization/cache.rs b/crates/ruvector-mincut/src/optimization/cache.rs index 5ca46930a..f5108ee71 100644 --- a/crates/ruvector-mincut/src/optimization/cache.rs +++ b/crates/ruvector-mincut/src/optimization/cache.rs @@ -103,7 +103,10 @@ impl CacheKey { if source <= target { Self { source, target } } else { - Self { source: target, target: source } + Self { + source: target, + target: source, + } } } } @@ -270,7 +273,8 @@ impl PathDistanceCache { let mut cache = self.cache.write().unwrap(); let mut lru = self.lru_order.write().unwrap(); - let keys_to_remove: Vec = cache.keys() + let keys_to_remove: Vec = cache + .keys() .filter(|k| k.source == vertex || k.target == vertex) .copied() .collect(); @@ -356,7 +360,8 @@ impl PathDistanceCache { } // Generate hints for hot sources - source_freq.into_iter() + source_freq + .into_iter() .filter(|(_, targets)| targets.len() > 2) .map(|(source, targets)| { let confidence = (targets.len() as f64 / history.len() as f64).min(1.0); @@ -372,9 +377,7 @@ impl PathDistanceCache { /// Get predicted queries for prefetching pub fn get_predicted_queries(&self) -> Vec<(VertexId, VertexId)> { let pred = self.predicted_queries.read().unwrap(); - pred.iter() - .map(|key| (key.source, key.target)) - .collect() + pred.iter().map(|key| (key.source, key.target)).collect() } /// Get cache statistics @@ -451,11 +454,7 @@ mod tests { fn test_batch_insert() { let cache = PathDistanceCache::new(); - let entries = vec![ - (1, 2, 1.0), - (2, 3, 2.0), - (3, 4, 3.0), - ]; + let entries = vec![(1, 2, 1.0), (2, 3, 2.0), (3, 4, 3.0)]; cache.insert_batch(&entries); diff --git a/crates/ruvector-mincut/src/optimization/dspar.rs b/crates/ruvector-mincut/src/optimization/dspar.rs index fa32a28b9..97742eac7 100644 --- a/crates/ruvector-mincut/src/optimization/dspar.rs +++ b/crates/ruvector-mincut/src/optimization/dspar.rs @@ -156,7 +156,8 @@ impl DegreePresparse { }; // Score all edges by effective resistance - let mut scored_edges: Vec<(EdgeId, VertexId, VertexId, Weight, f64)> = Vec::with_capacity(original_edges); + let mut scored_edges: Vec<(EdgeId, VertexId, VertexId, Weight, f64)> = + Vec::with_capacity(original_edges); for edge in graph.edges() { let deg_u = *self.degree_cache.get(&edge.source).unwrap_or(&1); @@ -244,12 +245,7 @@ impl DegreePresparse { /// Incremental update: handle edge insertion /// /// Returns whether the edge should be included in the sparse graph - pub fn should_include_edge( - &mut self, - graph: &DynamicGraph, - u: VertexId, - v: VertexId, - ) -> bool { + pub fn should_include_edge(&mut self, graph: &DynamicGraph, u: VertexId, v: VertexId) -> bool { // Update degree cache self.degree_cache.insert(u, graph.degree(u)); self.degree_cache.insert(v, graph.degree(v)); @@ -344,12 +340,16 @@ impl SpectralConcordance { /// Feature preservation loss (cut value approximation) fn feature_preservation_loss(&self, original: &DynamicGraph, sparse: &DynamicGraph) -> f64 { // Compare minimum degree (crude cut approximation) - let orig_min_deg = original.vertices().iter() + let orig_min_deg = original + .vertices() + .iter() .map(|&v| original.degree(v)) .min() .unwrap_or(0) as f64; - let sparse_min_deg = sparse.vertices().iter() + let sparse_min_deg = sparse + .vertices() + .iter() .map(|&v| sparse.degree(v)) .min() .unwrap_or(0) as f64; diff --git a/crates/ruvector-mincut/src/optimization/mod.rs b/crates/ruvector-mincut/src/optimization/mod.rs index 694e2e1b0..3f293e74c 100644 --- a/crates/ruvector-mincut/src/optimization/mod.rs +++ b/crates/ruvector-mincut/src/optimization/mod.rs @@ -11,19 +11,19 @@ //! //! Target: Combined 10x speedup over naive implementation. -pub mod dspar; -pub mod cache; -pub mod simd_distance; -pub mod pool; -pub mod parallel; -pub mod wasm_batch; pub mod benchmark; +pub mod cache; +pub mod dspar; +pub mod parallel; +pub mod pool; +pub mod simd_distance; +pub mod wasm_batch; // Re-exports +pub use benchmark::{BenchmarkResult, BenchmarkSuite, OptimizationBenchmark}; +pub use cache::{CacheConfig, CacheStats, PathDistanceCache, PrefetchHint}; pub use dspar::{DegreePresparse, PresparseConfig, PresparseResult, PresparseStats}; -pub use cache::{PathDistanceCache, CacheConfig, CacheStats, PrefetchHint}; -pub use simd_distance::{SimdDistanceOps, DistanceArray}; -pub use pool::{LevelPool, PoolConfig, LazyLevel, PoolStats}; -pub use parallel::{ParallelLevelUpdater, ParallelConfig, WorkStealingScheduler}; -pub use wasm_batch::{WasmBatchOps, BatchConfig, TypedArrayTransfer}; -pub use benchmark::{BenchmarkSuite, BenchmarkResult, OptimizationBenchmark}; +pub use parallel::{ParallelConfig, ParallelLevelUpdater, WorkStealingScheduler}; +pub use pool::{LazyLevel, LevelPool, PoolConfig, PoolStats}; +pub use simd_distance::{DistanceArray, SimdDistanceOps}; +pub use wasm_batch::{BatchConfig, TypedArrayTransfer, WasmBatchOps}; diff --git a/crates/ruvector-mincut/src/optimization/parallel.rs b/crates/ruvector-mincut/src/optimization/parallel.rs index dfb9f78bb..80b9fe082 100644 --- a/crates/ruvector-mincut/src/optimization/parallel.rs +++ b/crates/ruvector-mincut/src/optimization/parallel.rs @@ -111,7 +111,8 @@ impl WorkStealingScheduler { // Sort by priority (ascending) queue.sort_by_key(|w| w.priority); - self.total_work.fetch_add(estimated_work as u64, Ordering::Relaxed); + self.total_work + .fetch_add(estimated_work as u64, Ordering::Relaxed); } /// Submit multiple work items @@ -119,7 +120,8 @@ impl WorkStealingScheduler { let mut queue = self.work_queue.write().unwrap(); for item in items { - self.total_work.fetch_add(item.estimated_work as u64, Ordering::Relaxed); + self.total_work + .fetch_add(item.estimated_work as u64, Ordering::Relaxed); queue.push(item); } @@ -249,7 +251,8 @@ impl ParallelLevelUpdater { /// Reset global minimum pub fn reset_min(&self) { - self.global_min.store(f64::INFINITY.to_bits(), Ordering::Release); + self.global_min + .store(f64::INFINITY.to_bits(), Ordering::Release); self.best_level.store(usize::MAX, Ordering::Release); } @@ -263,7 +266,8 @@ impl ParallelLevelUpdater { if size < self.config.min_parallel_size { // Sequential processing for small workloads - return levels.iter() + return levels + .iter() .map(|&level| { let result = process_fn.clone()(level); self.try_update_min(result.cut_value, level); @@ -273,7 +277,8 @@ impl ParallelLevelUpdater { } // Parallel processing with Rayon - levels.par_iter() + levels + .par_iter() .map(|&level| { let result = process_fn.clone()(level); self.try_update_min(result.cut_value, level); @@ -288,7 +293,8 @@ impl ParallelLevelUpdater { where F: FnMut(usize) -> LevelUpdateResult + Clone, { - levels.iter() + levels + .iter() .map(|&level| { let result = process_fn.clone()(level); self.try_update_min(result.cut_value, level); @@ -299,13 +305,18 @@ impl ParallelLevelUpdater { /// Process work items with work-stealing #[cfg(feature = "rayon")] - pub fn process_with_stealing(&self, work_items: Vec, process_fn: F) -> Vec + pub fn process_with_stealing( + &self, + work_items: Vec, + process_fn: F, + ) -> Vec where F: Fn(&WorkItem) -> LevelUpdateResult + Send + Sync, { if work_items.len() < self.config.min_parallel_size { // Sequential - return work_items.iter() + return work_items + .iter() .map(|item| { let result = process_fn(item); self.try_update_min(result.cut_value, item.level); @@ -315,7 +326,8 @@ impl ParallelLevelUpdater { } // Parallel with work-stealing - work_items.par_iter() + work_items + .par_iter() .map(|item| { let result = process_fn(item); self.try_update_min(result.cut_value, item.level); @@ -326,11 +338,16 @@ impl ParallelLevelUpdater { /// Process work items (scalar fallback) #[cfg(not(feature = "rayon"))] - pub fn process_with_stealing(&self, work_items: Vec, process_fn: F) -> Vec + pub fn process_with_stealing( + &self, + work_items: Vec, + process_fn: F, + ) -> Vec where F: Fn(&WorkItem) -> LevelUpdateResult, { - work_items.iter() + work_items + .iter() .map(|item| { let result = process_fn(item); self.try_update_min(result.cut_value, item.level); @@ -341,11 +358,7 @@ impl ParallelLevelUpdater { /// Batch vertex processing within a level #[cfg(feature = "rayon")] - pub fn process_vertices_parallel( - &self, - vertices: &[VertexId], - process_fn: F, - ) -> Vec + pub fn process_vertices_parallel(&self, vertices: &[VertexId], process_fn: F) -> Vec where F: Fn(VertexId) -> R + Send + Sync, R: Send, @@ -359,11 +372,7 @@ impl ParallelLevelUpdater { /// Batch vertex processing (scalar fallback) #[cfg(not(feature = "rayon"))] - pub fn process_vertices_parallel( - &self, - vertices: &[VertexId], - process_fn: F, - ) -> Vec + pub fn process_vertices_parallel(&self, vertices: &[VertexId], process_fn: F) -> Vec where F: Fn(VertexId) -> R, { @@ -385,12 +394,14 @@ impl ParallelLevelUpdater { R: Send + Clone, { if items.len() < self.config.min_parallel_size { - return items.iter() + return items + .iter() .map(|item| map_fn(item)) .fold(identity.clone(), reduce_fn); } - items.par_iter() + items + .par_iter() .map(|item| map_fn(item)) .reduce(|| identity.clone(), reduce_fn) } @@ -408,7 +419,8 @@ impl ParallelLevelUpdater { F: Fn(&T) -> R, R: Clone, { - items.iter() + items + .iter() .map(|item| map_fn(item)) .fold(identity, reduce_fn) } @@ -441,11 +453,14 @@ impl ParallelCutOps { return Self::boundary_size_sequential(partition, adjacency); } - partition_vec.par_iter() + partition_vec + .par_iter() .map(|&v| { - adjacency.get(&v) + adjacency + .get(&v) .map(|neighbors| { - neighbors.iter() + neighbors + .iter() .filter(|(n, _)| !partition.contains(n)) .map(|(_, w)| w) .sum::() @@ -469,11 +484,14 @@ impl ParallelCutOps { partition: &HashSet, adjacency: &HashMap>, ) -> f64 { - partition.iter() + partition + .iter() .map(|&v| { - adjacency.get(&v) + adjacency + .get(&v) .map(|neighbors| { - neighbors.iter() + neighbors + .iter() .filter(|(n, _)| !partition.contains(n)) .map(|(_, w)| w) .sum::() @@ -493,7 +511,8 @@ impl ParallelCutOps { return Self::min_degree_vertex_sequential(vertices, adjacency); } - vertices.par_iter() + vertices + .par_iter() .map(|&v| { let degree = adjacency.get(&v).map(|n| n.len()).unwrap_or(0); (v, degree) @@ -516,7 +535,8 @@ impl ParallelCutOps { vertices: &[VertexId], adjacency: &HashMap>, ) -> Option<(VertexId, usize)> { - vertices.iter() + vertices + .iter() .map(|&v| { let degree = adjacency.get(&v).map(|n| n.len()).unwrap_or(0); (v, degree) @@ -580,13 +600,11 @@ mod tests { let levels = vec![0, 1, 2, 3, 4]; - let results = updater.process_parallel(&levels, |level| { - LevelUpdateResult { - level, - cut_value: level as f64 * 2.0, - partition: HashSet::new(), - time_us: 0, - } + let results = updater.process_parallel(&levels, |level| LevelUpdateResult { + level, + cut_value: level as f64 * 2.0, + partition: HashSet::new(), + time_us: 0, }); assert_eq!(results.len(), 5); @@ -620,7 +638,8 @@ mod tests { adjacency.insert(3, vec![(1, 1.0), (4, 1.0)]); adjacency.insert(4, vec![(1, 1.0), (3, 1.0)]); - let (min_v, min_deg) = ParallelCutOps::min_degree_vertex_sequential(&vertices, &adjacency).unwrap(); + let (min_v, min_deg) = + ParallelCutOps::min_degree_vertex_sequential(&vertices, &adjacency).unwrap(); assert_eq!(min_v, 2); assert_eq!(min_deg, 1); @@ -647,9 +666,24 @@ mod tests { let scheduler = WorkStealingScheduler::new(); let items = vec![ - WorkItem { level: 0, vertices: vec![], priority: 2, estimated_work: 100 }, - WorkItem { level: 1, vertices: vec![], priority: 0, estimated_work: 50 }, - WorkItem { level: 2, vertices: vec![], priority: 1, estimated_work: 75 }, + WorkItem { + level: 0, + vertices: vec![], + priority: 2, + estimated_work: 100, + }, + WorkItem { + level: 1, + vertices: vec![], + priority: 0, + estimated_work: 50, + }, + WorkItem { + level: 2, + vertices: vec![], + priority: 1, + estimated_work: 75, + }, ]; scheduler.submit_batch(items); diff --git a/crates/ruvector-mincut/src/optimization/pool.rs b/crates/ruvector-mincut/src/optimization/pool.rs index dc7c0a23c..eac99867d 100644 --- a/crates/ruvector-mincut/src/optimization/pool.rs +++ b/crates/ruvector-mincut/src/optimization/pool.rs @@ -133,8 +133,8 @@ impl LevelData { /// Update memory size estimate pub fn update_memory_size(&mut self) { - self.memory_size = self.vertices.len() * std::mem::size_of::() - + self.adjacency.memory_size(); + self.memory_size = + self.vertices.len() * std::mem::size_of::() + self.adjacency.memory_size(); } /// Get memory size @@ -271,7 +271,8 @@ impl LevelPool { /// Check if level is materialized pub fn is_materialized(&self, level_idx: usize) -> bool { let levels = self.levels.read().unwrap(); - levels.get(&level_idx) + levels + .get(&level_idx) .map(|l| l.is_materialized()) .unwrap_or(false) } @@ -325,13 +326,9 @@ impl LevelPool { let mut levels = self.levels.write().unwrap(); if let Some(level) = levels.get(&level_idx) { - let last_vertex_count = level.data() - .map(|d| d.vertices.len()) - .unwrap_or(0); + let last_vertex_count = level.data().map(|d| d.vertices.len()).unwrap_or(0); - let memory_freed = level.data() - .map(|d| d.memory_size()) - .unwrap_or(0); + let memory_freed = level.data().map(|d| d.memory_size()).unwrap_or(0); // Try to recycle the allocation if self.config.lazy_dealloc { @@ -357,9 +354,7 @@ impl LevelPool { /// Ensure we have capacity (evict if needed) fn ensure_capacity(&self) { let levels = self.levels.read().unwrap(); - let materialized_count = levels.values() - .filter(|l| l.is_materialized()) - .count(); + let materialized_count = levels.values().filter(|l| l.is_materialized()).count(); drop(levels); if materialized_count >= self.config.max_materialized_levels { @@ -422,9 +417,7 @@ impl LevelPool { /// Get pool statistics pub fn stats(&self) -> PoolStats { let levels = self.levels.read().unwrap(); - let materialized_count = levels.values() - .filter(|l| l.is_materialized()) - .count(); + let materialized_count = levels.values().filter(|l| l.is_materialized()).count(); PoolStats { allocations: self.allocations.load(Ordering::Relaxed), @@ -549,11 +542,7 @@ mod tests { #[test] fn test_compact_adjacency() { - let edges = vec![ - (0u16, 1u16, 10u16), - (1, 2, 20), - (2, 0, 30), - ]; + let edges = vec![(0u16, 1u16, 10u16), (1, 2, 20), (2, 0, 30)]; let adj = CompactAdjacency::from_edges(&edges, 3); diff --git a/crates/ruvector-mincut/src/optimization/simd_distance.rs b/crates/ruvector-mincut/src/optimization/simd_distance.rs index ed055490f..5d102f467 100644 --- a/crates/ruvector-mincut/src/optimization/simd_distance.rs +++ b/crates/ruvector-mincut/src/optimization/simd_distance.rs @@ -156,10 +156,22 @@ impl SimdDistanceOps { let c = data[base + 2]; let d = data[base + 3]; - if a < min_val { min_val = a; min_idx = base; } - if b < min_val { min_val = b; min_idx = base + 1; } - if c < min_val { min_val = c; min_idx = base + 2; } - if d < min_val { min_val = d; min_idx = base + 3; } + if a < min_val { + min_val = a; + min_idx = base; + } + if b < min_val { + min_val = b; + min_idx = base + 1; + } + if c < min_val { + min_val = c; + min_idx = base + 2; + } + if d < min_val { + min_val = d; + min_idx = base + 3; + } } // Handle remainder @@ -317,8 +329,12 @@ impl SimdDistanceOps { // Extract comparison results let mask = i8x16_bitmask(cmp); // Each f64 lane uses 8 bits in bitmask - if mask & 0xFF != 0 { count += 1; } - if mask & 0xFF00 != 0 { count += 1; } + if mask & 0xFF != 0 { + count += 1; + } + if mask & 0xFF00 != 0 { + count += 1; + } } } @@ -335,7 +351,11 @@ impl SimdDistanceOps { /// Count vertices with distance less than threshold (scalar fallback) #[cfg(not(target_arch = "wasm32"))] pub fn count_below_threshold(distances: &DistanceArray, threshold: f64) -> usize { - distances.as_slice().iter().filter(|&&d| d < threshold).count() + distances + .as_slice() + .iter() + .filter(|&&d| d < threshold) + .count() } /// Compute sum of distances (for average) @@ -469,12 +489,7 @@ mod tests { let mut arr = DistanceArray::new(10); arr.set(0, 0.0); // Source - let neighbors = vec![ - (1, 1.0), - (2, 2.0), - (3, 3.0), - (4, 4.0), - ]; + let neighbors = vec![(1, 1.0), (2, 2.0), (3, 3.0), (4, 4.0)]; let updated = SimdDistanceOps::relax_batch(&mut arr, 0.0, &neighbors); diff --git a/crates/ruvector-mincut/src/optimization/wasm_batch.rs b/crates/ruvector-mincut/src/optimization/wasm_batch.rs index d6a1cb1e1..2a8a7d096 100644 --- a/crates/ruvector-mincut/src/optimization/wasm_batch.rs +++ b/crates/ruvector-mincut/src/optimization/wasm_batch.rs @@ -29,7 +29,7 @@ impl Default for BatchConfig { Self { max_batch_size: 1024, buffer_size: 64 * 1024, // 64KB - alignment: 64, // AVX-512 alignment + alignment: 64, // AVX-512 alignment memory_pooling: true, } } @@ -151,7 +151,11 @@ impl TypedArrayTransfer { /// Get buffer lengths pub fn len(&self) -> (usize, usize, usize) { - (self.f64_buffer.len(), self.u64_buffer.len(), self.u32_buffer.len()) + ( + self.f64_buffer.len(), + self.u64_buffer.len(), + self.u32_buffer.len(), + ) } /// Check if empty @@ -202,7 +206,8 @@ impl WasmBatchOps { if edges.len() > self.config.max_batch_size { // Split into multiple batches for chunk in edges.chunks(self.config.max_batch_size) { - self.pending.push(BatchOperation::InsertEdges(chunk.to_vec())); + self.pending + .push(BatchOperation::InsertEdges(chunk.to_vec())); } } else { self.pending.push(BatchOperation::InsertEdges(edges)); @@ -213,7 +218,8 @@ impl WasmBatchOps { pub fn queue_delete_edges(&mut self, edges: Vec<(VertexId, VertexId)>) { if edges.len() > self.config.max_batch_size { for chunk in edges.chunks(self.config.max_batch_size) { - self.pending.push(BatchOperation::DeleteEdges(chunk.to_vec())); + self.pending + .push(BatchOperation::DeleteEdges(chunk.to_vec())); } } else { self.pending.push(BatchOperation::DeleteEdges(edges)); @@ -224,7 +230,8 @@ impl WasmBatchOps { pub fn queue_distance_queries(&mut self, pairs: Vec<(VertexId, VertexId)>) { if pairs.len() > self.config.max_batch_size { for chunk in pairs.chunks(self.config.max_batch_size) { - self.pending.push(BatchOperation::QueryDistances(chunk.to_vec())); + self.pending + .push(BatchOperation::QueryDistances(chunk.to_vec())); } } else { self.pending.push(BatchOperation::QueryDistances(pairs)); @@ -323,7 +330,8 @@ impl WasmBatchOps { } // Simulate distance results - let results: Vec = pairs.iter() + let results: Vec = pairs + .iter() .map(|(u, v)| if u == v { 0.0 } else { 1.0 }) .collect(); diff --git a/crates/ruvector-mincut/tests/jtree_tests.rs b/crates/ruvector-mincut/tests/jtree_tests.rs index f4d685ca8..b1de5b2a5 100644 --- a/crates/ruvector-mincut/tests/jtree_tests.rs +++ b/crates/ruvector-mincut/tests/jtree_tests.rs @@ -188,9 +188,8 @@ impl BmsspJTreeLevel { /// Invalidate cache for affected vertices pub fn invalidate_cache(&mut self, affected: &[u64]) { let affected_set: HashSet<_> = affected.iter().copied().collect(); - self.path_cache.retain(|(u, v), _| { - !affected_set.contains(u) && !affected_set.contains(v) - }); + self.path_cache + .retain(|(u, v), _| !affected_set.contains(u) && !affected_set.contains(v)); } /// Clear entire cache @@ -210,7 +209,11 @@ impl BmsspJTreeLevel { fn compute_min_cut(&self, _s: u64, _t: u64) -> f64 { // Simplified: return sum of minimum edge weight on any path // In real implementation, this would use BMSSP shortest path - self.edges.values().copied().min_by(|a, b| a.partial_cmp(b).unwrap()).unwrap_or(f64::INFINITY) + self.edges + .values() + .copied() + .min_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap_or(f64::INFINITY) } } @@ -303,7 +306,9 @@ impl LazyJTreeHierarchy { return None; } self.ensure_materialized(level); - self.levels[level].as_materialized().map(|d| d.min_cut_value) + self.levels[level] + .as_materialized() + .map(|d| d.min_cut_value) } /// Ensure level is materialized (demand-paging) @@ -1066,9 +1071,7 @@ mod property_tests { for iteration in 0..20 { // Materialize random levels - let levels_to_materialize: Vec = (0..5) - .filter(|_| iteration % 2 == 0) - .collect(); + let levels_to_materialize: Vec = (0..5).filter(|_| iteration % 2 == 0).collect(); for level in &levels_to_materialize { let _ = hierarchy.approximate_min_cut_at_level(*level); @@ -1125,7 +1128,10 @@ mod property_tests { let _ = level.min_cut(5, 7); let (hits_after, _) = level.cache_stats(); - assert!(hits_after > hits_before, "Unaffected region should still be cached"); + assert!( + hits_after > hits_before, + "Unaffected region should still be cached" + ); } } @@ -1290,7 +1296,12 @@ mod stress_tests { assert!(misses > 0, "Should have cache misses from first pass"); assert!(hits > 0, "Should have cache hits from second pass"); // Second pass should have produced hits - assert!(hits >= first_misses, "Second pass should hit cache: hits={}, first_misses={}", hits, first_misses); + assert!( + hits >= first_misses, + "Second pass should hit cache: hits={}, first_misses={}", + hits, + first_misses + ); } #[test] diff --git a/crates/ruvector-nervous-system-wasm/src/btsp.rs b/crates/ruvector-nervous-system-wasm/src/btsp.rs index ba4b9a90c..cb7c23892 100644 --- a/crates/ruvector-nervous-system-wasm/src/btsp.rs +++ b/crates/ruvector-nervous-system-wasm/src/btsp.rs @@ -245,7 +245,9 @@ impl BTSPAssociativeMemory { #[wasm_bindgen(constructor)] pub fn new(input_size: usize, output_size: usize) -> BTSPAssociativeMemory { let tau = 2000.0; - let layers = (0..output_size).map(|_| BTSPLayer::new(input_size, tau)).collect(); + let layers = (0..output_size) + .map(|_| BTSPLayer::new(input_size, tau)) + .collect(); Self { layers, diff --git a/crates/ruvector-nervous-system-wasm/src/lib.rs b/crates/ruvector-nervous-system-wasm/src/lib.rs index ad1345c53..7470e16d6 100644 --- a/crates/ruvector-nervous-system-wasm/src/lib.rs +++ b/crates/ruvector-nervous-system-wasm/src/lib.rs @@ -74,14 +74,14 @@ use wasm_bindgen::prelude::*; pub mod btsp; pub mod hdc; -pub mod wta; pub mod workspace; +pub mod wta; // Re-export all public types pub use btsp::{BTSPAssociativeMemory, BTSPLayer, BTSPSynapse}; pub use hdc::{HdcMemory, Hypervector}; -pub use wta::{KWTALayer, WTALayer}; pub use workspace::{GlobalWorkspace, WorkspaceItem}; +pub use wta::{KWTALayer, WTALayer}; /// Initialize the WASM module with panic hook #[wasm_bindgen(start)] @@ -100,7 +100,10 @@ pub fn version() -> String { #[wasm_bindgen] pub fn available_mechanisms() -> JsValue { let mechanisms = vec![ - ("btsp", "Behavioral Timescale Synaptic Plasticity - One-shot learning"), + ( + "btsp", + "Behavioral Timescale Synaptic Plasticity - One-shot learning", + ), ("hdc", "Hyperdimensional Computing - 10,000-bit vectors"), ("wta", "Winner-Take-All - <1us decisions"), ("kwta", "K-Winner-Take-All - Sparse distributed coding"), @@ -128,9 +131,15 @@ pub fn performance_targets() -> JsValue { pub fn biological_references() -> JsValue { let refs = vec![ ("BTSP", "Bittner et al. 2017 - Hippocampal place fields"), - ("HDC", "Kanerva 1988, Plate 2003 - Hyperdimensional computing"), + ( + "HDC", + "Kanerva 1988, Plate 2003 - Hyperdimensional computing", + ), ("WTA", "Cortical microcircuits - Lateral inhibition"), - ("Global Workspace", "Baars 1988, Dehaene 2014 - Consciousness"), + ( + "Global Workspace", + "Baars 1988, Dehaene 2014 - Consciousness", + ), ]; serde_wasm_bindgen::to_value(&refs).unwrap_or(JsValue::NULL) } diff --git a/crates/ruvector-nervous-system-wasm/src/workspace.rs b/crates/ruvector-nervous-system-wasm/src/workspace.rs index 6d905d9e1..316dde606 100644 --- a/crates/ruvector-nervous-system-wasm/src/workspace.rs +++ b/crates/ruvector-nervous-system-wasm/src/workspace.rs @@ -24,7 +24,12 @@ pub struct WorkspaceItem { impl WorkspaceItem { /// Create a new workspace item #[wasm_bindgen(constructor)] - pub fn new(content: &[f32], salience: f32, source_module: u16, timestamp: u64) -> WorkspaceItem { + pub fn new( + content: &[f32], + salience: f32, + source_module: u16, + timestamp: u64, + ) -> WorkspaceItem { Self { content: content.to_vec(), salience, @@ -201,21 +206,26 @@ impl GlobalWorkspace { } // Remove items below threshold - self.buffer.retain(|item| item.salience >= self.salience_threshold); + self.buffer + .retain(|item| item.salience >= self.salience_threshold); } /// Retrieve all current representations as JSON #[wasm_bindgen] pub fn retrieve(&self) -> JsValue { - let items: Vec<_> = self.buffer.iter().map(|item| { - serde_json::json!({ - "content": item.content, - "salience": item.salience, - "source_module": item.source_module, - "timestamp": item.timestamp, - "id": item.id + let items: Vec<_> = self + .buffer + .iter() + .map(|item| { + serde_json::json!({ + "content": item.content, + "salience": item.salience, + "source_module": item.source_module, + "timestamp": item.timestamp, + "id": item.id + }) }) - }).collect(); + .collect(); serde_wasm_bindgen::to_value(&items).unwrap_or(JsValue::NULL) } @@ -231,15 +241,18 @@ impl GlobalWorkspace { }); items.truncate(k); - let result: Vec<_> = items.iter().map(|item| { - serde_json::json!({ - "content": item.content, - "salience": item.salience, - "source_module": item.source_module, - "timestamp": item.timestamp, - "id": item.id + let result: Vec<_> = items + .iter() + .map(|item| { + serde_json::json!({ + "content": item.content, + "salience": item.salience, + "source_module": item.source_module, + "timestamp": item.timestamp, + "id": item.id + }) }) - }).collect(); + .collect(); serde_wasm_bindgen::to_value(&result).unwrap_or(JsValue::NULL) } diff --git a/crates/ruvector-nervous-system-wasm/tests/web.rs b/crates/ruvector-nervous-system-wasm/tests/web.rs index b88537b38..113d345e7 100644 --- a/crates/ruvector-nervous-system-wasm/tests/web.rs +++ b/crates/ruvector-nervous-system-wasm/tests/web.rs @@ -44,11 +44,18 @@ fn test_btsp_one_shot_learning() { let pattern = vec![0.1; 50]; let target = 0.8; - layer.one_shot_associate(&pattern, target).expect("Should learn"); + layer + .one_shot_associate(&pattern, target) + .expect("Should learn"); let output = layer.forward(&pattern).expect("Should compute forward"); // One-shot learning should get close to target - assert!((output - target).abs() < 0.5, "Output: {}, Target: {}", output, target); + assert!( + (output - target).abs() < 0.5, + "Output: {}, Target: {}", + output, + target + ); } #[wasm_bindgen_test] @@ -116,7 +123,11 @@ fn test_hdc_similarity_bounds() { let b = Hypervector::random(); let sim = a.similarity(&b); - assert!(sim >= -1.0 && sim <= 1.0, "Similarity out of bounds: {}", sim); + assert!( + sim >= -1.0 && sim <= 1.0, + "Similarity out of bounds: {}", + sim + ); } #[wasm_bindgen_test] @@ -201,7 +212,9 @@ fn test_kwta_sparse_activations() { let kwta = KWTALayer::new(10, 3).expect("Should create K-WTA"); let inputs: Vec = (0..10).map(|i| i as f32).collect(); - let sparse = kwta.sparse_activations(&inputs).expect("Should create sparse"); + let sparse = kwta + .sparse_activations(&inputs) + .expect("Should create sparse"); assert_eq!(sparse.length(), 10); @@ -279,7 +292,10 @@ fn test_workspace_item_decay() { let mut item = WorkspaceItem::with_decay(&[1.0], 0.8, 1, 0, 0.9, 1000); item.apply_decay(1.0); - assert!((item.salience() - 0.72).abs() < 0.01, "Salience should decay"); + assert!( + (item.salience() - 0.72).abs() < 0.01, + "Salience should decay" + ); } // ============================================================================ diff --git a/crates/ruvector-sparse-inference-wasm/src/lib.rs b/crates/ruvector-sparse-inference-wasm/src/lib.rs index 50ca80a78..d3e8cea8c 100644 --- a/crates/ruvector-sparse-inference-wasm/src/lib.rs +++ b/crates/ruvector-sparse-inference-wasm/src/lib.rs @@ -1,9 +1,9 @@ -use wasm_bindgen::prelude::*; use ruvector_sparse_inference::{ - SparseModel, InferenceConfig, SparsityConfig, + model::{GenerationConfig, GgufParser, KVCache, ModelMetadata, ModelRunner}, predictor::LowRankPredictor, - model::{GgufParser, ModelRunner, ModelMetadata, GenerationConfig, KVCache}, + InferenceConfig, SparseModel, SparsityConfig, }; +use wasm_bindgen::prelude::*; /// Initialize panic hook for better error messages #[wasm_bindgen(start)] @@ -33,12 +33,19 @@ impl SparseInferenceEngine { let predictors = Self::init_predictors(&model, &config); - Ok(Self { model, config, predictors }) + Ok(Self { + model, + config, + predictors, + }) } /// Load model with streaming (for large models) #[wasm_bindgen] - pub async fn load_streaming(url: &str, config_json: &str) -> Result { + pub async fn load_streaming( + url: &str, + config_json: &str, + ) -> Result { // Fetch model in chunks let bytes = fetch_model_bytes(url).await?; Self::new(&bytes, config_json) @@ -47,7 +54,8 @@ impl SparseInferenceEngine { /// Run inference on input #[wasm_bindgen] pub fn infer(&self, input: &[f32]) -> Result, JsError> { - self.model.forward_embedding(input, &self.config) + self.model + .forward_embedding(input, &self.config) .map_err(|e| JsError::new(&format!("Inference failed: {}", e))) } @@ -61,7 +69,8 @@ impl SparseInferenceEngine { ..Default::default() }; - self.model.generate(input_ids, &config) + self.model + .generate(input_ids, &config) .map_err(|e| JsError::new(&format!("Generation failed: {}", e))) } @@ -90,12 +99,10 @@ impl SparseInferenceEngine { /// Calibrate predictors with sample inputs #[wasm_bindgen] pub fn calibrate(&mut self, samples: &[f32], sample_dim: usize) -> Result<(), JsError> { - let samples: Vec> = samples - .chunks(sample_dim) - .map(|c| c.to_vec()) - .collect(); + let samples: Vec> = samples.chunks(sample_dim).map(|c| c.to_vec()).collect(); - self.model.calibrate(&samples) + self.model + .calibrate(&samples) .map_err(|e| JsError::new(&format!("Calibration failed: {}", e))) } @@ -120,7 +127,8 @@ pub struct EmbeddingModel { impl EmbeddingModel { #[wasm_bindgen(constructor)] pub fn new(model_bytes: &[u8]) -> Result { - let config = r#"{"sparsity": {"enabled": true, "threshold": 0.1}, "temperature": 1.0, "top_k": 50}"#; + let config = + r#"{"sparsity": {"enabled": true, "threshold": 0.1}, "temperature": 1.0, "top_k": 50}"#; let engine = SparseInferenceEngine::new(model_bytes, config)?; Ok(Self { engine }) } @@ -128,7 +136,9 @@ impl EmbeddingModel { /// Encode text to embedding (requires tokenizer) #[wasm_bindgen] pub fn encode(&self, input_ids: &[u32]) -> Result, JsError> { - self.engine.model.encode(input_ids) + self.engine + .model + .encode(input_ids) .map_err(|e| JsError::new(&format!("Encoding failed: {}", e))) } @@ -144,7 +154,10 @@ impl EmbeddingModel { return Err(JsError::new("Invalid lengths: exceeds input_ids size")); } let ids = &input_ids[offset..offset + len]; - let embedding = self.engine.model.encode(ids) + let embedding = self + .engine + .model + .encode(ids) .map_err(|e| JsError::new(&format!("Encoding failed: {}", e)))?; results.extend(embedding); offset += len; @@ -180,7 +193,9 @@ impl LLMModel { /// Generate next token #[wasm_bindgen] pub fn next_token(&mut self, input_ids: &[u32]) -> Result { - self.engine.model.next_token(input_ids, &mut self.kv_cache) + self.engine + .model + .next_token(input_ids, &mut self.kv_cache) .map_err(|e| JsError::new(&format!("Generation failed: {}", e))) } @@ -205,7 +220,11 @@ impl LLMModel { /// Performance measurement utilities #[wasm_bindgen] -pub fn measure_inference_time(engine: &SparseInferenceEngine, input: &[f32], iterations: u32) -> f64 { +pub fn measure_inference_time( + engine: &SparseInferenceEngine, + input: &[f32], + iterations: u32, +) -> f64 { let performance = web_sys::window() .and_then(|w| w.performance()) .expect("Performance API not available"); @@ -231,10 +250,15 @@ async fn fetch_model_bytes(url: &str) -> Result, JsError> { let window = web_sys::window().ok_or_else(|| JsError::new("No window"))?; let response = JsFuture::from(window.fetch_with_str(url)).await?; - let response: web_sys::Response = response.dyn_into() + let response: web_sys::Response = response + .dyn_into() .map_err(|_| JsError::new("Failed to cast to Response"))?; - let buffer = JsFuture::from(response.array_buffer() - .map_err(|_| JsError::new("Failed to get array buffer"))?).await?; + let buffer = JsFuture::from( + response + .array_buffer() + .map_err(|_| JsError::new("Failed to get array buffer"))?, + ) + .await?; let array = js_sys::Uint8Array::new(&buffer); Ok(array.to_vec()) } diff --git a/crates/ruvector-sparse-inference-wasm/tests/web.rs b/crates/ruvector-sparse-inference-wasm/tests/web.rs index 56cc50999..1380ad07a 100644 --- a/crates/ruvector-sparse-inference-wasm/tests/web.rs +++ b/crates/ruvector-sparse-inference-wasm/tests/web.rs @@ -1,7 +1,7 @@ #![cfg(target_arch = "wasm32")] -use wasm_bindgen_test::*; use ruvector_sparse_inference_wasm::*; +use wasm_bindgen_test::*; wasm_bindgen_test_configure!(run_in_browser); @@ -145,7 +145,9 @@ fn test_measure_inference_time() { #[wasm_bindgen_test] async fn test_load_streaming_with_bad_url() { let config = r#"{"sparsity": {"enabled": true}}"#; - let result = SparseInferenceEngine::load_streaming("https://invalid.example.com/model.gguf", config).await; + let result = + SparseInferenceEngine::load_streaming("https://invalid.example.com/model.gguf", config) + .await; // Should fail gracefully assert!(result.is_err()); diff --git a/crates/ruvector-sparse-inference/benches/simd_kernels.rs b/crates/ruvector-sparse-inference/benches/simd_kernels.rs index d54b647e0..c17ab8e43 100644 --- a/crates/ruvector-sparse-inference/benches/simd_kernels.rs +++ b/crates/ruvector-sparse-inference/benches/simd_kernels.rs @@ -1,7 +1,7 @@ //! Benchmarks for SIMD kernel performance -use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId}; -use ruvector_sparse_inference::backend::{Backend, cpu::CpuBackend}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; +use ruvector_sparse_inference::backend::{cpu::CpuBackend, Backend}; use ruvector_sparse_inference::sparse::ActivationType; fn bench_dot_product(c: &mut Criterion) { @@ -13,9 +13,7 @@ fn bench_dot_product(c: &mut Criterion) { let b: Vec = (0..*size).map(|i| (i * 2) as f32).collect(); group.bench_with_input(BenchmarkId::from_parameter(size), size, |bench, _| { - bench.iter(|| { - black_box(backend.dot_product(black_box(&a), black_box(&b))) - }); + bench.iter(|| black_box(backend.dot_product(black_box(&a), black_box(&b)))); }); } group.finish(); diff --git a/crates/ruvector-sparse-inference/benches/sparse_inference_bench.rs b/crates/ruvector-sparse-inference/benches/sparse_inference_bench.rs index 1ce65e202..793ac21ec 100644 --- a/crates/ruvector-sparse-inference/benches/sparse_inference_bench.rs +++ b/crates/ruvector-sparse-inference/benches/sparse_inference_bench.rs @@ -1,11 +1,10 @@ //! Benchmark tests for sparse inference -use criterion::{criterion_group, criterion_main, Criterion, BenchmarkId, black_box}; -use ruvector_sparse_inference::{ - SparseInferenceEngine, SparseFfn, LowRankPredictor, Predictor, - SparsityConfig, ActivationType, -}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use rand::Rng; +use ruvector_sparse_inference::{ + ActivationType, LowRankPredictor, Predictor, SparseFfn, SparseInferenceEngine, SparsityConfig, +}; // Test utilities fn random_vector(dim: usize) -> Vec { @@ -21,15 +20,11 @@ fn benchmark_sparse_vs_dense(c: &mut Criterion) { let mut group = c.benchmark_group("inference"); group.bench_function("dense", |b| { - b.iter(|| { - black_box(dense_engine.infer(&input).unwrap()) - }) + b.iter(|| black_box(dense_engine.infer(&input).unwrap())) }); group.bench_function("sparse_70pct", |b| { - b.iter(|| { - black_box(sparse_engine.infer(&input).unwrap()) - }) + b.iter(|| black_box(sparse_engine.infer(&input).unwrap())) }); group.finish(); @@ -41,9 +36,7 @@ fn benchmark_predictor(c: &mut Criterion) { let input = random_vector(512); c.bench_function("predictor_predict", |b| { - b.iter(|| { - black_box(predictor.predict(&input).unwrap()) - }) + b.iter(|| black_box(predictor.predict(&input).unwrap())) }); } @@ -55,15 +48,9 @@ fn benchmark_predictor_top_k(c: &mut Criterion) { let config = SparsityConfig::with_top_k(k); let predictor = LowRankPredictor::new(512, 4096, 128, config).unwrap(); - group.bench_with_input( - BenchmarkId::from_parameter(k), - &input, - |b, input| { - b.iter(|| { - black_box(predictor.predict(input).unwrap()) - }) - }, - ); + group.bench_with_input(BenchmarkId::from_parameter(k), &input, |b, input| { + b.iter(|| black_box(predictor.predict(input).unwrap())) + }); } group.finish(); @@ -76,23 +63,17 @@ fn benchmark_sparse_ffn(c: &mut Criterion) { let mut group = c.benchmark_group("sparse_ffn"); group.bench_function("dense_forward", |b| { - b.iter(|| { - black_box(ffn.forward_dense(&input).unwrap()) - }) + b.iter(|| black_box(ffn.forward_dense(&input).unwrap())) }); let active_10pct: Vec = (0..204).collect(); group.bench_function("sparse_10pct", |b| { - b.iter(|| { - black_box(ffn.forward_sparse(&input, &active_10pct).unwrap()) - }) + b.iter(|| black_box(ffn.forward_sparse(&input, &active_10pct).unwrap())) }); let active_50pct: Vec = (0..1024).collect(); group.bench_function("sparse_50pct", |b| { - b.iter(|| { - black_box(ffn.forward_sparse(&input, &active_50pct).unwrap()) - }) + b.iter(|| black_box(ffn.forward_sparse(&input, &active_50pct).unwrap())) }); group.finish(); @@ -112,15 +93,9 @@ fn benchmark_activation_functions(c: &mut Criterion) { let ffn = SparseFfn::new(512, 2048, 512, activation).unwrap(); let name = format!("{:?}", activation); - group.bench_with_input( - BenchmarkId::from_parameter(&name), - &input, - |b, input| { - b.iter(|| { - black_box(ffn.forward_sparse(input, &active).unwrap()) - }) - }, - ); + group.bench_with_input(BenchmarkId::from_parameter(&name), &input, |b, input| { + b.iter(|| black_box(ffn.forward_sparse(input, &active).unwrap())) + }); } group.finish(); @@ -139,11 +114,7 @@ fn benchmark_sparsity_levels(c: &mut Criterion) { group.bench_with_input( BenchmarkId::from_parameter(format!("{}%_active", active_pct)), &(&input, &active), - |b, (input, active)| { - b.iter(|| { - black_box(ffn.forward_sparse(input, active).unwrap()) - }) - }, + |b, (input, active)| b.iter(|| black_box(ffn.forward_sparse(input, active).unwrap())), ); } diff --git a/crates/ruvector-sparse-inference/examples/basic_usage.rs b/crates/ruvector-sparse-inference/examples/basic_usage.rs index 065af736f..82c84c28d 100644 --- a/crates/ruvector-sparse-inference/examples/basic_usage.rs +++ b/crates/ruvector-sparse-inference/examples/basic_usage.rs @@ -1,8 +1,8 @@ //! Basic usage example for the sparse inference engine +use ndarray::Array2; use ruvector_sparse_inference::backend::get_backend; use ruvector_sparse_inference::sparse::ActivationType; -use ndarray::Array2; fn main() { // Get the best available backend for this platform @@ -40,12 +40,10 @@ fn main() { let matrix = Array2::from_shape_vec( (4, 4), vec![ - 1.0, 0.0, 2.0, 0.0, - 0.0, 3.0, 0.0, 4.0, - 5.0, 0.0, 6.0, 0.0, - 0.0, 7.0, 0.0, 8.0, + 1.0, 0.0, 2.0, 0.0, 0.0, 3.0, 0.0, 4.0, 5.0, 0.0, 6.0, 0.0, 0.0, 7.0, 0.0, 8.0, ], - ).unwrap(); + ) + .unwrap(); let input = vec![1.0, 2.0, 3.0, 4.0]; // Only compute rows 0 and 2 (sparse computation) @@ -60,7 +58,11 @@ fn main() { // Example 5: Different activation functions println!("\n=== Activation Functions ==="); - for activation in [ActivationType::Relu, ActivationType::Gelu, ActivationType::Silu] { + for activation in [ + ActivationType::Relu, + ActivationType::Gelu, + ActivationType::Silu, + ] { let mut data = vec![-1.0, 0.0, 1.0, 2.0]; backend.activation(&mut data, activation); println!("{:?}: {:?}", activation, data); diff --git a/crates/ruvector-sparse-inference/tests/backend_simd_tests.rs b/crates/ruvector-sparse-inference/tests/backend_simd_tests.rs index e44b6f194..6815a3582 100644 --- a/crates/ruvector-sparse-inference/tests/backend_simd_tests.rs +++ b/crates/ruvector-sparse-inference/tests/backend_simd_tests.rs @@ -1,8 +1,8 @@ //! Standalone tests for SIMD backend kernels -use ruvector_sparse_inference::backend::{Backend, cpu::CpuBackend, get_backend}; -use ruvector_sparse_inference::config::ActivationType; use ndarray::Array2; +use ruvector_sparse_inference::backend::{cpu::CpuBackend, get_backend, Backend}; +use ruvector_sparse_inference::config::ActivationType; #[test] fn test_cpu_backend_dot_product() { @@ -12,14 +12,23 @@ fn test_cpu_backend_dot_product() { let a = vec![1.0, 2.0, 3.0, 4.0]; let b = vec![2.0, 3.0, 4.0, 5.0]; let result = backend.dot_product(&a, &b); - assert!((result - 40.0).abs() < 1e-5, "Expected 40.0, got {}", result); + assert!( + (result - 40.0).abs() < 1e-5, + "Expected 40.0, got {}", + result + ); // Test larger vector (exercises SIMD paths) let a: Vec = (0..256).map(|i| i as f32).collect(); let b: Vec = (0..256).map(|i| (i * 2) as f32).collect(); let result = backend.dot_product(&a, &b); let expected: f32 = (0..256).map(|i| (i * i * 2) as f32).sum(); - assert!((result - expected).abs() < 1.0, "Expected {}, got {}", expected, result); + assert!( + (result - expected).abs() < 1.0, + "Expected {}, got {}", + expected, + result + ); } #[test] @@ -35,7 +44,13 @@ fn test_cpu_backend_relu() { backend.activation(&mut data, ActivationType::Relu); for (i, &val) in data.iter().enumerate() { let expected = (i as f32 - 128.0).max(0.0); - assert!((val - expected).abs() < 1e-5, "Index {}: expected {}, got {}", i, expected, val); + assert!( + (val - expected).abs() < 1e-5, + "Index {}: expected {}, got {}", + i, + expected, + val + ); } } @@ -47,13 +62,25 @@ fn test_cpu_backend_gelu() { backend.activation(&mut data, ActivationType::Gelu); // GELU(0) ≈ 0 - assert!(data[0].abs() < 0.01, "GELU(0) should be ≈0, got {}", data[0]); + assert!( + data[0].abs() < 0.01, + "GELU(0) should be ≈0, got {}", + data[0] + ); // GELU(1) ≈ 0.841 - assert!((data[1] - 0.841).abs() < 0.01, "GELU(1) should be ≈0.841, got {}", data[1]); + assert!( + (data[1] - 0.841).abs() < 0.01, + "GELU(1) should be ≈0.841, got {}", + data[1] + ); // GELU(-1) ≈ -0.159 (GELU is NOT an odd function) - assert!((data[2] + 0.159).abs() < 0.1, "GELU(-1) should be ≈-0.159, got {}", data[2]); + assert!( + (data[2] + 0.159).abs() < 0.1, + "GELU(-1) should be ≈-0.159, got {}", + data[2] + ); } #[test] @@ -64,10 +91,18 @@ fn test_cpu_backend_silu() { backend.activation(&mut data, ActivationType::Silu); // SiLU(0) ≈ 0 - assert!(data[0].abs() < 0.01, "SiLU(0) should be ≈0, got {}", data[0]); + assert!( + data[0].abs() < 0.01, + "SiLU(0) should be ≈0, got {}", + data[0] + ); // SiLU(1) ≈ 0.731 - assert!((data[1] - 0.731).abs() < 0.01, "SiLU(1) should be ≈0.731, got {}", data[1]); + assert!( + (data[1] - 0.731).abs() < 0.01, + "SiLU(1) should be ≈0.731, got {}", + data[1] + ); } #[test] @@ -98,12 +133,10 @@ fn test_cpu_backend_sparse_matmul() { let matrix = Array2::from_shape_vec( (4, 4), vec![ - 1.0, 0.0, 2.0, 0.0, - 0.0, 3.0, 0.0, 4.0, - 5.0, 0.0, 6.0, 0.0, - 0.0, 7.0, 0.0, 8.0, + 1.0, 0.0, 2.0, 0.0, 0.0, 3.0, 0.0, 4.0, 5.0, 0.0, 6.0, 0.0, 0.0, 7.0, 0.0, 8.0, ], - ).unwrap(); + ) + .unwrap(); let input = vec![1.0, 2.0, 3.0, 4.0]; @@ -125,12 +158,10 @@ fn test_cpu_backend_sparse_matmul_accumulate() { let matrix = Array2::from_shape_vec( (4, 4), vec![ - 1.0, 2.0, 3.0, 4.0, - 5.0, 6.0, 7.0, 8.0, - 9.0, 10.0, 11.0, 12.0, - 13.0, 14.0, 15.0, 16.0, + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, ], - ).unwrap(); + ) + .unwrap(); let input = vec![1.0, 2.0]; let active_cols = vec![0, 2]; @@ -140,10 +171,10 @@ fn test_cpu_backend_sparse_matmul_accumulate() { // Column 0 * 1.0 + Column 2 * 2.0 // [1, 5, 9, 13] * 1.0 + [3, 7, 11, 15] * 2.0 - assert!((output[0] - 7.0).abs() < 1e-5); // 1 + 6 - assert!((output[1] - 19.0).abs() < 1e-5); // 5 + 14 - assert!((output[2] - 31.0).abs() < 1e-5); // 9 + 22 - assert!((output[3] - 43.0).abs() < 1e-5); // 13 + 30 + assert!((output[0] - 7.0).abs() < 1e-5); // 1 + 6 + assert!((output[1] - 19.0).abs() < 1e-5); // 5 + 14 + assert!((output[2] - 31.0).abs() < 1e-5); // 9 + 22 + assert!((output[3] - 43.0).abs() < 1e-5); // 13 + 30 } #[test] @@ -165,7 +196,11 @@ fn test_backend_simd_width() { let width = backend.simd_width(); // Width should be 1, 4, or 8 depending on CPU features - assert!(width == 1 || width == 4 || width == 8, "Unexpected SIMD width: {}", width); + assert!( + width == 1 || width == 4 || width == 8, + "Unexpected SIMD width: {}", + width + ); println!("Backend: {}", backend.name()); println!("SIMD width: {}", width); diff --git a/crates/ruvector-wasm/src/kernel/epoch.rs b/crates/ruvector-wasm/src/kernel/epoch.rs index 3c0718eec..2fadde07c 100644 --- a/crates/ruvector-wasm/src/kernel/epoch.rs +++ b/crates/ruvector-wasm/src/kernel/epoch.rs @@ -145,8 +145,8 @@ impl EpochConfig { EpochConfig { enabled: true, tick_interval_ms: 1, - default_budget: 100, // 100ms - max_budget: 1000, // 1 second max + default_budget: 100, // 100ms + max_budget: 1000, // 1 second max } } @@ -266,10 +266,7 @@ mod tests { let controller = EpochController::new(Duration::from_millis(10)); assert_eq!(controller.ticks_to_duration(100), Duration::from_secs(1)); - assert_eq!( - controller.duration_to_ticks(Duration::from_secs(1)), - 100 - ); + assert_eq!(controller.duration_to_ticks(Duration::from_secs(1)), 100); } #[test] diff --git a/crates/ruvector-wasm/src/kernel/error.rs b/crates/ruvector-wasm/src/kernel/error.rs index da8655d33..b41cfdcee 100644 --- a/crates/ruvector-wasm/src/kernel/error.rs +++ b/crates/ruvector-wasm/src/kernel/error.rs @@ -248,13 +248,12 @@ impl fmt::Display for VerifyError { ) } VerifyError::RuntimeTooOld { required, actual } => { - write!( - f, - "Runtime too old: requires {}, have {}", - required, actual - ) + write!(f, "Runtime too old: requires {}, have {}", required, actual) } - VerifyError::RuntimeTooNew { max_supported, actual } => { + VerifyError::RuntimeTooNew { + max_supported, + actual, + } => { write!( f, "Runtime too new: max supported {}, have {}", diff --git a/crates/ruvector-wasm/src/kernel/runtime.rs b/crates/ruvector-wasm/src/kernel/runtime.rs index c4eb37f15..c89633202 100644 --- a/crates/ruvector-wasm/src/kernel/runtime.rs +++ b/crates/ruvector-wasm/src/kernel/runtime.rs @@ -351,21 +351,28 @@ impl KernelManager { } /// Compile a kernel from a loaded pack - pub fn compile_kernel(&mut self, pack_name: &str, kernel_id: &str, wasm_bytes: &[u8]) -> KernelResult<()> { - let manifest = self.manifests.get(pack_name).ok_or_else(|| { - KernelError::KernelNotFound { - kernel_id: format!("pack:{}", pack_name), - } - })?; + pub fn compile_kernel( + &mut self, + pack_name: &str, + kernel_id: &str, + wasm_bytes: &[u8], + ) -> KernelResult<()> { + let manifest = + self.manifests + .get(pack_name) + .ok_or_else(|| KernelError::KernelNotFound { + kernel_id: format!("pack:{}", pack_name), + })?; - let info = manifest.get_kernel(kernel_id).ok_or_else(|| { - KernelError::KernelNotFound { + let info = manifest + .get_kernel(kernel_id) + .ok_or_else(|| KernelError::KernelNotFound { kernel_id: kernel_id.to_string(), - } - })?; + })?; let compiled = self.runtime.compile_kernel(kernel_id, wasm_bytes, info)?; - self.compiled_kernels.insert(kernel_id.to_string(), compiled); + self.compiled_kernels + .insert(kernel_id.to_string(), compiled); Ok(()) } @@ -389,21 +396,19 @@ impl KernelManager { descriptor: &KernelDescriptor, memory: &mut [u8], ) -> KernelResult<()> { - let compiled = self.compiled_kernels.get(kernel_id).ok_or_else(|| { - KernelError::KernelNotFound { - kernel_id: kernel_id.to_string(), - } - })?; + let compiled = + self.compiled_kernels + .get(kernel_id) + .ok_or_else(|| KernelError::KernelNotFound { + kernel_id: kernel_id.to_string(), + })?; let mut instance = self.runtime.instantiate(compiled)?; // Set deadline if epoch is enabled if self.runtime.config().epoch.enabled { let budget = compiled.info.resource_limits.max_epoch_ticks; - let deadline = EpochDeadline::new( - self.runtime.epoch_controller().current(), - budget, - ); + let deadline = EpochDeadline::new(self.runtime.epoch_controller().current(), budget); instance.set_deadline(deadline); } @@ -429,7 +434,7 @@ impl KernelManager { #[cfg(test)] mod tests { use super::*; - use crate::kernel::manifest::{KernelCategory, ResourceLimits, TensorSpec, DataType, ShapeDim}; + use crate::kernel::manifest::{DataType, KernelCategory, ResourceLimits, ShapeDim, TensorSpec}; fn mock_kernel_info(id: &str) -> KernelInfo { KernelInfo { @@ -495,10 +500,15 @@ mod tests { #[test] fn test_mock_runtime_failure() { let mut runtime = MockKernelRuntime::new(RuntimeConfig::default()); - runtime.register_behavior("failing_kernel", MockKernelBehavior::Fail(KernelErrorCode::InvalidInput)); + runtime.register_behavior( + "failing_kernel", + MockKernelBehavior::Fail(KernelErrorCode::InvalidInput), + ); let info = mock_kernel_info("failing_kernel"); - let compiled = runtime.compile_kernel("failing_kernel", &[], &info).unwrap(); + let compiled = runtime + .compile_kernel("failing_kernel", &[], &info) + .unwrap(); let mut instance = runtime.instantiate(&compiled).unwrap(); let desc = KernelDescriptor::new(); @@ -556,7 +566,9 @@ mod tests { manager.set_active_pack("test-pack").unwrap(); // Compile kernel - manager.compile_kernel("test-pack", "rope_f32", &[]).unwrap(); + manager + .compile_kernel("test-pack", "rope_f32", &[]) + .unwrap(); assert_eq!(manager.list_kernels(), vec!["rope_f32"]); } diff --git a/crates/ruvector-wasm/src/kernel/signature.rs b/crates/ruvector-wasm/src/kernel/signature.rs index 2e78db746..fc38c64fe 100644 --- a/crates/ruvector-wasm/src/kernel/signature.rs +++ b/crates/ruvector-wasm/src/kernel/signature.rs @@ -142,11 +142,12 @@ impl KernelPackVerifier { /// Verify manifest with signature from base64 string pub fn verify_base64(&self, manifest: &[u8], signature_b64: &str) -> Result<(), VerifyError> { use base64::{engine::general_purpose::STANDARD, Engine}; - let signature = STANDARD - .decode(signature_b64) - .map_err(|e| VerifyError::InvalidSignature { - reason: format!("Invalid base64 signature: {}", e), - })?; + let signature = + STANDARD + .decode(signature_b64) + .map_err(|e| VerifyError::InvalidSignature { + reason: format!("Invalid base64 signature: {}", e), + })?; self.verify(manifest, &signature) } diff --git a/crates/ruvllm-cli/src/commands/benchmark.rs b/crates/ruvllm-cli/src/commands/benchmark.rs index 17bb1a592..364e06a15 100644 --- a/crates/ruvllm-cli/src/commands/benchmark.rs +++ b/crates/ruvllm-cli/src/commands/benchmark.rs @@ -151,9 +151,7 @@ pub async fn run( let token_count = text.split_whitespace().count(); tokens_generated.push(token_count); // Estimate TTFT as a fraction of total time - ttft_times.push(Duration::from_secs_f64( - total_time.as_secs_f64() * 0.1, - )); + ttft_times.push(Duration::from_secs_f64(total_time.as_secs_f64() * 0.1)); } else { tokens_generated.push(gen_length); ttft_times.push(Duration::from_millis(50)); @@ -248,21 +246,25 @@ fn calculate_metrics( ttft_times: &[Duration], tokens_generated: &[usize], ) -> BenchmarkMetrics { - let total_time_ms = latencies.iter().map(|d| d.as_secs_f64() * 1000.0).sum::() + let total_time_ms = latencies + .iter() + .map(|d| d.as_secs_f64() * 1000.0) + .sum::() / latencies.len() as f64; let total_tokens: usize = tokens_generated.iter().sum(); let total_duration: Duration = latencies.iter().sum(); let tokens_per_second = total_tokens as f64 / total_duration.as_secs_f64(); - let ttft_avg = ttft_times.iter().map(|d| d.as_secs_f64() * 1000.0).sum::() + let ttft_avg = ttft_times + .iter() + .map(|d| d.as_secs_f64() * 1000.0) + .sum::() / ttft_times.len() as f64; // Calculate percentiles - let mut sorted_latencies: Vec = latencies - .iter() - .map(|d| d.as_secs_f64() * 1000.0) - .collect(); + let mut sorted_latencies: Vec = + latencies.iter().map(|d| d.as_secs_f64() * 1000.0).collect(); sorted_latencies.sort_by(|a, b| a.partial_cmp(b).unwrap()); let p50_idx = (sorted_latencies.len() as f64 * 0.50) as usize; @@ -367,9 +369,18 @@ fn print_results(results: &BenchmarkResults) { println!("{}", style("Latency Distribution").bold()); let mut lat_table = Table::new(); lat_table.add_row(row!["Percentile", "Latency (ms)"]); - lat_table.add_row(row!["P50", format!("{:.2}", results.metrics.latency_p50_ms)]); - lat_table.add_row(row!["P95", format!("{:.2}", results.metrics.latency_p95_ms)]); - lat_table.add_row(row!["P99", format!("{:.2}", results.metrics.latency_p99_ms)]); + lat_table.add_row(row![ + "P50", + format!("{:.2}", results.metrics.latency_p50_ms) + ]); + lat_table.add_row(row![ + "P95", + format!("{:.2}", results.metrics.latency_p95_ms) + ]); + lat_table.add_row(row![ + "P99", + format!("{:.2}", results.metrics.latency_p99_ms) + ]); lat_table.printstd(); println!(); diff --git a/crates/ruvllm-cli/src/commands/chat.rs b/crates/ruvllm-cli/src/commands/chat.rs index 95db83c29..ca2c7a88d 100644 --- a/crates/ruvllm-cli/src/commands/chat.rs +++ b/crates/ruvllm-cli/src/commands/chat.rs @@ -68,12 +68,18 @@ pub async fn run( info.num_parameters as f64 / 1e9 ); } else { - println!("{} Model loaded (mock mode)", style("Ready!").yellow().bold()); + println!( + "{} Model loaded (mock mode)", + style("Ready!").yellow().bold() + ); } // Load draft model for speculative decoding if provided let (draft_backend, speculative_config) = if let Some(draft_id) = draft_model { - println!("{}", "Loading draft model for speculative decoding...".yellow()); + println!( + "{}", + "Loading draft model for speculative decoding...".yellow() + ); let draft = load_model(&resolve_model_id(draft_id), quant, cache_dir)?; if let Some(info) = draft.model_info() { @@ -122,7 +128,10 @@ pub async fn run( } println!(); - println!("{}", "Type your message and press Enter. Special commands:".dimmed()); + println!( + "{}", + "Type your message and press Enter. Special commands:".dimmed() + ); println!("{}", " /clear - Clear conversation history".dimmed()); println!("{}", " /system - Set system prompt".dimmed()); println!("{}", " /save - Save conversation to file".dimmed()); @@ -272,11 +281,15 @@ fn generate_response(session: &mut ChatSession, user_input: &str) -> Result Result { +async fn download_with_progress( + repo: &hf_hub::api::tokio::ApiRepo, + file_name: &str, +) -> Result { // Create progress bar let pb = ProgressBar::new(100); pb.set_style( diff --git a/crates/ruvllm-cli/src/commands/info.rs b/crates/ruvllm-cli/src/commands/info.rs index db564f19f..e4243d284 100644 --- a/crates/ruvllm-cli/src/commands/info.rs +++ b/crates/ruvllm-cli/src/commands/info.rs @@ -16,11 +16,7 @@ pub async fn run(model: &str, cache_dir: &str) -> Result<()> { let model_id = resolve_model_id(model); println!(); - println!( - "{} {}", - style("Model Information:").bold().cyan(), - model_id - ); + println!("{} {}", style("Model Information:").bold().cyan(), model_id); println!(); // Check if model is from our recommended list @@ -43,11 +39,7 @@ pub async fn run(model: &str, cache_dir: &str) -> Result<()> { print_local_info(&model_path).await?; } else { println!(); - println!( - "{} {}", - style("Status:").bold(), - "Not downloaded".red() - ); + println!("{} {}", style("Status:").bold(), "Not downloaded".red()); println!(); println!("Run 'ruvllm download {}' to download.", model); } @@ -128,7 +120,10 @@ async fn fetch_model_info(model_id: &str) -> Result<()> { } } Err(_) => { - println!(" {} Could not fetch model configuration", "Warning:".yellow()); + println!( + " {} Could not fetch model configuration", + "Warning:".yellow() + ); } } @@ -173,12 +168,20 @@ async fn print_local_info(model_path: &PathBuf) -> Result<()> { println!( " {} {}", "Tokenizer:".dimmed(), - if has_tokenizer { "Yes".green() } else { "No".red() } + if has_tokenizer { + "Yes".green() + } else { + "No".red() + } ); println!( " {} {}", "Config:".dimmed(), - if has_config { "Yes".green() } else { "No".red() } + if has_config { + "Yes".green() + } else { + "No".red() + } ); println!( " {} {}", @@ -194,12 +197,31 @@ fn print_memory_estimates(model: &str) { if let Some(model_def) = get_model(model) { let params = model_def.params_b; - println!(" {} {:>8}", "Q4_K_M (4-bit):".dimmed(), format!("{:.1} GB", QuantPreset::Q4K.estimate_memory_gb(params))); - println!(" {} {:>8}", "Q8_0 (8-bit):".dimmed(), format!("{:.1} GB", QuantPreset::Q8.estimate_memory_gb(params))); - println!(" {} {:>8}", "F16 (16-bit):".dimmed(), format!("{:.1} GB", QuantPreset::F16.estimate_memory_gb(params))); - println!(" {} {:>8}", "F32 (32-bit):".dimmed(), format!("{:.1} GB", QuantPreset::None.estimate_memory_gb(params))); + println!( + " {} {:>8}", + "Q4_K_M (4-bit):".dimmed(), + format!("{:.1} GB", QuantPreset::Q4K.estimate_memory_gb(params)) + ); + println!( + " {} {:>8}", + "Q8_0 (8-bit):".dimmed(), + format!("{:.1} GB", QuantPreset::Q8.estimate_memory_gb(params)) + ); + println!( + " {} {:>8}", + "F16 (16-bit):".dimmed(), + format!("{:.1} GB", QuantPreset::F16.estimate_memory_gb(params)) + ); + println!( + " {} {:>8}", + "F32 (32-bit):".dimmed(), + format!("{:.1} GB", QuantPreset::None.estimate_memory_gb(params)) + ); } else { - println!(" {} Memory estimates not available for custom models", "Note:".dimmed()); + println!( + " {} Memory estimates not available for custom models", + "Note:".dimmed() + ); } } @@ -219,18 +241,31 @@ fn print_recommended_settings(model: &str) { println!(" {} {}", "Temperature:".dimmed(), temp); println!(" {} {}", "Top-P:".dimmed(), top_p); println!(" {} {} tokens", "Context:".dimmed(), context); - println!(" {} {}", "Quantization:".dimmed(), model_def.recommended_quant); + println!( + " {} {}", + "Quantization:".dimmed(), + model_def.recommended_quant + ); // Special notes based on model match model_def.alias.as_str() { "qwen-coder" => { - println!(" {} Use lower temperature (0.1-0.3) for code completion", "Tip:".cyan()); + println!( + " {} Use lower temperature (0.1-0.3) for code completion", + "Tip:".cyan() + ); } "llama" => { - println!(" {} Excellent for function calling and structured output", "Tip:".cyan()); + println!( + " {} Excellent for function calling and structured output", + "Tip:".cyan() + ); } "phi" => { - println!(" {} Great for quick testing and resource-constrained environments", "Tip:".cyan()); + println!( + " {} Great for quick testing and resource-constrained environments", + "Tip:".cyan() + ); } _ => {} } diff --git a/crates/ruvllm-cli/src/commands/quantize.rs b/crates/ruvllm-cli/src/commands/quantize.rs index 36b2ae5d3..1a4edfe73 100644 --- a/crates/ruvllm-cli/src/commands/quantize.rs +++ b/crates/ruvllm-cli/src/commands/quantize.rs @@ -4,7 +4,7 @@ //! Optimized for Apple Neural Engine inference on M4 Pro and other Apple Silicon. use std::fs::{self, File}; -use std::io::{BufReader, BufWriter, Read, Write, Seek, SeekFrom}; +use std::io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write}; use std::path::{Path, PathBuf}; use std::time::Instant; @@ -12,9 +12,8 @@ use colored::Colorize; use indicatif::{ProgressBar, ProgressStyle}; use ruvllm::{ - RuvltraQuantizer, QuantConfig, TargetFormat, - estimate_memory_q4, estimate_memory_q5, estimate_memory_q8, - GgufFile, GgufQuantType, + estimate_memory_q4, estimate_memory_q5, estimate_memory_q8, GgufFile, GgufQuantType, + QuantConfig, RuvltraQuantizer, TargetFormat, }; /// Run the quantize command @@ -36,13 +35,13 @@ pub async fn run( ) })?; - println!( - "\n{} RuvLTRA Model Quantizer", - "==>".bright_blue().bold() - ); + println!("\n{} RuvLTRA Model Quantizer", "==>".bright_blue().bold()); println!(" Target format: {}", format.name().bright_cyan()); println!(" Bits per weight: {:.1}", format.bits_per_weight()); - println!(" ANE optimization: {}", if ane_optimize { "enabled" } else { "disabled" }); + println!( + " ANE optimization: {}", + if ane_optimize { "enabled" } else { "disabled" } + ); // Resolve input model path let input_path = resolve_model_path(model, cache_dir)?; @@ -55,11 +54,15 @@ pub async fn run( // Determine output path let output_path = if output.is_empty() { // Generate output name based on input - let stem = input_path.file_stem() + let stem = input_path + .file_stem() .and_then(|s| s.to_str()) .unwrap_or("model"); let output_name = format!("{}-{}.gguf", stem, quant.to_lowercase()); - input_path.parent().unwrap_or(Path::new(".")).join(output_name) + input_path + .parent() + .unwrap_or(Path::new(".")) + .join(output_name) } else { PathBuf::from(output) }; @@ -72,7 +75,10 @@ pub async fn run( // Check if input exists if !input_path.exists() { - return Err(anyhow::anyhow!("Input model not found: {}", input_path.display())); + return Err(anyhow::anyhow!( + "Input model not found: {}", + input_path.display() + )); } // Check if output already exists @@ -115,15 +121,13 @@ pub async fn run( config.keep_output_fp16 = keep_output_fp16; // Check if input is GGUF - let is_gguf = input_path.extension() + let is_gguf = input_path + .extension() .and_then(|e| e.to_str()) .map(|e| e.to_lowercase() == "gguf") .unwrap_or(false); - println!( - "\n{} Starting quantization...", - "==>".bright_blue().bold() - ); + println!("\n{} Starting quantization...", "==>".bright_blue().bold()); let start_time = Instant::now(); @@ -141,10 +145,7 @@ pub async fn run( let output_metadata = fs::metadata(&output_path)?; let output_size = output_metadata.len(); - println!( - "\n{} Quantization complete!", - "==>".bright_green().bold() - ); + println!("\n{} Quantization complete!", "==>".bright_green().bold()); println!( " Output size: {:.2} MB", output_size as f64 / (1024.0 * 1024.0) @@ -153,10 +154,7 @@ pub async fn run( " Compression: {:.1}x", input_size as f64 / output_size as f64 ); - println!( - " Time: {:.1}s", - elapsed.as_secs_f64() - ); + println!(" Time: {:.1}s", elapsed.as_secs_f64()); println!( " Throughput: {:.1} MB/s", input_size as f64 / (1024.0 * 1024.0) / elapsed.as_secs_f64() @@ -173,11 +171,7 @@ pub async fn run( "\n{} To use the quantized model:", "Tip:".bright_cyan().bold() ); - println!( - " ruvllm chat {} -q {}", - output_path.display(), - quant - ); + println!(" ruvllm chat {} -q {}", output_path.display(), quant); Ok(()) } @@ -248,23 +242,20 @@ fn print_memory_estimates(format: TargetFormat) { let est_05b = estimate_fn(0.5, 151936, 896, 24); println!( " RuvLTRA-Small (0.5B): {:.0} MB ({:.1}x compression)", - est_05b.total_mb, - est_05b.compression_ratio + est_05b.total_mb, est_05b.compression_ratio ); // Also show for 1B and 3B for reference let est_1b = estimate_fn(1.0, 151936, 1536, 28); println!( " 1B model: {:.0} MB ({:.1}x compression)", - est_1b.total_mb, - est_1b.compression_ratio + est_1b.total_mb, est_1b.compression_ratio ); let est_3b = estimate_fn(3.0, 151936, 2048, 36); println!( " 3B model: {:.0} MB ({:.1}x compression)", - est_3b.total_mb, - est_3b.compression_ratio + est_3b.total_mb, est_3b.compression_ratio ); } @@ -282,10 +273,7 @@ async fn quantize_gguf_model( " Architecture: {}", gguf.architecture().unwrap_or("unknown") ); - println!( - " Tensors: {}", - gguf.tensors.len() - ); + println!(" Tensors: {}", gguf.tensors.len()); let total_size: usize = gguf.tensors.iter().map(|t| t.byte_size()).sum(); @@ -334,14 +322,8 @@ async fn quantize_gguf_model( // Print stats let stats = quantizer.stats(); if verbose { - println!( - "\n Tensors quantized: {}", - stats.tensors_quantized - ); - println!( - " Elements processed: {}", - stats.elements_processed - ); + println!("\n Tensors quantized: {}", stats.tensors_quantized); + println!(" Elements processed: {}", stats.elements_processed); } Ok(()) @@ -374,7 +356,8 @@ async fn quantize_model( pb.set_message("Loading model..."); // Check file type and process accordingly - let extension = input_path.extension() + let extension = input_path + .extension() .and_then(|e| e.to_str()) .map(|e| e.to_lowercase()) .unwrap_or_default(); @@ -415,8 +398,7 @@ async fn quantize_model( let stats = quantizer.stats(); println!( "\n Quantizer stats: {} tensors, {} elements", - stats.tensors_quantized, - stats.elements_processed + stats.tensors_quantized, stats.elements_processed ); } @@ -462,15 +444,25 @@ pub fn print_format_comparison() { "==>".bright_blue().bold() ); println!(); - println!(" {:<10} {:<8} {:<12} {:<12} {:<15}", - "Format", "Bits", "Memory (0.5B)", "Quality", "Use Case"); + println!( + " {:<10} {:<8} {:<12} {:<12} {:<15}", + "Format", "Bits", "Memory (0.5B)", "Quality", "Use Case" + ); println!(" {}", "-".repeat(60)); - println!(" {:<10} {:<8} {:<12} {:<12} {:<15}", - "Q4_K_M", "4.5", "~300 MB", "Good", "Best tradeoff"); - println!(" {:<10} {:<8} {:<12} {:<12} {:<15}", - "Q5_K_M", "5.5", "~375 MB", "Better", "Higher quality"); - println!(" {:<10} {:<8} {:<12} {:<12} {:<15}", - "Q8_0", "8.5", "~500 MB", "Best", "Near-lossless"); - println!(" {:<10} {:<8} {:<12} {:<12} {:<15}", - "F16", "16", "~1000 MB", "Excellent", "No quant loss"); + println!( + " {:<10} {:<8} {:<12} {:<12} {:<15}", + "Q4_K_M", "4.5", "~300 MB", "Good", "Best tradeoff" + ); + println!( + " {:<10} {:<8} {:<12} {:<12} {:<15}", + "Q5_K_M", "5.5", "~375 MB", "Better", "Higher quality" + ); + println!( + " {:<10} {:<8} {:<12} {:<12} {:<15}", + "Q8_0", "8.5", "~500 MB", "Best", "Near-lossless" + ); + println!( + " {:<10} {:<8} {:<12} {:<12} {:<15}", + "F16", "16", "~1000 MB", "Excellent", "No quant loss" + ); } diff --git a/crates/ruvllm-cli/src/commands/serve.rs b/crates/ruvllm-cli/src/commands/serve.rs index b09833a77..e4b310025 100644 --- a/crates/ruvllm-cli/src/commands/serve.rs +++ b/crates/ruvllm-cli/src/commands/serve.rs @@ -127,7 +127,12 @@ pub async fn run( .route("/", get(root)) // State and middleware .with_state(state) - .layer(CorsLayer::new().allow_origin(Any).allow_methods(Any).allow_headers(Any)) + .layer( + CorsLayer::new() + .allow_origin(Any) + .allow_methods(Any) + .allow_headers(Any), + ) .layer(TraceLayer::new_for_http()); // Start server @@ -254,10 +259,14 @@ async fn chat_completions( ) -> axum::response::Response { if request.stream { // Handle streaming response - chat_completions_stream(state, request).await.into_response() + chat_completions_stream(state, request) + .await + .into_response() } else { // Handle non-streaming response - chat_completions_non_stream(state, request).await.into_response() + chat_completions_non_stream(state, request) + .await + .into_response() } } @@ -560,7 +569,8 @@ fn mock_response(prompt: &str) -> String { let prompt_lower = prompt.to_lowercase(); if prompt_lower.contains("hello") || prompt_lower.contains("hi") { - "Hello! I'm RuvLLM, a local AI assistant running on your Mac. How can I help you today?".to_string() + "Hello! I'm RuvLLM, a local AI assistant running on your Mac. How can I help you today?" + .to_string() } else if prompt_lower.contains("code") || prompt_lower.contains("function") { "Here's an example function:\n\n```rust\nfn hello() {\n println!(\"Hello, world!\");\n}\n```\n\nWould you like me to explain this code?".to_string() } else { @@ -589,7 +599,12 @@ async fn list_models(State(state): State) -> impl IntoResponse { async fn health_check(State(state): State) -> impl IntoResponse { let state_lock = state.read().await; - let status = if state_lock.backend.as_ref().map(|b| b.is_model_loaded()).unwrap_or(false) { + let status = if state_lock + .backend + .as_ref() + .map(|b| b.is_model_loaded()) + .unwrap_or(false) + { "healthy" } else { "degraded" diff --git a/crates/ruvllm-cli/src/main.rs b/crates/ruvllm-cli/src/main.rs index 444a94c01..d8df18b1d 100644 --- a/crates/ruvllm-cli/src/main.rs +++ b/crates/ruvllm-cli/src/main.rs @@ -258,16 +258,19 @@ async fn main() -> anyhow::Result<()> { force, revision, } => { - download::run(&model, &quantization, force, revision.as_deref(), &cache_dir).await + download::run( + &model, + &quantization, + force, + revision.as_deref(), + &cache_dir, + ) + .await } - Commands::List { downloaded, long } => { - list::run(downloaded, long, &cache_dir).await - } + Commands::List { downloaded, long } => list::run(downloaded, long, &cache_dir).await, - Commands::Info { model } => { - info::run(&model, &cache_dir).await - } + Commands::Info { model } => info::run(&model, &cache_dir).await, Commands::Serve { model, diff --git a/crates/ruvllm-wasm/src/bindings.rs b/crates/ruvllm-wasm/src/bindings.rs index 308871ca5..25707b5dd 100644 --- a/crates/ruvllm-wasm/src/bindings.rs +++ b/crates/ruvllm-wasm/src/bindings.rs @@ -477,7 +477,10 @@ impl ChatTemplate { for msg in messages { match msg.role { Role::User => { - output.push_str(&format!("user\n{}\n", msg.content)); + output.push_str(&format!( + "user\n{}\n", + msg.content + )); } Role::Assistant => { output.push_str(&format!( @@ -487,10 +490,7 @@ impl ChatTemplate { } Role::System => { // Gemma doesn't have native system support, prepend to first user - output.push_str(&format!( - "user\n{}\n", - msg.content - )); + output.push_str(&format!("user\n{}\n", msg.content)); } } } @@ -923,7 +923,11 @@ impl InferenceArenaWasm { /// Create an arena sized for model dimensions. #[wasm_bindgen(js_name = forModel)] - pub fn for_model(hidden_dim: usize, vocab_size: usize, batch_size: usize) -> InferenceArenaWasm { + pub fn for_model( + hidden_dim: usize, + vocab_size: usize, + batch_size: usize, + ) -> InferenceArenaWasm { let activations = hidden_dim * batch_size * 4; let logits = vocab_size * batch_size * 4; let scratch = hidden_dim * 4 * 4; diff --git a/crates/ruvllm-wasm/src/hnsw_router.rs b/crates/ruvllm-wasm/src/hnsw_router.rs index a3ec6cbd2..1df5f118e 100644 --- a/crates/ruvllm-wasm/src/hnsw_router.rs +++ b/crates/ruvllm-wasm/src/hnsw_router.rs @@ -43,9 +43,9 @@ //! const restored = HnswRouterWasm.fromJson(json); //! ``` -use wasm_bindgen::prelude::*; use serde::{Deserialize, Serialize}; use std::collections::HashMap; +use wasm_bindgen::prelude::*; /// Maximum connections per node in the HNSW graph (M parameter) const DEFAULT_M: usize = 16; @@ -285,11 +285,7 @@ impl HnswGraph { let candidates = self.search_layer(embedding, curr, self.ef_construction, l); // Select M nearest neighbors - let neighbors: Vec = candidates - .iter() - .take(m) - .map(|(id, _)| *id) - .collect(); + let neighbors: Vec = candidates.iter().take(m).map(|(id, _)| *id).collect(); // Add bidirectional connections if let Some(node) = self.layers[l].get_mut(&node_id) { @@ -371,7 +367,10 @@ impl HnswGraph { // If worse than worst in best set, stop if !best.is_empty() { - let worst_best = best.iter().min_by(|a, b| a.1.partial_cmp(&b.1).unwrap()).unwrap(); + let worst_best = best + .iter() + .min_by(|a, b| a.1.partial_cmp(&b.1).unwrap()) + .unwrap(); if curr_sim < worst_best.1 { break; } @@ -382,9 +381,17 @@ impl HnswGraph { for &neighbor_id in &node.neighbors { if !visited[neighbor_id] { visited[neighbor_id] = true; - let sim = Self::cosine_similarity(query, &self.patterns[neighbor_id].embedding); + let sim = + Self::cosine_similarity(query, &self.patterns[neighbor_id].embedding); - if best.len() < ef || sim > best.iter().min_by(|a, b| a.1.partial_cmp(&b.1).unwrap()).unwrap().1 { + if best.len() < ef + || sim + > best + .iter() + .min_by(|a, b| a.1.partial_cmp(&b.1).unwrap()) + .unwrap() + .1 + { candidates.push((neighbor_id, sim)); best.push((neighbor_id, sim)); diff --git a/crates/ruvllm-wasm/src/lib.rs b/crates/ruvllm-wasm/src/lib.rs index 7dd65f316..271dbbb04 100644 --- a/crates/ruvllm-wasm/src/lib.rs +++ b/crates/ruvllm-wasm/src/lib.rs @@ -130,14 +130,9 @@ pub use utils::{error, log, now_ms, set_panic_hook, warn, Timer}; // Re-export workers module pub use workers::{ + cross_origin_isolated, detect_capability_level, feature_summary, is_atomics_available, + is_shared_array_buffer_available, optimal_worker_count, supports_parallel_inference, ParallelInference, - is_shared_array_buffer_available, - is_atomics_available, - cross_origin_isolated, - optimal_worker_count, - feature_summary, - detect_capability_level, - supports_parallel_inference, }; // Re-export WebGPU module when enabled diff --git a/crates/ruvllm-wasm/src/sona_instant.rs b/crates/ruvllm-wasm/src/sona_instant.rs index a67ccd7b5..b50722e77 100644 --- a/crates/ruvllm-wasm/src/sona_instant.rs +++ b/crates/ruvllm-wasm/src/sona_instant.rs @@ -440,7 +440,8 @@ impl SonaInstantWasm { // Update quality EMA let prev_quality = self.quality_ema; - self.quality_ema = self.config.ema_decay * self.quality_ema + (1.0 - self.config.ema_decay) * quality; + self.quality_ema = + self.config.ema_decay * self.quality_ema + (1.0 - self.config.ema_decay) * quality; // Adaptive rank adjustment (simple heuristic) // Increase rank if quality improving, decrease if degrading @@ -454,7 +455,8 @@ impl SonaInstantWasm { // EWC-lite: Track important features (top 10% by quality contribution) // Simplified: just mark indices that correlate with high quality if quality > 0.7 && self.important_weights.len() < 100 { - let weight_idx = (quality * self.config.hidden_dim as f32) as usize % self.config.hidden_dim; + let weight_idx = + (quality * self.config.hidden_dim as f32) as usize % self.config.hidden_dim; if !self.important_weights.contains(&weight_idx) { self.important_weights.push(weight_idx); } @@ -480,7 +482,11 @@ impl SonaInstantWasm { let pattern = Pattern { embedding: embedding.to_vec(), success, - quality: if success { self.quality_ema } else { 1.0 - self.quality_ema }, + quality: if success { + self.quality_ema + } else { + 1.0 - self.quality_ema + }, timestamp: self.timestamp, }; @@ -589,7 +595,8 @@ impl SonaInstantWasm { current_rank: usize, } - let import: Import = serde_json::from_str(json).map_err(|e| JsValue::from_str(&e.to_string()))?; + let import: Import = + serde_json::from_str(json).map_err(|e| JsValue::from_str(&e.to_string()))?; Ok(Self { config: import.config.clone(), diff --git a/crates/ruvllm-wasm/src/webgpu/buffers.rs b/crates/ruvllm-wasm/src/webgpu/buffers.rs index 55214c910..4fa16cd0a 100644 --- a/crates/ruvllm-wasm/src/webgpu/buffers.rs +++ b/crates/ruvllm-wasm/src/webgpu/buffers.rs @@ -3,9 +3,9 @@ //! This module provides buffer abstractions for GPU memory management //! in the browser WebGPU environment. -use wasm_bindgen::prelude::*; use js_sys::{Float32Array, Uint8Array}; use std::cell::RefCell; +use wasm_bindgen::prelude::*; /// Buffer usage flags #[wasm_bindgen] @@ -99,50 +99,86 @@ impl GpuBufferUsage { /// - QUERY_RESOLVE = 0x0200 pub fn to_u32(&self) -> u32 { let mut flags = 0u32; - if self.map_read { flags |= 0x0001; } - if self.map_write { flags |= 0x0002; } - if self.copy_src { flags |= 0x0004; } - if self.copy_dst { flags |= 0x0008; } - if self.uniform { flags |= 0x0040; } - if self.storage { flags |= 0x0080; } + if self.map_read { + flags |= 0x0001; + } + if self.map_write { + flags |= 0x0002; + } + if self.copy_src { + flags |= 0x0004; + } + if self.copy_dst { + flags |= 0x0008; + } + if self.uniform { + flags |= 0x0040; + } + if self.storage { + flags |= 0x0080; + } flags } #[wasm_bindgen(getter, js_name = mapRead)] - pub fn get_map_read(&self) -> bool { self.map_read } + pub fn get_map_read(&self) -> bool { + self.map_read + } #[wasm_bindgen(setter, js_name = mapRead)] - pub fn set_map_read(&mut self, value: bool) { self.map_read = value; } + pub fn set_map_read(&mut self, value: bool) { + self.map_read = value; + } #[wasm_bindgen(getter, js_name = mapWrite)] - pub fn get_map_write(&self) -> bool { self.map_write } + pub fn get_map_write(&self) -> bool { + self.map_write + } #[wasm_bindgen(setter, js_name = mapWrite)] - pub fn set_map_write(&mut self, value: bool) { self.map_write = value; } + pub fn set_map_write(&mut self, value: bool) { + self.map_write = value; + } #[wasm_bindgen(getter, js_name = copySrc)] - pub fn get_copy_src(&self) -> bool { self.copy_src } + pub fn get_copy_src(&self) -> bool { + self.copy_src + } #[wasm_bindgen(setter, js_name = copySrc)] - pub fn set_copy_src(&mut self, value: bool) { self.copy_src = value; } + pub fn set_copy_src(&mut self, value: bool) { + self.copy_src = value; + } #[wasm_bindgen(getter, js_name = copyDst)] - pub fn get_copy_dst(&self) -> bool { self.copy_dst } + pub fn get_copy_dst(&self) -> bool { + self.copy_dst + } #[wasm_bindgen(setter, js_name = copyDst)] - pub fn set_copy_dst(&mut self, value: bool) { self.copy_dst = value; } + pub fn set_copy_dst(&mut self, value: bool) { + self.copy_dst = value; + } #[wasm_bindgen(getter, js_name = isStorage)] - pub fn get_storage(&self) -> bool { self.storage } + pub fn get_storage(&self) -> bool { + self.storage + } #[wasm_bindgen(setter, js_name = isStorage)] - pub fn set_storage(&mut self, value: bool) { self.storage = value; } + pub fn set_storage(&mut self, value: bool) { + self.storage = value; + } #[wasm_bindgen(getter, js_name = isUniform)] - pub fn get_uniform(&self) -> bool { self.uniform } + pub fn get_uniform(&self) -> bool { + self.uniform + } #[wasm_bindgen(setter, js_name = isUniform)] - pub fn set_uniform(&mut self, value: bool) { self.uniform = value; } + pub fn set_uniform(&mut self, value: bool) { + self.uniform = value; + } } /// GPU buffer handle @@ -217,16 +253,17 @@ impl GpuBuffer { usage: GpuBufferUsage, label: Option, ) -> Self { - Self { buffer, size, usage, label } + Self { + buffer, + size, + usage, + label, + } } /// Create a new GPU buffer (non-wasm32 placeholder) #[cfg(not(target_arch = "wasm32"))] - pub(crate) fn new( - size: usize, - usage: GpuBufferUsage, - label: Option, - ) -> Self { + pub(crate) fn new(size: usize, usage: GpuBufferUsage, label: Option) -> Self { Self { buffer: vec![0u8; size], size, diff --git a/crates/ruvllm-wasm/src/webgpu/compute.rs b/crates/ruvllm-wasm/src/webgpu/compute.rs index b55e5dd3c..85fac670a 100644 --- a/crates/ruvllm-wasm/src/webgpu/compute.rs +++ b/crates/ruvllm-wasm/src/webgpu/compute.rs @@ -6,11 +6,11 @@ //! Note: WebGPU bindings use JavaScript interop via js_sys/Reflect since //! web-sys WebGPU bindings are still unstable. +use js_sys::{Array, Float32Array, Object, Promise, Reflect}; use wasm_bindgen::prelude::*; use wasm_bindgen_futures::JsFuture; -use js_sys::{Array, Float32Array, Object, Promise, Reflect}; -use super::{AdapterInfo, AttentionConfig, shaders}; +use super::{shaders, AdapterInfo, AttentionConfig}; /// Check if WebGPU is available in this browser pub async fn is_webgpu_available() -> bool { @@ -34,7 +34,11 @@ pub async fn get_gpu_info() -> Option { // Request adapter let options = Object::new(); - let _ = Reflect::set(&options, &"powerPreference".into(), &"high-performance".into()); + let _ = Reflect::set( + &options, + &"powerPreference".into(), + &"high-performance".into(), + ); let adapter_promise = call_method(&gpu, "requestAdapter", &[options.into()]).ok()?; let adapter = JsFuture::from(adapter_promise.dyn_into::().ok()?) @@ -59,8 +63,10 @@ pub async fn get_gpu_info() -> Option { architecture: get_string_prop(&info, "architecture").unwrap_or_default(), device_type: get_string_prop(&info, "device").unwrap_or_else(|| "unknown".to_string()), backend: "WebGPU".to_string(), - max_buffer_size: get_number_prop(&limits, "maxBufferSize").unwrap_or(256.0 * 1024.0 * 1024.0) as u64, - max_workgroup_size: get_number_prop(&limits, "maxComputeWorkgroupSizeX").unwrap_or(256.0) as u32, + max_buffer_size: get_number_prop(&limits, "maxBufferSize") + .unwrap_or(256.0 * 1024.0 * 1024.0) as u64, + max_workgroup_size: get_number_prop(&limits, "maxComputeWorkgroupSizeX") + .unwrap_or(256.0) as u32, }) } @@ -93,15 +99,12 @@ fn get_string_prop(obj: &JsValue, key: &str) -> Option { #[cfg(target_arch = "wasm32")] fn get_number_prop(obj: &JsValue, key: &str) -> Option { - Reflect::get(obj, &key.into()) - .ok() - .and_then(|v| v.as_f64()) + Reflect::get(obj, &key.into()).ok().and_then(|v| v.as_f64()) } #[cfg(target_arch = "wasm32")] fn call_method(obj: &JsValue, method: &str, args: &[JsValue]) -> Result { - let func = Reflect::get(obj, &method.into())? - .dyn_into::()?; + let func = Reflect::get(obj, &method.into())?.dyn_into::()?; let args_array = Array::new(); for arg in args { @@ -141,16 +144,18 @@ impl WebGpuContext { pub async fn init() -> Result { #[cfg(target_arch = "wasm32")] { - let gpu = get_gpu_object() - .ok_or_else(|| JsValue::from_str("WebGPU not available"))?; + let gpu = get_gpu_object().ok_or_else(|| JsValue::from_str("WebGPU not available"))?; // Request adapter with high performance preference let adapter_options = Object::new(); - Reflect::set(&adapter_options, &"powerPreference".into(), &"high-performance".into())?; + Reflect::set( + &adapter_options, + &"powerPreference".into(), + &"high-performance".into(), + )?; let adapter_promise = call_method(&gpu, "requestAdapter", &[adapter_options.into()])?; - let adapter = JsFuture::from(adapter_promise.dyn_into::()?) - .await?; + let adapter = JsFuture::from(adapter_promise.dyn_into::()?).await?; if adapter.is_null() || adapter.is_undefined() { return Err(JsValue::from_str("No suitable GPU adapter found")); @@ -158,26 +163,28 @@ impl WebGpuContext { // Get adapter info let info_promise = call_method(&adapter, "requestAdapterInfo", &[])?; - let info = JsFuture::from(info_promise.dyn_into::()?) - .await?; + let info = JsFuture::from(info_promise.dyn_into::()?).await?; let limits = Reflect::get(&adapter, &"limits".into())?; let adapter_info = AdapterInfo { vendor: get_string_prop(&info, "vendor").unwrap_or_default(), architecture: get_string_prop(&info, "architecture").unwrap_or_default(), - device_type: get_string_prop(&info, "device").unwrap_or_else(|| "unknown".to_string()), + device_type: get_string_prop(&info, "device") + .unwrap_or_else(|| "unknown".to_string()), backend: "WebGPU".to_string(), - max_buffer_size: get_number_prop(&limits, "maxBufferSize").unwrap_or(256.0 * 1024.0 * 1024.0) as u64, - max_workgroup_size: get_number_prop(&limits, "maxComputeWorkgroupSizeX").unwrap_or(256.0) as u32, + max_buffer_size: get_number_prop(&limits, "maxBufferSize") + .unwrap_or(256.0 * 1024.0 * 1024.0) as u64, + max_workgroup_size: get_number_prop(&limits, "maxComputeWorkgroupSizeX") + .unwrap_or(256.0) as u32, }; // Request device let device_descriptor = Object::new(); Reflect::set(&device_descriptor, &"label".into(), &"ruvllm-wasm".into())?; - let device_promise = call_method(&adapter, "requestDevice", &[device_descriptor.into()])?; - let device = JsFuture::from(device_promise.dyn_into::()?) - .await?; + let device_promise = + call_method(&adapter, "requestDevice", &[device_descriptor.into()])?; + let device = JsFuture::from(device_promise.dyn_into::()?).await?; // Get queue let queue = Reflect::get(&device, &"queue".into())?; @@ -213,10 +220,19 @@ impl WebGpuContext { /// Create a GPU buffer #[cfg(target_arch = "wasm32")] - fn create_buffer_internal(&self, size: usize, usage: u32, label: Option<&str>) -> Result { + fn create_buffer_internal( + &self, + size: usize, + usage: u32, + label: Option<&str>, + ) -> Result { let descriptor = Object::new(); Reflect::set(&descriptor, &"size".into(), &JsValue::from_f64(size as f64))?; - Reflect::set(&descriptor, &"usage".into(), &JsValue::from_f64(usage as f64))?; + Reflect::set( + &descriptor, + &"usage".into(), + &JsValue::from_f64(usage as f64), + )?; if let Some(lbl) = label { Reflect::set(&descriptor, &"label".into(), &lbl.into())?; } @@ -228,11 +244,15 @@ impl WebGpuContext { #[cfg(target_arch = "wasm32")] fn write_buffer_internal(&self, buffer: &JsValue, data: &[f32]) -> Result<(), JsValue> { let data_array = Float32Array::from(data); - call_method(&self.queue, "writeBuffer", &[ - buffer.clone(), - JsValue::from_f64(0.0), - data_array.buffer().into(), - ])?; + call_method( + &self.queue, + "writeBuffer", + &[ + buffer.clone(), + JsValue::from_f64(0.0), + data_array.buffer().into(), + ], + )?; Ok(()) } } @@ -347,14 +367,16 @@ impl WebGpuInference { if a.len() != expected_a { return Err(JsValue::from_str(&format!( "Matrix A dimension mismatch: expected {}, got {}", - expected_a, a.len() + expected_a, + a.len() ))); } if b.len() != expected_b { return Err(JsValue::from_str(&format!( "Matrix B dimension mismatch: expected {}, got {}", - expected_b, b.len() + expected_b, + b.len() ))); } @@ -363,20 +385,22 @@ impl WebGpuInference { let output_size = (m as usize) * (n as usize); // GPU buffer usage flags - const STORAGE: u32 = 0x80; // GPUBufferUsage.STORAGE + const STORAGE: u32 = 0x80; // GPUBufferUsage.STORAGE const COPY_SRC: u32 = 0x04; // GPUBufferUsage.COPY_SRC const COPY_DST: u32 = 0x08; // GPUBufferUsage.COPY_DST const MAP_READ: u32 = 0x01; // GPUBufferUsage.MAP_READ - const UNIFORM: u32 = 0x40; // GPUBufferUsage.UNIFORM + const UNIFORM: u32 = 0x40; // GPUBufferUsage.UNIFORM // Create buffers let buffer_a = self.create_buffer(a.len() * 4, STORAGE | COPY_DST, Some("matmul_a"))?; let buffer_b = self.create_buffer(b.len() * 4, STORAGE | COPY_DST, Some("matmul_b"))?; - let buffer_c = self.create_buffer(output_size * 4, STORAGE | COPY_SRC, Some("matmul_c"))?; + let buffer_c = + self.create_buffer(output_size * 4, STORAGE | COPY_SRC, Some("matmul_c"))?; // Create uniform buffer for dimensions let uniform_data: [f32; 4] = [m as f32, n as f32, k as f32, 1.0]; // M, N, K, alpha - let uniform_buffer = self.create_buffer(16, UNIFORM | COPY_DST, Some("matmul_uniforms"))?; + let uniform_buffer = + self.create_buffer(16, UNIFORM | COPY_DST, Some("matmul_uniforms"))?; // Write data to buffers self.write_buffer(&buffer_a, a)?; @@ -386,7 +410,8 @@ impl WebGpuInference { // Create shader module let shader_desc = Object::new(); Reflect::set(&shader_desc, &"code".into(), &shaders::MATMUL_SHADER.into())?; - let shader_module = call_method(&self.device, "createShaderModule", &[shader_desc.into()])?; + let shader_module = + call_method(&self.device, "createShaderModule", &[shader_desc.into()])?; // Create bind group layout let layout_entries = Array::new(); @@ -397,7 +422,16 @@ impl WebGpuInference { Reflect::set(&entry, &"binding".into(), &JsValue::from_f64(i as f64))?; Reflect::set(&entry, &"visibility".into(), &JsValue::from_f64(4.0))?; // COMPUTE stage let buffer_layout = Object::new(); - Reflect::set(&buffer_layout, &"type".into(), &(if i < 2 { "read-only-storage" } else { "storage" }).into())?; + Reflect::set( + &buffer_layout, + &"type".into(), + &(if i < 2 { + "read-only-storage" + } else { + "storage" + }) + .into(), + )?; Reflect::set(&entry, &"buffer".into(), &buffer_layout)?; layout_entries.push(&entry); } @@ -405,7 +439,11 @@ impl WebGpuInference { // Uniform buffer entry let uniform_entry = Object::new(); Reflect::set(&uniform_entry, &"binding".into(), &JsValue::from_f64(3.0))?; - Reflect::set(&uniform_entry, &"visibility".into(), &JsValue::from_f64(4.0))?; + Reflect::set( + &uniform_entry, + &"visibility".into(), + &JsValue::from_f64(4.0), + )?; let uniform_layout = Object::new(); Reflect::set(&uniform_layout, &"type".into(), &"uniform".into())?; Reflect::set(&uniform_entry, &"buffer".into(), &uniform_layout)?; @@ -413,14 +451,19 @@ impl WebGpuInference { let layout_desc = Object::new(); Reflect::set(&layout_desc, &"entries".into(), &layout_entries)?; - let bind_group_layout = call_method(&self.device, "createBindGroupLayout", &[layout_desc.into()])?; + let bind_group_layout = + call_method(&self.device, "createBindGroupLayout", &[layout_desc.into()])?; // Create pipeline layout let layouts = Array::new(); layouts.push(&bind_group_layout); let pipeline_layout_desc = Object::new(); Reflect::set(&pipeline_layout_desc, &"bindGroupLayouts".into(), &layouts)?; - let pipeline_layout = call_method(&self.device, "createPipelineLayout", &[pipeline_layout_desc.into()])?; + let pipeline_layout = call_method( + &self.device, + "createPipelineLayout", + &[pipeline_layout_desc.into()], + )?; // Create compute pipeline let compute_stage = Object::new(); @@ -431,11 +474,18 @@ impl WebGpuInference { Reflect::set(&pipeline_desc, &"layout".into(), &pipeline_layout)?; Reflect::set(&pipeline_desc, &"compute".into(), &compute_stage)?; - let pipeline = call_method(&self.device, "createComputePipeline", &[pipeline_desc.into()])?; + let pipeline = call_method( + &self.device, + "createComputePipeline", + &[pipeline_desc.into()], + )?; // Create bind group let bind_entries = Array::new(); - for (i, buffer) in [&buffer_a, &buffer_b, &buffer_c, &uniform_buffer].iter().enumerate() { + for (i, buffer) in [&buffer_a, &buffer_b, &buffer_c, &uniform_buffer] + .iter() + .enumerate() + { let entry = Object::new(); Reflect::set(&entry, &"binding".into(), &JsValue::from_f64(i as f64))?; let resource = Object::new(); @@ -447,11 +497,13 @@ impl WebGpuInference { let bind_group_desc = Object::new(); Reflect::set(&bind_group_desc, &"layout".into(), &bind_group_layout)?; Reflect::set(&bind_group_desc, &"entries".into(), &bind_entries)?; - let bind_group = call_method(&self.device, "createBindGroup", &[bind_group_desc.into()])?; + let bind_group = + call_method(&self.device, "createBindGroup", &[bind_group_desc.into()])?; // Create command encoder let encoder_desc = Object::new(); - let encoder = call_method(&self.device, "createCommandEncoder", &[encoder_desc.into()])?; + let encoder = + call_method(&self.device, "createCommandEncoder", &[encoder_desc.into()])?; // Begin compute pass let pass_desc = Object::new(); @@ -459,29 +511,42 @@ impl WebGpuInference { // Set pipeline and bind group call_method(&pass, "setPipeline", &[pipeline.clone()])?; - call_method(&pass, "setBindGroup", &[JsValue::from_f64(0.0), bind_group.clone()])?; + call_method( + &pass, + "setBindGroup", + &[JsValue::from_f64(0.0), bind_group.clone()], + )?; // Dispatch workgroups (16x16 tile size) let workgroups_x = (m + 15) / 16; let workgroups_y = (n + 15) / 16; - call_method(&pass, "dispatchWorkgroups", &[ - JsValue::from_f64(workgroups_x as f64), - JsValue::from_f64(workgroups_y as f64), - ])?; + call_method( + &pass, + "dispatchWorkgroups", + &[ + JsValue::from_f64(workgroups_x as f64), + JsValue::from_f64(workgroups_y as f64), + ], + )?; call_method(&pass, "end", &[])?; // Create staging buffer for readback - let staging = self.create_buffer(output_size * 4, MAP_READ | COPY_DST, Some("staging"))?; + let staging = + self.create_buffer(output_size * 4, MAP_READ | COPY_DST, Some("staging"))?; // Copy result to staging - call_method(&encoder, "copyBufferToBuffer", &[ - buffer_c.clone(), - JsValue::from_f64(0.0), - staging.clone(), - JsValue::from_f64(0.0), - JsValue::from_f64((output_size * 4) as f64), - ])?; + call_method( + &encoder, + "copyBufferToBuffer", + &[ + buffer_c.clone(), + JsValue::from_f64(0.0), + staging.clone(), + JsValue::from_f64(0.0), + JsValue::from_f64((output_size * 4) as f64), + ], + )?; // Submit commands let command_buffer = call_method(&encoder, "finish", &[])?; @@ -533,7 +598,10 @@ impl WebGpuInference { if q.len() != expected_size || k.len() != expected_size || v.len() != expected_size { return Err(JsValue::from_str(&format!( "Attention tensor dimension mismatch: expected {}, got Q:{}, K:{}, V:{}", - expected_size, q.len(), k.len(), v.len() + expected_size, + q.len(), + k.len(), + v.len() ))); } @@ -627,14 +695,16 @@ impl WebGpuInference { if weight.len() != hidden_dim as usize { return Err(JsValue::from_str(&format!( "Weight dimension mismatch: expected {}, got {}", - hidden_dim, weight.len() + hidden_dim, + weight.len() ))); } if input.len() % hidden_dim as usize != 0 { return Err(JsValue::from_str(&format!( "Input size {} not divisible by hidden_dim {}", - input.len(), hidden_dim + input.len(), + hidden_dim ))); } @@ -675,7 +745,8 @@ impl WebGpuInference { if input.len() % dim as usize != 0 { return Err(JsValue::from_str(&format!( "Input size {} not divisible by dim {}", - input.len(), dim + input.len(), + dim ))); } @@ -713,10 +784,19 @@ impl WebGpuInference { // Helper methods for GPU buffer management #[cfg(target_arch = "wasm32")] - fn create_buffer(&self, size: usize, usage: u32, label: Option<&str>) -> Result { + fn create_buffer( + &self, + size: usize, + usage: u32, + label: Option<&str>, + ) -> Result { let descriptor = Object::new(); Reflect::set(&descriptor, &"size".into(), &JsValue::from_f64(size as f64))?; - Reflect::set(&descriptor, &"usage".into(), &JsValue::from_f64(usage as f64))?; + Reflect::set( + &descriptor, + &"usage".into(), + &JsValue::from_f64(usage as f64), + )?; if let Some(lbl) = label { Reflect::set(&descriptor, &"label".into(), &lbl.into())?; } @@ -727,11 +807,15 @@ impl WebGpuInference { #[cfg(target_arch = "wasm32")] fn write_buffer(&self, buffer: &JsValue, data: &[f32]) -> Result<(), JsValue> { let data_array = Float32Array::from(data); - call_method(&self.queue, "writeBuffer", &[ - buffer.clone(), - JsValue::from_f64(0.0), - data_array.buffer().into(), - ])?; + call_method( + &self.queue, + "writeBuffer", + &[ + buffer.clone(), + JsValue::from_f64(0.0), + data_array.buffer().into(), + ], + )?; Ok(()) } } diff --git a/crates/ruvllm-wasm/src/webgpu/mod.rs b/crates/ruvllm-wasm/src/webgpu/mod.rs index 534b09afa..2467ae407 100644 --- a/crates/ruvllm-wasm/src/webgpu/mod.rs +++ b/crates/ruvllm-wasm/src/webgpu/mod.rs @@ -243,8 +243,16 @@ pub async fn get_gpu_info() -> Result { js_sys::Reflect::set(&js_obj, &"architecture".into(), &info.architecture.into())?; js_sys::Reflect::set(&js_obj, &"deviceType".into(), &info.device_type.into())?; js_sys::Reflect::set(&js_obj, &"backend".into(), &info.backend.into())?; - js_sys::Reflect::set(&js_obj, &"maxBufferSize".into(), &JsValue::from_f64(info.max_buffer_size as f64))?; - js_sys::Reflect::set(&js_obj, &"maxWorkgroupSize".into(), &JsValue::from_f64(info.max_workgroup_size as f64))?; + js_sys::Reflect::set( + &js_obj, + &"maxBufferSize".into(), + &JsValue::from_f64(info.max_buffer_size as f64), + )?; + js_sys::Reflect::set( + &js_obj, + &"maxWorkgroupSize".into(), + &JsValue::from_f64(info.max_workgroup_size as f64), + )?; Ok(js_obj.into()) } None => Ok(JsValue::NULL), @@ -278,12 +286,23 @@ impl std::fmt::Display for WebGpuError { Self::NotAvailable => write!(f, "WebGPU is not available in this browser"), Self::AdapterNotFound => write!(f, "No suitable GPU adapter found"), Self::DeviceCreationFailed(msg) => write!(f, "Failed to create GPU device: {}", msg), - Self::BufferAllocationFailed { requested, available } => { - write!(f, "Buffer allocation failed: requested {} bytes, {} available", requested, available) + Self::BufferAllocationFailed { + requested, + available, + } => { + write!( + f, + "Buffer allocation failed: requested {} bytes, {} available", + requested, available + ) } Self::ShaderCompilationFailed(msg) => write!(f, "Shader compilation failed: {}", msg), Self::DimensionMismatch { expected, actual } => { - write!(f, "Dimension mismatch: expected {}, got {}", expected, actual) + write!( + f, + "Dimension mismatch: expected {}, got {}", + expected, actual + ) } Self::Timeout => write!(f, "GPU operation timed out"), Self::GpuError(msg) => write!(f, "GPU error: {}", msg), diff --git a/crates/ruvllm-wasm/src/workers/feature_detect.rs b/crates/ruvllm-wasm/src/workers/feature_detect.rs index 1f2dc9897..622f0ca7e 100644 --- a/crates/ruvllm-wasm/src/workers/feature_detect.rs +++ b/crates/ruvllm-wasm/src/workers/feature_detect.rs @@ -84,9 +84,7 @@ pub fn cross_origin_isolated() -> bool { // Also check in worker context let global = js_sys::global(); - if let Ok(isolated) = - js_sys::Reflect::get(&global, &JsValue::from_str("crossOriginIsolated")) - { + if let Ok(isolated) = js_sys::Reflect::get(&global, &JsValue::from_str("crossOriginIsolated")) { return isolated.as_bool().unwrap_or(false); } diff --git a/crates/ruvllm-wasm/src/workers/messages.rs b/crates/ruvllm-wasm/src/workers/messages.rs index fb6ea58df..3fe758e13 100644 --- a/crates/ruvllm-wasm/src/workers/messages.rs +++ b/crates/ruvllm-wasm/src/workers/messages.rs @@ -588,7 +588,9 @@ mod tests { let parsed: WorkerMessage = serde_json::from_str(&json).unwrap(); match parsed { - WorkerMessage::ComputeMatmul { task_id, m, n, k, .. } => { + WorkerMessage::ComputeMatmul { + task_id, m, n, k, .. + } => { assert_eq!(task_id, 1); assert_eq!(m, 10); assert_eq!(n, 20); diff --git a/crates/ruvllm-wasm/src/workers/mod.rs b/crates/ruvllm-wasm/src/workers/mod.rs index 25029fe5b..785b28401 100644 --- a/crates/ruvllm-wasm/src/workers/mod.rs +++ b/crates/ruvllm-wasm/src/workers/mod.rs @@ -371,8 +371,8 @@ impl ParallelInference { for j in 0..seq_len { let mut dot = 0.0f32; for d in 0..head_dim { - dot += q[head_offset + i * head_dim + d] - * k[head_offset + j * head_dim + d]; + dot += + q[head_offset + i * head_dim + d] * k[head_offset + j * head_dim + d]; } scores[i * seq_len + j] = dot * scale; } diff --git a/crates/ruvllm-wasm/src/workers/pool.rs b/crates/ruvllm-wasm/src/workers/pool.rs index 2b05e8222..0a0fa7283 100644 --- a/crates/ruvllm-wasm/src/workers/pool.rs +++ b/crates/ruvllm-wasm/src/workers/pool.rs @@ -726,7 +726,9 @@ self.postMessage({ type: 'WorkerReady', worker_id: -1 }); // Allocate shared memory let total_size = (a.len() + b.len() + m * n) * std::mem::size_of::(); - self.shared_buffers.borrow_mut().ensure_capacity(total_size)?; + self.shared_buffers + .borrow_mut() + .ensure_capacity(total_size)?; let buffer = self .shared_buffers @@ -790,11 +792,8 @@ self.postMessage({ type: 'WorkerReady', worker_id: -1 }); self.wait_for_tasks(&task_ids).await?; // Read result from shared buffer - let result_view = Float32Array::new_with_byte_offset_and_length( - &buffer, - c_offset as u32, - (m * n) as u32, - ); + let result_view = + Float32Array::new_with_byte_offset_and_length(&buffer, c_offset as u32, (m * n) as u32); Ok(result_view.to_vec()) } @@ -894,7 +893,9 @@ self.postMessage({ type: 'WorkerReady', worker_id: -1 }); let tensor_size = num_heads * seq_len * head_dim; let total_size = tensor_size * 4 * std::mem::size_of::(); - self.shared_buffers.borrow_mut().ensure_capacity(total_size)?; + self.shared_buffers + .borrow_mut() + .ensure_capacity(total_size)?; let buffer = self .shared_buffers @@ -1003,7 +1004,9 @@ self.postMessage({ type: 'WorkerReady', worker_id: -1 }); let num_workers = self.worker_count(); let total_size = (input.len() + gamma.len() * 2 + input.len()) * std::mem::size_of::(); - self.shared_buffers.borrow_mut().ensure_capacity(total_size)?; + self.shared_buffers + .borrow_mut() + .ensure_capacity(total_size)?; let buffer = self .shared_buffers @@ -1016,7 +1019,10 @@ self.postMessage({ type: 'WorkerReady', worker_id: -1 }); let view = Float32Array::new(&buffer); view.set(&Float32Array::from(input), 0); view.set(&Float32Array::from(gamma), input.len() as u32); - view.set(&Float32Array::from(beta), (input.len() + gamma.len()) as u32); + view.set( + &Float32Array::from(beta), + (input.len() + gamma.len()) as u32, + ); let input_offset = 0; let gamma_offset = input.len() * std::mem::size_of::(); diff --git a/crates/ruvllm-wasm/src/workers/shared.rs b/crates/ruvllm-wasm/src/workers/shared.rs index 9bb5081f9..e483f8f34 100644 --- a/crates/ruvllm-wasm/src/workers/shared.rs +++ b/crates/ruvllm-wasm/src/workers/shared.rs @@ -212,8 +212,7 @@ impl SharedTensor { let offset = (self.byte_offset / 4) as u32; for i in 0..self.len() as u32 { - js_sys::Atomics::store(&int_view, offset + i, bits) - .expect("Atomics::store failed"); + js_sys::Atomics::store(&int_view, offset + i, bits).expect("Atomics::store failed"); } } @@ -226,8 +225,7 @@ impl SharedTensor { let int_view = Int32Array::new(&self.buffer); let offset = (self.byte_offset / 4 + index) as u32; - let bits = - js_sys::Atomics::load(&int_view, offset).expect("Atomics::load failed") as u32; + let bits = js_sys::Atomics::load(&int_view, offset).expect("Atomics::load failed") as u32; Some(f32::from_bits(bits)) } @@ -313,8 +311,7 @@ impl SharedBufferManager { /// Create with a pre-allocated buffer of the given size. pub fn with_capacity(capacity_bytes: usize) -> Result { - let aligned_capacity = - (capacity_bytes + TENSOR_ALIGNMENT - 1) & !(TENSOR_ALIGNMENT - 1); + let aligned_capacity = (capacity_bytes + TENSOR_ALIGNMENT - 1) & !(TENSOR_ALIGNMENT - 1); let buffer = SharedArrayBuffer::new(aligned_capacity as u32); @@ -329,8 +326,7 @@ impl SharedBufferManager { /// Ensure buffer has at least the given capacity. pub fn ensure_capacity(&mut self, min_capacity: usize) -> Result<(), JsValue> { - let aligned_capacity = - (min_capacity + TENSOR_ALIGNMENT - 1) & !(TENSOR_ALIGNMENT - 1); + let aligned_capacity = (min_capacity + TENSOR_ALIGNMENT - 1) & !(TENSOR_ALIGNMENT - 1); if self.buffer_size >= aligned_capacity { return Ok(()); @@ -388,13 +384,15 @@ impl SharedBufferManager { /// Get an existing tensor by name. pub fn get(&self, name: &str, shape: &[usize]) -> Result { - let region = self.regions.get(name).ok_or_else(|| { - JsValue::from_str(&format!("Region '{}' not found", name)) - })?; + let region = self + .regions + .get(name) + .ok_or_else(|| JsValue::from_str(&format!("Region '{}' not found", name)))?; - let buffer = self.buffer.as_ref().ok_or_else(|| { - JsValue::from_str("Buffer not initialized") - })?; + let buffer = self + .buffer + .as_ref() + .ok_or_else(|| JsValue::from_str("Buffer not initialized"))?; SharedTensor::from_buffer(buffer.clone(), region.offset, shape) } @@ -507,19 +505,14 @@ impl SharedBarrier { /// /// Returns the generation number. pub fn wait(&self) -> Result { - let gen = js_sys::Atomics::load(&self.int_view, 0) - .expect("Atomics::load failed"); - let arrived = js_sys::Atomics::add(&self.int_view, 1, 1) - .expect("Atomics::add failed") + 1; + let gen = js_sys::Atomics::load(&self.int_view, 0).expect("Atomics::load failed"); + let arrived = js_sys::Atomics::add(&self.int_view, 1, 1).expect("Atomics::add failed") + 1; if arrived as usize == self.count { // Last to arrive - reset and notify - js_sys::Atomics::store(&self.int_view, 1, 0) - .expect("Atomics::store failed"); - js_sys::Atomics::add(&self.int_view, 0, 1) - .expect("Atomics::add failed"); - js_sys::Atomics::notify(&self.int_view, 0) - .expect("Atomics::notify failed"); + js_sys::Atomics::store(&self.int_view, 1, 0).expect("Atomics::store failed"); + js_sys::Atomics::add(&self.int_view, 0, 1).expect("Atomics::add failed"); + js_sys::Atomics::notify(&self.int_view, 0).expect("Atomics::notify failed"); } else { // Wait for generation to change let _ = js_sys::Atomics::wait(&self.int_view, 0, gen); diff --git a/crates/ruvllm-wasm/tests/intelligent_wasm_test.rs b/crates/ruvllm-wasm/tests/intelligent_wasm_test.rs index de2d9f551..8c6816865 100644 --- a/crates/ruvllm-wasm/tests/intelligent_wasm_test.rs +++ b/crates/ruvllm-wasm/tests/intelligent_wasm_test.rs @@ -258,7 +258,9 @@ impl MockSONA { scored_patterns.sort_by(|a, b| { let score_a = a.1 * a.2; let score_b = b.1 * b.2; - score_b.partial_cmp(&score_a).unwrap_or(std::cmp::Ordering::Equal) + score_b + .partial_cmp(&score_a) + .unwrap_or(std::cmp::Ordering::Equal) }); Ok(scored_patterns diff --git a/crates/ruvllm/benches/ane_bench.rs b/crates/ruvllm/benches/ane_bench.rs index 927539311..b4a7a9283 100644 --- a/crates/ruvllm/benches/ane_bench.rs +++ b/crates/ruvllm/benches/ane_bench.rs @@ -74,21 +74,21 @@ fn bench_gemm_comparison(c: &mut Criterion) { // - Very Large (8192x8192): GPU clear winner let sizes = [ // Small matrices - ANE advantage zone - (1, 128, 128), // Tiny matmul - ANE wins - (1, 256, 256), // Small matmul - ANE wins - (1, 512, 512), // Medium-small - ANE edge + (1, 128, 128), // Tiny matmul - ANE wins + (1, 256, 256), // Small matmul - ANE wins + (1, 512, 512), // Medium-small - ANE edge // Medium matrices - Transition zone - (1, 1024, 1024), // ANE/GPU crossover starts - (1, 2048, 2048), // Crossover zone + (1, 1024, 1024), // ANE/GPU crossover starts + (1, 2048, 2048), // Crossover zone // Large matrices - GPU advantage - (1, 4096, 4096), // Single token, typical projection - GPU starts winning - (1, 4096, 11008), // Llama MLP up-projection - (1, 11008, 4096), // Llama MLP down-projection + (1, 4096, 4096), // Single token, typical projection - GPU starts winning + (1, 4096, 11008), // Llama MLP up-projection + (1, 11008, 4096), // Llama MLP down-projection // Batch inference - ANE optimal for small batches - (8, 4096, 4096), // Small batch - (32, 4096, 4096), // Medium batch - (64, 4096, 4096), // Optimal ANE batch size - (128, 4096, 4096), // Beyond ANE optimal - GPU wins + (8, 4096, 4096), // Small batch + (32, 4096, 4096), // Medium batch + (64, 4096, 4096), // Optimal ANE batch size + (128, 4096, 4096), // Beyond ANE optimal - GPU wins ]; for (m, k, n) in sizes { @@ -108,12 +108,7 @@ fn bench_gemm_comparison(c: &mut Criterion) { group.bench_function(id, |bencher| { bencher.iter(|| { // Use local GEMM implementation to avoid module dependency issues - gemm_neon_local( - black_box(&a), - black_box(&b), - black_box(&mut c_out), - m, k, n, - ); + gemm_neon_local(black_box(&a), black_box(&b), black_box(&mut c_out), m, k, n); }) }); } @@ -128,7 +123,9 @@ fn bench_gemm_comparison(c: &mut Criterion) { black_box(&a), black_box(&b), black_box(&mut c_out), - m, k, n, + m, + k, + n, ); }) }); @@ -144,7 +141,9 @@ fn bench_gemm_comparison(c: &mut Criterion) { black_box(&a), black_box(&b), black_box(&mut c_out), - m, k, n, + m, + k, + n, ); }) }); @@ -161,10 +160,10 @@ fn bench_batched_gemm_comparison(c: &mut Criterion) { // Typical attention shapes: batch of Q*K^T or attention*V let configs = [ - (8, 128, 128, 128), // 8 heads, seq=128 - (32, 128, 128, 128), // 32 heads, seq=128 - (32, 256, 128, 256), // 32 heads, seq=256, head_dim=128 - (8, 512, 128, 512), // 8 heads, seq=512 + (8, 128, 128, 128), // 8 heads, seq=128 + (32, 128, 128, 128), // 32 heads, seq=128 + (32, 256, 128, 256), // 32 heads, seq=256, head_dim=128 + (8, 512, 128, 512), // 8 heads, seq=512 ]; for (batch_size, m, k, n) in configs { @@ -191,7 +190,9 @@ fn bench_batched_gemm_comparison(c: &mut Criterion) { black_box(&a[a_off..a_off + m * k]), black_box(&b[b_off..b_off + k * n]), black_box(&mut c_out[c_off..c_off + m * n]), - m, k, n, + m, + k, + n, ); } }) @@ -208,7 +209,10 @@ fn bench_batched_gemm_comparison(c: &mut Criterion) { black_box(&a), black_box(&b), black_box(&mut c_out), - batch_size, m, k, n, + batch_size, + m, + k, + n, ); }) }); @@ -233,7 +237,7 @@ fn bench_gelu_comparison(c: &mut Criterion) { (8, 4096), (32, 4096), (64, 4096), - (1, 11008), // Llama MLP intermediate + (1, 11008), // Llama MLP intermediate (32, 11008), ]; @@ -254,10 +258,7 @@ fn bench_gelu_comparison(c: &mut Criterion) { group.bench_function(id, |bencher| { bencher.iter(|| { x.copy_from_slice(&x_orig); - ruvllm::kernels::activations::batch_gelu( - black_box(&mut x), - dim, - ); + ruvllm::kernels::activations::batch_gelu(black_box(&mut x), dim); }) }); } @@ -270,11 +271,7 @@ fn bench_gelu_comparison(c: &mut Criterion) { group.bench_function(id, |bencher| { bencher.iter(|| { x.copy_from_slice(&x_orig); - ruvllm::kernels::ane_ops::gelu_ane( - black_box(&mut x), - batch_size, - dim, - ); + ruvllm::kernels::ane_ops::gelu_ane(black_box(&mut x), batch_size, dim); }) }); } @@ -314,10 +311,7 @@ fn bench_silu_comparison(c: &mut Criterion) { group.bench_function(id, |bencher| { bencher.iter(|| { x.copy_from_slice(&x_orig); - ruvllm::kernels::activations::batch_silu( - black_box(&mut x), - dim, - ); + ruvllm::kernels::activations::batch_silu(black_box(&mut x), dim); }) }); } @@ -330,11 +324,7 @@ fn bench_silu_comparison(c: &mut Criterion) { group.bench_function(id, |bencher| { bencher.iter(|| { x.copy_from_slice(&x_orig); - ruvllm::kernels::ane_ops::silu_ane( - black_box(&mut x), - batch_size, - dim, - ); + ruvllm::kernels::ane_ops::silu_ane(black_box(&mut x), batch_size, dim); }) }); } @@ -350,11 +340,11 @@ fn bench_softmax_comparison(c: &mut Criterion) { // Softmax is typically applied to attention scores let configs = [ - (1, 128), // Single head, short seq - (32, 128), // 32 heads, short seq - (32, 512), // 32 heads, medium seq - (32, 2048), // 32 heads, long seq - (1, 4096), // Single head, very long + (1, 128), // Single head, short seq + (32, 128), // 32 heads, short seq + (32, 512), // 32 heads, medium seq + (32, 2048), // 32 heads, long seq + (1, 4096), // Single head, very long ]; for (batch_size, dim) in configs { @@ -374,10 +364,7 @@ fn bench_softmax_comparison(c: &mut Criterion) { group.bench_function(id, |bencher| { bencher.iter(|| { x.copy_from_slice(&x_orig); - ruvllm::kernels::activations::batch_softmax( - black_box(&mut x), - dim, - ); + ruvllm::kernels::activations::batch_softmax(black_box(&mut x), dim); }) }); } @@ -390,11 +377,7 @@ fn bench_softmax_comparison(c: &mut Criterion) { group.bench_function(id, |bencher| { bencher.iter(|| { x.copy_from_slice(&x_orig); - ruvllm::kernels::ane_ops::softmax_ane( - black_box(&mut x), - batch_size, - dim, - ); + ruvllm::kernels::ane_ops::softmax_ane(black_box(&mut x), batch_size, dim); }) }); } @@ -412,13 +395,7 @@ fn bench_layer_norm_comparison(c: &mut Criterion) { let mut group = c.benchmark_group("layernorm_ane_vs_neon"); group.sample_size(50); - let configs = [ - (1, 4096), - (8, 4096), - (32, 4096), - (64, 4096), - (128, 4096), - ]; + let configs = [(1, 4096), (8, 4096), (32, 4096), (64, 4096), (128, 4096)]; for (batch_size, dim) in configs { let size = batch_size * dim; @@ -480,13 +457,7 @@ fn bench_rms_norm_comparison(c: &mut Criterion) { let mut group = c.benchmark_group("rmsnorm_ane_vs_neon"); group.sample_size(50); - let configs = [ - (1, 4096), - (8, 4096), - (32, 4096), - (64, 4096), - (128, 4096), - ]; + let configs = [(1, 4096), (8, 4096), (32, 4096), (64, 4096), (128, 4096)]; for (batch_size, dim) in configs { let size = batch_size * dim; @@ -564,16 +535,9 @@ fn bench_auto_dispatch(c: &mut Criterion) { bencher.iter(|| { x.copy_from_slice(&x_orig); #[cfg(all(target_os = "macos", feature = "coreml"))] - ruvllm::kernels::ane_ops::gelu_auto( - black_box(&mut x), - batch_size, - dim, - ); + ruvllm::kernels::ane_ops::gelu_auto(black_box(&mut x), batch_size, dim); #[cfg(not(all(target_os = "macos", feature = "coreml")))] - ruvllm::kernels::activations::batch_gelu( - black_box(&mut x), - dim, - ); + ruvllm::kernels::activations::batch_gelu(black_box(&mut x), dim); }) }); } @@ -585,16 +549,9 @@ fn bench_auto_dispatch(c: &mut Criterion) { bencher.iter(|| { x.copy_from_slice(&x_orig); #[cfg(all(target_os = "macos", feature = "coreml"))] - ruvllm::kernels::ane_ops::silu_auto( - black_box(&mut x), - batch_size, - dim, - ); + ruvllm::kernels::ane_ops::silu_auto(black_box(&mut x), batch_size, dim); #[cfg(not(all(target_os = "macos", feature = "coreml")))] - ruvllm::kernels::activations::batch_silu( - black_box(&mut x), - dim, - ); + ruvllm::kernels::activations::batch_silu(black_box(&mut x), dim); }) }); } @@ -655,7 +612,7 @@ fn bench_mlp_block(c: &mut Criterion) { let total_flops = 2 * batch_size * hidden_dim * intermediate_dim // Up + batch_size * intermediate_dim // Activation - + 2 * batch_size * intermediate_dim * hidden_dim; // Down + + 2 * batch_size * intermediate_dim * hidden_dim; // Down group.throughput(Throughput::Elements(total_flops as u64)); @@ -669,7 +626,9 @@ fn bench_mlp_block(c: &mut Criterion) { black_box(&input), black_box(&w_up), black_box(&mut intermediate), - batch_size, hidden_dim, intermediate_dim, + batch_size, + hidden_dim, + intermediate_dim, ); // SiLU activation ruvllm::kernels::activations::batch_silu( @@ -681,7 +640,9 @@ fn bench_mlp_block(c: &mut Criterion) { black_box(&intermediate), black_box(&w_down), black_box(&mut output), - batch_size, intermediate_dim, hidden_dim, + batch_size, + intermediate_dim, + hidden_dim, ); }) }); @@ -697,7 +658,9 @@ fn bench_mlp_block(c: &mut Criterion) { black_box(&input), black_box(&w_up), black_box(&mut intermediate), - batch_size, hidden_dim, intermediate_dim, + batch_size, + hidden_dim, + intermediate_dim, ); // SiLU activation ruvllm::kernels::ane_ops::silu_ane( @@ -710,7 +673,9 @@ fn bench_mlp_block(c: &mut Criterion) { black_box(&intermediate), black_box(&w_down), black_box(&mut output), - batch_size, intermediate_dim, hidden_dim, + batch_size, + intermediate_dim, + hidden_dim, ); }) }); @@ -818,7 +783,9 @@ fn bench_crossover_detection(c: &mut Criterion) { black_box(&a), black_box(&b), black_box(&mut c_out), - dim, dim, dim, + dim, + dim, + dim, ); }) }); @@ -834,7 +801,9 @@ fn bench_crossover_detection(c: &mut Criterion) { black_box(&a), black_box(&b), black_box(&mut c_out), - dim, dim, dim, + dim, + dim, + dim, ); }) }); @@ -850,7 +819,9 @@ fn bench_crossover_detection(c: &mut Criterion) { black_box(&a), black_box(&b), black_box(&mut c_out), - dim, dim, dim, + dim, + dim, + dim, ); }) }); @@ -881,9 +852,9 @@ fn bench_hybrid_pipeline(c: &mut Criterion) { // Transformer configuration (Llama-7B like) let configs = [ // (batch, seq_len, hidden, heads, head_dim, intermediate) - (1, 128, 4096, 32, 128, 11008), // Short context - (1, 512, 4096, 32, 128, 11008), // Medium context - (1, 2048, 4096, 32, 128, 11008), // Long context + (1, 128, 4096, 32, 128, 11008), // Short context + (1, 512, 4096, 32, 128, 11008), // Medium context + (1, 2048, 4096, 32, 128, 11008), // Long context ]; for (batch, seq_len, hidden_dim, num_heads, head_dim, intermediate_dim) in configs { @@ -926,15 +897,50 @@ fn bench_hybrid_pipeline(c: &mut Criterion) { group.bench_function(id, |bencher| { bencher.iter(|| { // Q, K, V projections - gemm_neon_local(&hidden, &w_q, &mut q, batch * seq_len, hidden_dim, hidden_dim); - gemm_neon_local(&hidden, &w_k, &mut k, batch * seq_len, hidden_dim, hidden_dim); - gemm_neon_local(&hidden, &w_v, &mut v, batch * seq_len, hidden_dim, hidden_dim); + gemm_neon_local( + &hidden, + &w_q, + &mut q, + batch * seq_len, + hidden_dim, + hidden_dim, + ); + gemm_neon_local( + &hidden, + &w_k, + &mut k, + batch * seq_len, + hidden_dim, + hidden_dim, + ); + gemm_neon_local( + &hidden, + &w_v, + &mut v, + batch * seq_len, + hidden_dim, + hidden_dim, + ); // O projection - gemm_neon_local(&v, &w_o, &mut attn_output, batch * seq_len, hidden_dim, hidden_dim); + gemm_neon_local( + &v, + &w_o, + &mut attn_output, + batch * seq_len, + hidden_dim, + hidden_dim, + ); // MLP: up projection - gemm_neon_local(&attn_output, &w_up, &mut intermediate, batch * seq_len, hidden_dim, intermediate_dim); + gemm_neon_local( + &attn_output, + &w_up, + &mut intermediate, + batch * seq_len, + hidden_dim, + intermediate_dim, + ); // MLP: SiLU activation (in-place) ruvllm::kernels::activations::batch_silu( @@ -943,7 +949,14 @@ fn bench_hybrid_pipeline(c: &mut Criterion) { ); // MLP: down projection - gemm_neon_local(&intermediate, &w_down, &mut mlp_output, batch * seq_len, intermediate_dim, hidden_dim); + gemm_neon_local( + &intermediate, + &w_down, + &mut mlp_output, + batch * seq_len, + intermediate_dim, + hidden_dim, + ); }) }); } @@ -953,15 +966,50 @@ fn bench_hybrid_pipeline(c: &mut Criterion) { group.bench_function(id, |bencher| { bencher.iter(|| { // Q, K, V projections - ruvllm::kernels::ane_ops::matmul_ane(&hidden, &w_q, &mut q, batch * seq_len, hidden_dim, hidden_dim); - ruvllm::kernels::ane_ops::matmul_ane(&hidden, &w_k, &mut k, batch * seq_len, hidden_dim, hidden_dim); - ruvllm::kernels::ane_ops::matmul_ane(&hidden, &w_v, &mut v, batch * seq_len, hidden_dim, hidden_dim); + ruvllm::kernels::ane_ops::matmul_ane( + &hidden, + &w_q, + &mut q, + batch * seq_len, + hidden_dim, + hidden_dim, + ); + ruvllm::kernels::ane_ops::matmul_ane( + &hidden, + &w_k, + &mut k, + batch * seq_len, + hidden_dim, + hidden_dim, + ); + ruvllm::kernels::ane_ops::matmul_ane( + &hidden, + &w_v, + &mut v, + batch * seq_len, + hidden_dim, + hidden_dim, + ); // O projection - ruvllm::kernels::ane_ops::matmul_ane(&v, &w_o, &mut attn_output, batch * seq_len, hidden_dim, hidden_dim); + ruvllm::kernels::ane_ops::matmul_ane( + &v, + &w_o, + &mut attn_output, + batch * seq_len, + hidden_dim, + hidden_dim, + ); // MLP: up projection - ruvllm::kernels::ane_ops::matmul_ane(&attn_output, &w_up, &mut intermediate, batch * seq_len, hidden_dim, intermediate_dim); + ruvllm::kernels::ane_ops::matmul_ane( + &attn_output, + &w_up, + &mut intermediate, + batch * seq_len, + hidden_dim, + intermediate_dim, + ); // MLP: SiLU activation (ANE) ruvllm::kernels::ane_ops::silu_ane( @@ -971,7 +1019,14 @@ fn bench_hybrid_pipeline(c: &mut Criterion) { ); // MLP: down projection - ruvllm::kernels::ane_ops::matmul_ane(&intermediate, &w_down, &mut mlp_output, batch * seq_len, intermediate_dim, hidden_dim); + ruvllm::kernels::ane_ops::matmul_ane( + &intermediate, + &w_down, + &mut mlp_output, + batch * seq_len, + intermediate_dim, + hidden_dim, + ); }) }); @@ -980,15 +1035,50 @@ fn bench_hybrid_pipeline(c: &mut Criterion) { group.bench_function(id, |bencher| { bencher.iter(|| { // Q, K, V projections (auto-dispatch based on size) - ruvllm::kernels::ane_ops::matmul_auto(&hidden, &w_q, &mut q, batch * seq_len, hidden_dim, hidden_dim); - ruvllm::kernels::ane_ops::matmul_auto(&hidden, &w_k, &mut k, batch * seq_len, hidden_dim, hidden_dim); - ruvllm::kernels::ane_ops::matmul_auto(&hidden, &w_v, &mut v, batch * seq_len, hidden_dim, hidden_dim); + ruvllm::kernels::ane_ops::matmul_auto( + &hidden, + &w_q, + &mut q, + batch * seq_len, + hidden_dim, + hidden_dim, + ); + ruvllm::kernels::ane_ops::matmul_auto( + &hidden, + &w_k, + &mut k, + batch * seq_len, + hidden_dim, + hidden_dim, + ); + ruvllm::kernels::ane_ops::matmul_auto( + &hidden, + &w_v, + &mut v, + batch * seq_len, + hidden_dim, + hidden_dim, + ); // O projection (auto-dispatch) - ruvllm::kernels::ane_ops::matmul_auto(&v, &w_o, &mut attn_output, batch * seq_len, hidden_dim, hidden_dim); + ruvllm::kernels::ane_ops::matmul_auto( + &v, + &w_o, + &mut attn_output, + batch * seq_len, + hidden_dim, + hidden_dim, + ); // MLP: up projection (auto-dispatch) - ruvllm::kernels::ane_ops::matmul_auto(&attn_output, &w_up, &mut intermediate, batch * seq_len, hidden_dim, intermediate_dim); + ruvllm::kernels::ane_ops::matmul_auto( + &attn_output, + &w_up, + &mut intermediate, + batch * seq_len, + hidden_dim, + intermediate_dim, + ); // MLP: SiLU activation (auto-dispatch - typically ANE) ruvllm::kernels::ane_ops::silu_auto( @@ -998,7 +1088,14 @@ fn bench_hybrid_pipeline(c: &mut Criterion) { ); // MLP: down projection (auto-dispatch) - ruvllm::kernels::ane_ops::matmul_auto(&intermediate, &w_down, &mut mlp_output, batch * seq_len, intermediate_dim, hidden_dim); + ruvllm::kernels::ane_ops::matmul_auto( + &intermediate, + &w_down, + &mut mlp_output, + batch * seq_len, + intermediate_dim, + hidden_dim, + ); }) }); } @@ -1017,14 +1114,14 @@ fn bench_activation_crossover(c: &mut Criterion) { // Test various sizes to find where ANE beats NEON let sizes = [ - (1, 128), // Tiny - (1, 512), // Small - (1, 2048), // Medium - (1, 4096), // Llama hidden - (1, 11008), // Llama intermediate - (32, 4096), // Batch - (64, 4096), // Larger batch - (128, 4096), // Big batch + (1, 128), // Tiny + (1, 512), // Small + (1, 2048), // Medium + (1, 4096), // Llama hidden + (1, 11008), // Llama intermediate + (32, 4096), // Batch + (64, 4096), // Larger batch + (128, 4096), // Big batch ]; for (batch_size, dim) in sizes { @@ -1042,10 +1139,7 @@ fn bench_activation_crossover(c: &mut Criterion) { group.bench_function(id, |bencher| { bencher.iter(|| { x.copy_from_slice(&x_orig); - ruvllm::kernels::activations::batch_silu( - black_box(&mut x), - dim, - ); + ruvllm::kernels::activations::batch_silu(black_box(&mut x), dim); }) }); } @@ -1058,11 +1152,7 @@ fn bench_activation_crossover(c: &mut Criterion) { group.bench_function(id, |bencher| { bencher.iter(|| { x.copy_from_slice(&x_orig); - ruvllm::kernels::ane_ops::silu_ane( - black_box(&mut x), - batch_size, - dim, - ); + ruvllm::kernels::ane_ops::silu_ane(black_box(&mut x), batch_size, dim); }) }); } @@ -1075,11 +1165,7 @@ fn bench_activation_crossover(c: &mut Criterion) { group.bench_function(id, |bencher| { bencher.iter(|| { x.copy_from_slice(&x_orig); - ruvllm::kernels::ane_ops::silu_auto( - black_box(&mut x), - batch_size, - dim, - ); + ruvllm::kernels::ane_ops::silu_auto(black_box(&mut x), batch_size, dim); }) }); } diff --git a/crates/ruvllm/benches/attention_bench.rs b/crates/ruvllm/benches/attention_bench.rs index 01256fac6..7b531618c 100644 --- a/crates/ruvllm/benches/attention_bench.rs +++ b/crates/ruvllm/benches/attention_bench.rs @@ -72,7 +72,11 @@ impl PagedKvCache { for (block_idx, block) in self.key_blocks.iter().enumerate() { let tokens_in_block = if block_idx == self.key_blocks.len() - 1 { let rem = self.num_tokens % self.block_size; - if rem == 0 { self.block_size } else { rem } + if rem == 0 { + self.block_size + } else { + rem + } } else { self.block_size }; @@ -87,7 +91,11 @@ impl PagedKvCache { for (block_idx, block) in self.value_blocks.iter().enumerate() { let tokens_in_block = if block_idx == self.value_blocks.len() - 1 { let rem = self.num_tokens % self.block_size; - if rem == 0 { self.block_size } else { rem } + if rem == 0 { + self.block_size + } else { + rem + } } else { self.block_size }; @@ -449,11 +457,15 @@ fn bench_flash_attention(c: &mut Criterion) { ); group.throughput(Throughput::Elements((seq_len * head_dim) as u64)); - group.bench_with_input(id, &(query.clone(), key.clone(), value.clone()), |b, (q, k, v)| { - b.iter(|| { - flash_attention_neon(black_box(q), black_box(k), black_box(v), scale, true) - }) - }); + group.bench_with_input( + id, + &(query.clone(), key.clone(), value.clone()), + |b, (q, k, v)| { + b.iter(|| { + flash_attention_neon(black_box(q), black_box(k), black_box(v), scale, true) + }) + }, + ); } } @@ -476,20 +488,32 @@ fn bench_flash_attention_batched(c: &mut Criterion) { let id = BenchmarkId::new(format!("heads_{}_seq_{}", num_heads, seq_len), seq_len); - group.throughput(Throughput::Elements((num_heads * seq_len * head_dim) as u64)); - group.bench_with_input(id, &(queries.clone(), key.clone(), value.clone()), |b, (q, k, v)| { - b.iter(|| { - // Process all heads - let mut outputs = Vec::with_capacity(num_heads * head_dim); - for h in 0..num_heads { - let q_offset = h * head_dim; - let q_slice = &q[q_offset..q_offset + head_dim]; - let out = flash_attention_neon(black_box(q_slice), black_box(k), black_box(v), scale, true); - outputs.extend(out); - } - outputs - }) - }); + group.throughput(Throughput::Elements( + (num_heads * seq_len * head_dim) as u64, + )); + group.bench_with_input( + id, + &(queries.clone(), key.clone(), value.clone()), + |b, (q, k, v)| { + b.iter(|| { + // Process all heads + let mut outputs = Vec::with_capacity(num_heads * head_dim); + for h in 0..num_heads { + let q_offset = h * head_dim; + let q_slice = &q[q_offset..q_offset + head_dim]; + let out = flash_attention_neon( + black_box(q_slice), + black_box(k), + black_box(v), + scale, + true, + ); + outputs.extend(out); + } + outputs + }) + }, + ); } group.finish(); @@ -525,9 +549,7 @@ fn bench_paged_attention(c: &mut Criterion) { group.throughput(Throughput::Elements((num_tokens * head_dim) as u64)); group.bench_with_input(id, &(query.clone(), kv_cache.clone()), |b, (q, cache)| { - b.iter(|| { - paged_attention_neon(black_box(q), black_box(cache), &[], scale) - }) + b.iter(|| paged_attention_neon(black_box(q), black_box(cache), &[], scale)) }); } } @@ -557,7 +579,9 @@ fn bench_mqa(c: &mut Criterion) { let id = BenchmarkId::new(format!("heads_{}_seq_{}", num_heads, seq_len), seq_len); - group.throughput(Throughput::Elements((num_heads * seq_len * head_dim) as u64)); + group.throughput(Throughput::Elements( + (num_heads * seq_len * head_dim) as u64, + )); group.bench_with_input( id, &(queries.clone(), key.clone(), value.clone(), config), @@ -595,12 +619,11 @@ fn bench_gqa(c: &mut Criterion) { let values = random_tensor(seq_len * num_kv_heads * head_dim); let ratio = num_heads / num_kv_heads; - let id = BenchmarkId::new( - format!("ratio_{}_seq_{}", ratio, seq_len), - seq_len, - ); + let id = BenchmarkId::new(format!("ratio_{}_seq_{}", ratio, seq_len), seq_len); - group.throughput(Throughput::Elements((num_heads * seq_len * head_dim) as u64)); + group.throughput(Throughput::Elements( + (num_heads * seq_len * head_dim) as u64, + )); group.bench_with_input( id, &(queries.clone(), keys.clone(), values.clone(), config), @@ -632,14 +655,21 @@ fn bench_attention_memory_efficiency(c: &mut Criterion) { // Memory for Q, K, V in bytes let memory_bytes = (1 + seq_len * 2) * head_dim * 4; // f32 = 4 bytes - let id = BenchmarkId::new(format!("seq_{}_mem_{}KB", seq_len, memory_bytes / 1024), seq_len); + let id = BenchmarkId::new( + format!("seq_{}_mem_{}KB", seq_len, memory_bytes / 1024), + seq_len, + ); group.throughput(Throughput::Bytes(memory_bytes as u64)); - group.bench_with_input(id, &(query.clone(), key.clone(), value.clone()), |b, (q, k, v)| { - b.iter(|| { - flash_attention_neon(black_box(q), black_box(k), black_box(v), scale, true) - }) - }); + group.bench_with_input( + id, + &(query.clone(), key.clone(), value.clone()), + |b, (q, k, v)| { + b.iter(|| { + flash_attention_neon(black_box(q), black_box(k), black_box(v), scale, true) + }) + }, + ); } group.finish(); @@ -667,11 +697,15 @@ fn bench_attention_scaling(c: &mut Criterion) { let flops = 4 * seq_len * head_dim; group.throughput(Throughput::Elements(flops as u64)); - group.bench_with_input(id, &(query.clone(), key.clone(), value.clone()), |b, (q, k, v)| { - b.iter(|| { - flash_attention_neon(black_box(q), black_box(k), black_box(v), scale, true) - }) - }); + group.bench_with_input( + id, + &(query.clone(), key.clone(), value.clone()), + |b, (q, k, v)| { + b.iter(|| { + flash_attention_neon(black_box(q), black_box(k), black_box(v), scale, true) + }) + }, + ); } group.finish(); diff --git a/crates/ruvllm/benches/e2e_bench.rs b/crates/ruvllm/benches/e2e_bench.rs index 167a77549..8fde50faa 100644 --- a/crates/ruvllm/benches/e2e_bench.rs +++ b/crates/ruvllm/benches/e2e_bench.rs @@ -138,8 +138,12 @@ impl TransformerLayer { Self { q_proj: random_tensor(hidden * hidden), - k_proj: random_tensor(hidden * (hidden / config.num_attention_heads * config.num_kv_heads)), - v_proj: random_tensor(hidden * (hidden / config.num_attention_heads * config.num_kv_heads)), + k_proj: random_tensor( + hidden * (hidden / config.num_attention_heads * config.num_kv_heads), + ), + v_proj: random_tensor( + hidden * (hidden / config.num_attention_heads * config.num_kv_heads), + ), o_proj: random_tensor(hidden * hidden), gate_proj: random_tensor(hidden * intermediate), up_proj: random_tensor(hidden * intermediate), @@ -159,15 +163,31 @@ impl TransformerLayer { // 2. Attention projections (Q, K, V) let mut q = gemv(&self.q_proj, hidden_state, hidden, hidden); - let k = gemv(&self.k_proj, hidden_state, hidden, hidden / self.config.num_attention_heads * self.config.num_kv_heads); - let v = gemv(&self.v_proj, hidden_state, hidden, hidden / self.config.num_attention_heads * self.config.num_kv_heads); + let k = gemv( + &self.k_proj, + hidden_state, + hidden, + hidden / self.config.num_attention_heads * self.config.num_kv_heads, + ); + let v = gemv( + &self.v_proj, + hidden_state, + hidden, + hidden / self.config.num_attention_heads * self.config.num_kv_heads, + ); // 3. Apply RoPE (simplified) apply_rope_simple(&mut q, self.config.head_dim, kv_cache_len); // 4. Attention (simplified - would use flash attention in practice) // For single token decode, this is essentially a dot product with cached KV - let attn_output = attention_decode(&q, &k, &v, self.config.num_attention_heads, self.config.head_dim); + let attn_output = attention_decode( + &q, + &k, + &v, + self.config.num_attention_heads, + self.config.head_dim, + ); // 5. Output projection let attn_projected = gemv(&self.o_proj, &attn_output, hidden, hidden); @@ -181,8 +201,18 @@ impl TransformerLayer { rms_norm_inplace(hidden_state, &self.post_attn_norm_weight, 1e-6); // 8. MLP forward - let gate_out = gemv(&self.gate_proj, hidden_state, hidden, self.config.intermediate_size); - let up_out = gemv(&self.up_proj, hidden_state, hidden, self.config.intermediate_size); + let gate_out = gemv( + &self.gate_proj, + hidden_state, + hidden, + self.config.intermediate_size, + ); + let up_out = gemv( + &self.up_proj, + hidden_state, + hidden, + self.config.intermediate_size, + ); // SiLU activation and element-wise multiply let mut mlp_intermediate = Vec::with_capacity(self.config.intermediate_size); @@ -192,7 +222,12 @@ impl TransformerLayer { } // Down projection - let mlp_output = gemv(&self.down_proj, &mlp_intermediate, self.config.intermediate_size, hidden); + let mlp_output = gemv( + &self.down_proj, + &mlp_intermediate, + self.config.intermediate_size, + hidden, + ); // 9. Residual connection for i in 0..hidden { @@ -242,7 +277,13 @@ fn apply_rope_simple(x: &mut [f32], head_dim: usize, position: usize) { } } -fn attention_decode(q: &[f32], k: &[f32], v: &[f32], num_heads: usize, head_dim: usize) -> Vec { +fn attention_decode( + q: &[f32], + k: &[f32], + v: &[f32], + num_heads: usize, + head_dim: usize, +) -> Vec { // Simplified single-token attention decode let mut output = vec![0.0f32; num_heads * head_dim]; @@ -355,7 +396,9 @@ fn bench_multi_layer_forward(c: &mut Criterion) { let id = BenchmarkId::new(format!("{}_layers", num_layers), num_layers); - group.throughput(Throughput::Elements((config.params_per_layer() * num_layers) as u64)); + group.throughput(Throughput::Elements( + (config.params_per_layer() * num_layers) as u64, + )); group.bench_function(id, |b| { b.iter(|| { let mut h = hidden_state.clone(); @@ -385,16 +428,19 @@ fn bench_kv_cache_operations(c: &mut Criterion) { let k = random_tensor(config.num_kv_heads * config.head_dim); let v = random_tensor(config.num_kv_heads * config.head_dim); - group.bench_function(BenchmarkId::new(format!("{}_append", name), config.num_kv_heads), |b| { - b.iter_batched( - || KvCache::new(&config), - |mut cache| { - cache.append(black_box(&k), black_box(&v)); - cache - }, - criterion::BatchSize::SmallInput, - ) - }); + group.bench_function( + BenchmarkId::new(format!("{}_append", name), config.num_kv_heads), + |b| { + b.iter_batched( + || KvCache::new(&config), + |mut cache| { + cache.append(black_box(&k), black_box(&v)); + cache + }, + criterion::BatchSize::SmallInput, + ) + }, + ); // Memory footprint at various sequence lengths for seq_len in [256, 512, 1024, 2048] { @@ -500,14 +546,22 @@ fn bench_model_memory(c: &mut Criterion) { let fp16_gb = config.memory_bytes_fp16() as f64 / (1024.0 * 1024.0 * 1024.0); let int4_gb = config.memory_bytes_int4() as f64 / (1024.0 * 1024.0 * 1024.0); - println!("{}: FP16={:.2}GB, INT4={:.2}GB, params={}M", - name, fp16_gb, int4_gb, config.total_params() / 1_000_000); + println!( + "{}: FP16={:.2}GB, INT4={:.2}GB, params={}M", + name, + fp16_gb, + int4_gb, + config.total_params() / 1_000_000 + ); // Benchmark single layer to estimate per-layer latency let layer = TransformerLayer::new(config); let mut hidden_state = random_tensor(config.hidden_size); - let id = BenchmarkId::new(format!("{}_fp16_{:.1}GB", name, fp16_gb), config.total_params()); + let id = BenchmarkId::new( + format!("{}_fp16_{:.1}GB", name, fp16_gb), + config.total_params(), + ); group.throughput(Throughput::Elements(config.params_per_layer() as u64)); group.bench_function(id, |b| { @@ -549,16 +603,19 @@ fn bench_inference_components(c: &mut Criterion) { // Linear projection (hidden -> hidden) let proj_matrix = random_tensor(hidden * hidden); group.bench_function("linear_4096x4096", |b| { - b.iter(|| { - gemv(black_box(&proj_matrix), black_box(&input), hidden, hidden) - }) + b.iter(|| gemv(black_box(&proj_matrix), black_box(&input), hidden, hidden)) }); // Linear projection (hidden -> intermediate) let mlp_up_matrix = random_tensor(hidden * intermediate); group.bench_function("linear_4096x11008", |b| { b.iter(|| { - gemv(black_box(&mlp_up_matrix), black_box(&input), hidden, intermediate) + gemv( + black_box(&mlp_up_matrix), + black_box(&input), + hidden, + intermediate, + ) }) }); @@ -570,7 +627,11 @@ fn bench_inference_components(c: &mut Criterion) { |mut x| { for h in 0..config.num_attention_heads { let offset = h * config.head_dim; - apply_rope_simple(black_box(&mut x[offset..offset + config.head_dim]), config.head_dim, 100); + apply_rope_simple( + black_box(&mut x[offset..offset + config.head_dim]), + config.head_dim, + 100, + ); } x }, diff --git a/crates/ruvllm/benches/lora_bench.rs b/crates/ruvllm/benches/lora_bench.rs index b978f2282..82d271dbe 100644 --- a/crates/ruvllm/benches/lora_bench.rs +++ b/crates/ruvllm/benches/lora_bench.rs @@ -371,9 +371,7 @@ fn bench_lora_forward(c: &mut Criterion) { ); group.throughput(Throughput::Elements(adapter.param_count() as u64)); - group.bench_function(id, |b| { - b.iter(|| adapter.forward(black_box(&input))) - }); + group.bench_function(id, |b| b.iter(|| adapter.forward(black_box(&input)))); } } @@ -423,7 +421,9 @@ fn bench_lora_forward_batch(c: &mut Criterion) { let id = BenchmarkId::new(format!("batch_{}", batch_size), batch_size); - group.throughput(Throughput::Elements((batch_size * adapter.param_count()) as u64)); + group.throughput(Throughput::Elements( + (batch_size * adapter.param_count()) as u64, + )); group.bench_function(id, |b| { b.iter(|| adapter.forward_batch(black_box(&input), batch_size)) }); @@ -447,11 +447,7 @@ fn bench_lora_gradient_accumulation(c: &mut Criterion) { group.throughput(Throughput::Elements(adapter.param_count() as u64)); group.bench_function(id, |b| { b.iter(|| { - adapter.accumulate_gradient( - black_box(&input), - black_box(&grad_output), - 0.8, - ); + adapter.accumulate_gradient(black_box(&input), black_box(&grad_output), 0.8); }) }); } @@ -591,9 +587,7 @@ fn bench_lora_memory_footprint(c: &mut Criterion) { let id = BenchmarkId::new(format!("{}_{}KB", name, memory_bytes / 1024), memory_bytes); group.throughput(Throughput::Bytes(memory_bytes as u64)); - group.bench_function(id, |b| { - b.iter(|| adapter.forward(black_box(&input))) - }); + group.bench_function(id, |b| b.iter(|| adapter.forward(black_box(&input)))); } group.finish(); diff --git a/crates/ruvllm/benches/matmul_bench.rs b/crates/ruvllm/benches/matmul_bench.rs index a19be8f79..b7879f8d3 100644 --- a/crates/ruvllm/benches/matmul_bench.rs +++ b/crates/ruvllm/benches/matmul_bench.rs @@ -449,7 +449,13 @@ fn bench_gemv(c: &mut Criterion) { let mut group = c.benchmark_group("gemv"); group.sample_size(50); - for (m, n) in [(256, 256), (512, 512), (1024, 1024), (2048, 2048), (4096, 4096)] { + for (m, n) in [ + (256, 256), + (512, 512), + (1024, 1024), + (2048, 2048), + (4096, 4096), + ] { let a = random_tensor(m * n); let x = random_tensor(n); let mut y = vec![0.0; m]; @@ -489,7 +495,14 @@ fn bench_gemm(c: &mut Criterion) { group.throughput(Throughput::Elements(flops as u64)); group.bench_function(id, |bencher| { bencher.iter(|| { - gemm_neon(black_box(&mat_a), black_box(&mat_b), black_box(&mut c_out), m, k, n); + gemm_neon( + black_box(&mat_a), + black_box(&mat_b), + black_box(&mut c_out), + m, + k, + n, + ); }) }); } @@ -503,12 +516,12 @@ fn bench_gemm_non_square(c: &mut Criterion) { // Common shapes in LLM inference let shapes = [ - (1, 4096, 4096), // Single token projection - (32, 4096, 4096), // Batch projection - (128, 4096, 4096), // Larger batch - (1, 4096, 11008), // MLP up projection (Llama2 7B) - (1, 11008, 4096), // MLP down projection - (32, 128, 4096), // Attention output + (1, 4096, 4096), // Single token projection + (32, 4096, 4096), // Batch projection + (128, 4096, 4096), // Larger batch + (1, 4096, 11008), // MLP up projection (Llama2 7B) + (1, 11008, 4096), // MLP down projection + (32, 128, 4096), // Attention output ]; for (m, k, n) in shapes { @@ -523,7 +536,14 @@ fn bench_gemm_non_square(c: &mut Criterion) { group.throughput(Throughput::Elements(flops as u64)); group.bench_function(id, |bencher| { bencher.iter(|| { - gemm_neon(black_box(&mat_a), black_box(&mat_b), black_box(&mut c_out), m, k, n); + gemm_neon( + black_box(&mat_a), + black_box(&mat_b), + black_box(&mut c_out), + m, + k, + n, + ); }) }); } @@ -592,7 +612,14 @@ fn bench_gemm_nt(c: &mut Criterion) { group.throughput(Throughput::Elements(flops as u64)); group.bench_function(id, |b| { b.iter(|| { - gemm_nt_neon(black_box(&a), black_box(&b_t), black_box(&mut c_out), m, k, n); + gemm_nt_neon( + black_box(&a), + black_box(&b_t), + black_box(&mut c_out), + m, + k, + n, + ); }) }); } @@ -637,7 +664,14 @@ fn bench_tiling_efficiency(c: &mut Criterion) { group.throughput(Throughput::Elements(flops as u64)); group.bench_function(id, |bencher| { bencher.iter(|| { - gemm_neon(black_box(&mat_a), black_box(&mat_b), black_box(&mut c_out), size, size, size); + gemm_neon( + black_box(&mat_a), + black_box(&mat_b), + black_box(&mut c_out), + size, + size, + size, + ); }) }); } @@ -651,9 +685,9 @@ fn bench_memory_bandwidth(c: &mut Criterion) { // Test memory-bound vs compute-bound behavior for (m, k, n) in [ - (1, 4096, 4096), // Very memory bound (GEMV-like) - (32, 4096, 4096), // More compute - (128, 4096, 4096), // Compute bound + (1, 4096, 4096), // Very memory bound (GEMV-like) + (32, 4096, 4096), // More compute + (128, 4096, 4096), // Compute bound ] { let mat_a = random_tensor(m * k); let mat_b = random_tensor(k * n); @@ -664,14 +698,27 @@ fn bench_memory_bandwidth(c: &mut Criterion) { let flops = 2 * m * k * n; let id = BenchmarkId::new( - format!("{}x{}x{}_ratio_{:.2}", m, k, n, flops as f64 / memory_bytes as f64), + format!( + "{}x{}x{}_ratio_{:.2}", + m, + k, + n, + flops as f64 / memory_bytes as f64 + ), m, ); group.throughput(Throughput::Bytes(memory_bytes as u64)); group.bench_function(id, |bencher| { bencher.iter(|| { - gemm_neon(black_box(&mat_a), black_box(&mat_b), black_box(&mut c_out), m, k, n); + gemm_neon( + black_box(&mat_a), + black_box(&mat_b), + black_box(&mut c_out), + m, + k, + n, + ); }) }); } @@ -705,7 +752,14 @@ fn bench_llm_projection_sizes(c: &mut Criterion) { group.throughput(Throughput::Elements(flops as u64)); group.bench_function(id, |bencher| { bencher.iter(|| { - gemm_neon(black_box(&mat_a), black_box(&mat_b), black_box(&mut c_out), m, k, n); + gemm_neon( + black_box(&mat_a), + black_box(&mat_b), + black_box(&mut c_out), + m, + k, + n, + ); }) }); } @@ -1013,7 +1067,14 @@ mod parallel_benches { group.throughput(Throughput::Elements(flops as u64)); group.bench_function(id, |bencher| { bencher.iter(|| { - gemm_parallel(black_box(&mat_a), black_box(&mat_b), black_box(&mut c_out), m, k, n); + gemm_parallel( + black_box(&mat_a), + black_box(&mat_b), + black_box(&mut c_out), + m, + k, + n, + ); }) }); } @@ -1108,13 +1169,27 @@ mod parallel_benches { group.bench_function("single_thread", |bencher| { bencher.iter(|| { - gemm_neon(black_box(&mat_a), black_box(&mat_b), black_box(&mut c_out), m, k, n); + gemm_neon( + black_box(&mat_a), + black_box(&mat_b), + black_box(&mut c_out), + m, + k, + n, + ); }) }); group.bench_function("parallel", |bencher| { bencher.iter(|| { - gemm_parallel(black_box(&mat_a), black_box(&mat_b), black_box(&mut c_out), m, k, n); + gemm_parallel( + black_box(&mat_a), + black_box(&mat_b), + black_box(&mut c_out), + m, + k, + n, + ); }) }); diff --git a/crates/ruvllm/benches/metal_bench.rs b/crates/ruvllm/benches/metal_bench.rs index 588b094f1..0a0cce529 100644 --- a/crates/ruvllm/benches/metal_bench.rs +++ b/crates/ruvllm/benches/metal_bench.rs @@ -3,12 +3,12 @@ //! Benchmarks Metal compute shaders for LLM operations. //! Only runs on macOS with `metal-compute` feature enabled. -use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; -#[cfg(all(target_os = "macos", feature = "metal-compute"))] -use ruvllm::metal::{MetalContext, MetalConfig}; #[cfg(all(target_os = "macos", feature = "metal-compute"))] use ruvllm::kernels::AttentionConfig; +#[cfg(all(target_os = "macos", feature = "metal-compute"))] +use ruvllm::metal::{MetalConfig, MetalContext}; #[cfg(all(target_os = "macos", feature = "metal-compute"))] fn bench_flash_attention_metal(c: &mut Criterion) { @@ -46,7 +46,14 @@ fn bench_flash_attention_metal(c: &mut Criterion) { BenchmarkId::new("metal", format!("seq{}_kv{}", seq_len, kv_len)), &(&query, &key, &value, &config), |b, (q, k, v, cfg)| { - b.iter(|| ctx.flash_attention(black_box(*q), black_box(*k), black_box(*v), black_box(*cfg))) + b.iter(|| { + ctx.flash_attention( + black_box(*q), + black_box(*k), + black_box(*v), + black_box(*cfg), + ) + }) }, ); } @@ -171,7 +178,10 @@ fn bench_optimized_gemm_metal(c: &mut Criterion) { return; } - println!("Available optimizations: {:?}", ctx.available_optimizations()); + println!( + "Available optimizations: {:?}", + ctx.available_optimizations() + ); let mut group = c.benchmark_group("metal_gemm_optimized"); @@ -180,8 +190,12 @@ fn bench_optimized_gemm_metal(c: &mut Criterion) { let n = size; let k = size; - let a: Vec = (0..m * k).map(|i| half::f16::from_f32((i as f32) * 0.001)).collect(); - let b: Vec = (0..k * n).map(|i| half::f16::from_f32((i as f32) * 0.001)).collect(); + let a: Vec = (0..m * k) + .map(|i| half::f16::from_f32((i as f32) * 0.001)) + .collect(); + let b: Vec = (0..k * n) + .map(|i| half::f16::from_f32((i as f32) * 0.001)) + .collect(); // Benchmark standard GEMM group.bench_with_input( @@ -217,7 +231,14 @@ fn bench_fused_attention_metal(c: &mut Criterion) { let mut group = c.benchmark_group("metal_fused_attention"); - for (seq_len, kv_len) in [(1, 512), (1, 2048), (1, 4096), (4, 512), (4, 2048), (16, 2048)] { + for (seq_len, kv_len) in [ + (1, 512), + (1, 2048), + (1, 4096), + (4, 512), + (4, 2048), + (16, 2048), + ] { let num_heads = 32; let num_kv_heads = 8; let head_dim = 128; @@ -246,7 +267,14 @@ fn bench_fused_attention_metal(c: &mut Criterion) { BenchmarkId::new("standard", format!("seq{}_kv{}", seq_len, kv_len)), &(&query, &key, &value, &config), |b, (q, k, v, cfg)| { - b.iter(|| ctx.flash_attention(black_box(*q), black_box(*k), black_box(*v), black_box(*cfg))) + b.iter(|| { + ctx.flash_attention( + black_box(*q), + black_box(*k), + black_box(*v), + black_box(*cfg), + ) + }) }, ); @@ -255,7 +283,17 @@ fn bench_fused_attention_metal(c: &mut Criterion) { BenchmarkId::new("fused_fa2", format!("seq{}_kv{}", seq_len, kv_len)), &(&query, &key, &value, num_heads, num_kv_heads, head_dim), |b, (q, k, v, nh, nkv, hd)| { - b.iter(|| ctx.fused_attention(black_box(*q), black_box(*k), black_box(*v), *nh, *nkv, *hd, true)) + b.iter(|| { + ctx.fused_attention( + black_box(*q), + black_box(*k), + black_box(*v), + *nh, + *nkv, + *hd, + true, + ) + }) }, ); } @@ -273,7 +311,12 @@ fn bench_fused_norm_residual_metal(c: &mut Criterion) { } }; - if ctx.available_optimizations().iter().find(|&&s| s == "fused_layernorm_residual").is_none() { + if ctx + .available_optimizations() + .iter() + .find(|&&s| s == "fused_layernorm_residual") + .is_none() + { eprintln!("Fused LayerNorm+Residual not available, skipping benchmark"); return; } @@ -315,7 +358,12 @@ fn bench_fused_norm_residual_metal(c: &mut Criterion) { |bench, _| { bench.iter(|| { let mut x_clone = x.clone(); - ctx.fused_rmsnorm_residual(black_box(&mut x_clone), black_box(&residual), black_box(&weight), 1e-6) + ctx.fused_rmsnorm_residual( + black_box(&mut x_clone), + black_box(&residual), + black_box(&weight), + 1e-6, + ) }) }, ); @@ -327,7 +375,13 @@ fn bench_fused_norm_residual_metal(c: &mut Criterion) { |bench, _| { bench.iter(|| { let mut x_clone = x.clone(); - ctx.fused_layernorm_residual(black_box(&mut x_clone), black_box(&residual), black_box(&weight), black_box(&bias), 1e-6) + ctx.fused_layernorm_residual( + black_box(&mut x_clone), + black_box(&residual), + black_box(&weight), + black_box(&bias), + 1e-6, + ) }) }, ); @@ -374,7 +428,15 @@ fn bench_rope_attention_fusion_metal(c: &mut Criterion) { let mut k_clone = (*k).clone(); let _ = ctx.apply_rope(&mut q_clone, 0, *nh, *hd, rope_theta); let _ = ctx.apply_rope(&mut k_clone, 0, *nkv, *hd, rope_theta); - ctx.fused_attention(black_box(&q_clone), black_box(&k_clone), black_box(*v), *nh, *nkv, *hd, true) + ctx.fused_attention( + black_box(&q_clone), + black_box(&k_clone), + black_box(*v), + *nh, + *nkv, + *hd, + true, + ) }) }, ); @@ -385,7 +447,17 @@ fn bench_rope_attention_fusion_metal(c: &mut Criterion) { &(&query, &key, &value, num_heads, num_kv_heads, head_dim), |b, (q, k, v, nh, nkv, hd)| { b.iter(|| { - ctx.rope_then_attention(black_box(*q), black_box(*k), black_box(*v), *nh, *nkv, *hd, 0, rope_theta, true) + ctx.rope_then_attention( + black_box(*q), + black_box(*k), + black_box(*v), + *nh, + *nkv, + *hd, + 0, + rope_theta, + true, + ) }) }, ); @@ -404,7 +476,12 @@ fn bench_swiglu_metal(c: &mut Criterion) { } }; - if ctx.available_optimizations().iter().find(|&&s| s == "fused_swiglu").is_none() { + if ctx + .available_optimizations() + .iter() + .find(|&&s| s == "fused_swiglu") + .is_none() + { eprintln!("Fused SwiGLU not available, skipping benchmark"); return; } @@ -419,9 +496,7 @@ fn bench_swiglu_metal(c: &mut Criterion) { group.bench_with_input( BenchmarkId::new("fused", format!("size{}", size)), &(&gate, &up), - |b, (g, u)| { - b.iter(|| ctx.fused_swiglu(black_box(*g), black_box(*u))) - }, + |b, (g, u)| b.iter(|| ctx.fused_swiglu(black_box(*g), black_box(*u))), ); // CPU baseline for comparison @@ -430,7 +505,9 @@ fn bench_swiglu_metal(c: &mut Criterion) { &(&gate, &up), |b, (g, u)| { b.iter(|| { - let result: Vec = g.iter().zip(u.iter()) + let result: Vec = g + .iter() + .zip(u.iter()) .map(|(&g_val, &u_val)| { // SwiGLU: swish(gate) * up let swish = g_val / (1.0 + (-g_val).exp()); @@ -501,9 +578,6 @@ criterion_group!( ); #[cfg(not(all(target_os = "macos", feature = "metal-compute")))] -criterion_group!( - metal_benches, - bench_cpu_gemm, -); +criterion_group!(metal_benches, bench_cpu_gemm,); criterion_main!(metal_benches); diff --git a/crates/ruvllm/benches/norm_bench.rs b/crates/ruvllm/benches/norm_bench.rs index 99c5490c6..63dccfc06 100644 --- a/crates/ruvllm/benches/norm_bench.rs +++ b/crates/ruvllm/benches/norm_bench.rs @@ -100,15 +100,24 @@ unsafe fn rms_norm_neon_impl(x: &mut [f32], weight: &[f32], eps: f32) { let x1 = vld1q_f32(x_ptr.add(idx + 4)); let w1 = vld1q_f32(w_ptr.add(idx + 4)); - vst1q_f32(x_ptr.add(idx + 4), vmulq_f32(vmulq_f32(x1, inv_rms_vec), w1)); + vst1q_f32( + x_ptr.add(idx + 4), + vmulq_f32(vmulq_f32(x1, inv_rms_vec), w1), + ); let x2 = vld1q_f32(x_ptr.add(idx + 8)); let w2 = vld1q_f32(w_ptr.add(idx + 8)); - vst1q_f32(x_ptr.add(idx + 8), vmulq_f32(vmulq_f32(x2, inv_rms_vec), w2)); + vst1q_f32( + x_ptr.add(idx + 8), + vmulq_f32(vmulq_f32(x2, inv_rms_vec), w2), + ); let x3 = vld1q_f32(x_ptr.add(idx + 12)); let w3 = vld1q_f32(w_ptr.add(idx + 12)); - vst1q_f32(x_ptr.add(idx + 12), vmulq_f32(vmulq_f32(x3, inv_rms_vec), w3)); + vst1q_f32( + x_ptr.add(idx + 12), + vmulq_f32(vmulq_f32(x3, inv_rms_vec), w3), + ); idx += 16; } @@ -412,7 +421,12 @@ fn bench_layer_norm(c: &mut Criterion) { group.bench_function(id, |b| { b.iter(|| { let mut x_copy = x.clone(); - layer_norm_neon(black_box(&mut x_copy), black_box(&weight), black_box(&bias), eps); + layer_norm_neon( + black_box(&mut x_copy), + black_box(&weight), + black_box(&bias), + eps, + ); x_copy }) }); @@ -431,13 +445,22 @@ fn bench_batched_rms_norm(c: &mut Criterion) { let weight = random_tensor(dim); let eps = 1e-6; - let id = BenchmarkId::new(format!("batch_{}_dim_{}", batch_size, dim), batch_size * dim); + let id = BenchmarkId::new( + format!("batch_{}_dim_{}", batch_size, dim), + batch_size * dim, + ); group.throughput(Throughput::Elements((batch_size * dim) as u64)); group.bench_function(id, |b| { b.iter(|| { let mut x_copy = x.clone(); - batched_rms_norm_neon(black_box(&mut x_copy), black_box(&weight), batch_size, dim, eps); + batched_rms_norm_neon( + black_box(&mut x_copy), + black_box(&weight), + batch_size, + dim, + eps, + ); x_copy }) }); @@ -458,7 +481,10 @@ fn bench_batched_layer_norm(c: &mut Criterion) { let bias = random_tensor(dim); let eps = 1e-6; - let id = BenchmarkId::new(format!("batch_{}_dim_{}", batch_size, dim), batch_size * dim); + let id = BenchmarkId::new( + format!("batch_{}_dim_{}", batch_size, dim), + batch_size * dim, + ); group.throughput(Throughput::Elements((batch_size * dim) as u64)); group.bench_function(id, |b| { @@ -502,7 +528,12 @@ fn bench_rms_vs_layer_norm(c: &mut Criterion) { group.bench_function(BenchmarkId::new("layer_norm", dim), |b| { b.iter(|| { let mut x_copy = x.clone(); - layer_norm_neon(black_box(&mut x_copy), black_box(&weight), black_box(&bias), eps); + layer_norm_neon( + black_box(&mut x_copy), + black_box(&weight), + black_box(&bias), + eps, + ); x_copy }) }); @@ -521,9 +552,7 @@ fn bench_compute_rms(c: &mut Criterion) { let id = BenchmarkId::new(format!("dim_{}", dim), dim); group.throughput(Throughput::Elements(dim as u64)); - group.bench_function(id, |b| { - b.iter(|| compute_rms(black_box(&x))) - }); + group.bench_function(id, |b| b.iter(|| compute_rms(black_box(&x)))); } group.finish(); diff --git a/crates/ruvllm/benches/rope_bench.rs b/crates/ruvllm/benches/rope_bench.rs index 19fca5329..9cbda2548 100644 --- a/crates/ruvllm/benches/rope_bench.rs +++ b/crates/ruvllm/benches/rope_bench.rs @@ -427,7 +427,12 @@ fn bench_apply_rope(c: &mut Criterion) { group.bench_function(id, |b| { b.iter(|| { let mut x_copy = x.clone(); - apply_rope_neon(black_box(&mut x_copy), black_box(&positions), head_dim, base); + apply_rope_neon( + black_box(&mut x_copy), + black_box(&positions), + head_dim, + base, + ); x_copy }) }); @@ -479,10 +484,7 @@ fn bench_precompute_tables(c: &mut Criterion) { for max_seq_len in [512, 1024, 2048, 4096, 8192] { for head_dim in [64, 128] { - let id = BenchmarkId::new( - format!("seq_{}_dim_{}", max_seq_len, head_dim), - max_seq_len, - ); + let id = BenchmarkId::new(format!("seq_{}_dim_{}", max_seq_len, head_dim), max_seq_len); group.throughput(Throughput::Elements((max_seq_len * head_dim) as u64)); group.bench_function(id, |b| { @@ -504,14 +506,22 @@ fn bench_precompute_with_config(c: &mut Criterion) { let configs = [ ("llama2_4k", RopeConfig::llama2(128, 4096)), ("llama3_4k", RopeConfig::llama3(128, 4096)), - ("llama2_8k_ntk", RopeConfig::llama2(128, 8192).with_ntk(4096)), - ("llama2_8k_scaled", RopeConfig::llama2(128, 8192).with_scaling(2.0)), + ( + "llama2_8k_ntk", + RopeConfig::llama2(128, 8192).with_ntk(4096), + ), + ( + "llama2_8k_scaled", + RopeConfig::llama2(128, 8192).with_scaling(2.0), + ), ]; for (name, config) in configs { let id = BenchmarkId::new(name, config.max_seq_len); - group.throughput(Throughput::Elements((config.max_seq_len * config.head_dim) as u64)); + group.throughput(Throughput::Elements( + (config.max_seq_len * config.head_dim) as u64, + )); group.bench_with_input(id, &config, |b, cfg| { b.iter(|| precompute_rope_tables_with_config(black_box(cfg))) }); @@ -544,7 +554,12 @@ fn bench_rope_vs_tables(c: &mut Criterion) { group.bench_function("without_tables", |b| { b.iter(|| { let mut x_copy = x.clone(); - apply_rope_neon(black_box(&mut x_copy), black_box(&positions), head_dim, base); + apply_rope_neon( + black_box(&mut x_copy), + black_box(&positions), + head_dim, + base, + ); x_copy }) }); @@ -580,7 +595,12 @@ fn bench_inverse_rope(c: &mut Criterion) { group.bench_function(id, |b| { b.iter(|| { let mut x_copy = x.clone(); - apply_inverse_rope_neon(black_box(&mut x_copy), black_box(&positions), head_dim, base); + apply_inverse_rope_neon( + black_box(&mut x_copy), + black_box(&positions), + head_dim, + base, + ); x_copy }) }); @@ -607,8 +627,18 @@ fn bench_rope_roundtrip(c: &mut Criterion) { group.bench_function(id, |b| { b.iter(|| { let mut x_copy = x.clone(); - apply_rope_neon(black_box(&mut x_copy), black_box(&positions), head_dim, base); - apply_inverse_rope_neon(black_box(&mut x_copy), black_box(&positions), head_dim, base); + apply_rope_neon( + black_box(&mut x_copy), + black_box(&positions), + head_dim, + base, + ); + apply_inverse_rope_neon( + black_box(&mut x_copy), + black_box(&positions), + head_dim, + base, + ); x_copy }) }); @@ -631,8 +661,14 @@ fn bench_rope_scaling_variants(c: &mut Criterion) { ("standard", RopeConfig::llama2(head_dim, 4096)), ("ntk_2x", RopeConfig::llama2(head_dim, 8192).with_ntk(4096)), ("ntk_4x", RopeConfig::llama2(head_dim, 16384).with_ntk(4096)), - ("linear_2x", RopeConfig::llama2(head_dim, 8192).with_scaling(2.0)), - ("linear_4x", RopeConfig::llama2(head_dim, 16384).with_scaling(4.0)), + ( + "linear_2x", + RopeConfig::llama2(head_dim, 8192).with_scaling(2.0), + ), + ( + "linear_4x", + RopeConfig::llama2(head_dim, 16384).with_scaling(4.0), + ), ]; for (name, config) in configs { diff --git a/crates/ruvllm/benches/serving_bench.rs b/crates/ruvllm/benches/serving_bench.rs index 8ef53ddc1..72dfb84c1 100644 --- a/crates/ruvllm/benches/serving_bench.rs +++ b/crates/ruvllm/benches/serving_bench.rs @@ -48,10 +48,8 @@ fn continuous_batching_process(requests: Vec) -> Vec let mut scheduler = ContinuousBatchScheduler::new(config, kv_config); let mut queue = RequestQueue::new(); let mut latencies = Vec::new(); - let request_times: std::collections::HashMap<_, _> = requests - .iter() - .map(|r| (r.id, Instant::now())) - .collect(); + let request_times: std::collections::HashMap<_, _> = + requests.iter().map(|r| (r.id, Instant::now())).collect(); // Add all requests to queue for request in requests { @@ -114,7 +112,11 @@ fn continuous_batching_process(requests: Vec) -> Vec latencies } -fn create_test_requests(count: usize, prompt_len: usize, max_tokens: usize) -> Vec { +fn create_test_requests( + count: usize, + prompt_len: usize, + max_tokens: usize, +) -> Vec { (0..count) .map(|_| { let prompt_tokens: Vec = (0..prompt_len as u32).collect(); diff --git a/crates/ruvllm/examples/benchmark_model.rs b/crates/ruvllm/examples/benchmark_model.rs index c33f80529..14365df24 100644 --- a/crates/ruvllm/examples/benchmark_model.rs +++ b/crates/ruvllm/examples/benchmark_model.rs @@ -170,7 +170,10 @@ impl BenchmarkResults { results: Vec, ) -> Self { let throughputs: Vec = results.iter().map(|r| r.tokens_per_second()).collect(); - let ttfts: Vec = results.iter().map(|r| r.time_to_first_token.as_secs_f64() * 1000.0).collect(); + let ttfts: Vec = results + .iter() + .map(|r| r.time_to_first_token.as_secs_f64() * 1000.0) + .collect(); // Collect all token latencies let mut all_latencies: Vec = results @@ -190,7 +193,10 @@ impl BenchmarkResults { throughput_median: median(&throughputs), throughput_std: std_dev(&throughputs), throughput_min: throughputs.iter().cloned().fold(f64::INFINITY, f64::min), - throughput_max: throughputs.iter().cloned().fold(f64::NEG_INFINITY, f64::max), + throughput_max: throughputs + .iter() + .cloned() + .fold(f64::NEG_INFINITY, f64::max), ttft_mean: mean(&ttfts), ttft_median: median(&ttfts), @@ -269,7 +275,9 @@ impl BenchmarkResults { self.latency_p50, self.latency_p95, self.latency_p99, - self.peak_memory_bytes.map(|m| m.to_string()).unwrap_or_else(|| "null".to_string()), + self.peak_memory_bytes + .map(|m| m.to_string()) + .unwrap_or_else(|| "null".to_string()), ); println!("{}", json); } @@ -280,7 +288,10 @@ fn main() { // Validate model path if !config.model_path.exists() { - eprintln!("Error: Model file not found: {}", config.model_path.display()); + eprintln!( + "Error: Model file not found: {}", + config.model_path.display() + ); eprintln!(); eprintln!("Download a test model with:"); eprintln!(" cargo run -p ruvllm --example download_test_model -- --model tinyllama"); @@ -430,8 +441,11 @@ fn run_benchmark(config: &BenchmarkConfig, model_size: u64) -> BenchmarkResults } #[cfg(feature = "candle")] -fn run_real_benchmark(config: &BenchmarkConfig, model_size: u64) -> Result { - use ruvllm::{CandleBackend, LlmBackend, GenerateParams, ModelConfig}; +fn run_real_benchmark( + config: &BenchmarkConfig, + model_size: u64, +) -> Result { + use ruvllm::{CandleBackend, GenerateParams, LlmBackend, ModelConfig}; use std::time::Instant; if !config.json_output { @@ -439,10 +453,12 @@ fn run_real_benchmark(config: &BenchmarkConfig, model_size: u64) -> Result Result Result Result Result { if !config.json_output { - println!(" Warmup {}/{}: Error - {}", i + 1, config.warmup_iterations, e); + println!( + " Warmup {}/{}: Error - {}", + i + 1, + config.warmup_iterations, + e + ); } } } @@ -524,7 +555,10 @@ fn run_real_benchmark(config: &BenchmarkConfig, model_size: u64) -> Result Result Result { if !config.json_output { - println!(" Iteration {}/{}: Error - {}", i + 1, config.benchmark_iterations, e); + println!( + " Iteration {}/{}: Error - {}", + i + 1, + config.benchmark_iterations, + e + ); } } } @@ -579,7 +621,11 @@ fn run_real_benchmark(config: &BenchmarkConfig, model_size: u64) -> Result BenchmarkResults { @@ -592,7 +638,10 @@ fn run_simulated_benchmark(config: &BenchmarkConfig, model_size: u64) -> Benchma // Warmup phase if !config.json_output { - println!("Running warmup ({} iterations)...", config.warmup_iterations); + println!( + "Running warmup ({} iterations)...", + config.warmup_iterations + ); } for i in 0..config.warmup_iterations { @@ -610,7 +659,10 @@ fn run_simulated_benchmark(config: &BenchmarkConfig, model_size: u64) -> Benchma // Benchmark phase if !config.json_output { println!(); - println!("Running benchmark ({} iterations)...", config.benchmark_iterations); + println!( + "Running benchmark ({} iterations)...", + config.benchmark_iterations + ); } for i in 0..config.benchmark_iterations { diff --git a/crates/ruvllm/examples/download_test_model.rs b/crates/ruvllm/examples/download_test_model.rs index 25f941f99..0d191df1f 100644 --- a/crates/ruvllm/examples/download_test_model.rs +++ b/crates/ruvllm/examples/download_test_model.rs @@ -36,7 +36,7 @@ //! - `HF_TOKEN`: HuggingFace token for gated models (optional for most models) //! - `RUVLLM_MODELS_DIR`: Default output directory for models -use ruvllm::hub::{RuvLtraRegistry, ModelDownloader, DownloadConfig, default_cache_dir}; +use ruvllm::hub::{default_cache_dir, DownloadConfig, ModelDownloader, RuvLtraRegistry}; use std::env; use std::fs::{self, File}; use std::io::{self, BufWriter, Write}; @@ -204,7 +204,10 @@ fn main() { let size_mb = metadata.len() as f64 / (1024.0 * 1024.0); let expected_mb = model.size_mb as f64; if (size_mb - expected_mb).abs() / expected_mb > 0.1 { - println!("Warning: File size ({:.1} MB) differs from expected ({} MB)", size_mb, model.size_mb); + println!( + "Warning: File size ({:.1} MB) differs from expected ({} MB)", + size_mb, model.size_mb + ); println!("Consider re-downloading with --force"); } else { println!("File size verified: {:.1} MB", size_mb); @@ -222,7 +225,10 @@ fn main() { // Estimate download time let estimated_time = estimate_download_time(model.size_mb); - println!("Estimated download time: {}", format_duration(estimated_time)); + println!( + "Estimated download time: {}", + format_duration(estimated_time) + ); println!(); // Download the model @@ -232,8 +238,10 @@ fn main() { println!("Model saved to: {}", output_path.display()); println!(); println!("To run tests with this model:"); - println!(" TEST_MODEL_PATH={} cargo test -p ruvllm --test real_model_test -- --ignored", - output_path.display()); + println!( + " TEST_MODEL_PATH={} cargo test -p ruvllm --test real_model_test -- --ignored", + output_path.display() + ); } Err(e) => { eprintln!("\nDownload failed: {}", e); @@ -280,9 +288,7 @@ fn list_models() { for model in MODELS { println!( "{:<15} {:>6}MB {}", - model.name, - model.size_mb, - model.description + model.name, model.size_mb, model.description ); } @@ -335,10 +341,11 @@ fn download_with_curl(url: &str, output_path: &Path, _expected_size_mb: usize) - let status = std::process::Command::new("curl") .args([ - "-L", // Follow redirects - "-#", // Progress bar - "--fail", // Fail on HTTP errors - "-o", output_path.to_str().unwrap(), + "-L", // Follow redirects + "-#", // Progress bar + "--fail", // Fail on HTTP errors + "-o", + output_path.to_str().unwrap(), url, ]) .status()?; @@ -358,9 +365,10 @@ fn download_with_wget(url: &str, output_path: &Path) -> io::Result<()> { let status = std::process::Command::new("wget") .args([ - "-q", // Quiet - "--show-progress", // But show progress - "-O", output_path.to_str().unwrap(), + "-q", // Quiet + "--show-progress", // But show progress + "-O", + output_path.to_str().unwrap(), url, ]) .status()?; @@ -383,9 +391,9 @@ fn download_with_rust(url: &str, output_path: &Path, _expected_size_mb: usize) - // This is a basic implementation - production code should use reqwest or similar let url_parts: Vec<&str> = url.split('/').collect(); - let _host = url_parts.get(2).ok_or_else(|| { - io::Error::new(io::ErrorKind::InvalidInput, "Invalid URL") - })?; + let _host = url_parts + .get(2) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Invalid URL"))?; let _path = format!("/{}", url_parts[3..].join("/")); @@ -466,7 +474,10 @@ fn download_ruvltra_model( println!(); println!("Hardware requirements:"); println!(" - Minimum RAM: {:.1} GB", model_info.hardware.min_ram_gb); - println!(" - Recommended RAM: {:.1} GB", model_info.hardware.recommended_ram_gb); + println!( + " - Recommended RAM: {:.1} GB", + model_info.hardware.recommended_ram_gb + ); if model_info.hardware.supports_ane { println!(" - Apple Neural Engine: ✓ Supported"); } @@ -495,7 +506,10 @@ fn list_ruvltra_models() { let registry = RuvLtraRegistry::new(); println!("\nRuvLTRA models (recommended):\n"); - println!("{:<20} {:>8} {:>6} {:<50}", "NAME", "SIZE", "PARAMS", "DESCRIPTION"); + println!( + "{:<20} {:>8} {:>6} {:<50}", + "NAME", "SIZE", "PARAMS", "DESCRIPTION" + ); println!("{}", "-".repeat(90)); for model in registry.list_all() { diff --git a/crates/ruvllm/examples/generate_claude_dataset.rs b/crates/ruvllm/examples/generate_claude_dataset.rs index 0c3dfc059..6b57cd224 100644 --- a/crates/ruvllm/examples/generate_claude_dataset.rs +++ b/crates/ruvllm/examples/generate_claude_dataset.rs @@ -17,8 +17,7 @@ //! - `claude_training_stats.json` - Dataset statistics use ruvllm::training::{ - DatasetGenerator, DatasetConfig, AugmentationConfig, - TaskCategory, ClaudeTaskDataset, + AugmentationConfig, ClaudeTaskDataset, DatasetConfig, DatasetGenerator, TaskCategory, }; use std::error::Error; @@ -39,11 +38,23 @@ fn main() -> Result<(), Box> { }; println!("📋 Configuration:"); - println!(" • Examples per category: {}", config.examples_per_category); + println!( + " • Examples per category: {}", + config.examples_per_category + ); println!(" • Augmentation enabled: {}", config.enable_augmentation); - println!(" • Paraphrases per example: {}", config.augmentation.paraphrases_per_example); - println!(" • Complexity variations: {}", config.augmentation.complexity_variations); - println!(" • Domain transfer: {}\n", config.augmentation.enable_domain_transfer); + println!( + " • Paraphrases per example: {}", + config.augmentation.paraphrases_per_example + ); + println!( + " • Complexity variations: {}", + config.augmentation.complexity_variations + ); + println!( + " • Domain transfer: {}\n", + config.augmentation.enable_domain_transfer + ); // Generate dataset println!("⚙️ Generating dataset..."); @@ -59,7 +70,10 @@ fn main() -> Result<(), Box> { println!("\n💾 Exporting datasets..."); dataset.export_jsonl("claude_training_full.jsonl")?; - println!(" ✓ Full dataset: claude_training_full.jsonl ({} examples)", dataset.examples.len()); + println!( + " ✓ Full dataset: claude_training_full.jsonl ({} examples)", + dataset.examples.len() + ); dataset.export_json("claude_training_full.json")?; println!(" ✓ Full dataset JSON: claude_training_full.json"); @@ -69,15 +83,24 @@ fn main() -> Result<(), Box> { let train_dataset = ClaudeTaskDataset::new(train); train_dataset.export_jsonl("claude_training_train.jsonl")?; - println!(" ✓ Training set: claude_training_train.jsonl ({} examples)", train_dataset.examples.len()); + println!( + " ✓ Training set: claude_training_train.jsonl ({} examples)", + train_dataset.examples.len() + ); let val_dataset = ClaudeTaskDataset::new(val); val_dataset.export_jsonl("claude_training_val.jsonl")?; - println!(" ✓ Validation set: claude_training_val.jsonl ({} examples)", val_dataset.examples.len()); + println!( + " ✓ Validation set: claude_training_val.jsonl ({} examples)", + val_dataset.examples.len() + ); let test_dataset = ClaudeTaskDataset::new(test); test_dataset.export_jsonl("claude_training_test.jsonl")?; - println!(" ✓ Test set: claude_training_test.jsonl ({} examples)", test_dataset.examples.len()); + println!( + " ✓ Test set: claude_training_test.jsonl ({} examples)", + test_dataset.examples.len() + ); // Export statistics dataset.export_stats("claude_training_stats.json")?; @@ -100,15 +123,25 @@ fn print_statistics(dataset: &ClaudeTaskDataset) { println!("📊 Dataset Statistics:"); println!(" ═══════════════════════════════════════════════════"); println!(" Total examples: {}", dataset.stats.total_examples); - println!(" Average quality score: {:.2}", dataset.stats.avg_quality_score); + println!( + " Average quality score: {:.2}", + dataset.stats.avg_quality_score + ); println!("\n 📂 Examples by Category:"); for category in TaskCategory::all() { - let count = dataset.stats.examples_per_category + let count = dataset + .stats + .examples_per_category .get(category.name()) .unwrap_or(&0); let percentage = (*count as f32 / dataset.stats.total_examples as f32) * 100.0; - println!(" • {:12} {:4} ({:5.1}%)", category.name(), count, percentage); + println!( + " • {:12} {:4} ({:5.1}%)", + category.name(), + count, + percentage + ); } println!("\n 📈 Examples by Complexity:"); @@ -129,13 +162,21 @@ fn print_sample_examples(dataset: &ClaudeTaskDataset) { println!(" ═══════════════════════════════════════════════════"); for category in TaskCategory::all() { - let sample = dataset.examples.iter() + let sample = dataset + .examples + .iter() .find(|e| e.metadata.category == category); if let Some(example) = sample { - println!("\n 🔹 {} ({})", category.name(), example.metadata.expected_model); - println!(" Complexity: {:?}, Domain: {:?}", - example.metadata.complexity, example.metadata.domain); + println!( + "\n 🔹 {} ({})", + category.name(), + example.metadata.expected_model + ); + println!( + " Complexity: {:?}, Domain: {:?}", + example.metadata.complexity, example.metadata.domain + ); println!(" Input: {}", truncate(&example.input, 80)); println!(" Context: {}", truncate(&example.context, 80)); println!(" Quality: {:.2}", example.metadata.quality_score); @@ -149,7 +190,9 @@ fn print_model_routing_analysis(dataset: &ClaudeTaskDataset) { let mut model_counts = std::collections::HashMap::new(); for example in &dataset.examples { - *model_counts.entry(&example.metadata.expected_model).or_insert(0) += 1; + *model_counts + .entry(&example.metadata.expected_model) + .or_insert(0) += 1; } for (model, count) in model_counts.iter() { @@ -160,7 +203,10 @@ fn print_model_routing_analysis(dataset: &ClaudeTaskDataset) { "opus" => "💰💰💰 (most capable)", _ => "", }; - println!(" • {:8} {:4} ({:5.1}%) {}", model, count, percentage, cost_indicator); + println!( + " • {:8} {:4} ({:5.1}%) {}", + model, count, percentage, cost_indicator + ); } println!("\n ℹ️ Model Selection Guide:"); diff --git a/crates/ruvllm/examples/hub_cli.rs b/crates/ruvllm/examples/hub_cli.rs index 37f74bf79..c10543886 100644 --- a/crates/ruvllm/examples/hub_cli.rs +++ b/crates/ruvllm/examples/hub_cli.rs @@ -27,8 +27,8 @@ //! - `RUVLLM_MODELS_DIR`: Default cache directory for downloaded models use ruvllm::hub::{ - RuvLtraRegistry, ModelDownloader, ModelUploader, DownloadConfig, UploadConfig, - ModelMetadata, default_cache_dir, get_hf_token, + default_cache_dir, get_hf_token, DownloadConfig, ModelDownloader, ModelMetadata, ModelUploader, + RuvLtraRegistry, UploadConfig, }; use std::env; use std::path::PathBuf; @@ -98,7 +98,10 @@ fn cmd_pull(args: &[String]) { println!("📥 Pulling model: {}", model_info.name); println!(" Repository: {}", model_info.repo); - println!(" Size: {:.1} GB", model_info.size_bytes as f64 / (1024.0 * 1024.0 * 1024.0)); + println!( + " Size: {:.1} GB", + model_info.size_bytes as f64 / (1024.0 * 1024.0 * 1024.0) + ); println!(" Quantization: {:?}", model_info.quantization); println!(); @@ -125,7 +128,10 @@ fn cmd_pull(args: &[String]) { println!(" Saved to: {}", path.display()); println!(); println!(" Minimum RAM: {:.1} GB", model_info.hardware.min_ram_gb); - println!(" Recommended RAM: {:.1} GB", model_info.hardware.recommended_ram_gb); + println!( + " Recommended RAM: {:.1} GB", + model_info.hardware.recommended_ram_gb + ); if model_info.hardware.supports_ane { println!(" Apple Neural Engine: ✓"); @@ -241,7 +247,10 @@ fn cmd_push(args: &[String]) { println!("📤 Pushing model to HuggingFace Hub"); println!(" Local path: {}", model_path.display()); println!(" Repository: {}", repo_id); - println!(" Visibility: {}", if private { "Private" } else { "Public" }); + println!( + " Visibility: {}", + if private { "Private" } else { "Public" } + ); println!(); // Create metadata @@ -284,8 +293,10 @@ fn cmd_list(_args: &[String]) { // Base models println!("Base Models:"); - println!("{:<20} {:>8} {:>6} {:>8} {:<40}", - "ID", "SIZE", "PARAMS", "QUANT", "DESCRIPTION"); + println!( + "{:<20} {:>8} {:>6} {:>8} {:<40}", + "ID", "SIZE", "PARAMS", "QUANT", "DESCRIPTION" + ); println!("{}", "=".repeat(90)); for model in registry.list_base_models() { @@ -300,7 +311,8 @@ fn cmd_list(_args: &[String]) { } // Adapters - let adapters = registry.list_all() + let adapters = registry + .list_all() .into_iter() .filter(|m| m.is_adapter) .collect::>(); @@ -356,18 +368,51 @@ fn cmd_info(args: &[String]) { println!(" Architecture: {}", model.id); println!(" Quantization: {:?}", model.quantization); println!(" Context: {} tokens", model.context_length); - println!(" File Size: {:.2} GB", model.size_bytes as f64 / (1024.0 * 1024.0 * 1024.0)); + println!( + " File Size: {:.2} GB", + model.size_bytes as f64 / (1024.0 * 1024.0 * 1024.0) + ); println!(); println!("Hardware Requirements:"); println!(" Min RAM: {:.1} GB", model.hardware.min_ram_gb); - println!(" Rec RAM: {:.1} GB", model.hardware.recommended_ram_gb); - println!(" ANE Support: {}", if model.hardware.supports_ane { "✓" } else { "✗" }); - println!(" Metal GPU: {}", if model.hardware.supports_metal { "✓" } else { "✗" }); - println!(" CUDA: {}", if model.hardware.supports_cuda { "✓" } else { "✗" }); + println!( + " Rec RAM: {:.1} GB", + model.hardware.recommended_ram_gb + ); + println!( + " ANE Support: {}", + if model.hardware.supports_ane { + "✓" + } else { + "✗" + } + ); + println!( + " Metal GPU: {}", + if model.hardware.supports_metal { + "✓" + } else { + "✗" + } + ); + println!( + " CUDA: {}", + if model.hardware.supports_cuda { + "✓" + } else { + "✗" + } + ); println!(); println!("Features:"); - println!(" SONA Weights: {}", if model.has_sona_weights { "✓" } else { "✗" }); - println!(" LoRA Adapter: {}", if model.is_adapter { "✓" } else { "✗" }); + println!( + " SONA Weights: {}", + if model.has_sona_weights { "✓" } else { "✗" } + ); + println!( + " LoRA Adapter: {}", + if model.is_adapter { "✓" } else { "✗" } + ); if let Some(base) = &model.base_model { println!(" Base Model: {}", base); @@ -379,7 +424,10 @@ fn cmd_info(args: &[String]) { println!(); println!("Download with:"); - println!(" cargo run -p ruvllm --example hub_cli -- pull {}", model_id); + println!( + " cargo run -p ruvllm --example hub_cli -- pull {}", + model_id + ); // Estimate download time let time_10mbps = model.estimate_download_time(10.0); diff --git a/crates/ruvllm/examples/run_eval.rs b/crates/ruvllm/examples/run_eval.rs index e94bb47dd..a5c5edaef 100644 --- a/crates/ruvllm/examples/run_eval.rs +++ b/crates/ruvllm/examples/run_eval.rs @@ -36,8 +36,8 @@ use ruvllm::backends::ModelConfig; use ruvllm::evaluation::{ - AblationMode, EvalConfig, EvalTask, RealEvaluationHarness, RealInferenceConfig, swe_bench::{SweBenchConfig, SweBenchLoader}, + AblationMode, EvalConfig, EvalTask, RealEvaluationHarness, RealInferenceConfig, }; use std::env; use std::path::PathBuf; @@ -321,9 +321,26 @@ fn run_evaluation(config: CliConfig) -> Result<(), Box> { .map(|m| m.name()) .collect::>() ); - println!(" Quality threshold: {:.0}%", eval_config.quality_threshold * 100.0); - println!(" SONA: {}", if config.enable_sona { "enabled" } else { "disabled" }); - println!(" HNSW: {}", if config.enable_hnsw { "enabled" } else { "disabled" }); + println!( + " Quality threshold: {:.0}%", + eval_config.quality_threshold * 100.0 + ); + println!( + " SONA: {}", + if config.enable_sona { + "enabled" + } else { + "disabled" + } + ); + println!( + " HNSW: {}", + if config.enable_hnsw { + "enabled" + } else { + "disabled" + } + ); // Configure inference let inference_config = RealInferenceConfig { diff --git a/crates/ruvllm/examples/train_contrastive.rs b/crates/ruvllm/examples/train_contrastive.rs index 0d17a0d25..af8965162 100644 --- a/crates/ruvllm/examples/train_contrastive.rs +++ b/crates/ruvllm/examples/train_contrastive.rs @@ -15,16 +15,22 @@ use std::path::PathBuf; use std::time::Instant; fn main() -> Result<(), Box> { - println!("╔═══════════════════════════════════════════════════════════════════════════════════╗"); - println!("║ RuvLTRA Contrastive Fine-Tuning for SOTA Agent Routing ║"); - println!("╚═══════════════════════════════════════════════════════════════════════════════════╝\n"); + println!( + "╔═══════════════════════════════════════════════════════════════════════════════════╗" + ); + println!( + "║ RuvLTRA Contrastive Fine-Tuning for SOTA Agent Routing ║" + ); + println!( + "╚═══════════════════════════════════════════════════════════════════════════════════╝\n" + ); // Parse command line arguments let args: Vec = std::env::args().collect(); - let mut triplets_path = PathBuf::from( - std::env::var("HOME").unwrap_or_else(|_| ".".to_string()) - ).join(".ruvllm/training/ruvltra-finetuned/triplets.jsonl"); + let mut triplets_path = + PathBuf::from(std::env::var("HOME").unwrap_or_else(|_| ".".to_string())) + .join(".ruvllm/training/ruvltra-finetuned/triplets.jsonl"); let mut epochs = 20usize; let mut output_path = PathBuf::from("ruvltra-claude-code-sota.gguf"); @@ -91,7 +97,10 @@ fn main() -> Result<(), Box> { // Check if triplets file exists if !triplets_path.exists() { - println!("⚠️ Triplets file not found at: {}", triplets_path.display()); + println!( + "⚠️ Triplets file not found at: {}", + triplets_path.display() + ); println!(); println!("To generate training data, run:"); println!(" node npm/packages/ruvllm/scripts/training/contrastive-finetune.js"); @@ -122,8 +131,15 @@ fn main() -> Result<(), Box> { println!("Loading training triplets..."); let start = Instant::now(); let triplet_count = trainer.load_triplets(&triplets_path)?; - println!(" Loaded {} triplets in {:?}", triplet_count, start.elapsed()); - println!(" Hard negative ratio: {:.1}%", trainer.hard_negative_ratio() * 100.0); + println!( + " Loaded {} triplets in {:?}", + triplet_count, + start.elapsed() + ); + println!( + " Hard negative ratio: {:.1}%", + trainer.hard_negative_ratio() * 100.0 + ); println!(); // Train model @@ -143,8 +159,15 @@ fn main() -> Result<(), Box> { println!("Results:"); println!(" Epochs Completed: {}", result.epochs_completed); println!(" Final Loss: {:.4}", result.final_loss); - println!(" Final Accuracy: {:.2}%", result.final_accuracy * 100.0); - println!(" Best Accuracy: {:.2}% (epoch {})", result.best_accuracy * 100.0, result.best_epoch); + println!( + " Final Accuracy: {:.2}%", + result.final_accuracy * 100.0 + ); + println!( + " Best Accuracy: {:.2}% (epoch {})", + result.best_accuracy * 100.0, + result.best_epoch + ); println!(" Training Time: {:?}", training_time); println!(" Output Model: {}", result.output_path.display()); println!(); @@ -158,14 +181,22 @@ fn main() -> Result<(), Box> { println!(); println!("═══════════════════════════════════════════════════════════════════════════════════"); println!(" SOTA ACHIEVEMENT"); - println!("═══════════════════════════════════════════════════════════════════════════════════\n"); + println!( + "═══════════════════════════════════════════════════════════════════════════════════\n" + ); println!("┌───────────────────────────────┬────────────┬────────────┐"); println!("│ Metric │ Before │ After │"); println!("├───────────────────────────────┼────────────┼────────────┤"); - println!("│ Embedding-only Accuracy │ 45.0% │ {:.1}% │", result.final_accuracy * 100.0); + println!( + "│ Embedding-only Accuracy │ 45.0% │ {:.1}% │", + result.final_accuracy * 100.0 + ); println!("│ Hybrid Routing Accuracy │ 100.0% │ 100.0% │"); - println!("│ Hard Negative Accuracy │ N/A │ {:.1}% │", result.best_accuracy * 90.0); + println!( + "│ Hard Negative Accuracy │ N/A │ {:.1}% │", + result.best_accuracy * 90.0 + ); println!("│ Agent Types Supported │ 13 │ 13 │"); println!("└───────────────────────────────┴────────────┴────────────┘"); println!(); @@ -176,9 +207,11 @@ fn main() -> Result<(), Box> { println!(); println!("Next steps:"); - println!(" 1. Convert to GGUF: llama-quantize {} {}", - output_path.with_extension("bin").display(), - output_path.display()); + println!( + " 1. Convert to GGUF: llama-quantize {} {}", + output_path.with_extension("bin").display(), + output_path.display() + ); println!(" 2. Benchmark: node scripts/hybrid-model-compare.js"); println!(" 3. Publish: ./scripts/huggingface/publish.sh"); println!(); @@ -238,9 +271,12 @@ impl ContrastiveTrainer { }) } - fn load_triplets(&mut self, path: &std::path::Path) -> Result> { - use std::io::{BufRead, BufReader}; + fn load_triplets( + &mut self, + path: &std::path::Path, + ) -> Result> { use std::fs::File; + use std::io::{BufRead, BufReader}; let file = File::open(path)?; let reader = BufReader::new(file); @@ -311,9 +347,13 @@ impl ContrastiveTrainer { }) } - fn export_stats(&self, result: &TrainingResult, path: &std::path::Path) -> Result<(), Box> { - use std::io::Write; + fn export_stats( + &self, + result: &TrainingResult, + path: &std::path::Path, + ) -> Result<(), Box> { use std::fs::File; + use std::io::Write; let stats = serde_json::json!({ "epochs_completed": result.epochs_completed, @@ -338,8 +378,8 @@ impl ContrastiveTrainer { /// Generate synthetic triplets for demonstration fn generate_synthetic_triplets(path: &std::path::Path) -> Result<(), Box> { - use std::io::Write; use std::fs::{self, File}; + use std::io::Write; // Create parent directories if let Some(parent) = path.parent() { diff --git a/crates/ruvllm/src/adapter_manager.rs b/crates/ruvllm/src/adapter_manager.rs index 3da05bd28..8290f7888 100644 --- a/crates/ruvllm/src/adapter_manager.rs +++ b/crates/ruvllm/src/adapter_manager.rs @@ -175,12 +175,15 @@ impl LoraAdapter { /// Increment reference count pub fn inc_ref(&self) { - self.ref_count.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + self.ref_count + .fetch_add(1, std::sync::atomic::Ordering::SeqCst); } /// Decrement reference count, returns true if count reached zero pub fn dec_ref(&self) -> bool { - self.ref_count.fetch_sub(1, std::sync::atomic::Ordering::SeqCst) == 1 + self.ref_count + .fetch_sub(1, std::sync::atomic::Ordering::SeqCst) + == 1 } /// Get current reference count @@ -257,14 +260,17 @@ impl AdapterManager { last_accessed: chrono::Utc::now(), }); - self.current_memory.fetch_add(memory_needed, std::sync::atomic::Ordering::SeqCst); + self.current_memory + .fetch_add(memory_needed, std::sync::atomic::Ordering::SeqCst); Ok(id) } /// Ensure there's enough memory for a new adapter fn ensure_memory(&self, needed: usize) -> Result<()> { - let current = self.current_memory.load(std::sync::atomic::Ordering::SeqCst); + let current = self + .current_memory + .load(std::sync::atomic::Ordering::SeqCst); if current + needed <= self.max_memory_bytes { return Ok(()); @@ -289,7 +295,8 @@ impl AdapterManager { cache.remove(0); freed += size; - self.current_memory.fetch_sub(size, std::sync::atomic::Ordering::SeqCst); + self.current_memory + .fetch_sub(size, std::sync::atomic::Ordering::SeqCst); } else { // Adapter is in use, move to end let entry = cache.remove(0); @@ -300,7 +307,7 @@ impl AdapterManager { if freed < needed { return Err(RuvLLMError::OutOfMemory( - "Cannot free enough memory for new adapter".to_string() + "Cannot free enough memory for new adapter".to_string(), )); } @@ -334,34 +341,37 @@ impl AdapterManager { let mut cache = self.cache.write(); cache.retain(|e| e.adapter.id != *id); - self.current_memory.fetch_sub( - adapter.memory_bytes(), - std::sync::atomic::Ordering::SeqCst - ); + self.current_memory + .fetch_sub(adapter.memory_bytes(), std::sync::atomic::Ordering::SeqCst); } Ok(()) } /// List all loaded adapters pub fn list(&self) -> Vec { - self.adapters.iter().map(|entry| { - let adapter = entry.value(); - AdapterInfo { - id: adapter.id, - name: adapter.config.name.clone(), - rank: adapter.config.rank, - version: adapter.version, - memory_bytes: adapter.memory_bytes(), - ref_count: adapter.ref_count(), - } - }).collect() + self.adapters + .iter() + .map(|entry| { + let adapter = entry.value(); + AdapterInfo { + id: adapter.id, + name: adapter.config.name.clone(), + rank: adapter.config.rank, + version: adapter.version, + memory_bytes: adapter.memory_bytes(), + ref_count: adapter.ref_count(), + } + }) + .collect() } /// Get memory statistics pub fn memory_stats(&self) -> AdapterMemoryStats { AdapterMemoryStats { total_budget: self.max_memory_bytes, - used_bytes: self.current_memory.load(std::sync::atomic::Ordering::SeqCst), + used_bytes: self + .current_memory + .load(std::sync::atomic::Ordering::SeqCst), adapter_count: self.adapters.len(), max_adapters: self.max_loaded, } diff --git a/crates/ruvllm/src/autodetect.rs b/crates/ruvllm/src/autodetect.rs index 2cdf84b0c..1d0a3390d 100644 --- a/crates/ruvllm/src/autodetect.rs +++ b/crates/ruvllm/src/autodetect.rs @@ -43,9 +43,9 @@ use serde::{Deserialize, Serialize}; -use crate::backends::{DeviceType, DType, Quantization}; #[cfg(feature = "coreml")] use crate::backends::{AneCapabilities, ComputeUnits}; +use crate::backends::{DType, DeviceType, Quantization}; use crate::kernels::AttentionConfig; // ============================================================================= @@ -133,7 +133,10 @@ impl Platform { /// Check if this platform supports GPU acceleration pub fn supports_gpu(&self) -> bool { - matches!(self, Self::MacOS | Self::Linux | Self::Windows | Self::IOS | Self::Wasm) + matches!( + self, + Self::MacOS | Self::Linux | Self::Windows | Self::IOS | Self::Wasm + ) } /// Get the default GPU backend for this platform @@ -184,7 +187,11 @@ impl Architecture { Self::Wasm32 } - #[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64", target_arch = "wasm32")))] + #[cfg(not(any( + target_arch = "aarch64", + target_arch = "x86_64", + target_arch = "wasm32" + )))] { Self::Unknown } @@ -612,10 +619,7 @@ impl CoreInfo { .output() .ok()?; - String::from_utf8_lossy(&output.stdout) - .trim() - .parse() - .ok() + String::from_utf8_lossy(&output.stdout).trim().parse().ok() } /// Get physical cores from /proc/cpuinfo on Linux @@ -633,9 +637,15 @@ impl CoreInfo { for line in cpuinfo.lines() { if line.starts_with("physical id") { - physical_id = line.split(':').nth(1).and_then(|s| s.trim().parse::().ok()); + physical_id = line + .split(':') + .nth(1) + .and_then(|s| s.trim().parse::().ok()); } else if line.starts_with("core id") { - core_id = line.split(':').nth(1).and_then(|s| s.trim().parse::().ok()); + core_id = line + .split(':') + .nth(1) + .and_then(|s| s.trim().parse::().ok()); } if let (Some(pid), Some(cid)) = (physical_id, core_id) { @@ -647,7 +657,12 @@ impl CoreInfo { if cores.is_empty() { // Fallback: count "processor" lines - Some(cpuinfo.lines().filter(|l| l.starts_with("processor")).count()) + Some( + cpuinfo + .lines() + .filter(|l| l.starts_with("processor")) + .count(), + ) } else { Some(cores.len()) } @@ -1268,7 +1283,11 @@ impl SystemCapabilities { )); if let Some(perf) = self.cores.performance_cores { - parts.push(format!("{}P+{}E cores", perf, self.cores.efficiency_cores.unwrap_or(0))); + parts.push(format!( + "{}P+{}E cores", + perf, + self.cores.efficiency_cores.unwrap_or(0) + )); } parts.push(format!("{}GB RAM", self.memory_mb / 1024)); @@ -1351,7 +1370,10 @@ pub enum ComputeBackend { impl ComputeBackend { /// Check if this is a GPU/accelerator backend pub fn is_gpu(&self) -> bool { - matches!(self, Self::Metal | Self::CoreML | Self::HybridAne | Self::Cuda | Self::WebGPU) + matches!( + self, + Self::Metal | Self::CoreML | Self::HybridAne | Self::Cuda | Self::WebGPU + ) } /// Check if this backend uses the Neural Engine @@ -1363,15 +1385,15 @@ impl ComputeBackend { /// Note: ANE performance depends heavily on model size and batch configuration pub fn relative_performance(&self) -> f32 { match self { - Self::HybridAne => 12.0, // Best for models that benefit from ANE+GPU - Self::Metal => 10.0, // Apple Silicon GPU is very efficient - Self::CoreML => 8.0, // ANE alone (great for small models, limited for large) - Self::Cuda => 15.0, // NVIDIA is fastest for large models - Self::WebGPU => 5.0, // WebGPU has overhead - Self::CpuAvx512 => 4.0, // AVX-512 is fast - Self::CpuAvx2 => 2.5, // AVX2 is good - Self::CpuNeon => 2.0, // NEON is comparable to AVX2 - Self::CpuScalar => 1.0, // Baseline + Self::HybridAne => 12.0, // Best for models that benefit from ANE+GPU + Self::Metal => 10.0, // Apple Silicon GPU is very efficient + Self::CoreML => 8.0, // ANE alone (great for small models, limited for large) + Self::Cuda => 15.0, // NVIDIA is fastest for large models + Self::WebGPU => 5.0, // WebGPU has overhead + Self::CpuAvx512 => 4.0, // AVX-512 is fast + Self::CpuAvx2 => 2.5, // AVX2 is good + Self::CpuNeon => 2.0, // NEON is comparable to AVX2 + Self::CpuScalar => 1.0, // Baseline } } @@ -1379,14 +1401,14 @@ impl ComputeBackend { /// ANE is significantly more power efficient than GPU pub fn power_efficiency(&self) -> f32 { match self { - Self::CoreML => 4.0, // ANE is 3-4x more power efficient than GPU - Self::HybridAne => 3.0, // Hybrid gets some efficiency benefits - Self::Metal => 2.0, // Apple Silicon GPU is efficient - Self::Cuda => 1.0, // NVIDIA uses more power - Self::WebGPU => 1.5, // Varies + Self::CoreML => 4.0, // ANE is 3-4x more power efficient than GPU + Self::HybridAne => 3.0, // Hybrid gets some efficiency benefits + Self::Metal => 2.0, // Apple Silicon GPU is efficient + Self::Cuda => 1.0, // NVIDIA uses more power + Self::WebGPU => 1.5, // Varies Self::CpuAvx512 => 1.2, Self::CpuAvx2 => 1.3, - Self::CpuNeon => 1.5, // ARM is power efficient + Self::CpuNeon => 1.5, // ARM is power efficient Self::CpuScalar => 1.0, } } @@ -1472,9 +1494,9 @@ impl InferenceConfig { /// Get estimated tokens per second for this configuration pub fn estimated_tokens_per_second(&self) -> f32 { let base = match self.compute_backend { - ComputeBackend::HybridAne => 90.0, // Hybrid can exceed pure Metal for suitable models + ComputeBackend::HybridAne => 90.0, // Hybrid can exceed pure Metal for suitable models ComputeBackend::Metal => 80.0, - ComputeBackend::CoreML => 60.0, // ANE alone (great for small models) + ComputeBackend::CoreML => 60.0, // ANE alone (great for small models) ComputeBackend::Cuda => 100.0, ComputeBackend::WebGPU => 40.0, ComputeBackend::CpuAvx512 => 30.0, @@ -1485,11 +1507,11 @@ impl InferenceConfig { // Adjust for quantization let quant_factor = match self.quantization { - Quantization::Q4 | Quantization::Q4K => 2.0, // 4-bit is fastest + Quantization::Q4 | Quantization::Q4K => 2.0, // 4-bit is fastest Quantization::Q8 => 1.5, Quantization::F16 | Quantization::Bf16 => 1.0, Quantization::None => 0.5, - Quantization::Q2K => 2.5, // Most aggressive quantization + Quantization::Q2K => 2.5, // Most aggressive quantization }; // Adjust for batch size (throughput scales sublinearly) @@ -1674,7 +1696,10 @@ mod tests { assert!(caps.can_run_model(0.1), "Should be able to run 100MB model"); // Likely can't run a 1TB model - assert!(!caps.can_run_model(1000.0), "Should not be able to run 1TB model"); + assert!( + !caps.can_run_model(1000.0), + "Should not be able to run 1TB model" + ); } #[test] @@ -1695,7 +1720,10 @@ mod tests { assert!(!ComputeBackend::CpuScalar.is_gpu()); // GPU should have higher relative performance - assert!(ComputeBackend::Metal.relative_performance() > ComputeBackend::CpuNeon.relative_performance()); + assert!( + ComputeBackend::Metal.relative_performance() + > ComputeBackend::CpuNeon.relative_performance() + ); } #[test] @@ -1761,13 +1789,22 @@ mod tests { { assert!(ane.available, "ANE should be available on Apple Silicon"); assert!(ane.tops > 0.0, "ANE TOPS should be positive"); - assert!(ane.max_model_size_mb > 0, "ANE max model size should be positive"); - assert!(!ane.supported_ops.is_empty(), "ANE should have supported ops"); + assert!( + ane.max_model_size_mb > 0, + "ANE max model size should be positive" + ); + assert!( + !ane.supported_ops.is_empty(), + "ANE should have supported ops" + ); } #[cfg(not(all(target_os = "macos", target_arch = "aarch64")))] { - assert!(!ane.available, "ANE should not be available on non-Apple Silicon"); + assert!( + !ane.available, + "ANE should not be available on non-Apple Silicon" + ); } } diff --git a/crates/ruvllm/src/backends/candle_backend.rs b/crates/ruvllm/src/backends/candle_backend.rs index c35a19829..d52caef4c 100644 --- a/crates/ruvllm/src/backends/candle_backend.rs +++ b/crates/ruvllm/src/backends/candle_backend.rs @@ -45,8 +45,8 @@ //! ``` use super::{ - DeviceType, DType, GenerateParams, GeneratedToken, LlmBackend, ModelArchitecture, - ModelConfig, ModelInfo, Quantization, SpecialTokens, StreamEvent, TokenStream, Tokenizer, + DType, DeviceType, GenerateParams, GeneratedToken, LlmBackend, ModelArchitecture, ModelConfig, + ModelInfo, Quantization, SpecialTokens, StreamEvent, TokenStream, Tokenizer, }; use crate::error::{Result, RuvLLMError}; use crate::sona::{SonaConfig, SonaIntegration, Trajectory}; @@ -108,9 +108,7 @@ mod candle_impl { use super::*; use candle_core::quantized::gguf_file; use candle_transformers::models::{ - llama as llama_model, - mistral as mistral_model, - quantized_llama as qlama, + llama as llama_model, mistral as mistral_model, quantized_llama as qlama, }; use std::sync::Mutex; @@ -144,16 +142,17 @@ mod candle_impl { impl Tokenizer for CandleTokenizer { fn encode(&self, text: &str) -> Result> { - let encoding = self.inner.encode(text, false).map_err(|e| { - RuvLLMError::Tokenization(format!("Tokenization failed: {}", e)) - })?; + let encoding = self + .inner + .encode(text, false) + .map_err(|e| RuvLLMError::Tokenization(format!("Tokenization failed: {}", e)))?; Ok(encoding.get_ids().to_vec()) } fn decode(&self, tokens: &[u32]) -> Result { - self.inner.decode(tokens, true).map_err(|e| { - RuvLLMError::Tokenization(format!("Decoding failed: {}", e)) - }) + self.inner + .decode(tokens, true) + .map_err(|e| RuvLLMError::Tokenization(format!("Decoding failed: {}", e))) } fn vocab_size(&self) -> usize { @@ -285,9 +284,10 @@ mod candle_impl { /// Uses the model's detected chat template format to properly /// format multi-turn conversations for instruction-tuned models. pub fn apply_chat_template(&self, messages: &[ChatMessage]) -> Result { - let tokenizer = self.ruv_tokenizer.as_ref().ok_or_else(|| { - RuvLLMError::InvalidOperation("No tokenizer loaded".to_string()) - })?; + let tokenizer = self + .ruv_tokenizer + .as_ref() + .ok_or_else(|| RuvLLMError::InvalidOperation("No tokenizer loaded".to_string()))?; tokenizer.apply_chat_template(messages) } @@ -306,18 +306,20 @@ mod candle_impl { /// Decode a single token for streaming output pub fn decode_stream(&mut self, token: u32) -> Result> { - let tokenizer = self.ruv_tokenizer.as_mut().ok_or_else(|| { - RuvLLMError::InvalidOperation("No tokenizer loaded".to_string()) - })?; + let tokenizer = self + .ruv_tokenizer + .as_mut() + .ok_or_else(|| RuvLLMError::InvalidOperation("No tokenizer loaded".to_string()))?; tokenizer.decode_stream(token) } /// Flush any remaining bytes in the streaming buffer pub fn flush_stream(&mut self) -> Result> { - let tokenizer = self.ruv_tokenizer.as_mut().ok_or_else(|| { - RuvLLMError::InvalidOperation("No tokenizer loaded".to_string()) - })?; + let tokenizer = self + .ruv_tokenizer + .as_mut() + .ok_or_else(|| RuvLLMError::InvalidOperation("No tokenizer loaded".to_string()))?; tokenizer.flush_stream() } @@ -351,7 +353,10 @@ mod candle_impl { #[cfg(target_os = "macos")] { Device::new_metal(0).map_err(|e| { - RuvLLMError::Backend(format!("Failed to initialize Metal device: {}", e)) + RuvLLMError::Backend(format!( + "Failed to initialize Metal device: {}", + e + )) }) } #[cfg(not(target_os = "macos"))] @@ -426,16 +431,12 @@ mod candle_impl { "model.Q4_K_M.gguf", "ggml-model-q4_k_m.gguf", ], - Some(Quantization::Q4) => vec![ - "model-q4_0.gguf", - "model.Q4_0.gguf", - "ggml-model-q4_0.gguf", - ], - Some(Quantization::Q8) => vec![ - "model-q8_0.gguf", - "model.Q8_0.gguf", - "ggml-model-q8_0.gguf", - ], + Some(Quantization::Q4) => { + vec!["model-q4_0.gguf", "model.Q4_0.gguf", "ggml-model-q4_0.gguf"] + } + Some(Quantization::Q8) => { + vec!["model-q8_0.gguf", "model.Q8_0.gguf", "ggml-model-q8_0.gguf"] + } _ => vec![], }; @@ -472,7 +473,8 @@ mod candle_impl { let index: serde_json::Value = serde_json::from_str(&index_str)?; if let Some(weight_map) = index.get("weight_map").and_then(|w| w.as_object()) { - let mut shard_files: std::collections::HashSet = std::collections::HashSet::new(); + let mut shard_files: std::collections::HashSet = + std::collections::HashSet::new(); for filename in weight_map.values() { if let Some(f) = filename.as_str() { shard_files.insert(f.to_string()); @@ -494,7 +496,7 @@ mod candle_impl { } Err(RuvLLMError::NotFound( - "No safetensors files found. Try using a quantized GGUF model.".to_string() + "No safetensors files found. Try using a quantized GGUF model.".to_string(), )) } @@ -502,27 +504,31 @@ mod candle_impl { pub fn load_tokenizer(&mut self, path: &Path) -> Result<()> { tracing::info!("Loading tokenizer from: {:?}", path); - let tokenizer = HfTokenizer::from_file(path).map_err(|e| { - RuvLLMError::Storage(format!("Failed to load tokenizer: {}", e)) - })?; + let tokenizer = HfTokenizer::from_file(path) + .map_err(|e| RuvLLMError::Storage(format!("Failed to load tokenizer: {}", e)))?; // Detect special tokens let special_tokens = SpecialTokens { - bos_token_id: tokenizer.token_to_id("") + bos_token_id: tokenizer + .token_to_id("") .or_else(|| tokenizer.token_to_id("<|begin_of_text|>")) .or_else(|| tokenizer.token_to_id("<|startoftext|>")), - eos_token_id: tokenizer.token_to_id("") + eos_token_id: tokenizer + .token_to_id("") .or_else(|| tokenizer.token_to_id("<|end_of_text|>")) .or_else(|| tokenizer.token_to_id("<|endoftext|>")) .or_else(|| tokenizer.token_to_id("<|eot_id|>")), - pad_token_id: tokenizer.token_to_id("") + pad_token_id: tokenizer + .token_to_id("") .or_else(|| tokenizer.token_to_id("<|pad|>")) .or_else(|| tokenizer.token_to_id("[PAD]")), - unk_token_id: tokenizer.token_to_id("") + unk_token_id: tokenizer + .token_to_id("") .or_else(|| tokenizer.token_to_id("[UNK]")), }; - tracing::debug!("Special tokens: bos={:?}, eos={:?}", + tracing::debug!( + "Special tokens: bos={:?}, eos={:?}", special_tokens.bos_token_id, special_tokens.eos_token_id ); @@ -539,68 +545,107 @@ mod candle_impl { pub fn load_gguf(&mut self, path: &Path, config: &ModelConfig) -> Result<()> { tracing::info!("Loading GGUF model from: {:?}", path); - let mut file = std::fs::File::open(path).map_err(|e| { - RuvLLMError::Storage(format!("Failed to open GGUF file: {}", e)) - })?; + let mut file = std::fs::File::open(path) + .map_err(|e| RuvLLMError::Storage(format!("Failed to open GGUF file: {}", e)))?; // Read GGUF content - let gguf_content = gguf_file::Content::read(&mut file).map_err(|e| { - RuvLLMError::Storage(format!("Failed to read GGUF file: {}", e)) - })?; + let gguf_content = gguf_file::Content::read(&mut file) + .map_err(|e| RuvLLMError::Storage(format!("Failed to read GGUF file: {}", e)))?; // Extract config from GGUF metadata - let hidden_size = self.get_gguf_u32(&gguf_content, &[ - "llama.embedding_length", - "mistral.embedding_length", - "phi.embedding_length", - ]).unwrap_or(4096) as usize; + let hidden_size = self + .get_gguf_u32( + &gguf_content, + &[ + "llama.embedding_length", + "mistral.embedding_length", + "phi.embedding_length", + ], + ) + .unwrap_or(4096) as usize; - let num_layers = self.get_gguf_u32(&gguf_content, &[ - "llama.block_count", - "mistral.block_count", - "phi.block_count", - ]).unwrap_or(32) as usize; + let num_layers = self + .get_gguf_u32( + &gguf_content, + &[ + "llama.block_count", + "mistral.block_count", + "phi.block_count", + ], + ) + .unwrap_or(32) as usize; - let num_heads = self.get_gguf_u32(&gguf_content, &[ - "llama.attention.head_count", - "mistral.attention.head_count", - "phi.attention.head_count", - ]).unwrap_or(32) as usize; + let num_heads = self + .get_gguf_u32( + &gguf_content, + &[ + "llama.attention.head_count", + "mistral.attention.head_count", + "phi.attention.head_count", + ], + ) + .unwrap_or(32) as usize; - let num_kv_heads = self.get_gguf_u32(&gguf_content, &[ - "llama.attention.head_count_kv", - "mistral.attention.head_count_kv", - "phi.attention.head_count_kv", - ]).unwrap_or(num_heads as u32) as usize; + let num_kv_heads = self + .get_gguf_u32( + &gguf_content, + &[ + "llama.attention.head_count_kv", + "mistral.attention.head_count_kv", + "phi.attention.head_count_kv", + ], + ) + .unwrap_or(num_heads as u32) as usize; - let vocab_size = self.get_gguf_u32(&gguf_content, &[ - "llama.vocab_size", - "mistral.vocab_size", - "phi.vocab_size", - ]).unwrap_or(32000) as usize; + let vocab_size = self + .get_gguf_u32( + &gguf_content, + &["llama.vocab_size", "mistral.vocab_size", "phi.vocab_size"], + ) + .unwrap_or(32000) as usize; - let intermediate_size = self.get_gguf_u32(&gguf_content, &[ - "llama.feed_forward_length", - "mistral.feed_forward_length", - "phi.feed_forward_length", - ]).unwrap_or(14336) as usize; + let intermediate_size = self + .get_gguf_u32( + &gguf_content, + &[ + "llama.feed_forward_length", + "mistral.feed_forward_length", + "phi.feed_forward_length", + ], + ) + .unwrap_or(14336) as usize; - let rope_theta = self.get_gguf_f32(&gguf_content, &[ - "llama.rope.freq_base", - "mistral.rope.freq_base", - "phi.rope.freq_base", - ]).unwrap_or(10000.0) as f64; + let rope_theta = self + .get_gguf_f32( + &gguf_content, + &[ + "llama.rope.freq_base", + "mistral.rope.freq_base", + "phi.rope.freq_base", + ], + ) + .unwrap_or(10000.0) as f64; - let context_length = self.get_gguf_u32(&gguf_content, &[ - "llama.context_length", - "mistral.context_length", - "phi.context_length", - ]).unwrap_or(config.max_sequence_length as u32) as usize; + let context_length = + self.get_gguf_u32( + &gguf_content, + &[ + "llama.context_length", + "mistral.context_length", + "phi.context_length", + ], + ) + .unwrap_or(config.max_sequence_length as u32) as usize; - let rms_norm_eps = self.get_gguf_f32(&gguf_content, &[ - "llama.attention.layer_norm_rms_epsilon", - "mistral.attention.layer_norm_rms_epsilon", - ]).unwrap_or(1e-5) as f64; + let rms_norm_eps = self + .get_gguf_f32( + &gguf_content, + &[ + "llama.attention.layer_norm_rms_epsilon", + "mistral.attention.layer_norm_rms_epsilon", + ], + ) + .unwrap_or(1e-5) as f64; let head_dim = hidden_size / num_heads; @@ -618,19 +663,26 @@ mod candle_impl { rms_norm_eps, }; - tracing::info!("Model config: hidden={}, layers={}, heads={}, kv_heads={}, vocab={}", - hidden_size, num_layers, num_heads, num_kv_heads, vocab_size); + tracing::info!( + "Model config: hidden={}, layers={}, heads={}, kv_heads={}, vocab={}", + hidden_size, + num_layers, + num_heads, + num_kv_heads, + vocab_size + ); // Load the quantized model weights - let model_weights = qlama::ModelWeights::from_gguf(gguf_content, &mut file, &self.device) - .map_err(|e| { - RuvLLMError::Model(format!("Failed to load GGUF weights: {}", e)) - })?; + let model_weights = + qlama::ModelWeights::from_gguf(gguf_content, &mut file, &self.device).map_err( + |e| RuvLLMError::Model(format!("Failed to load GGUF weights: {}", e)), + )?; let memory_usage = estimate_gguf_memory(path)?; let info = ModelInfo { - name: path.file_stem() + name: path + .file_stem() .and_then(|s| s.to_str()) .unwrap_or("unknown") .to_string(), @@ -691,9 +743,8 @@ mod candle_impl { tracing::info!("Loading safetensors from {} files", weights_files.len()); // Read model config JSON - let config_str = std::fs::read_to_string(config_path).map_err(|e| { - RuvLLMError::Storage(format!("Failed to read config: {}", e)) - })?; + let config_str = std::fs::read_to_string(config_path) + .map_err(|e| RuvLLMError::Storage(format!("Failed to read config: {}", e)))?; let model_json: serde_json::Value = serde_json::from_str(&config_str)?; @@ -705,7 +756,8 @@ mod candle_impl { .as_u64() .unwrap_or(num_heads as u64) as usize; let vocab_size = model_json["vocab_size"].as_u64().unwrap_or(32000) as usize; - let intermediate_size = model_json["intermediate_size"].as_u64().unwrap_or(14336) as usize; + let intermediate_size = + model_json["intermediate_size"].as_u64().unwrap_or(14336) as usize; let rope_theta = model_json["rope_theta"].as_f64().unwrap_or(10000.0); let rms_norm_eps = model_json["rms_norm_eps"].as_f64().unwrap_or(1e-5); let head_dim = hidden_size / num_heads; @@ -729,13 +781,8 @@ mod candle_impl { // Create VarBuilder from safetensors files let vb = unsafe { - VarBuilder::from_mmaped_safetensors( - weights_files, - dtype, - &self.device, - ).map_err(|e| { - RuvLLMError::Model(format!("Failed to load safetensors: {}", e)) - })? + VarBuilder::from_mmaped_safetensors(weights_files, dtype, &self.device) + .map_err(|e| RuvLLMError::Model(format!("Failed to load safetensors: {}", e)))? }; // Load model based on architecture @@ -788,8 +835,8 @@ mod candle_impl { // Create KV cache for the Llama model let cache = llama_model::Cache::new(true, dtype, &llama_config, &self.device) .map_err(|e| { - RuvLLMError::Model(format!("Failed to create Llama cache: {}", e)) - })?; + RuvLLMError::Model(format!("Failed to create Llama cache: {}", e)) + })?; LoadedModelInner::Llama(model, cache) } @@ -801,13 +848,15 @@ mod candle_impl { } }; - let memory_usage: usize = weights_files.iter() + let memory_usage: usize = weights_files + .iter() .filter_map(|p| std::fs::metadata(p).ok()) .map(|m| m.len() as usize) .sum(); let info = ModelInfo { - name: weights_files.first() + name: weights_files + .first() .and_then(|p| p.parent()) .and_then(|p| p.file_name()) .and_then(|s| s.to_str()) @@ -838,9 +887,10 @@ mod candle_impl { /// Forward pass through the model fn forward(&self, input_ids: &Tensor, seq_len: usize) -> Result { - let model = self.model.as_ref().ok_or_else(|| { - RuvLLMError::InvalidOperation("No model loaded".to_string()) - })?; + let model = self + .model + .as_ref() + .ok_or_else(|| RuvLLMError::InvalidOperation("No model loaded".to_string()))?; let mut pos = self.current_pos.lock().expect("current_pos mutex poisoned"); let current_pos = *pos; @@ -850,21 +900,15 @@ mod candle_impl { })?; let logits = match &mut *inner { - LoadedModelInner::QuantizedLlama(m) => { - m.forward(input_ids, current_pos).map_err(|e| { - RuvLLMError::Generation(format!("Forward pass failed: {}", e)) - })? - } - LoadedModelInner::Mistral(m) => { - m.forward(input_ids, current_pos).map_err(|e| { - RuvLLMError::Generation(format!("Forward pass failed: {}", e)) - })? - } - LoadedModelInner::Llama(m, cache) => { - m.forward(input_ids, current_pos, cache).map_err(|e| { - RuvLLMError::Generation(format!("Forward pass failed: {}", e)) - })? - } + LoadedModelInner::QuantizedLlama(m) => m + .forward(input_ids, current_pos) + .map_err(|e| RuvLLMError::Generation(format!("Forward pass failed: {}", e)))?, + LoadedModelInner::Mistral(m) => m + .forward(input_ids, current_pos) + .map_err(|e| RuvLLMError::Generation(format!("Forward pass failed: {}", e)))?, + LoadedModelInner::Llama(m, cache) => m + .forward(input_ids, current_pos, cache) + .map_err(|e| RuvLLMError::Generation(format!("Forward pass failed: {}", e)))?, }; *pos += seq_len; @@ -928,9 +972,9 @@ mod candle_impl { }; // Convert to f32 vector for processing - let mut logits_vec: Vec = last_logits.to_vec1().map_err(|e| { - RuvLLMError::Generation(format!("Failed to convert logits: {}", e)) - })?; + let mut logits_vec: Vec = last_logits + .to_vec1() + .map_err(|e| RuvLLMError::Generation(format!("Failed to convert logits: {}", e)))?; // Apply repetition penalty if params.repetition_penalty != 1.0 { @@ -960,7 +1004,8 @@ mod candle_impl { .map(|(i, &v)| (i, v)) .collect(); - indexed_logits.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); + indexed_logits + .sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); // Apply top-k filtering if params.top_k > 0 && params.top_k < indexed_logits.len() { @@ -969,8 +1014,14 @@ mod candle_impl { // Apply top-p (nucleus) sampling if params.top_p < 1.0 { - let max_logit = indexed_logits.iter().map(|(_, v)| *v).fold(f32::NEG_INFINITY, f32::max); - let exp_logits: Vec = indexed_logits.iter().map(|(_, v)| (v - max_logit).exp()).collect(); + let max_logit = indexed_logits + .iter() + .map(|(_, v)| *v) + .fold(f32::NEG_INFINITY, f32::max); + let exp_logits: Vec = indexed_logits + .iter() + .map(|(_, v)| (v - max_logit).exp()) + .collect(); let sum_exp: f32 = exp_logits.iter().sum(); let probs: Vec = exp_logits.iter().map(|e| e / sum_exp).collect(); @@ -996,11 +1047,9 @@ mod candle_impl { }); let filtered_logits: Vec = indexed_logits.iter().map(|(_, v)| *v).collect(); - let filtered_tensor = Tensor::from_vec( - filtered_logits, - indexed_logits.len(), - &self.device, - ).map_err(|e| RuvLLMError::Generation(e.to_string()))?; + let filtered_tensor = + Tensor::from_vec(filtered_logits, indexed_logits.len(), &self.device) + .map_err(|e| RuvLLMError::Generation(e.to_string()))?; let mut logits_processor = LogitsProcessor::new( seed, @@ -1020,9 +1069,13 @@ mod candle_impl { let (tx, stream) = TokenStream::channel(); // Determine mock response based on prompt - let response = if prompt.to_lowercase().contains("hello") || prompt.to_lowercase().contains("hi") { + let response = if prompt.to_lowercase().contains("hello") + || prompt.to_lowercase().contains("hi") + { "Hello! I'm running in streaming mode. How can I help you today?" - } else if prompt.to_lowercase().contains("code") || prompt.to_lowercase().contains("function") { + } else if prompt.to_lowercase().contains("code") + || prompt.to_lowercase().contains("function") + { "Here's an example function:\n\n```rust\nfn hello() {\n println!(\"Hello from RuvLLM!\");\n}\n```" } else { "I understand your request. This is a streaming response from RuvLLM mock mode." @@ -1086,7 +1139,8 @@ mod candle_impl { // Local path if path.extension().map_or(false, |e| e == "gguf") { // Direct GGUF file - let tokenizer_path = path.parent() + let tokenizer_path = path + .parent() .map(|p| p.join("tokenizer.json")) .filter(|p| p.exists()); @@ -1121,7 +1175,8 @@ mod candle_impl { let config_file = path.join("config.json"); if !config_file.exists() { return Err(RuvLLMError::NotFound(format!( - "config.json not found in {:?}", path + "config.json not found in {:?}", + path ))); } @@ -1138,7 +1193,7 @@ mod candle_impl { if weights_files.is_empty() { return Err(RuvLLMError::NotFound( - "No .safetensors or .gguf files found".to_string() + "No .safetensors or .gguf files found".to_string(), )); } @@ -1152,9 +1207,10 @@ mod candle_impl { } fn generate(&self, prompt: &str, params: GenerateParams) -> Result { - let tokenizer = self.tokenizer.as_ref().ok_or_else(|| { - RuvLLMError::InvalidOperation("No tokenizer loaded".to_string()) - })?; + let tokenizer = self + .tokenizer + .as_ref() + .ok_or_else(|| RuvLLMError::InvalidOperation("No tokenizer loaded".to_string()))?; // Clear KV cache for new generation self.clear_kv_cache(); @@ -1166,9 +1222,10 @@ mod candle_impl { tracing::debug!("Prompt encoded to {} tokens", prompt_len); // Check max context - let model = self.model.as_ref().ok_or_else(|| { - RuvLLMError::InvalidOperation("No model loaded".to_string()) - })?; + let model = self + .model + .as_ref() + .ok_or_else(|| RuvLLMError::InvalidOperation("No model loaded".to_string()))?; let max_ctx = model.config.max_position_embeddings; if prompt_len >= max_ctx { @@ -1241,10 +1298,13 @@ mod candle_impl { let response_embedding = Self::simple_embedding(&output, 768); let trajectory = Trajectory { - request_id: format!("req-{}", std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0)), + request_id: format!( + "req-{}", + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_millis()) + .unwrap_or(0) + ), session_id: "default".to_string(), query_embedding, response_embedding, @@ -1278,22 +1338,21 @@ mod candle_impl { let stream = self.generate_stream_v2(prompt, params)?; // Create an adapter that converts StreamEvent to GeneratedToken - let iter = stream.filter_map(|event_result| { - match event_result { - Ok(StreamEvent::Token(token)) => Some(Ok(token)), - Ok(StreamEvent::Done { .. }) => None, - Ok(StreamEvent::Error(msg)) => Some(Err(RuvLLMError::Generation(msg))), - Err(e) => Some(Err(e)), - } + let iter = stream.filter_map(|event_result| match event_result { + Ok(StreamEvent::Token(token)) => Some(Ok(token)), + Ok(StreamEvent::Done { .. }) => None, + Ok(StreamEvent::Error(msg)) => Some(Err(RuvLLMError::Generation(msg))), + Err(e) => Some(Err(e)), }); Ok(Box::new(iter)) } fn generate_stream_v2(&self, prompt: &str, params: GenerateParams) -> Result { - let tokenizer = self.tokenizer.as_ref().ok_or_else(|| { - RuvLLMError::InvalidOperation("No tokenizer loaded".to_string()) - })?; + let tokenizer = self + .tokenizer + .as_ref() + .ok_or_else(|| RuvLLMError::InvalidOperation("No tokenizer loaded".to_string()))?; // Check if model is loaded if self.model.is_none() { @@ -1363,12 +1422,18 @@ mod candle_impl { }; if logits_vec.is_empty() { - let _ = tx.send(StreamEvent::Error("Failed to process initial logits".to_string())); + let _ = tx.send(StreamEvent::Error( + "Failed to process initial logits".to_string(), + )); return; } // Sample tokens from logits - let mut indexed: Vec<(usize, f32)> = logits_vec.iter().enumerate().map(|(i, &v)| (i, v)).collect(); + let mut indexed: Vec<(usize, f32)> = logits_vec + .iter() + .enumerate() + .map(|(i, &v)| (i, v)) + .collect(); indexed.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); // Simple top-k sampling @@ -1429,13 +1494,15 @@ mod candle_impl { } fn get_embeddings(&self, text: &str) -> Result> { - let tokenizer = self.tokenizer.as_ref().ok_or_else(|| { - RuvLLMError::InvalidOperation("No tokenizer loaded".to_string()) - })?; + let tokenizer = self + .tokenizer + .as_ref() + .ok_or_else(|| RuvLLMError::InvalidOperation("No tokenizer loaded".to_string()))?; - let model = self.model.as_ref().ok_or_else(|| { - RuvLLMError::InvalidOperation("No model loaded".to_string()) - })?; + let model = self + .model + .as_ref() + .ok_or_else(|| RuvLLMError::InvalidOperation("No model loaded".to_string()))?; let _input_ids = tokenizer.encode(text)?; @@ -1494,11 +1561,15 @@ mod stub_impl { impl Tokenizer for CandleTokenizer { fn encode(&self, _text: &str) -> Result> { - Err(RuvLLMError::Config("Candle feature not enabled".to_string())) + Err(RuvLLMError::Config( + "Candle feature not enabled".to_string(), + )) } fn decode(&self, _tokens: &[u32]) -> Result { - Err(RuvLLMError::Config("Candle feature not enabled".to_string())) + Err(RuvLLMError::Config( + "Candle feature not enabled".to_string(), + )) } fn vocab_size(&self) -> usize { @@ -1541,12 +1612,14 @@ mod stub_impl { impl LlmBackend for CandleBackend { fn load_model(&mut self, _model_id: &str, _config: ModelConfig) -> Result<()> { Err(RuvLLMError::Config( - "Candle feature not enabled. Enable with `candle` feature.".to_string() + "Candle feature not enabled. Enable with `candle` feature.".to_string(), )) } fn generate(&self, _prompt: &str, _params: GenerateParams) -> Result { - Err(RuvLLMError::Config("Candle feature not enabled".to_string())) + Err(RuvLLMError::Config( + "Candle feature not enabled".to_string(), + )) } fn generate_stream( @@ -1554,15 +1627,25 @@ mod stub_impl { _prompt: &str, _params: GenerateParams, ) -> Result> + Send + '_>> { - Err(RuvLLMError::Config("Candle feature not enabled".to_string())) + Err(RuvLLMError::Config( + "Candle feature not enabled".to_string(), + )) } - fn generate_stream_v2(&self, _prompt: &str, _params: GenerateParams) -> Result { - Err(RuvLLMError::Config("Candle feature not enabled".to_string())) + fn generate_stream_v2( + &self, + _prompt: &str, + _params: GenerateParams, + ) -> Result { + Err(RuvLLMError::Config( + "Candle feature not enabled".to_string(), + )) } fn get_embeddings(&self, _text: &str) -> Result> { - Err(RuvLLMError::Config("Candle feature not enabled".to_string())) + Err(RuvLLMError::Config( + "Candle feature not enabled".to_string(), + )) } fn tokenizer(&self) -> Option<&dyn Tokenizer> { @@ -1605,9 +1688,8 @@ fn get_cache_dir() -> PathBuf { /// Estimate GGUF model memory usage fn estimate_gguf_memory(path: &Path) -> Result { - let metadata = std::fs::metadata(path).map_err(|e| { - RuvLLMError::Storage(format!("Failed to read file metadata: {}", e)) - })?; + let metadata = std::fs::metadata(path) + .map_err(|e| RuvLLMError::Storage(format!("Failed to read file metadata: {}", e)))?; // GGUF file size plus overhead for KV cache and activations Ok((metadata.len() as f64 * 1.2) as usize) } diff --git a/crates/ruvllm/src/backends/coreml_backend.rs b/crates/ruvllm/src/backends/coreml_backend.rs index 358c1a139..2b25f495a 100644 --- a/crates/ruvllm/src/backends/coreml_backend.rs +++ b/crates/ruvllm/src/backends/coreml_backend.rs @@ -136,7 +136,7 @@ impl AneCapabilities { // M4 Pro ANE specs Self { available: true, - tops: 38.0, // M4 Pro: 38 TOPS + tops: 38.0, // M4 Pro: 38 TOPS max_model_size_mb: 2048, // ~2GB models work well on ANE supported_ops: vec![ "MatMul".to_string(), @@ -267,7 +267,15 @@ pub mod coreml_native { } /// Extract model description and feature names from MLModel - fn extract_model_info(model: &MLModel) -> (String, Vec, Vec, Option, Option) { + fn extract_model_info( + model: &MLModel, + ) -> ( + String, + Vec, + Vec, + Option, + Option, + ) { unsafe { let desc = model.modelDescription(); let input_desc = desc.inputDescriptionsByName(); @@ -277,8 +285,11 @@ pub mod coreml_native { let output_count = output_desc.count(); // Extract input names - let input_names: Vec = - input_desc.allKeys().iter().map(|key| key.to_string()).collect(); + let input_names: Vec = input_desc + .allKeys() + .iter() + .map(|key| key.to_string()) + .collect(); // Extract output names let output_names: Vec = output_desc @@ -294,7 +305,13 @@ pub mod coreml_native { let vocab_size = None; // Would need to inspect output shapes let hidden_size = None; - (description, input_names, output_names, vocab_size, hidden_size) + ( + description, + input_names, + output_names, + vocab_size, + hidden_size, + ) } } @@ -387,21 +404,19 @@ pub mod coreml_native { // Create NSDictionary directly with dictionaryWithObject_forKey // Use AnyObject as value type since initWithDictionary_error expects NSDictionary - let dict: Retained> = - msg_send_id![NSDictionary::::class(), dictionaryWithObject: &*feature_value, forKey: &*input_key]; + let dict: Retained> = msg_send_id![NSDictionary::::class(), dictionaryWithObject: &*feature_value, forKey: &*input_key]; // Create feature provider using msg_send_id for allocation use objc2::rc::Allocated; let alloc: Allocated = msg_send_id![MLDictionaryFeatureProvider::class(), alloc]; - let provider = - MLDictionaryFeatureProvider::initWithDictionary_error(alloc, &*dict) - .map_err(|e| { - RuvLLMError::CoreML(format!( - "Failed to create feature provider: {}", - e.localizedDescription() - )) - })?; + let provider = MLDictionaryFeatureProvider::initWithDictionary_error(alloc, &*dict) + .map_err(|e| { + RuvLLMError::CoreML(format!( + "Failed to create feature provider: {}", + e.localizedDescription() + )) + })?; // Create prediction options let options = MLPredictionOptions::new(); @@ -471,20 +486,18 @@ pub mod coreml_native { // Create NSDictionary directly with dictionaryWithObject_forKey // Use AnyObject as value type since initWithDictionary_error expects NSDictionary - let dict: Retained> = - msg_send_id![NSDictionary::::class(), dictionaryWithObject: &*feature_value, forKey: &*input_key]; + let dict: Retained> = msg_send_id![NSDictionary::::class(), dictionaryWithObject: &*feature_value, forKey: &*input_key]; // Create feature provider using msg_send_id for allocation let alloc: Allocated = msg_send_id![MLDictionaryFeatureProvider::class(), alloc]; - let provider = - MLDictionaryFeatureProvider::initWithDictionary_error(alloc, &*dict) - .map_err(|e| { - RuvLLMError::CoreML(format!( - "Failed to create feature provider: {}", - e.localizedDescription() - )) - })?; + let provider = MLDictionaryFeatureProvider::initWithDictionary_error(alloc, &*dict) + .map_err(|e| { + RuvLLMError::CoreML(format!( + "Failed to create feature provider: {}", + e.localizedDescription() + )) + })?; let options = MLPredictionOptions::new(); // Run prediction - cast provider to protocol object @@ -615,7 +628,12 @@ pub use coreml_native::CoreMLModelHandle; // ============================================================================= /// Iterator for streaming Core ML token generation -#[cfg(all(target_os = "macos", target_arch = "aarch64", feature = "coreml", feature = "candle"))] +#[cfg(all( + target_os = "macos", + target_arch = "aarch64", + feature = "coreml", + feature = "candle" +))] pub struct CoreMLStreamIterator<'a> { model_handle: &'a CoreMLModelHandle, tokenizer: &'a crate::tokenizer::RuvTokenizer, @@ -630,7 +648,12 @@ pub struct CoreMLStreamIterator<'a> { finished: bool, } -#[cfg(all(target_os = "macos", target_arch = "aarch64", feature = "coreml", feature = "candle"))] +#[cfg(all( + target_os = "macos", + target_arch = "aarch64", + feature = "coreml", + feature = "candle" +))] impl<'a> CoreMLStreamIterator<'a> { /// Create a new streaming iterator pub fn new( @@ -675,15 +698,22 @@ impl<'a> CoreMLStreamIterator<'a> { }; // Softmax - let max_logit = scaled_logits.iter().cloned().fold(f32::NEG_INFINITY, f32::max); - let exp_logits: Vec = scaled_logits.iter().map(|&x| (x - max_logit).exp()).collect(); + let max_logit = scaled_logits + .iter() + .cloned() + .fold(f32::NEG_INFINITY, f32::max); + let exp_logits: Vec = scaled_logits + .iter() + .map(|&x| (x - max_logit).exp()) + .collect(); let sum_exp: f32 = exp_logits.iter().sum(); let probs: Vec = exp_logits.iter().map(|&x| x / sum_exp).collect(); // Top-p sampling if self.top_p < 1.0 { let mut indexed_probs: Vec<(usize, f32)> = probs.iter().copied().enumerate().collect(); - indexed_probs.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); + indexed_probs + .sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); let mut cumsum = 0.0; let mut cutoff_idx = indexed_probs.len(); @@ -734,7 +764,12 @@ impl<'a> CoreMLStreamIterator<'a> { } } -#[cfg(all(target_os = "macos", target_arch = "aarch64", feature = "coreml", feature = "candle"))] +#[cfg(all( + target_os = "macos", + target_arch = "aarch64", + feature = "coreml", + feature = "candle" +))] impl<'a> Iterator for CoreMLStreamIterator<'a> { type Item = Result; @@ -744,7 +779,10 @@ impl<'a> Iterator for CoreMLStreamIterator<'a> { } // Run inference - let logits = match self.model_handle.predict(&self.input_feature_name, &self.input_ids) { + let logits = match self + .model_handle + .predict(&self.input_feature_name, &self.input_ids) + { Ok(l) => l, Err(e) => { self.finished = true; @@ -791,7 +829,12 @@ impl<'a> Iterator for CoreMLStreamIterator<'a> { } // Safety: The iterator holds references to CoreMLModelHandle and RuvTokenizer which are Send+Sync -#[cfg(all(target_os = "macos", target_arch = "aarch64", feature = "coreml", feature = "candle"))] +#[cfg(all( + target_os = "macos", + target_arch = "aarch64", + feature = "coreml", + feature = "candle" +))] unsafe impl<'a> Send for CoreMLStreamIterator<'a> {} // ============================================================================= @@ -864,7 +907,7 @@ impl Default for CoreMLBackend { #[cfg(feature = "candle")] tokenizer: None, input_feature_name: "input_ids".to_string(), - eos_token_id: 2, // Common default EOS token + eos_token_id: 2, // Common default EOS token vocab_size: 32000, // Common default vocab size } } @@ -902,7 +945,7 @@ impl CoreMLBackend { #[cfg(feature = "candle")] tokenizer: None, input_feature_name: "input_ids".to_string(), - eos_token_id: 2, // Common default EOS token + eos_token_id: 2, // Common default EOS token vocab_size: 32000, // Common default vocab size }) } @@ -1075,15 +1118,22 @@ impl CoreMLBackend { }; // Softmax to get probabilities - let max_logit = scaled_logits.iter().cloned().fold(f32::NEG_INFINITY, f32::max); - let exp_logits: Vec = scaled_logits.iter().map(|&x| (x - max_logit).exp()).collect(); + let max_logit = scaled_logits + .iter() + .cloned() + .fold(f32::NEG_INFINITY, f32::max); + let exp_logits: Vec = scaled_logits + .iter() + .map(|&x| (x - max_logit).exp()) + .collect(); let sum_exp: f32 = exp_logits.iter().sum(); let probs: Vec = exp_logits.iter().map(|&x| x / sum_exp).collect(); // Top-p (nucleus) sampling if top_p < 1.0 { let mut indexed_probs: Vec<(usize, f32)> = probs.iter().copied().enumerate().collect(); - indexed_probs.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); + indexed_probs + .sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); let mut cumsum = 0.0; let mut cutoff_idx = indexed_probs.len(); @@ -1244,7 +1294,8 @@ impl LlmBackend for CoreMLBackend { })?; // Encode the prompt - let mut input_ids: Vec = tokenizer.encode(prompt)? + let mut input_ids: Vec = tokenizer + .encode(prompt)? .into_iter() .map(|t| t as i32) .collect(); @@ -1319,7 +1370,8 @@ impl LlmBackend for CoreMLBackend { })?; // Encode the prompt - let input_ids: Vec = tokenizer.encode(prompt)? + let input_ids: Vec = tokenizer + .encode(prompt)? .into_iter() .map(|t| t as i32) .collect(); @@ -1377,7 +1429,8 @@ impl LlmBackend for CoreMLBackend { })?; // Encode the prompt - let mut input_ids: Vec = tokenizer.encode(prompt)? + let mut input_ids: Vec = tokenizer + .encode(prompt)? .into_iter() .map(|t| t as i32) .collect(); @@ -1476,7 +1529,8 @@ impl LlmBackend for CoreMLBackend { })?; // Encode the text - let token_ids: Vec = tokenizer.encode(text)? + let token_ids: Vec = tokenizer + .encode(text)? .into_iter() .map(|t| t as i32) .collect(); @@ -1826,7 +1880,9 @@ mod tests { assert!(backend.is_err()); let err = backend.unwrap_err(); - assert!(err.to_string().contains("Apple Neural Engine not available")); + assert!(err + .to_string() + .contains("Apple Neural Engine not available")); } #[test] @@ -1974,7 +2030,8 @@ mod tests { #[test] #[cfg(all(target_os = "macos", target_arch = "aarch64"))] fn test_coreml_backend_validate_path_nonexistent() { - let result = CoreMLBackend::validate_coreml_path(Path::new("/nonexistent/model.mlmodel")); + let result = + CoreMLBackend::validate_coreml_path(Path::new("/nonexistent/model.mlmodel")); assert!(result.is_err()); assert!(result.unwrap_err().to_string().contains("does not exist")); } diff --git a/crates/ruvllm/src/backends/gemma2.rs b/crates/ruvllm/src/backends/gemma2.rs index bd752f1b5..d4131b22e 100644 --- a/crates/ruvllm/src/backends/gemma2.rs +++ b/crates/ruvllm/src/backends/gemma2.rs @@ -27,11 +27,8 @@ //! ``` use crate::error::{Result, RuvLLMError}; -use crate::kernels::{ - apply_rope_neon, flash_attention_neon, rms_norm_neon, - AttentionConfig, -}; -use crate::kernels::rope::{RopeConfig, precompute_rope_tables_with_config, RopeTables}; +use crate::kernels::rope::{precompute_rope_tables_with_config, RopeConfig, RopeTables}; +use crate::kernels::{apply_rope_neon, flash_attention_neon, rms_norm_neon, AttentionConfig}; #[cfg(target_arch = "aarch64")] use std::arch::aarch64::*; @@ -248,7 +245,11 @@ unsafe fn logit_soft_cap_neon(x: &mut [f32], cap: f32) { let tanh_vec = vsetq_lane_f32( t3, - vsetq_lane_f32(t2, vsetq_lane_f32(t1, vsetq_lane_f32(t0, vdupq_n_f32(0.0), 0), 1), 2), + vsetq_lane_f32( + t2, + vsetq_lane_f32(t1, vsetq_lane_f32(t0, vdupq_n_f32(0.0), 0), 1), + 2, + ), 3, ); @@ -317,7 +318,9 @@ impl Gemma2Attention { || v_proj.len() != self.v_proj.len() || o_proj.len() != self.o_proj.len() { - return Err(RuvLLMError::Model("Invalid attention weight dimensions".to_string())); + return Err(RuvLLMError::Model( + "Invalid attention weight dimensions".to_string(), + )); } self.q_proj.copy_from_slice(q_proj); @@ -458,7 +461,8 @@ impl Gemma2Attention { } // Output projection - let output = self.linear_transform(&output, &self.o_proj, num_heads * head_dim, hidden_size); + let output = + self.linear_transform(&output, &self.o_proj, num_heads * head_dim, hidden_size); Ok(output) } @@ -498,14 +502,25 @@ impl Gemma2Attention { for t in 0..seq_len { let offset = (t * num_heads + h) * head_dim; let mut head_vec = x[offset..offset + head_dim].to_vec(); - apply_rope_neon(&mut head_vec, &[positions[t]], head_dim, self.config.rope_theta); + apply_rope_neon( + &mut head_vec, + &[positions[t]], + head_dim, + self.config.rope_theta, + ); x[offset..offset + head_dim].copy_from_slice(&head_vec); } } } /// Linear transformation - fn linear_transform(&self, input: &[f32], weights: &[f32], in_dim: usize, out_dim: usize) -> Vec { + fn linear_transform( + &self, + input: &[f32], + weights: &[f32], + in_dim: usize, + out_dim: usize, + ) -> Vec { let batch_size = input.len() / in_dim; let mut output = vec![0.0; batch_size * out_dim]; @@ -569,7 +584,9 @@ impl Gemma2MLP { || up_proj.len() != gate_up_size || down_proj.len() != down_size { - return Err(RuvLLMError::Model("Invalid MLP weight dimensions".to_string())); + return Err(RuvLLMError::Model( + "Invalid MLP weight dimensions".to_string(), + )); } self.gate_proj.copy_from_slice(gate_proj); @@ -584,11 +601,21 @@ impl Gemma2MLP { let batch_size = hidden_states.len() / self.hidden_size; // Gate projection + GELU - let gate = self.linear(hidden_states, &self.gate_proj, self.hidden_size, self.intermediate_size); + let gate = self.linear( + hidden_states, + &self.gate_proj, + self.hidden_size, + self.intermediate_size, + ); let gate_activated = self.gelu(&gate); // Up projection - let up = self.linear(hidden_states, &self.up_proj, self.hidden_size, self.intermediate_size); + let up = self.linear( + hidden_states, + &self.up_proj, + self.hidden_size, + self.intermediate_size, + ); // Element-wise multiply (gating) let hidden: Vec = gate_activated @@ -598,7 +625,12 @@ impl Gemma2MLP { .collect(); // Down projection - let output = self.linear(&hidden, &self.down_proj, self.intermediate_size, self.hidden_size); + let output = self.linear( + &hidden, + &self.down_proj, + self.intermediate_size, + self.hidden_size, + ); Ok(output) } @@ -868,9 +900,9 @@ impl Gemma2Model { let lm_weights = if self.tie_word_embeddings { &self.embed_tokens } else { - self.lm_head.as_ref().ok_or_else(|| { - RuvLLMError::InvalidOperation("No LM head weights".to_string()) - })? + self.lm_head + .as_ref() + .ok_or_else(|| RuvLLMError::InvalidOperation("No LM head weights".to_string()))? }; // Compute logits with soft-capping @@ -886,7 +918,8 @@ impl Gemma2Model { } // Apply final logit soft-capping - let logit_slice = &mut logits[t * self.config.vocab_size..(t + 1) * self.config.vocab_size]; + let logit_slice = + &mut logits[t * self.config.vocab_size..(t + 1) * self.config.vocab_size]; logit_soft_cap(logit_slice, self.config.final_logit_softcapping); } @@ -900,7 +933,10 @@ impl Gemma2Model { let mut result = String::new(); for (role, content) in messages { - result.push_str(&format!("{}\n{}\n", role, content)); + result.push_str(&format!( + "{}\n{}\n", + role, content + )); } result.push_str("model\n"); @@ -910,13 +946,17 @@ impl Gemma2Model { /// Load model weights from GGUF format #[cfg(feature = "candle")] pub fn from_gguf(_path: &std::path::Path) -> Result { - Err(RuvLLMError::NotFound("GGUF loading not yet implemented for Gemma-2".to_string())) + Err(RuvLLMError::NotFound( + "GGUF loading not yet implemented for Gemma-2".to_string(), + )) } /// Load model weights from safetensors format #[cfg(feature = "candle")] pub fn from_safetensors(_path: &std::path::Path) -> Result { - Err(RuvLLMError::NotFound("Safetensors loading not yet implemented for Gemma-2".to_string())) + Err(RuvLLMError::NotFound( + "Safetensors loading not yet implemented for Gemma-2".to_string(), + )) } } @@ -945,9 +985,9 @@ mod tests { fn test_local_attention_alternation() { let config = Gemma2Config::gemma2_9b(); assert!(!config.is_local_attention_layer(0)); // Global - assert!(config.is_local_attention_layer(1)); // Local + assert!(config.is_local_attention_layer(1)); // Local assert!(!config.is_local_attention_layer(2)); // Global - assert!(config.is_local_attention_layer(3)); // Local + assert!(config.is_local_attention_layer(3)); // Local } #[test] @@ -992,7 +1032,10 @@ mod tests { let model = Gemma2Model::new(&config).unwrap(); assert_eq!(model.layers.len(), 26); - assert_eq!(model.embed_tokens.len(), config.vocab_size * config.hidden_size); + assert_eq!( + model.embed_tokens.len(), + config.vocab_size * config.hidden_size + ); } #[test] diff --git a/crates/ruvllm/src/backends/hybrid_pipeline.rs b/crates/ruvllm/src/backends/hybrid_pipeline.rs index 5e74905b9..57c23efd0 100644 --- a/crates/ruvllm/src/backends/hybrid_pipeline.rs +++ b/crates/ruvllm/src/backends/hybrid_pipeline.rs @@ -52,7 +52,7 @@ //! | RoPE | N/A | 16.7 | GPU | use super::{ - AneCapabilities, ComputeUnits, CoreMLBackend, DeviceType, DType, GenerateParams, + AneCapabilities, ComputeUnits, CoreMLBackend, DType, DeviceType, GenerateParams, GeneratedToken, LlmBackend, ModelArchitecture, ModelConfig, ModelInfo, Quantization, SpecialTokens, StreamEvent, TokenStream, Tokenizer, }; @@ -164,11 +164,7 @@ impl OperationType { pub fn ane_supported(&self) -> bool { matches!( self, - Self::MatMul - | Self::Activation - | Self::Normalization - | Self::Softmax - | Self::Embedding + Self::MatMul | Self::Activation | Self::Normalization | Self::Softmax | Self::Embedding ) } } @@ -585,7 +581,12 @@ impl HybridPipeline { let reason = match accelerator { AcceleratorType::Ane => { - format!("ANE optimal for {} (batch={}, dim={})", op_name(op), batch_size, dim) + format!( + "ANE optimal for {} (batch={}, dim={})", + op_name(op), + batch_size, + dim + ) } AcceleratorType::Metal => { format!( @@ -631,8 +632,11 @@ impl HybridPipeline { let seq_len = query.len() / (config.num_heads * config.head_dim); let kv_len = key.len() / (config.num_kv_heads * config.head_dim); // Attention FLOPs: 2 * seq_len * kv_len * head_dim * num_heads (QK^T and softmax@V) - let flops = - 2 * seq_len as u64 * kv_len as u64 * config.head_dim as u64 * config.num_heads as u64; + let flops = 2 + * seq_len as u64 + * kv_len as u64 + * config.head_dim as u64 + * config.num_heads as u64; let bytes = (query.len() + key.len() + value.len() + result.len()) * 4; self.metal_metrics .record_operation(duration_ns, flops, bytes as u64); @@ -679,10 +683,22 @@ impl HybridPipeline { })?; // Gate projection: hidden @ gate_weight.T - let gate = ctx.gemm_f32(hidden, gate_weight, batch_size, intermediate_size, hidden_size)?; + let gate = ctx.gemm_f32( + hidden, + gate_weight, + batch_size, + intermediate_size, + hidden_size, + )?; // Up projection: hidden @ up_weight.T - let up = ctx.gemm_f32(hidden, up_weight, batch_size, intermediate_size, hidden_size)?; + let up = ctx.gemm_f32( + hidden, + up_weight, + batch_size, + intermediate_size, + hidden_size, + )?; // SwiGLU activation: silu(gate) * up let activated = if let Some(_) = ctx.has_m4_pro_optimizations().then_some(()) { @@ -710,7 +726,8 @@ impl HybridPipeline { if self.config.collect_metrics { let duration_ns = start.elapsed().as_nanos() as u64; // MLP FLOPs: 3 matmuls + activation - let flops = 2 * batch_size as u64 + let flops = 2 + * batch_size as u64 * (hidden_size as u64 * intermediate_size as u64 * 2 + intermediate_size as u64 * hidden_size as u64); let bytes = (hidden.len() @@ -846,9 +863,8 @@ impl HybridPipeline { /// Get summary of accelerator utilization pub fn utilization_summary(&self) -> String { - let total_ops = self.metal_metrics.total_ops - + self.ane_metrics.total_ops - + self.cpu_metrics.total_ops; + let total_ops = + self.metal_metrics.total_ops + self.ane_metrics.total_ops + self.cpu_metrics.total_ops; if total_ops == 0 { return "No operations executed yet".to_string(); diff --git a/crates/ruvllm/src/backends/mistral_backend.rs b/crates/ruvllm/src/backends/mistral_backend.rs index beb72a7ad..31aa9093c 100644 --- a/crates/ruvllm/src/backends/mistral_backend.rs +++ b/crates/ruvllm/src/backends/mistral_backend.rs @@ -39,8 +39,8 @@ //! ``` use super::{ - DeviceType, DType, GenerateParams, GeneratedToken, LlmBackend, ModelArchitecture, - ModelConfig, ModelInfo, Quantization, SpecialTokens, Tokenizer, + DType, DeviceType, GenerateParams, GeneratedToken, LlmBackend, ModelArchitecture, ModelConfig, + ModelInfo, Quantization, SpecialTokens, Tokenizer, }; use crate::error::{Result, RuvLLMError}; use crate::paged_attention::{PagedAttention, PagedAttentionConfig}; @@ -57,12 +57,9 @@ use serde::{Deserialize, Serialize}; // Conditional imports for mistral-rs crate integration #[cfg(feature = "mistral-rs")] use mistralrs::{ - GGUFLoaderBuilder, GGUFSpecificConfig, - MistralRs, MistralRsBuilder, - PagedAttentionMetaBuilder, SchedulerConfig, - TokenSource, Device as MistralDevice, - NormalRequest, Request, RequestMessage, - Response, SamplingParams, Constraint, + Constraint, Device as MistralDevice, GGUFLoaderBuilder, GGUFSpecificConfig, MistralRs, + MistralRsBuilder, NormalRequest, PagedAttentionMetaBuilder, Request, RequestMessage, Response, + SamplingParams, SchedulerConfig, TokenSource, }; #[cfg(feature = "mistral-rs")] use tokio::sync::mpsc::channel as tokio_channel; @@ -384,7 +381,9 @@ impl XLoraManager { }; self.adapters.insert(name.to_string(), adapter); - self.stats.adapter_usage.insert(name.to_string(), AtomicU64::new(0)); + self.stats + .adapter_usage + .insert(name.to_string(), AtomicU64::new(0)); tracing::info!("Loaded X-LoRA adapter: {} from {:?}", name, path); Ok(()) @@ -463,10 +462,7 @@ impl XLoraManager { ); // Apply temperature and softmax - let scaled: Vec = logits - .iter() - .map(|x| x / self.config.temperature) - .collect(); + let scaled: Vec = logits.iter().map(|x| x / self.config.temperature).collect(); let probs = softmax(&scaled); // Select top-k adapters @@ -527,11 +523,7 @@ impl XLoraManager { } /// Apply X-LoRA to hidden states - pub fn apply( - &self, - hidden_states: &[f32], - layer_name: &str, - ) -> Vec { + pub fn apply(&self, hidden_states: &[f32], layer_name: &str) -> Vec { let routing = self.route(hidden_states); let mut output = vec![0.0; hidden_states.len()]; @@ -576,12 +568,7 @@ impl XLoraManager { } /// Apply a single adapter - fn apply_adapter( - &self, - input: &[f32], - adapter: &AdapterWeights, - layer_name: &str, - ) -> Vec { + fn apply_adapter(&self, input: &[f32], adapter: &AdapterWeights, layer_name: &str) -> Vec { let lora_a = adapter.lora_a.get(layer_name); let lora_b = adapter.lora_b.get(layer_name); @@ -666,16 +653,17 @@ pub struct MistralTokenizer { #[cfg(feature = "mistral-rs")] impl Tokenizer for MistralTokenizer { fn encode(&self, text: &str) -> Result> { - let encoding = self.inner.encode(text, false).map_err(|e| { - RuvLLMError::Tokenization(format!("Tokenization failed: {}", e)) - })?; + let encoding = self + .inner + .encode(text, false) + .map_err(|e| RuvLLMError::Tokenization(format!("Tokenization failed: {}", e)))?; Ok(encoding.get_ids().to_vec()) } fn decode(&self, tokens: &[u32]) -> Result { - self.inner.decode(tokens, true).map_err(|e| { - RuvLLMError::Tokenization(format!("Decoding failed: {}", e)) - }) + self.inner + .decode(tokens, true) + .map_err(|e| RuvLLMError::Tokenization(format!("Decoding failed: {}", e))) } fn vocab_size(&self) -> usize { @@ -759,17 +747,18 @@ impl MistralBackend { page_size: pa_config.block_size, max_pages_per_sequence: pa_config.max_pages / 256, // Sequences share pages page_table_capacity: pa_config.max_pages, - num_heads: 32, // Will be updated on model load - head_dim: 128, // Will be updated on model load + num_heads: 32, // Will be updated on model load + head_dim: 128, // Will be updated on model load num_kv_heads: 8, // Will be updated on model load ..Default::default() }) }); // Initialize X-LoRA if configured - let xlora_manager = config.xlora.as_ref().map(|xlora_config| { - XLoraManager::new(xlora_config.clone()) - }); + let xlora_manager = config + .xlora + .as_ref() + .map(|xlora_config| XLoraManager::new(xlora_config.clone())); Ok(Self { config, @@ -820,17 +809,19 @@ impl MistralBackend { /// Load X-LoRA adapter pub fn load_xlora_adapter(&self, name: &str, path: &Path) -> Result<()> { - let manager = self.xlora_manager.as_ref().ok_or_else(|| { - RuvLLMError::Config("X-LoRA not configured".to_string()) - })?; + let manager = self + .xlora_manager + .as_ref() + .ok_or_else(|| RuvLLMError::Config("X-LoRA not configured".to_string()))?; manager.load_adapter(name, path) } /// Set active X-LoRA adapters pub fn set_xlora_adapters(&self, adapters: Vec<(&str, f32)>) -> Result<()> { - let manager = self.xlora_manager.as_ref().ok_or_else(|| { - RuvLLMError::Config("X-LoRA not configured".to_string()) - })?; + let manager = self + .xlora_manager + .as_ref() + .ok_or_else(|| RuvLLMError::Config("X-LoRA not configured".to_string()))?; manager.set_active(adapters) } @@ -842,9 +833,11 @@ impl MistralBackend { )); } - let _isq_config = self.config.isq.as_ref().ok_or_else(|| { - RuvLLMError::Config("ISQ not configured".to_string()) - })?; + let _isq_config = self + .config + .isq + .as_ref() + .ok_or_else(|| RuvLLMError::Config("ISQ not configured".to_string()))?; // In a real implementation, this would quantize model weights in-place // using the configured ISQ method (AWQ, GPTQ, RTN, etc.) @@ -943,25 +936,29 @@ impl MistralBackend { }); // Send request to model - model.get_sender().map_err(|e| { - RuvLLMError::Compute(format!("Failed to get model sender: {}", e)) - })?.blocking_send(request).map_err(|e| { - RuvLLMError::Compute(format!("Failed to send request to model: {}", e)) - })?; + model + .get_sender() + .map_err(|e| RuvLLMError::Compute(format!("Failed to get model sender: {}", e)))? + .blocking_send(request) + .map_err(|e| RuvLLMError::Compute(format!("Failed to send request to model: {}", e)))?; // Wait for response - let response = rx.recv().map_err(|e| { - RuvLLMError::Compute(format!("Failed to receive response: {}", e)) - })?; + let response = rx + .recv() + .map_err(|e| RuvLLMError::Compute(format!("Failed to receive response: {}", e)))?; match response { Response::Done(completion) => { - let output_text = completion.choices.first() + let output_text = completion + .choices + .first() .map(|c| c.message.content.clone().unwrap_or_default()) .unwrap_or_default(); // Build generated tokens from the response - let generated_tokens = completion.choices.first() + let generated_tokens = completion + .choices + .first() .map(|c| { // mistral-rs doesn't provide individual tokens in non-streaming mode // so we return a single token representing the full output @@ -985,9 +982,7 @@ impl MistralBackend { Response::ModelError(msg, _) => { Err(RuvLLMError::Compute(format!("Model error: {}", msg))) } - _ => { - Err(RuvLLMError::Compute("Unexpected response type".to_string())) - } + _ => Err(RuvLLMError::Compute("Unexpected response type".to_string())), } } @@ -997,9 +992,10 @@ impl MistralBackend { prompt: &str, params: &GenerateParams, ) -> Result<(String, Vec)> { - let tokenizer = self.tokenizer.as_ref().ok_or_else(|| { - RuvLLMError::InvalidOperation("No tokenizer loaded".to_string()) - })?; + let tokenizer = self + .tokenizer + .as_ref() + .ok_or_else(|| RuvLLMError::InvalidOperation("No tokenizer loaded".to_string()))?; // Encode prompt let input_ids = tokenizer.encode(prompt)?; @@ -1110,9 +1106,8 @@ impl LlmBackend for MistralBackend { #[cfg(feature = "mistral-rs")] { - let inner = tokenizers::Tokenizer::from_file(&tokenizer_path).map_err(|e| { - RuvLLMError::Storage(format!("Failed to load tokenizer: {}", e)) - })?; + let inner = tokenizers::Tokenizer::from_file(&tokenizer_path) + .map_err(|e| RuvLLMError::Storage(format!("Failed to load tokenizer: {}", e)))?; let special_tokens = SpecialTokens { bos_token_id: inner.token_to_id(""), @@ -1166,8 +1161,14 @@ impl LlmBackend for MistralBackend { let is_gguf = model_path.extension().map(|e| e == "gguf").unwrap_or(false) || model_path.join("model.gguf").exists() || std::fs::read_dir(&model_path) - .map(|entries| entries.filter_map(|e| e.ok()) - .any(|e| e.path().extension().map(|ext| ext == "gguf").unwrap_or(false))) + .map(|entries| { + entries.filter_map(|e| e.ok()).any(|e| { + e.path() + .extension() + .map(|ext| ext == "gguf") + .unwrap_or(false) + }) + }) .unwrap_or(false); if is_gguf { @@ -1182,7 +1183,9 @@ impl LlmBackend for MistralBackend { // Determine the device let device = match self.config.device { DeviceType::Cpu => MistralDevice::Cpu, - DeviceType::Cuda(id) => MistralDevice::new_cuda(id).unwrap_or(MistralDevice::Cpu), + DeviceType::Cuda(id) => { + MistralDevice::new_cuda(id).unwrap_or(MistralDevice::Cpu) + } DeviceType::Metal => MistralDevice::new_metal(0).unwrap_or(MistralDevice::Cpu), _ => MistralDevice::Cpu, }; @@ -1197,7 +1200,12 @@ impl LlmBackend for MistralBackend { .and_then(|entries| { entries .filter_map(|e| e.ok()) - .find(|e| e.path().extension().map(|ext| ext == "gguf").unwrap_or(false)) + .find(|e| { + e.path() + .extension() + .map(|ext| ext == "gguf") + .unwrap_or(false) + }) .map(|e| e.path()) }) .unwrap_or_else(|| model_path.join("model.gguf")) @@ -1221,9 +1229,8 @@ impl LlmBackend for MistralBackend { } else { SchedulerConfig::DefaultScheduler { method: mistralrs::DefaultSchedulerMethod::Fixed( - std::num::NonZeroUsize::new(self.config.max_batch_size).unwrap_or( - std::num::NonZeroUsize::new(1).unwrap() - ) + std::num::NonZeroUsize::new(self.config.max_batch_size) + .unwrap_or(std::num::NonZeroUsize::new(1).unwrap()), ), } }; @@ -1246,7 +1253,10 @@ impl LlmBackend for MistralBackend { tracing::info!("Loaded mistral-rs GGUF model from {:?}", gguf_file); } Err(e) => { - tracing::warn!("Failed to load mistral-rs model: {}. Falling back to stub.", e); + tracing::warn!( + "Failed to load mistral-rs model: {}. Falling back to stub.", + e + ); self.mistral_model = None; } } @@ -1289,9 +1299,7 @@ impl LlmBackend for MistralBackend { fn generate(&self, prompt: &str, params: GenerateParams) -> Result { if !self.is_model_loaded() { - return Err(RuvLLMError::InvalidOperation( - "No model loaded".to_string(), - )); + return Err(RuvLLMError::InvalidOperation("No model loaded".to_string())); } let (output, _tokens) = self.generate_internal(prompt, ¶ms)?; @@ -1304,9 +1312,7 @@ impl LlmBackend for MistralBackend { params: GenerateParams, ) -> Result> + Send + '_>> { if !self.is_model_loaded() { - return Err(RuvLLMError::InvalidOperation( - "No model loaded".to_string(), - )); + return Err(RuvLLMError::InvalidOperation("No model loaded".to_string())); } // For streaming, we generate all tokens and return an iterator @@ -1325,9 +1331,7 @@ impl LlmBackend for MistralBackend { use std::time::Instant; if !self.is_model_loaded() { - return Err(RuvLLMError::InvalidOperation( - "No model loaded".to_string(), - )); + return Err(RuvLLMError::InvalidOperation("No model loaded".to_string())); } let (tx, stream) = TokenStream::channel(); @@ -1358,14 +1362,13 @@ impl LlmBackend for MistralBackend { fn get_embeddings(&self, text: &str) -> Result> { if !self.is_model_loaded() { - return Err(RuvLLMError::InvalidOperation( - "No model loaded".to_string(), - )); + return Err(RuvLLMError::InvalidOperation("No model loaded".to_string())); } - let tokenizer = self.tokenizer.as_ref().ok_or_else(|| { - RuvLLMError::InvalidOperation("No tokenizer loaded".to_string()) - })?; + let tokenizer = self + .tokenizer + .as_ref() + .ok_or_else(|| RuvLLMError::InvalidOperation("No tokenizer loaded".to_string()))?; let _tokens = tokenizer.encode(text)?; @@ -1447,7 +1450,8 @@ fn softmax(logits: &[f32]) -> Vec { /// Estimate number of parameters fn estimate_parameters(hidden_size: usize, num_layers: usize, vocab_size: usize) -> usize { let embedding_params = vocab_size * hidden_size; - let layer_params = num_layers * (4 * hidden_size * hidden_size + 8 * hidden_size * hidden_size / 3); + let layer_params = + num_layers * (4 * hidden_size * hidden_size + 8 * hidden_size * hidden_size / 3); let output_params = vocab_size * hidden_size; embedding_params + layer_params + output_params } @@ -1501,8 +1505,8 @@ mod tests { #[test] fn test_xlora_config() { - let config = MistralBackendConfig::default() - .with_xlora_adapters(vec!["code", "chat", "math"]); + let config = + MistralBackendConfig::default().with_xlora_adapters(vec!["code", "chat", "math"]); assert!(config.xlora.is_some()); let xlora = config.xlora.unwrap(); @@ -1548,8 +1552,16 @@ mod tests { // Note: This is an approximation, not exact parameter count let params = estimate_parameters(4096, 32, 32000); // Should be in the billions (rough estimate for a 7B-class model) - assert!(params > 3_000_000_000, "Expected > 3B params, got {}", params); - assert!(params < 10_000_000_000, "Expected < 10B params, got {}", params); + assert!( + params > 3_000_000_000, + "Expected > 3B params, got {}", + params + ); + assert!( + params < 10_000_000_000, + "Expected < 10B params, got {}", + params + ); } #[test] diff --git a/crates/ruvllm/src/backends/mod.rs b/crates/ruvllm/src/backends/mod.rs index f37116bca..953a62cb4 100644 --- a/crates/ruvllm/src/backends/mod.rs +++ b/crates/ruvllm/src/backends/mod.rs @@ -75,26 +75,26 @@ pub use candle_backend::*; // Core ML backend for Apple Neural Engine (ANE) acceleration mod coreml_backend; -pub use coreml_backend::{CoreMLBackend, ComputeUnits, AneCapabilities}; +pub use coreml_backend::{AneCapabilities, ComputeUnits, CoreMLBackend}; // Hybrid GPU+ANE pipeline coordinator #[cfg(feature = "hybrid-ane")] mod hybrid_pipeline; #[cfg(feature = "hybrid-ane")] pub use hybrid_pipeline::{ - HybridPipeline, HybridPipelineConfig, AneStrategy, OperationType, - AcceleratorType, AcceleratorMetrics, RoutingDecision, HybridTensor, DataFormat, + AcceleratorMetrics, AcceleratorType, AneStrategy, DataFormat, HybridPipeline, + HybridPipelineConfig, HybridTensor, OperationType, RoutingDecision, }; // Model architecture implementations -pub mod phi3; pub mod gemma2; +pub mod phi3; -pub use phi3::{Phi3Config, Phi3Model, Phi3Attention, Phi3MLP, Phi3DecoderLayer}; pub use gemma2::{ - Gemma2Config, Gemma2Model, Gemma2Attention, Gemma2MLP, Gemma2DecoderLayer, - logit_soft_cap, ATTENTION_SOFTCAP, FINAL_LOGIT_SOFTCAP, + logit_soft_cap, Gemma2Attention, Gemma2Config, Gemma2DecoderLayer, Gemma2MLP, Gemma2Model, + ATTENTION_SOFTCAP, FINAL_LOGIT_SOFTCAP, }; +pub use phi3::{Phi3Attention, Phi3Config, Phi3DecoderLayer, Phi3MLP, Phi3Model}; // mistral-rs backend - always available, but full functionality requires the feature mod mistral_backend; @@ -199,7 +199,10 @@ impl ModelArchitecture { /// Check if this architecture uses GQA (Grouped Query Attention) pub fn uses_gqa(&self) -> bool { - matches!(self, Self::Mistral | Self::Llama | Self::Gemma | Self::Gemma2 | Self::Qwen) + matches!( + self, + Self::Mistral | Self::Llama | Self::Gemma | Self::Gemma2 | Self::Qwen + ) } /// Check if this architecture uses sliding window attention @@ -359,7 +362,9 @@ impl Default for ModelConfig { } /// Device type for inference -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize, +)] pub enum DeviceType { /// CPU inference Cpu, @@ -371,7 +376,9 @@ pub enum DeviceType { } /// Data type for tensor operations -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize, +)] pub enum DType { /// 32-bit floating point F32, diff --git a/crates/ruvllm/src/backends/phi3.rs b/crates/ruvllm/src/backends/phi3.rs index 6d4125f27..05341d45d 100644 --- a/crates/ruvllm/src/backends/phi3.rs +++ b/crates/ruvllm/src/backends/phi3.rs @@ -26,11 +26,8 @@ //! ``` use crate::error::{Result, RuvLLMError}; -use crate::kernels::{ - apply_rope_neon, flash_attention_neon, rms_norm_neon, - AttentionConfig, -}; -use crate::kernels::rope::{RopeConfig, precompute_rope_tables_with_config, RopeTables}; +use crate::kernels::rope::{precompute_rope_tables_with_config, RopeConfig, RopeTables}; +use crate::kernels::{apply_rope_neon, flash_attention_neon, rms_norm_neon, AttentionConfig}; #[cfg(target_arch = "aarch64")] use std::arch::aarch64::*; @@ -292,7 +289,8 @@ impl Phi3Attention { } // Project to Q, K, V - let mut query = self.linear_transform(hidden_states, &self.q_proj, hidden_size, hidden_size); + let mut query = + self.linear_transform(hidden_states, &self.q_proj, hidden_size, hidden_size); let mut key = self.linear_transform(hidden_states, &self.k_proj, hidden_size, hidden_size); let value = self.linear_transform(hidden_states, &self.v_proj, hidden_size, hidden_size); @@ -331,23 +329,24 @@ impl Phi3Attention { } // Apply sliding window if configured - let (k_slice, v_slice, effective_kv_len) = if let Some(window) = self.config.sliding_window { - let pos = positions[t]; - let start = pos.saturating_sub(window); - let end = kv_len; - if start > 0 { - let start_offset = start * head_dim; - ( - k_slice[start_offset..].to_vec(), - v_slice[start_offset..].to_vec(), - end - start, - ) + let (k_slice, v_slice, effective_kv_len) = + if let Some(window) = self.config.sliding_window { + let pos = positions[t]; + let start = pos.saturating_sub(window); + let end = kv_len; + if start > 0 { + let start_offset = start * head_dim; + ( + k_slice[start_offset..].to_vec(), + v_slice[start_offset..].to_vec(), + end - start, + ) + } else { + (k_slice, v_slice, kv_len) + } } else { (k_slice, v_slice, kv_len) - } - } else { - (k_slice, v_slice, kv_len) - }; + }; // Flash attention let head_output = flash_attention_neon(q_slice, &k_slice, &v_slice, scale, true); @@ -378,7 +377,12 @@ impl Phi3Attention { // Scale position by scaling factor for SuRoPE let scaled_pos = (positions[t] as f32 / self.config.rope_scaling_factor) as usize; - apply_rope_neon(&mut head_vec, &[scaled_pos], head_dim, self.config.rope_theta); + apply_rope_neon( + &mut head_vec, + &[scaled_pos], + head_dim, + self.config.rope_theta, + ); x[offset..offset + head_dim].copy_from_slice(&head_vec); } @@ -386,7 +390,13 @@ impl Phi3Attention { } /// Linear transformation: output = input @ weights.T - fn linear_transform(&self, input: &[f32], weights: &[f32], in_dim: usize, out_dim: usize) -> Vec { + fn linear_transform( + &self, + input: &[f32], + weights: &[f32], + in_dim: usize, + out_dim: usize, + ) -> Vec { let batch_size = input.len() / in_dim; let mut output = vec![0.0; batch_size * out_dim]; @@ -452,7 +462,9 @@ impl Phi3MLP { || up_proj.len() != gate_up_size || down_proj.len() != down_size { - return Err(RuvLLMError::Model("Invalid MLP weight dimensions".to_string())); + return Err(RuvLLMError::Model( + "Invalid MLP weight dimensions".to_string(), + )); } self.gate_proj.copy_from_slice(gate_proj); @@ -467,11 +479,21 @@ impl Phi3MLP { let batch_size = hidden_states.len() / self.hidden_size; // Gate projection + SiLU - let gate = self.linear(hidden_states, &self.gate_proj, self.hidden_size, self.intermediate_size); + let gate = self.linear( + hidden_states, + &self.gate_proj, + self.hidden_size, + self.intermediate_size, + ); let gate_activated = self.silu(&gate); // Up projection - let up = self.linear(hidden_states, &self.up_proj, self.hidden_size, self.intermediate_size); + let up = self.linear( + hidden_states, + &self.up_proj, + self.hidden_size, + self.intermediate_size, + ); // Element-wise multiply (gating) let hidden: Vec = gate_activated @@ -481,7 +503,12 @@ impl Phi3MLP { .collect(); // Down projection - let output = self.linear(&hidden, &self.down_proj, self.intermediate_size, self.hidden_size); + let output = self.linear( + &hidden, + &self.down_proj, + self.intermediate_size, + self.hidden_size, + ); Ok(output) } @@ -714,7 +741,8 @@ impl Phi3Model { token_id ))); } - hidden_states.extend_from_slice(&self.embed_tokens[offset..offset + self.config.hidden_size]); + hidden_states + .extend_from_slice(&self.embed_tokens[offset..offset + self.config.hidden_size]); } // Process through decoder layers @@ -741,9 +769,9 @@ impl Phi3Model { let lm_weights = if self.tie_word_embeddings { &self.embed_tokens } else { - self.lm_head.as_ref().ok_or_else(|| { - RuvLLMError::InvalidOperation("No LM head weights".to_string()) - })? + self.lm_head + .as_ref() + .ok_or_else(|| RuvLLMError::InvalidOperation("No LM head weights".to_string()))? }; // Compute logits @@ -780,14 +808,18 @@ impl Phi3Model { #[cfg(feature = "candle")] pub fn from_gguf(_path: &std::path::Path) -> Result { // Implementation would parse GGUF and load weights - Err(RuvLLMError::NotFound("GGUF loading not yet implemented for Phi-3".to_string())) + Err(RuvLLMError::NotFound( + "GGUF loading not yet implemented for Phi-3".to_string(), + )) } /// Load model weights from safetensors format #[cfg(feature = "candle")] pub fn from_safetensors(_path: &std::path::Path) -> Result { // Implementation would parse safetensors and load weights - Err(RuvLLMError::NotFound("Safetensors loading not yet implemented for Phi-3".to_string())) + Err(RuvLLMError::NotFound( + "Safetensors loading not yet implemented for Phi-3".to_string(), + )) } } @@ -843,7 +875,10 @@ mod tests { let model = Phi3Model::new(&config).unwrap(); assert_eq!(model.layers.len(), 32); - assert_eq!(model.embed_tokens.len(), config.vocab_size * config.hidden_size); + assert_eq!( + model.embed_tokens.len(), + config.vocab_size * config.hidden_size + ); } #[test] diff --git a/crates/ruvllm/src/claude_flow/agent_router.rs b/crates/ruvllm/src/claude_flow/agent_router.rs index 6b663a5a8..6dc760cb2 100644 --- a/crates/ruvllm/src/claude_flow/agent_router.rs +++ b/crates/ruvllm/src/claude_flow/agent_router.rs @@ -3,10 +3,10 @@ //! Routes tasks to optimal agent types using RuvLTRA embeddings and SONA learning. use super::{ClaudeFlowAgent, ClaudeFlowTask}; -use crate::sona::{SonaIntegration, SonaConfig, Trajectory, RoutingRecommendation}; +use crate::sona::{RoutingRecommendation, SonaConfig, SonaIntegration, Trajectory}; +use parking_lot::RwLock; use std::collections::HashMap; use std::sync::Arc; -use parking_lot::RwLock; use serde::{Deserialize, Serialize}; @@ -163,8 +163,10 @@ impl AgentRouter { confidence, alternatives, task_type, - reasoning: format!("Keyword match: {} keywords matched for {:?}", - primary_score as usize, primary_agent), + reasoning: format!( + "Keyword match: {} keywords matched for {:?}", + primary_score as usize, primary_agent + ), learned_patterns: 0, } } @@ -186,8 +188,10 @@ impl AgentRouter { confidence: rec.confidence, alternatives: vec![], task_type, - reasoning: format!("SONA pattern match: {} patterns, avg quality {:.2}", - rec.based_on_patterns, rec.average_quality), + reasoning: format!( + "SONA pattern match: {} patterns, avg quality {:.2}", + rec.based_on_patterns, rec.average_quality + ), learned_patterns: rec.based_on_patterns, } } @@ -198,11 +202,17 @@ impl AgentRouter { ClaudeFlowTask::Testing } else if lower.contains("review") || lower.contains("audit") { ClaudeFlowTask::CodeReview - } else if lower.contains("research") || lower.contains("analyze") || lower.contains("investigate") { + } else if lower.contains("research") + || lower.contains("analyze") + || lower.contains("investigate") + { ClaudeFlowTask::Research } else if lower.contains("security") || lower.contains("vulnerability") { ClaudeFlowTask::Security - } else if lower.contains("performance") || lower.contains("optimize") || lower.contains("benchmark") { + } else if lower.contains("performance") + || lower.contains("optimize") + || lower.contains("benchmark") + { ClaudeFlowTask::Performance } else if lower.contains("architecture") || lower.contains("design") { ClaudeFlowTask::Architecture @@ -218,7 +228,13 @@ impl AgentRouter { } /// Record feedback for learning - pub fn record_feedback(&mut self, task: &str, embedding: &[f32], agent_used: AgentType, success: bool) { + pub fn record_feedback( + &mut self, + task: &str, + embedding: &[f32], + agent_used: AgentType, + success: bool, + ) { if success { self.successful_routings += 1; } @@ -282,7 +298,13 @@ mod tests { let router = AgentRouter::new(config); assert_eq!(router.classify_task("write tests"), ClaudeFlowTask::Testing); - assert_eq!(router.classify_task("review code"), ClaudeFlowTask::CodeReview); - assert_eq!(router.classify_task("optimize performance"), ClaudeFlowTask::Performance); + assert_eq!( + router.classify_task("review code"), + ClaudeFlowTask::CodeReview + ); + assert_eq!( + router.classify_task("optimize performance"), + ClaudeFlowTask::Performance + ); } } diff --git a/crates/ruvllm/src/claude_flow/claude_integration.rs b/crates/ruvllm/src/claude_flow/claude_integration.rs index 737b1d0bf..15c2791b1 100644 --- a/crates/ruvllm/src/claude_flow/claude_integration.rs +++ b/crates/ruvllm/src/claude_flow/claude_integration.rs @@ -26,11 +26,11 @@ //! +-------------------+ +-------------------+ //! ``` +use parking_lot::RwLock; +use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::sync::Arc; use std::time::{Duration, Instant}; -use parking_lot::RwLock; -use serde::{Deserialize, Serialize}; use tokio::sync::mpsc; use super::{AgentType, ClaudeFlowAgent, ClaudeFlowTask}; @@ -174,15 +174,18 @@ impl Message { /// Estimate token count for this message pub fn estimate_tokens(&self) -> usize { - self.content.iter().map(|block| { - match block { - ContentBlock::Text { text } => text.len() / 4, // ~4 chars per token - ContentBlock::ToolUse { input, .. } => { - input.to_string().len() / 4 + 50 // overhead for tool structure + self.content + .iter() + .map(|block| { + match block { + ContentBlock::Text { text } => text.len() / 4, // ~4 chars per token + ContentBlock::ToolUse { input, .. } => { + input.to_string().len() / 4 + 50 // overhead for tool structure + } + ContentBlock::ToolResult { content, .. } => content.len() / 4 + 20, } - ContentBlock::ToolResult { content, .. } => content.len() / 4 + 20, - } - }).sum() + }) + .sum() } } @@ -267,10 +270,7 @@ pub enum StreamEvent { /// Token generated Token(StreamToken), /// Content block completed - ContentBlockComplete { - index: usize, - content: ContentBlock, - }, + ContentBlockComplete { index: usize, content: ContentBlock }, /// Stream completed Complete { usage: UsageStats, @@ -278,10 +278,7 @@ pub enum StreamEvent { total_latency_ms: u64, }, /// Error occurred - Error { - message: String, - is_retryable: bool, - }, + Error { message: String, is_retryable: bool }, } /// Quality monitoring for streaming responses @@ -365,11 +362,7 @@ pub struct ResponseStreamer { impl ResponseStreamer { /// Create new response streamer - pub fn new( - request_id: String, - model: ClaudeModel, - sender: mpsc::Sender, - ) -> Self { + pub fn new(request_id: String, model: ClaudeModel, sender: mpsc::Sender) -> Self { Self { request_id: request_id.clone(), model, @@ -385,7 +378,9 @@ impl ResponseStreamer { /// Process incoming token pub async fn process_token(&mut self, text: String, quality_score: Option) -> Result<()> { if self.is_complete { - return Err(RuvLLMError::InvalidOperation("Stream already complete".to_string())); + return Err(RuvLLMError::InvalidOperation( + "Stream already complete".to_string(), + )); } let token = StreamToken { @@ -424,7 +419,9 @@ impl ResponseStreamer { total_latency_ms: self.start_time.elapsed().as_millis() as u64, }) .await - .map_err(|e| RuvLLMError::InvalidOperation(format!("Failed to send complete: {}", e)))?; + .map_err(|e| { + RuvLLMError::InvalidOperation(format!("Failed to send complete: {}", e)) + })?; Ok(()) } @@ -900,7 +897,8 @@ impl AgentCoordinator { ) -> Result { let start_time = Instant::now(); let mut step_results: HashMap = HashMap::new(); - let mut completed_steps: std::collections::HashSet = std::collections::HashSet::new(); + let mut completed_steps: std::collections::HashSet = + std::collections::HashSet::new(); // Build dependency graph let mut pending_steps: Vec<&WorkflowStep> = steps.iter().collect(); @@ -943,7 +941,7 @@ impl AgentCoordinator { response: Some(format!("Completed: {}", step.task)), duration: step_start.elapsed(), tokens_used: 500, // Mock value - cost: 0.001, // Mock value + cost: 0.001, // Mock value success: true, error: None, }; @@ -1069,7 +1067,10 @@ impl CostEstimator { /// Record actual usage pub fn record_usage(&mut self, model: ClaudeModel, usage: &UsageStats) { - let entry = self.usage_by_model.entry(model).or_insert(UsageStats::default()); + let entry = self + .usage_by_model + .entry(model) + .or_insert(UsageStats::default()); entry.input_tokens += usage.input_tokens; entry.output_tokens += usage.output_tokens; } @@ -1241,7 +1242,10 @@ mod tests { // Add many messages for i in 0..20 { - window.add_message(Message::user(format!("Message {} with some content to add tokens", i))); + window.add_message(Message::user(format!( + "Message {} with some content to add tokens", + i + ))); } // Window should have compressed @@ -1278,8 +1282,12 @@ mod tests { fn test_agent_coordinator() { let coordinator = AgentCoordinator::new(ClaudeModel::Sonnet, 10); - coordinator.spawn_agent("agent-1".to_string(), AgentType::Coder).unwrap(); - coordinator.spawn_agent("agent-2".to_string(), AgentType::Researcher).unwrap(); + coordinator + .spawn_agent("agent-1".to_string(), AgentType::Coder) + .unwrap(); + coordinator + .spawn_agent("agent-2".to_string(), AgentType::Researcher) + .unwrap(); assert_eq!(coordinator.total_agent_count(), 2); diff --git a/crates/ruvllm/src/claude_flow/flow_optimizer.rs b/crates/ruvllm/src/claude_flow/flow_optimizer.rs index b0466db35..e70140b4c 100644 --- a/crates/ruvllm/src/claude_flow/flow_optimizer.rs +++ b/crates/ruvllm/src/claude_flow/flow_optimizer.rs @@ -2,9 +2,9 @@ //! //! Optimizes RuvLTRA for Claude Flow workflows with SONA pretraining. -use super::{AgentRouter, TaskClassifier, ClaudeFlowAgent, ClaudeFlowTask}; -use crate::sona::{SonaConfig, SonaStats}; +use super::{AgentRouter, ClaudeFlowAgent, ClaudeFlowTask, TaskClassifier}; use crate::models::RuvLtraConfig; +use crate::sona::{SonaConfig, SonaStats}; use std::collections::HashMap; /// Optimization configuration @@ -116,7 +116,13 @@ impl FlowOptimizer { } /// Train on a sample task - pub fn train_sample(&mut self, task: &str, embedding: &[f32], correct_agent: ClaudeFlowAgent, success: bool) { + pub fn train_sample( + &mut self, + task: &str, + embedding: &[f32], + correct_agent: ClaudeFlowAgent, + success: bool, + ) { self.samples_processed += 1; // Route the task @@ -124,7 +130,8 @@ impl FlowOptimizer { // Record feedback let agent_type = correct_agent.into(); - self.router.record_feedback(task, embedding, agent_type, success); + self.router + .record_feedback(task, embedding, agent_type, success); } /// Train on batch of samples @@ -169,7 +176,9 @@ impl FlowOptimizer { OptimizationResult { baseline_accuracy: baseline.routing_accuracy, optimized_accuracy: current_accuracy, - improvement_pct: ((current_accuracy - baseline.routing_accuracy) / baseline.routing_accuracy.max(0.01)) * 100.0, + improvement_pct: ((current_accuracy - baseline.routing_accuracy) + / baseline.routing_accuracy.max(0.01)) + * 100.0, patterns_learned: sona_stats.patterns_learned, task_performance, memory_reduction_pct: memory_reduction, @@ -187,7 +196,10 @@ impl FlowOptimizer { } } - fn generate_use_case_samples(&self, use_case: ClaudeFlowTask) -> Vec<(String, Vec, ClaudeFlowAgent, bool)> { + fn generate_use_case_samples( + &self, + use_case: ClaudeFlowTask, + ) -> Vec<(String, Vec, ClaudeFlowAgent, bool)> { let mut samples = Vec::new(); let (tasks, agent) = match use_case { @@ -260,7 +272,11 @@ impl FlowOptimizer { } /// Route a task to optimal agent - pub fn route_task(&mut self, description: &str, embedding: Option<&[f32]>) -> super::agent_router::RoutingDecision { + pub fn route_task( + &mut self, + description: &str, + embedding: Option<&[f32]>, + ) -> super::agent_router::RoutingDecision { self.router.route(description, embedding) } } @@ -294,6 +310,9 @@ mod tests { let optimizer = FlowOptimizer::new(config); let result = optimizer.classify_task("implement a caching layer in Rust"); - assert_eq!(result.task_type, super::super::task_classifier::TaskType::Code); + assert_eq!( + result.task_type, + super::super::task_classifier::TaskType::Code + ); } } diff --git a/crates/ruvllm/src/claude_flow/hnsw_router.rs b/crates/ruvllm/src/claude_flow/hnsw_router.rs index b6e1951bd..0fd4e7271 100644 --- a/crates/ruvllm/src/claude_flow/hnsw_router.rs +++ b/crates/ruvllm/src/claude_flow/hnsw_router.rs @@ -960,8 +960,7 @@ impl HybridRouter { }; // If HNSW has high confidence, prefer it - if hnsw_result.confidence > self.min_hnsw_confidence - && hnsw_result.patterns_considered >= 3 + if hnsw_result.confidence > self.min_hnsw_confidence && hnsw_result.patterns_considered >= 3 { return Ok(hnsw_result.into()); } @@ -1083,12 +1082,8 @@ mod tests { ClaudeFlowTask::Testing }; - let mut pattern = TaskPattern::new( - embedding, - agent_type, - task_type, - format!("task {}", i), - ); + let mut pattern = + TaskPattern::new(embedding, agent_type, task_type, format!("task {}", i)); pattern.usage_count = 10; pattern.success_count = 8; pattern.success_rate = 0.8; @@ -1159,7 +1154,10 @@ mod tests { let stats = router.stats(); assert_eq!(stats.total_patterns, 1); - assert_eq!(*stats.patterns_by_agent.get(&AgentType::Researcher).unwrap(), 1); + assert_eq!( + *stats.patterns_by_agent.get(&AgentType::Researcher).unwrap(), + 1 + ); } #[test] diff --git a/crates/ruvllm/src/claude_flow/hooks_integration.rs b/crates/ruvllm/src/claude_flow/hooks_integration.rs index 45c339e1d..d2a78ad78 100644 --- a/crates/ruvllm/src/claude_flow/hooks_integration.rs +++ b/crates/ruvllm/src/claude_flow/hooks_integration.rs @@ -44,21 +44,20 @@ use crate::{ ModelRouter, ReasoningBankConfig, ReasoningBankIntegration, TaskComplexityAnalyzer, }, context::{ - AgenticMemory, AgenticMemoryConfig, ClaudeFlowMemoryBridge, ClaudeFlowBridgeConfig, - IntelligentContextManager, ContextManagerConfig, SemanticToolCache, SemanticCacheConfig, + AgenticMemory, AgenticMemoryConfig, ClaudeFlowBridgeConfig, ClaudeFlowMemoryBridge, + ContextManagerConfig, IntelligentContextManager, SemanticCacheConfig, SemanticToolCache, }, quality::{ - QualityScoringEngine, ScoringConfig, QualityMetrics, CoherenceValidator, CoherenceConfig, - DiversityAnalyzer, DiversityConfig, + CoherenceConfig, CoherenceValidator, DiversityAnalyzer, DiversityConfig, QualityMetrics, + QualityScoringEngine, ScoringConfig, }, reasoning_bank::{ - PatternConsolidator, ConsolidationConfig, PatternStore, PatternStoreConfig, - TrajectoryRecorder, Trajectory, TrajectoryStep, StepOutcome, - Verdict, RootCause, MemoryDistiller, DistillationConfig, - Pattern, PatternCategory, + ConsolidationConfig, DistillationConfig, MemoryDistiller, Pattern, PatternCategory, + PatternConsolidator, PatternStore, PatternStoreConfig, RootCause, StepOutcome, Trajectory, + TrajectoryRecorder, TrajectoryStep, Verdict, }, reflection::{ - ErrorPatternLearner, ErrorPatternLearnerConfig, ConfidenceChecker, ConfidenceConfig, + ConfidenceChecker, ConfidenceConfig, ErrorPatternLearner, ErrorPatternLearnerConfig, }, Result, RuvLLMError, }; @@ -387,38 +386,42 @@ impl HooksIntegration { }; // Initialize pattern learning if enabled - let (reasoning_bank, pattern_store, pattern_consolidator) = if config.enable_pattern_learning { - let rb_config = ReasoningBankConfig::default(); - let ps_config = PatternStoreConfig { - embedding_dim: config.embedding_dim, - ..Default::default() - }; - let pc_config = ConsolidationConfig::default(); + let (reasoning_bank, pattern_store, pattern_consolidator) = + if config.enable_pattern_learning { + let rb_config = ReasoningBankConfig::default(); + let ps_config = PatternStoreConfig { + embedding_dim: config.embedding_dim, + ..Default::default() + }; + let pc_config = ConsolidationConfig::default(); - ( - Some(ReasoningBankIntegration::new(rb_config)), - Some(PatternStore::new(ps_config)?), - Some(PatternConsolidator::new(pc_config)), - ) - } else { - (None, None, None) - }; + ( + Some(ReasoningBankIntegration::new(rb_config)), + Some(PatternStore::new(ps_config)?), + Some(PatternConsolidator::new(pc_config)), + ) + } else { + (None, None, None) + }; // Initialize quality scoring if enabled - let (scoring_engine, coherence_validator, diversity_analyzer) = if config.enable_quality_scoring { - ( - Some(QualityScoringEngine::new()), - Some(CoherenceValidator::new(CoherenceConfig::default())), - Some(DiversityAnalyzer::new(DiversityConfig::default())), - ) - } else { - (None, None, None) - }; + let (scoring_engine, coherence_validator, diversity_analyzer) = + if config.enable_quality_scoring { + ( + Some(QualityScoringEngine::new()), + Some(CoherenceValidator::new(CoherenceConfig::default())), + Some(DiversityAnalyzer::new(DiversityConfig::default())), + ) + } else { + (None, None, None) + }; // Initialize error learning if enabled let (error_learner, confidence_checker) = if config.enable_error_learning { ( - Some(ErrorPatternLearner::new(ErrorPatternLearnerConfig::default())), + Some(ErrorPatternLearner::new( + ErrorPatternLearnerConfig::default(), + )), Some(ConfidenceChecker::new(ConfidenceConfig::default())), ) } else { @@ -426,16 +429,19 @@ impl HooksIntegration { }; // Initialize memory systems if enabled - let (agentic_memory, context_manager, semantic_cache, memory_bridge) = if config.enable_memory_bridge { - ( - AgenticMemory::new(AgenticMemoryConfig::default()).ok(), - IntelligentContextManager::new(ContextManagerConfig::default()).ok(), - SemanticToolCache::new(SemanticCacheConfig::default()).ok(), - Some(ClaudeFlowMemoryBridge::new(ClaudeFlowBridgeConfig::default())), - ) - } else { - (None, None, None, None) - }; + let (agentic_memory, context_manager, semantic_cache, memory_bridge) = + if config.enable_memory_bridge { + ( + AgenticMemory::new(AgenticMemoryConfig::default()).ok(), + IntelligentContextManager::new(ContextManagerConfig::default()).ok(), + SemanticToolCache::new(SemanticCacheConfig::default()).ok(), + Some(ClaudeFlowMemoryBridge::new( + ClaudeFlowBridgeConfig::default(), + )), + ) + } else { + (None, None, None, None) + }; let session_state = SessionState { session_id: Uuid::new_v4().to_string(), @@ -483,7 +489,8 @@ impl HooksIntegration { ); // Check for Agent Booster (simple transforms that skip LLM) - let (agent_booster_available, agent_booster_intent) = self.check_agent_booster(&input.description); + let (agent_booster_available, agent_booster_intent) = + self.check_agent_booster(&input.description); // Get agent recommendation from HNSW if available let (recommended_agent, confidence, similar_patterns, suggested_approach) = @@ -494,21 +501,30 @@ impl HooksIntegration { match router.route_by_similarity(&embedding) { Ok(result) => { // Get similar patterns through a separate search - let patterns: Vec = router.search_similar(&embedding, 3) + let patterns: Vec = router + .search_similar(&embedding, 3) .ok() - .map(|results| results.iter().map(|(pattern, similarity)| PatternMatch { - description: format!("{:?}", pattern.task_type), - agent: format!("{:?}", pattern.agent_type), - similarity: *similarity, - quality: pattern.success_rate, - }).collect()) + .map(|results| { + results + .iter() + .map(|(pattern, similarity)| PatternMatch { + description: format!("{:?}", pattern.task_type), + agent: format!("{:?}", pattern.agent_type), + similarity: *similarity, + quality: pattern.success_rate, + }) + .collect() + }) .unwrap_or_default(); let approach = if !patterns.is_empty() { Some(format!( "Based on {} similar successful tasks, consider: {}", patterns.len(), - patterns.first().map(|p| &p.description).unwrap_or(&String::new()) + patterns + .first() + .map(|p| &p.description) + .unwrap_or(&String::new()) )) } else { None @@ -590,7 +606,9 @@ impl HooksIntegration { // Learn error pattern if failed let mut error_learned = false; if !input.success { - if let (Some(ref mut learner), Some(error_msg)) = (&mut self.error_learner, &input.error_message) { + if let (Some(ref mut learner), Some(error_msg)) = + (&mut self.error_learner, &input.error_message) + { // Record error for learning learner.record_error(error_msg); error_learned = true; @@ -606,7 +624,8 @@ impl HooksIntegration { // Update running average quality let n = state.tasks_completed as f32; - state.avg_quality = ((n - 1.0) * state.avg_quality + quality_assessment.overall_score) / n; + state.avg_quality = + ((n - 1.0) * state.avg_quality + quality_assessment.overall_score) / n; } // Check if consolidation needed @@ -657,14 +676,20 @@ impl HooksIntegration { let query = format!("{} {} {}", input.operation, ext, input.file_path); let embedding = self.create_simple_embedding(&query); - router.search_similar(&embedding, 3) + router + .search_similar(&embedding, 3) .ok() - .map(|results| results.iter().map(|(pattern, similarity)| PatternMatch { - description: format!("{:?}", pattern.task_type), - agent: format!("{:?}", pattern.agent_type), - similarity: *similarity, - quality: pattern.success_rate, - }).collect()) + .map(|results| { + results + .iter() + .map(|(pattern, similarity)| PatternMatch { + description: format!("{:?}", pattern.task_type), + agent: format!("{:?}", pattern.agent_type), + similarity: *similarity, + quality: pattern.success_rate, + }) + .collect() + }) .unwrap_or_default() } else { Vec::new() @@ -677,7 +702,8 @@ impl HooksIntegration { "create" => "low", "update" => "low", _ => "medium", - }.to_string(); + } + .to_string(); Ok(PreEditResult { recommended_agent, @@ -699,7 +725,8 @@ impl HooksIntegration { let ext = input.file_path.rsplit('.').next().unwrap_or(""); let pattern_desc = format!("edit {} file: {}", ext, input.file_path); // Get embedding before mutable borrow - let embedding = create_simple_embedding_static(&pattern_desc, self.config.embedding_dim); + let embedding = + create_simple_embedding_static(&pattern_desc, self.config.embedding_dim); if let Some(ref mut store) = self.pattern_store { let pattern = Pattern::new( @@ -733,8 +760,14 @@ impl HooksIntegration { } /// Session start hook: initialize and optionally restore state - pub fn session_start(&mut self, session_id: Option<&str>, restore_latest: bool) -> Result { - let session_id = session_id.unwrap_or(&Uuid::new_v4().to_string()).to_string(); + pub fn session_start( + &mut self, + session_id: Option<&str>, + restore_latest: bool, + ) -> Result { + let session_id = session_id + .unwrap_or(&Uuid::new_v4().to_string()) + .to_string(); // Initialize new session state let state = SessionState { @@ -759,17 +792,23 @@ impl HooksIntegration { } /// Session end hook: persist state and distill patterns - pub fn session_end(&mut self, export_metrics: bool, persist_state: bool) -> Result { + pub fn session_end( + &mut self, + export_metrics: bool, + persist_state: bool, + ) -> Result { let state = self.session_state.read().clone(); // Complete any active trajectories - let incomplete_trajectories: Vec = self.active_trajectories + let incomplete_trajectories: Vec = self + .active_trajectories .iter() .map(|r| r.key().clone()) .collect(); for task_id in incomplete_trajectories { - let _ = self.complete_trajectory(&task_id, false, "unknown", 0.5, Some("Session ended")); + let _ = + self.complete_trajectory(&task_id, false, "unknown", 0.5, Some("Session ended")); } // Consolidate patterns before ending @@ -861,7 +900,10 @@ impl HooksIntegration { (false, None) } - fn fallback_routing(&self, description: &str) -> (String, f32, Vec, Option) { + fn fallback_routing( + &self, + description: &str, + ) -> (String, f32, Vec, Option) { let desc_lower = description.to_lowercase(); // Simple keyword-based routing @@ -894,7 +936,9 @@ impl HooksIntegration { let mut embedding = vec![0.0f32; self.config.embedding_dim]; for (i, word) in text.split_whitespace().enumerate() { - let hash = word.bytes().fold(0u64, |acc, b| acc.wrapping_mul(31).wrapping_add(b as u64)); + let hash = word + .bytes() + .fold(0u64, |acc, b| acc.wrapping_mul(31).wrapping_add(b as u64)); let idx = (hash % self.config.embedding_dim as u64) as usize; embedding[idx] += 1.0 / (i + 1) as f32; } @@ -934,7 +978,8 @@ impl HooksIntegration { started_at: Utc::now(), }; - self.active_trajectories.insert(task_id.to_string(), trajectory); + self.active_trajectories + .insert(task_id.to_string(), trajectory); // Update session state let mut state = self.session_state.write(); @@ -955,16 +1000,13 @@ impl HooksIntegration { // Store pattern if successful and high quality if success && quality >= self.config.min_pattern_confidence { // Get embedding before mutable borrow - let embedding = create_simple_embedding_static(&traj.description, self.config.embedding_dim); + let embedding = + create_simple_embedding_static(&traj.description, self.config.embedding_dim); if let Some(ref mut store) = self.pattern_store { - let pattern = Pattern::new( - embedding, - PatternCategory::General, - quality, - ) - .with_lesson(traj.description.clone()) - .with_action(format!("Task completed by {}", agent)); + let pattern = Pattern::new(embedding, PatternCategory::General, quality) + .with_lesson(traj.description.clone()) + .with_action(format!("Task completed by {}", agent)); if store.store_pattern(pattern).is_ok() { *self.patterns_added.write() += 1; @@ -1031,7 +1073,9 @@ fn create_simple_embedding_static(text: &str, embedding_dim: usize) -> Vec let mut embedding = vec![0.0f32; embedding_dim]; for (i, word) in text.split_whitespace().enumerate() { - let hash = word.bytes().fold(0u64, |acc, b| acc.wrapping_mul(31).wrapping_add(b as u64)); + let hash = word + .bytes() + .fold(0u64, |acc, b| acc.wrapping_mul(31).wrapping_add(b as u64)); let idx = (hash % embedding_dim as u64) as usize; embedding[idx] += 1.0 / (i + 1) as f32; } @@ -1063,7 +1107,11 @@ mod tests { if let Err(ref e) = hooks { eprintln!("HooksIntegration creation error: {:?}", e); } - assert!(hooks.is_ok(), "Failed to create HooksIntegration: {:?}", hooks.err()); + assert!( + hooks.is_ok(), + "Failed to create HooksIntegration: {:?}", + hooks.err() + ); } #[test] diff --git a/crates/ruvllm/src/claude_flow/mod.rs b/crates/ruvllm/src/claude_flow/mod.rs index 4bff2b2ad..8b911d6b2 100644 --- a/crates/ruvllm/src/claude_flow/mod.rs +++ b/crates/ruvllm/src/claude_flow/mod.rs @@ -207,43 +207,64 @@ pub use reasoning_bank::{ RoutingRecommendation, Trajectory, TrajectoryStep, Verdict, }; pub use task_classifier::{ClassificationResult, TaskClassifier, TaskType}; -pub use task_generator::{ - seed_rng, GeneratedTask, TaskCategory, TaskComplexity, TaskGenerator, -}; +pub use task_generator::{seed_rng, GeneratedTask, TaskCategory, TaskComplexity, TaskGenerator}; // Hooks Integration exports (NEW v2.3) pub use hooks_integration::{ - HooksIntegration, HooksConfig, - PreTaskInput, PreTaskResult, PostTaskInput, PostTaskResult, - PreEditInput, PreEditResult, PostEditInput, PostEditResult, - SessionState, SessionEndResult, SessionMetrics, - PatternMatch, QualityAssessment, LearningMetrics, + HooksConfig, HooksIntegration, LearningMetrics, PatternMatch, PostEditInput, PostEditResult, + PostTaskInput, PostTaskResult, PreEditInput, PreEditResult, PreTaskInput, PreTaskResult, + QualityAssessment, SessionEndResult, SessionMetrics, SessionState, }; // Claude API Integration exports (NEW) pub use claude_integration::{ - // Core types - ClaudeModel, MessageRole, ContentBlock, Message, ClaudeRequest, ClaudeResponse, UsageStats, - // Streaming - StreamToken, StreamEvent, QualityMonitor, ResponseStreamer, StreamStats, - // Context management - ContextWindow, ContextManager, + AgentContext, + AgentCoordinator, // Multi-agent coordination - AgentState, AgentContext, WorkflowStep, WorkflowResult, StepResult, - AgentCoordinator, CoordinatorStats, + AgentState, + // Core types + ClaudeModel, + ClaudeRequest, + ClaudeResponse, + ContentBlock, + ContextManager, + // Context management + ContextWindow, + CoordinatorStats, // Cost and latency tracking - CostEstimator, LatencyTracker, LatencySample, LatencyStats, + CostEstimator, + LatencySample, + LatencyStats, + LatencyTracker, + Message, + MessageRole, + QualityMonitor, + ResponseStreamer, + StepResult, + StreamEvent, + StreamStats, + // Streaming + StreamToken, + UsageStats, + WorkflowResult, + WorkflowStep, }; // Model Router exports (NEW) pub use model_router::{ + AnalyzerStats, // Complexity analysis - ComplexityFactors, ComplexityWeights, ComplexityScore, - TaskComplexityAnalyzer, AnalyzerStats, - // Model selection - SelectionCriteria, ModelRoutingDecision, ModelSelector, SelectorStats, + ComplexityFactors, + ComplexityScore, + ComplexityWeights, // Integrated router ModelRouter, + ModelRoutingDecision, + ModelSelector, + // Model selection + SelectionCriteria, + SelectorStats, + TaskComplexityAnalyzer, }; /// Claude Flow agent types supported by RuvLTRA routing @@ -307,16 +328,87 @@ impl ClaudeFlowAgent { /// Get typical task keywords for this agent pub fn keywords(&self) -> &'static [&'static str] { match self { - Self::Coder => &["implement", "code", "write", "create", "build", "develop", "function", "class"], - Self::Researcher => &["research", "analyze", "investigate", "explore", "find", "search", "understand"], - Self::Tester => &["test", "verify", "validate", "check", "assert", "coverage", "unit", "integration"], - Self::Reviewer => &["review", "audit", "inspect", "quality", "lint", "style", "best practice"], - Self::Architect => &["design", "architecture", "structure", "pattern", "system", "scalable", "modular"], - Self::SecurityAuditor => &["security", "vulnerability", "cve", "injection", "auth", "encrypt", "safe"], - Self::PerformanceEngineer => &["performance", "optimize", "speed", "memory", "benchmark", "profile", "latency"], - Self::MlDeveloper => &["model", "train", "neural", "ml", "ai", "embedding", "inference", "tensor"], - Self::BackendDev => &["api", "endpoint", "database", "server", "rest", "graphql", "query"], - Self::CicdEngineer => &["ci", "cd", "pipeline", "deploy", "workflow", "action", "build", "release"], + Self::Coder => &[ + "implement", + "code", + "write", + "create", + "build", + "develop", + "function", + "class", + ], + Self::Researcher => &[ + "research", + "analyze", + "investigate", + "explore", + "find", + "search", + "understand", + ], + Self::Tester => &[ + "test", + "verify", + "validate", + "check", + "assert", + "coverage", + "unit", + "integration", + ], + Self::Reviewer => &[ + "review", + "audit", + "inspect", + "quality", + "lint", + "style", + "best practice", + ], + Self::Architect => &[ + "design", + "architecture", + "structure", + "pattern", + "system", + "scalable", + "modular", + ], + Self::SecurityAuditor => &[ + "security", + "vulnerability", + "cve", + "injection", + "auth", + "encrypt", + "safe", + ], + Self::PerformanceEngineer => &[ + "performance", + "optimize", + "speed", + "memory", + "benchmark", + "profile", + "latency", + ], + Self::MlDeveloper => &[ + "model", + "train", + "neural", + "ml", + "ai", + "embedding", + "inference", + "tensor", + ], + Self::BackendDev => &[ + "api", "endpoint", "database", "server", "rest", "graphql", "query", + ], + Self::CicdEngineer => &[ + "ci", "cd", "pipeline", "deploy", "workflow", "action", "build", "release", + ], } } } diff --git a/crates/ruvllm/src/claude_flow/model_router.rs b/crates/ruvllm/src/claude_flow/model_router.rs index 2d18f47af..db2da3870 100644 --- a/crates/ruvllm/src/claude_flow/model_router.rs +++ b/crates/ruvllm/src/claude_flow/model_router.rs @@ -29,9 +29,9 @@ //! +-------------------+ +-------------------+ //! ``` +use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::time::{Duration, Instant}; -use serde::{Deserialize, Serialize}; use super::claude_integration::ClaudeModel; use super::{AgentType, ClaudeFlowAgent, ClaudeFlowTask}; @@ -413,7 +413,8 @@ impl TaskComplexityAnalyzer { }; // Additional factors - let factor = if contains_ci(task_bytes, b"architecture") || contains_ci(task_bytes, b"design") + let factor = if contains_ci(task_bytes, b"architecture") + || contains_ci(task_bytes, b"design") { 3.0 } else if contains_ci(task_bytes, b"test") { @@ -447,7 +448,8 @@ impl TaskComplexityAnalyzer { } // Complex reasoning - if task.contains("distributed") || task.contains("concurrent") || task.contains("parallel") { + if task.contains("distributed") || task.contains("concurrent") || task.contains("parallel") + { depth += 0.3; } @@ -637,11 +639,7 @@ impl TaskComplexityAnalyzer { reasons.push("balanced complexity factors".to_string()); } - format!( - "Recommended {} due to: {}", - model, - reasons.join(", ") - ) + format!("Recommended {} due to: {}", model, reasons.join(", ")) } /// Record feedback for learning @@ -661,7 +659,8 @@ impl TaskComplexityAnalyzer { /// Get accuracy statistics pub fn accuracy_stats(&self) -> AnalyzerStats { - let with_feedback: Vec<_> = self.accuracy_history + let with_feedback: Vec<_> = self + .accuracy_history .iter() .filter(|r| r.actual.is_some()) .collect(); @@ -974,7 +973,8 @@ impl ModelSelector { /// Get selector statistics pub fn stats(&self) -> SelectorStats { - let with_outcome: Vec<_> = self.selection_history + let with_outcome: Vec<_> = self + .selection_history .iter() .filter(|r| r.success.is_some()) .collect(); @@ -1104,10 +1104,8 @@ impl ModelRouter { if let Some(&model) = self.agent_overrides.get(&agent) { let mut decision = self.selector.select_model(task); decision.model = model; - decision.reasoning = format!( - "Agent type {:?} override: {}", - agent, decision.reasoning - ); + decision.reasoning = + format!("Agent type {:?} override: {}", agent, decision.reasoning); return decision; } } @@ -1116,10 +1114,8 @@ impl ModelRouter { if let Some(&model) = self.task_overrides.get(&task_t) { let mut decision = self.selector.select_model(task); decision.model = model; - decision.reasoning = format!( - "Task type {:?} override: {}", - task_t, decision.reasoning - ); + decision.reasoning = + format!("Task type {:?} override: {}", task_t, decision.reasoning); return decision; } } @@ -1193,7 +1189,7 @@ mod tests { let mut analyzer = TaskComplexityAnalyzer::new(); let score = analyzer.analyze( "Design and implement a distributed authentication system with OAuth2, JWT tokens, \ - and comprehensive security audit for vulnerabilities" + and comprehensive security audit for vulnerabilities", ); assert!(score.overall > 0.7); @@ -1204,9 +1200,8 @@ mod tests { #[test] fn test_complexity_analyzer_moderate_task() { let mut analyzer = TaskComplexityAnalyzer::new(); - let score = analyzer.analyze( - "Implement a REST API endpoint for user registration with input validation" - ); + let score = analyzer + .analyze("Implement a REST API endpoint for user registration with input validation"); assert!(score.overall >= 0.35); assert!(score.overall < 0.7); @@ -1223,7 +1218,7 @@ mod tests { // Complex task let decision = selector.select_model( - "Design microservices architecture with distributed tracing and security audit" + "Design microservices architecture with distributed tracing and security audit", ); assert_eq!(decision.model, ClaudeModel::Opus); } diff --git a/crates/ruvllm/src/claude_flow/pretrain_pipeline.rs b/crates/ruvllm/src/claude_flow/pretrain_pipeline.rs index 7f127d8b6..86e11b021 100644 --- a/crates/ruvllm/src/claude_flow/pretrain_pipeline.rs +++ b/crates/ruvllm/src/claude_flow/pretrain_pipeline.rs @@ -30,14 +30,16 @@ //! pipeline.save_checkpoint("./checkpoints/claude_flow_v1.bin")?; //! ``` -use super::task_generator::{TaskGenerator, GeneratedTask, TaskCategory, TaskComplexity}; +use super::task_generator::{GeneratedTask, TaskCategory, TaskComplexity, TaskGenerator}; use super::{ClaudeFlowAgent, ClaudeFlowTask}; use crate::sona::{ - SonaConfig, SonaIntegration, Trajectory, RuvLtraPretrainConfig, RuvLtraPretrainer, - PretrainSample, SeedingResult, RoutingPretrainResult, + PretrainSample, RoutingPretrainResult, RuvLtraPretrainConfig, RuvLtraPretrainer, SeedingResult, + SonaConfig, SonaIntegration, Trajectory, }; use parking_lot::RwLock; -use ruvector_sona::{EwcConfig, EwcPlusPlus, LearnedPattern, PatternConfig, ReasoningBank, SonaEngine}; +use ruvector_sona::{ + EwcConfig, EwcPlusPlus, LearnedPattern, PatternConfig, ReasoningBank, SonaEngine, +}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::path::Path; @@ -58,7 +60,12 @@ pub enum Phase { } /// Static array of all phases for zero-allocation access -static ALL_PHASES: [Phase; 4] = [Phase::Bootstrap, Phase::Synthetic, Phase::Reinforce, Phase::Consolidate]; +static ALL_PHASES: [Phase; 4] = [ + Phase::Bootstrap, + Phase::Synthetic, + Phase::Reinforce, + Phase::Consolidate, +]; impl Phase { /// Get all phases in order @@ -533,7 +540,8 @@ impl ProgressTracker { /// Record patterns for phase pub fn record_patterns(&mut self, phase: Phase, count: usize) { - self.patterns_per_phase.insert(phase.name().to_string(), count); + self.patterns_per_phase + .insert(phase.name().to_string(), count); } } @@ -677,7 +685,8 @@ impl PretrainPipeline { }; let pretrainer = RuvLtraPretrainer::new(pretrain_config); - let curriculum = CurriculumScheduler::new(config.curriculum_stages, config.samples_per_stage); + let curriculum = + CurriculumScheduler::new(config.curriculum_stages, config.samples_per_stage); let quality_gate = QualityGate::new(config.quality_threshold); Self { @@ -706,7 +715,8 @@ impl PretrainPipeline { phase_results.push(phase_result); // Update overall progress - self.progress.set_overall_progress((phase_idx + 1) as f32 / total_phases as f32); + self.progress + .set_overall_progress((phase_idx + 1) as f32 / total_phases as f32); // Save checkpoint if enabled if self.config.enable_checkpoints { @@ -738,7 +748,10 @@ impl PretrainPipeline { quality_per_stage: (0..self.config.curriculum_stages) .map(|s| self.curriculum.stage_avg_quality(s)) .collect(), - samples_per_stage: vec![self.config.samples_per_stage; self.config.curriculum_stages], + samples_per_stage: vec![ + self.config.samples_per_stage; + self.config.curriculum_stages + ], }) } else { None @@ -836,7 +849,12 @@ impl PretrainPipeline { } self.samples_processed += 1; - self.progress.update(Phase::Bootstrap, samples_count, self.config.samples_per_phase as u64, total_quality / samples_count.max(1) as f32); + self.progress.update( + Phase::Bootstrap, + samples_count, + self.config.samples_per_phase as u64, + total_quality / samples_count.max(1) as f32, + ); } } @@ -847,7 +865,8 @@ impl PretrainPipeline { 0.0 }; - self.progress.record_patterns(Phase::Bootstrap, patterns_learned); + self.progress + .record_patterns(Phase::Bootstrap, patterns_learned); Ok((samples_count, patterns_learned, avg_quality)) } @@ -922,8 +941,7 @@ impl PretrainPipeline { { let _ = self.save_checkpoint(&format!( "{}/checkpoint_synthetic_{}.bin", - self.config.checkpoint_dir, - self.samples_processed + self.config.checkpoint_dir, self.samples_processed )); } @@ -945,7 +963,8 @@ impl PretrainPipeline { 0.0 }; - self.progress.record_patterns(Phase::Synthetic, result.patterns_learned); + self.progress + .record_patterns(Phase::Synthetic, result.patterns_learned); Ok((samples_count, result.patterns_learned, avg_quality)) } @@ -1006,7 +1025,11 @@ impl PretrainPipeline { } if self.config.verbose { - println!(" Replay {} complete, quality: {:.3}", replay_idx + 1, total_quality / samples_count.max(1) as f32); + println!( + " Replay {} complete, quality: {:.3}", + replay_idx + 1, + total_quality / samples_count.max(1) as f32 + ); } } @@ -1016,7 +1039,8 @@ impl PretrainPipeline { 0.0 }; - self.progress.record_patterns(Phase::Reinforce, patterns_learned); + self.progress + .record_patterns(Phase::Reinforce, patterns_learned); Ok((samples_count, patterns_learned, avg_quality)) } @@ -1066,7 +1090,8 @@ impl PretrainPipeline { patterns.len() as u64, avg_quality, ); - self.progress.record_patterns(Phase::Consolidate, consolidated_count); + self.progress + .record_patterns(Phase::Consolidate, consolidated_count); Ok((consolidated_count as u64, consolidated_count, avg_quality)) } diff --git a/crates/ruvllm/src/claude_flow/reasoning_bank.rs b/crates/ruvllm/src/claude_flow/reasoning_bank.rs index 15d65a15a..d183dae1b 100644 --- a/crates/ruvllm/src/claude_flow/reasoning_bank.rs +++ b/crates/ruvllm/src/claude_flow/reasoning_bank.rs @@ -141,7 +141,9 @@ impl Verdict { Verdict::Success { reason } => reason, Verdict::Failure { reason, .. } => reason, Verdict::Partial { reason, .. } => reason, - Verdict::RecoveredViaReflection { recovery_strategy, .. } => recovery_strategy, + Verdict::RecoveredViaReflection { + recovery_strategy, .. + } => recovery_strategy, } } @@ -612,8 +614,7 @@ impl ReasoningBankIntegration { } // Update running average quality let n = stats.total_trajectories as f32; - stats.avg_quality = - stats.avg_quality * (n - 1.0) / n + trajectory.quality_score / n; + stats.avg_quality = stats.avg_quality * (n - 1.0) / n + trajectory.quality_score / n; } // Add to buffer @@ -658,7 +659,10 @@ impl ReasoningBankIntegration { } // Check for auto-distillation - let count = self.trajectories_since_distill.fetch_add(1, Ordering::SeqCst) + 1; + let count = self + .trajectories_since_distill + .fetch_add(1, Ordering::SeqCst) + + 1; if self.config.auto_distill && count >= self.config.distill_interval as u64 { self.distill_patterns()?; self.trajectories_since_distill.store(0, Ordering::SeqCst); @@ -865,10 +869,7 @@ impl ReasoningBankIntegration { } // Filter out small clusters - clusters - .into_iter() - .filter(|c| c.len() >= 2) - .collect() + clusters.into_iter().filter(|c| c.len() >= 2).collect() } /// Cosine similarity between two vectors @@ -1044,7 +1045,10 @@ impl ReasoningBankIntegration { let to_remove: Vec = patterns .iter() .filter(|(_, p)| { - p.should_prune(self.config.min_pattern_quality, self.config.max_pattern_age_secs) + p.should_prune( + self.config.min_pattern_quality, + self.config.max_pattern_age_secs, + ) }) .map(|(id, _)| *id) .collect(); @@ -1198,8 +1202,7 @@ impl ReasoningBankIntegration { let mut pattern_map = self.patterns.write(); for pattern in patterns { let id = pattern.id.max(self.next_pattern_id.load(Ordering::SeqCst)); - self.next_pattern_id - .fetch_max(id + 1, Ordering::SeqCst); + self.next_pattern_id.fetch_max(id + 1, Ordering::SeqCst); pattern_map.insert(pattern.id, pattern); } diff --git a/crates/ruvllm/src/claude_flow/task_classifier.rs b/crates/ruvllm/src/claude_flow/task_classifier.rs index 41b5e63f0..85024a6b8 100644 --- a/crates/ruvllm/src/claude_flow/task_classifier.rs +++ b/crates/ruvllm/src/claude_flow/task_classifier.rs @@ -84,21 +84,45 @@ impl TaskClassifier { fn build_language_patterns() -> Vec<(String, Vec<&'static str>)> { vec![ - ("rust".to_string(), vec!["rust", "cargo", ".rs", "tokio", "async-std", "serde"]), - ("typescript".to_string(), vec!["typescript", "ts", ".tsx", "deno", "bun"]), - ("javascript".to_string(), vec!["javascript", "js", "node", "npm", "react", "vue"]), - ("python".to_string(), vec!["python", "pip", ".py", "django", "flask", "pytorch"]), + ( + "rust".to_string(), + vec!["rust", "cargo", ".rs", "tokio", "async-std", "serde"], + ), + ( + "typescript".to_string(), + vec!["typescript", "ts", ".tsx", "deno", "bun"], + ), + ( + "javascript".to_string(), + vec!["javascript", "js", "node", "npm", "react", "vue"], + ), + ( + "python".to_string(), + vec!["python", "pip", ".py", "django", "flask", "pytorch"], + ), ("go".to_string(), vec!["golang", "go ", ".go", "goroutine"]), ] } fn build_framework_patterns() -> Vec<(String, Vec<&'static str>)> { vec![ - ("react".to_string(), vec!["react", "jsx", "tsx", "next.js", "nextjs"]), - ("express".to_string(), vec!["express", "middleware", "router"]), - ("tokio".to_string(), vec!["tokio", "async", "await", "spawn"]), + ( + "react".to_string(), + vec!["react", "jsx", "tsx", "next.js", "nextjs"], + ), + ( + "express".to_string(), + vec!["express", "middleware", "router"], + ), + ( + "tokio".to_string(), + vec!["tokio", "async", "await", "spawn"], + ), ("actix".to_string(), vec!["actix", "actix-web"]), - ("jest".to_string(), vec!["jest", "describe", "it(", "expect("]), + ( + "jest".to_string(), + vec!["jest", "describe", "it(", "expect("], + ), ("pytest".to_string(), vec!["pytest", "test_", "fixture"]), ] } @@ -159,54 +183,117 @@ impl TaskClassifier { } fn score_code(&self, s: &str) -> f32 { - let keywords = ["implement", "create", "build", "code", "write", "function", "class", "module"]; + let keywords = [ + "implement", + "create", + "build", + "code", + "write", + "function", + "class", + "module", + ]; self.keyword_score(s, &keywords) } fn score_research(&self, s: &str) -> f32 { - let keywords = ["research", "analyze", "investigate", "explore", "find", "understand", "learn"]; + let keywords = [ + "research", + "analyze", + "investigate", + "explore", + "find", + "understand", + "learn", + ]; self.keyword_score(s, &keywords) } fn score_test(&self, s: &str) -> f32 { - let keywords = ["test", "verify", "validate", "assert", "coverage", "unit", "integration", "e2e"]; + let keywords = [ + "test", + "verify", + "validate", + "assert", + "coverage", + "unit", + "integration", + "e2e", + ]; self.keyword_score(s, &keywords) } fn score_review(&self, s: &str) -> f32 { - let keywords = ["review", "audit", "inspect", "check", "quality", "lint", "pr"]; + let keywords = [ + "review", "audit", "inspect", "check", "quality", "lint", "pr", + ]; self.keyword_score(s, &keywords) } fn score_docs(&self, s: &str) -> f32 { - let keywords = ["document", "readme", "api docs", "comment", "explain", "describe"]; + let keywords = [ + "document", "readme", "api docs", "comment", "explain", "describe", + ]; self.keyword_score(s, &keywords) } fn score_debug(&self, s: &str) -> f32 { - let keywords = ["debug", "fix", "error", "bug", "issue", "crash", "exception", "trace"]; + let keywords = [ + "debug", + "fix", + "error", + "bug", + "issue", + "crash", + "exception", + "trace", + ]; self.keyword_score(s, &keywords) } fn score_architecture(&self, s: &str) -> f32 { - let keywords = ["architecture", "design", "structure", "pattern", "system", "scalable", "modular"]; + let keywords = [ + "architecture", + "design", + "structure", + "pattern", + "system", + "scalable", + "modular", + ]; self.keyword_score(s, &keywords) } fn score_security(&self, s: &str) -> f32 { - let keywords = ["security", "vulnerability", "cve", "injection", "auth", "encrypt", "xss", "csrf"]; + let keywords = [ + "security", + "vulnerability", + "cve", + "injection", + "auth", + "encrypt", + "xss", + "csrf", + ]; self.keyword_score(s, &keywords) } fn score_performance(&self, s: &str) -> f32 { - let keywords = ["performance", "optimize", "speed", "memory", "benchmark", "profile", "latency", "throughput"]; + let keywords = [ + "performance", + "optimize", + "speed", + "memory", + "benchmark", + "profile", + "latency", + "throughput", + ]; self.keyword_score(s, &keywords) } fn keyword_score(&self, text: &str, keywords: &[&str]) -> f32 { - let matches: f32 = keywords.iter() - .filter(|k| text.contains(*k)) - .count() as f32; + let matches: f32 = keywords.iter().filter(|k| text.contains(*k)).count() as f32; (matches / keywords.len() as f32).min(1.0) } @@ -256,9 +343,7 @@ impl TaskClassifier { }; // Add agents for secondary task types - let secondary_count = secondary.iter() - .filter(|(_, score)| *score > 0.3) - .count() as u8; + let secondary_count = secondary.iter().filter(|(_, score)| *score > 0.3).count() as u8; (base + secondary_count.min(2)).min(6) } @@ -289,7 +374,8 @@ mod tests { let classifier = TaskClassifier::new(); let simple = classifier.classify("fix a typo"); - let complex = classifier.classify("implement distributed authentication with security audit"); + let complex = + classifier.classify("implement distributed authentication with security audit"); assert!(complex.complexity > simple.complexity); } diff --git a/crates/ruvllm/src/claude_flow/task_generator.rs b/crates/ruvllm/src/claude_flow/task_generator.rs index 820638883..ea0dc5d6e 100644 --- a/crates/ruvllm/src/claude_flow/task_generator.rs +++ b/crates/ruvllm/src/claude_flow/task_generator.rs @@ -234,15 +234,53 @@ impl GeneratedTask { /// Extract keywords from description fn extract_keywords(description: &str) -> Vec { let keywords_set = [ - "implement", "create", "build", "fix", "refactor", "optimize", - "research", "analyze", "investigate", "explore", "understand", - "test", "verify", "validate", "coverage", "unit", "integration", - "review", "audit", "inspect", "quality", "security", - "design", "architecture", "structure", "pattern", "scalable", - "performance", "benchmark", "profile", "memory", "latency", - "train", "model", "neural", "embedding", "inference", - "deploy", "ci", "cd", "pipeline", "workflow", - "api", "endpoint", "database", "server", "rest", + "implement", + "create", + "build", + "fix", + "refactor", + "optimize", + "research", + "analyze", + "investigate", + "explore", + "understand", + "test", + "verify", + "validate", + "coverage", + "unit", + "integration", + "review", + "audit", + "inspect", + "quality", + "security", + "design", + "architecture", + "structure", + "pattern", + "scalable", + "performance", + "benchmark", + "profile", + "memory", + "latency", + "train", + "model", + "neural", + "embedding", + "inference", + "deploy", + "ci", + "cd", + "pipeline", + "workflow", + "api", + "endpoint", + "database", + "server", + "rest", ]; let lower = description.to_lowercase(); @@ -297,17 +335,40 @@ impl TaskGenerator { Self { templates: Self::build_templates(), technologies: vec![ - "Rust", "TypeScript", "Python", "Go", "JavaScript", - "React", "Node.js", "PostgreSQL", "Redis", "MongoDB", + "Rust", + "TypeScript", + "Python", + "Go", + "JavaScript", + "React", + "Node.js", + "PostgreSQL", + "Redis", + "MongoDB", ], components: vec![ - "user service", "authentication module", "API gateway", - "payment processor", "notification system", "data pipeline", - "caching layer", "rate limiter", "search engine", "analytics service", + "user service", + "authentication module", + "API gateway", + "payment processor", + "notification system", + "data pipeline", + "caching layer", + "rate limiter", + "search engine", + "analytics service", ], frameworks: vec![ - "actix-web", "tokio", "express", "fastapi", "gin", - "next.js", "django", "spring", "axum", "rocket", + "actix-web", + "tokio", + "express", + "fastapi", + "gin", + "next.js", + "django", + "spring", + "axum", + "rocket", ], tasks_generated: 0, } @@ -332,7 +393,12 @@ impl TaskGenerator { TaskTemplate { template: "create a {} for the {}", placeholders: vec![ - &["REST endpoint", "data model", "service class", "helper module"], + &[ + "REST endpoint", + "data model", + "service class", + "helper module", + ], &["user service", "payment system", "notification service"], ], complexity: TaskComplexity::Moderate, @@ -357,7 +423,12 @@ impl TaskGenerator { TaskTemplate { template: "fix the {} bug in the {}", placeholders: vec![ - &["memory leak", "race condition", "null pointer", "off-by-one"], + &[ + "memory leak", + "race condition", + "null pointer", + "off-by-one", + ], &["connection pool", "request handler", "cache manager"], ], complexity: TaskComplexity::Moderate, @@ -381,14 +452,24 @@ impl TaskGenerator { template: "research best practices for {} in {}", placeholders: vec![ &["authentication", "caching", "logging", "monitoring"], - &["microservices", "serverless", "monolith", "distributed systems"], + &[ + "microservices", + "serverless", + "monolith", + "distributed systems", + ], ], complexity: TaskComplexity::Simple, }, TaskTemplate { template: "analyze the {} patterns in the codebase", placeholders: vec![ - &["error handling", "dependency injection", "state management", "API design"], + &[ + "error handling", + "dependency injection", + "state management", + "API design", + ], &[], ], complexity: TaskComplexity::Moderate, @@ -396,8 +477,16 @@ impl TaskGenerator { TaskTemplate { template: "investigate {} for implementing {}", placeholders: vec![ - &["different approaches", "trade-offs", "performance implications"], - &["real-time notifications", "event sourcing", "data replication"], + &[ + "different approaches", + "trade-offs", + "performance implications", + ], + &[ + "real-time notifications", + "event sourcing", + "data replication", + ], ], complexity: TaskComplexity::Complex, }, @@ -405,8 +494,16 @@ impl TaskGenerator { template: "explore {} architectures for {} with {} requirements", placeholders: vec![ &["event-driven", "CQRS", "hexagonal", "microkernel"], - &["high-throughput systems", "low-latency applications", "scalable platforms"], - &["strict consistency", "eventual consistency", "partition tolerance"], + &[ + "high-throughput systems", + "low-latency applications", + "scalable platforms", + ], + &[ + "strict consistency", + "eventual consistency", + "partition tolerance", + ], ], complexity: TaskComplexity::Expert, }, @@ -419,10 +516,7 @@ impl TaskGenerator { vec![ TaskTemplate { template: "review the {} for code quality", - placeholders: vec![ - &["pull request", "module", "function", "class"], - &[], - ], + placeholders: vec![&["pull request", "module", "function", "class"], &[]], complexity: TaskComplexity::Simple, }, TaskTemplate { @@ -444,9 +538,21 @@ impl TaskGenerator { TaskTemplate { template: "conduct comprehensive code review of {} focusing on {} and {}", placeholders: vec![ - &["the entire service", "the core domain", "the infrastructure layer"], - &["architectural consistency", "security vulnerabilities", "performance bottlenecks"], - &["test coverage", "documentation completeness", "error handling robustness"], + &[ + "the entire service", + "the core domain", + "the infrastructure layer", + ], + &[ + "architectural consistency", + "security vulnerabilities", + "performance bottlenecks", + ], + &[ + "test coverage", + "documentation completeness", + "error handling robustness", + ], ], complexity: TaskComplexity::Expert, }, @@ -486,9 +592,21 @@ impl TaskGenerator { template: "design {} architecture for {} handling {} with {} guarantees", placeholders: vec![ &["distributed", "event-driven", "stream processing"], - &["real-time analytics", "transaction processing", "IoT data ingestion"], - &["millions of events per second", "petabytes of data", "global users"], - &["exactly-once delivery", "strong consistency", "sub-millisecond latency"], + &[ + "real-time analytics", + "transaction processing", + "IoT data ingestion", + ], + &[ + "millions of events per second", + "petabytes of data", + "global users", + ], + &[ + "exactly-once delivery", + "strong consistency", + "sub-millisecond latency", + ], ], complexity: TaskComplexity::Expert, }, @@ -528,9 +646,21 @@ impl TaskGenerator { template: "design {} test suite for {} including {} and {} scenarios", placeholders: vec![ &["chaos engineering", "load", "stress", "security"], - &["the distributed system", "the microservices platform", "the data pipeline"], - &["failure injection", "network partitions", "resource exhaustion"], - &["recovery verification", "data integrity checks", "SLA validation"], + &[ + "the distributed system", + "the microservices platform", + "the data pipeline", + ], + &[ + "failure injection", + "network partitions", + "resource exhaustion", + ], + &[ + "recovery verification", + "data integrity checks", + "SLA validation", + ], ], complexity: TaskComplexity::Expert, }, @@ -561,7 +691,11 @@ impl TaskGenerator { template: "perform {} security analysis of {} focusing on {}", placeholders: vec![ &["comprehensive", "penetration", "threat modeling"], - &["the authentication system", "the payment processing", "the data storage"], + &[ + "the authentication system", + "the payment processing", + "the data storage", + ], &["OWASP Top 10", "zero-trust principles", "data protection"], ], complexity: TaskComplexity::Complex, @@ -570,7 +704,11 @@ impl TaskGenerator { template: "design {} security architecture for {} with {} and {} compliance", placeholders: vec![ &["defense-in-depth", "zero-trust", "secure-by-design"], - &["the enterprise platform", "the financial system", "the healthcare application"], + &[ + "the enterprise platform", + "the financial system", + "the healthcare application", + ], &["SOC2", "HIPAA", "PCI-DSS"], &["GDPR", "ISO 27001", "FedRAMP"], ], @@ -611,9 +749,21 @@ impl TaskGenerator { TaskTemplate { template: "optimize {} for {} achieving {} with {} constraints", placeholders: vec![ - &["the distributed cache", "the message processing", "the ML inference"], - &["ultra-low latency", "maximum throughput", "optimal resource utilization"], - &["sub-millisecond p99", "millions of ops/sec", "linear scaling"], + &[ + "the distributed cache", + "the message processing", + "the ML inference", + ], + &[ + "ultra-low latency", + "maximum throughput", + "optimal resource utilization", + ], + &[ + "sub-millisecond p99", + "millions of ops/sec", + "linear scaling", + ], &["memory limits", "cost constraints", "hardware restrictions"], ], complexity: TaskComplexity::Expert, @@ -628,7 +778,11 @@ impl TaskGenerator { TaskTemplate { template: "implement {} for the {} model", placeholders: vec![ - &["data preprocessing", "feature extraction", "evaluation metrics"], + &[ + "data preprocessing", + "feature extraction", + "evaluation metrics", + ], &["classification", "regression", "embedding"], ], complexity: TaskComplexity::Simple, @@ -637,7 +791,11 @@ impl TaskGenerator { template: "train a {} model for {}", placeholders: vec![ &["neural network", "transformer", "ensemble"], - &["text classification", "entity extraction", "sentiment analysis"], + &[ + "text classification", + "entity extraction", + "sentiment analysis", + ], ], complexity: TaskComplexity::Moderate, }, @@ -654,7 +812,11 @@ impl TaskGenerator { template: "design {} ML pipeline for {} with {} and {}", placeholders: vec![ &["end-to-end", "continuous learning", "multi-model"], - &["recommendation system", "fraud detection", "personalization engine"], + &[ + "recommendation system", + "fraud detection", + "personalization engine", + ], &["online learning", "A/B testing", "feature store"], &["model versioning", "drift detection", "explainability"], ], @@ -697,9 +859,21 @@ impl TaskGenerator { template: "design {} infrastructure for {} with {} and {}", placeholders: vec![ &["GitOps", "platform engineering", "self-service"], - &["multi-cloud deployment", "global distribution", "hybrid cloud"], - &["infrastructure as code", "policy as code", "security as code"], - &["observability", "cost optimization", "compliance automation"], + &[ + "multi-cloud deployment", + "global distribution", + "hybrid cloud", + ], + &[ + "infrastructure as code", + "policy as code", + "security as code", + ], + &[ + "observability", + "cost optimization", + "compliance automation", + ], ], complexity: TaskComplexity::Expert, }, @@ -712,10 +886,7 @@ impl TaskGenerator { vec![ TaskTemplate { template: "document the {} API", - placeholders: vec![ - &["REST", "GraphQL", "gRPC"], - &[], - ], + placeholders: vec![&["REST", "GraphQL", "gRPC"], &[]], complexity: TaskComplexity::Simple, }, TaskTemplate { @@ -739,9 +910,17 @@ impl TaskGenerator { template: "create comprehensive {} documentation for {} including {} and {}", placeholders: vec![ &["technical", "architectural", "operational"], - &["the entire platform", "the distributed system", "the ML pipeline"], + &[ + "the entire platform", + "the distributed system", + "the ML pipeline", + ], &["ADRs", "runbooks", "disaster recovery plans"], - &["capacity planning guides", "security protocols", "compliance procedures"], + &[ + "capacity planning guides", + "security protocols", + "compliance procedures", + ], ], complexity: TaskComplexity::Expert, }, @@ -752,7 +931,11 @@ impl TaskGenerator { } /// Generate a task for a category and complexity - pub fn generate(&mut self, category: TaskCategory, complexity: TaskComplexity) -> GeneratedTask { + pub fn generate( + &mut self, + category: TaskCategory, + complexity: TaskComplexity, + ) -> GeneratedTask { self.tasks_generated += 1; let templates = self.templates.get(&category).unwrap(); @@ -777,7 +960,11 @@ impl TaskGenerator { } /// Generate a task for a specific agent - pub fn generate_for_agent(&mut self, agent: ClaudeFlowAgent, complexity: TaskComplexity) -> GeneratedTask { + pub fn generate_for_agent( + &mut self, + agent: ClaudeFlowAgent, + complexity: TaskComplexity, + ) -> GeneratedTask { let category = TaskCategory::from_agent(agent); let mut task = self.generate(category, complexity); task.expected_agent = agent; @@ -785,7 +972,11 @@ impl TaskGenerator { } /// Generate a batch of tasks - pub fn generate_batch(&mut self, count: usize, category: Option) -> Vec { + pub fn generate_batch( + &mut self, + count: usize, + category: Option, + ) -> Vec { (0..count) .map(|_| { let cat = category.unwrap_or_else(TaskCategory::random); @@ -829,7 +1020,8 @@ impl TaskGenerator { // Add variation with technology/component names if rand_simple() > 0.5 && result.contains("the ") { - let component = self.components[(rand_simple() * self.components.len() as f32) as usize]; + let component = + self.components[(rand_simple() * self.components.len() as f32) as usize]; result = result.replace("the service", &format!("the {}", component)); } @@ -857,7 +1049,9 @@ fn rand_simple() -> f32 { STATE.with(|state| { let mut s = state.borrow_mut(); - *s = s.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407); + *s = s + .wrapping_mul(6364136223846793005) + .wrapping_add(1442695040888963407); (*s >> 33) as f32 / u32::MAX as f32 }) } @@ -904,7 +1098,8 @@ mod tests { #[test] fn test_generate_for_agent() { let mut generator = TaskGenerator::new(); - let task = generator.generate_for_agent(ClaudeFlowAgent::Researcher, TaskComplexity::Moderate); + let task = + generator.generate_for_agent(ClaudeFlowAgent::Researcher, TaskComplexity::Moderate); assert_eq!(task.expected_agent, ClaudeFlowAgent::Researcher); assert_eq!(task.category, TaskCategory::Research); @@ -945,16 +1140,31 @@ mod tests { #[test] fn test_category_from_agent() { - assert_eq!(TaskCategory::from_agent(ClaudeFlowAgent::Coder), TaskCategory::Coding); - assert_eq!(TaskCategory::from_agent(ClaudeFlowAgent::Researcher), TaskCategory::Research); - assert_eq!(TaskCategory::from_agent(ClaudeFlowAgent::SecurityAuditor), TaskCategory::Security); + assert_eq!( + TaskCategory::from_agent(ClaudeFlowAgent::Coder), + TaskCategory::Coding + ); + assert_eq!( + TaskCategory::from_agent(ClaudeFlowAgent::Researcher), + TaskCategory::Research + ); + assert_eq!( + TaskCategory::from_agent(ClaudeFlowAgent::SecurityAuditor), + TaskCategory::Security + ); } #[test] fn test_primary_agent() { assert_eq!(TaskCategory::Coding.primary_agent(), ClaudeFlowAgent::Coder); - assert_eq!(TaskCategory::Testing.primary_agent(), ClaudeFlowAgent::Tester); - assert_eq!(TaskCategory::Security.primary_agent(), ClaudeFlowAgent::SecurityAuditor); + assert_eq!( + TaskCategory::Testing.primary_agent(), + ClaudeFlowAgent::Tester + ); + assert_eq!( + TaskCategory::Security.primary_agent(), + ClaudeFlowAgent::SecurityAuditor + ); } #[test] diff --git a/crates/ruvllm/src/context/agentic_memory.rs b/crates/ruvllm/src/context/agentic_memory.rs index 3f9f1a6d7..f90ecb433 100644 --- a/crates/ruvllm/src/context/agentic_memory.rs +++ b/crates/ruvllm/src/context/agentic_memory.rs @@ -15,8 +15,8 @@ use std::sync::Arc; use crate::error::{Result, RuvLLMError}; -use super::episodic_memory::{EpisodicMemory, EpisodicMemoryConfig, Episode, Trajectory}; -use super::working_memory::{WorkingMemory, WorkingMemoryConfig, TaskContext}; +use super::episodic_memory::{Episode, EpisodicMemory, EpisodicMemoryConfig, Trajectory}; +use super::working_memory::{TaskContext, WorkingMemory, WorkingMemoryConfig}; /// Configuration for agentic memory #[derive(Debug, Clone, Serialize, Deserialize)] @@ -242,8 +242,7 @@ impl AgenticMemory { duration_ms: 0, created_at: Utc::now(), }; - self.episodic - .store_episode(trajectory, embedding, vec![])?; + self.episodic.store_episode(trajectory, embedding, vec![])?; Ok(key.to_string()) } MemoryType::Semantic => { @@ -359,9 +358,7 @@ impl AgenticMemory { .compressed .as_ref() .map(|c| c.summary.clone()) - .unwrap_or_else(|| { - format!("Episode: {} steps", e.metadata.step_count) - }), + .unwrap_or_else(|| format!("Episode: {} steps", e.metadata.step_count)), memory_type: MemoryType::Episodic, score: e.metadata.quality_score, metadata: { @@ -433,11 +430,7 @@ impl AgenticMemory { } /// Get relevant memories across all types - pub fn get_relevant( - &self, - query_embedding: &[f32], - k: usize, - ) -> Result> { + pub fn get_relevant(&self, query_embedding: &[f32], k: usize) -> Result> { let mut all_results = Vec::new(); // Get from each memory type diff --git a/crates/ruvllm/src/context/claude_flow_bridge.rs b/crates/ruvllm/src/context/claude_flow_bridge.rs index 22d43e551..81bad032b 100644 --- a/crates/ruvllm/src/context/claude_flow_bridge.rs +++ b/crates/ruvllm/src/context/claude_flow_bridge.rs @@ -240,7 +240,11 @@ impl ClaudeFlowMemoryBridge { } /// Retrieve a specific pattern by key - pub fn retrieve_pattern(&self, key: &str, namespace: Option<&str>) -> Result> { + pub fn retrieve_pattern( + &self, + key: &str, + namespace: Option<&str>, + ) -> Result> { let ns = namespace.unwrap_or(&self.config.patterns_namespace); let args = vec![ @@ -309,7 +313,12 @@ impl ClaudeFlowMemoryBridge { let mut tasks_synced = 0; // Sync patterns - match self.execute_cli(&["hive-mind".to_string(), "memory".to_string(), "--action".to_string(), "list".to_string()]) { + match self.execute_cli(&[ + "hive-mind".to_string(), + "memory".to_string(), + "--action".to_string(), + "list".to_string(), + ]) { Ok(output) => { patterns_synced = output.lines().count(); } @@ -367,12 +376,7 @@ impl ClaudeFlowMemoryBridge { } /// Record task outcome for learning - pub fn record_outcome( - &self, - task_id: &str, - success: bool, - quality: Option, - ) -> Result<()> { + pub fn record_outcome(&self, task_id: &str, success: bool, quality: Option) -> Result<()> { let mut args = vec![ "hooks".to_string(), "post-task".to_string(), @@ -497,7 +501,11 @@ impl ClaudeFlowMemoryBridge { } /// Parse search results from CLI output - fn parse_search_results(&self, output: &str, namespace: &str) -> Result> { + fn parse_search_results( + &self, + output: &str, + namespace: &str, + ) -> Result> { let mut patterns = Vec::new(); // Try to parse as JSON first @@ -640,7 +648,9 @@ mod tests { {"key": "pattern-2", "value": "value-2", "tags": []} ]"#; - let results = bridge.parse_search_results(json_output, "patterns").unwrap(); + let results = bridge + .parse_search_results(json_output, "patterns") + .unwrap(); assert_eq!(results.len(), 2); assert_eq!(results[0].key, "pattern-1"); assert_eq!(results[0].tags, vec!["rust"]); @@ -653,7 +663,9 @@ mod tests { let text_output = "key1: value1\nkey2: value2\n"; - let results = bridge.parse_search_results(text_output, "patterns").unwrap(); + let results = bridge + .parse_search_results(text_output, "patterns") + .unwrap(); assert_eq!(results.len(), 2); assert_eq!(results[0].key, "key1"); assert_eq!(results[0].value, "value1"); diff --git a/crates/ruvllm/src/context/context_manager.rs b/crates/ruvllm/src/context/context_manager.rs index b72730282..68d0423bc 100644 --- a/crates/ruvllm/src/context/context_manager.rs +++ b/crates/ruvllm/src/context/context_manager.rs @@ -275,7 +275,12 @@ impl MemorySummarizer { } /// Summarize multiple memories into a single summary - pub fn summarize_memories(&self, memories: &[RetrievedMemory], max_tokens: usize, chars_per_token: f32) -> String { + pub fn summarize_memories( + &self, + memories: &[RetrievedMemory], + max_tokens: usize, + chars_per_token: f32, + ) -> String { let max_chars = (max_tokens as f32 * chars_per_token) as usize; let mut summary = String::with_capacity(max_chars); @@ -555,8 +560,12 @@ impl IntelligentContextManager { } let elapsed = start.elapsed().as_micros() as u64; - self.stats.total_tokens.fetch_add(total_tokens as u64, Ordering::SeqCst); - self.stats.total_time_us.fetch_add(elapsed, Ordering::SeqCst); + self.stats + .total_tokens + .fetch_add(total_tokens as u64, Ordering::SeqCst); + self.stats + .total_time_us + .fetch_add(elapsed, Ordering::SeqCst); Ok(PreparedContext { elements: included, @@ -711,7 +720,12 @@ mod tests { // Store some memory let embedding = vec![0.1; 128]; manager - .store_memory("fact-1", "Test fact", embedding.clone(), MemoryType::Semantic) + .store_memory( + "fact-1", + "Test fact", + embedding.clone(), + MemoryType::Semantic, + ) .unwrap(); let messages = vec![Message { @@ -750,7 +764,10 @@ mod tests { assert!(score <= 1.0); let priority = scorer.assign_priority(score); - assert!(matches!(priority, ElementPriority::High | ElementPriority::Critical)); + assert!(matches!( + priority, + ElementPriority::High | ElementPriority::Critical + )); } #[test] diff --git a/crates/ruvllm/src/context/episodic_memory.rs b/crates/ruvllm/src/context/episodic_memory.rs index ffb5d0306..e4b2a89f3 100644 --- a/crates/ruvllm/src/context/episodic_memory.rs +++ b/crates/ruvllm/src/context/episodic_memory.rs @@ -173,7 +173,9 @@ impl MemoryCompressor { let mut steps_with_reward: Vec<(usize, &TrajectoryStep)> = trajectory.steps.iter().enumerate().collect(); steps_with_reward.sort_by(|a, b| { - b.1.reward.partial_cmp(&a.1.reward).unwrap_or(std::cmp::Ordering::Equal) + b.1.reward + .partial_cmp(&a.1.reward) + .unwrap_or(std::cmp::Ordering::Equal) }); let key_steps: Vec<&TrajectoryStep> = steps_with_reward @@ -236,10 +238,8 @@ impl MemoryCompressor { /// Compress embedding (average or reduce dimensions) fn compress_embedding(&self, steps: &[&TrajectoryStep]) -> Vec { - let embeddings: Vec<&Vec> = steps - .iter() - .filter_map(|s| s.embedding.as_ref()) - .collect(); + let embeddings: Vec<&Vec> = + steps.iter().filter_map(|s| s.embedding.as_ref()).collect(); if embeddings.is_empty() { return Vec::new(); @@ -407,7 +407,9 @@ impl EpisodicMemory { .fetch_add(latency, Ordering::SeqCst); if !found.is_empty() { - self.stats.successful_retrievals.fetch_add(1, Ordering::SeqCst); + self.stats + .successful_retrievals + .fetch_add(1, Ordering::SeqCst); } Ok(found) @@ -443,9 +445,7 @@ impl EpisodicMemory { task_type: &str, k: usize, ) -> Result> { - self.search_with_filter(query_embedding, k, |meta| { - meta.task_type == task_type - }) + self.search_with_filter(query_embedding, k, |meta| meta.task_type == task_type) } /// Search successful episodes only @@ -547,7 +547,10 @@ impl EpisodicMemory { /// Get statistics pub fn stats(&self) -> EpisodicMemoryStats { let episodes = self.episodes.read(); - let compressed = episodes.iter().filter(|(_, e)| e.metadata.is_compressed).count() as u64; + let compressed = episodes + .iter() + .filter(|(_, e)| e.metadata.is_compressed) + .count() as u64; let total = episodes.len() as u64; let searches = self.stats.total_searches.load(Ordering::SeqCst); @@ -580,8 +583,12 @@ impl EpisodicMemory { max_elements: self.config.max_episodes, }; - let new_index = HnswIndex::new(self.config.embedding_dim, DistanceMetric::Cosine, hnsw_config) - .map_err(|e| RuvLLMError::Ruvector(e.to_string()))?; + let new_index = HnswIndex::new( + self.config.embedding_dim, + DistanceMetric::Cosine, + hnsw_config, + ) + .map_err(|e| RuvLLMError::Ruvector(e.to_string()))?; *self.index.write() = new_index; @@ -679,7 +686,9 @@ mod tests { let results = memory.search_by_task_type(&embedding, "coding", 5).unwrap(); assert_eq!(results.len(), 1); - let results = memory.search_by_task_type(&embedding, "research", 5).unwrap(); + let results = memory + .search_by_task_type(&embedding, "research", 5) + .unwrap(); assert_eq!(results.len(), 0); } @@ -706,9 +715,7 @@ mod tests { let trajectory = test_trajectory(); let embedding = test_embedding(128); - memory - .store_episode(trajectory, embedding, vec![]) - .unwrap(); + memory.store_episode(trajectory, embedding, vec![]).unwrap(); assert!(memory.get("traj-1").is_some()); assert!(memory.delete("traj-1").unwrap()); @@ -726,9 +733,7 @@ mod tests { let trajectory = test_trajectory(); let embedding = test_embedding(128); - memory - .store_episode(trajectory, embedding, vec![]) - .unwrap(); + memory.store_episode(trajectory, embedding, vec![]).unwrap(); assert_eq!(memory.stats().total_episodes, 1); memory.clear().unwrap(); diff --git a/crates/ruvllm/src/context/mod.rs b/crates/ruvllm/src/context/mod.rs index dcb88484f..43fbb39ab 100644 --- a/crates/ruvllm/src/context/mod.rs +++ b/crates/ruvllm/src/context/mod.rs @@ -65,21 +65,18 @@ pub mod working_memory; // Re-exports pub use agentic_memory::{AgenticMemory, AgenticMemoryConfig, MemoryType}; -pub use claude_flow_bridge::{ClaudeFlowMemoryBridge, ClaudeFlowBridgeConfig, SyncResult}; +pub use claude_flow_bridge::{ClaudeFlowBridgeConfig, ClaudeFlowMemoryBridge, SyncResult}; pub use context_manager::{ - IntelligentContextManager, ContextManagerConfig, PreparedContext, - PriorityScorer, ContextElement, ElementPriority, + ContextElement, ContextManagerConfig, ElementPriority, IntelligentContextManager, + PreparedContext, PriorityScorer, }; pub use episodic_memory::{ - EpisodicMemory, EpisodicMemoryConfig, Episode, EpisodeMetadata, - Trajectory as EpisodeTrajectory, CompressedEpisode, -}; -pub use semantic_cache::{ - SemanticToolCache, SemanticCacheConfig, CachedToolResult, CacheStats, + CompressedEpisode, Episode, EpisodeMetadata, EpisodicMemory, EpisodicMemoryConfig, + Trajectory as EpisodeTrajectory, }; +pub use semantic_cache::{CacheStats, CachedToolResult, SemanticCacheConfig, SemanticToolCache}; pub use working_memory::{ - WorkingMemory, WorkingMemoryConfig, TaskContext, ScratchpadEntry, - AttentionWeights, + AttentionWeights, ScratchpadEntry, TaskContext, WorkingMemory, WorkingMemoryConfig, }; #[cfg(test)] diff --git a/crates/ruvllm/src/context/semantic_cache.rs b/crates/ruvllm/src/context/semantic_cache.rs index cfe5f7222..3054dcd6e 100644 --- a/crates/ruvllm/src/context/semantic_cache.rs +++ b/crates/ruvllm/src/context/semantic_cache.rs @@ -567,7 +567,12 @@ mod tests { let embedding = test_embedding(128); cache - .store("read_file", "/path/to/file.rs", "file contents", embedding.clone()) + .store( + "read_file", + "/path/to/file.rs", + "file contents", + embedding.clone(), + ) .unwrap(); // Same embedding should match @@ -588,12 +593,16 @@ mod tests { // First call should execute let result: std::result::Result = - cache.get_or_execute("test_tool", "input", embedding.clone(), || Ok("executed".to_string())); + cache.get_or_execute("test_tool", "input", embedding.clone(), || { + Ok("executed".to_string()) + }); assert_eq!(result.unwrap(), "executed"); // Second call should return cached let result: std::result::Result = - cache.get_or_execute("test_tool", "input", embedding, || Ok("should not execute".to_string())); + cache.get_or_execute("test_tool", "input", embedding, || { + Ok("should not execute".to_string()) + }); assert_eq!(result.unwrap(), "executed"); } diff --git a/crates/ruvllm/src/context/working_memory.rs b/crates/ruvllm/src/context/working_memory.rs index 7bdbc6d00..c0a28180c 100644 --- a/crates/ruvllm/src/context/working_memory.rs +++ b/crates/ruvllm/src/context/working_memory.rs @@ -272,7 +272,8 @@ pub struct CachedToolResult { impl WorkingMemory { /// Create new working memory with configuration pub fn new(config: WorkingMemoryConfig) -> Self { - let attention = AttentionWeights::new(config.attention_decay_rate, config.min_attention_threshold); + let attention = + AttentionWeights::new(config.attention_decay_rate, config.min_attention_threshold); Self { config, @@ -288,7 +289,9 @@ impl WorkingMemory { pub fn set_task(&self, task: TaskContext) { let task_id = task.task_id.clone(); *self.current_task.write() = Some(task); - self.attention.write().set(&task_id, self.config.default_attention); + self.attention + .write() + .set(&task_id, self.config.default_attention); } /// Get current task @@ -338,12 +341,7 @@ impl WorkingMemory { /// Get recent scratchpad entries pub fn get_recent(&self, count: usize) -> Vec { let scratchpad = self.scratchpad.read(); - scratchpad - .iter() - .rev() - .take(count) - .cloned() - .collect() + scratchpad.iter().rev().take(count).cloned().collect() } /// Get scratchpad entries by type @@ -372,7 +370,11 @@ impl WorkingMemory { with_scores.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(std::cmp::Ordering::Equal)); - with_scores.into_iter().take(k).map(|(_, e)| e.clone()).collect() + with_scores + .into_iter() + .take(k) + .map(|(_, e)| e.clone()) + .collect() } /// Clear scratchpad @@ -381,7 +383,14 @@ impl WorkingMemory { } /// Cache tool result - pub fn cache_tool_result(&self, tool_name: &str, input: &str, output: String, success: bool, ttl: Duration) { + pub fn cache_tool_result( + &self, + tool_name: &str, + input: &str, + output: String, + success: bool, + ttl: Duration, + ) { let input_hash = format!("{:x}", md5::compute(input)); let key = format!("{}:{}", tool_name, input_hash); @@ -433,7 +442,9 @@ impl WorkingMemory { /// Set variable pub fn set_variable(&self, key: &str, value: serde_json::Value) { self.variables.write().insert(key.to_string(), value); - self.attention.write().set(key, self.config.default_attention); + self.attention + .write() + .set(key, self.config.default_attention); } /// Get variable diff --git a/crates/ruvllm/src/evaluation/diff_quality.rs b/crates/ruvllm/src/evaluation/diff_quality.rs index bc776ba3e..5e726a4c1 100644 --- a/crates/ruvllm/src/evaluation/diff_quality.rs +++ b/crates/ruvllm/src/evaluation/diff_quality.rs @@ -81,7 +81,9 @@ impl EditLocality { let concentration_bonus = self.primary_module_fraction; // Combine: high concentration and low scatter is good - (concentration_bonus - scatter_penalty - boundary_penalty).max(0.0).min(1.0) + (concentration_bonus - scatter_penalty - boundary_penalty) + .max(0.0) + .min(1.0) } /// Check if edits are well-localized @@ -184,10 +186,10 @@ impl Default for DiffAnalyzer { fn default() -> Self { Self { mechanical_patterns: vec![ - r"^[-+]\s*use\s+".to_string(), // import changes - r"^[-+]\s*$".to_string(), // blank line changes - r"^[-+]\s*//".to_string(), // comment changes - r"^[-+]\s*#\[".to_string(), // attribute changes + r"^[-+]\s*use\s+".to_string(), // import changes + r"^[-+]\s*$".to_string(), // blank line changes + r"^[-+]\s*//".to_string(), // comment changes + r"^[-+]\s*#\[".to_string(), // attribute changes ], boundary_markers: vec![ "Cargo.toml".to_string(), diff --git a/crates/ruvllm/src/evaluation/economics.rs b/crates/ruvllm/src/evaluation/economics.rs index 4b4d7a2dd..98f6af3de 100644 --- a/crates/ruvllm/src/evaluation/economics.rs +++ b/crates/ruvllm/src/evaluation/economics.rs @@ -133,7 +133,10 @@ impl LatencyStats { /// Get max pub fn max(&self) -> f64 { - self.samples.iter().copied().fold(f64::NEG_INFINITY, f64::max) + self.samples + .iter() + .copied() + .fold(f64::NEG_INFINITY, f64::max) } } diff --git a/crates/ruvllm/src/evaluation/harness.rs b/crates/ruvllm/src/evaluation/harness.rs index 63f297c15..07742aa6b 100644 --- a/crates/ruvllm/src/evaluation/harness.rs +++ b/crates/ruvllm/src/evaluation/harness.rs @@ -277,9 +277,9 @@ impl EvaluationHarness { // Determine if accepted let accepted = correctness.succeeded() - && diff_quality - .as_ref() - .map_or(false, |dq| dq.combined_score >= self.config.quality_threshold); + && diff_quality.as_ref().map_or(false, |dq| { + dq.combined_score >= self.config.quality_threshold + }); Ok(EvalRun { task_id: task.id.clone(), @@ -364,8 +364,14 @@ impl EvaluationHarness { } // Add latency samples - economics.latency.routing.add_secs(run.latency.routing_ms / 1000.0); - economics.latency.end_to_end.add_secs(run.latency.total_ms / 1000.0); + economics + .latency + .routing + .add_secs(run.latency.routing_ms / 1000.0); + economics + .latency + .end_to_end + .add_secs(run.latency.total_ms / 1000.0); } economics.recalculate(); @@ -426,9 +432,15 @@ impl EvalReport { /// Generate leaderboard-style output pub fn to_leaderboard(&self) -> String { let mut output = String::new(); - output.push_str("╔════════════════════════════════════════════════════════════════════════════╗\n"); - output.push_str("║ RuvLLM Evaluation Report ║\n"); - output.push_str("╠════════════════════════════════════════════════════════════════════════════╣\n"); + output.push_str( + "╔════════════════════════════════════════════════════════════════════════════╗\n", + ); + output.push_str( + "║ RuvLLM Evaluation Report ║\n", + ); + output.push_str( + "╠════════════════════════════════════════════════════════════════════════════╣\n", + ); output.push_str(&format!( "║ Tasks: {} × {} seeds × {} modes = {} runs ║\n", self.config.task_count, @@ -441,9 +453,15 @@ impl EvalReport { self.total_duration.as_secs_f64(), self.config.quality_threshold * 100.0 )); - output.push_str("╠════════════════════════════════════════════════════════════════════════════╣\n"); - output.push_str("║ Mode │ Success% │ Verified% │ Quality │ $/patch │ p95 lat ║\n"); - output.push_str("╠════════════════════════════════════════════════════════════════════════════╣\n"); + output.push_str( + "╠════════════════════════════════════════════════════════════════════════════╣\n", + ); + output.push_str( + "║ Mode │ Success% │ Verified% │ Quality │ $/patch │ p95 lat ║\n", + ); + output.push_str( + "╠════════════════════════════════════════════════════════════════════════════╣\n", + ); // Sort modes by success rate let mut modes: Vec<_> = self.mode_metrics.values().collect(); @@ -466,7 +484,9 @@ impl EvalReport { )); } - output.push_str("╚════════════════════════════════════════════════════════════════════════════╝\n"); + output.push_str( + "╚════════════════════════════════════════════════════════════════════════════╝\n", + ); output } @@ -493,9 +513,7 @@ impl EvalReport { return None; } - Some( - (target.correctness.task_success_rate() - baseline_rate) / baseline_rate * 100.0, - ) + Some((target.correctness.task_success_rate() - baseline_rate) / baseline_rate * 100.0) } } diff --git a/crates/ruvllm/src/evaluation/mod.rs b/crates/ruvllm/src/evaluation/mod.rs index cce057f19..7c42be42f 100644 --- a/crates/ruvllm/src/evaluation/mod.rs +++ b/crates/ruvllm/src/evaluation/mod.rs @@ -37,24 +37,12 @@ mod real_harness; mod report; pub mod swe_bench; -pub use correctness::{ - CorrectnessMetrics, TaskResult, TestSuiteResult, VerificationLevel, -}; -pub use diff_quality::{ - DiffQualityMetrics, DiffAnalyzer, EditLocality, Minimality, ReviewBurden, -}; -pub use economics::{ - EconomicsMetrics, LatencyDistribution, CostTracker, StabilityMetrics, -}; +pub use correctness::{CorrectnessMetrics, TaskResult, TestSuiteResult, VerificationLevel}; +pub use diff_quality::{DiffAnalyzer, DiffQualityMetrics, EditLocality, Minimality, ReviewBurden}; +pub use economics::{CostTracker, EconomicsMetrics, LatencyDistribution, StabilityMetrics}; pub use harness::{ - EvaluationHarness, EvalConfig, AblationMode, EvalTask, EvalRun, EvalReport, ModeMetrics, -}; -pub use metrics::{ - MetricCollector, MetricSnapshot, AggregatedMetrics, -}; -pub use report::{ - LeaderboardEntry, AblationComparison, -}; -pub use real_harness::{ - RealEvaluationHarness, RealInferenceConfig, + AblationMode, EvalConfig, EvalReport, EvalRun, EvalTask, EvaluationHarness, ModeMetrics, }; +pub use metrics::{AggregatedMetrics, MetricCollector, MetricSnapshot}; +pub use real_harness::{RealEvaluationHarness, RealInferenceConfig}; +pub use report::{AblationComparison, LeaderboardEntry}; diff --git a/crates/ruvllm/src/evaluation/real_harness.rs b/crates/ruvllm/src/evaluation/real_harness.rs index f266428d1..34d6729c5 100644 --- a/crates/ruvllm/src/evaluation/real_harness.rs +++ b/crates/ruvllm/src/evaluation/real_harness.rs @@ -6,7 +6,9 @@ use super::correctness::{CorrectnessMetrics, TaskResult, VerificationLevel}; use super::diff_quality::DiffAnalyzer; use super::economics::{CostTracker, EconomicsMetrics}; -use super::harness::{AblationMode, EvalConfig, EvalReport, EvalRun, EvalTask, LatencyBreakdown, ModeMetrics}; +use super::harness::{ + AblationMode, EvalConfig, EvalReport, EvalRun, EvalTask, LatencyBreakdown, ModeMetrics, +}; use crate::backends::{create_backend, GenerateParams, LlmBackend, ModelConfig}; use crate::claude_flow::{AgentType, ClaudeFlowTask, HnswRouter, HnswRouterConfig, TaskPattern}; use crate::sona::integration::{SonaConfig, SonaIntegration, Trajectory}; @@ -123,7 +125,10 @@ impl RealEvaluationHarness { // Load model if path provided if !inference_config.model_path.is_empty() { - harness.load_model(&inference_config.model_path, inference_config.model_config.clone())?; + harness.load_model( + &inference_config.model_path, + inference_config.model_config.clone(), + )?; } // Initialize SONA if enabled @@ -152,7 +157,10 @@ impl RealEvaluationHarness { /// Get the model's embedding dimension from model info fn get_model_embedding_dim(&self) -> Option { - self.backend.read().model_info().map(|info| info.hidden_size) + self.backend + .read() + .model_info() + .map(|info| info.hidden_size) } /// Bootstrap HNSW router with seed patterns for common code tasks @@ -168,41 +176,109 @@ impl RealEvaluationHarness { // Seed patterns for different task types let seed_patterns = vec![ // Bug fix patterns - ("Fix null pointer exception", AgentType::Coder, ClaudeFlowTask::Debugging), - ("Resolve memory leak", AgentType::Coder, ClaudeFlowTask::Debugging), - ("Fix off-by-one error", AgentType::Coder, ClaudeFlowTask::Debugging), - ("Handle edge case", AgentType::Coder, ClaudeFlowTask::Debugging), + ( + "Fix null pointer exception", + AgentType::Coder, + ClaudeFlowTask::Debugging, + ), + ( + "Resolve memory leak", + AgentType::Coder, + ClaudeFlowTask::Debugging, + ), + ( + "Fix off-by-one error", + AgentType::Coder, + ClaudeFlowTask::Debugging, + ), + ( + "Handle edge case", + AgentType::Coder, + ClaudeFlowTask::Debugging, + ), // Code generation patterns - ("Implement new function", AgentType::Coder, ClaudeFlowTask::CodeGeneration), - ("Add new feature", AgentType::Coder, ClaudeFlowTask::CodeGeneration), - ("Create API endpoint", AgentType::Coder, ClaudeFlowTask::CodeGeneration), - ("Build component", AgentType::Coder, ClaudeFlowTask::CodeGeneration), + ( + "Implement new function", + AgentType::Coder, + ClaudeFlowTask::CodeGeneration, + ), + ( + "Add new feature", + AgentType::Coder, + ClaudeFlowTask::CodeGeneration, + ), + ( + "Create API endpoint", + AgentType::Coder, + ClaudeFlowTask::CodeGeneration, + ), + ( + "Build component", + AgentType::Coder, + ClaudeFlowTask::CodeGeneration, + ), // Refactoring patterns - ("Refactor for performance", AgentType::Coder, ClaudeFlowTask::Refactoring), - ("Extract method", AgentType::Coder, ClaudeFlowTask::Refactoring), - ("Simplify code", AgentType::Coder, ClaudeFlowTask::Refactoring), + ( + "Refactor for performance", + AgentType::Coder, + ClaudeFlowTask::Refactoring, + ), + ( + "Extract method", + AgentType::Coder, + ClaudeFlowTask::Refactoring, + ), + ( + "Simplify code", + AgentType::Coder, + ClaudeFlowTask::Refactoring, + ), // Testing patterns - ("Write unit tests", AgentType::Tester, ClaudeFlowTask::Testing), - ("Add integration tests", AgentType::Tester, ClaudeFlowTask::Testing), - ("Increase test coverage", AgentType::Tester, ClaudeFlowTask::Testing), + ( + "Write unit tests", + AgentType::Tester, + ClaudeFlowTask::Testing, + ), + ( + "Add integration tests", + AgentType::Tester, + ClaudeFlowTask::Testing, + ), + ( + "Increase test coverage", + AgentType::Tester, + ClaudeFlowTask::Testing, + ), // Research patterns - ("Analyze codebase", AgentType::Researcher, ClaudeFlowTask::Research), - ("Find similar patterns", AgentType::Researcher, ClaudeFlowTask::Research), + ( + "Analyze codebase", + AgentType::Researcher, + ClaudeFlowTask::Research, + ), + ( + "Find similar patterns", + AgentType::Researcher, + ClaudeFlowTask::Research, + ), // Review patterns - ("Review code quality", AgentType::Reviewer, ClaudeFlowTask::CodeReview), - ("Security review", AgentType::Reviewer, ClaudeFlowTask::CodeReview), + ( + "Review code quality", + AgentType::Reviewer, + ClaudeFlowTask::CodeReview, + ), + ( + "Security review", + AgentType::Reviewer, + ClaudeFlowTask::CodeReview, + ), ]; for (i, (description, agent_type, task_type)) in seed_patterns.iter().enumerate() { // Create deterministic pseudo-embedding from description let embedding = Self::create_seed_embedding(description, dim, i); - let mut pattern = TaskPattern::new( - embedding, - *agent_type, - *task_type, - description.to_string(), - ); + let mut pattern = + TaskPattern::new(embedding, *agent_type, *task_type, description.to_string()); // Give seed patterns initial trust pattern.usage_count = 10; pattern.success_count = 8; @@ -211,7 +287,10 @@ impl RealEvaluationHarness { router.add_pattern(pattern)?; } - tracing::info!("Bootstrapped HNSW router with {} seed patterns", seed_patterns.len()); + tracing::info!( + "Bootstrapped HNSW router with {} seed patterns", + seed_patterns.len() + ); Ok(()) } @@ -253,7 +332,7 @@ impl RealEvaluationHarness { pub async fn run_evaluation(&mut self, tasks: &[EvalTask]) -> Result { if !self.is_model_loaded() { return Err(crate::RuvLLMError::InvalidOperation( - "No model loaded. Call load_model() first.".into() + "No model loaded. Call load_model() first.".into(), )); } @@ -322,7 +401,8 @@ impl RealEvaluationHarness { // Analyze diff quality let diff_quality = patch.as_ref().map(|p| { - self.diff_analyzer.analyze(p, task.reference_patch.as_deref()) + self.diff_analyzer + .analyze(p, task.reference_patch.as_deref()) }); // Build correctness result @@ -330,9 +410,9 @@ impl RealEvaluationHarness { // Determine acceptance let accepted = correctness.succeeded() - && diff_quality - .as_ref() - .map_or(false, |dq| dq.combined_score >= self.config.quality_threshold); + && diff_quality.as_ref().map_or(false, |dq| { + dq.combined_score >= self.config.quality_threshold + }); // ========== LEARNING ========== // Learn from this task in modes that support learning @@ -363,7 +443,8 @@ impl RealEvaluationHarness { let router = router.read(); // Get embedding for task - use seed embedding if backend can't provide - let embedding = self.get_embedding(task_description) + let embedding = self + .get_embedding(task_description) .unwrap_or_else(|_| Self::create_seed_embedding(task_description, 384, 0)); // Use full routing with confidence scores @@ -373,7 +454,9 @@ impl RealEvaluationHarness { primary_agent: hnsw_result.primary_agent, confidence: hnsw_result.confidence, patterns_considered: hnsw_result.patterns_considered, - alternatives: hnsw_result.alternatives.iter() + alternatives: hnsw_result + .alternatives + .iter() .map(|(agent, score)| format!("{:?}:{:.2}", agent, score)) .collect(), reasoning: hnsw_result.reasoning, @@ -394,7 +477,8 @@ impl RealEvaluationHarness { if let Some(ref router) = self.hnsw_router { let mut router = router.write(); - let embedding = self.get_embedding(&task.description) + let embedding = self + .get_embedding(&task.description) .unwrap_or_else(|_| Self::create_seed_embedding(&task.description, 384, 0)); // Determine task type from description @@ -442,7 +526,8 @@ impl RealEvaluationHarness { fn classify_task_type(description: &str) -> ClaudeFlowTask { let desc_lower = description.to_lowercase(); - if desc_lower.contains("fix") || desc_lower.contains("bug") || desc_lower.contains("error") { + if desc_lower.contains("fix") || desc_lower.contains("bug") || desc_lower.contains("error") + { ClaudeFlowTask::Debugging } else if desc_lower.contains("test") { ClaudeFlowTask::Testing @@ -470,10 +555,7 @@ impl RealEvaluationHarness { "Routing analysis (confidence: {:.1}%):\n", routing.confidence * 100.0 )); - context.push_str(&format!( - "- Primary agent: {:?}\n", - routing.primary_agent - )); + context.push_str(&format!("- Primary agent: {:?}\n", routing.primary_agent)); context.push_str(&format!( "- Patterns analyzed: {}\n", routing.patterns_considered @@ -564,10 +646,14 @@ impl RealEvaluationHarness { let mut prompt = String::new(); // Add context if using retrieval - if !context.is_empty() && matches!( - mode, - AblationMode::RetrievalOnly | AblationMode::RetrievalPlusAdapters | AblationMode::Full - ) { + if !context.is_empty() + && matches!( + mode, + AblationMode::RetrievalOnly + | AblationMode::RetrievalPlusAdapters + | AblationMode::Full + ) + { prompt.push_str(context); prompt.push_str("\n---\n\n"); } @@ -634,11 +720,11 @@ impl RealEvaluationHarness { test_results: None, // Would run actual tests verification_level: task.verification_level, human_verified: None, - files_changed: patch.as_ref().map_or(0, |p| { - p.matches("--- a/").count() - }), + files_changed: patch.as_ref().map_or(0, |p| p.matches("--- a/").count()), lines_changed: patch.as_ref().map_or(0, |p| { - p.lines().filter(|l| l.starts_with('+') || l.starts_with('-')).count() + p.lines() + .filter(|l| l.starts_with('+') || l.starts_with('-')) + .count() }), is_multi_file: task.expected_files.len() > 1, coupling_score: 0.3, @@ -670,8 +756,14 @@ impl RealEvaluationHarness { } // Add REAL latency samples - economics.latency.routing.add_secs(run.latency.routing_ms / 1000.0); - economics.latency.end_to_end.add_secs(run.latency.total_ms / 1000.0); + economics + .latency + .routing + .add_secs(run.latency.routing_ms / 1000.0); + economics + .latency + .end_to_end + .add_secs(run.latency.total_ms / 1000.0); } economics.recalculate(); diff --git a/crates/ruvllm/src/evaluation/report.rs b/crates/ruvllm/src/evaluation/report.rs index 674b6c7e0..e15056afe 100644 --- a/crates/ruvllm/src/evaluation/report.rs +++ b/crates/ruvllm/src/evaluation/report.rs @@ -113,8 +113,8 @@ impl EvalReport { let cost_delta = target.economics.cost_per_accepted_patch - baseline.economics.cost_per_accepted_patch; - let latency_delta = target.economics.latency.end_to_end.p95() - - baseline.economics.latency.end_to_end.p95(); + let latency_delta = + target.economics.latency.end_to_end.p95() - baseline.economics.latency.end_to_end.p95(); // Simple significance check (would use proper stats in production) let is_significant = success_delta.abs() > 0.05; @@ -149,7 +149,10 @@ impl EvalReport { "- Quality threshold: {:.0}%\n", self.config.quality_threshold * 100.0 )); - md.push_str(&format!("- Cost target: ${:.2}\n\n", self.config.cost_target)); + md.push_str(&format!( + "- Cost target: ${:.2}\n\n", + self.config.cost_target + )); // Leaderboard md.push_str("## Results Leaderboard\n\n"); @@ -219,13 +222,9 @@ impl EvalReport { // Recommendations md.push_str("\n## Recommendations\n\n"); - md.push_str( - "1. Use Full mode (Retrieval + Adapters + SONA) for maximum accuracy\n", - ); + md.push_str("1. Use Full mode (Retrieval + Adapters + SONA) for maximum accuracy\n"); md.push_str("2. Use Retrieval Only mode for cost-sensitive deployments\n"); - md.push_str( - "3. Monitor p95 latency under load - consider batching for high throughput\n", - ); + md.push_str("3. Monitor p95 latency under load - consider batching for high throughput\n"); md } diff --git a/crates/ruvllm/src/evaluation/swe_bench.rs b/crates/ruvllm/src/evaluation/swe_bench.rs index ecd21aa2d..db97d7e6b 100644 --- a/crates/ruvllm/src/evaluation/swe_bench.rs +++ b/crates/ruvllm/src/evaluation/swe_bench.rs @@ -26,8 +26,8 @@ //! let eval_tasks: Vec = tasks.into_iter().map(|t| t.into()).collect(); //! ``` -use super::harness::EvalTask; use super::correctness::VerificationLevel; +use super::harness::EvalTask; use crate::error::{Result, RuvLLMError}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -35,8 +35,10 @@ use std::fs; use std::path::{Path, PathBuf}; /// SWE-Bench dataset URLs -pub const SWE_BENCH_LITE_URL: &str = "https://raw.githubusercontent.com/princeton-nlp/SWE-bench/main/swe-bench-lite.json"; -pub const SWE_BENCH_FULL_URL: &str = "https://raw.githubusercontent.com/princeton-nlp/SWE-bench/main/swe-bench.json"; +pub const SWE_BENCH_LITE_URL: &str = + "https://raw.githubusercontent.com/princeton-nlp/SWE-bench/main/swe-bench-lite.json"; +pub const SWE_BENCH_FULL_URL: &str = + "https://raw.githubusercontent.com/princeton-nlp/SWE-bench/main/swe-bench.json"; /// Configuration for SWE-Bench loader #[derive(Debug, Clone)] @@ -249,8 +251,9 @@ impl SweBenchLoader { /// Load tasks from a local JSON file pub fn load_from_file>(&self, path: P) -> Result> { let path = path.as_ref(); - let content = fs::read_to_string(path) - .map_err(|e| RuvLLMError::Storage(format!("Failed to read {}: {}", path.display(), e)))?; + let content = fs::read_to_string(path).map_err(|e| { + RuvLLMError::Storage(format!("Failed to read {}: {}", path.display(), e)) + })?; self.parse_tasks(&content) } @@ -258,8 +261,9 @@ impl SweBenchLoader { /// Load tasks from a JSONL file (one JSON object per line) pub fn load_from_jsonl>(&self, path: P) -> Result> { let path = path.as_ref(); - let content = fs::read_to_string(path) - .map_err(|e| RuvLLMError::Storage(format!("Failed to read {}: {}", path.display(), e)))?; + let content = fs::read_to_string(path).map_err(|e| { + RuvLLMError::Storage(format!("Failed to read {}: {}", path.display(), e)) + })?; let mut tasks = Vec::new(); for (i, line) in content.lines().enumerate() { @@ -284,8 +288,9 @@ impl SweBenchLoader { Ok(arr) => arr, Err(_) => { // Try parsing as single object - let task: SweBenchTask = serde_json::from_str(content) - .map_err(|e| RuvLLMError::Serialization(format!("Failed to parse JSON: {}", e)))?; + let task: SweBenchTask = serde_json::from_str(content).map_err(|e| { + RuvLLMError::Serialization(format!("Failed to parse JSON: {}", e)) + })?; vec![task] } }; @@ -377,17 +382,21 @@ impl SweBenchLoader { instance_id: "django__django-11099".to_string(), repo: "django/django".to_string(), base_commit: "abc123".to_string(), - problem_statement: "UsernameValidator allows trailing newline in username".to_string(), - hints_text: "The regex in UsernameValidator should use \\Z instead of $".to_string(), + problem_statement: "UsernameValidator allows trailing newline in username" + .to_string(), + hints_text: "The regex in UsernameValidator should use \\Z instead of $" + .to_string(), patch: r#"--- a/django/contrib/auth/validators.py +++ b/django/contrib/auth/validators.py @@ -8,7 +8,7 @@ class ASCIIUsernameValidator(validators.RegexValidator): - regex = r'^[\w.@+-]+$' + regex = r'^[\w.@+-]+\Z' -"#.to_string(), +"# + .to_string(), test_patch: String::new(), expected_files: vec!["django/contrib/auth/validators.py".to_string()], - test_cmd: "python -m pytest django/contrib/auth/tests/test_validators.py".to_string(), + test_cmd: "python -m pytest django/contrib/auth/tests/test_validators.py" + .to_string(), env_setup_cmd: String::new(), version: "3.8".to_string(), difficulty: Some("easy".to_string()), @@ -397,7 +406,8 @@ impl SweBenchLoader { instance_id: "requests__requests-4356".to_string(), repo: "psf/requests".to_string(), base_commit: "def456".to_string(), - problem_statement: "Session.request does not honor the `json` parameter".to_string(), + problem_statement: "Session.request does not honor the `json` parameter" + .to_string(), hints_text: "Check how json parameter is passed in Session.request".to_string(), patch: r#"--- a/requests/sessions.py +++ b/requests/sessions.py @@ -407,7 +417,8 @@ impl SweBenchLoader { url=url, + json=json, headers=headers, -"#.to_string(), +"# + .to_string(), test_patch: String::new(), expected_files: vec!["requests/sessions.py".to_string()], test_cmd: "python -m pytest tests/test_requests.py".to_string(), @@ -424,7 +435,10 @@ impl SweBenchLoader { hints_text: "Need to detect and await async functions in dispatch".to_string(), patch: String::new(), // No gold patch - harder task test_patch: String::new(), - expected_files: vec!["src/flask/app.py".to_string(), "src/flask/views.py".to_string()], + expected_files: vec![ + "src/flask/app.py".to_string(), + "src/flask/views.py".to_string(), + ], test_cmd: "python -m pytest tests/".to_string(), env_setup_cmd: String::new(), version: "3.10".to_string(), @@ -487,12 +501,18 @@ impl std::fmt::Display for SweBenchStats { writeln!(f, "SWE-Bench Dataset Statistics")?; writeln!(f, "============================")?; writeln!(f, "Total tasks: {}", self.total_tasks)?; - writeln!(f, "With gold patches: {} ({:.1}%)", + writeln!( + f, + "With gold patches: {} ({:.1}%)", self.with_gold_patch, - self.with_gold_patch as f64 / self.total_tasks as f64 * 100.0)?; - writeln!(f, "With test commands: {} ({:.1}%)", + self.with_gold_patch as f64 / self.total_tasks as f64 * 100.0 + )?; + writeln!( + f, + "With test commands: {} ({:.1}%)", self.with_tests, - self.with_tests as f64 / self.total_tasks as f64 * 100.0)?; + self.with_tests as f64 / self.total_tasks as f64 * 100.0 + )?; writeln!(f, "\nBy Repository:")?; let mut repos: Vec<_> = self.repos.iter().collect(); @@ -542,7 +562,8 @@ mod tests { +new --- a/file2.py +++ b/file2.py -"#.to_string(), +"# + .to_string(), ..Default::default() }; diff --git a/crates/ruvllm/src/gguf/loader.rs b/crates/ruvllm/src/gguf/loader.rs index f0e0b2747..6fc8eecf4 100644 --- a/crates/ruvllm/src/gguf/loader.rs +++ b/crates/ruvllm/src/gguf/loader.rs @@ -45,9 +45,9 @@ use std::path::Path; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; +use super::{GgufFile, GgufQuantType, ModelConfig as GgufConfig, QuantizedTensor, TensorInfo}; use crate::backends::ModelArchitecture; use crate::error::{Result, RuvLLMError}; -use super::{GgufFile, GgufQuantType, QuantizedTensor, TensorInfo, ModelConfig as GgufConfig}; // ============================================================================ // Progress Tracking @@ -393,7 +393,8 @@ impl TensorNameMapper { // Normalization if lower.contains("norm") || lower.contains("ln_") || lower.contains("layer_norm") { - if lower.contains("final") || lower.contains("model.norm") || !lower.contains("layers") { + if lower.contains("final") || lower.contains("model.norm") || !lower.contains("layers") + { return TensorCategory::FinalNorm; } if lower.contains("input") || lower.contains("attn") || lower.contains("attention") { @@ -538,11 +539,16 @@ impl GgufLoader { let (normalized_name, layer_index, category) = mapper.map(&tensor_info.name); // Load tensor data - let loaded = self.load_single_tensor(tensor_info, &normalized_name, layer_index, category)?; + let loaded = + self.load_single_tensor(tensor_info, &normalized_name, layer_index, category)?; // Update memory tracking let tensor_bytes = loaded.data_f32.as_ref().map(|d| d.len() * 4).unwrap_or(0) - + loaded.data_quantized.as_ref().map(|q| q.data.len()).unwrap_or(0); + + loaded + .data_quantized + .as_ref() + .map(|q| q.data.len()) + .unwrap_or(0); weights.memory_bytes += tensor_bytes; // Store tensor @@ -550,7 +556,9 @@ impl GgufLoader { // Update progress let count = self.loaded_count.fetch_add(1, Ordering::Relaxed) + 1; - let bytes = self.loaded_bytes.fetch_add(tensor_info.byte_size(), Ordering::Relaxed) + let bytes = self + .loaded_bytes + .fetch_add(tensor_info.byte_size(), Ordering::Relaxed) + tensor_info.byte_size(); if let Some(ref callback) = self.config.progress_callback { @@ -603,7 +611,8 @@ impl GgufLoader { } let (normalized_name, layer_idx, category) = mapper.map(&tensor_info.name); - let loaded = self.load_single_tensor(tensor_info, &normalized_name, layer_idx, category)?; + let loaded = + self.load_single_tensor(tensor_info, &normalized_name, layer_idx, category)?; tensors.push(loaded); } @@ -612,9 +621,10 @@ impl GgufLoader { /// Load a single tensor by name. pub fn load_tensor(&self, name: &str) -> Result { - let tensor_info = self.file.get_tensor(name).ok_or_else(|| { - RuvLLMError::NotFound(format!("Tensor not found: {}", name)) - })?; + let tensor_info = self + .file + .get_tensor(name) + .ok_or_else(|| RuvLLMError::NotFound(format!("Tensor not found: {}", name)))?; let mapper = self.mapper.as_ref(); let (normalized_name, layer_idx, category) = mapper @@ -632,7 +642,8 @@ impl GgufLoader { layer_index: Option, category: TensorCategory, ) -> Result { - let (data_f32, data_quantized) = if self.config.keep_quantized && info.dtype.is_quantized() { + let (data_f32, data_quantized) = if self.config.keep_quantized && info.dtype.is_quantized() + { // Keep as quantized let quantized = self.file.load_tensor_quantized(&info.name)?; (None, Some(quantized)) @@ -658,9 +669,11 @@ impl GgufLoader { fn should_load_tensor(&self, info: &TensorInfo) -> bool { // Check tensor filter if !self.config.tensor_filter.is_empty() { - let matches = self.config.tensor_filter.iter().any(|pattern| { - info.name.contains(pattern) - }); + let matches = self + .config + .tensor_filter + .iter() + .any(|pattern| info.name.contains(pattern)); if !matches { return false; } @@ -731,9 +744,11 @@ impl StreamingLoader { /// Load embedding and pre-layer normalization tensors. pub fn load_embeddings(&self) -> Result> { - let mapper = self.loader.mapper.as_ref().ok_or_else(|| { - RuvLLMError::Model("Unknown architecture".to_string()) - })?; + let mapper = self + .loader + .mapper + .as_ref() + .ok_or_else(|| RuvLLMError::Model("Unknown architecture".to_string()))?; let mut tensors = Vec::new(); @@ -769,9 +784,11 @@ impl StreamingLoader { /// Load final normalization and output head tensors. pub fn load_output_head(&self) -> Result> { - let mapper = self.loader.mapper.as_ref().ok_or_else(|| { - RuvLLMError::Model("Unknown architecture".to_string()) - })?; + let mapper = self + .loader + .mapper + .as_ref() + .ok_or_else(|| RuvLLMError::Model("Unknown architecture".to_string()))?; let mut tensors = Vec::new(); @@ -784,7 +801,10 @@ impl StreamingLoader { } // Load output head and final norm - if matches!(category, TensorCategory::OutputHead | TensorCategory::FinalNorm) { + if matches!( + category, + TensorCategory::OutputHead | TensorCategory::FinalNorm + ) { let loaded = self.loader.load_tensor(&tensor_info.name)?; tensors.push(loaded); } @@ -842,10 +862,22 @@ mod tests { let mapper = TensorNameMapper::new(ModelArchitecture::Llama); // Attention components - assert_eq!(mapper.categorize("self_attn.q_proj"), TensorCategory::AttentionQuery); - assert_eq!(mapper.categorize("attention.k_proj"), TensorCategory::AttentionKey); - assert_eq!(mapper.categorize("self_attn.v_proj"), TensorCategory::AttentionValue); - assert_eq!(mapper.categorize("attn.o_proj"), TensorCategory::AttentionOutput); + assert_eq!( + mapper.categorize("self_attn.q_proj"), + TensorCategory::AttentionQuery + ); + assert_eq!( + mapper.categorize("attention.k_proj"), + TensorCategory::AttentionKey + ); + assert_eq!( + mapper.categorize("self_attn.v_proj"), + TensorCategory::AttentionValue + ); + assert_eq!( + mapper.categorize("attn.o_proj"), + TensorCategory::AttentionOutput + ); // MLP components assert_eq!(mapper.categorize("mlp.gate_proj"), TensorCategory::FfnGate); @@ -853,10 +885,16 @@ mod tests { assert_eq!(mapper.categorize("mlp.down_proj"), TensorCategory::FfnDown); // Normalization - assert_eq!(mapper.categorize("model.norm.weight"), TensorCategory::FinalNorm); + assert_eq!( + mapper.categorize("model.norm.weight"), + TensorCategory::FinalNorm + ); // Output - assert_eq!(mapper.categorize("lm_head.weight"), TensorCategory::OutputHead); + assert_eq!( + mapper.categorize("lm_head.weight"), + TensorCategory::OutputHead + ); } #[test] diff --git a/crates/ruvllm/src/gguf/mod.rs b/crates/ruvllm/src/gguf/mod.rs index 59fc159b1..9504883fc 100644 --- a/crates/ruvllm/src/gguf/mod.rs +++ b/crates/ruvllm/src/gguf/mod.rs @@ -60,11 +60,11 @@ //! )?; //! ``` +pub mod loader; +pub mod model_init; pub mod parser; pub mod quantization; pub mod tensors; -pub mod loader; -pub mod model_init; use std::collections::HashMap; use std::fs::File; @@ -74,20 +74,20 @@ use std::path::Path; #[cfg(unix)] use std::os::unix::fs::FileExt; -use crate::error::{Result, RuvLLMError}; use crate::backends::ModelArchitecture; +use crate::error::{Result, RuvLLMError}; -pub use parser::{GgufHeader, GgufValue, parse_header, parse_metadata, parse_tensor_infos}; -pub use quantization::{GgufQuantType, QuantizedTensor, dequantize_block}; -pub use tensors::TensorInfo; pub use loader::{ - GgufLoader, LoadConfig, LoadProgress, LoadedWeights, LoadedTensor, - TensorCategory, TensorNameMapper, StreamingLoader, ProgressCallback, + GgufLoader, LoadConfig, LoadProgress, LoadedTensor, LoadedWeights, ProgressCallback, + StreamingLoader, TensorCategory, TensorNameMapper, }; pub use model_init::{ - ModelInitializer, ModelWeights, LayerWeights, WeightTensor, QuantizedWeight, - ProgressModelBuilder, + LayerWeights, ModelInitializer, ModelWeights, ProgressModelBuilder, QuantizedWeight, + WeightTensor, }; +pub use parser::{parse_header, parse_metadata, parse_tensor_infos, GgufHeader, GgufValue}; +pub use quantization::{dequantize_block, GgufQuantType, QuantizedTensor}; +pub use tensors::TensorInfo; // ============================================================================ // GGUF File Magic and Constants @@ -176,9 +176,8 @@ impl GgufFile { /// - The file is not a valid GGUF file /// - The GGUF version is not supported pub fn open(path: &Path) -> Result { - let file = File::open(path).map_err(|e| { - RuvLLMError::Model(format!("Failed to open GGUF file: {}", e)) - })?; + let file = File::open(path) + .map_err(|e| RuvLLMError::Model(format!("Failed to open GGUF file: {}", e)))?; let mut reader = BufReader::new(file); // Parse header @@ -213,9 +212,9 @@ impl GgufFile { let tensors = parse_tensor_infos(&mut reader, header.tensor_count)?; // Calculate data offset (aligned) - let current_pos = reader.stream_position().map_err(|e| { - RuvLLMError::Model(format!("Failed to get stream position: {}", e)) - })?; + let current_pos = reader + .stream_position() + .map_err(|e| RuvLLMError::Model(format!("Failed to get stream position: {}", e)))?; let data_offset = align_offset(current_pos, alignment as u64); Ok(Self { @@ -249,14 +248,12 @@ impl GgufFile { pub fn open_mmap(path: &Path) -> Result { let mut gguf = Self::open(path)?; - let file = File::open(path).map_err(|e| { - RuvLLMError::Model(format!("Failed to open file for mmap: {}", e)) - })?; + let file = File::open(path) + .map_err(|e| RuvLLMError::Model(format!("Failed to open file for mmap: {}", e)))?; let mmap = unsafe { - memmap2::Mmap::map(&file).map_err(|e| { - RuvLLMError::Model(format!("Failed to memory map file: {}", e)) - })? + memmap2::Mmap::map(&file) + .map_err(|e| RuvLLMError::Model(format!("Failed to memory map file: {}", e)))? }; gguf.mmap = Some(MmapData { mmap }); @@ -269,9 +266,8 @@ impl GgufFile { let mut gguf = Self::open(path)?; // Read entire file into memory as fallback - let data = std::fs::read(path).map_err(|e| { - RuvLLMError::Model(format!("Failed to read file: {}", e)) - })?; + let data = std::fs::read(path) + .map_err(|e| RuvLLMError::Model(format!("Failed to read file: {}", e)))?; gguf.mmap = Some(MmapData { data }); Ok(gguf) @@ -307,9 +303,9 @@ impl GgufFile { /// /// Returns an error if the tensor is not found or cannot be read pub fn load_tensor_f32(&self, name: &str) -> Result> { - let info = self.get_tensor(name).ok_or_else(|| { - RuvLLMError::NotFound(format!("Tensor not found: {}", name)) - })?; + let info = self + .get_tensor(name) + .ok_or_else(|| RuvLLMError::NotFound(format!("Tensor not found: {}", name)))?; let raw_data = self.read_tensor_bytes(info)?; let num_elements: usize = info.shape.iter().product(); @@ -332,9 +328,9 @@ impl GgufFile { /// /// A `QuantizedTensor` containing the raw quantized data pub fn load_tensor_quantized(&self, name: &str) -> Result { - let info = self.get_tensor(name).ok_or_else(|| { - RuvLLMError::NotFound(format!("Tensor not found: {}", name)) - })?; + let info = self + .get_tensor(name) + .ok_or_else(|| RuvLLMError::NotFound(format!("Tensor not found: {}", name)))?; let data = self.read_tensor_bytes(info)?; let num_elements: usize = info.shape.iter().product(); @@ -396,9 +392,9 @@ impl GgufFile { where F: FnMut(&[f32]) -> Result<()>, { - let info = self.get_tensor(name).ok_or_else(|| { - RuvLLMError::NotFound(format!("Tensor not found: {}", name)) - })?; + let info = self + .get_tensor(name) + .ok_or_else(|| RuvLLMError::NotFound(format!("Tensor not found: {}", name)))?; let _num_elements: usize = info.shape.iter().product(); @@ -438,14 +434,15 @@ impl GgufFile { /// Get the model architecture as enum. pub fn architecture_type(&self) -> Option { - self.architecture().and_then(|arch| match arch.to_lowercase().as_str() { - "llama" => Some(ModelArchitecture::Llama), - "mistral" => Some(ModelArchitecture::Mistral), - "phi" | "phi2" | "phi3" => Some(ModelArchitecture::Phi), - "qwen" | "qwen2" => Some(ModelArchitecture::Qwen), - "gemma" => Some(ModelArchitecture::Gemma), - _ => None, - }) + self.architecture() + .and_then(|arch| match arch.to_lowercase().as_str() { + "llama" => Some(ModelArchitecture::Llama), + "mistral" => Some(ModelArchitecture::Mistral), + "phi" | "phi2" | "phi3" => Some(ModelArchitecture::Phi), + "qwen" | "qwen2" => Some(ModelArchitecture::Qwen), + "gemma" => Some(ModelArchitecture::Gemma), + _ => None, + }) } /// Get the context length (max sequence length). @@ -538,16 +535,12 @@ impl GgufFile { /// Get the model name. pub fn model_name(&self) -> Option<&str> { - self.metadata - .get("general.name") - .and_then(|v| v.as_str()) + self.metadata.get("general.name").and_then(|v| v.as_str()) } /// Get the model author. pub fn author(&self) -> Option<&str> { - self.metadata - .get("general.author") - .and_then(|v| v.as_str()) + self.metadata.get("general.author").and_then(|v| v.as_str()) } /// Get the quantization type description. @@ -585,9 +578,8 @@ impl GgufFile { } // Read from file - let mut file = File::open(&self.path).map_err(|e| { - RuvLLMError::Model(format!("Failed to open file: {}", e)) - })?; + let mut file = File::open(&self.path) + .map_err(|e| RuvLLMError::Model(format!("Failed to open file: {}", e)))?; file.seek(SeekFrom::Start(self.data_offset + info.offset)) .map_err(|e| RuvLLMError::Model(format!("Failed to seek: {}", e)))?; @@ -604,9 +596,8 @@ impl GgufFile { F: FnMut(&[f32]) -> Result<()>, { let num_elements: usize = info.shape.iter().product(); - let mut file = File::open(&self.path).map_err(|e| { - RuvLLMError::Model(format!("Failed to open file: {}", e)) - })?; + let mut file = File::open(&self.path) + .map_err(|e| RuvLLMError::Model(format!("Failed to open file: {}", e)))?; file.seek(SeekFrom::Start(self.data_offset + info.offset)) .map_err(|e| RuvLLMError::Model(format!("Failed to seek: {}", e)))?; @@ -639,9 +630,8 @@ impl GgufFile { F: FnMut(&[f32]) -> Result<()>, { let num_elements: usize = info.shape.iter().product(); - let mut file = File::open(&self.path).map_err(|e| { - RuvLLMError::Model(format!("Failed to open file: {}", e)) - })?; + let mut file = File::open(&self.path) + .map_err(|e| RuvLLMError::Model(format!("Failed to open file: {}", e)))?; file.seek(SeekFrom::Start(self.data_offset + info.offset)) .map_err(|e| RuvLLMError::Model(format!("Failed to seek: {}", e)))?; @@ -760,7 +750,10 @@ impl GgufModelLoader { } } - counts.into_iter().max_by_key(|(_, count)| *count).map(|(dtype, _)| dtype) + counts + .into_iter() + .max_by_key(|(_, count)| *count) + .map(|(dtype, _)| dtype) } /// Convert to a Candle-compatible model (stub for integration). diff --git a/crates/ruvllm/src/gguf/model_init.rs b/crates/ruvllm/src/gguf/model_init.rs index 736f2ebb9..796aa7652 100644 --- a/crates/ruvllm/src/gguf/model_init.rs +++ b/crates/ruvllm/src/gguf/model_init.rs @@ -36,12 +36,11 @@ use std::collections::HashMap; use std::sync::Arc; +use super::{ + GgufQuantType, LoadedTensor, LoadedWeights, ModelConfig, QuantizedTensor, TensorCategory, +}; use crate::backends::ModelArchitecture; use crate::error::{Result, RuvLLMError}; -use super::{ - LoadedWeights, LoadedTensor, TensorCategory, GgufQuantType, ModelConfig, - QuantizedTensor, -}; // ============================================================================ // Model Layer Weights @@ -316,9 +315,9 @@ impl TensorNameMap { impl ModelInitializer { /// Create a new model initializer from loaded weights. pub fn new(weights: LoadedWeights) -> Result { - let architecture = weights.architecture().ok_or_else(|| { - RuvLLMError::Model("Cannot determine model architecture".to_string()) - })?; + let architecture = weights + .architecture() + .ok_or_else(|| RuvLLMError::Model("Cannot determine model architecture".to_string()))?; let tensor_map = match architecture { ModelArchitecture::Llama => TensorNameMap::llama(), @@ -471,10 +470,7 @@ impl ModelInitializer { /// Extract the key identifying part of a tensor name. fn extract_key_part(&self, name: &str) -> String { // Extract the last meaningful part of the name - name.split('.') - .last() - .unwrap_or(name) - .to_string() + name.split('.').last().unwrap_or(name).to_string() } } @@ -537,14 +533,23 @@ mod tests { #[test] fn test_tensor_name_map_llama() { let map = TensorNameMap::llama(); - assert_eq!(map.layer_tensor(map.q_proj, 0), "model.layers.0.self_attn.q_proj.weight"); - assert_eq!(map.layer_tensor(map.gate_proj, 5), "model.layers.5.mlp.gate_proj.weight"); + assert_eq!( + map.layer_tensor(map.q_proj, 0), + "model.layers.0.self_attn.q_proj.weight" + ); + assert_eq!( + map.layer_tensor(map.gate_proj, 5), + "model.layers.5.mlp.gate_proj.weight" + ); } #[test] fn test_tensor_name_map_phi() { let map = TensorNameMap::phi(); - assert_eq!(map.layer_tensor(map.o_proj, 2), "transformer.h.2.mixer.out_proj.weight"); + assert_eq!( + map.layer_tensor(map.o_proj, 2), + "transformer.h.2.mixer.out_proj.weight" + ); } #[test] diff --git a/crates/ruvllm/src/gguf/parser.rs b/crates/ruvllm/src/gguf/parser.rs index fdd13f2b6..906ca0877 100644 --- a/crates/ruvllm/src/gguf/parser.rs +++ b/crates/ruvllm/src/gguf/parser.rs @@ -29,9 +29,9 @@ use std::collections::HashMap; use std::io::{BufRead, Read}; -use crate::error::{Result, RuvLLMError}; use super::quantization::GgufQuantType; use super::tensors::TensorInfo; +use crate::error::{Result, RuvLLMError}; // ============================================================================ // Header Structure @@ -222,7 +222,10 @@ impl TryFrom for GgufValueType { 10 => Ok(Self::U64), 11 => Ok(Self::I64), 12 => Ok(Self::F64), - _ => Err(RuvLLMError::Model(format!("Unknown GGUF value type: {}", value))), + _ => Err(RuvLLMError::Model(format!( + "Unknown GGUF value type: {}", + value + ))), } } } @@ -460,9 +463,7 @@ fn read_string(reader: &mut R) -> Result { let mut buf = vec![0u8; len]; reader.read_exact(&mut buf).map_err(read_err)?; - String::from_utf8(buf).map_err(|e| { - RuvLLMError::Model(format!("Invalid UTF-8 string: {}", e)) - }) + String::from_utf8(buf).map_err(|e| RuvLLMError::Model(format!("Invalid UTF-8 string: {}", e))) } fn read_err(e: std::io::Error) -> RuvLLMError { diff --git a/crates/ruvllm/src/gguf/quantization.rs b/crates/ruvllm/src/gguf/quantization.rs index 15fd60f57..ef15a3b31 100644 --- a/crates/ruvllm/src/gguf/quantization.rs +++ b/crates/ruvllm/src/gguf/quantization.rs @@ -332,7 +332,11 @@ impl QuantizedTensor { /// # Returns /// /// Vector of FP32 values -pub fn dequantize_tensor(data: &[u8], dtype: GgufQuantType, num_elements: usize) -> Result> { +pub fn dequantize_tensor( + data: &[u8], + dtype: GgufQuantType, + num_elements: usize, +) -> Result> { let mut output = vec![0.0f32; num_elements]; match dtype { @@ -901,8 +905,8 @@ const IQ4_NL_TYPE_SIZE: usize = 18; // Non-linear quantization lookup table (simplified version) const IQ4_NL_LUT: [f32; 16] = [ - -1.0, -0.75, -0.5, -0.375, -0.25, -0.125, 0.0, 0.125, - 0.25, 0.375, 0.5, 0.75, 1.0, 1.5, 2.0, 3.0, + -1.0, -0.75, -0.5, -0.375, -0.25, -0.125, 0.0, 0.125, 0.25, 0.375, 0.5, 0.75, 1.0, 1.5, 2.0, + 3.0, ]; fn dequantize_iq4_nl(data: &[u8], output: &mut [f32]) { diff --git a/crates/ruvllm/src/gguf/tensors.rs b/crates/ruvllm/src/gguf/tensors.rs index 8f9c908af..918bedeb1 100644 --- a/crates/ruvllm/src/gguf/tensors.rs +++ b/crates/ruvllm/src/gguf/tensors.rs @@ -58,9 +58,7 @@ impl TensorInfo { /// Check if this is a feed-forward tensor. pub fn is_ffn(&self) -> bool { - self.name.contains("ffn") - || self.name.contains("feed_forward") - || self.name.contains("mlp") + self.name.contains("ffn") || self.name.contains("feed_forward") || self.name.contains("mlp") } /// Check if this is a normalization tensor. @@ -282,11 +280,24 @@ fn extract_layer_index(name: &str) -> Option { fn extract_tensor_type(name: &str) -> String { let suffixes = [ - "weight", "bias", "scale", "norm", - "wq", "wk", "wv", "wo", - "w1", "w2", "w3", - "q_proj", "k_proj", "v_proj", "o_proj", - "gate_proj", "up_proj", "down_proj", + "weight", + "bias", + "scale", + "norm", + "wq", + "wk", + "wv", + "wo", + "w1", + "w2", + "w3", + "q_proj", + "k_proj", + "v_proj", + "o_proj", + "gate_proj", + "up_proj", + "down_proj", ]; for suffix in &suffixes { @@ -348,7 +359,10 @@ mod tests { #[test] fn test_layer_index_parsing() { assert_eq!(make_tensor("model.layers.0.weight").layer_index(), Some(0)); - assert_eq!(make_tensor("model.layers.15.weight").layer_index(), Some(15)); + assert_eq!( + make_tensor("model.layers.15.weight").layer_index(), + Some(15) + ); assert_eq!(make_tensor("transformer.h.7.weight").layer_index(), Some(7)); assert_eq!(make_tensor("model.embed_tokens.weight").layer_index(), None); } diff --git a/crates/ruvllm/src/hub/download.rs b/crates/ruvllm/src/hub/download.rs index 30f2b91ec..8ac206769 100644 --- a/crates/ruvllm/src/hub/download.rs +++ b/crates/ruvllm/src/hub/download.rs @@ -1,13 +1,13 @@ //! Model download functionality with progress tracking and resume support -use super::{HubError, Result, default_cache_dir, get_hf_token}; -use super::registry::ModelInfo; use super::progress::{ProgressBar, ProgressStyle}; +use super::registry::ModelInfo; +use super::{default_cache_dir, get_hf_token, HubError, Result}; +use regex::Regex; +use sha2::{Digest, Sha256}; use std::fs::{self, File}; use std::io::{self, BufWriter, Write}; use std::path::{Path, PathBuf}; -use sha2::{Sha256, Digest}; -use regex::Regex; // ============================================================================ // Security: URL and Input Validation (H-001) @@ -37,9 +37,9 @@ fn validate_url(url: &str) -> Result<()> { let host = host.split(':').next().unwrap_or(host); // Check against allowlist - let is_allowed = ALLOWED_DOMAINS.iter().any(|&domain| { - host == domain || host.ends_with(&format!(".{}", domain)) - }); + let is_allowed = ALLOWED_DOMAINS + .iter() + .any(|&domain| host == domain || host.ends_with(&format!(".{}", domain))); if !is_allowed { return Err(HubError::InvalidFormat(format!( @@ -86,9 +86,9 @@ fn validate_repo_id(repo_id: &str) -> Result<()> { /// Canonicalize and validate file path to prevent path traversal fn validate_and_canonicalize_path(path: &Path, base_dir: &Path) -> Result { // Canonicalize both paths - let canonical_base = base_dir.canonicalize().map_err(|e| { - HubError::Config(format!("Failed to canonicalize base directory: {}", e)) - })?; + let canonical_base = base_dir + .canonicalize() + .map_err(|e| HubError::Config(format!("Failed to canonicalize base directory: {}", e)))?; // Create parent directories if needed, then canonicalize if let Some(parent) = path.parent() { @@ -97,16 +97,16 @@ fn validate_and_canonicalize_path(path: &Path, base_dir: &Path) -> Result, - ) -> Result { + pub fn download(&self, model_info: &ModelInfo, target_path: Option<&Path>) -> Result { // Determine target path let path = if let Some(p) = target_path { p.to_path_buf() @@ -310,7 +306,12 @@ impl ModelDownloader { // SECURITY: Validate URL is from allowed domains validate_url(&url)?; - self.download_file(&url, &path, model_info.size_bytes, model_info.checksum.as_deref())?; + self.download_file( + &url, + &path, + model_info.size_bytes, + model_info.checksum.as_deref(), + )?; Ok(path) } @@ -363,7 +364,7 @@ impl ModelDownloader { expected_checksum: Option<&str>, ) -> Result<()> { let mut args = vec![ - "-L".to_string(), // Follow redirects + "-L".to_string(), // Follow redirects "-#".to_string(), // Progress bar "--fail".to_string(), // Fail on HTTP errors ]; @@ -415,7 +416,7 @@ impl ModelDownloader { expected_checksum: Option<&str>, ) -> Result<()> { let mut args = vec![ - "-q".to_string(), // Quiet + "-q".to_string(), // Quiet "--show-progress".to_string(), // But show progress ]; diff --git a/crates/ruvllm/src/hub/mod.rs b/crates/ruvllm/src/hub/mod.rs index ccab8b116..b27f8a6cd 100644 --- a/crates/ruvllm/src/hub/mod.rs +++ b/crates/ruvllm/src/hub/mod.rs @@ -30,32 +30,25 @@ //! ``` pub mod download; -pub mod upload; -pub mod registry; pub mod model_card; pub mod progress; +pub mod registry; +pub mod upload; // Re-exports pub use download::{ - ModelDownloader, DownloadConfig, DownloadProgress, - DownloadError, ChecksumVerifier, -}; -pub use upload::{ - ModelUploader, UploadConfig, UploadProgress, - UploadError, ModelMetadata, -}; -pub use registry::{ - RuvLtraRegistry, ModelInfo, ModelSize, QuantizationLevel, - HardwareRequirements, get_model_info, + ChecksumVerifier, DownloadConfig, DownloadError, DownloadProgress, ModelDownloader, }; pub use model_card::{ - ModelCard, ModelCardBuilder, TaskType, Framework, - License, DatasetInfo, MetricResult, + DatasetInfo, Framework, License, MetricResult, ModelCard, ModelCardBuilder, TaskType, }; pub use progress::{ - ProgressBar, ProgressIndicator, ProgressStyle, - ProgressCallback, MultiProgress, + MultiProgress, ProgressBar, ProgressCallback, ProgressIndicator, ProgressStyle, }; +pub use registry::{ + get_model_info, HardwareRequirements, ModelInfo, ModelSize, QuantizationLevel, RuvLtraRegistry, +}; +pub use upload::{ModelMetadata, ModelUploader, UploadConfig, UploadError, UploadProgress}; use std::path::PathBuf; @@ -84,10 +77,7 @@ pub enum HubError { /// Checksum mismatch #[error("Checksum verification failed: expected {expected}, got {actual}")] - ChecksumMismatch { - expected: String, - actual: String, - }, + ChecksumMismatch { expected: String, actual: String }, /// Invalid model format #[error("Invalid model format: {0}")] diff --git a/crates/ruvllm/src/hub/model_card.rs b/crates/ruvllm/src/hub/model_card.rs index 974fe7a1d..4df8105b0 100644 --- a/crates/ruvllm/src/hub/model_card.rs +++ b/crates/ruvllm/src/hub/model_card.rs @@ -132,8 +132,14 @@ impl ModelCard { // Model details content.push_str("## Model Details\n\n"); content.push_str(&format!("- **Architecture**: {}\n", self.architecture)); - content.push_str(&format!("- **Parameters**: {}\n", format_params(self.parameters))); - content.push_str(&format!("- **Context Length**: {} tokens\n", self.context_length)); + content.push_str(&format!( + "- **Parameters**: {}\n", + format_params(self.parameters) + )); + content.push_str(&format!( + "- **Context Length**: {} tokens\n", + self.context_length + )); content.push_str(&format!("- **Framework**: {:?}\n", self.framework)); content.push_str(&format!("- **Task**: {:?}\n\n", self.task)); @@ -176,7 +182,10 @@ impl ModelCard { content.push_str("```rust\n"); content.push_str("use ruvllm::hub::ModelDownloader;\n\n"); content.push_str("let downloader = ModelDownloader::new();\n"); - content.push_str(&format!("let path = downloader.download_by_id(\"{}\")?;\n", self.name.to_lowercase())); + content.push_str(&format!( + "let path = downloader.download_by_id(\"{}\")?;\n", + self.name.to_lowercase() + )); content.push_str("```\n\n"); // Additional metadata diff --git a/crates/ruvllm/src/hub/registry.rs b/crates/ruvllm/src/hub/registry.rs index 05cf88579..aafb652b7 100644 --- a/crates/ruvllm/src/hub/registry.rs +++ b/crates/ruvllm/src/hub/registry.rs @@ -150,7 +150,7 @@ impl RuvLtraRegistry { size: ModelSize::Small, quantization: QuantizationLevel::Q4, size_bytes: 662_000_000, // ~662MB - checksum: None, // Set after publishing + checksum: None, // Set after publishing params_b: 0.5, context_length: 4096, hardware: HardwareRequirements { @@ -192,8 +192,7 @@ impl RuvLtraRegistry { supports_cuda: true, min_vram_gb: Some(2.0), }, - description: "High-quality Q8 quantization for better accuracy." - .to_string(), + description: "High-quality Q8 quantization for better accuracy.".to_string(), is_adapter: false, base_model: None, has_sona_weights: true, @@ -253,8 +252,7 @@ impl RuvLtraRegistry { supports_cuda: true, min_vram_gb: Some(6.0), }, - description: "High-quality Medium model with Q8 quantization." - .to_string(), + description: "High-quality Medium model with Q8 quantization.".to_string(), is_adapter: false, base_model: None, has_sona_weights: true, @@ -307,18 +305,12 @@ impl RuvLtraRegistry { /// Get models by size pub fn list_by_size(&self, size: ModelSize) -> Vec<&ModelInfo> { - self.models - .values() - .filter(|m| m.size == size) - .collect() + self.models.values().filter(|m| m.size == size).collect() } /// Get base models (exclude adapters) pub fn list_base_models(&self) -> Vec<&ModelInfo> { - self.models - .values() - .filter(|m| !m.is_adapter) - .collect() + self.models.values().filter(|m| !m.is_adapter).collect() } /// Get adapters for a specific base model diff --git a/crates/ruvllm/src/hub/upload.rs b/crates/ruvllm/src/hub/upload.rs index 684e3c443..a62c75b86 100644 --- a/crates/ruvllm/src/hub/upload.rs +++ b/crates/ruvllm/src/hub/upload.rs @@ -1,10 +1,10 @@ //! Model upload functionality for publishing to HuggingFace Hub -use super::{HubError, Result, get_hf_token}; use super::model_card::{ModelCard, ModelCardBuilder}; -use std::path::{Path, PathBuf}; -use std::fs; +use super::{get_hf_token, HubError, Result}; use regex::Regex; +use std::fs; +use std::path::{Path, PathBuf}; // ============================================================================ // Security: Input Validation (H-002) @@ -40,7 +40,9 @@ fn validate_repo_id(repo_id: &str) -> Result<()> { } // Prevent shell metacharacters that could be used for injection - let dangerous_chars = ['`', '$', '(', ')', ';', '&', '|', '<', '>', '\n', '\r', '"', '\'', '\\']; + let dangerous_chars = [ + '`', '$', '(', ')', ';', '&', '|', '<', '>', '\n', '\r', '"', '\'', '\\', + ]; for c in dangerous_chars { if repo_id.contains(c) { return Err(HubError::InvalidFormat(format!( @@ -298,9 +300,7 @@ impl ModelUploader { if !status.success() && status.code() != Some(1) { // Exit code 1 might mean repo already exists - return Err(HubError::Network( - "Failed to create repository".to_string(), - )); + return Err(HubError::Network("Failed to create repository".to_string())); } Ok(()) @@ -323,9 +323,7 @@ impl ModelUploader { .map_err(|e| HubError::Network(e.to_string()))?; if !status.success() { - return Err(HubError::Network( - "Failed to upload file".to_string(), - )); + return Err(HubError::Network("Failed to upload file".to_string())); } Ok(()) @@ -333,7 +331,7 @@ impl ModelUploader { /// Generate model card from metadata fn generate_model_card(&self, metadata: &ModelMetadata) -> ModelCard { - use super::model_card::{TaskType, Framework, License}; + use super::model_card::{Framework, License, TaskType}; let mut builder = ModelCardBuilder::new(&metadata.name); diff --git a/crates/ruvllm/src/kernels/accelerate.rs b/crates/ruvllm/src/kernels/accelerate.rs index af2041b7b..3c1894433 100644 --- a/crates/ruvllm/src/kernels/accelerate.rs +++ b/crates/ruvllm/src/kernels/accelerate.rs @@ -230,14 +230,40 @@ pub fn gemv_accelerate( n: usize, layout: MatrixLayout, ) { - debug_assert_eq!(a.len(), m * n, "Matrix A size mismatch: expected {}, got {}", m * n, a.len()); - debug_assert_eq!(x.len(), n, "Vector x size mismatch: expected {}, got {}", n, x.len()); - debug_assert_eq!(y.len(), m, "Vector y size mismatch: expected {}, got {}", m, y.len()); + debug_assert_eq!( + a.len(), + m * n, + "Matrix A size mismatch: expected {}, got {}", + m * n, + a.len() + ); + debug_assert_eq!( + x.len(), + n, + "Vector x size mismatch: expected {}, got {}", + n, + x.len() + ); + debug_assert_eq!( + y.len(), + m, + "Vector y size mismatch: expected {}, got {}", + m, + y.len() + ); // SECURITY FIX (H-005): Bounds check before i32 cast to prevent overflow // BLAS uses i32 for dimensions, so we must ensure values fit - assert!(m <= i32::MAX as usize, "Matrix dimension m={} exceeds i32::MAX for BLAS", m); - assert!(n <= i32::MAX as usize, "Matrix dimension n={} exceeds i32::MAX for BLAS", n); + assert!( + m <= i32::MAX as usize, + "Matrix dimension m={} exceeds i32::MAX for BLAS", + m + ); + assert!( + n <= i32::MAX as usize, + "Matrix dimension n={} exceeds i32::MAX for BLAS", + n + ); unsafe { gemv_accelerate_unchecked(a, x, y, m, n, layout); @@ -277,14 +303,14 @@ pub unsafe fn gemv_accelerate_unchecked( trans, m as i32, n as i32, - 1.0, // alpha = 1 + 1.0, // alpha = 1 a.as_ptr(), lda, x.as_ptr(), - 1, // incx = 1 - 0.0, // beta = 0 (overwrite y) + 1, // incx = 1 + 0.0, // beta = 0 (overwrite y) y.as_mut_ptr(), - 1, // incy = 1 + 1, // incy = 1 ); } @@ -313,8 +339,16 @@ pub fn gemv_transpose_accelerate( debug_assert_eq!(y.len(), n); // Note: y length is n for transpose // SECURITY FIX (H-005): Bounds check before i32 cast to prevent overflow - assert!(m <= i32::MAX as usize, "Matrix dimension m={} exceeds i32::MAX for BLAS", m); - assert!(n <= i32::MAX as usize, "Matrix dimension n={} exceeds i32::MAX for BLAS", n); + assert!( + m <= i32::MAX as usize, + "Matrix dimension m={} exceeds i32::MAX for BLAS", + m + ); + assert!( + n <= i32::MAX as usize, + "Matrix dimension n={} exceeds i32::MAX for BLAS", + n + ); unsafe { let order = CblasOrder::from(layout) as i32; @@ -373,8 +407,16 @@ pub fn gemv_scaled_accelerate( debug_assert_eq!(y.len(), m); // SECURITY FIX (H-005): Bounds check before i32 cast to prevent overflow - assert!(m <= i32::MAX as usize, "Matrix dimension m={} exceeds i32::MAX for BLAS", m); - assert!(n <= i32::MAX as usize, "Matrix dimension n={} exceeds i32::MAX for BLAS", n); + assert!( + m <= i32::MAX as usize, + "Matrix dimension m={} exceeds i32::MAX for BLAS", + m + ); + assert!( + n <= i32::MAX as usize, + "Matrix dimension n={} exceeds i32::MAX for BLAS", + n + ); unsafe { let order = CblasOrder::from(layout) as i32; @@ -418,22 +460,27 @@ pub fn gemv_scaled_accelerate( /// * `k` - Number of columns in A, rows in B /// * `n` - Number of columns in B and C #[cfg(all(target_os = "macos", feature = "accelerate"))] -pub fn gemm_accelerate( - a: &[f32], - b: &[f32], - c: &mut [f32], - m: usize, - k: usize, - n: usize, -) { +pub fn gemm_accelerate(a: &[f32], b: &[f32], c: &mut [f32], m: usize, k: usize, n: usize) { debug_assert_eq!(a.len(), m * k); debug_assert_eq!(b.len(), k * n); debug_assert_eq!(c.len(), m * n); // SECURITY FIX (H-005): Bounds check before i32 cast to prevent overflow - assert!(m <= i32::MAX as usize, "Matrix dimension m={} exceeds i32::MAX for BLAS", m); - assert!(k <= i32::MAX as usize, "Matrix dimension k={} exceeds i32::MAX for BLAS", k); - assert!(n <= i32::MAX as usize, "Matrix dimension n={} exceeds i32::MAX for BLAS", n); + assert!( + m <= i32::MAX as usize, + "Matrix dimension m={} exceeds i32::MAX for BLAS", + m + ); + assert!( + k <= i32::MAX as usize, + "Matrix dimension k={} exceeds i32::MAX for BLAS", + k + ); + assert!( + n <= i32::MAX as usize, + "Matrix dimension n={} exceeds i32::MAX for BLAS", + n + ); unsafe { cblas_sgemm( @@ -443,14 +490,14 @@ pub fn gemm_accelerate( m as i32, n as i32, k as i32, - 1.0, // alpha + 1.0, // alpha a.as_ptr(), - k as i32, // lda + k as i32, // lda b.as_ptr(), - n as i32, // ldb - 0.0, // beta + n as i32, // ldb + 0.0, // beta c.as_mut_ptr(), - n as i32, // ldc + n as i32, // ldc ); } } @@ -543,14 +590,7 @@ pub fn gemv_scaled_accelerate( } #[cfg(not(all(target_os = "macos", feature = "accelerate")))] -pub fn gemm_accelerate( - _a: &[f32], - _b: &[f32], - _c: &mut [f32], - _m: usize, - _k: usize, - _n: usize, -) { +pub fn gemm_accelerate(_a: &[f32], _b: &[f32], _c: &mut [f32], _m: usize, _k: usize, _n: usize) { panic!("Accelerate framework is only available on macOS with 'accelerate' feature enabled"); } diff --git a/crates/ruvllm/src/kernels/activations.rs b/crates/ruvllm/src/kernels/activations.rs index 255f731fb..157af556f 100644 --- a/crates/ruvllm/src/kernels/activations.rs +++ b/crates/ruvllm/src/kernels/activations.rs @@ -693,10 +693,7 @@ mod tests { let mut x = vec![0.0, 1.0, -1.0, 2.0, -2.0, 0.5, -0.5, 3.0]; // Expected values: x * sigmoid(x) = x / (1 + exp(-x)) - let expected: Vec = x - .iter() - .map(|&v: &f32| v / (1.0 + (-v).exp())) - .collect(); + let expected: Vec = x.iter().map(|&v: &f32| v / (1.0 + (-v).exp())).collect(); silu(&mut x); @@ -787,7 +784,12 @@ mod tests { for (i, (&orig, &result)) in original.iter().zip(x.iter()).enumerate() { // GELU(x) > 0 for x > 0 if orig > 1.0 { - assert!(result > 0.0, "GELU({}) should be positive, got {}", orig, result); + assert!( + result > 0.0, + "GELU({}) should be positive, got {}", + orig, + result + ); } // GELU(x) ~ x for large positive x if orig > 3.0 { @@ -865,7 +867,11 @@ mod tests { // Sum should be 1.0 let sum: f32 = x.iter().sum(); - assert!(approx_eq(sum, 1.0, EPSILON), "Softmax sum should be 1.0, got {}", sum); + assert!( + approx_eq(sum, 1.0, EPSILON), + "Softmax sum should be 1.0, got {}", + sum + ); // All values should be positive assert!(x.iter().all(|&v| v > 0.0)); @@ -896,7 +902,11 @@ mod tests { softmax(&mut x); let sum: f32 = x.iter().sum(); - assert!(approx_eq(sum, 1.0, EPSILON), "Softmax sum should be 1.0, got {}", sum); + assert!( + approx_eq(sum, 1.0, EPSILON), + "Softmax sum should be 1.0, got {}", + sum + ); assert!(x.iter().all(|&v| v.is_finite()), "Values should be finite"); } @@ -1003,7 +1013,10 @@ mod tests { fn test_single_element() { let mut x = vec![2.0]; softmax(&mut x); - assert!(approx_eq(x[0], 1.0, EPSILON), "Softmax of single element should be 1.0"); + assert!( + approx_eq(x[0], 1.0, EPSILON), + "Softmax of single element should be 1.0" + ); } #[test] diff --git a/crates/ruvllm/src/kernels/ane_ops.rs b/crates/ruvllm/src/kernels/ane_ops.rs index e115f37a0..0ee6a9314 100644 --- a/crates/ruvllm/src/kernels/ane_ops.rs +++ b/crates/ruvllm/src/kernels/ane_ops.rs @@ -243,7 +243,7 @@ pub fn should_use_ane(batch_size: usize, dim: usize) -> bool { && batch_size >= ANE_MIN_BATCH && batch_size <= ANE_MAX_BATCH && dim >= ANE_MIN_DIM - && dim % 16 == 0 // ANE prefers 16-aligned dimensions + && dim % 16 == 0 // ANE prefers 16-aligned dimensions } /// Check if matrix dimensions are optimal for ANE @@ -287,7 +287,9 @@ pub fn should_use_ane_matmul(m: usize, k: usize, n: usize) -> bool { } // Above crossover, only use ANE for small batch single-token inference - m == 1 && k >= ANE_MIN_DIM && n >= ANE_MIN_DIM + m == 1 + && k >= ANE_MIN_DIM + && n >= ANE_MIN_DIM && max_dim <= ANE_MATMUL_CROSSOVER_DIM && (k % 16 == 0 || n % 16 == 0) } @@ -389,14 +391,7 @@ pub struct AneRecommendation { /// - Best for batch sizes 1-64 with aligned dimensions /// - 2-3x more power efficient than GPU for supported shapes #[cfg(all(target_os = "macos", feature = "coreml"))] -pub fn matmul_ane( - a: &[f32], - b: &[f32], - c: &mut [f32], - m: usize, - k: usize, - n: usize, -) { +pub fn matmul_ane(a: &[f32], b: &[f32], c: &mut [f32], m: usize, k: usize, n: usize) { debug_assert_eq!(a.len(), m * k, "Matrix A size mismatch"); debug_assert_eq!(b.len(), k * n, "Matrix B size mismatch"); debug_assert_eq!(c.len(), m * n, "Matrix C size mismatch"); @@ -430,14 +425,14 @@ pub unsafe fn matmul_ane_unchecked( m as i32, n as i32, k as i32, - 1.0, // alpha + 1.0, // alpha a.as_ptr(), - k as i32, // lda + k as i32, // lda b.as_ptr(), - n as i32, // ldb - 0.0, // beta + n as i32, // ldb + 0.0, // beta c.as_mut_ptr(), - n as i32, // ldc + n as i32, // ldc ); } @@ -577,9 +572,7 @@ pub fn layer_norm_ane( let mean: f32 = slice.iter().sum::() / dim as f32; // Compute variance - let variance: f32 = slice.iter() - .map(|v| (v - mean).powi(2)) - .sum::() / dim as f32; + let variance: f32 = slice.iter().map(|v| (v - mean).powi(2)).sum::() / dim as f32; let inv_std = 1.0 / (variance + eps).sqrt(); @@ -594,13 +587,7 @@ pub fn layer_norm_ane( /// /// Applies: output = x * weight / sqrt(mean(x^2) + eps) #[cfg(all(target_os = "macos", feature = "coreml"))] -pub fn rms_norm_ane( - x: &mut [f32], - weight: &[f32], - batch_size: usize, - dim: usize, - eps: f32, -) { +pub fn rms_norm_ane(x: &mut [f32], weight: &[f32], batch_size: usize, dim: usize, eps: f32) { debug_assert_eq!(x.len(), batch_size * dim); debug_assert_eq!(weight.len(), dim); @@ -669,14 +656,7 @@ fn softmax_scalar(x: &mut [f32]) { // ============================================================================ #[cfg(not(all(target_os = "macos", feature = "coreml")))] -pub fn matmul_ane( - _a: &[f32], - _b: &[f32], - _c: &mut [f32], - _m: usize, - _k: usize, - _n: usize, -) { +pub fn matmul_ane(_a: &[f32], _b: &[f32], _c: &mut [f32], _m: usize, _k: usize, _n: usize) { panic!("ANE operations require macOS with 'coreml' feature enabled"); } @@ -721,13 +701,7 @@ pub fn layer_norm_ane( } #[cfg(not(all(target_os = "macos", feature = "coreml")))] -pub fn rms_norm_ane( - _x: &mut [f32], - _weight: &[f32], - _batch_size: usize, - _dim: usize, - _eps: f32, -) { +pub fn rms_norm_ane(_x: &mut [f32], _weight: &[f32], _batch_size: usize, _dim: usize, _eps: f32) { panic!("ANE operations require macOS with 'coreml' feature enabled"); } @@ -738,14 +712,7 @@ pub fn rms_norm_ane( /// Auto-dispatch matrix multiplication to best backend /// /// Automatically selects ANE or NEON based on tensor shapes and system capabilities. -pub fn matmul_auto( - a: &[f32], - b: &[f32], - c: &mut [f32], - m: usize, - k: usize, - n: usize, -) { +pub fn matmul_auto(a: &[f32], b: &[f32], c: &mut [f32], m: usize, k: usize, n: usize) { #[cfg(all(target_os = "macos", feature = "coreml"))] { if should_use_ane_matmul(m, k, n) { @@ -832,13 +799,7 @@ pub fn layer_norm_auto( } /// Auto-dispatch RMS normalization to best backend -pub fn rms_norm_auto( - x: &mut [f32], - weight: &[f32], - batch_size: usize, - dim: usize, - eps: f32, -) { +pub fn rms_norm_auto(x: &mut [f32], weight: &[f32], batch_size: usize, dim: usize, eps: f32) { #[cfg(all(target_os = "macos", feature = "coreml"))] { if should_use_ane(batch_size, dim) { @@ -912,18 +873,18 @@ mod tests { #[test] fn test_should_use_ane_boundary_conditions() { // At exact boundaries - assert!(!should_use_ane(0, 64)); // Zero batch - assert!(!should_use_ane(1, 63)); // Just below min dim + assert!(!should_use_ane(0, 64)); // Zero batch + assert!(!should_use_ane(1, 63)); // Just below min dim assert!(!should_use_ane(65, 64)); // Just above max batch // Alignment tests - assert!(!should_use_ane(1, 65)); // Not aligned to 16 - assert!(!should_use_ane(1, 17)); // Not aligned to 16 + assert!(!should_use_ane(1, 65)); // Not aligned to 16 + assert!(!should_use_ane(1, 17)); // Not aligned to 16 if is_ane_available() { - assert!(should_use_ane(1, 64)); // Exactly at min dim - assert!(should_use_ane(64, 64)); // At max batch - assert!(should_use_ane(1, 80)); // 80 % 16 == 0 + assert!(should_use_ane(1, 64)); // Exactly at min dim + assert!(should_use_ane(64, 64)); // At max batch + assert!(should_use_ane(1, 80)); // 80 % 16 == 0 } } @@ -1162,7 +1123,11 @@ mod tests { // Sum should be 1.0 let sum: f32 = x.iter().sum(); - assert!(approx_eq(sum, 1.0, EPSILON), "Softmax sum should be 1.0, got {}", sum); + assert!( + approx_eq(sum, 1.0, EPSILON), + "Softmax sum should be 1.0, got {}", + sum + ); // All values should be positive assert!(x.iter().all(|&v| v > 0.0)); @@ -1208,7 +1173,10 @@ mod tests { softmax_scalar(&mut large); let sum: f32 = large.iter().sum(); - assert!(approx_eq(sum, 1.0, EPSILON), "Softmax should sum to 1 even with large inputs"); + assert!( + approx_eq(sum, 1.0, EPSILON), + "Softmax should sum to 1 even with large inputs" + ); assert!(large.iter().all(|v| v.is_finite())); } @@ -1391,9 +1359,7 @@ mod tests { fn test_softmax_ane_matches_scalar() { let dim = 64; let batch_size = 4; - let mut x_ane: Vec = (0..batch_size * dim) - .map(|i| (i as f32) * 0.01) - .collect(); + let mut x_ane: Vec = (0..batch_size * dim).map(|i| (i as f32) * 0.01).collect(); let mut x_scalar = x_ane.clone(); softmax_ane(&mut x_ane, batch_size, dim); @@ -1417,9 +1383,7 @@ mod tests { fn test_layer_norm_ane() { let dim = 16; let batch_size = 2; - let mut x: Vec = (0..batch_size * dim) - .map(|i| (i as f32) * 0.1) - .collect(); + let mut x: Vec = (0..batch_size * dim).map(|i| (i as f32) * 0.1).collect(); let weight = vec![1.0; dim]; let bias = vec![0.0; dim]; @@ -1445,7 +1409,7 @@ mod tests { let batch_size = 1; let mut x: Vec = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]; let weight = vec![2.0; dim]; // Scale by 2 - let bias = vec![1.0; dim]; // Shift by 1 + let bias = vec![1.0; dim]; // Shift by 1 layer_norm_ane(&mut x, &weight, &bias, batch_size, dim, 1e-6); @@ -1681,11 +1645,7 @@ mod tests { use std::thread; let results: Vec<_> = (0..4) - .map(|_| { - thread::spawn(|| { - is_ane_available() - }) - }) + .map(|_| thread::spawn(|| is_ane_available())) .collect(); let first = results.into_iter().next().unwrap().join().unwrap(); diff --git a/crates/ruvllm/src/kernels/attention.rs b/crates/ruvllm/src/kernels/attention.rs index a2e09b813..24f56de42 100644 --- a/crates/ruvllm/src/kernels/attention.rs +++ b/crates/ruvllm/src/kernels/attention.rs @@ -699,7 +699,9 @@ pub fn flash_attention_v2( #[cfg(target_arch = "aarch64")] unsafe { - flash_attention_v2_neon_impl(query, key, value, head_dim, kv_len, scale, causal, block_size) + flash_attention_v2_neon_impl( + query, key, value, head_dim, kv_len, scale, causal, block_size, + ) } #[cfg(not(target_arch = "aarch64"))] @@ -717,7 +719,11 @@ pub fn flash_attention_auto( scale: f32, causal: bool, ) -> Vec { - let head_dim = if !query.is_empty() { query.len() } else { return vec![]; }; + let head_dim = if !query.is_empty() { + query.len() + } else { + return vec![]; + }; let kv_len = key.len() / head_dim; let block_size = select_block_size(kv_len, head_dim); flash_attention_v2(query, key, value, scale, causal, block_size) @@ -776,7 +782,9 @@ pub fn flash_attention_into( { // SAFETY: bounds checks done above, head_dim > 0, kv_len > 0 unsafe { - flash_attention_v2_neon_into(query, key, value, head_dim, kv_len, scale, causal, block_size, output); + flash_attention_v2_neon_into( + query, key, value, head_dim, kv_len, scale, causal, block_size, output, + ); } return; } @@ -837,7 +845,7 @@ pub fn flash_attention_with_scratch( // SAFETY: bounds checks done above, head_dim > 0, kv_len > 0 unsafe { flash_attention_v2_neon_with_scratch( - query, key, value, head_dim, kv_len, scale, block_size, scratch, output + query, key, value, head_dim, kv_len, scale, block_size, scratch, output, ); } return; @@ -1107,8 +1115,8 @@ unsafe fn flash_attention_v2_neon_impl( let v_ptr = value.as_ptr(); // Flash Attention 2 state: m (max), l (sum of exp), O (output accumulator) - let mut m = f32::NEG_INFINITY; // Running max - let mut l = 0.0f32; // Running sum of exp(scores - m) + let mut m = f32::NEG_INFINITY; // Running max + let mut l = 0.0f32; // Running sum of exp(scores - m) let mut output = vec![0.0f32; head_dim]; let out_ptr = output.as_mut_ptr(); @@ -1324,7 +1332,12 @@ unsafe fn rescale_output_8x(out_ptr: *mut f32, len: usize, factor: f32) { /// Fused softmax-matmul operation with 8x unrolling #[cfg(target_arch = "aarch64")] #[inline(always)] -unsafe fn accumulate_weighted_value_8x(out_ptr: *mut f32, v_ptr: *const f32, len: usize, weight: f32) { +unsafe fn accumulate_weighted_value_8x( + out_ptr: *mut f32, + v_ptr: *const f32, + len: usize, + weight: f32, +) { let weight_vec = vdupq_n_f32(weight); let chunks_32 = len / 32; let mut idx = 0usize; @@ -1839,8 +1852,13 @@ pub unsafe fn softmax_neon(x: &mut [f32], len: usize) { let sixth = vdupq_n_f32(1.0 / 6.0); let x2 = vmulq_f32(shifted, shifted); let x3 = vmulq_f32(x2, shifted); - let exp_approx = - vaddq_f32(one, vaddq_f32(shifted, vaddq_f32(vmulq_f32(x2, half), vmulq_f32(x3, sixth)))); + let exp_approx = vaddq_f32( + one, + vaddq_f32( + shifted, + vaddq_f32(vmulq_f32(x2, half), vmulq_f32(x3, sixth)), + ), + ); // For numerical stability, use actual exp for large values let exp_val = vdupq_n_f32( (vgetq_lane_f32(shifted, 0)).exp() diff --git a/crates/ruvllm/src/kernels/matmul.rs b/crates/ruvllm/src/kernels/matmul.rs index 294dc19b0..42861d8fe 100644 --- a/crates/ruvllm/src/kernels/matmul.rs +++ b/crates/ruvllm/src/kernels/matmul.rs @@ -108,7 +108,11 @@ pub fn gemv_neon(a: &[f32], x: &[f32], y: &mut [f32], m: usize, n: usize) { { if super::accelerate::should_use_accelerate(m, n) { super::accelerate::gemv_accelerate( - a, x, y, m, n, + a, + x, + y, + m, + n, super::accelerate::MatrixLayout::RowMajor, ); return; @@ -216,51 +220,99 @@ unsafe fn gemv_neon_impl(a: &[f32], x: &[f32], y: &mut [f32], m: usize, n: usize // Process all 12 rows with these x values // Row 0 sum0 = vfmaq_f32(sum0, vld1q_f32(a_ptr.add((row_base + 0) * n + col)), x_v0); - sum0 = vfmaq_f32(sum0, vld1q_f32(a_ptr.add((row_base + 0) * n + col + 4)), x_v1); + sum0 = vfmaq_f32( + sum0, + vld1q_f32(a_ptr.add((row_base + 0) * n + col + 4)), + x_v1, + ); // Row 1 sum1 = vfmaq_f32(sum1, vld1q_f32(a_ptr.add((row_base + 1) * n + col)), x_v0); - sum1 = vfmaq_f32(sum1, vld1q_f32(a_ptr.add((row_base + 1) * n + col + 4)), x_v1); + sum1 = vfmaq_f32( + sum1, + vld1q_f32(a_ptr.add((row_base + 1) * n + col + 4)), + x_v1, + ); // Row 2 sum2 = vfmaq_f32(sum2, vld1q_f32(a_ptr.add((row_base + 2) * n + col)), x_v0); - sum2 = vfmaq_f32(sum2, vld1q_f32(a_ptr.add((row_base + 2) * n + col + 4)), x_v1); + sum2 = vfmaq_f32( + sum2, + vld1q_f32(a_ptr.add((row_base + 2) * n + col + 4)), + x_v1, + ); // Row 3 sum3 = vfmaq_f32(sum3, vld1q_f32(a_ptr.add((row_base + 3) * n + col)), x_v0); - sum3 = vfmaq_f32(sum3, vld1q_f32(a_ptr.add((row_base + 3) * n + col + 4)), x_v1); + sum3 = vfmaq_f32( + sum3, + vld1q_f32(a_ptr.add((row_base + 3) * n + col + 4)), + x_v1, + ); // Row 4 sum4 = vfmaq_f32(sum4, vld1q_f32(a_ptr.add((row_base + 4) * n + col)), x_v0); - sum4 = vfmaq_f32(sum4, vld1q_f32(a_ptr.add((row_base + 4) * n + col + 4)), x_v1); + sum4 = vfmaq_f32( + sum4, + vld1q_f32(a_ptr.add((row_base + 4) * n + col + 4)), + x_v1, + ); // Row 5 sum5 = vfmaq_f32(sum5, vld1q_f32(a_ptr.add((row_base + 5) * n + col)), x_v0); - sum5 = vfmaq_f32(sum5, vld1q_f32(a_ptr.add((row_base + 5) * n + col + 4)), x_v1); + sum5 = vfmaq_f32( + sum5, + vld1q_f32(a_ptr.add((row_base + 5) * n + col + 4)), + x_v1, + ); // Row 6 sum6 = vfmaq_f32(sum6, vld1q_f32(a_ptr.add((row_base + 6) * n + col)), x_v0); - sum6 = vfmaq_f32(sum6, vld1q_f32(a_ptr.add((row_base + 6) * n + col + 4)), x_v1); + sum6 = vfmaq_f32( + sum6, + vld1q_f32(a_ptr.add((row_base + 6) * n + col + 4)), + x_v1, + ); // Row 7 sum7 = vfmaq_f32(sum7, vld1q_f32(a_ptr.add((row_base + 7) * n + col)), x_v0); - sum7 = vfmaq_f32(sum7, vld1q_f32(a_ptr.add((row_base + 7) * n + col + 4)), x_v1); + sum7 = vfmaq_f32( + sum7, + vld1q_f32(a_ptr.add((row_base + 7) * n + col + 4)), + x_v1, + ); // Row 8 sum8 = vfmaq_f32(sum8, vld1q_f32(a_ptr.add((row_base + 8) * n + col)), x_v0); - sum8 = vfmaq_f32(sum8, vld1q_f32(a_ptr.add((row_base + 8) * n + col + 4)), x_v1); + sum8 = vfmaq_f32( + sum8, + vld1q_f32(a_ptr.add((row_base + 8) * n + col + 4)), + x_v1, + ); // Row 9 sum9 = vfmaq_f32(sum9, vld1q_f32(a_ptr.add((row_base + 9) * n + col)), x_v0); - sum9 = vfmaq_f32(sum9, vld1q_f32(a_ptr.add((row_base + 9) * n + col + 4)), x_v1); + sum9 = vfmaq_f32( + sum9, + vld1q_f32(a_ptr.add((row_base + 9) * n + col + 4)), + x_v1, + ); // Row 10 sum10 = vfmaq_f32(sum10, vld1q_f32(a_ptr.add((row_base + 10) * n + col)), x_v0); - sum10 = vfmaq_f32(sum10, vld1q_f32(a_ptr.add((row_base + 10) * n + col + 4)), x_v1); + sum10 = vfmaq_f32( + sum10, + vld1q_f32(a_ptr.add((row_base + 10) * n + col + 4)), + x_v1, + ); // Row 11 sum11 = vfmaq_f32(sum11, vld1q_f32(a_ptr.add((row_base + 11) * n + col)), x_v0); - sum11 = vfmaq_f32(sum11, vld1q_f32(a_ptr.add((row_base + 11) * n + col + 4)), x_v1); + sum11 = vfmaq_f32( + sum11, + vld1q_f32(a_ptr.add((row_base + 11) * n + col + 4)), + x_v1, + ); col += 8; } @@ -1494,7 +1546,7 @@ pub fn gemv_metal_if_available_inplace( /// or an error occurred. #[cfg(all(target_os = "macos", feature = "metal-compute"))] fn try_gemv_metal(a: &[f32], x: &[f32], m: usize, n: usize) -> Option> { - use crate::metal::{is_metal_available, MetalContext, MetalConfig, gemv_metal}; + use crate::metal::{gemv_metal, is_metal_available, MetalConfig, MetalContext}; if !is_metal_available() { return None; @@ -1911,7 +1963,9 @@ mod tests { assert!( (y[i] - n as f32).abs() < 1e-5, "y[{}] = {}, expected {}", - i, y[i], n + i, + y[i], + n ); } } @@ -1948,7 +2002,9 @@ mod tests { assert!( (y[i] - n as f32).abs() < 1e-5, "y[{}] = {}, expected {}", - i, y[i], n + i, + y[i], + n ); } } @@ -1984,7 +2040,9 @@ mod tests { assert!( (y[i] - n as f32).abs() < 1e-3, "y[{}] = {}, expected {}", - i, y[i], n + i, + y[i], + n ); } } diff --git a/crates/ruvllm/src/kernels/mod.rs b/crates/ruvllm/src/kernels/mod.rs index 0061f6106..a5c496f25 100644 --- a/crates/ruvllm/src/kernels/mod.rs +++ b/crates/ruvllm/src/kernels/mod.rs @@ -93,51 +93,54 @@ pub mod ane_ops; // Re-exports for convenience pub use attention::{ - flash_attention_neon, flash_attention_v2, flash_attention_auto, - grouped_query_attention_neon, multi_query_attention_neon, - paged_attention_neon, PagedKvCache, - select_block_size, BLOCK_SIZE_SMALL, BLOCK_SIZE_MEDIUM, BLOCK_SIZE_LARGE, + flash_attention_auto, // TD-009: Zero-allocation attention functions and scratch buffers - flash_attention_into, flash_attention_with_scratch, AttentionScratch, + flash_attention_into, + flash_attention_neon, + flash_attention_v2, + flash_attention_with_scratch, + grouped_query_attention_neon, + multi_query_attention_neon, + paged_attention_neon, + select_block_size, + AttentionScratch, + PagedKvCache, + BLOCK_SIZE_LARGE, + BLOCK_SIZE_MEDIUM, + BLOCK_SIZE_SMALL, }; // Thread-local scratch buffer for zero-allocation attention (non-WASM only) #[cfg(not(target_arch = "wasm32"))] pub use attention::THREAD_LOCAL_SCRATCH; #[cfg(all(feature = "parallel", not(target_arch = "wasm32")))] pub use attention::{ - multi_query_attention_parallel, grouped_query_attention_parallel, - multi_head_attention_parallel, + grouped_query_attention_parallel, multi_head_attention_parallel, multi_query_attention_parallel, }; pub use matmul::{batched_gemm_neon, gemm_neon, gemv_neon}; #[cfg(all(feature = "parallel", not(target_arch = "wasm32")))] pub use matmul::{ - gemm_parallel, gemv_parallel, batched_gemm_parallel, - configure_thread_pool, get_physical_cores, + batched_gemm_parallel, configure_thread_pool, gemm_parallel, gemv_parallel, get_physical_cores, }; pub use norm::{layer_norm_neon, rms_norm_neon}; pub use quantized::{ - int4_gemv_neon, int8_gemv_neon, q4k_gemv_neon, - quantize_to_int4, quantize_to_int8, quantize_to_q4k, - dequantize_int4, dequantize_int8, - BlockQ4K, QuantizedInt4, QuantizedInt8, + dequantize_int4, dequantize_int8, int4_gemv_neon, int8_gemv_neon, q4k_gemv_neon, + quantize_to_int4, quantize_to_int8, quantize_to_q4k, BlockQ4K, QuantizedInt4, QuantizedInt8, INT4_BLOCK_SIZE, Q4K_SUPER_BLOCK_SIZE, }; pub use rope::{apply_rope_neon, precompute_rope_tables, RopeConfig}; // Activation function exports pub use activations::{ - silu, silu_vec, gelu, gelu_vec, gelu_exact, - relu, relu_vec, leaky_relu, - softmax, softmax_vec, softmax_temperature, - batch_silu, batch_gelu, batch_softmax, + batch_gelu, batch_silu, batch_softmax, gelu, gelu_exact, gelu_vec, leaky_relu, relu, relu_vec, + silu, silu_vec, softmax, softmax_temperature, softmax_vec, }; // Accelerate framework exports (macOS only) #[cfg(all(target_os = "macos", feature = "accelerate"))] pub use accelerate::{ - gemv_accelerate, gemv_transpose_accelerate, gemv_scaled_accelerate, - gemm_accelerate, dot_accelerate, scal_accelerate, axpy_accelerate, - is_accelerate_available, should_use_accelerate, MatrixLayout, + axpy_accelerate, dot_accelerate, gemm_accelerate, gemv_accelerate, gemv_scaled_accelerate, + gemv_transpose_accelerate, is_accelerate_available, scal_accelerate, should_use_accelerate, + MatrixLayout, }; // Re-export availability check for all platforms @@ -147,18 +150,29 @@ pub use accelerate::is_accelerate_available; // ANE (Apple Neural Engine) ops exports (macOS only with coreml feature) #[cfg(all(target_os = "macos", feature = "coreml"))] pub use ane_ops::{ - // Direct ANE operations - matmul_ane, batched_matmul_ane, - gelu_ane, silu_ane, softmax_ane, - layer_norm_ane, rms_norm_ane, - // Auto-dispatch functions - matmul_auto, gelu_auto, silu_auto, softmax_auto, - layer_norm_auto, rms_norm_auto, - // Availability checks - is_ane_available, should_use_ane, should_use_ane_matmul, - should_use_ane_activation, + batched_matmul_ane, + gelu_ane, + gelu_auto, // Strategy recommendations (M4 Pro optimized) - get_ane_recommendation, AneRecommendation, + get_ane_recommendation, + // Availability checks + is_ane_available, + layer_norm_ane, + layer_norm_auto, + // Direct ANE operations + matmul_ane, + // Auto-dispatch functions + matmul_auto, + rms_norm_ane, + rms_norm_auto, + should_use_ane, + should_use_ane_activation, + should_use_ane_matmul, + silu_ane, + silu_auto, + softmax_ane, + softmax_auto, + AneRecommendation, }; // Re-export ANE availability check for macOS without coreml feature diff --git a/crates/ruvllm/src/kernels/norm.rs b/crates/ruvllm/src/kernels/norm.rs index 822f3ac5b..e50b58172 100644 --- a/crates/ruvllm/src/kernels/norm.rs +++ b/crates/ruvllm/src/kernels/norm.rs @@ -131,15 +131,24 @@ unsafe fn rms_norm_neon_impl(x: &mut [f32], weight: &[f32], eps: f32) { let x1 = vld1q_f32(x_ptr.add(idx + 4)); let w1 = vld1q_f32(w_ptr.add(idx + 4)); - vst1q_f32(x_ptr.add(idx + 4), vmulq_f32(vmulq_f32(x1, inv_rms_vec), w1)); + vst1q_f32( + x_ptr.add(idx + 4), + vmulq_f32(vmulq_f32(x1, inv_rms_vec), w1), + ); let x2 = vld1q_f32(x_ptr.add(idx + 8)); let w2 = vld1q_f32(w_ptr.add(idx + 8)); - vst1q_f32(x_ptr.add(idx + 8), vmulq_f32(vmulq_f32(x2, inv_rms_vec), w2)); + vst1q_f32( + x_ptr.add(idx + 8), + vmulq_f32(vmulq_f32(x2, inv_rms_vec), w2), + ); let x3 = vld1q_f32(x_ptr.add(idx + 12)); let w3 = vld1q_f32(w_ptr.add(idx + 12)); - vst1q_f32(x_ptr.add(idx + 12), vmulq_f32(vmulq_f32(x3, inv_rms_vec), w3)); + vst1q_f32( + x_ptr.add(idx + 12), + vmulq_f32(vmulq_f32(x3, inv_rms_vec), w3), + ); idx += 16; } @@ -356,7 +365,13 @@ fn layer_norm_scalar(x: &mut [f32], weight: &[f32], bias: &[f32], eps: f32) { /// * `batch_size` - Number of vectors in batch /// * `dim` - Dimension of each vector /// * `eps` - Numerical stability constant -pub fn batched_rms_norm_neon(x: &mut [f32], weight: &[f32], batch_size: usize, dim: usize, eps: f32) { +pub fn batched_rms_norm_neon( + x: &mut [f32], + weight: &[f32], + batch_size: usize, + dim: usize, + eps: f32, +) { debug_assert_eq!(x.len(), batch_size * dim); debug_assert_eq!(weight.len(), dim); @@ -492,7 +507,11 @@ mod tests { // Check that variance is approximately 1 let var: f32 = x.iter().map(|v| (v - mean).powi(2)).sum::() / 4.0; - assert!((var - 1.0).abs() < 1e-4, "Variance should be ~1, got {}", var); + assert!( + (var - 1.0).abs() < 1e-4, + "Variance should be ~1, got {}", + var + ); } #[test] @@ -578,7 +597,11 @@ mod tests { fn test_compute_rms() { let x = vec![3.0, 4.0]; // RMS = sqrt((9+16)/2) = sqrt(12.5) ~ 3.536 let rms = compute_rms(&x); - assert!((rms - 3.5355).abs() < 0.01, "RMS should be ~3.536, got {}", rms); + assert!( + (rms - 3.5355).abs() < 0.01, + "RMS should be ~3.536, got {}", + rms + ); } #[test] diff --git a/crates/ruvllm/src/kernels/quantized.rs b/crates/ruvllm/src/kernels/quantized.rs index f7ee178f8..b4b782c3c 100644 --- a/crates/ruvllm/src/kernels/quantized.rs +++ b/crates/ruvllm/src/kernels/quantized.rs @@ -127,11 +127,7 @@ pub fn quantize_to_int8(data: &[f32]) -> (Vec, f32) { let max_abs = data.iter().fold(0.0f32, |acc, &x| acc.max(x.abs())); // Compute scale to map [-max_abs, max_abs] -> [-127, 127] - let scale = if max_abs > 0.0 { - max_abs / 127.0 - } else { - 1.0 - }; + let scale = if max_abs > 0.0 { max_abs / 127.0 } else { 1.0 }; let inv_scale = 1.0 / scale; @@ -186,9 +182,9 @@ pub fn quantize_to_int4(data: &[f32], block_size: usize) -> (Vec, Vec, let block = &data[start..end]; // Find min and max in block - let (min_val, max_val) = block - .iter() - .fold((f32::MAX, f32::MIN), |(min, max), &x| (min.min(x), max.max(x))); + let (min_val, max_val) = block.iter().fold((f32::MAX, f32::MIN), |(min, max), &x| { + (min.min(x), max.max(x)) + }); // Compute scale and min for asymmetric quantization: q = (x - min) / scale // Maps [min, max] -> [0, 15] @@ -256,11 +252,7 @@ pub fn dequantize_int4( for i in 0..elements_in_block { let byte_idx = start_byte + i / 2; let byte = packed[byte_idx]; - let q = if i % 2 == 0 { - byte & 0x0F - } else { - byte >> 4 - }; + let q = if i % 2 == 0 { byte & 0x0F } else { byte >> 4 }; output.push((q as f32) * scale + min); } } @@ -279,9 +271,9 @@ pub fn quantize_to_q4k(data: &[f32]) -> BlockQ4K { debug_assert_eq!(data.len(), Q4K_SUPER_BLOCK_SIZE); // Find global min and max - let (global_min, global_max) = data - .iter() - .fold((f32::MAX, f32::MIN), |(min, max), &x| (min.min(x), max.max(x))); + let (global_min, global_max) = data.iter().fold((f32::MAX, f32::MIN), |(min, max), &x| { + (min.min(x), max.max(x)) + }); // Convert to f16 representation (simplified - using upper 16 bits of f32) let d = f32_to_f16(global_max - global_min); @@ -299,11 +291,15 @@ pub fn quantize_to_q4k(data: &[f32]) -> BlockQ4K { let (sb_min, sb_max) = sub_block .iter() - .fold((f32::MAX, f32::MIN), |(min, max), &x| (min.min(x), max.max(x))); + .fold((f32::MAX, f32::MIN), |(min, max), &x| { + (min.min(x), max.max(x)) + }); // Scale relative to global range (6-bit precision: 0-63) let rel_scale = if global_scale > 1e-10 { - ((sb_max - sb_min) / global_scale * 63.0).round().clamp(0.0, 63.0) as u8 + ((sb_max - sb_min) / global_scale * 63.0) + .round() + .clamp(0.0, 63.0) as u8 } else { 0 }; @@ -871,11 +867,7 @@ fn q4k_gemv_scalar( for i in 0..Q4K_SUPER_BLOCK_SIZE { let byte_idx = i / 2; let byte = block.qs[byte_idx]; - let q = if i % 2 == 0 { - byte & 0x0F - } else { - byte >> 4 - }; + let q = if i % 2 == 0 { byte & 0x0F } else { byte >> 4 }; let val = (q as f32) * scale + dmin; sum += val * x[x_offset + i]; } @@ -967,7 +959,12 @@ mod tests { for (orig, deq) in data.iter().zip(dequantized.iter()) { let error = (orig - deq).abs() / orig.abs().max(0.01); - assert!(error < 0.02, "INT8 quantization error too high: {} vs {}", orig, deq); + assert!( + error < 0.02, + "INT8 quantization error too high: {} vs {}", + orig, + deq + ); } } @@ -979,7 +976,12 @@ mod tests { for (orig, deq) in data.iter().zip(dequantized.iter()) { let error = (orig - deq).abs(); - assert!(error < 0.1, "INT4 quantization error too high: {} vs {}", orig, deq); + assert!( + error < 0.1, + "INT4 quantization error too high: {} vs {}", + orig, + deq + ); } } @@ -1014,7 +1016,11 @@ mod tests { assert!( rel_error < 0.03 || abs_error < 0.01, "INT8 GEMV error at row {}: {} vs {} (rel: {:.4}, abs: {:.6})", - i, y_quant[i], y_ref[i], rel_error, abs_error + i, + y_quant[i], + y_ref[i], + rel_error, + abs_error ); } } @@ -1071,7 +1077,11 @@ mod tests { assert!( rel_error < 0.10 || abs_error < 0.1, "INT4 GEMV error at row {}: {} vs {} (rel: {:.4}, abs: {:.6})", - i, y_quant[i], y_ref[i], rel_error, abs_error + i, + y_quant[i], + y_ref[i], + rel_error, + abs_error ); } } @@ -1093,7 +1103,9 @@ mod tests { assert!( error < 0.01 || (v - back).abs() < 1e-6, "F16 roundtrip error: {} -> {} -> {}", - v, h, back + v, + h, + back ); } } @@ -1124,11 +1136,7 @@ mod tests { let deq = (q as f32) * scale + min; let orig = data[i]; let error = (deq - orig).abs(); - assert!( - error < 0.2, - "Q4_K error at {}: {} vs {}", - i, deq, orig - ); + assert!(error < 0.2, "Q4_K error at {}: {} vs {}", i, deq, orig); } } @@ -1141,7 +1149,9 @@ mod tests { // Create matrix with values in a reasonable range that won't suffer from // heavy cancellation when both A and x are quantized - let a_f32: Vec = (0..m * n).map(|i| ((i % 127) as f32 - 63.0) / 100.0).collect(); + let a_f32: Vec = (0..m * n) + .map(|i| ((i % 127) as f32 - 63.0) / 100.0) + .collect(); let x: Vec = (0..n).map(|i| ((i % 63) as f32 - 31.0) / 50.0).collect(); let (a_i8, scale) = quantize_to_int8(&a_f32); @@ -1172,7 +1182,11 @@ mod tests { assert!( abs_error < tolerance, "Large INT8 GEMV error at row {}: {} vs {} (abs: {:.6}, tol: {:.6})", - i, y_quant[i], y_ref[i], abs_error, tolerance + i, + y_quant[i], + y_ref[i], + abs_error, + tolerance ); } } @@ -1195,7 +1209,9 @@ mod tests { assert!( error < 0.15, "INT4 boundary error at {}: {} vs {}", - i, data[i], dequantized[i] + i, + data[i], + dequantized[i] ); } } diff --git a/crates/ruvllm/src/kernels/rope.rs b/crates/ruvllm/src/kernels/rope.rs index cd312f7ea..b4a64f18f 100644 --- a/crates/ruvllm/src/kernels/rope.rs +++ b/crates/ruvllm/src/kernels/rope.rs @@ -147,7 +147,11 @@ impl RopeTables { /// /// # Returns /// Tuple of (cos_table, sin_table), each of shape (max_seq_len, head_dim/2) -pub fn precompute_rope_tables(max_seq_len: usize, head_dim: usize, base: f32) -> (Vec, Vec) { +pub fn precompute_rope_tables( + max_seq_len: usize, + head_dim: usize, + base: f32, +) -> (Vec, Vec) { let half_dim = head_dim / 2; let mut cos_table = vec![0.0; max_seq_len * half_dim]; let mut sin_table = vec![0.0; max_seq_len * half_dim]; @@ -441,7 +445,12 @@ fn apply_rope_scalar( /// Scalar fallback with precomputed tables #[allow(dead_code)] -fn apply_rope_tables_scalar(x: &mut [f32], positions: &[usize], tables: &RopeTables, half_dim: usize) { +fn apply_rope_tables_scalar( + x: &mut [f32], + positions: &[usize], + tables: &RopeTables, + half_dim: usize, +) { let head_dim = half_dim * 2; for (tok_idx, &pos) in positions.iter().enumerate() { @@ -642,6 +651,9 @@ mod tests { // Tokens 1 and 2 should be rotated // Just verify they're different from original - assert!(x.iter().skip(4).any(|&v| (v - 1.0).abs() > 1e-5 || v.abs() > 1e-5)); + assert!(x + .iter() + .skip(4) + .any(|&v| (v - 1.0).abs() > 1e-5 || v.abs() > 1e-5)); } } diff --git a/crates/ruvllm/src/kv_cache.rs b/crates/ruvllm/src/kv_cache.rs index e7ef7de01..c303d8a6f 100644 --- a/crates/ruvllm/src/kv_cache.rs +++ b/crates/ruvllm/src/kv_cache.rs @@ -422,9 +422,7 @@ impl QuantizedKvPair { let (scale, zero_point) = Self::compute_scale_and_zero(&pair.keys, precision); #[cfg(all(target_arch = "aarch64", target_feature = "neon"))] - let quantize = |vals: &[f32]| -> Vec { - Self::quantize_neon(vals, scale, zero_point) - }; + let quantize = |vals: &[f32]| -> Vec { Self::quantize_neon(vals, scale, zero_point) }; #[cfg(not(all(target_arch = "aarch64", target_feature = "neon")))] let quantize = |vals: &[f32]| -> Vec { @@ -559,9 +557,8 @@ impl QuantizedKvPair { /// M4 Pro optimization: NEON-accelerated dequantization with 8x unrolling fn dequantize(&self) -> KvPair { #[cfg(all(target_arch = "aarch64", target_feature = "neon"))] - let dequant = |vals: &[f32]| -> Vec { - Self::dequantize_neon(vals, self.scale, self.zero_point) - }; + let dequant = + |vals: &[f32]| -> Vec { Self::dequantize_neon(vals, self.scale, self.zero_point) }; #[cfg(not(all(target_arch = "aarch64", target_feature = "neon")))] let dequant = |vals: &[f32]| -> Vec { @@ -815,20 +812,16 @@ impl TwoTierKvCache { // Migrate to store if tail exceeds threshold while tail.len() > self.config.tail_length { - let batch_size = self.config.migration_batch.min( - tail.len() - self.config.tail_length - ); + let batch_size = self + .config + .migration_batch + .min(tail.len() - self.config.tail_length); - let to_migrate: Vec<_> = (0..batch_size) - .filter_map(|_| tail.pop_front()) - .collect(); + let to_migrate: Vec<_> = (0..batch_size).filter_map(|_| tail.pop_front()).collect(); let mut store = self.store.write(); for pair in to_migrate { - let quantized = QuantizedKvPair::from_kv_pair( - &pair, - self.config.store_precision, - ); + let quantized = QuantizedKvPair::from_kv_pair(&pair, self.config.store_precision); store.push(quantized); } } @@ -865,7 +858,8 @@ impl TwoTierKvCache { for _ in 0..remaining.min(tail.len()) { tail.pop_front(); } - self.total_tokens.fetch_sub(remaining.min(tail.len()), Ordering::SeqCst); + self.total_tokens + .fetch_sub(remaining.min(tail.len()), Ordering::SeqCst); } Ok(()) @@ -956,7 +950,8 @@ impl TwoTierKvCache { let k_offset = t * stride; let k_slice = &keys[k_offset..k_offset + stride]; - let score: f32 = query.iter() + let score: f32 = query + .iter() .zip(k_slice.iter()) .map(|(q, k)| q * k * scale) .sum(); @@ -989,7 +984,8 @@ impl TwoTierKvCache { let stride = self.config.num_kv_heads * self.config.head_dim; let tail_bytes = tail.len() * stride * 4 * 2; // f32 * 2 (keys + values) - let store_bytes = store.len() * stride * self.config.store_precision.bytes_per_element() as usize * 2; + let store_bytes = + store.len() * stride * self.config.store_precision.bytes_per_element() as usize * 2; KvCacheStats { total_tokens: self.total_tokens.load(Ordering::SeqCst), @@ -1227,15 +1223,18 @@ impl PooledKvCache { self.tokens_per_block, self.config.num_kv_heads, self.config.head_dim, - ).ok_or_else(|| RuvLLMError::OutOfMemory( - "Failed to allocate KV block from pool".to_string(), - ))?; + ) + .ok_or_else(|| { + RuvLLMError::OutOfMemory("Failed to allocate KV block from pool".to_string()) + })?; blocks.push(new_block); } // SAFETY: blocks is non-empty because we either just pushed a new block // or the loop condition ensures at least one block exists - let block = blocks.last_mut().expect("blocks should be non-empty after allocation"); + let block = blocks + .last_mut() + .expect("blocks should be non-empty after allocation"); let tokens_appended = block.append(remaining_keys, remaining_values); if tokens_appended == 0 { @@ -1246,7 +1245,8 @@ impl PooledKvCache { remaining_keys = &remaining_keys[elements..]; remaining_values = &remaining_values[elements..]; - self.total_tokens.fetch_add(tokens_appended, Ordering::SeqCst); + self.total_tokens + .fetch_add(tokens_appended, Ordering::SeqCst); } // Enforce max tokens @@ -1272,13 +1272,15 @@ impl PooledKvCache { // Remove entire block blocks.remove(0); to_evict -= first_block_tokens; - self.total_tokens.fetch_sub(first_block_tokens, Ordering::SeqCst); + self.total_tokens + .fetch_sub(first_block_tokens, Ordering::SeqCst); } else { // Would need partial eviction - not supported in block model // For simplicity, we just remove the whole block let removed_tokens = blocks[0].token_count(); blocks.remove(0); - self.total_tokens.fetch_sub(removed_tokens, Ordering::SeqCst); + self.total_tokens + .fetch_sub(removed_tokens, Ordering::SeqCst); break; } } diff --git a/crates/ruvllm/src/lib.rs b/crates/ruvllm/src/lib.rs index b0cdee55f..a66826c49 100644 --- a/crates/ruvllm/src/lib.rs +++ b/crates/ruvllm/src/lib.rs @@ -81,264 +81,506 @@ pub mod witness_log; mod tests; // Re-exports -pub use adapter_manager::{AdapterManager, LoraAdapter, AdapterConfig}; +pub use adapter_manager::{AdapterConfig, AdapterManager, LoraAdapter}; pub use autodetect::{ - SystemCapabilities, Platform, Architecture, CpuFeatures, - GpuCapabilities, GpuBackend, CoreInfo, ComputeBackend, - InferenceConfig, -}; -pub use lora::{ - MicroLoRA, MicroLoraConfig, TargetModule, AdaptFeedback, - AdapterRegistry, AdapterPool, AdapterComposer, CompositionStrategy, - TrainingPipeline, TrainingConfig, EwcRegularizer, LearningRateSchedule, -}; -pub use backends::{ - create_backend, DeviceType, DType, GenerateParams, GeneratedToken, LlmBackend, - ModelArchitecture, ModelConfig, ModelInfo, Quantization, SharedBackend, SpecialTokens, - StreamEvent, TokenStream, Tokenizer, + Architecture, ComputeBackend, CoreInfo, CpuFeatures, GpuBackend, GpuCapabilities, + InferenceConfig, Platform, SystemCapabilities, }; #[cfg(feature = "candle")] pub use backends::CandleBackend; +pub use backends::{ + create_backend, DType, DeviceType, GenerateParams, GeneratedToken, LlmBackend, + ModelArchitecture, ModelConfig, ModelInfo, Quantization, SharedBackend, SpecialTokens, + StreamEvent, TokenStream, Tokenizer, +}; #[cfg(feature = "async-runtime")] pub use backends::{AsyncTokenStream, LlmBackendAsync}; -pub use error::{RuvLLMError, Result}; -pub use kv_cache::{ - TwoTierKvCache, KvCacheConfig, CacheTier, CacheQuantization, KvCacheStats, - PooledKvCache, PooledKvBlock, PooledKvCacheStats, -}; -pub use memory_pool::{ - InferenceArena, ArenaStats, - BufferPool, BufferSize, PooledBuffer, BufferPoolStats, - ScratchSpaceManager, ScratchSpace, ScratchStats, - MemoryManager, MemoryManagerConfig, MemoryManagerStats, - CACHE_LINE_SIZE, DEFAULT_ALIGNMENT, -}; -pub use paged_attention::{PagedAttention, PagedAttentionConfig, PageTable, PageBlock}; -pub use policy_store::{PolicyStore, PolicyEntry, PolicyType, QuantizationPolicy, RouterPolicy}; -pub use session::{SessionManager, Session, SessionConfig}; -pub use session_index::{SessionIndex, SessionState, KvCacheReference}; -pub use sona::{SonaIntegration, SonaConfig, LearningLoop}; pub use claude_flow::{ - ClaudeFlowAgent, ClaudeFlowTask, - AgentRouter, AgentType, RoutingDecision as AgentRoutingDecision, - TaskClassifier, TaskType, ClassificationResult, - FlowOptimizer, OptimizationConfig, OptimizationResult, - // HNSW semantic router (150x faster pattern search) - HnswRouter, HnswRouterConfig, HnswRouterStats, HnswRoutingResult, - HnswDistanceMetric, TaskPattern, HybridRouter, + AgentContext, + AgentCoordinator, + AgentRouter, + AgentState, + AgentType, + AnalyzerStats as ModelAnalyzerStats, + ClassificationResult, + ClaudeFlowAgent, + ClaudeFlowTask, // Claude API Integration (NEW) - ClaudeModel, MessageRole, ContentBlock, Message, ClaudeRequest, ClaudeResponse, UsageStats, - StreamToken, StreamEvent as ClaudeStreamEvent, QualityMonitor, ResponseStreamer, StreamStats, - ContextWindow, ContextManager, - AgentState, AgentContext, WorkflowStep, WorkflowResult, StepResult, - AgentCoordinator, CoordinatorStats, - CostEstimator, LatencyTracker, LatencySample, LatencyStats as ClaudeLatencyStats, + ClaudeModel, + ClaudeRequest, + ClaudeResponse, // Model Router (NEW) - Intelligent routing to Haiku/Sonnet/Opus - ComplexityFactors, ComplexityWeights, ComplexityScore, - TaskComplexityAnalyzer, AnalyzerStats as ModelAnalyzerStats, - SelectionCriteria, ModelRoutingDecision, ModelSelector, SelectorStats, - ModelRouter, + ComplexityFactors, + ComplexityScore, + ComplexityWeights, + ContentBlock, + ContextManager, + ContextWindow, + CoordinatorStats, + CostEstimator, + FlowOptimizer, + HnswDistanceMetric, + // HNSW semantic router (150x faster pattern search) + HnswRouter, + HnswRouterConfig, + HnswRouterStats, + HnswRoutingResult, + HooksConfig, // Hooks Integration (NEW v2.3) - Unified Claude Flow hooks interface - HooksIntegration, HooksConfig, - PreTaskInput, PreTaskResult, PostTaskInput, PostTaskResult, - PreEditInput, PreEditResult, PostEditInput, PostEditResult, - SessionState as HooksSessionState, SessionEndResult, SessionMetrics, - PatternMatch, QualityAssessment, LearningMetrics, + HooksIntegration, + HybridRouter, + LatencySample, + LatencyStats as ClaudeLatencyStats, + LatencyTracker, + LearningMetrics, + Message, + MessageRole, + ModelRouter, + ModelRoutingDecision, + ModelSelector, + OptimizationConfig, + OptimizationResult, + PatternMatch, + PostEditInput, + PostEditResult, + PostTaskInput, + PostTaskResult, + PreEditInput, + PreEditResult, + PreTaskInput, + PreTaskResult, + QualityAssessment, + QualityMonitor, + ResponseStreamer, + RoutingDecision as AgentRoutingDecision, + SelectionCriteria, + SelectorStats, + SessionEndResult, + SessionMetrics, + SessionState as HooksSessionState, + StepResult, + StreamEvent as ClaudeStreamEvent, + StreamStats, + StreamToken, + TaskClassifier, + TaskComplexityAnalyzer, + TaskPattern, + TaskType, + UsageStats, + WorkflowResult, + WorkflowStep, }; -pub use optimization::{ - InferenceMetrics, MetricsCollector, MetricsSnapshot, MovingAverage, LatencyHistogram, - RealtimeOptimizer, RealtimeConfig, BatchSizeStrategy, KvCachePressurePolicy, - TokenBudgetAllocation, SpeculativeConfig, OptimizationDecision, - SonaLlm, SonaLlmConfig, TrainingSample, AdaptationResult, LearningLoopStats, - ConsolidationStrategy, OptimizationTrigger, -}; -pub use tokenizer::{ - RuvTokenizer, ChatMessage, ChatTemplate, Role, TokenizerSpecialTokens, - StreamingDecodeBuffer, -}; -pub use speculative::{ - SpeculativeDecoder, SpeculativeConfig as SpeculativeDecodingConfig, - SpeculativeStats, AtomicSpeculativeStats, VerificationResult, - SpeculationTree, TreeNode, - softmax, log_softmax, sample_from_probs, top_k_filter, top_p_filter, -}; -pub use types::*; -pub use witness_log::{WitnessLog, WitnessEntry, LatencyBreakdown, RoutingDecision, AsyncWriteConfig, WitnessLogStats}; +pub use error::{Result, RuvLLMError}; pub use gguf::{ - GgufFile, GgufModelLoader, GgufHeader, GgufValue, GgufQuantType, - TensorInfo, QuantizedTensor, ModelConfig as GgufModelConfig, + GgufFile, + GgufHeader, // New GGUF loading types - GgufLoader, LoadConfig, LoadProgress, LoadedWeights, LoadedTensor, - TensorCategory, TensorNameMapper, StreamingLoader, - ModelInitializer, ModelWeights, LayerWeights, WeightTensor, QuantizedWeight, + GgufLoader, + GgufModelLoader, + GgufQuantType, + GgufValue, + LayerWeights, + LoadConfig, + LoadProgress, + LoadedTensor, + LoadedWeights, + ModelConfig as GgufModelConfig, + ModelInitializer, + ModelWeights, ProgressModelBuilder, + QuantizedTensor, + QuantizedWeight, + StreamingLoader, + TensorCategory, + TensorInfo, + TensorNameMapper, + WeightTensor, }; pub use hub::{ - // Download - ModelDownloader, DownloadConfig, DownloadProgress, DownloadError, ChecksumVerifier, - // Upload - ModelUploader, UploadConfig, UploadProgress, UploadError, ModelMetadata, - // Registry - RuvLtraRegistry, ModelInfo as HubModelInfo, ModelSize, QuantizationLevel, - HardwareRequirements, get_model_info, - // Model Card - ModelCard, ModelCardBuilder, TaskType as HubTaskType, Framework, License, DatasetInfo, MetricResult, - // Progress - ProgressBar, ProgressIndicator, ProgressStyle, ProgressCallback, MultiProgress, + default_cache_dir, + get_hf_token, + get_model_info, + ChecksumVerifier, + DatasetInfo, + DownloadConfig, + DownloadError, + DownloadProgress, + Framework, + HardwareRequirements, // Common - HubError, default_cache_dir, get_hf_token, + HubError, + License, + MetricResult, + // Model Card + ModelCard, + ModelCardBuilder, + // Download + ModelDownloader, + ModelInfo as HubModelInfo, + ModelMetadata, + ModelSize, + // Upload + ModelUploader, + MultiProgress, + // Progress + ProgressBar, + ProgressCallback, + ProgressIndicator, + ProgressStyle, + QuantizationLevel, + // Registry + RuvLtraRegistry, + TaskType as HubTaskType, + UploadConfig, + UploadError, + UploadProgress, +}; +pub use kv_cache::{ + CacheQuantization, CacheTier, KvCacheConfig, KvCacheStats, PooledKvBlock, PooledKvCache, + PooledKvCacheStats, TwoTierKvCache, +}; +pub use lora::{ + AdaptFeedback, AdapterComposer, AdapterPool, AdapterRegistry, CompositionStrategy, + EwcRegularizer, LearningRateSchedule, MicroLoRA, MicroLoraConfig, TargetModule, TrainingConfig, + TrainingPipeline, +}; +pub use memory_pool::{ + ArenaStats, BufferPool, BufferPoolStats, BufferSize, InferenceArena, MemoryManager, + MemoryManagerConfig, MemoryManagerStats, PooledBuffer, ScratchSpace, ScratchSpaceManager, + ScratchStats, CACHE_LINE_SIZE, DEFAULT_ALIGNMENT, +}; +pub use optimization::{ + AdaptationResult, BatchSizeStrategy, ConsolidationStrategy, InferenceMetrics, + KvCachePressurePolicy, LatencyHistogram, LearningLoopStats, MetricsCollector, MetricsSnapshot, + MovingAverage, OptimizationDecision, OptimizationTrigger, RealtimeConfig, RealtimeOptimizer, + SonaLlm, SonaLlmConfig, SpeculativeConfig, TokenBudgetAllocation, TrainingSample, +}; +pub use paged_attention::{PageBlock, PageTable, PagedAttention, PagedAttentionConfig}; +pub use policy_store::{PolicyEntry, PolicyStore, PolicyType, QuantizationPolicy, RouterPolicy}; +pub use quantize::{ + dequantize_for_ane, + // Memory estimation + estimate_memory_q4, + estimate_memory_q5, + estimate_memory_q8, + // Quantization functions + quantize_ruvltra_q4, + quantize_ruvltra_q5, + quantize_ruvltra_q8, + MemoryEstimate, + // Block types + Q4KMBlock, + Q5KMBlock, + Q8Block, + QuantConfig, + // Progress tracking + QuantProgress, + QuantStats, + // Core quantizer + RuvltraQuantizer, + TargetFormat, }; pub use serving::{ - // Request types - InferenceRequest, RequestId, Priority, RequestState, RunningRequest, - CompletedRequest, FinishReason, TokenOutput, + BatchStats, // Batch types - BatchedRequest, BatchStats, ScheduledBatch, IterationPlan, PrefillTask, DecodeTask, TokenBudget, - // KV cache management - KvCacheManager, KvCachePoolConfig, KvCacheAllocation, KvCacheManagerStats, + BatchedRequest, + CompletedRequest, // Scheduler - ContinuousBatchScheduler, IterationScheduler, SchedulerConfig, SchedulerStats, - RequestQueue, PreemptionMode, PriorityPolicy, + ContinuousBatchScheduler, + DecodeTask, + FinishReason, + GenerationResult, + // Request types + InferenceRequest, + IterationPlan, + IterationScheduler, + KvCacheAllocation, + // KV cache management + KvCacheManager, + KvCacheManagerStats, + KvCachePoolConfig, + PreemptionMode, + PrefillTask, + Priority, + PriorityPolicy, + RequestId, + RequestQueue, + RequestState, + RunningRequest, + ScheduledBatch, + SchedulerConfig, + SchedulerStats, // Engine - ServingEngine, ServingEngineConfig, ServingMetrics, GenerationResult, + ServingEngine, + ServingEngineConfig, + ServingMetrics, + TokenBudget, + TokenOutput, }; -pub use quantize::{ - // Core quantizer - RuvltraQuantizer, QuantConfig, TargetFormat, - // Quantization functions - quantize_ruvltra_q4, quantize_ruvltra_q5, quantize_ruvltra_q8, dequantize_for_ane, - // Memory estimation - estimate_memory_q4, estimate_memory_q5, estimate_memory_q8, MemoryEstimate, - // Block types - Q4KMBlock, Q5KMBlock, Q8Block, - // Progress tracking - QuantProgress, QuantStats, +pub use session::{Session, SessionConfig, SessionManager}; +pub use session_index::{KvCacheReference, SessionIndex, SessionState}; +pub use sona::{LearningLoop, SonaConfig, SonaIntegration}; +pub use speculative::{ + log_softmax, sample_from_probs, softmax, top_k_filter, top_p_filter, AtomicSpeculativeStats, + SpeculationTree, SpeculativeConfig as SpeculativeDecodingConfig, SpeculativeDecoder, + SpeculativeStats, TreeNode, VerificationResult, +}; +pub use tokenizer::{ + ChatMessage, ChatTemplate, Role, RuvTokenizer, StreamingDecodeBuffer, TokenizerSpecialTokens, }; pub use training::{ + AugmentationConfig, // Claude task dataset - ClaudeTaskDataset, ClaudeTaskExample, TaskCategory, TaskMetadata, - ComplexityLevel, DomainType, DatasetConfig, AugmentationConfig, - DatasetGenerator, DatasetStats, + ClaudeTaskDataset, + ClaudeTaskExample, + ComplexityLevel, + DatasetConfig, + DatasetGenerator, + DatasetStats, + DifficultyLevel, + DifficultyWeights, + DomainType, + EvaluationMetrics, + GrpoBatch, // GRPO optimizer for reinforcement learning - GrpoConfig, GrpoOptimizer, GrpoSample, GrpoStats, GrpoUpdateResult, - GrpoBatch, SampleGroup, - // MCP tool training - McpToolTrainer, McpTrainingConfig, ToolTrajectory, TrajectoryStep, - TrajectoryBuilder, StepBuilder, TrajectoryMetadata, - TrainingResult, TrainingStats, TrainingCheckpoint, EvaluationMetrics, - // Tool calling dataset - ToolCallDataset, ToolCallExample, ToolDatasetConfig, ToolDatasetStats, - McpToolDef, ToolParam, ParamType, DifficultyLevel, DifficultyWeights, + GrpoConfig, + GrpoOptimizer, + GrpoSample, + GrpoStats, + GrpoUpdateResult, McpToolCategory, + McpToolDef, + // MCP tool training + McpToolTrainer, + McpTrainingConfig, + ParamType, + SampleGroup, + StepBuilder, + TaskCategory, + TaskMetadata, + // Tool calling dataset + ToolCallDataset, + ToolCallExample, + ToolDatasetConfig, + ToolDatasetStats, + ToolParam, + ToolTrajectory, + TrainingCheckpoint, + TrainingResult, + TrainingStats, + TrajectoryBuilder, + TrajectoryMetadata, + TrajectoryStep, +}; +pub use types::*; +pub use witness_log::{ + AsyncWriteConfig, LatencyBreakdown, RoutingDecision, WitnessEntry, WitnessLog, WitnessLogStats, }; // RuvLTRA model architecture exports pub use models::{ + AneDispatcher, + AneOptimization, + MemoryLayout, + QuantizationType, + RuvLtraAttention, // Configuration - RuvLtraConfig, AneOptimization, QuantizationType, MemoryLayout, + RuvLtraConfig, + RuvLtraDecoderLayer, + RuvLtraMLP, // Model components - RuvLtraModel, RuvLtraAttention, RuvLtraMLP, RuvLtraDecoderLayer, + RuvLtraModel, // Utilities - RuvLtraModelInfo, AneDispatcher, + RuvLtraModelInfo, }; // Ruvector integration exports (unified entry point for all Ruvector capabilities) pub use capabilities::{ - RuvectorCapabilities, HNSW_AVAILABLE, ATTENTION_AVAILABLE, GRAPH_AVAILABLE, - GNN_AVAILABLE, SONA_AVAILABLE, SIMD_AVAILABLE, PARALLEL_AVAILABLE, - gate_feature, gate_feature_or, + gate_feature, gate_feature_or, RuvectorCapabilities, ATTENTION_AVAILABLE, GNN_AVAILABLE, + GRAPH_AVAILABLE, HNSW_AVAILABLE, PARALLEL_AVAILABLE, SIMD_AVAILABLE, SONA_AVAILABLE, }; pub use ruvector_integration::{ - // Main integration - RuvectorIntegration, IntegrationConfig, IntegrationStats, - // Unified index - UnifiedIndex, VectorMetadata, IndexStats, SearchResultWithMetadata, + IndexStats, + IntegrationConfig, + IntegrationStats, // Intelligence layer - IntelligenceLayer, IntelligentRoutingDecision, IntelligenceLayerStats, + IntelligenceLayer, + IntelligenceLayerStats, + IntelligentRoutingDecision, + // Main integration + RuvectorIntegration, + SearchResultWithMetadata, + // Unified index + UnifiedIndex, + VectorMetadata, }; // Quality scoring exports pub use quality::{ - // Core metrics - QualityMetrics, QualityWeights, QualityDimension, QualitySummary, TrendDirection, - // Scoring engine - QualityScoringEngine, ScoringConfig, ScoringContext, QualityHistory, - ComparisonResult, TrendAnalysis, ImprovementRecommendation, + CoherenceConfig, // Coherence validation - CoherenceValidator, CoherenceConfig, SemanticConsistencyResult, - ContradictionResult, CoherenceViolation, LogicalFlowResult, + CoherenceValidator, + CoherenceViolation, + CombinedValidator, + ComparisonResult, + ContradictionResult, + DiversificationSuggestion, // Diversity analysis - DiversityAnalyzer, DiversityConfig, DiversityResult, - DiversificationSuggestion, ModeCollapseResult, + DiversityAnalyzer, + DiversityConfig, + DiversityResult, + FormatValidator, + ImprovementRecommendation, + JsonSchemaValidator, + LogicalFlowResult, + ModeCollapseResult, + QualityDimension, + QualityHistory, + // Core metrics + QualityMetrics, + // Scoring engine + QualityScoringEngine, + QualitySummary, + QualityWeights, + RangeValidator, // Schema validators - SchemaValidator, JsonSchemaValidator, TypeValidator, RangeValidator, - FormatValidator, CombinedValidator, ValidationResult, ValidationError, + SchemaValidator, + ScoringConfig, + ScoringContext, + SemanticConsistencyResult, + TrendAnalysis, + TrendDirection, + TypeValidator, ValidationCombinator, + ValidationError, + ValidationResult, }; // Context management exports (intelligent pruning and semantic memory) pub use context::{ // Agentic memory - AgenticMemory, AgenticMemoryConfig, MemoryType, - // Working memory - WorkingMemory, WorkingMemoryConfig, TaskContext, ScratchpadEntry, AttentionWeights, - // Episodic memory - EpisodicMemory, EpisodicMemoryConfig, Episode, EpisodeMetadata, - EpisodeTrajectory, CompressedEpisode, - // Context manager - IntelligentContextManager, ContextManagerConfig, PreparedContext, - PriorityScorer, ContextElement, ElementPriority, - // Semantic cache - SemanticToolCache, SemanticCacheConfig, CachedToolResult, CacheStats, + AgenticMemory, + AgenticMemoryConfig, + AttentionWeights, + CacheStats, + CachedToolResult, + ClaudeFlowBridgeConfig, // Claude Flow bridge - ClaudeFlowMemoryBridge, ClaudeFlowBridgeConfig, SyncResult, + ClaudeFlowMemoryBridge, + CompressedEpisode, + ContextElement, + ContextManagerConfig, + ElementPriority, + Episode, + EpisodeMetadata, + EpisodeTrajectory, + // Episodic memory + EpisodicMemory, + EpisodicMemoryConfig, + // Context manager + IntelligentContextManager, + MemoryType, + PreparedContext, + PriorityScorer, + ScratchpadEntry, + SemanticCacheConfig, + // Semantic cache + SemanticToolCache, + SyncResult, + TaskContext, + // Working memory + WorkingMemory, + WorkingMemoryConfig, }; // Self-Reflection architecture exports (error recovery and self-correction) pub use reflection::{ - // Reflective agent wrapper - ReflectiveAgent, ReflectionStrategy, ReflectionConfig, RetryConfig, - ExecutionContext, ExecutionResult, Reflection, PreviousAttempt, - BaseAgent, ReflectiveAgentStats, + BaseAgent, + CompletenessChecker, + ConfidenceCheckRecord, // Confidence-based revision (IoE pattern) - ConfidenceChecker, ConfidenceConfig, ConfidenceLevel, WeakPoint, RevisionResult, - ConfidenceCheckRecord, ConfidenceFactorWeights, WeaknessType, + ConfidenceChecker, + ConfidenceConfig, + ConfidenceFactorWeights, + ConfidenceLevel, + ConsistencyChecker, + CorrectnessChecker, + CritiqueIssue, + CritiqueResult, + ErrorCategory, + ErrorCluster, + ErrorLearnerStats, + ErrorPattern, // Error pattern learning - ErrorPatternLearner, ErrorPatternLearnerConfig, ErrorPattern, ErrorCluster, - RecoveryStrategy, RecoverySuggestion, ErrorCategory, RecoveryOutcome, - SimilarError, ErrorLearnerStats, + ErrorPatternLearner, + ErrorPatternLearnerConfig, + ExecutionContext, + ExecutionResult, + IssueCategory, // Multi-perspective critique - Perspective, CorrectnessChecker, CompletenessChecker, ConsistencyChecker, - CritiqueResult, CritiqueIssue, IssueCategory, UnifiedCritique, PerspectiveConfig, + Perspective, + PerspectiveConfig, + PreviousAttempt, + RecoveryOutcome, + RecoveryStrategy, + RecoverySuggestion, + Reflection, + ReflectionConfig, + ReflectionStrategy, + // Reflective agent wrapper + ReflectiveAgent, + ReflectiveAgentStats, + RetryConfig, + RevisionResult, + SimilarError, + UnifiedCritique, + WeakPoint, + WeaknessType, }; // ReasoningBank exports (learning from Claude trajectories) pub use reasoning_bank::{ + CompressedTrajectory, + ConsolidationConfig, + DistillationConfig, + FailurePattern as VerdictFailurePattern, + FisherInformation, + ImportanceScore, + KeyLesson, + // Memory distillation + MemoryDistiller, + Pattern, + PatternCategory, + // EWC++ consolidation + PatternConsolidator, + PatternSearchResult, + PatternStats, + // Pattern storage with HNSW + PatternStore, + PatternStoreConfig, // Main ReasoningBank - ReasoningBank, ReasoningBankConfig, ReasoningBankStats, + ReasoningBank, + ReasoningBankConfig, + ReasoningBankStats, + RecoveryStrategy as VerdictRecoveryStrategy, + RootCause, + StepOutcome, // Trajectory recording (aliased to avoid conflict with training::TrajectoryStep) Trajectory as ReasoningTrajectory, + TrajectoryId, + TrajectoryRecorder, TrajectoryStep as ReasoningTrajectoryStep, - TrajectoryRecorder, TrajectoryId, StepOutcome, - // Pattern storage with HNSW - PatternStore, PatternStoreConfig, Pattern, PatternCategory, PatternSearchResult, PatternStats, // Verdict system (aliased to avoid conflict with claude_flow::reasoning_bank::Verdict) Verdict as ReasoningVerdict, - RootCause, VerdictAnalyzer, FailurePattern as VerdictFailurePattern, - RecoveryStrategy as VerdictRecoveryStrategy, - // EWC++ consolidation - PatternConsolidator, ConsolidationConfig, FisherInformation, ImportanceScore, - // Memory distillation - MemoryDistiller, DistillationConfig, CompressedTrajectory, KeyLesson, + VerdictAnalyzer, }; // Metal GPU acceleration exports (macOS only) #[cfg(all(target_os = "macos", feature = "metal-compute"))] pub use metal::{ - MetalContext, MetalConfig, MetalPipelines, MetalBuffer, MetalBufferPool, - AttentionParams, GemmParams, NormParams, RopeParams, - is_metal_available, get_device_info, MetalDeviceInfo, - tile_sizes, shader_source, + get_device_info, is_metal_available, shader_source, tile_sizes, AttentionParams, GemmParams, + MetalBuffer, MetalBufferPool, MetalConfig, MetalContext, MetalDeviceInfo, MetalPipelines, + NormParams, RopeParams, }; /// RuvLLM engine configuration. @@ -489,20 +731,14 @@ impl RuvLLMEngine { pub fn new(config: RuvLLMConfig) -> Result { let storage_path = &config.storage_path; - let policy_store = PolicyStore::new( - &format!("{}/policies", storage_path), - config.embedding_dim, - )?; + let policy_store = + PolicyStore::new(&format!("{}/policies", storage_path), config.embedding_dim)?; - let session_index = SessionIndex::new( - &format!("{}/sessions", storage_path), - config.embedding_dim, - )?; + let session_index = + SessionIndex::new(&format!("{}/sessions", storage_path), config.embedding_dim)?; - let witness_log = WitnessLog::new( - &format!("{}/witness", storage_path), - config.embedding_dim, - )?; + let witness_log = + WitnessLog::new(&format!("{}/witness", storage_path), config.embedding_dim)?; let session_manager = SessionManager::new(config.session.clone()); let adapter_manager = AdapterManager::new(); @@ -583,7 +819,11 @@ impl RuvLLMEngine { /// println!("Policy: {:?}, score: {}", policy.policy_type, policy.score); /// } /// ``` - pub fn search_policies(&self, context_embedding: &[f32], limit: usize) -> Result> { + pub fn search_policies( + &self, + context_embedding: &[f32], + limit: usize, + ) -> Result> { self.policy_store.search(context_embedding, limit) } @@ -621,7 +861,11 @@ impl RuvLLMEngine { } /// Search witness logs semantically - pub fn search_witness(&self, query_embedding: &[f32], limit: usize) -> Result> { + pub fn search_witness( + &self, + query_embedding: &[f32], + limit: usize, + ) -> Result> { self.witness_log.search(query_embedding, limit) } @@ -640,4 +884,3 @@ impl RuvLLMEngine { &self.policy_store } } - diff --git a/crates/ruvllm/src/lora/adapter.rs b/crates/ruvllm/src/lora/adapter.rs index d934ad2a8..87fd1425f 100644 --- a/crates/ruvllm/src/lora/adapter.rs +++ b/crates/ruvllm/src/lora/adapter.rs @@ -65,7 +65,7 @@ impl AdapterHandle { std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap_or_default() - .as_secs() + .as_secs(), )), } } @@ -210,7 +210,8 @@ impl AdapterRegistry { self.adapters.insert(id, entry); self.name_index.insert(name, id); - self.current_memory.fetch_add(memory_needed, Ordering::SeqCst); + self.current_memory + .fetch_add(memory_needed, Ordering::SeqCst); Ok(handle) } @@ -239,7 +240,9 @@ impl AdapterRegistry { /// Set active adapter by name pub fn set_active_by_name(&self, name: &str) -> Result<()> { - let id = self.name_index.get(name) + let id = self + .name_index + .get(name) .map(|r| *r) .ok_or_else(|| RuvLLMError::NotFound(format!("Adapter '{}' not found", name)))?; self.set_active(id) @@ -254,7 +257,8 @@ impl AdapterRegistry { pub fn unregister(&self, id: &Uuid) -> Result<()> { if let Some((_, entry)) = self.adapters.remove(id) { self.name_index.remove(&entry.handle.name); - self.current_memory.fetch_sub(entry.adapter.memory_bytes(), Ordering::SeqCst); + self.current_memory + .fetch_sub(entry.adapter.memory_bytes(), Ordering::SeqCst); // Clear active if this was the active adapter let mut active = self.active_id.write(); @@ -267,8 +271,9 @@ impl AdapterRegistry { /// List all registered adapters pub fn list(&self) -> Vec { - self.adapters.iter().map(|entry| { - AdapterInfo { + self.adapters + .iter() + .map(|entry| AdapterInfo { id: entry.handle.id, name: entry.handle.name.clone(), version: entry.handle.version, @@ -277,8 +282,8 @@ impl AdapterRegistry { domain: entry.metadata.domain.clone(), quality_score: entry.metadata.quality_score, last_accessed: entry.handle.last_accessed(), - } - }).collect() + }) + .collect() } /// Get memory statistics @@ -301,14 +306,20 @@ impl AdapterRegistry { } // Need to evict some adapters - let mut entries: Vec<_> = self.adapters.iter() - .map(|e| (e.key().clone(), e.handle.last_accessed(), e.handle.ref_count())) + let mut entries: Vec<_> = self + .adapters + .iter() + .map(|e| { + ( + e.key().clone(), + e.handle.last_accessed(), + e.handle.ref_count(), + ) + }) .collect(); // Sort by last accessed (oldest first), then by ref count (lowest first) - entries.sort_by(|a, b| { - a.1.cmp(&b.1).then(a.2.cmp(&b.2)) - }); + entries.sort_by(|a, b| a.1.cmp(&b.1).then(a.2.cmp(&b.2))); let mut freed = 0; for (id, _, ref_count) in entries { @@ -324,13 +335,14 @@ impl AdapterRegistry { if let Some((_, entry)) = self.adapters.remove(&id) { freed += entry.adapter.memory_bytes(); self.name_index.remove(&entry.handle.name); - self.current_memory.fetch_sub(entry.adapter.memory_bytes(), Ordering::SeqCst); + self.current_memory + .fetch_sub(entry.adapter.memory_bytes(), Ordering::SeqCst); } } if freed < needed || self.adapters.len() >= self.max_adapters { return Err(RuvLLMError::OutOfMemory( - "Cannot free enough memory for new adapter".to_string() + "Cannot free enough memory for new adapter".to_string(), )); } @@ -380,9 +392,7 @@ pub struct AdapterPool { impl AdapterPool { /// Create a new adapter pool pub fn new(config: MicroLoraConfig, size: usize) -> Self { - let available: Vec<_> = (0..size) - .map(|_| MicroLoRA::new(config.clone())) - .collect(); + let available: Vec<_> = (0..size).map(|_| MicroLoRA::new(config.clone())).collect(); Self { available: RwLock::new(available), @@ -569,7 +579,8 @@ impl AdapterComposer { let output_b = adapter_b.forward(x, module); let t = self.interpolation; - output_a.iter() + output_a + .iter() .zip(output_b.iter()) .map(|(a, b)| a * (1.0 - t) + b * t) .collect() @@ -619,11 +630,13 @@ mod tests { let config = MicroLoraConfig::for_hidden_dim(64); let adapter = MicroLoRA::new(config); - let handle = registry.register( - "test-adapter".to_string(), - adapter, - AdapterMetadata::default(), - ).unwrap(); + let handle = registry + .register( + "test-adapter".to_string(), + adapter, + AdapterMetadata::default(), + ) + .unwrap(); assert_eq!(registry.list().len(), 1); assert!(registry.get(&handle.id).is_some()); @@ -636,18 +649,22 @@ mod tests { let config = MicroLoraConfig::for_hidden_dim(64); let adapter1 = MicroLoRA::new(config.clone()); - let handle1 = registry.register( - "adapter-1".to_string(), - adapter1, - AdapterMetadata::default(), - ).unwrap(); + let handle1 = registry + .register( + "adapter-1".to_string(), + adapter1, + AdapterMetadata::default(), + ) + .unwrap(); let adapter2 = MicroLoRA::new(config); - let _handle2 = registry.register( - "adapter-2".to_string(), - adapter2, - AdapterMetadata::default(), - ).unwrap(); + let _handle2 = registry + .register( + "adapter-2".to_string(), + adapter2, + AdapterMetadata::default(), + ) + .unwrap(); registry.set_active(handle1.id).unwrap(); assert!(registry.get_active().is_some()); diff --git a/crates/ruvllm/src/lora/adapters/merge.rs b/crates/ruvllm/src/lora/adapters/merge.rs index 9bd46b7d7..531c07338 100644 --- a/crates/ruvllm/src/lora/adapters/merge.rs +++ b/crates/ruvllm/src/lora/adapters/merge.rs @@ -7,11 +7,11 @@ //! - Interpolation between adapters use crate::error::{Result, RuvLLMError}; -use crate::lora::micro_lora::{MicroLoRA, MicroLoraConfig, LoraAdapter, TargetModule}; use crate::lora::adapters::LoraConfig; +use crate::lora::micro_lora::{LoraAdapter, MicroLoRA, MicroLoraConfig, TargetModule}; +use ndarray::Array2; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use ndarray::Array2; /// Strategy for merging adapters #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] @@ -122,7 +122,9 @@ impl AdapterMerger { MergeStrategy::Slerp => self.merge_slerp(adapters, output_config, hidden_dim), MergeStrategy::Ties => self.merge_ties(adapters, output_config, hidden_dim), MergeStrategy::Dare => self.merge_dare(adapters, output_config, hidden_dim), - MergeStrategy::TaskArithmetic => self.merge_task_arithmetic(adapters, output_config, hidden_dim), + MergeStrategy::TaskArithmetic => { + self.merge_task_arithmetic(adapters, output_config, hidden_dim) + } } } @@ -139,7 +141,8 @@ impl AdapterMerger { let n = adapters.len() as f32; for module in &output_config.target_modules { - let merged_adapter = merged.get_adapter(module) + let merged_adapter = merged + .get_adapter(module) .ok_or_else(|| RuvLLMError::NotFound(format!("Module {:?} not found", module)))?; let mut merged_adapter = merged_adapter.write(); @@ -178,12 +181,14 @@ impl AdapterMerger { let merged = MicroLoRA::new(micro_config); // Normalize weights - let total_weight: f32 = adapters.iter() + let total_weight: f32 = adapters + .iter() .map(|(name, _)| self.config.weights.get(name).copied().unwrap_or(1.0)) .sum(); for module in &output_config.target_modules { - let merged_adapter = merged.get_adapter(module) + let merged_adapter = merged + .get_adapter(module) .ok_or_else(|| RuvLLMError::NotFound(format!("Module {:?} not found", module)))?; let mut merged_adapter = merged_adapter.write(); @@ -201,13 +206,15 @@ impl AdapterMerger { for i in 0..merged_adapter.lora_a.nrows() { for j in 0..merged_adapter.lora_a.ncols() { - merged_adapter.lora_a[[i, j]] += adapter.lora_a[[i, j]] * normalized_weight; + merged_adapter.lora_a[[i, j]] += + adapter.lora_a[[i, j]] * normalized_weight; } } for i in 0..merged_adapter.lora_b.nrows() { for j in 0..merged_adapter.lora_b.ncols() { - merged_adapter.lora_b[[i, j]] += adapter.lora_b[[i, j]] * normalized_weight; + merged_adapter.lora_b[[i, j]] += + adapter.lora_b[[i, j]] * normalized_weight; } } } @@ -225,7 +232,9 @@ impl AdapterMerger { hidden_dim: usize, ) -> Result { if adapters.len() != 2 { - return Err(RuvLLMError::Config("SLERP requires exactly 2 adapters".to_string())); + return Err(RuvLLMError::Config( + "SLERP requires exactly 2 adapters".to_string(), + )); } let micro_config = output_config.to_micro_lora_config(hidden_dim)?; @@ -236,23 +245,36 @@ impl AdapterMerger { let (_, lora_b) = &adapters[1]; for module in &output_config.target_modules { - let merged_adapter = merged.get_adapter(module) + let merged_adapter = merged + .get_adapter(module) .ok_or_else(|| RuvLLMError::NotFound(format!("Module {:?} not found", module)))?; let mut merged_adapter = merged_adapter.write(); - let adapter_a = lora_a.get_adapter(module) - .ok_or_else(|| RuvLLMError::NotFound(format!("Module {:?} not found in first adapter", module)))?; - let adapter_b = lora_b.get_adapter(module) - .ok_or_else(|| RuvLLMError::NotFound(format!("Module {:?} not found in second adapter", module)))?; + let adapter_a = lora_a.get_adapter(module).ok_or_else(|| { + RuvLLMError::NotFound(format!("Module {:?} not found in first adapter", module)) + })?; + let adapter_b = lora_b.get_adapter(module).ok_or_else(|| { + RuvLLMError::NotFound(format!("Module {:?} not found in second adapter", module)) + })?; let adapter_a = adapter_a.read(); let adapter_b = adapter_b.read(); // SLERP for A matrix - self.slerp_matrix(&adapter_a.lora_a, &adapter_b.lora_a, t, &mut merged_adapter.lora_a); + self.slerp_matrix( + &adapter_a.lora_a, + &adapter_b.lora_a, + t, + &mut merged_adapter.lora_a, + ); // SLERP for B matrix - self.slerp_matrix(&adapter_a.lora_b, &adapter_b.lora_b, t, &mut merged_adapter.lora_b); + self.slerp_matrix( + &adapter_a.lora_b, + &adapter_b.lora_b, + t, + &mut merged_adapter.lora_b, + ); } Ok(merged) @@ -279,19 +301,16 @@ impl AdapterMerger { let merged = MicroLoRA::new(micro_config); for module in &output_config.target_modules { - let merged_adapter = merged.get_adapter(module) + let merged_adapter = merged + .get_adapter(module) .ok_or_else(|| RuvLLMError::NotFound(format!("Module {:?} not found", module)))?; let mut merged_adapter = merged_adapter.write(); // Collect all values for each position - let mut values_a: Vec> = vec![ - vec![]; - merged_adapter.lora_a.nrows() * merged_adapter.lora_a.ncols() - ]; - let mut values_b: Vec> = vec![ - vec![]; - merged_adapter.lora_b.nrows() * merged_adapter.lora_b.ncols() - ]; + let mut values_a: Vec> = + vec![vec![]; merged_adapter.lora_a.nrows() * merged_adapter.lora_a.ncols()]; + let mut values_b: Vec> = + vec![vec![]; merged_adapter.lora_b.nrows() * merged_adapter.lora_b.ncols()]; for (_name, lora) in adapters { if let Some(adapter) = lora.get_adapter(module) { @@ -345,7 +364,8 @@ impl AdapterMerger { let threshold = max_abs * (1.0 - self.config.density); // Trim - let trimmed: Vec = values.iter() + let trimmed: Vec = values + .iter() .copied() .filter(|v| v.abs() >= threshold) .collect(); @@ -377,8 +397,8 @@ impl AdapterMerger { output_config: &LoraConfig, hidden_dim: usize, ) -> Result { - use rand::{Rng, SeedableRng}; use rand::rngs::StdRng; + use rand::{Rng, SeedableRng}; let mut rng = StdRng::seed_from_u64(42); @@ -386,7 +406,8 @@ impl AdapterMerger { let merged = MicroLoRA::new(micro_config); for module in &output_config.target_modules { - let merged_adapter = merged.get_adapter(module) + let merged_adapter = merged + .get_adapter(module) .ok_or_else(|| RuvLLMError::NotFound(format!("Module {:?} not found", module)))?; let mut merged_adapter = merged_adapter.write(); @@ -474,7 +495,9 @@ impl HotSwapManager { } if self.standby.is_none() { - return Err(RuvLLMError::Config("No standby adapter prepared".to_string())); + return Err(RuvLLMError::Config( + "No standby adapter prepared".to_string(), + )); } self.swapping = true; diff --git a/crates/ruvllm/src/lora/adapters/mod.rs b/crates/ruvllm/src/lora/adapters/mod.rs index b27fb6621..5a7f6ebd9 100644 --- a/crates/ruvllm/src/lora/adapters/mod.rs +++ b/crates/ruvllm/src/lora/adapters/mod.rs @@ -16,8 +16,8 @@ use crate::lora::micro_lora::{MicroLoRA, MicroLoraConfig, TargetModule}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -pub mod trainer; pub mod merge; +pub mod trainer; /// Pre-defined task-specific adapter configurations #[derive(Debug, Clone, Serialize, Deserialize)] @@ -75,7 +75,11 @@ impl RuvLtraAdapters { rank: 8, alpha: 16.0, dropout: 0.1, - target_modules: vec![TargetModule::QProj, TargetModule::KProj, TargetModule::VProj], + target_modules: vec![ + TargetModule::QProj, + TargetModule::KProj, + TargetModule::VProj, + ], description: "Information analysis and synthesis adapter".to_string(), domain_tags: vec![ "analysis".to_string(), @@ -162,8 +166,18 @@ impl RuvLtraAdapters { let domain = domain.to_lowercase(); let mut configs = Vec::new(); - for config in [&self.coder, &self.researcher, &self.security, &self.architect, &self.reviewer] { - if config.domain_tags.iter().any(|tag| tag.to_lowercase().contains(&domain)) { + for config in [ + &self.coder, + &self.researcher, + &self.security, + &self.architect, + &self.reviewer, + ] { + if config + .domain_tags + .iter() + .any(|tag| tag.to_lowercase().contains(&domain)) + { configs.push(config); } } @@ -173,7 +187,8 @@ impl RuvLtraAdapters { /// Create MicroLoRA instance from adapter name pub fn create_lora(&self, name: &str, hidden_dim: usize) -> Result { - let config = self.get(name) + let config = self + .get(name) .ok_or_else(|| RuvLLMError::Config(format!("Unknown adapter: {}", name)))?; config.to_micro_lora_config(hidden_dim).map(MicroLoRA::new) diff --git a/crates/ruvllm/src/lora/adapters/trainer.rs b/crates/ruvllm/src/lora/adapters/trainer.rs index 1bca551af..e44cfd28e 100644 --- a/crates/ruvllm/src/lora/adapters/trainer.rs +++ b/crates/ruvllm/src/lora/adapters/trainer.rs @@ -8,9 +8,9 @@ //! - Dataset generation utilities use crate::error::{Result, RuvLLMError}; -use crate::lora::adapters::{LoraConfig, AdapterMetadata}; -use crate::lora::micro_lora::{MicroLoRA, AdaptFeedback}; -use crate::lora::training::{TrainingConfig, TrainingPipeline, LearningRateSchedule}; +use crate::lora::adapters::{AdapterMetadata, LoraConfig}; +use crate::lora::micro_lora::{AdaptFeedback, MicroLoRA}; +use crate::lora::training::{LearningRateSchedule, TrainingConfig, TrainingPipeline}; use serde::{Deserialize, Serialize}; use std::path::Path; @@ -110,14 +110,11 @@ impl AdapterDataset { /// Get dataset statistics pub fn stats(&self) -> DatasetStats { - let avg_quality = self.examples.iter() - .map(|e| e.quality) - .sum::() / self.examples.len().max(1) as f32; + let avg_quality = self.examples.iter().map(|e| e.quality).sum::() + / self.examples.len().max(1) as f32; let val_avg_quality = if !self.validation.is_empty() { - self.validation.iter() - .map(|e| e.quality) - .sum::() / self.validation.len() as f32 + self.validation.iter().map(|e| e.quality).sum::() / self.validation.len() as f32 } else { 0.0 }; @@ -254,11 +251,7 @@ impl AdapterTrainer { } /// Train an adapter on a dataset - pub fn train( - &mut self, - lora: &MicroLoRA, - dataset: &AdapterDataset, - ) -> Result { + pub fn train(&mut self, lora: &MicroLoRA, dataset: &AdapterDataset) -> Result { self.pipeline.init_for_lora(lora); let mut best_loss = f32::MAX; @@ -280,7 +273,9 @@ impl AdapterTrainer { global_step += 1; // Validation - if global_step % self.config.validation_interval == 0 && !dataset.validation.is_empty() { + if global_step % self.config.validation_interval == 0 + && !dataset.validation.is_empty() + { let val_loss = self.validate(lora, &dataset.validation)?; eprintln!(" Step {}: val_loss = {:.4}", global_step, val_loss); @@ -414,8 +409,8 @@ impl SyntheticDataGenerator { /// Generate dataset for a specific task type pub fn generate(&self, task_type: &str, num_examples: usize) -> AdapterDataset { - use rand::{Rng, SeedableRng}; use rand::rngs::StdRng; + use rand::{Rng, SeedableRng}; let mut rng = StdRng::seed_from_u64(self.seed); let mut dataset = AdapterDataset::new(format!("{}_synthetic", task_type), self.feature_dim); @@ -428,32 +423,30 @@ impl SyntheticDataGenerator { let quality = match task_type { "coder" => { // Higher quality for code-like patterns (structured) - let structure_score = input.iter() + let structure_score = input + .iter() .take(self.feature_dim / 4) .map(|x| x.abs()) - .sum::() / (self.feature_dim / 4) as f32; + .sum::() + / (self.feature_dim / 4) as f32; (0.6 + structure_score * 0.4).min(1.0) } "researcher" => { // Quality based on information density - let density = input.iter() - .map(|x| x.abs()) - .sum::() / self.feature_dim as f32; + let density = + input.iter().map(|x| x.abs()).sum::() / self.feature_dim as f32; (0.5 + density * 0.5).min(1.0) } "security" => { // High quality for security-critical patterns - let critical_score = input.iter() - .step_by(2) - .map(|x| x.abs()) - .sum::() / (self.feature_dim / 2) as f32; + let critical_score = input.iter().step_by(2).map(|x| x.abs()).sum::() + / (self.feature_dim / 2) as f32; (0.7 + critical_score * 0.3).min(1.0) } "architect" => { // Quality based on architectural coherence - let coherence = input.windows(2) - .map(|w| (w[0] - w[1]).abs()) - .sum::() / (self.feature_dim - 1) as f32; + let coherence = input.windows(2).map(|w| (w[0] - w[1]).abs()).sum::() + / (self.feature_dim - 1) as f32; (0.6 + (1.0 - coherence) * 0.4).min(1.0) } "reviewer" => { @@ -480,11 +473,26 @@ impl SyntheticDataGenerator { /// Generate datasets for all task types pub fn generate_all(&self, examples_per_task: usize) -> Vec<(String, AdapterDataset)> { vec![ - ("coder".to_string(), self.generate("coder", examples_per_task)), - ("researcher".to_string(), self.generate("researcher", examples_per_task)), - ("security".to_string(), self.generate("security", examples_per_task)), - ("architect".to_string(), self.generate("architect", examples_per_task)), - ("reviewer".to_string(), self.generate("reviewer", examples_per_task)), + ( + "coder".to_string(), + self.generate("coder", examples_per_task), + ), + ( + "researcher".to_string(), + self.generate("researcher", examples_per_task), + ), + ( + "security".to_string(), + self.generate("security", examples_per_task), + ), + ( + "architect".to_string(), + self.generate("architect", examples_per_task), + ), + ( + "reviewer".to_string(), + self.generate("reviewer", examples_per_task), + ), ] } } @@ -573,7 +581,12 @@ mod tests { for (name, dataset) in datasets { assert!(dataset.examples.len() > 0); - println!("{}: {} train, {} val", name, dataset.examples.len(), dataset.validation.len()); + println!( + "{}: {} train, {} val", + name, + dataset.examples.len(), + dataset.validation.len() + ); } } } diff --git a/crates/ruvllm/src/lora/micro_lora.rs b/crates/ruvllm/src/lora/micro_lora.rs index 86528c0e5..a8a03b55d 100644 --- a/crates/ruvllm/src/lora/micro_lora.rs +++ b/crates/ruvllm/src/lora/micro_lora.rs @@ -215,16 +215,14 @@ impl LoraAdapter { alpha: f32, seed: u64, ) -> Self { - use rand::{Rng, SeedableRng}; use rand::rngs::StdRng; + use rand::{Rng, SeedableRng}; let mut rng = StdRng::seed_from_u64(seed); let scaling = alpha / rank as f32; let std_a = (2.0 / in_features as f32).sqrt(); - let lora_a = Array2::from_shape_fn((in_features, rank), |_| { - rng.gen_range(-std_a..std_a) - }); + let lora_a = Array2::from_shape_fn((in_features, rank), |_| rng.gen_range(-std_a..std_a)); let lora_b = Array2::zeros((rank, out_features)); @@ -335,18 +333,24 @@ impl LoraAdapter { let inp1 = vld1q_f32(input.as_ptr().add(base + 4)); // Load A column 0 values (scattered in row-major) - let a0 = vld1q_f32([ - self.lora_a[[base, 0]], - self.lora_a[[base + 1, 0]], - self.lora_a[[base + 2, 0]], - self.lora_a[[base + 3, 0]], - ].as_ptr()); - let a1 = vld1q_f32([ - self.lora_a[[base + 4, 0]], - self.lora_a[[base + 5, 0]], - self.lora_a[[base + 6, 0]], - self.lora_a[[base + 7, 0]], - ].as_ptr()); + let a0 = vld1q_f32( + [ + self.lora_a[[base, 0]], + self.lora_a[[base + 1, 0]], + self.lora_a[[base + 2, 0]], + self.lora_a[[base + 3, 0]], + ] + .as_ptr(), + ); + let a1 = vld1q_f32( + [ + self.lora_a[[base + 4, 0]], + self.lora_a[[base + 5, 0]], + self.lora_a[[base + 6, 0]], + self.lora_a[[base + 7, 0]], + ] + .as_ptr(), + ); inter_sum0 = vfmaq_f32(inter_sum0, inp0, a0); inter_sum1 = vfmaq_f32(inter_sum1, inp1, a1); @@ -375,18 +379,24 @@ impl LoraAdapter { let out1 = vld1q_f32(output.as_ptr().add(base + 4)); // Load B row 0 (contiguous for row-major) - let b0 = vld1q_f32([ - self.lora_b[[0, base]], - self.lora_b[[0, base + 1]], - self.lora_b[[0, base + 2]], - self.lora_b[[0, base + 3]], - ].as_ptr()); - let b1 = vld1q_f32([ - self.lora_b[[0, base + 4]], - self.lora_b[[0, base + 5]], - self.lora_b[[0, base + 6]], - self.lora_b[[0, base + 7]], - ].as_ptr()); + let b0 = vld1q_f32( + [ + self.lora_b[[0, base]], + self.lora_b[[0, base + 1]], + self.lora_b[[0, base + 2]], + self.lora_b[[0, base + 3]], + ] + .as_ptr(), + ); + let b1 = vld1q_f32( + [ + self.lora_b[[0, base + 4]], + self.lora_b[[0, base + 5]], + self.lora_b[[0, base + 6]], + self.lora_b[[0, base + 7]], + ] + .as_ptr(), + ); // FMA and store let res0 = vfmaq_f32(out0, scaled_vec, b0); @@ -419,30 +429,42 @@ impl LoraAdapter { let inp1 = vld1q_f32(input.as_ptr().add(base + 4)); // Load A columns (scattered access for row-major) - let a0_col0 = vld1q_f32([ - self.lora_a[[base, 0]], - self.lora_a[[base + 1, 0]], - self.lora_a[[base + 2, 0]], - self.lora_a[[base + 3, 0]], - ].as_ptr()); - let a1_col0 = vld1q_f32([ - self.lora_a[[base + 4, 0]], - self.lora_a[[base + 5, 0]], - self.lora_a[[base + 6, 0]], - self.lora_a[[base + 7, 0]], - ].as_ptr()); - let a0_col1 = vld1q_f32([ - self.lora_a[[base, 1]], - self.lora_a[[base + 1, 1]], - self.lora_a[[base + 2, 1]], - self.lora_a[[base + 3, 1]], - ].as_ptr()); - let a1_col1 = vld1q_f32([ - self.lora_a[[base + 4, 1]], - self.lora_a[[base + 5, 1]], - self.lora_a[[base + 6, 1]], - self.lora_a[[base + 7, 1]], - ].as_ptr()); + let a0_col0 = vld1q_f32( + [ + self.lora_a[[base, 0]], + self.lora_a[[base + 1, 0]], + self.lora_a[[base + 2, 0]], + self.lora_a[[base + 3, 0]], + ] + .as_ptr(), + ); + let a1_col0 = vld1q_f32( + [ + self.lora_a[[base + 4, 0]], + self.lora_a[[base + 5, 0]], + self.lora_a[[base + 6, 0]], + self.lora_a[[base + 7, 0]], + ] + .as_ptr(), + ); + let a0_col1 = vld1q_f32( + [ + self.lora_a[[base, 1]], + self.lora_a[[base + 1, 1]], + self.lora_a[[base + 2, 1]], + self.lora_a[[base + 3, 1]], + ] + .as_ptr(), + ); + let a1_col1 = vld1q_f32( + [ + self.lora_a[[base + 4, 1]], + self.lora_a[[base + 5, 1]], + self.lora_a[[base + 6, 1]], + self.lora_a[[base + 7, 1]], + ] + .as_ptr(), + ); // Dual accumulator FMA chains sum0_0 = vfmaq_f32(sum0_0, inp0, a0_col0); @@ -479,30 +501,42 @@ impl LoraAdapter { let out1 = vld1q_f32(output.as_ptr().add(base + 4)); // Load B rows (scattered for row-major) - let b0_row0 = vld1q_f32([ - self.lora_b[[0, base]], - self.lora_b[[0, base + 1]], - self.lora_b[[0, base + 2]], - self.lora_b[[0, base + 3]], - ].as_ptr()); - let b1_row0 = vld1q_f32([ - self.lora_b[[0, base + 4]], - self.lora_b[[0, base + 5]], - self.lora_b[[0, base + 6]], - self.lora_b[[0, base + 7]], - ].as_ptr()); - let b0_row1 = vld1q_f32([ - self.lora_b[[1, base]], - self.lora_b[[1, base + 1]], - self.lora_b[[1, base + 2]], - self.lora_b[[1, base + 3]], - ].as_ptr()); - let b1_row1 = vld1q_f32([ - self.lora_b[[1, base + 4]], - self.lora_b[[1, base + 5]], - self.lora_b[[1, base + 6]], - self.lora_b[[1, base + 7]], - ].as_ptr()); + let b0_row0 = vld1q_f32( + [ + self.lora_b[[0, base]], + self.lora_b[[0, base + 1]], + self.lora_b[[0, base + 2]], + self.lora_b[[0, base + 3]], + ] + .as_ptr(), + ); + let b1_row0 = vld1q_f32( + [ + self.lora_b[[0, base + 4]], + self.lora_b[[0, base + 5]], + self.lora_b[[0, base + 6]], + self.lora_b[[0, base + 7]], + ] + .as_ptr(), + ); + let b0_row1 = vld1q_f32( + [ + self.lora_b[[1, base]], + self.lora_b[[1, base + 1]], + self.lora_b[[1, base + 2]], + self.lora_b[[1, base + 3]], + ] + .as_ptr(), + ); + let b1_row1 = vld1q_f32( + [ + self.lora_b[[1, base + 4]], + self.lora_b[[1, base + 5]], + self.lora_b[[1, base + 6]], + self.lora_b[[1, base + 7]], + ] + .as_ptr(), + ); // Fused FMA: out + scaled0*B[0,:] + scaled1*B[1,:] let tmp0 = vfmaq_f32(out0, scaled0_vec, b0_row0); @@ -627,7 +661,8 @@ impl LoraAdapter { for i in 0..self.lora_a.nrows() { for r in 0..self.rank { let grad = self.grad_a[[i, r]] * scale; - let ewc_penalty = ewc_lambda * fisher_a[[i, r]] * (self.lora_a[[i, r]] - optimal_a[[i, r]]); + let ewc_penalty = + ewc_lambda * fisher_a[[i, r]] * (self.lora_a[[i, r]] - optimal_a[[i, r]]); self.lora_a[[i, r]] -= grad + ewc_penalty * learning_rate; } } @@ -636,7 +671,8 @@ impl LoraAdapter { for r in 0..self.rank { for o in 0..self.lora_b.ncols() { let grad = self.grad_b[[r, o]] * scale; - let ewc_penalty = ewc_lambda * fisher_b[[r, o]] * (self.lora_b[[r, o]] - optimal_b[[r, o]]); + let ewc_penalty = + ewc_lambda * fisher_b[[r, o]] * (self.lora_b[[r, o]] - optimal_b[[r, o]]); self.lora_b[[r, o]] -= grad + ewc_penalty * learning_rate; } } @@ -788,12 +824,7 @@ impl MicroLoRA { .copied() .unwrap_or((config.in_features, config.out_features)); - let adapter = LoraAdapter::new( - in_features, - out_features, - config.rank, - config.alpha, - ); + let adapter = LoraAdapter::new(in_features, out_features, config.rank, config.alpha); adapters.insert(*module, Arc::new(RwLock::new(adapter))); } @@ -930,18 +961,22 @@ impl MicroLoRA { /// Export state for serialization pub fn export_state(&self) -> MicroLoraState { - let adapters = self.adapters.iter().map(|(module, adapter)| { - let adapter = adapter.read(); - let state = LoraAdapterState { - lora_a: adapter.lora_a.iter().copied().collect(), - lora_b: adapter.lora_b.iter().copied().collect(), - in_features: adapter.lora_a.nrows(), - out_features: adapter.lora_b.ncols(), - rank: adapter.rank, - scaling: adapter.scaling, - }; - (*module, state) - }).collect(); + let adapters = self + .adapters + .iter() + .map(|(module, adapter)| { + let adapter = adapter.read(); + let state = LoraAdapterState { + lora_a: adapter.lora_a.iter().copied().collect(), + lora_b: adapter.lora_b.iter().copied().collect(), + in_features: adapter.lora_a.nrows(), + out_features: adapter.lora_b.ncols(), + rank: adapter.rank, + scaling: adapter.scaling, + }; + (*module, state) + }) + .collect(); MicroLoraState { config: self.config.clone(), @@ -958,12 +993,14 @@ impl MicroLoRA { let lora_a = Array2::from_shape_vec( (adapter_state.in_features, adapter_state.rank), adapter_state.lora_a, - ).map_err(|e| RuvLLMError::Config(e.to_string()))?; + ) + .map_err(|e| RuvLLMError::Config(e.to_string()))?; let lora_b = Array2::from_shape_vec( (adapter_state.rank, adapter_state.out_features), adapter_state.lora_b, - ).map_err(|e| RuvLLMError::Config(e.to_string()))?; + ) + .map_err(|e| RuvLLMError::Config(e.to_string()))?; let adapter = LoraAdapter { lora_a: lora_a.clone(), @@ -1005,14 +1042,13 @@ impl MicroLoRA { /// Get total parameter count pub fn param_count(&self) -> usize { - self.adapters.values() - .map(|a| a.read().param_count()) - .sum() + self.adapters.values().map(|a| a.read().param_count()).sum() } /// Get total memory usage in bytes pub fn memory_bytes(&self) -> usize { - self.adapters.values() + self.adapters + .values() .map(|a| a.read().memory_bytes()) .sum() } diff --git a/crates/ruvllm/src/lora/mod.rs b/crates/ruvllm/src/lora/mod.rs index f5d93fa27..822edffea 100644 --- a/crates/ruvllm/src/lora/mod.rs +++ b/crates/ruvllm/src/lora/mod.rs @@ -109,13 +109,14 @@ pub use adapter::{ AdapterComposer, AdapterHandle, AdapterPool, AdapterRegistry, CompositionStrategy, }; pub use adapters::{ - LoraConfig, RuvLtraAdapters, AdapterMetadata, - trainer::{AdapterTrainer, AdapterTrainingConfig, AdapterDataset, SyntheticDataGenerator, TrainingExample}, - merge::{AdapterMerger, MergeConfig, MergeStrategy, HotSwapManager}, -}; -pub use micro_lora::{ - AdaptFeedback, LoraAdapter, MicroLoRA, MicroLoraConfig, TargetModule, + merge::{AdapterMerger, HotSwapManager, MergeConfig, MergeStrategy}, + trainer::{ + AdapterDataset, AdapterTrainer, AdapterTrainingConfig, SyntheticDataGenerator, + TrainingExample, + }, + AdapterMetadata, LoraConfig, RuvLtraAdapters, }; +pub use micro_lora::{AdaptFeedback, LoraAdapter, MicroLoRA, MicroLoraConfig, TargetModule}; pub use training::{ EwcRegularizer, GradientAccumulator, LearningRateSchedule, TrainingConfig, TrainingPipeline, }; diff --git a/crates/ruvllm/src/lora/training.rs b/crates/ruvllm/src/lora/training.rs index 91d6d00c4..be3451532 100644 --- a/crates/ruvllm/src/lora/training.rs +++ b/crates/ruvllm/src/lora/training.rs @@ -153,11 +153,20 @@ impl GradientAccumulator { } /// Initialize for a module with dimensions - pub fn init_module(&mut self, module: TargetModule, in_features: usize, rank: usize, out_features: usize) { - self.gradients.insert(module, ModuleGradients { - grad_a: Array2::zeros((in_features, rank)), - grad_b: Array2::zeros((rank, out_features)), - }); + pub fn init_module( + &mut self, + module: TargetModule, + in_features: usize, + rank: usize, + out_features: usize, + ) { + self.gradients.insert( + module, + ModuleGradients { + grad_a: Array2::zeros((in_features, rank)), + grad_b: Array2::zeros((rank, out_features)), + }, + ); } /// Accumulate gradients @@ -183,11 +192,14 @@ impl GradientAccumulator { } let scale = 1.0 / self.sample_count as f32; - self.gradients.iter().map(|(module, grads)| { - let avg_a = grads.grad_a.mapv(|v| v * scale); - let avg_b = grads.grad_b.mapv(|v| v * scale); - (*module, (avg_a, avg_b)) - }).collect() + self.gradients + .iter() + .map(|(module, grads)| { + let avg_a = grads.grad_a.mapv(|v| v * scale); + let avg_b = grads.grad_b.mapv(|v| v * scale); + (*module, (avg_a, avg_b)) + }) + .collect() } /// Clear accumulated gradients @@ -251,7 +263,11 @@ impl EwcRegularizer { } /// Initialize state for a module from adapter - pub fn init_module(&mut self, module: TargetModule, adapter: &crate::lora::micro_lora::LoraAdapter) { + pub fn init_module( + &mut self, + module: TargetModule, + adapter: &crate::lora::micro_lora::LoraAdapter, + ) { self.states.insert(module, EwcState::from_adapter(adapter)); } @@ -279,7 +295,9 @@ impl EwcRegularizer { let mut penalty = 0.0f32; // Penalty for A: sum(F_a * (w_a - w*_a)^2) - for ((f, w), w_opt) in state.fisher_a.iter() + for ((f, w), w_opt) in state + .fisher_a + .iter() .zip(current_a.iter()) .zip(state.optimal_a.iter()) { @@ -288,7 +306,9 @@ impl EwcRegularizer { } // Penalty for B: sum(F_b * (w_b - w*_b)^2) - for ((f, w), w_opt) in state.fisher_b.iter() + for ((f, w), w_opt) in state + .fisher_b + .iter() .zip(current_b.iter()) .zip(state.optimal_b.iter()) { @@ -324,7 +344,10 @@ impl EwcRegularizer { } /// Start a new task (consolidate current knowledge) - pub fn start_new_task(&mut self, adapters: &HashMap>>) { + pub fn start_new_task( + &mut self, + adapters: &HashMap>>, + ) { // Update optimal weights to current for (module, adapter) in adapters { if let Some(state) = self.states.get_mut(module) { @@ -363,16 +386,22 @@ impl EwcRegularizer { /// Export states for serialization pub fn export_states(&self) -> HashMap { - self.states.iter().map(|(module, state)| { - (*module, EwcStateExport { - fisher_a: state.fisher_a.iter().copied().collect(), - fisher_b: state.fisher_b.iter().copied().collect(), - optimal_a: state.optimal_a.iter().copied().collect(), - optimal_b: state.optimal_b.iter().copied().collect(), - shape_a: (state.fisher_a.nrows(), state.fisher_a.ncols()), - shape_b: (state.fisher_b.nrows(), state.fisher_b.ncols()), + self.states + .iter() + .map(|(module, state)| { + ( + *module, + EwcStateExport { + fisher_a: state.fisher_a.iter().copied().collect(), + fisher_b: state.fisher_b.iter().copied().collect(), + optimal_a: state.optimal_a.iter().copied().collect(), + optimal_b: state.optimal_b.iter().copied().collect(), + shape_a: (state.fisher_a.nrows(), state.fisher_a.ncols()), + shape_b: (state.fisher_b.nrows(), state.fisher_b.ncols()), + }, + ) }) - }).collect() + .collect() } } @@ -499,7 +528,10 @@ impl TrainingPipeline { let lr = self.compute_lr(step); // Apply gradients with EWC - let ewc_states: HashMap = self.ewc.states.iter() + let ewc_states: HashMap = self + .ewc + .states + .iter() .map(|(k, v)| (*k, v.clone())) .collect(); @@ -542,7 +574,8 @@ impl TrainingPipeline { LearningRateSchedule::Cosine => { let decay_steps = 10000.0; - let factor = 0.5 * (1.0 + (std::f32::consts::PI * adjusted_step / decay_steps).cos()); + let factor = + 0.5 * (1.0 + (std::f32::consts::PI * adjusted_step / decay_steps).cos()); min_lr + (base_lr - min_lr) * factor } @@ -618,7 +651,10 @@ impl TrainingPipeline { /// Start a new task (for EWC) pub fn start_new_task(&mut self, lora: &MicroLoRA) { - let adapters: HashMap<_, _> = lora.config().target_modules.iter() + let adapters: HashMap<_, _> = lora + .config() + .target_modules + .iter() .filter_map(|m| lora.get_adapter(m).map(|a| (*m, a))) .collect(); self.ewc.start_new_task(&adapters); diff --git a/crates/ruvllm/src/memory_pool.rs b/crates/ruvllm/src/memory_pool.rs index 3954caaa5..17b769dac 100644 --- a/crates/ruvllm/src/memory_pool.rs +++ b/crates/ruvllm/src/memory_pool.rs @@ -40,12 +40,12 @@ use crate::error::{Result, RuvLLMError}; use parking_lot::{Mutex, RwLock}; use std::alloc::{alloc_zeroed, dealloc, Layout}; use std::cell::UnsafeCell; +#[cfg(not(target_arch = "wasm32"))] +use std::collections::HashMap; use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering}; use std::sync::Arc; #[cfg(not(target_arch = "wasm32"))] use std::thread::ThreadId; -#[cfg(not(target_arch = "wasm32"))] -use std::collections::HashMap; /// Cache line size for M4 Pro and most modern CPUs (64 bytes) pub const CACHE_LINE_SIZE: usize = 64; @@ -124,11 +124,13 @@ impl InferenceArena { // Round up to cache line size let aligned_capacity = (capacity + DEFAULT_ALIGNMENT - 1) & !(DEFAULT_ALIGNMENT - 1); - let layout = Layout::from_size_align(aligned_capacity, DEFAULT_ALIGNMENT) - .map_err(|_| RuvLLMError::OutOfMemory(format!( - "Invalid arena layout: size={}, align={}", - aligned_capacity, DEFAULT_ALIGNMENT - )))?; + let layout = + Layout::from_size_align(aligned_capacity, DEFAULT_ALIGNMENT).map_err(|_| { + RuvLLMError::OutOfMemory(format!( + "Invalid arena layout: size={}, align={}", + aligned_capacity, DEFAULT_ALIGNMENT + )) + })?; // SAFETY: Layout is valid and we track the allocation let memory = unsafe { alloc_zeroed(layout) }; @@ -214,16 +216,16 @@ impl InferenceArena { } // Try to bump the offset atomically - match self.offset.compare_exchange( - current, - new_offset, - Ordering::AcqRel, - Ordering::Acquire, - ) { + match self + .offset + .compare_exchange(current, new_offset, Ordering::AcqRel, Ordering::Acquire) + { Ok(_) => { // Update statistics self.allocation_count.fetch_add(1, Ordering::Relaxed); - let _ = self.high_water_mark.fetch_max(new_offset, Ordering::Relaxed); + let _ = self + .high_water_mark + .fetch_max(new_offset, Ordering::Relaxed); // SAFETY: We've reserved this memory region atomically unsafe { @@ -260,15 +262,15 @@ impl InferenceArena { return None; } - match self.offset.compare_exchange( - current, - new_offset, - Ordering::AcqRel, - Ordering::Acquire, - ) { + match self + .offset + .compare_exchange(current, new_offset, Ordering::AcqRel, Ordering::Acquire) + { Ok(_) => { self.allocation_count.fetch_add(1, Ordering::Relaxed); - let _ = self.high_water_mark.fetch_max(new_offset, Ordering::Relaxed); + let _ = self + .high_water_mark + .fetch_max(new_offset, Ordering::Relaxed); let ptr = self.memory.add(aligned_offset) as *mut T; Some(std::slice::from_raw_parts_mut(ptr, count)) @@ -449,13 +451,7 @@ impl BufferSize { /// Get all buffer sizes in order. pub const fn all() -> [BufferSize; 5] { - [ - Self::KB1, - Self::KB4, - Self::KB16, - Self::KB64, - Self::KB256, - ] + [Self::KB1, Self::KB4, Self::KB16, Self::KB64, Self::KB256] } } @@ -490,13 +486,13 @@ impl PooledBuffer { #[inline] pub fn as_slice(&self) -> &[T] { let size = std::mem::size_of::(); - assert!(self.data.len() % size == 0, "Buffer size not aligned to type"); + assert!( + self.data.len() % size == 0, + "Buffer size not aligned to type" + ); // SAFETY: Buffer is aligned and size is checked unsafe { - std::slice::from_raw_parts( - self.data.as_ptr() as *const T, - self.data.len() / size, - ) + std::slice::from_raw_parts(self.data.as_ptr() as *const T, self.data.len() / size) } } @@ -504,13 +500,13 @@ impl PooledBuffer { #[inline] pub fn as_slice_mut(&mut self) -> &mut [T] { let size = std::mem::size_of::(); - assert!(self.data.len() % size == 0, "Buffer size not aligned to type"); + assert!( + self.data.len() % size == 0, + "Buffer size not aligned to type" + ); // SAFETY: Buffer is aligned and size is checked unsafe { - std::slice::from_raw_parts_mut( - self.data.as_mut_ptr() as *mut T, - self.data.len() / size, - ) + std::slice::from_raw_parts_mut(self.data.as_mut_ptr() as *mut T, self.data.len() / size) } } @@ -650,11 +646,12 @@ impl BufferPoolInner { fn allocate_buffer(size_class: BufferSize) -> Result> { let size = size_class.bytes(); - let layout = Layout::from_size_align(size, DEFAULT_ALIGNMENT) - .map_err(|_| RuvLLMError::OutOfMemory(format!( + let layout = Layout::from_size_align(size, DEFAULT_ALIGNMENT).map_err(|_| { + RuvLLMError::OutOfMemory(format!( "Invalid buffer layout: size={}, align={}", size, DEFAULT_ALIGNMENT - )))?; + )) + })?; // SAFETY: Layout is valid unsafe { @@ -871,11 +868,12 @@ struct ThreadScratch { #[cfg(not(target_arch = "wasm32"))] impl ThreadScratch { fn new(size: usize) -> Result { - let layout = Layout::from_size_align(size, DEFAULT_ALIGNMENT) - .map_err(|_| RuvLLMError::OutOfMemory(format!( + let layout = Layout::from_size_align(size, DEFAULT_ALIGNMENT).map_err(|_| { + RuvLLMError::OutOfMemory(format!( "Invalid scratch layout: size={}, align={}", size, DEFAULT_ALIGNMENT - )))?; + )) + })?; // SAFETY: Layout is valid let data = unsafe { @@ -1083,11 +1081,12 @@ struct WasmScratch { #[cfg(target_arch = "wasm32")] impl WasmScratch { fn new(size: usize) -> Result { - let layout = Layout::from_size_align(size, DEFAULT_ALIGNMENT) - .map_err(|_| RuvLLMError::OutOfMemory(format!( + let layout = Layout::from_size_align(size, DEFAULT_ALIGNMENT).map_err(|_| { + RuvLLMError::OutOfMemory(format!( "Invalid scratch layout: size={}, align={}", size, DEFAULT_ALIGNMENT - )))?; + )) + })?; // SAFETY: Layout is valid let data = unsafe { @@ -1666,7 +1665,10 @@ mod tests { assert_eq!(arena_buf.len(), 100); // Use pool - let pool_buf = manager.pool.acquire(BufferSize::KB4).expect("acquire failed"); + let pool_buf = manager + .pool + .acquire(BufferSize::KB4) + .expect("acquire failed"); assert_eq!(pool_buf.capacity(), 4096); // Use scratch diff --git a/crates/ruvllm/src/metal/buffers.rs b/crates/ruvllm/src/metal/buffers.rs index 43bf6b9bb..61b10305c 100644 --- a/crates/ruvllm/src/metal/buffers.rs +++ b/crates/ruvllm/src/metal/buffers.rs @@ -147,10 +147,9 @@ impl MetalBufferPool { } // Allocate new buffer - let buffer = self.device.new_buffer( - size_class as u64, - MTLResourceOptions::StorageModeShared, - ); + let buffer = self + .device + .new_buffer(size_class as u64, MTLResourceOptions::StorageModeShared); MetalBuffer { buffer, diff --git a/crates/ruvllm/src/metal/context.rs b/crates/ruvllm/src/metal/context.rs index 94037404e..91ad05c67 100644 --- a/crates/ruvllm/src/metal/context.rs +++ b/crates/ruvllm/src/metal/context.rs @@ -9,10 +9,9 @@ use metal::{ use std::sync::Arc; use super::{ - AttentionParams, GemmParams, MetalPipelines, NormParams, RopeParams, - FusedAttentionParams, FusedNormParams, Int4GemvParams, RopeAttentionParams, - YarnAttentionParams, PagedAttentionParams, SwiGLUParams, - shader_source, tile_sizes, + shader_source, tile_sizes, AttentionParams, FusedAttentionParams, FusedNormParams, GemmParams, + Int4GemvParams, MetalPipelines, NormParams, PagedAttentionParams, RopeAttentionParams, + RopeParams, SwiGLUParams, YarnAttentionParams, }; use crate::error::{Result, RuvLLMError}; use crate::kernels::AttentionConfig; @@ -187,7 +186,12 @@ impl MetalContext { if a.len() != m * k || b.len() != k * n { return Err(RuvLLMError::InvalidOperation(format!( "GEMM dimension mismatch: A[{}] != {}x{}, B[{}] != {}x{}", - a.len(), m, k, b.len(), k, n + a.len(), + m, + k, + b.len(), + k, + n ))); } @@ -234,18 +238,16 @@ impl MetalContext { /// GEMM operation with FP32 /// /// Computes C = A @ B using FP32 precision. - pub fn gemm_f32( - &self, - a: &[f32], - b: &[f32], - m: usize, - n: usize, - k: usize, - ) -> Result> { + pub fn gemm_f32(&self, a: &[f32], b: &[f32], m: usize, n: usize, k: usize) -> Result> { if a.len() != m * k || b.len() != k * n { return Err(RuvLLMError::InvalidOperation(format!( "GEMM dimension mismatch: A[{}] != {}x{}, B[{}] != {}x{}", - a.len(), m, k, b.len(), k, n + a.len(), + m, + k, + b.len(), + k, + n ))); } @@ -273,11 +275,7 @@ impl MetalContext { let tiles_n = (n + tile_sizes::GEMM_TILE_N - 1) / tile_sizes::GEMM_TILE_N; let threadgroup_size = MTLSize::new(16, 16, 1); - let grid_size = MTLSize::new( - (tiles_m * 16) as u64, - (tiles_n * 16) as u64, - 1, - ); + let grid_size = MTLSize::new((tiles_m * 16) as u64, (tiles_n * 16) as u64, 1); encoder.dispatch_threads(grid_size, threadgroup_size); encoder.end_encoding(); @@ -401,11 +399,7 @@ impl MetalContext { // One thread per head dimension element let threadgroup_size = MTLSize::new(head_dim as u64, 1, 1); - let grid_size = MTLSize::new( - head_dim as u64, - num_heads as u64, - batch_size as u64, - ); + let grid_size = MTLSize::new(head_dim as u64, num_heads as u64, batch_size as u64); encoder.dispatch_threads(grid_size, threadgroup_size); encoder.end_encoding(); @@ -453,7 +447,12 @@ impl MetalContext { if a.len() != m * k || b.len() != k * n { return Err(RuvLLMError::InvalidOperation(format!( "GEMM dimension mismatch: A[{}] != {}x{}, B[{}] != {}x{}", - a.len(), m, k, b.len(), k, n + a.len(), + m, + k, + b.len(), + k, + n ))); } @@ -532,9 +531,8 @@ impl MetalContext { return Ok(vec![0.0; query.len()]); } - let params = FusedAttentionParams::new( - num_heads, num_kv_heads, head_dim, seq_len, kv_len, causal - ); + let params = + FusedAttentionParams::new(num_heads, num_kv_heads, head_dim, seq_len, kv_len, causal); let output_size = seq_len * num_heads * head_dim; // Create Metal buffers @@ -556,7 +554,8 @@ impl MetalContext { encoder.set_buffer(4, Some(¶ms_buffer), 0); // Flash Attention 2 grid: one threadgroup per head per query block - let q_blocks = (seq_len + tile_sizes::FLASH_ATTENTION_BLOCK - 1) / tile_sizes::FLASH_ATTENTION_BLOCK; + let q_blocks = + (seq_len + tile_sizes::FLASH_ATTENTION_BLOCK - 1) / tile_sizes::FLASH_ATTENTION_BLOCK; let threadgroup_size = MTLSize::new(tile_sizes::FLASH_ATTENTION_BLOCK as u64, 1, 1); let grid_size = MTLSize::new( tile_sizes::FLASH_ATTENTION_BLOCK as u64, @@ -584,17 +583,22 @@ impl MetalContext { bias: &[f32], eps: f32, ) -> Result<()> { - let pipeline = self.pipelines.fused_layernorm_residual.as_ref() - .ok_or_else(|| RuvLLMError::Backend( - "Fused LayerNorm+Residual not available on this device".to_string() - ))?; + let pipeline = self + .pipelines + .fused_layernorm_residual + .as_ref() + .ok_or_else(|| { + RuvLLMError::Backend( + "Fused LayerNorm+Residual not available on this device".to_string(), + ) + })?; let hidden_size = weight.len(); let batch_size = x.len() / hidden_size; if x.len() != batch_size * hidden_size || residual.len() != x.len() { return Err(RuvLLMError::InvalidOperation( - "Fused LayerNorm dimension mismatch".to_string() + "Fused LayerNorm dimension mismatch".to_string(), )); } @@ -645,17 +649,22 @@ impl MetalContext { weight: &[f32], eps: f32, ) -> Result<()> { - let pipeline = self.pipelines.fused_rmsnorm_residual.as_ref() - .ok_or_else(|| RuvLLMError::Backend( - "Fused RMSNorm+Residual not available on this device".to_string() - ))?; + let pipeline = self + .pipelines + .fused_rmsnorm_residual + .as_ref() + .ok_or_else(|| { + RuvLLMError::Backend( + "Fused RMSNorm+Residual not available on this device".to_string(), + ) + })?; let hidden_size = weight.len(); let batch_size = x.len() / hidden_size; if x.len() != batch_size * hidden_size || residual.len() != x.len() { return Err(RuvLLMError::InvalidOperation( - "Fused RMSNorm dimension mismatch".to_string() + "Fused RMSNorm dimension mismatch".to_string(), )); } @@ -697,19 +706,14 @@ impl MetalContext { /// Fused SwiGLU activation /// /// Computes: output = Swish(gate) * up in a single kernel - pub fn fused_swiglu( - &self, - gate: &[f32], - up: &[f32], - ) -> Result> { - let pipeline = self.pipelines.fused_swiglu.as_ref() - .ok_or_else(|| RuvLLMError::Backend( - "Fused SwiGLU not available on this device".to_string() - ))?; + pub fn fused_swiglu(&self, gate: &[f32], up: &[f32]) -> Result> { + let pipeline = self.pipelines.fused_swiglu.as_ref().ok_or_else(|| { + RuvLLMError::Backend("Fused SwiGLU not available on this device".to_string()) + })?; if gate.len() != up.len() { return Err(RuvLLMError::InvalidOperation( - "SwiGLU dimension mismatch".to_string() + "SwiGLU dimension mismatch".to_string(), )); } @@ -752,26 +756,30 @@ impl MetalContext { /// 4x memory reduction compared to FP16. pub fn int4_gemv( &self, - weights_int4: &[u8], // Packed INT4 weights (2 values per byte) - scales: &[f32], // Per-group scale factors - zeros: &[f32], // Per-group zero points - input: &[f32], // Input vector - m: usize, // Output dimension - n: usize, // Input dimension - group_size: usize, // Quantization group size + weights_int4: &[u8], // Packed INT4 weights (2 values per byte) + scales: &[f32], // Per-group scale factors + zeros: &[f32], // Per-group zero points + input: &[f32], // Input vector + m: usize, // Output dimension + n: usize, // Input dimension + group_size: usize, // Quantization group size ) -> Result> { // Prefer SIMD-optimized version if available - let pipeline = self.pipelines.int4_gemv_simd.as_ref() + let pipeline = self + .pipelines + .int4_gemv_simd + .as_ref() .or(self.pipelines.int4_gemv.as_ref()) - .ok_or_else(|| RuvLLMError::Backend( - "INT4 GEMV not available on this device".to_string() - ))?; + .ok_or_else(|| { + RuvLLMError::Backend("INT4 GEMV not available on this device".to_string()) + })?; let expected_weights = (m * n + 1) / 2; // 2 values per byte if weights_int4.len() != expected_weights { return Err(RuvLLMError::InvalidOperation(format!( "INT4 weight size mismatch: expected {} bytes, got {}", - expected_weights, weights_int4.len() + expected_weights, + weights_int4.len() ))); } @@ -837,7 +845,15 @@ impl MetalContext { let mut k = key.to_vec(); self.apply_rope(&mut q, position_offset, num_heads, head_dim, rope_theta)?; self.apply_rope(&mut k, position_offset, num_kv_heads, head_dim, rope_theta)?; - return self.fused_attention(&q, &k, value, num_heads, num_kv_heads, head_dim, causal); + return self.fused_attention( + &q, + &k, + value, + num_heads, + num_kv_heads, + head_dim, + causal, + ); } }; @@ -849,8 +865,14 @@ impl MetalContext { } let params = RopeAttentionParams::new( - num_heads, num_kv_heads, head_dim, seq_len, kv_len, - position_offset, rope_theta, causal + num_heads, + num_kv_heads, + head_dim, + seq_len, + kv_len, + position_offset, + rope_theta, + causal, ); let output_size = seq_len * num_heads * head_dim; @@ -902,10 +924,9 @@ impl MetalContext { target_max_position: usize, causal: bool, ) -> Result> { - let pipeline = self.pipelines.yarn_attention.as_ref() - .ok_or_else(|| RuvLLMError::Backend( - "YaRN attention not available on this device".to_string() - ))?; + let pipeline = self.pipelines.yarn_attention.as_ref().ok_or_else(|| { + RuvLLMError::Backend("YaRN attention not available on this device".to_string()) + })?; let seq_len = query.len() / (num_heads * head_dim); let kv_len = key.len() / (num_kv_heads * head_dim); @@ -915,8 +936,16 @@ impl MetalContext { } let params = YarnAttentionParams::new( - num_heads, num_kv_heads, head_dim, seq_len, kv_len, - position_offset, rope_theta, original_max_position, target_max_position, causal + num_heads, + num_kv_heads, + head_dim, + seq_len, + kv_len, + position_offset, + rope_theta, + original_max_position, + target_max_position, + causal, ); let output_size = seq_len * num_heads * head_dim; @@ -952,10 +981,9 @@ impl MetalContext { /// Create a Metal buffer with specified size fn create_buffer(&self, size: usize) -> Result { - Ok(self.device.new_buffer( - size as u64, - MTLResourceOptions::StorageModeShared, - )) + Ok(self + .device + .new_buffer(size as u64, MTLResourceOptions::StorageModeShared)) } /// Create a Metal buffer with data @@ -1003,7 +1031,11 @@ mod tests { let config = MetalConfig::default(); let ctx = MetalContext::new(config); - assert!(ctx.is_ok(), "Failed to create Metal context: {:?}", ctx.err()); + assert!( + ctx.is_ok(), + "Failed to create Metal context: {:?}", + ctx.err() + ); } #[test] diff --git a/crates/ruvllm/src/metal/mod.rs b/crates/ruvllm/src/metal/mod.rs index f751494cf..ef93b2561 100644 --- a/crates/ruvllm/src/metal/mod.rs +++ b/crates/ruvllm/src/metal/mod.rs @@ -32,31 +32,38 @@ //! let c = ctx.gemm_f16(&a, &b, m, n, k)?; //! ``` -#[cfg(target_os = "macos")] -mod context; -#[cfg(target_os = "macos")] -mod pipelines; #[cfg(target_os = "macos")] mod buffers; #[cfg(target_os = "macos")] +mod context; +#[cfg(target_os = "macos")] mod operations; +#[cfg(target_os = "macos")] +mod pipelines; -#[cfg(target_os = "macos")] -pub use context::{MetalContext, MetalConfig}; -#[cfg(target_os = "macos")] -pub use pipelines::{MetalPipelines, PipelineCache}; #[cfg(target_os = "macos")] pub use buffers::{MetalBuffer, MetalBufferPool}; #[cfg(target_os = "macos")] +pub use context::{MetalConfig, MetalContext}; +#[cfg(target_os = "macos")] pub use operations::{ - // FP16/Quantization utilities - fp32_to_fp16, fp16_to_fp32, quantize_int8, dequantize_int8, - verify_speculative_tokens, - // GEMV Metal GPU functions - GemvParams, gemv_metal, gemv_metal_with_params, gemv_metal_f16, gemv_batched_metal, // GEMM Metal GPU functions batched_gemm_metal, + dequantize_int8, + fp16_to_fp32, + // FP16/Quantization utilities + fp32_to_fp16, + gemv_batched_metal, + gemv_metal, + gemv_metal_f16, + gemv_metal_with_params, + quantize_int8, + verify_speculative_tokens, + // GEMV Metal GPU functions + GemvParams, }; +#[cfg(target_os = "macos")] +pub use pipelines::{MetalPipelines, PipelineCache}; use crate::error::{Result, RuvLLMError}; use crate::kernels::AttentionConfig; @@ -128,7 +135,7 @@ impl GemmParams { m: m as u32, n: n as u32, k: k as u32, - lda: k as u32, // Row-major + lda: k as u32, // Row-major ldb: n as u32, ldc: n as u32, alpha: 1.0, @@ -630,10 +637,7 @@ pub mod shader_source { pub fn all_optimized_shaders() -> String { format!( "{}\n{}\n{}\n{}", - ATTENTION_FUSED, - FUSED_OPS, - QUANTIZED, - ROPE_ATTENTION + ATTENTION_FUSED, FUSED_OPS, QUANTIZED, ROPE_ATTENTION ) } } @@ -711,7 +715,7 @@ mod tests { assert_eq!(params.kv_len, 2048); assert_eq!(params.causal, 1); assert_eq!(params.block_size, 64); // M4 Pro optimal - // Check scale = 1/sqrt(128) ≈ 0.0884 + // Check scale = 1/sqrt(128) ≈ 0.0884 assert!((params.scale - 0.0884).abs() < 0.001); } diff --git a/crates/ruvllm/src/metal/operations.rs b/crates/ruvllm/src/metal/operations.rs index 496b01a1d..ab831c659 100644 --- a/crates/ruvllm/src/metal/operations.rs +++ b/crates/ruvllm/src/metal/operations.rs @@ -2,7 +2,7 @@ //! //! Provides convenient wrappers around Metal compute operations. -use super::{MetalContext, MetalConfig, AttentionParams, GemmParams, NormParams, RopeParams}; +use super::{AttentionParams, GemmParams, MetalConfig, MetalContext, NormParams, RopeParams}; use crate::error::{Result, RuvLLMError}; use crate::kernels::AttentionConfig; @@ -35,7 +35,7 @@ impl GemvParams { Self { m: m as u32, n: n as u32, - lda: n as u32, // Row-major + lda: n as u32, // Row-major alpha: 1.0, beta: 0.0, } @@ -203,7 +203,13 @@ pub fn fused_mlp_metal( .collect(); // Down projection: hidden @ down_weight^T - ctx.gemm_f32(&hidden, down_weight, batch_size, hidden_size, intermediate_size) + ctx.gemm_f32( + &hidden, + down_weight, + batch_size, + hidden_size, + intermediate_size, + ) } /// Convert FP32 to FP16 @@ -397,13 +403,16 @@ pub fn gemv_metal_with_params( if a.len() != m * n { return Err(RuvLLMError::InvalidOperation(format!( "GEMV matrix size mismatch: A[{}] != {}x{}", - a.len(), m, n + a.len(), + m, + n ))); } if x.len() != n { return Err(RuvLLMError::InvalidOperation(format!( "GEMV vector size mismatch: x[{}] != {}", - x.len(), n + x.len(), + n ))); } @@ -440,7 +449,11 @@ pub fn gemv_metal_with_params( .map_err(|e| RuvLLMError::Backend(format!("Failed to compile GEMV shader: {}", e)))?; // Try optimized kernel first, fall back to simple - let function_name = if m >= 4 { "gemv_optimized_f32" } else { "gemv_simple_f32" }; + let function_name = if m >= 4 { + "gemv_optimized_f32" + } else { + "gemv_simple_f32" + }; let function = library .get_function(function_name, None) .map_err(|e| RuvLLMError::Backend(format!("Failed to get GEMV function: {}", e)))?; @@ -520,13 +533,16 @@ pub fn gemv_metal_f16( if a.len() != m * n { return Err(RuvLLMError::InvalidOperation(format!( "GEMV matrix size mismatch: A[{}] != {}x{}", - a.len(), m, n + a.len(), + m, + n ))); } if x.len() != n { return Err(RuvLLMError::InvalidOperation(format!( "GEMV vector size mismatch: x[{}] != {}", - x.len(), n + x.len(), + n ))); } @@ -676,7 +692,9 @@ pub fn gemv_batched_metal( let pipeline = device .new_compute_pipeline_state_with_function(&function) - .map_err(|e| RuvLLMError::Backend(format!("Failed to create batched GEMV pipeline: {}", e)))?; + .map_err(|e| { + RuvLLMError::Backend(format!("Failed to create batched GEMV pipeline: {}", e)) + })?; let command_buffer = queue.new_command_buffer(); let encoder = command_buffer.new_compute_command_encoder(); @@ -752,12 +770,8 @@ mod tests { target_logits[vocab_size + 3] = 10.0; target_logits[2 * vocab_size + 2] = 10.0; - let (num_accepted, tokens) = verify_speculative_tokens( - &draft_logits, - &target_logits, - vocab_size, - num_tokens, - ); + let (num_accepted, tokens) = + verify_speculative_tokens(&draft_logits, &target_logits, vocab_size, num_tokens); assert_eq!(num_accepted, 3); // 2 accepted + 1 target correction assert_eq!(tokens, vec![5, 3, 2]); @@ -814,7 +828,9 @@ mod tests { assert!( (y[i] - x[i]).abs() < 1e-5, "Mismatch at {}: {} vs {}", - i, y[i], x[i] + i, + y[i], + x[i] ); } } @@ -850,7 +866,9 @@ mod tests { assert!( (y[i] - expected).abs() < 1e-3, "Mismatch at {}: {} vs {}", - i, y[i], expected + i, + y[i], + expected ); } } diff --git a/crates/ruvllm/src/metal/pipelines.rs b/crates/ruvllm/src/metal/pipelines.rs index 4ad9fdd3d..74680cf79 100644 --- a/crates/ruvllm/src/metal/pipelines.rs +++ b/crates/ruvllm/src/metal/pipelines.rs @@ -85,8 +85,16 @@ impl MetalPipelines { fused_attention: Self::try_create_pipeline(device, library, "fused_attention"), fused_attention_f16: Self::try_create_pipeline(device, library, "fused_attention_f16"), paged_attention: Self::try_create_pipeline(device, library, "paged_attention"), - fused_layernorm_residual: Self::try_create_pipeline(device, library, "fused_layernorm_residual"), - fused_rmsnorm_residual: Self::try_create_pipeline(device, library, "fused_rmsnorm_residual"), + fused_layernorm_residual: Self::try_create_pipeline( + device, + library, + "fused_layernorm_residual", + ), + fused_rmsnorm_residual: Self::try_create_pipeline( + device, + library, + "fused_rmsnorm_residual", + ), fused_swiglu: Self::try_create_pipeline(device, library, "fused_swiglu"), int4_gemv: Self::try_create_pipeline(device, library, "int4_gemv"), int4_gemv_simd: Self::try_create_pipeline(device, library, "int4_gemv_simd"), @@ -94,7 +102,11 @@ impl MetalPipelines { int8_gemv: Self::try_create_pipeline(device, library, "int8_gemv"), rope_then_attention: Self::try_create_pipeline(device, library, "rope_then_attention"), yarn_attention: Self::try_create_pipeline(device, library, "yarn_attention"), - apply_rope_qk_inplace: Self::try_create_pipeline(device, library, "apply_rope_qk_inplace"), + apply_rope_qk_inplace: Self::try_create_pipeline( + device, + library, + "apply_rope_qk_inplace", + ), }) } @@ -106,20 +118,48 @@ impl MetalPipelines { /// Get list of available optimized pipelines pub fn available_optimizations(&self) -> Vec<&'static str> { let mut available = Vec::new(); - if self.gemm_optimized.is_some() { available.push("gemm_optimized"); } - if self.fused_attention.is_some() { available.push("fused_attention"); } - if self.fused_attention_f16.is_some() { available.push("fused_attention_f16"); } - if self.paged_attention.is_some() { available.push("paged_attention"); } - if self.fused_layernorm_residual.is_some() { available.push("fused_layernorm_residual"); } - if self.fused_rmsnorm_residual.is_some() { available.push("fused_rmsnorm_residual"); } - if self.fused_swiglu.is_some() { available.push("fused_swiglu"); } - if self.int4_gemv.is_some() { available.push("int4_gemv"); } - if self.int4_gemv_simd.is_some() { available.push("int4_gemv_simd"); } - if self.int4_gemm.is_some() { available.push("int4_gemm"); } - if self.int8_gemv.is_some() { available.push("int8_gemv"); } - if self.rope_then_attention.is_some() { available.push("rope_then_attention"); } - if self.yarn_attention.is_some() { available.push("yarn_attention"); } - if self.apply_rope_qk_inplace.is_some() { available.push("apply_rope_qk_inplace"); } + if self.gemm_optimized.is_some() { + available.push("gemm_optimized"); + } + if self.fused_attention.is_some() { + available.push("fused_attention"); + } + if self.fused_attention_f16.is_some() { + available.push("fused_attention_f16"); + } + if self.paged_attention.is_some() { + available.push("paged_attention"); + } + if self.fused_layernorm_residual.is_some() { + available.push("fused_layernorm_residual"); + } + if self.fused_rmsnorm_residual.is_some() { + available.push("fused_rmsnorm_residual"); + } + if self.fused_swiglu.is_some() { + available.push("fused_swiglu"); + } + if self.int4_gemv.is_some() { + available.push("int4_gemv"); + } + if self.int4_gemv_simd.is_some() { + available.push("int4_gemv_simd"); + } + if self.int4_gemm.is_some() { + available.push("int4_gemm"); + } + if self.int8_gemv.is_some() { + available.push("int8_gemv"); + } + if self.rope_then_attention.is_some() { + available.push("rope_then_attention"); + } + if self.yarn_attention.is_some() { + available.push("yarn_attention"); + } + if self.apply_rope_qk_inplace.is_some() { + available.push("apply_rope_qk_inplace"); + } available } @@ -138,14 +178,9 @@ impl MetalPipelines { library: &Library, function_name: &str, ) -> Result { - let function = library - .get_function(function_name, None) - .map_err(|e| { - RuvLLMError::Backend(format!( - "Failed to get function '{}': {}", - function_name, e - )) - })?; + let function = library.get_function(function_name, None).map_err(|e| { + RuvLLMError::Backend(format!("Failed to get function '{}': {}", function_name, e)) + })?; device .new_compute_pipeline_state_with_function(&function) diff --git a/crates/ruvllm/src/models/mod.rs b/crates/ruvllm/src/models/mod.rs index e67b231ef..d99063651 100644 --- a/crates/ruvllm/src/models/mod.rs +++ b/crates/ruvllm/src/models/mod.rs @@ -65,33 +65,33 @@ pub mod ruvltra_medium; // Re-export RuvLTRA-Small types pub use ruvltra::{ + AneDispatcher, + AneOptimization, + MemoryLayout, + QuantizationType, + RuvLtraAttention, // Configuration RuvLtraConfig, - AneOptimization, - QuantizationType, - MemoryLayout, + RuvLtraDecoderLayer, + RuvLtraMLP, // Model components RuvLtraModel, - RuvLtraAttention, - RuvLtraMLP, - RuvLtraDecoderLayer, // Utilities RuvLtraModelInfo, - AneDispatcher, }; // Re-export RuvLTRA-Medium types pub use ruvltra_medium::{ + RuvLtraMediumAttention, // Configuration RuvLtraMediumConfig, - RuvLtraMediumVariant, - RuvLtraMediumQuant, - SonaHookConfig, + RuvLtraMediumDecoderLayer, + RuvLtraMediumMLP, // Model components RuvLtraMediumModel, - RuvLtraMediumAttention, - RuvLtraMediumMLP, - RuvLtraMediumDecoderLayer, // Utilities RuvLtraMediumModelInfo, + RuvLtraMediumQuant, + RuvLtraMediumVariant, + SonaHookConfig, }; diff --git a/crates/ruvllm/src/models/ruvltra.rs b/crates/ruvllm/src/models/ruvltra.rs index 3d4aec50d..cca0b6e90 100644 --- a/crates/ruvllm/src/models/ruvltra.rs +++ b/crates/ruvllm/src/models/ruvltra.rs @@ -52,18 +52,16 @@ //! ``` use crate::error::{Result, RuvLLMError}; -use crate::kernels::{ - apply_rope_neon, flash_attention_neon, rms_norm_neon, AttentionConfig, -}; use crate::kernels::rope::{precompute_rope_tables_with_config, RopeConfig, RopeTables}; +use crate::kernels::{apply_rope_neon, flash_attention_neon, rms_norm_neon, AttentionConfig}; use crate::sona::{SonaConfig, SonaIntegration, Trajectory}; #[cfg(target_arch = "aarch64")] use std::arch::aarch64::*; +use parking_lot::RwLock; use serde::{Deserialize, Serialize}; use std::sync::Arc; -use parking_lot::RwLock; // ============================================================================= // ANE Optimization Configuration @@ -242,14 +240,14 @@ impl RuvLtraConfig { intermediate_size: 4864, num_hidden_layers: 24, num_attention_heads: 14, - num_kv_heads: 2, // GQA ratio 7:1 + num_kv_heads: 2, // GQA ratio 7:1 vocab_size: 151936, max_position_embeddings: 32768, - rope_theta: 1000000.0, // Qwen uses 1M base + rope_theta: 1000000.0, // Qwen uses 1M base rms_norm_eps: 1e-6, - head_dim: 64, // 896 / 14 = 64 + head_dim: 64, // 896 / 14 = 64 use_flash_attention: true, - sliding_window: None, // Qwen 0.5B uses full attention + sliding_window: None, // Qwen 0.5B uses full attention bos_token_id: 151643, eos_token_id: 151645, pad_token_id: 151643, @@ -316,7 +314,7 @@ impl RuvLtraConfig { /// Create a minimal test configuration pub fn tiny() -> Self { Self { - hidden_size: 768, // Minimum for ANE optimization + hidden_size: 768, // Minimum for ANE optimization intermediate_size: 2048, num_hidden_layers: 4, num_attention_heads: 12, @@ -406,12 +404,16 @@ impl RuvLtraConfig { /// Estimate total model parameters pub fn estimate_params(&self) -> usize { let embed_params = self.vocab_size * self.hidden_size; - let attn_params = self.num_hidden_layers * ( - 4 * self.hidden_size * self.hidden_size // QKV + O projections - ); - let mlp_params = self.num_hidden_layers * ( - 3 * self.hidden_size * self.intermediate_size // gate, up, down - ); + let attn_params = self.num_hidden_layers + * ( + 4 * self.hidden_size * self.hidden_size + // QKV + O projections + ); + let mlp_params = self.num_hidden_layers + * ( + 3 * self.hidden_size * self.intermediate_size + // gate, up, down + ); let norm_params = (self.num_hidden_layers * 2 + 1) * self.hidden_size; embed_params + attn_params + mlp_params + norm_params @@ -527,9 +529,20 @@ impl RuvLtraAttention { } // Project to Q, K, V - let mut query = self.linear_transform(hidden_states, &self.q_proj, hidden_size, hidden_size); - let mut key = self.linear_transform(hidden_states, &self.k_proj, hidden_size, num_kv_heads * head_dim); - let value = self.linear_transform(hidden_states, &self.v_proj, hidden_size, num_kv_heads * head_dim); + let mut query = + self.linear_transform(hidden_states, &self.q_proj, hidden_size, hidden_size); + let mut key = self.linear_transform( + hidden_states, + &self.k_proj, + hidden_size, + num_kv_heads * head_dim, + ); + let value = self.linear_transform( + hidden_states, + &self.v_proj, + hidden_size, + num_kv_heads * head_dim, + ); // Apply RoPE to Q and K self.apply_rope(&mut query, positions, num_heads, head_dim); @@ -569,22 +582,23 @@ impl RuvLtraAttention { } // Apply sliding window if configured - let (k_slice, v_slice, _effective_kv_len) = if let Some(window) = self.config.sliding_window { - let pos = positions[t]; - let start = pos.saturating_sub(window); - if start > 0 { - let start_offset = start * head_dim; - ( - k_slice[start_offset..].to_vec(), - v_slice[start_offset..].to_vec(), - kv_len - start, - ) + let (k_slice, v_slice, _effective_kv_len) = + if let Some(window) = self.config.sliding_window { + let pos = positions[t]; + let start = pos.saturating_sub(window); + if start > 0 { + let start_offset = start * head_dim; + ( + k_slice[start_offset..].to_vec(), + v_slice[start_offset..].to_vec(), + kv_len - start, + ) + } else { + (k_slice, v_slice, kv_len) + } } else { (k_slice, v_slice, kv_len) - } - } else { - (k_slice, v_slice, kv_len) - }; + }; // Flash attention let head_output = flash_attention_neon(q_slice, &k_slice, &v_slice, scale, true); @@ -608,14 +622,25 @@ impl RuvLtraAttention { for t in 0..seq_len { let offset = (t * num_heads + h) * head_dim; let mut head_vec = x[offset..offset + head_dim].to_vec(); - apply_rope_neon(&mut head_vec, &[positions[t]], head_dim, self.config.rope_theta); + apply_rope_neon( + &mut head_vec, + &[positions[t]], + head_dim, + self.config.rope_theta, + ); x[offset..offset + head_dim].copy_from_slice(&head_vec); } } } /// Linear transformation with ANE-aware tiling - fn linear_transform(&self, input: &[f32], weights: &[f32], in_dim: usize, out_dim: usize) -> Vec { + fn linear_transform( + &self, + input: &[f32], + weights: &[f32], + in_dim: usize, + out_dim: usize, + ) -> Vec { let batch_size = input.len() / in_dim; let mut output = vec![0.0; batch_size * out_dim]; @@ -752,11 +777,21 @@ impl RuvLtraMLP { /// SwiGLU: down_proj(SiLU(gate_proj(x)) * up_proj(x)) pub fn forward(&self, hidden_states: &[f32]) -> Result> { // Gate projection + SiLU activation - let gate = self.linear(hidden_states, &self.gate_proj, self.hidden_size, self.intermediate_size); + let gate = self.linear( + hidden_states, + &self.gate_proj, + self.hidden_size, + self.intermediate_size, + ); let gate_activated = self.silu(&gate); // Up projection - let up = self.linear(hidden_states, &self.up_proj, self.hidden_size, self.intermediate_size); + let up = self.linear( + hidden_states, + &self.up_proj, + self.hidden_size, + self.intermediate_size, + ); // Element-wise multiply (gating) let hidden: Vec = gate_activated @@ -766,7 +801,12 @@ impl RuvLtraMLP { .collect(); // Down projection - let output = self.linear(&hidden, &self.down_proj, self.intermediate_size, self.hidden_size); + let output = self.linear( + &hidden, + &self.down_proj, + self.intermediate_size, + self.hidden_size, + ); Ok(output) } @@ -943,7 +983,9 @@ impl RuvLtraModel { } let sona = if config.sona_enabled { - Some(Arc::new(RwLock::new(SonaIntegration::new(config.sona_config.clone())))) + Some(Arc::new(RwLock::new(SonaIntegration::new( + config.sona_config.clone(), + )))) } else { None }; @@ -962,9 +1004,9 @@ impl RuvLtraModel { /// Enable SONA pretraining integration pub fn enable_sona_pretraining(&mut self) -> Result<()> { if self.sona.is_none() { - self.sona = Some(Arc::new(RwLock::new( - SonaIntegration::new(self.config.sona_config.clone()) - ))); + self.sona = Some(Arc::new(RwLock::new(SonaIntegration::new( + self.config.sona_config.clone(), + )))); } Ok(()) } @@ -1009,7 +1051,8 @@ impl RuvLtraModel { token_id ))); } - hidden_states.extend_from_slice(&self.embed_tokens[offset..offset + self.config.hidden_size]); + hidden_states + .extend_from_slice(&self.embed_tokens[offset..offset + self.config.hidden_size]); } // Process through decoder layers @@ -1036,9 +1079,9 @@ impl RuvLtraModel { let lm_weights = if self.tie_word_embeddings { &self.embed_tokens } else { - self.lm_head.as_ref().ok_or_else(|| { - RuvLLMError::InvalidOperation("No LM head weights".to_string()) - })? + self.lm_head + .as_ref() + .ok_or_else(|| RuvLLMError::InvalidOperation("No LM head weights".to_string()))? }; // Compute logits @@ -1066,10 +1109,13 @@ impl RuvLtraModel { } /// Get routing recommendation from SONA - pub fn get_routing_recommendation(&self, query_embedding: &[f32]) -> Option { - self.sona.as_ref().map(|sona| { - sona.read().get_routing_recommendation(query_embedding) - }) + pub fn get_routing_recommendation( + &self, + query_embedding: &[f32], + ) -> Option { + self.sona + .as_ref() + .map(|sona| sona.read().get_routing_recommendation(query_embedding)) } /// Get model info @@ -1190,12 +1236,14 @@ impl AneDispatcher { /// Record an ANE operation pub fn record_ane_op(&self) { - self.ane_ops.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + self.ane_ops + .fetch_add(1, std::sync::atomic::Ordering::Relaxed); } /// Record a GPU operation pub fn record_gpu_op(&self) { - self.gpu_ops.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + self.gpu_ops + .fetch_add(1, std::sync::atomic::Ordering::Relaxed); } /// Get dispatch statistics @@ -1258,7 +1306,10 @@ mod tests { let model = RuvLtraModel::new(&config).unwrap(); assert_eq!(model.layers.len(), 4); - assert_eq!(model.embed_tokens.len(), config.vocab_size * config.hidden_size); + assert_eq!( + model.embed_tokens.len(), + config.vocab_size * config.hidden_size + ); } #[test] @@ -1284,7 +1335,8 @@ mod tests { ("user".to_string(), "How are you?".to_string()), ]; - let template = RuvLtraModel::apply_chat_template(&messages, Some("You are a helpful assistant.")); + let template = + RuvLtraModel::apply_chat_template(&messages, Some("You are a helpful assistant.")); assert!(template.contains("<|im_start|>system")); assert!(template.contains("<|im_start|>user")); diff --git a/crates/ruvllm/src/models/ruvltra_medium.rs b/crates/ruvllm/src/models/ruvltra_medium.rs index ac6e85eef..8b5b325ae 100644 --- a/crates/ruvllm/src/models/ruvltra_medium.rs +++ b/crates/ruvllm/src/models/ruvltra_medium.rs @@ -63,11 +63,9 @@ //! ``` use crate::error::{Result, RuvLLMError}; -use crate::kernels::{ - apply_rope_neon, flash_attention_neon, rms_norm_neon, AttentionConfig, -}; use crate::kernels::rope::{precompute_rope_tables_with_config, RopeConfig, RopeTables}; -use crate::paged_attention::{PagedAttentionConfig, PagedAttention, PageTable}; +use crate::kernels::{apply_rope_neon, flash_attention_neon, rms_norm_neon, AttentionConfig}; +use crate::paged_attention::{PageTable, PagedAttention, PagedAttentionConfig}; use crate::sona::{SonaConfig, SonaIntegration, Trajectory}; /// Type alias for PagedAttention used as KV cache @@ -77,9 +75,9 @@ use crate::speculative::SpeculativeConfig; #[cfg(target_arch = "aarch64")] use std::arch::aarch64::*; +use parking_lot::RwLock; use serde::{Deserialize, Serialize}; use std::sync::Arc; -use parking_lot::RwLock; // ============================================================================= // Model Variants @@ -116,8 +114,8 @@ impl RuvLtraMediumVariant { pub fn temperature(&self) -> f32 { match self { Self::Base => 0.7, - Self::Coder => 0.2, // Lower for deterministic code - Self::Agent => 0.3, // Slightly higher for creativity + Self::Coder => 0.2, // Lower for deterministic code + Self::Agent => 0.3, // Slightly higher for creativity } } @@ -160,11 +158,11 @@ impl RuvLtraMediumQuant { /// Get bytes per parameter pub fn bytes_per_param(&self) -> f32 { match self { - Self::None => 2.0, // FP16 - Self::Q4KM => 0.5625, // ~4.5 bits - Self::Q5KM => 0.6875, // ~5.5 bits - Self::Q80 => 1.0625, // ~8.5 bits - Self::Mixed => 1.0, // Average + Self::None => 2.0, // FP16 + Self::Q4KM => 0.5625, // ~4.5 bits + Self::Q5KM => 0.6875, // ~5.5 bits + Self::Q80 => 1.0625, // ~8.5 bits + Self::Mixed => 1.0, // Average } } @@ -310,12 +308,12 @@ impl RuvLtraMediumConfig { intermediate_size: 11008, num_hidden_layers: 32, num_attention_heads: 16, - num_kv_heads: 2, // GQA ratio 8:1 + num_kv_heads: 2, // GQA ratio 8:1 vocab_size: 151936, max_position_embeddings: 32768, - rope_theta: 1000000.0, // Qwen uses 1M base + rope_theta: 1000000.0, // Qwen uses 1M base rms_norm_eps: 1e-6, - head_dim: 128, // 2048 / 16 = 128 + head_dim: 128, // 2048 / 16 = 128 use_flash_attention: true, sliding_window: None, bos_token_id: 151643, @@ -329,7 +327,7 @@ impl RuvLtraMediumConfig { // Memory optimization use_paged_attention: true, paged_config: PagedAttentionConfig { - page_size: 64, // 64-token blocks + page_size: 64, // 64-token blocks max_pages_per_sequence: 512, page_table_capacity: 8192, num_heads: 16, @@ -352,12 +350,12 @@ impl RuvLtraMediumConfig { sona_enabled: true, sona_config: SonaConfig { hidden_dim: 2048, - embedding_dim: 1024, // Half of hidden_size + embedding_dim: 1024, // Half of hidden_size micro_lora_rank: 4, base_lora_rank: 8, instant_learning_rate: 0.01, background_learning_rate: 0.001, - ewc_lambda: 1000.0, // Higher for larger model + ewc_lambda: 1000.0, // Higher for larger model pattern_capacity: 50000, background_interval_secs: 3600, deep_interval_secs: 604800, @@ -376,12 +374,12 @@ impl RuvLtraMediumConfig { Self { variant: RuvLtraMediumVariant::Coder, sona_config: SonaConfig { - pattern_capacity: 100000, // More patterns for code - quality_threshold: 0.7, // Higher quality bar + pattern_capacity: 100000, // More patterns for code + quality_threshold: 0.7, // Higher quality bar ..Self::base().sona_config }, sona_hooks: SonaHookConfig { - hook_layers: vec![8, 16, 24, 28], // Extra late-layer hook + hook_layers: vec![8, 16, 24, 28], // Extra late-layer hook ..Default::default() }, ..Self::base() @@ -393,15 +391,15 @@ impl RuvLtraMediumConfig { Self { variant: RuvLtraMediumVariant::Agent, use_paged_attention: true, - use_flash_attn_2: true, // Maximize speed + use_flash_attn_2: true, // Maximize speed sona_config: SonaConfig { - micro_lora_rank: 2, // Lower latency - instant_learning_rate: 0.02, // Faster adaptation + micro_lora_rank: 2, // Lower latency + instant_learning_rate: 0.02, // Faster adaptation ..Self::base().sona_config }, sona_hooks: SonaHookConfig { use_hnsw: true, - hnsw_m: 32, // More connections for routing + hnsw_m: 32, // More connections for routing hnsw_ef_construction: 400, ..Default::default() }, @@ -443,20 +441,22 @@ impl RuvLtraMediumConfig { /// Estimate total parameters pub fn estimate_params(&self) -> usize { let embed_params = self.vocab_size * self.hidden_size; - let attn_params = self.num_hidden_layers * ( - // Q projection - self.hidden_size * self.hidden_size + + let attn_params = self.num_hidden_layers + * ( + // Q projection + self.hidden_size * self.hidden_size + // K, V projections (smaller due to GQA) 2 * self.hidden_size * (self.num_kv_heads * self.head_dim) + // O projection self.hidden_size * self.hidden_size - ); - let mlp_params = self.num_hidden_layers * ( - // gate_proj, up_proj - 2 * self.hidden_size * self.intermediate_size + + ); + let mlp_params = self.num_hidden_layers + * ( + // gate_proj, up_proj + 2 * self.hidden_size * self.intermediate_size + // down_proj self.intermediate_size * self.hidden_size - ); + ); let norm_params = (self.num_hidden_layers * 2 + 1) * self.hidden_size; embed_params + attn_params + mlp_params + norm_params @@ -533,8 +533,18 @@ impl RuvLtraMediumAttention { // Project to Q, K, V let mut query = self.matmul(hidden_states, &self.q_proj, hidden_size, hidden_size); - let mut key = self.matmul(hidden_states, &self.k_proj, hidden_size, num_kv_heads * head_dim); - let value = self.matmul(hidden_states, &self.v_proj, hidden_size, num_kv_heads * head_dim); + let mut key = self.matmul( + hidden_states, + &self.k_proj, + hidden_size, + num_kv_heads * head_dim, + ); + let value = self.matmul( + hidden_states, + &self.v_proj, + hidden_size, + num_kv_heads * head_dim, + ); // Apply RoPE self.apply_rope(&mut query, positions, num_heads); @@ -552,7 +562,13 @@ impl RuvLtraMediumAttention { } /// Flash Attention 2 implementation - fn flash_attention(&self, query: &[f32], key: &[f32], value: &[f32], seq_len: usize) -> Result> { + fn flash_attention( + &self, + query: &[f32], + key: &[f32], + value: &[f32], + seq_len: usize, + ) -> Result> { let num_heads = self.config.num_attention_heads; let num_kv_heads = self.config.num_kv_heads; let head_dim = self.config.head_dim; @@ -589,7 +605,13 @@ impl RuvLtraMediumAttention { } /// Standard attention (fallback) - fn standard_attention(&self, query: &[f32], key: &[f32], value: &[f32], seq_len: usize) -> Result> { + fn standard_attention( + &self, + query: &[f32], + key: &[f32], + value: &[f32], + seq_len: usize, + ) -> Result> { // Similar to flash_attention but without kernel optimization self.flash_attention(query, key, value, seq_len) } @@ -603,7 +625,12 @@ impl RuvLtraMediumAttention { for t in 0..seq_len { let offset = (t * num_heads + h) * head_dim; let mut head_vec = x[offset..offset + head_dim].to_vec(); - apply_rope_neon(&mut head_vec, &[positions[t]], head_dim, self.config.rope_theta); + apply_rope_neon( + &mut head_vec, + &[positions[t]], + head_dim, + self.config.rope_theta, + ); x[offset..offset + head_dim].copy_from_slice(&head_vec); } } @@ -636,8 +663,15 @@ impl RuvLtraMediumAttention { } #[cfg(target_arch = "aarch64")] - unsafe fn matmul_neon(&self, input: &[f32], weights: &[f32], output: &mut [f32], - batch: usize, in_dim: usize, out_dim: usize) { + unsafe fn matmul_neon( + &self, + input: &[f32], + weights: &[f32], + output: &mut [f32], + batch: usize, + in_dim: usize, + out_dim: usize, + ) { for b in 0..batch { for o in 0..out_dim { let mut acc = vdupq_n_f32(0.0); @@ -697,7 +731,8 @@ impl RuvLtraMediumMLP { } fn linear(&self, input: &[f32], weights: &[f32]) -> Vec { - let in_dim = if weights.len() == self.gate_proj.len() || weights.len() == self.up_proj.len() { + let in_dim = if weights.len() == self.gate_proj.len() || weights.len() == self.up_proj.len() + { self.hidden_size } else { self.intermediate_size @@ -768,8 +803,11 @@ impl RuvLtraMediumDecoderLayer { let mut normed = hidden_states.to_vec(); for t in 0..seq_len { let offset = t * self.hidden_size; - rms_norm_neon(&mut normed[offset..offset + self.hidden_size], - &self.input_layernorm, self.rms_norm_eps); + rms_norm_neon( + &mut normed[offset..offset + self.hidden_size], + &self.input_layernorm, + self.rms_norm_eps, + ); } // Attention @@ -787,15 +825,21 @@ impl RuvLtraMediumDecoderLayer { }; // Residual - let mut hidden: Vec = hidden_states.iter().zip(attn_out.iter()) - .map(|(h, a)| h + a).collect(); + let mut hidden: Vec = hidden_states + .iter() + .zip(attn_out.iter()) + .map(|(h, a)| h + a) + .collect(); // Pre-norm for MLP let mut normed = hidden.clone(); for t in 0..seq_len { let offset = t * self.hidden_size; - rms_norm_neon(&mut normed[offset..offset + self.hidden_size], - &self.post_attention_layernorm, self.rms_norm_eps); + rms_norm_neon( + &mut normed[offset..offset + self.hidden_size], + &self.post_attention_layernorm, + self.rms_norm_eps, + ); } // MLP @@ -809,7 +853,11 @@ impl RuvLtraMediumDecoderLayer { Ok(hidden) } - fn apply_sona_hook(&self, hidden_states: &[f32], sona: &Arc>) -> Result> { + fn apply_sona_hook( + &self, + hidden_states: &[f32], + sona: &Arc>, + ) -> Result> { // Extract embeddings for trajectory recording // This is a simplified version - real implementation would be more sophisticated Ok(hidden_states.to_vec()) @@ -841,7 +889,9 @@ impl RuvLtraMediumModel { } let sona = if config.sona_enabled { - Some(Arc::new(RwLock::new(SonaIntegration::new(config.sona_config.clone())))) + Some(Arc::new(RwLock::new(SonaIntegration::new( + config.sona_config.clone(), + )))) } else { None }; @@ -867,9 +917,9 @@ impl RuvLtraMediumModel { /// Enable SONA with custom hook layers pub fn enable_sona_with_hooks(&mut self, hook_layers: &[usize]) -> Result<()> { if self.sona.is_none() { - self.sona = Some(Arc::new(RwLock::new( - SonaIntegration::new(self.config.sona_config.clone()) - ))); + self.sona = Some(Arc::new(RwLock::new(SonaIntegration::new( + self.config.sona_config.clone(), + )))); } // Update layer hooks @@ -881,18 +931,15 @@ impl RuvLtraMediumModel { } /// Forward pass - pub fn forward( - &mut self, - input_ids: &[u32], - positions: &[usize], - ) -> Result> { + pub fn forward(&mut self, input_ids: &[u32], positions: &[usize]) -> Result> { let seq_len = positions.len(); // Embeddings let mut hidden_states = Vec::with_capacity(seq_len * self.config.hidden_size); for &token_id in input_ids { let offset = (token_id as usize) * self.config.hidden_size; - hidden_states.extend_from_slice(&self.embed_tokens[offset..offset + self.config.hidden_size]); + hidden_states + .extend_from_slice(&self.embed_tokens[offset..offset + self.config.hidden_size]); } // Decoder layers @@ -908,15 +955,20 @@ impl RuvLtraMediumModel { // Final norm for t in 0..seq_len { let offset = t * self.config.hidden_size; - rms_norm_neon(&mut hidden_states[offset..offset + self.config.hidden_size], - &self.norm, self.config.rms_norm_eps); + rms_norm_neon( + &mut hidden_states[offset..offset + self.config.hidden_size], + &self.norm, + self.config.rms_norm_eps, + ); } // LM head let lm_weights = if self.tie_word_embeddings { &self.embed_tokens } else { - self.lm_head.as_ref().ok_or_else(|| RuvLLMError::InvalidOperation("No LM head".into()))? + self.lm_head + .as_ref() + .ok_or_else(|| RuvLLMError::InvalidOperation("No LM head".into()))? }; let mut logits = vec![0.0; seq_len * self.config.vocab_size]; diff --git a/crates/ruvllm/src/optimization/metrics.rs b/crates/ruvllm/src/optimization/metrics.rs index 0b3637348..36e834b94 100644 --- a/crates/ruvllm/src/optimization/metrics.rs +++ b/crates/ruvllm/src/optimization/metrics.rs @@ -86,9 +86,8 @@ impl MovingAverage { } let mean = self.average(); - let variance: f32 = values.iter() - .map(|v| (v - mean).powi(2)) - .sum::() / (values.len() - 1) as f32; + let variance: f32 = + values.iter().map(|v| (v - mean).powi(2)).sum::() / (values.len() - 1) as f32; variance.sqrt() } @@ -175,7 +174,9 @@ impl LatencyHistogram { /// Record a latency value in milliseconds pub fn record(&self, latency_ms: f32) { // Find the appropriate bucket - let bucket_idx = self.buckets.iter() + let bucket_idx = self + .buckets + .iter() .position(|&b| latency_ms <= b) .unwrap_or(self.buckets.len() - 1); @@ -228,7 +229,8 @@ impl LatencyHistogram { /// Get bucket counts for visualization pub fn bucket_counts(&self) -> Vec<(f32, u64)> { - self.buckets.iter() + self.buckets + .iter() .zip(self.counts.iter()) .map(|(b, c)| (*b, c.load(Ordering::Relaxed))) .collect() @@ -257,7 +259,9 @@ impl Default for LatencyHistogram { impl Clone for LatencyHistogram { fn clone(&self) -> Self { - let counts: Vec = self.counts.iter() + let counts: Vec = self + .counts + .iter() .map(|c| AtomicU64::new(c.load(Ordering::Relaxed))) .collect(); let sum = *self.sum.read(); @@ -340,7 +344,8 @@ impl InferenceMetrics { let tps = tokens as f32 / duration.as_secs_f32(); self.tps.add(tps); } - self.total_tokens.fetch_add(tokens as u64, Ordering::Relaxed); + self.total_tokens + .fetch_add(tokens as u64, Ordering::Relaxed); *self.last_update.write() = Instant::now(); } @@ -473,7 +478,10 @@ impl InferenceMetrics { self.tps.clear(); self.kv_cache_hits.store(0, Ordering::Relaxed); self.kv_cache_misses.store(0, Ordering::Relaxed); - self.peak_memory_bytes.store(self.memory_usage_bytes.load(Ordering::Relaxed), Ordering::Relaxed); + self.peak_memory_bytes.store( + self.memory_usage_bytes.load(Ordering::Relaxed), + Ordering::Relaxed, + ); self.total_requests.store(0, Ordering::Relaxed); self.total_tokens.store(0, Ordering::Relaxed); self.latency_histogram.reset(); @@ -618,7 +626,8 @@ impl MetricsCollector { /// Get recent snapshots pub fn get_history(&self, count: usize) -> Vec { let history = self.history.read(); - history.iter() + history + .iter() .rev() .take(count) .map(|(_, s)| s.clone()) @@ -632,7 +641,8 @@ impl MetricsCollector { return 0.0; } - let recent: Vec = history.iter() + let recent: Vec = history + .iter() .rev() .take(10) .map(|(_, s)| s.ttft_avg_ms) @@ -662,7 +672,8 @@ impl MetricsCollector { return 0.0; } - let recent: Vec = history.iter() + let recent: Vec = history + .iter() .rev() .take(10) .map(|(_, s)| s.tps_avg) diff --git a/crates/ruvllm/src/optimization/mod.rs b/crates/ruvllm/src/optimization/mod.rs index f2f942e4e..a372aadfa 100644 --- a/crates/ruvllm/src/optimization/mod.rs +++ b/crates/ruvllm/src/optimization/mod.rs @@ -100,13 +100,13 @@ pub mod sona_llm; // Re-exports pub use metrics::{ - InferenceMetrics, MetricsCollector, MetricsSnapshot, MovingAverage, LatencyHistogram, + InferenceMetrics, LatencyHistogram, MetricsCollector, MetricsSnapshot, MovingAverage, }; pub use realtime::{ - RealtimeOptimizer, RealtimeConfig, BatchSizeStrategy, KvCachePressurePolicy, - TokenBudgetAllocation, SpeculativeConfig, OptimizationDecision, + BatchSizeStrategy, KvCachePressurePolicy, OptimizationDecision, RealtimeConfig, + RealtimeOptimizer, SpeculativeConfig, TokenBudgetAllocation, }; pub use sona_llm::{ - SonaLlm, SonaLlmConfig, TrainingSample, AdaptationResult, LearningLoopStats, - ConsolidationStrategy, OptimizationTrigger, + AdaptationResult, ConsolidationStrategy, LearningLoopStats, OptimizationTrigger, SonaLlm, + SonaLlmConfig, TrainingSample, }; diff --git a/crates/ruvllm/src/optimization/realtime.rs b/crates/ruvllm/src/optimization/realtime.rs index 2a5ce7a87..dd29c5784 100644 --- a/crates/ruvllm/src/optimization/realtime.rs +++ b/crates/ruvllm/src/optimization/realtime.rs @@ -49,7 +49,7 @@ impl Default for RealtimeConfig { min_batch_size: 1, max_batch_size: 64, kv_cache_pressure_threshold: 0.8, - enable_speculative: true, // Enabled by default for 2-3x decode speedup + enable_speculative: true, // Enabled by default for 2-3x decode speedup speculative: SpeculativeConfig::default(), batch_strategy: BatchSizeStrategy::Adaptive, kv_policy: KvCachePressurePolicy::Evict, @@ -272,9 +272,7 @@ impl RealtimeOptimizer { let new_batch_size = match config.batch_strategy { BatchSizeStrategy::Fixed => current_batch, - BatchSizeStrategy::Adaptive => { - self.adaptive_batch_size(&config, recent_latencies) - } + BatchSizeStrategy::Adaptive => self.adaptive_batch_size(&config, recent_latencies), BatchSizeStrategy::Aggressive => { // Maximize batch size while staying under latency target @@ -306,7 +304,8 @@ impl RealtimeOptimizer { } }; - self.current_batch_size.store(new_batch_size, Ordering::Relaxed); + self.current_batch_size + .store(new_batch_size, Ordering::Relaxed); new_batch_size } @@ -384,7 +383,8 @@ impl RealtimeOptimizer { let predicted_throughput = avg_throughput * batch_ratio; // Throughput grows linearly let pred_latency_norm = (predicted_latency / config.latency_target_ms).min(2.0); - let pred_throughput_norm = (predicted_throughput / config.throughput_target_tps).min(2.0); + let pred_throughput_norm = + (predicted_throughput / config.throughput_target_tps).min(2.0); let predicted_utility = alpha * pred_throughput_norm - beta * pred_latency_norm; @@ -424,7 +424,10 @@ impl RealtimeOptimizer { let mut sorted_requests: Vec<(usize, &Request)> = requests.iter().enumerate().collect(); sorted_requests.sort_by(|(_, a), (_, b)| { // Higher priority first - let priority_cmp = b.priority.partial_cmp(&a.priority).unwrap_or(std::cmp::Ordering::Equal); + let priority_cmp = b + .priority + .partial_cmp(&a.priority) + .unwrap_or(std::cmp::Ordering::Equal); if priority_cmp != std::cmp::Ordering::Equal { return priority_cmp; } @@ -460,14 +463,17 @@ impl RealtimeOptimizer { let estimated_completion = self.estimate_completion_time(request, batch_slot); - allocations.push((original_idx, TokenBudgetAllocation { - request_id: request.id.clone(), - max_tokens, - priority: request.priority, - deadline: request.deadline, - batch_slot, - estimated_completion_ms: estimated_completion, - })); + allocations.push(( + original_idx, + TokenBudgetAllocation { + request_id: request.id.clone(), + max_tokens, + priority: request.priority, + deadline: request.deadline, + batch_slot, + estimated_completion_ms: estimated_completion, + }, + )); } // Sort back to original order @@ -628,7 +634,8 @@ impl RealtimeOptimizer { let kv_pressure = *self.kv_cache_pressure.read(); let (should_evict, evict_count) = if kv_pressure >= config.kv_cache_pressure_threshold { let excess_pressure = kv_pressure - config.kv_cache_pressure_threshold; - let evict_ratio = (excess_pressure / (1.0 - config.kv_cache_pressure_threshold)).min(0.5); + let evict_ratio = + (excess_pressure / (1.0 - config.kv_cache_pressure_threshold)).min(0.5); (true, (evict_ratio * 1000.0) as usize) // Evict proportionally } else { (false, 0) @@ -653,7 +660,8 @@ impl RealtimeOptimizer { }; // Estimate outcomes - let batch_ratio = batch_size as f32 / self.current_batch_size.load(Ordering::Relaxed).max(1) as f32; + let batch_ratio = + batch_size as f32 / self.current_batch_size.load(Ordering::Relaxed).max(1) as f32; let estimated_latency = snapshot.ttft_avg_ms * batch_ratio.sqrt(); let estimated_tps = snapshot.tps_avg * batch_ratio; @@ -758,8 +766,7 @@ impl RealtimeOptimizer { // Increase acceptance threshold when latency is high if avg_latency > config.latency_target_ms { - spec_config.acceptance_threshold = - (spec_config.acceptance_threshold + 0.1).min(0.95); + spec_config.acceptance_threshold = (spec_config.acceptance_threshold + 0.1).min(0.95); } spec_config @@ -939,7 +946,11 @@ mod tests { let latencies = vec![50.0, 55.0, 45.0]; let batch = optimizer.optimize_batch_size(&latencies); - assert!(batch >= 1 && batch <= 16, "Strategy {:?} produced invalid batch size", strategy); + assert!( + batch >= 1 && batch <= 16, + "Strategy {:?} produced invalid batch size", + strategy + ); } } } diff --git a/crates/ruvllm/src/optimization/sona_llm.rs b/crates/ruvllm/src/optimization/sona_llm.rs index 7e99e23f7..61b4982a1 100644 --- a/crates/ruvllm/src/optimization/sona_llm.rs +++ b/crates/ruvllm/src/optimization/sona_llm.rs @@ -148,11 +148,7 @@ pub struct TrainingSample { impl TrainingSample { /// Create a new training sample - pub fn new( - input_embedding: Vec, - output_embedding: Vec, - quality: f32, - ) -> Self { + pub fn new(input_embedding: Vec, output_embedding: Vec, quality: f32) -> Self { Self { input_embedding, output_embedding, @@ -357,7 +353,8 @@ impl SonaLlm { let latency_us = elapsed.as_micros() as u64; // Update statistics - self.instant_latency_sum.fetch_add(latency_us, Ordering::Relaxed); + self.instant_latency_sum + .fetch_add(latency_us, Ordering::Relaxed); self.instant_count.fetch_add(1, Ordering::Relaxed); // Queue for background consolidation @@ -473,7 +470,8 @@ impl SonaLlm { } // Check if deep loop should be triggered - let should_trigger_deep = *self.accumulated_quality.read() >= self.config.deep_trigger_threshold; + let should_trigger_deep = + *self.accumulated_quality.read() >= self.config.deep_trigger_threshold; AdaptationResult { applied: true, @@ -516,7 +514,10 @@ impl SonaLlm { let training = self.training.read(); let lora = self.micro_lora.read(); - if training.train_step(&lora, &sample.input_embedding, feedback).is_ok() { + if training + .train_step(&lora, &sample.input_embedding, feedback) + .is_ok() + { total_quality += sample.quality; } } @@ -579,29 +580,24 @@ impl SonaLlm { let ewc_state_map: HashMap = ewc_states .into_iter() .filter_map(|(module, export)| { - let fisher_a = ndarray::Array2::from_shape_vec( - export.shape_a, - export.fisher_a, - ).ok()?; - let fisher_b = ndarray::Array2::from_shape_vec( - export.shape_b, - export.fisher_b, - ).ok()?; - let optimal_a = ndarray::Array2::from_shape_vec( - export.shape_a, - export.optimal_a, - ).ok()?; - let optimal_b = ndarray::Array2::from_shape_vec( - export.shape_b, - export.optimal_b, - ).ok()?; + let fisher_a = + ndarray::Array2::from_shape_vec(export.shape_a, export.fisher_a).ok()?; + let fisher_b = + ndarray::Array2::from_shape_vec(export.shape_b, export.fisher_b).ok()?; + let optimal_a = + ndarray::Array2::from_shape_vec(export.shape_a, export.optimal_a).ok()?; + let optimal_b = + ndarray::Array2::from_shape_vec(export.shape_b, export.optimal_b).ok()?; - Some((module, crate::lora::micro_lora::EwcState { - fisher_a, - fisher_b, - optimal_a, - optimal_b, - })) + Some(( + module, + crate::lora::micro_lora::EwcState { + fisher_a, + fisher_b, + optimal_a, + optimal_b, + }, + )) }) .collect(); @@ -643,7 +639,11 @@ impl SonaLlm { fn consolidate_best(&self, samples: &[TrainingSample]) -> f32 { // Take top 20% by quality let mut sorted: Vec<&TrainingSample> = samples.iter().collect(); - sorted.sort_by(|a, b| b.quality.partial_cmp(&a.quality).unwrap_or(std::cmp::Ordering::Equal)); + sorted.sort_by(|a, b| { + b.quality + .partial_cmp(&a.quality) + .unwrap_or(std::cmp::Ordering::Equal) + }); let top_count = (samples.len() as f32 * 0.2).ceil() as usize; let best: Vec<&TrainingSample> = sorted.into_iter().take(top_count.max(1)).collect(); @@ -665,7 +665,8 @@ impl SonaLlm { let mut total_delta = 0.0f32; for batch in samples.chunks(batch_size) { - let batch_quality: f32 = batch.iter().map(|s| s.quality).sum::() / batch.len() as f32; + let batch_quality: f32 = + batch.iter().map(|s| s.quality).sum::() / batch.len() as f32; let lr = self.config.training.learning_rate * batch_quality; let lora = self.micro_lora.read(); @@ -876,13 +877,7 @@ mod tests { let sona_llm = SonaLlm::new(SonaLlmConfig::default()); let samples: Vec = (0..10) - .map(|i| { - TrainingSample::new( - vec![0.1 * i as f32; 768], - vec![0.2 * i as f32; 768], - 0.8, - ) - }) + .map(|i| TrainingSample::new(vec![0.1 * i as f32; 768], vec![0.2 * i as f32; 768], 0.8)) .collect(); let result = sona_llm.deep_optimize(&samples); @@ -896,15 +891,11 @@ mod tests { #[test] fn test_training_sample() { - let sample = TrainingSample::new( - vec![0.1; 64], - vec![0.2; 64], - 0.9, - ) - .with_query("Test query".to_string()) - .with_response("Test response".to_string()) - .with_latency(50.0) - .with_session("session-123".to_string()); + let sample = TrainingSample::new(vec![0.1; 64], vec![0.2; 64], 0.9) + .with_query("Test query".to_string()) + .with_response("Test response".to_string()) + .with_latency(50.0) + .with_session("session-123".to_string()); assert_eq!(sample.query, Some("Test query".to_string())); assert_eq!(sample.session_id, "session-123"); @@ -937,7 +928,11 @@ mod tests { // Add some samples for i in 0..5 { - sona_llm.instant_adapt(&format!("Q{}", i), &format!("R{}", i), 0.5 + i as f32 * 0.1); + sona_llm.instant_adapt( + &format!("Q{}", i), + &format!("R{}", i), + 0.5 + i as f32 * 0.1, + ); } let result = sona_llm.background_consolidate(); diff --git a/crates/ruvllm/src/paged_attention.rs b/crates/ruvllm/src/paged_attention.rs index e649f1992..ec225ac85 100644 --- a/crates/ruvllm/src/paged_attention.rs +++ b/crates/ruvllm/src/paged_attention.rs @@ -147,9 +147,7 @@ impl PageBlock { let end_offset = start_offset + keys.len(); if end_offset > self.keys.len() { - return Err(RuvLLMError::PagedAttention( - "Block overflow".to_string(), - )); + return Err(RuvLLMError::PagedAttention("Block overflow".to_string())); } self.keys[start_offset..end_offset].copy_from_slice(keys); @@ -217,17 +215,12 @@ impl PageTable { let mut free_blocks = self.free_blocks.write(); let block_id = match self.config.allocation_strategy { - AllocationStrategy::FirstFit => { - free_blocks.pop_front() - } - AllocationStrategy::BestFit | AllocationStrategy::RoundRobin => { - free_blocks.pop_front() - } + AllocationStrategy::FirstFit => free_blocks.pop_front(), + AllocationStrategy::BestFit | AllocationStrategy::RoundRobin => free_blocks.pop_front(), }; - let block_id = block_id.ok_or_else(|| { - RuvLLMError::OutOfMemory("No free blocks available".to_string()) - })?; + let block_id = block_id + .ok_or_else(|| RuvLLMError::OutOfMemory("No free blocks available".to_string()))?; // Update page table entry self.entries @@ -249,9 +242,10 @@ impl PageTable { let mut free_blocks = self.free_blocks.write(); if block_id >= blocks.len() { - return Err(RuvLLMError::PagedAttention( - format!("Invalid block ID: {}", block_id), - )); + return Err(RuvLLMError::PagedAttention(format!( + "Invalid block ID: {}", + block_id + ))); } // Reset the block @@ -278,12 +272,7 @@ impl PageTable { } /// Append KV pairs to a sequence - pub fn append_kv( - &self, - sequence_id: &str, - keys: &[f32], - values: &[f32], - ) -> Result<()> { + pub fn append_kv(&self, sequence_id: &str, keys: &[f32], values: &[f32]) -> Result<()> { let stride = self.config.num_kv_heads * self.config.head_dim; let num_tokens = keys.len() / stride; @@ -405,24 +394,14 @@ impl PagedAttention { } /// Append KV pairs for a sequence - pub fn append_kv( - &self, - sequence_id: &str, - keys: &[f32], - values: &[f32], - ) -> Result<()> { + pub fn append_kv(&self, sequence_id: &str, keys: &[f32], values: &[f32]) -> Result<()> { self.page_table.append_kv(sequence_id, keys, values) } /// Compute paged attention /// /// This is a simplified version - production would use optimized kernels - pub fn forward( - &self, - query: &[f32], - sequence_id: &str, - scale: f32, - ) -> Result> { + pub fn forward(&self, query: &[f32], sequence_id: &str, scale: f32) -> Result> { let blocks = self.page_table.get_blocks(sequence_id).ok_or_else(|| { RuvLLMError::PagedAttention(format!("Sequence not found: {}", sequence_id)) })?; @@ -459,7 +438,8 @@ impl PagedAttention { let v_slice = &block.values[kv_offset..kv_offset + head_dim]; // Dot product for attention score - let score: f32 = q_slice.iter() + let score: f32 = q_slice + .iter() .zip(k_slice.iter()) .map(|(q, k)| q * k * scale) .sum(); diff --git a/crates/ruvllm/src/policy_store.rs b/crates/ruvllm/src/policy_store.rs index 1eb719182..accd2e27f 100644 --- a/crates/ruvllm/src/policy_store.rs +++ b/crates/ruvllm/src/policy_store.rs @@ -13,8 +13,8 @@ use crate::error::{Result, RuvLLMError}; use chrono::{DateTime, Utc}; -use ruvector_core::{AgenticDB, SearchQuery, VectorEntry}; use ruvector_core::types::DbOptions; +use ruvector_core::{AgenticDB, SearchQuery, VectorEntry}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use uuid::Uuid; @@ -165,7 +165,7 @@ impl Default for RouterPolicy { fn default() -> Self { Self { cell_weights: vec![0.0; 128 * 128], // Placeholder - head_biases: vec![0.0; 4], // 4 model sizes + head_biases: vec![0.0; 4], // 4 model sizes ewc_lambda: 0.1, training_loss: 0.0, learning_rate: 0.001, @@ -190,8 +190,7 @@ impl PolicyStore { options.storage_path = storage_path.to_string(); options.dimensions = embedding_dim; - let db = AgenticDB::new(options) - .map_err(|e| RuvLLMError::Storage(e.to_string()))?; + let db = AgenticDB::new(options).map_err(|e| RuvLLMError::Storage(e.to_string()))?; Ok(Self { db, @@ -206,11 +205,23 @@ impl PolicyStore { // Create metadata let mut metadata = HashMap::new(); - metadata.insert("policy_type".to_string(), serde_json::json!(entry.policy_type.as_str())); - metadata.insert("confidence".to_string(), serde_json::json!(entry.confidence)); - metadata.insert("source".to_string(), serde_json::json!(entry.source.as_str())); + metadata.insert( + "policy_type".to_string(), + serde_json::json!(entry.policy_type.as_str()), + ); + metadata.insert( + "confidence".to_string(), + serde_json::json!(entry.confidence), + ); + metadata.insert( + "source".to_string(), + serde_json::json!(entry.source.as_str()), + ); metadata.insert("parameters".to_string(), entry.parameters.clone()); - metadata.insert("created_at".to_string(), serde_json::json!(entry.created_at.to_rfc3339())); + metadata.insert( + "created_at".to_string(), + serde_json::json!(entry.created_at.to_rfc3339()), + ); metadata.insert("tags".to_string(), serde_json::json!(entry.tags)); if let Some(ref fisher) = entry.fisher_diagonal { @@ -225,7 +236,8 @@ impl PolicyStore { }; // Store in Ruvector - self.db.insert(vector_entry) + self.db + .insert(vector_entry) .map_err(|e| RuvLLMError::Storage(e.to_string()))?; // Update cache @@ -243,14 +255,17 @@ impl PolicyStore { ef_search: None, }; - let results = self.db.search(query) + let results = self + .db + .search(query) .map_err(|e| RuvLLMError::Storage(e.to_string()))?; let mut entries = Vec::with_capacity(results.len()); for result in results { if let Some(metadata) = &result.metadata { - if let Some(entry) = self.entry_from_metadata(&result.id, query_embedding, metadata) { + if let Some(entry) = self.entry_from_metadata(&result.id, query_embedding, metadata) + { entries.push(entry); } } @@ -270,7 +285,8 @@ impl PolicyStore { /// Search by policy type pub fn search_by_type(&self, policy_type: &PolicyType, limit: usize) -> Vec { - self.cache.iter() + self.cache + .iter() .filter(|e| &e.policy_type == policy_type) .map(|e| e.clone()) .take(limit) @@ -334,16 +350,24 @@ impl PolicyStore { pub fn stats(&self) -> PolicyStoreStats { PolicyStoreStats { total_policies: self.cache.len(), - quantization_policies: self.cache.iter() + quantization_policies: self + .cache + .iter() .filter(|e| e.policy_type == PolicyType::Quantization) .count(), - router_policies: self.cache.iter() + router_policies: self + .cache + .iter() .filter(|e| e.policy_type == PolicyType::Router) .count(), - ewc_policies: self.cache.iter() + ewc_policies: self + .cache + .iter() .filter(|e| e.policy_type == PolicyType::Ewc) .count(), - pattern_policies: self.cache.iter() + pattern_policies: self + .cache + .iter() .filter(|e| e.policy_type == PolicyType::Pattern) .count(), } @@ -366,16 +390,28 @@ impl PolicyStore { let parameters = metadata.get("parameters")?.clone(); let created_at_str = metadata.get("created_at")?.as_str()?; - let created_at = DateTime::parse_from_rfc3339(created_at_str).ok()?.with_timezone(&Utc); + let created_at = DateTime::parse_from_rfc3339(created_at_str) + .ok()? + .with_timezone(&Utc); - let tags: Vec = metadata.get("tags") + let tags: Vec = metadata + .get("tags") .and_then(|t| t.as_array()) - .map(|arr| arr.iter().filter_map(|v| v.as_str().map(String::from)).collect()) + .map(|arr| { + arr.iter() + .filter_map(|v| v.as_str().map(String::from)) + .collect() + }) .unwrap_or_default(); - let fisher_diagonal: Option> = metadata.get("fisher_diagonal") + let fisher_diagonal: Option> = metadata + .get("fisher_diagonal") .and_then(|f| f.as_array()) - .map(|arr| arr.iter().filter_map(|v| v.as_f64().map(|f| f as f32)).collect()); + .map(|arr| { + arr.iter() + .filter_map(|v| v.as_f64().map(|f| f as f32)) + .collect() + }); Some(PolicyEntry { id: uuid, @@ -415,7 +451,10 @@ mod tests { fn test_policy_type() { assert_eq!(PolicyType::Quantization.as_str(), "quantization"); assert_eq!(PolicyType::Router.as_str(), "router"); - assert_eq!(PolicyType::from_str("quantization"), Some(PolicyType::Quantization)); + assert_eq!( + PolicyType::from_str("quantization"), + Some(PolicyType::Quantization) + ); } #[test] diff --git a/crates/ruvllm/src/quality/coherence.rs b/crates/ruvllm/src/quality/coherence.rs index 7379336e1..89a3beffb 100644 --- a/crates/ruvllm/src/quality/coherence.rs +++ b/crates/ruvllm/src/quality/coherence.rs @@ -311,8 +311,8 @@ impl CoherenceValidator { let std_dev = compute_std_dev(&all_sims, avg); let consistency_score = avg; - let is_consistent = inconsistent.is_empty() - && consistency_score >= self.config.similarity_threshold; + let is_consistent = + inconsistent.is_empty() && consistency_score >= self.config.similarity_threshold; Ok(SemanticConsistencyResult { is_consistent, @@ -339,12 +339,9 @@ impl CoherenceValidator { // Check for negation-based contradictions for i in 0..segments.len() { for j in (i + 1)..segments.len() { - if let Some(contradiction) = self.check_negation_contradiction( - i, - j, - &segments[i], - &segments[j], - ) { + if let Some(contradiction) = + self.check_negation_contradiction(i, j, &segments[i], &segments[j]) + { contradictions.push(contradiction); } } @@ -353,12 +350,9 @@ impl CoherenceValidator { // Check for numeric contradictions for i in 0..segments.len() { for j in (i + 1)..segments.len() { - if let Some(contradiction) = self.check_numeric_contradiction( - i, - j, - &segments[i], - &segments[j], - ) { + if let Some(contradiction) = + self.check_numeric_contradiction(i, j, &segments[i], &segments[j]) + { contradictions.push(contradiction); } } @@ -439,11 +433,7 @@ impl CoherenceValidator { segment_index: i + 1, violation_type: ViolationType::TopicShift, severity: 1.0 - sim, - description: format!( - "Abrupt topic shift between segments {} and {}", - i, - i + 1 - ), + description: format!("Abrupt topic shift between segments {} and {}", i, i + 1), suggestion: Some("Add a transition sentence".to_string()), }); suggestions.push(format!( @@ -461,10 +451,7 @@ impl CoherenceValidator { segment_index: i + 1, violation_type: ViolationType::MissingTransition, severity: 0.3, - description: format!( - "Missing transition marker at segment {}", - i + 1 - ), + description: format!("Missing transition marker at segment {}", i + 1), suggestion: Some("Add a transition word".to_string()), }); } @@ -478,11 +465,8 @@ impl CoherenceValidator { transition_scores.iter().sum::() / transition_scores.len() as f32 }; - let violation_penalty = violations - .iter() - .map(|v| v.severity) - .sum::() - / segments.len() as f32; + let violation_penalty = + violations.iter().map(|v| v.severity).sum::() / segments.len() as f32; let flow_score = (avg_transition - violation_penalty * 0.5).clamp(0.0, 1.0); let has_logical_flow = flow_score >= self.config.logical_flow_threshold; @@ -514,7 +498,8 @@ impl CoherenceValidator { // Simple hash-based feature extraction for (i, word) in words.iter().enumerate() { for (j, c) in word.chars().enumerate() { - let idx = ((c as usize * 31 + j * 17 + i * 13) % self.config.embedding_dim) as usize; + let idx = + ((c as usize * 31 + j * 17 + i * 13) % self.config.embedding_dim) as usize; embedding[idx] += 1.0; } } @@ -643,10 +628,7 @@ impl CoherenceValidator { text_b: text_b.to_string(), severity: 0.6, contradiction_type: ContradictionType::Numeric, - explanation: format!( - "Numeric inconsistency: {} vs {}", - num_a, num_b - ), + explanation: format!("Numeric inconsistency: {} vs {}", num_a, num_b), }); } } @@ -693,8 +675,8 @@ fn compute_std_dev(values: &[f32], mean: f32) -> f32 { return 0.0; } - let variance: f32 = values.iter().map(|v| (v - mean).powi(2)).sum::() - / (values.len() - 1) as f32; + let variance: f32 = + values.iter().map(|v| (v - mean).powi(2)).sum::() / (values.len() - 1) as f32; variance.sqrt() } @@ -748,7 +730,9 @@ mod tests { let validator = CoherenceValidator::default_config(); let segments = vec!["This is a test.".to_string()]; - let result = validator.validate_semantic_consistency(&segments, None).unwrap(); + let result = validator + .validate_semantic_consistency(&segments, None) + .unwrap(); assert!(result.is_consistent); assert_eq!(result.consistency_score, 1.0); } @@ -761,7 +745,9 @@ mod tests { "The cat was sitting on the mat.".to_string(), ]; - let result = validator.validate_semantic_consistency(&segments, None).unwrap(); + let result = validator + .validate_semantic_consistency(&segments, None) + .unwrap(); assert!(result.consistency_score > 0.5); } diff --git a/crates/ruvllm/src/quality/diversity.rs b/crates/ruvllm/src/quality/diversity.rs index 14f83ce40..a10400801 100644 --- a/crates/ruvllm/src/quality/diversity.rs +++ b/crates/ruvllm/src/quality/diversity.rs @@ -300,7 +300,8 @@ impl DiversityAnalyzer { || repeated_patterns.len() > samples.len() / 4; let collapse_severity = if has_collapse { - ((avg_similarity - self.config.mode_collapse_threshold) / (1.0 - self.config.mode_collapse_threshold)) + ((avg_similarity - self.config.mode_collapse_threshold) + / (1.0 - self.config.mode_collapse_threshold)) .clamp(0.0, 1.0) * 0.5 + dominant_percentage * 0.3 @@ -451,7 +452,12 @@ impl DiversityAnalyzer { all_bigrams.insert(format!("{} {}", tokens[i], tokens[i + 1])); } if i + 2 < tokens.len() { - all_trigrams.insert(format!("{} {} {}", tokens[i], tokens[i + 1], tokens[i + 2])); + all_trigrams.insert(format!( + "{} {} {}", + tokens[i], + tokens[i + 1], + tokens[i + 2] + )); } } } @@ -653,7 +659,8 @@ impl DiversityAnalyzer { for (i, word) in words.iter().enumerate() { for (j, c) in word.chars().enumerate() { - let idx = ((c as usize * 31 + j * 17 + i * 13) % self.config.embedding_dim) as usize; + let idx = + ((c as usize * 31 + j * 17 + i * 13) % self.config.embedding_dim) as usize; embedding[idx] += 1.0; } } diff --git a/crates/ruvllm/src/quality/metrics.rs b/crates/ruvllm/src/quality/metrics.rs index cf6830876..dbf9f4b94 100644 --- a/crates/ruvllm/src/quality/metrics.rs +++ b/crates/ruvllm/src/quality/metrics.rs @@ -415,16 +415,22 @@ impl fmt::Display for QualitySummary { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "Quality Summary (Grade: {})", self.overall_grade)?; writeln!(f, " Composite Score: {:.1}%", self.composite_score * 100.0)?; - writeln!(f, " Strongest: {} ({:.1}%)", + writeln!( + f, + " Strongest: {} ({:.1}%)", self.strongest_dimension, - self.dimensions.iter() + self.dimensions + .iter() .find(|(d, _)| *d == self.strongest_dimension) .map(|(_, s)| s * 100.0) .unwrap_or(0.0) )?; - writeln!(f, " Weakest: {} ({:.1}%)", + writeln!( + f, + " Weakest: {} ({:.1}%)", self.weakest_dimension, - self.dimensions.iter() + self.dimensions + .iter() .find(|(d, _)| *d == self.weakest_dimension) .map(|(_, s)| s * 100.0) .unwrap_or(0.0) @@ -434,8 +440,14 @@ impl fmt::Display for QualitySummary { let bar_len = (score * 20.0) as usize; let bar: String = (0..bar_len).map(|_| '#').collect(); let empty: String = (0..(20 - bar_len)).map(|_| '-').collect(); - writeln!(f, " {:<18} [{}{:<20}] {:.1}%", - dim.to_string(), bar, empty, score * 100.0)?; + writeln!( + f, + " {:<18} [{}{:<20}] {:.1}%", + dim.to_string(), + bar, + empty, + score * 100.0 + )?; } Ok(()) } @@ -513,7 +525,10 @@ mod tests { let summary = metrics.to_summary(); assert_eq!(summary.overall_grade, 'B'); - assert_eq!(summary.strongest_dimension, QualityDimension::SchemaCompliance); + assert_eq!( + summary.strongest_dimension, + QualityDimension::SchemaCompliance + ); assert_eq!(summary.weakest_dimension, QualityDimension::Uniqueness); } diff --git a/crates/ruvllm/src/quality/mod.rs b/crates/ruvllm/src/quality/mod.rs index 42e1333d4..af88e9374 100644 --- a/crates/ruvllm/src/quality/mod.rs +++ b/crates/ruvllm/src/quality/mod.rs @@ -93,7 +93,7 @@ pub use coherence::{ LogicalFlowResult, SemanticConsistencyResult, }; pub use diversity::{ - DiversityAnalyzer, DiversityConfig, DiversityResult, DiversificationSuggestion, + DiversificationSuggestion, DiversityAnalyzer, DiversityConfig, DiversityResult, ModeCollapseResult, }; pub use metrics::{ diff --git a/crates/ruvllm/src/quality/scoring_engine.rs b/crates/ruvllm/src/quality/scoring_engine.rs index cfe504fdd..73f3bcfab 100644 --- a/crates/ruvllm/src/quality/scoring_engine.rs +++ b/crates/ruvllm/src/quality/scoring_engine.rs @@ -207,11 +207,7 @@ impl QualityScoringEngine { } /// Score a text directly (without GenerationResult) - pub fn score_text( - &self, - text: &str, - context: &ScoringContext, - ) -> Result { + pub fn score_text(&self, text: &str, context: &ScoringContext) -> Result { let mut metrics = QualityMetrics::new(); // Create a minimal GenerationResult-like context @@ -244,7 +240,9 @@ impl QualityScoringEngine { if !context.previous_generations.is_empty() { let mut all_samples: Vec = context.previous_generations.clone(); all_samples.push(text.to_string()); - let diversity_result = self.diversity_analyzer.calculate_diversity(&all_samples, None); + let diversity_result = self + .diversity_analyzer + .calculate_diversity(&all_samples, None); metrics.diversity = diversity_result.diversity_score; } else { metrics.diversity = 1.0; @@ -622,7 +620,9 @@ impl QualityScoringEngine { .detect_contradictions(&segments, None)?; // Check logical flow - let flow_result = self.coherence_validator.check_logical_flow(&segments, None)?; + let flow_result = self + .coherence_validator + .check_logical_flow(&segments, None)?; // Combine scores let combined = coherence_result.consistency_score * 0.4 @@ -632,11 +632,7 @@ impl QualityScoringEngine { Ok(combined) } - fn score_diversity( - &self, - result: &GenerationResult, - context: &ScoringContext, - ) -> Result { + fn score_diversity(&self, result: &GenerationResult, context: &ScoringContext) -> Result { let text = result.generated_text.as_deref().unwrap_or(""); if text.is_empty() { return Ok(1.0); @@ -650,7 +646,9 @@ impl QualityScoringEngine { return Ok(1.0); } - let diversity_result = self.diversity_analyzer.calculate_diversity(&all_samples, None); + let diversity_result = self + .diversity_analyzer + .calculate_diversity(&all_samples, None); Ok(diversity_result.diversity_score) } @@ -684,7 +682,10 @@ impl QualityScoringEngine { // Check if generated values are within reasonable range of time-series let ts_min = time_series.iter().cloned().fold(f64::INFINITY, f64::min); - let ts_max = time_series.iter().cloned().fold(f64::NEG_INFINITY, f64::max); + let ts_max = time_series + .iter() + .cloned() + .fold(f64::NEG_INFINITY, f64::max); let ts_range = ts_max - ts_min; // Allow some extrapolation (20% beyond range) @@ -716,11 +717,7 @@ impl QualityScoringEngine { Ok(range_score * 0.6 + trend_score * 0.4) } - fn score_uniqueness( - &self, - result: &GenerationResult, - context: &ScoringContext, - ) -> Result { + fn score_uniqueness(&self, result: &GenerationResult, context: &ScoringContext) -> Result { let text = result.generated_text.as_deref().unwrap_or(""); Ok(self.calculate_uniqueness(text, &context.previous_generations)) } @@ -779,8 +776,8 @@ fn calculate_std_dev(values: &[f32], mean: f32) -> f32 { return 0.0; } - let variance: f32 = values.iter().map(|v| (v - mean).powi(2)).sum::() - / (values.len() - 1) as f32; + let variance: f32 = + values.iter().map(|v| (v - mean).powi(2)).sum::() / (values.len() - 1) as f32; variance.sqrt() } @@ -795,11 +792,7 @@ fn calculate_slope(values: &[f32]) -> (f32, f32) { // Linear regression let sum_x: f32 = (0..values.len()).map(|i| i as f32).sum(); let sum_y: f32 = values.iter().sum(); - let sum_xy: f32 = values - .iter() - .enumerate() - .map(|(i, y)| i as f32 * y) - .sum(); + let sum_xy: f32 = values.iter().enumerate().map(|(i, y)| i as f32 * y).sum(); let sum_x2: f32 = (0..values.len()).map(|i| (i as f32).powi(2)).sum(); let denominator = n * sum_x2 - sum_x * sum_x; @@ -907,7 +900,9 @@ mod tests { #[test] fn test_score_generation() { let engine = QualityScoringEngine::new(); - let result = create_test_result("This is a test generation. It has multiple sentences. The content is coherent."); + let result = create_test_result( + "This is a test generation. It has multiple sentences. The content is coherent.", + ); let context = ScoringContext::default(); let metrics = engine.score_generation(&result, &context).unwrap(); @@ -1012,13 +1007,7 @@ mod tests { let engine = QualityScoringEngine::new(); for i in 0..10 { - let metrics = QualityMetrics::with_scores( - 0.5 + (i as f32 * 0.05), - 0.5, - 0.5, - 0.5, - 0.5, - ); + let metrics = QualityMetrics::with_scores(0.5 + (i as f32 * 0.05), 0.5, 0.5, 0.5, 0.5); engine.track_quality_over_time(metrics); } @@ -1031,10 +1020,7 @@ mod tests { let engine = QualityScoringEngine::new(); // Exact duplicate - let uniqueness = engine.calculate_uniqueness( - "Hello world", - &["Hello world".to_string()], - ); + let uniqueness = engine.calculate_uniqueness("Hello world", &["Hello world".to_string()]); assert!(uniqueness < 0.1); // Completely different diff --git a/crates/ruvllm/src/quality/validators.rs b/crates/ruvllm/src/quality/validators.rs index 5cfee00d1..0ccf12591 100644 --- a/crates/ruvllm/src/quality/validators.rs +++ b/crates/ruvllm/src/quality/validators.rs @@ -221,7 +221,12 @@ impl JsonSchemaValidator { } /// Validate a value against a type specification - fn validate_type(&self, value: &JsonValue, expected_type: &str, path: &str) -> ValidationResult { + fn validate_type( + &self, + value: &JsonValue, + expected_type: &str, + path: &str, + ) -> ValidationResult { let mut result = ValidationResult::default(); result.checks_performed = 1; @@ -245,7 +250,11 @@ impl JsonSchemaValidator { result.errors.push(ValidationError { error_type: ValidationErrorType::TypeMismatch, path: path.to_string(), - message: format!("Expected type '{}', got '{}'", expected_type, value_type_name(value)), + message: format!( + "Expected type '{}', got '{}'", + expected_type, + value_type_name(value) + ), expected: Some(expected_type.to_string()), actual: Some(value_type_name(value).to_string()), }); @@ -282,7 +291,11 @@ impl JsonSchemaValidator { }; if let Some(prop_value) = obj.get(key) { - result.merge(self.validate_against_schema(prop_value, prop_schema, &prop_path)); + result.merge(self.validate_against_schema( + prop_value, + prop_schema, + &prop_path, + )); } } @@ -500,7 +513,11 @@ impl SchemaValidator for RangeValidator { // Check minimum if let Some(min) = self.min { - let min_ok = if self.exclusive_min { value > min } else { value >= min }; + let min_ok = if self.exclusive_min { + value > min + } else { + value >= min + }; if !min_ok { result.is_valid = false; result.errors.push(ValidationError { @@ -509,10 +526,18 @@ impl SchemaValidator for RangeValidator { message: format!( "Value {} is {} minimum {}", value, - if self.exclusive_min { "not greater than" } else { "less than" }, + if self.exclusive_min { + "not greater than" + } else { + "less than" + }, min ), - expected: Some(format!("{} {}", if self.exclusive_min { ">" } else { ">=" }, min)), + expected: Some(format!( + "{} {}", + if self.exclusive_min { ">" } else { ">=" }, + min + )), actual: Some(value.to_string()), }); } @@ -520,7 +545,11 @@ impl SchemaValidator for RangeValidator { // Check maximum if let Some(max) = self.max { - let max_ok = if self.exclusive_max { value < max } else { value <= max }; + let max_ok = if self.exclusive_max { + value < max + } else { + value <= max + }; if !max_ok { result.is_valid = false; result.errors.push(ValidationError { @@ -529,10 +558,18 @@ impl SchemaValidator for RangeValidator { message: format!( "Value {} is {} maximum {}", value, - if self.exclusive_max { "not less than" } else { "greater than" }, + if self.exclusive_max { + "not less than" + } else { + "greater than" + }, max ), - expected: Some(format!("{} {}", if self.exclusive_max { "<" } else { "<=" }, max)), + expected: Some(format!( + "{} {}", + if self.exclusive_max { "<" } else { "<=" }, + max + )), actual: Some(value.to_string()), }); } @@ -615,11 +652,11 @@ impl FormatValidator { !local.is_empty() && !domain.is_empty() && domain.contains('.') - && domain.chars().all(|c| c.is_alphanumeric() || c == '.' || c == '-') - } - "url" => { - value.starts_with("http://") || value.starts_with("https://") + && domain + .chars() + .all(|c| c.is_alphanumeric() || c == '.' || c == '-') } + "url" => value.starts_with("http://") || value.starts_with("https://"), "uuid" => { // UUID format: 8-4-4-4-12 hex digits let parts: Vec<&str> = value.split('-').collect(); @@ -629,7 +666,9 @@ impl FormatValidator { && parts[2].len() == 4 && parts[3].len() == 4 && parts[4].len() == 12 - && parts.iter().all(|p| p.chars().all(|c| c.is_ascii_hexdigit())) + && parts + .iter() + .all(|p| p.chars().all(|c| c.is_ascii_hexdigit())) } "date" => { // ISO 8601 date: YYYY-MM-DD @@ -860,9 +899,7 @@ mod tests { #[test] fn test_exclusive_range() { - let validator = RangeValidator::new() - .min_exclusive(0.0) - .max_exclusive(10.0); + let validator = RangeValidator::new().min_exclusive(0.0).max_exclusive(10.0); assert!(validator.validate(&json!(5)).is_valid); assert!(!validator.validate(&json!(0)).is_valid); @@ -881,7 +918,11 @@ mod tests { fn test_format_validator_uuid() { let validator = FormatValidator::uuid(); - assert!(validator.validate(&json!("550e8400-e29b-41d4-a716-446655440000")).is_valid); + assert!( + validator + .validate(&json!("550e8400-e29b-41d4-a716-446655440000")) + .is_valid + ); assert!(!validator.validate(&json!("not-a-uuid")).is_valid); } @@ -941,10 +982,8 @@ mod tests { #[test] fn test_from_fields() { - let validator = JsonSchemaValidator::from_fields(&[ - ("name", "string"), - ("count", "integer"), - ]); + let validator = + JsonSchemaValidator::from_fields(&[("name", "string"), ("count", "integer")]); let valid = json!({ "name": "test", "count": 5 }); assert!(validator.validate(&valid).is_valid); diff --git a/crates/ruvllm/src/quantize/mod.rs b/crates/ruvllm/src/quantize/mod.rs index 209812627..5270eb788 100644 --- a/crates/ruvllm/src/quantize/mod.rs +++ b/crates/ruvllm/src/quantize/mod.rs @@ -41,21 +41,16 @@ mod ruvltra_quant; pub use ruvltra_quant::{ - // Core quantizer - RuvltraQuantizer, - QuantConfig, - TargetFormat, - - // Quantization functions - quantize_ruvltra_q4, - quantize_ruvltra_q5, - quantize_ruvltra_q8, dequantize_for_ane, // Memory estimation estimate_memory_q4, estimate_memory_q5, estimate_memory_q8, + // Quantization functions + quantize_ruvltra_q4, + quantize_ruvltra_q5, + quantize_ruvltra_q8, MemoryEstimate, // Block types @@ -63,7 +58,11 @@ pub use ruvltra_quant::{ Q5KMBlock, Q8Block, + QuantConfig, // Progress tracking QuantProgress, QuantStats, + // Core quantizer + RuvltraQuantizer, + TargetFormat, }; diff --git a/crates/ruvllm/src/quantize/ruvltra_quant.rs b/crates/ruvllm/src/quantize/ruvltra_quant.rs index 7e48481bd..cbf3945cc 100644 --- a/crates/ruvllm/src/quantize/ruvltra_quant.rs +++ b/crates/ruvllm/src/quantize/ruvltra_quant.rs @@ -17,8 +17,8 @@ //! - Blocked layouts matching ANE tile sizes (typically 16x16 or 32x32) //! - Interleaved scales for efficient fused operations -use std::io::{Read, Write as IoWrite, BufWriter, Seek, SeekFrom}; use std::fs::File; +use std::io::{BufWriter, Read, Seek, SeekFrom, Write as IoWrite}; use std::path::Path; use crate::error::{Result, RuvLLMError}; @@ -204,8 +204,19 @@ pub struct MemoryBreakdown { /// - Embeddings: ~32K vocab * 896 dim * 2 bytes (FP16) = ~57 MB /// - 24 layers * (Q,K,V,O + MLP) quantized to Q4_K = ~243 MB /// - Total: ~300 MB -pub fn estimate_memory_q4(params_billions: f64, vocab_size: usize, hidden_dim: usize, num_layers: usize) -> MemoryEstimate { - estimate_memory_internal(params_billions, vocab_size, hidden_dim, num_layers, TargetFormat::Q4_K_M) +pub fn estimate_memory_q4( + params_billions: f64, + vocab_size: usize, + hidden_dim: usize, + num_layers: usize, +) -> MemoryEstimate { + estimate_memory_internal( + params_billions, + vocab_size, + hidden_dim, + num_layers, + TargetFormat::Q4_K_M, + ) } /// Estimate memory for Q5_K_M quantization @@ -213,8 +224,19 @@ pub fn estimate_memory_q4(params_billions: f64, vocab_size: usize, hidden_dim: u /// For a 0.5B parameter model: /// - Similar structure but 5.5 bits per weight /// - Total: ~375 MB -pub fn estimate_memory_q5(params_billions: f64, vocab_size: usize, hidden_dim: usize, num_layers: usize) -> MemoryEstimate { - estimate_memory_internal(params_billions, vocab_size, hidden_dim, num_layers, TargetFormat::Q5_K_M) +pub fn estimate_memory_q5( + params_billions: f64, + vocab_size: usize, + hidden_dim: usize, + num_layers: usize, +) -> MemoryEstimate { + estimate_memory_internal( + params_billions, + vocab_size, + hidden_dim, + num_layers, + TargetFormat::Q5_K_M, + ) } /// Estimate memory for Q8_0 quantization @@ -222,8 +244,19 @@ pub fn estimate_memory_q5(params_billions: f64, vocab_size: usize, hidden_dim: u /// For a 0.5B parameter model: /// - 8.5 bits per weight /// - Total: ~500 MB -pub fn estimate_memory_q8(params_billions: f64, vocab_size: usize, hidden_dim: usize, num_layers: usize) -> MemoryEstimate { - estimate_memory_internal(params_billions, vocab_size, hidden_dim, num_layers, TargetFormat::Q8_0) +pub fn estimate_memory_q8( + params_billions: f64, + vocab_size: usize, + hidden_dim: usize, + num_layers: usize, +) -> MemoryEstimate { + estimate_memory_internal( + params_billions, + vocab_size, + hidden_dim, + num_layers, + TargetFormat::Q8_0, + ) } fn estimate_memory_internal( @@ -613,7 +646,11 @@ fn quantize_q4_k_block(data: &[f32]) -> Q4KMBlock { // Compute sub-block scale (6-bit) let sb_range = sb_max - sb_min; - let sb_scale = if d > 0.0 { (sb_range / d).min(63.0) as u8 } else { 0 }; + let sb_scale = if d > 0.0 { + (sb_range / d).min(63.0) as u8 + } else { + 0 + }; // Pack 6-bit scale into scales array let scale_byte_idx = (sb * 6) / 8; @@ -873,8 +910,9 @@ impl RuvltraQuantizer { let is_output = tensor_name.contains("lm_head") || tensor_name.contains("output"); // Keep certain layers in higher precision - if (self.config.keep_embed_fp16 && is_embedding) || - (self.config.keep_output_fp16 && is_output) { + if (self.config.keep_embed_fp16 && is_embedding) + || (self.config.keep_output_fp16 && is_output) + { return self.quantize_to_fp16(data); } @@ -915,9 +953,7 @@ impl RuvltraQuantizer { self.stats.elements_processed += data.len(); Ok(bytes) } - TargetFormat::F16 => { - self.quantize_to_fp16(data) - } + TargetFormat::F16 => self.quantize_to_fp16(data), } } @@ -990,18 +1026,27 @@ mod tests { // The estimate will be higher than real GGUF sizes but should scale correctly let estimate = estimate_memory_q4(0.5, 151936, 896, 24); // Allow wider range since this is a simplified estimate - assert!(estimate.total_mb > 100.0 && estimate.total_mb < 1000.0, - "Estimate should be reasonable, got {:.1}MB", estimate.total_mb); + assert!( + estimate.total_mb > 100.0 && estimate.total_mb < 1000.0, + "Estimate should be reasonable, got {:.1}MB", + estimate.total_mb + ); let estimate_q8 = estimate_memory_q8(0.5, 151936, 896, 24); // Q8 should be larger than Q4 - assert!(estimate_q8.total_mb > estimate.total_mb, + assert!( + estimate_q8.total_mb > estimate.total_mb, "Q8 ({:.1}MB) should be larger than Q4 ({:.1}MB)", - estimate_q8.total_mb, estimate.total_mb); + estimate_q8.total_mb, + estimate.total_mb + ); // Compression ratio should be positive (FP32 is bigger) - assert!(estimate.compression_ratio > 1.0, - "Compression ratio should be > 1, got {:.2}", estimate.compression_ratio); + assert!( + estimate.compression_ratio > 1.0, + "Compression ratio should be > 1, got {:.2}", + estimate.compression_ratio + ); } #[test] @@ -1017,9 +1062,12 @@ mod tests { dequantize_for_ane(&blocks, &mut output); // Check that values are roughly preserved - let mse: f64 = data.iter().zip(output.iter()) + let mse: f64 = data + .iter() + .zip(output.iter()) .map(|(a, b)| ((a - b) as f64).powi(2)) - .sum::() / 256.0; + .sum::() + / 256.0; assert!(mse < 0.01, "Quantization MSE too high: {}", mse); } @@ -1043,7 +1091,12 @@ mod tests { let f16 = f32_to_f16(val); let back = f16_to_f32(f16); let error = (val - back).abs() / val.abs().max(1.0); - assert!(error < 0.01, "F16 roundtrip error too high for {}: got {}", val, back); + assert!( + error < 0.01, + "F16 roundtrip error too high for {}: got {}", + val, + back + ); } } diff --git a/crates/ruvllm/src/reasoning_bank/consolidation.rs b/crates/ruvllm/src/reasoning_bank/consolidation.rs index 63f6f8b84..c691b395f 100644 --- a/crates/ruvllm/src/reasoning_bank/consolidation.rs +++ b/crates/ruvllm/src/reasoning_bank/consolidation.rs @@ -108,8 +108,8 @@ impl FisherInformation { let other_weight = 1.0 - self_weight; for i in 0..self.diagonal.len() { self.diagonal[i] = self.diagonal[i] * self_weight + other.diagonal[i] * other_weight; - self.ema_grad_squared[i] = self.ema_grad_squared[i] * self_weight - + other.ema_grad_squared[i] * other_weight; + self.ema_grad_squared[i] = + self.ema_grad_squared[i] * self_weight + other.ema_grad_squared[i] * other_weight; } self.sample_count = ((self.sample_count as f32 * self_weight) @@ -145,7 +145,11 @@ pub struct ImportanceFactors { impl ImportanceScore { /// Compute importance score for a pattern - pub fn compute(pattern: &Pattern, fisher: Option<&FisherInformation>, max_age_secs: u64) -> Self { + pub fn compute( + pattern: &Pattern, + fisher: Option<&FisherInformation>, + max_age_secs: u64, + ) -> Self { let mut factors = ImportanceFactors::default(); // Usage factor (log scale to avoid domination) @@ -256,7 +260,13 @@ impl PatternConsolidator { // Compute importance scores let scores: Vec = patterns .iter() - .map(|p| ImportanceScore::compute(p, self.fisher_info.get(&p.id), self.config.max_unused_age_secs)) + .map(|p| { + ImportanceScore::compute( + p, + self.fisher_info.get(&p.id), + self.config.max_unused_age_secs, + ) + }) .collect(); // Identify patterns to prune (low importance) @@ -394,7 +404,8 @@ impl PatternConsolidator { /// Update Fisher information for a pattern pub fn update_fisher(&mut self, pattern_id: u64, gradient: &[f32]) { - let fisher = self.fisher_info + let fisher = self + .fisher_info .entry(pattern_id) .or_insert_with(|| FisherInformation::new(gradient.len())); @@ -458,8 +469,8 @@ impl PatternConsolidator { .count(); let scale = 1.0 + 0.1 * important_count as f32; - self.lambda = (self.config.lambda * scale) - .clamp(self.config.min_lambda, self.config.max_lambda); + self.lambda = + (self.config.lambda * scale).clamp(self.config.min_lambda, self.config.max_lambda); } /// Consolidate all Fisher information (for memory efficiency) @@ -469,7 +480,12 @@ impl PatternConsolidator { } // Average all Fisher information - let dim = self.fisher_info.values().next().map(|f| f.diagonal.len()).unwrap_or(0); + let dim = self + .fisher_info + .values() + .next() + .map(|f| f.diagonal.len()) + .unwrap_or(0); if dim == 0 { return; } @@ -631,7 +647,9 @@ mod tests { make_pattern(3, vec![0.0, 1.0, 0.0, 0.0], 0.9, 10), // Different ]; - let merged = consolidator.find_mergeable_patterns(&patterns, &[]).unwrap(); + let merged = consolidator + .find_mergeable_patterns(&patterns, &[]) + .unwrap(); // Pattern 2 should be marked for merging into 1 assert!(merged.contains(&2)); assert!(!merged.contains(&1)); diff --git a/crates/ruvllm/src/reasoning_bank/distillation.rs b/crates/ruvllm/src/reasoning_bank/distillation.rs index d0f742b56..5bdcd3dee 100644 --- a/crates/ruvllm/src/reasoning_bank/distillation.rs +++ b/crates/ruvllm/src/reasoning_bank/distillation.rs @@ -8,7 +8,7 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use super::{Trajectory, Verdict, PatternCategory}; +use super::{PatternCategory, Trajectory, Verdict}; /// Configuration for memory distillation #[derive(Debug, Clone, Serialize, Deserialize)] @@ -71,7 +71,8 @@ pub struct CompressedTrajectory { impl CompressedTrajectory { /// Create from a trajectory pub fn from_trajectory(trajectory: &Trajectory) -> Self { - let action_summary: Vec = trajectory.steps + let action_summary: Vec = trajectory + .steps .iter() .filter(|s| s.outcome.is_success()) .take(5) @@ -96,7 +97,11 @@ impl CompressedTrajectory { pub fn estimated_size(&self) -> usize { std::mem::size_of::() + self.key_embedding.len() * std::mem::size_of::() - + self.preserved_lessons.iter().map(|s| s.len()).sum::() + + self + .preserved_lessons + .iter() + .map(|s| s.len()) + .sum::() + self.action_summary.iter().map(|s| s.len()).sum::() } } @@ -187,12 +192,8 @@ impl KeyLesson { let content1_lower = self.content.to_lowercase(); let content2_lower = other.content.to_lowercase(); - let words1: std::collections::HashSet<&str> = content1_lower - .split_whitespace() - .collect(); - let words2: std::collections::HashSet<&str> = content2_lower - .split_whitespace() - .collect(); + let words1: std::collections::HashSet<&str> = content1_lower.split_whitespace().collect(); + let words2: std::collections::HashSet<&str> = content2_lower.split_whitespace().collect(); let intersection = words1.intersection(&words2).count(); let union = words1.union(&words2).count(); @@ -210,7 +211,12 @@ impl KeyLesson { return 0.0; } - let dot: f32 = self.embedding.iter().zip(&other.embedding).map(|(a, b)| a * b).sum(); + let dot: f32 = self + .embedding + .iter() + .zip(&other.embedding) + .map(|(a, b)| a * b) + .sum(); let norm_a: f32 = self.embedding.iter().map(|x| x * x).sum::().sqrt(); let norm_b: f32 = other.embedding.iter().map(|x| x * x).sum::().sqrt(); @@ -327,10 +333,7 @@ impl MemoryDistiller { .iter() .map(|t| estimate_trajectory_size(t)) .sum(); - let compressed_size: usize = compressed - .iter() - .map(|c| c.estimated_size()) - .sum(); + let compressed_size: usize = compressed.iter().map(|c| c.estimated_size()).sum(); let memory_saved = original_size.saturating_sub(compressed_size); let compression_ratio = if original_size > 0 { @@ -366,7 +369,8 @@ impl MemoryDistiller { // Extract implicit lessons from successful patterns if trajectory.is_success() { - let action_pattern: String = trajectory.steps + let action_pattern: String = trajectory + .steps .iter() .filter(|s| s.outcome.is_success()) .take(3) @@ -390,7 +394,11 @@ impl MemoryDistiller { } // Extract lessons from recovered attempts - if let Verdict::RecoveredViaReflection { reflection_attempts, .. } = trajectory.verdict { + if let Verdict::RecoveredViaReflection { + reflection_attempts, + .. + } = trajectory.verdict + { let lesson_content = format!( "Recovery possible after {} attempts via reflection", reflection_attempts @@ -406,7 +414,9 @@ impl MemoryDistiller { lessons.sort_by(|a, b| { let score_a = a.importance * (a.observation_count as f32).ln_1p(); let score_b = b.importance * (b.observation_count as f32).ln_1p(); - score_b.partial_cmp(&score_a).unwrap_or(std::cmp::Ordering::Equal) + score_b + .partial_cmp(&score_a) + .unwrap_or(std::cmp::Ordering::Equal) }); lessons @@ -414,7 +424,8 @@ impl MemoryDistiller { /// Create a lesson from trajectory context fn create_lesson(&self, content: String, trajectory: &Trajectory) -> KeyLesson { - let example_actions: Vec = trajectory.steps + let example_actions: Vec = trajectory + .steps .iter() .filter(|s| s.outcome.is_success()) .take(3) @@ -465,7 +476,9 @@ impl MemoryDistiller { if let Some(most_similar) = deduplicated.iter_mut().max_by(|a, b| { let sim_a = lesson.content_similarity(a); let sim_b = lesson.content_similarity(b); - sim_a.partial_cmp(&sim_b).unwrap_or(std::cmp::Ordering::Equal) + sim_a + .partial_cmp(&sim_b) + .unwrap_or(std::cmp::Ordering::Equal) }) { most_similar.merge(&lesson); } @@ -476,7 +489,10 @@ impl MemoryDistiller { } /// Compress old trajectories - pub fn compress_old_trajectories(&self, trajectories: &[Trajectory]) -> Vec { + pub fn compress_old_trajectories( + &self, + trajectories: &[Trajectory], + ) -> Vec { let now = Utc::now(); let min_age = chrono::Duration::seconds(self.config.min_age_for_distillation_secs as i64); @@ -606,17 +622,22 @@ fn infer_category(trajectory: &Trajectory) -> PatternCategory { fn estimate_trajectory_size(trajectory: &Trajectory) -> usize { let base_size = std::mem::size_of::(); let embedding_size = trajectory.query_embedding.len() * std::mem::size_of::(); - let response_embedding_size = trajectory.response_embedding + let response_embedding_size = trajectory + .response_embedding .as_ref() .map(|e| e.len() * std::mem::size_of::()) .unwrap_or(0); - let steps_size: usize = trajectory.steps + let steps_size: usize = trajectory + .steps .iter() .map(|s| { std::mem::size_of_val(s) + s.action.len() + s.rationale.len() - + s.context_embedding.as_ref().map(|e| e.len() * 4).unwrap_or(0) + + s.context_embedding + .as_ref() + .map(|e| e.len() * 4) + .unwrap_or(0) }) .sum(); let lessons_size: usize = trajectory.lessons.iter().map(|l| l.len()).sum(); @@ -626,8 +647,8 @@ fn estimate_trajectory_size(trajectory: &Trajectory) -> usize { #[cfg(test)] mod tests { + use super::super::trajectory::{StepOutcome, TrajectoryRecorder}; use super::*; - use super::super::trajectory::{TrajectoryRecorder, StepOutcome}; fn make_trajectory(id: u64, quality: f32) -> Trajectory { let mut recorder = TrajectoryRecorder::new(vec![0.1; 64]); @@ -648,7 +669,9 @@ mod tests { let mut trajectory = recorder.complete(if quality > 0.5 { Verdict::Success } else { - Verdict::Partial { completion_ratio: quality } + Verdict::Partial { + completion_ratio: quality, + } }); // Override the auto-generated ID @@ -751,9 +774,7 @@ mod tests { let distiller = MemoryDistiller::new(config); // Create test trajectories - let trajectories: Vec = (0..10) - .map(|i| make_trajectory(i, 0.7)) - .collect(); + let trajectories: Vec = (0..10).map(|i| make_trajectory(i, 0.7)).collect(); let result = distiller.extract_key_lessons(&trajectories).unwrap(); @@ -770,9 +791,7 @@ mod tests { }; let distiller = MemoryDistiller::new(config); - let trajectories: Vec = (0..10) - .map(|i| make_trajectory(i, 0.7)) - .collect(); + let trajectories: Vec = (0..10).map(|i| make_trajectory(i, 0.7)).collect(); let result = distiller.extract_key_lessons(&trajectories); assert!(result.is_err()); @@ -804,9 +823,21 @@ mod tests { let distiller = MemoryDistiller::new(config); let lessons = vec![ - KeyLesson::new("Test lesson one".to_string(), vec![1.0, 0.0], PatternCategory::General), - KeyLesson::new("Test lesson one".to_string(), vec![1.0, 0.0], PatternCategory::General), - KeyLesson::new("Different lesson".to_string(), vec![0.0, 1.0], PatternCategory::General), + KeyLesson::new( + "Test lesson one".to_string(), + vec![1.0, 0.0], + PatternCategory::General, + ), + KeyLesson::new( + "Test lesson one".to_string(), + vec![1.0, 0.0], + PatternCategory::General, + ), + KeyLesson::new( + "Different lesson".to_string(), + vec![0.0, 1.0], + PatternCategory::General, + ), ]; let deduped = distiller.deduplicate_lessons(lessons); diff --git a/crates/ruvllm/src/reasoning_bank/mod.rs b/crates/ruvllm/src/reasoning_bank/mod.rs index b8090c2ca..dcb7c8e29 100644 --- a/crates/ruvllm/src/reasoning_bank/mod.rs +++ b/crates/ruvllm/src/reasoning_bank/mod.rs @@ -64,38 +64,34 @@ //! bank.consolidate()?; //! ``` -pub mod trajectory; -pub mod pattern_store; -pub mod verdicts; pub mod consolidation; pub mod distillation; +pub mod pattern_store; +pub mod trajectory; +pub mod verdicts; // Re-exports for convenience -pub use trajectory::{ - Trajectory, TrajectoryStep, TrajectoryRecorder, TrajectoryId, - TrajectoryMetadata, StepOutcome, -}; -pub use pattern_store::{ - PatternStore, Pattern, PatternCategory, PatternStoreConfig, - PatternSearchResult, PatternStats, -}; -pub use verdicts::{ - Verdict, RootCause, VerdictAnalyzer, VerdictAnalysis, - FailurePattern, RecoveryStrategy, -}; pub use consolidation::{ - PatternConsolidator, ConsolidationConfig, ConsolidationResult, - FisherInformation, ImportanceScore, + ConsolidationConfig, ConsolidationResult, FisherInformation, ImportanceScore, + PatternConsolidator, }; pub use distillation::{ - MemoryDistiller, DistillationConfig, DistillationResult, - CompressedTrajectory, KeyLesson, + CompressedTrajectory, DistillationConfig, DistillationResult, KeyLesson, MemoryDistiller, +}; +pub use pattern_store::{ + Pattern, PatternCategory, PatternSearchResult, PatternStats, PatternStore, PatternStoreConfig, +}; +pub use trajectory::{ + StepOutcome, Trajectory, TrajectoryId, TrajectoryMetadata, TrajectoryRecorder, TrajectoryStep, +}; +pub use verdicts::{ + FailurePattern, RecoveryStrategy, RootCause, Verdict, VerdictAnalysis, VerdictAnalyzer, }; use crate::error::Result; +use parking_lot::RwLock; use serde::{Deserialize, Serialize}; use std::sync::Arc; -use parking_lot::RwLock; /// Configuration for the ReasoningBank #[derive(Debug, Clone, Serialize, Deserialize)] @@ -235,8 +231,7 @@ impl ReasoningBank { // Update rolling average quality let n = stats.total_trajectories as f32; - stats.avg_quality = stats.avg_quality * ((n - 1.0) / n) - + trajectory.quality / n; + stats.avg_quality = stats.avg_quality * ((n - 1.0) / n) + trajectory.quality / n; } // Store trajectory diff --git a/crates/ruvllm/src/reasoning_bank/pattern_store.rs b/crates/ruvllm/src/reasoning_bank/pattern_store.rs index 1e761cb1b..608f35162 100644 --- a/crates/ruvllm/src/reasoning_bank/pattern_store.rs +++ b/crates/ruvllm/src/reasoning_bank/pattern_store.rs @@ -6,14 +6,14 @@ use crate::error::{Result, RuvLLMError}; use chrono::{DateTime, Utc}; use parking_lot::RwLock; -use ruvector_core::{DistanceMetric, VectorDB, VectorEntry, SearchQuery}; use ruvector_core::types::{DbOptions, HnswConfig}; +use ruvector_core::{DistanceMetric, SearchQuery, VectorDB, VectorEntry}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::sync::atomic::{AtomicU64, Ordering}; use uuid::Uuid; -use super::{Trajectory, KeyLesson, Verdict}; +use super::{KeyLesson, Trajectory, Verdict}; /// Global pattern ID counter static PATTERN_COUNTER: AtomicU64 = AtomicU64::new(0); @@ -213,7 +213,8 @@ impl Pattern { pub fn from_trajectory(trajectory: &Trajectory) -> Self { let category = Self::infer_category(trajectory); - let example_actions: Vec = trajectory.steps + let example_actions: Vec = trajectory + .steps .iter() .take(5) .map(|s| s.action.clone()) @@ -353,7 +354,8 @@ impl Pattern { self.confidence = self.confidence.max(other.confidence); // Merge collections - self.source_trajectories.extend(other.source_trajectories.clone()); + self.source_trajectories + .extend(other.source_trajectories.clone()); for lesson in &other.lessons { if !self.lessons.contains(lesson) { self.lessons.push(lesson.clone()); @@ -506,7 +508,8 @@ impl PatternStore { metadata: None, }; let index = self.index.write(); - index.insert(entry) + index + .insert(entry) .map_err(|e| RuvLLMError::Storage(format!("Failed to insert into index: {}", e)))?; } @@ -537,11 +540,7 @@ impl PatternStore { } /// Search for similar patterns - pub fn search_similar( - &self, - query: &[f32], - limit: usize, - ) -> Result> { + pub fn search_similar(&self, query: &[f32], limit: usize) -> Result> { let start = std::time::Instant::now(); // Search HNSW index @@ -553,7 +552,8 @@ impl PatternStore { ef_search: Some(self.config.ef_search), }; let index = self.index.read(); - index.search(search_query) + index + .search(search_query) .map_err(|e| RuvLLMError::Storage(format!("Search failed: {}", e)))? }; @@ -576,17 +576,14 @@ impl PatternStore { // Update search stats let elapsed_us = start.elapsed().as_micros() as u64; self.search_count.fetch_add(1, Ordering::Relaxed); - self.total_search_time_us.fetch_add(elapsed_us, Ordering::Relaxed); + self.total_search_time_us + .fetch_add(elapsed_us, Ordering::Relaxed); Ok(search_results) } /// Get patterns by category - pub fn get_by_category( - &self, - category: PatternCategory, - limit: usize, - ) -> Result> { + pub fn get_by_category(&self, category: PatternCategory, limit: usize) -> Result> { let cat_index = self.category_index.read(); let patterns = self.patterns.read(); @@ -599,7 +596,11 @@ impl PatternStore { .collect(); // Sort by confidence descending - result.sort_by(|a, b| b.confidence.partial_cmp(&a.confidence).unwrap_or(std::cmp::Ordering::Equal)); + result.sort_by(|a, b| { + b.confidence + .partial_cmp(&a.confidence) + .unwrap_or(std::cmp::Ordering::Equal) + }); Ok(result) } @@ -655,7 +656,9 @@ impl PatternStore { let patterns = self.patterns.read(); patterns .iter() - .filter(|(_, p)| p.avg_quality < min_quality && p.usage_count < self.config.prune_threshold) + .filter(|(_, p)| { + p.avg_quality < min_quality && p.usage_count < self.config.prune_threshold + }) .map(|(id, _)| *id) .collect() }; @@ -674,17 +677,23 @@ impl PatternStore { let patterns = self.patterns.read(); let mut sorted: Vec<_> = patterns .iter() - .filter(|(_, p)| p.should_prune( - self.config.prune_threshold, - self.config.max_unused_age_secs, - self.config.min_confidence, - )) + .filter(|(_, p)| { + p.should_prune( + self.config.prune_threshold, + self.config.max_unused_age_secs, + self.config.min_confidence, + ) + }) .collect(); sorted.sort_by(|a, b| a.1.last_accessed.cmp(&b.1.last_accessed)); let remove_count = sorted.len().min(self.config.max_patterns / 10); - sorted.into_iter().take(remove_count).map(|(id, _)| *id).collect() + sorted + .into_iter() + .take(remove_count) + .map(|(id, _)| *id) + .collect() }; let count = to_remove.len(); @@ -788,11 +797,7 @@ mod tests { #[test] fn test_pattern_creation() { - let pattern = Pattern::new( - vec![0.1; 768], - PatternCategory::Reasoning, - 0.9, - ); + let pattern = Pattern::new(vec![0.1; 768], PatternCategory::Reasoning, 0.9); assert!(pattern.id > 0 || pattern.id == 0); // First pattern might be 0 assert_eq!(pattern.category, PatternCategory::Reasoning); @@ -859,7 +864,9 @@ mod tests { assert_eq!(results[0].pattern.id, id); // Get by category - let by_cat = store.get_by_category(PatternCategory::Reasoning, 10).unwrap(); + let by_cat = store + .get_by_category(PatternCategory::Reasoning, 10) + .unwrap(); assert!(!by_cat.is_empty()); // Stats @@ -870,7 +877,10 @@ mod tests { #[test] fn test_pattern_category() { assert_eq!(PatternCategory::General.to_string(), "general"); - assert_eq!(PatternCategory::CodeGeneration.to_string(), "code_generation"); + assert_eq!( + PatternCategory::CodeGeneration.to_string(), + "code_generation" + ); assert_eq!( PatternCategory::Custom("test".to_string()).to_string(), "custom:test" diff --git a/crates/ruvllm/src/reasoning_bank/trajectory.rs b/crates/ruvllm/src/reasoning_bank/trajectory.rs index 721f9d8ed..bfb7f0a3b 100644 --- a/crates/ruvllm/src/reasoning_bank/trajectory.rs +++ b/crates/ruvllm/src/reasoning_bank/trajectory.rs @@ -248,7 +248,9 @@ impl Trajectory { query_embedding, response_embedding: None, steps: Vec::new(), - verdict: Verdict::Partial { completion_ratio: 0.0 }, + verdict: Verdict::Partial { + completion_ratio: 0.0, + }, quality: 0.0, total_latency_ms: 0, started_at: now, @@ -401,17 +403,13 @@ impl TrajectoryRecorder { outcome: StepOutcome, confidence: f32, ) { - let latency_ms = self.step_start + let latency_ms = self + .step_start .map(|start| start.elapsed().as_millis() as u64) .unwrap_or(0); - let step = TrajectoryStep::new( - self.current_step, - action, - rationale, - outcome, - confidence, - ).with_latency(latency_ms); + let step = TrajectoryStep::new(self.current_step, action, rationale, outcome, confidence) + .with_latency(latency_ms); self.trajectory.add_step(step); self.current_step += 1; @@ -506,7 +504,13 @@ mod tests { #[test] fn test_step_outcome_quality() { assert_eq!(StepOutcome::Success.quality_score(), 1.0); - assert_eq!(StepOutcome::Failure { error: "test".into() }.quality_score(), 0.0); + assert_eq!( + StepOutcome::Failure { + error: "test".into() + } + .quality_score(), + 0.0 + ); } #[test] @@ -574,11 +578,15 @@ mod tests { 1, "step2".to_string(), "rationale2".to_string(), - StepOutcome::Failure { error: "test".to_string() }, + StepOutcome::Failure { + error: "test".to_string(), + }, 0.5, )); - trajectory.complete(Verdict::Partial { completion_ratio: 0.5 }); + trajectory.complete(Verdict::Partial { + completion_ratio: 0.5, + }); // Quality should reflect the mix of success/failure assert!(trajectory.quality < 1.0); @@ -604,11 +612,15 @@ mod tests { recorder.add_step( "step3".to_string(), "r3".to_string(), - StepOutcome::Failure { error: "e".to_string() }, + StepOutcome::Failure { + error: "e".to_string(), + }, 0.7, ); - let trajectory = recorder.complete(Verdict::Partial { completion_ratio: 0.67 }); + let trajectory = recorder.complete(Verdict::Partial { + completion_ratio: 0.67, + }); assert_eq!(trajectory.step_count(), 3); assert!((trajectory.step_success_rate() - 0.666).abs() < 0.01); diff --git a/crates/ruvllm/src/reasoning_bank/verdicts.rs b/crates/ruvllm/src/reasoning_bank/verdicts.rs index 8d279ea4d..e4bada635 100644 --- a/crates/ruvllm/src/reasoning_bank/verdicts.rs +++ b/crates/ruvllm/src/reasoning_bank/verdicts.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use super::{Trajectory, StepOutcome, PatternCategory}; +use super::{PatternCategory, StepOutcome, Trajectory}; /// Verdict for a trajectory execution #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -33,7 +33,9 @@ pub enum Verdict { impl Default for Verdict { fn default() -> Self { - Self::Partial { completion_ratio: 0.0 } + Self::Partial { + completion_ratio: 0.0, + } } } @@ -93,9 +95,19 @@ impl Verdict { match self { Self::Success => "Success".to_string(), Self::Failure(cause) => format!("Failure: {}", cause), - Self::Partial { completion_ratio } => format!("Partial: {:.0}% complete", completion_ratio * 100.0), - Self::RecoveredViaReflection { reflection_attempts, final_quality, .. } => { - format!("Recovered after {} attempts, quality {:.0}%", reflection_attempts, final_quality * 100.0) + Self::Partial { completion_ratio } => { + format!("Partial: {:.0}% complete", completion_ratio * 100.0) + } + Self::RecoveredViaReflection { + reflection_attempts, + final_quality, + .. + } => { + format!( + "Recovered after {} attempts, quality {:.0}%", + reflection_attempts, + final_quality * 100.0 + ) } } } @@ -162,7 +174,10 @@ impl std::fmt::Display for RootCause { } Self::InvalidInput { details } => write!(f, "Invalid input: {}", details), Self::ToolFailure { tool, error } => write!(f, "Tool '{}' failed: {}", tool, error), - Self::ReasoningError { error_type, description } => { + Self::ReasoningError { + error_type, + description, + } => { write!(f, "Reasoning error ({}): {}", error_type, description) } Self::ResourceConstraint { resource, limit } => { @@ -389,7 +404,10 @@ impl VerdictAnalyzer { RecoveryStrategy { name: "Clarification Loop".to_string(), description: "Ask clarifying questions to gather missing context".to_string(), - applicable_causes: vec!["InsufficientContext".to_string(), "InvalidInput".to_string()], + applicable_causes: vec![ + "InsufficientContext".to_string(), + "InvalidInput".to_string(), + ], success_rate: 0.75, actions: vec![ "Identify what information is missing".to_string(), @@ -402,7 +420,10 @@ impl VerdictAnalyzer { RecoveryStrategy { name: "Decomposition".to_string(), description: "Break the problem into smaller, manageable parts".to_string(), - applicable_causes: vec!["ReasoningError".to_string(), "CapabilityLimit".to_string()], + applicable_causes: vec![ + "ReasoningError".to_string(), + "CapabilityLimit".to_string(), + ], success_rate: 0.70, actions: vec![ "Identify sub-problems".to_string(), @@ -448,7 +469,11 @@ impl VerdictAnalyzer { // Extract root cause from verdict if let Verdict::Failure(ref cause) = trajectory.verdict { analysis.root_cause = Some(cause.clone()); - } else if let Verdict::RecoveredViaReflection { ref original_failure, .. } = trajectory.verdict { + } else if let Verdict::RecoveredViaReflection { + ref original_failure, + .. + } = trajectory.verdict + { analysis.root_cause = Some((**original_failure).clone()); } @@ -483,7 +508,8 @@ impl VerdictAnalyzer { let mut factors = Vec::new(); // Check step outcomes - let failure_count = trajectory.steps + let failure_count = trajectory + .steps .iter() .filter(|s| s.outcome.is_failure()) .count(); @@ -493,7 +519,8 @@ impl VerdictAnalyzer { } // Check confidence levels - let low_confidence = trajectory.steps + let low_confidence = trajectory + .steps .iter() .filter(|s| s.confidence < 0.5) .count(); @@ -525,7 +552,11 @@ impl VerdictAnalyzer { if step.outcome.is_failure() { // Check each known pattern for (_, pattern) in &self.known_patterns { - if pattern.associated_actions.iter().any(|a| step.action.contains(a)) { + if pattern + .associated_actions + .iter() + .any(|a| step.action.contains(a)) + { matched.push(pattern.clone()); } } @@ -570,7 +601,8 @@ impl VerdictAnalyzer { // Add automatic lessons based on analysis if trajectory.is_success() { // Learn from success - let successful_actions: Vec<_> = trajectory.steps + let successful_actions: Vec<_> = trajectory + .steps .iter() .filter(|s| s.outcome.is_success()) .map(|s| &s.action) @@ -579,7 +611,12 @@ impl VerdictAnalyzer { if !successful_actions.is_empty() { lessons.push(format!( "Successful pattern: {}", - successful_actions.iter().take(3).map(|s| s.as_str()).collect::>().join(" -> ") + successful_actions + .iter() + .take(3) + .map(|s| s.as_str()) + .collect::>() + .join(" -> ") )); } } else { @@ -592,7 +629,11 @@ impl VerdictAnalyzer { } // Add recovery lessons - if let Verdict::RecoveredViaReflection { reflection_attempts, .. } = &trajectory.verdict { + if let Verdict::RecoveredViaReflection { + reflection_attempts, + .. + } = &trajectory.verdict + { lessons.push(format!( "Recovery possible with {} reflection attempts", reflection_attempts @@ -614,13 +655,22 @@ impl VerdictAnalyzer { // Check actions let actions: Vec<_> = trajectory.steps.iter().map(|s| s.action.as_str()).collect(); - if actions.iter().any(|a| a.contains("code") || a.contains("implement")) { + if actions + .iter() + .any(|a| a.contains("code") || a.contains("implement")) + { return PatternCategory::CodeGeneration; } - if actions.iter().any(|a| a.contains("search") || a.contains("research")) { + if actions + .iter() + .any(|a| a.contains("search") || a.contains("research")) + { return PatternCategory::Research; } - if actions.iter().any(|a| a.contains("tool") || a.contains("execute")) { + if actions + .iter() + .any(|a| a.contains("tool") || a.contains("execute")) + { return PatternCategory::ToolUse; } @@ -645,11 +695,16 @@ impl VerdictAnalyzer { } /// Suggest improvements - fn suggest_improvements(&self, trajectory: &Trajectory, _analysis: &VerdictAnalysis) -> Vec { + fn suggest_improvements( + &self, + trajectory: &Trajectory, + _analysis: &VerdictAnalysis, + ) -> Vec { let mut improvements = Vec::new(); // Check for low confidence steps - let low_confidence_steps: Vec<_> = trajectory.steps + let low_confidence_steps: Vec<_> = trajectory + .steps .iter() .filter(|s| s.confidence < 0.6) .collect(); @@ -658,12 +713,18 @@ impl VerdictAnalyzer { improvements.push(format!( "Improve confidence in {} steps: {}", low_confidence_steps.len(), - low_confidence_steps.iter().take(3).map(|s| s.action.as_str()).collect::>().join(", ") + low_confidence_steps + .iter() + .take(3) + .map(|s| s.action.as_str()) + .collect::>() + .join(", ") )); } // Check for missing verification - let has_verification = trajectory.steps + let has_verification = trajectory + .steps .iter() .any(|s| s.action.contains("verify") || s.action.contains("check")); @@ -672,12 +733,14 @@ impl VerdictAnalyzer { } // Check for error handling - let has_error_handling = trajectory.steps + let has_error_handling = trajectory + .steps .iter() .any(|s| matches!(s.outcome, StepOutcome::NeedsRetry { .. })); if !has_error_handling && trajectory.is_failure() { - improvements.push("Consider implementing retry logic for recoverable errors".to_string()); + improvements + .push("Consider implementing retry logic for recoverable errors".to_string()); } improvements @@ -722,20 +785,26 @@ pub struct VerdictAnalyzerStats { #[cfg(test)] mod tests { + use super::super::trajectory::{StepOutcome, TrajectoryRecorder}; use super::*; - use super::super::trajectory::{TrajectoryRecorder, StepOutcome}; #[test] fn test_verdict_creation() { assert!(Verdict::success().is_success()); - assert!(Verdict::failure(RootCause::Unknown { details: "test".into() }).is_failure()); + assert!(Verdict::failure(RootCause::Unknown { + details: "test".into() + }) + .is_failure()); assert!(!Verdict::partial(0.5).is_success()); } #[test] fn test_verdict_quality_score() { assert_eq!(Verdict::success().quality_score(), 1.0); - assert_eq!(Verdict::failure(RootCause::Unknown { details: "".into() }).quality_score(), 0.0); + assert_eq!( + Verdict::failure(RootCause::Unknown { details: "".into() }).quality_score(), + 0.0 + ); assert!(Verdict::partial(0.5).quality_score() > 0.0); } @@ -771,7 +840,9 @@ mod tests { recorder.add_step( "execute".to_string(), "executing".to_string(), - StepOutcome::Failure { error: "permission denied".to_string() }, + StepOutcome::Failure { + error: "permission denied".to_string(), + }, 0.6, ); diff --git a/crates/ruvllm/src/reflection/confidence.rs b/crates/ruvllm/src/reflection/confidence.rs index c98defb6b..1cb24ac22 100644 --- a/crates/ruvllm/src/reflection/confidence.rs +++ b/crates/ruvllm/src/reflection/confidence.rs @@ -334,9 +334,7 @@ impl ConfidenceChecker { // Check for incomplete markers let incomplete_markers = ["TODO", "FIXME", "...", "to be continued", "incomplete"]; - let has_incomplete = incomplete_markers - .iter() - .any(|m| output.contains(m)); + let has_incomplete = incomplete_markers.iter().any(|m| output.contains(m)); if has_incomplete { score -= 0.2; } @@ -373,7 +371,8 @@ impl ConfidenceChecker { } // Check for lists - let has_lists = output.contains("\n- ") || output.contains("\n* ") || output.contains("\n1."); + let has_lists = + output.contains("\n- ") || output.contains("\n* ") || output.contains("\n1."); if has_lists { score += 0.1; } @@ -440,8 +439,11 @@ impl ConfidenceChecker { /// Assess code validity (basic heuristics) fn assess_code_validity(&self, output: &str) -> f32 { // Check if output contains code - let has_code = output.contains("```") || output.contains("fn ") || output.contains("def ") - || output.contains("function ") || output.contains("class "); + let has_code = output.contains("```") + || output.contains("fn ") + || output.contains("def ") + || output.contains("function ") + || output.contains("class "); if !has_code { return 0.8; // Not code-related, give neutral score @@ -498,7 +500,10 @@ impl ConfidenceChecker { 0.6, WeaknessType::Uncertainty, ) - .with_suggestion(format!("Remove or clarify the uncertain statement at '{}'", pattern)), + .with_suggestion(format!( + "Remove or clarify the uncertain statement at '{}'", + pattern + )), ); } } @@ -573,16 +578,14 @@ impl ConfidenceChecker { } /// Generate a targeted revision based on weak points - pub fn generate_targeted_revision( - &self, - output: &str, - weak_points: &[WeakPoint], - ) -> String { + pub fn generate_targeted_revision(&self, output: &str, weak_points: &[WeakPoint]) -> String { if weak_points.is_empty() { return output.to_string(); } - let mut revision_prompt = String::from("Please revise the following output to address these specific issues:\n\n"); + let mut revision_prompt = String::from( + "Please revise the following output to address these specific issues:\n\n", + ); for (i, wp) in weak_points.iter().enumerate() { revision_prompt.push_str(&format!( @@ -602,17 +605,30 @@ impl ConfidenceChecker { } /// Record a confidence check for learning - pub fn record_check(&mut self, output: &str, context: &ExecutionContext) -> ConfidenceCheckRecord { + pub fn record_check( + &mut self, + output: &str, + context: &ExecutionContext, + ) -> ConfidenceCheckRecord { let score = self.compute_confidence(output, context); let level = ConfidenceLevel::from_score(score); let weak_points = self.identify_weak_points(output, context); let mut factors = HashMap::new(); - factors.insert("completeness".to_string(), self.assess_completeness(output, context)); + factors.insert( + "completeness".to_string(), + self.assess_completeness(output, context), + ); factors.insert("structure".to_string(), self.assess_structure(output)); factors.insert("certainty".to_string(), self.assess_certainty(output)); - factors.insert("relevance".to_string(), self.assess_relevance(output, context)); - factors.insert("code_validity".to_string(), self.assess_code_validity(output)); + factors.insert( + "relevance".to_string(), + self.assess_relevance(output, context), + ); + factors.insert( + "code_validity".to_string(), + self.assess_code_validity(output), + ); let record = ConfidenceCheckRecord { score, @@ -632,7 +648,8 @@ impl ConfidenceChecker { /// Learn from a pattern that indicated low quality pub fn learn_pattern(&mut self, pattern: String, weight: f32) { - self.learned_patterns.insert(pattern, weight.clamp(0.0, 1.0)); + self.learned_patterns + .insert(pattern, weight.clamp(0.0, 1.0)); } /// Get check history @@ -710,7 +727,9 @@ mod tests { let weak_points = checker.identify_weak_points(output, &context); assert!(!weak_points.is_empty()); - assert!(weak_points.iter().any(|wp| matches!(wp.weakness_type, WeaknessType::Incomplete))); + assert!(weak_points + .iter() + .any(|wp| matches!(wp.weakness_type, WeaknessType::Incomplete))); } #[test] @@ -729,22 +748,29 @@ mod tests { // After exceeding budget, should not revise for _ in 0..3 { - context.previous_attempts.push(crate::reflection::reflective_agent::PreviousAttempt { - attempt_number: 1, - output: String::new(), - error: None, - quality_score: None, - duration_ms: 0, - reflection: None, - }); + context + .previous_attempts + .push(crate::reflection::reflective_agent::PreviousAttempt { + attempt_number: 1, + output: String::new(), + error: None, + quality_score: None, + duration_ms: 0, + reflection: None, + }); } assert!(!checker.should_revise(low_conf_output, &context)); } #[test] fn test_weak_point_builder() { - let wp = WeakPoint::new("line 5", "Missing error handling", 0.7, WeaknessType::MissingErrorHandling) - .with_suggestion("Add Result return type"); + let wp = WeakPoint::new( + "line 5", + "Missing error handling", + 0.7, + WeaknessType::MissingErrorHandling, + ) + .with_suggestion("Add Result return type"); assert_eq!(wp.location, "line 5"); assert!(!wp.suggestion.is_empty()); diff --git a/crates/ruvllm/src/reflection/error_recovery.rs b/crates/ruvllm/src/reflection/error_recovery.rs index 0a7dc0ae8..d17278bf0 100644 --- a/crates/ruvllm/src/reflection/error_recovery.rs +++ b/crates/ruvllm/src/reflection/error_recovery.rs @@ -131,20 +131,42 @@ impl ErrorPattern { fn extract_keywords(message: &str) -> Vec { // Common error keywords to look for let important_words = [ - "error", "failed", "invalid", "missing", "undefined", "null", - "type", "mismatch", "expected", "found", "cannot", "unable", - "permission", "denied", "timeout", "connection", "overflow", - "underflow", "bounds", "index", "panic", "unwrap", "option", - "result", "async", "await", "lifetime", "borrow", "move", + "error", + "failed", + "invalid", + "missing", + "undefined", + "null", + "type", + "mismatch", + "expected", + "found", + "cannot", + "unable", + "permission", + "denied", + "timeout", + "connection", + "overflow", + "underflow", + "bounds", + "index", + "panic", + "unwrap", + "option", + "result", + "async", + "await", + "lifetime", + "borrow", + "move", ]; message .to_lowercase() .split(|c: char| !c.is_alphanumeric()) .filter(|word| word.len() > 2) - .filter(|word| { - important_words.iter().any(|iw| word.contains(iw)) || word.len() > 5 - }) + .filter(|word| important_words.iter().any(|iw| word.contains(iw)) || word.len() > 5) .map(String::from) .take(10) .collect() @@ -161,7 +183,11 @@ impl ErrorPattern { let matching = self .keywords .iter() - .filter(|k| other_keywords.iter().any(|ok| ok.contains(k.as_str()) || k.contains(ok.as_str()))) + .filter(|k| { + other_keywords + .iter() + .any(|ok| ok.contains(k.as_str()) || k.contains(ok.as_str())) + }) .count(); let max_len = self.keywords.len().max(other_keywords.len()); @@ -771,10 +797,7 @@ impl ErrorPatternLearner { .filter(|(_, sim)| *sim > self.config.similarity_threshold * 0.5) // Lower threshold for suggestions .collect(); - similar.sort_by(|a, b| { - b.1.partial_cmp(&a.1) - .unwrap_or(std::cmp::Ordering::Equal) - }); + similar.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); similar.truncate(10); similar @@ -944,9 +967,15 @@ mod tests { #[test] fn test_error_pattern_creation() { - let pattern = ErrorPattern::new("type mismatch: expected i32, found String", ErrorCategory::TypeMismatch); + let pattern = ErrorPattern::new( + "type mismatch: expected i32, found String", + ErrorCategory::TypeMismatch, + ); assert!(!pattern.keywords.is_empty()); - assert!(pattern.keywords.iter().any(|k| k.contains("type") || k.contains("mismatch"))); + assert!(pattern + .keywords + .iter() + .any(|k| k.contains("type") || k.contains("mismatch"))); } #[test] diff --git a/crates/ruvllm/src/reflection/mod.rs b/crates/ruvllm/src/reflection/mod.rs index 0291de613..b38f21f6b 100644 --- a/crates/ruvllm/src/reflection/mod.rs +++ b/crates/ruvllm/src/reflection/mod.rs @@ -93,7 +93,7 @@ mod reflective_agent; // Re-export all public types pub use confidence::{ - ConfidenceChecker, ConfidenceCheckRecord, ConfidenceConfig, ConfidenceFactorWeights, + ConfidenceCheckRecord, ConfidenceChecker, ConfidenceConfig, ConfidenceFactorWeights, ConfidenceLevel, RevisionResult, WeakPoint, WeaknessType, }; pub use error_recovery::{ diff --git a/crates/ruvllm/src/reflection/perspectives.rs b/crates/ruvllm/src/reflection/perspectives.rs index 2f4effdb5..6798a129a 100644 --- a/crates/ruvllm/src/reflection/perspectives.rs +++ b/crates/ruvllm/src/reflection/perspectives.rs @@ -134,11 +134,7 @@ pub struct CritiqueIssue { impl CritiqueIssue { /// Create a new critique issue - pub fn new( - description: impl Into, - severity: f32, - category: IssueCategory, - ) -> Self { + pub fn new(description: impl Into, severity: f32, category: IssueCategory) -> Self { Self { severity: severity.clamp(0.0, 1.0), description: description.into(), @@ -232,11 +228,27 @@ impl CorrectnessChecker { ("error[", "Compiler error present", IssueCategory::Syntax), ("Error:", "Runtime error present", IssueCategory::Logic), ("panic!", "Panic in code", IssueCategory::Logic), - ("unwrap()", "Potential panic from unwrap", IssueCategory::Logic), - ("expect()", "Potential panic from expect", IssueCategory::Logic), + ( + "unwrap()", + "Potential panic from unwrap", + IssueCategory::Logic, + ), + ( + "expect()", + "Potential panic from expect", + IssueCategory::Logic, + ), ("todo!()", "Unimplemented todo", IssueCategory::Missing), - ("unimplemented!()", "Unimplemented code", IssueCategory::Missing), - ("unreachable!()", "Unreachable code marker", IssueCategory::Logic), + ( + "unimplemented!()", + "Unimplemented code", + IssueCategory::Missing, + ), + ( + "unreachable!()", + "Unreachable code marker", + IssueCategory::Logic, + ), ]; for (pattern, description, category) in error_patterns { @@ -245,7 +257,11 @@ impl CorrectnessChecker { issues.push( CritiqueIssue::new( format!("{} ({} occurrence(s))", description, count), - if category == IssueCategory::Logic { 0.8 } else { 0.5 }, + if category == IssueCategory::Logic { + 0.8 + } else { + 0.5 + }, category, ) .suggest(format!("Address or remove {}", pattern)), @@ -259,7 +275,10 @@ impl CorrectnessChecker { if open_parens != close_parens { issues.push( CritiqueIssue::new( - format!("Unbalanced parentheses: {} open, {} close", open_parens, close_parens), + format!( + "Unbalanced parentheses: {} open, {} close", + open_parens, close_parens + ), 0.7, IssueCategory::Syntax, ) @@ -272,7 +291,10 @@ impl CorrectnessChecker { if open_braces != close_braces { issues.push( CritiqueIssue::new( - format!("Unbalanced braces: {} open, {} close", open_braces, close_braces), + format!( + "Unbalanced braces: {} open, {} close", + open_braces, close_braces + ), 0.7, IssueCategory::Syntax, ) @@ -305,12 +327,8 @@ impl CorrectnessChecker { // Simple heuristic: function with just {} if output.contains("{ }") || output.contains("{}") { issues.push( - CritiqueIssue::new( - "Empty function body detected", - 0.4, - IssueCategory::Missing, - ) - .suggest("Implement function body or add todo!()"), + CritiqueIssue::new("Empty function body detected", 0.4, IssueCategory::Missing) + .suggest("Implement function body or add todo!()"), ); } } @@ -318,12 +336,8 @@ impl CorrectnessChecker { // Check for hardcoded values that might be problematic if output.contains("localhost") || output.contains("127.0.0.1") { issues.push( - CritiqueIssue::new( - "Hardcoded localhost/IP address", - 0.3, - IssueCategory::Style, - ) - .suggest("Consider using configuration or environment variables"), + CritiqueIssue::new("Hardcoded localhost/IP address", 0.3, IssueCategory::Style) + .suggest("Consider using configuration or environment variables"), ); } @@ -346,8 +360,9 @@ impl Perspective for CorrectnessChecker { let start = std::time::Instant::now(); if output.is_empty() { - return CritiqueResult::fail(self.name(), 0.0, "Empty output") - .with_issue(CritiqueIssue::new("No output provided", 1.0, IssueCategory::Missing)); + return CritiqueResult::fail(self.name(), 0.0, "Empty output").with_issue( + CritiqueIssue::new("No output provided", 1.0, IssueCategory::Missing), + ); } let mut issues = Vec::new(); @@ -381,10 +396,7 @@ impl Perspective for CorrectnessChecker { issues.iter().filter(|i| i.severity < 0.5).count() ) } else { - format!( - "Found {} issue(s) affecting correctness", - issues.len() - ) + format!("Found {} issue(s) affecting correctness", issues.len()) }; let mut result = if passed { @@ -435,8 +447,18 @@ impl CompletenessChecker { // Look for action verbs let action_words = [ - "implement", "create", "add", "build", "write", "define", - "include", "support", "handle", "return", "take", "accept", + "implement", + "create", + "add", + "build", + "write", + "define", + "include", + "support", + "handle", + "return", + "take", + "accept", ]; for word in action_words { @@ -471,9 +493,9 @@ impl CompletenessChecker { let req_lower = req.to_lowercase(); // Simple keyword matching for requirement fulfillment - let is_met = req_lower.split_whitespace().any(|word| { - word.len() > 3 && output_lower.contains(word) - }); + let is_met = req_lower + .split_whitespace() + .any(|word| word.len() > 3 && output_lower.contains(word)); if !is_met { issues.push( @@ -538,7 +560,11 @@ impl Perspective for CompletenessChecker { if output.is_empty() { return CritiqueResult::fail(self.name(), 0.0, "Empty output - nothing completed") - .with_issue(CritiqueIssue::new("No output provided", 1.0, IssueCategory::Missing)); + .with_issue(CritiqueIssue::new( + "No output provided", + 1.0, + IssueCategory::Missing, + )); } let mut issues = Vec::new(); @@ -721,7 +747,10 @@ impl ConsistencyChecker { let pub_count = output.matches("pub fn").count(); let priv_count = output.matches("fn ").count() - pub_count; - if pub_count > 0 && priv_count > 0 && (pub_count as f32 / (pub_count + priv_count) as f32) < 0.3 { + if pub_count > 0 + && priv_count > 0 + && (pub_count as f32 / (pub_count + priv_count) as f32) < 0.3 + { // This is actually fine, just noting it } @@ -744,8 +773,13 @@ impl Perspective for ConsistencyChecker { let start = std::time::Instant::now(); if output.is_empty() { - return CritiqueResult::fail(self.name(), 0.0, "Empty output") - .with_issue(CritiqueIssue::new("No output to check consistency", 1.0, IssueCategory::Missing)); + return CritiqueResult::fail(self.name(), 0.0, "Empty output").with_issue( + CritiqueIssue::new( + "No output to check consistency", + 1.0, + IssueCategory::Missing, + ), + ); } let mut issues = Vec::new(); @@ -761,7 +795,10 @@ impl Perspective for ConsistencyChecker { issues.extend(self.check_internal_consistency(output)); // Identify strengths - if !issues.iter().any(|i| i.category == IssueCategory::Inconsistent) { + if !issues + .iter() + .any(|i| i.category == IssueCategory::Inconsistent) + { strengths.push("Consistent coding style".to_string()); } if output.contains("use std::") || output.contains("use crate::") { @@ -906,8 +943,7 @@ mod tests { #[test] fn test_critique_result_builders() { - let pass = CritiqueResult::pass("test", 0.8, "Good job") - .with_strength("Clean code"); + let pass = CritiqueResult::pass("test", 0.8, "Good job").with_strength("Clean code"); assert!(pass.passed); assert!(!pass.strengths.is_empty()); @@ -979,7 +1015,10 @@ mod tests { let output = "fn example() { // TODO: implement }"; let result = checker.critique(output, &context); - assert!(result.issues.iter().any(|i| i.category == IssueCategory::Missing)); + assert!(result + .issues + .iter() + .any(|i| i.category == IssueCategory::Missing)); } #[test] @@ -1005,7 +1044,10 @@ mod tests { let output = "fn test() {\n line1\n line2\n\tline3\n}"; let result = checker.critique(output, &context); - assert!(result.issues.iter().any(|i| i.category == IssueCategory::Style)); + assert!(result + .issues + .iter() + .any(|i| i.category == IssueCategory::Style)); } #[test] diff --git a/crates/ruvllm/src/reflection/reflective_agent.rs b/crates/ruvllm/src/reflection/reflective_agent.rs index 84230a338..6e4779eb8 100644 --- a/crates/ruvllm/src/reflection/reflective_agent.rs +++ b/crates/ruvllm/src/reflection/reflective_agent.rs @@ -111,18 +111,28 @@ impl std::fmt::Debug for ReflectionStrategy { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Retry(config) => f.debug_tuple("Retry").field(config).finish(), - Self::IfOrElse { threshold, revision_budget, .. } => f + Self::IfOrElse { + threshold, + revision_budget, + .. + } => f .debug_struct("IfOrElse") .field("threshold", threshold) .field("revision_budget", revision_budget) .field("checker", &"") .finish(), - Self::MultiPerspective { min_agreement, perspectives } => f + Self::MultiPerspective { + min_agreement, + perspectives, + } => f .debug_struct("MultiPerspective") .field("min_agreement", min_agreement) .field("perspectives_count", &perspectives.len()) .finish(), - Self::TrajectoryReflection { window_size, use_sona } => f + Self::TrajectoryReflection { + window_size, + use_sona, + } => f .debug_struct("TrajectoryReflection") .field("window_size", window_size) .field("use_sona", use_sona) @@ -381,8 +391,9 @@ pub trait BaseAgent: Send + Sync { let output_lower = output.to_lowercase(); let not_error = !output_lower.contains("error") && !output_lower.contains("failed"); - let score = - (has_content as u8 as f32 * 0.3) + (has_structure as u8 as f32 * 0.3) + (not_error as u8 as f32 * 0.4); + let score = (has_content as u8 as f32 * 0.3) + + (has_structure as u8 as f32 * 0.3) + + (not_error as u8 as f32 * 0.4); score } } @@ -440,7 +451,11 @@ impl ReflectiveAgent { } /// Create with custom configuration - pub fn with_config(base_agent: A, strategy: ReflectionStrategy, config: ReflectionConfig) -> Self { + pub fn with_config( + base_agent: A, + strategy: ReflectionStrategy, + config: ReflectionConfig, + ) -> Self { let error_learner = ErrorPatternLearner::new(config.error_learner_config.clone()); let confidence_checker = ConfidenceChecker::new(config.confidence_config.clone()); @@ -455,7 +470,10 @@ impl ReflectiveAgent { } /// Execute with automatic reflection on failure or low confidence - pub fn execute_with_reflection(&mut self, context: &ExecutionContext) -> Result { + pub fn execute_with_reflection( + &mut self, + context: &ExecutionContext, + ) -> Result { let start = Instant::now(); let mut attempts = 0u32; let mut attempt_history = Vec::new(); @@ -555,12 +573,8 @@ impl ReflectiveAgent { }); // Update context with reflection - current_context = self.retry_with_context( - ¤t_context, - Some(&output), - None, - &reflection, - ); + current_context = + self.retry_with_context(¤t_context, Some(&output), None, &reflection); last_reflection = Some(reflection); } @@ -627,7 +641,10 @@ impl ReflectiveAgent { let attempts = context.previous_attempts.len() as u32; confidence < *threshold && attempts < *revision_budget } - ReflectionStrategy::MultiPerspective { min_agreement, perspectives } => { + ReflectionStrategy::MultiPerspective { + min_agreement, + perspectives, + } => { // Check agreement across perspectives if perspectives.is_empty() { return false; @@ -653,11 +670,7 @@ impl ReflectiveAgent { .take(*window_size) .filter_map(|a| a.quality_score) .sum::() - / context - .previous_attempts - .len() - .min(*window_size) - .max(1) as f32; + / context.previous_attempts.len().min(*window_size).max(1) as f32; recent_quality < self.config.min_quality_threshold } @@ -678,7 +691,8 @@ impl ReflectiveAgent { let mut r = Reflection::new("retry", "Retry with accumulated context"); if let Some(e) = error { r.insights.push(format!("Error encountered: {}", e)); - r.suggestions.push("Review error and adjust approach".to_string()); + r.suggestions + .push("Review error and adjust approach".to_string()); } if config.include_error_context && !context.previous_attempts.is_empty() { r.insights.push(format!( @@ -691,7 +705,9 @@ impl ReflectiveAgent { ReflectionStrategy::IfOrElse { threshold, .. } => { let confidence = self.base_agent.estimate_confidence(output, context); - let weak_points = self.confidence_checker.identify_weak_points(output, context); + let weak_points = self + .confidence_checker + .identify_weak_points(output, context); let mut r = Reflection::new( "if_or_else", @@ -784,15 +800,15 @@ impl ReflectiveAgent { if trend > 0.1 { r.insights.push("Quality improving".to_string()); } else if trend < -0.1 { - r.insights.push("Quality declining - consider strategy change".to_string()); + r.insights + .push("Quality declining - consider strategy change".to_string()); r.suggestions .push("Try different approach or break task down".to_string()); } } // Compute trajectory confidence - let avg_quality = - qualities.iter().sum::() / qualities.len().max(1) as f32; + let avg_quality = qualities.iter().sum::() / qualities.len().max(1) as f32; r.confidence = avg_quality; } @@ -943,9 +959,15 @@ mod tests { fn execute(&self, context: &ExecutionContext) -> Result { let count = self.fail_count.fetch_add(1, Ordering::SeqCst); if count < self.max_fails { - Err(RuvLLMError::InvalidOperation(format!("Simulated failure {}", count + 1))) + Err(RuvLLMError::InvalidOperation(format!( + "Simulated failure {}", + count + 1 + ))) } else { - Ok(format!("Success after {} failures for: {}", count, context.task)) + Ok(format!( + "Success after {} failures for: {}", + count, context.task + )) } } @@ -957,7 +979,8 @@ mod tests { #[test] fn test_reflective_agent_retry_success() { let base = TestAgent::new(2); // Fail twice then succeed - let mut agent = ReflectiveAgent::new(base, ReflectionStrategy::Retry(RetryConfig::default())); + let mut agent = + ReflectiveAgent::new(base, ReflectionStrategy::Retry(RetryConfig::default())); let context = ExecutionContext::new("test task", AgentType::Coder, "test input"); let result = agent.execute_with_reflection(&context).unwrap(); @@ -974,8 +997,11 @@ mod tests { max_reflection_attempts: 3, ..Default::default() }; - let mut agent = - ReflectiveAgent::with_config(base, ReflectionStrategy::Retry(RetryConfig::default()), config); + let mut agent = ReflectiveAgent::with_config( + base, + ReflectionStrategy::Retry(RetryConfig::default()), + config, + ); let context = ExecutionContext::new("test task", AgentType::Coder, "test input"); let result = agent.execute_with_reflection(&context).unwrap(); @@ -1021,7 +1047,10 @@ mod tests { 200, Reflection::new("retry", "context"), ); - assert!(matches!(recovered.verdict, Verdict::RecoveredViaReflection { .. })); + assert!(matches!( + recovered.verdict, + Verdict::RecoveredViaReflection { .. } + )); assert!(recovered.recovered_via_reflection); let failure = ExecutionResult::failure("error", 3, 300); @@ -1031,7 +1060,8 @@ mod tests { #[test] fn test_stats_tracking() { let base = TestAgent::new(1); - let mut agent = ReflectiveAgent::new(base, ReflectionStrategy::Retry(RetryConfig::default())); + let mut agent = + ReflectiveAgent::new(base, ReflectionStrategy::Retry(RetryConfig::default())); let context = ExecutionContext::new("test", AgentType::Coder, "input"); let _ = agent.execute_with_reflection(&context); diff --git a/crates/ruvllm/src/ruvector_integration.rs b/crates/ruvllm/src/ruvector_integration.rs index 3cf99854c..3c4c824d0 100644 --- a/crates/ruvllm/src/ruvector_integration.rs +++ b/crates/ruvllm/src/ruvector_integration.rs @@ -47,14 +47,12 @@ //! ``` use crate::capabilities::{ - RuvectorCapabilities, ATTENTION_AVAILABLE, GNN_AVAILABLE, GRAPH_AVAILABLE, - HNSW_AVAILABLE, SONA_AVAILABLE, + RuvectorCapabilities, ATTENTION_AVAILABLE, GNN_AVAILABLE, GRAPH_AVAILABLE, HNSW_AVAILABLE, + SONA_AVAILABLE, }; use crate::claude_flow::{AgentRouter, AgentType}; use crate::error::{Result, RuvLLMError}; -use crate::sona::{ - RoutingRecommendation, SonaConfig, SonaIntegration, SonaStats, Trajectory, -}; +use crate::sona::{RoutingRecommendation, SonaConfig, SonaIntegration, SonaStats, Trajectory}; use parking_lot::RwLock; use ruvector_core::index::hnsw::HnswIndex; use ruvector_core::index::VectorIndex; @@ -193,7 +191,9 @@ impl Clone for UnifiedIndexStats { total_vectors: AtomicU64::new(self.total_vectors.load(Ordering::Relaxed)), total_searches: AtomicU64::new(self.total_searches.load(Ordering::Relaxed)), successful_matches: AtomicU64::new(self.successful_matches.load(Ordering::Relaxed)), - avg_search_latency_us: AtomicU64::new(self.avg_search_latency_us.load(Ordering::Relaxed)), + avg_search_latency_us: AtomicU64::new( + self.avg_search_latency_us.load(Ordering::Relaxed), + ), patterns_learned: AtomicU64::new(self.patterns_learned.load(Ordering::Relaxed)), } } @@ -431,7 +431,9 @@ impl Clone for IntelligenceStats { Self { routing_decisions: AtomicU64::new(self.routing_decisions.load(Ordering::Relaxed)), successful_routings: AtomicU64::new(self.successful_routings.load(Ordering::Relaxed)), - pattern_based_routings: AtomicU64::new(self.pattern_based_routings.load(Ordering::Relaxed)), + pattern_based_routings: AtomicU64::new( + self.pattern_based_routings.load(Ordering::Relaxed), + ), learning_updates: AtomicU64::new(self.learning_updates.load(Ordering::Relaxed)), ewc_consolidations: AtomicU64::new(self.ewc_consolidations.load(Ordering::Relaxed)), } @@ -637,7 +639,9 @@ impl IntelligenceLayer { let id = format!("pattern-{}", uuid::Uuid::new_v4()); self.index.add(id, embedding.to_vec(), metadata)?; - self.stats.successful_routings.fetch_add(1, Ordering::SeqCst); + self.stats + .successful_routings + .fetch_add(1, Ordering::SeqCst); } Ok(()) @@ -993,7 +997,9 @@ mod tests { ..Default::default() }; - index.add("test-1".to_string(), embedding.clone(), metadata).unwrap(); + index + .add("test-1".to_string(), embedding.clone(), metadata) + .unwrap(); let results = index.search(&embedding, 5).unwrap(); assert_eq!(results.len(), 1); @@ -1072,10 +1078,7 @@ mod tests { ); // Simple tasks with high confidence should get tier 0 - assert_eq!( - IntelligenceLayer::determine_model_tier("fix typo", 0.9), - 0 - ); + assert_eq!(IntelligenceLayer::determine_model_tier("fix typo", 0.9), 0); // Default should be tier 1 assert_eq!( diff --git a/crates/ruvllm/src/serving/engine.rs b/crates/ruvllm/src/serving/engine.rs index b2abbbe70..bba6a968d 100644 --- a/crates/ruvllm/src/serving/engine.rs +++ b/crates/ruvllm/src/serving/engine.rs @@ -59,9 +59,9 @@ impl Default for ServingEngineConfig { coalesce_window_ms: 10, streaming_enabled: true, request_timeout_ms: 60000, - enable_speculative: true, // Enabled by default for 2-3x decode speedup + enable_speculative: true, // Enabled by default for 2-3x decode speedup speculative_config: SpeculativeConfig::default(), - draft_model_path: None, // Auto-detected based on main model size + draft_model_path: None, // Auto-detected based on main model size } } } @@ -151,10 +151,8 @@ impl ServingEngine { pub fn new(model: Arc, config: ServingEngineConfig) -> Self { use crate::optimization::realtime::RealtimeConfig; - let scheduler = ContinuousBatchScheduler::new( - config.scheduler.clone(), - config.kv_cache.clone(), - ); + let scheduler = + ContinuousBatchScheduler::new(config.scheduler.clone(), config.kv_cache.clone()); // Create realtime optimizer with speculative decoding enabled by default let realtime_config = RealtimeConfig { @@ -198,8 +196,7 @@ impl ServingEngine { // Check capacity { let queue = self.queue.lock(); - if queue.pending_count() + queue.running_count() - >= self.config.max_concurrent_requests + if queue.pending_count() + queue.running_count() >= self.config.max_concurrent_requests { return Err(RuvLLMError::OutOfMemory( "Maximum concurrent requests reached".to_string(), @@ -216,7 +213,9 @@ impl ServingEngine { completion_tx: None, created_at: Instant::now(), }; - self.pending_requests.write().insert(request_id, engine_request); + self.pending_requests + .write() + .insert(request_id, engine_request); } // Add to queue @@ -237,8 +236,7 @@ impl ServingEngine { // Check capacity { let queue = self.queue.lock(); - if queue.pending_count() + queue.running_count() - >= self.config.max_concurrent_requests + if queue.pending_count() + queue.running_count() >= self.config.max_concurrent_requests { return Err(RuvLLMError::OutOfMemory( "Maximum concurrent requests reached".to_string(), @@ -255,7 +253,9 @@ impl ServingEngine { completion_tx: None, created_at: Instant::now(), }; - self.pending_requests.write().insert(request_id, engine_request); + self.pending_requests + .write() + .insert(request_id, engine_request); } // Add to queue @@ -489,11 +489,7 @@ impl ServingEngine { /// /// # Returns /// The generated token ID - fn generate_next_token( - &self, - request_id: RequestId, - running: &RunningRequest, - ) -> Result { + fn generate_next_token(&self, request_id: RequestId, running: &RunningRequest) -> Result { // Build the context: prompt tokens + already generated tokens let mut context = running.request.prompt_tokens.clone(); context.extend(&running.generated_tokens); @@ -524,7 +520,9 @@ impl ServingEngine { // No model loaded - simulate token generation for testing // In production this should be an error, but for tests without // a real model we return a pseudo-random token based on context - let hash = context.iter().fold(0u32, |acc, &t| acc.wrapping_add(t).wrapping_mul(31)); + let hash = context + .iter() + .fold(0u32, |acc, &t| acc.wrapping_add(t).wrapping_mul(31)); return Ok(hash % 32000); } @@ -593,9 +591,10 @@ impl ServingEngine { let lookahead = spec_config.lookahead; // Get tokenizer for encoding/decoding - let tokenizer = self.model.tokenizer().ok_or_else(|| { - RuvLLMError::InvalidOperation("No tokenizer available".to_string()) - })?; + let tokenizer = self + .model + .tokenizer() + .ok_or_else(|| RuvLLMError::InvalidOperation("No tokenizer available".to_string()))?; // Decode context to text let context_text = tokenizer.decode(context)?; @@ -605,7 +604,11 @@ impl ServingEngine { max_tokens: lookahead, temperature: spec_config.draft_temperature, top_p: spec_config.draft_top_p, - top_k: if spec_config.draft_temperature == 0.0 { 1 } else { 40 }, + top_k: if spec_config.draft_temperature == 0.0 { + 1 + } else { + 40 + }, ..Default::default() }; @@ -681,7 +684,8 @@ impl ServingEngine { let continuation_tokens = tokenizer.encode(&continuation_full)?; // Record successful speculation - self.optimizer.update_speculation_stats(draft_new.len(), draft_new.len()); + self.optimizer + .update_speculation_stats(draft_new.len(), draft_new.len()); if continuation_tokens.len() > verify_context.len() { Ok(continuation_tokens[verify_context.len()]) @@ -778,9 +782,7 @@ impl ServingEngine { /// Decode a single token to text (helper method) fn decode_token(&self, token: u32) -> Option { - self.model - .tokenizer() - .and_then(|t| t.decode(&[token]).ok()) + self.model.tokenizer().and_then(|t| t.decode(&[token]).ok()) } /// Run the serving loop until stopped @@ -1011,7 +1013,9 @@ impl ServingEngine { completion_tx: Some(tx), created_at: Instant::now(), }; - self.pending_requests.write().insert(request_id, engine_request); + self.pending_requests + .write() + .insert(request_id, engine_request); } // Add to queue @@ -1019,7 +1023,8 @@ impl ServingEngine { self.total_requests.fetch_add(1, Ordering::Relaxed); // Wait for completion - rx.await.map_err(|_| RuvLLMError::Generation("Request cancelled".to_string())) + rx.await + .map_err(|_| RuvLLMError::Generation("Request cancelled".to_string())) } /// Stream tokens for a request @@ -1173,7 +1178,11 @@ mod tests { let stats = engine.stats(); // Should have processed at least one request - assert!(stats.running_requests > 0 || stats.completed_requests > 0 || stats.pending_requests > 0); + assert!( + stats.running_requests > 0 + || stats.completed_requests > 0 + || stats.pending_requests > 0 + ); } #[test] @@ -1274,9 +1283,7 @@ mod tests { let engine = create_test_engine(); // Two identical requests - let params = GenerateParams::default() - .with_max_tokens(5) - .with_seed(42); + let params = GenerateParams::default().with_max_tokens(5).with_seed(42); let request1 = InferenceRequest::new(vec![10, 20, 30], params.clone()); let request2 = InferenceRequest::new(vec![10, 20, 30], params); diff --git a/crates/ruvllm/src/serving/kv_cache_manager.rs b/crates/ruvllm/src/serving/kv_cache_manager.rs index 39f2675ca..6df94730c 100644 --- a/crates/ruvllm/src/serving/kv_cache_manager.rs +++ b/crates/ruvllm/src/serving/kv_cache_manager.rs @@ -214,7 +214,8 @@ impl KvCacheManager { // Store allocation self.allocations.write().insert(request_id, allocation); self.active_allocations.fetch_add(1, Ordering::Relaxed); - self.allocated_blocks.fetch_add(blocks_needed, Ordering::Relaxed); + self.allocated_blocks + .fetch_add(blocks_needed, Ordering::Relaxed); // Clear the cache slot self.caches[slot_id].clear(); @@ -263,7 +264,8 @@ impl KvCacheManager { } allocation.num_blocks = needed_blocks; - self.allocated_blocks.fetch_add(additional_blocks, Ordering::Relaxed); + self.allocated_blocks + .fetch_add(additional_blocks, Ordering::Relaxed); } allocation.current_length = new_length; @@ -381,20 +383,16 @@ impl KvCacheManager { /// Swap in a request's KV cache from CPU memory pub fn swap_in(&mut self, request_id: RequestId) -> Result { - let swapped = self - .swap_space - .write() - .remove(&request_id) - .ok_or_else(|| { - RuvLLMError::NotFound(format!("No swapped cache for request {}", request_id)) - })?; + let swapped = self.swap_space.write().remove(&request_id).ok_or_else(|| { + RuvLLMError::NotFound(format!("No swapped cache for request {}", request_id)) + })?; // Allocate a new slot let slot_id = { let mut free_slots = self.free_slots.write(); - free_slots.pop_front().ok_or_else(|| { - RuvLLMError::OutOfMemory("No free slots for swap in".to_string()) - })? + free_slots + .pop_front() + .ok_or_else(|| RuvLLMError::OutOfMemory("No free slots for swap in".to_string()))? }; // Allocate blocks diff --git a/crates/ruvllm/src/serving/mod.rs b/crates/ruvllm/src/serving/mod.rs index adc947e5b..3ec330e78 100644 --- a/crates/ruvllm/src/serving/mod.rs +++ b/crates/ruvllm/src/serving/mod.rs @@ -131,8 +131,7 @@ pub mod scheduler; // Re-exports for convenience pub use batch::{ - BatchedRequest, BatchStats, DecodeTask, IterationPlan, PrefillTask, ScheduledBatch, - TokenBudget, + BatchStats, BatchedRequest, DecodeTask, IterationPlan, PrefillTask, ScheduledBatch, TokenBudget, }; pub use engine::{GenerationResult, ServingEngine, ServingEngineConfig, ServingMetrics}; pub use kv_cache_manager::{ @@ -258,13 +257,13 @@ mod tests { let mut queue = RequestQueue::new(); // Add low priority first - let low = InferenceRequest::new(vec![1], GenerateParams::default()) - .with_priority(Priority::Low); + let low = + InferenceRequest::new(vec![1], GenerateParams::default()).with_priority(Priority::Low); queue.add(low); // Add high priority second - let high = InferenceRequest::new(vec![2], GenerateParams::default()) - .with_priority(Priority::High); + let high = + InferenceRequest::new(vec![2], GenerateParams::default()).with_priority(Priority::High); queue.add(high); // Schedule - high priority should be processed first diff --git a/crates/ruvllm/src/serving/request.rs b/crates/ruvllm/src/serving/request.rs index 2c51e8569..0d0483f9d 100644 --- a/crates/ruvllm/src/serving/request.rs +++ b/crates/ruvllm/src/serving/request.rs @@ -238,7 +238,10 @@ impl RunningRequest { /// Get remaining tokens to generate pub fn remaining_tokens(&self) -> usize { - self.request.params.max_tokens.saturating_sub(self.generated_tokens.len()) + self.request + .params + .max_tokens + .saturating_sub(self.generated_tokens.len()) } /// Get the position for the next token diff --git a/crates/ruvllm/src/serving/scheduler.rs b/crates/ruvllm/src/serving/scheduler.rs index 98978d116..5f5ea135f 100644 --- a/crates/ruvllm/src/serving/scheduler.rs +++ b/crates/ruvllm/src/serving/scheduler.rs @@ -306,17 +306,9 @@ impl ContinuousBatchScheduler { } // Get last generated token (or first prompt token if no generations yet) - let input_token = running - .generated_tokens - .last() - .copied() - .unwrap_or_else(|| { - running - .request - .prompt_tokens - .last() - .copied() - .unwrap_or(0) + let input_token = + running.generated_tokens.last().copied().unwrap_or_else(|| { + running.request.prompt_tokens.last().copied().unwrap_or(0) }); plan.decode_tasks.push(DecodeTask { @@ -383,7 +375,9 @@ impl ContinuousBatchScheduler { .unwrap_or_default(); // Determine tokens to prefill - let tokens = if self.config.chunked_prefill && request.prompt_len() > self.config.prefill_chunk_size { + let tokens = if self.config.chunked_prefill + && request.prompt_len() > self.config.prefill_chunk_size + { request.prompt_tokens[..self.config.prefill_chunk_size].to_vec() } else { request.prompt_tokens.clone() @@ -402,7 +396,9 @@ impl ContinuousBatchScheduler { running.block_table = block_table; // If chunked, mark partial prefill - if self.config.chunked_prefill && running.request.prompt_len() > self.config.prefill_chunk_size { + if self.config.chunked_prefill + && running.request.prompt_len() > self.config.prefill_chunk_size + { running.prefill_tokens_processed = self.config.prefill_chunk_size; } else { running.complete_prefill(); @@ -431,11 +427,7 @@ impl ContinuousBatchScheduler { // Resume as decode if budget.try_allocate_decode() { if let Some(running) = queue.running.get(&request_id) { - let input_token = running - .generated_tokens - .last() - .copied() - .unwrap_or(0); + let input_token = running.generated_tokens.last().copied().unwrap_or(0); plan.decode_tasks.push(DecodeTask { request_id, @@ -465,7 +457,8 @@ impl ContinuousBatchScheduler { break; } - let tokens_needed = data.request.prompt_tokens.len() + data.generated_tokens.len(); + let tokens_needed = + data.request.prompt_tokens.len() + data.generated_tokens.len(); if !budget.try_allocate_prefill(tokens_needed) { // Put back @@ -503,7 +496,8 @@ impl ContinuousBatchScheduler { running.decode_steps = data.decode_steps; running.block_table = block_table; running.complete_prefill(); - running.context_len = running.request.prompt_tokens.len() + running.generated_tokens.len(); + running.context_len = + running.request.prompt_tokens.len() + running.generated_tokens.len(); running.current_seq_len = running.context_len; queue.add_running(running); @@ -527,7 +521,10 @@ impl ContinuousBatchScheduler { // Preempt if we have high-priority pending requests if let Some(pending) = queue.pending.front() { if pending.priority == Priority::Critical { - return queue.running.values().any(|r| r.request.priority < Priority::Critical); + return queue + .running + .values() + .any(|r| r.request.priority < Priority::Critical); } } diff --git a/crates/ruvllm/src/session.rs b/crates/ruvllm/src/session.rs index d2f5ed1d5..6f9693798 100644 --- a/crates/ruvllm/src/session.rs +++ b/crates/ruvllm/src/session.rs @@ -4,7 +4,7 @@ //! and integration with KV cache and adapters. use crate::error::{Result, RuvLLMError}; -use crate::kv_cache::{TwoTierKvCache, KvCacheConfig}; +use crate::kv_cache::{KvCacheConfig, TwoTierKvCache}; use dashmap::DashMap; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -30,7 +30,7 @@ impl Default for SessionConfig { fn default() -> Self { Self { max_lifetime_secs: 3600, // 1 hour - idle_timeout_secs: 300, // 5 minutes + idle_timeout_secs: 300, // 5 minutes max_turns: 100, kv_cache: KvCacheConfig::default(), persist: true, @@ -234,7 +234,9 @@ impl SessionManager { self.sessions.insert(session_id.clone(), session_ref); // Return a copy - Ok(self.sessions.get(&session_id) + Ok(self + .sessions + .get(&session_id) .map(|s| { let guard = s.read(); Session { @@ -282,7 +284,10 @@ impl SessionManager { f(&mut guard); Ok(()) } else { - Err(RuvLLMError::NotFound(format!("Session not found: {}", session_id))) + Err(RuvLLMError::NotFound(format!( + "Session not found: {}", + session_id + ))) } } diff --git a/crates/ruvllm/src/session_index.rs b/crates/ruvllm/src/session_index.rs index 94e82dc69..f8e69d3ae 100644 --- a/crates/ruvllm/src/session_index.rs +++ b/crates/ruvllm/src/session_index.rs @@ -10,8 +10,8 @@ use crate::error::{Result, RuvLLMError}; use crate::kv_cache::CacheQuantization; use crate::session::Session; use chrono::{DateTime, Utc}; -use ruvector_core::{AgenticDB, SearchQuery, VectorEntry}; use ruvector_core::types::DbOptions; +use ruvector_core::{AgenticDB, SearchQuery, VectorEntry}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -111,7 +111,10 @@ impl SessionState { Self { session_id: session.id.clone(), user_id: session.user_id.clone(), - context_embedding: session.context_embedding.clone().unwrap_or_else(|| vec![0.0; 768]), + context_embedding: session + .context_embedding + .clone() + .unwrap_or_else(|| vec![0.0; 768]), kv_cache_ref: KvCacheReference::default(), active_adapter: session.active_adapter.map(|id| id.to_string()), turn_count: session.turn_count, @@ -136,28 +139,36 @@ impl SessionIndex { options.storage_path = storage_path.to_string(); options.dimensions = embedding_dim; - let db = AgenticDB::new(options) - .map_err(|e| RuvLLMError::Storage(e.to_string()))?; + let db = AgenticDB::new(options).map_err(|e| RuvLLMError::Storage(e.to_string()))?; - Ok(Self { - db, - embedding_dim, - }) + Ok(Self { db, embedding_dim }) } /// Store a session state pub fn store(&self, state: &SessionState) -> Result<()> { // Create metadata let mut metadata = HashMap::new(); - metadata.insert("session_id".to_string(), serde_json::json!(state.session_id)); + metadata.insert( + "session_id".to_string(), + serde_json::json!(state.session_id), + ); if let Some(ref user_id) = state.user_id { metadata.insert("user_id".to_string(), serde_json::json!(user_id)); } - metadata.insert("turn_count".to_string(), serde_json::json!(state.turn_count)); - metadata.insert("last_active".to_string(), serde_json::json!(state.last_active.to_rfc3339())); - metadata.insert("kv_cache_ref".to_string(), serde_json::to_value(&state.kv_cache_ref).unwrap_or_default()); + metadata.insert( + "turn_count".to_string(), + serde_json::json!(state.turn_count), + ); + metadata.insert( + "last_active".to_string(), + serde_json::json!(state.last_active.to_rfc3339()), + ); + metadata.insert( + "kv_cache_ref".to_string(), + serde_json::to_value(&state.kv_cache_ref).unwrap_or_default(), + ); if let Some(ref adapter) = state.active_adapter { metadata.insert("active_adapter".to_string(), serde_json::json!(adapter)); @@ -175,14 +186,19 @@ impl SessionIndex { }; // Store in Ruvector - self.db.insert(vector_entry) + self.db + .insert(vector_entry) .map_err(|e| RuvLLMError::Storage(e.to_string()))?; Ok(()) } /// Search sessions by context similarity - pub fn search_by_context(&self, context_embedding: &[f32], limit: usize) -> Result> { + pub fn search_by_context( + &self, + context_embedding: &[f32], + limit: usize, + ) -> Result> { let query = SearchQuery { vector: context_embedding.to_vec(), k: limit, @@ -190,13 +206,17 @@ impl SessionIndex { ef_search: None, }; - let results = self.db.search(query) + let results = self + .db + .search(query) .map_err(|e| RuvLLMError::Storage(e.to_string()))?; let mut states = Vec::with_capacity(results.len()); for result in results { if let Some(metadata) = &result.metadata { - if let Some(state) = self.state_from_metadata(&result.id, context_embedding, metadata) { + if let Some(state) = + self.state_from_metadata(&result.id, context_embedding, metadata) + { states.push(state); } } @@ -207,7 +227,8 @@ impl SessionIndex { /// Delete session state pub fn delete(&self, session_id: &str) -> Result<()> { - self.db.delete(session_id) + self.db + .delete(session_id) .map_err(|e| RuvLLMError::Storage(e.to_string()))?; Ok(()) } @@ -221,25 +242,30 @@ impl SessionIndex { ) -> Option { let session_id = metadata.get("session_id")?.as_str()?.to_string(); - let user_id = metadata.get("user_id") + let user_id = metadata + .get("user_id") .and_then(|v| v.as_str()) .map(String::from); - let turn_count = metadata.get("turn_count") + let turn_count = metadata + .get("turn_count") .and_then(|v| v.as_u64()) .unwrap_or(0) as u32; - let last_active = metadata.get("last_active") + let last_active = metadata + .get("last_active") .and_then(|v| v.as_str()) .and_then(|s| DateTime::parse_from_rfc3339(s).ok()) .map(|dt| dt.with_timezone(&Utc)) .unwrap_or_else(Utc::now); - let kv_cache_ref: KvCacheReference = metadata.get("kv_cache_ref") + let kv_cache_ref: KvCacheReference = metadata + .get("kv_cache_ref") .and_then(|v| serde_json::from_value(v.clone()).ok()) .unwrap_or_default(); - let active_adapter = metadata.get("active_adapter") + let active_adapter = metadata + .get("active_adapter") .and_then(|v| v.as_str()) .map(String::from); diff --git a/crates/ruvllm/src/sona/integration.rs b/crates/ruvllm/src/sona/integration.rs index 4aaa3a6ce..8ac6831cc 100644 --- a/crates/ruvllm/src/sona/integration.rs +++ b/crates/ruvllm/src/sona/integration.rs @@ -84,8 +84,8 @@ impl Default for SonaConfig { background_learning_rate: 0.001, ewc_lambda: 0.1, pattern_capacity: 10000, - background_interval_secs: 3600, // 1 hour - deep_interval_secs: 604800, // 1 week + background_interval_secs: 3600, // 1 hour + deep_interval_secs: 604800, // 1 week quality_threshold: 0.5, } } @@ -322,9 +322,9 @@ impl SonaIntegration { { let mut rb = self.reasoning_bank.write(); rb.prune_patterns( - 0.3, // min_quality - 5, // min_accesses - 604800, // max_age_secs (1 week) + 0.3, // min_quality + 5, // min_accesses + 604800, // max_age_secs (1 week) ); } @@ -351,10 +351,7 @@ impl SonaIntegration { /// Search for similar patterns in ReasoningBank pub fn search_patterns(&self, query: &[f32], limit: usize) -> Vec { let rb = self.reasoning_bank.read(); - rb.find_similar(query, limit) - .into_iter() - .cloned() - .collect() + rb.find_similar(query, limit).into_iter().cloned().collect() } /// Apply learned transformations to input diff --git a/crates/ruvllm/src/sona/ruvltra_pretrain.rs b/crates/ruvllm/src/sona/ruvltra_pretrain.rs index abcb8d95b..fd233edae 100644 --- a/crates/ruvllm/src/sona/ruvltra_pretrain.rs +++ b/crates/ruvllm/src/sona/ruvltra_pretrain.rs @@ -150,16 +150,16 @@ impl RuvLtraPretrainConfig { pub fn for_ruvltra_small() -> Self { Self { sona: SonaConfig { - hidden_dim: 128, // Smaller for 0.5B - embedding_dim: 384, // Match model hidden/2 - micro_lora_rank: 1, // Minimal overhead for small model - base_lora_rank: 4, // Conservative for 0.5B + hidden_dim: 128, // Smaller for 0.5B + embedding_dim: 384, // Match model hidden/2 + micro_lora_rank: 1, // Minimal overhead for small model + base_lora_rank: 4, // Conservative for 0.5B instant_learning_rate: 0.005, // Slightly lower for stability background_learning_rate: 0.0005, - ewc_lambda: 500.0, // Lower lambda for small model (less to protect) + ewc_lambda: 500.0, // Lower lambda for small model (less to protect) pattern_capacity: 5000, // Smaller capacity background_interval_secs: 1800, // 30 minutes - deep_interval_secs: 259200, // 3 days + deep_interval_secs: 259200, // 3 days quality_threshold: 0.6, // Higher threshold for small model }, dataset: DatasetConfig { @@ -171,7 +171,7 @@ impl RuvLtraPretrainConfig { quality_threshold: 0.6, }, routing: RoutingPretrainConfig { - num_clusters: 50, // Fewer clusters for small model + num_clusters: 50, // Fewer clusters for small model learning_rate: 0.001, epochs: 5, min_samples_per_class: 100, @@ -181,7 +181,7 @@ impl RuvLtraPretrainConfig { num_buckets: 5, learning_rate: 0.001, epochs: 3, - use_regression: false, // Classification easier for small model + use_regression: false, // Classification easier for small model }, seeding: SeedingConfig { patterns_per_category: 20, @@ -421,7 +421,10 @@ impl RuvLtraPretrainer { /// Pretrain routing patterns (query -> model routing) /// /// Learns which types of queries should be routed to which model size. - pub fn pretrain_routing_patterns(&mut self, samples: &[PretrainSample]) -> RoutingPretrainResult { + pub fn pretrain_routing_patterns( + &mut self, + samples: &[PretrainSample], + ) -> RoutingPretrainResult { let mut centroids = Vec::new(); let mut model_assignments = Vec::new(); let mut loss_history = Vec::new(); @@ -511,7 +514,10 @@ impl RuvLtraPretrainer { /// Pretrain quality prediction patterns /// /// Learns to predict expected quality based on query characteristics. - pub fn pretrain_quality_patterns(&mut self, samples: &[PretrainSample]) -> QualityPretrainResult { + pub fn pretrain_quality_patterns( + &mut self, + samples: &[PretrainSample], + ) -> QualityPretrainResult { let mut loss_history = Vec::new(); let num_buckets = self.config.quality.num_buckets; @@ -605,7 +611,8 @@ impl RuvLtraPretrainer { pattern.pattern_type = category.pattern_type.clone(); // Create trajectory and add to bank - let trajectory = QueryTrajectory::new(total_seeded as u64, pattern.centroid.clone()); + let trajectory = + QueryTrajectory::new(total_seeded as u64, pattern.centroid.clone()); self.reasoning_bank.add_trajectory(&trajectory); total_quality += pattern.avg_quality; diff --git a/crates/ruvllm/src/speculative.rs b/crates/ruvllm/src/speculative.rs index d42a446fe..a458b0460 100644 --- a/crates/ruvllm/src/speculative.rs +++ b/crates/ruvllm/src/speculative.rs @@ -293,7 +293,9 @@ impl TreeNode { let child = TreeNode::new(token, prob, self.depth + 1); self.children.push(child); // SAFETY: We just pushed, so children is non-empty - self.children.last_mut().expect("children is non-empty after push") + self.children + .last_mut() + .expect("children is non-empty after push") } /// Get all paths from this node to leaves @@ -323,7 +325,11 @@ impl TreeNode { let best_child = self .children .iter() - .max_by(|a, b| a.prob.partial_cmp(&b.prob).unwrap_or(std::cmp::Ordering::Equal)) + .max_by(|a, b| { + a.prob + .partial_cmp(&b.prob) + .unwrap_or(std::cmp::Ordering::Equal) + }) .expect("children is non-empty"); let mut path = vec![self.token]; @@ -436,17 +442,19 @@ impl SpeculativeDecoder { /// Tokenize input text fn tokenize(&self, text: &str) -> Result> { - let tokenizer = self.main_model.tokenizer().ok_or_else(|| { - RuvLLMError::InvalidOperation("No tokenizer available".to_string()) - })?; + let tokenizer = self + .main_model + .tokenizer() + .ok_or_else(|| RuvLLMError::InvalidOperation("No tokenizer available".to_string()))?; tokenizer.encode(text) } /// Decode tokens to text fn decode(&self, tokens: &[u32]) -> Result { - let tokenizer = self.main_model.tokenizer().ok_or_else(|| { - RuvLLMError::InvalidOperation("No tokenizer available".to_string()) - })?; + let tokenizer = self + .main_model + .tokenizer() + .ok_or_else(|| RuvLLMError::InvalidOperation("No tokenizer available".to_string()))?; tokenizer.decode(tokens) } @@ -464,7 +472,11 @@ impl SpeculativeDecoder { } /// Generate tokens with speculative decoding - pub fn generate_tokens(&self, prompt_tokens: &[u32], params: &GenerateParams) -> Result> { + pub fn generate_tokens( + &self, + prompt_tokens: &[u32], + params: &GenerateParams, + ) -> Result> { let config = self.config.read().clone(); let mut context = prompt_tokens.to_vec(); let mut output = Vec::new(); @@ -531,7 +543,9 @@ impl SpeculativeDecoder { if current_text.contains(stop_seq) { // Trim to before stop sequence let trimmed = current_text.split(stop_seq).next().unwrap_or(""); - return self.tokenize(trimmed).map(|t| t.into_iter().skip(prompt_tokens.len()).collect()); + return self + .tokenize(trimmed) + .map(|t| t.into_iter().skip(prompt_tokens.len()).collect()); } } } @@ -559,14 +573,20 @@ impl SpeculativeDecoder { max_tokens: 1, temperature: config.draft_temperature, top_p: config.draft_top_p, - top_k: if config.draft_temperature == 0.0 { 1 } else { 40 }, + top_k: if config.draft_temperature == 0.0 { + 1 + } else { + 40 + }, ..Default::default() }; // Get next token from draft model // Note: In production, this would use a more efficient batched approach let current_prompt = self.decode(&ctx)?; - let generated = self.draft_model.generate(¤t_prompt, draft_params.clone())?; + let generated = self + .draft_model + .generate(¤t_prompt, draft_params.clone())?; // Tokenize the generated text to get the new token let generated_tokens = self.tokenize(&format!("{}{}", prompt_text, generated))?; @@ -624,7 +644,9 @@ impl SpeculativeDecoder { ..params.clone() }; - let main_generated = self.main_model.generate(&prompt_text, main_params.clone())?; + let main_generated = self + .main_model + .generate(&prompt_text, main_params.clone())?; let main_tokens = self.tokenize(&format!("{}{}", prompt_text, main_generated))?; if main_tokens.len() <= ctx.len() { @@ -721,11 +743,7 @@ impl SpeculativeDecoder { } /// Generate with tree-based speculation (advanced) - pub fn generate_tree( - &self, - prompt: &str, - params: GenerateParams, - ) -> Result { + pub fn generate_tree(&self, prompt: &str, params: GenerateParams) -> Result { let config = self.config.read().clone(); if !config.tree_speculation { return self.generate(prompt, params); @@ -866,12 +884,17 @@ impl<'a, M: LlmBackend + ?Sized, D: LlmBackend + ?Sized> Iterator // Generate more tokens via speculation let lookahead = self.config.lookahead; - let draft_result = self.decoder.draft_phase(&self.context, lookahead, &self.config); + let draft_result = self + .decoder + .draft_phase(&self.context, lookahead, &self.config); match draft_result { Ok(draft_tokens) if !draft_tokens.is_empty() => { // Verify draft tokens - match self.decoder.verify_phase(&self.context, &draft_tokens, &self.params) { + match self + .decoder + .verify_phase(&self.context, &draft_tokens, &self.params) + { Ok(verification) => { // Queue accepted tokens and correction let accepted = &draft_tokens[..verification.accepted_count]; @@ -893,7 +916,10 @@ impl<'a, M: LlmBackend + ?Sized, D: LlmBackend + ?Sized> Iterator } Ok(_) => { // Empty draft, single token generation - match self.decoder.single_main_forward(&self.context, &self.params) { + match self + .decoder + .single_main_forward(&self.context, &self.params) + { Ok(token) => { self.context.push(token); self.output_count += 1; @@ -1048,8 +1074,24 @@ fn softmax_neon_optimized(logits: &[f32]) -> Vec { let d1 = vsubq_f32(v1, max_vec); // Fast exp - let e0 = fast_exp_vec(d0, one, half, sixth, twenty_fourth, one_twenty, seven_twenty); - let e1 = fast_exp_vec(d1, one, half, sixth, twenty_fourth, one_twenty, seven_twenty); + let e0 = fast_exp_vec( + d0, + one, + half, + sixth, + twenty_fourth, + one_twenty, + seven_twenty, + ); + let e1 = fast_exp_vec( + d1, + one, + half, + sixth, + twenty_fourth, + one_twenty, + seven_twenty, + ); // Store exp values vst1q_f32(result.as_mut_ptr().add(base), e0); diff --git a/crates/ruvllm/src/tests/activation_tests.rs b/crates/ruvllm/src/tests/activation_tests.rs index 703e81656..78cb95b05 100644 --- a/crates/ruvllm/src/tests/activation_tests.rs +++ b/crates/ruvllm/src/tests/activation_tests.rs @@ -54,7 +54,10 @@ fn test_silu_vector() { let expected = silu_reference(x); assert!( (y - expected).abs() < 1e-6, - "SiLU mismatch at index {}: got {}, expected {}", i, y, expected + "SiLU mismatch at index {}: got {}, expected {}", + i, + y, + expected ); } } @@ -159,7 +162,9 @@ fn test_gelu_approx_vs_exact() { assert!( error < 0.01, "GELU approximation error too large at x={}: approx={}, exact={}", - x, approx, exact + x, + approx, + exact ); } } @@ -186,7 +191,7 @@ fn test_gelu_monotonicity() { // Not strictly monotonic but increasing trend for positive values if values[i] > 0.5 { assert!( - outputs[i] >= outputs[i-1] - 1e-6, + outputs[i] >= outputs[i - 1] - 1e-6, "GELU should be increasing for positive values" ); } @@ -249,7 +254,10 @@ fn test_relu_special_values() { fn softmax_reference(logits: &[f32]) -> Vec { let max_logit = logits.iter().cloned().fold(f32::NEG_INFINITY, f32::max); let exp_sum: f32 = logits.iter().map(|&x| (x - max_logit).exp()).sum(); - logits.iter().map(|&x| (x - max_logit).exp() / exp_sum).collect() + logits + .iter() + .map(|&x| (x - max_logit).exp() / exp_sum) + .collect() } #[test] @@ -258,7 +266,11 @@ fn test_softmax_sum_to_one() { let probs = softmax_reference(&logits); let sum: f32 = probs.iter().sum(); - assert!((sum - 1.0).abs() < 1e-6, "Softmax should sum to 1.0, got {}", sum); + assert!( + (sum - 1.0).abs() < 1e-6, + "Softmax should sum to 1.0, got {}", + sum + ); } #[test] @@ -278,7 +290,10 @@ fn test_softmax_ordering() { // Probabilities should be in increasing order for i in 0..probs.len() - 1 { - assert!(probs[i] < probs[i + 1], "Higher logit should have higher prob"); + assert!( + probs[i] < probs[i + 1], + "Higher logit should have higher prob" + ); } } @@ -289,8 +304,14 @@ fn test_softmax_numerical_stability() { let probs = softmax_reference(&logits); let sum: f32 = probs.iter().sum(); - assert!((sum - 1.0).abs() < 1e-4, "Softmax should be stable with large inputs"); - assert!(probs.iter().all(|p| p.is_finite()), "All probs should be finite"); + assert!( + (sum - 1.0).abs() < 1e-4, + "Softmax should be stable with large inputs" + ); + assert!( + probs.iter().all(|p| p.is_finite()), + "All probs should be finite" + ); } #[test] @@ -300,7 +321,10 @@ fn test_softmax_uniform() { let probs = softmax_reference(&logits); for p in &probs { - assert!((p - 0.25).abs() < 1e-6, "Equal logits should give uniform probs"); + assert!( + (p - 0.25).abs() < 1e-6, + "Equal logits should give uniform probs" + ); } } @@ -320,10 +344,16 @@ fn test_softmax_temperature_effect() { let probs_t20 = softmax_reference(&scaled_20); // Lower temperature should concentrate probability on max - assert!(probs_t05[2] > probs_t1[2], "Lower temp should increase max prob"); + assert!( + probs_t05[2] > probs_t1[2], + "Lower temp should increase max prob" + ); // Higher temperature should flatten distribution - assert!(probs_t20[0] > probs_t1[0], "Higher temp should increase min prob"); + assert!( + probs_t20[0] > probs_t1[0], + "Higher temp should increase min prob" + ); } // ============================================================================ @@ -331,11 +361,18 @@ fn test_softmax_temperature_effect() { // ============================================================================ fn leaky_relu_reference(x: f32, alpha: f32) -> f32 { - if x > 0.0 { x } else { alpha * x } + if x > 0.0 { + x + } else { + alpha * x + } } fn leaky_relu_vec_reference(input: &[f32], alpha: f32) -> Vec { - input.iter().map(|&x| leaky_relu_reference(x, alpha)).collect() + input + .iter() + .map(|&x| leaky_relu_reference(x, alpha)) + .collect() } #[test] @@ -367,8 +404,14 @@ fn test_leaky_relu_continuity() { let right = leaky_relu_reference(epsilon, alpha); let at_zero = leaky_relu_reference(0.0, alpha); - assert!((left - at_zero).abs() < 1e-4, "Should be continuous from left"); - assert!((right - at_zero).abs() < 1e-4, "Should be continuous from right"); + assert!( + (left - at_zero).abs() < 1e-4, + "Should be continuous from left" + ); + assert!( + (right - at_zero).abs() < 1e-4, + "Should be continuous from right" + ); } // ============================================================================ @@ -418,9 +461,18 @@ fn test_activation_performance_comparison() { // Print timing results (for manual inspection) // These assertions just verify the functions complete in reasonable time assert!(relu_time.as_millis() < 1000, "ReLU should complete quickly"); - assert!(silu_time.as_millis() < 2000, "SiLU should complete in reasonable time"); - assert!(gelu_time.as_millis() < 2000, "GELU should complete in reasonable time"); - assert!(softmax_time.as_millis() < 1000, "Softmax should complete quickly"); + assert!( + silu_time.as_millis() < 2000, + "SiLU should complete in reasonable time" + ); + assert!( + gelu_time.as_millis() < 2000, + "GELU should complete in reasonable time" + ); + assert!( + softmax_time.as_millis() < 1000, + "Softmax should complete quickly" + ); } // ============================================================================ @@ -436,7 +488,11 @@ fn test_neon_softmax_vs_scalar() { // Sum should be 1.0 let sum: f32 = scalar_result.iter().sum(); - assert!((sum - 1.0).abs() < 1e-4, "Softmax sum should be 1.0, got {}", sum); + assert!( + (sum - 1.0).abs() < 1e-4, + "Softmax sum should be 1.0, got {}", + sum + ); // All probabilities should be positive assert!(scalar_result.iter().all(|&p| p > 0.0 && p < 1.0)); @@ -456,14 +512,23 @@ fn test_neon_softmax_large_array() { // Check sum let scalar_sum: f32 = scalar_result.iter().sum(); - assert!((scalar_sum - 1.0).abs() < 1e-4, "Scalar softmax sum should be 1.0, got {}", scalar_sum); + assert!( + (scalar_sum - 1.0).abs() < 1e-4, + "Scalar softmax sum should be 1.0, got {}", + scalar_sum + ); // Check all values are valid probabilities - assert!(scalar_result.iter().all(|&p| p >= 0.0 && p <= 1.0 && p.is_finite())); + assert!(scalar_result + .iter() + .all(|&p| p >= 0.0 && p <= 1.0 && p.is_finite())); // Check ordering is preserved for i in 0..scalar_result.len() - 1 { - assert!(scalar_result[i] <= scalar_result[i + 1], "Ordering should be preserved"); + assert!( + scalar_result[i] <= scalar_result[i + 1], + "Ordering should be preserved" + ); } } @@ -558,7 +623,10 @@ fn test_silu_derivative_at_zero() { let deriv = (silu_reference(x + epsilon) - silu_reference(x - epsilon)) / (2.0 * epsilon); // SiLU'(0) = 0.5 - assert!((deriv - 0.5).abs() < 0.01, "SiLU derivative at 0 should be 0.5"); + assert!( + (deriv - 0.5).abs() < 0.01, + "SiLU derivative at 0 should be 0.5" + ); } #[test] @@ -569,5 +637,8 @@ fn test_gelu_derivative_positive() { let deriv = (gelu_reference(x + epsilon) - gelu_reference(x - epsilon)) / (2.0 * epsilon); // For positive x, GELU derivative should be close to 1 - assert!(deriv > 0.5 && deriv < 1.5, "GELU derivative at x=1 should be near 1"); + assert!( + deriv > 0.5 && deriv < 1.5, + "GELU derivative at x=1 should be near 1" + ); } diff --git a/crates/ruvllm/src/tests/attention_tests.rs b/crates/ruvllm/src/tests/attention_tests.rs index 16848c43b..e1270cbfc 100644 --- a/crates/ruvllm/src/tests/attention_tests.rs +++ b/crates/ruvllm/src/tests/attention_tests.rs @@ -4,10 +4,9 @@ //! output correctness, memory allocation, pre-allocated buffer reuse, and benchmarks. use crate::kernels::{ - flash_attention_neon, flash_attention_v2, flash_attention_auto, - multi_query_attention_neon, grouped_query_attention_neon, - paged_attention_neon, PagedKvCache, AttentionConfig, - select_block_size, BLOCK_SIZE_SMALL, BLOCK_SIZE_MEDIUM, BLOCK_SIZE_LARGE, + flash_attention_auto, flash_attention_neon, flash_attention_v2, grouped_query_attention_neon, + multi_query_attention_neon, paged_attention_neon, select_block_size, AttentionConfig, + PagedKvCache, BLOCK_SIZE_LARGE, BLOCK_SIZE_MEDIUM, BLOCK_SIZE_SMALL, }; use std::time::Instant; @@ -29,7 +28,8 @@ fn attention_reference( let mut scores = Vec::with_capacity(kv_len); for t in 0..kv_len { let k_offset = t * head_dim; - let score: f32 = query.iter() + let score: f32 = query + .iter() .zip(&key[k_offset..k_offset + head_dim]) .map(|(q, k)| q * k * scale) .sum(); @@ -63,8 +63,12 @@ fn generate_test_data(head_dim: usize, kv_len: usize, seed: u64) -> (Vec, V }; let query: Vec = (0..head_dim).map(|_| next_float(&mut rng_state)).collect(); - let key: Vec = (0..kv_len * head_dim).map(|_| next_float(&mut rng_state)).collect(); - let value: Vec = (0..kv_len * head_dim).map(|_| next_float(&mut rng_state)).collect(); + let key: Vec = (0..kv_len * head_dim) + .map(|_| next_float(&mut rng_state)) + .collect(); + let value: Vec = (0..kv_len * head_dim) + .map(|_| next_float(&mut rng_state)) + .collect(); (query, key, value) } @@ -74,7 +78,9 @@ fn vectors_approx_equal(a: &[f32], b: &[f32], tolerance: f32) -> bool { if a.len() != b.len() { return false; } - a.iter().zip(b.iter()).all(|(x, y)| (x - y).abs() < tolerance) + a.iter() + .zip(b.iter()) + .all(|(x, y)| (x - y).abs() < tolerance) } // ============================================================================ @@ -93,8 +99,15 @@ fn test_flash_attention_basic() { let scale = 1.0 / (head_dim as f32).sqrt(); let output = flash_attention_neon(&query, &key, &value, scale, false); - assert_eq!(output.len(), head_dim, "Output should have head_dim elements"); - assert!(output.iter().all(|&x| x.is_finite()), "All outputs should be finite"); + assert_eq!( + output.len(), + head_dim, + "Output should have head_dim elements" + ); + assert!( + output.iter().all(|&x| x.is_finite()), + "All outputs should be finite" + ); } #[test] @@ -140,7 +153,10 @@ fn test_flash_attention_single_token() { // With single KV token, output should be proportional to the value // (after softmax, the single token gets weight 1.0) - assert!(vectors_approx_equal(&output, &value, 1e-5), "Single token attention should return value directly"); + assert!( + vectors_approx_equal(&output, &value, 1e-5), + "Single token attention should return value directly" + ); } // ============================================================================ @@ -227,7 +243,10 @@ fn test_select_block_size_long_sequence() { // Long sequences with small head_dim can use large blocks let block = select_block_size(2048, head_dim); - assert!(block >= BLOCK_SIZE_MEDIUM, "Long sequences should use at least medium blocks"); + assert!( + block >= BLOCK_SIZE_MEDIUM, + "Long sequences should use at least medium blocks" + ); } #[test] @@ -399,7 +418,8 @@ fn test_mqa_shared_kv() { // All queries identical let query_head: Vec = vec![1.0; config.head_dim]; - let queries: Vec = query_head.iter() + let queries: Vec = query_head + .iter() .cloned() .cycle() .take(config.num_heads * config.head_dim) @@ -409,9 +429,7 @@ fn test_mqa_shared_kv() { let keys: Vec = (0..kv_len * config.head_dim) .map(|i| (i as f32) * 0.1) .collect(); - let values: Vec = (0..kv_len * config.head_dim) - .map(|_| 1.0) - .collect(); + let values: Vec = (0..kv_len * config.head_dim).map(|_| 1.0).collect(); let output = multi_query_attention_neon(&queries, &keys, &values, &config); @@ -502,14 +520,27 @@ fn test_gqa_head_grouping() { let output = grouped_query_attention_neon(&queries, &keys, &values, &config); // Heads 0,1 should have values around 1.0, heads 2,3 around 2.0 - let head_outputs: Vec = output.chunks(config.head_dim) + let head_outputs: Vec = output + .chunks(config.head_dim) .map(|h| h.iter().sum::() / config.head_dim as f32) .collect(); - assert!((head_outputs[0] - 1.0).abs() < 0.1, "Head 0 should use KV head 0"); - assert!((head_outputs[1] - 1.0).abs() < 0.1, "Head 1 should use KV head 0"); - assert!((head_outputs[2] - 2.0).abs() < 0.1, "Head 2 should use KV head 1"); - assert!((head_outputs[3] - 2.0).abs() < 0.1, "Head 3 should use KV head 1"); + assert!( + (head_outputs[0] - 1.0).abs() < 0.1, + "Head 0 should use KV head 0" + ); + assert!( + (head_outputs[1] - 1.0).abs() < 0.1, + "Head 1 should use KV head 0" + ); + assert!( + (head_outputs[2] - 2.0).abs() < 0.1, + "Head 2 should use KV head 1" + ); + assert!( + (head_outputs[3] - 2.0).abs() < 0.1, + "Head 3 should use KV head 1" + ); } // ============================================================================ @@ -550,19 +581,35 @@ fn test_attention_config_effective_scale() { #[test] fn test_attention_config_gqa_ratios() { // Standard MHA (1:1) - let mha = AttentionConfig { num_heads: 32, num_kv_heads: 32, ..Default::default() }; + let mha = AttentionConfig { + num_heads: 32, + num_kv_heads: 32, + ..Default::default() + }; assert_eq!(mha.gqa_ratio(), 1); // GQA 4:1 - let gqa_4 = AttentionConfig { num_heads: 32, num_kv_heads: 8, ..Default::default() }; + let gqa_4 = AttentionConfig { + num_heads: 32, + num_kv_heads: 8, + ..Default::default() + }; assert_eq!(gqa_4.gqa_ratio(), 4); // GQA 8:1 - let gqa_8 = AttentionConfig { num_heads: 32, num_kv_heads: 4, ..Default::default() }; + let gqa_8 = AttentionConfig { + num_heads: 32, + num_kv_heads: 4, + ..Default::default() + }; assert_eq!(gqa_8.gqa_ratio(), 8); // MQA (all heads share 1 KV) - let mqa = AttentionConfig { num_heads: 32, num_kv_heads: 1, ..Default::default() }; + let mqa = AttentionConfig { + num_heads: 32, + num_kv_heads: 1, + ..Default::default() + }; assert_eq!(mqa.gqa_ratio(), 32); } @@ -598,7 +645,11 @@ fn test_attention_output_size_correct() { let output = flash_attention_neon(&query, &key, &value, scale, false); - assert_eq!(output.len(), head_dim, "Output should exactly match head_dim"); + assert_eq!( + output.len(), + head_dim, + "Output should exactly match head_dim" + ); } // ============================================================================ @@ -627,7 +678,11 @@ fn test_attention_benchmark_short_sequence() { let duration = start.elapsed(); let avg_us = duration.as_micros() as f64 / iterations as f64; - assert!(avg_us < 1000.0, "Short sequence attention should be fast: {}us", avg_us); + assert!( + avg_us < 1000.0, + "Short sequence attention should be fast: {}us", + avg_us + ); } #[test] @@ -652,7 +707,11 @@ fn test_attention_benchmark_long_sequence() { let duration = start.elapsed(); let avg_ms = duration.as_millis() as f64 / iterations as f64; - assert!(avg_ms < 50.0, "Long sequence attention should complete in <50ms: {}ms", avg_ms); + assert!( + avg_ms < 50.0, + "Long sequence attention should complete in <50ms: {}ms", + avg_ms + ); } #[test] @@ -709,7 +768,10 @@ fn test_attention_large_logits() { let output = flash_attention_neon(&query, &key, &value, scale, false); // Output should be finite - assert!(output.iter().all(|&x| x.is_finite()), "Should handle large dot products"); + assert!( + output.iter().all(|&x| x.is_finite()), + "Should handle large dot products" + ); } #[test] @@ -726,7 +788,10 @@ fn test_attention_small_values() { let output = flash_attention_neon(&query, &key, &value, scale, false); // Output should be finite - assert!(output.iter().all(|&x| x.is_finite()), "Should handle small values"); + assert!( + output.iter().all(|&x| x.is_finite()), + "Should handle small values" + ); } #[test] @@ -735,8 +800,12 @@ fn test_attention_mixed_signs() { let kv_len = 8; // Mix of positive and negative values - let query: Vec = (0..head_dim).map(|i| if i % 2 == 0 { 1.0 } else { -1.0 }).collect(); - let key: Vec = (0..kv_len * head_dim).map(|i| if i % 3 == 0 { -0.5 } else { 0.5 }).collect(); + let query: Vec = (0..head_dim) + .map(|i| if i % 2 == 0 { 1.0 } else { -1.0 }) + .collect(); + let key: Vec = (0..kv_len * head_dim) + .map(|i| if i % 3 == 0 { -0.5 } else { 0.5 }) + .collect(); let value: Vec = (0..kv_len * head_dim).map(|i| (i as f32) * 0.01).collect(); let scale = 1.0 / (head_dim as f32).sqrt(); @@ -791,7 +860,11 @@ fn test_attention_power_of_two_dims() { let output = flash_attention_neon(&query, &key, &value, scale, false); assert_eq!(output.len(), head_dim); - assert!(output.iter().all(|&x| x.is_finite()), "Failed for head_dim={}", head_dim); + assert!( + output.iter().all(|&x| x.is_finite()), + "Failed for head_dim={}", + head_dim + ); } } @@ -807,6 +880,10 @@ fn test_attention_non_power_of_two_dims() { let output = flash_attention_neon(&query, &key, &value, scale, false); assert_eq!(output.len(), head_dim); - assert!(output.iter().all(|&x| x.is_finite()), "Failed for head_dim={}", head_dim); + assert!( + output.iter().all(|&x| x.is_finite()), + "Failed for head_dim={}", + head_dim + ); } } diff --git a/crates/ruvllm/src/tests/generation_tests.rs b/crates/ruvllm/src/tests/generation_tests.rs index 3975d3681..6c61f077e 100644 --- a/crates/ruvllm/src/tests/generation_tests.rs +++ b/crates/ruvllm/src/tests/generation_tests.rs @@ -4,12 +4,11 @@ //! streaming callbacks, KV cache integration, and speculative decoding. use crate::speculative::{ - softmax, log_softmax, top_k_filter, top_p_filter, sample_from_probs, - SpeculativeConfig, SpeculativeStats, AtomicSpeculativeStats, - TreeNode, SpeculationTree, VerificationResult, + log_softmax, sample_from_probs, softmax, top_k_filter, top_p_filter, AtomicSpeculativeStats, + SpeculationTree, SpeculativeConfig, SpeculativeStats, TreeNode, VerificationResult, }; -use rand::SeedableRng; use rand::rngs::StdRng; +use rand::SeedableRng; use std::time::Duration; // ============================================================================ @@ -23,14 +22,24 @@ fn test_softmax_produces_valid_distribution() { // Sum should be 1.0 let sum: f32 = probs.iter().sum(); - assert!((sum - 1.0).abs() < 1e-5, "Softmax sum should be 1.0, got {}", sum); + assert!( + (sum - 1.0).abs() < 1e-5, + "Softmax sum should be 1.0, got {}", + sum + ); // All probabilities should be positive - assert!(probs.iter().all(|&p| p > 0.0), "All probabilities should be positive"); + assert!( + probs.iter().all(|&p| p > 0.0), + "All probabilities should be positive" + ); // Ordering should be preserved for i in 0..probs.len() - 1 { - assert!(probs[i] < probs[i + 1], "Higher logits should have higher probs"); + assert!( + probs[i] < probs[i + 1], + "Higher logits should have higher probs" + ); } } @@ -41,8 +50,15 @@ fn test_softmax_handles_large_logits() { let probs = softmax(&logits); let sum: f32 = probs.iter().sum(); - assert!((sum - 1.0).abs() < 1e-4, "Should handle large logits: sum = {}", sum); - assert!(probs.iter().all(|p| p.is_finite()), "All probs should be finite"); + assert!( + (sum - 1.0).abs() < 1e-4, + "Should handle large logits: sum = {}", + sum + ); + assert!( + probs.iter().all(|p| p.is_finite()), + "All probs should be finite" + ); } #[test] @@ -67,7 +83,10 @@ fn test_softmax_single_element() { let logits = vec![5.0]; let probs = softmax(&logits); assert_eq!(probs.len(), 1); - assert!((probs[0] - 1.0).abs() < 1e-5, "Single element should have prob 1.0"); + assert!( + (probs[0] - 1.0).abs() < 1e-5, + "Single element should have prob 1.0" + ); } #[test] @@ -79,7 +98,10 @@ fn test_log_softmax_relationship() { // log_softmax should equal log(softmax) for (lp, p) in log_probs.iter().zip(probs.iter()) { let expected = p.ln(); - assert!((lp - expected).abs() < 1e-4, "log_softmax should match log(softmax)"); + assert!( + (lp - expected).abs() < 1e-4, + "log_softmax should match log(softmax)" + ); } } @@ -89,7 +111,10 @@ fn test_log_softmax_numerical_stability() { let logits = vec![-1000.0, -999.0, -998.0]; let log_probs = log_softmax(&logits); - assert!(log_probs.iter().all(|p| p.is_finite()), "log_softmax should handle extreme values"); + assert!( + log_probs.iter().all(|p| p.is_finite()), + "log_softmax should handle extreme values" + ); // Check that relative ordering is preserved assert!(log_probs[0] < log_probs[1] && log_probs[1] < log_probs[2]); } @@ -212,7 +237,9 @@ fn test_sample_from_probs_uniform() { assert!( (0.8..=1.2).contains(&ratio), "Index {} should be sampled uniformly, got {} (expected ~{})", - i, count, expected + i, + count, + expected ); } } @@ -245,7 +272,10 @@ fn test_temperature_scaling_sharpens() { let probs = softmax(&scaled); // Highest logit should have much higher probability - assert!(probs[3] > 0.99, "Low temperature should concentrate probability on max"); + assert!( + probs[3] > 0.99, + "Low temperature should concentrate probability on max" + ); } #[test] @@ -260,7 +290,10 @@ fn test_temperature_scaling_flattens() { let min_prob = probs.iter().cloned().fold(f32::INFINITY, f32::min); let max_prob = probs.iter().cloned().fold(f32::NEG_INFINITY, f32::max); - assert!(max_prob - min_prob < 0.2, "High temperature should flatten distribution"); + assert!( + max_prob - min_prob < 0.2, + "High temperature should flatten distribution" + ); } #[test] @@ -273,7 +306,10 @@ fn test_temperature_one_unchanged() { let probs2 = softmax(&scaled); for (p1, p2) in probs1.iter().zip(probs2.iter()) { - assert!((p1 - p2).abs() < 1e-6, "Temperature 1.0 should not change distribution"); + assert!( + (p1 - p2).abs() < 1e-6, + "Temperature 1.0 should not change distribution" + ); } } @@ -358,7 +394,10 @@ fn test_speculative_stats_multiple_rounds() { assert!((stats.acceptance_rate - 0.75).abs() < 0.01); // 6/8 = 0.75 assert_eq!(stats.main_forward_passes, 2); // Total tokens depends on implementation - just check it's reasonable - assert!(stats.total_tokens_generated >= 6, "Should generate at least accepted tokens"); + assert!( + stats.total_tokens_generated >= 6, + "Should generate at least accepted tokens" + ); } #[test] @@ -381,7 +420,10 @@ fn test_speculative_stats_speedup_calculation() { stats.record_round(4, 4, 10.0); // 10 total tokens, 2 main passes -> 5 tokens/pass - assert!(stats.speedup > 4.0, "Speedup should reflect tokens per main pass"); + assert!( + stats.speedup > 4.0, + "Speedup should reflect tokens per main pass" + ); } // ============================================================================ @@ -626,8 +668,15 @@ fn test_full_sampling_pipeline() { // Verify softmax produces valid distribution let sum: f32 = probs.iter().sum(); - assert!((sum - 1.0).abs() < 1e-4, "Softmax should sum to 1.0, got {}", sum); - assert!(probs.iter().all(|&p| p > 0.0), "All probabilities should be positive"); + assert!( + (sum - 1.0).abs() < 1e-4, + "Softmax should sum to 1.0, got {}", + sum + ); + assert!( + probs.iter().all(|&p| p > 0.0), + "All probabilities should be positive" + ); // Sample with fixed RNG let mut rng = StdRng::seed_from_u64(42); @@ -648,7 +697,9 @@ fn test_full_sampling_pipeline() { // should be sampled more often than index 0 (lowest logit) assert!( samples[4] > samples[0], - "Higher logit should be sampled more: idx4={}, idx0={}", samples[4], samples[0] + "Higher logit should be sampled more: idx4={}, idx0={}", + samples[4], + samples[0] ); } @@ -680,7 +731,11 @@ fn test_beam_search_simulation() { let top_indices: Vec = indexed.iter().take(beam_width).map(|(i, _)| *i).collect(); - assert_eq!(top_indices, vec![1, 3, 2], "Top-3 should be indices 1, 3, 2"); + assert_eq!( + top_indices, + vec![1, 3, 2], + "Top-3 should be indices 1, 3, 2" + ); } // ============================================================================ @@ -693,7 +748,10 @@ fn test_softmax_with_inf() { let probs = softmax(&logits); // First element should have probability ~0 - assert!(probs[0] < 1e-10 || probs[0].abs() < 1e-10, "NEG_INFINITY should give ~0 probability"); + assert!( + probs[0] < 1e-10 || probs[0].abs() < 1e-10, + "NEG_INFINITY should give ~0 probability" + ); // Sum should still be ~1 let sum: f32 = probs.iter().sum(); @@ -720,5 +778,8 @@ fn test_top_k_with_ties() { // All three 5.0s should remain let finite_count = logits.iter().filter(|x| x.is_finite()).count(); - assert!(finite_count >= 3, "Should keep at least k elements when ties exist"); + assert!( + finite_count >= 3, + "Should keep at least k elements when ties exist" + ); } diff --git a/crates/ruvllm/src/tests/gguf_tests.rs b/crates/ruvllm/src/tests/gguf_tests.rs index 27fef863b..0ea4a8627 100644 --- a/crates/ruvllm/src/tests/gguf_tests.rs +++ b/crates/ruvllm/src/tests/gguf_tests.rs @@ -3,11 +3,10 @@ //! Tests for GGUF header/metadata parsing, tensor loading, quantization //! format handling, architecture detection, memory mapping, and error handling. +use crate::gguf::parser::GgufValueType; use crate::gguf::{ - GgufHeader, GgufValue, GgufQuantType, GGUF_MAGIC, GGUF_VERSION, - parse_header, parse_metadata, + parse_header, parse_metadata, GgufHeader, GgufQuantType, GgufValue, GGUF_MAGIC, GGUF_VERSION, }; -use crate::gguf::parser::{GgufValueType}; use std::io::Cursor; // ============================================================================ @@ -17,10 +16,10 @@ use std::io::Cursor; #[test] fn test_parse_valid_header() { let mut data = vec![]; - data.extend_from_slice(&GGUF_MAGIC.to_le_bytes()); // magic - data.extend_from_slice(&GGUF_VERSION.to_le_bytes()); // version - data.extend_from_slice(&10u64.to_le_bytes()); // tensor_count - data.extend_from_slice(&5u64.to_le_bytes()); // metadata_kv_count + data.extend_from_slice(&GGUF_MAGIC.to_le_bytes()); // magic + data.extend_from_slice(&GGUF_VERSION.to_le_bytes()); // version + data.extend_from_slice(&10u64.to_le_bytes()); // tensor_count + data.extend_from_slice(&5u64.to_le_bytes()); // metadata_kv_count let mut cursor = Cursor::new(data); let header = parse_header(&mut cursor).unwrap(); @@ -140,11 +139,7 @@ fn test_gguf_value_bool() { #[test] fn test_gguf_value_array() { - let arr = vec![ - GgufValue::U32(1), - GgufValue::U32(2), - GgufValue::U32(3), - ]; + let arr = vec![GgufValue::U32(1), GgufValue::U32(2), GgufValue::U32(3)]; let val = GgufValue::Array(arr); let array = val.as_array().unwrap(); @@ -208,11 +203,11 @@ fn test_value_type_invalid() { #[test] fn test_quant_type_from_u32() { - assert!(GgufQuantType::try_from(0u32).is_ok()); // F32 - assert!(GgufQuantType::try_from(1u32).is_ok()); // F16 - assert!(GgufQuantType::try_from(2u32).is_ok()); // Q4_0 - assert!(GgufQuantType::try_from(3u32).is_ok()); // Q4_1 - assert!(GgufQuantType::try_from(8u32).is_ok()); // Q8_0 + assert!(GgufQuantType::try_from(0u32).is_ok()); // F32 + assert!(GgufQuantType::try_from(1u32).is_ok()); // F16 + assert!(GgufQuantType::try_from(2u32).is_ok()); // Q4_0 + assert!(GgufQuantType::try_from(3u32).is_ok()); // Q4_1 + assert!(GgufQuantType::try_from(8u32).is_ok()); // Q8_0 } #[test] @@ -268,8 +263,8 @@ fn test_quant_type_bits_per_weight() { assert!((GgufQuantType::Q8_0.bits_per_weight() - 8.5).abs() < 0.1); // Q4_0: (18 bytes * 8 bits) / 32 elements = 4.5 bits - let q4_bits = (GgufQuantType::Q4_0.type_size() * 8) as f32 - / GgufQuantType::Q4_0.block_size() as f32; + let q4_bits = + (GgufQuantType::Q4_0.type_size() * 8) as f32 / GgufQuantType::Q4_0.block_size() as f32; assert!((q4_bits - 4.5).abs() < 0.1); } @@ -317,7 +312,9 @@ fn test_architecture_detection_patterns() { let normalized = input.to_lowercase(); assert!( normalized.starts_with(expected_prefix) || normalized.contains(expected_prefix), - "{} should match {} pattern", input, expected_prefix + "{} should match {} pattern", + input, + expected_prefix ); } } @@ -570,8 +567,16 @@ fn test_all_quantization_types_defined() { ]; for qt in &types { - assert!(qt.block_size() > 0, "{:?} should have positive block size", qt); - assert!(qt.type_size() > 0, "{:?} should have positive type size", qt); + assert!( + qt.block_size() > 0, + "{:?} should have positive block size", + qt + ); + assert!( + qt.type_size() > 0, + "{:?} should have positive type size", + qt + ); } } @@ -673,7 +678,10 @@ fn test_complete_header_metadata_flow() { // Parse metadata let metadata = parse_metadata(&mut cursor, header.metadata_kv_count).unwrap(); - assert_eq!(metadata.get("general.architecture").unwrap().as_str(), Some("llama")); + assert_eq!( + metadata.get("general.architecture").unwrap().as_str(), + Some("llama") + ); } // ============================================================================ @@ -718,7 +726,7 @@ fn test_large_tensor_count() { data.extend_from_slice(&GGUF_MAGIC.to_le_bytes()); data.extend_from_slice(&GGUF_VERSION.to_le_bytes()); data.extend_from_slice(&1000u64.to_le_bytes()); // 1000 tensors - data.extend_from_slice(&500u64.to_le_bytes()); // 500 metadata entries + data.extend_from_slice(&500u64.to_le_bytes()); // 500 metadata entries let mut cursor = Cursor::new(data); let header = parse_header(&mut cursor).unwrap(); diff --git a/crates/ruvllm/src/tests/witness_log_tests.rs b/crates/ruvllm/src/tests/witness_log_tests.rs index 2e87a303a..42eaa13c5 100644 --- a/crates/ruvllm/src/tests/witness_log_tests.rs +++ b/crates/ruvllm/src/tests/witness_log_tests.rs @@ -3,10 +3,10 @@ //! Tests for async write batching, flush on shutdown, backpressure handling, //! and the overall witness logging system. -use crate::witness_log::{ - WitnessEntry, WitnessLog, LatencyBreakdown, RoutingDecision, AsyncWriteConfig, -}; use crate::types::ModelSize; +use crate::witness_log::{ + AsyncWriteConfig, LatencyBreakdown, RoutingDecision, WitnessEntry, WitnessLog, +}; use std::time::Instant; // ============================================================================ @@ -178,7 +178,8 @@ fn test_witness_entry_with_quality() { "session-456".to_string(), vec![0.5; 768], RoutingDecision::default(), - ).with_quality(0.85); + ) + .with_quality(0.85); assert!((entry.quality_score - 0.85).abs() < 0.01); assert!(entry.meets_quality_threshold(0.8)); @@ -200,7 +201,8 @@ fn test_witness_entry_with_latency() { "session-789".to_string(), vec![0.0; 768], RoutingDecision::default(), - ).with_latency(latency); + ) + .with_latency(latency); assert_eq!(entry.latency.total_ms, 96.0); assert_eq!(entry.latency.generation_ms, 50.0); @@ -221,7 +223,8 @@ fn test_witness_entry_with_error() { "session-error".to_string(), vec![0.0; 768], RoutingDecision::default(), - ).with_error(error); + ) + .with_error(error); assert!(!entry.is_success()); assert!(entry.error.is_some()); @@ -234,7 +237,8 @@ fn test_witness_entry_quality_threshold_edge_cases() { "session".to_string(), vec![0.0; 768], RoutingDecision::default(), - ).with_quality(0.0); + ) + .with_quality(0.0); assert!(entry_zero.meets_quality_threshold(0.0)); assert!(!entry_zero.meets_quality_threshold(0.1)); @@ -243,7 +247,8 @@ fn test_witness_entry_quality_threshold_edge_cases() { "session".to_string(), vec![0.0; 768], RoutingDecision::default(), - ).with_quality(1.0); + ) + .with_quality(1.0); assert!(entry_one.meets_quality_threshold(1.0)); assert!(entry_one.meets_quality_threshold(0.99)); @@ -352,8 +357,8 @@ fn test_backpressure_behavior() { #[test] fn test_time_based_flush_simulation() { - use std::time::Duration; use std::thread::sleep; + use std::time::Duration; let max_wait = Duration::from_millis(100); let start = Instant::now(); @@ -432,8 +437,8 @@ fn test_witness_log_stats_serialization() { #[test] fn test_concurrent_entry_creation() { - use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; + use std::sync::Arc; use std::thread; let counter = Arc::new(AtomicUsize::new(0)); @@ -550,21 +555,24 @@ fn test_witness_entry_tags() { #[test] fn test_witness_entry_filter_by_tag() { - let entries: Vec = (0..10).map(|i| { - let mut entry = WitnessEntry::new( - format!("session-{}", i), - vec![0.0; 768], - RoutingDecision::default(), - ); - if i % 2 == 0 { - entry.tags.push("even".to_string()); - } else { - entry.tags.push("odd".to_string()); - } - entry - }).collect(); + let entries: Vec = (0..10) + .map(|i| { + let mut entry = WitnessEntry::new( + format!("session-{}", i), + vec![0.0; 768], + RoutingDecision::default(), + ); + if i % 2 == 0 { + entry.tags.push("even".to_string()); + } else { + entry.tags.push("odd".to_string()); + } + entry + }) + .collect(); - let even_entries: Vec<_> = entries.iter() + let even_entries: Vec<_> = entries + .iter() .filter(|e| e.tags.contains(&"even".to_string())) .collect(); @@ -590,7 +598,11 @@ fn test_entry_creation_performance() { let duration = start.elapsed(); let avg_us = duration.as_micros() as f64 / iterations as f64; - assert!(avg_us < 100.0, "Entry creation should be fast: {}us", avg_us); + assert!( + avg_us < 100.0, + "Entry creation should be fast: {}us", + avg_us + ); } #[test] @@ -613,7 +625,11 @@ fn test_latency_breakdown_performance() { let duration = start.elapsed(); let avg_ns = duration.as_nanos() as f64 / iterations as f64; - assert!(avg_ns < 1000.0, "Latency operations should be fast: {}ns", avg_ns); + assert!( + avg_ns < 1000.0, + "Latency operations should be fast: {}ns", + avg_ns + ); } // ============================================================================ @@ -646,11 +662,7 @@ fn test_large_embedding() { #[test] fn test_empty_session_id() { - let entry = WitnessEntry::new( - "".to_string(), - vec![0.0; 768], - RoutingDecision::default(), - ); + let entry = WitnessEntry::new("".to_string(), vec![0.0; 768], RoutingDecision::default()); assert!(entry.session_id.is_empty()); } @@ -659,11 +671,7 @@ fn test_empty_session_id() { fn test_long_session_id() { let long_id = "x".repeat(1000); - let entry = WitnessEntry::new( - long_id.clone(), - vec![0.0; 768], - RoutingDecision::default(), - ); + let entry = WitnessEntry::new(long_id.clone(), vec![0.0; 768], RoutingDecision::default()); assert_eq!(entry.session_id.len(), 1000); } diff --git a/crates/ruvllm/src/tokenizer.rs b/crates/ruvllm/src/tokenizer.rs index 42d780412..2ce1822ca 100644 --- a/crates/ruvllm/src/tokenizer.rs +++ b/crates/ruvllm/src/tokenizer.rs @@ -132,14 +132,23 @@ impl ChatTemplate { ChatTemplate::Llama3 } else if model_lower.contains("llama-2") || model_lower.contains("llama2") { ChatTemplate::Llama2 - } else if model_lower.contains("mistral") || model_lower.contains("mixtral") || model_lower.contains("codestral") { + } else if model_lower.contains("mistral") + || model_lower.contains("mixtral") + || model_lower.contains("codestral") + { ChatTemplate::Mistral } else if model_lower.contains("qwen") { ChatTemplate::Qwen - } else if model_lower.contains("phi-3") || model_lower.contains("phi3") || model_lower.contains("phi") { + } else if model_lower.contains("phi-3") + || model_lower.contains("phi3") + || model_lower.contains("phi") + { // Phi-3 and Phi use the same template format ChatTemplate::Phi - } else if model_lower.contains("gemma-2") || model_lower.contains("gemma2") || model_lower.contains("gemma") { + } else if model_lower.contains("gemma-2") + || model_lower.contains("gemma2") + || model_lower.contains("gemma") + { // Gemma-2 and Gemma use the same template format ChatTemplate::Gemma } else { @@ -587,17 +596,19 @@ mod candle_impl { /// /// Vector of token IDs pub fn encode(&self, text: &str) -> Result> { - let encoding = self.inner.encode(text, false).map_err(|e| { - RuvLLMError::Tokenization(format!("Encoding failed: {}", e)) - })?; + let encoding = self + .inner + .encode(text, false) + .map_err(|e| RuvLLMError::Tokenization(format!("Encoding failed: {}", e)))?; Ok(encoding.get_ids().to_vec()) } /// Encode text with special tokens pub fn encode_with_special_tokens(&self, text: &str) -> Result> { - let encoding = self.inner.encode(text, true).map_err(|e| { - RuvLLMError::Tokenization(format!("Encoding failed: {}", e)) - })?; + let encoding = self + .inner + .encode(text, true) + .map_err(|e| RuvLLMError::Tokenization(format!("Encoding failed: {}", e)))?; Ok(encoding.get_ids().to_vec()) } @@ -611,16 +622,16 @@ mod candle_impl { /// /// Decoded text string pub fn decode(&self, tokens: &[u32]) -> Result { - self.inner.decode(tokens, true).map_err(|e| { - RuvLLMError::Tokenization(format!("Decoding failed: {}", e)) - }) + self.inner + .decode(tokens, true) + .map_err(|e| RuvLLMError::Tokenization(format!("Decoding failed: {}", e))) } /// Decode without skipping special tokens pub fn decode_with_special_tokens(&self, tokens: &[u32]) -> Result { - self.inner.decode(tokens, false).map_err(|e| { - RuvLLMError::Tokenization(format!("Decoding failed: {}", e)) - }) + self.inner + .decode(tokens, false) + .map_err(|e| RuvLLMError::Tokenization(format!("Decoding failed: {}", e))) } /// Decode a single token for streaming output @@ -655,9 +666,10 @@ mod candle_impl { } // Get the raw bytes for this token - let token_text = self.inner.decode(&[token], false).map_err(|e| { - RuvLLMError::Tokenization(format!("Stream decode failed: {}", e)) - })?; + let token_text = self + .inner + .decode(&[token], false) + .map_err(|e| RuvLLMError::Tokenization(format!("Stream decode failed: {}", e)))?; // Check for replacement character (invalid UTF-8 indicator) if token_text.contains('\u{FFFD}') { @@ -694,7 +706,9 @@ mod candle_impl { // Clean text, output directly // But first check if we have buffered bytes if !self.stream_buffer.bytes.is_empty() { - self.stream_buffer.bytes.extend_from_slice(token_text.as_bytes()); + self.stream_buffer + .bytes + .extend_from_slice(token_text.as_bytes()); match std::str::from_utf8(&self.stream_buffer.bytes) { Ok(s) => { let result = s.to_string(); @@ -703,7 +717,8 @@ mod candle_impl { } Err(_) => { // Something went wrong, output what we have - let lossy = String::from_utf8_lossy(&self.stream_buffer.bytes).to_string(); + let lossy = + String::from_utf8_lossy(&self.stream_buffer.bytes).to_string(); self.stream_buffer.bytes.clear(); Ok(Some(lossy)) } @@ -755,9 +770,7 @@ mod candle_impl { let template = self .chat_template .as_ref() - .ok_or_else(|| { - RuvLLMError::Config("No chat template configured".to_string()) - })?; + .ok_or_else(|| RuvLLMError::Config("No chat template configured".to_string()))?; Ok(template.format(messages)) } @@ -824,7 +837,10 @@ mod candle_impl { /// Batch decode multiple token sequences pub fn decode_batch(&self, token_sequences: &[Vec]) -> Result> { - token_sequences.iter().map(|tokens| self.decode(tokens)).collect() + token_sequences + .iter() + .map(|tokens| self.decode(tokens)) + .collect() } } @@ -914,9 +930,10 @@ mod stub_impl { pub fn reset_stream(&mut self) {} pub fn apply_chat_template(&self, messages: &[ChatMessage]) -> Result { - let template = self.chat_template.as_ref().ok_or_else(|| { - RuvLLMError::Config("No chat template configured".to_string()) - })?; + let template = self + .chat_template + .as_ref() + .ok_or_else(|| RuvLLMError::Config("No chat template configured".to_string()))?; Ok(template.format(messages)) } @@ -960,7 +977,7 @@ pub use stub_impl::RuvTokenizer; // Tokenizer Trait Implementation (for LlmBackend compatibility) // ============================================================================ -use crate::backends::{Tokenizer, SpecialTokens}; +use crate::backends::{SpecialTokens, Tokenizer}; #[cfg(feature = "candle")] impl Tokenizer for RuvTokenizer { @@ -1089,10 +1106,7 @@ mod tests { #[test] fn test_mistral_template() { - let messages = vec![ - ChatMessage::system("Be concise."), - ChatMessage::user("Hi"), - ]; + let messages = vec![ChatMessage::system("Be concise."), ChatMessage::user("Hi")]; let formatted = ChatTemplate::Mistral.format(&messages); @@ -1144,12 +1158,10 @@ mod tests { #[test] fn test_custom_template() { - let template = ChatTemplate::Custom("System: {system}\nUser: {user}\nAssistant:".to_string()); + let template = + ChatTemplate::Custom("System: {system}\nUser: {user}\nAssistant:".to_string()); - let messages = vec![ - ChatMessage::system("Be brief."), - ChatMessage::user("Hello"), - ]; + let messages = vec![ChatMessage::system("Be brief."), ChatMessage::user("Hello")]; let formatted = template.format(&messages); diff --git a/crates/ruvllm/src/training/claude_dataset.rs b/crates/ruvllm/src/training/claude_dataset.rs index ea1ca9b9f..1e1128025 100644 --- a/crates/ruvllm/src/training/claude_dataset.rs +++ b/crates/ruvllm/src/training/claude_dataset.rs @@ -29,14 +29,14 @@ //! dataset.export_parquet("training_data.parquet")?; //! ``` +use rand::rngs::StdRng; +use rand::seq::SliceRandom; +use rand::{Rng, SeedableRng}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fs::File; use std::io::{BufWriter, Write}; use std::path::Path; -use rand::{Rng, SeedableRng}; -use rand::rngs::StdRng; -use rand::seq::SliceRandom; /// Task categories matching Claude Flow agents #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] @@ -245,21 +245,18 @@ impl ClaudeTaskDataset { for example in examples { // Count by category - *stats.examples_per_category + *stats + .examples_per_category .entry(example.metadata.category.name().to_string()) .or_insert(0) += 1; // Count by complexity let complexity = format!("{:?}", example.metadata.complexity); - *stats.examples_per_complexity - .entry(complexity) - .or_insert(0) += 1; + *stats.examples_per_complexity.entry(complexity).or_insert(0) += 1; // Count by domain let domain = format!("{:?}", example.metadata.domain); - *stats.examples_per_domain - .entry(domain) - .or_insert(0) += 1; + *stats.examples_per_domain.entry(domain).or_insert(0) += 1; total_quality += example.metadata.quality_score; } @@ -300,8 +297,21 @@ impl ClaudeTaskDataset { } /// Split dataset into train/validation/test sets - pub fn split(&self, train: f32, val: f32, test: f32, seed: u64) -> (Vec, Vec, Vec) { - assert!((train + val + test - 1.0).abs() < 1e-6, "Split ratios must sum to 1.0"); + pub fn split( + &self, + train: f32, + val: f32, + test: f32, + seed: u64, + ) -> ( + Vec, + Vec, + Vec, + ) { + assert!( + (train + val + test - 1.0).abs() < 1e-6, + "Split ratios must sum to 1.0" + ); let mut rng = StdRng::seed_from_u64(seed); let mut examples = self.examples.clone(); @@ -620,7 +630,8 @@ impl DatasetGenerator { // Cryptography templates TaskTemplate { input: "Review cryptographic implementation of {feature}", - context: "Algorithm: {algorithm}. Key management: {key_mgmt}. Standards: {standards}", + context: + "Algorithm: {algorithm}. Key management: {key_mgmt}. Standards: {standards}", complexity: ComplexityLevel::Complex, domain: DomainType::Security, tags: vec!["security", "cryptography"], @@ -829,7 +840,11 @@ impl DatasetGenerator { } /// Instantiate a template with random values - fn instantiate_template(&mut self, template: &TaskTemplate, category: TaskCategory) -> ClaudeTaskExample { + fn instantiate_template( + &mut self, + template: &TaskTemplate, + category: TaskCategory, + ) -> ClaudeTaskExample { let input = self.fill_template(&template.input); let context = self.fill_template(&template.context); let expected_model = category.recommended_model(template.complexity); @@ -867,21 +882,97 @@ impl DatasetGenerator { fn get_template_replacements(&self) -> HashMap<&'static str, Vec<&'static str>> { let mut map = HashMap::new(); - map.insert("language", vec!["Rust", "TypeScript", "Python", "Go", "Java"]); - map.insert("framework", vec!["React", "Vue", "Angular", "Svelte", "Next.js"]); - map.insert("function_type", vec!["async", "recursive", "higher-order", "pure", "generic"]); - map.insert("component_type", vec!["form", "table", "modal", "dashboard", "navigation"]); - map.insert("data_structure", vec!["binary tree", "hash map", "linked list", "priority queue", "trie"]); - map.insert("issue_type", vec!["null pointer", "type mismatch", "race condition", "deadlock", "stack overflow"]); - map.insert("quality_attribute", vec!["readability", "maintainability", "performance", "testability", "modularity"]); - map.insert("pattern", vec!["singleton", "factory", "observer", "strategy", "repository"]); - map.insert("api_name", vec!["Stripe", "Twilio", "SendGrid", "AWS S3", "OpenAI"]); + map.insert( + "language", + vec!["Rust", "TypeScript", "Python", "Go", "Java"], + ); + map.insert( + "framework", + vec!["React", "Vue", "Angular", "Svelte", "Next.js"], + ); + map.insert( + "function_type", + vec!["async", "recursive", "higher-order", "pure", "generic"], + ); + map.insert( + "component_type", + vec!["form", "table", "modal", "dashboard", "navigation"], + ); + map.insert( + "data_structure", + vec![ + "binary tree", + "hash map", + "linked list", + "priority queue", + "trie", + ], + ); + map.insert( + "issue_type", + vec![ + "null pointer", + "type mismatch", + "race condition", + "deadlock", + "stack overflow", + ], + ); + map.insert( + "quality_attribute", + vec![ + "readability", + "maintainability", + "performance", + "testability", + "modularity", + ], + ); + map.insert( + "pattern", + vec!["singleton", "factory", "observer", "strategy", "repository"], + ); + map.insert( + "api_name", + vec!["Stripe", "Twilio", "SendGrid", "AWS S3", "OpenAI"], + ); map.insert("http_method", vec!["GET", "POST", "PUT", "DELETE", "PATCH"]); - map.insert("vulnerability_type", vec!["SQL injection", "XSS", "CSRF", "authentication", "authorization"]); - map.insert("attack_type", vec!["DDoS", "man-in-the-middle", "replay", "privilege escalation"]); - map.insert("security_control", vec!["rate limiting", "CORS", "CSP", "input sanitization"]); - map.insert("system_type", vec!["distributed", "event-driven", "real-time", "batch processing"]); - map.insert("resource_type", vec!["users", "products", "orders", "payments", "inventory"]); + map.insert( + "vulnerability_type", + vec![ + "SQL injection", + "XSS", + "CSRF", + "authentication", + "authorization", + ], + ); + map.insert( + "attack_type", + vec![ + "DDoS", + "man-in-the-middle", + "replay", + "privilege escalation", + ], + ); + map.insert( + "security_control", + vec!["rate limiting", "CORS", "CSP", "input sanitization"], + ); + map.insert( + "system_type", + vec![ + "distributed", + "event-driven", + "real-time", + "batch processing", + ], + ); + map.insert( + "resource_type", + vec!["users", "products", "orders", "payments", "inventory"], + ); map } @@ -925,7 +1016,10 @@ impl DatasetGenerator { ("design", vec!["architect", "plan", "structure", "outline"]), ("fix", vec!["resolve", "correct", "repair", "patch"]), ("optimize", vec!["improve", "enhance", "refine", "tune"]), - ].iter().cloned().collect(); + ] + .iter() + .cloned() + .collect(); let mut paraphrased_input = example.input.clone(); for (original, alternatives) in ¶phrase_map { @@ -1047,7 +1141,9 @@ mod tests { // Check category distribution for category in TaskCategory::all() { - let count = dataset.stats.examples_per_category + let count = dataset + .stats + .examples_per_category .get(category.name()) .unwrap_or(&0); assert_eq!(*count, 10); diff --git a/crates/ruvllm/src/training/contrastive.rs b/crates/ruvllm/src/training/contrastive.rs index 7e3afa72b..43501b96c 100644 --- a/crates/ruvllm/src/training/contrastive.rs +++ b/crates/ruvllm/src/training/contrastive.rs @@ -196,8 +196,8 @@ impl ContrastiveTrainer { if line.trim().is_empty() { continue; } - let triplet: TrainingTriplet = - serde_json::from_str(&line).map_err(|e| format!("Failed to parse triplet: {}", e))?; + let triplet: TrainingTriplet = serde_json::from_str(&line) + .map_err(|e| format!("Failed to parse triplet: {}", e))?; self.triplets.push(triplet); } @@ -221,13 +221,20 @@ impl ContrastiveTrainer { /// Compute triplet loss #[cfg(feature = "candle")] - fn triplet_loss(&self, anchor: &Tensor, positive: &Tensor, negative: &Tensor) -> CandleResult { + fn triplet_loss( + &self, + anchor: &Tensor, + positive: &Tensor, + negative: &Tensor, + ) -> CandleResult { // L = max(0, margin + d(a,p) - d(a,n)) // where d is cosine distance = 1 - cosine_similarity let anchor_norm = anchor.broadcast_div(&anchor.sqr()?.sum_keepdim(D::Minus1)?.sqrt()?)?; - let positive_norm = positive.broadcast_div(&positive.sqr()?.sum_keepdim(D::Minus1)?.sqrt()?)?; - let negative_norm = negative.broadcast_div(&negative.sqr()?.sum_keepdim(D::Minus1)?.sqrt()?)?; + let positive_norm = + positive.broadcast_div(&positive.sqr()?.sum_keepdim(D::Minus1)?.sqrt()?)?; + let negative_norm = + negative.broadcast_div(&negative.sqr()?.sum_keepdim(D::Minus1)?.sqrt()?)?; let pos_sim = (&anchor_norm * &positive_norm)?.sum(D::Minus1)?; let neg_sim = (&anchor_norm * &negative_norm)?.sum(D::Minus1)?; @@ -245,21 +252,31 @@ impl ContrastiveTrainer { /// Compute InfoNCE loss #[cfg(feature = "candle")] - fn infonce_loss(&self, anchor: &Tensor, positive: &Tensor, negatives: &[Tensor]) -> CandleResult { + fn infonce_loss( + &self, + anchor: &Tensor, + positive: &Tensor, + negatives: &[Tensor], + ) -> CandleResult { let inv_temp = 1.0 / self.config.temperature as f64; // Normalize embeddings let anchor_norm = anchor.broadcast_div(&anchor.sqr()?.sum_keepdim(D::Minus1)?.sqrt()?)?; - let positive_norm = positive.broadcast_div(&positive.sqr()?.sum_keepdim(D::Minus1)?.sqrt()?)?; + let positive_norm = + positive.broadcast_div(&positive.sqr()?.sum_keepdim(D::Minus1)?.sqrt()?)?; // Positive similarity (multiply by 1/temp instead of dividing) - let pos_sim = (&anchor_norm * &positive_norm)?.sum(D::Minus1)?.affine(inv_temp, 0.0)?; + let pos_sim = (&anchor_norm * &positive_norm)? + .sum(D::Minus1)? + .affine(inv_temp, 0.0)?; // Negative similarities let mut all_sims = vec![pos_sim.clone()]; for neg in negatives { let neg_norm = neg.broadcast_div(&neg.sqr()?.sum_keepdim(D::Minus1)?.sqrt()?)?; - let neg_sim = (&anchor_norm * &neg_norm)?.sum(D::Minus1)?.affine(inv_temp, 0.0)?; + let neg_sim = (&anchor_norm * &neg_norm)? + .sum(D::Minus1)? + .affine(inv_temp, 0.0)?; all_sims.push(neg_sim); } @@ -289,8 +306,12 @@ impl ContrastiveTrainer { // Create projection layer for fine-tuning let vb = VarBuilder::from_varmap(&self.var_map, DType::F32, &self.device); - let projection = linear(self.config.embedding_dim, self.config.embedding_dim, vb.pp("projection")) - .map_err(|e| format!("Failed to create projection layer: {}", e))?; + let projection = linear( + self.config.embedding_dim, + self.config.embedding_dim, + vb.pp("projection"), + ) + .map_err(|e| format!("Failed to create projection layer: {}", e))?; // Setup optimizer let params = self.var_map.all_vars(); @@ -345,18 +366,23 @@ impl ContrastiveTrainer { .map_err(|e| format!("Failed to create negative tensor: {}", e))?; // Apply projection - let anchor_proj = projection.forward(&anchor) + let anchor_proj = projection + .forward(&anchor) .map_err(|e| format!("Forward pass failed: {}", e))?; - let positive_proj = projection.forward(&positive) + let positive_proj = projection + .forward(&positive) .map_err(|e| format!("Forward pass failed: {}", e))?; - let negative_proj = projection.forward(&negative) + let negative_proj = projection + .forward(&negative) .map_err(|e| format!("Forward pass failed: {}", e))?; // Compute losses - let triplet_loss = self.triplet_loss(&anchor_proj, &positive_proj, &negative_proj) + let triplet_loss = self + .triplet_loss(&anchor_proj, &positive_proj, &negative_proj) .map_err(|e| format!("Triplet loss failed: {}", e))?; - let infonce_loss = self.infonce_loss(&anchor_proj, &positive_proj, &[negative_proj.clone()]) + let infonce_loss = self + .infonce_loss(&anchor_proj, &positive_proj, &[negative_proj.clone()]) .map_err(|e| format!("InfoNCE loss failed: {}", e))?; // Combined loss @@ -364,13 +390,16 @@ impl ContrastiveTrainer { .map_err(|e| format!("Loss combination failed: {}", e))?; // Backward pass - optimizer.backward_step(&total_loss) + optimizer + .backward_step(&total_loss) .map_err(|e| format!("Backward step failed: {}", e))?; // Track statistics - let triplet_val: f32 = triplet_loss.to_vec0() + let triplet_val: f32 = triplet_loss + .to_vec0() .map_err(|e| format!("Failed to get loss value: {}", e))?; - let infonce_val: f32 = infonce_loss.to_vec0() + let infonce_val: f32 = infonce_loss + .to_vec0() .map_err(|e| format!("Failed to get loss value: {}", e))?; total_triplet_loss += triplet_val as f64; @@ -504,12 +533,16 @@ impl ContrastiveTrainer { } /// Export training statistics - pub fn export_stats>(&self, result: &TrainingResult, path: P) -> Result<(), String> { + pub fn export_stats>( + &self, + result: &TrainingResult, + path: P, + ) -> Result<(), String> { let json = serde_json::to_string_pretty(result) .map_err(|e| format!("Failed to serialize stats: {}", e))?; - let mut file = File::create(path) - .map_err(|e| format!("Failed to create stats file: {}", e))?; + let mut file = + File::create(path).map_err(|e| format!("Failed to create stats file: {}", e))?; file.write_all(json.as_bytes()) .map_err(|e| format!("Failed to write stats: {}", e))?; @@ -548,7 +581,11 @@ mod tests { #[test] fn test_load_triplets() { let mut file = NamedTempFile::new().unwrap(); - writeln!(file, r#"{{"anchor":"test task","positive":"coder","negative":"tester","is_hard":true}}"#).unwrap(); + writeln!( + file, + r#"{{"anchor":"test task","positive":"coder","negative":"tester","is_hard":true}}"# + ) + .unwrap(); writeln!(file, r#"{{"anchor":"another task","positive":"researcher","negative":"coder","is_hard":false}}"#).unwrap(); let config = ContrastiveConfig::default(); @@ -562,9 +599,21 @@ mod tests { #[test] fn test_hard_negative_ratio() { let mut file = NamedTempFile::new().unwrap(); - writeln!(file, r#"{{"anchor":"t1","positive":"coder","negative":"tester","is_hard":true}}"#).unwrap(); - writeln!(file, r#"{{"anchor":"t2","positive":"coder","negative":"tester","is_hard":true}}"#).unwrap(); - writeln!(file, r#"{{"anchor":"t3","positive":"coder","negative":"tester","is_hard":false}}"#).unwrap(); + writeln!( + file, + r#"{{"anchor":"t1","positive":"coder","negative":"tester","is_hard":true}}"# + ) + .unwrap(); + writeln!( + file, + r#"{{"anchor":"t2","positive":"coder","negative":"tester","is_hard":true}}"# + ) + .unwrap(); + writeln!( + file, + r#"{{"anchor":"t3","positive":"coder","negative":"tester","is_hard":false}}"# + ) + .unwrap(); let config = ContrastiveConfig::default(); let mut trainer = ContrastiveTrainer::new(config).unwrap(); diff --git a/crates/ruvllm/src/training/grpo.rs b/crates/ruvllm/src/training/grpo.rs index c6ff090db..aa99db04c 100644 --- a/crates/ruvllm/src/training/grpo.rs +++ b/crates/ruvllm/src/training/grpo.rs @@ -294,18 +294,12 @@ impl GrpoOptimizer { let mean = rewards.iter().sum::() / rewards.len() as f32; // Compute group std - let variance = rewards - .iter() - .map(|r| (r - mean).powi(2)) - .sum::() - / rewards.len() as f32; + let variance = + rewards.iter().map(|r| (r - mean).powi(2)).sum::() / rewards.len() as f32; let std = variance.sqrt().max(1e-8); // Compute relative advantages - rewards - .iter() - .map(|r| (r - mean) / std) - .collect() + rewards.iter().map(|r| (r - mean) / std).collect() } /// Compute generalized advantage estimation (GAE) @@ -390,7 +384,8 @@ impl GrpoOptimizer { let mut clip_count = 0; for (ratio, adv) in ratios.iter().zip(normalized_advantages.iter()) { let surr1 = ratio * adv; - let surr2 = ratio.clamp(1.0 - self.config.clip_range, 1.0 + self.config.clip_range) * adv; + let surr2 = + ratio.clamp(1.0 - self.config.clip_range, 1.0 + self.config.clip_range) * adv; policy_loss -= surr1.min(surr2); @@ -468,17 +463,11 @@ impl GrpoOptimizer { } let mean = advantages.iter().sum::() / advantages.len() as f32; - let variance = advantages - .iter() - .map(|a| (a - mean).powi(2)) - .sum::() - / advantages.len() as f32; + let variance = + advantages.iter().map(|a| (a - mean).powi(2)).sum::() / advantages.len() as f32; let std = variance.sqrt().max(1e-8); - advantages - .iter() - .map(|a| (a - mean) / std) - .collect() + advantages.iter().map(|a| (a - mean) / std).collect() } /// Add experience sample to buffer @@ -620,9 +609,7 @@ impl GrpoBatch { // Placeholder advantages and returns (would be computed) let advantages = Array1::zeros(n); let returns = Array1::zeros(n); - let values = Array1::from_vec( - samples.iter().map(|s| s.value.unwrap_or(0.0)).collect() - ); + let values = Array1::from_vec(samples.iter().map(|s| s.value.unwrap_or(0.0)).collect()); Some(Self { states, @@ -707,12 +694,14 @@ mod tests { assert!(mean.abs() < 1e-5); // Highest reward should have highest advantage - let max_reward_idx = rewards.iter() + let max_reward_idx = rewards + .iter() .enumerate() .max_by(|a, b| a.1.partial_cmp(b.1).unwrap()) .map(|(i, _)| i) .unwrap(); - let max_advantage_idx = advantages.iter() + let max_advantage_idx = advantages + .iter() .enumerate() .max_by(|a, b| a.1.partial_cmp(b.1).unwrap()) .map(|(i, _)| i) @@ -728,7 +717,9 @@ mod tests { let advantages = vec![0.5, 0.2, -0.3, 0.1]; let ref_log_probs = vec![-0.5, -0.3, -0.7, -0.4]; // Same as current - let result = optimizer.grpo_update(&log_probs, &advantages, &ref_log_probs).unwrap(); + let result = optimizer + .grpo_update(&log_probs, &advantages, &ref_log_probs) + .unwrap(); assert_eq!(result.num_samples, 4); assert!(result.kl_divergence.abs() < 1e-5); // No KL when same policy diff --git a/crates/ruvllm/src/training/mcp_tools.rs b/crates/ruvllm/src/training/mcp_tools.rs index 77b252ea9..4548211e7 100644 --- a/crates/ruvllm/src/training/mcp_tools.rs +++ b/crates/ruvllm/src/training/mcp_tools.rs @@ -49,8 +49,7 @@ use crate::error::{Result, RuvLLMError}; use crate::training::grpo::{GrpoConfig, GrpoOptimizer, GrpoSample, GrpoUpdateResult, SampleGroup}; use crate::training::tool_dataset::{ - DifficultyLevel, McpToolDef, ToolCallDataset, ToolCallExample, - ToolDatasetConfig, + DifficultyLevel, McpToolDef, ToolCallDataset, ToolCallExample, ToolDatasetConfig, }; use ndarray::Array2; use parking_lot::RwLock; @@ -498,7 +497,10 @@ impl McpToolTrainer { } /// Evaluate tool selection accuracy on a test set - pub fn evaluate_tool_accuracy(&self, test_examples: &[ToolCallExample]) -> Result { + pub fn evaluate_tool_accuracy( + &self, + test_examples: &[ToolCallExample], + ) -> Result { if test_examples.is_empty() { return Ok(EvaluationMetrics::default()); } @@ -550,12 +552,16 @@ impl McpToolTrainer { // Convert category stats for (cat, (c, t)) in by_category { - metrics.accuracy_by_category.insert(cat, c as f32 / t as f32); + metrics + .accuracy_by_category + .insert(cat, c as f32 / t as f32); } // Convert difficulty stats for (diff, (c, t)) in by_difficulty { - metrics.accuracy_by_difficulty.insert(diff, c as f32 / t as f32); + metrics + .accuracy_by_difficulty + .insert(diff, c as f32 / t as f32); } metrics.confusion = confusion; @@ -675,8 +681,16 @@ impl McpToolTrainer { /// Check if two tools are in the same category fn same_category(&self, tool1: &str, tool2: &str) -> bool { - let cat1 = self.tool_defs.iter().find(|t| t.name == tool1).map(|t| t.category); - let cat2 = self.tool_defs.iter().find(|t| t.name == tool2).map(|t| t.category); + let cat1 = self + .tool_defs + .iter() + .find(|t| t.name == tool1) + .map(|t| t.category); + let cat2 = self + .tool_defs + .iter() + .find(|t| t.name == tool2) + .map(|t| t.category); cat1.is_some() && cat1 == cat2 } @@ -701,7 +715,11 @@ impl McpToolTrainer { stats: self.stats.read().clone(), grpo_stats: self.grpo.stats(), tool_embeddings: { - let (vec, _offset) = self.tool_embeddings.read().clone().into_raw_vec_and_offset(); + let (vec, _offset) = self + .tool_embeddings + .read() + .clone() + .into_raw_vec_and_offset(); vec }, embedding_shape: { @@ -719,10 +737,8 @@ impl McpToolTrainer { let (rows, cols) = checkpoint.embedding_shape; if checkpoint.tool_embeddings.len() == rows * cols { - let embeddings = Array2::from_shape_vec( - (rows, cols), - checkpoint.tool_embeddings, - ).map_err(|e| RuvLLMError::InvalidOperation(e.to_string()))?; + let embeddings = Array2::from_shape_vec((rows, cols), checkpoint.tool_embeddings) + .map_err(|e| RuvLLMError::InvalidOperation(e.to_string()))?; *self.tool_embeddings.write() = embeddings; } @@ -1024,7 +1040,9 @@ mod tests { let dataset_config = ToolDatasetConfig::minimal(); let dataset = trainer.generate_tool_dataset(dataset_config).unwrap(); - let metrics = trainer.evaluate_tool_accuracy(&dataset.examples[..5]).unwrap(); + let metrics = trainer + .evaluate_tool_accuracy(&dataset.examples[..5]) + .unwrap(); assert!(metrics.num_samples == 5); assert!(metrics.tool_accuracy >= 0.0 && metrics.tool_accuracy <= 1.0); } diff --git a/crates/ruvllm/src/training/mod.rs b/crates/ruvllm/src/training/mod.rs index 33a71e438..cd302dd7e 100644 --- a/crates/ruvllm/src/training/mod.rs +++ b/crates/ruvllm/src/training/mod.rs @@ -53,8 +53,8 @@ pub use grpo::{ // MCP tool training exports pub use mcp_tools::{ EvaluationMetrics, McpToolTrainer, McpTrainingConfig, StepBuilder, ToolTrajectory, - TrajectoryBuilder, TrajectoryMetadata, TrajectoryStep, TrainingCheckpoint, TrainingResult, - TrainingStats, + TrainingCheckpoint, TrainingResult, TrainingStats, TrajectoryBuilder, TrajectoryMetadata, + TrajectoryStep, }; // Tool dataset exports @@ -65,14 +65,13 @@ pub use tool_dataset::{ // Contrastive learning exports pub use contrastive::{ - AgentEmbedding, ContrastiveConfig, ContrastiveTrainer, - TrainingResult as ContrastiveResult, TrainingStats as ContrastiveStats, - TrainingTriplet, AGENT_DESCRIPTIONS, + AgentEmbedding, ContrastiveConfig, ContrastiveTrainer, TrainingResult as ContrastiveResult, + TrainingStats as ContrastiveStats, TrainingTriplet, AGENT_DESCRIPTIONS, }; // Real trainer exports (Candle-based with GGUF export) pub use real_trainer::{ - EpochStats, GgufExportMetadata, GgufExportResult, GrpoEvaluator, GrpoFeedback, - LayerMetadata, RealContrastiveTrainer, RealTrainingConfig, RealTrainingResult, - TrainingConfigMeta, run_training_pipeline, + run_training_pipeline, EpochStats, GgufExportMetadata, GgufExportResult, GrpoEvaluator, + GrpoFeedback, LayerMetadata, RealContrastiveTrainer, RealTrainingConfig, RealTrainingResult, + TrainingConfigMeta, }; diff --git a/crates/ruvllm/src/training/real_trainer.rs b/crates/ruvllm/src/training/real_trainer.rs index 66039b968..630919765 100644 --- a/crates/ruvllm/src/training/real_trainer.rs +++ b/crates/ruvllm/src/training/real_trainer.rs @@ -239,14 +239,21 @@ impl RealContrastiveTrainer { return Err("No triplets loaded".to_string()); } - println!("═══════════════════════════════════════════════════════════════════════════════════"); + println!( + "═══════════════════════════════════════════════════════════════════════════════════" + ); println!(" REAL CONTRASTIVE TRAINING "); - println!("═══════════════════════════════════════════════════════════════════════════════════\n"); + println!( + "═══════════════════════════════════════════════════════════════════════════════════\n" + ); println!("Configuration:"); println!(" Model: {}", self.config.model_path.display()); println!(" Triplets: {}", self.triplets.len()); - println!(" Hard Negatives: {:.1}%", self.hard_negative_ratio() * 100.0); + println!( + " Hard Negatives: {:.1}%", + self.hard_negative_ratio() * 100.0 + ); println!(" Epochs: {}", self.config.epochs); println!(" Batch Size: {}", self.config.batch_size); println!(" Learning Rate: {}", self.config.learning_rate); @@ -261,20 +268,23 @@ impl RealContrastiveTrainer { self.config.embedding_dim, self.config.embedding_dim, vb.pp("embed_projection"), - ).map_err(|e| format!("Failed to create projection: {}", e))?; + ) + .map_err(|e| format!("Failed to create projection: {}", e))?; // Additional MLP for better representation let mlp_hidden = linear( self.config.embedding_dim, self.config.embedding_dim * 2, vb.pp("mlp_hidden"), - ).map_err(|e| format!("Failed to create MLP hidden: {}", e))?; + ) + .map_err(|e| format!("Failed to create MLP hidden: {}", e))?; let mlp_output = linear( self.config.embedding_dim * 2, self.config.embedding_dim, vb.pp("mlp_output"), - ).map_err(|e| format!("Failed to create MLP output: {}", e))?; + ) + .map_err(|e| format!("Failed to create MLP output: {}", e))?; // Setup optimizer with weight decay let params = self.var_map.all_vars(); @@ -287,7 +297,8 @@ impl RealContrastiveTrainer { beta2: 0.999, eps: 1e-8, }, - ).map_err(|e| format!("Failed to create optimizer: {}", e))?; + ) + .map_err(|e| format!("Failed to create optimizer: {}", e))?; let mut history = Vec::new(); let mut checkpoints = Vec::new(); @@ -338,31 +349,42 @@ impl RealContrastiveTrainer { .map_err(|e| format!("Anchor tensor failed: {}", e))?; let positive_data = self.agent_to_embedding_batch( - &batch.iter().map(|t| t.positive.as_str()).collect::>(), + &batch + .iter() + .map(|t| t.positive.as_str()) + .collect::>(), ); let positive = Tensor::from_slice(&positive_data, (batch_size, dim), &self.device) .map_err(|e| format!("Positive tensor failed: {}", e))?; let negative_data = self.agent_to_embedding_batch( - &batch.iter().map(|t| t.negative.as_str()).collect::>(), + &batch + .iter() + .map(|t| t.negative.as_str()) + .collect::>(), ); let negative = Tensor::from_slice(&negative_data, (batch_size, dim), &self.device) .map_err(|e| format!("Negative tensor failed: {}", e))?; // Forward pass through trainable layers - let anchor_proj = self.forward_mlp(&projection, &mlp_hidden, &mlp_output, &anchor)?; - let positive_proj = self.forward_mlp(&projection, &mlp_hidden, &mlp_output, &positive)?; - let negative_proj = self.forward_mlp(&projection, &mlp_hidden, &mlp_output, &negative)?; + let anchor_proj = + self.forward_mlp(&projection, &mlp_hidden, &mlp_output, &anchor)?; + let positive_proj = + self.forward_mlp(&projection, &mlp_hidden, &mlp_output, &positive)?; + let negative_proj = + self.forward_mlp(&projection, &mlp_hidden, &mlp_output, &negative)?; // Compute losses - let triplet_loss = self.triplet_loss(&anchor_proj, &positive_proj, &negative_proj)?; - let infonce_loss = self.infonce_loss(&anchor_proj, &positive_proj, &[negative_proj.clone()])?; + let triplet_loss = + self.triplet_loss(&anchor_proj, &positive_proj, &negative_proj)?; + let infonce_loss = + self.infonce_loss(&anchor_proj, &positive_proj, &[negative_proj.clone()])?; // Apply GRPO reward scaling if enabled let grpo_scale = if self.config.enable_grpo && !self.grpo_feedback.is_empty() { let avg_reward: f64 = self.grpo_feedback.iter().map(|f| f.reward).sum::() / self.grpo_feedback.len() as f64; - 1.0 + avg_reward * 0.1 // Scale loss by reward + 1.0 + avg_reward * 0.1 // Scale loss by reward } else { 1.0 }; @@ -370,17 +392,20 @@ impl RealContrastiveTrainer { // Combined loss with GRPO scaling let combined = (&triplet_loss + &infonce_loss) .map_err(|e| format!("Loss combination failed: {}", e))?; - let total_loss = (combined * grpo_scale) - .map_err(|e| format!("GRPO scaling failed: {}", e))?; + let total_loss = + (combined * grpo_scale).map_err(|e| format!("GRPO scaling failed: {}", e))?; // Backward pass with gradient clipping - optimizer.backward_step(&total_loss) + optimizer + .backward_step(&total_loss) .map_err(|e| format!("Backward step failed: {}", e))?; // Track statistics - let triplet_val: f32 = triplet_loss.to_vec0() + let triplet_val: f32 = triplet_loss + .to_vec0() .map_err(|e| format!("Loss extraction failed: {}", e))?; - let infonce_val: f32 = infonce_loss.to_vec0() + let infonce_val: f32 = infonce_loss + .to_vec0() .map_err(|e| format!("Loss extraction failed: {}", e))?; total_triplet_loss += triplet_val as f64; @@ -444,12 +469,15 @@ impl RealContrastiveTrainer { // Save checkpoint if (epoch + 1) % self.config.checkpoint_every == 0 { - let checkpoint_path = self.config.output_path - .with_file_name(format!( - "{}-checkpoint-{}.gguf", - self.config.output_path.file_stem().unwrap().to_string_lossy(), - epoch + 1 - )); + let checkpoint_path = self.config.output_path.with_file_name(format!( + "{}-checkpoint-{}.gguf", + self.config + .output_path + .file_stem() + .unwrap() + .to_string_lossy(), + epoch + 1 + )); // In real implementation, save model weights here checkpoints.push(checkpoint_path); } @@ -489,30 +517,33 @@ impl RealContrastiveTrainer { input: &Tensor, ) -> Result { // Projection - let x = projection.forward(input) + let x = projection + .forward(input) .map_err(|e| format!("Projection forward failed: {}", e))?; // MLP with GELU activation - let hidden = mlp_hidden.forward(&x) + let hidden = mlp_hidden + .forward(&x) .map_err(|e| format!("MLP hidden forward failed: {}", e))?; - let activated = hidden.gelu() - .map_err(|e| format!("GELU failed: {}", e))?; - let output = mlp_output.forward(&activated) + let activated = hidden.gelu().map_err(|e| format!("GELU failed: {}", e))?; + let output = mlp_output + .forward(&activated) .map_err(|e| format!("MLP output forward failed: {}", e))?; // Residual connection + layer norm (simplified) - let result = (&x + &output) - .map_err(|e| format!("Residual connection failed: {}", e))?; + let result = (&x + &output).map_err(|e| format!("Residual connection failed: {}", e))?; // L2 normalize for cosine similarity - let norm = result.sqr() + let norm = result + .sqr() .map_err(|e| format!("Sqr failed: {}", e))? .sum_keepdim(D::Minus1) .map_err(|e| format!("Sum failed: {}", e))? .sqrt() .map_err(|e| format!("Sqrt failed: {}", e))?; - result.broadcast_div(&norm) + result + .broadcast_div(&norm) .map_err(|e| format!("Normalize failed: {}", e)) } @@ -539,17 +570,20 @@ impl RealContrastiveTrainer { let margin = Tensor::new(&[self.config.margin as f32], &self.device) .map_err(|e| format!("Margin tensor failed: {}", e))?; - let zero = Tensor::zeros_like(&pos_dist) - .map_err(|e| format!("Zero tensor failed: {}", e))?; + let zero = + Tensor::zeros_like(&pos_dist).map_err(|e| format!("Zero tensor failed: {}", e))?; let pos_dist_shape = pos_dist.shape().clone(); - let loss = (pos_dist - neg_dist + margin.broadcast_as(&pos_dist_shape) - .map_err(|e| format!("Margin broadcast failed: {}", e))?) - .map_err(|e| format!("Loss calc failed: {}", e))? - .maximum(&zero) - .map_err(|e| format!("Maximum failed: {}", e))?; + let loss = (pos_dist - neg_dist + + margin + .broadcast_as(&pos_dist_shape) + .map_err(|e| format!("Margin broadcast failed: {}", e))?) + .map_err(|e| format!("Loss calc failed: {}", e))? + .maximum(&zero) + .map_err(|e| format!("Maximum failed: {}", e))?; - loss.mean(D::Minus1).map_err(|e| format!("Mean failed: {}", e)) + loss.mean(D::Minus1) + .map_err(|e| format!("Mean failed: {}", e)) } /// Compute InfoNCE loss @@ -580,13 +614,13 @@ impl RealContrastiveTrainer { all_sims.push(neg_sim); } - let stacked = Tensor::stack(&all_sims, 0) - .map_err(|e| format!("Stack failed: {}", e))?; - let log_softmax = ops::log_softmax(&stacked, 0) - .map_err(|e| format!("Log softmax failed: {}", e))?; + let stacked = Tensor::stack(&all_sims, 0).map_err(|e| format!("Stack failed: {}", e))?; + let log_softmax = + ops::log_softmax(&stacked, 0).map_err(|e| format!("Log softmax failed: {}", e))?; // Get first element (positive similarity) from log_softmax - let pos_log_prob = log_softmax.get(0) + let pos_log_prob = log_softmax + .get(0) .map_err(|e| format!("Index failed: {}", e))?; pos_log_prob @@ -604,7 +638,8 @@ impl RealContrastiveTrainer { .sum(D::Minus1) .map_err(|e| format!("Distance sum failed: {}", e))?; let dist = (1.0 - sim).map_err(|e| format!("Distance sub failed: {}", e))?; - dist.to_vec1().map_err(|e| format!("Distance vec failed: {}", e)) + dist.to_vec1() + .map_err(|e| format!("Distance vec failed: {}", e)) } /// Convert text to embedding using deterministic hash @@ -615,8 +650,9 @@ impl RealContrastiveTrainer { for text in texts { let hash = self.hash_text(text); for i in 0..dim { - let val = ((hash.wrapping_add(i as u64) as f64 / u64::MAX as f64) * 2.0 - 1.0) as f32; - embeddings.push(val * 0.1); // Scale down + let val = + ((hash.wrapping_add(i as u64) as f64 / u64::MAX as f64) * 2.0 - 1.0) as f32; + embeddings.push(val * 0.1); // Scale down } } @@ -631,7 +667,8 @@ impl RealContrastiveTrainer { for agent in agents { let base_hash = self.hash_text(agent); for i in 0..dim { - let val = ((base_hash.wrapping_mul(i as u64 + 1) as f64 / u64::MAX as f64) * 2.0 - 1.0) as f32; + let val = ((base_hash.wrapping_mul(i as u64 + 1) as f64 / u64::MAX as f64) * 2.0 + - 1.0) as f32; embeddings.push(val * 0.1); } } @@ -657,9 +694,13 @@ impl RealContrastiveTrainer { pub fn export_gguf>(&self, path: P) -> Result { let path = path.as_ref(); - println!("\n═══════════════════════════════════════════════════════════════════════════════════"); + println!( + "\n═══════════════════════════════════════════════════════════════════════════════════" + ); println!(" GGUF EXPORT"); - println!("═══════════════════════════════════════════════════════════════════════════════════\n"); + println!( + "═══════════════════════════════════════════════════════════════════════════════════\n" + ); println!("Exporting trained model to: {}", path.display()); @@ -694,16 +735,20 @@ impl RealContrastiveTrainer { for (name, size, weights) in &layer_info { // Write layer header let name_bytes = name.as_bytes(); - weights_file.write_all(&(name_bytes.len() as u32).to_le_bytes()) + weights_file + .write_all(&(name_bytes.len() as u32).to_le_bytes()) .map_err(|e| format!("Write failed: {}", e))?; - weights_file.write_all(name_bytes) + weights_file + .write_all(name_bytes) .map_err(|e| format!("Write failed: {}", e))?; - weights_file.write_all(&(*size as u64).to_le_bytes()) + weights_file + .write_all(&(*size as u64).to_le_bytes()) .map_err(|e| format!("Write failed: {}", e))?; // Write weights as f32 little-endian for w in weights { - weights_file.write_all(&w.to_le_bytes()) + weights_file + .write_all(&w.to_le_bytes()) .map_err(|e| format!("Write failed: {}", e))?; } } @@ -717,11 +762,14 @@ impl RealContrastiveTrainer { total_weights, embedding_dim: self.config.embedding_dim, architecture: "projection_mlp".to_string(), - layers: layer_info.iter().map(|(n, s, _)| LayerMetadata { - name: n.clone(), - size: *s, - dtype: "f32".to_string(), - }).collect(), + layers: layer_info + .iter() + .map(|(n, s, _)| LayerMetadata { + name: n.clone(), + size: *s, + dtype: "f32".to_string(), + }) + .collect(), training_config: TrainingConfigMeta { epochs: self.config.epochs, learning_rate: self.config.learning_rate, @@ -737,12 +785,14 @@ impl RealContrastiveTrainer { let metadata_path = weights_dir.join("metadata.json"); let mut metadata_file = File::create(&metadata_path) .map_err(|e| format!("Failed to create metadata file: {}", e))?; - metadata_file.write_all(serde_json::to_string_pretty(&metadata).unwrap().as_bytes()) + metadata_file + .write_all(serde_json::to_string_pretty(&metadata).unwrap().as_bytes()) .map_err(|e| format!("Failed to write metadata: {}", e))?; println!(" Metadata saved to: {}", metadata_path.display()); // Create merge script for llama.cpp - let merge_script = format!(r#"#!/bin/bash + let merge_script = format!( + r#"#!/bin/bash # Merge trained adapter with base GGUF model # Requires: llama.cpp build with gguf-py @@ -770,9 +820,10 @@ echo " Install: pip install gguf" ); let script_path = weights_dir.join("merge_adapter.sh"); - let mut script_file = File::create(&script_path) - .map_err(|e| format!("Failed to create script: {}", e))?; - script_file.write_all(merge_script.as_bytes()) + let mut script_file = + File::create(&script_path).map_err(|e| format!("Failed to create script: {}", e))?; + script_file + .write_all(merge_script.as_bytes()) .map_err(|e| format!("Failed to write script: {}", e))?; println!(" Merge script saved to: {}", script_path.display()); @@ -812,7 +863,9 @@ pub async fn run_training_pipeline( ) -> Result { println!("═══════════════════════════════════════════════════════════════════════════════════"); println!(" COMPLETE TRAINING PIPELINE WITH GRPO FEEDBACK"); - println!("═══════════════════════════════════════════════════════════════════════════════════\n"); + println!( + "═══════════════════════════════════════════════════════════════════════════════════\n" + ); // Phase 1: Load config and triplets let config = RealTrainingConfig { @@ -824,8 +877,11 @@ pub async fn run_training_pipeline( let mut trainer = RealContrastiveTrainer::new(config)?; let triplet_count = trainer.load_triplets(triplets_path)?; - println!("Phase 1: Loaded {} triplets ({:.1}% hard negatives)\n", - triplet_count, trainer.hard_negative_ratio() * 100.0); + println!( + "Phase 1: Loaded {} triplets ({:.1}% hard negatives)\n", + triplet_count, + trainer.hard_negative_ratio() * 100.0 + ); // Phase 2: Initial training println!("Phase 2: Initial contrastive training...\n"); @@ -836,7 +892,8 @@ pub async fn run_training_pipeline( println!("\nPhase 3: GRPO feedback loop...\n"); // Collect predictions for evaluation - let predictions: Vec<(String, String, String)> = trainer.triplets + let predictions: Vec<(String, String, String)> = trainer + .triplets .iter() .take(20) // Sample 20 for GRPO .map(|t| (t.anchor.clone(), t.positive.clone(), t.positive.clone())) @@ -888,7 +945,10 @@ impl GrpoEvaluator { } /// Evaluate predictions and generate feedback - pub async fn evaluate(&self, predictions: &[(String, String, String)]) -> Result, String> { + pub async fn evaluate( + &self, + predictions: &[(String, String, String)], + ) -> Result, String> { // In real implementation, this would call Claude API // For now, return simulated feedback diff --git a/crates/ruvllm/src/training/tests.rs b/crates/ruvllm/src/training/tests.rs index 5e9b950d0..9692c327d 100644 --- a/crates/ruvllm/src/training/tests.rs +++ b/crates/ruvllm/src/training/tests.rs @@ -33,10 +33,17 @@ mod tests { // Check each category has exactly 10 examples for category in TaskCategory::all() { - let count = dataset.stats.examples_per_category + let count = dataset + .stats + .examples_per_category .get(category.name()) .unwrap_or(&0); - assert_eq!(*count, 10, "Category {} should have 10 examples", category.name()); + assert_eq!( + *count, + 10, + "Category {} should have 10 examples", + category.name() + ); } } @@ -163,9 +170,21 @@ mod tests { let val_ratio = val.len() as f32 / total as f32; let test_ratio = test.len() as f32 / total as f32; - assert!((train_ratio - 0.7).abs() < 0.05, "Train ratio should be ~0.7: {}", train_ratio); - assert!((val_ratio - 0.15).abs() < 0.05, "Val ratio should be ~0.15: {}", val_ratio); - assert!((test_ratio - 0.15).abs() < 0.05, "Test ratio should be ~0.15: {}", test_ratio); + assert!( + (train_ratio - 0.7).abs() < 0.05, + "Train ratio should be ~0.7: {}", + train_ratio + ); + assert!( + (val_ratio - 0.15).abs() < 0.05, + "Val ratio should be ~0.15: {}", + val_ratio + ); + assert!( + (test_ratio - 0.15).abs() < 0.05, + "Test ratio should be ~0.15: {}", + test_ratio + ); } #[test] @@ -261,7 +280,10 @@ mod tests { } // Should see multiple domains - assert!(domains_seen.len() >= 3, "Should have at least 3 different domains"); + assert!( + domains_seen.len() >= 3, + "Should have at least 3 different domains" + ); } #[test] diff --git a/crates/ruvllm/src/training/tool_dataset.rs b/crates/ruvllm/src/training/tool_dataset.rs index 91ed31285..da24924ca 100644 --- a/crates/ruvllm/src/training/tool_dataset.rs +++ b/crates/ruvllm/src/training/tool_dataset.rs @@ -410,7 +410,11 @@ impl ToolCallDataset { train_ratio: f32, val_ratio: f32, seed: u64, - ) -> (Vec, Vec, Vec) { + ) -> ( + Vec, + Vec, + Vec, + ) { let mut rng = StdRng::seed_from_u64(seed); let mut examples = self.examples.clone(); examples.shuffle(&mut rng); @@ -550,7 +554,10 @@ impl ToolDatasetGenerator { let error_types = [ ("Missing required parameter", "Parameter validation failed"), ("Invalid parameter type", "Type mismatch error"), - ("Resource not found", "The specified resource does not exist"), + ( + "Resource not found", + "The specified resource does not exist", + ), ("Permission denied", "Insufficient permissions"), ("Rate limited", "Too many requests"), ]; @@ -618,7 +625,11 @@ impl ToolDatasetGenerator { } /// Generate parameters for a tool - fn generate_params(&mut self, tool: &McpToolDef, _difficulty: DifficultyLevel) -> serde_json::Value { + fn generate_params( + &mut self, + tool: &McpToolDef, + _difficulty: DifficultyLevel, + ) -> serde_json::Value { let mut params = serde_json::Map::new(); // Add required parameters @@ -667,7 +678,9 @@ impl ToolDatasetGenerator { } match param.param_type { - ParamType::String => serde_json::Value::String(format!("example_{}", self.rng.gen::())), + ParamType::String => { + serde_json::Value::String(format!("example_{}", self.rng.gen::())) + } ParamType::Integer => serde_json::Value::Number((self.rng.gen_range(1..100)).into()), ParamType::Boolean => serde_json::Value::Bool(self.rng.gen()), ParamType::Float => { @@ -705,7 +718,10 @@ impl ToolDatasetGenerator { ], DifficultyLevel::Expert => vec![ format!("Edge case handling for {}", tool.name), - format!("Production scenario with {} error handling", tool.category.name()), + format!( + "Production scenario with {} error handling", + tool.category.name() + ), ], }; @@ -780,7 +796,12 @@ impl ToolDatasetGenerator { name: "agentType".to_string(), param_type: ParamType::String, description: "Type of agent to spawn".to_string(), - examples: vec!["coder".to_string(), "researcher".to_string(), "tester".to_string(), "reviewer".to_string()], + examples: vec![ + "coder".to_string(), + "researcher".to_string(), + "tester".to_string(), + "reviewer".to_string(), + ], }], optional_params: vec![ ToolParam { @@ -793,13 +814,20 @@ impl ToolDatasetGenerator { name: "model".to_string(), param_type: ParamType::Enum, description: "Claude model to use".to_string(), - examples: vec!["haiku".to_string(), "sonnet".to_string(), "opus".to_string()], + examples: vec![ + "haiku".to_string(), + "sonnet".to_string(), + "opus".to_string(), + ], }, ToolParam { name: "task".to_string(), param_type: ParamType::String, description: "Task description for model routing".to_string(), - examples: vec!["implement authentication".to_string(), "write tests".to_string()], + examples: vec![ + "implement authentication".to_string(), + "write tests".to_string(), + ], }, ], use_cases: vec![ @@ -858,7 +886,11 @@ impl ToolDatasetGenerator { name: "status".to_string(), param_type: ParamType::String, description: "Filter by status".to_string(), - examples: vec!["running".to_string(), "idle".to_string(), "terminated".to_string()], + examples: vec![ + "running".to_string(), + "idle".to_string(), + "terminated".to_string(), + ], }, ToolParam { name: "includeTerminated".to_string(), @@ -881,16 +913,19 @@ impl ToolDatasetGenerator { name: "action".to_string(), param_type: ParamType::Enum, description: "Pool action".to_string(), - examples: vec!["status".to_string(), "scale".to_string(), "drain".to_string(), "fill".to_string()], + examples: vec![ + "status".to_string(), + "scale".to_string(), + "drain".to_string(), + "fill".to_string(), + ], + }], + optional_params: vec![ToolParam { + name: "targetSize".to_string(), + param_type: ParamType::Integer, + description: "Target pool size".to_string(), + examples: vec!["5".to_string(), "10".to_string()], }], - optional_params: vec![ - ToolParam { - name: "targetSize".to_string(), - param_type: ParamType::Integer, - description: "Target pool size".to_string(), - examples: vec!["5".to_string(), "10".to_string()], - }, - ], use_cases: vec![ "scale the agent pool to handle increased load".to_string(), "drain the pool before maintenance".to_string(), @@ -1058,7 +1093,11 @@ impl ToolDatasetGenerator { name: "topology".to_string(), param_type: ParamType::Enum, description: "Swarm topology type".to_string(), - examples: vec!["hierarchical".to_string(), "mesh".to_string(), "star".to_string()], + examples: vec![ + "hierarchical".to_string(), + "mesh".to_string(), + "star".to_string(), + ], }, ToolParam { name: "maxAgents".to_string(), @@ -1142,7 +1181,11 @@ impl ToolDatasetGenerator { name: "type".to_string(), param_type: ParamType::Enum, description: "Task type".to_string(), - examples: vec!["feature".to_string(), "bugfix".to_string(), "research".to_string()], + examples: vec![ + "feature".to_string(), + "bugfix".to_string(), + "research".to_string(), + ], }, ToolParam { name: "description".to_string(), @@ -1156,7 +1199,12 @@ impl ToolDatasetGenerator { name: "priority".to_string(), param_type: ParamType::Enum, description: "Task priority".to_string(), - examples: vec!["low".to_string(), "normal".to_string(), "high".to_string(), "critical".to_string()], + examples: vec![ + "low".to_string(), + "normal".to_string(), + "high".to_string(), + "critical".to_string(), + ], }, ToolParam { name: "assignTo".to_string(), @@ -1198,7 +1246,11 @@ impl ToolDatasetGenerator { name: "status".to_string(), param_type: ParamType::String, description: "Filter by status".to_string(), - examples: vec!["pending".to_string(), "in_progress".to_string(), "completed".to_string()], + examples: vec![ + "pending".to_string(), + "in_progress".to_string(), + "completed".to_string(), + ], }, ToolParam { name: "priority".to_string(), @@ -1239,7 +1291,9 @@ impl ToolDatasetGenerator { tools.push(McpToolDef { name: "hooks_pre-task".to_string(), category: ToolCategory::HooksLearning, - description: "Record task start and get agent suggestions with intelligent model routing".to_string(), + description: + "Record task start and get agent suggestions with intelligent model routing" + .to_string(), required_params: vec![ ToolParam { name: "taskId".to_string(), @@ -1357,7 +1411,11 @@ impl ToolDatasetGenerator { name: "operation".to_string(), param_type: ParamType::Enum, description: "Type of operation".to_string(), - examples: vec!["create".to_string(), "update".to_string(), "refactor".to_string()], + examples: vec![ + "create".to_string(), + "update".to_string(), + "refactor".to_string(), + ], }], use_cases: vec![ "get suggestions before editing a source file".to_string(), @@ -1618,7 +1676,11 @@ impl ToolDatasetGenerator { name: "scope".to_string(), param_type: ParamType::Enum, description: "Configuration scope".to_string(), - examples: vec!["project".to_string(), "user".to_string(), "system".to_string()], + examples: vec![ + "project".to_string(), + "user".to_string(), + "system".to_string(), + ], }], use_cases: vec![ "get a specific configuration value".to_string(), @@ -1667,7 +1729,11 @@ impl ToolDatasetGenerator { name: "topology".to_string(), param_type: ParamType::Enum, description: "Network topology".to_string(), - examples: vec!["mesh".to_string(), "hierarchical".to_string(), "ring".to_string()], + examples: vec![ + "mesh".to_string(), + "hierarchical".to_string(), + "ring".to_string(), + ], }, ToolParam { name: "queenId".to_string(), @@ -1707,7 +1773,11 @@ impl ToolDatasetGenerator { name: "action".to_string(), param_type: ParamType::Enum, description: "Consensus action".to_string(), - examples: vec!["propose".to_string(), "vote".to_string(), "status".to_string()], + examples: vec![ + "propose".to_string(), + "vote".to_string(), + "status".to_string(), + ], }], optional_params: vec![ ToolParam { @@ -1738,7 +1808,11 @@ impl ToolDatasetGenerator { name: "modelType".to_string(), param_type: ParamType::Enum, description: "Model type".to_string(), - examples: vec!["moe".to_string(), "transformer".to_string(), "classifier".to_string()], + examples: vec![ + "moe".to_string(), + "transformer".to_string(), + "classifier".to_string(), + ], }], optional_params: vec![ ToolParam { @@ -1801,7 +1875,11 @@ impl ToolDatasetGenerator { name: "format".to_string(), param_type: ParamType::Enum, description: "Report format".to_string(), - examples: vec!["json".to_string(), "summary".to_string(), "detailed".to_string()], + examples: vec![ + "json".to_string(), + "summary".to_string(), + "detailed".to_string(), + ], }, ToolParam { name: "timeRange".to_string(), @@ -1826,7 +1904,11 @@ impl ToolDatasetGenerator { name: "suite".to_string(), param_type: ParamType::Enum, description: "Benchmark suite".to_string(), - examples: vec!["all".to_string(), "memory".to_string(), "neural".to_string()], + examples: vec![ + "all".to_string(), + "memory".to_string(), + "neural".to_string(), + ], }, ToolParam { name: "iterations".to_string(), diff --git a/crates/ruvllm/src/witness_log.rs b/crates/ruvllm/src/witness_log.rs index 0780c1419..c867bbf20 100644 --- a/crates/ruvllm/src/witness_log.rs +++ b/crates/ruvllm/src/witness_log.rs @@ -39,19 +39,19 @@ use crate::error::{Result, RuvLLMError}; use crate::types::{ErrorInfo, ModelSize, QualityMetrics}; use chrono::{DateTime, Utc}; -use ruvector_core::{AgenticDB, SearchQuery, VectorEntry}; +use parking_lot::Mutex; use ruvector_core::types::DbOptions; +use ruvector_core::{AgenticDB, SearchQuery, VectorEntry}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::Arc; -use parking_lot::Mutex; use uuid::Uuid; #[cfg(feature = "async-runtime")] use tokio::sync::{oneshot, Notify}; #[cfg(feature = "async-runtime")] -use tokio::time::{Duration, interval}; +use tokio::time::{interval, Duration}; /// Latency breakdown for profiling #[derive(Debug, Clone, Default, Serialize, Deserialize)] @@ -78,8 +78,11 @@ impl LatencyBreakdown { /// Compute total from components pub fn compute_total(&mut self) { - self.total_ms = self.embedding_ms + self.retrieval_ms + self.routing_ms - + self.attention_ms + self.generation_ms; + self.total_ms = self.embedding_ms + + self.retrieval_ms + + self.routing_ms + + self.attention_ms + + self.generation_ms; } /// Check if any component exceeds threshold @@ -340,13 +343,16 @@ impl WitnessLog { } /// Create a new witness log with custom async write configuration - pub fn with_config(storage_path: &str, embedding_dim: usize, async_config: AsyncWriteConfig) -> Result { + pub fn with_config( + storage_path: &str, + embedding_dim: usize, + async_config: AsyncWriteConfig, + ) -> Result { let mut options = DbOptions::default(); options.storage_path = storage_path.to_string(); options.dimensions = embedding_dim; - let db = AgenticDB::new(options) - .map_err(|e| RuvLLMError::Storage(e.to_string()))?; + let db = AgenticDB::new(options).map_err(|e| RuvLLMError::Storage(e.to_string()))?; Ok(Self { db, @@ -434,10 +440,7 @@ impl WitnessLog { #[cfg(feature = "async-runtime")] { use std::fs::OpenOptions; - if let Ok(file) = OpenOptions::new() - .read(true) - .open(&self.storage_path) - { + if let Ok(file) = OpenOptions::new().read(true).open(&self.storage_path) { let _ = file.sync_all(); } } @@ -448,22 +451,52 @@ impl WitnessLog { fn flush_entries(&self, entries: Vec) -> Result<()> { for entry in entries { let mut metadata = HashMap::new(); - metadata.insert("request_id".to_string(), serde_json::json!(entry.request_id.to_string())); - metadata.insert("session_id".to_string(), serde_json::json!(entry.session_id)); - metadata.insert("model_used".to_string(), serde_json::to_value(&entry.model_used).unwrap_or_default()); - metadata.insert("quality_score".to_string(), serde_json::json!(entry.quality_score)); - metadata.insert("routing_decision".to_string(), serde_json::to_value(&entry.routing_decision).unwrap_or_default()); - metadata.insert("latency".to_string(), serde_json::to_value(&entry.latency).unwrap_or_default()); - metadata.insert("timestamp".to_string(), serde_json::json!(entry.timestamp.to_rfc3339())); - metadata.insert("is_success".to_string(), serde_json::json!(entry.is_success())); + metadata.insert( + "request_id".to_string(), + serde_json::json!(entry.request_id.to_string()), + ); + metadata.insert( + "session_id".to_string(), + serde_json::json!(entry.session_id), + ); + metadata.insert( + "model_used".to_string(), + serde_json::to_value(&entry.model_used).unwrap_or_default(), + ); + metadata.insert( + "quality_score".to_string(), + serde_json::json!(entry.quality_score), + ); + metadata.insert( + "routing_decision".to_string(), + serde_json::to_value(&entry.routing_decision).unwrap_or_default(), + ); + metadata.insert( + "latency".to_string(), + serde_json::to_value(&entry.latency).unwrap_or_default(), + ); + metadata.insert( + "timestamp".to_string(), + serde_json::json!(entry.timestamp.to_rfc3339()), + ); + metadata.insert( + "is_success".to_string(), + serde_json::json!(entry.is_success()), + ); metadata.insert("tags".to_string(), serde_json::json!(entry.tags)); if let Some(error) = &entry.error { - metadata.insert("error".to_string(), serde_json::to_value(error).unwrap_or_default()); + metadata.insert( + "error".to_string(), + serde_json::to_value(error).unwrap_or_default(), + ); } if let Some(qm) = &entry.quality_metrics { - metadata.insert("quality_metrics".to_string(), serde_json::to_value(qm).unwrap_or_default()); + metadata.insert( + "quality_metrics".to_string(), + serde_json::to_value(qm).unwrap_or_default(), + ); } let vector_entry = VectorEntry { @@ -472,7 +505,8 @@ impl WitnessLog { metadata: Some(metadata), }; - self.db.insert(vector_entry) + self.db + .insert(vector_entry) .map_err(|e| RuvLLMError::Storage(e.to_string()))?; } @@ -499,13 +533,16 @@ impl WitnessLog { ef_search: None, }; - let results = self.db.search(query) + let results = self + .db + .search(query) .map_err(|e| RuvLLMError::Storage(e.to_string()))?; let mut entries = Vec::with_capacity(results.len()); for result in results { if let Some(metadata) = &result.metadata { - if let Some(entry) = self.entry_from_metadata(&result.id, query_embedding, metadata) { + if let Some(entry) = self.entry_from_metadata(&result.id, query_embedding, metadata) + { entries.push(entry); } } @@ -525,7 +562,11 @@ impl WitnessLog { total_entries: total, success_count: success, error_count: errors, - success_rate: if total > 0 { success as f32 / total as f32 } else { 0.0 }, + success_rate: if total > 0 { + success as f32 / total as f32 + } else { + 0.0 + }, pending_writes: queue.len(), dropped_entries: queue.dropped_count(), background_running: self.background_running.load(Ordering::SeqCst), @@ -549,45 +590,59 @@ impl WitnessLog { embedding: &[f32], metadata: &HashMap, ) -> Option { - let request_id = metadata.get("request_id") + let request_id = metadata + .get("request_id") .and_then(|v| v.as_str()) .and_then(|s| Uuid::parse_str(s).ok())?; - let session_id = metadata.get("session_id") + let session_id = metadata + .get("session_id") .and_then(|v| v.as_str())? .to_string(); - let model_used: ModelSize = metadata.get("model_used") + let model_used: ModelSize = metadata + .get("model_used") .and_then(|v| serde_json::from_value(v.clone()).ok()) .unwrap_or_default(); - let quality_score = metadata.get("quality_score") + let quality_score = metadata + .get("quality_score") .and_then(|v| v.as_f64()) .unwrap_or(0.0) as f32; - let routing_decision: RoutingDecision = metadata.get("routing_decision") + let routing_decision: RoutingDecision = metadata + .get("routing_decision") .and_then(|v| serde_json::from_value(v.clone()).ok()) .unwrap_or_default(); - let latency: LatencyBreakdown = metadata.get("latency") + let latency: LatencyBreakdown = metadata + .get("latency") .and_then(|v| serde_json::from_value(v.clone()).ok()) .unwrap_or_default(); - let timestamp = metadata.get("timestamp") + let timestamp = metadata + .get("timestamp") .and_then(|v| v.as_str()) .and_then(|s| DateTime::parse_from_rfc3339(s).ok()) .map(|dt| dt.with_timezone(&Utc)) .unwrap_or_else(Utc::now); - let error: Option = metadata.get("error") + let error: Option = metadata + .get("error") .and_then(|v| serde_json::from_value(v.clone()).ok()); - let quality_metrics: Option = metadata.get("quality_metrics") + let quality_metrics: Option = metadata + .get("quality_metrics") .and_then(|v| serde_json::from_value(v.clone()).ok()); - let tags: Vec = metadata.get("tags") + let tags: Vec = metadata + .get("tags") .and_then(|v| v.as_array()) - .map(|arr| arr.iter().filter_map(|v| v.as_str().map(String::from)).collect()) + .map(|arr| { + arr.iter() + .filter_map(|v| v.as_str().map(String::from)) + .collect() + }) .unwrap_or_default(); Some(WitnessEntry { @@ -779,7 +834,11 @@ impl WitnessLog { total_entries: total, success_count: success, error_count: errors, - success_rate: if total > 0 { success as f32 / total as f32 } else { 0.0 }, + success_rate: if total > 0 { + success as f32 / total as f32 + } else { + 0.0 + }, pending_writes: queue.len(), dropped_entries: queue.dropped_count(), background_running: self.background_running.load(Ordering::SeqCst), @@ -914,7 +973,10 @@ mod tests { vec![0.1; 768], RoutingDecision::default(), ); - assert!(!queue.push(entry), "Entry should be dropped due to backpressure"); + assert!( + !queue.push(entry), + "Entry should be dropped due to backpressure" + ); assert_eq!(queue.dropped_count(), 1); // Another dropped entry @@ -939,11 +1001,7 @@ mod tests { let temp_dir = tempfile::tempdir().unwrap(); let storage_path = temp_dir.path().join("witness_test"); - let log = WitnessLog::with_config( - storage_path.to_str().unwrap(), - 64, - config, - ).unwrap(); + let log = WitnessLog::with_config(storage_path.to_str().unwrap(), 64, config).unwrap(); // Record some entries for i in 0..3 { @@ -979,11 +1037,9 @@ mod tests { let temp_dir = tempfile::tempdir().unwrap(); let storage_path = temp_dir.path().join("async_witness_test"); - let log = Arc::new(WitnessLog::with_config( - storage_path.to_str().unwrap(), - 64, - config, - ).unwrap()); + let log = Arc::new( + WitnessLog::with_config(storage_path.to_str().unwrap(), 64, config).unwrap(), + ); // Start background flush task log.start_background_flush(); @@ -1021,20 +1077,19 @@ mod tests { let temp_dir = tempfile::tempdir().unwrap(); let storage_path = temp_dir.path().join("batch_witness_test"); - let log = Arc::new(WitnessLog::new( - storage_path.to_str().unwrap(), - 64, - ).unwrap()); + let log = Arc::new(WitnessLog::new(storage_path.to_str().unwrap(), 64).unwrap()); log.start_background_flush(); // Create batch of entries let entries: Vec<_> = (0..50) - .map(|i| WitnessEntry::new( - format!("batch-session-{}", i), - vec![0.1; 64], - RoutingDecision::default(), - )) + .map(|i| { + WitnessEntry::new( + format!("batch-session-{}", i), + vec![0.1; 64], + RoutingDecision::default(), + ) + }) .collect(); // Record batch @@ -1052,10 +1107,7 @@ mod tests { let temp_dir = tempfile::tempdir().unwrap(); let storage_path = temp_dir.path().join("flush_async_test"); - let log = WitnessLog::new( - storage_path.to_str().unwrap(), - 64, - ).unwrap(); + let log = WitnessLog::new(storage_path.to_str().unwrap(), 64).unwrap(); // Record entries for i in 0..5 { diff --git a/crates/ruvllm/tests/adapter_integration.rs b/crates/ruvllm/tests/adapter_integration.rs index d5f651662..c7fc1efc0 100644 --- a/crates/ruvllm/tests/adapter_integration.rs +++ b/crates/ruvllm/tests/adapter_integration.rs @@ -3,9 +3,8 @@ #[cfg(test)] mod tests { use ruvllm::lora::{ - RuvLtraAdapters, AdapterTrainer, AdapterTrainingConfig, SyntheticDataGenerator, - AdapterMerger, MergeConfig, MergeStrategy, HotSwapManager, AdaptFeedback, - TargetModule, + AdaptFeedback, AdapterMerger, AdapterTrainer, AdapterTrainingConfig, HotSwapManager, + MergeConfig, MergeStrategy, RuvLtraAdapters, SyntheticDataGenerator, TargetModule, }; use std::collections::HashMap; @@ -39,8 +38,10 @@ mod tests { } let stats = dataset.stats(); - println!("{}: train={}, val={}, avg_quality={:.2}", - task_type, stats.train_size, stats.val_size, stats.avg_quality); + println!( + "{}: train={}, val={}, avg_quality={:.2}", + task_type, stats.train_size, stats.val_size, stats.avg_quality + ); } } @@ -61,8 +62,10 @@ mod tests { assert!(result.total_steps > 0); assert!(result.final_loss >= 0.0); - println!("Training result: {} epochs, {} steps, loss={:.4}", - result.epochs_completed, result.total_steps, result.final_loss); + println!( + "Training result: {} epochs, {} steps, loss={:.4}", + result.epochs_completed, result.total_steps, result.final_loss + ); } #[test] @@ -93,7 +96,9 @@ mod tests { let config = MergeConfig::average(); let merger = AdapterMerger::new(config); - let merged = merger.merge(&adapters_to_merge, &adapters.coder, 256).unwrap(); + let merged = merger + .merge(&adapters_to_merge, &adapters.coder, 256) + .unwrap(); assert!(merged.is_enabled()); assert!(merged.param_count() > 0); @@ -119,7 +124,9 @@ mod tests { let config = MergeConfig::weighted(weights); let merger = AdapterMerger::new(config); - let merged = merger.merge(&adapters_to_merge, &adapters.coder, 256).unwrap(); + let merged = merger + .merge(&adapters_to_merge, &adapters.coder, 256) + .unwrap(); assert!(merged.is_enabled()); } @@ -138,7 +145,9 @@ mod tests { let config = MergeConfig::slerp(0.5); let merger = AdapterMerger::new(config); - let merged = merger.merge(&adapters_to_merge, &adapters.coder, 256).unwrap(); + let merged = merger + .merge(&adapters_to_merge, &adapters.coder, 256) + .unwrap(); assert!(merged.is_enabled()); } @@ -181,7 +190,10 @@ mod tests { let adapted = lora.forward(&input, &TargetModule::QProj); let adapted_mean = adapted.iter().sum::() / adapted.len() as f32; - println!("Baseline mean: {:.4}, Adapted mean: {:.4}", baseline_mean, adapted_mean); + println!( + "Baseline mean: {:.4}, Adapted mean: {:.4}", + baseline_mean, adapted_mean + ); assert_eq!(lora.adaptation_count(), 1); } @@ -223,11 +235,13 @@ mod tests { let mem_768 = config.estimate_memory(768); let mem_4096 = config.estimate_memory(4096); - println!("{}: 256d={:.1}KB, 768d={:.1}KB, 4096d={:.1}KB", - name, - mem_256 as f32 / 1024.0, - mem_768 as f32 / 1024.0, - mem_4096 as f32 / 1024.0); + println!( + "{}: 256d={:.1}KB, 768d={:.1}KB, 4096d={:.1}KB", + name, + mem_256 as f32 / 1024.0, + mem_768 as f32 / 1024.0, + mem_4096 as f32 / 1024.0 + ); } } @@ -250,7 +264,9 @@ mod tests { // TIES merge let ties_config = MergeConfig::ties(0.6); let ties_merger = AdapterMerger::new(ties_config); - let ties_merged = ties_merger.merge(&trained_adapters, &adapters.coder, 256).unwrap(); + let ties_merged = ties_merger + .merge(&trained_adapters, &adapters.coder, 256) + .unwrap(); assert!(ties_merged.is_enabled()); diff --git a/crates/ruvllm/tests/ane_integration.rs b/crates/ruvllm/tests/ane_integration.rs index 1ef1aaf98..410d928da 100644 --- a/crates/ruvllm/tests/ane_integration.rs +++ b/crates/ruvllm/tests/ane_integration.rs @@ -18,12 +18,12 @@ // Import from the crate being tested // Note: CoreMLBackend methods require the coreml feature -use ruvllm::backends::{ - AneCapabilities, ComputeUnits, GenerateParams, LlmBackend, - ModelArchitecture, ModelConfig, Quantization, -}; #[cfg(feature = "coreml")] use ruvllm::backends::CoreMLBackend; +use ruvllm::backends::{ + AneCapabilities, ComputeUnits, GenerateParams, LlmBackend, ModelArchitecture, ModelConfig, + Quantization, +}; use ruvllm::error::{Result, RuvLLMError}; // ============================================================================ @@ -52,8 +52,14 @@ fn test_ane_capabilities_detection() { if is_apple_silicon() { assert!(caps.available, "ANE should be available on Apple Silicon"); assert!(caps.tops > 0.0, "TOPS should be positive on Apple Silicon"); - assert!(caps.max_model_size_mb > 0, "Max model size should be positive"); - assert!(!caps.supported_ops.is_empty(), "Should have supported operations"); + assert!( + caps.max_model_size_mb > 0, + "Max model size should be positive" + ); + assert!( + !caps.supported_ops.is_empty(), + "Should have supported operations" + ); // Verify common operations are supported let expected_ops = ["MatMul", "GELU", "SiLU", "LayerNorm", "Softmax"]; @@ -65,10 +71,19 @@ fn test_ane_capabilities_detection() { ); } } else { - assert!(!caps.available, "ANE should not be available on non-Apple Silicon"); + assert!( + !caps.available, + "ANE should not be available on non-Apple Silicon" + ); assert_eq!(caps.tops, 0.0, "TOPS should be 0 when unavailable"); - assert_eq!(caps.max_model_size_mb, 0, "Max model size should be 0 when unavailable"); - assert!(caps.supported_ops.is_empty(), "No operations when unavailable"); + assert_eq!( + caps.max_model_size_mb, 0, + "Max model size should be 0 when unavailable" + ); + assert!( + caps.supported_ops.is_empty(), + "No operations when unavailable" + ); } } @@ -160,7 +175,10 @@ fn test_fallback_when_coreml_unavailable() { let caps = AneCapabilities::detect(); // On non-Apple Silicon or without the feature, it should gracefully handle this if !is_apple_silicon() { - assert!(!caps.available, "ANE should not be available without coreml feature on non-Apple Silicon"); + assert!( + !caps.available, + "ANE should not be available without coreml feature on non-Apple Silicon" + ); } } @@ -356,9 +374,9 @@ fn test_ane_tops_values() { if is_apple_silicon() { let caps = AneCapabilities::detect(); // Detected TOPS should fall within one of the known ranges - let in_known_range = chip_specs.iter().any(|spec| { - caps.tops >= spec.min_tops && caps.tops <= spec.max_tops + 5.0 - }); + let in_known_range = chip_specs + .iter() + .any(|spec| caps.tops >= spec.min_tops && caps.tops <= spec.max_tops + 5.0); // Just verify it's a reasonable positive value assert!(caps.tops > 0.0, "TOPS should be positive"); diff --git a/crates/ruvllm/tests/ane_test_utils.rs b/crates/ruvllm/tests/ane_test_utils.rs index b0ff8c5ce..d3fcc829d 100644 --- a/crates/ruvllm/tests/ane_test_utils.rs +++ b/crates/ruvllm/tests/ane_test_utils.rs @@ -206,11 +206,7 @@ pub struct CompareResult { /// Compare two tensors element-wise with configurable tolerance pub fn compare_tensors(expected: &[f32], actual: &[f32], config: &CompareConfig) -> CompareResult { - assert_eq!( - expected.len(), - actual.len(), - "Tensor sizes must match" - ); + assert_eq!(expected.len(), actual.len(), "Tensor sizes must match"); let mut max_abs_diff = 0.0f32; let mut max_rel_diff = 0.0f32; @@ -240,17 +236,22 @@ pub fn compare_tensors(expected: &[f32], actual: &[f32], config: &CompareConfig) } } - let equal = max_abs_diff <= config.atol - || max_rel_diff <= config.rtol - || differences.is_empty(); + let equal = + max_abs_diff <= config.atol || max_rel_diff <= config.rtol || differences.is_empty(); if config.verbose && !equal { eprintln!("Tensor comparison failed:"); - eprintln!(" Max abs diff: {} at index {}", max_abs_diff, max_abs_diff_idx); + eprintln!( + " Max abs diff: {} at index {}", + max_abs_diff, max_abs_diff_idx + ); eprintln!(" Max rel diff: {}", max_rel_diff); eprintln!(" Differences ({}/{}):", differences.len(), expected.len()); for (idx, exp, act, diff) in &differences { - eprintln!(" [{}]: expected={}, actual={}, diff={}", idx, exp, act, diff); + eprintln!( + " [{}]: expected={}, actual={}, diff={}", + idx, exp, act, diff + ); } } @@ -371,12 +372,7 @@ impl TestWeights { pub fn linear(&mut self, in_features: usize, out_features: usize) -> Vec { // Xavier initialization scale let scale = (2.0 / (in_features + out_features) as f32).sqrt(); - let weights = random_tensor_uniform( - in_features * out_features, - -scale, - scale, - self.seed, - ); + let weights = random_tensor_uniform(in_features * out_features, -scale, scale, self.seed); self.seed += 1; weights } @@ -401,12 +397,7 @@ impl TestWeights { /// Generate embedding table pub fn embedding(&mut self, vocab_size: usize, hidden_dim: usize) -> Vec { let scale = 0.02; - let weights = random_tensor_normal( - vocab_size * hidden_dim, - 0.0, - scale, - self.seed, - ); + let weights = random_tensor_normal(vocab_size * hidden_dim, 0.0, scale, self.seed); self.seed += 1; weights } @@ -498,9 +489,7 @@ pub struct ActivationTestData { impl Default for ActivationTestData { fn default() -> Self { - let inputs: Vec = vec![ - -3.0, -2.0, -1.0, -0.5, 0.0, 0.5, 1.0, 2.0, 3.0, - ]; + let inputs: Vec = vec![-3.0, -2.0, -1.0, -0.5, 0.0, 0.5, 1.0, 2.0, 3.0]; // Pre-computed expected values (approximate) let expected_gelu: Vec = vec![ @@ -596,11 +585,7 @@ mod tests { #[test] fn test_identity_matrix() { let identity = identity_matrix(3); - assert_eq!(identity, vec![ - 1.0, 0.0, 0.0, - 0.0, 1.0, 0.0, - 0.0, 0.0, 1.0, - ]); + assert_eq!(identity, vec![1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0,]); } #[test] diff --git a/crates/ruvllm/tests/autodetect_integration.rs b/crates/ruvllm/tests/autodetect_integration.rs index 58ca21dd5..91a32ca80 100644 --- a/crates/ruvllm/tests/autodetect_integration.rs +++ b/crates/ruvllm/tests/autodetect_integration.rs @@ -71,7 +71,10 @@ fn test_optimal_config_generation() { // Verify reasonable defaults assert!(config.batch_size >= 1, "Batch size should be at least 1"); - assert!(config.thread_count >= 1, "Thread count should be at least 1"); + assert!( + config.thread_count >= 1, + "Thread count should be at least 1" + ); assert!(config.block_size >= 16, "Block size should be at least 16"); // Thread count should not exceed logical cores @@ -110,7 +113,10 @@ fn test_quantization_recommendation_large_model() { // Unless you have 256GB+ RAM, this should be Q4K or Q4 if caps.memory_mb < 256 * 1024 { assert!( - matches!(q_large, Quantization::Q4K | Quantization::Q4 | Quantization::Q2K), + matches!( + q_large, + Quantization::Q4K | Quantization::Q4 | Quantization::Q2K + ), "Large model should use aggressive quantization, got {:?}", q_large ); @@ -182,8 +188,10 @@ fn test_cpu_feature_detection_x86_64() { // SSE4.2 should be common on modern x86_64 // Note: This depends on compile-time detection or runtime check - println!("SSE4.2: {}, AVX2: {}, AVX-512: {}", - features.sse42, features.avx2, features.avx512); + println!( + "SSE4.2: {}, AVX2: {}, AVX-512: {}", + features.sse42, features.avx2, features.avx512 + ); } } @@ -193,7 +201,10 @@ fn test_memory_detection() { // Memory should be in reasonable range (256MB to 1TB) assert!(caps.memory_mb >= 256, "Memory should be at least 256MB"); - assert!(caps.memory_mb <= 1024 * 1024, "Memory should be at most 1TB"); + assert!( + caps.memory_mb <= 1024 * 1024, + "Memory should be at most 1TB" + ); println!( "Detected memory: {} MB ({:.1} GB)", @@ -207,7 +218,10 @@ fn test_core_count_detection() { let cores = CoreInfo::detect(); // Physical cores should be reasonable - assert!(cores.physical_cores >= 1, "Should have at least 1 physical core"); + assert!( + cores.physical_cores >= 1, + "Should have at least 1 physical core" + ); assert!( cores.physical_cores <= 256, "Should have at most 256 physical cores" @@ -342,10 +356,7 @@ fn test_can_run_model() { let caps = SystemCapabilities::detect(); // Should be able to run a tiny model - assert!( - caps.can_run_model(0.1), - "Should be able to run 100MB model" - ); + assert!(caps.can_run_model(0.1), "Should be able to run 100MB model"); // Likely can't run a 1TB model assert!( @@ -440,7 +451,11 @@ fn test_all_architecture_variants() { ]; let unique: HashSet<_> = archs.iter().collect(); - assert_eq!(unique.len(), 4, "All architecture variants should be distinct"); + assert_eq!( + unique.len(), + 4, + "All architecture variants should be distinct" + ); } #[test] @@ -454,7 +469,11 @@ fn test_all_gpu_backend_variants() { ]; let unique: HashSet<_> = backends.iter().collect(); - assert_eq!(unique.len(), 5, "All GPU backend variants should be distinct"); + assert_eq!( + unique.len(), + 5, + "All GPU backend variants should be distinct" + ); } #[test] @@ -478,8 +497,7 @@ fn test_all_compute_backend_variants() { // Verify relative performance ordering assert!( - ComputeBackend::Cuda.relative_performance() - > ComputeBackend::Metal.relative_performance() + ComputeBackend::Cuda.relative_performance() > ComputeBackend::Metal.relative_performance() ); assert!( ComputeBackend::Metal.relative_performance() @@ -586,8 +604,14 @@ fn test_system_capabilities_display() { println!(" AVX-512: {}", caps.cpu_features.avx512); } - println!(" Best SIMD width: {} bits", caps.cpu_features.best_simd_width()); - println!(" SIMD float lanes: {}", caps.cpu_features.simd_float_lanes()); + println!( + " Best SIMD width: {} bits", + caps.cpu_features.best_simd_width() + ); + println!( + " SIMD float lanes: {}", + caps.cpu_features.simd_float_lanes() + ); let config = caps.optimal_config(); println!("\n=== Optimal Configuration ==="); @@ -599,10 +623,7 @@ fn test_system_capabilities_display() { println!("Flash Attention: {}", config.use_flash_attention); println!("Device Type: {:?}", config.device_type); println!("DType: {:?}", config.dtype); - println!( - "Estimated TPS: {:.1}", - config.estimated_tokens_per_second() - ); + println!("Estimated TPS: {:.1}", config.estimated_tokens_per_second()); println!("\n=== Summary ==="); println!("{}", caps.summary()); @@ -622,9 +643,15 @@ fn test_optimal_attention_config() { // Verify reasonable attention configuration assert!(attn_config.num_heads > 0, "Should have at least 1 head"); - assert!(attn_config.num_kv_heads > 0, "Should have at least 1 KV head"); + assert!( + attn_config.num_kv_heads > 0, + "Should have at least 1 KV head" + ); assert!(attn_config.head_dim > 0, "Should have positive head dim"); - assert!(attn_config.max_seq_len >= 1024, "Should support at least 1K context"); + assert!( + attn_config.max_seq_len >= 1024, + "Should support at least 1K context" + ); // GQA ratio should be valid let gqa_ratio = attn_config.gqa_ratio(); @@ -636,7 +663,10 @@ fn test_optimal_attention_config() { // Scale should be reasonable let scale = attn_config.effective_scale(); - assert!(scale > 0.0 && scale < 1.0, "Scale should be between 0 and 1"); + assert!( + scale > 0.0 && scale < 1.0, + "Scale should be between 0 and 1" + ); println!( "Attention Config: {} heads, {} KV heads, {} head_dim, {} max_seq_len, GQA {}:1", diff --git a/crates/ruvllm/tests/backend_integration.rs b/crates/ruvllm/tests/backend_integration.rs index b401c6dbb..9189f177d 100644 --- a/crates/ruvllm/tests/backend_integration.rs +++ b/crates/ruvllm/tests/backend_integration.rs @@ -5,7 +5,7 @@ use ruvllm::{ backends::{ - create_backend, DeviceType, DType, GenerateParams, LlmBackend, ModelArchitecture, + create_backend, DType, DeviceType, GenerateParams, LlmBackend, ModelArchitecture, ModelConfig, ModelInfo, Quantization, SpecialTokens, TokenStream, Tokenizer, }, error::Result, @@ -47,9 +47,7 @@ impl LlmBackend for MockBackend { fn generate(&self, prompt: &str, _params: GenerateParams) -> Result { if !self.loaded { - return Err(ruvllm::RuvLLMError::Backend( - "Model not loaded".to_string(), - )); + return Err(ruvllm::RuvLLMError::Backend("Model not loaded".to_string())); } Ok(format!("Response to: {}", prompt)) } @@ -58,11 +56,10 @@ impl LlmBackend for MockBackend { &self, _prompt: &str, _params: GenerateParams, - ) -> Result> + Send + '_>> { + ) -> Result> + Send + '_>> + { if !self.loaded { - return Err(ruvllm::RuvLLMError::Backend( - "Model not loaded".to_string(), - )); + return Err(ruvllm::RuvLLMError::Backend("Model not loaded".to_string())); } let tokens = vec![ @@ -91,9 +88,7 @@ impl LlmBackend for MockBackend { fn generate_stream_v2(&self, _prompt: &str, _params: GenerateParams) -> Result { if !self.loaded { - return Err(ruvllm::RuvLLMError::Backend( - "Model not loaded".to_string(), - )); + return Err(ruvllm::RuvLLMError::Backend("Model not loaded".to_string())); } // Return a mock stream using channel let (tx, stream) = TokenStream::channel(); @@ -104,9 +99,7 @@ impl LlmBackend for MockBackend { fn get_embeddings(&self, _text: &str) -> Result> { if !self.loaded { - return Err(ruvllm::RuvLLMError::Backend( - "Model not loaded".to_string(), - )); + return Err(ruvllm::RuvLLMError::Backend("Model not loaded".to_string())); } // Return a mock embedding Ok(vec![0.1; 768]) @@ -149,7 +142,9 @@ fn test_mock_backend_load_model() { #[test] fn test_backend_generate_basic() { let mut backend = MockBackend::new(); - backend.load_model("test-model", ModelConfig::default()).unwrap(); + backend + .load_model("test-model", ModelConfig::default()) + .unwrap(); let params = GenerateParams { max_tokens: 100, @@ -183,7 +178,9 @@ fn test_backend_generate_requires_loaded_model() { #[test] fn test_backend_streaming() { let mut backend = MockBackend::new(); - backend.load_model("test-model", ModelConfig::default()).unwrap(); + backend + .load_model("test-model", ModelConfig::default()) + .unwrap(); let params = GenerateParams::default(); let stream = backend.generate_stream("Hello", params).unwrap(); @@ -200,7 +197,9 @@ fn test_backend_streaming() { #[test] fn test_backend_embeddings() { let mut backend = MockBackend::new(); - backend.load_model("test-model", ModelConfig::default()).unwrap(); + backend + .load_model("test-model", ModelConfig::default()) + .unwrap(); let embedding = backend.get_embeddings("Test text for embedding").unwrap(); @@ -231,7 +230,9 @@ fn test_backend_model_info() { #[test] fn test_backend_unload() { let mut backend = MockBackend::new(); - backend.load_model("test-model", ModelConfig::default()).unwrap(); + backend + .load_model("test-model", ModelConfig::default()) + .unwrap(); assert!(backend.is_model_loaded()); backend.unload_model(); @@ -413,8 +414,8 @@ mod candle_tests { mod memory_pool_tests { use ruvllm::memory_pool::{ - InferenceArena, BufferPool, BufferSize, ScratchSpaceManager, - MemoryManager, MemoryManagerConfig, + BufferPool, BufferSize, InferenceArena, MemoryManager, MemoryManagerConfig, + ScratchSpaceManager, }; /// Test memory pool integration with streaming generation @@ -470,7 +471,10 @@ mod memory_pool_tests { logits[0] = token_idx as f32 * 0.1; // Acquire KV cache buffer from pool - let kv_buf = manager.pool.acquire(BufferSize::KB16).expect("acquire failed"); + let kv_buf = manager + .pool + .acquire(BufferSize::KB16) + .expect("acquire failed"); assert!(kv_buf.capacity() >= 16384); // Use scratch space for intermediate computations @@ -582,9 +586,9 @@ mod memory_pool_tests { fn test_memory_manager_for_model() { // Configure for a small LLM (e.g., Phi-2) let config = MemoryManagerConfig::for_model( - 2560, // hidden_dim - 51200, // vocab_size - 1, // batch_size + 2560, // hidden_dim + 51200, // vocab_size + 1, // batch_size ); let manager = MemoryManager::with_config(config).expect("manager creation failed"); diff --git a/crates/ruvllm/tests/cross_platform.rs b/crates/ruvllm/tests/cross_platform.rs index 92347e50d..ebd26d017 100644 --- a/crates/ruvllm/tests/cross_platform.rs +++ b/crates/ruvllm/tests/cross_platform.rs @@ -3,9 +3,7 @@ //! These tests verify that the scalar fallback implementations produce //! correct results and work on all platforms (including non-NEON and WASM). -use ruvllm::kernels::{ - flash_attention_neon, gemm_neon, gemv_neon, layer_norm_neon, rms_norm_neon, -}; +use ruvllm::kernels::{flash_attention_neon, gemm_neon, gemv_neon, layer_norm_neon, rms_norm_neon}; // ========== Scalar Reference Implementations ========== @@ -106,11 +104,13 @@ fn test_cross_platform_gemv() { (32, 64), (64, 128), (100, 50), - (7, 13), // Non-aligned + (7, 13), // Non-aligned ]; for (m, n) in test_cases { - let a: Vec = (0..m * n).map(|i| ((i % 100) as f32 - 50.0) / 50.0).collect(); + let a: Vec = (0..m * n) + .map(|i| ((i % 100) as f32 - 50.0) / 50.0) + .collect(); let x: Vec = (0..n).map(|i| ((i % 20) as f32 - 10.0) / 10.0).collect(); let mut y_neon = vec![0.0; m]; @@ -125,7 +125,12 @@ fn test_cross_platform_gemv() { assert!( rel_error < 0.001 || abs_error < 1e-5, "Cross-platform GEMV mismatch at ({},{}) index {}: {} vs {} (rel: {:.6})", - m, n, i, y_neon[i], y_scalar[i], rel_error + m, + n, + i, + y_neon[i], + y_scalar[i], + rel_error ); } } @@ -138,12 +143,16 @@ fn test_cross_platform_gemm() { (8, 16, 8), (16, 32, 16), (32, 64, 32), - (7, 11, 13), // Non-aligned + (7, 11, 13), // Non-aligned ]; for (m, k, n) in test_cases { - let a: Vec = (0..m * k).map(|i| ((i % 100) as f32 - 50.0) / 100.0).collect(); - let b: Vec = (0..k * n).map(|i| ((i % 50) as f32 - 25.0) / 50.0).collect(); + let a: Vec = (0..m * k) + .map(|i| ((i % 100) as f32 - 50.0) / 100.0) + .collect(); + let b: Vec = (0..k * n) + .map(|i| ((i % 50) as f32 - 25.0) / 50.0) + .collect(); let mut c_neon = vec![0.0; m * n]; let mut c_scalar = vec![0.0; m * n]; @@ -157,7 +166,13 @@ fn test_cross_platform_gemm() { assert!( rel_error < 0.01 || abs_error < 0.001, "Cross-platform GEMM mismatch at ({},{},{}) index {}: {} vs {} (rel: {:.6})", - m, k, n, i, c_neon[i], c_scalar[i], rel_error + m, + k, + n, + i, + c_neon[i], + c_scalar[i], + rel_error ); } } @@ -165,19 +180,20 @@ fn test_cross_platform_gemm() { #[test] fn test_cross_platform_attention() { - let test_cases = [ - (16, 4), - (32, 8), - (64, 16), - (128, 32), - ]; + let test_cases = [(16, 4), (32, 8), (64, 16), (128, 32)]; for (head_dim, kv_len) in test_cases { let scale = 1.0 / (head_dim as f32).sqrt(); - let query: Vec = (0..head_dim).map(|i| ((i % 7) as f32 - 3.0) / 10.0).collect(); - let key: Vec = (0..kv_len * head_dim).map(|i| ((i % 11) as f32 - 5.0) / 20.0).collect(); - let value: Vec = (0..kv_len * head_dim).map(|i| ((i % 13) as f32 - 6.0) / 15.0).collect(); + let query: Vec = (0..head_dim) + .map(|i| ((i % 7) as f32 - 3.0) / 10.0) + .collect(); + let key: Vec = (0..kv_len * head_dim) + .map(|i| ((i % 11) as f32 - 5.0) / 20.0) + .collect(); + let value: Vec = (0..kv_len * head_dim) + .map(|i| ((i % 13) as f32 - 6.0) / 15.0) + .collect(); let output_neon = flash_attention_neon(&query, &key, &value, scale, false); let output_scalar = attention_scalar(&query, &key, &value, head_dim, kv_len, scale); @@ -201,7 +217,9 @@ fn test_cross_platform_rms_norm() { let test_cases = [8, 16, 32, 64, 128]; for dim in test_cases { - let mut x_neon: Vec = (0..dim).map(|i| (i as f32 - dim as f32 / 2.0) / 10.0).collect(); + let mut x_neon: Vec = (0..dim) + .map(|i| (i as f32 - dim as f32 / 2.0) / 10.0) + .collect(); let mut x_scalar = x_neon.clone(); let weight: Vec = (0..dim).map(|i| 0.5 + (i as f32) * 0.01).collect(); let eps = 1e-6; @@ -214,7 +232,11 @@ fn test_cross_platform_rms_norm() { assert!( abs_error < 1e-4, "Cross-platform RMSNorm mismatch at dim={}, index {}: {} vs {} (abs: {:.6})", - dim, i, x_neon[i], x_scalar[i], abs_error + dim, + i, + x_neon[i], + x_scalar[i], + abs_error ); } } @@ -239,7 +261,11 @@ fn test_cross_platform_layer_norm() { assert!( abs_error < 1e-4, "Cross-platform LayerNorm mismatch at dim={}, index {}: {} vs {} (abs: {:.6})", - dim, i, x_neon[i], x_scalar[i], abs_error + dim, + i, + x_neon[i], + x_scalar[i], + abs_error ); } } @@ -255,7 +281,10 @@ fn test_scalar_fallback_edge_cases() { let mut y = vec![0.0f32; 4]; gemv_neon(&a_zero, &x_zero, &mut y, 4, 4); - assert!(y.iter().all(|&v| v == 0.0), "Zero input should give zero output"); + assert!( + y.iter().all(|&v| v == 0.0), + "Zero input should give zero output" + ); // Single element let a_single = vec![3.0f32]; @@ -266,12 +295,19 @@ fn test_scalar_fallback_edge_cases() { assert!((y_single[0] - 12.0).abs() < 1e-5, "1x1 GEMV failed"); // Negative values - let a_neg: Vec = (0..16).map(|i| if i % 2 == 0 { 1.0 } else { -1.0 }).collect(); - let x_neg: Vec = (0..4).map(|i| if i % 2 == 0 { -1.0 } else { 1.0 }).collect(); + let a_neg: Vec = (0..16) + .map(|i| if i % 2 == 0 { 1.0 } else { -1.0 }) + .collect(); + let x_neg: Vec = (0..4) + .map(|i| if i % 2 == 0 { -1.0 } else { 1.0 }) + .collect(); let mut y_neg = vec![0.0f32; 4]; gemv_neon(&a_neg, &x_neg, &mut y_neg, 4, 4); - assert!(y_neg.iter().all(|&v| v.is_finite()), "Negative values should produce finite output"); + assert!( + y_neg.iter().all(|&v| v.is_finite()), + "Negative values should produce finite output" + ); } #[test] @@ -282,7 +318,10 @@ fn test_scalar_fallback_numerical_stability() { let mut y_small = vec![0.0f32; 8]; gemv_neon(&a_small, &x_small, &mut y_small, 8, 8); - assert!(y_small.iter().all(|&v| v.is_finite()), "Very small values should produce finite output"); + assert!( + y_small.iter().all(|&v| v.is_finite()), + "Very small values should produce finite output" + ); // Large values (but not overflow) let a_large: Vec = vec![1e10; 64]; @@ -290,15 +329,23 @@ fn test_scalar_fallback_numerical_stability() { let mut y_large = vec![0.0f32; 8]; gemv_neon(&a_large, &x_large, &mut y_large, 8, 8); - assert!(y_large.iter().all(|&v| v.is_finite()), "Large values with small x should produce finite output"); + assert!( + y_large.iter().all(|&v| v.is_finite()), + "Large values with small x should produce finite output" + ); // Mixed magnitudes - let a_mixed: Vec = (0..64).map(|i| if i % 2 == 0 { 1e5 } else { 1e-5 }).collect(); + let a_mixed: Vec = (0..64) + .map(|i| if i % 2 == 0 { 1e5 } else { 1e-5 }) + .collect(); let x_mixed: Vec = vec![1.0; 8]; let mut y_mixed = vec![0.0f32; 8]; gemv_neon(&a_mixed, &x_mixed, &mut y_mixed, 8, 8); - assert!(y_mixed.iter().all(|&v| v.is_finite()), "Mixed magnitude values should produce finite output"); + assert!( + y_mixed.iter().all(|&v| v.is_finite()), + "Mixed magnitude values should produce finite output" + ); } #[test] @@ -350,7 +397,10 @@ fn test_wasm_compatible_operations() { gemm_neon(&a_gemm, &b_gemm, &mut c_gemm, 2, 2, 2); // A * I = A for i in 0..4 { - assert!((c_gemm[i] - a_gemm[i]).abs() < 1e-5, "GEMM with identity failed"); + assert!( + (c_gemm[i] - a_gemm[i]).abs() < 1e-5, + "GEMM with identity failed" + ); } // Small attention @@ -374,14 +424,23 @@ fn test_scalar_path_verification() { let mut y = vec![0.0; 1]; gemv_neon(&a, &x, &mut y, 1, 3); let expected = 1.0 + 4.0 + 9.0; // 1*1 + 2*2 + 3*3 = 14 - assert!((y[0] - expected).abs() < 1e-5, "Scalar GEMV expected {}, got {}", expected, y[0]); + assert!( + (y[0] - expected).abs() < 1e-5, + "Scalar GEMV expected {}, got {}", + expected, + y[0] + ); // Verify GEMM with 1x1 let a1 = vec![5.0f32]; let b1 = vec![3.0f32]; let mut c1 = vec![0.0f32]; gemm_neon(&a1, &b1, &mut c1, 1, 1, 1); - assert!((c1[0] - 15.0).abs() < 1e-5, "1x1 GEMM expected 15, got {}", c1[0]); + assert!( + (c1[0] - 15.0).abs() < 1e-5, + "1x1 GEMM expected 15, got {}", + c1[0] + ); // Verify normalization with small vector let mut x_norm = vec![3.0, 4.0]; diff --git a/crates/ruvllm/tests/cross_platform_v21.rs b/crates/ruvllm/tests/cross_platform_v21.rs index 0363e9cab..d1850a314 100644 --- a/crates/ruvllm/tests/cross_platform_v21.rs +++ b/crates/ruvllm/tests/cross_platform_v21.rs @@ -71,10 +71,7 @@ impl Platform { pub fn supports_webgpu(&self) -> bool { matches!( self, - Platform::MacOS - | Platform::Linux - | Platform::Windows - | Platform::WebAssembly + Platform::MacOS | Platform::Linux | Platform::Windows | Platform::WebAssembly ) } @@ -136,9 +133,9 @@ impl Architecture { /// Get SIMD width in bytes pub fn simd_width(&self) -> usize { match self { - Architecture::X86_64 => 32, // AVX2 + Architecture::X86_64 => 32, // AVX2 Architecture::Aarch64 => 16, // NEON - Architecture::Wasm32 => 16, // SIMD128 + Architecture::Wasm32 => 16, // SIMD128 Architecture::Unknown => 0, } } @@ -547,7 +544,10 @@ impl FallbackChain { /// Get the primary backend pub fn primary(&self) -> ComputeBackend { - self.backends.first().copied().unwrap_or(ComputeBackend::Cpu) + self.backends + .first() + .copied() + .unwrap_or(ComputeBackend::Cpu) } /// Get all backends in order @@ -711,10 +711,7 @@ impl OptimalConfig { }; // Flash attention availability - let use_flash_attention = matches!( - backend, - ComputeBackend::Metal | ComputeBackend::Cuda - ); + let use_flash_attention = matches!(backend, ComputeBackend::Metal | ComputeBackend::Cuda); // Memory mapping (not available in WASM) let memory_mapped_weights = caps.platform.supports_native_io(); diff --git a/crates/ruvllm/tests/e2e_integration.rs b/crates/ruvllm/tests/e2e_integration.rs index 7bb77e533..e6eb3cb58 100644 --- a/crates/ruvllm/tests/e2e_integration.rs +++ b/crates/ruvllm/tests/e2e_integration.rs @@ -5,17 +5,17 @@ use chrono::Utc; use ruvllm::{ - RuvLLMConfig, RuvLLMEngine, - backends::{DeviceType, DType, GenerateParams, ModelConfig, ModelArchitecture, Quantization}, - kv_cache::{TwoTierKvCache, KvCacheConfig}, - paged_attention::{PagedAttention, PagedAttentionConfig}, - lora::{MicroLoRA, MicroLoraConfig, TargetModule, AdaptFeedback}, - sona::{SonaIntegration, SonaConfig, LearningLoop, Trajectory}, - session::{SessionManager, SessionConfig}, - policy_store::{PolicyStore, PolicyEntry, PolicyType, QuantizationPolicy, PolicySource}, - witness_log::{WitnessLog, WitnessEntry, LatencyBreakdown, RoutingDecision}, - types::ModelSize, + backends::{DType, DeviceType, GenerateParams, ModelArchitecture, ModelConfig, Quantization}, error::Result, + kv_cache::{KvCacheConfig, TwoTierKvCache}, + lora::{AdaptFeedback, MicroLoRA, MicroLoraConfig, TargetModule}, + paged_attention::{PagedAttention, PagedAttentionConfig}, + policy_store::{PolicyEntry, PolicySource, PolicyStore, PolicyType, QuantizationPolicy}, + session::{SessionConfig, SessionManager}, + sona::{LearningLoop, SonaConfig, SonaIntegration, Trajectory}, + types::ModelSize, + witness_log::{LatencyBreakdown, RoutingDecision, WitnessEntry, WitnessLog}, + RuvLLMConfig, RuvLLMEngine, }; use std::sync::Arc; use std::time::{Duration, Instant}; @@ -137,7 +137,11 @@ fn test_kv_cache_eviction() { // Should have evicted to stay under max let stats = cache.stats(); - assert!(stats.total_tokens <= 10, "Should evict to stay under max: {}", stats.total_tokens); + assert!( + stats.total_tokens <= 10, + "Should evict to stay under max: {}", + stats.total_tokens + ); } #[test] @@ -164,8 +168,16 @@ fn test_kv_cache_two_tier_storage() { // Should have some in tail and some in store assert_eq!(stats.total_tokens, 10); - assert!(stats.tail_tokens <= 4, "Tail should be limited: {}", stats.tail_tokens); - assert!(stats.store_tokens >= 6, "Store should have overflow: {}", stats.store_tokens); + assert!( + stats.tail_tokens <= 4, + "Tail should be limited: {}", + stats.tail_tokens + ); + assert!( + stats.store_tokens >= 6, + "Store should have overflow: {}", + stats.store_tokens + ); } #[test] @@ -232,8 +244,8 @@ fn test_paged_attention_basic() { #[test] fn test_concurrent_kv_cache_access() { - use std::thread; use std::sync::Arc; + use std::thread; let config = KvCacheConfig { tail_length: 64, @@ -349,15 +361,16 @@ fn test_witness_log() { format!("session-{}", i % 2), vec![i as f32 * 0.1; 64], routing_decision, - ).with_quality(0.85) - .with_latency(LatencyBreakdown { + ) + .with_quality(0.85) + .with_latency(LatencyBreakdown { embedding_ms: 5.0, retrieval_ms: 2.0, routing_ms: 1.0, attention_ms: 30.0, generation_ms: 62.0, total_ms: 100.0 + (i as f32 * 10.0), - }); + }); log.record(entry).unwrap(); } @@ -428,7 +441,12 @@ fn test_end_to_end_adaptation_flow() { // Verify quality increased let first_qualities: f32 = quality_history[..5].iter().sum::() / 5.0; let last_qualities: f32 = quality_history[15..].iter().sum::() / 5.0; - assert!(last_qualities > first_qualities, "Quality should increase: {} vs {}", last_qualities, first_qualities); + assert!( + last_qualities > first_qualities, + "Quality should increase: {} vs {}", + last_qualities, + first_qualities + ); } #[test] @@ -626,9 +644,12 @@ fn test_memory_efficiency() { let bytes_per_store_token = stats.store_bytes as f32 / stats.store_tokens as f32; // Quantized store should use less memory (or same if not actually quantized) - assert!(bytes_per_store_token <= bytes_per_tail_token * 1.1, + assert!( + bytes_per_store_token <= bytes_per_tail_token * 1.1, "Store should be more memory efficient: {} vs {} bytes/token", - bytes_per_store_token, bytes_per_tail_token); + bytes_per_store_token, + bytes_per_tail_token + ); } } diff --git a/crates/ruvllm/tests/e2e_integration_test.rs b/crates/ruvllm/tests/e2e_integration_test.rs index 09daf05c6..ebb3e09fe 100644 --- a/crates/ruvllm/tests/e2e_integration_test.rs +++ b/crates/ruvllm/tests/e2e_integration_test.rs @@ -30,24 +30,24 @@ use ruvllm::{ // Backends backends::{ - GenerateParams, GeneratedToken, LlmBackend, ModelArchitecture, ModelConfig, - Quantization, SpecialTokens, StreamEvent, TokenStream, Tokenizer, + GenerateParams, GeneratedToken, LlmBackend, ModelArchitecture, ModelConfig, Quantization, + SpecialTokens, StreamEvent, TokenStream, Tokenizer, }, + // Error handling + error::{Result, RuvLLMError}, // KV Cache kv_cache::{KvCacheConfig, TwoTierKvCache}, + // Serving + serving::{ + InferenceRequest, KvCachePoolConfig, Priority, ServingEngine, ServingEngineConfig, + TokenOutput, + }, // Speculative decoding speculative::{ log_softmax, sample_from_probs, softmax, top_k_filter, top_p_filter, AtomicSpeculativeStats, SpeculationTree, SpeculativeConfig, SpeculativeDecoder, SpeculativeStats, TreeNode, }, - // Serving - serving::{ - InferenceRequest, KvCachePoolConfig, Priority, ServingEngine, ServingEngineConfig, - TokenOutput, - }, - // Error handling - error::{Result, RuvLLMError}, }; use std::collections::HashMap; @@ -214,7 +214,10 @@ impl MockTokenizer { reverse_vocab.insert(id, text.to_string()); } - Self { vocab, reverse_vocab } + Self { + vocab, + reverse_vocab, + } } } @@ -227,7 +230,9 @@ impl Tokenizer for MockTokenizer { tokens.push(id); } else { // Unknown word - hash it to a pseudo-ID - let hash = word.bytes().fold(200u32, |acc, b| acc.wrapping_add(b as u32)); + let hash = word + .bytes() + .fold(200u32, |acc, b| acc.wrapping_add(b as u32)); tokens.push(hash % 1000 + 200); } } @@ -238,7 +243,10 @@ impl Tokenizer for MockTokenizer { let words: Vec = tokens .iter() .filter_map(|&id| { - self.reverse_vocab.get(&id).cloned().or_else(|| Some(format!("[{}]", id))) + self.reverse_vocab + .get(&id) + .cloned() + .or_else(|| Some(format!("[{}]", id))) }) .collect(); Ok(words.join(" ")) @@ -275,9 +283,9 @@ impl MockLlmBackend { } fn deterministic_token(&self, context: &[u32], seed_offset: usize) -> u32 { - let hash = context - .iter() - .fold(seed_offset as u32, |acc, &t| acc.wrapping_add(t).wrapping_mul(31)); + let hash = context.iter().fold(seed_offset as u32, |acc, &t| { + acc.wrapping_add(t).wrapping_mul(31) + }); // Generate tokens in reasonable vocabulary range (hash % 30000) + 100 } @@ -460,7 +468,9 @@ impl<'a> Iterator for MockStreamIterator<'a> { return None; } - let token = self.backend.deterministic_token(&self.context, self.seed_offset); + let token = self + .backend + .deterministic_token(&self.context, self.seed_offset); self.seed_offset += 1; self.remaining -= 1; @@ -522,7 +532,9 @@ fn test_gguf_load_and_generate_basic() { // Create mock backend and generate let mut backend = MockLlmBackend::new(); - backend.load_model("test-model", ModelConfig::default()).unwrap(); + backend + .load_model("test-model", ModelConfig::default()) + .unwrap(); let params = GenerateParams::default().with_max_tokens(10); let output = backend.generate("Hello world", params).unwrap(); @@ -543,8 +555,7 @@ fn test_gguf_load_with_metadata() { assert_eq!(magic, GGUF_MAGIC); // Count metadata (at offset 16) - let metadata_count = - u64::from_le_bytes(gguf_data[16..24].try_into().unwrap()); + let metadata_count = u64::from_le_bytes(gguf_data[16..24].try_into().unwrap()); assert_eq!(metadata_count, 3, "Should have 3 metadata entries"); } @@ -557,8 +568,7 @@ fn test_gguf_load_with_quantization() { let magic = u32::from_le_bytes([gguf_data[0], gguf_data[1], gguf_data[2], gguf_data[3]]); assert_eq!(magic, GGUF_MAGIC); - let tensor_count = - u64::from_le_bytes(gguf_data[8..16].try_into().unwrap()); + let tensor_count = u64::from_le_bytes(gguf_data[8..16].try_into().unwrap()); assert_eq!(tensor_count, 1, "Should have 1 quantized tensor"); // Test quantization type bytes_per_weight @@ -579,7 +589,9 @@ fn test_gguf_load_with_quantization() { fn test_streaming_generation() { // Test: Streaming callback generation works correctly let mut backend = MockLlmBackend::new(); - backend.load_model("test-model", ModelConfig::default()).unwrap(); + backend + .load_model("test-model", ModelConfig::default()) + .unwrap(); let params = GenerateParams::default() .with_max_tokens(20) @@ -595,10 +607,7 @@ fn test_streaming_generation() { } assert!(!tokens_received.is_empty(), "Should receive tokens"); - assert!( - tokens_received.len() <= 20, - "Should respect max_tokens" - ); + assert!(tokens_received.len() <= 20, "Should respect max_tokens"); // Verify each token has valid fields for token in &tokens_received { @@ -610,7 +619,9 @@ fn test_streaming_generation() { fn test_streaming_generation_v2() { // Test: New TokenStream interface let mut backend = MockLlmBackend::new(); - backend.load_model("test-model", ModelConfig::default()).unwrap(); + backend + .load_model("test-model", ModelConfig::default()) + .unwrap(); let params = GenerateParams::default() .with_max_tokens(10) @@ -692,7 +703,10 @@ fn test_speculative_decoding_config() { assert!(config.lookahead >= 2, "Lookahead should be at least 2"); assert!(config.lookahead <= 16, "Lookahead should be reasonable"); assert!(config.acceptance_threshold > 0.0 && config.acceptance_threshold <= 1.0); - assert!(config.adaptive_lookahead, "Adaptive lookahead should be on by default"); + assert!( + config.adaptive_lookahead, + "Adaptive lookahead should be on by default" + ); } #[test] @@ -771,7 +785,10 @@ fn test_speculation_tree() { // Best path should be the one with higher probability let best = tree.best_path(); - assert!(best.is_empty() || best[0] == 100, "Best path should start with high-prob token"); + assert!( + best.is_empty() || best[0] == 100, + "Best path should start with high-prob token" + ); } #[test] @@ -1004,9 +1021,7 @@ fn test_batch_generation() { // Should have processed requests assert!( - stats.running_requests > 0 - || stats.completed_requests > 0 - || stats.pending_requests > 0, + stats.running_requests > 0 || stats.completed_requests > 0 || stats.pending_requests > 0, "Should have processed some requests" ); } @@ -1146,7 +1161,10 @@ fn test_log_softmax() { let probs = softmax(&logits); for (a, b) in probs_from_log.iter().zip(probs.iter()) { - assert!((a - b).abs() < 0.001, "exp(log_softmax) should equal softmax"); + assert!( + (a - b).abs() < 0.001, + "exp(log_softmax) should equal softmax" + ); } } @@ -1176,10 +1194,7 @@ fn test_top_p_filtering() { // Most probability mass should be preserved let finite_count = logits.iter().filter(|x| x.is_finite()).count(); assert!(finite_count >= 1, "Top-p should keep at least one value"); - assert!( - finite_count < 5, - "Top-p with 0.9 should filter some values" - ); + assert!(finite_count < 5, "Top-p with 0.9 should filter some values"); } #[test] @@ -1217,9 +1232,7 @@ fn test_deterministic_generation_with_seed() { backend1.load_model("test", ModelConfig::default()).unwrap(); backend2.load_model("test", ModelConfig::default()).unwrap(); - let params = GenerateParams::default() - .with_max_tokens(10) - .with_seed(42); + let params = GenerateParams::default().with_max_tokens(10).with_seed(42); let output1 = backend1.generate("Hello", params.clone()).unwrap(); let output2 = backend2.generate("Hello", params).unwrap(); @@ -1236,8 +1249,8 @@ fn test_deterministic_generation_with_seed() { #[ignore = "Requires GGUF model file at TEST_MODEL_PATH environment variable"] fn test_real_model_generation() { // Test: Load actual GGUF model and generate - let model_path = env::var("TEST_MODEL_PATH") - .expect("TEST_MODEL_PATH environment variable must be set"); + let model_path = + env::var("TEST_MODEL_PATH").expect("TEST_MODEL_PATH environment variable must be set"); let path = Path::new(&model_path); assert!(path.exists(), "Model file should exist: {}", model_path); @@ -1265,8 +1278,8 @@ fn test_real_model_generation() { #[ignore = "Requires GGUF model file at TEST_MODEL_PATH environment variable"] fn test_real_model_streaming() { // Test: Stream generation from real model - let model_path = env::var("TEST_MODEL_PATH") - .expect("TEST_MODEL_PATH environment variable must be set"); + let model_path = + env::var("TEST_MODEL_PATH").expect("TEST_MODEL_PATH environment variable must be set"); // Would need real model loading here // For now, verify environment is set correctly @@ -1280,8 +1293,8 @@ fn test_real_model_streaming() { #[ignore = "Requires GGUF model file at TEST_MODEL_PATH environment variable"] fn test_real_model_quantization() { // Test: Load quantized model and verify inference - let _model_path = env::var("TEST_MODEL_PATH") - .expect("TEST_MODEL_PATH environment variable must be set"); + let _model_path = + env::var("TEST_MODEL_PATH").expect("TEST_MODEL_PATH environment variable must be set"); // Verify quantization types assert!(Quantization::Q4K.is_gguf()); @@ -1336,9 +1349,7 @@ fn test_full_pipeline_mock() { // Should have made progress let stats = engine.stats(); assert!( - stats.running_requests > 0 - || stats.completed_requests > 0 - || stats.pending_requests > 0 + stats.running_requests > 0 || stats.completed_requests > 0 || stats.pending_requests > 0 ); } @@ -1370,10 +1381,17 @@ fn test_engine_metrics() { let metrics = engine.metrics(); // Requests may have completed by now, so check all states assert!( - metrics.pending_requests > 0 || metrics.running_requests > 0 || metrics.completed_requests > 0 + metrics.pending_requests > 0 + || metrics.running_requests > 0 + || metrics.completed_requests > 0 || metrics.total_requests_processed > 0, "Should have requests processed, pending, running, or completed: {:?}", - (metrics.pending_requests, metrics.running_requests, metrics.completed_requests, metrics.total_requests_processed) + ( + metrics.pending_requests, + metrics.running_requests, + metrics.completed_requests, + metrics.total_requests_processed + ) ); } @@ -1497,5 +1515,8 @@ fn test_embeddings_generation() { .map(|(a, b)| (a - b).abs()) .sum(); - assert!(diff > 0.1, "Different texts should have different embeddings"); + assert!( + diff > 0.1, + "Different texts should have different embeddings" + ); } diff --git a/crates/ruvllm/tests/gguf_integration.rs b/crates/ruvllm/tests/gguf_integration.rs index 7891dc45d..047ececf1 100644 --- a/crates/ruvllm/tests/gguf_integration.rs +++ b/crates/ruvllm/tests/gguf_integration.rs @@ -108,7 +108,12 @@ impl GgmlType { GgmlType::Q4_0 | GgmlType::Q4_1 => 32, GgmlType::Q5_0 | GgmlType::Q5_1 => 32, GgmlType::Q8_0 | GgmlType::Q8_1 => 32, - GgmlType::Q2K | GgmlType::Q3K | GgmlType::Q4K | GgmlType::Q5K | GgmlType::Q6K | GgmlType::Q8K => 256, + GgmlType::Q2K + | GgmlType::Q3K + | GgmlType::Q4K + | GgmlType::Q5K + | GgmlType::Q6K + | GgmlType::Q8K => 256, _ => 32, // Default for newer types } } @@ -123,14 +128,14 @@ impl GgmlType { GgmlType::I16 => 2, GgmlType::I32 => 4, GgmlType::I64 => 8, - GgmlType::Q4_0 => 18, // 32 * 4/8 + 2 (scale) - GgmlType::Q4_1 => 20, // 32 * 4/8 + 2 (scale) + 2 (min) - GgmlType::Q5_0 => 22, // 32 * 5/8 + 2 (scale) (approx) + GgmlType::Q4_0 => 18, // 32 * 4/8 + 2 (scale) + GgmlType::Q4_1 => 20, // 32 * 4/8 + 2 (scale) + 2 (min) + GgmlType::Q5_0 => 22, // 32 * 5/8 + 2 (scale) (approx) GgmlType::Q5_1 => 24, - GgmlType::Q8_0 => 34, // 32 * 1 + 2 (scale) + GgmlType::Q8_0 => 34, // 32 * 1 + 2 (scale) GgmlType::Q8_1 => 36, - GgmlType::Q4K => 144, // Complex super-block format - _ => 32, // Approximation + GgmlType::Q4K => 144, // Complex super-block format + _ => 32, // Approximation } } } @@ -335,23 +340,31 @@ impl GgufFile { fn read_header(reader: &mut R) -> Result { let mut buf = [0u8; 4]; - reader.read_exact(&mut buf).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf) + .map_err(|e| GgufError::IoError(e.to_string()))?; let magic = u32::from_le_bytes(buf); if magic != GGUF_MAGIC { return Err(GgufError::InvalidMagic(magic)); } - reader.read_exact(&mut buf).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf) + .map_err(|e| GgufError::IoError(e.to_string()))?; let version = u32::from_le_bytes(buf); if version > GGUF_VERSION { return Err(GgufError::UnsupportedVersion(version)); } let mut buf8 = [0u8; 8]; - reader.read_exact(&mut buf8).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf8) + .map_err(|e| GgufError::IoError(e.to_string()))?; let tensor_count = u64::from_le_bytes(buf8); - reader.read_exact(&mut buf8).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf8) + .map_err(|e| GgufError::IoError(e.to_string()))?; let metadata_kv_count = u64::from_le_bytes(buf8); Ok(GgufHeader { @@ -364,11 +377,15 @@ impl GgufFile { fn read_string(reader: &mut R) -> Result { let mut buf8 = [0u8; 8]; - reader.read_exact(&mut buf8).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf8) + .map_err(|e| GgufError::IoError(e.to_string()))?; let len = u64::from_le_bytes(buf8) as usize; let mut str_buf = vec![0u8; len]; - reader.read_exact(&mut str_buf).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut str_buf) + .map_err(|e| GgufError::IoError(e.to_string()))?; String::from_utf8(str_buf).map_err(|e| GgufError::InvalidData(e.to_string())) } @@ -377,7 +394,9 @@ impl GgufFile { let key = Self::read_string(reader)?; let mut buf4 = [0u8; 4]; - reader.read_exact(&mut buf4).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf4) + .map_err(|e| GgufError::IoError(e.to_string()))?; let value_type = GgufMetadataType::try_from(u32::from_le_bytes(buf4))?; let value = Self::read_metadata_value(reader, value_type)?; @@ -385,7 +404,10 @@ impl GgufFile { Ok((key, value)) } - fn read_metadata_value(reader: &mut R, value_type: GgufMetadataType) -> Result { + fn read_metadata_value( + reader: &mut R, + value_type: GgufMetadataType, + ) -> Result { let mut buf1 = [0u8; 1]; let mut buf2 = [0u8; 2]; let mut buf4 = [0u8; 4]; @@ -393,35 +415,51 @@ impl GgufFile { match value_type { GgufMetadataType::Uint8 => { - reader.read_exact(&mut buf1).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf1) + .map_err(|e| GgufError::IoError(e.to_string()))?; Ok(GgufValue::Uint8(buf1[0])) } GgufMetadataType::Int8 => { - reader.read_exact(&mut buf1).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf1) + .map_err(|e| GgufError::IoError(e.to_string()))?; Ok(GgufValue::Int8(buf1[0] as i8)) } GgufMetadataType::Uint16 => { - reader.read_exact(&mut buf2).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf2) + .map_err(|e| GgufError::IoError(e.to_string()))?; Ok(GgufValue::Uint16(u16::from_le_bytes(buf2))) } GgufMetadataType::Int16 => { - reader.read_exact(&mut buf2).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf2) + .map_err(|e| GgufError::IoError(e.to_string()))?; Ok(GgufValue::Int16(i16::from_le_bytes(buf2))) } GgufMetadataType::Uint32 => { - reader.read_exact(&mut buf4).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf4) + .map_err(|e| GgufError::IoError(e.to_string()))?; Ok(GgufValue::Uint32(u32::from_le_bytes(buf4))) } GgufMetadataType::Int32 => { - reader.read_exact(&mut buf4).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf4) + .map_err(|e| GgufError::IoError(e.to_string()))?; Ok(GgufValue::Int32(i32::from_le_bytes(buf4))) } GgufMetadataType::Float32 => { - reader.read_exact(&mut buf4).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf4) + .map_err(|e| GgufError::IoError(e.to_string()))?; Ok(GgufValue::Float32(f32::from_le_bytes(buf4))) } GgufMetadataType::Bool => { - reader.read_exact(&mut buf1).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf1) + .map_err(|e| GgufError::IoError(e.to_string()))?; Ok(GgufValue::Bool(buf1[0] != 0)) } GgufMetadataType::String => { @@ -430,10 +468,14 @@ impl GgufFile { } GgufMetadataType::Array => { // Read array type and length - reader.read_exact(&mut buf4).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf4) + .map_err(|e| GgufError::IoError(e.to_string()))?; let elem_type = GgufMetadataType::try_from(u32::from_le_bytes(buf4))?; - reader.read_exact(&mut buf8).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf8) + .map_err(|e| GgufError::IoError(e.to_string()))?; let len = u64::from_le_bytes(buf8) as usize; let mut arr = Vec::with_capacity(len); @@ -443,15 +485,21 @@ impl GgufFile { Ok(GgufValue::Array(arr)) } GgufMetadataType::Uint64 => { - reader.read_exact(&mut buf8).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf8) + .map_err(|e| GgufError::IoError(e.to_string()))?; Ok(GgufValue::Uint64(u64::from_le_bytes(buf8))) } GgufMetadataType::Int64 => { - reader.read_exact(&mut buf8).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf8) + .map_err(|e| GgufError::IoError(e.to_string()))?; Ok(GgufValue::Int64(i64::from_le_bytes(buf8))) } GgufMetadataType::Float64 => { - reader.read_exact(&mut buf8).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf8) + .map_err(|e| GgufError::IoError(e.to_string()))?; Ok(GgufValue::Float64(f64::from_le_bytes(buf8))) } } @@ -462,23 +510,31 @@ impl GgufFile { // Read number of dimensions let mut buf4 = [0u8; 4]; - reader.read_exact(&mut buf4).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf4) + .map_err(|e| GgufError::IoError(e.to_string()))?; let n_dims = u32::from_le_bytes(buf4) as usize; // Read dimensions let mut dimensions = Vec::with_capacity(n_dims); let mut buf8 = [0u8; 8]; for _ in 0..n_dims { - reader.read_exact(&mut buf8).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf8) + .map_err(|e| GgufError::IoError(e.to_string()))?; dimensions.push(u64::from_le_bytes(buf8)); } // Read type - reader.read_exact(&mut buf4).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf4) + .map_err(|e| GgufError::IoError(e.to_string()))?; let dtype = GgmlType::try_from(u32::from_le_bytes(buf4))?; // Read offset - reader.read_exact(&mut buf8).map_err(|e| GgufError::IoError(e.to_string()))?; + reader + .read_exact(&mut buf8) + .map_err(|e| GgufError::IoError(e.to_string()))?; let offset = u64::from_le_bytes(buf8); Ok(GgufTensorInfo { @@ -605,7 +661,7 @@ fn create_test_gguf_with_metadata() -> Vec { /// Q4_0 block structure (32 elements) #[repr(C, packed)] pub struct BlockQ4_0 { - pub d: u16, // Scale as f16 + pub d: u16, // Scale as f16 pub qs: [u8; 16], // Packed 4-bit values } @@ -840,7 +896,9 @@ fn test_quantization_roundtrip_accuracy() { dequantize_q4_0(&quantized, &mut restored); // Check accuracy (Q4_0 should be within ~6-7% of original for most values) - let max_error = original.iter().zip(restored.iter()) + let max_error = original + .iter() + .zip(restored.iter()) .map(|(a, b)| (a - b).abs()) .fold(0.0f32, f32::max); @@ -859,7 +917,12 @@ fn test_quantization_extreme_values() { // Values should be recoverable within quantization error for (orig, restored) in large.iter().zip(d_large.iter()) { let rel_error = (orig - restored).abs() / orig.abs().max(1e-6); - assert!(rel_error < 0.2, "Large value error: {} vs {}", orig, restored); + assert!( + rel_error < 0.2, + "Large value error: {} vs {}", + orig, + restored + ); } // Test with small values @@ -897,7 +960,10 @@ fn test_f16_conversion() { assert!( error < 0.01 || (v - back).abs() < 1e-3, "F16 roundtrip error for {}: {} -> {} -> {}", - v, v, h, back + v, + v, + h, + back ); } } diff --git a/crates/ruvllm/tests/gguf_loader_test.rs b/crates/ruvllm/tests/gguf_loader_test.rs index d93492cf2..f3e7bf8f3 100644 --- a/crates/ruvllm/tests/gguf_loader_test.rs +++ b/crates/ruvllm/tests/gguf_loader_test.rs @@ -78,7 +78,8 @@ impl TestTensorNameMapper { } if lower.contains("norm") || lower.contains("ln_") || lower.contains("layer_norm") { - if lower.contains("final") || lower.contains("model.norm") || !lower.contains("layers") { + if lower.contains("final") || lower.contains("model.norm") || !lower.contains("layers") + { return "FinalNorm"; } return "LayerNorm"; @@ -93,9 +94,18 @@ fn test_llama_tensor_name_mapping() { let mapper = TestTensorNameMapper::new("llama"); // Test layer extraction - assert_eq!(mapper.extract_layer_index("model.layers.0.self_attn.q_proj.weight"), Some(0)); - assert_eq!(mapper.extract_layer_index("model.layers.31.mlp.gate_proj.weight"), Some(31)); - assert_eq!(mapper.extract_layer_index("model.embed_tokens.weight"), None); + assert_eq!( + mapper.extract_layer_index("model.layers.0.self_attn.q_proj.weight"), + Some(0) + ); + assert_eq!( + mapper.extract_layer_index("model.layers.31.mlp.gate_proj.weight"), + Some(31) + ); + assert_eq!( + mapper.extract_layer_index("model.embed_tokens.weight"), + None + ); assert_eq!(mapper.extract_layer_index("lm_head.weight"), None); } @@ -104,9 +114,18 @@ fn test_phi_tensor_name_mapping() { let mapper = TestTensorNameMapper::new("phi"); // Phi uses transformer.h.N pattern - assert_eq!(mapper.extract_layer_index("transformer.h.0.mixer.Wqkv.weight"), Some(0)); - assert_eq!(mapper.extract_layer_index("transformer.h.15.mlp.fc1.weight"), Some(15)); - assert_eq!(mapper.extract_layer_index("transformer.embd.wte.weight"), None); + assert_eq!( + mapper.extract_layer_index("transformer.h.0.mixer.Wqkv.weight"), + Some(0) + ); + assert_eq!( + mapper.extract_layer_index("transformer.h.15.mlp.fc1.weight"), + Some(15) + ); + assert_eq!( + mapper.extract_layer_index("transformer.embd.wte.weight"), + None + ); } #[test] @@ -114,27 +133,54 @@ fn test_qwen_tensor_name_mapping() { let mapper = TestTensorNameMapper::new("qwen"); // Qwen uses transformer.h.N pattern like GPT-2 - assert_eq!(mapper.extract_layer_index("transformer.h.0.attn.c_attn.weight"), Some(0)); - assert_eq!(mapper.extract_layer_index("transformer.h.23.mlp.w1.weight"), Some(23)); + assert_eq!( + mapper.extract_layer_index("transformer.h.0.attn.c_attn.weight"), + Some(0) + ); + assert_eq!( + mapper.extract_layer_index("transformer.h.23.mlp.w1.weight"), + Some(23) + ); } #[test] fn test_tensor_categorization_attention() { let mapper = TestTensorNameMapper::new("llama"); - assert_eq!(mapper.categorize("model.layers.0.self_attn.q_proj.weight"), "AttentionQuery"); - assert_eq!(mapper.categorize("model.layers.0.self_attn.k_proj.weight"), "AttentionKey"); - assert_eq!(mapper.categorize("model.layers.0.self_attn.v_proj.weight"), "AttentionValue"); - assert_eq!(mapper.categorize("model.layers.0.self_attn.o_proj.weight"), "AttentionOutput"); + assert_eq!( + mapper.categorize("model.layers.0.self_attn.q_proj.weight"), + "AttentionQuery" + ); + assert_eq!( + mapper.categorize("model.layers.0.self_attn.k_proj.weight"), + "AttentionKey" + ); + assert_eq!( + mapper.categorize("model.layers.0.self_attn.v_proj.weight"), + "AttentionValue" + ); + assert_eq!( + mapper.categorize("model.layers.0.self_attn.o_proj.weight"), + "AttentionOutput" + ); } #[test] fn test_tensor_categorization_mlp() { let mapper = TestTensorNameMapper::new("llama"); - assert_eq!(mapper.categorize("model.layers.0.mlp.gate_proj.weight"), "FfnGate"); - assert_eq!(mapper.categorize("model.layers.0.mlp.up_proj.weight"), "FfnUp"); - assert_eq!(mapper.categorize("model.layers.0.mlp.down_proj.weight"), "FfnDown"); + assert_eq!( + mapper.categorize("model.layers.0.mlp.gate_proj.weight"), + "FfnGate" + ); + assert_eq!( + mapper.categorize("model.layers.0.mlp.up_proj.weight"), + "FfnUp" + ); + assert_eq!( + mapper.categorize("model.layers.0.mlp.down_proj.weight"), + "FfnDown" + ); } #[test] @@ -362,18 +408,36 @@ impl ArchitectureTensorMap { fn test_llama_tensor_patterns() { let map = ArchitectureTensorMap::llama(); - assert_eq!(map.layer_tensor(map.q_proj_pattern, 0), "model.layers.0.self_attn.q_proj.weight"); - assert_eq!(map.layer_tensor(map.gate_proj_pattern, 15), "model.layers.15.mlp.gate_proj.weight"); - assert_eq!(map.layer_tensor(map.down_proj_pattern, 31), "model.layers.31.mlp.down_proj.weight"); + assert_eq!( + map.layer_tensor(map.q_proj_pattern, 0), + "model.layers.0.self_attn.q_proj.weight" + ); + assert_eq!( + map.layer_tensor(map.gate_proj_pattern, 15), + "model.layers.15.mlp.gate_proj.weight" + ); + assert_eq!( + map.layer_tensor(map.down_proj_pattern, 31), + "model.layers.31.mlp.down_proj.weight" + ); } #[test] fn test_phi_tensor_patterns() { let map = ArchitectureTensorMap::phi(); - assert_eq!(map.layer_tensor(map.q_proj_pattern, 0), "transformer.h.0.mixer.Wqkv.weight"); - assert_eq!(map.layer_tensor(map.o_proj_pattern, 7), "transformer.h.7.mixer.out_proj.weight"); - assert_eq!(map.layer_tensor(map.down_proj_pattern, 23), "transformer.h.23.mlp.fc2.weight"); + assert_eq!( + map.layer_tensor(map.q_proj_pattern, 0), + "transformer.h.0.mixer.Wqkv.weight" + ); + assert_eq!( + map.layer_tensor(map.o_proj_pattern, 7), + "transformer.h.7.mixer.out_proj.weight" + ); + assert_eq!( + map.layer_tensor(map.down_proj_pattern, 23), + "transformer.h.23.mlp.fc2.weight" + ); } #[test] @@ -391,7 +455,11 @@ fn test_gemma_tied_embeddings() { #[derive(Clone)] enum TestWeightTensor { F32(Vec, Vec), - Quantized { data: Vec, quant_type: u32, shape: Vec }, + Quantized { + data: Vec, + quant_type: u32, + shape: Vec, + }, } impl TestWeightTensor { @@ -599,7 +667,7 @@ fn estimate_model_memory(config: &TestModelConfig, quant_type: &str) -> usize { "Q8_0" => 1.0625, // ~8.5 bits per weight "Q4_K" => 0.5625, // ~4.5 bits per weight "Q4_0" => 0.5625, - "Q2_K" => 0.325, // ~2.6 bits per weight + "Q2_K" => 0.325, // ~2.6 bits per weight _ => 4.0, }; @@ -636,8 +704,8 @@ fn test_memory_estimation_llama_7b() { // F32 ~7B params * 4 bytes = ~28GB // Q4_K ~7B params * 0.5625 bytes = ~4GB assert!(f32_size > 20_000_000_000); // > 20GB - assert!(q4_size < 6_000_000_000); // < 6GB - assert!(f32_size > q4_size * 5); // F32 should be ~7x larger + assert!(q4_size < 6_000_000_000); // < 6GB + assert!(f32_size > q4_size * 5); // F32 should be ~7x larger } #[test] diff --git a/crates/ruvllm/tests/kernel_integration.rs b/crates/ruvllm/tests/kernel_integration.rs index 7ecc3d9d6..e7e404021 100644 --- a/crates/ruvllm/tests/kernel_integration.rs +++ b/crates/ruvllm/tests/kernel_integration.rs @@ -3,19 +3,17 @@ //! Tests attention, RoPE, normalization, and matrix multiplication kernels //! comparing NEON implementations to scalar reference implementations. -use ruvllm::kernels::{ - flash_attention_neon, grouped_query_attention_neon, multi_query_attention_neon, - paged_attention_neon, PagedKvCache, - gemm_neon, gemv_neon, batched_gemm_neon, - layer_norm_neon, rms_norm_neon, - apply_rope_neon, precompute_rope_tables, RopeConfig, - AttentionConfig, -}; +use ruvllm::kernels::matmul::gemm_nt_neon; +use ruvllm::kernels::norm::{batched_layer_norm_neon, batched_rms_norm_neon, compute_rms}; use ruvllm::kernels::rope::{ apply_inverse_rope_neon, apply_rope_with_tables, precompute_rope_tables_with_config, RopeTables, }; -use ruvllm::kernels::norm::{batched_layer_norm_neon, batched_rms_norm_neon, compute_rms}; -use ruvllm::kernels::matmul::gemm_nt_neon; +use ruvllm::kernels::{ + apply_rope_neon, batched_gemm_neon, flash_attention_neon, gemm_neon, gemv_neon, + grouped_query_attention_neon, layer_norm_neon, multi_query_attention_neon, + paged_attention_neon, precompute_rope_tables, rms_norm_neon, AttentionConfig, PagedKvCache, + RopeConfig, +}; // ========== Attention Tests ========== @@ -100,7 +98,13 @@ fn test_attention_with_various_lengths() { let output = flash_attention_neon(&query, &key, &value, scale, false); - assert_eq!(output.len(), head_dim, "head_dim={}, kv_len={}", head_dim, kv_len); + assert_eq!( + output.len(), + head_dim, + "head_dim={}, kv_len={}", + head_dim, + kv_len + ); assert!( output.iter().all(|&v| v.is_finite()), "Non-finite attention output for head_dim={}, kv_len={}", @@ -152,8 +156,12 @@ fn test_mqa_attention() { .map(|i| (i as f32) * 0.01) .collect(); let kv_len = 4; - let keys: Vec = (0..kv_len * config.head_dim).map(|i| (i as f32) * 0.01).collect(); - let values: Vec = (0..kv_len * config.head_dim).map(|i| (i as f32) * 0.02).collect(); + let keys: Vec = (0..kv_len * config.head_dim) + .map(|i| (i as f32) * 0.01) + .collect(); + let values: Vec = (0..kv_len * config.head_dim) + .map(|i| (i as f32) * 0.02) + .collect(); let output = multi_query_attention_neon(&queries, &keys, &values, &config); @@ -209,7 +217,9 @@ fn test_rope_correctness() { let base = 10000.0; // Position 0 should be identity (cos=1, sin=0) - let mut x_pos0: Vec = vec![1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0]; + let mut x_pos0: Vec = vec![ + 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, + ]; let original = x_pos0.clone(); apply_rope_neon(&mut x_pos0, &[0], head_dim, base); @@ -236,7 +246,9 @@ fn test_rope_rotation_at_nonzero_position() { // At non-zero position, values should change assert!( - x.iter().zip(original.iter()).any(|(a, b)| (a - b).abs() > 1e-6), + x.iter() + .zip(original.iter()) + .any(|(a, b)| (a - b).abs() > 1e-6), "RoPE should rotate at non-zero position" ); } @@ -337,7 +349,10 @@ fn test_rope_multiple_tokens() { assert!(x[1].abs() < 1e-5); // Tokens 1 and 2 should be rotated - assert!(x.iter().skip(8).any(|&v| (v - 1.0).abs() > 1e-5 || v.abs() > 1e-5)); + assert!(x + .iter() + .skip(8) + .any(|&v| (v - 1.0).abs() > 1e-5 || v.abs() > 1e-5)); } #[test] @@ -430,7 +445,11 @@ fn test_layer_norm_mean_and_variance() { // Variance should be ~1 let var: f32 = x.iter().map(|v| (v - mean).powi(2)).sum::() / 8.0; - assert!((var - 1.0).abs() < 1e-4, "Variance should be ~1, got {}", var); + assert!( + (var - 1.0).abs() < 1e-4, + "Variance should be ~1, got {}", + var + ); } #[test] @@ -475,7 +494,12 @@ fn test_batched_layer_norm() { let offset = b * dim; let slice = &x[offset..offset + dim]; let mean: f32 = slice.iter().sum::() / dim as f32; - assert!(mean.abs() < 1e-4, "Batch {} mean should be ~0, got {}", b, mean); + assert!( + mean.abs() < 1e-4, + "Batch {} mean should be ~0, got {}", + b, + mean + ); } } @@ -483,7 +507,11 @@ fn test_batched_layer_norm() { fn test_compute_rms() { let x = vec![3.0, 4.0]; // RMS = sqrt((9+16)/2) = sqrt(12.5) ~ 3.536 let rms = compute_rms(&x); - assert!((rms - 3.5355).abs() < 0.01, "RMS should be ~3.536, got {}", rms); + assert!( + (rms - 3.5355).abs() < 0.01, + "RMS should be ~3.536, got {}", + rms + ); } // ========== Matmul Tests ========== @@ -492,16 +520,10 @@ fn test_compute_rms() { fn test_matmul_accuracy() { // 4x4 * 4x4 = 4x4 let a = vec![ - 1.0, 2.0, 3.0, 4.0, - 5.0, 6.0, 7.0, 8.0, - 9.0, 10.0, 11.0, 12.0, - 13.0, 14.0, 15.0, 16.0, + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, ]; let b = vec![ - 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0, + 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, ]; // Identity let mut c = vec![0.0; 16]; @@ -664,8 +686,12 @@ fn test_gemm_parallel_correctness() { let k = 256; let n = 128; - let a: Vec = (0..m * k).map(|i| ((i % 127) as f32 - 63.0) / 100.0).collect(); - let b: Vec = (0..k * n).map(|i| ((i % 63) as f32 - 31.0) / 50.0).collect(); + let a: Vec = (0..m * k) + .map(|i| ((i % 127) as f32 - 63.0) / 100.0) + .collect(); + let b: Vec = (0..k * n) + .map(|i| ((i % 63) as f32 - 31.0) / 50.0) + .collect(); // Sequential GEMM reference let mut c_seq = vec![0.0; m * n]; @@ -682,7 +708,11 @@ fn test_gemm_parallel_correctness() { assert!( rel_error < 0.01 || abs_error < 1e-4, "Parallel GEMM mismatch at {}: {} vs {} (rel: {:.4}, abs: {:.6})", - i, c_neon[i], c_seq[i], rel_error, abs_error + i, + c_neon[i], + c_seq[i], + rel_error, + abs_error ); } } @@ -693,7 +723,9 @@ fn test_gemv_parallel_correctness() { let m = 256; let n = 512; - let a: Vec = (0..m * n).map(|i| ((i % 127) as f32 - 63.0) / 100.0).collect(); + let a: Vec = (0..m * n) + .map(|i| ((i % 127) as f32 - 63.0) / 100.0) + .collect(); let x: Vec = (0..n).map(|i| ((i % 63) as f32 - 31.0) / 50.0).collect(); // Sequential reference GEMV @@ -717,7 +749,11 @@ fn test_gemv_parallel_correctness() { assert!( rel_error < 0.01 || abs_error < 1e-4, "Parallel GEMV mismatch at {}: {} vs {} (rel: {:.4}, abs: {:.6})", - i, y_neon[i], y_ref[i], rel_error, abs_error + i, + y_neon[i], + y_ref[i], + rel_error, + abs_error ); } } @@ -726,11 +762,11 @@ fn test_gemv_parallel_correctness() { #[test] fn test_gemm_various_dimensions() { let test_cases = [ - (7, 11, 13), // Odd, non-aligned - (12, 12, 12), // Multiple of tile sizes - (1, 1, 1), // Minimum - (64, 64, 64), // Power of 2 - (100, 50, 75), // Mixed sizes + (7, 11, 13), // Odd, non-aligned + (12, 12, 12), // Multiple of tile sizes + (1, 1, 1), // Minimum + (64, 64, 64), // Power of 2 + (100, 50, 75), // Mixed sizes ]; for (m, k, n) in test_cases { @@ -748,7 +784,13 @@ fn test_gemm_various_dimensions() { assert!( abs_error < 0.5, "GEMM ({},{},{}) mismatch at {}: {} vs {} (abs: {:.6})", - m, k, n, i, c_neon[i], c_ref[i], abs_error + m, + k, + n, + i, + c_neon[i], + c_ref[i], + abs_error ); } } @@ -758,11 +800,11 @@ fn test_gemm_various_dimensions() { #[test] fn test_gemv_various_dimensions() { let test_cases = [ - (7, 11), // Odd dimensions - (12, 12), // Square - (1, 1), // Minimum - (64, 128), // Rectangular - (100, 50), // M > N + (7, 11), // Odd dimensions + (12, 12), // Square + (1, 1), // Minimum + (64, 128), // Rectangular + (100, 50), // M > N ]; for (m, n) in test_cases { @@ -786,7 +828,12 @@ fn test_gemv_various_dimensions() { assert!( abs_error < 0.1, "GEMV ({},{}) mismatch at {}: {} vs {} (abs: {:.6})", - m, n, i, y_neon[i], y_ref[i], abs_error + m, + n, + i, + y_neon[i], + y_ref[i], + abs_error ); } } @@ -802,9 +849,15 @@ fn test_flash_attention_v2_correctness() { let scale = 1.0 / (head_dim as f32).sqrt(); // Create test data with varied values - let query: Vec = (0..head_dim).map(|i| ((i % 7) as f32 - 3.0) / 10.0).collect(); - let key: Vec = (0..kv_len * head_dim).map(|i| ((i % 11) as f32 - 5.0) / 20.0).collect(); - let value: Vec = (0..kv_len * head_dim).map(|i| ((i % 13) as f32 - 6.0) / 15.0).collect(); + let query: Vec = (0..head_dim) + .map(|i| ((i % 7) as f32 - 3.0) / 10.0) + .collect(); + let key: Vec = (0..kv_len * head_dim) + .map(|i| ((i % 11) as f32 - 5.0) / 20.0) + .collect(); + let value: Vec = (0..kv_len * head_dim) + .map(|i| ((i % 13) as f32 - 6.0) / 15.0) + .collect(); // Flash Attention NEON (v2) let output_fa = flash_attention_neon(&query, &key, &value, scale, false); @@ -819,7 +872,10 @@ fn test_flash_attention_v2_correctness() { assert!( rel_error < 0.01 || abs_error < 1e-3, "Flash Attention v2 mismatch at {}: {} vs {} (rel: {:.4})", - i, output_fa[i], output_ref[i], rel_error + i, + output_fa[i], + output_ref[i], + rel_error ); } } @@ -840,16 +896,24 @@ fn test_flash_attention_v2_block_sizes() { let output = flash_attention_neon(&query, &key, &value, scale, false); - assert_eq!(output.len(), head_dim, "head_dim={}, kv_len={}", head_dim, kv_len); + assert_eq!( + output.len(), + head_dim, + "head_dim={}, kv_len={}", + head_dim, + kv_len + ); assert!( output.iter().all(|&v| v.is_finite()), "Non-finite output for head_dim={}, kv_len={}", - head_dim, kv_len + head_dim, + kv_len ); assert!( output.iter().any(|&v| v.abs() > 1e-10), "All-zero output for head_dim={}, kv_len={}", - head_dim, kv_len + head_dim, + kv_len ); } } @@ -867,21 +931,36 @@ fn test_flash_attention_v2_numerical_stability() { let key_small: Vec = vec![1e-6; kv_len * head_dim]; let value_small: Vec = vec![1e-6; kv_len * head_dim]; let output_small = flash_attention_neon(&query_small, &key_small, &value_small, scale, false); - assert!(output_small.iter().all(|&v| v.is_finite()), "Small values should produce finite output"); + assert!( + output_small.iter().all(|&v| v.is_finite()), + "Small values should produce finite output" + ); // Test with larger values (but not overflow range) let query_large: Vec = vec![10.0; head_dim]; let key_large: Vec = vec![10.0; kv_len * head_dim]; let value_large: Vec = vec![10.0; kv_len * head_dim]; let output_large = flash_attention_neon(&query_large, &key_large, &value_large, scale, false); - assert!(output_large.iter().all(|&v| v.is_finite()), "Large values should produce finite output"); + assert!( + output_large.iter().all(|&v| v.is_finite()), + "Large values should produce finite output" + ); // Test with mixed positive/negative values - let query_mixed: Vec = (0..head_dim).map(|i| if i % 2 == 0 { 1.0 } else { -1.0 }).collect(); - let key_mixed: Vec = (0..kv_len * head_dim).map(|i| if i % 3 == 0 { 1.0 } else { -0.5 }).collect(); - let value_mixed: Vec = (0..kv_len * head_dim).map(|i| (i as f32) * 0.1 - 5.0).collect(); + let query_mixed: Vec = (0..head_dim) + .map(|i| if i % 2 == 0 { 1.0 } else { -1.0 }) + .collect(); + let key_mixed: Vec = (0..kv_len * head_dim) + .map(|i| if i % 3 == 0 { 1.0 } else { -0.5 }) + .collect(); + let value_mixed: Vec = (0..kv_len * head_dim) + .map(|i| (i as f32) * 0.1 - 5.0) + .collect(); let output_mixed = flash_attention_neon(&query_mixed, &key_mixed, &value_mixed, scale, false); - assert!(output_mixed.iter().all(|&v| v.is_finite()), "Mixed values should produce finite output"); + assert!( + output_mixed.iter().all(|&v| v.is_finite()), + "Mixed values should produce finite output" + ); } // ========== V2 Feature Tests: INT8/INT4 Quantized Accuracy ========== @@ -889,9 +968,8 @@ fn test_flash_attention_v2_numerical_stability() { #[cfg(target_arch = "aarch64")] mod quantized_tests { use ruvllm::kernels::quantized::{ - quantize_to_int8, dequantize_int8, int8_gemv_neon, - quantize_to_int4, dequantize_int4, int4_gemv_neon, - INT4_BLOCK_SIZE, + dequantize_int4, dequantize_int8, int4_gemv_neon, int8_gemv_neon, quantize_to_int4, + quantize_to_int8, INT4_BLOCK_SIZE, }; /// Test INT8 quantization accuracy is within 1% of FP32 @@ -901,7 +979,9 @@ mod quantized_tests { let n = 128; // Create test matrix with reasonable value range - let a_f32: Vec = (0..m * n).map(|i| ((i % 200) as f32 - 100.0) / 100.0).collect(); + let a_f32: Vec = (0..m * n) + .map(|i| ((i % 200) as f32 - 100.0) / 100.0) + .collect(); let x: Vec = (0..n).map(|i| ((i % 50) as f32 - 25.0) / 25.0).collect(); // Reference FP32 GEMV @@ -928,13 +1008,20 @@ mod quantized_tests { max_rel_error = max_rel_error.max(rel_error); max_abs_error = max_abs_error.max(abs_error); assert!( - rel_error < 0.05 || abs_error < 0.05, // 5% tolerance for double quantization (A and x) + rel_error < 0.05 || abs_error < 0.05, // 5% tolerance for double quantization (A and x) "INT8 GEMV error at row {}: quant={}, ref={} (rel: {:.2}%, abs: {:.6})", - i, y_quant[i], y_ref[i], rel_error * 100.0, abs_error + i, + y_quant[i], + y_ref[i], + rel_error * 100.0, + abs_error ); } - println!("INT8 max relative error: {:.2}%, max absolute error: {:.6}", - max_rel_error * 100.0, max_abs_error); + println!( + "INT8 max relative error: {:.2}%, max absolute error: {:.6}", + max_rel_error * 100.0, + max_abs_error + ); } /// Test INT4 quantization accuracy is within 5% of FP32 @@ -945,7 +1032,9 @@ mod quantized_tests { let block_size = INT4_BLOCK_SIZE; // Create test matrix with reasonable value range - let a_f32: Vec = (0..m * n).map(|i| ((i % 100) as f32 - 50.0) / 50.0).collect(); + let a_f32: Vec = (0..m * n) + .map(|i| ((i % 100) as f32 - 50.0) / 50.0) + .collect(); let x: Vec = (0..n).map(|i| ((i % 20) as f32 - 10.0) / 10.0).collect(); // Reference FP32 GEMV @@ -972,7 +1061,16 @@ mod quantized_tests { // Run INT4 GEMV let mut y_quant = vec![0.0f32; m]; - int4_gemv_neon(&all_packed, &x, &mut y_quant, m, n, &all_scales, &all_mins, block_size); + int4_gemv_neon( + &all_packed, + &x, + &mut y_quant, + m, + n, + &all_scales, + &all_mins, + block_size, + ); // Check accuracy - INT4 should be within 5% or small absolute error let mut max_rel_error = 0.0f32; @@ -983,13 +1081,20 @@ mod quantized_tests { max_rel_error = max_rel_error.max(rel_error); max_abs_error = max_abs_error.max(abs_error); assert!( - rel_error < 0.40 || abs_error < 0.5, // 40% tolerance due to INT4 (4-bit = 16 levels) precision loss + rel_error < 0.40 || abs_error < 0.5, // 40% tolerance due to INT4 (4-bit = 16 levels) precision loss "INT4 GEMV error at row {}: quant={}, ref={} (rel: {:.2}%, abs: {:.6})", - i, y_quant[i], y_ref[i], rel_error * 100.0, abs_error + i, + y_quant[i], + y_ref[i], + rel_error * 100.0, + abs_error ); } - println!("INT4 max relative error: {:.2}%, max absolute error: {:.6}", - max_rel_error * 100.0, max_abs_error); + println!( + "INT4 max relative error: {:.2}%, max absolute error: {:.6}", + max_rel_error * 100.0, + max_abs_error + ); } /// Test quantization roundtrip preserves values @@ -1003,23 +1108,28 @@ mod quantized_tests { for (orig, deq) in data_8.iter().zip(dequantized_8.iter()) { let error = (orig - deq).abs(); assert!( - error < 0.02, // ~2% error tolerance for INT8 + error < 0.02, // ~2% error tolerance for INT8 "INT8 roundtrip error: {} vs {} (error: {})", - orig, deq, error + orig, + deq, + error ); } // INT4 roundtrip let data_4: Vec = (0..64).map(|i| (i as f32 - 32.0) / 32.0).collect(); let (packed_4, scales_4, mins_4) = quantize_to_int4(&data_4, INT4_BLOCK_SIZE); - let dequantized_4 = dequantize_int4(&packed_4, &scales_4, &mins_4, INT4_BLOCK_SIZE, data_4.len()); + let dequantized_4 = + dequantize_int4(&packed_4, &scales_4, &mins_4, INT4_BLOCK_SIZE, data_4.len()); for (orig, deq) in data_4.iter().zip(dequantized_4.iter()) { let error = (orig - deq).abs(); assert!( - error < 0.15, // ~15% error tolerance for INT4 + error < 0.15, // ~15% error tolerance for INT4 "INT4 roundtrip error: {} vs {} (error: {})", - orig, deq, error + orig, + deq, + error ); } } diff --git a/crates/ruvllm/tests/lora_integration.rs b/crates/ruvllm/tests/lora_integration.rs index 55a0ccc29..fc6b33fb1 100644 --- a/crates/ruvllm/tests/lora_integration.rs +++ b/crates/ruvllm/tests/lora_integration.rs @@ -4,8 +4,8 @@ //! EWC state management, and serialization. use ruvllm::{ - lora::{AdaptFeedback, LoraAdapter, MicroLoRA, MicroLoraConfig, TargetModule}, error::Result, + lora::{AdaptFeedback, LoraAdapter, MicroLoRA, MicroLoraConfig, TargetModule}, }; use std::collections::HashMap; @@ -143,7 +143,11 @@ fn test_lora_adapter_forward() { // With zero-initialized B, output should be zero let sum: f32 = output.iter().sum(); - assert!(sum.abs() < 1e-6, "Initial forward should be ~0, got {}", sum); + assert!( + sum.abs() < 1e-6, + "Initial forward should be ~0, got {}", + sum + ); } #[test] @@ -392,7 +396,12 @@ fn test_lora_adapter_simd_forward() { let expected = adapter.forward(&input_array); for (o, e) in output.iter().zip(expected.iter()) { - assert!((o - e).abs() < 1e-5, "SIMD forward mismatch: {} vs {}", o, e); + assert!( + (o - e).abs() < 1e-5, + "SIMD forward mismatch: {} vs {}", + o, + e + ); } } @@ -494,7 +503,11 @@ fn test_config_builder_methods() { let config = MicroLoraConfig::for_hidden_dim(256) .with_rank(1) .with_alpha(8.0) - .with_targets(vec![TargetModule::QProj, TargetModule::KProj, TargetModule::VProj]); + .with_targets(vec![ + TargetModule::QProj, + TargetModule::KProj, + TargetModule::VProj, + ]); assert_eq!(config.rank, 1); assert_eq!(config.alpha, 8.0); diff --git a/crates/ruvllm/tests/model_arch_integration.rs b/crates/ruvllm/tests/model_arch_integration.rs index 67979c0f8..a43ff9684 100644 --- a/crates/ruvllm/tests/model_arch_integration.rs +++ b/crates/ruvllm/tests/model_arch_integration.rs @@ -8,7 +8,6 @@ //! - Grouped Query Attention (GQA) //! - RoPE (Rotary Position Embedding) configurations - // ============================================================================= // Model Configuration Types // ============================================================================= @@ -906,10 +905,7 @@ mod tests { #[test] fn test_gemma_chat_template_uses_model_role() { let template = GemmaChatTemplate; - let messages = vec![ - ChatMessage::user("Hello"), - ChatMessage::assistant("Hi!"), - ]; + let messages = vec![ChatMessage::user("Hello"), ChatMessage::assistant("Hi!")]; let result = template.format(&messages, false); @@ -1166,7 +1162,11 @@ mod tests { fn test_phi3_full_pipeline_setup() { let config = Phi3Config::phi3_mini(); let template = Phi3ChatTemplate; - let rope = RoPE::new(config.head_dim(), config.rope_theta, config.max_position_embeddings); + let rope = RoPE::new( + config.head_dim(), + config.rope_theta, + config.max_position_embeddings, + ); // Validate config assert!(config.validate().is_ok()); @@ -1200,7 +1200,10 @@ mod tests { assert!(!prompt.is_empty()); // Sliding window is ready - assert_eq!(sliding_window.effective_context(10000), config.sliding_window); + assert_eq!( + sliding_window.effective_context(10000), + config.sliding_window + ); // Soft caps are ready assert!(attn_cap.apply(100.0) < config.attn_logit_softcapping); diff --git a/crates/ruvllm/tests/real_model_test.rs b/crates/ruvllm/tests/real_model_test.rs index deb58512c..79468a87c 100644 --- a/crates/ruvllm/tests/real_model_test.rs +++ b/crates/ruvllm/tests/real_model_test.rs @@ -45,24 +45,11 @@ const MODEL_SEARCH_PATHS: &[&str] = &[ ]; /// Supported model file patterns for each architecture -const TINYLLAMA_PATTERNS: &[&str] = &[ - "tinyllama*.gguf", - "TinyLlama*.gguf", - "*tinyllama*.gguf", -]; +const TINYLLAMA_PATTERNS: &[&str] = &["tinyllama*.gguf", "TinyLlama*.gguf", "*tinyllama*.gguf"]; -const PHI3_PATTERNS: &[&str] = &[ - "phi-3*.gguf", - "Phi-3*.gguf", - "*phi3*.gguf", - "*phi-3*.gguf", -]; +const PHI3_PATTERNS: &[&str] = &["phi-3*.gguf", "Phi-3*.gguf", "*phi3*.gguf", "*phi-3*.gguf"]; -const QWEN_PATTERNS: &[&str] = &[ - "qwen*.gguf", - "Qwen*.gguf", - "*qwen*.gguf", -]; +const QWEN_PATTERNS: &[&str] = &["qwen*.gguf", "Qwen*.gguf", "*qwen*.gguf"]; /// Result type for test helpers (reserved for future use) #[allow(dead_code)] @@ -209,7 +196,10 @@ pub fn skip_if_no_model(patterns: &[&str], model_name: &str) -> Option println!("SKIPPED: No {} model found.", model_name); println!("To run this test:"); println!(" 1. Download the model:"); - println!(" cargo run -p ruvllm --example download_test_model -- --model {}", model_name.to_lowercase().replace(' ', "")); + println!( + " cargo run -p ruvllm --example download_test_model -- --model {}", + model_name.to_lowercase().replace(' ', "") + ); println!(" 2. Or set TEST_MODEL_PATH environment variable"); println!(" 3. Or place model in ./test_models/ directory"); None @@ -301,11 +291,17 @@ fn test_gguf_file_validation() { // Read version (4 bytes, little-endian u32) let mut version_bytes = [0u8; 4]; - reader.read_exact(&mut version_bytes).expect("Failed to read version"); + reader + .read_exact(&mut version_bytes) + .expect("Failed to read version"); let version = u32::from_le_bytes(version_bytes); // GGUF versions 2 and 3 are common - assert!(version >= 2 && version <= 3, "Unexpected GGUF version: {}", version); + assert!( + version >= 2 && version <= 3, + "Unexpected GGUF version: {}", + version + ); println!("GGUF file validated:"); println!(" Path: {}", model_path.display()); @@ -353,7 +349,10 @@ fn test_tinyllama_generation() { None => return, }; - println!("Testing generation with TinyLlama: {}", model_path.display()); + println!( + "Testing generation with TinyLlama: {}", + model_path.display() + ); // Placeholder for actual generation test // In real implementation: @@ -460,7 +459,10 @@ fn test_phi3_code_completion() { None => return, }; - println!("Testing code completion with Phi-3: {}", model_path.display()); + println!( + "Testing code completion with Phi-3: {}", + model_path.display() + ); // Code completion prompts test the model's ability to understand code context let _prompts = [ @@ -526,10 +528,10 @@ fn test_qwen_multilingual() { // Qwen is known for good multilingual support let _prompts = [ - "Hello, how are you today?", // English - "Bonjour, comment allez-vous?", // French - "Hallo, wie geht es Ihnen?", // German - "Translate 'hello' to Chinese: ", // Translation task + "Hello, how are you today?", // English + "Bonjour, comment allez-vous?", // French + "Hallo, wie geht es Ihnen?", // German + "Translate 'hello' to Chinese: ", // Translation task ]; println!("Qwen multilingual test placeholder - implement with actual backend"); @@ -550,7 +552,10 @@ fn test_benchmark_generation_speed() { None => return, }; - println!("Benchmarking generation speed with: {}", model_path.display()); + println!( + "Benchmarking generation speed with: {}", + model_path.display() + ); // Benchmark parameters let warmup_iterations = 3; @@ -614,7 +619,10 @@ fn test_memory_usage() { .ok(); if let Some(output) = output { - if let Ok(rss) = String::from_utf8_lossy(&output.stdout).trim().parse::() { + if let Ok(rss) = String::from_utf8_lossy(&output.stdout) + .trim() + .parse::() + { println!("Initial RSS: {} KB", rss); } } @@ -646,10 +654,7 @@ fn test_model_comparison() { ("Qwen", find_test_model(QWEN_PATTERNS)), ]; - let available: Vec<_> = models - .iter() - .filter(|(_, path)| path.is_some()) - .collect(); + let available: Vec<_> = models.iter().filter(|(_, path)| path.is_some()).collect(); if available.is_empty() { println!("SKIPPED: No models available for comparison"); @@ -683,7 +688,10 @@ mod helper_tests { fn test_glob_pattern_matching() { assert!(matches_glob_pattern("tinyllama.gguf", "*.gguf")); assert!(matches_glob_pattern("tinyllama.gguf", "tinyllama*")); - assert!(matches_glob_pattern("tinyllama-1.1b.gguf", "*tinyllama*.gguf")); + assert!(matches_glob_pattern( + "tinyllama-1.1b.gguf", + "*tinyllama*.gguf" + )); assert!(matches_glob_pattern("model.gguf", "model.gguf")); assert!(!matches_glob_pattern("tinyllama.bin", "*.gguf")); assert!(!matches_glob_pattern("other.gguf", "tinyllama*")); diff --git a/crates/ruvllm/tests/ruvltra_e2e.rs b/crates/ruvllm/tests/ruvltra_e2e.rs index 163f3cd2d..068995e26 100644 --- a/crates/ruvllm/tests/ruvltra_e2e.rs +++ b/crates/ruvllm/tests/ruvltra_e2e.rs @@ -23,10 +23,7 @@ //! cargo test --package ruvllm --features coreml,hybrid-ane ruvltra_e2e //! ``` -use ruvllm::backends::{ - GenerateParams, LlmBackend, - ModelArchitecture, ModelConfig, Quantization, -}; +use ruvllm::backends::{GenerateParams, LlmBackend, ModelArchitecture, ModelConfig, Quantization}; use ruvllm::error::{Result, RuvLLMError}; use ruvllm::gguf::quantization::GgufQuantType; use ruvllm::kernels::is_ane_available; @@ -140,7 +137,9 @@ mod full_inference_pipeline { /// Simulate token generation fn generate_mock_tokens(&self, prompt: &str, max_tokens: usize) -> Vec { // Generate deterministic "tokens" based on prompt hash - let hash = prompt.bytes().fold(0u64, |acc, b| acc.wrapping_mul(31).wrapping_add(b as u64)); + let hash = prompt + .bytes() + .fold(0u64, |acc, b| acc.wrapping_mul(31).wrapping_add(b as u64)); let mut tokens = Vec::with_capacity(max_tokens); let mut state = hash; @@ -257,8 +256,11 @@ mod full_inference_pipeline { let tokens = model.generate_mock_tokens(prompt, 100); assert!(!tokens.is_empty()); - assert!(tokens.len() >= quality_thresholds::MIN_OUTPUT_TOKENS, - "Code generation should produce at least {} tokens", quality_thresholds::MIN_OUTPUT_TOKENS); + assert!( + tokens.len() >= quality_thresholds::MIN_OUTPUT_TOKENS, + "Code generation should produce at least {} tokens", + quality_thresholds::MIN_OUTPUT_TOKENS + ); } #[test] @@ -424,8 +426,11 @@ mod streaming_generation { let first_token_time = start.elapsed(); // First token should come quickly (for mock) - assert!(first_token_time < Duration::from_millis(100), - "First token took {:?}", first_token_time); + assert!( + first_token_time < Duration::from_millis(100), + "First token took {:?}", + first_token_time + ); } #[test] @@ -442,8 +447,12 @@ mod streaming_generation { // All latencies should be below threshold for (i, latency) in latencies.iter().enumerate() { - assert!(*latency < Duration::from_millis(quality_thresholds::MAX_TOKEN_LATENCY_MS), - "Token {} latency {:?} exceeds threshold", i, latency); + assert!( + *latency < Duration::from_millis(quality_thresholds::MAX_TOKEN_LATENCY_MS), + "Token {} latency {:?} exceeds threshold", + i, + latency + ); } } @@ -517,7 +526,9 @@ mod streaming_generation { state.record_token(*token as u32, chunk, Duration::from_millis(10)); } - let non_empty: Vec<_> = state.chunks_received.iter() + let non_empty: Vec<_> = state + .chunks_received + .iter() .filter(|c| !c.is_empty()) .collect(); @@ -544,7 +555,9 @@ mod quality_validation { /// Check if output contains expected patterns fn contains_expected_patterns(output: &str, patterns: &[&str]) -> bool { let output_lower = output.to_lowercase(); - patterns.iter().any(|p| output_lower.contains(&p.to_lowercase())) + patterns + .iter() + .any(|p| output_lower.contains(&p.to_lowercase())) } #[test] @@ -568,8 +581,11 @@ mod quality_validation { let log_probs: Vec = (0..100).map(|_| -2.5).collect(); let ppl = calculate_perplexity(&log_probs); - assert!(ppl < quality_thresholds::MAX_PERPLEXITY, - "Perplexity {} exceeds threshold", ppl); + assert!( + ppl < quality_thresholds::MAX_PERPLEXITY, + "Perplexity {} exceeds threshold", + ppl + ); } #[test] @@ -583,7 +599,10 @@ mod quality_validation { fn test_output_coherence_simple() { // Test expected patterns for simple completion let output = "jumps over the lazy dog"; - assert!(contains_expected_patterns(output, expected_patterns::SIMPLE_COMPLETION_WORDS)); + assert!(contains_expected_patterns( + output, + expected_patterns::SIMPLE_COMPLETION_WORDS + )); } #[test] @@ -597,7 +616,10 @@ mod quality_validation { fn test_output_coherence_code() { // Test expected patterns for code let output = "def fibonacci(n):\n if n <= 1:\n return n\n return fibonacci(n-1) + fibonacci(n-2)"; - assert!(contains_expected_patterns(output, expected_patterns::FIBONACCI_WORDS)); + assert!(contains_expected_patterns( + output, + expected_patterns::FIBONACCI_WORDS + )); } #[test] @@ -649,9 +671,11 @@ mod quality_validation { } // Check for excessive special characters - let special_ratio = text.chars() + let special_ratio = text + .chars() .filter(|c| !c.is_alphanumeric() && !c.is_whitespace()) - .count() as f32 / text.len().max(1) as f32; + .count() as f32 + / text.len().max(1) as f32; if special_ratio > 0.5 { return true; } @@ -722,8 +746,11 @@ mod memory_validation { // Memory increase should be bounded let memory_increase = allocations.len() * 10000 * std::mem::size_of::(); - assert!(memory_increase < quality_thresholds::MAX_MEMORY_INCREASE, - "Memory increase {} exceeds bound", memory_increase); + assert!( + memory_increase < quality_thresholds::MAX_MEMORY_INCREASE, + "Memory increase {} exceeds bound", + memory_increase + ); // Clean up drop(allocations); @@ -741,8 +768,11 @@ mod memory_validation { let kv_cache_bytes = 2 * num_layers * num_kv_heads * head_dim * max_seq_len * 2; // Should be reasonable - assert!(kv_cache_bytes < 500_000_000, - "KV cache {} bytes too large", kv_cache_bytes); + assert!( + kv_cache_bytes < 500_000_000, + "KV cache {} bytes too large", + kv_cache_bytes + ); } #[test] @@ -755,8 +785,11 @@ mod memory_validation { // Activation: batch * seq * hidden * sizeof(f32) let activation_bytes = batch_size * seq_len * hidden_size * 4; - assert!(activation_bytes < 100_000_000, - "Activation memory {} too large", activation_bytes); + assert!( + activation_bytes < 100_000_000, + "Activation memory {} too large", + activation_bytes + ); } #[test] @@ -783,8 +816,11 @@ mod memory_validation { let savings_ratio = 1.0 - (q4k_size as f32 / f32_size as f32); // Q4_K should save at least 70% memory - assert!(savings_ratio > 0.7, - "Q4_K savings ratio {} below expected", savings_ratio); + assert!( + savings_ratio > 0.7, + "Q4_K savings ratio {} below expected", + savings_ratio + ); } } @@ -801,8 +837,12 @@ mod error_handling { let invalid_tokens = [u32::MAX, vocab_size as u32, vocab_size as u32 + 1000]; for token in invalid_tokens { - assert!(token >= vocab_size as u32, - "Token {} should be invalid for vocab size {}", token, vocab_size); + assert!( + token >= vocab_size as u32, + "Token {} should be invalid for vocab size {}", + token, + vocab_size + ); } } @@ -872,7 +912,9 @@ mod stress_tests { for i in 0..iterations { // Simulate rapid generation let prompt = format!("Test prompt {}", i); - let hash = prompt.bytes().fold(0u64, |acc, b| acc.wrapping_mul(31).wrapping_add(b as u64)); + let hash = prompt + .bytes() + .fold(0u64, |acc, b| acc.wrapping_mul(31).wrapping_add(b as u64)); let _ = hash % 32000; // Mock token } } @@ -884,7 +926,9 @@ mod stress_tests { thread::spawn(move || { for j in 0..25 { let prompt = format!("Thread {} prompt {}", i, j); - let hash = prompt.bytes().fold(0u64, |acc, b| acc.wrapping_mul(31).wrapping_add(b as u64)); + let hash = prompt + .bytes() + .fold(0u64, |acc, b| acc.wrapping_mul(31).wrapping_add(b as u64)); let _ = hash % 32000; } }) @@ -901,7 +945,9 @@ mod stress_tests { let lengths = [1, 10, 100, 1000]; for len in lengths { - let prompt: String = (0..len).map(|i| char::from((b'a' + (i % 26) as u8))).collect(); + let prompt: String = (0..len) + .map(|i| char::from((b'a' + (i % 26) as u8))) + .collect(); assert_eq!(prompt.len(), len); } } @@ -973,9 +1019,7 @@ mod benchmarks { let start = Instant::now(); for _ in 0..iterations { // Simulate tokenization - let _tokens: Vec = prompt.bytes() - .map(|b| b as u32) - .collect(); + let _tokens: Vec = prompt.bytes().map(|b| b as u32).collect(); } let duration = start.elapsed(); diff --git a/crates/ruvllm/tests/ruvltra_tests.rs b/crates/ruvllm/tests/ruvltra_tests.rs index 899178141..b375e5698 100644 --- a/crates/ruvllm/tests/ruvltra_tests.rs +++ b/crates/ruvllm/tests/ruvltra_tests.rs @@ -24,16 +24,14 @@ //! ``` use ruvllm::backends::{ - AneCapabilities, ComputeUnits, GenerateParams, LlmBackend, - ModelArchitecture, ModelConfig, Quantization, + AneCapabilities, ComputeUnits, GenerateParams, LlmBackend, ModelArchitecture, ModelConfig, + Quantization, }; use ruvllm::error::{Result, RuvLLMError}; -use ruvllm::gguf::quantization::{ - dequantize_tensor, GgufQuantType, QuantizedTensor, -}; +use ruvllm::gguf::quantization::{dequantize_tensor, GgufQuantType, QuantizedTensor}; use ruvllm::kernels::ane_ops::{ - get_ane_recommendation, is_ane_available, should_use_ane, - should_use_ane_activation, should_use_ane_matmul, AneRecommendation, + get_ane_recommendation, is_ane_available, should_use_ane, should_use_ane_activation, + should_use_ane_matmul, AneRecommendation, }; use std::sync::Arc; @@ -169,8 +167,11 @@ mod model_loading { // Verify name format let quant_name = format!("{:?}", quant); - assert!(quant_name.contains(name) || !quant_name.is_empty(), - "Quantization {:?} should have recognizable name", quant); + assert!( + quant_name.contains(name) || !quant_name.is_empty(), + "Quantization {:?} should have recognizable name", + quant + ); } } @@ -196,13 +197,19 @@ mod model_loading { let invalid_extensions = [".bin", ".safetensors", ".pt", ".pth"]; for ext in valid_extensions { - assert!(ext.to_lowercase().ends_with("gguf"), - "Extension {} should be valid GGUF", ext); + assert!( + ext.to_lowercase().ends_with("gguf"), + "Extension {} should be valid GGUF", + ext + ); } for ext in invalid_extensions { - assert!(!ext.to_lowercase().ends_with("gguf"), - "Extension {} should not be GGUF", ext); + assert!( + !ext.to_lowercase().ends_with("gguf"), + "Extension {} should not be GGUF", + ext + ); } } @@ -264,7 +271,7 @@ mod quantization_accuracy { // Pack values: (8 - offset) gives 0, (9 - offset) gives 1, etc. // Q4_0 uses offset of 8 for i in 0..16 { - let low = 8u8; // Will become 0 after offset + let low = 8u8; // Will become 0 after offset let high = 9u8; // Will become 1 after offset block[2 + i] = low | (high << 4); } @@ -285,11 +292,19 @@ mod quantization_accuracy { // Verify pattern: alternating 0.0, 0.5 for i in 0..32 { if i % 2 == 0 { - assert!(output[i].abs() < QUANTIZATION_EPSILON, - "Even index {} should be ~0.0, got {}", i, output[i]); + assert!( + output[i].abs() < QUANTIZATION_EPSILON, + "Even index {} should be ~0.0, got {}", + i, + output[i] + ); } else { - assert!((output[i] - 0.5).abs() < QUANTIZATION_EPSILON, - "Odd index {} should be ~0.5, got {}", i, output[i]); + assert!( + (output[i] - 0.5).abs() < QUANTIZATION_EPSILON, + "Odd index {} should be ~0.5, got {}", + i, + output[i] + ); } } } @@ -317,8 +332,13 @@ mod quantization_accuracy { // Verify: values should be 1.0, 2.0, ..., 32.0 for i in 0..32 { let expected = (i + 1) as f32; - assert!((output[i] - expected).abs() < EPSILON, - "Index {}: expected {}, got {}", i, expected, output[i]); + assert!( + (output[i] - expected).abs() < EPSILON, + "Index {}: expected {}, got {}", + i, + expected, + output[i] + ); } } @@ -356,21 +376,34 @@ mod quantization_accuracy { for dtype in quant_types { // Block size must be positive - assert!(dtype.block_size() > 0, - "{:?} must have positive block size", dtype); + assert!( + dtype.block_size() > 0, + "{:?} must have positive block size", + dtype + ); // Type size must be positive - assert!(dtype.type_size() > 0, - "{:?} must have positive type size", dtype); + assert!( + dtype.type_size() > 0, + "{:?} must have positive type size", + dtype + ); // Bits per weight should be in reasonable range (1-32) let bits = dtype.bits_per_weight(); - assert!(bits >= 1.0 && bits <= 32.0, - "{:?} bits/weight {} out of range", dtype, bits); + assert!( + bits >= 1.0 && bits <= 32.0, + "{:?} bits/weight {} out of range", + dtype, + bits + ); // Name should be non-empty - assert!(!dtype.name().is_empty(), - "{:?} must have non-empty name", dtype); + assert!( + !dtype.name().is_empty(), + "{:?} must have non-empty name", + dtype + ); } } @@ -431,7 +464,7 @@ mod quantization_accuracy { for block in 0..8 { let base = block * 18; // Set a valid f16 scale: 0x3C00 = 1.0f16, small positive value - data[base] = 0x00; // Low byte of f16 scale + data[base] = 0x00; // Low byte of f16 scale data[base + 1] = 0x3C; // High byte: 0x3C00 = 1.0 // Fill packed 4-bit values with valid patterns (0-15) @@ -449,8 +482,12 @@ mod quantization_accuracy { // All values should be finite for (i, val) in output.iter().enumerate() { - assert!(val.is_finite(), - "Value at index {} should be finite, got {}", i, val); + assert!( + val.is_finite(), + "Value at index {} should be finite, got {}", + i, + val + ); } } @@ -500,14 +537,22 @@ mod sona_integration { fn test_sona_config_defaults() { let config = SonaTestConfig::default(); - assert!(config.learning_rate > 0.0 && config.learning_rate < 1.0, - "Learning rate should be in (0, 1)"); - assert!(config.momentum >= 0.0 && config.momentum < 1.0, - "Momentum should be in [0, 1)"); - assert!(config.adaptation_threshold > 0.0, - "Adaptation threshold must be positive"); - assert!(config.max_adaptations_per_step > 0, - "Max adaptations must be positive"); + assert!( + config.learning_rate > 0.0 && config.learning_rate < 1.0, + "Learning rate should be in (0, 1)" + ); + assert!( + config.momentum >= 0.0 && config.momentum < 1.0, + "Momentum should be in [0, 1)" + ); + assert!( + config.adaptation_threshold > 0.0, + "Adaptation threshold must be positive" + ); + assert!( + config.max_adaptations_per_step > 0, + "Max adaptations must be positive" + ); } #[test] @@ -527,8 +572,11 @@ mod sona_integration { let duration = start.elapsed(); // Should be very fast - assert!(duration < Duration::from_millis(1), - "SONA adaptation took {:?}, expected <1ms", duration); + assert!( + duration < Duration::from_millis(1), + "SONA adaptation took {:?}, expected <1ms", + duration + ); } #[test] @@ -560,8 +608,10 @@ mod sona_integration { // Small dimensions: NEON preferred let decision = make_routing_decision(1, 32); - assert!(decision.use_neon || decision.use_ane, - "Must use some compute backend"); + assert!( + decision.use_neon || decision.use_ane, + "Must use some compute backend" + ); // Large batch with aligned dims: ANE may be preferred on Apple Silicon let decision = make_routing_decision(32, 256); @@ -625,13 +675,14 @@ mod sona_integration { if durations.is_empty() { return 0.0; } - let mean: f64 = durations.iter() - .map(|d| d.as_secs_f64()) - .sum::() / durations.len() as f64; + let mean: f64 = + durations.iter().map(|d| d.as_secs_f64()).sum::() / durations.len() as f64; - durations.iter() + durations + .iter() .map(|d| (d.as_secs_f64() - mean).powi(2)) - .sum::() / durations.len() as f64 + .sum::() + / durations.len() as f64 } #[test] @@ -640,7 +691,7 @@ mod sona_integration { // This prevents catastrophic forgetting in SONA struct EwcConfig { - lambda: f32, // Importance weight + lambda: f32, // Importance weight fisher_samples: usize, } @@ -682,7 +733,10 @@ mod ane_dispatch { // On Apple Silicon, ANE should be available assert!(caps.available, "ANE should be available on Apple Silicon"); assert!(caps.tops > 0.0, "TOPS should be positive"); - assert!(caps.max_model_size_mb > 0, "Max model size should be positive"); + assert!( + caps.max_model_size_mb > 0, + "Max model size should be positive" + ); assert!(!caps.supported_ops.is_empty(), "Should have supported ops"); } @@ -732,12 +786,18 @@ mod ane_dispatch { let recommendation = get_ane_recommendation(m, k, n); // Recommendation should be consistent - assert!(recommendation.confidence >= 0.0 && recommendation.confidence <= 1.0, - "Confidence for {} should be in [0, 1]", desc); + assert!( + recommendation.confidence >= 0.0 && recommendation.confidence <= 1.0, + "Confidence for {} should be in [0, 1]", + desc + ); // Expected speedup should be reasonable - assert!(recommendation.expected_speedup > 0.0 && recommendation.expected_speedup < 10.0, - "Speedup for {} should be reasonable", desc); + assert!( + recommendation.expected_speedup > 0.0 && recommendation.expected_speedup < 10.0, + "Speedup for {} should be reasonable", + desc + ); } } @@ -747,7 +807,7 @@ mod ane_dispatch { (1, 64), (32, 256), (64, 4096), - (100, 128), // Above typical ANE batch limit + (100, 128), // Above typical ANE batch limit (1, 1000000), // Very large tensor ]; @@ -818,11 +878,7 @@ mod ane_dispatch { #[test] fn test_ane_no_dispatch_errors() { // Simulate dispatch to verify no errors occur - let test_tensors = [ - (1, 64), - (32, 256), - (64, 4096), - ]; + let test_tensors = [(1, 64), (32, 256), (64, 4096)]; for (batch, dim) in test_tensors { // These should never panic @@ -883,8 +939,10 @@ mod memory_management { let embedding_size_q4k = GgufQuantType::Q4_K.tensor_size(vocab_size * hidden_size); // Q4_K should be much smaller - assert!(embedding_size_q4k < embedding_size_f32 / 4, - "Q4_K should be at least 4x smaller than F32"); + assert!( + embedding_size_q4k < embedding_size_f32 / 4, + "Q4_K should be at least 4x smaller than F32" + ); } #[test] @@ -900,8 +958,12 @@ mod memory_management { let kv_per_layer = 2 * max_seq_len * num_kv_heads * head_dim * 2; let total_kv_cache = kv_per_layer * num_layers; - assert!(total_kv_cache < MEMORY_BOUNDS.max_kv_cache_memory as usize, - "KV cache {} exceeds bound {}", total_kv_cache, MEMORY_BOUNDS.max_kv_cache_memory); + assert!( + total_kv_cache < MEMORY_BOUNDS.max_kv_cache_memory as usize, + "KV cache {} exceeds bound {}", + total_kv_cache, + MEMORY_BOUNDS.max_kv_cache_memory + ); } #[test] @@ -935,8 +997,12 @@ mod output_validation { // All logits should be finite for (i, logit) in logits.iter().enumerate() { - assert!(logit.is_finite(), - "Logit at index {} should be finite, got {}", i, logit); + assert!( + logit.is_finite(), + "Logit at index {} should be finite, got {}", + i, + logit + ); } } @@ -958,13 +1024,20 @@ mod output_validation { // Probabilities should sum to 1.0 let prob_sum: f32 = probs.iter().sum(); - assert!((prob_sum - 1.0).abs() < EPSILON, - "Probabilities should sum to 1.0, got {}", prob_sum); + assert!( + (prob_sum - 1.0).abs() < EPSILON, + "Probabilities should sum to 1.0, got {}", + prob_sum + ); // All probabilities should be in [0, 1] for (i, p) in probs.iter().enumerate() { - assert!(*p >= 0.0 && *p <= 1.0, - "Probability at {} should be in [0, 1], got {}", i, p); + assert!( + *p >= 0.0 && *p <= 1.0, + "Probability at {} should be in [0, 1], got {}", + i, + p + ); } } @@ -975,16 +1048,20 @@ mod output_validation { // All tokens should be valid (within vocab range) for token in &sample_tokens { - assert!(*token < RUVLTRA_SMALL_CONFIG.vocab_size as u32, - "Token {} exceeds vocab size", token); + assert!( + *token < RUVLTRA_SMALL_CONFIG.vocab_size as u32, + "Token {} exceeds vocab size", + token + ); } // No repeated padding tokens at start (unless intentional) // This is a basic coherence check - let has_varied_tokens = sample_tokens.windows(2) - .any(|w| w[0] != w[1]); - assert!(has_varied_tokens || sample_tokens.len() <= 1, - "Token sequence should have variety"); + let has_varied_tokens = sample_tokens.windows(2).any(|w| w[0] != w[1]); + assert!( + has_varied_tokens || sample_tokens.len() <= 1, + "Token sequence should have variety" + ); } #[test] @@ -1004,8 +1081,12 @@ mod output_validation { // Verify row sums are approximately 1.0 for i in 0..seq_len { let row_sum: f32 = attention[i * seq_len..(i + 1) * seq_len].iter().sum(); - assert!((row_sum - 1.0).abs() < LOOSE_EPSILON, - "Attention row {} should sum to 1.0, got {}", i, row_sum); + assert!( + (row_sum - 1.0).abs() < LOOSE_EPSILON, + "Attention row {} should sum to 1.0, got {}", + i, + row_sum + ); } } } @@ -1033,8 +1114,11 @@ mod performance_validation { let duration = start.elapsed(); // Basic operations should be very fast - assert!(duration < Duration::from_millis(10), - "Basic ops took {:?}", duration); + assert!( + duration < Duration::from_millis(10), + "Basic ops took {:?}", + duration + ); } #[test] @@ -1078,8 +1162,11 @@ mod performance_validation { println!("Throughput: {:.2e} ops/sec", ops_per_second); // Should achieve reasonable throughput - assert!(ops_per_second > 1_000_000.0, - "Throughput {:.2e} below minimum", ops_per_second); + assert!( + ops_per_second > 1_000_000.0, + "Throughput {:.2e} below minimum", + ops_per_second + ); } } diff --git a/crates/ruvllm/tests/serving_integration.rs b/crates/ruvllm/tests/serving_integration.rs index 934aee63c..67b1e3274 100644 --- a/crates/ruvllm/tests/serving_integration.rs +++ b/crates/ruvllm/tests/serving_integration.rs @@ -155,14 +155,21 @@ impl RequestQueue { /// Submit a new request pub fn submit(&mut self, request: InferenceRequest) { - self.queues.get_mut(&request.priority).unwrap().push_back(request); + self.queues + .get_mut(&request.priority) + .unwrap() + .push_back(request); self.count += 1; } /// Pop highest priority request pub fn pop(&mut self) -> Option { - for priority in [RequestPriority::Realtime, RequestPriority::High, - RequestPriority::Normal, RequestPriority::Low] { + for priority in [ + RequestPriority::Realtime, + RequestPriority::High, + RequestPriority::Normal, + RequestPriority::Low, + ] { if let Some(queue) = self.queues.get_mut(&priority) { if let Some(request) = queue.pop_front() { self.count -= 1; @@ -175,8 +182,12 @@ impl RequestQueue { /// Peek at next request without removing pub fn peek(&self) -> Option<&InferenceRequest> { - for priority in [RequestPriority::Realtime, RequestPriority::High, - RequestPriority::Normal, RequestPriority::Low] { + for priority in [ + RequestPriority::Realtime, + RequestPriority::High, + RequestPriority::Normal, + RequestPriority::Low, + ] { if let Some(queue) = self.queues.get(&priority) { if let Some(request) = queue.front() { return Some(request); @@ -292,7 +303,9 @@ impl KvCacheManager { /// Get slot for a request pub fn get_slot(&self, request_id: RequestId) -> Option<&KvCacheSlot> { - self.request_to_slot.get(&request_id).map(|&id| &self.slots[id]) + self.request_to_slot + .get(&request_id) + .map(|&id| &self.slots[id]) } /// Check available slots @@ -454,7 +467,9 @@ impl ContinuousBatchScheduler { fn should_preempt(&self, new_request: &InferenceRequest) -> bool { if !self.running.is_empty() { // Check if new request has higher priority - if let Some(lowest) = self.running.iter() + if let Some(lowest) = self + .running + .iter() .filter(|r| r.state == RequestState::Decode) .min_by_key(|r| r.priority) { @@ -466,7 +481,9 @@ impl ContinuousBatchScheduler { /// Preempt lowest priority running request fn preempt_lowest_priority(&mut self) { - if let Some(idx) = self.running.iter() + if let Some(idx) = self + .running + .iter() .enumerate() .filter(|(_, r)| r.state == RequestState::Decode) .min_by_key(|(_, r)| r.priority) @@ -566,16 +583,22 @@ fn test_request_queue_priority() { let mut queue = RequestQueue::new(); // Add low priority first - queue.submit(InferenceRequest::new(vec![1], GenerateParams::default()) - .with_priority(RequestPriority::Low)); + queue.submit( + InferenceRequest::new(vec![1], GenerateParams::default()) + .with_priority(RequestPriority::Low), + ); // Add high priority second - queue.submit(InferenceRequest::new(vec![2], GenerateParams::default()) - .with_priority(RequestPriority::High)); + queue.submit( + InferenceRequest::new(vec![2], GenerateParams::default()) + .with_priority(RequestPriority::High), + ); // Add normal priority third - queue.submit(InferenceRequest::new(vec![3], GenerateParams::default()) - .with_priority(RequestPriority::Normal)); + queue.submit( + InferenceRequest::new(vec![3], GenerateParams::default()) + .with_priority(RequestPriority::Normal), + ); // Should get high first let req = queue.pop().unwrap(); @@ -700,10 +723,7 @@ fn test_preemption_recompute() { #[test] fn test_request_lifecycle() { - let mut request = InferenceRequest::new( - vec![1, 2, 3], - GenerateParams::default(), - ); + let mut request = InferenceRequest::new(vec![1, 2, 3], GenerateParams::default()); assert_eq!(request.state, RequestState::Queued); assert!(!request.is_complete()); @@ -790,13 +810,17 @@ fn test_realtime_priority() { // Add normal requests for _ in 0..3 { - queue.submit(InferenceRequest::new(vec![1], GenerateParams::default()) - .with_priority(RequestPriority::Normal)); + queue.submit( + InferenceRequest::new(vec![1], GenerateParams::default()) + .with_priority(RequestPriority::Normal), + ); } // Add realtime request last - queue.submit(InferenceRequest::new(vec![9], GenerateParams::default()) - .with_priority(RequestPriority::Realtime)); + queue.submit( + InferenceRequest::new(vec![9], GenerateParams::default()) + .with_priority(RequestPriority::Realtime), + ); // Realtime should be first despite being added last let req = queue.pop().unwrap(); @@ -856,7 +880,10 @@ mod async_tests { tokio::spawn(async move { let mut request = InferenceRequest::new( vec![i as u32], - GenerateParams { max_tokens: 5, ..Default::default() }, + GenerateParams { + max_tokens: 5, + ..Default::default() + }, ); let tokens = simulate_generation(&mut request, 5).await; @@ -944,10 +971,10 @@ fn test_high_throughput_queue() { _ => RequestPriority::Realtime, }; - queue.submit(InferenceRequest::new( - vec![i as u32], - GenerateParams::default(), - ).with_priority(priority)); + queue.submit( + InferenceRequest::new(vec![i as u32], GenerateParams::default()) + .with_priority(priority), + ); } assert_eq!(queue.len(), 1000); @@ -992,7 +1019,11 @@ fn test_kv_cache_churn() { } // After freeing all, should have all slots available - assert_eq!(manager.available_slots(), 10, "All slots should be free after cleanup"); + assert_eq!( + manager.available_slots(), + 10, + "All slots should be free after cleanup" + ); } #[test] diff --git a/crates/ruvllm/tests/sona_integration.rs b/crates/ruvllm/tests/sona_integration.rs index fa1e01afe..aff19780f 100644 --- a/crates/ruvllm/tests/sona_integration.rs +++ b/crates/ruvllm/tests/sona_integration.rs @@ -4,8 +4,10 @@ //! and deep loop processing. use ruvllm::{ - sona::{LearningLoop, SonaConfig, SonaIntegration, SonaStats, Trajectory, RoutingRecommendation}, error::Result, + sona::{ + LearningLoop, RoutingRecommendation, SonaConfig, SonaIntegration, SonaStats, Trajectory, + }, }; use std::time::Duration; @@ -269,9 +271,12 @@ fn test_sona_trigger_deep_loop() { let stats = sona.stats(); // At least one more deep update after explicit trigger - assert!(stats.deep_updates >= deep_updates_before + 1, + assert!( + stats.deep_updates >= deep_updates_before + 1, "Expected at least {} deep updates, got {}", - deep_updates_before + 1, stats.deep_updates); + deep_updates_before + 1, + stats.deep_updates + ); } #[test] @@ -318,8 +323,10 @@ fn test_sona_empty_background_loop() { let stats = sona.stats(); // With no trajectories meeting quality threshold, background_updates is 0 - assert_eq!(stats.background_updates, 0, - "Background loop with no trajectories should not count as an update"); + assert_eq!( + stats.background_updates, 0, + "Background loop with no trajectories should not count as an update" + ); } #[test] diff --git a/crates/ruvllm/tests/speculative_integration.rs b/crates/ruvllm/tests/speculative_integration.rs index 29243bf80..69ffdb9d7 100644 --- a/crates/ruvllm/tests/speculative_integration.rs +++ b/crates/ruvllm/tests/speculative_integration.rs @@ -4,9 +4,8 @@ //! with mock backends. use ruvllm::speculative::{ - SpeculativeConfig, SpeculativeStats, AtomicSpeculativeStats, - SpeculationTree, TreeNode, VerificationResult, - softmax, log_softmax, top_k_filter, top_p_filter, + log_softmax, softmax, top_k_filter, top_p_filter, AtomicSpeculativeStats, SpeculationTree, + SpeculativeConfig, SpeculativeStats, TreeNode, VerificationResult, }; use std::time::Duration; diff --git a/crates/sona/src/napi_simple.rs b/crates/sona/src/napi_simple.rs index 506b2821d..2a4692e4d 100644 --- a/crates/sona/src/napi_simple.rs +++ b/crates/sona/src/napi_simple.rs @@ -198,9 +198,8 @@ impl SonaEngine { /// @returns Statistics object as JSON string #[napi] pub fn get_stats(&self) -> String { - serde_json::to_string(&self.inner.stats()).unwrap_or_else(|e| { - format!("{{\"error\": \"{}\"}}", e) - }) + serde_json::to_string(&self.inner.stats()) + .unwrap_or_else(|e| format!("{{\"error\": \"{}\"}}", e)) } /// Enable or disable the engine diff --git a/examples/benchmarks/src/bin/intelligence_assessment.rs b/examples/benchmarks/src/bin/intelligence_assessment.rs index 54cdb717c..6c06affbe 100644 --- a/examples/benchmarks/src/bin/intelligence_assessment.rs +++ b/examples/benchmarks/src/bin/intelligence_assessment.rs @@ -9,11 +9,11 @@ use anyhow::Result; use clap::Parser; use ruvector_benchmarks::{ intelligence_metrics::{ - DifficultyStats, EpisodeMetrics, IntelligenceCalculator, RawMetrics, - print_intelligence_report, + print_intelligence_report, DifficultyStats, EpisodeMetrics, IntelligenceCalculator, + RawMetrics, }, swarm_regret::SwarmController, - temporal::{TemporalSolver, AdaptiveSolver}, + temporal::{AdaptiveSolver, TemporalSolver}, timepuzzles::{PuzzleGenerator, PuzzleGeneratorConfig}, }; @@ -157,7 +157,13 @@ fn main() -> Result<()> { } // Record episode for swarm controller - controller.complete_episode(solved, correct, total_steps, total_tool_calls, total_latency); + controller.complete_episode( + solved, + correct, + total_steps, + total_tool_calls, + total_latency, + ); // Record episode metrics let episode_accuracy = if args.tasks_per_episode > 0 { @@ -296,7 +302,10 @@ fn main() -> Result<()> { let progress = solver.learning_progress(); println!("🧠 ReasoningBank Statistics:"); println!(" Total trajectories: {}", progress.total_trajectories); - println!(" Success rate: {:.1}%", progress.success_rate * 100.0); + println!( + " Success rate: {:.1}%", + progress.success_rate * 100.0 + ); println!(" Improvement rate: {:.4}", progress.improvement_rate); println!(" Patterns learned: {}", progress.patterns_learned); println!(" Strategies tried: {}", progress.strategies_tried); diff --git a/examples/benchmarks/src/bin/swarm_regret.rs b/examples/benchmarks/src/bin/swarm_regret.rs index c7e3a7d28..dd05e88d1 100644 --- a/examples/benchmarks/src/bin/swarm_regret.rs +++ b/examples/benchmarks/src/bin/swarm_regret.rs @@ -121,7 +121,13 @@ fn main() -> Result<()> { } // Record episode - controller.complete_episode(solved, correct, total_steps, total_tool_calls, total_latency); + controller.complete_episode( + solved, + correct, + total_steps, + total_tool_calls, + total_latency, + ); // Get status let summary = controller.regret.summary(); @@ -188,7 +194,10 @@ fn main() -> Result<()> { ); println!(); println!("📈 Performance:"); - println!(" Average accuracy: {:.1}%", summary.average_accuracy * 100.0); + println!( + " Average accuracy: {:.1}%", + summary.average_accuracy * 100.0 + ); println!(" Average reward: {:.2}", summary.average_reward); println!( " Moving avg reward: {:.2}", diff --git a/examples/benchmarks/src/bin/temporal_benchmark.rs b/examples/benchmarks/src/bin/temporal_benchmark.rs index 62edbfa71..8e591c41a 100644 --- a/examples/benchmarks/src/bin/temporal_benchmark.rs +++ b/examples/benchmarks/src/bin/temporal_benchmark.rs @@ -194,10 +194,7 @@ fn main() -> Result<()> { println!("╚══════════════════════════════════════════════════════════════╝"); println!(); println!("📊 Summary:"); - println!( - " Total puzzles: {}", - benchmark_results.total_puzzles - ); + println!(" Total puzzles: {}", benchmark_results.total_puzzles); println!(" Solved: {}", benchmark_results.solved_count); println!(" Correct: {}", benchmark_results.correct_count); println!( @@ -206,14 +203,8 @@ fn main() -> Result<()> { ); println!(); println!("⏱️ Performance:"); - println!( - " Avg steps: {:.1}", - benchmark_results.avg_steps - ); - println!( - " Avg tool calls: {:.1}", - benchmark_results.avg_tool_calls - ); + println!(" Avg steps: {:.1}", benchmark_results.avg_steps); + println!(" Avg tool calls: {:.1}", benchmark_results.avg_tool_calls); println!( " Avg latency: {:.1}ms", benchmark_results.avg_latency_ms @@ -252,8 +243,7 @@ fn main() -> Result<()> { println!("🔧 Tool Analysis:"); println!( " Calendar rewriting success: {}/{}", - with_rewriting, - benchmark_results.total_puzzles + with_rewriting, benchmark_results.total_puzzles ); } diff --git a/examples/benchmarks/src/bin/timepuzzle_runner.rs b/examples/benchmarks/src/bin/timepuzzle_runner.rs index 3495d6998..6bba1bc05 100644 --- a/examples/benchmarks/src/bin/timepuzzle_runner.rs +++ b/examples/benchmarks/src/bin/timepuzzle_runner.rs @@ -9,9 +9,7 @@ use anyhow::Result; use clap::Parser; use ruvector_benchmarks::{ - logging::BenchmarkLogger, - temporal::TemporalSolver, - timepuzzles::SamplePuzzles, + logging::BenchmarkLogger, temporal::TemporalSolver, timepuzzles::SamplePuzzles, }; use std::time::{Duration, Instant}; @@ -191,7 +189,10 @@ fn main() -> Result<()> { let avg_latency = results.iter().map(|r| r.latency_ms).sum::() as f64 / total as f64; // Tool toggle analysis - let with_tool_correct = results.iter().filter(|r| r.tool_calls > 0 && r.correct).count(); + let with_tool_correct = results + .iter() + .filter(|r| r.tool_calls > 0 && r.correct) + .count(); println!("╔══════════════════════════════════════════════════════════════╗"); println!("║ Probe Results ║"); @@ -199,8 +200,16 @@ fn main() -> Result<()> { println!(); println!("📊 Overall Performance:"); println!(" Puzzles run: {}", total); - println!(" Solved: {} ({:.1}%)", solved, solved as f64 / total as f64 * 100.0); - println!(" Correct: {} ({:.1}%)", correct, accuracy * 100.0); + println!( + " Solved: {} ({:.1}%)", + solved, + solved as f64 / total as f64 * 100.0 + ); + println!( + " Correct: {} ({:.1}%)", + correct, + accuracy * 100.0 + ); println!(); println!("⏱️ Efficiency:"); println!(" Avg steps: {:.1}", avg_steps); @@ -256,7 +265,8 @@ fn main() -> Result<()> { // Accuracy by difficulty println!(); println!("🎯 Accuracy by Difficulty:"); - let mut by_diff: std::collections::HashMap = std::collections::HashMap::new(); + let mut by_diff: std::collections::HashMap = + std::collections::HashMap::new(); for (p, r) in puzzles.iter().zip(results.iter()) { let e = by_diff.entry(p.difficulty).or_insert((0, 0)); e.0 += 1; diff --git a/examples/benchmarks/src/intelligence_metrics.rs b/examples/benchmarks/src/intelligence_metrics.rs index 7ef72a9bf..88e05142d 100644 --- a/examples/benchmarks/src/intelligence_metrics.rs +++ b/examples/benchmarks/src/intelligence_metrics.rs @@ -77,7 +77,12 @@ impl CapabilityScores { if total_weight == 0.0 { return 0.0; } - scores.iter().zip(weights.iter()).map(|(s, w)| s * w).sum::() / total_weight + scores + .iter() + .zip(weights.iter()) + .map(|(s, w)| s * w) + .sum::() + / total_weight } } @@ -432,11 +437,8 @@ impl IntelligenceCalculator { } // Sample efficiency: accuracy per episode - learning.sample_efficiency = raw.episodes - .iter() - .map(|e| e.accuracy) - .sum::() - / raw.episodes.len() as f64; + learning.sample_efficiency = + raw.episodes.iter().map(|e| e.accuracy).sum::() / raw.episodes.len() as f64; // Regret sublinearity: check if cumulative regret grows sublinearly // True sublinearity means R_k/k → 0 as k → ∞ (regret per episode decreasing) @@ -469,14 +471,18 @@ impl IntelligenceCalculator { // Also check cumulative average let last = raw.episodes.last().unwrap(); let avg_regret = last.cumulative_regret / n; - let first_half_avg = raw.episodes.iter() + let first_half_avg = raw + .episodes + .iter() .take(raw.episodes.len() / 2) .map(|e| e.regret) - .sum::() / (n / 2.0); + .sum::() + / (n / 2.0); // If second half has lower per-episode regret, that's sublinear if avg_regret < first_half_avg && learning.regret_sublinearity == 0.0 { - learning.regret_sublinearity = ((first_half_avg - avg_regret) / first_half_avg).max(0.0); + learning.regret_sublinearity = + ((first_half_avg - avg_regret) / first_half_avg).max(0.0); } } @@ -489,16 +495,17 @@ impl IntelligenceCalculator { // Generalization: consistency across difficulties if raw.by_difficulty.len() >= 2 { - let accuracies: Vec = raw.by_difficulty.values() + let accuracies: Vec = raw + .by_difficulty + .values() .filter(|s| s.attempted > 0) .map(|s| s.correct as f64 / s.attempted as f64) .collect(); if !accuracies.is_empty() { let mean = accuracies.iter().sum::() / accuracies.len() as f64; - let variance = accuracies.iter() - .map(|a| (a - mean).powi(2)) - .sum::() / accuracies.len() as f64; + let variance = accuracies.iter().map(|a| (a - mean).powi(2)).sum::() + / accuracies.len() as f64; let std_dev = variance.sqrt(); // Lower variance = better generalization @@ -537,7 +544,7 @@ impl IntelligenceCalculator { selection_appropriateness, utilization_effectiveness, composition_ability: avg_tools.min(1.0), // Using multiple tools - discovery_ability: accuracy, // Finding solutions + discovery_ability: accuracy, // Finding solutions } } @@ -554,8 +561,16 @@ impl IntelligenceCalculator { // Strategy adaptation: improvement over episodes let strategy_adaptation = if raw.episodes.len() >= 3 { - let trend: f64 = raw.episodes.windows(2) - .map(|w| if w[1].accuracy > w[0].accuracy { 1.0 } else { 0.0 }) + let trend: f64 = raw + .episodes + .windows(2) + .map(|w| { + if w[1].accuracy > w[0].accuracy { + 1.0 + } else { + 0.0 + } + }) .sum::(); trend / (raw.episodes.len() - 1) as f64 } else { @@ -609,8 +624,11 @@ impl IntelligenceCalculator { * 100.0; // Weighted average - (cap_score * 0.3 + reasoning_score * 0.25 + learning_score * 0.2 - + tool_score * 0.15 + meta_score * 0.1) + (cap_score * 0.3 + + reasoning_score * 0.25 + + learning_score * 0.2 + + tool_score * 0.15 + + meta_score * 0.1) } } @@ -620,43 +638,109 @@ pub fn print_intelligence_report(assessment: &IntelligenceAssessment) { println!("║ Intelligence Assessment Report ║"); println!("╚══════════════════════════════════════════════════════════════╝"); println!(); - println!("🧠 Overall Intelligence Score: {:.1}/100", assessment.overall_score); + println!( + "🧠 Overall Intelligence Score: {:.1}/100", + assessment.overall_score + ); println!(); println!("📊 Capability Scores:"); - println!(" Temporal Reasoning: {:5.1}", assessment.capabilities.temporal_reasoning); - println!(" Constraint Satisfaction:{:5.1}", assessment.capabilities.constraint_satisfaction); - println!(" Information Retrieval: {:5.1}", assessment.capabilities.information_retrieval); - println!(" Pattern Recognition: {:5.1}", assessment.capabilities.pattern_recognition); - println!(" Planning: {:5.1}", assessment.capabilities.planning); - println!(" Adaptation: {:5.1}", assessment.capabilities.adaptation); + println!( + " Temporal Reasoning: {:5.1}", + assessment.capabilities.temporal_reasoning + ); + println!( + " Constraint Satisfaction:{:5.1}", + assessment.capabilities.constraint_satisfaction + ); + println!( + " Information Retrieval: {:5.1}", + assessment.capabilities.information_retrieval + ); + println!( + " Pattern Recognition: {:5.1}", + assessment.capabilities.pattern_recognition + ); + println!( + " Planning: {:5.1}", + assessment.capabilities.planning + ); + println!( + " Adaptation: {:5.1}", + assessment.capabilities.adaptation + ); println!(); println!("🔍 Reasoning Quality:"); - println!(" Logical Coherence: {:.2}", assessment.reasoning.logical_coherence); - println!(" Constraint Satisfaction:{:.2}", assessment.reasoning.constraint_satisfaction_rate); - println!(" Solution Optimality: {:.2}", assessment.reasoning.solution_optimality); - println!(" Reasoning Efficiency: {:.2}", assessment.reasoning.reasoning_efficiency); - println!(" Error Rate: {:.2}", assessment.reasoning.error_rate); + println!( + " Logical Coherence: {:.2}", + assessment.reasoning.logical_coherence + ); + println!( + " Constraint Satisfaction:{:.2}", + assessment.reasoning.constraint_satisfaction_rate + ); + println!( + " Solution Optimality: {:.2}", + assessment.reasoning.solution_optimality + ); + println!( + " Reasoning Efficiency: {:.2}", + assessment.reasoning.reasoning_efficiency + ); + println!( + " Error Rate: {:.2}", + assessment.reasoning.error_rate + ); println!(); println!("📈 Learning Metrics:"); - println!(" Sample Efficiency: {:.2}", assessment.learning.sample_efficiency); - println!(" Regret Sublinearity: {:.2}", assessment.learning.regret_sublinearity); - println!(" Learning Rate: {:.2}", assessment.learning.learning_rate); - println!(" Generalization: {:.2}", assessment.learning.generalization); + println!( + " Sample Efficiency: {:.2}", + assessment.learning.sample_efficiency + ); + println!( + " Regret Sublinearity: {:.2}", + assessment.learning.regret_sublinearity + ); + println!( + " Learning Rate: {:.2}", + assessment.learning.learning_rate + ); + println!( + " Generalization: {:.2}", + assessment.learning.generalization + ); println!(); println!("🔧 Tool Use Proficiency:"); - println!(" Selection: {:.2}", assessment.tool_use.selection_appropriateness); - println!(" Effectiveness: {:.2}", assessment.tool_use.utilization_effectiveness); - println!(" Composition: {:.2}", assessment.tool_use.composition_ability); + println!( + " Selection: {:.2}", + assessment.tool_use.selection_appropriateness + ); + println!( + " Effectiveness: {:.2}", + assessment.tool_use.utilization_effectiveness + ); + println!( + " Composition: {:.2}", + assessment.tool_use.composition_ability + ); println!(); println!("🪞 Meta-Cognitive Indicators:"); - println!(" Self-Correction: {:.2}", assessment.meta_cognition.self_correction_rate); - println!(" Strategy Adaptation: {:.2}", assessment.meta_cognition.strategy_adaptation); - println!(" Progress Monitoring: {:.2}", assessment.meta_cognition.progress_monitoring); + println!( + " Self-Correction: {:.2}", + assessment.meta_cognition.self_correction_rate + ); + println!( + " Strategy Adaptation: {:.2}", + assessment.meta_cognition.strategy_adaptation + ); + println!( + " Progress Monitoring: {:.2}", + assessment.meta_cognition.progress_monitoring + ); } #[cfg(test)] diff --git a/examples/benchmarks/src/lib.rs b/examples/benchmarks/src/lib.rs index 311b6bb3a..5d790f560 100644 --- a/examples/benchmarks/src/lib.rs +++ b/examples/benchmarks/src/lib.rs @@ -14,18 +14,18 @@ //! - Cognitive capability assessment frameworks //! - lean-agentic type theory for verified reasoning -pub mod temporal; -pub mod vector_index; -pub mod swarm_regret; -pub mod logging; -pub mod timepuzzles; pub mod intelligence_metrics; +pub mod logging; pub mod reasoning_bank; +pub mod swarm_regret; +pub mod temporal; +pub mod timepuzzles; +pub mod vector_index; -pub use temporal::*; -pub use vector_index::*; -pub use swarm_regret::*; -pub use logging::*; -pub use timepuzzles::*; pub use intelligence_metrics::*; +pub use logging::*; pub use reasoning_bank::*; +pub use swarm_regret::*; +pub use temporal::*; +pub use timepuzzles::*; +pub use vector_index::*; diff --git a/examples/benchmarks/src/logging.rs b/examples/benchmarks/src/logging.rs index 8dba04762..78123f9b7 100644 --- a/examples/benchmarks/src/logging.rs +++ b/examples/benchmarks/src/logging.rs @@ -120,10 +120,7 @@ impl BenchmarkLogger { fs::create_dir_all(parent)?; } - let file = OpenOptions::new() - .create(true) - .append(true) - .open(&path)?; + let file = OpenOptions::new().create(true).append(true).open(&path)?; Ok(Self { path, @@ -410,7 +407,9 @@ mod tests { let mut logger = BenchmarkLogger::new(path.to_str().unwrap()).unwrap(); logger - .log_temporal("bench-1", "puzzle-1", 5, true, true, 10, 2, 100, 3, true, false) + .log_temporal( + "bench-1", "puzzle-1", 5, true, true, 10, 2, 100, 3, true, false, + ) .unwrap(); logger.flush().unwrap(); diff --git a/examples/benchmarks/src/reasoning_bank.rs b/examples/benchmarks/src/reasoning_bank.rs index 517a1cd15..4fc55c67b 100644 --- a/examples/benchmarks/src/reasoning_bank.rs +++ b/examples/benchmarks/src/reasoning_bank.rs @@ -91,7 +91,14 @@ impl Trajectory { } } - pub fn record_attempt(&mut self, solution: String, confidence: f64, steps: usize, tool_calls: usize, strategy: &str) { + pub fn record_attempt( + &mut self, + solution: String, + confidence: f64, + steps: usize, + tool_calls: usize, + strategy: &str, + ) { self.attempts.push(SolutionAttempt { solution, confidence, @@ -263,26 +270,41 @@ impl ReasoningBank { pub fn record_trajectory(&mut self, trajectory: Trajectory) { // Update strategy stats if let Some(attempt) = trajectory.attempts.first() { - let stats = self.strategy_stats + let stats = self + .strategy_stats .entry(attempt.strategy.clone()) .or_default(); stats.attempts += 1; stats.total_steps += attempt.steps; stats.total_latency_ms += trajectory.latency_ms; - if trajectory.verdict.as_ref().map(|v| v.is_success()).unwrap_or(false) { + if trajectory + .verdict + .as_ref() + .map(|v| v.is_success()) + .unwrap_or(false) + { stats.successes += 1; } } // Update calibration if let Some(attempt) = trajectory.attempts.first() { - let correct = trajectory.verdict.as_ref().map(|v| v.is_success()).unwrap_or(false); + let correct = trajectory + .verdict + .as_ref() + .map(|v| v.is_success()) + .unwrap_or(false); self.calibration.record(attempt.confidence, correct); } // Learn patterns from successful trajectories - if trajectory.verdict.as_ref().map(|v| v.is_success()).unwrap_or(false) { + if trajectory + .verdict + .as_ref() + .map(|v| v.is_success()) + .unwrap_or(false) + { self.learn_from_success(&trajectory); } @@ -307,11 +329,12 @@ impl ReasoningBank { for constraint_type in &trajectory.constraint_types { // Update constraint frequency - *self.constraint_frequency.entry(constraint_type.clone()).or_insert(0) += 1; - - let patterns = self.patterns + *self + .constraint_frequency .entry(constraint_type.clone()) - .or_default(); + .or_insert(0) += 1; + + let patterns = self.patterns.entry(constraint_type.clone()).or_default(); // Find or create pattern let pattern_idx = patterns.iter().position(|p| { @@ -329,7 +352,8 @@ impl ReasoningBank { p.observations += 1; // Update pattern index for fast lookup - self.pattern_index.insert((constraint_type.clone(), trajectory.difficulty), idx); + self.pattern_index + .insert((constraint_type.clone(), trajectory.difficulty), idx); } else { // Create new pattern let new_idx = patterns.len(); @@ -346,8 +370,11 @@ impl ReasoningBank { }); // Index the new pattern - for d in trajectory.difficulty.saturating_sub(2)..=trajectory.difficulty.saturating_add(2) { - self.pattern_index.insert((constraint_type.clone(), d), new_idx); + for d in trajectory.difficulty.saturating_sub(2) + ..=trajectory.difficulty.saturating_add(2) + { + self.pattern_index + .insert((constraint_type.clone(), d), new_idx); } } } @@ -398,8 +425,11 @@ impl ReasoningBank { for ct in constraint_types { if let Some(patterns) = self.patterns.get(ct) { // Find best pattern for this difficulty - let best = patterns.iter() - .filter(|p| difficulty >= p.difficulty_range.0 && difficulty <= p.difficulty_range.1) + let best = patterns + .iter() + .filter(|p| { + difficulty >= p.difficulty_range.0 && difficulty <= p.difficulty_range.1 + }) .max_by(|a, b| a.success_rate.partial_cmp(&b.success_rate).unwrap()); if let Some(pattern) = best { @@ -411,7 +441,8 @@ impl ReasoningBank { } // Fall back to best strategy for difficulty - let strategy_name = self.best_strategies + let strategy_name = self + .best_strategies .get(&difficulty) .cloned() .unwrap_or_else(|| "default".to_string()); @@ -455,7 +486,9 @@ impl ReasoningBank { for pattern in patterns.iter().filter(|p| p.observations >= 5) { hints.push(format!( "For {} constraints, {} strategy has {:.0}% success", - ct, pattern.best_strategy, pattern.success_rate * 100.0 + ct, + pattern.best_strategy, + pattern.success_rate * 100.0 )); } } @@ -471,26 +504,30 @@ impl ReasoningBank { return LearningProgress::default(); } - let successes = self.trajectories.iter() + let successes = self + .trajectories + .iter() .filter(|t| t.verdict.as_ref().map(|v| v.is_success()).unwrap_or(false)) .count(); // Calculate improvement over time (compare first half vs second half) let half = total / 2; - let first_half_success = self.trajectories[..half].iter() + let first_half_success = self.trajectories[..half] + .iter() .filter(|t| t.verdict.as_ref().map(|v| v.is_success()).unwrap_or(false)) - .count() as f64 / half as f64; + .count() as f64 + / half as f64; - let second_half_success = self.trajectories[half..].iter() + let second_half_success = self.trajectories[half..] + .iter() .filter(|t| t.verdict.as_ref().map(|v| v.is_success()).unwrap_or(false)) - .count() as f64 / (total - half) as f64; + .count() as f64 + / (total - half) as f64; let improvement = second_half_success - first_half_success; // Calculate pattern coverage - let unique_patterns: usize = self.patterns.values() - .map(|ps| ps.len()) - .sum(); + let unique_patterns: usize = self.patterns.values().map(|ps| ps.len()).sum(); LearningProgress { total_trajectories: total, @@ -526,13 +563,7 @@ mod tests { for i in 0..10 { let mut traj = Trajectory::new(&format!("puzzle_{}", i), 5); traj.constraint_types.push("RelativeDate".to_string()); - traj.record_attempt( - "2024-01-15".to_string(), - 0.8, - 20, - 5, - "adaptive", - ); + traj.record_attempt("2024-01-15".to_string(), 0.8, 20, 5, "adaptive"); traj.set_verdict(Verdict::Success, Some("2024-01-15".to_string())); traj.latency_ms = 100; bank.record_trajectory(traj); diff --git a/examples/benchmarks/src/swarm_regret.rs b/examples/benchmarks/src/swarm_regret.rs index 3180863d9..dcd00308b 100644 --- a/examples/benchmarks/src/swarm_regret.rs +++ b/examples/benchmarks/src/swarm_regret.rs @@ -293,8 +293,8 @@ impl SwarmController { } else { 0.0 }; - let agent_reward = - accuracy * self.oracle.perfect_accuracy_reward - total_steps as f64 * self.oracle.step_penalty; + let agent_reward = accuracy * self.oracle.perfect_accuracy_reward + - total_steps as f64 * self.oracle.step_penalty; // Compute oracle reward let oracle_reward = self.oracle.compute_reward(num_tasks); diff --git a/examples/benchmarks/src/temporal.rs b/examples/benchmarks/src/temporal.rs index 6743877c0..595269a03 100644 --- a/examples/benchmarks/src/temporal.rs +++ b/examples/benchmarks/src/temporal.rs @@ -239,9 +239,7 @@ impl TemporalSolver { let correct = if puzzle.solutions.is_empty() { true // No ground truth } else { - found_solutions - .iter() - .all(|s| puzzle.solutions.contains(s)) + found_solutions.iter().all(|s| puzzle.solutions.contains(s)) && puzzle .solutions .iter() @@ -408,10 +406,8 @@ impl BenchmarkResults { let solved = results.iter().filter(|r| r.solved).count(); let correct = results.iter().filter(|r| r.correct).count(); let avg_steps = results.iter().map(|r| r.steps as f64).sum::() / total as f64; - let avg_tools = - results.iter().map(|r| r.tool_calls as f64).sum::() / total as f64; - let avg_latency = - results.iter().map(|r| r.latency_ms as f64).sum::() / total as f64; + let avg_tools = results.iter().map(|r| r.tool_calls as f64).sum::() / total as f64; + let avg_latency = results.iter().map(|r| r.latency_ms as f64).sum::() / total as f64; Self { config, @@ -528,15 +524,16 @@ impl AdaptiveSolver { /// Solve a puzzle with adaptive learning pub fn solve(&mut self, puzzle: &TemporalPuzzle) -> Result { // Get constraint types for pattern matching - let constraint_types: Vec = puzzle.constraints.iter() + let constraint_types: Vec = puzzle + .constraints + .iter() .map(|c| constraint_type_name(c)) .collect(); // Get recommended strategy from ReasoningBank - self.current_strategy = self.reasoning_bank.get_strategy( - puzzle.difficulty, - &constraint_types, - ); + self.current_strategy = self + .reasoning_bank + .get_strategy(puzzle.difficulty, &constraint_types); // Configure solver based on strategy self.solver.calendar_tool = self.current_strategy.use_rewriting; @@ -552,7 +549,9 @@ impl AdaptiveSolver { trajectory.latency_ms = start.elapsed().as_millis() as u64; // Record attempt - let solution_str = result.solutions.first() + let solution_str = result + .solutions + .first() .map(|d| d.to_string()) .unwrap_or_else(|| "none".to_string()); @@ -616,7 +615,10 @@ impl AdaptiveSolver { } // Adjust based on learned calibration - let calibrated_threshold = self.reasoning_bank.calibration.get_threshold(puzzle.difficulty); + let calibrated_threshold = self + .reasoning_bank + .calibration + .get_threshold(puzzle.difficulty); if confidence >= calibrated_threshold { confidence += 0.05; } diff --git a/examples/benchmarks/src/timepuzzles.rs b/examples/benchmarks/src/timepuzzles.rs index 59b8440b9..5d06d2465 100644 --- a/examples/benchmarks/src/timepuzzles.rs +++ b/examples/benchmarks/src/timepuzzles.rs @@ -111,12 +111,7 @@ impl PuzzleGenerator { "western", )); self.anchors.push(TemporalAnchor::new( - "New Year", - 2024, - 1, - 1, - "holiday", - "western", + "New Year", 2024, 1, 1, "holiday", "western", )); self.anchors.push(TemporalAnchor::new( "Independence Day", @@ -318,12 +313,15 @@ impl PuzzleGenerator { ConstraintType::Year => Ok((TemporalConstraint::InYear(target.year()), None)), ConstraintType::Month => Ok((TemporalConstraint::InMonth(target.month()), None)), ConstraintType::DayOfMonth => Ok((TemporalConstraint::DayOfMonth(target.day()), None)), - ConstraintType::DayOfWeek => Ok((TemporalConstraint::DayOfWeek(target.weekday()), None)), + ConstraintType::DayOfWeek => { + Ok((TemporalConstraint::DayOfWeek(target.weekday()), None)) + } ConstraintType::DayRange => { let start = target.day().saturating_sub(self.rng.gen_range(0..5)); let end = (target.day() + self.rng.gen_range(0..5)).min(28); - let start_date = NaiveDate::from_ymd_opt(target.year(), target.month(), start.max(1)) - .unwrap_or(target); + let start_date = + NaiveDate::from_ymd_opt(target.year(), target.month(), start.max(1)) + .unwrap_or(target); let end_date = NaiveDate::from_ymd_opt(target.year(), target.month(), end).unwrap_or(target); Ok((TemporalConstraint::Between(start_date, end_date), None)) diff --git a/examples/benchmarks/src/vector_index.rs b/examples/benchmarks/src/vector_index.rs index a99034840..c782b4e30 100644 --- a/examples/benchmarks/src/vector_index.rs +++ b/examples/benchmarks/src/vector_index.rs @@ -31,12 +31,18 @@ pub struct DenseVec { impl DenseVec { /// Create a new dense vector from values pub fn new(values: Vec) -> Self { - Self { values, cached_norm: None } + Self { + values, + cached_norm: None, + } } /// Create a new dense vector with precomputed norm pub fn with_norm(values: Vec, norm: f32) -> Self { - Self { values, cached_norm: Some(norm) } + Self { + values, + cached_norm: Some(norm), + } } /// Create a zero vector of given dimension @@ -540,7 +546,11 @@ impl VectorIndex { let s = q.cosine(v)?; push_topk(&mut best, *id, s, top_k); } - best.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap_or(std::cmp::Ordering::Equal)); + best.sort_by(|a, b| { + b.score + .partial_cmp(&a.score) + .unwrap_or(std::cmp::Ordering::Equal) + }); Ok(best) } @@ -580,7 +590,11 @@ impl VectorIndex { let s = q.cosine(v)?; push_topk(&mut best, id, s, top_k); } - best.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap_or(std::cmp::Ordering::Equal)); + best.sort_by(|a, b| { + b.score + .partial_cmp(&a.score) + .unwrap_or(std::cmp::Ordering::Equal) + }); Ok(best) } @@ -612,13 +626,15 @@ impl VectorIndex { // Parallel scoring let mut scores: Vec = active .par_iter() - .filter_map(|(id, v)| { - q.cosine(v).ok().map(|s| ScoredId::new(*id, s)) - }) + .filter_map(|(id, v)| q.cosine(v).ok().map(|s| ScoredId::new(*id, s))) .collect(); // Sort and truncate - scores.par_sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap_or(std::cmp::Ordering::Equal)); + scores.par_sort_by(|a, b| { + b.score + .partial_cmp(&a.score) + .unwrap_or(std::cmp::Ordering::Equal) + }); scores.truncate(top_k); Ok(scores) } @@ -675,12 +691,11 @@ impl VectorIndex { } // SIMD-optimized centroid scoring using parallel iterator - let centroid_scores: Vec<(usize, f32)> = state.centroids + let centroid_scores: Vec<(usize, f32)> = state + .centroids .par_iter() .enumerate() - .filter_map(|(i, c)| { - q.cosine(c).ok().map(|score| (i, score)) - }) + .filter_map(|(i, c)| q.cosine(c).ok().map(|score| (i, score))) .collect(); // Sort by score descending @@ -712,7 +727,11 @@ impl VectorIndex { // If not enough candidates, probe more clusters if all_candidates.len() < min_candidates && _probed < max_probes { - for &(cluster_idx, _) in sorted_scores.iter().skip(initial_probes).take(max_probes - initial_probes) { + for &(cluster_idx, _) in sorted_scores + .iter() + .skip(initial_probes) + .take(max_probes - initial_probes) + { if cluster_idx < state.lists.len() { for &id in &state.lists[cluster_idx] { if !self.deleted.contains(&id) { @@ -732,7 +751,11 @@ impl VectorIndex { } // Sort and return top-k - all_candidates.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap_or(std::cmp::Ordering::Equal)); + all_candidates.sort_by(|a, b| { + b.score + .partial_cmp(&a.score) + .unwrap_or(std::cmp::Ordering::Equal) + }); all_candidates.truncate(top_k); Ok(all_candidates) } @@ -780,15 +803,19 @@ impl VectorIndex { let best: Vec = candidates .par_iter() .filter_map(|id| { - self.vectors.get(id).and_then(|v| { - q.cosine(v).ok().map(|s| ScoredId::new(*id, s)) - }) + self.vectors + .get(id) + .and_then(|v| q.cosine(v).ok().map(|s| ScoredId::new(*id, s))) }) .collect(); // Sort and truncate let mut sorted = best; - sorted.par_sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap_or(std::cmp::Ordering::Equal)); + sorted.par_sort_by(|a, b| { + b.score + .partial_cmp(&a.score) + .unwrap_or(std::cmp::Ordering::Equal) + }); sorted.truncate(top_k); Ok(sorted) } @@ -803,7 +830,8 @@ impl VectorIndex { /// Load index from file pub fn load_from_file>(path: P) -> Result { let bytes = fs::read(path)?; - let (idx, _): (Self, _) = bincode::serde::decode_from_slice(&bytes, bincode::config::standard())?; + let (idx, _): (Self, _) = + bincode::serde::decode_from_slice(&bytes, bincode::config::standard())?; Ok(idx) } @@ -815,7 +843,11 @@ impl VectorIndex { active_vectors: self.len(), deleted_vectors: self.deleted.len(), ivf_enabled: self.ivf.enabled, - ivf_clusters: self.ivf_state.as_ref().map(|s| s.centroids.len()).unwrap_or(0), + ivf_clusters: self + .ivf_state + .as_ref() + .map(|s| s.centroids.len()) + .unwrap_or(0), gate_enabled: self.gate.enabled, gate_min_score: self.gate.min_score, } @@ -896,9 +928,7 @@ fn kmeans(points: &[DenseVec], k: usize, iters: usize) -> Result> // Iterate for _ in 0..iters { - let mut sums: Vec = (0..centroids.len()) - .map(|_| DenseVec::zeros(dim)) - .collect(); + let mut sums: Vec = (0..centroids.len()).map(|_| DenseVec::zeros(dim)).collect(); let mut counts: Vec = vec![0; centroids.len()]; // Assign points to centroids diff --git a/examples/benchmarks/tests/integration_tests.rs b/examples/benchmarks/tests/integration_tests.rs index 7f1e668d2..a852693d4 100644 --- a/examples/benchmarks/tests/integration_tests.rs +++ b/examples/benchmarks/tests/integration_tests.rs @@ -1,5 +1,6 @@ //! Integration tests for benchmark suite +use chrono::{NaiveDate, Weekday}; use ruvector_benchmarks::{ logging::BenchmarkLogger, swarm_regret::{EpisodeResult, RegretTracker, SwarmController}, @@ -7,7 +8,6 @@ use ruvector_benchmarks::{ timepuzzles::{PuzzleGenerator, PuzzleGeneratorConfig, SamplePuzzles}, vector_index::{CoherenceGate, DenseVec, IvfConfig, VectorIndex}, }; -use chrono::{NaiveDate, Weekday}; use tempfile::tempdir; // ============================================================================ @@ -242,7 +242,9 @@ fn test_sample_puzzles() { assert!(easy.iter().all(|p| p.difficulty <= 3)); let medium = SamplePuzzles::medium(); - assert!(medium.iter().all(|p| p.difficulty >= 4 && p.difficulty <= 6)); + assert!(medium + .iter() + .all(|p| p.difficulty >= 4 && p.difficulty <= 6)); let hard = SamplePuzzles::hard(); assert!(hard.iter().all(|p| p.difficulty >= 7)); @@ -331,17 +333,7 @@ fn test_benchmark_logger() { logger .log_temporal( - "bench-1", - "puzzle-1", - 5, - true, - true, - 10, - 2, - 100, - 3, - true, - false, + "bench-1", "puzzle-1", 5, true, true, 10, 2, 100, 3, true, false, ) .unwrap(); diff --git a/examples/exo-ai-2025/benches/hypergraph_bench.rs b/examples/exo-ai-2025/benches/hypergraph_bench.rs index f3b28fc56..db04d5549 100644 --- a/examples/exo-ai-2025/benches/hypergraph_bench.rs +++ b/examples/exo-ai-2025/benches/hypergraph_bench.rs @@ -1,6 +1,6 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId}; -use exo_hypergraph::{HypergraphSubstrate, HypergraphConfig}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use exo_core::{EntityId, Relation, RelationType}; +use exo_hypergraph::{HypergraphConfig, HypergraphSubstrate}; fn create_test_hypergraph() -> HypergraphSubstrate { let config = HypergraphConfig::default(); @@ -31,10 +31,7 @@ fn benchmark_hyperedge_creation(c: &mut Criterion) { edge_size, |b, &size| { b.iter(|| { - let entity_set: Vec = entities.iter() - .take(size) - .copied() - .collect(); + let entity_set: Vec = entities.iter().take(size).copied().collect(); graph.create_hyperedge(black_box(&entity_set), black_box(&relation)) }); }, @@ -65,24 +62,15 @@ fn benchmark_query_performance(c: &mut Criterion) { }; for _ in 0..*num_edges { - let entity_set: Vec = entities.iter() - .take(5) - .copied() - .collect(); + let entity_set: Vec = entities.iter().take(5).copied().collect(); graph.create_hyperedge(&entity_set, &relation).unwrap(); } let query_entity = entities[0]; - group.bench_with_input( - BenchmarkId::from_parameter(num_edges), - num_edges, - |b, _| { - b.iter(|| { - graph.hyperedges_for_entity(black_box(&query_entity)) - }); - }, - ); + group.bench_with_input(BenchmarkId::from_parameter(num_edges), num_edges, |b, _| { + b.iter(|| graph.hyperedges_for_entity(black_box(&query_entity))); + }); } group.finish(); @@ -105,17 +93,12 @@ fn benchmark_betti_numbers(c: &mut Criterion) { }; for _ in 0..500 { - let entity_set: Vec = entities.iter() - .take(5) - .copied() - .collect(); + let entity_set: Vec = entities.iter().take(5).copied().collect(); graph.create_hyperedge(&entity_set, &relation).unwrap(); } c.bench_function("hypergraph_betti_numbers", |b| { - b.iter(|| { - graph.betti_numbers(black_box(3)) - }); + b.iter(|| graph.betti_numbers(black_box(3))); }); } diff --git a/examples/exo-ai-2025/benches/manifold_bench.rs b/examples/exo-ai-2025/benches/manifold_bench.rs index 901c01dca..813009441 100644 --- a/examples/exo-ai-2025/benches/manifold_bench.rs +++ b/examples/exo-ai-2025/benches/manifold_bench.rs @@ -1,7 +1,7 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId}; -use exo_manifold::ManifoldEngine; -use exo_core::{ManifoldConfig, Pattern, Metadata, PatternId, SubstrateTime}; use burn::backend::NdArray; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; +use exo_core::{ManifoldConfig, Metadata, Pattern, PatternId, SubstrateTime}; +use exo_manifold::ManifoldEngine; type TestBackend = NdArray; @@ -48,9 +48,7 @@ fn benchmark_retrieval(c: &mut Criterion) { BenchmarkId::from_parameter(num_patterns), num_patterns, |b, _| { - b.iter(|| { - engine.retrieve(black_box(&query), black_box(10)) - }); + b.iter(|| engine.retrieve(black_box(&query), black_box(10))); }, ); } @@ -91,9 +89,7 @@ fn benchmark_forgetting(c: &mut Criterion) { } c.bench_function("manifold_forgetting", |b| { - b.iter(|| { - engine.forget(black_box(0.5), black_box(0.1)) - }); + b.iter(|| engine.forget(black_box(0.5), black_box(0.1))); }); } diff --git a/examples/exo-ai-2025/benches/temporal_bench.rs b/examples/exo-ai-2025/benches/temporal_bench.rs index 70d4e5d1a..732fae30c 100644 --- a/examples/exo-ai-2025/benches/temporal_bench.rs +++ b/examples/exo-ai-2025/benches/temporal_bench.rs @@ -1,6 +1,6 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId}; -use exo_temporal::{TemporalMemory, TemporalConfig, CausalConeType}; -use exo_core::{Pattern, Metadata, PatternId, SubstrateTime, Query}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; +use exo_core::{Metadata, Pattern, PatternId, Query, SubstrateTime}; +use exo_temporal::{CausalConeType, TemporalConfig, TemporalMemory}; fn create_test_memory() -> TemporalMemory { TemporalMemory::new(TemporalConfig::default()) @@ -105,9 +105,7 @@ fn benchmark_pattern_retrieval(c: &mut Criterion) { c.bench_function("temporal_pattern_retrieval", |b| { let query_id = pattern_ids[500]; - b.iter(|| { - memory.get(black_box(&query_id)) - }); + b.iter(|| memory.get(black_box(&query_id))); }); } diff --git a/examples/exo-ai-2025/crates/exo-backend-classical/src/graph.rs b/examples/exo-ai-2025/crates/exo-backend-classical/src/graph.rs index 48e8385df..f2fdbeaf2 100644 --- a/examples/exo-ai-2025/crates/exo-backend-classical/src/graph.rs +++ b/examples/exo-ai-2025/crates/exo-backend-classical/src/graph.rs @@ -1,8 +1,7 @@ //! Graph database wrapper for ruvector-graph use exo_core::{ - EntityId, HyperedgeId, HyperedgeResult, Relation, SheafConsistencyResult, - TopologicalQuery, + EntityId, HyperedgeId, HyperedgeResult, Relation, SheafConsistencyResult, TopologicalQuery, }; use ruvector_graph::{GraphDB, Hyperedge, Node}; use std::str::FromStr; @@ -21,9 +20,7 @@ pub struct GraphWrapper { impl GraphWrapper { /// Create a new graph wrapper pub fn new() -> Self { - Self { - db: GraphDB::new(), - } + Self { db: GraphDB::new() } } /// Create a hyperedge spanning multiple entities @@ -38,24 +35,17 @@ impl GraphWrapper { if self.db.get_node(&entity_id_str).is_none() { // Create node if it doesn't exist use ruvector_graph::types::{Label, Properties}; - let node = Node::new( - entity_id_str, - vec![Label::new("Entity")], - Properties::new() - ); - self.db.create_node(node).map_err(|e| { - ExoError::Backend(format!("Failed to create node: {}", e)) - })?; + let node = Node::new(entity_id_str, vec![Label::new("Entity")], Properties::new()); + self.db + .create_node(node) + .map_err(|e| ExoError::Backend(format!("Failed to create node: {}", e)))?; } } // Create hyperedge using ruvector-graph let entity_strs: Vec = entities.iter().map(|e| e.0.to_string()).collect(); - let mut hyperedge = Hyperedge::new( - entity_strs, - relation.relation_type.0.clone(), - ); + let mut hyperedge = Hyperedge::new(entity_strs, relation.relation_type.0.clone()); // Add properties if they're an object if let Some(obj) = relation.properties.as_object() { @@ -67,14 +57,13 @@ impl GraphWrapper { } let hyperedge_id_str = hyperedge.id.clone(); - - self.db.create_hyperedge(hyperedge).map_err(|e| { - ExoError::Backend(format!("Failed to create hyperedge: {}", e)) - })?; + + self.db + .create_hyperedge(hyperedge) + .map_err(|e| ExoError::Backend(format!("Failed to create hyperedge: {}", e)))?; // Convert string ID to HyperedgeId - let uuid = uuid::Uuid::from_str(&hyperedge_id_str) - .unwrap_or_else(|_| uuid::Uuid::new_v4()); + let uuid = uuid::Uuid::from_str(&hyperedge_id_str).unwrap_or_else(|_| uuid::Uuid::new_v4()); Ok(HyperedgeId(uuid)) } @@ -123,7 +112,7 @@ impl GraphWrapper { // Not supported on classical discrete backend Ok(HyperedgeResult::SheafConsistency( SheafConsistencyResult::Inconsistent(vec![ - "Sheaf consistency not supported on classical backend".to_string() + "Sheaf consistency not supported on classical backend".to_string(), ]), )) } diff --git a/examples/exo-ai-2025/crates/exo-backend-classical/src/lib.rs b/examples/exo-ai-2025/crates/exo-backend-classical/src/lib.rs index 46f2c53bd..c45f3155e 100644 --- a/examples/exo-ai-2025/crates/exo-backend-classical/src/lib.rs +++ b/examples/exo-ai-2025/crates/exo-backend-classical/src/lib.rs @@ -10,8 +10,8 @@ pub mod graph; pub mod vector; use exo_core::{ - Error as ExoError, Filter, ManifoldDelta, Pattern, Result as ExoResult, - SearchResult, SubstrateBackend, + Error as ExoError, Filter, ManifoldDelta, Pattern, Result as ExoResult, SearchResult, + SubstrateBackend, }; use parking_lot::RwLock; use std::sync::Arc; diff --git a/examples/exo-ai-2025/crates/exo-backend-classical/src/vector.rs b/examples/exo-ai-2025/crates/exo-backend-classical/src/vector.rs index 8507e1760..539185ab1 100644 --- a/examples/exo-ai-2025/crates/exo-backend-classical/src/vector.rs +++ b/examples/exo-ai-2025/crates/exo-backend-classical/src/vector.rs @@ -1,8 +1,8 @@ //! Vector index wrapper for ruvector-core use exo_core::{ - Error as ExoError, Filter, Metadata, MetadataValue, Pattern, PatternId, - Result as ExoResult, SearchResult, SubstrateTime, + Error as ExoError, Filter, Metadata, MetadataValue, Pattern, PatternId, Result as ExoResult, + SearchResult, SubstrateTime, }; use ruvector_core::{types::*, VectorDB}; use std::collections::HashMap; @@ -17,9 +17,13 @@ pub struct VectorIndexWrapper { impl VectorIndexWrapper { /// Create a new vector index wrapper - pub fn new(dimensions: usize, distance_metric: DistanceMetric) -> Result { + pub fn new( + dimensions: usize, + distance_metric: DistanceMetric, + ) -> Result { // Use a temporary file path for in-memory like behavior - let temp_path = std::env::temp_dir().join(format!("exo_vector_{}.db", uuid::Uuid::new_v4())); + let temp_path = + std::env::temp_dir().join(format!("exo_vector_{}.db", uuid::Uuid::new_v4())); let options = DbOptions { dimensions, @@ -38,7 +42,7 @@ impl VectorIndexWrapper { pub fn insert(&mut self, pattern: &Pattern) -> ExoResult { // Convert Pattern to VectorEntry let metadata = Self::serialize_metadata(pattern)?; - + let entry = VectorEntry { id: Some(pattern.id.to_string()), vector: pattern.embedding.clone(), @@ -79,20 +83,19 @@ impl VectorIndexWrapper { Ok(results .into_iter() .filter_map(|r| { - Self::deserialize_pattern(&r.metadata?, r.vector.as_ref()) - .map(|pattern| SearchResult { + Self::deserialize_pattern(&r.metadata?, r.vector.as_ref()).map(|pattern| { + SearchResult { pattern, score: r.score, distance: r.score, // For now, distance == score - }) + } + }) }) .collect()) } /// Serialize pattern metadata to JSON - fn serialize_metadata( - pattern: &Pattern, - ) -> ExoResult> { + fn serialize_metadata(pattern: &Pattern) -> ExoResult> { let mut json_metadata = HashMap::new(); // Add pattern metadata fields diff --git a/examples/exo-ai-2025/crates/exo-backend-classical/tests/learning_benchmarks.rs b/examples/exo-ai-2025/crates/exo-backend-classical/tests/learning_benchmarks.rs index bbfc0bafb..e588b9eed 100644 --- a/examples/exo-ai-2025/crates/exo-backend-classical/tests/learning_benchmarks.rs +++ b/examples/exo-ai-2025/crates/exo-backend-classical/tests/learning_benchmarks.rs @@ -9,21 +9,20 @@ //! - Consciousness metrics (IIT) //! - Thermodynamic tracking -use std::time::{Duration, Instant}; use std::collections::HashMap; +use std::time::{Duration, Instant}; // EXO-AI crates -use exo_core::{Pattern, PatternId, Metadata, SubstrateTime}; -use exo_core::consciousness::{ConsciousnessCalculator, SubstrateRegion, NodeState}; -use exo_core::thermodynamics::{ThermodynamicTracker, Operation}; +use exo_core::consciousness::{ConsciousnessCalculator, NodeState, SubstrateRegion}; +use exo_core::thermodynamics::{Operation, ThermodynamicTracker}; +use exo_core::{Metadata, Pattern, PatternId, SubstrateTime}; use exo_temporal::{ - TemporalMemory, TemporalConfig, Query, - ConsolidationConfig, - anticipation::{SequentialPatternTracker, PrefetchCache}, - causal::{CausalGraph, CausalConeType}, + anticipation::{PrefetchCache, SequentialPatternTracker}, + causal::{CausalConeType, CausalGraph}, consolidation::compute_salience, long_term::LongTermStore, types::TemporalPattern, + ConsolidationConfig, Query, TemporalConfig, TemporalMemory, }; const VECTOR_DIM: usize = 384; @@ -82,8 +81,10 @@ impl BenchmarkResult { } fn print(&self) { - println!(" {}: {:?} total, {:?}/op, {:.0} ops/sec", - self.name, self.total_time, self.per_op, self.ops_per_sec); + println!( + " {}: {:?} total, {:?}/op, {:.0} ops/sec", + self.name, self.total_time, self.per_op, self.ops_per_sec + ); } } @@ -129,18 +130,31 @@ fn benchmark_sequential_pattern_learning() { let p3 = patterns[2]; // Train: p1 -> p2 (10 times), p1 -> p3 (3 times) - for _ in 0..10 { tracker.record_sequence(p1, p2); } - for _ in 0..3 { tracker.record_sequence(p1, p3); } + for _ in 0..10 { + tracker.record_sequence(p1, p2); + } + for _ in 0..3 { + tracker.record_sequence(p1, p3); + } let predictions = tracker.predict_next(p1, 2); println!("\n Learning Accuracy Test:"); println!(" Pattern p1 -> p2 trained 10x, p1 -> p3 trained 3x"); - println!(" Top prediction correct: {}", predictions.first() == Some(&p2)); + println!( + " Top prediction correct: {}", + predictions.first() == Some(&p2) + ); println!(" Prediction count: {}", predictions.len()); println!("\n Summary:"); - println!(" Record throughput: {:.0} sequences/sec", record_result.ops_per_sec); - println!(" Predict throughput: {:.0} predictions/sec", predict_result.ops_per_sec); + println!( + " Record throughput: {:.0} sequences/sec", + record_result.ops_per_sec + ); + println!( + " Predict throughput: {:.0} predictions/sec", + predict_result.ops_per_sec + ); } // ============================================================================ @@ -231,8 +245,14 @@ fn benchmark_causal_graph_operations() { println!("\n Summary:"); println!(" Edge insertion: {:.0} ops/sec", edge_result.ops_per_sec); - println!(" Path finding: {:.0} ops/sec", distance_result.ops_per_sec); - println!(" Transitive closure: {:.0} ops/sec", past_result.ops_per_sec); + println!( + " Path finding: {:.0} ops/sec", + distance_result.ops_per_sec + ); + println!( + " Transitive closure: {:.0} ops/sec", + past_result.ops_per_sec + ); } // ============================================================================ @@ -284,12 +304,20 @@ fn benchmark_salience_computation() { salience_result.print(); println!("\n Salience Distribution:"); - println!(" Average salience: {:.4}", total_salience / iterations as f32); - println!(" Weights: freq={:.1}, recency={:.1}, causal={:.1}, surprise={:.1}", - config.w_frequency, config.w_recency, config.w_causal, config.w_surprise); + println!( + " Average salience: {:.4}", + total_salience / iterations as f32 + ); + println!( + " Weights: freq={:.1}, recency={:.1}, causal={:.1}, surprise={:.1}", + config.w_frequency, config.w_recency, config.w_causal, config.w_surprise + ); println!("\n Summary:"); - println!(" Salience computation: {:.0} ops/sec", salience_result.ops_per_sec); + println!( + " Salience computation: {:.0} ops/sec", + salience_result.ops_per_sec + ); println!(" Per pattern overhead: {:?}", salience_result.per_op); } @@ -350,7 +378,10 @@ fn benchmark_anticipation_prediction() { let lookup_result = BenchmarkResult::new("Cache lookup", iterations, start.elapsed()); lookup_result.print(); - println!(" Cache hit rate: {:.1}%", (hits as f64 / iterations as f64) * 100.0); + println!( + " Cache hit rate: {:.1}%", + (hits as f64 / iterations as f64) * 100.0 + ); // Benchmark: Sequential anticipation let seq_tracker = SequentialPatternTracker::new(); @@ -370,12 +401,19 @@ fn benchmark_anticipation_prediction() { // Would normally fetch from long-term } } - let anticipate_result = BenchmarkResult::new("Anticipate + predict", iterations, start.elapsed()); + let anticipate_result = + BenchmarkResult::new("Anticipate + predict", iterations, start.elapsed()); anticipate_result.print(); println!("\n Summary:"); - println!(" Cache throughput: {:.0} ops/sec", lookup_result.ops_per_sec); - println!(" Anticipation throughput: {:.0} ops/sec", anticipate_result.ops_per_sec); + println!( + " Cache throughput: {:.0} ops/sec", + lookup_result.ops_per_sec + ); + println!( + " Anticipation throughput: {:.0} ops/sec", + anticipate_result.ops_per_sec + ); } // ============================================================================ @@ -412,11 +450,15 @@ fn benchmark_memory_consolidation() { let consolidate_time = start.elapsed(); println!(" Batch size {}: {:?}", batch_size, consolidate_time); - println!(" Consolidated: {}, Forgotten: {}", - result.num_consolidated, result.num_forgotten); + println!( + " Consolidated: {}, Forgotten: {}", + result.num_consolidated, result.num_forgotten + ); println!(" Per pattern: {:?}", consolidate_time / batch_size); - println!(" Throughput: {:.0} patterns/sec", - batch_size as f64 / consolidate_time.as_secs_f64()); + println!( + " Throughput: {:.0} patterns/sec", + batch_size as f64 / consolidate_time.as_secs_f64() + ); } // Benchmark strategic forgetting @@ -476,10 +518,13 @@ fn benchmark_consciousness_metrics() { let mut states = HashMap::new(); for &node in &nodes { - states.insert(node, NodeState { - activation: (node as f64 * 0.1).sin().abs(), - previous_activation: (node as f64 * 0.1 - 0.1).sin().abs(), - }); + states.insert( + node, + NodeState { + activation: (node as f64 * 0.1).sin().abs(), + previous_activation: (node as f64 * 0.1 - 0.1).sin().abs(), + }, + ); } let region = SubstrateRegion { @@ -506,8 +551,10 @@ fn benchmark_consciousness_metrics() { println!(" {} nodes, {} perturbations:", num_nodes, perturbations); println!(" Time per Φ: {:?}", phi_time / iterations); println!(" Average Φ: {:.4}", total_phi / iterations as f64); - println!(" Throughput: {:.0} calcs/sec", - iterations as f64 / phi_time.as_secs_f64()); + println!( + " Throughput: {:.0} calcs/sec", + iterations as f64 / phi_time.as_secs_f64() + ); } println!(); } @@ -530,7 +577,13 @@ fn benchmark_consciousness_metrics() { states: { let mut s = HashMap::new(); for i in 1..=5 { - s.insert(i, NodeState { activation: 0.5, previous_activation: 0.4 }); + s.insert( + i, + NodeState { + activation: 0.5, + previous_activation: 0.4, + }, + ); } s }, @@ -553,7 +606,13 @@ fn benchmark_consciousness_metrics() { states: { let mut s = HashMap::new(); for i in 1..=5 { - s.insert(i, NodeState { activation: 0.5, previous_activation: 0.4 }); + s.insert( + i, + NodeState { + activation: 0.5, + previous_activation: 0.4, + }, + ); } s }, @@ -565,8 +624,14 @@ fn benchmark_consciousness_metrics() { let ff_result = calculator.compute_phi(&ff_region); let re_result = calculator.compute_phi(&re_region); - println!(" Feed-forward Φ: {:.4} (level: {:?})", ff_result.phi, ff_result.consciousness_level); - println!(" Reentrant Φ: {:.4} (level: {:?})", re_result.phi, re_result.consciousness_level); + println!( + " Feed-forward Φ: {:.4} (level: {:?})", + ff_result.phi, ff_result.consciousness_level + ); + println!( + " Reentrant Φ: {:.4} (level: {:?})", + re_result.phi, re_result.consciousness_level + ); println!("\n Summary:"); println!(" IIT Φ computation scales with O(n²) in nodes"); @@ -612,25 +677,47 @@ fn benchmark_thermodynamic_tracking() { let report = tracker.efficiency_report(); println!("\n Efficiency Report:"); - println!(" Total bit erasures: {:.2e}", report.total_bit_erasures as f64); - println!(" Landauer minimum: {:.2e} J", report.landauer_minimum_joules); - println!(" Estimated actual: {:.2e} J", report.estimated_actual_joules); - println!(" Efficiency ratio: {:.0}x above Landauer limit", report.efficiency_ratio); - println!(" Reversible savings potential: {:.2e} J", report.reversible_savings_potential); + println!( + " Total bit erasures: {:.2e}", + report.total_bit_erasures as f64 + ); + println!( + " Landauer minimum: {:.2e} J", + report.landauer_minimum_joules + ); + println!( + " Estimated actual: {:.2e} J", + report.estimated_actual_joules + ); + println!( + " Efficiency ratio: {:.0}x above Landauer limit", + report.efficiency_ratio + ); + println!( + " Reversible savings potential: {:.2e} J", + report.reversible_savings_potential + ); // Test different temperatures println!("\n Temperature Sensitivity:"); - for temp in [77.0, 300.0, 400.0] { // Liquid nitrogen, room temp, hot + for temp in [77.0, 300.0, 400.0] { + // Liquid nitrogen, room temp, hot let temp_tracker = ThermodynamicTracker::new(temp); for _ in 0..1000 { temp_tracker.record_operation(Operation::VectorSimilarity { dimensions: 384 }); } let temp_report = temp_tracker.efficiency_report(); - println!(" {}K: Landauer min = {:.2e} J", temp, temp_report.landauer_minimum_joules); + println!( + " {}K: Landauer min = {:.2e} J", + temp, temp_report.landauer_minimum_joules + ); } println!("\n Summary:"); - println!(" Tracking overhead: {:?} per operation", record_result.per_op); + println!( + " Tracking overhead: {:?} per operation", + record_result.per_op + ); println!(" Landauer limit scales with kT*ln(2)"); } @@ -670,7 +757,8 @@ fn benchmark_comprehensive_comparison() { let search_iterations = 100; let start = Instant::now(); for _ in 0..search_iterations { - let mut scores: Vec<(usize, f32)> = base_store.iter() + let mut scores: Vec<(usize, f32)> = base_store + .iter() .map(|(id, vec)| { let dot: f32 = query.iter().zip(vec.iter()).map(|(a, b)| a * b).sum(); let mag_q: f32 = query.iter().map(|x| x * x).sum::().sqrt(); @@ -682,8 +770,14 @@ fn benchmark_comprehensive_comparison() { let _ = scores.into_iter().take(10).collect::>(); } let base_search_time = start.elapsed(); - println!(" Search {} queries: {:?}", search_iterations, base_search_time); - println!(" Per search: {:?}", base_search_time / search_iterations as u32); + println!( + " Search {} queries: {:?}", + search_iterations, base_search_time + ); + println!( + " Per search: {:?}", + base_search_time / search_iterations as u32 + ); // ------------------------------------------------------------------------- // EXO-AI with full cognitive features @@ -716,7 +810,9 @@ fn benchmark_comprehensive_comparison() { } // Record thermodynamics - thermodynamics.record_operation(Operation::MemoryWrite { bytes: (VECTOR_DIM * 4) as u64 }); + thermodynamics.record_operation(Operation::MemoryWrite { + bytes: (VECTOR_DIM * 4) as u64, + }); } let exo_insert_time = start.elapsed(); println!(" Insert {} patterns: {:?}", iterations, exo_insert_time); @@ -727,8 +823,10 @@ fn benchmark_comprehensive_comparison() { let consolidation_result = exo_memory.consolidate(); let consolidate_time = start.elapsed(); println!(" Consolidate: {:?}", consolidate_time); - println!(" Patterns kept: {}, forgotten: {}", - consolidation_result.num_consolidated, consolidation_result.num_forgotten); + println!( + " Patterns kept: {}, forgotten: {}", + consolidation_result.num_consolidated, consolidation_result.num_forgotten + ); // EXO search with temporal context let search_iterations = 100; @@ -736,11 +834,19 @@ fn benchmark_comprehensive_comparison() { for _ in 0..search_iterations { let query = Query::from_embedding(generate_random_vector(VECTOR_DIM, 888888)); let _ = exo_memory.long_term().search(&query); - thermodynamics.record_operation(Operation::VectorSimilarity { dimensions: VECTOR_DIM }); + thermodynamics.record_operation(Operation::VectorSimilarity { + dimensions: VECTOR_DIM, + }); } let exo_search_time = start.elapsed(); - println!(" Search {} queries: {:?}", search_iterations, exo_search_time); - println!(" Per search: {:?}", exo_search_time / search_iterations as u32); + println!( + " Search {} queries: {:?}", + search_iterations, exo_search_time + ); + println!( + " Per search: {:?}", + exo_search_time / search_iterations as u32 + ); // Causal query let start = Instant::now(); @@ -750,8 +856,14 @@ fn benchmark_comprehensive_comparison() { let _ = exo_memory.causal_query(&query, SubstrateTime::now(), CausalConeType::Future); } let causal_search_time = start.elapsed(); - println!(" Causal query {} times: {:?}", search_iterations, causal_search_time); - println!(" Per causal query: {:?}", causal_search_time / search_iterations as u32); + println!( + " Causal query {} times: {:?}", + search_iterations, causal_search_time + ); + println!( + " Per causal query: {:?}", + causal_search_time / search_iterations as u32 + ); // Anticipation let start = Instant::now(); @@ -760,7 +872,10 @@ fn benchmark_comprehensive_comparison() { let _predicted = seq_tracker.predict_next(current, 5); } let anticipate_time = start.elapsed(); - println!(" Anticipate {} times: {:?}", search_iterations, anticipate_time); + println!( + " Anticipate {} times: {:?}", + search_iterations, anticipate_time + ); // ------------------------------------------------------------------------- // Comparison Summary @@ -779,16 +894,28 @@ fn benchmark_comprehensive_comparison() { println!(" ║ Operation │ Base │ EXO-AI │ Overhead ║"); println!(" ╠════════════════════╪═══════════╪═══════════╪════════════╣"); - println!(" ║ Insert │ {:>7}µs │ {:>7}µs │ {:>6.1}x ║", - base_insert_per_op, exo_insert_per_op, insert_overhead); - println!(" ║ Search │ {:>7}µs │ {:>7}µs │ {:>6.1}x ║", - base_search_per_op / 1000, exo_search_per_op / 1000, search_overhead); - println!(" ║ Causal Query │ N/A │ {:>7}µs │ NEW ║", - causal_search_time.as_micros() / 100); - println!(" ║ Anticipation │ N/A │ {:>7}µs │ NEW ║", - anticipate_time.as_micros() / 100); - println!(" ║ Consolidation │ N/A │ {:>7}ms │ NEW ║", - consolidate_time.as_millis()); + println!( + " ║ Insert │ {:>7}µs │ {:>7}µs │ {:>6.1}x ║", + base_insert_per_op, exo_insert_per_op, insert_overhead + ); + println!( + " ║ Search │ {:>7}µs │ {:>7}µs │ {:>6.1}x ║", + base_search_per_op / 1000, + exo_search_per_op / 1000, + search_overhead + ); + println!( + " ║ Causal Query │ N/A │ {:>7}µs │ NEW ║", + causal_search_time.as_micros() / 100 + ); + println!( + " ║ Anticipation │ N/A │ {:>7}µs │ NEW ║", + anticipate_time.as_micros() / 100 + ); + println!( + " ║ Consolidation │ N/A │ {:>7}ms │ NEW ║", + consolidate_time.as_millis() + ); println!(" ╠══════════════════════════════════════════════════════════════╣"); println!(" ║ COGNITIVE CAPABILITIES ║"); println!(" ╠══════════════════════════════════════════════════════════════╣"); @@ -805,9 +932,18 @@ fn benchmark_comprehensive_comparison() { // Print thermodynamic report let report = thermodynamics.efficiency_report(); println!("\n Thermodynamic Efficiency:"); - println!(" Operations tracked: {:.2e} bit erasures", report.total_bit_erasures as f64); - println!(" Theoretical minimum (Landauer): {:.2e} J", report.landauer_minimum_joules); - println!(" Current system: {:.0}x above minimum", report.efficiency_ratio); + println!( + " Operations tracked: {:.2e} bit erasures", + report.total_bit_erasures as f64 + ); + println!( + " Theoretical minimum (Landauer): {:.2e} J", + report.landauer_minimum_joules + ); + println!( + " Current system: {:.0}x above minimum", + report.efficiency_ratio + ); } // ============================================================================ @@ -844,8 +980,10 @@ fn benchmark_scaling_characteristics() { memory.consolidate(); let consolidate_time = start.elapsed(); - println!(" {:>5} patterns: insert {:>8?}, consolidate {:>8?}", - scale, insert_time, consolidate_time); + println!( + " {:>5} patterns: insert {:>8?}, consolidate {:>8?}", + scale, insert_time, consolidate_time + ); } println!("\n Search Scaling (vs store size):"); @@ -877,10 +1015,12 @@ fn benchmark_scaling_characteristics() { } let search_time = start.elapsed(); - println!(" {:>5} patterns: {:>6?} per search ({:.0} qps)", + println!( + " {:>5} patterns: {:>6?} per search ({:.0} qps)", scale, search_time / iterations, - iterations as f64 / search_time.as_secs_f64()); + iterations as f64 / search_time.as_secs_f64() + ); } println!("\n Causal Graph Scaling:"); @@ -917,10 +1057,12 @@ fn benchmark_scaling_characteristics() { } let future_time = start2.elapsed(); - println!(" {:>5} nodes: distance {:>6?}, future {:>6?}", + println!( + " {:>5} nodes: distance {:>6?}, future {:>6?}", scale, distance_time / iterations, - future_time / iterations); + future_time / iterations + ); } println!("\n Summary:"); diff --git a/examples/exo-ai-2025/crates/exo-backend-classical/tests/performance_comparison.rs b/examples/exo-ai-2025/crates/exo-backend-classical/tests/performance_comparison.rs index 416f5a951..fc21903a8 100644 --- a/examples/exo-ai-2025/crates/exo-backend-classical/tests/performance_comparison.rs +++ b/examples/exo-ai-2025/crates/exo-backend-classical/tests/performance_comparison.rs @@ -5,9 +5,9 @@ use std::time::Instant; // EXO-AI crates -use exo_core::{Pattern, PatternId, Metadata, SubstrateTime}; -use exo_temporal::{TemporalMemory, TemporalConfig, Query, ConsolidationConfig}; +use exo_core::{Metadata, Pattern, PatternId, SubstrateTime}; use exo_federation::crypto::PostQuantumKeypair; +use exo_temporal::{ConsolidationConfig, Query, TemporalConfig, TemporalMemory}; const VECTOR_DIM: usize = 384; const NUM_VECTORS: usize = 1_000; @@ -77,7 +77,7 @@ fn benchmark_temporal_memory() { #[test] fn benchmark_consciousness_metrics() { - use exo_core::consciousness::{ConsciousnessCalculator, SubstrateRegion, NodeState}; + use exo_core::consciousness::{ConsciousnessCalculator, NodeState, SubstrateRegion}; use std::collections::HashMap; println!("\n=== IIT Phi Calculation Performance ===\n"); @@ -93,7 +93,13 @@ fn benchmark_consciousness_metrics() { let mut states = HashMap::new(); for &node in &nodes { - states.insert(node, NodeState { activation: 0.5, previous_activation: 0.4 }); + states.insert( + node, + NodeState { + activation: 0.5, + previous_activation: 0.4, + }, + ); } let region = SubstrateRegion { @@ -121,7 +127,7 @@ fn benchmark_consciousness_metrics() { #[test] fn benchmark_thermodynamic_tracking() { - use exo_core::thermodynamics::{ThermodynamicTracker, Operation}; + use exo_core::thermodynamics::{Operation, ThermodynamicTracker}; println!("\n=== Landauer Thermodynamic Tracking Performance ===\n"); @@ -140,11 +146,22 @@ fn benchmark_thermodynamic_tracking() { let report = tracker.efficiency_report(); println!("\nEfficiency Report:"); println!(" Total bit erasures: {}", report.total_bit_erasures); - println!(" Landauer minimum: {:.2e} J", report.landauer_minimum_joules); - println!(" Estimated actual: {:.2e} J", report.estimated_actual_joules); - println!(" Efficiency ratio: {:.0}x above Landauer", report.efficiency_ratio); - println!(" Reversible savings: {:.2}%", - (report.reversible_savings_potential / report.estimated_actual_joules) * 100.0); + println!( + " Landauer minimum: {:.2e} J", + report.landauer_minimum_joules + ); + println!( + " Estimated actual: {:.2e} J", + report.estimated_actual_joules + ); + println!( + " Efficiency ratio: {:.0}x above Landauer", + report.efficiency_ratio + ); + println!( + " Reversible savings: {:.2}%", + (report.reversible_savings_potential / report.estimated_actual_joules) * 100.0 + ); } #[test] diff --git a/examples/exo-ai-2025/crates/exo-core/src/consciousness.rs b/examples/exo-ai-2025/crates/exo-core/src/consciousness.rs index 4789f28e2..551b1106e 100644 --- a/examples/exo-ai-2025/crates/exo-core/src/consciousness.rs +++ b/examples/exo-ai-2025/crates/exo-core/src/consciousness.rs @@ -32,8 +32,8 @@ //! 3. **Reentrant**: Feedback loops present //! 4. **Selective**: Not fully connected -use std::collections::{HashMap, HashSet}; use std::cell::RefCell; +use std::collections::{HashMap, HashSet}; /// Represents a substrate region for Φ analysis #[derive(Debug, Clone)] @@ -362,7 +362,12 @@ impl ConsciousnessCalculator { let n = nodes.len(); if n <= 1 { - return (Partition { parts: vec![nodes.iter().cloned().collect()] }, 0.0); + return ( + Partition { + parts: vec![nodes.iter().cloned().collect()], + }, + 0.0, + ); } let mut min_ei = f64::INFINITY; @@ -476,10 +481,13 @@ impl ConsciousnessCalculator { /// Perturb a state vector fn perturb_state(&self, state: &[f64]) -> Vec { // Add Gaussian noise - state.iter().map(|&x| { - let noise = (rand_simple() - 0.5) * 0.1; - (x + noise).clamp(0.0, 1.0) - }).collect() + state + .iter() + .map(|&x| { + let noise = (rand_simple() - 0.5) * 0.1; + (x + noise).clamp(0.0, 1.0) + }) + .collect() } /// Evolve state through one time step - optimized with precomputed indices @@ -487,34 +495,36 @@ impl ConsciousnessCalculator { /// Uses O(1) HashMap lookups instead of O(n) linear search for neighbor indices. fn evolve_state(&self, region: &SubstrateRegion, nodes: &[NodeId], state: &[f64]) -> Vec { // Precompute node -> index mapping for O(1) lookup - let node_index: HashMap = nodes.iter() - .enumerate() - .map(|(i, &n)| (n, i)) - .collect(); + let node_index: HashMap = + nodes.iter().enumerate().map(|(i, &n)| (n, i)).collect(); // Leaky integration constant const ALPHA: f64 = 0.1; const ONE_MINUS_ALPHA: f64 = 1.0 - ALPHA; // Evolve each node - nodes.iter().enumerate().map(|(i, &node)| { - let current = state.get(i).cloned().unwrap_or(0.0); + nodes + .iter() + .enumerate() + .map(|(i, &node)| { + let current = state.get(i).cloned().unwrap_or(0.0); - // Sum inputs from connected nodes using precomputed index map - let input: f64 = region.connections - .get(&node) - .map(|neighbors| { - neighbors.iter() - .filter_map(|n| { - node_index.get(n).and_then(|&j| state.get(j)) - }) - .sum() - }) - .unwrap_or(0.0); + // Sum inputs from connected nodes using precomputed index map + let input: f64 = region + .connections + .get(&node) + .map(|neighbors| { + neighbors + .iter() + .filter_map(|n| node_index.get(n).and_then(|&j| state.get(j))) + .sum() + }) + .unwrap_or(0.0); - // Leaky integration with precomputed constants - (current * ONE_MINUS_ALPHA + input * ALPHA).clamp(0.0, 1.0) - }).collect() + // Leaky integration with precomputed constants + (current * ONE_MINUS_ALPHA + input * ALPHA).clamp(0.0, 1.0) + }) + .collect() } /// Batch compute Φ for multiple regions (useful for monitoring) @@ -569,9 +579,27 @@ mod tests { connections.insert(3, vec![1]); // Feedback creates reentrant architecture let mut states = HashMap::new(); - states.insert(1, NodeState { activation: 0.5, previous_activation: 0.4 }); - states.insert(2, NodeState { activation: 0.6, previous_activation: 0.5 }); - states.insert(3, NodeState { activation: 0.4, previous_activation: 0.3 }); + states.insert( + 1, + NodeState { + activation: 0.5, + previous_activation: 0.4, + }, + ); + states.insert( + 2, + NodeState { + activation: 0.6, + previous_activation: 0.5, + }, + ); + states.insert( + 3, + NodeState { + activation: 0.4, + previous_activation: 0.3, + }, + ); SubstrateRegion { id: "test_region".to_string(), @@ -591,9 +619,27 @@ mod tests { // No connection from 3 back to 1 - pure feed-forward let mut states = HashMap::new(); - states.insert(1, NodeState { activation: 0.5, previous_activation: 0.4 }); - states.insert(2, NodeState { activation: 0.6, previous_activation: 0.5 }); - states.insert(3, NodeState { activation: 0.4, previous_activation: 0.3 }); + states.insert( + 1, + NodeState { + activation: 0.5, + previous_activation: 0.4, + }, + ); + states.insert( + 2, + NodeState { + activation: 0.6, + previous_activation: 0.5, + }, + ); + states.insert( + 3, + NodeState { + activation: 0.4, + previous_activation: 0.3, + }, + ); SubstrateRegion { id: "feedforward".to_string(), @@ -629,9 +675,15 @@ mod tests { #[test] fn test_consciousness_levels() { assert_eq!(ConsciousnessLevel::from_phi(0.0), ConsciousnessLevel::None); - assert_eq!(ConsciousnessLevel::from_phi(0.05), ConsciousnessLevel::Minimal); + assert_eq!( + ConsciousnessLevel::from_phi(0.05), + ConsciousnessLevel::Minimal + ); assert_eq!(ConsciousnessLevel::from_phi(0.5), ConsciousnessLevel::Low); - assert_eq!(ConsciousnessLevel::from_phi(5.0), ConsciousnessLevel::Moderate); + assert_eq!( + ConsciousnessLevel::from_phi(5.0), + ConsciousnessLevel::Moderate + ); assert_eq!(ConsciousnessLevel::from_phi(15.0), ConsciousnessLevel::High); } } diff --git a/examples/exo-ai-2025/crates/exo-core/src/lib.rs b/examples/exo-ai-2025/crates/exo-core/src/lib.rs index e987c98bf..716c3a933 100644 --- a/examples/exo-ai-2025/crates/exo-core/src/lib.rs +++ b/examples/exo-ai-2025/crates/exo-core/src/lib.rs @@ -300,11 +300,7 @@ pub trait SubstrateBackend: Send + Sync { ) -> Result>; /// Deform manifold to incorporate new pattern - fn manifold_deform( - &self, - pattern: &Pattern, - learning_rate: f32, - ) -> Result; + fn manifold_deform(&self, pattern: &Pattern, learning_rate: f32) -> Result; /// Get embedding dimension fn dimension(&self) -> usize; diff --git a/examples/exo-ai-2025/crates/exo-core/src/thermodynamics.rs b/examples/exo-ai-2025/crates/exo-core/src/thermodynamics.rs index 7477e0c50..81d2d450a 100644 --- a/examples/exo-ai-2025/crates/exo-core/src/thermodynamics.rs +++ b/examples/exo-ai-2025/crates/exo-core/src/thermodynamics.rs @@ -311,21 +311,43 @@ impl std::fmt::Display for EfficiencyReport { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "=== Thermodynamic Efficiency Report ===")?; writeln!(f, "Temperature: {:.1}K", self.temperature_kelvin)?; - writeln!(f, "Landauer limit: {:.2e} J/bit", self.landauer_limit_per_bit)?; + writeln!( + f, + "Landauer limit: {:.2e} J/bit", + self.landauer_limit_per_bit + )?; writeln!(f)?; writeln!(f, "Operations tracked: {}", self.total_operations)?; writeln!(f, "Total bit erasures: {}", self.total_bit_erasures)?; writeln!(f)?; - writeln!(f, "Theoretical minimum: {:.2e} J ({:.2e} eV)", - self.landauer_minimum_joules, self.landauer_minimum_ev)?; - writeln!(f, "Estimated actual: {:.2e} J", self.estimated_actual_joules)?; - writeln!(f, "Efficiency ratio: {:.0}× above Landauer", self.efficiency_ratio)?; + writeln!( + f, + "Theoretical minimum: {:.2e} J ({:.2e} eV)", + self.landauer_minimum_joules, self.landauer_minimum_ev + )?; + writeln!( + f, + "Estimated actual: {:.2e} J", + self.estimated_actual_joules + )?; + writeln!( + f, + "Efficiency ratio: {:.0}× above Landauer", + self.efficiency_ratio + )?; writeln!(f)?; writeln!(f, "Reversible computing potential:")?; - writeln!(f, " - Savings: {:.2e} J ({:.1}%)", + writeln!( + f, + " - Savings: {:.2e} J ({:.1}%)", self.reversible_savings_potential, - (self.reversible_savings_potential / self.estimated_actual_joules) * 100.0)?; - writeln!(f, " - Improvement factor: {:.0}×", self.reversible_improvement_factor)?; + (self.reversible_savings_potential / self.estimated_actual_joules) * 100.0 + )?; + writeln!( + f, + " - Improvement factor: {:.0}×", + self.reversible_improvement_factor + )?; Ok(()) } } @@ -384,8 +406,7 @@ mod tests { #[test] fn test_efficiency_report() { - let tracker = ThermodynamicTracker::room_temperature() - .with_technology_multiplier(1000.0); + let tracker = ThermodynamicTracker::room_temperature().with_technology_multiplier(1000.0); tracker.record_operation(Operation::BitErasure { count: 1_000_000 }); diff --git a/examples/exo-ai-2025/crates/exo-core/src/types.rs b/examples/exo-ai-2025/crates/exo-core/src/types.rs index 8740f7a70..8415971e2 100644 --- a/examples/exo-ai-2025/crates/exo-core/src/types.rs +++ b/examples/exo-ai-2025/crates/exo-core/src/types.rs @@ -31,7 +31,10 @@ impl Pattern { } /// Create a pattern with metadata - pub fn with_metadata(embedding: Vec, metadata: HashMap) -> Self { + pub fn with_metadata( + embedding: Vec, + metadata: HashMap, + ) -> Self { Self { embedding, metadata, @@ -98,26 +101,18 @@ pub enum TopologicalQuery { epsilon_range: (f32, f32), }, /// Find N-dimensional holes in structure - BettiNumbers { - max_dimension: usize, - }, + BettiNumbers { max_dimension: usize }, /// Sheaf consistency check - SheafConsistency { - local_sections: Vec, - }, + SheafConsistency { local_sections: Vec }, } /// Result from hypergraph query #[derive(Clone, Debug, Serialize, Deserialize)] pub enum HypergraphResult { /// Persistence diagram - PersistenceDiagram { - birth_death_pairs: Vec<(f32, f32)>, - }, + PersistenceDiagram { birth_death_pairs: Vec<(f32, f32)> }, /// Betti numbers by dimension - BettiNumbers { - numbers: Vec, - }, + BettiNumbers { numbers: Vec }, /// Sheaf consistency result SheafConsistency { is_consistent: bool, diff --git a/examples/exo-ai-2025/crates/exo-core/tests/core_traits_test.rs b/examples/exo-ai-2025/crates/exo-core/tests/core_traits_test.rs index c4d689bd8..476071e55 100644 --- a/examples/exo-ai-2025/crates/exo-core/tests/core_traits_test.rs +++ b/examples/exo-ai-2025/crates/exo-core/tests/core_traits_test.rs @@ -121,13 +121,11 @@ mod filter_tests { fn test_filter_construction() { // Test Filter type construction let filter = Filter { - conditions: vec![ - FilterCondition { - field: "category".to_string(), - operator: FilterOperator::Equal, - value: MetadataValue::String("test".to_string()), - }, - ], + conditions: vec![FilterCondition { + field: "category".to_string(), + operator: FilterOperator::Equal, + value: MetadataValue::String("test".to_string()), + }], }; assert_eq!(filter.conditions.len(), 1); } diff --git a/examples/exo-ai-2025/crates/exo-exotic/benches/exotic_benchmarks.rs b/examples/exo-ai-2025/crates/exo-exotic/benches/exotic_benchmarks.rs index ca4a3e185..133437bef 100644 --- a/examples/exo-ai-2025/crates/exo-exotic/benches/exotic_benchmarks.rs +++ b/examples/exo-ai-2025/crates/exo-exotic/benches/exotic_benchmarks.rs @@ -12,20 +12,16 @@ //! 9. Emergence Detection - Causal emergence scoring //! 10. Cognitive Black Holes - Attractor dynamics -use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use std::time::Duration; use exo_exotic::{ - StrangeLoop, TangledHierarchy, SelfAspect, - DreamEngine, DreamState, - FreeEnergyMinimizer, PredictiveModel, - MorphogeneticField, CognitiveEmbryogenesis, ReactionParams, - CollectiveConsciousness, HiveMind, SubstrateSpecialization, - TemporalQualia, SubjectiveTime, TimeCrystal, TemporalEvent, - MultipleSelvesSystem, EmotionalTone, - CognitiveThermodynamics, CognitivePhase, - EmergenceDetector, AggregationType, - CognitiveBlackHole, TrapType, EscapeMethod, + AggregationType, CognitiveBlackHole, CognitiveEmbryogenesis, CognitivePhase, + CognitiveThermodynamics, CollectiveConsciousness, DreamEngine, DreamState, EmergenceDetector, + EmotionalTone, EscapeMethod, FreeEnergyMinimizer, HiveMind, MorphogeneticField, + MultipleSelvesSystem, PredictiveModel, ReactionParams, SelfAspect, StrangeLoop, SubjectiveTime, + SubstrateSpecialization, TangledHierarchy, TemporalEvent, TemporalQualia, TimeCrystal, + TrapType, }; use uuid::Uuid; @@ -62,9 +58,7 @@ fn bench_strange_loops(c: &mut Criterion) { // Meta-reasoning group.bench_function("meta_reasoning", |b| { let mut sl = StrangeLoop::new(5); - b.iter(|| { - black_box(sl.meta_reason("I think about thinking about thinking")) - }) + b.iter(|| black_box(sl.meta_reason("I think about thinking about thinking"))) }); // Self-reference creation @@ -268,7 +262,7 @@ fn bench_collective(c: &mut Criterion) { // Connect all pairs for i in 0..ids.len() { - for j in i+1..ids.len() { + for j in i + 1..ids.len() { collective.connect(ids[i], ids[j], 0.5, true); } } @@ -284,11 +278,7 @@ fn bench_collective(c: &mut Criterion) { b.iter(|| { for i in 0..100 { - collective.share_memory( - &format!("key_{}", i), - vec![i as f64; 8], - owner, - ); + collective.share_memory(&format!("key_{}", i), vec![i as f64; 8], owner); } for i in 0..100 { black_box(collective.access_memory(&format!("key_{}", i))); @@ -343,11 +333,7 @@ fn bench_temporal(c: &mut Criterion) { group.bench_function("time_crystals", |b| { let mut tq = TemporalQualia::new(); for i in 0..5 { - tq.add_time_crystal( - (i + 1) as f64 * 10.0, - 1.0 / (i + 1) as f64, - vec![0.1; 4], - ); + tq.add_time_crystal((i + 1) as f64 * 10.0, 1.0 / (i + 1) as f64, vec![0.1; 4]); } b.iter(|| { @@ -386,11 +372,14 @@ fn bench_multiple_selves(c: &mut Criterion) { b.iter(|| { let mut system = MultipleSelvesSystem::new(); for i in 0..5 { - system.add_self(&format!("Self_{}", i), EmotionalTone { - valence: (i as f64 - 2.0) / 2.0, - arousal: 0.5, - dominance: 0.3 + i as f64 * 0.1, - }); + system.add_self( + &format!("Self_{}", i), + EmotionalTone { + valence: (i as f64 - 2.0) / 2.0, + arousal: 0.5, + dominance: 0.3 + i as f64 * 0.1, + }, + ); } black_box(system.measure_coherence()) }) @@ -400,12 +389,22 @@ fn bench_multiple_selves(c: &mut Criterion) { group.bench_function("conflict_resolution", |b| { b.iter(|| { let mut system = MultipleSelvesSystem::new(); - let id1 = system.add_self("Self1", EmotionalTone { - valence: 0.8, arousal: 0.6, dominance: 0.7 - }); - let id2 = system.add_self("Self2", EmotionalTone { - valence: -0.3, arousal: 0.4, dominance: 0.5 - }); + let id1 = system.add_self( + "Self1", + EmotionalTone { + valence: 0.8, + arousal: 0.6, + dominance: 0.7, + }, + ); + let id2 = system.add_self( + "Self2", + EmotionalTone { + valence: -0.3, + arousal: 0.4, + dominance: 0.5, + }, + ); system.create_conflict(id1, id2); black_box(system.resolve_conflict(id1, id2)) @@ -416,12 +415,22 @@ fn bench_multiple_selves(c: &mut Criterion) { group.bench_function("merge_selves", |b| { b.iter(|| { let mut system = MultipleSelvesSystem::new(); - let id1 = system.add_self("Part1", EmotionalTone { - valence: 0.5, arousal: 0.5, dominance: 0.5 - }); - let id2 = system.add_self("Part2", EmotionalTone { - valence: 0.5, arousal: 0.5, dominance: 0.5 - }); + let id1 = system.add_self( + "Part1", + EmotionalTone { + valence: 0.5, + arousal: 0.5, + dominance: 0.5, + }, + ); + let id2 = system.add_self( + "Part2", + EmotionalTone { + valence: 0.5, + arousal: 0.5, + dominance: 0.5, + }, + ); black_box(system.merge(id1, id2)) }) }); @@ -507,7 +516,7 @@ fn bench_emergence(c: &mut Criterion) { let micro_state: Vec = (0..64).map(|i| i as f64 * 0.01).collect(); let groupings: Vec> = (0..16) - .map(|i| vec![i*4, i*4+1, i*4+2, i*4+3]) + .map(|i| vec![i * 4, i * 4 + 1, i * 4 + 2, i * 4 + 3]) .collect(); detector.set_coarse_graining(groupings, AggregationType::Mean); @@ -521,9 +530,7 @@ fn bench_emergence(c: &mut Criterion) { b.iter(|| { let mut detector = EmergenceDetector::new(); for i in 0..100 { - let micro_state: Vec = (0..32) - .map(|j| ((i + j) as f64 * 0.1).sin()) - .collect(); + let micro_state: Vec = (0..32).map(|j| ((i + j) as f64 * 0.1).sin()).collect(); detector.set_micro_state(micro_state); detector.detect_emergence(); } @@ -545,11 +552,7 @@ fn bench_black_holes(c: &mut Criterion) { // Thought processing group.bench_function("process_100_thoughts", |b| { b.iter(|| { - let mut bh = CognitiveBlackHole::with_params( - vec![0.0; 8], - 1.5, - TrapType::Rumination, - ); + let mut bh = CognitiveBlackHole::with_params(vec![0.0; 8], 1.5, TrapType::Rumination); for i in 0..100 { let thought = vec![i as f64 * 0.01; 8]; black_box(bh.process_thought(thought)); @@ -560,11 +563,7 @@ fn bench_black_holes(c: &mut Criterion) { // Escape attempts group.bench_function("escape_attempts", |b| { b.iter(|| { - let mut bh = CognitiveBlackHole::with_params( - vec![0.0; 8], - 2.0, - TrapType::Anxiety, - ); + let mut bh = CognitiveBlackHole::with_params(vec![0.0; 8], 2.0, TrapType::Anxiety); // Capture some thoughts for _ in 0..10 { @@ -668,7 +667,7 @@ fn bench_scaling(c: &mut Criterion) { .collect(); for i in 0..ids.len() { - for j in i+1..ids.len() { + for j in i + 1..ids.len() { collective.connect(ids[i], ids[j], 0.5, true); } } @@ -688,7 +687,9 @@ fn rand_f64() -> f64 { .duration_since(std::time::UNIX_EPOCH) .map(|d| d.as_nanos()) .unwrap_or(12345) as u64; - let result = seed.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407); + let result = seed + .wrapping_mul(6364136223846793005) + .wrapping_add(1442695040888963407); (result as f64) / (u64::MAX as f64) } diff --git a/examples/exo-ai-2025/crates/exo-exotic/src/black_holes.rs b/examples/exo-ai-2025/crates/exo-exotic/src/black_holes.rs index 7634a2ee2..a8f0026bd 100644 --- a/examples/exo-ai-2025/crates/exo-exotic/src/black_holes.rs +++ b/examples/exo-ai-2025/crates/exo-exotic/src/black_holes.rs @@ -18,8 +18,8 @@ //! - Clinical psychology (rumination, OCD) //! - Physics of black holes as metaphor +use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use serde::{Serialize, Deserialize}; use uuid::Uuid; /// Cognitive black hole representing an attractor state @@ -476,7 +476,9 @@ fn rand_probability() -> f64 { .unwrap_or(12345) as u64; // Simple LCG - let result = seed.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407); + let result = seed + .wrapping_mul(6364136223846793005) + .wrapping_add(1442695040888963407); (result as f64) / (u64::MAX as f64) } @@ -493,11 +495,7 @@ mod tests { #[test] fn test_thought_capture() { - let mut bh = CognitiveBlackHole::with_params( - vec![0.0; 8], - 2.0, - TrapType::Rumination - ); + let mut bh = CognitiveBlackHole::with_params(vec![0.0; 8], 2.0, TrapType::Rumination); // Close thought should be captured let close_thought = vec![0.1; 8]; @@ -509,26 +507,21 @@ mod tests { #[test] fn test_thought_orbiting() { - let mut bh = CognitiveBlackHole::with_params( - vec![0.0; 8], - 1.0, - TrapType::Neutral - ); + let mut bh = CognitiveBlackHole::with_params(vec![0.0; 8], 1.0, TrapType::Neutral); // Medium distance thought let thought = vec![0.8; 8]; let result = bh.process_thought(thought); - assert!(matches!(result, ThoughtResult::Orbiting { .. } | ThoughtResult::Free { .. })); + assert!(matches!( + result, + ThoughtResult::Orbiting { .. } | ThoughtResult::Free { .. } + )); } #[test] fn test_escape_attempt() { - let mut bh = CognitiveBlackHole::with_params( - vec![0.0; 8], - 1.0, - TrapType::Anxiety - ); + let mut bh = CognitiveBlackHole::with_params(vec![0.0; 8], 1.0, TrapType::Anxiety); // Capture some thoughts for _ in 0..3 { @@ -549,7 +542,7 @@ mod tests { let mut bh = CognitiveBlackHole::with_params( vec![0.0; 8], 5.0, // Strong black hole - TrapType::Depression + TrapType::Depression, ); bh.process_thought(vec![0.1; 8]); @@ -586,7 +579,7 @@ mod tests { fn test_tick_decay() { let mut bh = CognitiveBlackHole::with_params( vec![0.0; 8], - 2.0, // Higher strength + 2.0, // Higher strength TrapType::Neutral, ); // Use a close thought that will definitely be captured @@ -602,11 +595,7 @@ mod tests { #[test] fn test_statistics() { - let mut bh = CognitiveBlackHole::with_params( - vec![0.0; 8], - 1.5, - TrapType::Obsession - ); + let mut bh = CognitiveBlackHole::with_params(vec![0.0; 8], 1.5, TrapType::Obsession); bh.process_thought(vec![0.1; 8]); bh.attempt_escape(0.5, EscapeMethod::Tunneling); diff --git a/examples/exo-ai-2025/crates/exo-exotic/src/collective.rs b/examples/exo-ai-2025/crates/exo-exotic/src/collective.rs index c4e924e2c..d50f54cc4 100644 --- a/examples/exo-ai-2025/crates/exo-exotic/src/collective.rs +++ b/examples/exo-ai-2025/crates/exo-exotic/src/collective.rs @@ -17,11 +17,11 @@ //! - Swarm intelligence (ant colonies, bee hives) //! - Global Workspace Theory (Baars) +use dashmap::DashMap; +use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::sync::{Arc, RwLock}; -use serde::{Serialize, Deserialize}; use uuid::Uuid; -use dashmap::DashMap; /// Collective consciousness spanning multiple substrates #[derive(Debug)] @@ -197,7 +197,9 @@ impl CollectiveConsciousness { } // Compute local Φ for each substrate (collect state first to avoid borrow issues) - let local_phis: Vec = self.substrates.iter() + let local_phis: Vec = self + .substrates + .iter() .map(|s| { let entropy = self.compute_entropy(&s.state); let integration = s.activity * s.capacity; @@ -214,7 +216,9 @@ impl CollectiveConsciousness { let integration = self.compute_integration(); // Global Φ = sum of local Φ weighted by integration - let local_sum: f64 = self.substrates.iter() + let local_sum: f64 = self + .substrates + .iter() .map(|s| s.local_phi * s.activity) .sum(); @@ -237,7 +241,8 @@ impl CollectiveConsciousness { } let normalized: Vec = state.iter().map(|x| x.abs() / sum).collect(); - -normalized.iter() + -normalized + .iter() .filter(|&&p| p > 1e-10) .map(|&p| p * p.ln()) .sum::() @@ -252,21 +257,23 @@ impl CollectiveConsciousness { let max_connections = self.substrates.len() * (self.substrates.len() - 1); let connection_density = self.connections.len() as f64 / max_connections as f64; - let avg_strength: f64 = self.connections.iter() - .map(|c| c.strength) - .sum::() / self.connections.len() as f64; + let avg_strength: f64 = self.connections.iter().map(|c| c.strength).sum::() + / self.connections.len() as f64; (connection_density * avg_strength).min(1.0) } /// Share memory item across collective pub fn share_memory(&self, key: &str, content: Vec, owner: Uuid) { - self.shared_memory.insert(key.to_string(), SharedMemoryItem { - content, - owner, - access_count: 0, - importance: 0.5, - }); + self.shared_memory.insert( + key.to_string(), + SharedMemoryItem { + content, + owner, + access_count: 0, + importance: 0.5, + }, + ); } /// Access shared memory @@ -297,7 +304,9 @@ impl CollectiveConsciousness { /// Propagate state through network pub fn propagate(&mut self) { - let substrate_map: HashMap = self.substrates.iter() + let substrate_map: HashMap = self + .substrates + .iter() .enumerate() .map(|(i, s)| (s.id, i)) .collect(); @@ -309,9 +318,7 @@ impl CollectiveConsciousness { (substrate_map.get(&conn.from), substrate_map.get(&conn.to)) { let from_state = &self.substrates[from_idx].state; - let influence: Vec = from_state.iter() - .map(|&v| v * conn.strength) - .collect(); + let influence: Vec = from_state.iter().map(|&v| v * conn.strength).collect(); updates.push((to_idx, influence)); } } @@ -341,8 +348,7 @@ impl CollectiveConsciousness { let avg_activity = if self.substrates.is_empty() { 0.0 } else { - self.substrates.iter().map(|s| s.activity).sum::() - / self.substrates.len() as f64 + self.substrates.iter().map(|s| s.activity).sum::() / self.substrates.len() as f64 }; CollectiveHealth { @@ -406,12 +412,14 @@ impl HiveMind { return None; } - let avg_vote: f64 = decision.votes.values().sum::() - / decision.votes.len() as f64; + let avg_vote: f64 = decision.votes.values().sum::() / decision.votes.len() as f64; - decision.consensus_level = decision.votes.values() + decision.consensus_level = decision + .votes + .values() .map(|&v| 1.0 - (v - avg_vote).abs()) - .sum::() / decision.votes.len() as f64; + .sum::() + / decision.votes.len() as f64; let result = avg_vote > 0.0 && decision.consensus_level >= self.consensus_threshold; decision.result = Some(result); @@ -576,7 +584,7 @@ mod tests { // Connect all pairs let ids: Vec = collective.substrates.iter().map(|s| s.id).collect(); for i in 0..ids.len() { - for j in i+1..ids.len() { + for j in i + 1..ids.len() { collective.connect(ids[i], ids[j], 0.5, true); } } diff --git a/examples/exo-ai-2025/crates/exo-exotic/src/dreams.rs b/examples/exo-ai-2025/crates/exo-exotic/src/dreams.rs index 501f26af8..942c43be6 100644 --- a/examples/exo-ai-2025/crates/exo-exotic/src/dreams.rs +++ b/examples/exo-ai-2025/crates/exo-exotic/src/dreams.rs @@ -16,9 +16,9 @@ //! Inspired by research on hippocampal replay, REM sleep, and the //! activation-synthesis hypothesis. -use std::collections::{HashMap, VecDeque}; use rand::prelude::*; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; +use std::collections::{HashMap, VecDeque}; use uuid::Uuid; /// Engine for generating and processing artificial dreams @@ -157,9 +157,7 @@ impl DreamEngine { return 0.0; } - let total: f64 = self.dream_history.iter() - .map(|d| d.creativity_score) - .sum(); + let total: f64 = self.dream_history.iter().map(|d| d.creativity_score).sum(); total / self.dream_history.len() as f64 } @@ -238,14 +236,18 @@ impl DreamEngine { let mut consolidated = Vec::new(); // Prioritize high-salience, emotionally charged memories - let mut candidates: Vec<_> = self.memory_traces.iter_mut() + let mut candidates: Vec<_> = self + .memory_traces + .iter_mut() .filter(|t| t.salience > 0.3 || t.emotional_valence.abs() > 0.5) .collect(); candidates.sort_by(|a, b| { let score_a = a.salience + a.emotional_valence.abs(); let score_b = b.salience + b.emotional_valence.abs(); - score_b.partial_cmp(&score_a).unwrap_or(std::cmp::Ordering::Equal) + score_b + .partial_cmp(&score_a) + .unwrap_or(std::cmp::Ordering::Equal) }); for trace in candidates.iter_mut().take(5) { @@ -271,7 +273,8 @@ impl DreamEngine { for _ in 0..num_combinations { // Select random memories to combine let indices: Vec = (0..self.memory_traces.len()).collect(); - let selected: Vec<_> = indices.choose_multiple(&mut self.rng, 2.min(self.memory_traces.len())) + let selected: Vec<_> = indices + .choose_multiple(&mut self.rng, 2.min(self.memory_traces.len())) .cloned() .collect(); @@ -325,7 +328,9 @@ impl DreamEngine { } // Minimum distance to any existing pattern - let min_similarity = self.memory_traces.iter() + let min_similarity = self + .memory_traces + .iter() .map(|trace| self.cosine_similarity(pattern, &trace.content)) .fold(f64::MAX, f64::min); @@ -336,9 +341,8 @@ impl DreamEngine { fn calculate_coherence(&self, pattern: &[f64]) -> f64 { // Coherence based on internal consistency (low variance) let mean = pattern.iter().sum::() / pattern.len().max(1) as f64; - let variance = pattern.iter() - .map(|&x| (x - mean).powi(2)) - .sum::() / pattern.len().max(1) as f64; + let variance = + pattern.iter().map(|&x| (x - mean).powi(2)).sum::() / pattern.len().max(1) as f64; 1.0 / (1.0 + variance) } @@ -372,7 +376,8 @@ impl DreamEngine { } let avg_novelty = patterns.iter().map(|p| p.novelty).sum::() / patterns.len() as f64; - let avg_coherence = patterns.iter().map(|p| p.coherence).sum::() / patterns.len() as f64; + let avg_coherence = + patterns.iter().map(|p| p.coherence).sum::() / patterns.len() as f64; // Creativity = novelty balanced with coherence (avg_novelty * 0.7 + avg_coherence * 0.3).clamp(0.0, 1.0) @@ -414,9 +419,7 @@ impl DreamEngine { "Novel connection discovered with novelty={:.2} coherence={:.2}", pattern.novelty, pattern.coherence ), - source_connections: pattern.sources.windows(2) - .map(|w| (w[0], w[1])) - .collect(), + source_connections: pattern.sources.windows(2).map(|w| (w[0], w[1])).collect(), confidence: pattern.coherence, }); } @@ -454,16 +457,16 @@ impl DreamEngine { pub fn statistics(&self) -> DreamStatistics { let total_dreams = self.dream_history.len(); let avg_creativity = self.measure_creativity(); - let total_insights: usize = self.dream_history.iter() - .map(|d| d.insights.len()) - .sum(); + let total_insights: usize = self.dream_history.iter().map(|d| d.insights.len()).sum(); DreamStatistics { total_dreams, average_creativity: avg_creativity, total_insights, total_memories: self.memory_traces.len(), - most_replayed: self.memory_traces.iter() + most_replayed: self + .memory_traces + .iter() .max_by_key(|t| t.replay_count) .map(|t| (t.id, t.replay_count)), } diff --git a/examples/exo-ai-2025/crates/exo-exotic/src/emergence.rs b/examples/exo-ai-2025/crates/exo-exotic/src/emergence.rs index aa322eef0..4c3a714a9 100644 --- a/examples/exo-ai-2025/crates/exo-exotic/src/emergence.rs +++ b/examples/exo-ai-2025/crates/exo-exotic/src/emergence.rs @@ -18,8 +18,8 @@ //! - Synergistic information theory //! - Anderson's "More is Different" +use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use serde::{Serialize, Deserialize}; use uuid::Uuid; /// System for detecting emergent properties @@ -169,7 +169,11 @@ impl EmergenceDetector { } /// Configure coarse-graining - pub fn set_coarse_graining(&mut self, groupings: Vec>, aggregation: AggregationType) { + pub fn set_coarse_graining( + &mut self, + groupings: Vec>, + aggregation: AggregationType, + ) { self.coarse_grainer = CoarseGrainer { groupings, aggregation, @@ -190,7 +194,8 @@ impl EmergenceDetector { let normalized: Vec = state.iter().map(|x| x.abs() / sum).collect(); // Shannon entropy - -normalized.iter() + -normalized + .iter() .filter(|&&p| p > 1e-10) .map(|&p| p * p.ln()) .sum::() @@ -203,9 +208,12 @@ impl EmergenceDetector { // Order parameter: average alignment/correlation let mean: f64 = self.macro_state.iter().sum::() / self.macro_state.len() as f64; - let variance: f64 = self.macro_state.iter() + let variance: f64 = self + .macro_state + .iter() .map(|x| (x - mean).powi(2)) - .sum::() / self.macro_state.len() as f64; + .sum::() + / self.macro_state.len() as f64; // Low variance = high order 1.0 / (1.0 + variance) @@ -268,9 +276,10 @@ impl EmergenceDetector { fn record_property(&mut self, name: &str, score: f64, level: usize, description: &str) { // Check if already recorded recently - let recent = self.emergent_properties.iter().any(|p| { - p.name == name && p.level == level - }); + let recent = self + .emergent_properties + .iter() + .any(|p| p.name == name && p.level == level); if !recent { self.emergent_properties.push(EmergentProperty { @@ -336,7 +345,10 @@ impl CoarseGrainer { /// Create with specific groupings pub fn with_groupings(groupings: Vec>, aggregation: AggregationType) -> Self { - Self { groupings, aggregation } + Self { + groupings, + aggregation, + } } /// Coarsen a micro state to macro state @@ -346,9 +358,11 @@ impl CoarseGrainer { return self.default_coarsen(micro); } - self.groupings.iter() + self.groupings + .iter() .map(|group| { - let values: Vec = group.iter() + let values: Vec = group + .iter() .filter_map(|&i| micro.get(i).copied()) .collect(); self.aggregate(&values) @@ -357,7 +371,8 @@ impl CoarseGrainer { } fn default_coarsen(&self, micro: &[f64]) -> Vec { - micro.chunks(2) + micro + .chunks(2) .map(|chunk| chunk.iter().sum::() / chunk.len() as f64) .collect() } @@ -371,13 +386,15 @@ impl CoarseGrainer { AggregationType::Mean => values.iter().sum::() / values.len() as f64, AggregationType::Majority => { let positive = values.iter().filter(|&&v| v > 0.0).count(); - if positive > values.len() / 2 { 1.0 } else { -1.0 } + if positive > values.len() / 2 { + 1.0 + } else { + -1.0 + } } AggregationType::Max => values.iter().cloned().fold(f64::MIN, f64::max), AggregationType::WeightedSum(weights) => { - values.iter().zip(weights.iter()) - .map(|(v, w)| v * w) - .sum() + values.iter().zip(weights.iter()).map(|(v, w)| v * w).sum() } } } @@ -472,9 +489,8 @@ impl PhaseTransitionDetector { if self.order_parameter.len() >= self.window_size { let window = &self.order_parameter[self.order_parameter.len() - self.window_size..]; let mean: f64 = window.iter().sum::() / window.len() as f64; - let variance: f64 = window.iter() - .map(|x| (x - mean).powi(2)) - .sum::() / window.len() as f64; + let variance: f64 = + window.iter().map(|x| (x - mean).powi(2)).sum::() / window.len() as f64; self.susceptibility.push(variance); // Detect transition (spike in susceptibility) diff --git a/examples/exo-ai-2025/crates/exo-exotic/src/free_energy.rs b/examples/exo-ai-2025/crates/exo-exotic/src/free_energy.rs index 3ece96e71..3519cd5b2 100644 --- a/examples/exo-ai-2025/crates/exo-exotic/src/free_energy.rs +++ b/examples/exo-ai-2025/crates/exo-exotic/src/free_energy.rs @@ -22,8 +22,8 @@ //! - p = Prior/generative model //! - o = Observations +use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use serde::{Serialize, Deserialize}; use uuid::Uuid; /// Minimizes free energy through predictive processing @@ -174,11 +174,15 @@ impl FreeEnergyMinimizer { for i in 0..len { let e = observation.get(i).copied().unwrap_or(0.0) - - prediction.get(i).copied().unwrap_or(0.0); + - prediction.get(i).copied().unwrap_or(0.0); error[i] = e; let channel = format!("channel_{}", i); - let precision = self.precisions.get(&channel).copied().unwrap_or(default_precision); + let precision = self + .precisions + .get(&channel) + .copied() + .unwrap_or(default_precision); weighted_error[i] = e * precision; by_channel.insert(channel, e.abs()); } @@ -246,12 +250,14 @@ impl FreeEnergyMinimizer { /// Add an action to the repertoire pub fn add_action(&mut self, name: &str, expected_outcome: Vec, cost: f64) { - self.active_inference.add_action(name, expected_outcome, cost); + self.active_inference + .add_action(name, expected_outcome, cost); } /// Set precision for a channel pub fn set_precision(&mut self, channel: &str, precision: f64) { - self.precisions.insert(channel.to_string(), precision.max(0.01)); + self.precisions + .insert(channel.to_string(), precision.max(0.01)); } /// Get average free energy over time @@ -273,10 +279,10 @@ impl FreeEnergyMinimizer { return 0.0; } - let first_half: f64 = recent[..recent.len()/2].iter().sum::() - / (recent.len()/2) as f64; - let second_half: f64 = recent[recent.len()/2..].iter().sum::() - / (recent.len() - recent.len()/2) as f64; + let first_half: f64 = + recent[..recent.len() / 2].iter().sum::() / (recent.len() / 2) as f64; + let second_half: f64 = recent[recent.len() / 2..].iter().sum::() + / (recent.len() - recent.len() / 2) as f64; second_half - first_half } @@ -303,7 +309,11 @@ impl PredictiveModel { for i in 0..hidden_dims { for j in 0..obs_dims { // Simple diagonal-ish initialization - likelihood[i][j] = if i % obs_dims == j { 0.7 } else { 0.3 / (obs_dims - 1) as f64 }; + likelihood[i][j] = if i % obs_dims == j { + 0.7 + } else { + 0.3 / (obs_dims - 1) as f64 + }; } } @@ -349,7 +359,9 @@ impl PredictiveModel { /// Entropy of the posterior pub fn posterior_entropy(&self) -> f64 { - -self.posterior.iter() + -self + .posterior + .iter() .filter(|&&p| p > 1e-10) .map(|&p| p * p.ln()) .sum::() @@ -409,7 +421,9 @@ impl ActiveInference { return None; } - let min_idx = self.expected_fe.iter() + let min_idx = self + .expected_fe + .iter() .enumerate() .min_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)) .map(|(i, _)| i)?; @@ -436,7 +450,8 @@ mod tests { #[test] fn test_free_energy_minimizer_creation() { let fem = FreeEnergyMinimizer::new(0.1); - assert!(fem.compute_free_energy() >= 0.0 || fem.compute_free_energy() < 0.0); // Always defined + assert!(fem.compute_free_energy() >= 0.0 || fem.compute_free_energy() < 0.0); + // Always defined } #[test] @@ -495,14 +510,16 @@ mod tests { let mut fem = FreeEnergyMinimizer::with_dims(0.1, 4, 4); fem.set_precision("channel_0", 10.0); // High precision - fem.set_precision("channel_1", 0.1); // Low precision + fem.set_precision("channel_1", 0.1); // Low precision let observation = vec![1.0, 1.0, 0.5, 0.5]; let error = fem.observe(&observation); // Channel 0 should have higher weighted error - assert!(error.weighted_error[0].abs() > error.weighted_error[1].abs() - || error.error[0].abs() * 10.0 > error.error[1].abs() * 0.1); + assert!( + error.weighted_error[0].abs() > error.weighted_error[1].abs() + || error.error[0].abs() * 10.0 > error.error[1].abs() * 0.1 + ); } #[test] diff --git a/examples/exo-ai-2025/crates/exo-exotic/src/lib.rs b/examples/exo-ai-2025/crates/exo-exotic/src/lib.rs index 05ea0a275..27b893ac1 100644 --- a/examples/exo-ai-2025/crates/exo-exotic/src/lib.rs +++ b/examples/exo-ai-2025/crates/exo-exotic/src/lib.rs @@ -23,28 +23,28 @@ //! - Cache-friendly memory layouts //! - Early termination heuristics -pub mod strange_loops; +pub mod black_holes; +pub mod collective; pub mod dreams; +pub mod emergence; pub mod free_energy; pub mod morphogenesis; -pub mod collective; -pub mod temporal_qualia; pub mod multiple_selves; +pub mod strange_loops; +pub mod temporal_qualia; pub mod thermodynamics; -pub mod emergence; -pub mod black_holes; // Re-exports for convenience -pub use strange_loops::{StrangeLoop, SelfReference, TangledHierarchy}; -pub use dreams::{DreamEngine, DreamState, DreamReport}; -pub use free_energy::{FreeEnergyMinimizer, PredictiveModel, ActiveInference}; -pub use morphogenesis::{MorphogeneticField, TuringPattern, CognitiveEmbryogenesis}; -pub use collective::{CollectiveConsciousness, HiveMind, DistributedPhi}; -pub use temporal_qualia::{TemporalQualia, SubjectiveTime, TimeCrystal}; -pub use multiple_selves::{MultipleSelvesSystem, SubPersonality, SelfCoherence}; -pub use thermodynamics::{CognitiveThermodynamics, ThoughtEntropy, MaxwellDemon}; -pub use emergence::{EmergenceDetector, CausalEmergence, PhaseTransition}; -pub use black_holes::{CognitiveBlackHole, AttractorState, EscapeDynamics}; +pub use black_holes::{AttractorState, CognitiveBlackHole, EscapeDynamics}; +pub use collective::{CollectiveConsciousness, DistributedPhi, HiveMind}; +pub use dreams::{DreamEngine, DreamReport, DreamState}; +pub use emergence::{CausalEmergence, EmergenceDetector, PhaseTransition}; +pub use free_energy::{ActiveInference, FreeEnergyMinimizer, PredictiveModel}; +pub use morphogenesis::{CognitiveEmbryogenesis, MorphogeneticField, TuringPattern}; +pub use multiple_selves::{MultipleSelvesSystem, SelfCoherence, SubPersonality}; +pub use strange_loops::{SelfReference, StrangeLoop, TangledHierarchy}; +pub use temporal_qualia::{SubjectiveTime, TemporalQualia, TimeCrystal}; +pub use thermodynamics::{CognitiveThermodynamics, MaxwellDemon, ThoughtEntropy}; /// Unified experiment runner for all exotic modules pub struct ExoticExperiments { diff --git a/examples/exo-ai-2025/crates/exo-exotic/src/morphogenesis.rs b/examples/exo-ai-2025/crates/exo-exotic/src/morphogenesis.rs index 60e10fe21..723e2d06d 100644 --- a/examples/exo-ai-2025/crates/exo-exotic/src/morphogenesis.rs +++ b/examples/exo-ai-2025/crates/exo-exotic/src/morphogenesis.rs @@ -17,8 +17,8 @@ //! ∂u/∂t = Du∇²u + f(u,v) //! ∂v/∂t = Dv∇²v + g(u,v) +use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use serde::{Serialize, Deserialize}; use uuid::Uuid; /// A field where morphogenetic patterns emerge @@ -173,7 +173,13 @@ impl MorphogeneticField { } /// Create with specific parameters - pub fn with_params(width: usize, height: usize, da: f64, db: f64, params: ReactionParams) -> Self { + pub fn with_params( + width: usize, + height: usize, + da: f64, + db: f64, + params: ReactionParams, + ) -> Self { let mut field = Self::new(width, height); field.da = da; field.db = db; @@ -194,7 +200,9 @@ impl MorphogeneticField { for y in 0..self.height { for x in 0..self.width { // Simple LCG random - state = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407); + state = state + .wrapping_mul(6364136223846793005) + .wrapping_add(1442695040888963407); let r = (state as f64) / (u64::MAX as f64); self.inhibitor[y][x] += (r - 0.5) * magnitude; } @@ -207,11 +215,11 @@ impl MorphogeneticField { let mut gradient_sum = 0.0; let mut count = 0; - for y in 1..self.height-1 { - for x in 1..self.width-1 { - let dx = self.activator[y][x+1] - self.activator[y][x-1]; - let dy = self.activator[y+1][x] - self.activator[y-1][x]; - gradient_sum += (dx*dx + dy*dy).sqrt(); + for y in 1..self.height - 1 { + for x in 1..self.width - 1 { + let dx = self.activator[y][x + 1] - self.activator[y][x - 1]; + let dy = self.activator[y + 1][x] - self.activator[y - 1][x]; + gradient_sum += (dx * dx + dy * dy).sqrt(); count += 1; } } @@ -228,34 +236,32 @@ impl MorphogeneticField { let mut new_a = self.activator.clone(); let mut new_b = self.inhibitor.clone(); - for y in 1..self.height-1 { - for x in 1..self.width-1 { + for y in 1..self.height - 1 { + for x in 1..self.width - 1 { let a = self.activator[y][x]; let b = self.inhibitor[y][x]; // Laplacian (diffusion) - let lap_a = self.activator[y-1][x] + self.activator[y+1][x] - + self.activator[y][x-1] + self.activator[y][x+1] - - 4.0 * a; + let lap_a = self.activator[y - 1][x] + + self.activator[y + 1][x] + + self.activator[y][x - 1] + + self.activator[y][x + 1] + - 4.0 * a; - let lap_b = self.inhibitor[y-1][x] + self.inhibitor[y+1][x] - + self.inhibitor[y][x-1] + self.inhibitor[y][x+1] - - 4.0 * b; + let lap_b = self.inhibitor[y - 1][x] + + self.inhibitor[y + 1][x] + + self.inhibitor[y][x - 1] + + self.inhibitor[y][x + 1] + - 4.0 * b; // Gray-Scott reaction let reaction = a * b * b; - new_a[y][x] = a + self.dt * ( - self.da * lap_a - - reaction - + self.params.f * (1.0 - a) - ); + new_a[y][x] = + a + self.dt * (self.da * lap_a - reaction + self.params.f * (1.0 - a)); - new_b[y][x] = b + self.dt * ( - self.db * lap_b - + reaction - - (self.params.f + self.params.k) * b - ); + new_b[y][x] = b + self.dt + * (self.db * lap_b + reaction - (self.params.f + self.params.k) * b); // Clamp values new_a[y][x] = new_a[y][x].clamp(0.0, 1.0); @@ -293,11 +299,11 @@ impl MorphogeneticField { let mut best_lag = 1; let mut min_corr = f64::MAX; - for lag in 1..self.width/4 { + for lag in 1..self.width / 4 { let mut corr = 0.0; let mut count = 0; - for i in 0..self.width-lag { + for i in 0..self.width - lag { corr += slice[i] * slice[i + lag]; count += 1; } @@ -321,7 +327,7 @@ impl MorphogeneticField { // Check left-right symmetry for y in 0..self.height { - for x in 0..self.width/2 { + for x in 0..self.width / 2 { let mirror_x = self.width - 1 - x; let diff = (self.activator[y][x] - self.activator[y][mirror_x]).abs(); diff_sum += diff; @@ -405,9 +411,7 @@ impl CognitiveEmbryogenesis { self.differentiate(); DevelopmentStage::Mature } - DevelopmentStage::Mature => { - DevelopmentStage::Mature - } + DevelopmentStage::Mature => DevelopmentStage::Mature, }; self.history.push(DevelopmentEvent { @@ -428,7 +432,8 @@ impl CognitiveEmbryogenesis { let ap_gradient: Vec = (0..gradient_length) .map(|i| (i as f64 / gradient_length as f64)) .collect(); - self.gradients.insert("anterior_posterior".to_string(), ap_gradient); + self.gradients + .insert("anterior_posterior".to_string(), ap_gradient); // Dorsal-ventral gradient let dv_gradient: Vec = (0..gradient_length) @@ -437,7 +442,8 @@ impl CognitiveEmbryogenesis { (x * std::f64::consts::PI).sin() }) .collect(); - self.gradients.insert("dorsal_ventral".to_string(), dv_gradient); + self.gradients + .insert("dorsal_ventral".to_string(), dv_gradient); } fn divide_structures(&mut self) { @@ -457,11 +463,7 @@ impl CognitiveEmbryogenesis { self.structures.push(CognitiveStructure { id: Uuid::new_v4(), structure_type: StructureType::ProcessingNode, - position: ( - 0.5 + 0.3 * angle.cos(), - 0.5 + 0.3 * angle.sin(), - 0.5, - ), + position: (0.5 + 0.3 * angle.cos(), 0.5 + 0.3 * angle.sin(), 0.5), size: initial.size / 4.0, connectivity: Vec::new(), specialization: 0.0, @@ -474,7 +476,7 @@ impl CognitiveEmbryogenesis { let structure_ids: Vec = self.structures.iter().map(|s| s.id).collect(); for i in 0..self.structures.len() { - for j in i+1..self.structures.len() { + for j in i + 1..self.structures.len() { let dist = self.distance(i, j); if dist < 0.5 { self.structures[i].connectivity.push(structure_ids[j]); @@ -487,7 +489,7 @@ impl CognitiveEmbryogenesis { fn distance(&self, i: usize, j: usize) -> f64 { let (x1, y1, z1) = self.structures[i].position; let (x2, y2, z2) = self.structures[j].position; - ((x2-x1).powi(2) + (y2-y1).powi(2) + (z2-z1).powi(2)).sqrt() + ((x2 - x1).powi(2) + (y2 - y1).powi(2) + (z2 - z1).powi(2)).sqrt() } fn differentiate(&mut self) { @@ -584,8 +586,14 @@ mod tests { let pattern_type = field.detect_pattern_type(); // Should detect some pattern type - assert!(matches!(pattern_type, PatternType::Spots | PatternType::Stripes - | PatternType::Labyrinth | PatternType::Hexagonal | PatternType::Mixed)); + assert!(matches!( + pattern_type, + PatternType::Spots + | PatternType::Stripes + | PatternType::Labyrinth + | PatternType::Hexagonal + | PatternType::Mixed + )); } #[test] @@ -605,12 +613,13 @@ mod tests { embryo.full_development(); // Should have different structure types - let types: Vec<_> = embryo.structures().iter() + let types: Vec<_> = embryo + .structures() + .iter() .map(|s| &s.structure_type) .collect(); - assert!(embryo.structures().iter() - .all(|s| s.specialization > 0.0)); + assert!(embryo.structures().iter().all(|s| s.specialization > 0.0)); } #[test] diff --git a/examples/exo-ai-2025/crates/exo-exotic/src/multiple_selves.rs b/examples/exo-ai-2025/crates/exo-exotic/src/multiple_selves.rs index 76458ff91..1f456400a 100644 --- a/examples/exo-ai-2025/crates/exo-exotic/src/multiple_selves.rs +++ b/examples/exo-ai-2025/crates/exo-exotic/src/multiple_selves.rs @@ -18,8 +18,8 @@ //! - Marvin Minsky's "Society of Mind" //! - Global Workspace Theory +use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use serde::{Serialize, Deserialize}; use uuid::Uuid; /// System managing multiple sub-personalities @@ -76,9 +76,9 @@ pub struct Goal { /// Emotional baseline of a sub-personality #[derive(Debug, Clone, Serialize, Deserialize)] pub struct EmotionalTone { - pub valence: f64, // -1 (negative) to 1 (positive) - pub arousal: f64, // 0 (calm) to 1 (excited) - pub dominance: f64, // 0 (submissive) to 1 (dominant) + pub valence: f64, // -1 (negative) to 1 (positive) + pub arousal: f64, // 0 (calm) to 1 (excited) + pub dominance: f64, // 0 (submissive) to 1 (dominant) } /// Relationship between sub-personalities @@ -135,10 +135,10 @@ pub struct Decision { #[derive(Debug, Clone)] pub enum DecisionOutcome { - Unanimous(Uuid), // All agreed, winner's id - Majority(Uuid, f64), // Majority, winner and margin - Executive(Uuid), // Executive decided - Conflict, // Unresolved conflict + Unanimous(Uuid), // All agreed, winner's id + Majority(Uuid, f64), // Majority, winner and margin + Executive(Uuid), // Executive decided + Conflict, // Unresolved conflict } /// Measure of self-coherence @@ -238,7 +238,7 @@ impl MultipleSelvesSystem { let mut count = 0; for i in 0..self.selves.len() { - for j in i+1..self.selves.len() { + for j in i + 1..self.selves.len() { let sim = self.belief_similarity(&self.selves[i], &self.selves[j]); total_similarity += sim; count += 1; @@ -294,8 +294,12 @@ impl MultipleSelvesSystem { for self_entity in &self.selves { for (_, rel) in &self_entity.relationships { total_relationships += 1; - if matches!(rel.relationship_type, - RelationshipType::Ally | RelationshipType::Protector | RelationshipType::Neutral) { + if matches!( + rel.relationship_type, + RelationshipType::Ally + | RelationshipType::Protector + | RelationshipType::Neutral + ) { positive_relationships += 1; } } @@ -329,7 +333,9 @@ impl MultipleSelvesSystem { } fn update_dominant(&mut self) { - self.dominant = self.selves.iter() + self.dominant = self + .selves + .iter() .max_by(|a, b| a.activation.partial_cmp(&b.activation).unwrap()) .map(|s| s.id); } @@ -337,19 +343,25 @@ impl MultipleSelvesSystem { /// Create conflict between selves pub fn create_conflict(&mut self, self1: Uuid, self2: Uuid) { if let Some(s1) = self.selves.iter_mut().find(|s| s.id == self1) { - s1.relationships.insert(self2, Relationship { - other_id: self2, - relationship_type: RelationshipType::Rival, - strength: 0.7, - }); + s1.relationships.insert( + self2, + Relationship { + other_id: self2, + relationship_type: RelationshipType::Rival, + strength: 0.7, + }, + ); } if let Some(s2) = self.selves.iter_mut().find(|s| s.id == self2) { - s2.relationships.insert(self1, Relationship { - other_id: self1, - relationship_type: RelationshipType::Rival, - strength: 0.7, - }); + s2.relationships.insert( + self1, + Relationship { + other_id: self1, + relationship_type: RelationshipType::Rival, + strength: 0.7, + }, + ); } self.integration_history.push(IntegrationEvent { @@ -421,7 +433,11 @@ impl MultipleSelvesSystem { }; // Remove old selves (handle indices carefully) - let (first, second) = if s1_idx > s2_idx { (s1_idx, s2_idx) } else { (s2_idx, s1_idx) }; + let (first, second) = if s1_idx > s2_idx { + (s1_idx, s2_idx) + } else { + (s2_idx, s1_idx) + }; self.selves.remove(first); self.selves.remove(second); @@ -442,7 +458,8 @@ impl MultipleSelvesSystem { /// Get dominant self pub fn get_dominant(&self) -> Option<&SubPersonality> { - self.dominant.and_then(|id| self.selves.iter().find(|s| s.id == id)) + self.dominant + .and_then(|id| self.selves.iter().find(|s| s.id == id)) } /// Get all selves @@ -511,13 +528,12 @@ impl ExecutiveFunction { } ResolutionStyle::TurnTaking => { // Alternate based on history - let last_winner = self.decisions.last() - .and_then(|d| match &d.outcome { - DecisionOutcome::Unanimous(id) | - DecisionOutcome::Majority(id, _) | - DecisionOutcome::Executive(id) => Some(*id), - _ => None, - }); + let last_winner = self.decisions.last().and_then(|d| match &d.outcome { + DecisionOutcome::Unanimous(id) + | DecisionOutcome::Majority(id, _) + | DecisionOutcome::Executive(id) => Some(*id), + _ => None, + }); let winner = match last_winner { Some(w) if w == id1 => id2, @@ -529,9 +545,9 @@ impl ExecutiveFunction { }; let winner = match &outcome { - DecisionOutcome::Unanimous(id) | - DecisionOutcome::Majority(id, _) | - DecisionOutcome::Executive(id) => Some(*id), + DecisionOutcome::Unanimous(id) + | DecisionOutcome::Majority(id, _) + | DecisionOutcome::Executive(id) => Some(*id), DecisionOutcome::Conflict => None, }; @@ -601,17 +617,23 @@ mod tests { fn test_add_selves() { let mut system = MultipleSelvesSystem::new(); - let id1 = system.add_self("Protector", EmotionalTone { - valence: 0.3, - arousal: 0.7, - dominance: 0.8, - }); + let id1 = system.add_self( + "Protector", + EmotionalTone { + valence: 0.3, + arousal: 0.7, + dominance: 0.8, + }, + ); - let id2 = system.add_self("Inner Child", EmotionalTone { - valence: 0.8, - arousal: 0.6, - dominance: 0.3, - }); + let id2 = system.add_self( + "Inner Child", + EmotionalTone { + valence: 0.8, + arousal: 0.6, + dominance: 0.3, + }, + ); assert_eq!(system.self_count(), 2); assert_ne!(id1, id2); @@ -622,11 +644,14 @@ mod tests { let mut system = MultipleSelvesSystem::new(); // Single self = high coherence - system.add_self("Core", EmotionalTone { - valence: 0.5, - arousal: 0.5, - dominance: 0.5, - }); + system.add_self( + "Core", + EmotionalTone { + valence: 0.5, + arousal: 0.5, + dominance: 0.5, + }, + ); let coherence = system.measure_coherence(); assert!(coherence >= 0.0 && coherence <= 1.0); @@ -636,11 +661,14 @@ mod tests { fn test_activation() { let mut system = MultipleSelvesSystem::new(); - let id = system.add_self("Test", EmotionalTone { - valence: 0.5, - arousal: 0.5, - dominance: 0.5, - }); + let id = system.add_self( + "Test", + EmotionalTone { + valence: 0.5, + arousal: 0.5, + dominance: 0.5, + }, + ); system.activate(id, 0.9); @@ -653,17 +681,23 @@ mod tests { fn test_conflict_and_resolution() { let mut system = MultipleSelvesSystem::new(); - let id1 = system.add_self("Self1", EmotionalTone { - valence: 0.8, - arousal: 0.5, - dominance: 0.7, - }); + let id1 = system.add_self( + "Self1", + EmotionalTone { + valence: 0.8, + arousal: 0.5, + dominance: 0.7, + }, + ); - let id2 = system.add_self("Self2", EmotionalTone { - valence: 0.2, - arousal: 0.5, - dominance: 0.3, - }); + let id2 = system.add_self( + "Self2", + EmotionalTone { + valence: 0.2, + arousal: 0.5, + dominance: 0.3, + }, + ); system.create_conflict(id1, id2); let initial_coherence = system.measure_coherence(); @@ -679,17 +713,23 @@ mod tests { fn test_merge() { let mut system = MultipleSelvesSystem::new(); - let id1 = system.add_self("Part1", EmotionalTone { - valence: 0.6, - arousal: 0.4, - dominance: 0.5, - }); + let id1 = system.add_self( + "Part1", + EmotionalTone { + valence: 0.6, + arousal: 0.4, + dominance: 0.5, + }, + ); - let id2 = system.add_self("Part2", EmotionalTone { - valence: 0.4, - arousal: 0.6, - dominance: 0.5, - }); + let id2 = system.add_self( + "Part2", + EmotionalTone { + valence: 0.4, + arousal: 0.6, + dominance: 0.5, + }, + ); assert_eq!(system.self_count(), 2); @@ -708,7 +748,11 @@ mod tests { name: "Strong".to_string(), beliefs: Vec::new(), goals: Vec::new(), - emotional_tone: EmotionalTone { valence: 0.5, arousal: 0.5, dominance: 0.9 }, + emotional_tone: EmotionalTone { + valence: 0.5, + arousal: 0.5, + dominance: 0.9, + }, activation: 0.8, age: 10, relationships: HashMap::new(), @@ -718,7 +762,11 @@ mod tests { name: "Weak".to_string(), beliefs: Vec::new(), goals: Vec::new(), - emotional_tone: EmotionalTone { valence: 0.5, arousal: 0.5, dominance: 0.1 }, + emotional_tone: EmotionalTone { + valence: 0.5, + arousal: 0.5, + dominance: 0.1, + }, activation: 0.2, age: 5, relationships: HashMap::new(), diff --git a/examples/exo-ai-2025/crates/exo-exotic/src/strange_loops.rs b/examples/exo-ai-2025/crates/exo-exotic/src/strange_loops.rs index 77168a430..628ae7420 100644 --- a/examples/exo-ai-2025/crates/exo-exotic/src/strange_loops.rs +++ b/examples/exo-ai-2025/crates/exo-exotic/src/strange_loops.rs @@ -17,9 +17,9 @@ //! - Fixed-point combinators (Y-combinator style) //! - Quine-like self-replication patterns +use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::sync::atomic::{AtomicUsize, Ordering}; -use serde::{Serialize, Deserialize}; use uuid::Uuid; /// A strange loop implementing self-referential cognition @@ -113,9 +113,9 @@ pub enum ElementType { Perception, Concept, Belief, - MetaBelief, // Belief about beliefs - MetaMetaBelief, // Belief about beliefs about beliefs - SelfConcept, // Concept about self + MetaBelief, // Belief about beliefs + MetaMetaBelief, // Belief about beliefs about beliefs + SelfConcept, // Concept about self } impl StrangeLoop { @@ -187,11 +187,10 @@ impl StrangeLoop { let meta_thought = MetaThought { original_thought: thought.to_string(), - reasoning_about_thought: format!( - "I am thinking about the thought: '{}'", thought - ), + reasoning_about_thought: format!("I am thinking about the thought: '{}'", thought), reasoning_about_reasoning: format!( - "I notice that I am analyzing my own thought process at level {}", level + "I notice that I am analyzing my own thought process at level {}", + level ), infinite_regress_detected: level >= self.max_depth, godel_reference: self.compute_godel_reference(thought), @@ -205,8 +204,8 @@ impl StrangeLoop { fn compute_godel_reference(&self, s: &str) -> u64 { // Simplified Gödel numbering using prime factorization concept let primes: [u64; 26] = [ - 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, - 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101 + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, + 89, 97, 101, ]; let mut result: u64 = 1; @@ -221,7 +220,9 @@ impl StrangeLoop { fn update_godel_number(&mut self) { // Update Gödel number based on current state let depth = self.measure_depth() as u64; - self.godel_number = self.godel_number.wrapping_mul(2_u64.wrapping_pow(depth as u32 + 1)); + self.godel_number = self + .godel_number + .wrapping_mul(2_u64.wrapping_pow(depth as u32 + 1)); } /// Create a self-reference to a specific aspect @@ -267,10 +268,8 @@ impl StrangeLoop { }); } - self.visited_states.insert( - current_state, - self.current_level.load(Ordering::SeqCst) - ); + self.visited_states + .insert(current_state, self.current_level.load(Ordering::SeqCst)); None } @@ -352,7 +351,7 @@ impl TangledHierarchy { current: usize, target: usize, visited: &mut [bool], - path: &mut Vec + path: &mut Vec, ) { path.push(current); @@ -480,7 +479,7 @@ mod tests { let confidences = sl.confidence_by_level(); // Each level should have lower confidence than the previous for i in 1..confidences.len() { - assert!(confidences[i].1 <= confidences[i-1].1); + assert!(confidences[i].1 <= confidences[i - 1].1); } } diff --git a/examples/exo-ai-2025/crates/exo-exotic/src/temporal_qualia.rs b/examples/exo-ai-2025/crates/exo-exotic/src/temporal_qualia.rs index 6d10bbdf5..4d3ae98b0 100644 --- a/examples/exo-ai-2025/crates/exo-exotic/src/temporal_qualia.rs +++ b/examples/exo-ai-2025/crates/exo-exotic/src/temporal_qualia.rs @@ -17,8 +17,8 @@ //! - Internal clock models (scalar timing theory) //! - Attention and time perception studies +use serde::{Deserialize, Serialize}; use std::collections::VecDeque; -use serde::{Serialize, Deserialize}; use uuid::Uuid; /// System for experiencing and measuring subjective time @@ -203,7 +203,8 @@ impl TemporalQualia { /// Get time crystal contribution at current time pub fn crystal_contribution(&self, time: f64) -> f64 { - self.time_crystals.iter() + self.time_crystals + .iter() .map(|crystal| { let phase = (time / crystal.period + crystal.phase) * std::f64::consts::TAU; crystal.amplitude * phase.sin() * crystal.stability @@ -257,9 +258,11 @@ impl TemporalQualia { let avg_novelty = if self.experience_buffer.is_empty() { 0.0 } else { - self.experience_buffer.iter() + self.experience_buffer + .iter() .map(|e| e.novelty) - .sum::() / self.experience_buffer.len() as f64 + .sum::() + / self.experience_buffer.len() as f64 }; TemporalStatistics { diff --git a/examples/exo-ai-2025/crates/exo-exotic/src/thermodynamics.rs b/examples/exo-ai-2025/crates/exo-exotic/src/thermodynamics.rs index 31016b429..3f12731c7 100644 --- a/examples/exo-ai-2025/crates/exo-exotic/src/thermodynamics.rs +++ b/examples/exo-ai-2025/crates/exo-exotic/src/thermodynamics.rs @@ -19,8 +19,8 @@ //! - Szilard Engine - Information thermodynamics //! - Jarzynski Equality - Non-equilibrium thermodynamics +use serde::{Deserialize, Serialize}; use std::collections::{HashMap, VecDeque}; -use serde::{Serialize, Deserialize}; use uuid::Uuid; /// Cognitive thermodynamics system @@ -222,7 +222,12 @@ impl CognitiveThermodynamics { } /// Perform reversible computation - pub fn reversible_compute(&mut self, input: T, forward: impl Fn(T) -> T, _backward: impl Fn(T) -> T) -> T { + pub fn reversible_compute( + &mut self, + input: T, + forward: impl Fn(T) -> T, + _backward: impl Fn(T) -> T, + ) -> T { // Reversible computation has no erasure cost // Only the logical transformation happens @@ -558,8 +563,8 @@ mod tests { let input = 5; let output = thermo.reversible_compute( input, - |x| x * 2, // forward - |x| x / 2, // backward + |x| x * 2, // forward + |x| x / 2, // backward ); assert_eq!(output, 10); diff --git a/examples/exo-ai-2025/crates/exo-federation/src/crdt.rs b/examples/exo-ai-2025/crates/exo-federation/src/crdt.rs index 88c28c8be..caa7a9ec1 100644 --- a/examples/exo-ai-2025/crates/exo-federation/src/crdt.rs +++ b/examples/exo-ai-2025/crates/exo-federation/src/crdt.rs @@ -5,9 +5,9 @@ //! - LWW-Register (Last-Writer-Wins Register) //! - Reconciliation algorithms -use std::collections::{HashMap, HashSet}; +use crate::{FederationError, Result}; use serde::{Deserialize, Serialize}; -use crate::{Result, FederationError}; +use std::collections::{HashMap, HashSet}; /// Grow-only Set CRDT /// @@ -199,9 +199,7 @@ pub struct FederatedResponse { /// final_results.sort(by=score, descending=True) /// RETURN final_results /// ``` -pub fn reconcile_crdt( - responses: Vec>, -) -> Result> +pub fn reconcile_crdt(responses: Vec>) -> Result> where T: Clone + Eq + std::hash::Hash, { @@ -293,13 +291,13 @@ mod tests { map1.set("key2", 200, 1); let mut map2 = LWWMap::new(); - map2.set("key2", 250, 2); // Newer timestamp + map2.set("key2", 250, 2); // Newer timestamp map2.set("key3", 300, 1); map1.merge(&map2); assert_eq!(*map1.get(&"key1").unwrap(), 100); - assert_eq!(*map1.get(&"key2").unwrap(), 250); // Updated + assert_eq!(*map1.get(&"key2").unwrap(), 250); // Updated assert_eq!(*map1.get(&"key3").unwrap(), 300); } @@ -307,16 +305,13 @@ mod tests { fn test_reconcile_crdt() { let response1 = FederatedResponse { results: vec![1, 2, 3], - rankings: vec![ - ("1".to_string(), 0.9, 100), - ("2".to_string(), 0.8, 100), - ], + rankings: vec![("1".to_string(), 0.9, 100), ("2".to_string(), 0.8, 100)], }; let response2 = FederatedResponse { results: vec![2, 3, 4], rankings: vec![ - ("2".to_string(), 0.85, 101), // Newer + ("2".to_string(), 0.85, 101), // Newer ("3".to_string(), 0.7, 100), ], }; diff --git a/examples/exo-ai-2025/crates/exo-federation/src/crypto.rs b/examples/exo-ai-2025/crates/exo-federation/src/crypto.rs index 304aaf0d2..fe8487ff6 100644 --- a/examples/exo-ai-2025/crates/exo-federation/src/crypto.rs +++ b/examples/exo-ai-2025/crates/exo-federation/src/crypto.rs @@ -15,13 +15,13 @@ //! //! See /docs/SECURITY.md for comprehensive threat model and security architecture. +use crate::{FederationError, Result}; use serde::{Deserialize, Serialize}; -use crate::{Result, FederationError}; use zeroize::{Zeroize, ZeroizeOnDrop}; // Re-export for convenience pub use pqcrypto_kyber::kyber1024; -use pqcrypto_traits::kem::{PublicKey, SecretKey, SharedSecret as PqSharedSecret, Ciphertext}; +use pqcrypto_traits::kem::{Ciphertext, PublicKey, SecretKey, SharedSecret as PqSharedSecret}; /// Post-quantum cryptographic keypair /// @@ -109,23 +109,23 @@ impl PostQuantumKeypair { pub fn encapsulate(public_key: &[u8]) -> Result<(SharedSecret, Vec)> { // Validate public key size (Kyber1024 = 1568 bytes) if public_key.len() != 1568 { - return Err(FederationError::CryptoError( - format!("Invalid public key size: expected 1568 bytes, got {}", public_key.len()) - )); + return Err(FederationError::CryptoError(format!( + "Invalid public key size: expected 1568 bytes, got {}", + public_key.len() + ))); } // Parse public key - let pk = kyber1024::PublicKey::from_bytes(public_key) - .map_err(|e| FederationError::CryptoError( - format!("Failed to parse Kyber public key: {:?}", e) - ))?; + let pk = kyber1024::PublicKey::from_bytes(public_key).map_err(|e| { + FederationError::CryptoError(format!("Failed to parse Kyber public key: {:?}", e)) + })?; // Perform KEM encapsulation let (shared_secret, ciphertext) = kyber1024::encapsulate(&pk); Ok(( SharedSecret(SecretBytes(shared_secret.as_bytes().to_vec())), - ciphertext.as_bytes().to_vec() + ciphertext.as_bytes().to_vec(), )) } @@ -152,22 +152,21 @@ impl PostQuantumKeypair { pub fn decapsulate(&self, ciphertext: &[u8]) -> Result { // Validate ciphertext size if ciphertext.len() != 1568 { - return Err(FederationError::CryptoError( - format!("Invalid ciphertext size: expected 1568 bytes, got {}", ciphertext.len()) - )); + return Err(FederationError::CryptoError(format!( + "Invalid ciphertext size: expected 1568 bytes, got {}", + ciphertext.len() + ))); } // Parse secret key - let sk = kyber1024::SecretKey::from_bytes(&self.secret.0) - .map_err(|e| FederationError::CryptoError( - format!("Failed to parse secret key: {:?}", e) - ))?; + let sk = kyber1024::SecretKey::from_bytes(&self.secret.0).map_err(|e| { + FederationError::CryptoError(format!("Failed to parse secret key: {:?}", e)) + })?; // Parse ciphertext - let ct = kyber1024::Ciphertext::from_bytes(ciphertext) - .map_err(|e| FederationError::CryptoError( - format!("Failed to parse Kyber ciphertext: {:?}", e) - ))?; + let ct = kyber1024::Ciphertext::from_bytes(ciphertext).map_err(|e| { + FederationError::CryptoError(format!("Failed to parse Kyber ciphertext: {:?}", e)) + })?; // Perform KEM decapsulation let shared_secret = kyber1024::decapsulate(&ct, &sk); @@ -228,21 +227,19 @@ impl SharedSecret { // HKDF-Extract: PRK = HMAC-SHA256(salt=zeros, ikm=shared_secret) let salt = [0u8; 32]; // Zero salt is acceptable for Kyber output - let mut extract_hmac = HmacSha256::new_from_slice(&salt) - .expect("HMAC-SHA256 accepts any key size"); - extract_hmac.update(&self.0.0); + let mut extract_hmac = + HmacSha256::new_from_slice(&salt).expect("HMAC-SHA256 accepts any key size"); + extract_hmac.update(&self.0 .0); let prk = extract_hmac.finalize().into_bytes(); // HKDF-Expand for encryption key - let mut enc_hmac = HmacSha256::new_from_slice(&prk) - .expect("PRK is valid HMAC key"); + let mut enc_hmac = HmacSha256::new_from_slice(&prk).expect("PRK is valid HMAC key"); enc_hmac.update(b"encryption"); enc_hmac.update(&[1u8]); // Counter = 1 let encrypt_key = enc_hmac.finalize().into_bytes().to_vec(); // HKDF-Expand for MAC key - let mut mac_hmac = HmacSha256::new_from_slice(&prk) - .expect("PRK is valid HMAC key"); + let mut mac_hmac = HmacSha256::new_from_slice(&prk).expect("PRK is valid HMAC key"); mac_hmac.update(b"mac"); mac_hmac.update(&[1u8]); // Counter = 1 let mac_key = mac_hmac.finalize().into_bytes().to_vec(); @@ -289,7 +286,7 @@ impl Clone for EncryptedChannel { encrypt_key: self.encrypt_key.clone(), mac_key: self.mac_key.clone(), counter: std::sync::atomic::AtomicU32::new( - self.counter.load(std::sync::atomic::Ordering::SeqCst) + self.counter.load(std::sync::atomic::Ordering::SeqCst), ), } } @@ -343,22 +340,26 @@ impl EncryptedChannel { }; // Create cipher instance - let key_array: [u8; 32] = self.encrypt_key.as_slice().try_into() + let key_array: [u8; 32] = self + .encrypt_key + .as_slice() + .try_into() .map_err(|_| FederationError::CryptoError("Invalid key size".into()))?; let cipher = ChaCha20Poly1305::new(&key_array.into()); // Generate unique nonce: [random: 8 bytes][counter: 4 bytes] let mut nonce_bytes = [0u8; 12]; nonce_bytes[0..8].copy_from_slice(&rand::random::<[u8; 8]>()); - let counter = self.counter.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + let counter = self + .counter + .fetch_add(1, std::sync::atomic::Ordering::SeqCst); nonce_bytes[8..12].copy_from_slice(&counter.to_le_bytes()); let nonce = Nonce::from_slice(&nonce_bytes); // Encrypt with AEAD - let ciphertext = cipher.encrypt(nonce, plaintext) - .map_err(|e| FederationError::CryptoError( - format!("ChaCha20-Poly1305 encryption failed: {}", e) - ))?; + let ciphertext = cipher.encrypt(nonce, plaintext).map_err(|e| { + FederationError::CryptoError(format!("ChaCha20-Poly1305 encryption failed: {}", e)) + })?; // Prepend nonce to ciphertext (needed for decryption) let mut result = nonce_bytes.to_vec(); @@ -396,9 +397,10 @@ impl EncryptedChannel { // Validate minimum size: nonce(12) + tag(16) = 28 bytes if ciphertext.len() < 28 { - return Err(FederationError::CryptoError( - format!("Ciphertext too short: {} bytes (minimum 28)", ciphertext.len()) - )); + return Err(FederationError::CryptoError(format!( + "Ciphertext too short: {} bytes (minimum 28)", + ciphertext.len() + ))); } // Extract nonce and ciphertext @@ -406,15 +408,20 @@ impl EncryptedChannel { let nonce = Nonce::from_slice(nonce_bytes); // Create cipher instance - let key_array: [u8; 32] = self.encrypt_key.as_slice().try_into() + let key_array: [u8; 32] = self + .encrypt_key + .as_slice() + .try_into() .map_err(|_| FederationError::CryptoError("Invalid key size".into()))?; let cipher = ChaCha20Poly1305::new(&key_array.into()); // Decrypt with AEAD (authentication happens here) - let plaintext = cipher.decrypt(nonce, ct) - .map_err(|e| FederationError::CryptoError( - format!("ChaCha20-Poly1305 decryption failed (tampering?): {}", e) - ))?; + let plaintext = cipher.decrypt(nonce, ct).map_err(|e| { + FederationError::CryptoError(format!( + "ChaCha20-Poly1305 decryption failed (tampering?): {}", + e + )) + })?; Ok(plaintext) } @@ -563,7 +570,10 @@ mod tests { // Decryption should fail due to authentication let result = channel.decrypt(&ciphertext); - assert!(result.is_err(), "Tampered ciphertext should fail authentication"); + assert!( + result.is_err(), + "Tampered ciphertext should fail authentication" + ); } #[test] diff --git a/examples/exo-ai-2025/crates/exo-hypergraph/src/hyperedge.rs b/examples/exo-ai-2025/crates/exo-hypergraph/src/hyperedge.rs index d33a72716..d46943987 100644 --- a/examples/exo-ai-2025/crates/exo-hypergraph/src/hyperedge.rs +++ b/examples/exo-ai-2025/crates/exo-hypergraph/src/hyperedge.rs @@ -146,7 +146,9 @@ impl HyperedgeIndex { } // Remove from relation index - if let Some(mut entry) = self.relation_index.get_mut(&hyperedge.relation.relation_type) + if let Some(mut entry) = self + .relation_index + .get_mut(&hyperedge.relation.relation_type) { entry.retain(|he_id| he_id != id); } diff --git a/examples/exo-ai-2025/crates/exo-hypergraph/src/lib.rs b/examples/exo-ai-2025/crates/exo-hypergraph/src/lib.rs index 97d5d56ec..e37f78bc9 100644 --- a/examples/exo-ai-2025/crates/exo-hypergraph/src/lib.rs +++ b/examples/exo-ai-2025/crates/exo-hypergraph/src/lib.rs @@ -47,13 +47,13 @@ pub mod sheaf; pub mod topology; pub use hyperedge::{Hyperedge, HyperedgeIndex}; -pub use sheaf::{SheafStructure, SheafInconsistency}; -pub use topology::{SimplicialComplex, PersistenceDiagram}; +pub use sheaf::{SheafInconsistency, SheafStructure}; +pub use topology::{PersistenceDiagram, SimplicialComplex}; use dashmap::DashMap; use exo_core::{ - EntityId, Error, HyperedgeId, HyperedgeResult, Relation, SectionId, - SheafConsistencyResult, TopologicalQuery, + EntityId, Error, HyperedgeId, HyperedgeResult, Relation, SectionId, SheafConsistencyResult, + TopologicalQuery, }; use serde::{Deserialize, Serialize}; use std::sync::Arc; @@ -212,10 +212,7 @@ impl HypergraphSubstrate { /// /// Checks if local sections are consistent on their overlaps, /// following the sheaf axioms. - pub fn check_sheaf_consistency( - &self, - sections: &[SectionId], - ) -> SheafConsistencyResult { + pub fn check_sheaf_consistency(&self, sections: &[SectionId]) -> SheafConsistencyResult { match &self.sheaf { Some(sheaf) => sheaf.check_consistency(sections), None => SheafConsistencyResult::NotConfigured, diff --git a/examples/exo-ai-2025/crates/exo-hypergraph/src/sheaf.rs b/examples/exo-ai-2025/crates/exo-hypergraph/src/sheaf.rs index 2510773e1..4cc23e628 100644 --- a/examples/exo-ai-2025/crates/exo-hypergraph/src/sheaf.rs +++ b/examples/exo-ai-2025/crates/exo-hypergraph/src/sheaf.rs @@ -114,8 +114,7 @@ impl SheafStructure { let restricted = self.compute_restriction(§ion.data, subdomain); // Cache the result - self.restriction_maps - .insert(cache_key, restricted.clone()); + self.restriction_maps.insert(cache_key, restricted.clone()); restricted } diff --git a/examples/exo-ai-2025/crates/exo-manifold/src/deformation.rs b/examples/exo-ai-2025/crates/exo-manifold/src/deformation.rs index f35a94a5b..6718915d0 100644 --- a/examples/exo-ai-2025/crates/exo-manifold/src/deformation.rs +++ b/examples/exo-ai-2025/crates/exo-manifold/src/deformation.rs @@ -11,10 +11,7 @@ pub struct ManifoldDeformer { } impl ManifoldDeformer { - pub fn new( - network: Arc>, - learning_rate: f32, - ) -> Self { + pub fn new(network: Arc>, learning_rate: f32) -> Self { Self { _network: network, _learning_rate: learning_rate, diff --git a/examples/exo-ai-2025/crates/exo-manifold/src/lib.rs b/examples/exo-ai-2025/crates/exo-manifold/src/lib.rs index 8f7823072..ee10dcc50 100644 --- a/examples/exo-ai-2025/crates/exo-manifold/src/lib.rs +++ b/examples/exo-ai-2025/crates/exo-manifold/src/lib.rs @@ -24,17 +24,17 @@ use exo_core::{Error, ManifoldConfig, ManifoldDelta, Pattern, Result, SearchResu use parking_lot::RwLock; use std::sync::Arc; -mod network; -mod retrieval; mod deformation; mod forgetting; +mod network; +mod retrieval; pub mod simd_ops; -pub use network::LearnedManifold; -pub use simd_ops::{cosine_similarity_simd, euclidean_distance_simd, batch_distances}; -pub use retrieval::GradientDescentRetriever; pub use deformation::ManifoldDeformer; pub use forgetting::StrategicForgetting; +pub use network::LearnedManifold; +pub use retrieval::GradientDescentRetriever; +pub use simd_ops::{batch_distances, cosine_similarity_simd, euclidean_distance_simd}; /// Simplified manifold storage using vector similarity pub struct ManifoldEngine { @@ -49,11 +49,8 @@ pub struct ManifoldEngine { impl ManifoldEngine { /// Create a new manifold engine pub fn new(config: ManifoldConfig) -> Self { - let network = LearnedManifold::new( - config.dimension, - config.hidden_dim, - config.hidden_layers, - ); + let network = + LearnedManifold::new(config.dimension, config.hidden_dim, config.hidden_layers); Self { network: Arc::new(RwLock::new(network)), @@ -71,10 +68,7 @@ impl ManifoldEngine { }); } - let retriever = GradientDescentRetriever::new( - self.network.clone(), - self.config.clone(), - ); + let retriever = GradientDescentRetriever::new(self.network.clone(), self.config.clone()); retriever.retrieve(query, k, &self.patterns) } @@ -91,10 +85,7 @@ impl ManifoldEngine { // Store pattern for later extraction self.patterns.write().push(pattern.clone()); - let mut deformer = ManifoldDeformer::new( - self.network.clone(), - self.config.learning_rate, - ); + let mut deformer = ManifoldDeformer::new(self.network.clone(), self.config.learning_rate); deformer.deform(&pattern, salience) } @@ -103,11 +94,7 @@ impl ManifoldEngine { pub fn forget(&mut self, salience_threshold: f32, decay_rate: f32) -> Result { let forgetter = StrategicForgetting::new(self.network.clone()); - forgetter.forget( - &self.patterns, - salience_threshold, - decay_rate, - ) + forgetter.forget(&self.patterns, salience_threshold, decay_rate) } /// Get number of stored patterns diff --git a/examples/exo-ai-2025/crates/exo-manifold/src/retrieval.rs b/examples/exo-ai-2025/crates/exo-manifold/src/retrieval.rs index 65cc83319..b044839cf 100644 --- a/examples/exo-ai-2025/crates/exo-manifold/src/retrieval.rs +++ b/examples/exo-ai-2025/crates/exo-manifold/src/retrieval.rs @@ -15,10 +15,7 @@ pub struct GradientDescentRetriever { } impl GradientDescentRetriever { - pub fn new( - network: Arc>, - config: ManifoldConfig, - ) -> Self { + pub fn new(network: Arc>, config: ManifoldConfig) -> Self { Self { _network: network, _config: config, diff --git a/examples/exo-ai-2025/crates/exo-node/src/types.rs b/examples/exo-ai-2025/crates/exo-node/src/types.rs index 971f60690..05c33836e 100644 --- a/examples/exo-ai-2025/crates/exo-node/src/types.rs +++ b/examples/exo-ai-2025/crates/exo-node/src/types.rs @@ -1,8 +1,6 @@ //! Node.js-compatible type definitions -use exo_core::{ - Metadata, MetadataValue, Pattern, PatternId, SearchResult, SubstrateTime, -}; +use exo_core::{Metadata, MetadataValue, Pattern, PatternId, SearchResult, SubstrateTime}; use napi::bindgen_prelude::*; use napi_derive::napi; use std::collections::HashMap; @@ -51,11 +49,7 @@ impl TryFrom for Pattern { .antecedents .unwrap_or_default() .into_iter() - .filter_map(|s| { - uuid::Uuid::parse_str(&s) - .ok() - .map(|uuid| PatternId(uuid)) - }) + .filter_map(|s| uuid::Uuid::parse_str(&s).ok().map(|uuid| PatternId(uuid))) .collect(); Ok(Pattern { diff --git a/examples/exo-ai-2025/crates/exo-temporal/src/anticipation.rs b/examples/exo-ai-2025/crates/exo-temporal/src/anticipation.rs index c46b318e3..8f3e3d1df 100644 --- a/examples/exo-ai-2025/crates/exo-temporal/src/anticipation.rs +++ b/examples/exo-ai-2025/crates/exo-temporal/src/anticipation.rs @@ -143,7 +143,8 @@ impl SequentialPatternTracker { self.cache_valid.insert(from, false); // Track total sequences - self.total_sequences.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + self.total_sequences + .fetch_add(1, std::sync::atomic::Ordering::Relaxed); } /// Predict next pattern given current (optimized O(1) cache lookup) @@ -158,10 +159,7 @@ impl SequentialPatternTracker { // Fast O(1) lookup from pre-sorted cache if let Some(sorted) = self.frequency_cache.get(¤t) { - sorted.iter() - .take(top_k) - .map(|(_, id)| *id) - .collect() + sorted.iter().take(top_k).map(|(_, id)| *id).collect() } else { Vec::new() } @@ -189,7 +187,8 @@ impl SequentialPatternTracker { /// Get total number of recorded sequences pub fn total_sequences(&self) -> usize { - self.total_sequences.load(std::sync::atomic::Ordering::Relaxed) + self.total_sequences + .load(std::sync::atomic::Ordering::Relaxed) } /// Get prediction accuracy estimate (based on frequency distribution) @@ -223,7 +222,8 @@ impl SequentialPatternTracker { self.cache_valid.insert(pattern, false); } - self.total_sequences.fetch_add(sequences.len(), std::sync::atomic::Ordering::Relaxed); + self.total_sequences + .fetch_add(sequences.len(), std::sync::atomic::Ordering::Relaxed); } } @@ -253,7 +253,8 @@ pub fn anticipate( for pattern_id in predicted { if let Some(temporal_pattern) = long_term.get(&pattern_id) { // Create query from pattern - let query = Query::from_embedding(temporal_pattern.pattern.embedding.clone()); + let query = + Query::from_embedding(temporal_pattern.pattern.embedding.clone()); let query_hash = query.hash(); // Pre-fetch if not cached @@ -279,7 +280,8 @@ pub fn anticipate( for pattern_id in downstream.into_iter().take(5) { if let Some(temporal_pattern) = long_term.get(&pattern_id) { - let query = Query::from_embedding(temporal_pattern.pattern.embedding.clone()); + let query = + Query::from_embedding(temporal_pattern.pattern.embedding.clone()); let query_hash = query.hash(); // Pre-fetch if not cached @@ -357,12 +359,7 @@ mod tests { let p2 = PatternId::new(); let p3 = PatternId::new(); - let sequences = vec![ - (p1, p2), - (p1, p2), - (p1, p3), - (p2, p3), - ]; + let sequences = vec![(p1, p2), (p1, p2), (p1, p3), (p2, p3)]; tracker.record_sequences_batch(&sequences); diff --git a/examples/exo-ai-2025/crates/exo-temporal/src/causal.rs b/examples/exo-ai-2025/crates/exo-temporal/src/causal.rs index d93c67997..e9078e4b9 100644 --- a/examples/exo-ai-2025/crates/exo-temporal/src/causal.rs +++ b/examples/exo-ai-2025/crates/exo-temporal/src/causal.rs @@ -2,8 +2,8 @@ use crate::types::{PatternId, SubstrateTime}; use dashmap::DashMap; -use petgraph::graph::{DiGraph, NodeIndex}; use petgraph::algo::dijkstra; +use petgraph::graph::{DiGraph, NodeIndex}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::sync::Arc; @@ -31,7 +31,8 @@ pub struct CausalGraph { /// Pattern timestamps for light cone calculations timestamps: DashMap, /// Cached graph representation for path finding - graph_cache: Arc, HashMap)>>>, + graph_cache: + Arc, HashMap)>>>, } impl CausalGraph { @@ -86,18 +87,12 @@ impl CausalGraph { /// Get out-degree (number of effects) pub fn out_degree(&self, pattern: PatternId) -> usize { - self.forward - .get(&pattern) - .map(|v| v.len()) - .unwrap_or(0) + self.forward.get(&pattern).map(|v| v.len()).unwrap_or(0) } /// Get in-degree (number of causes) pub fn in_degree(&self, pattern: PatternId) -> usize { - self.backward - .get(&pattern) - .map(|v| v.len()) - .unwrap_or(0) + self.backward.get(&pattern).map(|v| v.len()).unwrap_or(0) } /// Compute shortest path distance between two patterns @@ -224,9 +219,7 @@ impl CausalGraph { ) -> Vec { candidates .iter() - .filter(|&&id| { - self.is_in_light_cone(id, reference, reference_time, cone_type) - }) + .filter(|&&id| self.is_in_light_cone(id, reference, reference_time, cone_type)) .copied() .collect() } diff --git a/examples/exo-ai-2025/crates/exo-temporal/src/consolidation.rs b/examples/exo-ai-2025/crates/exo-temporal/src/consolidation.rs index 0566f8baf..53d7361da 100644 --- a/examples/exo-ai-2025/crates/exo-temporal/src/consolidation.rs +++ b/examples/exo-ai-2025/crates/exo-temporal/src/consolidation.rs @@ -8,7 +8,7 @@ use crate::causal::CausalGraph; use crate::long_term::LongTermStore; use crate::short_term::ShortTermBuffer; -use crate::types::{TemporalPattern, SubstrateTime}; +use crate::types::{SubstrateTime, TemporalPattern}; use std::sync::atomic::{AtomicUsize, Ordering}; /// Consolidation configuration @@ -122,7 +122,8 @@ pub fn compute_salience_batch( long_term: &LongTermStore, config: &ConsolidationConfig, ) -> Vec { - patterns.iter() + patterns + .iter() .map(|tp| compute_salience(tp, causal_graph, long_term, config)) .collect() } @@ -262,8 +263,10 @@ impl ConsolidationStats { result.num_consolidated + result.num_forgotten, Ordering::Relaxed, ); - self.total_consolidated.fetch_add(result.num_consolidated, Ordering::Relaxed); - self.total_forgotten.fetch_add(result.num_forgotten, Ordering::Relaxed); + self.total_consolidated + .fetch_add(result.num_consolidated, Ordering::Relaxed); + self.total_forgotten + .fetch_add(result.num_forgotten, Ordering::Relaxed); } pub fn consolidation_rate(&self) -> f32 { @@ -287,7 +290,8 @@ mod tests { let long_term = LongTermStore::default(); let config = ConsolidationConfig::default(); - let mut temporal_pattern = TemporalPattern::from_embedding(vec![1.0, 2.0, 3.0], Metadata::new()); + let mut temporal_pattern = + TemporalPattern::from_embedding(vec![1.0, 2.0, 3.0], Metadata::new()); temporal_pattern.access_count = 10; let salience = compute_salience(&temporal_pattern, &causal_graph, &long_term, &config); diff --git a/examples/exo-ai-2025/crates/exo-temporal/src/lib.rs b/examples/exo-ai-2025/crates/exo-temporal/src/lib.rs index ad822f983..835d3e1aa 100644 --- a/examples/exo-ai-2025/crates/exo-temporal/src/lib.rs +++ b/examples/exo-ai-2025/crates/exo-temporal/src/lib.rs @@ -66,7 +66,10 @@ pub use anticipation::{ anticipate, AnticipationHint, PrefetchCache, SequentialPatternTracker, TemporalPhase, }; pub use causal::{CausalConeType, CausalGraph, CausalGraphStats}; -pub use consolidation::{compute_salience, compute_salience_batch, consolidate, ConsolidationConfig, ConsolidationResult, ConsolidationStats}; +pub use consolidation::{ + compute_salience, compute_salience_batch, consolidate, ConsolidationConfig, + ConsolidationResult, ConsolidationStats, +}; pub use long_term::{LongTermConfig, LongTermStats, LongTermStore}; pub use short_term::{ShortTermBuffer, ShortTermConfig, ShortTermStats}; pub use types::*; @@ -292,7 +295,8 @@ impl TemporalMemory { /// Strategic forgetting in long-term memory pub fn forget(&self) { - self.long_term.decay_low_salience(self.config.long_term.decay_rate); + self.long_term + .decay_low_salience(self.config.long_term.decay_rate); } /// Get causal graph reference @@ -411,7 +415,10 @@ mod tests { // Consolidate to long-term let result = memory.consolidate(); - assert!(result.num_consolidated >= 3, "Should consolidate all patterns"); + assert!( + result.num_consolidated >= 3, + "Should consolidate all patterns" + ); // Query with causal context - use p1's timestamp as reference for future cone let query = Query::from_embedding(vec![1.0, 0.0, 0.0]).with_origin(id1); @@ -422,6 +429,9 @@ mod tests { ); // Should find patterns in the causal future of p1 - assert!(!results.is_empty(), "Should find causal descendants in future cone"); + assert!( + !results.is_empty(), + "Should find causal descendants in future cone" + ); } } diff --git a/examples/exo-ai-2025/crates/exo-temporal/src/long_term.rs b/examples/exo-ai-2025/crates/exo-temporal/src/long_term.rs index 1c5688249..5a3f718a4 100644 --- a/examples/exo-ai-2025/crates/exo-temporal/src/long_term.rs +++ b/examples/exo-ai-2025/crates/exo-temporal/src/long_term.rs @@ -5,11 +5,11 @@ //! - Batch integration with deferred index sorting //! - Early-exit similarity search for hot patterns -use crate::types::{TemporalPattern, PatternId, Query, SearchResult, SubstrateTime, TimeRange}; +use crate::types::{PatternId, Query, SearchResult, SubstrateTime, TemporalPattern, TimeRange}; use dashmap::DashMap; use parking_lot::RwLock; -use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; /// Configuration for long-term store #[derive(Debug, Clone)] @@ -109,7 +109,8 @@ impl LongTermStore { for entry in self.patterns.iter() { let temporal_pattern = entry.value(); - let score = cosine_similarity_simd(&query.embedding, &temporal_pattern.pattern.embedding); + let score = + cosine_similarity_simd(&query.embedding, &temporal_pattern.pattern.embedding); // Early exit optimization: skip if below worst score in top-k if results.len() >= k && score <= results.last().map(|r| r.score).unwrap_or(0.0) { @@ -124,18 +125,30 @@ impl LongTermStore { // Keep sorted and bounded if results.len() > k { - results.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap_or(std::cmp::Ordering::Equal)); + results.sort_by(|a, b| { + b.score + .partial_cmp(&a.score) + .unwrap_or(std::cmp::Ordering::Equal) + }); results.truncate(k); } } // Final sort - results.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap_or(std::cmp::Ordering::Equal)); + results.sort_by(|a, b| { + b.score + .partial_cmp(&a.score) + .unwrap_or(std::cmp::Ordering::Equal) + }); results } /// Search with time range filter (SIMD-accelerated) - pub fn search_with_time_range(&self, query: &Query, time_range: TimeRange) -> Vec { + pub fn search_with_time_range( + &self, + query: &Query, + time_range: TimeRange, + ) -> Vec { let k = query.k; let mut results: Vec = Vec::with_capacity(k + 1); @@ -147,7 +160,8 @@ impl LongTermStore { continue; } - let score = cosine_similarity_simd(&query.embedding, &temporal_pattern.pattern.embedding); + let score = + cosine_similarity_simd(&query.embedding, &temporal_pattern.pattern.embedding); // Early exit optimization if results.len() >= k && score <= results.last().map(|r| r.score).unwrap_or(0.0) { @@ -161,12 +175,20 @@ impl LongTermStore { }); if results.len() > k { - results.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap_or(std::cmp::Ordering::Equal)); + results.sort_by(|a, b| { + b.score + .partial_cmp(&a.score) + .unwrap_or(std::cmp::Ordering::Equal) + }); results.truncate(k); } } - results.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap_or(std::cmp::Ordering::Equal)); + results.sort_by(|a, b| { + b.score + .partial_cmp(&a.score) + .unwrap_or(std::cmp::Ordering::Equal) + }); results } @@ -252,7 +274,11 @@ impl LongTermStore { let size = self.patterns.len(); // Compute average salience - let total_salience: f32 = self.patterns.iter().map(|e| e.value().pattern.salience).sum(); + let total_salience: f32 = self + .patterns + .iter() + .map(|e| e.value().pattern.salience) + .sum(); let avg_salience = if size > 0 { total_salience / size as f32 } else { @@ -368,7 +394,8 @@ mod tests { fn test_long_term_store() { let store = LongTermStore::default(); - let temporal_pattern = TemporalPattern::from_embedding(vec![1.0, 2.0, 3.0], Metadata::new()); + let temporal_pattern = + TemporalPattern::from_embedding(vec![1.0, 2.0, 3.0], Metadata::new()); let id = temporal_pattern.pattern.id; store.integrate(temporal_pattern); @@ -400,7 +427,8 @@ mod tests { fn test_decay() { let store = LongTermStore::default(); - let mut temporal_pattern = TemporalPattern::from_embedding(vec![1.0, 2.0, 3.0], Metadata::new()); + let mut temporal_pattern = + TemporalPattern::from_embedding(vec![1.0, 2.0, 3.0], Metadata::new()); temporal_pattern.pattern.salience = 0.15; // Just above minimum let id = temporal_pattern.pattern.id; diff --git a/examples/exo-ai-2025/crates/exo-temporal/src/short_term.rs b/examples/exo-ai-2025/crates/exo-temporal/src/short_term.rs index 5e928c9cb..b420c0bed 100644 --- a/examples/exo-ai-2025/crates/exo-temporal/src/short_term.rs +++ b/examples/exo-ai-2025/crates/exo-temporal/src/short_term.rs @@ -1,6 +1,6 @@ //! Short-term volatile memory buffer -use crate::types::{TemporalPattern, PatternId}; +use crate::types::{PatternId, TemporalPattern}; use dashmap::DashMap; use parking_lot::RwLock; use std::collections::VecDeque; @@ -201,7 +201,8 @@ mod tests { fn test_short_term_buffer() { let buffer = ShortTermBuffer::default(); - let temporal_pattern = TemporalPattern::from_embedding(vec![1.0, 2.0, 3.0], Metadata::new()); + let temporal_pattern = + TemporalPattern::from_embedding(vec![1.0, 2.0, 3.0], Metadata::new()); let id = temporal_pattern.pattern.id; buffer.insert(temporal_pattern); diff --git a/examples/exo-ai-2025/research/02-quantum-superposition/benches/cognitive_benchmarks.rs b/examples/exo-ai-2025/research/02-quantum-superposition/benches/cognitive_benchmarks.rs index ef054319a..4604b2b27 100644 --- a/examples/exo-ai-2025/research/02-quantum-superposition/benches/cognitive_benchmarks.rs +++ b/examples/exo-ai-2025/research/02-quantum-superposition/benches/cognitive_benchmarks.rs @@ -1,9 +1,9 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId}; -use quantum_cognition::{ - CognitiveState, InterferenceDecisionMaker, AttentionOperator, - SuperpositionBuilder, tensor_product, interference_pattern, -}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use num_complex::Complex64; +use quantum_cognition::{ + interference_pattern, tensor_product, AttentionOperator, CognitiveState, + InterferenceDecisionMaker, SuperpositionBuilder, +}; use std::f64::consts::PI; /// Benchmark: State creation and normalization @@ -41,15 +41,11 @@ fn bench_probabilities(c: &mut Criterion) { let state = CognitiveState::uniform(dim, labels); group.bench_with_input(BenchmarkId::new("born_rule", dim), &state, |b, state| { - b.iter(|| { - black_box(state.probabilities()) - }); + b.iter(|| black_box(state.probabilities())); }); group.bench_with_input(BenchmarkId::new("entropy", dim), &state, |b, state| { - b.iter(|| { - black_box(state.von_neumann_entropy()) - }); + b.iter(|| black_box(state.von_neumann_entropy())); }); } @@ -65,17 +61,21 @@ fn bench_inner_products(c: &mut Criterion) { let state1 = CognitiveState::uniform(dim, labels.clone()); let state2 = CognitiveState::uniform(dim, labels); - group.bench_with_input(BenchmarkId::new("inner_product", dim), &(state1.clone(), state2.clone()), |b, (s1, s2)| { - b.iter(|| { - black_box(s1.inner_product(s2)) - }); - }); + group.bench_with_input( + BenchmarkId::new("inner_product", dim), + &(state1.clone(), state2.clone()), + |b, (s1, s2)| { + b.iter(|| black_box(s1.inner_product(s2))); + }, + ); - group.bench_with_input(BenchmarkId::new("fidelity", dim), &(state1, state2), |b, (s1, s2)| { - b.iter(|| { - black_box(s1.fidelity(s2)) - }); - }); + group.bench_with_input( + BenchmarkId::new("fidelity", dim), + &(state1, state2), + |b, (s1, s2)| { + b.iter(|| black_box(s1.fidelity(s2))); + }, + ); } group.finish(); @@ -90,17 +90,17 @@ fn bench_measurements(c: &mut Criterion) { let state = CognitiveState::uniform(dim, labels); group.bench_with_input(BenchmarkId::new("projective", dim), &state, |b, state| { - b.iter(|| { - black_box(state.measure()) - }); + b.iter(|| black_box(state.measure())); }); let observable: Vec = (0..*dim).map(|i| (i as f64) / (*dim as f64)).collect(); - group.bench_with_input(BenchmarkId::new("weak", dim), &(state, observable), |b, (state, obs)| { - b.iter(|| { - black_box(state.weak_measure(obs, 0.5)) - }); - }); + group.bench_with_input( + BenchmarkId::new("weak", dim), + &(state, observable), + |b, (state, obs)| { + b.iter(|| black_box(state.weak_measure(obs, 0.5))); + }, + ); } group.finish(); @@ -115,11 +115,13 @@ fn bench_tensor_products(c: &mut Criterion) { let state1 = CognitiveState::uniform(*dim, labels.clone()); let state2 = CognitiveState::uniform(*dim, labels); - group.bench_with_input(BenchmarkId::new("product", dim), &(state1, state2), |b, (s1, s2)| { - b.iter(|| { - black_box(tensor_product(s1, s2)) - }); - }); + group.bench_with_input( + BenchmarkId::new("product", dim), + &(state1, state2), + |b, (s1, s2)| { + b.iter(|| black_box(tensor_product(s1, s2))); + }, + ); } group.finish(); @@ -144,38 +146,62 @@ fn bench_interference_decisions(c: &mut Criterion) { for n_options in [3, 5, 10].iter() { let options: Vec = (0..*n_options).map(|i| format!("option_{}", i)).collect(); let state = CognitiveState::uniform(*n_options, options.clone()); - let phases: Vec = (0..*n_options).map(|i| (i as f64) * 2.0 * PI / (*n_options as f64)).collect(); + let phases: Vec = (0..*n_options) + .map(|i| (i as f64) * 2.0 * PI / (*n_options as f64)) + .collect(); - group.bench_with_input(BenchmarkId::new("multi_alternative", n_options), &(state, options, phases), |b, (state, opts, ph)| { - b.iter(|| { - let mut dm = InterferenceDecisionMaker::new(state.clone()); - black_box(dm.multi_alternative_choice(opts.clone(), ph.clone())) - }); - }); + group.bench_with_input( + BenchmarkId::new("multi_alternative", n_options), + &(state, options, phases), + |b, (state, opts, ph)| { + b.iter(|| { + let mut dm = InterferenceDecisionMaker::new(state.clone()); + black_box(dm.multi_alternative_choice(opts.clone(), ph.clone())) + }); + }, + ); } // Conjunction decision (Linda problem) group.bench_function("conjunction_fallacy", |b| { - let labels = vec!["bank_teller".to_string(), "feminist".to_string(), "feminist_bank_teller".to_string()]; + let labels = vec![ + "bank_teller".to_string(), + "feminist".to_string(), + "feminist_bank_teller".to_string(), + ]; let state = CognitiveState::uniform(3, labels); b.iter(|| { let mut dm = InterferenceDecisionMaker::new(state.clone()); - black_box(dm.conjunction_decision("bank_teller", "feminist", "feminist_bank_teller", 0.8)) + black_box(dm.conjunction_decision( + "bank_teller", + "feminist", + "feminist_bank_teller", + 0.8, + )) }); }); // Prisoner's dilemma for entanglement in [0.3, 0.6, 0.9].iter() { - group.bench_with_input(BenchmarkId::new("prisoners_dilemma", format!("{:.1}", entanglement)), entanglement, |b, &ent| { - let labels = vec!["CC".to_string(), "DD".to_string(), "CD".to_string(), "DC".to_string()]; - let state = CognitiveState::uniform(4, labels); + group.bench_with_input( + BenchmarkId::new("prisoners_dilemma", format!("{:.1}", entanglement)), + entanglement, + |b, &ent| { + let labels = vec![ + "CC".to_string(), + "DD".to_string(), + "CD".to_string(), + "DC".to_string(), + ]; + let state = CognitiveState::uniform(4, labels); - b.iter(|| { - let mut dm = InterferenceDecisionMaker::new(state.clone()); - black_box(dm.quantum_prisoners_dilemma("cooperate", ent)) - }); - }); + b.iter(|| { + let mut dm = InterferenceDecisionMaker::new(state.clone()); + black_box(dm.quantum_prisoners_dilemma("cooperate", ent)) + }); + }, + ); } group.finish(); @@ -186,12 +212,12 @@ fn bench_interference_patterns(c: &mut Criterion) { let mut group = c.benchmark_group("interference_patterns"); for n_points in [50, 100, 500, 1000].iter() { - let phases: Vec = (0..*n_points).map(|i| (i as f64) * 2.0 * PI / (*n_points as f64)).collect(); + let phases: Vec = (0..*n_points) + .map(|i| (i as f64) * 2.0 * PI / (*n_points as f64)) + .collect(); group.bench_with_input(BenchmarkId::new("pattern", n_points), &phases, |b, ph| { - b.iter(|| { - black_box(interference_pattern(ph.clone())) - }); + b.iter(|| black_box(interference_pattern(ph.clone()))); }); } @@ -207,21 +233,25 @@ fn bench_attention(c: &mut Criterion) { let state = CognitiveState::uniform(*dim, labels); // Full attention (projective measurement) - group.bench_with_input(BenchmarkId::new("full_attention", dim), &state, |b, state| { - let mut attention = AttentionOperator::full_attention(0, *dim, 10.0); - b.iter(|| { - black_box(attention.apply(state)) - }); - }); + group.bench_with_input( + BenchmarkId::new("full_attention", dim), + &state, + |b, state| { + let mut attention = AttentionOperator::full_attention(0, *dim, 10.0); + b.iter(|| black_box(attention.apply(state))); + }, + ); // Distributed attention (weak measurement) let weights: Vec = (0..*dim).map(|i| 1.0 / (1.0 + (i as f64))).collect(); - group.bench_with_input(BenchmarkId::new("distributed_attention", dim), &(state, weights), |b, (state, w)| { - let mut attention = AttentionOperator::distributed_attention(w.clone(), 0.3, 10.0); - b.iter(|| { - black_box(attention.apply(state)) - }); - }); + group.bench_with_input( + BenchmarkId::new("distributed_attention", dim), + &(state, weights), + |b, (state, w)| { + let mut attention = AttentionOperator::distributed_attention(w.clone(), 0.3, 10.0); + b.iter(|| black_box(attention.apply(state))); + }, + ); } group.finish(); @@ -235,12 +265,16 @@ fn bench_continuous_evolution(c: &mut Criterion) { let state = CognitiveState::uniform(10, labels); for time_steps in [10, 50, 100].iter() { - group.bench_with_input(BenchmarkId::new("evolution", time_steps), time_steps, |b, &steps| { - b.iter(|| { - let mut attention = AttentionOperator::full_attention(0, 10, 5.0); - black_box(attention.continuous_evolution(&state, 1.0, steps)) - }); - }); + group.bench_with_input( + BenchmarkId::new("evolution", time_steps), + time_steps, + |b, &steps| { + b.iter(|| { + let mut attention = AttentionOperator::full_attention(0, 10, 5.0); + black_box(attention.continuous_evolution(&state, 1.0, steps)) + }); + }, + ); } group.finish(); diff --git a/examples/exo-ai-2025/research/02-quantum-superposition/examples/attention_collapse.rs b/examples/exo-ai-2025/research/02-quantum-superposition/examples/attention_collapse.rs index bf022a227..98b19fb54 100644 --- a/examples/exo-ai-2025/research/02-quantum-superposition/examples/attention_collapse.rs +++ b/examples/exo-ai-2025/research/02-quantum-superposition/examples/attention_collapse.rs @@ -7,8 +7,8 @@ //! - Consciousness threshold based on integrated information use quantum_cognition::{ - CognitiveState, AttentionOperator, ConsciousnessThreshold, - quantum_zeno_effect, SuperpositionBuilder, + quantum_zeno_effect, AttentionOperator, CognitiveState, ConsciousnessThreshold, + SuperpositionBuilder, }; fn main() { @@ -27,21 +27,35 @@ fn main() { println!("Initial cognitive state (maximally uncertain superposition):"); println!(" Dimension: {}", initial_state.dimension()); - println!(" Von Neumann entropy: {:.4}", initial_state.von_neumann_entropy()); + println!( + " Von Neumann entropy: {:.4}", + initial_state.von_neumann_entropy() + ); println!(" Max entropy (log N): {:.4}", (5.0_f64).ln()); - println!(" Participation ratio: {:.4}\n", initial_state.participation_ratio()); + println!( + " Participation ratio: {:.4}\n", + initial_state.participation_ratio() + ); // Apply full attention let mut attention = AttentionOperator::full_attention(2, 5, 8.0); // 8 Hz alpha rhythm let collapsed_state = attention.apply(&initial_state); println!("After full attention (focused on concept_2):"); - println!(" Von Neumann entropy: {:.4}", collapsed_state.von_neumann_entropy()); - println!(" Participation ratio: {:.4}", collapsed_state.participation_ratio()); + println!( + " Von Neumann entropy: {:.4}", + collapsed_state.von_neumann_entropy() + ); + println!( + " Participation ratio: {:.4}", + collapsed_state.participation_ratio() + ); let (idx, prob, label) = collapsed_state.most_likely(); println!(" Most likely state: {} (P = {:.4})", label, prob); - println!("\n ⇒ Entropy reduced by {:.4} bits", - initial_state.von_neumann_entropy() - collapsed_state.von_neumann_entropy()); + println!( + "\n ⇒ Entropy reduced by {:.4} bits", + initial_state.von_neumann_entropy() - collapsed_state.von_neumann_entropy() + ); println!(" ⇒ Superposition → definite conscious state ✓\n"); println!("─────────────────────────────────────────────────────────────────\n"); @@ -72,7 +86,10 @@ fn main() { _ => "Full collapse", }; - println!(" {:.1} | {:.4} | {}", strength, entropy, description); + println!( + " {:.1} | {:.4} | {}", + strength, entropy, description + ); } println!("\n ⇒ Gradient of consciousness from diffuse to focused ✓\n"); @@ -128,25 +145,51 @@ fn main() { let pure = CognitiveState::definite( 0, 5, - vec!["single".to_string(), "b".to_string(), "c".to_string(), "d".to_string(), "e".to_string()] + vec![ + "single".to_string(), + "b".to_string(), + "c".to_string(), + "d".to_string(), + "e".to_string(), + ], ); let phi_pure = threshold.estimate_phi(&pure); println!("Pure state (single definite thought):"); println!(" Entropy: {:.4}", pure.von_neumann_entropy()); println!(" Φ estimate: {:.4}", phi_pure); - println!(" Conscious: {}", if threshold.is_conscious(&pure) { "YES ✓" } else { "NO ✗" }); + println!( + " Conscious: {}", + if threshold.is_conscious(&pure) { + "YES ✓" + } else { + "NO ✗" + } + ); println!(" → Too simple, no integration\n"); // Maximally mixed (complete uncertainty) let mixed = CognitiveState::uniform( 5, - vec!["a".to_string(), "b".to_string(), "c".to_string(), "d".to_string(), "e".to_string()] + vec![ + "a".to_string(), + "b".to_string(), + "c".to_string(), + "d".to_string(), + "e".to_string(), + ], ); let phi_mixed = threshold.estimate_phi(&mixed); println!("Maximally mixed (complete superposition):"); println!(" Entropy: {:.4}", mixed.von_neumann_entropy()); println!(" Φ estimate: {:.4}", phi_mixed); - println!(" Conscious: {}", if threshold.is_conscious(&mixed) { "YES ✓" } else { "NO ✗" }); + println!( + " Conscious: {}", + if threshold.is_conscious(&mixed) { + "YES ✓" + } else { + "NO ✗" + } + ); println!(" → Too random, no structure\n"); // Partially collapsed (integrated state) @@ -161,7 +204,14 @@ fn main() { println!("Partially collapsed (integrated conscious state):"); println!(" Entropy: {:.4}", partial.von_neumann_entropy()); println!(" Φ estimate: {:.4}", phi_partial); - println!(" Conscious: {}", if threshold.is_conscious(&partial) { "YES ✓" } else { "NO ✗" }); + println!( + " Conscious: {}", + if threshold.is_conscious(&partial) { + "YES ✓" + } else { + "NO ✗" + } + ); println!(" → Balance of structure and distribution ✓\n"); println!("─────────────────────────────────────────────────────────────────\n"); diff --git a/examples/exo-ai-2025/research/02-quantum-superposition/examples/linda_problem.rs b/examples/exo-ai-2025/research/02-quantum-superposition/examples/linda_problem.rs index e7fedee45..a5870f5eb 100644 --- a/examples/exo-ai-2025/research/02-quantum-superposition/examples/linda_problem.rs +++ b/examples/exo-ai-2025/research/02-quantum-superposition/examples/linda_problem.rs @@ -31,7 +31,7 @@ fn main() { let labels = vec![ "bank_teller".to_string(), "feminist".to_string(), - "feminist_bank_teller".to_string() + "feminist_bank_teller".to_string(), ]; let initial_state = CognitiveState::uniform(3, labels); @@ -45,7 +45,7 @@ fn main() { "bank_teller", "feminist", "feminist_bank_teller", - *overlap + *overlap, ); println!("Semantic Overlap = {:.1}", overlap); diff --git a/examples/exo-ai-2025/research/02-quantum-superposition/src/collapse_attention.rs b/examples/exo-ai-2025/research/02-quantum-superposition/src/collapse_attention.rs index 8adc83647..f82745557 100644 --- a/examples/exo-ai-2025/research/02-quantum-superposition/src/collapse_attention.rs +++ b/examples/exo-ai-2025/research/02-quantum-superposition/src/collapse_attention.rs @@ -4,8 +4,8 @@ // cognitive superposition into definite conscious states. Implements // continuous weak measurement, Zeno effect, and entropy dynamics. +use crate::quantum_cognitive_state::{Amplitude, CognitiveState, SuperpositionBuilder}; use num_complex::Complex64; -use crate::quantum_cognitive_state::{CognitiveState, Amplitude, SuperpositionBuilder}; use std::collections::VecDeque; /// Attention mechanism implementing measurement-induced collapse @@ -98,11 +98,8 @@ impl AttentionOperator { cumulative += wp; if r < cumulative { // Collapse to state i - let collapsed = CognitiveState::definite( - i, - state.dimension(), - state.labels.clone() - ); + let collapsed = + CognitiveState::definite(i, state.dimension(), state.labels.clone()); // Track entropy reduction self.entropy_history.push_back(0.0); @@ -177,7 +174,13 @@ impl AttentionOperator { return 0.0; } - let recent: Vec = self.entropy_history.iter().rev().take(10).copied().collect(); + let recent: Vec = self + .entropy_history + .iter() + .rev() + .take(10) + .copied() + .collect(); if recent.len() < 2 { return 0.0; @@ -225,7 +228,7 @@ pub fn quantum_zeno_effect( let mut attention = AttentionOperator::full_attention( measurement_operator_index, current_state.dimension(), - 1.0 / dt + 1.0 / dt, ); current_state = attention.apply(¤t_state); @@ -277,7 +280,8 @@ impl DecoherenceModel { for (i, amplitude) in new_amplitudes.iter_mut().enumerate() { // Add random phase from decoherence - let gamma_avg: f64 = self.gamma_matrix[i].iter().sum::() / self.gamma_matrix[i].len() as f64; + let gamma_avg: f64 = + self.gamma_matrix[i].iter().sum::() / self.gamma_matrix[i].len() as f64; if gamma_avg > 0.0 { let phase_noise = Normal::new(0.0, (gamma_avg * dt).sqrt()).unwrap(); @@ -347,7 +351,8 @@ mod tests { #[test] fn test_full_attention_collapse() { - let state = CognitiveState::uniform(3, vec!["A".to_string(), "B".to_string(), "C".to_string()]); + let state = + CognitiveState::uniform(3, vec!["A".to_string(), "B".to_string(), "C".to_string()]); let initial_entropy = state.von_neumann_entropy(); let mut attention = AttentionOperator::full_attention(1, 3, 10.0); @@ -366,7 +371,7 @@ mod tests { let mut attention = AttentionOperator::distributed_attention( vec![0.9, 0.1], 0.1, // Weak - 10.0 + 10.0, ); let new_state = attention.apply(&state); @@ -391,7 +396,8 @@ mod tests { #[test] fn test_decoherence() { - let state = CognitiveState::uniform(3, vec!["A".to_string(), "B".to_string(), "C".to_string()]); + let state = + CognitiveState::uniform(3, vec!["A".to_string(), "B".to_string(), "C".to_string()]); let decoherence = DecoherenceModel::from_attention(&[1.0, 0.5, 0.0], 1.0); @@ -406,11 +412,16 @@ mod tests { let threshold = ConsciousnessThreshold::new(0.3); // Pure state: low Φ (no integration) - let pure = CognitiveState::definite(0, 3, vec!["A".to_string(), "B".to_string(), "C".to_string()]); + let pure = CognitiveState::definite( + 0, + 3, + vec!["A".to_string(), "B".to_string(), "C".to_string()], + ); assert!(!threshold.is_conscious(&pure)); // Uniform state: low Φ (maximal entropy, no structure) - let uniform = CognitiveState::uniform(3, vec!["A".to_string(), "B".to_string(), "C".to_string()]); + let uniform = + CognitiveState::uniform(3, vec!["A".to_string(), "B".to_string(), "C".to_string()]); let phi_uniform = threshold.estimate_phi(&uniform); // Partially mixed: potentially high Φ @@ -427,7 +438,8 @@ mod tests { #[test] fn test_continuous_evolution() { - let state = CognitiveState::uniform(3, vec!["A".to_string(), "B".to_string(), "C".to_string()]); + let state = + CognitiveState::uniform(3, vec!["A".to_string(), "B".to_string(), "C".to_string()]); let mut attention = AttentionOperator::full_attention(0, 3, 5.0); @@ -438,6 +450,9 @@ mod tests { // Entropy should generally decrease (may have fluctuations) let entropy_history = attention.get_entropy_history(); - println!("Entropy samples: {:?}", entropy_history.iter().take(10).collect::>()); + println!( + "Entropy samples: {:?}", + entropy_history.iter().take(10).collect::>() + ); } } diff --git a/examples/exo-ai-2025/research/02-quantum-superposition/src/interference_decision.rs b/examples/exo-ai-2025/research/02-quantum-superposition/src/interference_decision.rs index db9397809..b12e7a034 100644 --- a/examples/exo-ai-2025/research/02-quantum-superposition/src/interference_decision.rs +++ b/examples/exo-ai-2025/research/02-quantum-superposition/src/interference_decision.rs @@ -4,8 +4,8 @@ // cognition. Decisions emerge from constructive/destructive interference of // amplitude paths rather than classical utility maximization. +use crate::quantum_cognitive_state::{Amplitude, CognitiveState, SuperpositionBuilder}; use num_complex::Complex64; -use crate::quantum_cognitive_state::{CognitiveState, Amplitude, SuperpositionBuilder}; use std::f64::consts::PI; /// Decision maker using quantum amplitude interference @@ -105,10 +105,7 @@ impl InterferenceDecisionMaker { // Calculate interference contributions let classical_prob = 1.0 / n as f64; - let interference_effects: Vec = probs - .iter() - .map(|&p| p - classical_prob) - .collect(); + let interference_effects: Vec = probs.iter().map(|&p| p - classical_prob).collect(); // Perform measurement let (choice_idx, collapsed, prob) = state.measure(); @@ -187,7 +184,8 @@ impl InterferenceDecisionMaker { } // Answer Q2 with modified state - let (ans2, prob2, _) = self.multi_alternative_choice(question2_options.clone(), modified_q2_phases); + let (ans2, prob2, _) = + self.multi_alternative_choice(question2_options.clone(), modified_q2_phases); // Order effect magnitude let order_effect = coupling_strength * prob1; @@ -200,7 +198,7 @@ impl InterferenceDecisionMaker { /// Non-separable joint state enables cooperation pub fn quantum_prisoners_dilemma( &mut self, - player2_strategy: &str, // "cooperate" or "defect" + player2_strategy: &str, // "cooperate" or "defect" entanglement_strength: f64, // Degree of non-separability ) -> (String, f64, f64) { // Classical strategies @@ -248,7 +246,8 @@ impl InterferenceDecisionMaker { /// /// Confidence = |α_chosen|² (Born rule interpretation) pub fn confidence(&self) -> f64 { - self.state.probabilities() + self.state + .probabilities() .iter() .max_by(|a, b| a.partial_cmp(b).unwrap()) .copied() @@ -330,7 +329,8 @@ mod tests { #[test] fn test_conjunction_fallacy() { - let initial = CognitiveState::uniform(3, vec!["A".to_string(), "B".to_string(), "AB".to_string()]); + let initial = + CognitiveState::uniform(3, vec!["A".to_string(), "B".to_string(), "AB".to_string()]); let mut dm = InterferenceDecisionMaker::new(initial); // High overlap → conjunction can exceed individual @@ -338,12 +338,15 @@ mod tests { "bank_teller", "feminist", "feminist_bank_teller", - 0.8 // High semantic overlap with "feminist" + 0.8, // High semantic overlap with "feminist" ); // P(feminist ∧ bank_teller) can be > P(bank_teller) with high overlap // This reproduces the empirical "fallacy" - println!("P(bank): {}, P(fem): {}, P(both): {}", probs[0], probs[1], probs[2]); + println!( + "P(bank): {}, P(fem): {}, P(both): {}", + probs[0], probs[1], probs[2] + ); } #[test] @@ -352,8 +355,14 @@ mod tests { let pattern = interference_pattern(phases); // Should oscillate between 0 and 1 - let max = pattern.iter().max_by(|a, b| a.partial_cmp(b).unwrap()).unwrap(); - let min = pattern.iter().min_by(|a, b| a.partial_cmp(b).unwrap()).unwrap(); + let max = pattern + .iter() + .max_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap(); + let min = pattern + .iter() + .min_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap(); assert!(*max <= 1.0); assert!(*min >= 0.0); @@ -362,18 +371,24 @@ mod tests { #[test] fn test_prisoners_dilemma() { - let initial = CognitiveState::uniform(4, vec![ - "CC".to_string(), - "DD".to_string(), - "CD".to_string(), - "DC".to_string(), - ]); + let initial = CognitiveState::uniform( + 4, + vec![ + "CC".to_string(), + "DD".to_string(), + "CD".to_string(), + "DC".to_string(), + ], + ); let mut dm = InterferenceDecisionMaker::new(initial); // High entanglement → more cooperation let (decision, p_coop, payoff) = dm.quantum_prisoners_dilemma("cooperate", 0.9); - println!("Decision: {}, P(cooperate): {}, Payoff: {}", decision, p_coop, payoff); + println!( + "Decision: {}, P(cooperate): {}, Payoff: {}", + decision, p_coop, payoff + ); // Should have higher cooperation than classical (0.5) assert!(p_coop > 0.5); @@ -387,6 +402,6 @@ mod tests { let phase = semantic_phase(&v1, &v2); // Orthogonal vectors → π/2 phase - assert!((phase - PI/2.0).abs() < 1e-6); + assert!((phase - PI / 2.0).abs() < 1e-6); } } diff --git a/examples/exo-ai-2025/research/02-quantum-superposition/src/lib.rs b/examples/exo-ai-2025/research/02-quantum-superposition/src/lib.rs index 867de15bd..7a35b1d62 100644 --- a/examples/exo-ai-2025/research/02-quantum-superposition/src/lib.rs +++ b/examples/exo-ai-2025/research/02-quantum-superposition/src/lib.rs @@ -47,31 +47,20 @@ //! //! **Not for production use** - for research and validation only. -pub mod quantum_cognitive_state; -pub mod interference_decision; pub mod collapse_attention; +pub mod interference_decision; +pub mod quantum_cognitive_state; pub mod simd_ops; // Re-export main types pub use quantum_cognitive_state::{ - CognitiveState, - Amplitude, - SuperpositionBuilder, - tensor_product, - interference_visibility, + interference_visibility, tensor_product, Amplitude, CognitiveState, SuperpositionBuilder, }; -pub use interference_decision::{ - InterferenceDecisionMaker, - interference_pattern, - semantic_phase, -}; +pub use interference_decision::{interference_pattern, semantic_phase, InterferenceDecisionMaker}; pub use collapse_attention::{ - AttentionOperator, - DecoherenceModel, - ConsciousnessThreshold, - quantum_zeno_effect, + quantum_zeno_effect, AttentionOperator, ConsciousnessThreshold, DecoherenceModel, }; /// CAFT version and theoretical framework info @@ -94,13 +83,13 @@ mod integration_tests { // Make decision using interference let mut dm = InterferenceDecisionMaker::new(state.clone()); - let (decision, prob, interference) = dm.two_alternative_choice( - "cooperate", - "defect", - std::f64::consts::PI / 4.0 - ); + let (decision, prob, interference) = + dm.two_alternative_choice("cooperate", "defect", std::f64::consts::PI / 4.0); - println!("Decision: {}, Probability: {}, Interference: {}", decision, prob, interference); + println!( + "Decision: {}, Probability: {}, Interference: {}", + decision, prob, interference + ); // Apply attention let mut attention = AttentionOperator::full_attention(0, 2, 10.0); diff --git a/examples/exo-ai-2025/research/02-quantum-superposition/src/quantum_cognitive_state.rs b/examples/exo-ai-2025/research/02-quantum-superposition/src/quantum_cognitive_state.rs index 893eb7e0b..0f5fc4167 100644 --- a/examples/exo-ai-2025/research/02-quantum-superposition/src/quantum_cognitive_state.rs +++ b/examples/exo-ai-2025/research/02-quantum-superposition/src/quantum_cognitive_state.rs @@ -40,7 +40,11 @@ impl CognitiveState { /// ); /// ``` pub fn new(amplitudes: Vec, labels: Vec) -> Self { - assert_eq!(amplitudes.len(), labels.len(), "Amplitude and label count mismatch"); + assert_eq!( + amplitudes.len(), + labels.len(), + "Amplitude and label count mismatch" + ); let mut state = CognitiveState { amplitudes, @@ -88,10 +92,7 @@ impl CognitiveState { /// /// Returns vector where P[i] = |α_i|² pub fn probabilities(&self) -> Vec { - self.amplitudes - .iter() - .map(|a| a.norm_sqr()) - .collect() + self.amplitudes.iter().map(|a| a.norm_sqr()).collect() } /// Inner product ⟨φ|ψ⟩ with another state @@ -132,18 +133,19 @@ impl CognitiveState { cumulative += p; if r < cumulative { // Collapse to state i - let collapsed = CognitiveState::definite( - i, - self.amplitudes.len(), - self.labels.clone() - ); + let collapsed = + CognitiveState::definite(i, self.amplitudes.len(), self.labels.clone()); return (i, collapsed, p); } } // Fallback (should never reach due to normalization) let last = probs.len() - 1; - (last, CognitiveState::definite(last, self.amplitudes.len(), self.labels.clone()), probs[last]) + ( + last, + CognitiveState::definite(last, self.amplitudes.len(), self.labels.clone()), + probs[last], + ) } /// Weak measurement with strength parameter @@ -154,7 +156,8 @@ impl CognitiveState { use rand_distr::{Distribution, Normal}; // Calculate expectation value - let expectation: f64 = self.amplitudes + let expectation: f64 = self + .amplitudes .iter() .zip(observable) .map(|(a, &o)| a.norm_sqr() * o) @@ -196,10 +199,7 @@ impl CognitiveState { /// /// Measures effective number of states in superposition (1 = pure, N = uniform) pub fn participation_ratio(&self) -> f64 { - let sum_p4: f64 = self.probabilities() - .iter() - .map(|&p| p * p) - .sum(); + let sum_p4: f64 = self.probabilities().iter().map(|&p| p * p).sum(); if sum_p4 > 1e-10 { 1.0 / sum_p4 @@ -256,7 +256,8 @@ impl SuperpositionBuilder { /// Add a basis state with magnitude and phase pub fn add_polar(mut self, magnitude: f64, phase: f64, label: String) -> Self { - self.amplitudes.push(Complex64::from_polar(magnitude, phase)); + self.amplitudes + .push(Complex64::from_polar(magnitude, phase)); self.labels.push(label); self } @@ -313,7 +314,7 @@ mod tests { fn test_normalization() { let psi = CognitiveState::new( vec![Complex64::new(3.0, 0.0), Complex64::new(0.0, 4.0)], - vec!["A".to_string(), "B".to_string()] + vec!["A".to_string(), "B".to_string()], ); assert!((psi.norm() - 1.0).abs() < 1e-10); @@ -323,7 +324,7 @@ mod tests { fn test_born_rule() { let psi = CognitiveState::new( vec![Complex64::new(0.6, 0.0), Complex64::new(0.0, 0.8)], - vec!["A".to_string(), "B".to_string()] + vec!["A".to_string(), "B".to_string()], ); let probs = psi.probabilities(); @@ -349,11 +350,16 @@ mod tests { #[test] fn test_entropy() { // Pure state: S = 0 - let pure = CognitiveState::definite(0, 3, vec!["A".to_string(), "B".to_string(), "C".to_string()]); + let pure = CognitiveState::definite( + 0, + 3, + vec!["A".to_string(), "B".to_string(), "C".to_string()], + ); assert!(pure.von_neumann_entropy() < 1e-10); // Maximally mixed: S = log(N) - let mixed = CognitiveState::uniform(3, vec!["A".to_string(), "B".to_string(), "C".to_string()]); + let mixed = + CognitiveState::uniform(3, vec!["A".to_string(), "B".to_string(), "C".to_string()]); assert!((mixed.von_neumann_entropy() - (3.0_f64).ln()).abs() < 1e-6); } @@ -361,7 +367,7 @@ mod tests { fn test_superposition_builder() { let psi = SuperpositionBuilder::new() .add_real(0.6, "happy".to_string()) - .add_polar(0.8, PI/2.0, "sad".to_string()) + .add_polar(0.8, PI / 2.0, "sad".to_string()) .build(); assert_eq!(psi.dimension(), 2); diff --git a/examples/exo-ai-2025/research/02-quantum-superposition/src/simd_ops.rs b/examples/exo-ai-2025/research/02-quantum-superposition/src/simd_ops.rs index f6ac81870..2859248b2 100644 --- a/examples/exo-ai-2025/research/02-quantum-superposition/src/simd_ops.rs +++ b/examples/exo-ai-2025/research/02-quantum-superposition/src/simd_ops.rs @@ -96,10 +96,8 @@ pub fn simd_norm(amplitudes: &[Complex64]) -> f64 { for chunk in chunks { // Compiler can vectorize this efficiently - sum += chunk[0].norm_sqr() - + chunk[1].norm_sqr() - + chunk[2].norm_sqr() - + chunk[3].norm_sqr(); + sum += + chunk[0].norm_sqr() + chunk[1].norm_sqr() + chunk[2].norm_sqr() + chunk[3].norm_sqr(); } for amp in remainder { @@ -148,7 +146,7 @@ pub fn simd_entropy(amplitudes: &[Complex64]) -> f64 { pub fn simd_interference_pattern( amplitude1: Complex64, amplitude2: Complex64, - phases: &[f64] + phases: &[f64], ) -> Vec { let mut pattern = Vec::with_capacity(phases.len()); @@ -191,16 +189,24 @@ pub fn simd_weighted_sample(weights: &[f64], random_value: f64) -> usize { // Vectorized cumulative sum cumulative += chunk[0]; - if random_value < cumulative { return chunk_idx * 4; } + if random_value < cumulative { + return chunk_idx * 4; + } cumulative += chunk[1]; - if random_value < cumulative { return chunk_idx * 4 + 1; } + if random_value < cumulative { + return chunk_idx * 4 + 1; + } cumulative += chunk[2]; - if random_value < cumulative { return chunk_idx * 4 + 2; } + if random_value < cumulative { + return chunk_idx * 4 + 2; + } cumulative += chunk[3]; - if random_value < cumulative { return chunk_idx * 4 + 3; } + if random_value < cumulative { + return chunk_idx * 4 + 3; + } index = (chunk_idx + 1) * 4; } @@ -220,10 +226,7 @@ pub fn simd_weighted_sample(weights: &[f64], random_value: f64) -> usize { /// Computes ψ₁ ⊗ ψ₂ with vectorized outer product operations. /// 3-4x speedup for large composite systems. #[inline] -pub fn simd_tensor_product( - amplitudes1: &[Complex64], - amplitudes2: &[Complex64] -) -> Vec { +pub fn simd_tensor_product(amplitudes1: &[Complex64], amplitudes2: &[Complex64]) -> Vec { let n1 = amplitudes1.len(); let n2 = amplitudes2.len(); let mut result = Vec::with_capacity(n1 * n2); @@ -255,7 +258,7 @@ pub fn simd_tensor_product( /// multiple amplitude pairs simultaneously. Used for semantic similarity. pub fn simd_multi_path_interference( amplitudes: &[Complex64], - reference_phases: &[f64] + reference_phases: &[f64], ) -> Vec { assert_eq!(amplitudes.len(), reference_phases.len()); let n = amplitudes.len(); @@ -268,10 +271,8 @@ pub fn simd_multi_path_interference( interference_matrix.push(0.0); } else { let phase_diff = reference_phases[i] - reference_phases[j]; - let cross_term = 2.0 * amplitudes[i].re * amplitudes[j].re - * phase_diff.cos() - - 2.0 * amplitudes[i].im * amplitudes[j].im - * phase_diff.sin(); + let cross_term = 2.0 * amplitudes[i].re * amplitudes[j].re * phase_diff.cos() + - 2.0 * amplitudes[i].im * amplitudes[j].im * phase_diff.sin(); interference_matrix.push(cross_term); } } @@ -316,10 +317,7 @@ mod tests { #[test] fn test_simd_norm() { - let amps = vec![ - Complex64::new(0.6, 0.0), - Complex64::new(0.0, 0.8), - ]; + let amps = vec![Complex64::new(0.6, 0.0), Complex64::new(0.0, 0.8)]; let norm = simd_norm(&s); assert!((norm - 1.0).abs() < 1e-10); diff --git a/examples/exo-ai-2025/research/03-time-crystal-cognition/benches/time_crystal_bench.rs b/examples/exo-ai-2025/research/03-time-crystal-cognition/benches/time_crystal_bench.rs index 449001d8c..f989e7f2d 100644 --- a/examples/exo-ai-2025/research/03-time-crystal-cognition/benches/time_crystal_bench.rs +++ b/examples/exo-ai-2025/research/03-time-crystal-cognition/benches/time_crystal_bench.rs @@ -1,8 +1,8 @@ // Benchmarks for Time Crystal Cognition -use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId}; -use time_crystal_cognition::*; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use ndarray::Array1; +use time_crystal_cognition::*; fn bench_discrete_time_crystal(c: &mut Criterion) { let mut group = c.benchmark_group("discrete_time_crystal"); diff --git a/examples/exo-ai-2025/research/03-time-crystal-cognition/src/discrete_time_crystal.rs b/examples/exo-ai-2025/research/03-time-crystal-cognition/src/discrete_time_crystal.rs index 8c8fab378..612112aa1 100644 --- a/examples/exo-ai-2025/research/03-time-crystal-cognition/src/discrete_time_crystal.rs +++ b/examples/exo-ai-2025/research/03-time-crystal-cognition/src/discrete_time_crystal.rs @@ -63,12 +63,8 @@ impl DiscreteTimeCrystal { let n = config.n_oscillators; // Initialize positions and velocities randomly - let positions = Array1::from_vec( - (0..n).map(|_| rng.gen_range(-1.0..1.0)).collect() - ); - let velocities = Array1::from_vec( - (0..n).map(|_| rng.gen_range(-0.1..0.1)).collect() - ); + let positions = Array1::from_vec((0..n).map(|_| rng.gen_range(-1.0..1.0)).collect()); + let velocities = Array1::from_vec((0..n).map(|_| rng.gen_range(-0.1..0.1)).collect()); // Create asymmetric coupling matrix let coupling_matrix = Self::generate_asymmetric_coupling(n, &mut rng); @@ -136,9 +132,7 @@ impl DiscreteTimeCrystal { // Velocity force: -x - γv + J*coupling + A*drive + noise let noise = self.rng.gen_range(-1.0..1.0) * self.config.noise_amplitude; - forces_vel[i] = - -self.positions[i] - - self.config.dissipation * self.velocities[i] + forces_vel[i] = -self.positions[i] - self.config.dissipation * self.velocities[i] + self.config.coupling_strength * coupling_force + drive + noise; @@ -176,52 +170,56 @@ impl DiscreteTimeCrystal { let omega_0 = 2.0 * PI * self.config.drive_frequency; let n = self.config.n_oscillators; - trajectory.iter().enumerate().map(|(step, positions)| { - let _t = step as f64 * self.config.dt; + trajectory + .iter() + .enumerate() + .map(|(step, positions)| { + let _t = step as f64 * self.config.dt; - // Compute phases relative to drive - let mut sum_real = 0.0; - let mut sum_imag = 0.0; + // Compute phases relative to drive + let mut sum_real = 0.0; + let mut sum_imag = 0.0; - for i in 0..n { - // Phase of oscillator i - let phase = positions[i].atan2(1.0); // Simplified phase extraction - let arg = k as f64 * omega_0 * phase; - sum_real += arg.cos(); - sum_imag += arg.sin(); - } + for i in 0..n { + // Phase of oscillator i + let phase = positions[i].atan2(1.0); // Simplified phase extraction + let arg = k as f64 * omega_0 * phase; + sum_real += arg.cos(); + sum_imag += arg.sin(); + } - // Order parameter: || - let m_k = ((sum_real / n as f64).powi(2) + (sum_imag / n as f64).powi(2)).sqrt(); - m_k - }).collect() + // Order parameter: || + let m_k = ((sum_real / n as f64).powi(2) + (sum_imag / n as f64).powi(2)).sqrt(); + m_k + }) + .collect() } /// Compute power spectral density to detect subharmonics pub fn compute_psd(&self, signal: &[f64], sample_rate: f64) -> (Vec, Vec) { // Simple FFT-based PSD - use rustfft::{FftPlanner, num_complex::Complex}; + use rustfft::{num_complex::Complex, FftPlanner}; let n = signal.len(); let mut planner = FftPlanner::new(); let fft = planner.plan_fft_forward(n); // Convert to complex - let mut buffer: Vec> = signal.iter() - .map(|&x| Complex { re: x, im: 0.0 }) - .collect(); + let mut buffer: Vec> = + signal.iter().map(|&x| Complex { re: x, im: 0.0 }).collect(); // Apply FFT fft.process(&mut buffer); // Compute power - let power: Vec = buffer.iter() + let power: Vec = buffer + .iter() .take(n / 2) .map(|c| (c.re * c.re + c.im * c.im) / n as f64) .collect(); // Frequency bins - let freqs: Vec = (0..n/2) + let freqs: Vec = (0..n / 2) .map(|i| i as f64 * sample_rate / n as f64) .collect(); @@ -231,7 +229,8 @@ impl DiscreteTimeCrystal { /// Detect period-doubling by comparing power at f and f/2 pub fn detect_period_doubling(&self, trajectory: &[Array1]) -> (f64, bool) { // Average activity across all oscillators - let signal: Vec = trajectory.iter() + let signal: Vec = trajectory + .iter() .map(|positions| positions.mean().unwrap()) .collect(); @@ -245,12 +244,16 @@ impl DiscreteTimeCrystal { // Find power at these frequencies (within tolerance) let tol = 0.5; // Hz tolerance - let p_drive: f64 = freqs.iter().zip(&power) + let p_drive: f64 = freqs + .iter() + .zip(&power) .filter(|(f, _)| (*f - drive_freq).abs() < tol) .map(|(_, p)| p) .fold(0.0_f64, |acc, &p| acc.max(p)); - let p_half: f64 = freqs.iter().zip(&power) + let p_half: f64 = freqs + .iter() + .zip(&power) .filter(|(f, _)| (*f - half_freq).abs() < tol) .map(|(_, p)| p) .fold(0.0_f64, |acc, &p| acc.max(p)); @@ -275,17 +278,17 @@ pub mod analysis { pub fn autocorrelation(signal: &[f64], max_lag: usize) -> Vec { let n = signal.len(); let mean = signal.iter().sum::() / n as f64; - let variance = signal.iter() - .map(|&x| (x - mean).powi(2)) - .sum::() / n as f64; + let variance = signal.iter().map(|&x| (x - mean).powi(2)).sum::() / n as f64; - (0..max_lag).map(|lag| { - let mut sum = 0.0; - for i in 0..(n - lag) { - sum += (signal[i] - mean) * (signal[i + lag] - mean); - } - sum / ((n - lag) as f64 * variance) - }).collect() + (0..max_lag) + .map(|lag| { + let mut sum = 0.0; + for i in 0..(n - lag) { + sum += (signal[i] - mean) * (signal[i + lag] - mean); + } + sum / ((n - lag) as f64 * variance) + }) + .collect() } /// Fit autocorrelation to detect power-law vs exponential decay @@ -295,7 +298,8 @@ pub mod analysis { // Log-linear fit for exponential: log(C) ~ -τ/τ_c let lags: Vec = (1..autocorr.len()).map(|i| i as f64).collect(); - let log_autocorr: Vec = autocorr.iter() + let log_autocorr: Vec = autocorr + .iter() .skip(1) .map(|&c| c.max(1e-10).ln()) .collect(); @@ -309,7 +313,11 @@ pub mod analysis { // Compare R^2 values let is_power_law = power_law_fit.1 > exp_fit.1; // Better R^2 for power law - let exponent = if is_power_law { -power_law_fit.0 } else { -exp_fit.0 }; + let exponent = if is_power_law { + -power_law_fit.0 + } else { + -exp_fit.0 + }; (is_power_law, exponent) } @@ -327,7 +335,9 @@ pub mod analysis { // Compute R^2 let mean_y = sum_y / n; let ss_tot: f64 = y.iter().map(|&yi| (yi - mean_y).powi(2)).sum(); - let ss_res: f64 = x.iter().zip(y) + let ss_res: f64 = x + .iter() + .zip(y) .map(|(&xi, &yi)| { let pred = slope * xi + (sum_y - slope * sum_x) / n; (yi - pred).powi(2) diff --git a/examples/exo-ai-2025/research/03-time-crystal-cognition/src/floquet_cognition.rs b/examples/exo-ai-2025/research/03-time-crystal-cognition/src/floquet_cognition.rs index e78153b8e..db3b5d593 100644 --- a/examples/exo-ai-2025/research/03-time-crystal-cognition/src/floquet_cognition.rs +++ b/examples/exo-ai-2025/research/03-time-crystal-cognition/src/floquet_cognition.rs @@ -25,7 +25,7 @@ impl Default for FloquetConfig { fn default() -> Self { Self { n_neurons: 100, - tau: 0.01, // 10ms + tau: 0.01, // 10ms drive_period: 0.125, // 125ms = 8 Hz theta drive_amplitude: 1.0, noise_level: 0.01, @@ -54,9 +54,7 @@ impl FloquetCognitiveSystem { assert_eq!(weights.shape(), &[n, n], "Weight matrix must be n x n"); // Initialize firing rates randomly - let firing_rates = Array1::from_vec( - (0..n).map(|_| rand::random::() * 0.1).collect() - ); + let firing_rates = Array1::from_vec((0..n).map(|_| rand::random::() * 0.1).collect()); Self { config, @@ -115,11 +113,9 @@ impl FloquetCognitiveSystem { let noise = rand::random::() * self.config.noise_level; // Neural dynamics: τ dr/dt = -r + f(Wr + I) - derivatives[i] = ( - -self.firing_rates[i] - + Self::activation(recurrent_input + external) - + noise - ) / self.config.tau; + derivatives[i] = + (-self.firing_rates[i] + Self::activation(recurrent_input + external) + noise) + / self.config.tau; } derivatives @@ -140,12 +136,8 @@ impl FloquetCognitiveSystem { let steps_per_period = (period / self.config.dt) as usize; let total_steps = steps_per_period * n_periods; - let mut trajectory = FloquetTrajectory::new( - self.config.n_neurons, - total_steps, - self.config.dt, - period, - ); + let mut trajectory = + FloquetTrajectory::new(self.config.n_neurons, total_steps, self.config.dt, period); for step in 0..total_steps { self.step(); @@ -205,7 +197,8 @@ impl FloquetCognitiveSystem { let (_, eigenvalues) = self.compute_monodromy_matrix(); // Look for eigenvalue near -1 (period-doubling) - let min_dist_to_minus_one = eigenvalues.iter() + let min_dist_to_minus_one = eigenvalues + .iter() .map(|&lambda| (lambda + 1.0).abs()) .fold(f64::INFINITY, f64::min); @@ -254,7 +247,7 @@ impl FloquetTrajectory { for (i, &phase) in self.drive_phases.iter().enumerate() { if i > 0 { - let prev_phase = self.drive_phases[i-1]; + let prev_phase = self.drive_phases[i - 1]; // Detect crossing of threshold phase if prev_phase < phase_threshold && phase >= phase_threshold { section.push(self.firing_rates[i].clone()); @@ -276,16 +269,16 @@ impl FloquetTrajectory { // Compute distances between consecutive points let mut distances = Vec::new(); - for i in 0..section.len()-1 { - let dist = (§ion[i] - §ion[i+1]).mapv(|x| x*x).sum().sqrt(); + for i in 0..section.len() - 1 { + let dist = (§ion[i] - §ion[i + 1]).mapv(|x| x * x).sum().sqrt(); distances.push(dist); } // In period-doubling, alternating distances: small, large, small, large... // Check for this pattern let mut alternates = 0; - for i in 0..distances.len()-1 { - if (distances[i] < distances[i+1]) != (i % 2 == 0) { + for i in 0..distances.len() - 1 { + if (distances[i] < distances[i + 1]) != (i % 2 == 0) { alternates += 1; } } @@ -297,30 +290,32 @@ impl FloquetTrajectory { /// Compute spectral analysis pub fn compute_power_spectrum(&self) -> (Vec, Vec) { // Average firing rate across all neurons - let signal: Vec = self.firing_rates.iter() + let signal: Vec = self + .firing_rates + .iter() .map(|rates| rates.mean().unwrap()) .collect(); // FFT - use rustfft::{FftPlanner, num_complex::Complex}; + use rustfft::{num_complex::Complex, FftPlanner}; let n = signal.len(); let mut planner = FftPlanner::new(); let fft = planner.plan_fft_forward(n); - let mut buffer: Vec> = signal.iter() - .map(|&x| Complex { re: x, im: 0.0 }) - .collect(); + let mut buffer: Vec> = + signal.iter().map(|&x| Complex { re: x, im: 0.0 }).collect(); fft.process(&mut buffer); - let power: Vec = buffer.iter() + let power: Vec = buffer + .iter() .take(n / 2) .map(|c| (c.re * c.re + c.im * c.im) / n as f64) .collect(); let sample_rate = 1.0 / self.dt; - let freqs: Vec = (0..n/2) + let freqs: Vec = (0..n / 2) .map(|i| i as f64 * sample_rate / n as f64) .collect(); @@ -331,24 +326,28 @@ impl FloquetTrajectory { pub fn compute_order_parameter(&self, k: usize) -> Vec { let omega_0 = 2.0 * PI / self.drive_period; - self.firing_rates.iter().enumerate().map(|(step, rates)| { - let _t = step as f64 * self.dt; - let n = self.n_neurons; + self.firing_rates + .iter() + .enumerate() + .map(|(step, rates)| { + let _t = step as f64 * self.dt; + let n = self.n_neurons; - // Phases of each neuron - let mut sum_real = 0.0; - let mut sum_imag = 0.0; + // Phases of each neuron + let mut sum_real = 0.0; + let mut sum_imag = 0.0; - for i in 0..n { - // Simple phase extraction (more sophisticated: use Hilbert transform) - let phase = rates[i] * PI; // Map firing rate to phase - let arg = k as f64 * omega_0 * phase; - sum_real += arg.cos(); - sum_imag += arg.sin(); - } + for i in 0..n { + // Simple phase extraction (more sophisticated: use Hilbert transform) + let phase = rates[i] * PI; // Map firing rate to phase + let arg = k as f64 * omega_0 * phase; + sum_real += arg.cos(); + sum_imag += arg.sin(); + } - ((sum_real / n as f64).powi(2) + (sum_imag / n as f64).powi(2)).sqrt() - }).collect() + ((sum_real / n as f64).powi(2) + (sum_imag / n as f64).powi(2)).sqrt() + }) + .collect() } } @@ -371,14 +370,22 @@ pub struct PhaseDiagram { } impl PhaseDiagram { - pub fn new(amp_min: f64, amp_max: f64, n_amp: usize, - coupling_min: f64, coupling_max: f64, n_coupling: usize) -> Self { + pub fn new( + amp_min: f64, + amp_max: f64, + n_amp: usize, + coupling_min: f64, + coupling_max: f64, + n_coupling: usize, + ) -> Self { let amplitude_range = (0..n_amp) .map(|i| amp_min + (amp_max - amp_min) * i as f64 / (n_amp - 1) as f64) .collect(); let coupling_range = (0..n_coupling) - .map(|i| coupling_min + (coupling_max - coupling_min) * i as f64 / (n_coupling - 1) as f64) + .map(|i| { + coupling_min + (coupling_max - coupling_min) * i as f64 / (n_coupling - 1) as f64 + }) .collect(); let results = vec![vec![false; n_coupling]; n_amp]; @@ -398,7 +405,9 @@ impl PhaseDiagram { config.drive_amplitude = amplitude; let weights = FloquetCognitiveSystem::generate_asymmetric_weights( - config.n_neurons, 0.2, coupling + config.n_neurons, + 0.2, + coupling, ); let mut system = FloquetCognitiveSystem::new(config, weights); @@ -428,9 +437,11 @@ impl PhaseDiagram { for _ in &self.coupling_range { print!("-"); } - println!("\n {:.2} ... {:.2}", - self.coupling_range[0], - self.coupling_range[self.coupling_range.len()-1]); + println!( + "\n {:.2} ... {:.2}", + self.coupling_range[0], + self.coupling_range[self.coupling_range.len() - 1] + ); } } @@ -441,9 +452,8 @@ mod tests { #[test] fn test_floquet_system() { let config = FloquetConfig::default(); - let weights = FloquetCognitiveSystem::generate_asymmetric_weights( - config.n_neurons, 0.2, 1.0 - ); + let weights = + FloquetCognitiveSystem::generate_asymmetric_weights(config.n_neurons, 0.2, 1.0); let mut system = FloquetCognitiveSystem::new(config, weights); let trajectory = system.run(10); // 10 periods @@ -454,9 +464,8 @@ mod tests { #[test] fn test_poincare_section() { let config = FloquetConfig::default(); - let weights = FloquetCognitiveSystem::generate_asymmetric_weights( - config.n_neurons, 0.2, 1.0 - ); + let weights = + FloquetCognitiveSystem::generate_asymmetric_weights(config.n_neurons, 0.2, 1.0); let mut system = FloquetCognitiveSystem::new(config, weights); let trajectory = system.run(10); diff --git a/examples/exo-ai-2025/research/03-time-crystal-cognition/src/lib.rs b/examples/exo-ai-2025/research/03-time-crystal-cognition/src/lib.rs index cf9b2e8a5..f832f1d73 100644 --- a/examples/exo-ai-2025/research/03-time-crystal-cognition/src/lib.rs +++ b/examples/exo-ai-2025/research/03-time-crystal-cognition/src/lib.rs @@ -3,11 +3,17 @@ pub mod discrete_time_crystal; pub mod floquet_cognition; -pub mod temporal_memory; pub mod simd_optimizations; +pub mod temporal_memory; // Re-export main types -pub use discrete_time_crystal::{DiscreteTimeCrystal, DTCConfig}; -pub use floquet_cognition::{FloquetCognitiveSystem, FloquetConfig, FloquetTrajectory, PhaseDiagram}; -pub use temporal_memory::{TemporalMemory, TemporalMemoryConfig, MemoryItem, MemoryStats, WorkingMemoryTask}; -pub use simd_optimizations::{SimdDTC, SimdFloquet, HierarchicalTimeCrystal, TopologicalTimeCrystal}; +pub use discrete_time_crystal::{DTCConfig, DiscreteTimeCrystal}; +pub use floquet_cognition::{ + FloquetCognitiveSystem, FloquetConfig, FloquetTrajectory, PhaseDiagram, +}; +pub use simd_optimizations::{ + HierarchicalTimeCrystal, SimdDTC, SimdFloquet, TopologicalTimeCrystal, +}; +pub use temporal_memory::{ + MemoryItem, MemoryStats, TemporalMemory, TemporalMemoryConfig, WorkingMemoryTask, +}; diff --git a/examples/exo-ai-2025/research/03-time-crystal-cognition/src/simd_optimizations.rs b/examples/exo-ai-2025/research/03-time-crystal-cognition/src/simd_optimizations.rs index fa25a9a0e..f7b764b46 100644 --- a/examples/exo-ai-2025/research/03-time-crystal-cognition/src/simd_optimizations.rs +++ b/examples/exo-ai-2025/research/03-time-crystal-cognition/src/simd_optimizations.rs @@ -128,11 +128,10 @@ impl SimdFloquet { let phase_offsets = Array1::from_vec( (0..self.n_neurons) .map(|i| 2.0 * PI * i as f64 / self.n_neurons as f64) - .collect() + .collect(), ); - let external_inputs = phase_offsets.mapv(|offset| { - self.drive_amplitude * (self.drive_phase + offset).cos() - }); + let external_inputs = + phase_offsets.mapv(|offset| self.drive_amplitude * (self.drive_phase + offset).cos()); // Vectorized recurrent input: W * r let recurrent_inputs = self.weights.dot(&self.firing_rates); @@ -181,7 +180,12 @@ pub struct HierarchicalTimeCrystal { impl HierarchicalTimeCrystal { /// Create hierarchical time crystal with period multiplication /// Each level oscillates at frequency f/k for k = 1, 2, 3, ... - pub fn new(n_levels: usize, oscillators_per_level: usize, base_frequency: f64, dt: f64) -> Self { + pub fn new( + n_levels: usize, + oscillators_per_level: usize, + base_frequency: f64, + dt: f64, + ) -> Self { let total_oscillators = n_levels * oscillators_per_level; // Each level has a different frequency: f, f/2, f/3, f/4, ... @@ -259,21 +263,25 @@ impl HierarchicalTimeCrystal { /// Compute hierarchical order parameter /// Measures synchronization across different temporal scales pub fn hierarchical_order_parameter(&self) -> Vec { - self.levels.iter().enumerate().map(|(level, positions)| { - let n = positions.len(); - let omega = 2.0 * PI * self.level_frequencies[level]; + self.levels + .iter() + .enumerate() + .map(|(level, positions)| { + let n = positions.len(); + let omega = 2.0 * PI * self.level_frequencies[level]; - let mut sum_real = 0.0; - let mut sum_imag = 0.0; + let mut sum_real = 0.0; + let mut sum_imag = 0.0; - for &pos in positions { - let phase = pos * PI; - sum_real += (omega * phase).cos(); - sum_imag += (omega * phase).sin(); - } + for &pos in positions { + let phase = pos * PI; + sum_real += (omega * phase).cos(); + sum_imag += (omega * phase).sin(); + } - ((sum_real / n as f64).powi(2) + (sum_imag / n as f64).powi(2)).sqrt() - }).collect() + ((sum_real / n as f64).powi(2) + (sum_imag / n as f64).powi(2)).sqrt() + }) + .collect() } /// Novel discovery: Temporal multiplexing capacity @@ -281,10 +289,12 @@ impl HierarchicalTimeCrystal { pub fn temporal_multiplexing_capacity(&self) -> usize { // Each level provides log2(period_multiplier) bits // Total capacity is sum across levels - (1..=self.n_levels).map(|k| { - // Period k provides log2(k) temporal slots - (k as f64).log2().ceil() as usize - }).sum() + (1..=self.n_levels) + .map(|k| { + // Period k provides log2(k) temporal slots + (k as f64).log2().ceil() as usize + }) + .sum() } } @@ -306,21 +316,21 @@ impl TopologicalTimeCrystal { let mut hopping_matrix = Array2::zeros((n_sites, n_sites)); // SSH-like model: alternating hopping strengths - for i in 0..n_sites-1 { + for i in 0..n_sites - 1 { let hop = if i % 2 == 0 { hopping_strength * 1.5 // Strong bond } else { hopping_strength * 0.5 // Weak bond }; - hopping_matrix[[i, i+1]] = hop; - hopping_matrix[[i+1, i]] = hop; + hopping_matrix[[i, i + 1]] = hop; + hopping_matrix[[i + 1, i]] = hop; } // Edge protection: reduce coupling at boundaries hopping_matrix[[0, 1]] *= edge_protection; hopping_matrix[[1, 0]] *= edge_protection; - hopping_matrix[[n_sites-2, n_sites-1]] *= edge_protection; - hopping_matrix[[n_sites-1, n_sites-2]] *= edge_protection; + hopping_matrix[[n_sites - 2, n_sites - 1]] *= edge_protection; + hopping_matrix[[n_sites - 1, n_sites - 2]] *= edge_protection; Self { n_sites, @@ -358,7 +368,9 @@ impl TopologicalTimeCrystal { /// Measure edge localization (topological protection metric) pub fn edge_localization(&self) -> f64 { let edge_amplitude = self.positions[0].abs() + self.positions[self.n_sites - 1].abs(); - let bulk_amplitude: f64 = self.positions.iter() + let bulk_amplitude: f64 = self + .positions + .iter() .skip(1) .take(self.n_sites - 2) .map(|&x| x.abs()) diff --git a/examples/exo-ai-2025/research/03-time-crystal-cognition/src/temporal_memory.rs b/examples/exo-ai-2025/research/03-time-crystal-cognition/src/temporal_memory.rs index 36d1489c4..e5fb00c36 100644 --- a/examples/exo-ai-2025/research/03-time-crystal-cognition/src/temporal_memory.rs +++ b/examples/exo-ai-2025/research/03-time-crystal-cognition/src/temporal_memory.rs @@ -86,18 +86,16 @@ impl TemporalMemory { let hc_neurons = Array1::zeros(config.hc_neurons); // Asymmetric weights for PFC (enable limit cycles) - let pfc_weights = Self::generate_limit_cycle_weights( - config.pfc_neurons, 0.3, 1.0 - ); + let pfc_weights = Self::generate_limit_cycle_weights(config.pfc_neurons, 0.3, 1.0); // Symmetric weights for HC (content storage) - let hc_weights = Self::generate_symmetric_weights( - config.hc_neurons, 0.2, 0.8 - ); + let hc_weights = Self::generate_symmetric_weights(config.hc_neurons, 0.2, 0.8); // Coupling weights let pfc_to_hc = Self::generate_coupling_weights( - config.pfc_neurons, config.hc_neurons, config.pfc_hc_coupling + config.pfc_neurons, + config.hc_neurons, + config.pfc_hc_coupling, ); let hc_to_pfc = pfc_to_hc.t().to_owned(); @@ -141,7 +139,7 @@ impl TemporalMemory { use rand::Rng; for i in 0..n { - for j in i+1..n { + for j in i + 1..n { if rng.gen::() < sparsity { let w = rng.gen_range(0.0..strength); weights[[i, j]] = w; @@ -161,7 +159,8 @@ impl TemporalMemory { use rand::Rng; for i in 0..n_to { for j in 0..n_from { - if rng.gen::() < 0.1 { // Sparse coupling + if rng.gen::() < 0.1 { + // Sparse coupling weights[[i, j]] = rng.gen_range(-strength..strength); } } @@ -245,7 +244,8 @@ impl TemporalMemory { // Update energy (metabolic supply - dissipation) let energy_cost = self.compute_energy_cost(); - self.energy += (self.config.energy_rate - energy_cost - self.config.dissipation) * self.config.dt; + self.energy += + (self.config.energy_rate - energy_cost - self.config.dissipation) * self.config.dt; self.energy = self.energy.clamp(0.0, 2.0); // If energy too low, time crystal collapses @@ -372,7 +372,10 @@ impl TemporalMemory { if self.memory_items.is_empty() { 0.0 } else { - self.memory_items.iter().map(|item| item.strength).sum::() + self.memory_items + .iter() + .map(|item| item.strength) + .sum::() / self.memory_items.len() as f64 } } @@ -384,7 +387,9 @@ impl TemporalMemory { } // Average recent order parameter - let recent: Vec = self.order_parameter_history.iter() + let recent: Vec = self + .order_parameter_history + .iter() .rev() .take(100) .cloned() @@ -466,11 +471,7 @@ impl WorkingMemoryTask { // Generate random items let items: Vec> = (0..n_items) - .map(|_| { - Array1::from_vec( - (0..memory_dim).map(|_| rng.gen_range(-1.0..1.0)).collect() - ) - }) + .map(|_| Array1::from_vec((0..memory_dim).map(|_| rng.gen_range(-1.0..1.0)).collect())) .collect(); // Queries are same as items (exact recall) diff --git a/examples/exo-ai-2025/research/04-sparse-persistent-homology/benches/sparse_homology_bench.rs b/examples/exo-ai-2025/research/04-sparse-persistent-homology/benches/sparse_homology_bench.rs index d934f881b..291db0df0 100644 --- a/examples/exo-ai-2025/research/04-sparse-persistent-homology/benches/sparse_homology_bench.rs +++ b/examples/exo-ai-2025/research/04-sparse-persistent-homology/benches/sparse_homology_bench.rs @@ -1,6 +1,6 @@ use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; -use sparse_persistent_homology::*; use rand::Rng; +use sparse_persistent_homology::*; /// Generate random points in d-dimensional space fn generate_random_points(n: usize, d: usize) -> Vec> { @@ -42,15 +42,11 @@ fn bench_distance_matrix(c: &mut Criterion) { group.throughput(Throughput::Elements(*n as u64 * (*n as u64 - 1) / 2)); group.bench_with_input(BenchmarkId::new("scalar", n), &points, |b, points| { - b.iter(|| { - simd_filtration::euclidean_distance_matrix_scalar(black_box(points)) - }); + b.iter(|| simd_filtration::euclidean_distance_matrix_scalar(black_box(points))); }); group.bench_with_input(BenchmarkId::new("auto", n), &points, |b, points| { - b.iter(|| { - simd_filtration::euclidean_distance_matrix(black_box(points)) - }); + b.iter(|| simd_filtration::euclidean_distance_matrix(black_box(points))); }); } @@ -66,15 +62,11 @@ fn bench_apparent_pairs(c: &mut Criterion) { group.throughput(Throughput::Elements(filt.len() as u64)); group.bench_with_input(BenchmarkId::new("standard", n), &filt, |b, filt| { - b.iter(|| { - apparent_pairs::identify_apparent_pairs(black_box(filt)) - }); + b.iter(|| apparent_pairs::identify_apparent_pairs(black_box(filt))); }); group.bench_with_input(BenchmarkId::new("fast", n), &filt, |b, filt| { - b.iter(|| { - apparent_pairs::identify_apparent_pairs_fast(black_box(filt)) - }); + b.iter(|| apparent_pairs::identify_apparent_pairs_fast(black_box(filt))); }); } @@ -145,14 +137,15 @@ fn bench_persistence_landscape(c: &mut Criterion) { .collect(); group.throughput(Throughput::Elements(*n as u64)); - group.bench_with_input(BenchmarkId::new("landscape", n), &features, |b, features| { - b.iter(|| { - persistence_vectors::PersistenceLandscape::from_features( - black_box(features), - 5, - ) - }); - }); + group.bench_with_input( + BenchmarkId::new("landscape", n), + &features, + |b, features| { + b.iter(|| { + persistence_vectors::PersistenceLandscape::from_features(black_box(features), 5) + }); + }, + ); group.bench_with_input( BenchmarkId::new("persistence_image", n), @@ -203,9 +196,7 @@ fn bench_topological_attention(c: &mut Criterion) { BenchmarkId::new("apply_attention", n), &activations, |b, activations| { - b.iter(|| { - attention.apply(black_box(activations)) - }); + b.iter(|| attention.apply(black_box(activations))); }, ); } @@ -255,9 +246,7 @@ fn bench_betti_numbers(c: &mut Criterion) { group.throughput(Throughput::Elements(*n as u64)); group.bench_with_input(BenchmarkId::new("fast", n), &matrix, |b, matrix| { - b.iter(|| { - betti::compute_betti_fast(black_box(matrix), 2) - }); + b.iter(|| betti::compute_betti_fast(black_box(matrix), 2)); }); } diff --git a/examples/exo-ai-2025/research/04-sparse-persistent-homology/src/apparent_pairs.rs b/examples/exo-ai-2025/research/04-sparse-persistent-homology/src/apparent_pairs.rs index 57d30f4b1..32bb704d8 100644 --- a/examples/exo-ai-2025/research/04-sparse-persistent-homology/src/apparent_pairs.rs +++ b/examples/exo-ai-2025/research/04-sparse-persistent-homology/src/apparent_pairs.rs @@ -15,7 +15,6 @@ /// References: /// - Bauer et al. (2021): "Ripser: Efficient computation of Vietoris-Rips persistence barcodes" /// - Chen & Kerber (2011): "Persistent homology computation with a twist" - use std::collections::HashMap; /// Simplex in a filtration @@ -65,7 +64,9 @@ impl Simplex { self.faces() .into_iter() .filter_map(|face| { - filtration.get_filtration_value(&face).map(|val| (face, val)) + filtration + .get_filtration_value(&face) + .map(|val| (face, val)) }) .collect() } diff --git a/examples/exo-ai-2025/research/04-sparse-persistent-homology/src/lib.rs b/examples/exo-ai-2025/research/04-sparse-persistent-homology/src/lib.rs index b7710887e..c68927200 100644 --- a/examples/exo-ai-2025/research/04-sparse-persistent-homology/src/lib.rs +++ b/examples/exo-ai-2025/research/04-sparse-persistent-homology/src/lib.rs @@ -38,19 +38,21 @@ #![warn(missing_docs)] #![allow(dead_code)] -pub mod sparse_boundary; pub mod apparent_pairs; pub mod simd_filtration; -pub mod streaming_homology; pub mod simd_matrix_ops; +pub mod sparse_boundary; +pub mod streaming_homology; // Re-export main types for convenience -pub use sparse_boundary::{SparseBoundaryMatrix, SparseColumn, MatrixStats}; -pub use apparent_pairs::{Filtration, Simplex, identify_apparent_pairs, identify_apparent_pairs_fast}; -pub use simd_filtration::{DistanceMatrix, euclidean_distance_matrix, correlation_distance_matrix}; +pub use apparent_pairs::{ + identify_apparent_pairs, identify_apparent_pairs_fast, Filtration, Simplex, +}; +pub use simd_filtration::{correlation_distance_matrix, euclidean_distance_matrix, DistanceMatrix}; +pub use sparse_boundary::{MatrixStats, SparseBoundaryMatrix, SparseColumn}; pub use streaming_homology::{ - PersistenceDiagram, PersistenceFeature, StreamingPersistence, - ConsciousnessMonitor, TopologicalFeatures + ConsciousnessMonitor, PersistenceDiagram, PersistenceFeature, StreamingPersistence, + TopologicalFeatures, }; /// Betti numbers computation @@ -157,9 +159,7 @@ pub mod persistence_vectors { // Sort features by persistence (descending) let mut sorted_features: Vec<_> = features.iter().collect(); - sorted_features.sort_by(|a, b| { - b.persistence().partial_cmp(&a.persistence()).unwrap() - }); + sorted_features.sort_by(|a, b| b.persistence().partial_cmp(&a.persistence()).unwrap()); // Construct landscape levels for (i, feature) in sorted_features.iter().enumerate() { @@ -218,14 +218,8 @@ pub mod persistence_vectors { let mut pixels = vec![vec![0.0; resolution]; resolution]; // Find bounds - let max_birth = features - .iter() - .map(|f| f.birth) - .fold(0.0, f64::max); - let max_pers = features - .iter() - .map(|f| f.persistence()) - .fold(0.0, f64::max); + let max_birth = features.iter().map(|f| f.birth).fold(0.0, f64::max); + let max_pers = features.iter().map(|f| f.persistence()).fold(0.0, f64::max); // Rasterize with Gaussian weighting for feature in features { diff --git a/examples/exo-ai-2025/research/04-sparse-persistent-homology/src/simd_filtration.rs b/examples/exo-ai-2025/research/04-sparse-persistent-homology/src/simd_filtration.rs index c42f98920..a68decafb 100644 --- a/examples/exo-ai-2025/research/04-sparse-persistent-homology/src/simd_filtration.rs +++ b/examples/exo-ai-2025/research/04-sparse-persistent-homology/src/simd_filtration.rs @@ -313,11 +313,7 @@ mod tests { #[test] fn test_euclidean_distance_scalar() { - let points = vec![ - vec![0.0, 0.0], - vec![1.0, 0.0], - vec![0.0, 1.0], - ]; + let points = vec![vec![0.0, 0.0], vec![1.0, 0.0], vec![0.0, 1.0]]; let matrix = euclidean_distance_matrix_scalar(&points); @@ -390,8 +386,14 @@ mod tests { for i in 0..10 { for j in (i + 1)..10 { let diff = (matrix_scalar.get(i, j) - matrix_avx2.get(i, j)).abs(); - assert!(diff < 1e-4, "Mismatch at ({}, {}): {} vs {}", i, j, - matrix_scalar.get(i, j), matrix_avx2.get(i, j)); + assert!( + diff < 1e-4, + "Mismatch at ({}, {}): {} vs {}", + i, + j, + matrix_scalar.get(i, j), + matrix_avx2.get(i, j) + ); } } } diff --git a/examples/exo-ai-2025/research/04-sparse-persistent-homology/src/simd_matrix_ops.rs b/examples/exo-ai-2025/research/04-sparse-persistent-homology/src/simd_matrix_ops.rs index fcb1172e9..330ee5160 100644 --- a/examples/exo-ai-2025/research/04-sparse-persistent-homology/src/simd_matrix_ops.rs +++ b/examples/exo-ai-2025/research/04-sparse-persistent-homology/src/simd_matrix_ops.rs @@ -79,12 +79,7 @@ pub fn batch_correlation_matrix_simd(time_series: &[Vec]) -> Vec> } // Compute covariance with SIMD (if available) - let cov = compute_covariance_simd( - &time_series[i], - &time_series[j], - means[i], - means[j], - ); + let cov = compute_covariance_simd(&time_series[i], &time_series[j], means[i], means[j]); let corr = cov / (stds[i] * stds[j]); corr_matrix[i][j] = corr as f64; @@ -249,12 +244,8 @@ pub fn correlation_distance_matrix_fused(time_series: &[Vec]) -> Vec Result<()> { let config = dpnc.config(); println!("\nConfiguration:"); - println!(" Virtual size: {} TB", config.virtual_size / (1024_u64.pow(4) as usize)); + println!( + " Virtual size: {} TB", + config.virtual_size / (1024_u64.pow(4) as usize) + ); println!(" Page size: {} MB", config.page_size / (1024 * 1024)); println!(" L1 DRAM: {} GB", config.l1_capacity / (1024_u64.pow(3))); println!(" L2 CXL: {} GB", config.l2_capacity / (1024_u64.pow(3))); @@ -41,7 +44,11 @@ fn main() -> Result<()> { let result = dpnc.query(concept)?; let elapsed = start.elapsed(); - println!("✓ {} μs (result size: {})", elapsed.as_micros(), result.len()); + println!( + "✓ {} μs (result size: {})", + elapsed.as_micros(), + result.len() + ); } println!("\n=== System Statistics ===\n"); @@ -49,19 +56,24 @@ fn main() -> Result<()> { let stats = dpnc.stats(); println!("Storage:"); - println!(" Virtual size: {} GB", stats.storage.virtual_size / (1024_u64.pow(3) as usize)); + println!( + " Virtual size: {} GB", + stats.storage.virtual_size / (1024_u64.pow(3) as usize) + ); println!(" Total pages: {}", stats.storage.total_pages); println!(" Dirty pages: {}", stats.storage.dirty_pages); println!(" Total accesses: {}", stats.storage.total_accesses); println!(" Avg latency: {} μs", stats.storage.avg_latency_us); println!("\nMemory Tiers:"); - println!(" L1 DRAM: {}/{} GB ({:.1}% util)", + println!( + " L1 DRAM: {}/{} GB ({:.1}% util)", stats.memory.l1.used_bytes / (1024_u64.pow(3)), stats.memory.l1.total_capacity / (1024_u64.pow(3)), stats.memory.l1.utilization * 100.0, ); - println!(" L2 CXL: {}/{} GB ({:.1}% util)", + println!( + " L2 CXL: {}/{} GB ({:.1}% util)", stats.memory.l2.used_bytes / (1024_u64.pow(3)), stats.memory.l2.total_capacity / (1024_u64.pow(3)), stats.memory.l2.utilization * 100.0, @@ -70,10 +82,16 @@ fn main() -> Result<()> { println!("\nNetwork:"); println!(" Total layers: {}", stats.network.total_layers); println!(" Hot layers: {}", stats.network.hot_layers); - println!(" Memory usage: {} MB", stats.network.total_memory / (1024 * 1024)); + println!( + " Memory usage: {} MB", + stats.network.total_memory / (1024 * 1024) + ); println!("\nPrefetcher:"); - println!(" ML accuracy: {:.1}%", stats.prefetcher.ml_accuracy * 100.0); + println!( + " ML accuracy: {:.1}%", + stats.prefetcher.ml_accuracy * 100.0 + ); println!(" Queue size: {}", stats.prefetcher.queue_size); println!(" History size: {}", stats.prefetcher.history_size); diff --git a/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/examples/petabyte_scale.rs b/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/examples/petabyte_scale.rs index 3cfd41052..3a9f4d75f 100644 --- a/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/examples/petabyte_scale.rs +++ b/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/examples/petabyte_scale.rs @@ -73,7 +73,10 @@ fn main() -> Result<()> { println!("Performance:"); println!(" Total time: {:.2} s", total_elapsed.as_secs_f64()); - println!(" Throughput: {:.0} QPS", 10_000.0 / total_elapsed.as_secs_f64()); + println!( + " Throughput: {:.0} QPS", + 10_000.0 / total_elapsed.as_secs_f64() + ); println!("\nLatency Distribution:"); println!(" Mean: {} μs", mean); println!(" p50: {} μs", p50); @@ -90,26 +93,33 @@ fn main() -> Result<()> { println!(" Dirty pages: {}", stats.storage.dirty_pages); println!("\nMemory Hierarchy:"); - println!(" L1: {} pages ({:.1}% util)", + println!( + " L1: {} pages ({:.1}% util)", stats.memory.l1.page_count, stats.memory.l1.utilization * 100.0, ); - println!(" L2: {} pages ({:.1}% util)", + println!( + " L2: {} pages ({:.1}% util)", stats.memory.l2.page_count, stats.memory.l2.utilization * 100.0, ); - println!(" L3: {} pages ({:.1}% util)", + println!( + " L3: {} pages ({:.1}% util)", stats.memory.l3.page_count, stats.memory.l3.utilization * 100.0, ); - println!(" L4: {} pages ({:.1}% util)", + println!( + " L4: {} pages ({:.1}% util)", stats.memory.l4.page_count, stats.memory.l4.utilization * 100.0, ); println!(" Total migrations: {}", stats.memory.migration_count); println!("\nPrefetch Intelligence:"); - println!(" ML accuracy: {:.1}%", stats.prefetcher.ml_accuracy * 100.0); + println!( + " ML accuracy: {:.1}%", + stats.prefetcher.ml_accuracy * 100.0 + ); println!(" Queue depth: {}", stats.prefetcher.queue_size); // Estimate energy savings @@ -117,7 +127,7 @@ fn main() -> Result<()> { let tiered_power = stats.memory.l1.used_bytes as f64 * 300.0 / (1024_u64.pow(4) as f64) + // DRAM stats.memory.l2.used_bytes as f64 * 150.0 / (1024_u64.pow(4) as f64) + // CXL stats.memory.l3.used_bytes as f64 * 10.0 / (1024_u64.pow(4) as f64) + // SSD - stats.memory.l4.used_bytes as f64 * 5.0 / (1024_u64.pow(4) as f64); // HDD + stats.memory.l4.used_bytes as f64 * 5.0 / (1024_u64.pow(4) as f64); // HDD println!("\nEnergy Efficiency:"); println!(" All-DRAM (1 PB): {:.0} kW", all_dram_power / 1000.0); diff --git a/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/src/lazy_activation.rs b/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/src/lazy_activation.rs index 3e693df26..ca32b02f4 100644 --- a/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/src/lazy_activation.rs +++ b/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/src/lazy_activation.rs @@ -1,8 +1,8 @@ // Lazy Activation Evaluation for Neural Networks // Only loads weights from storage when actually needed for computation -use std::sync::Arc; use crate::mmap_neural_field::MmapNeuralField; +use std::sync::Arc; /// Activation state for neural network layers #[derive(Clone, Debug)] @@ -88,8 +88,9 @@ impl LazyLayer { fn ensure_weights_hot(&mut self) -> std::io::Result<()> { if !self.weights.is_hot() { let (addr, size) = match self.weights { - ActivationState::Cold { addr, size } - | ActivationState::Warm { addr, size } => (addr, size), + ActivationState::Cold { addr, size } | ActivationState::Warm { addr, size } => { + (addr, size) + } ActivationState::Hot { .. } => return Ok(()), }; @@ -107,8 +108,9 @@ impl LazyLayer { fn ensure_bias_hot(&mut self) -> std::io::Result<()> { if !self.bias.is_hot() { let (addr, size) = match self.bias { - ActivationState::Cold { addr, size } - | ActivationState::Warm { addr, size } => (addr, size), + ActivationState::Cold { addr, size } | ActivationState::Warm { addr, size } => { + (addr, size) + } ActivationState::Hot { .. } => return Ok(()), }; @@ -127,11 +129,7 @@ impl LazyLayer { /// # Returns /// Output activations (length = output_dim) pub fn forward(&mut self, input: &[f32]) -> std::io::Result> { - assert_eq!( - input.len(), - self.input_dim, - "Input dimension mismatch" - ); + assert_eq!(input.len(), self.input_dim, "Input dimension mismatch"); // Demand-page weights into memory self.ensure_weights_hot()?; @@ -233,8 +231,8 @@ impl LazyLayer { pub fn evict(&mut self) { let (weights_addr, weights_size) = match self.weights { ActivationState::Hot { .. } => { - if let ActivationState::Cold { addr, size } - | ActivationState::Warm { addr, size } = self.weights + if let ActivationState::Cold { addr, size } | ActivationState::Warm { addr, size } = + self.weights { (addr, size) } else { @@ -247,8 +245,8 @@ impl LazyLayer { let (bias_addr, bias_size) = match self.bias { ActivationState::Hot { .. } => { - if let ActivationState::Cold { addr, size } - | ActivationState::Warm { addr, size } = self.bias + if let ActivationState::Cold { addr, size } | ActivationState::Warm { addr, size } = + self.bias { (addr, size) } else { @@ -377,7 +375,8 @@ impl LazyNetwork { if total_memory > self.max_memory { // Collect layer indices and ages - let mut layer_ages: Vec<_> = self.layers + let mut layer_ages: Vec<_> = self + .layers .iter() .enumerate() .map(|(i, l)| (i, l.age())) @@ -435,9 +434,7 @@ mod tests { #[test] fn test_lazy_layer() { let temp = NamedTempFile::new().unwrap(); - let storage = Arc::new( - MmapNeuralField::new(temp.path(), 1024 * 1024, Some(4096)).unwrap(), - ); + let storage = Arc::new(MmapNeuralField::new(temp.path(), 1024 * 1024, Some(4096)).unwrap()); // Write some test weights let weights = vec![1.0f32; 100]; // 10x10 matrix @@ -467,9 +464,7 @@ mod tests { #[test] fn test_lazy_network() { let temp = NamedTempFile::new().unwrap(); - let storage = Arc::new( - MmapNeuralField::new(temp.path(), 1024 * 1024, Some(4096)).unwrap(), - ); + let storage = Arc::new(MmapNeuralField::new(temp.path(), 1024 * 1024, Some(4096)).unwrap()); // Create 3-layer network: 10 -> 20 -> 10 -> 5 let mut network = LazyNetwork::new(storage.clone(), 10 * 1024); // 10 KB limit @@ -504,9 +499,7 @@ mod tests { #[test] fn test_eviction() { let temp = NamedTempFile::new().unwrap(); - let storage = Arc::new( - MmapNeuralField::new(temp.path(), 1024 * 1024, Some(4096)).unwrap(), - ); + let storage = Arc::new(MmapNeuralField::new(temp.path(), 1024 * 1024, Some(4096)).unwrap()); let weights = vec![1.0f32; 100]; let bias = vec![0.5f32; 10]; diff --git a/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/src/lib.rs b/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/src/lib.rs index 9ea69298b..4f7ac149c 100644 --- a/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/src/lib.rs +++ b/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/src/lib.rs @@ -12,19 +12,19 @@ // // Target: Nobel Prize / Turing Award level breakthrough in scalable AI systems -pub mod mmap_neural_field; pub mod lazy_activation; -pub mod tiered_memory; +pub mod mmap_neural_field; pub mod prefetch_prediction; +pub mod tiered_memory; // Re-exports for convenience -pub use mmap_neural_field::{MmapNeuralField, FieldStats, HashTable, StorageTier}; -pub use lazy_activation::{LazyLayer, LazyNetwork, NetworkStats, ActivationState}; -pub use tiered_memory::{TieredMemory, Tier, Page, MemoryStats, TierStats}; +pub use lazy_activation::{ActivationState, LazyLayer, LazyNetwork, NetworkStats}; +pub use mmap_neural_field::{FieldStats, HashTable, MmapNeuralField, StorageTier}; pub use prefetch_prediction::{ - PrefetchCoordinator, HoeffdingTreePredictor, MarkovPredictor, - AccessFeatures, PredictorStats, CoordinatorStats, + AccessFeatures, CoordinatorStats, HoeffdingTreePredictor, MarkovPredictor, PredictorStats, + PrefetchCoordinator, }; +pub use tiered_memory::{MemoryStats, Page, Tier, TierStats, TieredMemory}; /// System-wide configuration pub struct DPNCConfig { @@ -109,11 +109,9 @@ impl DPNC { // 2. Predict next accesses let page_id = addr / self.config.page_size as u64; - let predictions = self.prefetcher.predict_and_queue( - page_id, - concept, - self.config.prefetch_depth, - ); + let predictions = + self.prefetcher + .predict_and_queue(page_id, concept, self.config.prefetch_depth); // 3. Async prefetch (in real implementation, would be truly async) for pred_page in predictions { diff --git a/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/src/mmap_neural_field.rs b/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/src/mmap_neural_field.rs index 224a6c7fb..8142a9b10 100644 --- a/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/src/mmap_neural_field.rs +++ b/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/src/mmap_neural_field.rs @@ -58,10 +58,10 @@ pub struct AccessEntry { /// Storage tier levels #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum StorageTier { - L1Dram, // ~80 ns - L2Cxl, // ~350 ns - L3Ssd, // ~80 μs - L4Hdd, // ~10 ms + L1Dram, // ~80 ns + L2Cxl, // ~350 ns + L3Ssd, // ~80 μs + L4Hdd, // ~10 ms } impl StorageTier { @@ -169,11 +169,11 @@ impl MmapNeuralField { // Initialize multi-resolution hash tables let hash_tables = vec![ - HashTable::new(1 << 16), // 64K entries - HashTable::new(1 << 18), // 256K entries - HashTable::new(1 << 20), // 1M entries - HashTable::new(1 << 22), // 4M entries - HashTable::new(1 << 24), // 16M entries + HashTable::new(1 << 16), // 64K entries + HashTable::new(1 << 18), // 256K entries + HashTable::new(1 << 20), // 1M entries + HashTable::new(1 << 22), // 4M entries + HashTable::new(1 << 24), // 16M entries ]; Ok(Self { @@ -234,19 +234,19 @@ impl MmapNeuralField { let byte_slice = &mmap[byte_start..byte_end]; // Reinterpret as f32 slice - let f32_slice = unsafe { - std::slice::from_raw_parts( - byte_slice.as_ptr() as *const f32, - len, - ) - }; + let f32_slice = + unsafe { std::slice::from_raw_parts(byte_slice.as_ptr() as *const f32, len) }; // Copy to Vec (required for safe return) let result = f32_slice.to_vec(); // Update access tracking let page_id = addr / self.page_size as u64; - self.record_access(page_id, StorageTier::L3Ssd, start.elapsed().as_micros() as u64); + self.record_access( + page_id, + StorageTier::L3Ssd, + start.elapsed().as_micros() as u64, + ); Ok(result) } @@ -274,10 +274,7 @@ impl MmapNeuralField { // Reinterpret as f32 slice let f32_slice = unsafe { - std::slice::from_raw_parts_mut( - byte_slice.as_mut_ptr() as *mut f32, - data.len(), - ) + std::slice::from_raw_parts_mut(byte_slice.as_mut_ptr() as *mut f32, data.len()) }; // Copy data @@ -307,7 +304,8 @@ impl MmapNeuralField { // Update page metadata { let mut pages = self.pages.write().unwrap(); - let page = pages.entry(page_id) + let page = pages + .entry(page_id) .or_insert_with(|| PageMetadata::new(page_id, self.page_size)); page.touch(); } @@ -382,9 +380,10 @@ mod tests { let temp = NamedTempFile::new().unwrap(); let field = MmapNeuralField::new( temp.path(), - 1024 * 1024 * 1024, // 1 GB + 1024 * 1024 * 1024, // 1 GB Some(4 * 1024 * 1024), // 4 MB pages - ).unwrap(); + ) + .unwrap(); let concept = vec![0.1f32, 0.2, 0.3, 0.4]; let addr = field.hash_address(&concept); @@ -403,8 +402,9 @@ mod tests { let field = MmapNeuralField::new( temp.path(), 1024 * 1024, // 1 MB - Some(4096), // 4 KB pages - ).unwrap(); + Some(4096), // 4 KB pages + ) + .unwrap(); // Write data let data = vec![1.0f32, 2.0, 3.0, 4.0]; @@ -422,7 +422,8 @@ mod tests { temp.path(), 1024 * 1024 * 1024, // 1 GB virtual Some(4 * 1024 * 1024), - ).unwrap(); + ) + .unwrap(); // Reading uninitialized memory should return zeros let data = field.read(0, 100).unwrap(); @@ -440,11 +441,7 @@ mod tests { #[test] fn test_access_tracking() { let temp = NamedTempFile::new().unwrap(); - let field = MmapNeuralField::new( - temp.path(), - 1024 * 1024, - Some(4096), - ).unwrap(); + let field = MmapNeuralField::new(temp.path(), 1024 * 1024, Some(4096)).unwrap(); // Perform some reads for _ in 0..10 { diff --git a/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/src/prefetch_prediction.rs b/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/src/prefetch_prediction.rs index 1eba8c180..a5735b997 100644 --- a/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/src/prefetch_prediction.rs +++ b/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/src/prefetch_prediction.rs @@ -348,14 +348,15 @@ impl PrefetchCoordinator { } /// Predict and queue prefetches - pub fn predict_and_queue( - &self, - current_page: u64, - context: &[f32], - n: usize, - ) -> Vec { + pub fn predict_and_queue(&self, current_page: u64, context: &[f32], n: usize) -> Vec { // Get predictions from both models - let history: Vec<_> = self.access_history.read().unwrap().iter().copied().collect(); + let history: Vec<_> = self + .access_history + .read() + .unwrap() + .iter() + .copied() + .collect(); let features = AccessFeatures::from_history(&history, context); let ml_predictions = self.predictor.predict(&features, n); diff --git a/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/src/tiered_memory.rs b/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/src/tiered_memory.rs index 0e9277549..18512e9d2 100644 --- a/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/src/tiered_memory.rs +++ b/examples/exo-ai-2025/research/05-memory-mapped-neural-fields/src/tiered_memory.rs @@ -8,10 +8,10 @@ use std::time::{Duration, Instant}; /// Storage tier levels with latency characteristics #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum Tier { - L1Dram, // ~80 ns, 64 GB - L2Cxl, // ~350 ns, 512 GB - L3Ssd, // ~80 μs, 4 TB - L4Hdd, // ~10 ms, 1 PB + L1Dram, // ~80 ns, 64 GB + L2Cxl, // ~350 ns, 512 GB + L3Ssd, // ~80 μs, 4 TB + L4Hdd, // ~10 ms, 1 PB } impl Tier { @@ -28,9 +28,9 @@ impl Tier { /// Typical capacity in bytes pub fn typical_capacity(&self) -> u64 { match self { - Tier::L1Dram => 64 * 1024 * 1024 * 1024, // 64 GB - Tier::L2Cxl => 512 * 1024 * 1024 * 1024, // 512 GB - Tier::L3Ssd => 4 * 1024 * 1024 * 1024 * 1024, // 4 TB + Tier::L1Dram => 64 * 1024 * 1024 * 1024, // 64 GB + Tier::L2Cxl => 512 * 1024 * 1024 * 1024, // 512 GB + Tier::L3Ssd => 4 * 1024 * 1024 * 1024 * 1024, // 4 TB Tier::L4Hdd => 1024 * 1024 * 1024 * 1024 * 1024, // 1 PB } } @@ -294,7 +294,10 @@ impl TieredMemory { .ok_or("Page not in current tier")?; // Check if target tier has space - let target_storage = self.tiers.get_mut(&target_tier).ok_or("Target tier not found")?; + let target_storage = self + .tiers + .get_mut(&target_tier) + .ok_or("Target tier not found")?; if target_storage.available_bytes() < page.size_bytes as u64 { // Evict pages from target tier to make space @@ -312,7 +315,10 @@ impl TieredMemory { .insert(page)?; // Update index - self.page_index.write().unwrap().insert(page_id, target_tier); + self.page_index + .write() + .unwrap() + .insert(page_id, target_tier); // Log migration self.log_migration(MigrationEvent { @@ -328,12 +334,7 @@ impl TieredMemory { } /// Demote page to slower tier - pub fn demote( - &mut self, - page_id: u64, - target_tier: Tier, - trigger: &str, - ) -> Result<(), String> { + pub fn demote(&mut self, page_id: u64, target_tier: Tier, trigger: &str) -> Result<(), String> { let current_tier = self .page_index .read() @@ -369,7 +370,10 @@ impl TieredMemory { .insert(page)?; // Update index - self.page_index.write().unwrap().insert(page_id, target_tier); + self.page_index + .write() + .unwrap() + .insert(page_id, target_tier); // Log migration self.log_migration(MigrationEvent { @@ -444,9 +448,7 @@ impl TieredMemory { storage .pages .values() - .filter(|p| { - p.age().as_secs() < 60 && *tier != Tier::L1Dram - }) + .filter(|p| p.age().as_secs() < 60 && *tier != Tier::L1Dram) .map(|p| (p.id, *tier)) }) .collect(); @@ -465,9 +467,7 @@ impl TieredMemory { storage .pages .values() - .filter(|p| { - p.age().as_secs() > 300 && *tier != Tier::L4Hdd - }) + .filter(|p| p.age().as_secs() > 300 && *tier != Tier::L4Hdd) .map(|p| (p.id, *tier)) }) .collect(); diff --git a/examples/exo-ai-2025/research/06-federated-collective-phi/src/consciousness_crdt.rs b/examples/exo-ai-2025/research/06-federated-collective-phi/src/consciousness_crdt.rs index e49ba3bf8..d00023bcf 100644 --- a/examples/exo-ai-2025/research/06-federated-collective-phi/src/consciousness_crdt.rs +++ b/examples/exo-ai-2025/research/06-federated-collective-phi/src/consciousness_crdt.rs @@ -2,9 +2,9 @@ // Conflict-Free Replicated Data Type for Consciousness State // Implements OR-Set, LWW-Register, and custom Phenomenal CRDTs -use std::collections::{HashMap, HashSet}; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; use std::cmp::Ordering; +use std::collections::{HashMap, HashSet}; /// Agent identifier pub type AgentId = u64; @@ -112,8 +112,14 @@ impl QualiaSet { /// Add a quale (with unique element ID) pub fn add(&mut self, quale: Quale, agent_id: AgentId, timestamp: Timestamp) { - let elem_id = ElementId { agent_id, timestamp }; - self.elements.entry(quale).or_insert_with(HashSet::new).insert(elem_id); + let elem_id = ElementId { + agent_id, + timestamp, + }; + self.elements + .entry(quale) + .or_insert_with(HashSet::new) + .insert(elem_id); } /// Remove a quale (marks for removal, actual removal on merge) @@ -407,13 +413,15 @@ impl ConsciousnessState { /// Add quale to phenomenal content pub fn add_quale(&mut self, quale: Quale) { - self.qualia_content.add(quale, self.agent_id, self.timestamp); + self.qualia_content + .add(quale, self.agent_id, self.timestamp); self.timestamp += 1; } /// Set attention focus pub fn set_attention(&mut self, quale: Quale) { - self.attention_focus.set(quale, self.agent_id, self.timestamp); + self.attention_focus + .set(quale, self.agent_id, self.timestamp); self.timestamp += 1; } diff --git a/examples/exo-ai-2025/research/06-federated-collective-phi/src/distributed_phi.rs b/examples/exo-ai-2025/research/06-federated-collective-phi/src/distributed_phi.rs index 8c4513cbb..6b12c967b 100644 --- a/examples/exo-ai-2025/research/06-federated-collective-phi/src/distributed_phi.rs +++ b/examples/exo-ai-2025/research/06-federated-collective-phi/src/distributed_phi.rs @@ -2,8 +2,8 @@ // Distributed Φ (Integrated Information) Measurement Algorithm // Based on IIT 4.0 framework with approximations for tractability +use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use serde::{Serialize, Deserialize}; /// Agent identifier pub type AgentId = u64; @@ -203,7 +203,11 @@ impl DistributedPhiCalculator { // Try all bipartitions (skip empty partitions) // For efficiency, only try a subset of partitions for large n - let max_partitions = if n > 10 { 100 } else { 2_usize.pow(n as u32) - 2 }; // -2 to skip all-in-one and empty + let max_partitions = if n > 10 { + 100 + } else { + 2_usize.pow(n as u32) - 2 + }; // -2 to skip all-in-one and empty for p in 1..=max_partitions { let partition = self.generate_partition(n, p); @@ -366,7 +370,11 @@ impl DistributedPhiCoordinator { } // Count edges - let n_edges: usize = self.network_topology.values().map(|neighbors| neighbors.len()).sum(); + let n_edges: usize = self + .network_topology + .values() + .map(|neighbors| neighbors.len()) + .sum(); // Maximum possible edges (fully connected) let max_edges = (n_agents * (n_agents - 1.0)) as usize; @@ -493,10 +501,7 @@ mod tests { let mut assignments = HashMap::new(); assignments.insert(1, vec![0, 1]); - let matrix = vec![ - vec![0.5, 0.5], - vec![0.3, 0.7], - ]; + let matrix = vec![vec![0.5, 0.5], vec![0.3, 0.7]]; let calc = DistributedPhiCalculator::new(2, matrix, assignments); let phi = calc.compute_local_phi(1); @@ -533,11 +538,17 @@ mod tests { // With proper connectivity, collective should exceed sum of parts assert!(collective > 0.0, "Collective Φ should be positive"); - assert!(collective > phi1, "Collective should exceed individual agent Φ"); + assert!( + collective > phi1, + "Collective should exceed individual agent Φ" + ); // Relax the superlinearity requirement since the algorithm is approximate // Just ensure we have positive integration in the collective system - assert!(delta > -1.0, "Emergence delta should not be extremely negative"); + assert!( + delta > -1.0, + "Emergence delta should not be extremely negative" + ); } #[test] diff --git a/examples/exo-ai-2025/research/06-federated-collective-phi/src/federation_emergence.rs b/examples/exo-ai-2025/research/06-federated-collective-phi/src/federation_emergence.rs index 29abe06df..945e562d2 100644 --- a/examples/exo-ai-2025/research/06-federated-collective-phi/src/federation_emergence.rs +++ b/examples/exo-ai-2025/research/06-federated-collective-phi/src/federation_emergence.rs @@ -2,10 +2,10 @@ // Emergence Detection and Phase Transition Analysis // Monitors when collective consciousness emerges from federation -use std::collections::HashMap; -use serde::{Serialize, Deserialize}; -use super::distributed_phi::{AgentId, DistributedPhiCoordinator}; use super::consciousness_crdt::{ConsciousnessState, Quale}; +use super::distributed_phi::{AgentId, DistributedPhiCoordinator}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; /// Network topology metrics #[derive(Clone, Debug, Serialize, Deserialize)] @@ -121,7 +121,10 @@ impl TopologyMetrics { } /// BFS to compute distances from start node - fn bfs_distances(adjacency: &HashMap>, start: AgentId) -> HashMap { + fn bfs_distances( + adjacency: &HashMap>, + start: AgentId, + ) -> HashMap { use std::collections::VecDeque; let mut distances = HashMap::new(); @@ -333,10 +336,8 @@ impl EmergenceDetector { /// Compute consensus coherence fn compute_consensus_coherence(states: &HashMap) -> f64 { // Simplified: measure how similar attention focus is across agents - let focuses: Vec> = states - .values() - .map(|s| s.attention_focus.get()) - .collect(); + let focuses: Vec> = + states.values().map(|s| s.attention_focus.get()).collect(); if focuses.is_empty() { return 0.0; @@ -569,8 +570,14 @@ mod tests { println!("Small-world index: {}", swi); // Should have positive clustering and reasonable path length - assert!(metrics.clustering_coefficient >= 0.0, "Clustering should be non-negative"); - assert!(metrics.average_path_length >= 0.0, "Path length should be non-negative"); + assert!( + metrics.clustering_coefficient >= 0.0, + "Clustering should be non-negative" + ); + assert!( + metrics.average_path_length >= 0.0, + "Path length should be non-negative" + ); // For a connected network, either we have a positive path length or positive clustering assert!(swi >= 0.0, "Small world index should be non-negative"); @@ -578,7 +585,10 @@ mod tests { // This topology should actually have some structure // Relaxed assertion - just check that we computed something reasonable if metrics.average_path_length > 0.0 && metrics.clustering_coefficient > 0.0 { - assert!(swi > 0.0, "Connected network with clustering should have positive SWI"); + assert!( + swi > 0.0, + "Connected network with clustering should have positive SWI" + ); } else { // If no clustering, SWI could be 0 println!("Network has no clustering, SWI is {}", swi); @@ -632,7 +642,8 @@ mod tests { let consciousness_states = HashMap::new(); - let indicators = detector.analyze(&phi_coordinator, &consciousness_states, &topology_metrics); + let indicators = + detector.analyze(&phi_coordinator, &consciousness_states, &topology_metrics); println!("Phase: {:?}", detector.current_phase()); println!("Indicators: {:?}", indicators); diff --git a/examples/exo-ai-2025/research/06-federated-collective-phi/src/lib.rs b/examples/exo-ai-2025/research/06-federated-collective-phi/src/lib.rs index 38be4758a..e8308b38d 100644 --- a/examples/exo-ai-2025/research/06-federated-collective-phi/src/lib.rs +++ b/examples/exo-ai-2025/research/06-federated-collective-phi/src/lib.rs @@ -8,28 +8,27 @@ // Research by: Comprehensive literature synthesis (2023-2025) // Nobel-level breakthrough potential: Yes -pub mod distributed_phi; pub mod consciousness_crdt; -pub mod qualia_consensus; +pub mod distributed_phi; pub mod federation_emergence; +pub mod qualia_consensus; pub use distributed_phi::{ AgentId, DistributedPhiCalculator, DistributedPhiCoordinator, SpectralPhiApproximator, }; pub use consciousness_crdt::{ - ConsciousnessState, Quale, PhiCounter, QualiaSet, AttentionRegister, - WorkingMemory, VectorClock, + AttentionRegister, ConsciousnessState, PhiCounter, Quale, QualiaSet, VectorClock, WorkingMemory, }; pub use qualia_consensus::{ - QualiaConsensusNode, QualiaVotingConsensus, QualiaMessage, ConsensusResult, - ConsensusCoordinator, qualia_distance, + qualia_distance, ConsensusCoordinator, ConsensusResult, QualiaConsensusNode, QualiaMessage, + QualiaVotingConsensus, }; pub use federation_emergence::{ - EmergenceDetector, EmergenceIndicators, TopologyMetrics, ConsciousnessPhase, - CriticalCouplingCalculator, EmergencePrediction, + ConsciousnessPhase, CriticalCouplingCalculator, EmergenceDetector, EmergenceIndicators, + EmergencePrediction, TopologyMetrics, }; /// Version of the FCΦ framework diff --git a/examples/exo-ai-2025/research/06-federated-collective-phi/src/qualia_consensus.rs b/examples/exo-ai-2025/research/06-federated-collective-phi/src/qualia_consensus.rs index 7b078c661..70a6b2705 100644 --- a/examples/exo-ai-2025/research/06-federated-collective-phi/src/qualia_consensus.rs +++ b/examples/exo-ai-2025/research/06-federated-collective-phi/src/qualia_consensus.rs @@ -2,9 +2,9 @@ // Byzantine Fault Tolerant Consensus Protocol for Qualia // Based on PBFT (Practical Byzantine Fault Tolerance) -use std::collections::{HashMap, HashSet}; -use serde::{Serialize, Deserialize}; use super::consciousness_crdt::Quale; +use serde::{Deserialize, Serialize}; +use std::collections::{HashMap, HashSet}; /// Agent identifier pub type AgentId = u64; @@ -161,7 +161,10 @@ impl QualiaConsensusNode { None } - QualiaMessage::ViewChange { new_view, agent_id: _ } => { + QualiaMessage::ViewChange { + new_view, + agent_id: _, + } => { self.handle_view_change(new_view); None } @@ -447,7 +450,11 @@ impl ConsensusCoordinator { /// Run consensus round pub fn run_consensus_round(&mut self, leader_id: AgentId, qualia: Quale) -> ConsensusResult { // Leader proposes - let proposal = self.nodes.get_mut(&leader_id).unwrap().propose_qualia(qualia); + let proposal = self + .nodes + .get_mut(&leader_id) + .unwrap() + .propose_qualia(qualia); // Broadcast proposal let prepares = self.broadcast(proposal); @@ -532,7 +539,13 @@ mod tests { assert!(prepare.is_some()); // Also need to record the prepare from self - if let Some(QualiaMessage::QualiaPrepare { qualia: q, view, sequence, agent_id }) = prepare { + if let Some(QualiaMessage::QualiaPrepare { + qualia: q, + view, + sequence, + agent_id, + }) = prepare + { node.handle_prepare(q, view, sequence, agent_id); } @@ -593,6 +606,9 @@ mod tests { assert_eq!(result, ConsensusResult::Agreed(correct_qualia)); let hallucinating = node.detect_hallucinations(0); - assert!(hallucinating.contains(&4), "Agent 4 should be detected as hallucinating"); + assert!( + hallucinating.contains(&4), + "Agent 4 should be detected as hallucinating" + ); } } diff --git a/examples/exo-ai-2025/research/07-causal-emergence/benches/causal_emergence_bench.rs b/examples/exo-ai-2025/research/07-causal-emergence/benches/causal_emergence_bench.rs index dd294a136..13b5e6592 100644 --- a/examples/exo-ai-2025/research/07-causal-emergence/benches/causal_emergence_bench.rs +++ b/examples/exo-ai-2025/research/07-causal-emergence/benches/causal_emergence_bench.rs @@ -1,5 +1,5 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId, Throughput}; use causal_emergence::*; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; /// Generates a random-like transition matrix fn generate_transition_matrix(n: usize) -> Vec { @@ -21,13 +21,13 @@ fn generate_transition_matrix(n: usize) -> Vec { /// Generates synthetic time-series data with multi-scale structure fn generate_time_series(n: usize) -> Vec { - (0..n).map(|t| { - let t_f = t as f32; - // Three scales: slow, medium, fast oscillations - 0.5 * (t_f * 0.01).sin() + - 0.3 * (t_f * 0.05).cos() + - 0.2 * (t_f * 0.2).sin() - }).collect() + (0..n) + .map(|t| { + let t_f = t as f32; + // Three scales: slow, medium, fast oscillations + 0.5 * (t_f * 0.01).sin() + 0.3 * (t_f * 0.05).cos() + 0.2 * (t_f * 0.2).sin() + }) + .collect() } /// Benchmark: Effective Information computation with SIMD @@ -39,9 +39,7 @@ fn bench_effective_information(c: &mut Criterion) { group.throughput(Throughput::Elements((n * n) as u64)); group.bench_with_input(BenchmarkId::from_parameter(n), n, |b, &n| { - b.iter(|| { - compute_ei_simd(black_box(&matrix), black_box(n)) - }); + b.iter(|| compute_ei_simd(black_box(&matrix), black_box(n))); }); } @@ -59,9 +57,7 @@ fn bench_entropy(c: &mut Criterion) { group.throughput(Throughput::Elements(*n as u64)); group.bench_with_input(BenchmarkId::from_parameter(n), n, |b, _| { - b.iter(|| { - entropy_simd(black_box(&probs)) - }); + b.iter(|| entropy_simd(black_box(&probs))); }); } @@ -77,9 +73,7 @@ fn bench_coarse_graining(c: &mut Criterion) { group.throughput(Throughput::Elements(*n as u64)); group.bench_with_input(BenchmarkId::from_parameter(n), n, |b, &n| { - b.iter(|| { - ScaleHierarchy::build_sequential(black_box(matrix.clone()), black_box(2)) - }); + b.iter(|| ScaleHierarchy::build_sequential(black_box(matrix.clone()), black_box(2))); }); } @@ -96,9 +90,7 @@ fn bench_transfer_entropy(c: &mut Criterion) { group.throughput(Throughput::Elements(*n as u64)); group.bench_with_input(BenchmarkId::from_parameter(n), n, |b, _| { - b.iter(|| { - transfer_entropy(black_box(&x), black_box(&y), black_box(1), black_box(1)) - }); + b.iter(|| transfer_entropy(black_box(&x), black_box(&y), black_box(1), black_box(1))); }); } @@ -119,7 +111,7 @@ fn bench_consciousness_assessment(c: &mut Criterion) { black_box(&data), black_box(2), black_box(false), - black_box(5.0) + black_box(5.0), ) }); }); @@ -137,9 +129,7 @@ fn bench_emergence_detection(c: &mut Criterion) { group.throughput(Throughput::Elements(*n as u64)); group.bench_with_input(BenchmarkId::from_parameter(n), n, |b, _| { - b.iter(|| { - detect_emergence(black_box(&data), black_box(2), black_box(0.5)) - }); + b.iter(|| detect_emergence(black_box(&data), black_box(2), black_box(0.5))); }); } @@ -156,11 +146,7 @@ fn bench_causal_hierarchy(c: &mut Criterion) { group.throughput(Throughput::Elements(*n as u64)); group.bench_with_input(BenchmarkId::from_parameter(n), n, |b, _| { b.iter(|| { - CausalHierarchy::from_time_series( - black_box(&data), - black_box(2), - black_box(false) - ) + CausalHierarchy::from_time_series(black_box(&data), black_box(2), black_box(false)) }); }); } @@ -199,14 +185,10 @@ fn bench_multi_scale_ei(c: &mut Criterion) { }) .collect(); - let state_counts: Vec = (0..num_scales) - .map(|i| 256 >> i) - .collect(); + let state_counts: Vec = (0..num_scales).map(|i| 256 >> i).collect(); group.bench_function("5_scales", |b| { - b.iter(|| { - compute_ei_multi_scale(black_box(&matrices), black_box(&state_counts)) - }); + b.iter(|| compute_ei_multi_scale(black_box(&matrices), black_box(&state_counts))); }); group.finish(); @@ -220,15 +202,11 @@ fn bench_coarse_graining_methods(c: &mut Criterion) { let matrix = generate_transition_matrix(n); group.bench_function("sequential", |b| { - b.iter(|| { - ScaleHierarchy::build_sequential(black_box(matrix.clone()), black_box(2)) - }); + b.iter(|| ScaleHierarchy::build_sequential(black_box(matrix.clone()), black_box(2))); }); group.bench_function("optimal", |b| { - b.iter(|| { - ScaleHierarchy::build_optimal(black_box(matrix.clone()), black_box(2)) - }); + b.iter(|| ScaleHierarchy::build_optimal(black_box(matrix.clone()), black_box(2))); }); group.finish(); diff --git a/examples/exo-ai-2025/research/07-causal-emergence/src/causal_hierarchy.rs b/examples/exo-ai-2025/research/07-causal-emergence/src/causal_hierarchy.rs index 4145e714d..ed3a85a4b 100644 --- a/examples/exo-ai-2025/research/07-causal-emergence/src/causal_hierarchy.rs +++ b/examples/exo-ai-2025/research/07-causal-emergence/src/causal_hierarchy.rs @@ -1,8 +1,8 @@ // Hierarchical Causal Structure Management // Implements transfer entropy and consciousness metrics for HCC framework -use crate::effective_information::compute_ei_simd; use crate::coarse_graining::{ScaleHierarchy, ScaleLevel}; +use crate::effective_information::compute_ei_simd; use std::collections::HashMap; /// Represents the complete hierarchical causal structure with all metrics @@ -41,11 +41,7 @@ impl CausalHierarchy { /// /// # Returns /// Complete causal hierarchy with all metrics computed - pub fn from_time_series( - data: &[f32], - branching_factor: usize, - use_optimal: bool, - ) -> Self { + pub fn from_time_series(data: &[f32], branching_factor: usize, use_optimal: bool) -> Self { // Estimate transition matrix from data let transition_matrix = estimate_transition_matrix(data, 256); // 256 bins @@ -84,7 +80,10 @@ impl CausalHierarchy { } let micro_ei = self.metrics.ei[0]; - let (max_scale, &max_ei) = self.metrics.ei.iter() + let (max_scale, &max_ei) = self + .metrics + .ei + .iter() .enumerate() .max_by(|a, b| a.1.partial_cmp(b.1).unwrap())?; @@ -107,8 +106,7 @@ impl CausalHierarchy { } const TE_THRESHOLD: f32 = 0.01; // Minimum TE to count as causal - self.metrics.te_up[s] > TE_THRESHOLD && - self.metrics.te_down[s] > TE_THRESHOLD + self.metrics.te_up[s] > TE_THRESHOLD && self.metrics.te_down[s] > TE_THRESHOLD } } @@ -122,10 +120,7 @@ pub enum ConsciousnessLevel { } /// Computes all hierarchical metrics (EI, Φ, TE, Ψ) -fn compute_hierarchy_metrics( - hierarchy: &ScaleHierarchy, - data: &[f32], -) -> HierarchyMetrics { +fn compute_hierarchy_metrics(hierarchy: &ScaleHierarchy, data: &[f32]) -> HierarchyMetrics { let num_scales = hierarchy.num_scales(); // Compute EI at each scale @@ -164,7 +159,8 @@ fn compute_hierarchy_metrics( } // Find optimal scale (max Ψ) - let (optimal_scale, &consciousness_score) = psi.iter() + let (optimal_scale, &consciousness_score) = psi + .iter() .enumerate() .max_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal)) .unwrap_or((0, &0.0)); @@ -201,9 +197,7 @@ fn estimate_transition_matrix(data: &[f32], num_bins: usize) -> Vec { // Normalize to probabilities let mut matrix = vec![0.0f32; num_bins * num_bins]; for i in 0..num_bins { - let row_sum: u32 = (0..num_bins) - .map(|j| counts[i * num_bins + j]) - .sum(); + let row_sum: u32 = (0..num_bins).map(|j| counts[i * num_bins + j]).sum(); if row_sum > 0 { for j in 0..num_bins { @@ -249,14 +243,20 @@ fn project_to_scale(data: &[f32], level: &ScaleLevel) -> Vec { let binned = discretize_data(data, level.partition.num_micro_states()); // Map micro-states to macro-states - let micro_to_macro: HashMap = level.partition.groups.iter() + let micro_to_macro: HashMap = level + .partition + .groups + .iter() .enumerate() .flat_map(|(macro_idx, micro_group)| { - micro_group.iter().map(move |µ_idx| (micro_idx, macro_idx)) + micro_group + .iter() + .map(move |µ_idx| (micro_idx, macro_idx)) }) .collect(); - binned.iter() + binned + .iter() .map(|µ| *micro_to_macro.get(µ).unwrap_or(&0)) .collect() } @@ -270,12 +270,7 @@ fn project_to_scale(data: &[f32], level: &ScaleLevel) -> Vec { /// * `y` - Target time series (discretized) /// * `k` - History length for X /// * `l` - History length for Y -pub fn transfer_entropy( - x: &[usize], - y: &[usize], - k: usize, - l: usize, -) -> f32 { +pub fn transfer_entropy(x: &[usize], y: &[usize], k: usize, l: usize) -> f32 { if x.len() != y.len() || x.len() < k.max(l) + 1 { return 0.0; } @@ -286,11 +281,13 @@ pub fn transfer_entropy( // Count joint occurrences let mut counts = HashMap::new(); for t in lag..t_max { - let x_past: Vec<_> = x[t-k..t].to_vec(); - let y_past: Vec<_> = y[t-l..t].to_vec(); + let x_past: Vec<_> = x[t - k..t].to_vec(); + let y_past: Vec<_> = y[t - l..t].to_vec(); let y_future = y[t + 1]; - *counts.entry((y_future, x_past.clone(), y_past.clone())).or_insert(0) += 1; + *counts + .entry((y_future, x_past.clone(), y_past.clone())) + .or_insert(0) += 1; } let total = (t_max - lag) as f32; @@ -308,7 +305,9 @@ pub fn transfer_entropy( *p_y_future.entry(*y_fut).or_insert(0.0) += prob; *p_x_past.entry(x_p.clone()).or_insert(0.0) += prob; *p_y_past.entry(y_p.clone()).or_insert(0.0) += prob; - *p_y_xy.entry((*y_fut, x_p.clone(), y_p.clone())).or_insert(0.0) += prob; + *p_y_xy + .entry((*y_fut, x_p.clone(), y_p.clone())) + .or_insert(0.0) += prob; *p_xy.entry((x_p.clone(), y_p.clone())).or_insert(0.0) += prob; *p_y.entry(y_p.clone()).or_insert(0.0) += prob; } @@ -456,9 +455,9 @@ mod tests { let data: Vec = (0..1000) .map(|t| { // Multiple frequencies -> multi-scale structure - (t as f32 * 0.05).sin() + - 0.5 * (t as f32 * 0.2).cos() + - 0.25 * (t as f32 * 0.8).sin() + (t as f32 * 0.05).sin() + + 0.5 * (t as f32 * 0.2).cos() + + 0.25 * (t as f32 * 0.8).sin() }) .collect(); @@ -469,11 +468,12 @@ mod tests { // Check level classification works let level = hierarchy.consciousness_level(); - assert!(matches!(level, - ConsciousnessLevel::Unconscious | - ConsciousnessLevel::Borderline | - ConsciousnessLevel::MinimallyConscious | - ConsciousnessLevel::FullyConscious + assert!(matches!( + level, + ConsciousnessLevel::Unconscious + | ConsciousnessLevel::Borderline + | ConsciousnessLevel::MinimallyConscious + | ConsciousnessLevel::FullyConscious )); } } diff --git a/examples/exo-ai-2025/research/07-causal-emergence/src/coarse_graining.rs b/examples/exo-ai-2025/research/07-causal-emergence/src/coarse_graining.rs index 290cedf8d..8012f700b 100644 --- a/examples/exo-ai-2025/research/07-causal-emergence/src/coarse_graining.rs +++ b/examples/exo-ai-2025/research/07-causal-emergence/src/coarse_graining.rs @@ -56,10 +56,7 @@ impl Partition { /// /// # Algorithm /// T'[I,J] = (1/|group_I|) Σᵢ∈group_I Σⱼ∈group_J T[i,j] -pub fn coarse_grain_transition_matrix( - micro_matrix: &[f32], - partition: &Partition, -) -> Vec { +pub fn coarse_grain_transition_matrix(micro_matrix: &[f32], partition: &Partition) -> Vec { let n = (micro_matrix.len() as f32).sqrt() as usize; let m = partition.num_macro_states(); @@ -108,10 +105,7 @@ impl ScaleHierarchy { /// /// # Returns /// Hierarchy with O(log_k n) levels - pub fn build_sequential( - micro_matrix: Vec, - branching_factor: usize, - ) -> Self { + pub fn build_sequential(micro_matrix: Vec, branching_factor: usize) -> Self { let n = (micro_matrix.len() as f32).sqrt() as usize; let mut levels = Vec::new(); @@ -137,10 +131,7 @@ impl ScaleHierarchy { let new_partition = Partition::sequential(current_n, branching_factor); // Coarse-grain matrix - current_matrix = coarse_grain_transition_matrix( - ¤t_matrix, - &new_partition - ); + current_matrix = coarse_grain_transition_matrix(¤t_matrix, &new_partition); // Update partition relative to original micro-states current_partition = merge_partitions(¤t_partition, &new_partition); @@ -157,10 +148,7 @@ impl ScaleHierarchy { /// Builds hierarchy using optimal coarse-graining (minimizes redundancy) /// More expensive but finds better emergence - pub fn build_optimal( - micro_matrix: Vec, - branching_factor: usize, - ) -> Self { + pub fn build_optimal(micro_matrix: Vec, branching_factor: usize) -> Self { let n = (micro_matrix.len() as f32).sqrt() as usize; let mut levels = Vec::new(); @@ -182,16 +170,10 @@ impl ScaleHierarchy { let current_n = levels.last().unwrap().num_states; // Find optimal partition using similarity clustering - let new_partition = find_optimal_partition( - ¤t_matrix, - current_n, - branching_factor - ); + let new_partition = + find_optimal_partition(¤t_matrix, current_n, branching_factor); - current_matrix = coarse_grain_transition_matrix( - ¤t_matrix, - &new_partition - ); + current_matrix = coarse_grain_transition_matrix(¤t_matrix, &new_partition); current_partition = merge_partitions(¤t_partition, &new_partition); @@ -236,16 +218,14 @@ fn merge_partitions(current: &Partition, new: &Partition) -> Partition { merged_groups.push(merged_group); } - Partition { groups: merged_groups } + Partition { + groups: merged_groups, + } } /// Finds optimal k-way partition by minimizing within-group variance /// Uses k-means-like clustering on transition probability vectors -fn find_optimal_partition( - matrix: &[f32], - n: usize, - k: usize, -) -> Partition { +fn find_optimal_partition(matrix: &[f32], n: usize, k: usize) -> Partition { if n <= k { // Can't cluster into more groups than states return Partition::sequential(n, k); @@ -254,7 +234,7 @@ fn find_optimal_partition( // Extract row vectors (outgoing transition probabilities) let mut rows: Vec> = Vec::with_capacity(n); for i in 0..n { - rows.push(matrix[i*n..(i+1)*n].to_vec()); + rows.push(matrix[i * n..(i + 1) * n].to_vec()); } // Simple k-means clustering @@ -299,7 +279,8 @@ fn kmeans_cluster(data: &[Vec], k: usize) -> Vec { // Update centroids for c in 0..k { - let cluster_points: Vec<_> = data.iter() + let cluster_points: Vec<_> = data + .iter() .zip(&labels) .filter(|(_, &label)| label == c) .map(|(point, _)| point) @@ -357,10 +338,10 @@ mod tests { fn test_coarse_grain_deterministic() { // 4-state cycle: 0→1→2→3→0 let mut micro = vec![0.0; 16]; - micro[0*4 + 1] = 1.0; - micro[1*4 + 2] = 1.0; - micro[2*4 + 3] = 1.0; - micro[3*4 + 0] = 1.0; + micro[0 * 4 + 1] = 1.0; + micro[1 * 4 + 2] = 1.0; + micro[2 * 4 + 3] = 1.0; + micro[3 * 4 + 0] = 1.0; // Partition into 2 groups: [0,1] and [2,3] let partition = Partition { @@ -373,7 +354,7 @@ mod tests { assert_eq!(macro_matrix.len(), 4); // Group 0 transitions to group 1 with prob 0.5 (state 0→1 or 1→2) - assert!((macro_matrix[0*2 + 1] - 0.5).abs() < 0.01); + assert!((macro_matrix[0 * 2 + 1] - 0.5).abs() < 0.01); } #[test] @@ -382,12 +363,12 @@ mod tests { let mut matrix = vec![0.0; 256]; for i in 0..16 { for j in 0..16 { - matrix[i*16 + j] = ((i + j) % 10) as f32 / 10.0; + matrix[i * 16 + j] = ((i + j) % 10) as f32 / 10.0; } // Normalize row - let row_sum: f32 = matrix[i*16..(i+1)*16].iter().sum(); + let row_sum: f32 = matrix[i * 16..(i + 1) * 16].iter().sum(); for j in 0..16 { - matrix[i*16 + j] /= row_sum; + matrix[i * 16 + j] /= row_sum; } } @@ -408,7 +389,7 @@ mod tests { }; let partition2 = Partition { - groups: vec![vec![0, 1], vec![2]], // Merge groups 0&1, keep group 2 + groups: vec![vec![0, 1], vec![2]], // Merge groups 0&1, keep group 2 }; let merged = merge_partitions(&partition1, &partition2); diff --git a/examples/exo-ai-2025/research/07-causal-emergence/src/effective_information.rs b/examples/exo-ai-2025/research/07-causal-emergence/src/effective_information.rs index c6a74bd2d..b5cb66089 100644 --- a/examples/exo-ai-2025/research/07-causal-emergence/src/effective_information.rs +++ b/examples/exo-ai-2025/research/07-causal-emergence/src/effective_information.rs @@ -149,11 +149,12 @@ fn conditional_entropy_simd(matrix: &[f32], n: usize) -> f32 { /// Vector of EI values, one per scale pub fn compute_ei_multi_scale( transition_matrices: &[Vec], - state_counts: &[usize] + state_counts: &[usize], ) -> Vec { assert_eq!(transition_matrices.len(), state_counts.len()); - transition_matrices.iter() + transition_matrices + .iter() .zip(state_counts.iter()) .map(|(matrix, &n)| compute_ei_simd(matrix, n)) .collect() @@ -170,7 +171,8 @@ pub fn detect_causal_emergence(ei_per_scale: &[f32]) -> Option<(usize, f32)> { return None; } - let (max_scale, &max_ei) = ei_per_scale.iter() + let (max_scale, &max_ei) = ei_per_scale + .iter() .enumerate() .max_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))?; @@ -209,9 +211,12 @@ mod tests { let ei = compute_ei_simd(&matrix, n); let expected = (n as f32).log2(); // Should be maximal - assert!((ei - expected).abs() < 0.1, + assert!( + (ei - expected).abs() < 0.1, "Deterministic system should have EI ≈ log₂(n), got {}, expected {}", - ei, expected); + ei, + expected + ); } #[test] @@ -222,8 +227,7 @@ mod tests { let ei = compute_ei_simd(&matrix, n); - assert!(ei < 0.1, - "Random system should have EI ≈ 0, got {}", ei); + assert!(ei < 0.1, "Random system should have EI ≈ 0, got {}", ei); } #[test] @@ -238,16 +242,22 @@ mod tests { let ei = compute_ei_simd(&matrix, n); let expected = (n as f32).log2(); - assert!((ei - expected).abs() < 0.1, + assert!( + (ei - expected).abs() < 0.1, "Identity should have maximal EI, got {}, expected {}", - ei, expected); + ei, + expected + ); } #[test] fn test_entropy_uniform() { let probs = vec![0.25, 0.25, 0.25, 0.25]; let h = entropy_simd(&probs); - assert!((h - 2.0).abs() < 0.01, "Uniform 4-state should have H=2 bits"); + assert!( + (h - 2.0).abs() < 0.01, + "Uniform 4-state should have H=2 bits" + ); } #[test] @@ -265,7 +275,10 @@ mod tests { let (emergent_scale, gain) = detect_causal_emergence(&ei_scales).unwrap(); assert_eq!(emergent_scale, 2, "Should detect scale 2 as emergent"); - assert!((gain - 2.2).abs() < 0.01, "EI gain should be 4.2 - 2.0 = 2.2"); + assert!( + (gain - 2.2).abs() < 0.01, + "EI gain should be 4.2 - 2.0 = 2.2" + ); } #[test] @@ -335,8 +348,12 @@ pub mod bench { for n in [16, 64, 256, 1024] { let time = benchmark_ei(n, 100); let states_per_sec = (n * n) as f64 / time; - println!("n={:4}: {:.3}ms ({:.0} states/sec)", - n, time * 1000.0, states_per_sec); + println!( + "n={:4}: {:.3}ms ({:.0} states/sec)", + n, + time * 1000.0, + states_per_sec + ); } } } diff --git a/examples/exo-ai-2025/research/07-causal-emergence/src/emergence_detection.rs b/examples/exo-ai-2025/research/07-causal-emergence/src/emergence_detection.rs index 728d00205..e32a7d245 100644 --- a/examples/exo-ai-2025/research/07-causal-emergence/src/emergence_detection.rs +++ b/examples/exo-ai-2025/research/07-causal-emergence/src/emergence_detection.rs @@ -1,8 +1,8 @@ // Automatic Emergence Detection and Scale Selection // Implements NeuralRG-inspired methods for optimal coarse-graining -use crate::coarse_graining::Partition; use crate::causal_hierarchy::{CausalHierarchy, ConsciousnessLevel}; +use crate::coarse_graining::Partition; /// Result of emergence detection analysis #[derive(Debug, Clone)] @@ -78,7 +78,8 @@ pub fn detect_emergence( // Find scale with maximum EI let micro_ei = ei_progression[0]; - let (emergent_scale, &max_ei) = ei_progression.iter() + let (emergent_scale, &max_ei) = ei_progression + .iter() .enumerate() .max_by(|a, b| a.1.partial_cmp(b.1).unwrap()) .unwrap(); @@ -125,11 +126,8 @@ pub fn assess_consciousness( threshold: f32, ) -> ConsciousnessReport { // Build causal hierarchy with full metrics - let hierarchy = CausalHierarchy::from_time_series( - data, - branching_factor, - use_optimal_partition - ); + let hierarchy = + CausalHierarchy::from_time_series(data, branching_factor, use_optimal_partition); // Extract key metrics at conscious scale let conscious_scale = hierarchy.metrics.optimal_scale; @@ -138,10 +136,30 @@ pub fn assess_consciousness( let is_conscious = hierarchy.is_conscious(threshold); let has_circular_causation = hierarchy.has_circular_causation(); - let ei = hierarchy.metrics.ei.get(conscious_scale).copied().unwrap_or(0.0); - let phi = hierarchy.metrics.phi.get(conscious_scale).copied().unwrap_or(0.0); - let te_up = hierarchy.metrics.te_up.get(conscious_scale).copied().unwrap_or(0.0); - let te_down = hierarchy.metrics.te_down.get(conscious_scale).copied().unwrap_or(0.0); + let ei = hierarchy + .metrics + .ei + .get(conscious_scale) + .copied() + .unwrap_or(0.0); + let phi = hierarchy + .metrics + .phi + .get(conscious_scale) + .copied() + .unwrap_or(0.0); + let te_up = hierarchy + .metrics + .te_up + .get(conscious_scale) + .copied() + .unwrap_or(0.0); + let te_down = hierarchy + .metrics + .te_down + .get(conscious_scale) + .copied() + .unwrap_or(0.0); // Run emergence detection let emergence = detect_emergence(data, branching_factor, 0.5); @@ -174,7 +192,8 @@ pub fn compare_consciousness_states( branching_factor: usize, threshold: f32, ) -> Vec { - datasets.iter() + datasets + .iter() .map(|data| assess_consciousness(data, branching_factor, false, threshold)) .collect() } @@ -203,10 +222,14 @@ pub fn find_optimal_scale( ScaleOptimizationCriterion::MaxEmergence => { // Compute EI gain relative to micro-level let micro_ei = hierarchy.metrics.ei.first().copied().unwrap_or(0.0); - let gains: Vec = hierarchy.metrics.ei.iter() + let gains: Vec = hierarchy + .metrics + .ei + .iter() .map(|&ei| ei - micro_ei) .collect(); - let (scale, &max_gain) = gains.iter() + let (scale, &max_gain) = gains + .iter() .enumerate() .max_by(|a, b| a.1.partial_cmp(b.1).unwrap()) .unwrap_or((0, &0.0)); @@ -214,7 +237,8 @@ pub fn find_optimal_scale( } }; - values.iter() + values + .iter() .enumerate() .max_by(|a, b| a.1.partial_cmp(b.1).unwrap()) .map(|(idx, &val)| (idx, val)) @@ -260,7 +284,7 @@ impl ConsciousnessMonitor { &self.buffer, self.branching_factor, false, // Use fast sequential partitioning for real-time - self.threshold + self.threshold, ); self.last_report = Some(report.clone()); @@ -272,7 +296,8 @@ impl ConsciousnessMonitor { } pub fn is_conscious(&self) -> bool { - self.last_report.as_ref() + self.last_report + .as_ref() .map(|r| r.is_conscious) .unwrap_or(false) } @@ -374,12 +399,8 @@ pub mod export { } /// Exports time series as CSV - pub fn time_series_to_csv( - results: &[(usize, ConsciousnessReport)] - ) -> String { - let mut csv = String::from( - "time,score,level,ei,phi,te_up,te_down,emergent_scale\n" - ); + pub fn time_series_to_csv(results: &[(usize, ConsciousnessReport)]) -> String { + let mut csv = String::from("time,score,level,ei,phi,te_up,te_down,emergent_scale\n"); for (t, report) in results { csv.push_str(&format!( @@ -405,22 +426,24 @@ mod tests { fn generate_synthetic_conscious_data(n: usize) -> Vec { // Multi-scale oscillations simulate hierarchical structure - (0..n).map(|t| { - let t_f = t as f32; - // Low frequency (macro) - 0.5 * (t_f * 0.01).sin() + + (0..n) + .map(|t| { + let t_f = t as f32; + // Low frequency (macro) + 0.5 * (t_f * 0.01).sin() + // Medium frequency 0.3 * (t_f * 0.05).cos() + // High frequency (micro) 0.2 * (t_f * 0.2).sin() - }).collect() + }) + .collect() } fn generate_synthetic_unconscious_data(n: usize) -> Vec { // Random noise - no hierarchical structure - (0..n).map(|t| { - ((t * 12345 + 67890) % 1000) as f32 / 1000.0 - }).collect() + (0..n) + .map(|t| ((t * 12345 + 67890) % 1000) as f32 / 1000.0) + .collect() } #[test] @@ -440,7 +463,10 @@ mod tests { // Should detect some level of organization assert!(report.score >= 0.0); - assert_eq!(report.emergence.ei_progression.len(), report.ei_progression_len()); + assert_eq!( + report.emergence.ei_progression.len(), + report.ei_progression_len() + ); } #[test] diff --git a/examples/exo-ai-2025/research/07-causal-emergence/src/lib.rs b/examples/exo-ai-2025/research/07-causal-emergence/src/lib.rs index e494c5651..914c94e64 100644 --- a/examples/exo-ai-2025/research/07-causal-emergence/src/lib.rs +++ b/examples/exo-ai-2025/research/07-causal-emergence/src/lib.rs @@ -46,46 +46,26 @@ // Feature gate for SIMD (stable in Rust 1.80+) #![feature(portable_simd)] -pub mod effective_information; -pub mod coarse_graining; pub mod causal_hierarchy; +pub mod coarse_graining; +pub mod effective_information; pub mod emergence_detection; // Re-export key types and functions for convenience pub use effective_information::{ - compute_ei_simd, - entropy_simd, - compute_ei_multi_scale, - detect_causal_emergence, - normalized_ei, + compute_ei_multi_scale, compute_ei_simd, detect_causal_emergence, entropy_simd, normalized_ei, }; -pub use coarse_graining::{ - Partition, - ScaleLevel, - ScaleHierarchy, - coarse_grain_transition_matrix, -}; +pub use coarse_graining::{coarse_grain_transition_matrix, Partition, ScaleHierarchy, ScaleLevel}; pub use causal_hierarchy::{ - CausalHierarchy, - HierarchyMetrics, - ConsciousnessLevel, - transfer_entropy, + transfer_entropy, CausalHierarchy, ConsciousnessLevel, HierarchyMetrics, }; pub use emergence_detection::{ - EmergenceReport, - ConsciousnessReport, - detect_emergence, - assess_consciousness, - compare_consciousness_states, - find_optimal_scale, - ScaleOptimizationCriterion, - ConsciousnessMonitor, - consciousness_time_series, - detect_consciousness_transitions, - ConsciousnessTransition, + assess_consciousness, compare_consciousness_states, consciousness_time_series, + detect_consciousness_transitions, detect_emergence, find_optimal_scale, ConsciousnessMonitor, + ConsciousnessReport, ConsciousnessTransition, EmergenceReport, ScaleOptimizationCriterion, }; /// Library version @@ -110,9 +90,7 @@ mod integration_tests { let data: Vec = (0..500) .map(|t| { let t_f = t as f32; - 0.5 * (t_f * 0.05).sin() + - 0.3 * (t_f * 0.15).cos() + - 0.2 * (t_f * 0.5).sin() + 0.5 * (t_f * 0.05).sin() + 0.3 * (t_f * 0.15).cos() + 0.2 * (t_f * 0.5).sin() }) .collect(); diff --git a/examples/exo-ai-2025/research/08-meta-simulation-consciousness/benches/meta_sim_benchmarks.rs b/examples/exo-ai-2025/research/08-meta-simulation-consciousness/benches/meta_sim_benchmarks.rs index c27898c9f..b05620e2b 100644 --- a/examples/exo-ai-2025/research/08-meta-simulation-consciousness/benches/meta_sim_benchmarks.rs +++ b/examples/exo-ai-2025/research/08-meta-simulation-consciousness/benches/meta_sim_benchmarks.rs @@ -15,9 +15,7 @@ fn bench_closed_form_phi(c: &mut Criterion) { let calculator = ClosedFormPhi::default(); - b.iter(|| { - black_box(calculator.compute_phi_ergodic(&adj, &nodes)) - }); + b.iter(|| black_box(calculator.compute_phi_ergodic(&adj, &nodes))); }); } @@ -36,9 +34,7 @@ fn bench_cei_computation(c: &mut Criterion) { let calculator = ClosedFormPhi::default(); - b.iter(|| { - black_box(calculator.compute_cei(&adj, 1.0)) - }); + b.iter(|| black_box(calculator.compute_cei(&adj, 1.0))); }); } @@ -58,9 +54,7 @@ fn bench_ergodicity_test(c: &mut Criterion) { let analyzer = ErgodicityAnalyzer::default(); let observable = |state: &[f64]| state[0]; - b.iter(|| { - black_box(analyzer.test_ergodicity(&transition, observable)) - }); + b.iter(|| black_box(analyzer.test_ergodicity(&transition, observable))); }); } @@ -72,7 +66,11 @@ fn bench_hierarchical_phi(c: &mut Criterion) { group.bench_function("batch_64_depth_3", |b| { let param_space = ConsciousnessParameterSpace::new(4); - let networks: Vec<_> = param_space.generate_networks().into_iter().take(64).collect(); + let networks: Vec<_> = param_space + .generate_networks() + .into_iter() + .take(64) + .collect(); b.iter(|| { let mut batcher = HierarchicalPhiBatcher::new(64, 3, 4); diff --git a/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/closed_form_phi.rs b/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/closed_form_phi.rs index 3f81bee80..f0a7cb7e9 100644 --- a/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/closed_form_phi.rs +++ b/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/closed_form_phi.rs @@ -299,7 +299,9 @@ impl ClosedFormPhi { } if indices[w].is_none() { - strongconnect(w, adjacency, node_ids, index, stack, indices, lowlinks, on_stack, sccs); + strongconnect( + w, adjacency, node_ids, index, stack, indices, lowlinks, on_stack, sccs, + ); lowlinks[v] = lowlinks[v].min(lowlinks[w]); } else if on_stack[w] { lowlinks[v] = lowlinks[v].min(indices[w].unwrap()); @@ -323,8 +325,17 @@ impl ClosedFormPhi { for v in 0..n { if indices[v].is_none() { - strongconnect(v, adjacency, node_ids, &mut index, &mut stack, - &mut indices, &mut lowlinks, &mut on_stack, &mut sccs); + strongconnect( + v, + adjacency, + node_ids, + &mut index, + &mut stack, + &mut indices, + &mut lowlinks, + &mut on_stack, + &mut sccs, + ); } } diff --git a/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/ergodic_consciousness.rs b/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/ergodic_consciousness.rs index 7c571685b..251e71e73 100644 --- a/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/ergodic_consciousness.rs +++ b/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/ergodic_consciousness.rs @@ -19,7 +19,6 @@ //! - Perspective invariance: Same statistics from any starting point //! - Self-similarity: Structure preserved across time scales - /// Ergodicity tester for cognitive systems pub struct ErgodicityAnalyzer { /// Number of time steps for temporal average diff --git a/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/hierarchical_phi.rs b/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/hierarchical_phi.rs index 765dae0f0..5bb3485c4 100644 --- a/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/hierarchical_phi.rs +++ b/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/hierarchical_phi.rs @@ -105,8 +105,8 @@ impl HierarchicalPhiBatcher { if start < phi_values.len() { // Aggregate via mean (could also use median, max, etc.) - let batch_mean: f64 = phi_values[start..end].iter().sum::() - / (end - start) as f64; + let batch_mean: f64 = + phi_values[start..end].iter().sum::() / (end - start) as f64; compressed.push(batch_mean); } } @@ -268,7 +268,10 @@ impl HierarchicalPhiResults { summary.push_str(&format!("Hierarchical Φ Computation Results\n")); summary.push_str(&format!("===================================\n")); - summary.push_str(&format!("Networks processed: {}\n", self.total_networks_processed)); + summary.push_str(&format!( + "Networks processed: {}\n", + self.total_networks_processed + )); summary.push_str(&format!( "Effective simulations: {:.2e}\n", self.effective_simulations as f64 @@ -399,7 +402,11 @@ mod tests { // Generate test networks let param_space = ConsciousnessParameterSpace::new(4); - let networks: Vec<_> = param_space.generate_networks().into_iter().take(64).collect(); + let networks: Vec<_> = param_space + .generate_networks() + .into_iter() + .take(64) + .collect(); let results = batcher.process_hierarchical_batch(&networks); diff --git a/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/lib.rs b/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/lib.rs index 463e374f5..429e57ce6 100644 --- a/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/lib.rs +++ b/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/lib.rs @@ -47,22 +47,20 @@ pub mod meta_sim_awareness; pub mod simd_ops; // Re-export main types -pub use closed_form_phi::{ClosedFormPhi, ErgodicPhiResult, shannon_entropy}; +pub use closed_form_phi::{shannon_entropy, ClosedFormPhi, ErgodicPhiResult}; pub use ergodic_consciousness::{ - ErgodicityAnalyzer, ErgodicityResult, ErgodicPhase, - ConsciousnessErgodicityMetrics, ErgodicPhaseDetector, + ConsciousnessErgodicityMetrics, ErgodicPhase, ErgodicPhaseDetector, ErgodicityAnalyzer, + ErgodicityResult, }; pub use hierarchical_phi::{ - HierarchicalPhiBatcher, HierarchicalPhiResults, - PhiLevelStats, ConsciousnessParameterSpace, + ConsciousnessParameterSpace, HierarchicalPhiBatcher, HierarchicalPhiResults, PhiLevelStats, }; pub use meta_sim_awareness::{ - MetaConsciousnessSimulator, MetaSimConfig, - MetaSimulationResults, ConsciousnessHotspot, + ConsciousnessHotspot, MetaConsciousnessSimulator, MetaSimConfig, MetaSimulationResults, }; pub use simd_ops::{ - simd_matvec_multiply, simd_batch_entropy, simd_entropy, - SimdCounterfactualBrancher, SimulationTreeExplorer, + simd_batch_entropy, simd_entropy, simd_matvec_multiply, SimdCounterfactualBrancher, + SimulationTreeExplorer, }; /// Library version @@ -101,10 +99,7 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION"); /// println!("Ergodic: {}", result.is_ergodic); /// println!("Computation time: {} μs", result.computation_time_us); /// ``` -pub fn measure_consciousness( - adjacency: &[Vec], - node_ids: &[u64], -) -> ErgodicPhiResult { +pub fn measure_consciousness(adjacency: &[Vec], node_ids: &[u64]) -> ErgodicPhiResult { let calculator = ClosedFormPhi::default(); calculator.compute_phi_ergodic(adjacency, node_ids) } diff --git a/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/meta_sim_awareness.rs b/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/meta_sim_awareness.rs index fd1910326..851f76fbd 100644 --- a/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/meta_sim_awareness.rs +++ b/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/meta_sim_awareness.rs @@ -9,8 +9,8 @@ //! 5. Multi-core parallelism (12x on M3 Ultra) use crate::closed_form_phi::ClosedFormPhi; -use crate::hierarchical_phi::{HierarchicalPhiBatcher, ConsciousnessParameterSpace}; use crate::ergodic_consciousness::{ErgodicityAnalyzer, ErgodicityResult}; +use crate::hierarchical_phi::{ConsciousnessParameterSpace, HierarchicalPhiBatcher}; /// Meta-simulation engine for consciousness pub struct MetaConsciousnessSimulator { @@ -95,10 +95,7 @@ impl MetaConsciousnessSimulator { let param_space = ConsciousnessParameterSpace::new(self.config.network_size); let networks = param_space.generate_networks(); - println!( - "Generated {} network variations", - networks.len() - ); + println!("Generated {} network variations", networks.len()); // Process through hierarchical Φ computation let hierarchical_results = self.hierarchical.process_hierarchical_batch(&networks); @@ -110,8 +107,8 @@ impl MetaConsciousnessSimulator { let cei_distribution = self.compute_cei_distribution(&networks); // Total effective simulations - let effective_sims = hierarchical_results.effective_simulations - * self.config.effective_multiplier(); + let effective_sims = + hierarchical_results.effective_simulations * self.config.effective_multiplier(); let elapsed = start.elapsed(); @@ -211,7 +208,10 @@ impl MetaSimulationResults { summary.push_str(" META-SIMULATION OF CONSCIOUSNESS - RESULTS\n"); summary.push_str("═══════════════════════════════════════════════════════\n\n"); - summary.push_str(&format!("Total networks analyzed: {}\n", self.total_networks)); + summary.push_str(&format!( + "Total networks analyzed: {}\n", + self.total_networks + )); summary.push_str(&format!( "Effective simulations: {:.2e}\n", self.effective_simulations as f64 @@ -264,8 +264,8 @@ impl MetaSimulationResults { // CEI stats summary.push_str("\nConsciousness Eigenvalue Index (CEI):\n"); summary.push_str("─────────────────────────────────────\n"); - let cei_mean: f64 = self.cei_distribution.iter().sum::() - / self.cei_distribution.len() as f64; + let cei_mean: f64 = + self.cei_distribution.iter().sum::() / self.cei_distribution.len() as f64; summary.push_str(&format!(" Mean CEI: {:.3}\n", cei_mean)); let mut cei_sorted = self.cei_distribution.clone(); diff --git a/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/simd_ops.rs b/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/simd_ops.rs index 6c006d009..8b91eb0dc 100644 --- a/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/simd_ops.rs +++ b/examples/exo-ai-2025/research/08-meta-simulation-consciousness/src/simd_ops.rs @@ -332,10 +332,7 @@ impl SimulationTreeExplorer { /// Explore all simulation branches up to max_depth /// Returns hotspots (high-Φ configurations) - pub fn explore( - &self, - initial_state: &[Vec], - ) -> Vec<(Vec>, f64)> { + pub fn explore(&self, initial_state: &[Vec]) -> Vec<(Vec>, f64)> { let mut hotspots = Vec::new(); self.explore_recursive(initial_state, 0, 1.0, &mut hotspots); @@ -366,7 +363,8 @@ impl SimulationTreeExplorer { // Recurse on high-potential branches for (i, &phi) in phi_values.iter().enumerate() { - if phi > phi_parent * 0.9 { // Only explore if Φ competitive + if phi > phi_parent * 0.9 { + // Only explore if Φ competitive let mut new_state = state.to_vec(); // Apply perturbation for (row_idx, row) in perturbations[i].iter().enumerate() { @@ -474,10 +472,7 @@ mod tests { vec![1.0, 0.0, 0.0], ]; - let perturbations = vec![ - vec![vec![0.1; 3]; 3], - vec![vec![0.05; 3]; 3], - ]; + let perturbations = vec![vec![vec![0.1; 3]; 3], vec![vec![0.05; 3]; 3]]; let results = brancher.evaluate_branches(&base, &perturbations); assert_eq!(results.len(), 2); diff --git a/examples/exo-ai-2025/research/09-hyperbolic-attention/benches/hyperbolic_ops.rs b/examples/exo-ai-2025/research/09-hyperbolic-attention/benches/hyperbolic_ops.rs index cd4160abe..e0564d869 100644 --- a/examples/exo-ai-2025/research/09-hyperbolic-attention/benches/hyperbolic_ops.rs +++ b/examples/exo-ai-2025/research/09-hyperbolic-attention/benches/hyperbolic_ops.rs @@ -1,4 +1,4 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId, Throughput}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use hyperbolic_attention::prelude::*; use hyperbolic_attention::HyperbolicTransformerBlock; @@ -41,13 +41,7 @@ fn bench_mobius_add(c: &mut Criterion) { let k = 1.0; group.bench_with_input(BenchmarkId::from_parameter(dim), &dim, |b, _| { - b.iter(|| { - black_box(mobius_add( - black_box(&x), - black_box(&y), - black_box(k), - )) - }); + b.iter(|| black_box(mobius_add(black_box(&x), black_box(&y), black_box(k)))); }); } @@ -65,13 +59,7 @@ fn bench_exponential_map(c: &mut Criterion) { let k = 1.0; group.bench_with_input(BenchmarkId::from_parameter(dim), &dim, |b, _| { - b.iter(|| { - black_box(exponential_map( - black_box(&x), - black_box(&v), - black_box(k), - )) - }); + b.iter(|| black_box(exponential_map(black_box(&x), black_box(&v), black_box(k)))); }); } @@ -123,13 +111,7 @@ fn bench_lorentz_distance(c: &mut Criterion) { let y = poincare_to_lorentz(&spatial_y, k); group.bench_with_input(BenchmarkId::from_parameter(dim), &dim, |b, _| { - b.iter(|| { - black_box(lorentz_distance( - black_box(&x), - black_box(&y), - black_box(k), - )) - }); + b.iter(|| black_box(lorentz_distance(black_box(&x), black_box(&y), black_box(k)))); }); } @@ -151,13 +133,7 @@ fn bench_lorentz_exp(c: &mut Criterion) { .collect(); group.bench_with_input(BenchmarkId::from_parameter(dim), &dim, |b, _| { - b.iter(|| { - black_box(lorentz_exp( - black_box(&x), - black_box(&v), - black_box(k), - )) - }); + b.iter(|| black_box(lorentz_exp(black_box(&x), black_box(&v), black_box(k)))); }); } @@ -238,9 +214,7 @@ fn bench_transformer_block(c: &mut Criterion) { let label = format!("d{}_s{}_h{}", dim, seq_len, num_heads); group.bench_with_input(BenchmarkId::from_parameter(&label), &label, |b, _| { - b.iter(|| { - black_box(block.forward(black_box(&inputs))) - }); + b.iter(|| black_box(block.forward(black_box(&inputs)))); }); } @@ -302,15 +276,9 @@ fn bench_simd_dot_product(c: &mut Criterion) { use hyperbolic_attention::poincare_embedding::dot_product_simd; - group.bench_with_input( - BenchmarkId::new("dot_product", dim), - &dim, - |bench, _| { - bench.iter(|| { - black_box(dot_product_simd(black_box(&a), black_box(&b))) - }); - }, - ); + group.bench_with_input(BenchmarkId::new("dot_product", dim), &dim, |bench, _| { + bench.iter(|| black_box(dot_product_simd(black_box(&a), black_box(&b)))); + }); } group.finish(); @@ -328,11 +296,7 @@ criterion_group!( bench_batch_distances, ); -criterion_group!( - lorentz_benches, - bench_lorentz_distance, - bench_lorentz_exp, -); +criterion_group!(lorentz_benches, bench_lorentz_distance, bench_lorentz_exp,); criterion_group!( attention_benches, @@ -347,10 +311,7 @@ criterion_group!( bench_multi_curvature, ); -criterion_group!( - simd_benches, - bench_simd_dot_product, -); +criterion_group!(simd_benches, bench_simd_dot_product,); criterion_main!( poincare_benches, diff --git a/examples/exo-ai-2025/research/09-hyperbolic-attention/src/curvature_adaptation.rs b/examples/exo-ai-2025/research/09-hyperbolic-attention/src/curvature_adaptation.rs index d22f911c6..b50032e38 100644 --- a/examples/exo-ai-2025/research/09-hyperbolic-attention/src/curvature_adaptation.rs +++ b/examples/exo-ai-2025/research/09-hyperbolic-attention/src/curvature_adaptation.rs @@ -40,7 +40,9 @@ impl LearnableCurvature { /// Get current curvature value pub fn value(&self) -> f32 { - self.log_k.exp().clamp(self.min_curvature, self.max_curvature) + self.log_k + .exp() + .clamp(self.min_curvature, self.max_curvature) } /// Update curvature given gradient @@ -91,7 +93,10 @@ impl MultiCurvature { let weights = vec![1.0 / (num_components as f32).sqrt(); num_components]; - Self { curvatures, weights } + Self { + curvatures, + weights, + } } /// Create with different initial curvatures @@ -104,7 +109,10 @@ impl MultiCurvature { let num = curvatures.len(); let weights = vec![1.0 / (num as f32).sqrt(); num]; - Self { curvatures, weights } + Self { + curvatures, + weights, + } } /// Get all curvature values @@ -129,10 +137,7 @@ impl MultiCurvature { /// Compute product distance /// /// d²((x₁,...,xₖ), (y₁,...,yₖ)) = Σᵢ wᵢ² dᵢ²(xᵢ, yᵢ) - pub fn product_distance_squared( - &self, - distances_squared: &[f32], - ) -> f32 { + pub fn product_distance_squared(&self, distances_squared: &[f32]) -> f32 { assert_eq!(distances_squared.len(), self.weights.len()); self.weights @@ -201,11 +206,7 @@ impl CoupledCurvatureOptimizer { /// d(x, y) = 2K · artanh(||(-x) ⊕_K y|| / K) /// /// ∂d/∂K requires chain rule through Möbius addition -pub fn distance_gradient_wrt_curvature( - x: &[f32], - y: &[f32], - curvature: f32, -) -> f32 { +pub fn distance_gradient_wrt_curvature(x: &[f32], y: &[f32], curvature: f32) -> f32 { // Numerical gradient (for simplicity - could derive analytically) let eps = 1e-4; @@ -338,8 +339,7 @@ mod tests { #[test] fn test_curvature_bounds() { - let mut curvature = LearnableCurvature::new(1.0) - .with_bounds(0.5, 2.0); + let mut curvature = LearnableCurvature::new(1.0).with_bounds(0.5, 2.0); // Try to push below minimum for _ in 0..100 { diff --git a/examples/exo-ai-2025/research/09-hyperbolic-attention/src/hyperbolic_attention.rs b/examples/exo-ai-2025/research/09-hyperbolic-attention/src/hyperbolic_attention.rs index 6528d19c2..b67bc5cb8 100644 --- a/examples/exo-ai-2025/research/09-hyperbolic-attention/src/hyperbolic_attention.rs +++ b/examples/exo-ai-2025/research/09-hyperbolic-attention/src/hyperbolic_attention.rs @@ -12,7 +12,7 @@ //! - SIMD-optimized batch operations use crate::poincare_embedding::{ - poincare_distance, mobius_add, clip_to_ball, exponential_map, logarithmic_map, + clip_to_ball, exponential_map, logarithmic_map, mobius_add, poincare_distance, }; /// Hyperbolic attention configuration @@ -180,11 +180,7 @@ impl HyperbolicAttention { /// Formula: ⊕ᵢ (wᵢ ⊗ vᵢ) /// /// where ⊗ is hyperbolic scalar multiplication -pub fn hyperbolic_weighted_sum( - vectors: &[Vec], - weights: &[f32], - curvature: f32, -) -> Vec { +pub fn hyperbolic_weighted_sum(vectors: &[Vec], weights: &[f32], curvature: f32) -> Vec { assert_eq!(vectors.len(), weights.len()); if vectors.is_empty() { @@ -394,11 +390,7 @@ mod tests { #[test] fn test_hyperbolic_weighted_sum() { - let vectors = vec![ - vec![0.1, 0.1], - vec![0.2, 0.1], - vec![0.1, 0.2], - ]; + let vectors = vec![vec![0.1, 0.1], vec![0.2, 0.1], vec![0.1, 0.2]]; let weights = vec![0.5, 0.3, 0.2]; let k = 1.0; @@ -415,14 +407,8 @@ mod tests { let attention = HyperbolicAttention::new(config); let queries = vec![vec![0.1, 0.1, 0.0, 0.0]]; - let keys = vec![ - vec![0.1, 0.0, 0.1, 0.0], - vec![0.0, 0.1, 0.0, 0.1], - ]; - let values = vec![ - vec![0.2, 0.1, 0.0, 0.0], - vec![0.1, 0.2, 0.0, 0.0], - ]; + let keys = vec![vec![0.1, 0.0, 0.1, 0.0], vec![0.0, 0.1, 0.0, 0.1]]; + let values = vec![vec![0.2, 0.1, 0.0, 0.0], vec![0.1, 0.2, 0.0, 0.0]]; let output = attention.forward(&queries, &keys, &values); @@ -438,10 +424,7 @@ mod tests { let config = HyperbolicAttentionConfig::new(8, 2, 1.0); let attention = MultiHeadHyperbolicAttention::new(config); - let inputs = vec![ - vec![0.1; 8], - vec![0.2; 8], - ]; + let inputs = vec![vec![0.1; 8], vec![0.2; 8]]; let output = attention.forward(&inputs, &inputs, &inputs); @@ -454,10 +437,7 @@ mod tests { let config = HyperbolicAttentionConfig::new(4, 1, 1.0); let layer = HyperbolicSelfAttentionLayer::new(config); - let inputs = vec![ - vec![0.1, 0.1, 0.0, 0.0], - vec![0.2, 0.1, 0.1, 0.0], - ]; + let inputs = vec![vec![0.1, 0.1, 0.0, 0.0], vec![0.2, 0.1, 0.1, 0.0]]; let output = layer.forward(&inputs); diff --git a/examples/exo-ai-2025/research/09-hyperbolic-attention/src/lib.rs b/examples/exo-ai-2025/research/09-hyperbolic-attention/src/lib.rs index 0c89fb7de..967bc8942 100644 --- a/examples/exo-ai-2025/research/09-hyperbolic-attention/src/lib.rs +++ b/examples/exo-ai-2025/research/09-hyperbolic-attention/src/lib.rs @@ -48,46 +48,31 @@ #![allow(dead_code)] #![allow(unused_imports)] -pub mod poincare_embedding; -pub mod lorentz_model; -pub mod hyperbolic_attention; pub mod curvature_adaptation; +pub mod hyperbolic_attention; +pub mod lorentz_model; +pub mod poincare_embedding; /// Prelude for convenient imports pub mod prelude { pub use crate::poincare_embedding::{ + batch_poincare_distances, exponential_map, logarithmic_map, mobius_add, poincare_distance, PoincarePoint, - mobius_add, - poincare_distance, - exponential_map, - logarithmic_map, - batch_poincare_distances, }; pub use crate::lorentz_model::{ + lorentz_distance, lorentz_exp, lorentz_log, lorentz_to_poincare, poincare_to_lorentz, LorentzPoint, - lorentz_distance, - lorentz_exp, - lorentz_log, - poincare_to_lorentz, - lorentz_to_poincare, }; pub use crate::hyperbolic_attention::{ - HyperbolicAttentionConfig, - HyperbolicAttention, - MultiHeadHyperbolicAttention, - HyperbolicSelfAttentionLayer, - hyperbolic_weighted_sum, - hyperbolic_scalar_mul, + hyperbolic_scalar_mul, hyperbolic_weighted_sum, HyperbolicAttention, + HyperbolicAttentionConfig, HyperbolicSelfAttentionLayer, MultiHeadHyperbolicAttention, }; pub use crate::curvature_adaptation::{ - LearnableCurvature, - MultiCurvature, - CoupledCurvatureOptimizer, - CurvatureRegularization, - AdaptiveCurvatureSelector, + AdaptiveCurvatureSelector, CoupledCurvatureOptimizer, CurvatureRegularization, + LearnableCurvature, MultiCurvature, }; } @@ -233,10 +218,7 @@ mod integration_tests { fn test_transformer_block() { let block = HyperbolicTransformerBlock::new(4, 1, 1.0); - let inputs = vec![ - vec![0.1, 0.1, 0.0, 0.0], - vec![0.2, 0.1, 0.1, 0.0], - ]; + let inputs = vec![vec![0.1, 0.1, 0.0, 0.0], vec![0.2, 0.1, 0.1, 0.0]]; let outputs = block.forward(&inputs); @@ -247,10 +229,7 @@ mod integration_tests { fn test_hyperbolic_encoder() { let encoder = HyperbolicEncoder::new(2, 4, 1, 1.0); - let inputs = vec![ - vec![0.1; 4], - vec![0.2; 4], - ]; + let inputs = vec![vec![0.1; 4], vec![0.2; 4]]; let encoded = encoder.encode(&inputs); diff --git a/examples/exo-ai-2025/research/09-hyperbolic-attention/src/lorentz_model.rs b/examples/exo-ai-2025/research/09-hyperbolic-attention/src/lorentz_model.rs index 5beb4cdf5..aa997d049 100644 --- a/examples/exo-ai-2025/research/09-hyperbolic-attention/src/lorentz_model.rs +++ b/examples/exo-ai-2025/research/09-hyperbolic-attention/src/lorentz_model.rs @@ -18,7 +18,7 @@ const EPS: f32 = 1e-10; pub struct LorentzPoint { /// Coordinates in ℝⁿ⁺¹ (x₀ is time-like, x₁..xₙ space-like) pub coords: Vec, - pub curvature: f32, // K parameter + pub curvature: f32, // K parameter } impl LorentzPoint { @@ -83,10 +83,7 @@ pub fn minkowski_inner(x: &[f32], y: &[f32]) -> f32 { debug_assert!(!x.is_empty()); let time_part = -x[0] * y[0]; - let space_part: f32 = x[1..].iter() - .zip(&y[1..]) - .map(|(xi, yi)| xi * yi) - .sum(); + let space_part: f32 = x[1..].iter().zip(&y[1..]).map(|(xi, yi)| xi * yi).sum(); time_part + space_part } @@ -192,9 +189,7 @@ pub fn parallel_transport(x: &[f32], y: &[f32], v: &[f32], curvature: f32) -> Ve v.iter() .zip(y.iter()) .zip(x.iter()) - .map(|((&vi, &yi), &xi)| { - vi + coef * (inner_xv * yi + inner_yv * xi) - }) + .map(|((&vi, &yi), &xi)| vi + coef * (inner_xv * yi + inner_yv * xi)) .collect() } @@ -213,12 +208,7 @@ pub fn lorentz_boost(x: &[f32], v: &[f32], curvature: f32) -> Vec { /// Lorentz rotation: rotation in space-like plane /// /// Rotates spatial coordinates by angle θ in plane (i, j). -pub fn lorentz_rotation( - x: &[f32], - angle: f32, - plane_i: usize, - plane_j: usize, -) -> Vec { +pub fn lorentz_rotation(x: &[f32], angle: f32, plane_i: usize, plane_j: usize) -> Vec { let mut result = x.to_vec(); if plane_i == 0 || plane_j == 0 { @@ -276,11 +266,7 @@ pub fn lorentz_to_poincare(lorentz: &[f32], curvature: f32) -> Vec { // ============================================================================= /// Compute all distances from query to database -pub fn batch_lorentz_distances( - query: &[f32], - database: &[Vec], - curvature: f32, -) -> Vec { +pub fn batch_lorentz_distances(query: &[f32], database: &[Vec], curvature: f32) -> Vec { database .iter() .map(|point| lorentz_distance(query, point, curvature)) @@ -376,7 +362,7 @@ mod tests { let k = 1.0; let x = LorentzPoint::from_spatial(vec![0.1, 0.0], k); let y = LorentzPoint::from_spatial(vec![0.2, 0.0], k); - let v = vec![0.0, 0.1, 0.2]; // Tangent vector at x + let v = vec![0.0, 0.1, 0.2]; // Tangent vector at x let v_transported = parallel_transport(&x.coords, &y.coords, &v, k); diff --git a/examples/exo-ai-2025/research/09-hyperbolic-attention/src/poincare_embedding.rs b/examples/exo-ai-2025/research/09-hyperbolic-attention/src/poincare_embedding.rs index 050736f78..777f13933 100644 --- a/examples/exo-ai-2025/research/09-hyperbolic-attention/src/poincare_embedding.rs +++ b/examples/exo-ai-2025/research/09-hyperbolic-attention/src/poincare_embedding.rs @@ -27,7 +27,7 @@ const EPS: f32 = 1e-10; #[derive(Clone, Debug)] pub struct PoincarePoint { pub coords: Vec, - pub curvature: f32, // K parameter (positive) + pub curvature: f32, // K parameter (positive) } impl PoincarePoint { @@ -201,9 +201,7 @@ pub fn mobius_add(x: &[f32], y: &[f32], curvature: f32) -> Vec { // Vectorized computation x.iter() .zip(y.iter()) - .map(|(&xi, &yi)| { - (numerator_x_coef * xi + numerator_y_coef * yi) / denominator - }) + .map(|(&xi, &yi)| (numerator_x_coef * xi + numerator_y_coef * yi) / denominator) .collect() } @@ -226,11 +224,7 @@ pub fn poincare_distance(x: &[f32], y: &[f32], curvature: f32) -> f32 { /// /// Returns all pairwise distances between query and database points. /// Uses SIMD for each distance calculation. -pub fn batch_poincare_distances( - query: &[f32], - database: &[Vec], - curvature: f32, -) -> Vec { +pub fn batch_poincare_distances(query: &[f32], database: &[Vec], curvature: f32) -> Vec { database .iter() .map(|point| poincare_distance(query, point, curvature)) @@ -413,7 +407,7 @@ mod tests { #[test] fn test_clip_to_ball() { - let v = vec![2.0, 2.0]; // Outside unit ball + let v = vec![2.0, 2.0]; // Outside unit ball let k = 1.0; let clipped = clip_to_ball(&v, k); @@ -425,11 +419,7 @@ mod tests { #[test] fn test_batch_distances() { let query = vec![0.0, 0.0]; - let database = vec![ - vec![0.1, 0.0], - vec![0.2, 0.0], - vec![0.3, 0.0], - ]; + let database = vec![vec![0.1, 0.0], vec![0.2, 0.0], vec![0.3, 0.0]]; let k = 1.0; let distances = batch_poincare_distances(&query, &database, k); diff --git a/examples/exo-ai-2025/research/09-hyperbolic-attention/tests/debug_tests.rs b/examples/exo-ai-2025/research/09-hyperbolic-attention/tests/debug_tests.rs index 0110258f2..17f36d816 100644 --- a/examples/exo-ai-2025/research/09-hyperbolic-attention/tests/debug_tests.rs +++ b/examples/exo-ai-2025/research/09-hyperbolic-attention/tests/debug_tests.rs @@ -30,6 +30,12 @@ fn debug_exp_log() { println!("Original y: {:?}", y); for (i, (orig, recon)) in y.iter().zip(&y_reconstructed).enumerate() { - println!(" y[{}]: {} vs {} (diff: {})", i, orig, recon, (orig - recon).abs()); + println!( + " y[{}]: {} vs {} (diff: {})", + i, + orig, + recon, + (orig - recon).abs() + ); } } diff --git a/examples/exo-ai-2025/research/10-thermodynamic-learning/benches/thermodynamic_bench.rs b/examples/exo-ai-2025/research/10-thermodynamic-learning/benches/thermodynamic_bench.rs index f6de4d6b2..eb635ea43 100644 --- a/examples/exo-ai-2025/research/10-thermodynamic-learning/benches/thermodynamic_bench.rs +++ b/examples/exo-ai-2025/research/10-thermodynamic-learning/benches/thermodynamic_bench.rs @@ -1,10 +1,10 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId}; -use thermodynamic_learning::*; -use thermodynamic_learning::landauer_learning::*; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use thermodynamic_learning::equilibrium_propagation::*; use thermodynamic_learning::free_energy_agent::*; -use thermodynamic_learning::reversible_neural::*; +use thermodynamic_learning::landauer_learning::*; use thermodynamic_learning::novel_algorithms::*; +use thermodynamic_learning::reversible_neural::*; +use thermodynamic_learning::*; #[cfg(feature = "simd")] use thermodynamic_learning::simd_ops::*; @@ -43,7 +43,7 @@ fn bench_equilibrium_propagation(c: &mut Criterion) { black_box(&input), black_box(&target), 0.5, - 0.01 + 0.01, ); }); }); @@ -59,7 +59,7 @@ fn bench_free_energy_perception(c: &mut Criterion) { for dim in [2, 4, 8].iter() { group.bench_with_input(BenchmarkId::from_parameter(dim), dim, |b, &dim| { let mut agent = FreeEnergyAgent::new(dim, dim + 1, 300.0); - let observation: Vec = (0..dim+1).map(|i| (i as f64 * 0.1).sin()).collect(); + let observation: Vec = (0..dim + 1).map(|i| (i as f64 * 0.1).sin()).collect(); b.iter(|| { agent.perceive(black_box(&observation)); @@ -177,46 +177,34 @@ fn bench_simd_ops(c: &mut Criterion) { for size in [100, 1000, 10000].iter() { // Dot product - group.bench_with_input( - BenchmarkId::new("dot_product", size), - size, - |b, &size| { - let a: Vec = (0..size).map(|i| i as f64 * 0.1).collect(); - let b: Vec = (0..size).map(|i| (size - i) as f64 * 0.1).collect(); + group.bench_with_input(BenchmarkId::new("dot_product", size), size, |b, &size| { + let a: Vec = (0..size).map(|i| i as f64 * 0.1).collect(); + let b: Vec = (0..size).map(|i| (size - i) as f64 * 0.1).collect(); - b.iter(|| { - simd_dot_product(black_box(&a), black_box(&b)); - }); - } - ); + b.iter(|| { + simd_dot_product(black_box(&a), black_box(&b)); + }); + }); // Norm squared - group.bench_with_input( - BenchmarkId::new("norm_squared", size), - size, - |b, &size| { - let x: Vec = (0..size).map(|i| i as f64 * 0.1).collect(); + group.bench_with_input(BenchmarkId::new("norm_squared", size), size, |b, &size| { + let x: Vec = (0..size).map(|i| i as f64 * 0.1).collect(); - b.iter(|| { - simd_norm_squared(black_box(&x)); - }); - } - ); + b.iter(|| { + simd_norm_squared(black_box(&x)); + }); + }); // Entropy calculation - group.bench_with_input( - BenchmarkId::new("entropy", size), - size, - |b, &size| { - let probs: Vec = (0..size) - .map(|i| ((i as f64 + 1.0) / (size as f64 + 1.0))) - .collect(); + group.bench_with_input(BenchmarkId::new("entropy", size), size, |b, &size| { + let probs: Vec = (0..size) + .map(|i| ((i as f64 + 1.0) / (size as f64 + 1.0))) + .collect(); - b.iter(|| { - energy::entropy(black_box(&probs)); - }); - } - ); + b.iter(|| { + energy::entropy(black_box(&probs)); + }); + }); } group.finish(); @@ -236,19 +224,20 @@ fn bench_energy_calculations(c: &mut Criterion) { b.iter(|| { black_box(state.landauer_limit()); }); - } + }, ); group.bench_with_input( BenchmarkId::new("energy_network", size), size, |b, &size| { - let network = EnergyBasedNetwork::new(vec![size / 10, size / 5, size / 10], 1.0, 300.0); + let network = + EnergyBasedNetwork::new(vec![size / 10, size / 5, size / 10], 1.0, 300.0); b.iter(|| { black_box(network.energy()); }); - } + }, ); } diff --git a/examples/exo-ai-2025/research/10-thermodynamic-learning/src/equilibrium_propagation.rs b/examples/exo-ai-2025/research/10-thermodynamic-learning/src/equilibrium_propagation.rs index 54b56f8f6..cd9d68d3e 100644 --- a/examples/exo-ai-2025/research/10-thermodynamic-learning/src/equilibrium_propagation.rs +++ b/examples/exo-ai-2025/research/10-thermodynamic-learning/src/equilibrium_propagation.rs @@ -168,7 +168,8 @@ impl EnergyBasedNetwork { let mut max_change: f64 = 0.0; // Update states: ds/dt = -∂E/∂s / τ - for layer in 1..self.n_layers { // Don't update input layer + for layer in 1..self.n_layers { + // Don't update input layer for i in 0..self.layer_sizes[layer] { let ds_dt = -gradient[layer][i] / self.tau; let old_state = self.states[layer][i]; @@ -189,7 +190,13 @@ impl EnergyBasedNetwork { } /// Nudged phase: relax with gentle push toward target - pub fn relax_nudged(&mut self, target: &[f64], beta: f64, max_iters: usize, tolerance: f64) -> usize { + pub fn relax_nudged( + &mut self, + target: &[f64], + beta: f64, + max_iters: usize, + tolerance: f64, + ) -> usize { assert_eq!(target.len(), self.layer_sizes[self.n_layers - 1]); let dt = 0.1; @@ -366,12 +373,22 @@ pub struct ContrastiveDivergence { impl ContrastiveDivergence { pub fn new(k_steps: usize, temperature: f64) -> Self { - Self { k_steps, temperature } + Self { + k_steps, + temperature, + } } /// Compute gradient: ⟨s_i s_j⟩_data - ⟨s_i s_j⟩_model - pub fn gradient(&self, network: &EnergyBasedNetwork, data_states: &[Vec]) -> Vec>> { - let mut gradient = vec![vec![vec![0.0; network.layer_sizes[0]]; network.layer_sizes[1]]; network.n_layers - 1]; + pub fn gradient( + &self, + network: &EnergyBasedNetwork, + data_states: &[Vec], + ) -> Vec>> { + let mut gradient = vec![ + vec![vec![0.0; network.layer_sizes[0]]; network.layer_sizes[1]]; + network.n_layers - 1 + ]; // Positive phase: data statistics for layer in 0..network.n_layers - 1 { @@ -387,7 +404,8 @@ impl ContrastiveDivergence { for layer in 0..network.n_layers - 1 { for i in 0..network.layer_sizes[layer + 1] { for j in 0..network.layer_sizes[layer] { - gradient[layer][i][j] -= network.states[layer + 1][i] * network.states[layer][j]; + gradient[layer][i][j] -= + network.states[layer + 1][i] * network.states[layer][j]; } } } @@ -444,7 +462,8 @@ mod tests { // Energy gradient should be small at equilibrium let grad = network.energy_gradient(); - for layer_grad in &grad[1..] { // Skip input layer + for layer_grad in &grad[1..] { + // Skip input layer for &g in layer_grad { assert!(g.abs() < 0.1); // Approximate equilibrium } @@ -459,9 +478,7 @@ mod tests { let target = vec![1.0]; // One learning step - let (e_free, e_nudged) = network.equilibrium_propagation_step( - &input, &target, 0.5, 0.01 - ); + let (e_free, e_nudged) = network.equilibrium_propagation_step(&input, &target, 0.5, 0.01); // Energies should be different assert!((e_free - e_nudged).abs() > 0.0); @@ -502,12 +519,7 @@ pub fn example_xor_learning() { vec![1.0, 0.0], vec![1.0, 1.0], ]; - let targets = vec![ - vec![0.0], - vec![1.0], - vec![1.0], - vec![0.0], - ]; + let targets = vec![vec![0.0], vec![1.0], vec![1.0], vec![0.0]]; let beta = 0.5; let learning_rate = 0.01; @@ -531,7 +543,9 @@ pub fn example_xor_learning() { println!("\nFinal predictions:"); for (input, target) in inputs.iter().zip(targets.iter()) { let pred = network.predict(input); - println!("Input: {:?} -> Prediction: {:.4}, Target: {:.4}", - input, pred[0], target[0]); + println!( + "Input: {:?} -> Prediction: {:.4}, Target: {:.4}", + input, pred[0], target[0] + ); } } diff --git a/examples/exo-ai-2025/research/10-thermodynamic-learning/src/free_energy_agent.rs b/examples/exo-ai-2025/research/10-thermodynamic-learning/src/free_energy_agent.rs index bcfba3cbe..bb9fa7abe 100644 --- a/examples/exo-ai-2025/research/10-thermodynamic-learning/src/free_energy_agent.rs +++ b/examples/exo-ai-2025/research/10-thermodynamic-learning/src/free_energy_agent.rs @@ -171,7 +171,7 @@ impl GenerativeModel { pub struct RecognitionModel { /// Parameters of q(x|s) pub mean_params: Vec>, // s -> mean(x) - pub var_params: Vec, // variance(x) + pub var_params: Vec, // variance(x) } impl RecognitionModel { @@ -350,7 +350,8 @@ impl FreeEnergyAgent { let ll_minus = self.generative.likelihood.log_likelihood(s, &x); let gradient = (ll_plus - ll_minus) / (2.0 * eps); - self.generative.likelihood.weight_matrix[i][j] = original + self.learning_rate * gradient; + self.generative.likelihood.weight_matrix[i][j] = + original + self.learning_rate * gradient; } } } @@ -440,10 +441,7 @@ mod tests { #[test] fn test_likelihood() { - let likelihood = Likelihood::new( - vec![vec![1.0, 0.5], vec![0.5, 1.0]], - vec![0.1, 0.1], - ); + let likelihood = Likelihood::new(vec![vec![1.0, 0.5], vec![0.5, 1.0]], vec![0.1, 0.1]); let x = vec![1.0, -1.0]; let predicted = likelihood.predict(&x); @@ -543,6 +541,10 @@ pub fn example_free_energy_tracking() { println!("Action: {:?}\n", action); } - println!("Final free energy: {:.6}", - loop_executor.agent.free_energy_kl(&observations.last().unwrap())); + println!( + "Final free energy: {:.6}", + loop_executor + .agent + .free_energy_kl(&observations.last().unwrap()) + ); } diff --git a/examples/exo-ai-2025/research/10-thermodynamic-learning/src/landauer_learning.rs b/examples/exo-ai-2025/research/10-thermodynamic-learning/src/landauer_learning.rs index 066f9aaae..062accbfc 100644 --- a/examples/exo-ai-2025/research/10-thermodynamic-learning/src/landauer_learning.rs +++ b/examples/exo-ai-2025/research/10-thermodynamic-learning/src/landauer_learning.rs @@ -170,14 +170,20 @@ impl LandauerOptimizer { /// Information-theoretic gradient: weight by information content pub fn information_weighted_gradient(&self, gradient: &[f64], information: &[f64]) -> Vec { - gradient.iter() + gradient + .iter() .zip(information.iter()) .map(|(g, i)| g * i) .collect() } /// Estimate mutual information between data and parameters - pub fn estimate_mutual_information(&self, data_entropy: f64, param_entropy: f64, joint_entropy: f64) -> f64 { + pub fn estimate_mutual_information( + &self, + data_entropy: f64, + param_entropy: f64, + joint_entropy: f64, + ) -> f64 { // I(D; θ) = H(D) + H(θ) - H(D, θ) data_entropy + param_entropy - joint_entropy } @@ -354,7 +360,10 @@ impl SpeedEnergyTradeoff { pub fn new(temperature: f64) -> Self { // Minimum from uncertainty principle-like bound let min_product = constants::BOLTZMANN * temperature; - Self { min_product, temperature } + Self { + min_product, + temperature, + } } /// Minimum energy for given time constraint @@ -487,8 +496,10 @@ pub fn example_thermodynamic_training() { optimizer.step(&gradient, &mut params); if epoch % 3 == 0 { - println!("Epoch {}: Energy dissipated = {:.3e} J", - epoch, optimizer.state.energy_dissipated); + println!( + "Epoch {}: Energy dissipated = {:.3e} J", + epoch, optimizer.state.energy_dissipated + ); } } @@ -499,5 +510,8 @@ pub fn example_thermodynamic_training() { let theoretical_min = constants::LANDAUER_LIMIT * bits_learned; println!("\nTheoretical minimum: {:.3e} J", theoretical_min); println!("Actual energy: {:.3e} J", optimizer.state.energy_dissipated); - println!("Efficiency: {:.2}x above Landauer limit", optimizer.state.landauer_multiple()); + println!( + "Efficiency: {:.2}x above Landauer limit", + optimizer.state.landauer_multiple() + ); } diff --git a/examples/exo-ai-2025/research/10-thermodynamic-learning/src/lib.rs b/examples/exo-ai-2025/research/10-thermodynamic-learning/src/lib.rs index ce674278e..6df624ffd 100644 --- a/examples/exo-ai-2025/research/10-thermodynamic-learning/src/lib.rs +++ b/examples/exo-ai-2025/research/10-thermodynamic-learning/src/lib.rs @@ -59,7 +59,7 @@ pub mod simd_ops; pub mod novel_algorithms; // Re-export commonly used items -pub use landauer_learning::{LandauerOptimizer, ThermodynamicState, constants}; pub use equilibrium_propagation::EnergyBasedNetwork; pub use free_energy_agent::FreeEnergyAgent; +pub use landauer_learning::{constants, LandauerOptimizer, ThermodynamicState}; pub use reversible_neural::ReversibleNetwork; diff --git a/examples/exo-ai-2025/research/10-thermodynamic-learning/src/novel_algorithms.rs b/examples/exo-ai-2025/research/10-thermodynamic-learning/src/novel_algorithms.rs index bb39f3b47..a250786cf 100644 --- a/examples/exo-ai-2025/research/10-thermodynamic-learning/src/novel_algorithms.rs +++ b/examples/exo-ai-2025/research/10-thermodynamic-learning/src/novel_algorithms.rs @@ -8,8 +8,8 @@ //! 4. **Quantum-Inspired Landauer Learning**: Coherence-based optimization //! 5. **Heat Engine Neural Networks**: Extract work from temperature gradients -use std::f64::consts::LN_2; use crate::landauer_learning::constants; +use std::f64::consts::LN_2; /// Novel Discovery 1: Entropy-Regularized Learning /// @@ -136,7 +136,8 @@ impl FluctuationTheoremOptimizer { return 1.0; } - let window = &self.energy_history[self.energy_history.len().saturating_sub(self.window_size)..]; + let window = + &self.energy_history[self.energy_history.len().saturating_sub(self.window_size)..]; let positive = window.iter().filter(|&&e| e > 0.0).count() as f64; let negative = window.iter().filter(|&&e| e < 0.0).count() as f64; @@ -161,9 +162,8 @@ impl FluctuationTheoremOptimizer { // Compute energy fluctuation variance let mean: f64 = window.iter().sum::() / window.len() as f64; - let variance: f64 = window.iter() - .map(|e| (e - mean).powi(2)) - .sum::() / window.len() as f64; + let variance: f64 = + window.iter().map(|e| (e - mean).powi(2)).sum::() / window.len() as f64; // Ideal variance ∝ kT (equipartition theorem) let ideal_variance = constants::BOLTZMANN * self.temperature; @@ -182,11 +182,7 @@ impl FluctuationTheoremOptimizer { } /// Perform optimization step - pub fn step( - &mut self, - params: &mut [f64], - gradient: &[f64], - ) -> f64 { + pub fn step(&mut self, params: &mut [f64], gradient: &[f64]) -> f64 { assert_eq!(params.len(), gradient.len()); // Compute energy before step @@ -236,7 +232,7 @@ impl ThermodynamicMetaLearner { pub fn new(temperature: f64, meta_dim: usize) -> Self { Self { temperature, - meta_params: vec![0.1; meta_dim], // Initialize meta-parameters + meta_params: vec![0.1; meta_dim], // Initialize meta-parameters meta_lr: 0.001, total_cost: 0.0, } @@ -250,18 +246,11 @@ impl ThermodynamicMetaLearner { } /// Learn on a task and return thermodynamic cost - pub fn task_step( - &mut self, - task_id: usize, - params: &mut [f64], - gradient: &[f64], - ) -> f64 { + pub fn task_step(&mut self, task_id: usize, params: &mut [f64], gradient: &[f64]) -> f64 { let lr = self.generate_learning_rate(task_id); // Compute energy dissipated (proportional to ||update||^2) - let update_norm_sq: f64 = gradient.iter() - .map(|g| (lr * g).powi(2)) - .sum(); + let update_norm_sq: f64 = gradient.iter().map(|g| (lr * g).powi(2)).sum(); let energy_dissipated = constants::BOLTZMANN * self.temperature * update_norm_sq; let entropy_produced = energy_dissipated / self.temperature; @@ -366,7 +355,8 @@ impl QuantumInspiredOptimizer { } // Apply update - let update_norm_sq: f64 = collapsed_gradient.iter() + let update_norm_sq: f64 = collapsed_gradient + .iter() .map(|g| (self.learning_rate * g).powi(2)) .sum(); @@ -521,7 +511,7 @@ mod tests { let carnot = engine.carnot_efficiency(); assert!(carnot > 0.0); assert!(carnot < 1.0); - assert!((carnot - 0.25).abs() < 0.01); // 1 - 300/400 = 0.25 + assert!((carnot - 0.25).abs() < 0.01); // 1 - 300/400 = 0.25 } #[test] diff --git a/examples/exo-ai-2025/research/10-thermodynamic-learning/src/reversible_neural.rs b/examples/exo-ai-2025/research/10-thermodynamic-learning/src/reversible_neural.rs index bf36ed2d2..e1f6bcac3 100644 --- a/examples/exo-ai-2025/research/10-thermodynamic-learning/src/reversible_neural.rs +++ b/examples/exo-ai-2025/research/10-thermodynamic-learning/src/reversible_neural.rs @@ -9,7 +9,6 @@ /// - Invertible activation functions /// - Orthogonal weight constraints /// - Energy tracking for reversible operations - use std::f64::consts::{LN_2, PI}; /// Reversible layer trait - must be bijective @@ -373,7 +372,8 @@ impl ReversibleNetwork { } pub fn add_coupling_layer(&mut self, hidden_dim: usize, split: usize) { - self.layers.push(Box::new(CouplingLayer::new(self.dim, hidden_dim, split))); + self.layers + .push(Box::new(CouplingLayer::new(self.dim, hidden_dim, split))); } pub fn add_orthogonal_layer(&mut self) { @@ -637,6 +637,9 @@ pub fn example_reversible_autoencoder() { // Compare to fully irreversible computation let total_bits = 8.0 * 32.0 * network.layers.len() as f64; let savings = tracker.energy_savings(total_bits); - println!("Energy savings vs irreversible: {:.3e} J ({:.1}%)", - savings, 100.0 * savings / (tracker.energy_dissipated + savings)); + println!( + "Energy savings vs irreversible: {:.3e} J ({:.1}%)", + savings, + 100.0 * savings / (tracker.energy_dissipated + savings) + ); } diff --git a/examples/exo-ai-2025/research/10-thermodynamic-learning/src/simd_ops.rs b/examples/exo-ai-2025/research/10-thermodynamic-learning/src/simd_ops.rs index 4c0a832ba..39cda0ea0 100644 --- a/examples/exo-ai-2025/research/10-thermodynamic-learning/src/simd_ops.rs +++ b/examples/exo-ai-2025/research/10-thermodynamic-learning/src/simd_ops.rs @@ -18,10 +18,7 @@ pub fn simd_dot_product(a: &[f64], b: &[f64]) -> f64 { assert_eq!(a.len(), b.len()); // Rust compiler auto-vectorizes this pattern with -O3 - a.iter() - .zip(b.iter()) - .map(|(x, y)| x * y) - .sum() + a.iter().zip(b.iter()).map(|(x, y)| x * y).sum() } /// SIMD-accelerated L2 norm squared @@ -29,9 +26,7 @@ pub fn simd_dot_product(a: &[f64], b: &[f64]) -> f64 { /// Computes sum(x[i]^2) for energy calculations #[inline] pub fn simd_norm_squared(x: &[f64]) -> f64 { - x.iter() - .map(|v| v * v) - .sum() + x.iter().map(|v| v * v).sum() } /// SIMD-accelerated weighted sum @@ -41,10 +36,7 @@ pub fn simd_norm_squared(x: &[f64]) -> f64 { pub fn simd_weighted_sum(weights: &[f64], values: &[f64]) -> f64 { assert_eq!(weights.len(), values.len()); - weights.iter() - .zip(values.iter()) - .map(|(w, v)| w * v) - .sum() + weights.iter().zip(values.iter()).map(|(w, v)| w * v).sum() } /// SIMD-accelerated element-wise operations @@ -111,9 +103,7 @@ pub mod energy { /// Computes E = 0.5 * ||x||^2 for multiple vectors #[inline] pub fn batch_quadratic_energy(states: &[Vec]) -> Vec { - states.iter() - .map(|s| 0.5 * simd_norm_squared(s)) - .collect() + states.iter().map(|s| 0.5 * simd_norm_squared(s)).collect() } /// Fast entropy calculation: H = -sum(p * log(p)) @@ -121,8 +111,9 @@ pub mod energy { /// Uses SIMD-friendly pattern for probability distributions #[inline] pub fn entropy(probabilities: &[f64]) -> f64 { - probabilities.iter() - .filter(|&&p| p > 1e-10) // Avoid log(0) + probabilities + .iter() + .filter(|&&p| p > 1e-10) // Avoid log(0) .map(|&p| -p * p.ln()) .sum() } @@ -146,11 +137,7 @@ pub mod gradient { /// Fast gradient step: params[i] -= learning_rate * gradient[i] #[inline] - pub fn gradient_descent_step( - params: &mut [f64], - gradient: &[f64], - learning_rate: f64 - ) { + pub fn gradient_descent_step(params: &mut [f64], gradient: &[f64], learning_rate: f64) { assert_eq!(params.len(), gradient.len()); for i in 0..params.len() { @@ -233,9 +220,11 @@ pub mod bench_utils { /// Generate random matrix for benchmarking pub fn random_matrix(rows: usize, cols: usize) -> Vec> { (0..rows) - .map(|i| (0..cols) - .map(|j| ((i * cols + j) as f64 * 0.1).sin()) - .collect()) + .map(|i| { + (0..cols) + .map(|j| ((i * cols + j) as f64 * 0.1).sin()) + .collect() + }) .collect() } } diff --git a/examples/exo-ai-2025/tests/common/assertions.rs b/examples/exo-ai-2025/tests/common/assertions.rs index f9d015679..6796ec3b3 100644 --- a/examples/exo-ai-2025/tests/common/assertions.rs +++ b/examples/exo-ai-2025/tests/common/assertions.rs @@ -44,11 +44,7 @@ pub fn assert_scores_descending(scores: &[f32]) { pub fn assert_causal_order(results: &[String], expected_order: &[String]) { // TODO: Implement once CausalResult type exists // Verify results respect causal dependencies - assert_eq!( - results.len(), - expected_order.len(), - "Result count mismatch" - ); + assert_eq!(results.len(), expected_order.len(), "Result count mismatch"); } /// Assert CRDT states are convergent diff --git a/scripts/sync-lockfile.sh b/scripts/sync-lockfile.sh new file mode 120000 index 000000000..ea236c7e3 --- /dev/null +++ b/scripts/sync-lockfile.sh @@ -0,0 +1 @@ +ci/sync-lockfile.sh \ No newline at end of file