Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 43 additions & 18 deletions rust/examples/bench_handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ fn fmt_duration(d: std::time::Duration) -> String {

#[tokio::main]
async fn main() {
let url = std::env::var("DATABASE_URL")
.expect("Set DATABASE_URL to run benchmarks");
let url = std::env::var("DATABASE_URL").expect("Set DATABASE_URL to run benchmarks");

let pool = cuba_memorys::db::create_pool(&url).await
let pool = cuba_memorys::db::create_pool(&url)
.await
.expect("Failed to connect");

println!("\n ═══ cuba-memorys Rust Benchmark ═══\n");
Expand Down Expand Up @@ -57,32 +57,44 @@ async fn main() {
for i in 0..iterations {
let name = format!("bench_alma_{i}_{}", uuid::Uuid::new_v4());
let _ = cuba_memorys::handlers::dispatch(
&pool, "cuba_alma",
&pool,
"cuba_alma",
serde_json::json!({"action": "create", "name": &name, "entity_type": "concept"}),
).await;
)
.await;
}
let elapsed = start.elapsed();
println!(" alma::create {:>8} /call ({iterations} calls)", fmt_duration(elapsed / iterations));
println!(
" alma::create {:>8} /call ({iterations} calls)",
fmt_duration(elapsed / iterations)
);
}

// ── 3. alma::get ──────────────────────────────────────────────
{
let name = format!("bench_get_{}", uuid::Uuid::new_v4());
let _ = cuba_memorys::handlers::dispatch(
&pool, "cuba_alma",
&pool,
"cuba_alma",
serde_json::json!({"action": "create", "name": &name, "entity_type": "concept"}),
).await;
)
.await;

let iterations = 100;
let start = Instant::now();
for _ in 0..iterations {
let _ = cuba_memorys::handlers::dispatch(
&pool, "cuba_alma",
&pool,
"cuba_alma",
serde_json::json!({"action": "get", "name": &name}),
).await;
)
.await;
}
let elapsed = start.elapsed();
println!(" alma::get {:>8} /call ({iterations} calls)", fmt_duration(elapsed / iterations));
println!(
" alma::get {:>8} /call ({iterations} calls)",
fmt_duration(elapsed / iterations)
);
}

// ── 4. cronica::add ───────────────────────────────────────────
Expand All @@ -92,18 +104,23 @@ async fn main() {
let start = Instant::now();
for i in 0..iterations {
let _ = cuba_memorys::handlers::dispatch(
&pool, "cuba_cronica",
&pool,
"cuba_cronica",
serde_json::json!({
"action": "add",
"entity_name": &name,
"content": format!("Bench observation number {i} with unique content"),
"observation_type": "fact",
"source": "agent"
}),
).await;
)
.await;
}
let elapsed = start.elapsed();
println!(" cronica::add {:>8} /call ({iterations} calls)", fmt_duration(elapsed / iterations));
println!(
" cronica::add {:>8} /call ({iterations} calls)",
fmt_duration(elapsed / iterations)
);
}

// ── 5. faro::search (hybrid) ──────────────────────────────────
Expand All @@ -117,7 +134,10 @@ async fn main() {
).await;
}
let elapsed = start.elapsed();
println!(" faro::search {:>8} /call ({iterations} calls)", fmt_duration(elapsed / iterations));
println!(
" faro::search {:>8} /call ({iterations} calls)",
fmt_duration(elapsed / iterations)
);
}

// ── 6. vigia::summary ─────────────────────────────────────────
Expand All @@ -126,12 +146,17 @@ async fn main() {
let start = Instant::now();
for _ in 0..iterations {
let _ = cuba_memorys::handlers::dispatch(
&pool, "cuba_vigia",
&pool,
"cuba_vigia",
serde_json::json!({"metric": "summary"}),
).await;
)
.await;
}
let elapsed = start.elapsed();
println!(" vigia::summary {:>8} /call ({iterations} calls)", fmt_duration(elapsed / iterations));
println!(
" vigia::summary {:>8} /call ({iterations} calls)",
fmt_duration(elapsed / iterations)
);
}

println!("\n ═══ Benchmark Complete ═══\n");
Expand Down
5 changes: 4 additions & 1 deletion rust/src/cognitive/density.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ mod tests {
fn test_density_mixed() {
// Non-uniform distribution: "fast" appears 5x, rest 1x each → skewed entropy
let d = information_density("fast fast fast fast fast safe modern language");
assert!(d > 0.3 && d < 0.9, "skewed distribution should be medium: got {d}");
assert!(
d > 0.3 && d < 0.9,
"skewed distribution should be medium: got {d}"
);
}
}
5 changes: 4 additions & 1 deletion rust/src/cognitive/dual_strength.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,10 @@ mod tests {
fn test_sac_parameters_calibrated() {
// V3: α=0.5, β=0.5: at SS=0, RS=0 → ΔSS = 0.5 * 1.0 * e^0 = 0.5
let s = increment_storage(0.0, 0.0);
assert!((s - 0.5).abs() < 0.001, "SS=0,RS=0 → ΔSS should be α: got {s}");
assert!(
(s - 0.5).abs() < 0.001,
"SS=0,RS=0 → ΔSS should be α: got {s}"
);

// At SS=0, RS=1 → ΔSS = 0.5 * 1.0 * e^(-0.5) ≈ 0.5 * 0.6065 ≈ 0.303
let s = increment_storage(0.0, 1.0);
Expand Down
35 changes: 25 additions & 10 deletions rust/src/cognitive/fsrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,7 @@ pub fn update_stability(
let s = current_stability.max(0.01);

match rating {
0 => {
w[11] * d.powf(-w[12]) * ((s + 1.0).powf(w[13]) - 1.0)
* (w[14] * (1.0 - r)).exp()
}
0 => w[11] * d.powf(-w[12]) * ((s + 1.0).powf(w[13]) - 1.0) * (w[14] * (1.0 - r)).exp(),
_ => {
let rating_bonus = match rating {
1 => w[15],
Expand Down Expand Up @@ -182,14 +179,20 @@ mod tests {
fn test_retrievability_custom_decay_slow() {
let r_slow = retrievability_with_decay(1.0, 10.0, 0.1);
let r_default = retrievability(1.0, 10.0);
assert!(r_slow > r_default, "slow decay should retain more: {r_slow} vs {r_default}");
assert!(
r_slow > r_default,
"slow decay should retain more: {r_slow} vs {r_default}"
);
}

#[test]
fn test_retrievability_custom_decay_fast() {
let r_fast = retrievability_with_decay(1.0, 10.0, 0.8);
let r_default = retrievability(1.0, 10.0);
assert!(r_fast < r_default, "fast decay should retain less: {r_fast} vs {r_default}");
assert!(
r_fast < r_default,
"fast decay should retain less: {r_fast} vs {r_default}"
);
}

#[test]
Expand All @@ -208,7 +211,10 @@ mod tests {
fn test_adaptive_decay_rate_bounds() {
for count in [0, 1, 5, 10, 30, 50, 100, 500, 1000] {
let rate = adaptive_decay_rate(count);
assert!((0.1..=0.8).contains(&rate), "out of bounds at count={count}: {rate}");
assert!(
(0.1..=0.8).contains(&rate),
"out of bounds at count={count}: {rate}"
);
}
}

Expand All @@ -221,7 +227,10 @@ mod tests {
#[test]
fn test_update_stability_forget() {
let new_s = update_stability(10.0, 5.0, 0.3, 0);
assert!(new_s < 10.0, "stability should decrease on forget, got {new_s}");
assert!(
new_s < 10.0,
"stability should decrease on forget, got {new_s}"
);
assert!(new_s > 0.0, "stability should remain positive");
}

Expand All @@ -238,7 +247,10 @@ mod tests {
// High PageRank → stability multiplied up
let base_s = 10.0;
let s_hub = apply_topological_inertia(base_s, 0.5);
assert!(s_hub > base_s, "hub should have higher stability: {s_hub} vs {base_s}");
assert!(
s_hub > base_s,
"hub should have higher stability: {s_hub} vs {base_s}"
);
}

#[test]
Expand All @@ -257,7 +269,10 @@ mod tests {
// PageRank=0 → ln(1+0) = 0 → no boost
let base_s = 10.0;
let s = apply_topological_inertia(base_s, 0.0);
assert!((s - base_s).abs() < 1e-10, "zero PR should have no boost: {s}");
assert!(
(s - base_s).abs() < 1e-10,
"zero PR should have no boost: {s}"
);
}

#[test]
Expand Down
32 changes: 22 additions & 10 deletions rust/src/cognitive/hebbian.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ use anyhow::Result;
use sqlx::PgPool;

use crate::constants::{
HEBBIAN_ACCESS_BOOST, HEBBIAN_SEARCH_BOOST, HEBBIAN_OJA_RATE,
BCM_THROTTLE_SCALE,
BCM_THROTTLE_SCALE, HEBBIAN_ACCESS_BOOST, HEBBIAN_OJA_RATE, HEBBIAN_SEARCH_BOOST,
};

// ── BCM V2: Dynamic Sliding Threshold ────────────────────────────
Expand Down Expand Up @@ -113,7 +112,7 @@ pub async fn oja_boost(pool: &PgPool, observation_id: uuid::Uuid, positive: bool
"UPDATE brain_observations SET
importance = LEAST(importance + $1, 1.0),
updated_at = NOW()
WHERE id = $2"
WHERE id = $2",
)
.bind(HEBBIAN_OJA_RATE)
.bind(observation_id)
Expand All @@ -124,7 +123,7 @@ pub async fn oja_boost(pool: &PgPool, observation_id: uuid::Uuid, positive: bool
"UPDATE brain_observations SET
importance = GREATEST(importance * 0.8, 0.0),
updated_at = NOW()
WHERE id = $2"
WHERE id = $2",
)
.bind(observation_id)
.execute(pool)
Expand All @@ -134,12 +133,16 @@ pub async fn oja_boost(pool: &PgPool, observation_id: uuid::Uuid, positive: bool
}

/// Strengthen relation on traversal (Hebbian synapse strengthening).
pub async fn strengthen_relation(pool: &PgPool, from_entity: uuid::Uuid, to_entity: uuid::Uuid) -> Result<()> {
pub async fn strengthen_relation(
pool: &PgPool,
from_entity: uuid::Uuid,
to_entity: uuid::Uuid,
) -> Result<()> {
sqlx::query(
"UPDATE brain_relations SET
strength = LEAST(strength + $1, 1.0),
updated_at = NOW()
WHERE from_entity = $2 AND to_entity = $3"
WHERE from_entity = $2 AND to_entity = $3",
)
.bind(HEBBIAN_OJA_RATE)
.bind(from_entity)
Expand All @@ -162,7 +165,7 @@ pub async fn boost_neighbors(pool: &PgPool, entity_id: uuid::Uuid) -> Result<usi
END
FROM brain_relations
WHERE from_entity = $2 OR to_entity = $2
)"
)",
)
.bind(HEBBIAN_ACCESS_BOOST)
.bind(entity_id)
Expand Down Expand Up @@ -192,7 +195,10 @@ mod tests {
fn test_dynamic_threshold_floor() {
// Low access + low prev → clamped to θ_min
let theta = dynamic_bcm_threshold(3, 5.0);
assert!((theta - BCM_THETA_MIN).abs() < 0.001, "low access → floor: {theta}");
assert!(
(theta - BCM_THETA_MIN).abs() < 0.001,
"low access → floor: {theta}"
);
}

#[test]
Expand All @@ -207,15 +213,21 @@ mod tests {
fn test_dynamic_throttle_low_activity() {
// Few accesses → near-full boost (θ_M = 10, count = 5 → ratio = 0.5)
let boost = bcm_throttle_dynamic(0.01, 5, 10.0);
assert!(boost > 0.004, "low activity should give decent boost: {boost}");
assert!(
boost > 0.004,
"low activity should give decent boost: {boost}"
);
}

#[test]
fn test_dynamic_throttle_high_activity() {
// Many accesses → fully self-adjusted throttle
// count=100, θ=100 → ratio=1.0 → throttle = max(0.1, 1.0 - 0.8) = 0.2
let boost = bcm_throttle_dynamic(0.01, 100, 100.0);
assert!(boost > 0.001 && boost < 0.005, "high activity → moderate throttle: {boost}");
assert!(
boost > 0.001 && boost < 0.005,
"high activity → moderate throttle: {boost}"
);
}

#[test]
Expand Down
11 changes: 5 additions & 6 deletions rust/src/cognitive/prediction_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@
//! Create: z ≤ 1σ (bottom 84.1%)
//! Handles vector space anisotropy better than static thresholds.

use crate::constants::{
PRED_ERROR_REINFORCE,
PRED_ERROR_UPDATE,
};
use crate::constants::{PRED_ERROR_REINFORCE, PRED_ERROR_UPDATE};

/// Action to take based on prediction error.
#[derive(Debug, Clone, PartialEq)]
Expand Down Expand Up @@ -84,9 +81,11 @@ pub fn adaptive_thresholds_zscore(recent_similarities: &[f64]) -> (f64, f64) {

let n = recent_similarities.len() as f64;
let mean = recent_similarities.iter().sum::<f64>() / n;
let variance = recent_similarities.iter()
let variance = recent_similarities
.iter()
.map(|x| (x - mean).powi(2))
.sum::<f64>() / n;
.sum::<f64>()
/ n;
let sigma = variance.sqrt();

// V5.2: Z-score thresholds — μ + Nσ
Expand Down
6 changes: 3 additions & 3 deletions rust/src/cognitive/spreading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub async fn neighbor_diffusion(pool: &PgPool) -> Result<()> {
"SELECT id, importance FROM brain_entities
WHERE importance > 0.5
ORDER BY importance DESC
LIMIT 20"
LIMIT 20",
)
.fetch_all(pool)
.await?;
Expand All @@ -39,7 +39,7 @@ pub async fn neighbor_diffusion(pool: &PgPool) -> Result<()> {
"SELECT CASE WHEN from_entity = $1 THEN to_entity ELSE from_entity END,
strength
FROM brain_relations
WHERE from_entity = $1 OR to_entity = $1"
WHERE from_entity = $1 OR to_entity = $1",
)
.bind(seed_id)
.fetch_all(pool)
Expand All @@ -59,7 +59,7 @@ pub async fn neighbor_diffusion(pool: &PgPool) -> Result<()> {
"UPDATE brain_entities SET
importance = LEAST(importance + $1, 1.0),
updated_at = NOW()
WHERE id = $2"
WHERE id = $2",
)
.bind(weighted_boost)
.bind(neighbor_id)
Expand Down
Loading