pub struct ModelRealization<S: Spin> {
pub couplings: Vec<S::Value>,
pub spins: Vec<S>,
pub temperatures: Vec<S::Value>,
pub system_ids: Vec<usize>,
pub rngs: Vec<Xoshiro256StarStar>,
pub pair_rngs: Vec<Xoshiro256StarStar>,
pub energies: Vec<S::Value>,
/* private fields */
}Expand description
Mutable state for one disorder realization.
Holds the coupling array (fixed after construction), spin configurations for every replica at every temperature, and bookkeeping for parallel tempering.
With n_replicas replicas and n_temps temperatures there are
n_systems = n_replicas * n_temps independent spin configurations.
Spins are stored in a single flat Vec of length n_systems * n_spins,
where system i occupies spins[i*n_spins .. (i+1)*n_spins].
Fields§
§couplings: Vec<S::Value>Forward couplings, length n_spins * n_neighbors.
spins: Vec<S>All spin configurations, length n_systems * n_spins (±1 for Ising, unit vectors for XY).
temperatures: Vec<S::Value>Temperature assigned to each system slot, length n_systems.
system_ids: Vec<usize>Parallel-tempering permutation: system_ids[slot] is the system index
currently occupying temperature slot slot.
rngs: Vec<Xoshiro256StarStar>One PRNG per system.
pair_rngs: Vec<Xoshiro256StarStar>One PRNG per overlap-update pair slot, length n_temps * (n_replicas / 2).
energies: Vec<S::Value>Scratch cache of the interaction energy per system (-H/N), length
n_systems. It is current only right after a measurement or tempering step.
Implementations§
Source§impl<S: Spin> ModelRealization<S>
impl<S: Spin> ModelRealization<S>
Sourcepub fn new(
lattice: &Lattice,
couplings: Vec<S::Value>,
temps: &[S::Value],
n_replicas: usize,
base_seed: u64,
) -> Self
pub fn new( lattice: &Lattice, couplings: Vec<S::Value>, temps: &[S::Value], n_replicas: usize, base_seed: u64, ) -> Self
Initialize a realization with random spins (±1, or uniform unit vectors for XY).
Seeds independent system and pair streams from domain-separated child seeds.
Examples found in repository?
30fn main() {
31 let l = env_usize("PEAPODS_L", 128);
32 let n_dims = env_usize("PEAPODS_DIM", 2);
33 let n_temps = env_usize("PEAPODS_TEMPS", 16);
34 let n_replicas = env_usize("PEAPODS_REPLICAS", 2);
35 let n_sweeps = env_usize("PEAPODS_SWEEPS", 50);
36 let n_realizations = env_usize("PEAPODS_NREAL", 100);
37 let sequential = env::var_os("PEAPODS_SEQUENTIAL").is_some();
38 let overlap_cluster_mode = if env::var_os("PEAPODS_OVERLAP_WOLFF").is_some() {
39 ClusterMode::Wolff
40 } else {
41 ClusterMode::Sw
42 };
43 let generic_lattice = env::var_os("PEAPODS_GENERIC_LATTICE").is_some();
44 let mode = env::var("PEAPODS_MODE").unwrap_or_else(|_| "cmr".to_string());
45 let couplings_kind = env::var("PEAPODS_COUPLINGS").unwrap_or_else(|_| "bimodal".to_string());
46 let sweep_mode = match env::var("PEAPODS_SWEEP").as_deref().unwrap_or("metropolis") {
47 "metropolis" => SweepMode::Metropolis,
48 "gibbs" => SweepMode::Gibbs,
49 other => panic!("unknown PEAPODS_SWEEP '{other}'"),
50 };
51 let shape = vec![l; n_dims];
52 let lattice = if generic_lattice {
53 Lattice::with_offsets(shape, hypercubic(n_dims))
54 } else {
55 Lattice::new(shape)
56 };
57 let n_spins = lattice.n_spins;
58 let n_neighbors = lattice.n_neighbors;
59
60 let t_min: f32 = env::var("PEAPODS_TMIN").map_or(0.1, |v| v.parse().unwrap());
61 let t_max: f32 = env::var("PEAPODS_TMAX").map_or(5.0, |v| v.parse().unwrap());
62 let temps: Vec<f32> = (0..n_temps)
63 .map(|i| t_min * (t_max / t_min).powf(i as f32 / (n_temps.max(2) - 1) as f32))
64 .collect();
65
66 let n_pairs = n_replicas / 2;
67 let n_systems = n_replicas * n_temps;
68 let rngs_per_real = n_systems + n_temps * n_pairs;
69
70 let mut rng = Xoshiro256StarStar::seed_from_u64(0x5eed);
71 let mut realizations = Vec::with_capacity(n_realizations);
72 for r in 0..n_realizations {
73 let couplings: Vec<f32> = (0..n_spins * n_neighbors)
74 .map(|_| match couplings_kind.as_str() {
75 "bimodal" => {
76 if rng.gen::<bool>() {
77 1.0
78 } else {
79 -1.0
80 }
81 }
82 "ferro" => 1.0,
83 // Box-Muller keeps the bench free of extra distribution crates.
84 "gaussian" => {
85 let u1: f64 = 1.0 - rng.gen::<f64>();
86 let u2: f64 = rng.gen();
87 ((-2.0 * u1.ln()).sqrt() * (std::f64::consts::TAU * u2).cos()) as f32
88 }
89 other => panic!("unknown PEAPODS_COUPLINGS '{other}'"),
90 })
91 .collect();
92 let base_seed = 42 + (r * rngs_per_real) as u64;
93 realizations.push(Realization::new(
94 &lattice, couplings, &temps, n_replicas, base_seed,
95 ));
96 }
97
98 let interrupted = AtomicBool::new(false);
99 let (cluster_update, pt_interval, overlap_cluster) = match mode.as_str() {
100 "metropolis" => (None, None, None),
101 "pt" => (None, Some(1), None),
102 "sw" => (
103 Some(ClusterConfig {
104 interval: 1,
105 mode: ClusterMode::Sw,
106 action: ClusterAction::Update,
107 collect_stats: false,
108 }),
109 None,
110 None,
111 ),
112 "wolff" => (
113 Some(ClusterConfig {
114 interval: 1,
115 mode: ClusterMode::Wolff,
116 action: ClusterAction::Update,
117 collect_stats: false,
118 }),
119 None,
120 None,
121 ),
122 "cmr" => (
123 None,
124 Some(1),
125 Some(OverlapClusterConfig {
126 interval: 1,
127 modes: vec![OverlapClusterBuildMode::Cmr],
128 cluster_mode: overlap_cluster_mode,
129 action: ClusterAction::Update,
130 collect_stats: false,
131 snapshot_interval: None,
132 max_temperature: None,
133 }),
134 ),
135 "sw_pt" => (
136 Some(ClusterConfig {
137 interval: 1,
138 mode: ClusterMode::Sw,
139 action: ClusterAction::Update,
140 collect_stats: false,
141 }),
142 Some(1),
143 None,
144 ),
145 "houdayer" | "jorg" => (
146 None,
147 Some(1),
148 Some(OverlapClusterConfig {
149 interval: 1,
150 modes: vec![if mode == "jorg" {
151 OverlapClusterBuildMode::Jorg(2)
152 } else {
153 OverlapClusterBuildMode::Houdayer(2)
154 }],
155 cluster_mode: overlap_cluster_mode,
156 action: ClusterAction::Update,
157 collect_stats: false,
158 snapshot_interval: None,
159 max_temperature: None,
160 }),
161 ),
162 // Any other overlap build mode string, e.g. "pair4", "jorg4" or "rmc", with PT.
163 other => (
164 None,
165 Some(1),
166 Some(OverlapClusterConfig {
167 interval: 1,
168 modes: parse_overlap_modes(other)
169 .unwrap_or_else(|err| panic!("unknown PEAPODS_MODE '{other}': {err}")),
170 cluster_mode: overlap_cluster_mode,
171 action: ClusterAction::Update,
172 collect_stats: false,
173 snapshot_interval: None,
174 max_temperature: None,
175 }),
176 ),
177 };
178
179 let config = SimConfig {
180 n_sweeps,
181 warmup_sweeps: 0,
182 sweep_mode,
183 cluster_update,
184 pt_interval,
185 pt_schedule: PtSchedule::SingleRandomEdge,
186 overlap_cluster,
187 autocorrelation_max_lag: None,
188 autocorrelation_backend: AutocorrelationBackend::Ring,
189 sequential,
190 equilibration_diagnostic: false,
191 conditional_link_overlap: true,
192 };
193
194 println!(
195 "Lattice: {l}^{n_dims} | Temps: {n_temps} | Replicas: {n_replicas} | Sweeps: {n_sweeps} | Realizations: {n_realizations}"
196 );
197 println!(
198 "Config: {couplings_kind}, mode={mode}, sequential={sequential}, generic_lattice={generic_lattice}"
199 );
200 println!("{}", "-".repeat(70));
201
202 let t0 = Instant::now();
203 let result = run_sweep_parallel(
204 &lattice,
205 &mut realizations,
206 n_replicas,
207 n_temps,
208 &config,
209 &interrupted,
210 &|| {},
211 )
212 .unwrap();
213 let elapsed = t0.elapsed().as_secs_f64();
214
215 let mut state_hash = DefaultHasher::new();
216 for realization in &realizations {
217 realization.spins.hash(&mut state_hash);
218 realization.system_ids.hash(&mut state_hash);
219 for rng in realization.rngs.iter().chain(&realization.pair_rngs) {
220 let mut rng = rng.clone();
221 rng.next_u64().hash(&mut state_hash);
222 }
223 }
224 for values in [
225 &result.mags,
226 &result.mags2,
227 &result.mags4,
228 &result.energies,
229 &result.energies2,
230 ] {
231 for value in values {
232 value.to_bits().hash(&mut state_hash);
233 }
234 }
235 for values in [
236 &result.overlap_stats.overlap,
237 &result.overlap_stats.overlap2,
238 &result.overlap_stats.overlap4,
239 &result.overlap_stats.link_overlap,
240 &result.overlap_stats.link_overlap2,
241 &result.overlap_stats.link_overlap4,
242 ] {
243 for value in values {
244 value.to_bits().hash(&mut state_hash);
245 }
246 }
247 result.overlap_stats.histogram.hash(&mut state_hash);
248 result
249 .overlap_stats
250 .per_sample_histogram
251 .hash(&mut state_hash);
252 for samples in [
253 &result.overlap_stats.ql_at_q_sum,
254 &result.overlap_stats.ql2_at_q_sum,
255 ] {
256 for bins in samples {
257 for value in bins {
258 value.to_bits().hash(&mut state_hash);
259 }
260 }
261 }
262 for samples in [
263 &result.overlap_stats.per_sample_ql_at_q_sum,
264 &result.overlap_stats.per_sample_ql2_at_q_sum,
265 ] {
266 for temperatures in samples {
267 for bins in temperatures {
268 for value in bins {
269 value.to_bits().hash(&mut state_hash);
270 }
271 }
272 }
273 }
274
275 let per_sweep = elapsed / n_sweeps as f64 * 1000.0;
276 println!("Total: {:.3} s | {:.3} ms/sweep", elapsed, per_sweep);
277 println!("State checksum: {:016x}", state_hash.finish());
278}Sourcepub fn reset(
&mut self,
lattice: &Lattice,
n_replicas: usize,
n_temps: usize,
base_seed: u64,
)
pub fn reset( &mut self, lattice: &Lattice, n_replicas: usize, n_temps: usize, base_seed: u64, )
Re-randomize all spins and reset the tempering permutation.
Sourcepub fn validate(
&self,
lattice: &Lattice,
n_replicas: usize,
n_temps: usize,
) -> Result<(), String>
pub fn validate( &self, lattice: &Lattice, n_replicas: usize, n_temps: usize, ) -> Result<(), String>
Validate externally mutable buffers before entering parallel kernels.
Sourcepub fn pt_edge_attempts(&self) -> &[u64]
pub fn pt_edge_attempts(&self) -> &[u64]
Swap attempts per temperature edge since creation or the last reset. Like the acceptances and round trips, they count warmup sweeps too.
pub fn pt_edge_acceptances(&self) -> &[u64]
pub fn pt_round_trips(&self) -> &[u64]
Sourcepub fn pt_flow_up(&self) -> &[u64]
pub fn pt_flow_up(&self) -> &[u64]
Per temperature slot, visits after each tempering event by systems that last visited the cold end of the ladder.
Sourcepub fn pt_flow_labelled(&self) -> &[u64]
pub fn pt_flow_labelled(&self) -> &[u64]
Per temperature slot, visits by systems that have visited either end; the
denominator of the flow fraction pt_flow_up / pt_flow_labelled.
Auto Trait Implementations§
impl<S> Forget for ModelRealization<S>
impl<S> Freeze for ModelRealization<S>
impl<S> RefUnwindSafe for ModelRealization<S>
impl<S> Send for ModelRealization<S>
impl<S> Sync for ModelRealization<S>
impl<S> Unpin for ModelRealization<S>
impl<S> UnsafeUnpin for ModelRealization<S>
impl<S> UnwindSafe for ModelRealization<S>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more