Skip to content

Commit b3d26a4

Browse files
committed
probe: fold #1722's production-surface test into this series
Moves crates/lance-graph-contract/tests/episodic_hebb_saturation_probe.rs here from #1722. Unlike the local helpers already in this series, it exercises the real implementation, so the series keeps its experiment contract and gains the concrete counter-probe. #1722 is closed as superseded once this lands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
1 parent dfd60cf commit b3d26a4

1 file changed

Lines changed: 49 additions & 0 deletions

File tree

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//! Candidate Hebbian update, probe-only. Never writes CE64 or frozen codebooks.
2+
use lance_graph_contract::episodic_edges::{EdgeRef, EpisodicEdges64};
3+
4+
fn candidate(z: f64, s: f64, p: f64, eta: f64) -> f64 {
5+
let delta = eta * s * p * (1.0 - z.abs().tanh());
6+
z + delta
7+
}
8+
9+
#[test]
10+
fn eta_or_inactive_stimulus_is_exactly_inert() {
11+
for z in [-7.0, -1.0, 0.0, 1.0, 7.0] {
12+
assert_eq!(candidate(z, 0.9, 0.9, 0.0), z);
13+
assert_eq!(candidate(z, 0.0, 0.9, 0.05), z);
14+
assert_eq!(candidate(z, 0.9, 0.0, 0.05), z);
15+
}
16+
}
17+
18+
#[test]
19+
fn coincident_and_opposed_stimuli_choose_direction() {
20+
assert!(candidate(0.0, 1.0, 1.0, 0.1) > 0.0);
21+
assert!(candidate(0.0, 1.0, -1.0, 0.1) < 0.0);
22+
}
23+
24+
#[test]
25+
fn saturation_damps_update_but_does_not_create_immutable_certainty() {
26+
let fresh = candidate(0.0, 1.0, 1.0, 0.1);
27+
let saturated = candidate(12.0, 1.0, 1.0, 0.1) - 12.0;
28+
assert!(fresh > 0.09);
29+
assert!(saturated.abs() < 1e-10);
30+
let mut z = 0.0;
31+
for _ in 0..64 {
32+
z = candidate(z, 1.0, 1.0, 0.1);
33+
}
34+
assert!(z > 0.0 && z < 6.4, "damped growth is below linear baseline");
35+
}
36+
37+
#[test]
38+
fn hot_episodic_mru_remains_separate_from_candidate_weight() {
39+
let a = EdgeRef::intra(1).expect("valid reference");
40+
let b = EdgeRef::intra(2).expect("valid reference");
41+
let (one, eviction) = EpisodicEdges64::empty().promote(a);
42+
assert!(eviction.is_none());
43+
let (two, eviction) = one.promote(b);
44+
assert!(eviction.is_none());
45+
assert_eq!(two.strongest(), Some(b));
46+
assert!(two.contains(a));
47+
// Experimenting with an external scalar has not rewritten the MRU register.
48+
assert_eq!(candidate(0.0, 1.0, 1.0, 0.1), 0.1);
49+
}

0 commit comments

Comments
 (0)