use crate::flow::FlowEquation;
use crate::fixed_point::{FixedPoint, analyze_stability};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum FleetPhase {
Ordered,
Disordered,
Critical,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CriticalSurface {
pub fixed_point: FixedPoint,
pub codimension: usize,
pub description: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PhaseResult {
pub phase: FleetPhase,
pub distance_to_critical: Option<f64>,
pub order_parameter: f64,
}
pub fn determine_phase(
flow_eq: &dyn FlowEquation,
initial_params: &[f64],
order_param_index: usize,
n_steps: usize,
) -> PhaseResult {
let mut current = initial_params.to_vec();
let _dim = flow_eq.dim();
for _ in 0..n_steps {
current = flow_eq.flow_step(¤t, 0);
}
let magnitude: f64 = current.iter().map(|x| x * x).sum::<f64>().sqrt();
let order_param = if order_param_index < current.len() {
current[order_param_index]
} else {
magnitude
};
let phase = if magnitude > 1e10 {
FleetPhase::Ordered
} else if magnitude < 1e-3 {
FleetPhase::Disordered
} else {
FleetPhase::Critical
};
PhaseResult {
phase,
distance_to_critical: None,
order_parameter: order_param.abs(),
}
}
pub fn compute_critical_surface(
flow_eq: &dyn FlowEquation,
fixed_point_location: &[f64],
) -> CriticalSurface {
let fp = analyze_stability(flow_eq, fixed_point_location);
CriticalSurface {
codimension: fp.n_relevant(),
fixed_point: fp.clone(),
description: format!(
"Critical surface with codimension {} at {:?}",
fp.n_relevant(),
fixed_point_location
),
}
}
pub fn find_critical_point(
flow_eq: &dyn FlowEquation,
ordered_params: &[f64],
disordered_params: &[f64],
order_param_index: usize,
n_steps: usize,
tolerance: f64,
max_iter: usize,
) -> Vec<f64> {
let mut lo = ordered_params.to_vec();
let mut hi = disordered_params.to_vec();
let dim = lo.len();
for _ in 0..max_iter {
let mid: Vec<f64> = (0..dim).map(|i| 0.5 * (lo[i] + hi[i])).collect();
let mut hi_diff = 0.0;
for i in 0..dim {
hi_diff += (hi[i] - lo[i]).powi(2);
}
if hi_diff.sqrt() < tolerance {
return mid;
}
let result = determine_phase(flow_eq, &mid, order_param_index, n_steps);
match result.phase {
FleetPhase::Ordered => lo = mid,
FleetPhase::Disordered => hi = mid,
FleetPhase::Critical => return mid,
}
}
(0..dim).map(|i| 0.5 * (lo[i] + hi[i])).collect()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::flow::{GaussianFlow, Ising1DFlow};
#[test]
fn test_disordered_phase_1d_ising() {
let flow_eq = Ising1DFlow::new(2.0);
let result = determine_phase(&flow_eq, &[0.01, 0.0], 0, 30);
assert_eq!(result.phase, FleetPhase::Disordered);
}
#[test]
fn test_ordered_direction_gaussian() {
let flow_eq = GaussianFlow::new(3, 2.0);
let result = determine_phase(&flow_eq, &[100.0, 0.0], 0, 20);
assert_eq!(result.phase, FleetPhase::Ordered);
}
#[test]
fn test_disordered_direction_gaussian() {
let flow_eq = GaussianFlow::new(3, 2.0);
let result = determine_phase(&flow_eq, &[0.0, 0.0], 0, 10);
assert_eq!(result.phase, FleetPhase::Disordered);
}
#[test]
fn test_critical_surface_codimension() {
let flow_eq = GaussianFlow::new(5, 2.0);
let surface = compute_critical_surface(&flow_eq, &[0.0, 0.0]);
assert_eq!(surface.codimension, 1); }
#[test]
fn test_bisection_finds_critical() {
let flow_eq = GaussianFlow::new(3, 2.0);
let critical = find_critical_point(
&flow_eq,
&[1.0, 0.0], &[-1.0, 0.0], 0,
20,
0.01,
50,
);
assert!(critical[0].abs() < 1.0);
}
}