feat(SNNBot): recency-weighted exemplars for fast re-acquisition

Exponential decay (0.95^age) makes newest exemplars dominate the
weighted mean. Old stale exemplars fade naturally, so re-learning
after target movement is always fast regardless of buffer history.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 08:02:06 +02:00
parent b4e2cb5b3c
commit 01a30b7570
+4 -1
View File
@@ -26,6 +26,7 @@ proc initBinaryAimer*(): BinaryAimer =
proc forward*(aimer: var BinaryAimer, input: BitVec80): float = proc forward*(aimer: var BinaryAimer, input: BitVec80): float =
## Kernel regression: weighted circular mean over active exemplars. ## Kernel regression: weighted circular mean over active exemplars.
## Weights by similarity^2 and exponential recency decay (0.95^age).
## Returns angle in [0,360) degrees, or -1.0 sentinel when cold (no weight). ## Returns angle in [0,360) degrees, or -1.0 sentinel when cold (no weight).
var sinSum = 0.0 var sinSum = 0.0
var cosSum = 0.0 var cosSum = 0.0
@@ -37,7 +38,9 @@ proc forward*(aimer: var BinaryAimer, input: BitVec80): float =
sim += popcount(input[w] and aimer.exemplars[i].pattern[w]).int sim += popcount(input[w] and aimer.exemplars[i].pattern[w]).int
let excess = sim - MIN_SIM let excess = sim - MIN_SIM
if excess <= 0: continue if excess <= 0: continue
let weight = float(excess * excess) let age = (aimer.nextSlot - 1 - i + MAX_K) mod MAX_K
let recency = pow(0.95, age.float)
let weight = float(excess * excess) * recency
let rad = degToRad(aimer.exemplars[i].angle) let rad = degToRad(aimer.exemplars[i].angle)
sinSum += weight * sin(rad) sinSum += weight * sin(rad)
cosSum += weight * cos(rad) cosSum += weight * cos(rad)