noether_guard/
renormalize.rs1#[derive(Debug, Clone)]
4pub struct RenormalizationGroup<'a> {
5 data: &'a [(f64, f64)],
6}
7
8impl<'a> RenormalizationGroup<'a> {
9 pub fn new(data: &'a [(f64, f64)]) -> Self {
11 Self { data }
12 }
13
14 pub fn coarse_grain(&self, block_size: usize) -> Vec<(f64, f64)> {
16 if block_size == 0 || self.data.is_empty() {
17 return Vec::new();
18 }
19 self.data
20 .chunks(block_size)
21 .map(|chunk| {
22 let n = chunk.len() as f64;
23 let avg_t: f64 = chunk.iter().map(|(t, _)| t).sum::<f64>() / n;
24 let avg_d: f64 = chunk.iter().map(|(_, d)| d).sum::<f64>() / n;
25 (avg_t, avg_d)
26 })
27 .collect()
28 }
29
30 pub fn find_breaking_scale(&self) -> Option<f64> {
33 if self.data.len() < 4 {
34 return None;
35 }
36
37 for block_log in 0.. {
39 let block_size = 1usize << block_log;
40 if block_size >= self.data.len() {
41 break;
42 }
43 let coarse = self.coarse_grain(block_size);
44 if coarse.len() < 3 {
45 continue;
46 }
47 let monotonic = coarse
49 .windows(2)
50 .all(|w| w[1].1 >= w[0].1 - f64::EPSILON);
51 let exceeds = coarse.iter().any(|(_, d)| *d > 0.0);
52
53 if monotonic && exceeds {
54 if let (Some(first), Some(last)) = (coarse.first(), coarse.last()) {
56 let span = last.0 - first.0;
57 return Some(span / coarse.len() as f64);
58 }
59 }
60 }
61 None
62 }
63
64 pub fn beta_function(&self, scales: &[usize]) -> Vec<(usize, f64)> {
66 scales
67 .iter()
68 .map(|&block_size| {
69 let coarse = self.coarse_grain(block_size);
70 if coarse.len() < 2 {
71 return (block_size, 0.0);
72 }
73 let total_drift: f64 = coarse.iter().map(|(_, d)| d).sum();
75 let avg_drift = total_drift / coarse.len() as f64;
76 (block_size, avg_drift)
77 })
78 .collect()
79 }
80}