File size: 2,610 Bytes
c99bf3c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | """
Predictive Cortex — Vitalis FSI
Implements predictive processing: the cortex maintains a model
of what it expects to see next, and only forwards the PREDICTION
ERROR to higher cognitive layers — not the raw input.
This is how biological cortex works. It predicts constantly.
What gets attention is what violates prediction.
Steps:
1. Maintain a running prediction of the next input
2. Compute prediction error = actual - predicted
3. Update prediction based on error
4. Forward error vector to cognitive core
5. Strong errors = surprise = attention = learning
"""
import numpy as np
from vitalis_ide.math_core.kernel import VitalisKernel
class PredictiveCortex:
LEARNING_RATE = 0.05
SURPRISE_THRESHOLD = 0.3
def __init__(self, dim: int = 10_000):
self.dim = dim
self.kernel = VitalisKernel()
self._prediction = np.zeros(dim, dtype=np.float32)
self._cycle = 0
self._surprise_history = []
def process(self, hv: np.ndarray) -> tuple:
"""
Feed an input hypervector through predictive processing.
Returns:
error_vec : prediction error as bipolar int8 vector
surprise : float [0,1] — how surprising was this input
is_novel : bool — above surprise threshold
"""
self._cycle += 1
hv_f = hv.astype(np.float32)
# Prediction error
error_f = hv_f - self._prediction
# Surprise = normalized magnitude of error
surprise = float(np.tanh(np.linalg.norm(error_f) / np.sqrt(self.dim)))
# Update prediction toward actual input
self._prediction += self.LEARNING_RATE * error_f
# Binarize error for downstream HDC processing
error_vec = np.sign(error_f).astype(np.int8)
error_vec[error_vec == 0] = 1
is_novel = surprise > self.SURPRISE_THRESHOLD
self._surprise_history.append(surprise)
if len(self._surprise_history) > 100:
self._surprise_history.pop(0)
return error_vec, surprise, is_novel
def reset_prediction(self):
"""Call after dream cycle — fresh prediction slate."""
self._prediction *= 0.5
def avg_surprise(self) -> float:
if not self._surprise_history:
return 0.0
return round(float(np.mean(self._surprise_history[-20:])), 4)
def report(self) -> dict:
return {
"cycles": self._cycle,
"avg_surprise": self.avg_surprise(),
"prediction_norm": round(float(np.linalg.norm(self._prediction)), 4),
}
|