Skip to main content

spin_sim/statistics/
results.rs

1use super::equilibration::EquilCheckpoint;
2
3pub struct ClusterStats {
4    /// FK cluster size histogram per temperature: `hist[s]` = count of size-`s` clusters.
5    pub fk_csd: Vec<Vec<u64>>,
6    /// Overlap cluster size histogram per temperature: `hist[s]` = count of size-`s` clusters.
7    pub overlap_csd: Vec<Vec<u64>>,
8    /// Average relative size of k-th largest blue cluster per temperature.
9    /// Shape: [n_temps][4]. Empty if collect_top_clusters=false.
10    pub top_cluster_sizes: Vec<[f64; 4]>,
11}
12
13pub struct Diagnostics {
14    /// Integrated autocorrelation time τ_int(m²) per temperature.
15    /// Empty if autocorrelation_max_lag is None.
16    pub mags2_tau: Vec<f64>,
17    /// Integrated autocorrelation time τ_int(q²) per temperature.
18    /// Empty if autocorrelation_max_lag is None or n_replicas < 2.
19    pub overlap2_tau: Vec<f64>,
20    /// Equilibration diagnostic checkpoints (energy + link overlap running averages).
21    /// Empty if equilibration_diagnostic is false.
22    pub equil_checkpoints: Vec<EquilCheckpoint>,
23}
24
25/// Per-temperature observables averaged over measurement sweeps and replicas.
26///
27/// All vectors are indexed by temperature index and have length `n_temps`.
28/// Overlap vectors are empty when `n_replicas < 2`.
29pub struct SweepResult {
30    /// ⟨m⟩ — mean magnetization per spin.
31    pub mags: Vec<f64>,
32    /// ⟨m²⟩.
33    pub mags2: Vec<f64>,
34    /// ⟨m⁴⟩.
35    pub mags4: Vec<f64>,
36    /// ⟨E⟩ — mean energy per spin.
37    pub energies: Vec<f64>,
38    /// ⟨E²⟩.
39    pub energies2: Vec<f64>,
40    /// ⟨q⟩ — mean replica overlap.
41    pub overlap: Vec<f64>,
42    /// ⟨q²⟩.
43    pub overlap2: Vec<f64>,
44    /// ⟨q⁴⟩.
45    pub overlap4: Vec<f64>,
46    pub cluster_stats: ClusterStats,
47    pub diagnostics: Diagnostics,
48}
49
50impl SweepResult {
51    /// Average [`SweepResult`]s across disorder realizations.
52    pub fn aggregate(results: &[Self]) -> Self {
53        let n = results.len() as f64;
54        let n_temps = results[0].mags.len();
55        let n_overlap = results[0].overlap.len();
56        let n_fk_csd = results[0].cluster_stats.fk_csd.len();
57        let n_ov_csd = results[0].cluster_stats.overlap_csd.len();
58
59        let fk_len = results[0]
60            .cluster_stats
61            .fk_csd
62            .first()
63            .map_or(0, |v| v.len());
64        let ov_len = results[0]
65            .cluster_stats
66            .overlap_csd
67            .first()
68            .map_or(0, |v| v.len());
69
70        let n_top = results[0].cluster_stats.top_cluster_sizes.len();
71
72        let m2_tau_len = results[0].diagnostics.mags2_tau.len();
73        let q2_tau_len = results[0].diagnostics.overlap2_tau.len();
74        let n_ckpts = results[0].diagnostics.equil_checkpoints.len();
75
76        let mut agg = SweepResult {
77            mags: vec![0.0; n_temps],
78            mags2: vec![0.0; n_temps],
79            mags4: vec![0.0; n_temps],
80            energies: vec![0.0; n_temps],
81            energies2: vec![0.0; n_temps],
82            overlap: vec![0.0; n_overlap],
83            overlap2: vec![0.0; n_overlap],
84            overlap4: vec![0.0; n_overlap],
85            cluster_stats: ClusterStats {
86                fk_csd: (0..n_fk_csd).map(|_| vec![0u64; fk_len]).collect(),
87                overlap_csd: (0..n_ov_csd).map(|_| vec![0u64; ov_len]).collect(),
88                top_cluster_sizes: vec![[0.0; 4]; n_top],
89            },
90            diagnostics: Diagnostics {
91                mags2_tau: vec![0.0; m2_tau_len],
92                overlap2_tau: vec![0.0; q2_tau_len],
93                equil_checkpoints: (0..n_ckpts)
94                    .map(|i| EquilCheckpoint {
95                        sweep: results[0].diagnostics.equil_checkpoints[i].sweep,
96                        energy_avg: vec![0.0; n_temps],
97                        link_overlap_avg: vec![0.0; n_temps],
98                    })
99                    .collect(),
100            },
101        };
102
103        for r in results {
104            for (a, &v) in agg.mags.iter_mut().zip(r.mags.iter()) {
105                *a += v;
106            }
107            for (a, &v) in agg.mags2.iter_mut().zip(r.mags2.iter()) {
108                *a += v;
109            }
110            for (a, &v) in agg.mags4.iter_mut().zip(r.mags4.iter()) {
111                *a += v;
112            }
113            for (a, &v) in agg.energies.iter_mut().zip(r.energies.iter()) {
114                *a += v;
115            }
116            for (a, &v) in agg.energies2.iter_mut().zip(r.energies2.iter()) {
117                *a += v;
118            }
119            for (a, &v) in agg.overlap.iter_mut().zip(r.overlap.iter()) {
120                *a += v;
121            }
122            for (a, &v) in agg.overlap2.iter_mut().zip(r.overlap2.iter()) {
123                *a += v;
124            }
125            for (a, &v) in agg.overlap4.iter_mut().zip(r.overlap4.iter()) {
126                *a += v;
127            }
128            for (a, s) in agg
129                .cluster_stats
130                .fk_csd
131                .iter_mut()
132                .zip(r.cluster_stats.fk_csd.iter())
133            {
134                for (ah, &sh) in a.iter_mut().zip(s.iter()) {
135                    *ah += sh;
136                }
137            }
138            for (a, s) in agg
139                .cluster_stats
140                .overlap_csd
141                .iter_mut()
142                .zip(r.cluster_stats.overlap_csd.iter())
143            {
144                for (ah, &sh) in a.iter_mut().zip(s.iter()) {
145                    *ah += sh;
146                }
147            }
148            for (a, &s) in agg
149                .cluster_stats
150                .top_cluster_sizes
151                .iter_mut()
152                .zip(r.cluster_stats.top_cluster_sizes.iter())
153            {
154                for k in 0..4 {
155                    a[k] += s[k];
156                }
157            }
158            for (a, &v) in agg
159                .diagnostics
160                .mags2_tau
161                .iter_mut()
162                .zip(r.diagnostics.mags2_tau.iter())
163            {
164                *a += v;
165            }
166            for (a, &v) in agg
167                .diagnostics
168                .overlap2_tau
169                .iter_mut()
170                .zip(r.diagnostics.overlap2_tau.iter())
171            {
172                *a += v;
173            }
174            for (ac, rc) in agg
175                .diagnostics
176                .equil_checkpoints
177                .iter_mut()
178                .zip(r.diagnostics.equil_checkpoints.iter())
179            {
180                for (a, &v) in ac.energy_avg.iter_mut().zip(rc.energy_avg.iter()) {
181                    *a += v;
182                }
183                for (a, &v) in ac
184                    .link_overlap_avg
185                    .iter_mut()
186                    .zip(rc.link_overlap_avg.iter())
187                {
188                    *a += v;
189                }
190            }
191        }
192
193        for v in agg
194            .mags
195            .iter_mut()
196            .chain(agg.mags2.iter_mut())
197            .chain(agg.mags4.iter_mut())
198            .chain(agg.energies.iter_mut())
199            .chain(agg.energies2.iter_mut())
200            .chain(agg.overlap.iter_mut())
201            .chain(agg.overlap2.iter_mut())
202            .chain(agg.overlap4.iter_mut())
203        {
204            *v /= n;
205        }
206
207        for arr in agg.cluster_stats.top_cluster_sizes.iter_mut() {
208            for v in arr.iter_mut() {
209                *v /= n;
210            }
211        }
212
213        for v in agg.diagnostics.mags2_tau.iter_mut() {
214            *v /= n;
215        }
216        for v in agg.diagnostics.overlap2_tau.iter_mut() {
217            *v /= n;
218        }
219        for ckpt in agg.diagnostics.equil_checkpoints.iter_mut() {
220            for v in ckpt.energy_avg.iter_mut() {
221                *v /= n;
222            }
223            for v in ckpt.link_overlap_avg.iter_mut() {
224                *v /= n;
225            }
226        }
227
228        agg
229    }
230}