1use crate::lattice::Lattice;
2use crate::stats::Statistics;
3use crate::{clusters, energy, sweep, tempering};
4use rand::{Rng, SeedableRng};
5use rand_xoshiro::Xoshiro256StarStar;
6
7pub struct Realization {
17 pub couplings: Vec<f32>,
19 pub spins: Vec<i8>,
21 pub temperatures: Vec<f32>,
23 pub system_ids: Vec<usize>,
26 pub rngs: Vec<Xoshiro256StarStar>,
28 pub energies: Vec<f32>,
30 pub interactions: Vec<f32>,
32}
33
34impl Realization {
35 pub fn new(
39 lattice: &Lattice,
40 couplings: Vec<f32>,
41 temps: &[f32],
42 n_replicas: usize,
43 base_seed: u64,
44 ) -> Self {
45 let n_spins = lattice.n_spins;
46 let n_temps = temps.len();
47 let n_systems = n_replicas * n_temps;
48
49 let temperatures = temps.repeat(n_replicas);
50
51 let mut rngs = Vec::with_capacity(n_systems);
52 for i in 0..n_systems {
53 rngs.push(Xoshiro256StarStar::seed_from_u64(base_seed + i as u64));
54 }
55
56 let mut spins = vec![0i8; n_systems * n_spins];
57 for (i, rng) in rngs.iter_mut().enumerate() {
58 for j in 0..n_spins {
59 spins[i * n_spins + j] = if rng.gen::<f32>() < 0.5 { -1 } else { 1 };
60 }
61 }
62
63 let system_ids: Vec<usize> = (0..n_systems).collect();
64
65 let (energies, interactions) =
66 energy::compute_energies(lattice, &spins, &couplings, n_systems, true);
67 let interactions = interactions.unwrap();
68
69 Self {
70 couplings,
71 spins,
72 temperatures,
73 system_ids,
74 rngs,
75 energies,
76 interactions,
77 }
78 }
79
80 pub fn reset(&mut self, lattice: &Lattice, n_replicas: usize, n_temps: usize, base_seed: u64) {
82 let n_spins = lattice.n_spins;
83 let n_systems = n_replicas * n_temps;
84
85 for i in 0..n_systems {
86 self.rngs[i] = Xoshiro256StarStar::seed_from_u64(base_seed + i as u64);
87 for j in 0..n_spins {
88 self.spins[i * n_spins + j] = if self.rngs[i].gen::<f32>() < 0.5 {
89 -1
90 } else {
91 1
92 };
93 }
94 }
95
96 self.system_ids = (0..n_systems).collect();
97
98 let (energies, interactions) =
99 energy::compute_energies(lattice, &self.spins, &self.couplings, n_systems, true);
100 self.energies = energies;
101 self.interactions = interactions.unwrap();
102 }
103}
104
105pub struct SweepResult {
110 pub mags: Vec<f64>,
112 pub mags2: Vec<f64>,
114 pub mags4: Vec<f64>,
116 pub energies: Vec<f64>,
118 pub energies2: Vec<f64>,
120 pub overlap: Vec<f64>,
122 pub overlap2: Vec<f64>,
124 pub overlap4: Vec<f64>,
126}
127
128#[allow(clippy::too_many_arguments)]
140pub fn run_sweep_loop(
141 lattice: &Lattice,
142 real: &mut Realization,
143 n_replicas: usize,
144 n_temps: usize,
145 n_sweeps: usize,
146 warmup_sweeps: usize,
147 sweep_mode: &str,
148 cluster_update_interval: Option<usize>,
149 cluster_mode: &str,
150 pt_interval: Option<usize>,
151 houdayer_interval: Option<usize>,
152 on_sweep: &(dyn Fn() + Sync),
153) -> SweepResult {
154 let n_spins = lattice.n_spins;
155 let n_systems = n_replicas * n_temps;
156
157 let mut mags_stat = Statistics::new(n_temps, 1);
158 let mut mags2_stat = Statistics::new(n_temps, 1);
159 let mut mags4_stat = Statistics::new(n_temps, 1);
160 let mut energies_stat = Statistics::new(n_temps, 1);
161 let mut energies2_stat = Statistics::new(n_temps, 2);
162
163 let n_pairs = n_replicas / 2;
164 let mut overlap_stat = Statistics::new(n_temps, 1);
165 let mut overlap2_stat = Statistics::new(n_temps, 1);
166 let mut overlap4_stat = Statistics::new(n_temps, 1);
167
168 for sweep_id in 0..n_sweeps {
169 on_sweep();
170 let record = sweep_id >= warmup_sweeps;
171
172 match sweep_mode {
173 "metropolis" => sweep::metropolis_sweep(
174 lattice,
175 &mut real.spins,
176 &real.couplings,
177 &real.temperatures,
178 &real.system_ids,
179 &mut real.rngs,
180 ),
181 "gibbs" => sweep::gibbs_sweep(
182 lattice,
183 &mut real.spins,
184 &real.couplings,
185 &real.temperatures,
186 &real.system_ids,
187 &mut real.rngs,
188 ),
189 _ => unreachable!(),
190 }
191
192 let do_cluster = cluster_update_interval.is_some_and(|interval| sweep_id % interval == 0);
193
194 if do_cluster {
195 match cluster_mode {
196 "wolff" => {
197 clusters::wolff_update(
198 lattice,
199 &mut real.spins,
200 &real.couplings,
201 &real.temperatures,
202 &real.system_ids,
203 &mut real.rngs,
204 );
205 (real.energies, _) = energy::compute_energies(
206 lattice,
207 &real.spins,
208 &real.couplings,
209 n_systems,
210 false,
211 );
212 }
213 "sw" => {
214 let (energies, interactions) = energy::compute_energies(
215 lattice,
216 &real.spins,
217 &real.couplings,
218 n_systems,
219 true,
220 );
221 real.energies = energies;
222 real.interactions = interactions.unwrap();
223
224 clusters::sw_update(
225 lattice,
226 &mut real.spins,
227 &real.interactions,
228 &real.temperatures,
229 &real.system_ids,
230 &mut real.rngs,
231 );
232
233 (real.energies, _) = energy::compute_energies(
234 lattice,
235 &real.spins,
236 &real.couplings,
237 n_systems,
238 false,
239 );
240 }
241 _ => unreachable!(),
242 }
243 } else {
244 (real.energies, _) =
245 energy::compute_energies(lattice, &real.spins, &real.couplings, n_systems, false);
246 }
247
248 if record {
249 let mut mags = vec![0.0f32; n_temps];
250 let mut mags2 = vec![0.0f32; n_temps];
251 let mut mags4 = vec![0.0f32; n_temps];
252 let mut energies_ordered = vec![0.0f32; n_temps];
253
254 for r in 0..n_replicas {
255 let offset = r * n_temps;
256 for t in 0..n_temps {
257 let system_id = real.system_ids[offset + t];
258 let spin_base = system_id * n_spins;
259 let mut sum = 0i64;
260 for j in 0..n_spins {
261 sum += real.spins[spin_base + j] as i64;
262 }
263 let mag = sum as f32 / n_spins as f32;
264 let m2 = mag * mag;
265 mags[t] = mag;
266 mags2[t] = m2;
267 mags4[t] = m2 * m2;
268 energies_ordered[t] = real.energies[system_id];
269 }
270
271 mags_stat.update(&mags);
272 mags2_stat.update(&mags2);
273 mags4_stat.update(&mags4);
274 energies_stat.update(&energies_ordered);
275 energies2_stat.update(&energies_ordered);
276 }
277
278 for pair_idx in 0..n_pairs {
279 let r_a = 2 * pair_idx;
280 let r_b = 2 * pair_idx + 1;
281 let mut overlaps = vec![0.0f32; n_temps];
282 let mut overlaps2 = vec![0.0f32; n_temps];
283 let mut overlaps4 = vec![0.0f32; n_temps];
284
285 for t in 0..n_temps {
286 let sys_a = real.system_ids[r_a * n_temps + t];
287 let sys_b = real.system_ids[r_b * n_temps + t];
288 let base_a = sys_a * n_spins;
289 let base_b = sys_b * n_spins;
290 let mut dot = 0i64;
291 for j in 0..n_spins {
292 dot += (real.spins[base_a + j] as i64) * (real.spins[base_b + j] as i64);
293 }
294 let q = dot as f32 / n_spins as f32;
295 let q2 = q * q;
296 overlaps[t] = q;
297 overlaps2[t] = q2;
298 overlaps4[t] = q2 * q2;
299 }
300
301 overlap_stat.update(&overlaps);
302 overlap2_stat.update(&overlaps2);
303 overlap4_stat.update(&overlaps4);
304 }
305 }
306
307 if let Some(interval) = houdayer_interval {
308 if sweep_id % interval == 0 && n_replicas >= 2 {
309 clusters::houdayer_update(
310 lattice,
311 &mut real.spins,
312 &real.system_ids,
313 n_replicas,
314 n_temps,
315 &mut real.rngs[0],
316 );
317 (real.energies, _) = energy::compute_energies(
318 lattice,
319 &real.spins,
320 &real.couplings,
321 n_systems,
322 false,
323 );
324 }
325 }
326
327 if let Some(interval) = pt_interval {
328 if sweep_id % interval == 0 {
329 for r in 0..n_replicas {
330 let offset = r * n_temps;
331 let sid_slice = &mut real.system_ids[offset..offset + n_temps];
332 let temp_slice = &real.temperatures[offset..offset + n_temps];
333 tempering::parallel_tempering(
334 &real.energies,
335 temp_slice,
336 sid_slice,
337 n_spins,
338 &mut real.rngs[offset],
339 );
340 }
341 }
342 }
343 }
344
345 SweepResult {
346 mags: mags_stat.average(),
347 mags2: mags2_stat.average(),
348 mags4: mags4_stat.average(),
349 energies: energies_stat.average(),
350 energies2: energies2_stat.average(),
351 overlap: if n_pairs > 0 {
352 overlap_stat.average()
353 } else {
354 vec![]
355 },
356 overlap2: if n_pairs > 0 {
357 overlap2_stat.average()
358 } else {
359 vec![]
360 },
361 overlap4: if n_pairs > 0 {
362 overlap4_stat.average()
363 } else {
364 vec![]
365 },
366 }
367}
368
369pub fn aggregate_results(results: &[SweepResult]) -> SweepResult {
371 let n = results.len() as f64;
372 let n_temps = results[0].mags.len();
373 let n_overlap = results[0].overlap.len();
374
375 let mut agg = SweepResult {
376 mags: vec![0.0; n_temps],
377 mags2: vec![0.0; n_temps],
378 mags4: vec![0.0; n_temps],
379 energies: vec![0.0; n_temps],
380 energies2: vec![0.0; n_temps],
381 overlap: vec![0.0; n_overlap],
382 overlap2: vec![0.0; n_overlap],
383 overlap4: vec![0.0; n_overlap],
384 };
385
386 for r in results {
387 for (a, &v) in agg.mags.iter_mut().zip(r.mags.iter()) {
388 *a += v;
389 }
390 for (a, &v) in agg.mags2.iter_mut().zip(r.mags2.iter()) {
391 *a += v;
392 }
393 for (a, &v) in agg.mags4.iter_mut().zip(r.mags4.iter()) {
394 *a += v;
395 }
396 for (a, &v) in agg.energies.iter_mut().zip(r.energies.iter()) {
397 *a += v;
398 }
399 for (a, &v) in agg.energies2.iter_mut().zip(r.energies2.iter()) {
400 *a += v;
401 }
402 for (a, &v) in agg.overlap.iter_mut().zip(r.overlap.iter()) {
403 *a += v;
404 }
405 for (a, &v) in agg.overlap2.iter_mut().zip(r.overlap2.iter()) {
406 *a += v;
407 }
408 for (a, &v) in agg.overlap4.iter_mut().zip(r.overlap4.iter()) {
409 *a += v;
410 }
411 }
412
413 for v in agg
414 .mags
415 .iter_mut()
416 .chain(agg.mags2.iter_mut())
417 .chain(agg.mags4.iter_mut())
418 .chain(agg.energies.iter_mut())
419 .chain(agg.energies2.iter_mut())
420 .chain(agg.overlap.iter_mut())
421 .chain(agg.overlap2.iter_mut())
422 .chain(agg.overlap4.iter_mut())
423 {
424 *v /= n;
425 }
426
427 agg
428}