use serde::{Serialize, Deserialize};
use anyhow::Result;
use nalgebra::{DVector, DMatrix};
pub struct HamiltonianDynamics {
energy_scale: f64,
dimension: usize,
phase_space: PhaseSpace,
potential: Box<dyn PotentialFunction>,
friction: f64,
}
impl HamiltonianDynamics {
pub fn new(energy_scale: f64, dimension: usize) -> Self {
let phase_space = PhaseSpace::new(dimension);
let potential = Box::new(QuadraticPotential::new(energy_scale));
Self {
energy_scale,
dimension,
phase_space,
potential,
friction: 0.1, }
}
pub fn evolve(&mut self, dt: f64) -> Result<()> {
let m = 1.0;
let new_positions = &self.phase_space.positions + &self.phase_space.momenta * (dt / m);
let forces = self.potential.gradient(&self.phase_space.positions)?;
let new_momenta = &self.phase_space.momenta
- forces * dt
- &self.phase_space.momenta * (self.friction * dt);
self.phase_space.positions = new_positions;
self.phase_space.momenta = new_momenta;
self.phase_space.time += dt;
Ok(())
}
pub fn total_energy(&self) -> Result<f64> {
let kinetic = self.kinetic_energy();
let potential = self.potential.value(&self.phase_space.positions)?;
Ok(kinetic + potential)
}
fn kinetic_energy(&self) -> f64 {
let m = 1.0;
self.phase_space.momenta.dot(&self.phase_space.momenta) / (2.0 * m)
}
pub fn calculate_price(&self, base_price: f64) -> Result<f64> {
let energy = self.total_energy()?;
let volatility_factor = (energy / self.energy_scale).tanh();
let position_factor = self.phase_space.positions.norm();
let momentum_factor = self.phase_space.momenta.mean();
let price = base_price * (1.0 + 0.1 * position_factor)
* (1.0 + 0.05 * momentum_factor)
* (1.0 + 0.2 * volatility_factor);
Ok(price.max(0.0)) }
pub fn get_phase_space(&self) -> Result<PhaseSpace> {
Ok(self.phase_space.clone())
}
pub fn set_potential(&mut self, potential: Box<dyn PotentialFunction>) {
self.potential = potential;
}
pub fn perturb(&mut self, energy: f64) -> Result<()> {
let mut rng = rand::thread_rng();
use rand::Rng;
for i in 0..self.dimension {
let kick = rng.gen_range(-1.0..1.0) * energy.sqrt();
self.phase_space.momenta[i] += kick;
}
Ok(())
}
pub fn lyapunov_exponent(&self, steps: usize, dt: f64) -> Result<f64> {
let epsilon = 1e-8;
let mut system1 = self.phase_space.clone();
let mut system2 = self.phase_space.clone();
system2.positions[0] += epsilon;
let mut sum_log_divergence = 0.0;
for _ in 0..steps {
system1.evolve_step(&*self.potential, dt, self.friction)?;
system2.evolve_step(&*self.potential, dt, self.friction)?;
let distance = (&system2.positions - &system1.positions).norm();
sum_log_divergence += (distance / epsilon).ln();
let scale = epsilon / distance;
system2.positions = &system1.positions + (&system2.positions - &system1.positions) * scale;
}
Ok(sum_log_divergence / (steps as f64 * dt))
}
}
#[derive(Debug, Clone)]
pub struct PhaseSpace {
pub positions: DVector<f64>,
pub momenta: DVector<f64>,
pub time: f64,
}
impl PhaseSpace {
pub fn new(dimension: usize) -> Self {
Self {
positions: DVector::zeros(dimension),
momenta: DVector::zeros(dimension),
time: 0.0,
}
}
pub fn random(dimension: usize, scale: f64) -> Self {
use rand::Rng;
let mut rng = rand::thread_rng();
let positions = DVector::from_fn(dimension, |_, _| rng.gen_range(-scale..scale));
let momenta = DVector::from_fn(dimension, |_, _| rng.gen_range(-scale..scale));
Self {
positions,
momenta,
time: 0.0,
}
}
pub fn total_energy(&self) -> f64 {
let kinetic = self.momenta.dot(&self.momenta) / 2.0;
let potential = self.positions.dot(&self.positions) / 2.0;
kinetic + potential
}
fn evolve_step(
&mut self,
potential: &dyn PotentialFunction,
dt: f64,
friction: f64,
) -> Result<()> {
let forces = potential.gradient(&self.positions)?;
self.momenta -= forces * (dt / 2.0);
self.momenta *= 1.0 - friction * dt / 2.0;
self.positions += &self.momenta * dt;
let forces = potential.gradient(&self.positions)?;
self.momenta -= forces * (dt / 2.0);
self.momenta *= 1.0 - friction * dt / 2.0;
self.time += dt;
Ok(())
}
}
pub trait PotentialFunction: Send + Sync {
fn value(&self, positions: &DVector<f64>) -> Result<f64>;
fn gradient(&self, positions: &DVector<f64>) -> Result<DVector<f64>>;
}
pub struct QuadraticPotential {
spring_constant: f64,
}
impl QuadraticPotential {
pub fn new(k: f64) -> Self {
Self { spring_constant: k }
}
}
impl PotentialFunction for QuadraticPotential {
fn value(&self, positions: &DVector<f64>) -> Result<f64> {
Ok(self.spring_constant * positions.dot(positions) / 2.0)
}
fn gradient(&self, positions: &DVector<f64>) -> Result<DVector<f64>> {
Ok(positions * self.spring_constant)
}
}
pub struct AnharmonicPotential {
quadratic_coeff: f64,
quartic_coeff: f64,
}
impl AnharmonicPotential {
pub fn new(k2: f64, k4: f64) -> Self {
Self {
quadratic_coeff: k2,
quartic_coeff: k4,
}
}
}
impl PotentialFunction for AnharmonicPotential {
fn value(&self, positions: &DVector<f64>) -> Result<f64> {
let r2 = positions.dot(positions);
Ok(self.quadratic_coeff * r2 / 2.0 + self.quartic_coeff * r2 * r2 / 4.0)
}
fn gradient(&self, positions: &DVector<f64>) -> Result<DVector<f64>> {
let r2 = positions.dot(positions);
Ok(positions * (self.quadratic_coeff + self.quartic_coeff * r2))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PriceDynamics {
pub base_price: f64,
pub volatility: f64,
pub mean_reversion: f64,
pub current_price: f64,
pub momentum: f64,
}
impl PriceDynamics {
pub fn new(base_price: f64, volatility: f64, mean_reversion: f64) -> Self {
Self {
base_price,
volatility,
mean_reversion,
current_price: base_price,
momentum: 0.0,
}
}
pub fn update(&mut self, hamiltonian: &HamiltonianDynamics, dt: f64) -> Result<()> {
let energy = hamiltonian.total_energy()?;
let energy_factor = (energy / hamiltonian.energy_scale).tanh();
let reversion_force = self.mean_reversion * (self.base_price - self.current_price);
use rand_distr::{Normal, Distribution};
let noise_dist = Normal::new(0.0, self.volatility * dt.sqrt()).unwrap();
let noise = noise_dist.sample(&mut rand::thread_rng());
self.momentum += (reversion_force + noise * energy_factor) * dt;
self.momentum *= 1.0 - 0.1 * dt;
self.current_price += self.momentum * dt;
self.current_price = self.current_price.max(0.01);
Ok(())
}
pub fn cost_per_token(&self) -> f64 {
self.current_price / 1_000_000.0 }
pub fn statistics(&self) -> PriceStatistics {
PriceStatistics {
current: self.current_price,
base: self.base_price,
momentum: self.momentum,
volatility: self.volatility,
deviation: (self.current_price - self.base_price).abs() / self.base_price,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PriceStatistics {
pub current: f64,
pub base: f64,
pub momentum: f64,
pub volatility: f64,
pub deviation: f64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hamiltonian_energy_conservation() {
let mut ham = HamiltonianDynamics::new(1.0, 3);
ham.phase_space = PhaseSpace::random(3, 1.0);
ham.friction = 0.0;
let initial_energy = ham.total_energy().unwrap();
for _ in 0..100 {
ham.evolve(0.01).unwrap();
}
let final_energy = ham.total_energy().unwrap();
assert!((final_energy - initial_energy).abs() < 0.1);
}
#[test]
fn test_price_dynamics() {
let ham = HamiltonianDynamics::new(1.0, 2);
let mut price = PriceDynamics::new(100.0, 0.2, 0.1);
for _ in 0..10 {
price.update(&ham, 0.1).unwrap();
}
assert!(price.current_price > 0.0);
assert!(price.current_price <= price.base_price * 2.0);
}
}