Skip to main content

spin_sim/simulation/
realization.rs

1use crate::geometry::Lattice;
2use crate::spins;
3use rand::{Rng, SeedableRng};
4use rand_xoshiro::Xoshiro256StarStar;
5
6/// Mutable state for one disorder realization.
7///
8/// Holds the coupling array (fixed after construction), spin configurations for
9/// every replica at every temperature, and bookkeeping for parallel tempering.
10///
11/// With `n_replicas` replicas and `n_temps` temperatures there are
12/// `n_systems = n_replicas * n_temps` independent spin configurations.
13/// Spins are stored in a single flat `Vec` of length `n_systems * n_spins`,
14/// where system `i` occupies `spins[i*n_spins .. (i+1)*n_spins]`.
15pub struct Realization {
16    /// Forward couplings, length `n_spins * n_neighbors`.
17    pub couplings: Vec<f32>,
18    /// All spin configurations, length `n_systems * n_spins` (+1/−1).
19    pub spins: Vec<i8>,
20    /// Temperature assigned to each system slot, length `n_systems`.
21    pub temperatures: Vec<f32>,
22    /// Parallel-tempering permutation: `system_ids[slot]` is the system index
23    /// currently occupying temperature slot `slot`.
24    pub system_ids: Vec<usize>,
25    /// One PRNG per system.
26    pub rngs: Vec<Xoshiro256StarStar>,
27    /// One PRNG per overlap-update pair slot, length `n_temps * (n_replicas / 2)`.
28    pub pair_rngs: Vec<Xoshiro256StarStar>,
29    /// Cached total energy per system (E / N), length `n_systems`.
30    pub energies: Vec<f32>,
31}
32
33impl Realization {
34    /// Initialize a realization with random ±1 spins.
35    ///
36    /// Seeds replica RNGs deterministically as `base_seed, base_seed+1, …`.
37    pub fn new(
38        lattice: &Lattice,
39        couplings: Vec<f32>,
40        temps: &[f32],
41        n_replicas: usize,
42        base_seed: u64,
43    ) -> Self {
44        let n_spins = lattice.n_spins;
45        let n_temps = temps.len();
46        let n_systems = n_replicas * n_temps;
47
48        let temperatures = temps.repeat(n_replicas);
49
50        let mut rngs = Vec::with_capacity(n_systems);
51        for i in 0..n_systems {
52            rngs.push(Xoshiro256StarStar::seed_from_u64(base_seed + i as u64));
53        }
54
55        let mut spins = vec![0i8; n_systems * n_spins];
56        for (i, rng) in rngs.iter_mut().enumerate() {
57            for j in 0..n_spins {
58                spins[i * n_spins + j] = if rng.gen::<f32>() < 0.5 { -1 } else { 1 };
59            }
60        }
61
62        let system_ids: Vec<usize> = (0..n_systems).collect();
63
64        let n_pairs = n_replicas / 2;
65        let mut pair_rngs = Vec::with_capacity(n_temps * n_pairs);
66        for i in 0..n_temps * n_pairs {
67            pair_rngs.push(Xoshiro256StarStar::seed_from_u64(
68                base_seed + n_systems as u64 + i as u64,
69            ));
70        }
71
72        let (energies, _) =
73            spins::energy::compute_energies(lattice, &spins, &couplings, n_systems, false);
74
75        Self {
76            couplings,
77            spins,
78            temperatures,
79            system_ids,
80            rngs,
81            pair_rngs,
82            energies,
83        }
84    }
85
86    /// Re-randomize all spins and reset the tempering permutation.
87    pub fn reset(&mut self, lattice: &Lattice, n_replicas: usize, n_temps: usize, base_seed: u64) {
88        let n_spins = lattice.n_spins;
89        let n_systems = n_replicas * n_temps;
90
91        for i in 0..n_systems {
92            self.rngs[i] = Xoshiro256StarStar::seed_from_u64(base_seed + i as u64);
93            for j in 0..n_spins {
94                self.spins[i * n_spins + j] = if self.rngs[i].gen::<f32>() < 0.5 {
95                    -1
96                } else {
97                    1
98                };
99            }
100        }
101
102        self.system_ids = (0..n_systems).collect();
103
104        let n_pairs = n_replicas / 2;
105        for i in 0..n_temps * n_pairs {
106            self.pair_rngs[i] =
107                Xoshiro256StarStar::seed_from_u64(base_seed + n_systems as u64 + i as u64);
108        }
109
110        let (energies, _) = spins::energy::compute_energies(
111            lattice,
112            &self.spins,
113            &self.couplings,
114            n_systems,
115            false,
116        );
117        self.energies = energies;
118    }
119}