viva_math/free_energy
Free Energy Principle (FEP) calculations.
Based on Karl Friston’s work (2010, 2019). Free Energy approximates surprise/entropy in the brain’s predictions.
F ≈ Prediction_Error² + Complexity
In VIVA, this is used for interoception - sensing internal state and minimizing “surprise” through prediction.
References:
- Friston (2010) “The free-energy principle: a unified brain theory?”
- Parr & Friston (2019) “Generalised free energy and active inference”
Types
Qualitative feeling based on free energy level.
pub type Feeling {
Homeostatic
Surprised
Alarmed
Overwhelmed
}
Constructors
-
HomeostaticLow free energy - predictions match reality
-
SurprisedModerate free energy - slight mismatch
-
AlarmedHigh free energy - significant mismatch
-
OverwhelmedVery high free energy - system overwhelmed
Free Energy state for a system.
pub type FreeEnergyState {
FreeEnergyState(
free_energy: Float,
prediction_error: Float,
complexity: Float,
feeling: Feeling,
)
}
Constructors
-
FreeEnergyState( free_energy: Float, prediction_error: Float, complexity: Float, feeling: Feeling, )Arguments
- free_energy
-
The free energy value (lower is better)
- prediction_error
-
Prediction error component
- complexity
-
Complexity/prior divergence component
- feeling
-
Qualitative feeling based on free energy level
Values
pub fn active_inference_delta(
current: vector.Vec3,
target: vector.Vec3,
rate: Float,
) -> vector.Vec3
Active Inference: compute action that minimizes expected free energy.
This returns the delta to apply to current state to move toward target. Rate controls how quickly to move (0 = no movement, 1 = instant).
pub fn belief_update(
prior: Float,
observation: Float,
precision_prior: Float,
precision_likelihood: Float,
) -> Float
Bayesian belief update: combine prior with likelihood.
posterior ∝ likelihood × prior Using precision-weighted combination: new_belief = (precision_prior × prior + precision_likelihood × observation) / (precision_prior + precision_likelihood)
pub fn classify_feeling(free_energy: Float) -> Feeling
Classify feeling based on free energy level.
Thresholds calibrated for PAD space (max distance ~3.46).
pub fn complexity(
current: vector.Vec3,
baseline: vector.Vec3,
weight: Float,
) -> Float
Compute complexity term (prior divergence approximation).
In full FEP, this is KL divergence between approximate and true posterior. Here we use a simplified version based on deviation from a baseline.
pub fn compute_state(
expected: vector.Vec3,
actual: vector.Vec3,
baseline: vector.Vec3,
complexity_weight: Float,
) -> FreeEnergyState
Compute free energy and return full state with feeling.
pub fn estimate_precision(errors: List(Float)) -> Float
Estimate precision from recent prediction errors.
Precision = 1 / variance of errors Higher precision means more reliable predictions.
pub fn free_energy(
expected: vector.Vec3,
actual: vector.Vec3,
baseline: vector.Vec3,
complexity_weight: Float,
) -> Float
Compute free energy: F = prediction_error + complexity
Parameters
- expected: predicted/expected state
- actual: observed/actual state
- baseline: prior baseline state (e.g., personality)
- complexity_weight: how much to weight complexity (default ~0.1)
pub fn generalized_free_energy(
expected_state: vector.Vec3,
preferred_state: vector.Vec3,
uncertainty: Float,
) -> Float
Generalized Free Energy (expected free energy for planning).
G = ambiguity + risk
- ambiguity: expected surprise under model
- risk: KL divergence from preferred outcomes
Used for action selection in active inference.
pub fn precision_weighted_error(
expected: vector.Vec3,
actual: vector.Vec3,
precisions: vector.Vec3,
) -> Float
Precision-weighted prediction error.
Precision = 1/variance. Higher precision = more weight on that dimension. Returns weighted sum of squared errors.
pub fn prediction_error(
expected: vector.Vec3,
actual: vector.Vec3,
) -> Float
Compute prediction error between expected and actual state.
Uses squared Euclidean distance for continuous states.