use std::sync::Arc;
use serde::Deserialize;
use serde::Serialize;
use crate::symbolic::calculus::differentiate;
use crate::symbolic::calculus::substitute;
use crate::symbolic::core::Expr;
use crate::symbolic::simplify_dag::simplify;
use crate::symbolic::solve::solve;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct IteratedFunctionSystem {
pub functions: Vec<Expr>,
pub probabilities: Vec<Expr>,
pub variables: Vec<String>,
}
impl IteratedFunctionSystem {
#[must_use]
pub const fn new(
functions: Vec<Expr>,
probabilities: Vec<Expr>,
variables: Vec<String>,
) -> Self {
Self {
functions,
probabilities,
variables,
}
}
#[must_use]
pub fn apply(
&self,
point: &[Expr],
) -> Vec<Vec<Expr>> {
if point.len() != self.variables.len() {
return vec![];
}
let mut results = Vec::new();
for func in &self.functions {
let mut new_point = Vec::new();
if let Expr::Vector(coords) = func {
for coord_expr in coords {
let mut substituted = coord_expr.clone();
for (i, var) in self.variables.iter().enumerate() {
substituted = substitute(&substituted, var, &point[i]);
}
new_point.push(simplify(&substituted));
}
} else {
let mut substituted = func.clone();
for (i, var) in self.variables.iter().enumerate() {
substituted = substitute(&substituted, var, &point[i]);
}
new_point.push(simplify(&substituted));
}
results.push(new_point);
}
results
}
#[must_use]
pub fn similarity_dimension(scaling_factors: &[Expr]) -> Expr {
let first = &scaling_factors[0];
let all_same = scaling_factors.iter().all(|r| r == first);
if all_same {
let n = Expr::Constant(scaling_factors.len() as f64);
let r = first.clone();
let num = Expr::new_neg(Expr::new_log(n));
let den = Expr::new_log(r);
simplify(&Expr::new_div(num, den))
} else {
let d = Expr::Variable("D".to_string());
let mut sum = Expr::Constant(0.0);
for r in scaling_factors {
sum = Expr::new_add(sum, Expr::new_pow(r.clone(), d.clone()));
}
Expr::Eq(Arc::new(sum), Arc::new(Expr::Constant(1.0)))
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ComplexDynamicalSystem {
pub function: Expr,
pub c: Expr,
}
impl ComplexDynamicalSystem {
#[must_use]
pub fn new_mandelbrot_family(c: Expr) -> Self {
let z = Expr::Variable("z".to_string());
let f = Expr::new_pow(z, Expr::Constant(2.0));
Self { function: f, c }
}
#[must_use]
pub fn iterate(
&self,
z_n: &Expr,
) -> Expr {
let f_z = substitute(&self.function, "z", z_n);
simplify(&Expr::new_add(f_z, self.c.clone()))
}
#[must_use]
pub fn orbit(
&self,
start_z: Expr,
n: usize,
) -> Vec<Expr> {
let mut orbit = Vec::with_capacity(n + 1);
orbit.push(start_z.clone());
let mut current = start_z;
for _ in 0..n {
current = self.iterate(¤t);
orbit.push(current.clone());
}
orbit
}
#[must_use]
pub fn fixed_points(&self) -> Vec<Expr> {
let z = Expr::Variable("z".to_string());
let rhs = Expr::new_add(self.function.clone(), self.c.clone());
let eq = Expr::new_sub(rhs, z);
solve(&eq, "z")
}
#[must_use]
pub fn stability_index(
&self,
fixed_point: &Expr,
) -> Expr {
let map = Expr::new_add(self.function.clone(), self.c.clone());
let deriv = differentiate(&map, "z");
let val = substitute(&deriv, "z", fixed_point);
simplify(&Expr::new_abs(val))
}
}
#[must_use]
pub fn find_fixed_points(
map_function: &Expr,
var: &str,
) -> Vec<Expr> {
let x = Expr::Variable(var.to_string());
let eq = Expr::new_sub(map_function.clone(), x);
solve(&eq, var)
}
#[must_use]
pub fn analyze_stability(
map_function: &Expr,
var: &str,
fixed_point: &Expr,
) -> Expr {
let deriv = differentiate(map_function, var);
let val = substitute(&deriv, var, fixed_point);
simplify(&val)
}
#[must_use]
pub fn lyapunov_exponent(
map_function: &Expr,
var: &str,
initial_x: &Expr,
n_iterations: usize,
) -> Expr {
let mut current_x = initial_x.clone();
let mut sum_log_derivs = Expr::Constant(0.0);
let deriv_func = differentiate(map_function, var);
for _ in 0..n_iterations {
let deriv_val = substitute(&deriv_func, var, ¤t_x);
let log_abs = Expr::new_log(Expr::new_abs(deriv_val));
sum_log_derivs = Expr::new_add(sum_log_derivs, log_abs);
current_x = substitute(map_function, var, ¤t_x);
current_x = simplify(¤t_x);
sum_log_derivs = simplify(&sum_log_derivs);
}
let n = Expr::Constant(n_iterations as f64);
simplify(&Expr::new_div(sum_log_derivs, n))
}
#[must_use]
pub fn lorenz_system() -> (Expr, Expr, Expr) {
let x = Expr::Variable("x".to_string());
let y = Expr::Variable("y".to_string());
let z = Expr::Variable("z".to_string());
let sigma = Expr::Variable("sigma".to_string());
let rho = Expr::Variable("rho".to_string());
let beta = Expr::Variable("beta".to_string());
let dx = Expr::new_mul(sigma, Expr::new_sub(y.clone(), x.clone()));
let dy_term1 = Expr::new_mul(x.clone(), Expr::new_sub(rho, z.clone()));
let dy = Expr::new_sub(dy_term1, y.clone());
let dz = Expr::new_sub(Expr::new_mul(x, y), Expr::new_mul(beta, z));
(dx, dy, dz)
}