0b480cfb6b
Full-featured terminal explorer for the Engram knowledge graph built natively in El. Features: - ANSI-colored TUI with box-drawing borders and salience bars - All API endpoints: stats, nodes by type/tier, search, edges, spreading activation, node detail with neighbor traversal - Text report export via fs_write - Offline/unreachable mode with helpful startup messages - Interactive mode command reference - ENGRAM_URL env var for connecting to non-default servers - Uses json_get_raw for nested JSON object traversal
244 lines
11 KiB
EmacsLisp
244 lines
11 KiB
EmacsLisp
// field_test.el — Proof of concept: Hebbian field model
|
|
//
|
|
// Tests the core mechanics:
|
|
// 1. Nodes with 2D semantic positions and temporal coordinates
|
|
// 2. Edges with weights that grow via co-activation (Hebb's rule)
|
|
// 3. Spreading activation with path strength
|
|
// 4. Temporal decay in path strength
|
|
// 5. Epistemic confidence = node confidence * path strength
|
|
//
|
|
// No storage — pure in-memory demonstration using print output.
|
|
|
|
// ── Math helpers ──────────────────────────────────────────────────────────────
|
|
|
|
fn clamp_f(v: Float, lo: Float, hi: Float) -> Float {
|
|
if v < lo {
|
|
return lo
|
|
}
|
|
if v > hi {
|
|
return hi
|
|
}
|
|
return v
|
|
}
|
|
|
|
// L2 distance squared — no sqrt needed for proximity comparison
|
|
fn dist_sq(ax: Float, ay: Float, bx: Float, by: Float) -> Float {
|
|
let dx: Float = ax - bx
|
|
let dy: Float = ay - by
|
|
return dx * dx + dy * dy
|
|
}
|
|
|
|
// Proximity score: 1 / (1 + dist_sq) — range (0, 1], closer = higher
|
|
fn proximity(ax: Float, ay: Float, bx: Float, by: Float) -> Float {
|
|
let d: Float = dist_sq(ax, ay, bx, by)
|
|
return 1.0 / (1.0 + d)
|
|
}
|
|
|
|
// Linear temporal decay: 1.0 at t=0, decaying toward 0
|
|
// decay_rate: fraction lost per time unit
|
|
fn temporal_decay(age: Float, decay_rate: Float) -> Float {
|
|
let d: Float = 1.0 - decay_rate * age
|
|
return clamp_f(d, 0.0, 1.0)
|
|
}
|
|
|
|
// ── Hebbian learning ──────────────────────────────────────────────────────────
|
|
|
|
// New edge weight after co-activation event
|
|
// w_new = clamp(w + lr * a_i * a_j, 0, 1)
|
|
fn hebbian_update(weight: Float, act_i: Float, act_j: Float, lr: Float) -> Float {
|
|
let delta: Float = lr * act_i * act_j
|
|
return clamp_f(weight + delta, 0.0, 1.0)
|
|
}
|
|
|
|
// Edge decay between activations
|
|
// w_new = w * (1 - decay_rate)
|
|
fn edge_decay(weight: Float, decay_rate: Float) -> Float {
|
|
return clamp_f(weight * (1.0 - decay_rate), 0.0, 1.0)
|
|
}
|
|
|
|
// ── Path strength ─────────────────────────────────────────────────────────────
|
|
|
|
// Path strength = edge_weight * temporal_decay(node_age)
|
|
// This is the confidence qualifier on a retrieval result
|
|
fn path_strength(edge_weight: Float, node_age: Float, decay_rate: Float) -> Float {
|
|
let td: Float = temporal_decay(node_age, decay_rate)
|
|
return edge_weight * td
|
|
}
|
|
|
|
// Epistemic confidence = node_confidence * path_strength
|
|
fn epistemic_confidence(node_confidence: Float, ps: Float) -> Float {
|
|
return node_confidence * ps
|
|
}
|
|
|
|
// ── Simulation ────────────────────────────────────────────────────────────────
|
|
|
|
fn run_test() -> String {
|
|
println("=== Engram Field Model — Proof of Concept ===")
|
|
println("")
|
|
|
|
// ── Nodes ──
|
|
// Each node: id, label, semantic position (x, y), temporal coordinate, confidence
|
|
// Semantic space: 2D for demonstration
|
|
// x=0..1: concrete←→abstract
|
|
// y=0..1: negative←→positive valence
|
|
|
|
println("── Nodes ──────────────────────────────────────")
|
|
println("A: 'patient has fever' pos=(0.2, 0.4) t=100")
|
|
println("B: 'influenza' pos=(0.3, 0.5) t=90")
|
|
println("C: 'drug interaction warning' pos=(0.6, 0.3) t=10 (old, unactivated)")
|
|
println("D: 'ibuprofen' pos=(0.65, 0.3) t=10 (old, unactivated)")
|
|
println("")
|
|
|
|
// Semantic positions
|
|
let ax: Float = 0.2
|
|
let ay: Float = 0.4
|
|
let bx: Float = 0.3
|
|
let by: Float = 0.5
|
|
let cx: Float = 0.6
|
|
let cy: Float = 0.3
|
|
let dx_pos: Float = 0.65
|
|
let dy_pos: Float = 0.3
|
|
|
|
// Initial proximity (latent gradient — implicit from positions)
|
|
let prox_ab: Float = proximity(ax, ay, bx, by)
|
|
let prox_ac: Float = proximity(ax, ay, cx, cy)
|
|
let prox_cd: Float = proximity(cx, cy, dx_pos, dy_pos)
|
|
|
|
println("── Initial latent gradients (proximity) ───────")
|
|
println("A↔B proximity: " + float_to_str(prox_ab) + " (semantically close — fever/flu)")
|
|
println("A↔C proximity: " + float_to_str(prox_ac) + " (semantically distant)")
|
|
println("C↔D proximity: " + float_to_str(prox_cd) + " (close — drug interaction/ibuprofen)")
|
|
println("")
|
|
|
|
// ── Initial edge weights (all start near zero — no co-activation yet) ──
|
|
let w_ab: Float = 0.0
|
|
let w_ac: Float = 0.0
|
|
let w_cd: Float = 0.0
|
|
|
|
println("── Initial edge weights ────────────────────────")
|
|
println("A→B: " + float_to_str(w_ab) + " (no co-activation yet)")
|
|
println("A→C: " + float_to_str(w_ac))
|
|
println("C→D: " + float_to_str(w_cd))
|
|
println("")
|
|
|
|
// ── Co-activation events ──
|
|
// The doctor CGI processes a patient with fever.
|
|
// A (fever) and B (influenza) co-activate — the diagnosis fires both.
|
|
// Learning rate: 0.3
|
|
|
|
let lr: Float = 0.3
|
|
let act_strength: Float = 1.0 // full activation
|
|
|
|
println("── Co-activation event 1: patient with fever → flu diagnosis ──")
|
|
let w_ab_1: Float = hebbian_update(w_ab, act_strength, act_strength, lr)
|
|
println("A and B fire together (strength=1.0)")
|
|
println("A→B weight: " + float_to_str(w_ab) + " → " + float_to_str(w_ab_1))
|
|
println("")
|
|
|
|
println("── Co-activation event 2: another flu case ──")
|
|
let w_ab_2: Float = hebbian_update(w_ab_1, act_strength, act_strength, lr)
|
|
println("A and B fire together again")
|
|
println("A→B weight: " + float_to_str(w_ab_1) + " → " + float_to_str(w_ab_2))
|
|
println("")
|
|
|
|
println("── Co-activation event 3: third flu case ──")
|
|
let w_ab_3: Float = hebbian_update(w_ab_2, act_strength, act_strength, lr)
|
|
println("A and B fire together again")
|
|
println("A→B weight: " + float_to_str(w_ab_2) + " → " + float_to_str(w_ab_3))
|
|
println("")
|
|
|
|
// C and D have never been activated in this context — weights stay near zero
|
|
// (In practice, proximity means a latent gradient exists, but no learned edge yet)
|
|
|
|
// ── Query: activate A (fever), what spreads? ──
|
|
println("── Query: activate A (fever) ───────────────────")
|
|
println("Spreading activation from A...")
|
|
println("")
|
|
|
|
// Decay rates
|
|
let temporal_decay_rate: Float = 0.005 // per time unit
|
|
let current_t: Int = 200
|
|
|
|
// Node ages (current_t - node_t)
|
|
let age_b: Float = 110.0 // t=90, current=200
|
|
let age_c: Float = 190.0 // t=10, current=200
|
|
let age_d: Float = 190.0 // t=10, current=200
|
|
|
|
// Path strength to B: strong edge, recently relevant
|
|
let ps_b: Float = path_strength(w_ab_3, age_b, temporal_decay_rate)
|
|
let conf_b: Float = epistemic_confidence(0.9, ps_b)
|
|
|
|
// Path strength to C: no learned edge — only latent proximity gradient
|
|
// Spreading activation from A can weakly reach C via proximity alone
|
|
let latent_ac: Float = prox_ac * 0.1 // proximity contributes small initial signal
|
|
let ps_c: Float = path_strength(latent_ac, age_c, temporal_decay_rate)
|
|
let conf_c: Float = epistemic_confidence(0.9, ps_c)
|
|
|
|
// Path strength to D via C: chain of weak signals
|
|
let ps_d: Float = path_strength(latent_ac * prox_cd * 0.1, age_d, temporal_decay_rate)
|
|
let conf_d: Float = epistemic_confidence(0.9, ps_d)
|
|
|
|
println("Result: B (influenza)")
|
|
println(" Edge weight: " + float_to_str(w_ab_3))
|
|
println(" Node age: " + float_to_str(age_b) + " time units")
|
|
println(" Temporal decay: " + float_to_str(temporal_decay(age_b, temporal_decay_rate)))
|
|
println(" Path strength: " + float_to_str(ps_b))
|
|
println(" Confidence: " + float_to_str(conf_b))
|
|
println(" → STRONG: B surfaces with high confidence")
|
|
println("")
|
|
|
|
println("Result: C (drug interaction warning)")
|
|
println(" Edge weight: " + float_to_str(latent_ac) + " (latent only — never co-activated)")
|
|
println(" Node age: " + float_to_str(age_c) + " time units")
|
|
println(" Temporal decay: " + float_to_str(temporal_decay(age_c, temporal_decay_rate)))
|
|
println(" Path strength: " + float_to_str(ps_c))
|
|
println(" Confidence: " + float_to_str(conf_c))
|
|
println(" → WEAK: C is distant and old — attenuated path triggers refresh signal")
|
|
println("")
|
|
|
|
println("Result: D (ibuprofen) via C")
|
|
println(" Path strength: " + float_to_str(ps_d))
|
|
println(" Confidence: " + float_to_str(conf_d))
|
|
println(" → VERY WEAK: chain of attenuated edges")
|
|
println("")
|
|
|
|
// ── Refresh trigger ──
|
|
println("── Attenuation trigger ─────────────────────────")
|
|
let threshold: Float = 0.2
|
|
println("Confidence threshold: " + float_to_str(threshold))
|
|
if conf_c < threshold {
|
|
println("C is below threshold (" + float_to_str(conf_c) + " < " + float_to_str(threshold) + ")")
|
|
println("→ Trigger: 'drug interaction warning node found but path is weak.")
|
|
println(" Last activated " + float_to_str(age_c) + " time units ago.")
|
|
println(" Recommend fetching current information before acting.'")
|
|
}
|
|
println("")
|
|
|
|
// ── After refresh: C and D co-activate with A ──
|
|
println("── Refresh: doctor looks up drug interactions ──")
|
|
println("A, C, D all co-activate during research")
|
|
let w_ac_new: Float = hebbian_update(0.0, act_strength, act_strength, lr)
|
|
let w_cd_new: Float = hebbian_update(0.0, act_strength, act_strength, lr)
|
|
let age_c_new: Float = 0.0 // just activated
|
|
let age_d_new: Float = 0.0
|
|
|
|
let ps_c_new: Float = path_strength(w_ac_new, age_c_new, temporal_decay_rate)
|
|
let conf_c_new: Float = epistemic_confidence(0.9, ps_c_new)
|
|
|
|
println("A→C weight after refresh: " + float_to_str(w_ac_new))
|
|
println("C→D weight after refresh: " + float_to_str(w_cd_new))
|
|
println("C confidence after refresh: " + float_to_str(conf_c_new))
|
|
println("→ Edges strengthened. Drug interaction now reachable with high confidence.")
|
|
println("")
|
|
|
|
println("=== Result ===")
|
|
println("Perfect memory: all nodes present throughout.")
|
|
println("Calibrated confidence: B (flu) high, C/D (drug interaction) low until refreshed.")
|
|
println("Self-correcting: attenuation triggered fetch, fetch strengthened edges.")
|
|
println("The field works.")
|
|
|
|
return "ok"
|
|
}
|
|
|
|
let result: String = run_test()
|