use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
use super::functions::*;
use super::functions::{EPSILON_0, MU_0};
#[derive(Debug, Clone)]
pub struct GeodesicEquation {
pub metric_components: Vec<Vec<f64>>,
}
impl GeodesicEquation {
pub fn new(dim: usize) -> Self {
let mut g = vec![vec![0.0; dim]; dim];
for i in 0..dim {
g[i][i] = 1.0;
}
Self {
metric_components: g,
}
}
pub fn christoffel_symbol(&self, _i: usize, _j: usize, _k: usize) -> f64 {
0.0
}
pub fn geodesic_deviation(&self) -> f64 {
0.0
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IntegratorMethod {
Euler,
Leapfrog,
RK4,
SymplecticEuler,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct BRSTComplex {
pub cochains: Vec<Vec<f64>>,
pub differentials: Vec<Vec<Vec<f64>>>,
}
#[allow(dead_code)]
impl BRSTComplex {
pub fn new(dims: Vec<usize>) -> Self {
let n = dims.len();
let cochains = dims.iter().map(|&d| vec![0.0; d]).collect();
let differentials = (0..n.saturating_sub(1))
.map(|k| vec![vec![0.0; dims[k]]; dims[k + 1]])
.collect();
Self {
cochains,
differentials,
}
}
pub fn apply_differential(&self, k: usize, v: &[f64]) -> Vec<f64> {
if k + 1 >= self.differentials.len() + 1 {
return vec![];
}
let q = &self.differentials[k];
q.iter()
.map(|row| row.iter().zip(v.iter()).map(|(a, b)| a * b).sum())
.collect()
}
pub fn check_nilpotency(&self, k: usize, eps: f64) -> bool {
if k + 1 >= self.differentials.len() {
return true;
}
let qk = &self.differentials[k];
let qk1 = &self.differentials[k + 1];
let rows1 = qk1.len();
let cols1 = if rows1 > 0 {
qk1[0].len()
} else {
return true;
};
let cols0 = if !qk.is_empty() {
qk[0].len()
} else {
return true;
};
for i in 0..rows1 {
for j in 0..cols0 {
let val: f64 = (0..cols1)
.map(|l| {
qk1[i].get(l).copied().unwrap_or(0.0)
* qk.get(l).and_then(|r| r.get(j)).copied().unwrap_or(0.0)
})
.sum();
if val.abs() > eps {
return false;
}
}
}
true
}
pub fn euler_characteristic(&self) -> i64 {
self.cochains
.iter()
.enumerate()
.map(|(k, c)| {
if k % 2 == 0 {
c.len() as i64
} else {
-(c.len() as i64)
}
})
.sum()
}
pub fn dimensions(&self) -> Vec<usize> {
self.cochains.iter().map(|c| c.len()).collect()
}
}
#[derive(Debug, Clone)]
pub struct KdVSoliton {
pub kappa: f64,
pub x0: f64,
}
impl KdVSoliton {
pub fn new(kappa: f64, x0: f64) -> Self {
assert!(kappa > 0.0, "kappa must be positive");
Self { kappa, x0 }
}
pub fn eval(&self, x: f64, t: f64) -> f64 {
let xi = 0.5 * self.kappa * (x - self.kappa * self.kappa * t - self.x0);
let sech = 1.0 / xi.cosh();
-0.5 * self.kappa * self.kappa * sech * sech
}
pub fn speed(&self) -> f64 {
self.kappa * self.kappa
}
pub fn amplitude(&self) -> f64 {
-0.5 * self.kappa * self.kappa
}
pub fn eval_grid(&self, xs: &[f64], t: f64) -> Vec<f64> {
xs.iter().map(|&x| self.eval(x, t)).collect()
}
}
#[derive(Debug, Clone)]
pub struct HamiltonianSystem {
pub q: Vec<f64>,
pub p: Vec<f64>,
pub dt: f64,
pub time: f64,
}
impl HamiltonianSystem {
pub fn new(q: Vec<f64>, p: Vec<f64>, dt: f64) -> Self {
assert_eq!(q.len(), p.len(), "q and p must have the same dimension");
Self {
q,
p,
dt,
time: 0.0,
}
}
pub fn step(&mut self, grad_h: &dyn Fn(&[f64], &[f64]) -> (Vec<f64>, Vec<f64>)) {
let integrator = SymplecticIntegrator {
dt: self.dt,
method: IntegratorMethod::SymplecticEuler,
};
let (q_new, p_new) = integrator.step(&self.q, &self.p, grad_h);
self.q = q_new;
self.p = p_new;
self.time += self.dt;
}
pub fn run(
&mut self,
n: usize,
grad_h: &dyn Fn(&[f64], &[f64]) -> (Vec<f64>, Vec<f64>),
) -> (Vec<Vec<f64>>, Vec<Vec<f64>>) {
let mut qs = Vec::with_capacity(n);
let mut ps = Vec::with_capacity(n);
for _ in 0..n {
self.step(grad_h);
qs.push(self.q.clone());
ps.push(self.p.clone());
}
(qs, ps)
}
pub fn kinetic_energy(&self, masses: &[f64]) -> f64 {
self.p
.iter()
.zip(masses.iter())
.map(|(&pi, &mi)| pi * pi / (2.0 * mi))
.sum()
}
}
#[derive(Debug, Clone)]
pub struct ClassicalParticle {
pub mass: f64,
pub position: Vec<f64>,
pub velocity: Vec<f64>,
}
impl ClassicalParticle {
pub fn new(mass: f64, dim: usize) -> Self {
Self {
mass,
position: vec![0.0; dim],
velocity: vec![0.0; dim],
}
}
pub fn kinetic_energy(&self) -> f64 {
let v_sq: f64 = self.velocity.iter().map(|&vi| vi * vi).sum();
0.5 * self.mass * v_sq
}
pub fn set_position(&mut self, pos: Vec<f64>) {
self.position = pos;
}
pub fn set_velocity(&mut self, vel: Vec<f64>) {
self.velocity = vel;
}
pub fn momentum(&self) -> Vec<f64> {
self.velocity.iter().map(|&vi| self.mass * vi).collect()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct PathIntegralMonteCarlo {
pub n_slices: usize,
pub tau: f64,
pub mass: f64,
pub path: Vec<f64>,
}
#[allow(dead_code)]
impl PathIntegralMonteCarlo {
pub fn new(n_slices: usize, beta: f64, mass: f64) -> Self {
Self {
n_slices,
tau: beta / (n_slices as f64),
mass,
path: vec![0.0; n_slices],
}
}
pub fn kinetic_action(&self) -> f64 {
let prefactor = self.mass / (2.0 * self.tau);
let n = self.n_slices;
(0..n)
.map(|k| {
let diff = self.path[(k + 1) % n] - self.path[k];
prefactor * diff * diff
})
.sum()
}
pub fn potential_action_harmonic(&self, omega: f64) -> f64 {
self.tau
* self
.path
.iter()
.map(|&x| 0.5 * omega * omega * x * x)
.sum::<f64>()
}
pub fn total_action_harmonic(&self, omega: f64) -> f64 {
self.kinetic_action() + self.potential_action_harmonic(omega)
}
pub fn virial_energy_estimate(&self, omega: f64) -> f64 {
let x_sq: f64 = self.path.iter().map(|&x| x * x).sum::<f64>() / (self.n_slices as f64);
0.5 * omega * omega * x_sq
}
pub fn set_path(&mut self, path: Vec<f64>) {
assert_eq!(path.len(), self.n_slices);
self.path = path;
}
pub fn winding_number(&self) -> f64 {
let first = self.path[0];
let last = self.path[self.n_slices - 1];
(last - first) / (2.0 * std::f64::consts::PI)
}
}
#[derive(Debug, Clone)]
pub struct SchrodingerPropagator {
pub n_grid: usize,
pub dx: f64,
pub mass: f64,
pub dt: f64,
pub potential: Vec<f64>,
}
impl SchrodingerPropagator {
pub fn new(n_grid: usize, dx: f64, mass: f64, dt: f64, potential: Vec<f64>) -> Self {
assert_eq!(
potential.len(),
n_grid,
"potential length must equal n_grid"
);
Self {
n_grid,
dx,
mass,
dt,
potential,
}
}
pub fn kinetic_half_step(&self, psi_re: &[f64], psi_im: &[f64]) -> (Vec<f64>, Vec<f64>) {
let coeff = self.dt / (2.0 * self.mass * self.dx * self.dx);
let n = self.n_grid;
let mut re_out = vec![0.0; n];
let mut im_out = vec![0.0; n];
for i in 0..n {
let left = if i == 0 { 0.0 } else { 1.0 };
let right = if i + 1 == n { 0.0 } else { 1.0 };
let lap_re = left * psi_re[i.saturating_sub(1)] - 2.0 * psi_re[i]
+ right * psi_re[(i + 1).min(n - 1)];
let lap_im = left * psi_im[i.saturating_sub(1)] - 2.0 * psi_im[i]
+ right * psi_im[(i + 1).min(n - 1)];
re_out[i] = psi_re[i] - coeff * lap_im;
im_out[i] = psi_im[i] + coeff * lap_re;
}
(re_out, im_out)
}
pub fn potential_half_step(&self, psi_re: &[f64], psi_im: &[f64]) -> (Vec<f64>, Vec<f64>) {
let mut re_out = vec![0.0; self.n_grid];
let mut im_out = vec![0.0; self.n_grid];
for i in 0..self.n_grid {
let phase = -self.potential[i] * self.dt / 2.0;
let (s, c) = phase.sin_cos();
re_out[i] = psi_re[i] * c - psi_im[i] * s;
im_out[i] = psi_re[i] * s + psi_im[i] * c;
}
(re_out, im_out)
}
pub fn step(&self, psi_re: &[f64], psi_im: &[f64]) -> (Vec<f64>, Vec<f64>) {
let (r1, i1) = self.potential_half_step(psi_re, psi_im);
let (r2, i2) = self.kinetic_half_step(&r1, &i1);
self.potential_half_step(&r2, &i2)
}
pub fn norm_squared(&self, psi_re: &[f64], psi_im: &[f64]) -> f64 {
psi_re
.iter()
.zip(psi_im.iter())
.map(|(&r, &i)| (r * r + i * i) * self.dx)
.sum()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FockSpaceVec {
pub occupations: Vec<u64>,
}
impl FockSpaceVec {
pub fn vacuum(n_modes: usize) -> Self {
Self {
occupations: vec![0; n_modes],
}
}
pub fn total_number(&self) -> u64 {
self.occupations.iter().sum()
}
pub fn apply_creation(&self, k: usize) -> Option<(Self, f64)> {
if k >= self.occupations.len() {
return None;
}
let mut new_occ = self.occupations.clone();
let n_k = new_occ[k];
new_occ[k] += 1;
let norm = ((n_k + 1) as f64).sqrt();
Some((
Self {
occupations: new_occ,
},
norm,
))
}
pub fn apply_annihilation(&self, k: usize) -> Option<(Self, f64)> {
if k >= self.occupations.len() || self.occupations[k] == 0 {
return None;
}
let mut new_occ = self.occupations.clone();
let n_k = new_occ[k];
new_occ[k] -= 1;
let norm = (n_k as f64).sqrt();
Some((
Self {
occupations: new_occ,
},
norm,
))
}
pub fn number_op(&self, k: usize) -> u64 {
self.occupations.get(k).copied().unwrap_or(0)
}
pub fn inner_product(&self, other: &FockSpaceVec) -> f64 {
if self.occupations == other.occupations {
1.0
} else {
0.0
}
}
}
#[derive(Debug, Clone)]
pub struct ElectromagneticField {
pub e: Vec<f64>,
pub b: Vec<f64>,
}
impl ElectromagneticField {
pub fn new() -> Self {
Self {
e: vec![0.0; 3],
b: vec![0.0; 3],
}
}
pub fn energy_density(&self) -> f64 {
let e_sq: f64 = self.e.iter().map(|&ei| ei * ei).sum();
let b_sq: f64 = self.b.iter().map(|&bi| bi * bi).sum();
(EPSILON_0 * e_sq + b_sq / MU_0) / 2.0
}
pub fn poynting_vector(&self) -> Vec<f64> {
let cross = cross3(&self.e, &self.b);
cross.into_iter().map(|c| c / MU_0).collect()
}
pub fn lorentz_force(&self, q: f64, v: &[f64]) -> Vec<f64> {
let vxb = cross3(v, &self.b);
(0..3).map(|i| q * (self.e[i] + vxb[i])).collect()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct MirrorSymmetryData {
pub complex_dim: usize,
pub hodge_x: Vec<Vec<u64>>,
pub hodge_y: Vec<Vec<u64>>,
}
#[allow(dead_code)]
impl MirrorSymmetryData {
pub fn new(complex_dim: usize) -> Self {
let n = complex_dim + 1;
Self {
complex_dim,
hodge_x: vec![vec![0; n]; n],
hodge_y: vec![vec![0; n]; n],
}
}
pub fn set_hodge_x(&mut self, p: usize, q: usize, val: u64) {
self.hodge_x[p][q] = val;
}
pub fn set_hodge_y(&mut self, p: usize, q: usize, val: u64) {
self.hodge_y[p][q] = val;
}
pub fn verify_mirror(&self) -> bool {
let n = self.complex_dim;
for p in 0..=n {
for q in 0..=n {
if n < p {
continue;
}
let mirror_p = n - p;
if self.hodge_x[p][q] != self.hodge_y[mirror_p][q] {
return false;
}
}
}
true
}
pub fn euler_characteristic_x(&self) -> i64 {
self.hodge_x
.iter()
.enumerate()
.flat_map(|(p, row)| {
row.iter().enumerate().map(move |(q, &h)| {
if (p + q) % 2 == 0 {
h as i64
} else {
-(h as i64)
}
})
})
.sum()
}
pub fn h11_x(&self) -> u64 {
if self.complex_dim >= 1 {
self.hodge_x[1][1]
} else {
0
}
}
pub fn h11_y(&self) -> u64 {
if self.complex_dim >= 1 {
self.hodge_y[1][1]
} else {
0
}
}
}
#[derive(Debug, Clone)]
pub struct SymplecticIntegrator {
pub dt: f64,
pub method: IntegratorMethod,
}
impl SymplecticIntegrator {
pub fn step(
&self,
q: &[f64],
p: &[f64],
grad_h: &dyn Fn(&[f64], &[f64]) -> (Vec<f64>, Vec<f64>),
) -> (Vec<f64>, Vec<f64>) {
let dt = self.dt;
match self.method {
IntegratorMethod::Euler => {
let (dh_dp, dh_dq) = grad_h(q, p);
let q_new: Vec<f64> = q
.iter()
.zip(&dh_dp)
.map(|(&qi, &di)| qi + dt * di)
.collect();
let p_new: Vec<f64> = p
.iter()
.zip(&dh_dq)
.map(|(&pi, &di)| pi - dt * di)
.collect();
(q_new, p_new)
}
IntegratorMethod::SymplecticEuler => {
let (_, dh_dq) = grad_h(q, p);
let p_new: Vec<f64> = p
.iter()
.zip(&dh_dq)
.map(|(&pi, &di)| pi - dt * di)
.collect();
let (dh_dp_new, _) = grad_h(q, &p_new);
let q_new: Vec<f64> = q
.iter()
.zip(&dh_dp_new)
.map(|(&qi, &di)| qi + dt * di)
.collect();
(q_new, p_new)
}
IntegratorMethod::Leapfrog => {
let (_, dh_dq) = grad_h(q, p);
let p_half: Vec<f64> = p
.iter()
.zip(&dh_dq)
.map(|(&pi, &di)| pi - 0.5 * dt * di)
.collect();
let (dh_dp_half, _) = grad_h(q, &p_half);
let q_new: Vec<f64> = q
.iter()
.zip(&dh_dp_half)
.map(|(&qi, &di)| qi + dt * di)
.collect();
let (_, dh_dq_new) = grad_h(&q_new, &p_half);
let p_new: Vec<f64> = p_half
.iter()
.zip(&dh_dq_new)
.map(|(&pi, &di)| pi - 0.5 * dt * di)
.collect();
(q_new, p_new)
}
IntegratorMethod::RK4 => {
let f = |q: &[f64], p: &[f64]| -> (Vec<f64>, Vec<f64>) {
let (dh_dp, dh_dq) = grad_h(q, p);
let dq: Vec<f64> = dh_dp;
let dp: Vec<f64> = dh_dq.iter().map(|&d| -d).collect();
(dq, dp)
};
let (k1q, k1p) = f(q, p);
let q1: Vec<f64> = q
.iter()
.zip(&k1q)
.map(|(&qi, &ki)| qi + 0.5 * dt * ki)
.collect();
let p1: Vec<f64> = p
.iter()
.zip(&k1p)
.map(|(&pi, &ki)| pi + 0.5 * dt * ki)
.collect();
let (k2q, k2p) = f(&q1, &p1);
let q2: Vec<f64> = q
.iter()
.zip(&k2q)
.map(|(&qi, &ki)| qi + 0.5 * dt * ki)
.collect();
let p2: Vec<f64> = p
.iter()
.zip(&k2p)
.map(|(&pi, &ki)| pi + 0.5 * dt * ki)
.collect();
let (k3q, k3p) = f(&q2, &p2);
let q3: Vec<f64> = q.iter().zip(&k3q).map(|(&qi, &ki)| qi + dt * ki).collect();
let p3: Vec<f64> = p.iter().zip(&k3p).map(|(&pi, &ki)| pi + dt * ki).collect();
let (k4q, k4p) = f(&q3, &p3);
let q_new: Vec<f64> = q
.iter()
.enumerate()
.map(|(i, &qi)| qi + dt / 6.0 * (k1q[i] + 2.0 * k2q[i] + 2.0 * k3q[i] + k4q[i]))
.collect();
let p_new: Vec<f64> = p
.iter()
.enumerate()
.map(|(i, &pi)| pi + dt / 6.0 * (k1p[i] + 2.0 * k2p[i] + 2.0 * k3p[i] + k4p[i]))
.collect();
(q_new, p_new)
}
}
}
}
#[derive(Debug, Clone)]
pub struct GaugeField {
pub size: usize,
pub links: Vec<Vec<f64>>,
pub beta: f64,
}
impl GaugeField {
pub fn new_cold(size: usize, beta: f64) -> Self {
let n = size * size;
Self {
size,
links: vec![vec![0.0; n], vec![0.0; n]],
beta,
}
}
fn site(&self, i: usize, j: usize) -> usize {
(i % self.size) * self.size + (j % self.size)
}
pub fn plaquette_angle(&self, i: usize, j: usize) -> f64 {
let s00 = self.site(i, j);
let s10 = self.site(i + 1, j);
let s01 = self.site(i, j + 1);
self.links[0][s00] + self.links[1][s10] - self.links[0][s01] - self.links[1][s00]
}
pub fn action(&self) -> f64 {
let n = self.size;
let mut s = 0.0;
for i in 0..n {
for j in 0..n {
s += 1.0 - self.plaquette_angle(i, j).cos();
}
}
self.beta * s
}
pub fn polyakov_loop(&self, j: usize) -> f64 {
let angle: f64 = (0..self.size).map(|i| self.links[0][self.site(i, j)]).sum();
angle.cos()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct SupersymmetricOscillator {
pub omega: f64,
}
#[allow(dead_code)]
impl SupersymmetricOscillator {
pub fn new(omega: f64) -> Self {
assert!(omega > 0.0, "omega must be positive");
Self { omega }
}
pub fn energy(&self, n_b: u64, _n_f: u64) -> f64 {
self.omega * (n_b as f64)
}
pub fn degeneracy(&self, n: u64) -> u64 {
if n == 0 {
1
} else {
2
}
}
pub fn witten_index(&self) -> i64 {
1
}
pub fn partition_function(&self, beta: f64, n_max: u64) -> f64 {
(0..=n_max)
.map(|n| {
let e = self.energy(n, 0);
(self.degeneracy(n) as f64) * (-beta * e).exp()
})
.sum()
}
pub fn ground_state_energy(&self) -> f64 {
0.0
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct DonaldsonWitten {
pub manifold_name: String,
pub signature: i64,
pub euler_char: i64,
pub invariants: Vec<(u64, f64)>,
}
#[allow(dead_code)]
impl DonaldsonWitten {
pub fn new(name: impl Into<String>, signature: i64, euler_char: i64) -> Self {
Self {
manifold_name: name.into(),
signature,
euler_char,
invariants: Vec::new(),
}
}
pub fn virtual_dimension(&self, k: u64) -> i64 {
let b2_plus = (self.euler_char + self.signature) / 2;
let b1 = 0i64;
8 * (k as i64) - 3 * (1 + b1 - b2_plus)
}
pub fn add_invariant(&mut self, k: u64, value: f64) {
self.invariants.push((k, value));
}
pub fn get_invariant(&self, k: u64) -> Option<f64> {
self.invariants
.iter()
.find(|&&(kk, _)| kk == k)
.map(|&(_, v)| v)
}
pub fn b2_plus(&self) -> i64 {
(self.euler_char + self.signature) / 2
}
pub fn b2_minus(&self) -> i64 {
(self.euler_char - self.signature) / 2
}
}