viva_math IS NOT JUST A MATH LIB. It is the kernel of VIVA’s affective computing stack — PAD emotional space, Cusp catastrophe for mood transitions, Friston’s Free Energy Principle, attractor dynamics, and information theory — all expressed as small, type-safe Gleam functions.
Self-contained: depends only on
gleam_stdlibat runtime. All transcendentals route throughviva_math/scalar(Erlang:mathBIFs). Targets the BEAM (Erlang).
🎯 Overview
Core mathematical foundations for VIVA — a sentient digital life research project. The library models emotional dynamics as a dynamical system: PAD state vectors, Cusp catastrophe for sudden mood shifts, Free Energy for interoception, and Shannon-family entropy for affective complexity.
The math is small, deliberate, and grounded in real papers (Mehrabian 1996, Thom 1972, Friston 2010, Shannon 1948).
| Property | Value |
|---|---|
| Language | Pure Gleam (type-safe functional) |
| Targets | Erlang (BEAM) + JavaScript |
| Runtime deps | gleam_stdlib only |
| Tests | 522 passing on both Erlang and JavaScript targets |
| Domain | Affective computing, dynamical systems, info theory, optimal transport |
| Public API | viva_math/{scalar,common,vector,cusp,free_energy,attractor,entropy,ou,transport,ode,statistics,distributions,…} |
⚡ Quick Start
gleam add viva_math
import viva_math/attractor
import viva_math/cusp
import viva_math/free_energy
import viva_math/ou
import viva_math/random
import viva_math/transport
import viva_math/vector
pub fn main() {
// PAD emotional state: Pleasure / Arousal / Dominance
let state = vector.pad(-0.3, 0.7, -0.2)
// Nearest discrete emotion
let _ = attractor.classify_emotion(state)
// -> "fear"
// Bistability check — sudden mood transition possible?
let cusp_params = cusp.from_arousal_dominance(0.7, -0.2)
let _ = cusp.is_bistable(cusp_params)
// -> True
// Free energy (prediction error vs baseline).
let baseline = vector.pad(0.0, 0.0, 0.0)
let fe = free_energy.compute_state_simple(baseline, state, baseline, 0.1)
// fe.feeling -> Alarmed | Overwhelmed
// Ornstein-Uhlenbeck mood dynamics — mean-reverting noise.
let ou_params = ou.OUParams1D(theta: 1.0, mu: 0.5, sigma: 0.2)
let seed = random.from_int(42)
let #(trajectory, _) = ou.simulate(ou_params, 0.0, 0.01, 1000, seed)
// trajectory: List(Float) of length 1000
// Wasserstein distance between two populations.
let assert Ok(w2) =
transport.wasserstein_2_empirical([0.0, 0.5, 1.0], [0.2, 0.4, 0.8])
// w2 ≈ 0.158
}
📋 Prerequisites
| Tool | Version | Required for |
|---|---|---|
| Gleam | >= 1.4 | Build / runtime |
| Erlang/OTP | >= 26 | BEAM target |
| Node.js | >= 18 | JS target (opt.) |
Zero NIFs. Zero C dependencies. Pure functional.
🏗️ Architecture
┌──────────────────────────────────────────────────────────┐
│ Gleam application code │
│ viva_math/{common,vector,...} │
└────────┬─────────────────────────────────────────────────┘
│
┌────────▼─────────────────────────────────────────────────┐
│ viva_math modules │
│ │
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌──────────────┐ │
│ │ common │ │ vector │ │ cusp │ │ free_energy │ │
│ │ clamp │ │ Vec3 │ │ Thom │ │ Friston │ │
│ │ sigmoid│ │ PAD ℝ³ │ │ 1972 │ │ 2010 │ │
│ │ noise │ │ ops │ │ stoch. │ │ homeostasis │ │
│ └────────┘ └────────┘ └────────┘ └──────────────┘ │
│ │
│ ┌────────────┐ ┌──────────────────┐ │
│ │ attractor │ │ entropy │ │
│ │ Mehrabian │ │ Shannon · KL │ │
│ │ 1996 │ │ JS · Rényi │ │
│ │ 8 emotions │ │ hybrid affect. │ │
│ └────────────┘ └──────────────────┘ │
└────────┬─────────────────────────────────────────────────┘
│
┌────────▼─────────────────────────────────────────────────┐
│ gleam_community_maths (trig, stats, …) │
└──────────────────────────────────────────────────────────┘
📋 Core modules
| Module | Purpose |
|---|---|
viva_math/common | clamp, sigmoid, softmax, lerp, smoothstep, Wiener noise, decays |
viva_math/vector | Vec3 PAD type — Pleasure / Arousal / Dominance space |
viva_math/cusp | Cusp catastrophe (Thom 1972) + Stochastic Cusp (Euler-Maruyama) |
viva_math/free_energy | Free Energy Principle (Friston 2010) — interoception |
viva_math/attractor | 8 basic-emotion attractors in PAD space (Mehrabian 1996) |
viva_math/entropy | Shannon, KL divergence, Jensen-Shannon, Rényi, hybrid affective |
🧬 Theoretical Background
PAD Model — Mehrabian (1996)
Emotions live as points in 3D vector space:
- Pleasure
[-1, 1]— sadness ↔ joy - Arousal
[-1, 1]— calm ↔ excitement - Dominance
[-1, 1]— submission ↔ control
Cusp Catastrophe — Thom (1972)
Sudden mood transitions modeled by the potential:
V(x) = x⁴/4 + αx²/2 + βx
When arousal pushes α < 0 and the discriminant Δ > 0, the system goes
bistable — tiny perturbations trigger discrete mood jumps. The stochastic
variant adds Wiener noise (dV/dx + σξ(t)) integrated via Euler-Maruyama.
Free Energy Principle — Friston (2010)
Agents minimize “surprise” via prediction:
F ≈ Prediction_Error² + Complexity
Low free energy → predictions match reality (homeostasis). High free energy → significant mismatch (alarm).
Attractor Dynamics
Eight basic emotions form attractors in PAD space:
| Emotion | P | A | D |
|---|---|---|---|
| Joy | +0.76 | +0.48 | +0.35 |
| Sadness | -0.63 | -0.27 | -0.33 |
| Fear | -0.64 | +0.60 | -0.43 |
| Anger | -0.51 | +0.59 | +0.25 |
| Trust | +0.58 | -0.23 | +0.42 |
| Disgust | -0.60 | +0.35 | +0.11 |
| Serenity | +0.45 | -0.42 | +0.21 |
| Excitement | +0.62 | +0.75 | +0.38 |
Information Theory — Shannon (1948) + extensions
Shannon entropy H(p), KL divergence D(p‖q), Jensen-Shannon JS(p,q),
Rényi H_α, and a hybrid affective entropy
H_hybrid = αH₁ + (1-α)H₂ for mixed emotional states.
🎨 Design Principles
| Principle | Description |
|---|---|
| Math grounded in papers | Every function cites the source (Thom, Friston, Mehrabian, …) |
| Type-safe affect | Vec3 constructor enforces PAD axis order at compile time |
| Multi-target | Same code runs on BEAM and in the browser via JS target |
| Small surface, deep meaning | 6 modules, ~60 public functions — nothing speculative |
| Zero runtime deps | Builds only on gleam_stdlib (self-contained since 1.2.101) |
📚 Public API Highlights
Vec3 / PAD space
import viva_math/vector
let v = vector.pad(0.5, -0.2, 0.7)
let mag = vector.magnitude(v)
let unit = vector.normalize(v)
let dist = vector.euclidean(v, vector.pad(0.0, 0.0, 0.0))
Cusp catastrophe — deterministic + stochastic
import viva_math/cusp
let params = cusp.from_arousal_dominance(0.7, -0.2)
let v = cusp.potential(params, 0.3) // V(x)
let g = cusp.gradient(params, 0.3) // dV/dx
// Stochastic mood walk
let trajectory =
cusp.simulate_stochastic(
cusp.StochasticCuspParams(params, sigma: 0.05, seed: 42),
start: 0.0,
steps: 1000,
dt: 0.01,
)
Free Energy
import viva_math/free_energy
import viva_math/vector
let expected = vector.pad(0.0, 0.0, 0.0)
let actual = vector.pad(-0.3, 0.7, -0.2)
let state = free_energy.compute_state(expected, actual, expected, 0.1)
// state.feeling -> Calm | Surprised | Alarmed
// state.free_energy -> Float
Entropy and divergence
import viva_math/entropy
let h = entropy.shannon([0.2, 0.3, 0.5])
let d = entropy.kl_divergence([0.2, 0.8], [0.5, 0.5])
let js = entropy.jensen_shannon([0.2, 0.8], [0.5, 0.5])
let r = entropy.renyi([0.2, 0.3, 0.5], alpha: 2.0)
📚 Guides
Conceptual guides published alongside the API reference on hexdocs.pm/viva_math:
| Guide | Topic |
|---|---|
| PAD Emotional Model | The Vec3 type, 8 basic emotion attractors, Mehrabian (1996) |
| Ornstein-Uhlenbeck Mood Dynamics | Doob exact kernel, closed-form moments, Vec3 PAD dynamics |
| Wasserstein Distance | 1D empirical W₁/W₂, Gaussian closed form, sliced PAD pseudo-metric |
| Numerical Accuracy | Tolerance regimes, cancellation defences, measured precision per function |
🗺️ Roadmap
| Phase | Status |
|---|---|
| PAD vector space + emotion attractors | ✅ |
| Deterministic Cusp catastrophe | ✅ |
| Stochastic Cusp (Wiener + Euler-Maruyama) | ✅ |
| Free Energy Principle (basic interoception) | ✅ |
| Shannon / KL / Jensen-Shannon / Rényi entropy | ✅ |
| Hybrid affective entropy | ✅ |
| Arousal-weighted KL sensitivity | ✅ |
| Ornstein-Uhlenbeck mood dynamics | ✅ |
| Variational Free Energy (deeper Bayesian model) | ✅ |
| Wasserstein distance between affective distributions | ✅ |
| Property-based tests on every closed form | ✅ |
| Multivariate Wasserstein (log-domain Sinkhorn + early stop) | ✅ |
ULP-by-ULP mpmath reference validation | ✅ |
| JavaScript target — full module coverage | ✅ |
digamma ≤5 ULP precision | ✅ |
🤝 Contributing
git checkout -b feature/your-feature
gleam test # 522 tests (Erlang, default)
gleam test --target javascript # 522 tests (JavaScript)
gleam format --check src test
gleam docs build
See CONTRIBUTING.md for guidelines.
📖 References
- Mehrabian (1996) — Pleasure-arousal-dominance: A general framework
- Thom (1972) — Structural Stability and Morphogenesis
- Friston (2010) — The free-energy principle: a unified brain theory?
- Grasman et al. (2009) — Fitting the Cusp Catastrophe in R
- Oravecz et al. (2009) — Ornstein-Uhlenbeck Process in Affective Dynamics
- Shannon (1948) — A Mathematical Theory of Communication
🌌 VIVA Ecosystem
| Package | Purpose |
|---|---|
viva_math | Mathematical foundations (this package) |
viva_emotion | PAD emotional dynamics |
viva_tensor | FP8 LLM inference on the BEAM |
viva_telemetry | Observability suite |
viva_aion | Time perception |
viva_glyph | Symbolic language |