mirror of
https://github.com/ruvnet/RuVector.git
synced 2026-08-13 02:33:59 +00:00
style: apply rustfmt across entire codebase
Run rustfmt on all Rust files to fix CI formatting checks. This addresses pre-existing formatting inconsistencies across: - cognitum-gate-kernel - cognitum-gate-tilezero - prime-radiant - ruvector-* crates - examples/benchmarks - and other crates Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
859f93e916
commit
42d869a196
645 changed files with 17864 additions and 11023 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -404,7 +404,10 @@ impl Delta {
|
|||
// Compile-time size assertions
|
||||
const _: () = assert!(size_of::<EdgeAdd>() == 8, "EdgeAdd must be 8 bytes");
|
||||
const _: () = assert!(size_of::<EdgeRemove>() == 8, "EdgeRemove must be 8 bytes");
|
||||
const _: () = assert!(size_of::<WeightUpdate>() == 8, "WeightUpdate must be 8 bytes");
|
||||
const _: () = assert!(
|
||||
size_of::<WeightUpdate>() == 8,
|
||||
"WeightUpdate must be 8 bytes"
|
||||
);
|
||||
const _: () = assert!(size_of::<Observation>() == 8, "Observation must be 8 bytes");
|
||||
const _: () = assert!(size_of::<Delta>() == 16, "Delta must be 16 bytes");
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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<TileEdgeId> {
|
||||
pub unsafe fn find_edge_unchecked(
|
||||
&self,
|
||||
source: TileVertexId,
|
||||
target: TileVertexId,
|
||||
) -> Option<TileEdgeId> {
|
||||
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<Item = FixedWeight> + '_ {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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<WorkerReport> {
|
||||
fn create_all_tile_reports(
|
||||
epoch: u64,
|
||||
nodes_per_tile: usize,
|
||||
edges_per_tile: usize,
|
||||
) -> Vec<WorkerReport> {
|
||||
(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<f64> = (0..tile_count)
|
||||
.map(|i| 1.0 + (i as f64 * 0.01))
|
||||
.collect();
|
||||
let e_values: Vec<f64> = (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
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<f64> = (0..tile_count)
|
||||
.map(|i| 1.0 + (i as f64 * 0.01))
|
||||
.collect();
|
||||
let e_values: Vec<f64> = (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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -23,8 +23,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
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
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
|
||||
// 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<dyn std::error::Error>> {
|
|||
println!(" - Provide additional context");
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
println!("Decision: {:?}", token.decision);
|
||||
println!("(Automatic - no human review needed)");
|
||||
|
|
|
|||
|
|
@ -61,7 +61,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
// 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<dyn std::error::Error>> {
|
|||
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()
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@ use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
|||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// 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<dyn std::error::Error>> {
|
|||
// 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?;
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Vec<f32>> = (0..batch_size + 1).map(|i| generate_state(dim, i as u64)).collect();
|
||||
let states: Vec<Vec<f32>> = (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<u64, SheafNode> = (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<SheafEdge> = (0..num_nodes - 1)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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<usize> = expert_scores.iter().take(top_k).map(|(idx, _)| *idx).collect();
|
||||
let top_experts: Vec<usize> = 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,
|
||||
|
|
|
|||
|
|
@ -374,7 +374,9 @@ fn bench_knn_hyperbolic(c: &mut Criterion) {
|
|||
let dim = 64;
|
||||
let curvature = -1.0;
|
||||
|
||||
let points: Vec<Vec<f32>> = (0..1000).map(|i| generate_point(dim, i as u64, 0.9)).collect();
|
||||
let points: Vec<Vec<f32>> = (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::<Vec<_>>();
|
||||
let result = distances[..k]
|
||||
.iter()
|
||||
.map(|(i, d)| (*i, *d))
|
||||
.collect::<Vec<_>>();
|
||||
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))),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -266,7 +266,11 @@ fn generate_state(dim: usize, seed: u64) -> Vec<f32> {
|
|||
.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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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::<f32>())
|
||||
})
|
||||
});
|
||||
black_box(accum.iter().sum::<f32>())
|
||||
})
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
group.finish();
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<f32> = (0..param_count)
|
||||
.map(|i| (i as f32 * 0.001).sin())
|
||||
.collect();
|
||||
let weights: Vec<f32> = (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<f32> = (0..param_count)
|
||||
.map(|i| (i as f32 * 0.001).sin())
|
||||
.collect();
|
||||
let weights: Vec<f32> = (0..param_count).map(|i| (i as f32 * 0.001).sin()).collect();
|
||||
let new_fisher: Vec<f32> = (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))),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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::<f32>()
|
||||
/ weighted.len().max(1) as f32;
|
||||
let avg_attention: f32 =
|
||||
weighted.iter().map(|w| w.attention_weight).sum::<f32>() / 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<Vec<f32>> {
|
||||
(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 {
|
||||
|
|
|
|||
|
|
@ -347,7 +347,8 @@ mod tests {
|
|||
let inputs: Vec<Vec<f32>> = (0..10).map(|i| vec![0.1 * (i + 1) as f32; 8]).collect();
|
||||
let context = vec![0.1f32; 8];
|
||||
|
||||
let routings: Vec<ExpertRouting> = inputs.iter().map(|inp| moe.route(inp, &context)).collect();
|
||||
let routings: Vec<ExpertRouting> =
|
||||
inputs.iter().map(|inp| moe.route(inp, &context)).collect();
|
||||
|
||||
let usage = moe.expert_usage(&routings);
|
||||
|
||||
|
|
|
|||
|
|
@ -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<usize> = 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<f32> = 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::<f32>() / 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,
|
||||
|
|
|
|||
|
|
@ -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<HotspotInfo> {
|
||||
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::<f32>()
|
||||
/ energies.len() as f32;
|
||||
let variance: f32 =
|
||||
energies.iter().map(|e| (e - mean).powi(2)).sum::<f32>() / 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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<f32>], k: usize) -> Vec<f32> {
|
|||
|
||||
// Compute eigenvalues
|
||||
let eigen = SymmetricEigen::new(matrix);
|
||||
let mut eigenvalues: Vec<f32> = eigen
|
||||
.eigenvalues
|
||||
.iter()
|
||||
.map(|&x| x as f32)
|
||||
.collect();
|
||||
let mut eigenvalues: Vec<f32> = 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));
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -266,9 +266,7 @@ impl CohomologyComputer {
|
|||
let (rref, pivot_cols) = self.row_reduce(matrix);
|
||||
|
||||
let n_cols = matrix.ncols();
|
||||
let free_vars: Vec<usize> = (0..n_cols)
|
||||
.filter(|c| !pivot_cols.contains(c))
|
||||
.collect();
|
||||
let free_vars: Vec<usize> = (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<usize, SimplexId> = simplex_to_idx
|
||||
.iter()
|
||||
.map(|(&id, &idx)| (idx, id))
|
||||
.collect();
|
||||
let idx_to_simplex: HashMap<usize, SimplexId> =
|
||||
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<CohomologyGroup> {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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::<f64>() / recent_energies.len() as f64;
|
||||
let recent_energies =
|
||||
&energy_history[energy_history.len().saturating_sub(10)..];
|
||||
let avg_recent: f64 =
|
||||
recent_energies.iter().sum::<f64>() / 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<f64> = node.state.as_slice().iter()
|
||||
.map(|&x| x as f64)
|
||||
.collect();
|
||||
let values: Vec<f64> = 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<NodeId, f64> {
|
||||
fn compute_node_energies(
|
||||
&self,
|
||||
graph: &SheafGraph,
|
||||
section: &SheafSection,
|
||||
) -> HashMap<NodeId, f64> {
|
||||
let mut node_energies: HashMap<NodeId, f64> = 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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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::<f64>() * scale - scale / 2.0,
|
||||
);
|
||||
let weights = Array2::from_shape_fn((config.output_dim, config.input_dim), |_| {
|
||||
rand::random::<f64>() * 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<f64>, bias: Array1<f64>) -> Self {
|
||||
pub fn with_weights(
|
||||
config: SheafNeuralConfig,
|
||||
weights: Array2<f64>,
|
||||
bias: Array1<f64>,
|
||||
) -> 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::<f64>() * scale - scale / 2.0,
|
||||
);
|
||||
let neighbor_weight = Array2::from_shape_fn(
|
||||
(output_dim, input_dim),
|
||||
|_| rand::random::<f64>() * scale - scale / 2.0,
|
||||
);
|
||||
let self_weight = Array2::from_shape_fn((output_dim, input_dim), |_| {
|
||||
rand::random::<f64>() * scale - scale / 2.0
|
||||
});
|
||||
let neighbor_weight = Array2::from_shape_fn((output_dim, input_dim), |_| {
|
||||
rand::random::<f64>() * 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::<f64>()))
|
||||
.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);
|
||||
|
|
|
|||
|
|
@ -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<f64> = node.state.as_slice().iter()
|
||||
.map(|&x| x as f64)
|
||||
.collect();
|
||||
let values: Vec<f64> = 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::<Vec<_>>()
|
||||
obs.hotspots
|
||||
.iter()
|
||||
.take(3)
|
||||
.map(|(n, _)| n)
|
||||
.collect::<Vec<_>>()
|
||||
));
|
||||
}
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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<f64>) -> Option<Array1<f64>> {
|
||||
pub fn restrict(
|
||||
&self,
|
||||
source: NodeId,
|
||||
target: NodeId,
|
||||
value: &Array1<f64>,
|
||||
) -> Option<Array1<f64>> {
|
||||
self.restriction_maps
|
||||
.get(&(source, target))
|
||||
.map(|rho| rho(value))
|
||||
|
|
@ -338,9 +343,8 @@ impl SheafBuilder {
|
|||
target: NodeId,
|
||||
indices: Vec<usize>,
|
||||
) -> Self {
|
||||
let proj_fn: RestrictionFn = Arc::new(move |v: &Array1<f64>| {
|
||||
Array1::from_iter(indices.iter().map(|&i| v[i]))
|
||||
});
|
||||
let proj_fn: RestrictionFn =
|
||||
Arc::new(move |v: &Array1<f64>| Array1::from_iter(indices.iter().map(|&i| v[i])));
|
||||
self.sheaf.add_restriction(source, target, proj_fn);
|
||||
self
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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::<f64>().sqrt()
|
||||
self.coefficients
|
||||
.values()
|
||||
.map(|c| c * c)
|
||||
.sum::<f64>()
|
||||
.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);
|
||||
|
|
|
|||
|
|
@ -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<f32>,
|
||||
},
|
||||
SetNodeState { node_id: u64, state: Vec<f32> },
|
||||
/// 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<u64>,
|
||||
},
|
||||
MarkIncoherent { region_id: u64, nodes: Vec<u64> },
|
||||
/// 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<CommandResult> {
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -80,7 +80,10 @@ impl DistributedCoherenceConfig {
|
|||
|
||||
/// Create configuration for a 3-node cluster
|
||||
pub fn three_node_cluster(node_id: &str, members: Vec<String>) -> 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<String>) -> 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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<CommandResult> {
|
||||
pub fn update_energy(
|
||||
&mut self,
|
||||
source: u64,
|
||||
target: u64,
|
||||
energy: f32,
|
||||
) -> Result<CommandResult> {
|
||||
let result = self.raft.update_energy((source, target), energy)?;
|
||||
|
||||
// Apply to local state machine
|
||||
|
|
|
|||
|
|
@ -174,11 +174,14 @@ impl CoherenceStateMachine {
|
|||
fn apply_set_node_state(&mut self, node_id: u64, state: Vec<f32>) -> ApplyResult {
|
||||
let truncated_state: Vec<f32> = 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]
|
||||
|
|
|
|||
|
|
@ -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<String>, description: impl Into<String>, actor_id: impl Into<String>) -> Self {
|
||||
pub fn new(
|
||||
action_type: impl Into<String>,
|
||||
description: impl Into<String>,
|
||||
actor_id: impl Into<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
id: ActionId::new(),
|
||||
action_type: action_type.into(),
|
||||
|
|
|
|||
|
|
@ -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<WitnessRecord> {
|
||||
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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -610,7 +610,12 @@ impl GpuBuffer {
|
|||
}
|
||||
|
||||
/// Create a new storage buffer with initial data (for dispatch compatibility)
|
||||
pub fn new_storage<T: Pod>(device: &Device, queue: &Queue, data: &[T], read_write: bool) -> GpuResult<Self> {
|
||||
pub fn new_storage<T: Pod>(
|
||||
device: &Device,
|
||||
queue: &Queue,
|
||||
data: &[T],
|
||||
read_write: bool,
|
||||
) -> GpuResult<Self> {
|
||||
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<T: Pod>(device: &Device, count: usize, read_write: bool) -> GpuResult<Self> {
|
||||
pub fn new_storage_uninit<T: Pod>(
|
||||
device: &Device,
|
||||
count: usize,
|
||||
read_write: bool,
|
||||
) -> GpuResult<Self> {
|
||||
let size = count * std::mem::size_of::<T>();
|
||||
let usage = if read_write {
|
||||
BufferUsage::Residuals
|
||||
|
|
@ -632,7 +641,13 @@ impl GpuBuffer {
|
|||
|
||||
/// Create a new uniform buffer with data
|
||||
pub fn new_uniform<T: Pod>(device: &Device, queue: &Queue, data: &T) -> GpuResult<Self> {
|
||||
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",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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!(
|
||||
|
|
|
|||
|
|
@ -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::<f32>();
|
||||
let energies_size = num_edges as usize * std::mem::size_of::<f32>();
|
||||
|
||||
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::<f32>()) 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::<f32>() 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<GpuCoherenceEnergy> {
|
||||
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<f32> = bytemuck::cast_slice(&data[..count * std::mem::size_of::<f32>()])
|
||||
.to_vec();
|
||||
let result: Vec<f32> =
|
||||
bytemuck::cast_slice(&data[..count * std::mem::size_of::<f32>()]).to_vec();
|
||||
|
||||
drop(data);
|
||||
buffer.unmap();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,11 +21,11 @@ pub enum MapState {
|
|||
/// A simple dense layer.
|
||||
#[derive(Debug, Clone)]
|
||||
struct DenseLayer {
|
||||
weights: Vec<Vec<f32>>, // [output_dim][input_dim]
|
||||
biases: Vec<f32>, // [output_dim]
|
||||
weights: Vec<Vec<f32>>, // [output_dim][input_dim]
|
||||
biases: Vec<f32>, // [output_dim]
|
||||
weight_gradients: Vec<Vec<f32>>,
|
||||
bias_gradients: Vec<f32>,
|
||||
input_cache: Vec<f32>, // For backprop
|
||||
input_cache: Vec<f32>, // For backprop
|
||||
pre_activation_cache: Vec<f32>,
|
||||
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<Self> {
|
||||
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::<usize>() + l.biases.len()
|
||||
}).sum();
|
||||
let num_params: usize = layers
|
||||
.iter()
|
||||
.map(|l| l.weights.iter().map(|r| r.len()).sum::<usize>() + 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<Vec<f32>> {
|
||||
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<TrainingMetrics> {
|
||||
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::<f32>().sqrt();
|
||||
let gradient_norm: f32 = self
|
||||
.layers
|
||||
.iter()
|
||||
.map(|l| l.gradient_norm())
|
||||
.sum::<f32>()
|
||||
.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();
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -154,10 +154,7 @@ impl MinCutAdapter {
|
|||
}
|
||||
|
||||
/// Compute isolation for high-energy vertices
|
||||
pub fn compute_isolation(
|
||||
&self,
|
||||
high_energy_vertices: &HashSet<VertexId>,
|
||||
) -> Result<CutResult> {
|
||||
pub fn compute_isolation(&self, high_energy_vertices: &HashSet<VertexId>) -> Result<CutResult> {
|
||||
if high_energy_vertices.is_empty() {
|
||||
return Ok(CutResult {
|
||||
isolated_set: HashSet::new(),
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -52,7 +52,8 @@ impl HysteresisTracker {
|
|||
|
||||
fn update(&mut self, energy: f32) -> Option<HysteresisState> {
|
||||
// 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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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<ValidationResult, ValidationError> {
|
||||
pub fn validate(
|
||||
&mut self,
|
||||
context: &ValidationContext,
|
||||
) -> Result<ValidationResult, ValidationError> {
|
||||
// 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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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<f32> = energies.iter().map(|&e| mapper.confidence_from_energy(e)).collect();
|
||||
let confidences: Vec<f32> = 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]
|
||||
|
|
|
|||
|
|
@ -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::<f32>().sqrt();
|
||||
let mag_b: f32 = response.response_embedding.iter().map(|x| x * x).sum::<f32>().sqrt();
|
||||
let mag_a: f32 = response
|
||||
.context_embedding
|
||||
.iter()
|
||||
.map(|x| x * x)
|
||||
.sum::<f32>()
|
||||
.sqrt();
|
||||
let mag_b: f32 = response
|
||||
.response_embedding
|
||||
.iter()
|
||||
.map(|x| x * x)
|
||||
.sum::<f32>()
|
||||
.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 (
|
||||
|
|
|
|||
|
|
@ -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<String>, value: impl Into<serde_json::Value>) -> Self {
|
||||
pub fn with_metadata(
|
||||
mut self,
|
||||
key: impl Into<String>,
|
||||
value: impl Into<serde_json::Value>,
|
||||
) -> 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<String> {
|
||||
|
|
@ -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 { .. })
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
|||
|
|
@ -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<ExportResult> {
|
||||
pub fn export_to_prime_radiant(
|
||||
&mut self,
|
||||
graph: &mut SheafGraph,
|
||||
) -> BridgeResult<ExportResult> {
|
||||
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<ExportResult> {
|
||||
pub fn export_to_prime_radiant(
|
||||
&mut self,
|
||||
graph: &mut SheafGraph,
|
||||
) -> BridgeResult<ExportResult> {
|
||||
let exported_categories: Vec<String> = 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<PatternData> {
|
||||
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<PatternData> {
|
||||
|
|
|
|||
|
|
@ -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<MemoryAddResult>;
|
||||
fn add_with_coherence(
|
||||
&mut self,
|
||||
entry: MemoryEntry,
|
||||
) -> RuvllmIntegrationResult<MemoryAddResult>;
|
||||
|
||||
/// Check if adding an entry would cause incoherence.
|
||||
fn check_coherence(&self, entry: &MemoryEntry) -> RuvllmIntegrationResult<f32>;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<Vec<f32>> {
|
||||
pub fn validate_and_clamp(
|
||||
&self,
|
||||
state: &[f32],
|
||||
min: f32,
|
||||
max: f32,
|
||||
) -> ValidationResult<Vec<f32>> {
|
||||
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<String> {
|
|||
/// 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);
|
||||
|
|
|
|||
|
|
@ -67,10 +67,7 @@ pub enum SignalType {
|
|||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum NormalizedPayload {
|
||||
/// State update payload.
|
||||
StateUpdate {
|
||||
node_id: String,
|
||||
state: Vec<f32>,
|
||||
},
|
||||
StateUpdate { node_id: String, state: Vec<f32> },
|
||||
/// Edge modification payload.
|
||||
EdgeMod {
|
||||
source: String,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue