Skip to main content

renormalization_group/
universality.rs

1//! # Universality Classes
2//!
3//! Different fleet topologies can have the same RG fixed point → same
4//! large-scale behavior. This module classifies universality classes
5//! based on critical exponents.
6
7use serde::{Deserialize, Serialize};
8
9/// A single critical exponent.
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct CriticalExponent {
12    /// Name of the exponent (α, β, γ, δ, ν, η)
13    pub name: String,
14    /// Numerical value
15    pub value: f64,
16    /// Description of what this exponent characterizes
17    pub description: String,
18}
19
20/// A universality class defined by its critical exponents.
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct UniversalityClass {
23    /// Name of the universality class
24    pub name: String,
25    /// Critical exponents
26    pub exponents: Vec<CriticalExponent>,
27    /// Spatial dimension
28    pub dimension: usize,
29    /// Number of order parameter components (n)
30    pub n_components: usize,
31    /// Description
32    pub description: String,
33}
34
35impl UniversalityClass {
36    /// Get a critical exponent by name.
37    pub fn exponent(&self, name: &str) -> Option<f64> {
38        self.exponents.iter().find(|e| e.name == name).map(|e| e.value)
39    }
40
41    /// Check if two universality classes are the same by comparing exponents.
42    pub fn is_same_universality_class(&self, other: &UniversalityClass) -> bool {
43        if self.exponents.len() != other.exponents.len() {
44            return false;
45        }
46        for e in &self.exponents {
47            if let Some(other_val) = other.exponent(&e.name) {
48                if (e.value - other_val).abs() > 0.01 {
49                    return false;
50                }
51            } else {
52                return false;
53            }
54        }
55        true
56    }
57}
58
59/// Mean-field universality class (d ≥ 4).
60pub fn mean_field_class() -> UniversalityClass {
61    UniversalityClass {
62        name: "Mean Field".into(),
63        dimension: 4,
64        n_components: 1,
65        description: "Mean-field universality class valid for d ≥ 4".into(),
66        exponents: vec![
67            CriticalExponent {
68                name: "alpha".into(),
69                value: 0.0,
70                description: "Specific heat: C ~ |t|^(-α)".into(),
71            },
72            CriticalExponent {
73                name: "beta".into(),
74                value: 0.5,
75                description: "Order parameter: M ~ |t|^β".into(),
76            },
77            CriticalExponent {
78                name: "gamma".into(),
79                value: 1.0,
80                description: "Susceptibility: χ ~ |t|^(-γ)".into(),
81            },
82            CriticalExponent {
83                name: "delta".into(),
84                value: 3.0,
85                description: "Critical isotherm: M ~ h^(1/δ)".into(),
86            },
87            CriticalExponent {
88                name: "nu".into(),
89                value: 0.5,
90                description: "Correlation length: ξ ~ |t|^(-ν)".into(),
91            },
92            CriticalExponent {
93                name: "eta".into(),
94                value: 0.0,
95                description: "Correlation function decay".into(),
96            },
97        ],
98    }
99}
100
101/// 2D Ising universality class (exact solution).
102pub fn ising_2d_class() -> UniversalityClass {
103    UniversalityClass {
104        name: "2D Ising".into(),
105        dimension: 2,
106        n_components: 1,
107        description: "2D Ising universality class (Onsager solution)".into(),
108        exponents: vec![
109            CriticalExponent {
110                name: "alpha".into(),
111                value: 0.0, // log divergence
112                description: "Specific heat: log divergence".into(),
113            },
114            CriticalExponent {
115                name: "beta".into(),
116                value: 1.0 / 8.0,
117                description: "Order parameter: M ~ |t|^(1/8)".into(),
118            },
119            CriticalExponent {
120                name: "gamma".into(),
121                value: 7.0 / 4.0,
122                description: "Susceptibility: χ ~ |t|^(-7/4)".into(),
123            },
124            CriticalExponent {
125                name: "delta".into(),
126                value: 15.0,
127                description: "Critical isotherm: M ~ h^(1/15)".into(),
128            },
129            CriticalExponent {
130                name: "nu".into(),
131                value: 1.0,
132                description: "Correlation length: ξ ~ |t|^(-1)".into(),
133            },
134            CriticalExponent {
135                name: "eta".into(),
136                value: 0.25,
137                description: "Correlation function decay".into(),
138            },
139        ],
140    }
141}
142
143/// 3D Ising universality class (numerical).
144pub fn ising_3d_class() -> UniversalityClass {
145    UniversalityClass {
146        name: "3D Ising".into(),
147        dimension: 3,
148        n_components: 1,
149        description: "3D Ising universality class (numerical estimates)".into(),
150        exponents: vec![
151            CriticalExponent {
152                name: "alpha".into(),
153                value: 0.110,
154                description: "Specific heat exponent".into(),
155            },
156            CriticalExponent {
157                name: "beta".into(),
158                value: 0.326,
159                description: "Order parameter exponent".into(),
160            },
161            CriticalExponent {
162                name: "gamma".into(),
163                value: 1.237,
164                description: "Susceptibility exponent".into(),
165            },
166            CriticalExponent {
167                name: "delta".into(),
168                value: 4.789,
169                description: "Critical isotherm exponent".into(),
170            },
171            CriticalExponent {
172                name: "nu".into(),
173                value: 0.630,
174                description: "Correlation length exponent".into(),
175            },
176            CriticalExponent {
177                name: "eta".into(),
178                value: 0.036,
179                description: "Anomalous dimension".into(),
180            },
181        ],
182    }
183}
184
185/// XY universality class (n=2, includes superfluid transition).
186pub fn xy_class() -> UniversalityClass {
187    UniversalityClass {
188        name: "3D XY".into(),
189        dimension: 3,
190        n_components: 2,
191        description: "3D XY universality class (superfluid transition)".into(),
192        exponents: vec![
193            CriticalExponent {
194                name: "alpha".into(),
195                value: -0.015,
196                description: "Specific heat exponent".into(),
197            },
198            CriticalExponent {
199                name: "beta".into(),
200                value: 0.349,
201                description: "Order parameter exponent".into(),
202            },
203            CriticalExponent {
204                name: "gamma".into(),
205                value: 1.316,
206                description: "Susceptibility exponent".into(),
207            },
208            CriticalExponent {
209                name: "nu".into(),
210                value: 0.671,
211                description: "Correlation length exponent".into(),
212            },
213            CriticalExponent {
214                name: "eta".into(),
215                value: 0.038,
216                description: "Anomalous dimension".into(),
217            },
218            CriticalExponent {
219                name: "delta".into(),
220                value: 4.77,
221                description: "Critical isotherm exponent".into(),
222            },
223        ],
224    }
225}
226
227/// Heisenberg universality class (n=3).
228pub fn heisenberg_class() -> UniversalityClass {
229    UniversalityClass {
230        name: "3D Heisenberg".into(),
231        dimension: 3,
232        n_components: 3,
233        description: "3D Heisenberg universality class".into(),
234        exponents: vec![
235            CriticalExponent {
236                name: "alpha".into(),
237                value: -0.115,
238                description: "Specific heat exponent".into(),
239            },
240            CriticalExponent {
241                name: "beta".into(),
242                value: 0.366,
243                description: "Order parameter exponent".into(),
244            },
245            CriticalExponent {
246                name: "gamma".into(),
247                value: 1.386,
248                description: "Susceptibility exponent".into(),
249            },
250            CriticalExponent {
251                name: "nu".into(),
252                value: 0.706,
253                description: "Correlation length exponent".into(),
254            },
255            CriticalExponent {
256                name: "eta".into(),
257                value: 0.037,
258                description: "Anomalous dimension".into(),
259            },
260            CriticalExponent {
261                name: "delta".into(),
262                value: 4.78,
263                description: "Critical isotherm exponent".into(),
264            },
265        ],
266    }
267}
268
269/// Verify the Rushbrooke scaling relation: α + 2β + γ = 2
270pub fn verify_rushbrooke(class: &UniversalityClass) -> bool {
271    let alpha = class.exponent("alpha").unwrap_or(0.0);
272    let beta = class.exponent("beta").unwrap_or(0.0);
273    let gamma = class.exponent("gamma").unwrap_or(0.0);
274    (alpha + 2.0 * beta + gamma - 2.0).abs() < 0.05
275}
276
277/// Verify the Widom scaling relation: γ = β(δ - 1)
278pub fn verify_widom(class: &UniversalityClass) -> bool {
279    let gamma = class.exponent("gamma").unwrap_or(0.0);
280    let beta = class.exponent("beta").unwrap_or(0.0);
281    let delta = class.exponent("delta").unwrap_or(0.0);
282    (gamma - beta * (delta - 1.0)).abs() < 0.05
283}
284
285/// Verify the Fisher scaling relation: γ = ν(2 - η)
286pub fn verify_fisher(class: &UniversalityClass) -> bool {
287    let gamma = class.exponent("gamma").unwrap_or(0.0);
288    let nu = class.exponent("nu").unwrap_or(0.0);
289    let eta = class.exponent("eta").unwrap_or(0.0);
290    (gamma - nu * (2.0 - eta)).abs() < 0.05
291}
292
293/// Verify the Josephson scaling relation: 2 - α = dν
294pub fn verify_josephson(class: &UniversalityClass) -> bool {
295    let alpha = class.exponent("alpha").unwrap_or(0.0);
296    let nu = class.exponent("nu").unwrap_or(0.0);
297    (2.0 - alpha - class.dimension as f64 * nu).abs() < 0.05
298}
299
300/// A "microscopic" fleet model that can be classified into a universality class.
301#[derive(Debug, Clone, Serialize, Deserialize)]
302pub struct FleetModel {
303    /// Name of the model
304    pub name: String,
305    /// Parameters
306    pub parameters: Vec<f64>,
307    /// Topology type
308    pub topology: String,
309}
310
311impl FleetModel {
312    pub fn new(name: &str, parameters: Vec<f64>, topology: &str) -> Self {
313        Self {
314            name: name.into(),
315            parameters,
316            topology: topology.into(),
317        }
318    }
319}
320
321#[cfg(test)]
322mod tests {
323    use super::*;
324
325    #[test]
326    fn test_mean_field_exponents() {
327        let mf = mean_field_class();
328        assert!((mf.exponent("alpha").unwrap() - 0.0).abs() < 1e-10);
329        assert!((mf.exponent("beta").unwrap() - 0.5).abs() < 1e-10);
330        assert!((mf.exponent("gamma").unwrap() - 1.0).abs() < 1e-10);
331    }
332
333    #[test]
334    fn test_2d_ising_exact_exponents() {
335        let ising = ising_2d_class();
336        assert!((ising.exponent("beta").unwrap() - 1.0 / 8.0).abs() < 1e-10);
337        assert!((ising.exponent("gamma").unwrap() - 7.0 / 4.0).abs() < 1e-10);
338        assert!((ising.exponent("nu").unwrap() - 1.0).abs() < 1e-10);
339    }
340
341    #[test]
342    fn test_rushbrooke_mean_field() {
343        assert!(verify_rushbrooke(&mean_field_class()));
344    }
345
346    #[test]
347    fn test_rushbrooke_2d_ising() {
348        assert!(verify_rushbrooke(&ising_2d_class()));
349    }
350
351    #[test]
352    fn test_widom_mean_field() {
353        assert!(verify_widom(&mean_field_class()));
354    }
355
356    #[test]
357    fn test_widom_2d_ising() {
358        assert!(verify_widom(&ising_2d_class()));
359    }
360
361    #[test]
362    fn test_fisher_mean_field() {
363        assert!(verify_fisher(&mean_field_class()));
364    }
365
366    #[test]
367    fn test_josephson_mean_field() {
368        assert!(verify_josephson(&mean_field_class()));
369    }
370
371    #[test]
372    fn test_universality_same_class() {
373        // Two different microscopic models with same exponents → same class
374        let class1 = ising_3d_class();
375        let mut class2 = ising_3d_class();
376        class2.name = "Fleet Ising 3D".into();
377        assert!(class1.is_same_universality_class(&class2));
378    }
379
380    #[test]
381    fn test_different_universality_classes() {
382        let ising = ising_3d_class();
383        let xy = xy_class();
384        assert!(!ising.is_same_universality_class(&xy));
385    }
386
387    #[test]
388    fn test_fleet_model_creation() {
389        let model = FleetModel::new("test", vec![1.0, 2.0], "1D chain");
390        assert_eq!(model.name, "test");
391        assert_eq!(model.parameters.len(), 2);
392    }
393}