viva_math/attractor
Attractor dynamics for emotional states.
Based on Mehrabian’s PAD model (1996) and dynamical systems theory. Emotions form attractors in PAD space - stable states the system tends toward.
The 8 basic emotions correspond to the octants of the PAD cube:
- Joy: (+P, +A, +D)
- Sadness: (-P, -A, -D)
- Anger: (-P, +A, +D)
- Fear: (-P, +A, -D)
- Surprise: (+P, +A, -D) or (-P, +A, -D)
- Disgust: (-P, -A, +D)
- Trust: (+P, -A, +D)
- Anticipation: (+P, +A, +D)
References:
- Mehrabian (1996) “Pleasure-arousal-dominance: A general framework”
- Russell (2003) “Core affect and the psychological construction of emotion”
Types
An attractor in PAD space with a name and position.
pub type Attractor {
Attractor(name: String, position: vector.Vec3)
}
Constructors
-
Attractor(name: String, position: vector.Vec3)
Values
pub fn analyze(
point: vector.Vec3,
attractors: List(Attractor),
temperature: Float,
) -> Result(AttractorResult, Nil)
Comprehensive attractor analysis for a point.
pub fn attractor_pull(
point: vector.Vec3,
attractor: Attractor,
strength: Float,
) -> vector.Vec3
Compute attractor pull - force vector toward nearest attractor.
The pull strength increases with distance from attractor (spring-like). strength parameter controls overall force magnitude.
pub fn basin_weights(
point: vector.Vec3,
attractors: List(Attractor),
temperature: Float,
) -> List(#(Attractor, Float))
Calculate influence weights for all attractors using softmax of negative distances.
CORRECTED per DeepSeek R1 validation: w_i = exp(-γ × d_i) / Σ_j exp(-γ × d_j)
Where γ = 1/temperature (higher temp = softer weights, lower temp = sharper). This is more numerically stable than 1/d and matches Boltzmann distribution.
Closer attractors have higher weights. The temperature parameter controls how “sharp” the weighting is (lower temp = more weight on nearest).
pub fn blend_attractors(
a: Attractor,
b: Attractor,
t: Float,
) -> Attractor
Interpolate between two attractors based on a blend factor.
t=0 gives first attractor, t=1 gives second.
pub fn classify_emotion(point: vector.Vec3) -> String
Classify emotional state by nearest attractor name.
pub fn create(
name: String,
pleasure: Float,
arousal: Float,
dominance: Float,
) -> Attractor
Create a custom attractor from name and PAD values.
pub fn dominant_dimension(attractor: Attractor) -> String
Get the dominant emotion component (P, A, or D) for an attractor.
pub fn emotional_attractors() -> List(Attractor)
The 8 basic emotional attractors (Mehrabian octants). Values from empirical research on emotion self-reports.
pub fn in_basin(
point: vector.Vec3,
attractor: Attractor,
all: List(Attractor),
) -> Bool
Check if point is in basin of an attractor.
A point is “in” a basin if that attractor has the highest weight.
pub fn nearby_attractors(
point: vector.Vec3,
attractors: List(Attractor),
radius: Float,
) -> List(Attractor)
Find all attractors within a given distance.
pub fn nearest(
point: vector.Vec3,
attractors: List(Attractor),
) -> Result(Attractor, Nil)
Find the nearest attractor to a given point.
pub fn ou_mean_reversion(
current: vector.Vec3,
attractor: vector.Vec3,
theta: Float,
dt: Float,
) -> vector.Vec3
Ornstein-Uhlenbeck mean reversion toward attractor.
dx = theta * (attractor - x) * dt
This is the deterministic part of O-U process. theta controls reversion speed (higher = faster return to attractor).
pub fn weighted_pull(
point: vector.Vec3,
attractors: List(Attractor),
strength: Float,
temperature: Float,
) -> vector.Vec3
Compute weighted pull from all attractors.
Each attractor pulls proportionally to its basin weight.