Vitalis_Devcore / src /valence /valence_engine.py
FerrellSyntheticIntelligence
Add ValenceEngine, SyntheticThalamus, DecisionGate
d32bfdb
"""
Valence Engine — Vitalis FSI
Computes an affective score from a hypervector.
Score in [-1, 1]:
-1 = strong negative (failure, frustration)
0 = neutral / unknown
+1 = strong positive (success, curiosity)
Learned online from outcomes. No pretrained weights.
"""
import numpy as np
import os
from pathlib import Path
class ValenceEngine:
LEARNING_RATE = 0.01
BUFFER_MAX = 50
EPISODIC_BIAS = 0.3
def __init__(self, dim: int = 10_000):
self.dim = dim
self.path = Path.home() / ".vitalis_workspace" / "valence_weights.npy"
self.w = self._load_weights()
self.buffer = [] # (hv, reward) tuples
def _load_weights(self) -> np.ndarray:
if self.path.exists():
return np.load(self.path)
return np.random.randn(self.dim) * 0.001
def _save_weights(self):
self.path.parent.mkdir(parents=True, exist_ok=True)
np.save(self.path, self.w)
def reinforce(self, hv: np.ndarray, reward: float):
"""
Called after every outcome.
reward: +1.0 for success, -1.0 for failure, 0.0 for neutral.
"""
hv = hv.astype(np.float32)
pred = np.dot(self.w, hv) / self.dim
err = reward - pred
self.w += self.LEARNING_RATE * err * hv
self.buffer.append((hv.copy(), reward))
if len(self.buffer) > self.BUFFER_MAX:
self.buffer.pop(0)
self._save_weights()
def evaluate(self, hv: np.ndarray) -> tuple:
"""
Returns (valence, confidence).
confidence = |valence| — how certain the system is.
"""
hv_f = hv.astype(np.float32)
# Resonance term
raw = float(np.tanh(np.dot(self.w, hv_f) / self.dim))
# Episodic bias from recent high-valence experiences
bias = 0.0
if self.buffer:
sims = [float(np.mean(hv_f == bh)) for bh, _ in self.buffer]
weights = [bv for _, bv in self.buffer]
denom = sum(sims) + 1e-9
bias = float(np.dot(sims, weights) / denom)
valence = float(np.clip(0.7 * raw + self.EPISODIC_BIAS * bias, -1.0, 1.0))
confidence = abs(valence)
return valence, confidence
def report(self) -> dict:
return {
"buffer_size": len(self.buffer),
"avg_recent_reward": round(float(np.mean([r for _, r in self.buffer])), 4)
if self.buffer else 0.0,
"weight_norm": round(float(np.linalg.norm(self.w)), 4),
}