""" 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), }