renormalization_group/
critical_surface.rs1use crate::flow::FlowEquation;
8use crate::fixed_point::{FixedPoint, analyze_stability};
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13pub enum FleetPhase {
14 Ordered,
16 Disordered,
18 Critical,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct CriticalSurface {
25 pub fixed_point: FixedPoint,
27 pub codimension: usize,
29 pub description: String,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct PhaseResult {
36 pub phase: FleetPhase,
38 pub distance_to_critical: Option<f64>,
40 pub order_parameter: f64,
42}
43
44pub fn determine_phase(
47 flow_eq: &dyn FlowEquation,
48 initial_params: &[f64],
49 order_param_index: usize,
50 n_steps: usize,
51) -> PhaseResult {
52 let mut current = initial_params.to_vec();
53 let _dim = flow_eq.dim();
54
55 for _ in 0..n_steps {
57 current = flow_eq.flow_step(¤t, 0);
58 }
59
60 let magnitude: f64 = current.iter().map(|x| x * x).sum::<f64>().sqrt();
62 let order_param = if order_param_index < current.len() {
63 current[order_param_index]
64 } else {
65 magnitude
66 };
67
68 let phase = if magnitude > 1e10 {
69 FleetPhase::Ordered
70 } else if magnitude < 1e-3 {
71 FleetPhase::Disordered
72 } else {
73 FleetPhase::Critical
74 };
75
76 PhaseResult {
77 phase,
78 distance_to_critical: None,
79 order_parameter: order_param.abs(),
80 }
81}
82
83pub fn compute_critical_surface(
87 flow_eq: &dyn FlowEquation,
88 fixed_point_location: &[f64],
89) -> CriticalSurface {
90 let fp = analyze_stability(flow_eq, fixed_point_location);
91 CriticalSurface {
92 codimension: fp.n_relevant(),
93 fixed_point: fp.clone(),
94 description: format!(
95 "Critical surface with codimension {} at {:?}",
96 fp.n_relevant(),
97 fixed_point_location
98 ),
99 }
100}
101
102pub fn find_critical_point(
105 flow_eq: &dyn FlowEquation,
106 ordered_params: &[f64],
107 disordered_params: &[f64],
108 order_param_index: usize,
109 n_steps: usize,
110 tolerance: f64,
111 max_iter: usize,
112) -> Vec<f64> {
113 let mut lo = ordered_params.to_vec();
114 let mut hi = disordered_params.to_vec();
115 let dim = lo.len();
116
117 for _ in 0..max_iter {
118 let mid: Vec<f64> = (0..dim).map(|i| 0.5 * (lo[i] + hi[i])).collect();
119 let mut hi_diff = 0.0;
120 for i in 0..dim {
121 hi_diff += (hi[i] - lo[i]).powi(2);
122 }
123 if hi_diff.sqrt() < tolerance {
124 return mid;
125 }
126
127 let result = determine_phase(flow_eq, &mid, order_param_index, n_steps);
128 match result.phase {
129 FleetPhase::Ordered => lo = mid,
130 FleetPhase::Disordered => hi = mid,
131 FleetPhase::Critical => return mid,
132 }
133 }
134 (0..dim).map(|i| 0.5 * (lo[i] + hi[i])).collect()
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140 use crate::flow::{GaussianFlow, Ising1DFlow};
141
142 #[test]
143 fn test_disordered_phase_1d_ising() {
144 let flow_eq = Ising1DFlow::new(2.0);
146 let result = determine_phase(&flow_eq, &[0.01, 0.0], 0, 30);
147 assert_eq!(result.phase, FleetPhase::Disordered);
148 }
149
150 #[test]
151 fn test_ordered_direction_gaussian() {
152 let flow_eq = GaussianFlow::new(3, 2.0);
154 let result = determine_phase(&flow_eq, &[100.0, 0.0], 0, 20);
155 assert_eq!(result.phase, FleetPhase::Ordered);
156 }
157
158 #[test]
159 fn test_disordered_direction_gaussian() {
160 let flow_eq = GaussianFlow::new(3, 2.0);
162 let result = determine_phase(&flow_eq, &[0.0, 0.0], 0, 10);
163 assert_eq!(result.phase, FleetPhase::Disordered);
164 }
165
166 #[test]
167 fn test_critical_surface_codimension() {
168 let flow_eq = GaussianFlow::new(5, 2.0);
171 let surface = compute_critical_surface(&flow_eq, &[0.0, 0.0]);
172 assert_eq!(surface.codimension, 1); }
174
175 #[test]
176 fn test_bisection_finds_critical() {
177 let flow_eq = GaussianFlow::new(3, 2.0);
179 let critical = find_critical_point(
180 &flow_eq,
181 &[1.0, 0.0], &[-1.0, 0.0], 0,
184 20,
185 0.01,
186 50,
187 );
188 assert!(critical[0].abs() < 1.0);
192 }
193}