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:

Types

Qualitative feeling based on free energy level.

pub type Feeling {
  Homeostatic
  Surprised
  Alarmed
  Overwhelmed
}

Constructors

  • Homeostatic

    Low free energy - predictions match reality

  • Surprised

    Moderate free energy - slight mismatch

  • Alarmed

    High free energy - significant mismatch

  • Overwhelmed

    Very 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.

pub fn surprise(
  expected: Float,
  observed: Float,
  sigma: Float,
) -> Float

Compute surprise for a single dimension.

Surprise = -log(p(observation | model)) Using Gaussian approximation: surprise ∝ (x - μ)² / (2σ²)

Search Document