Skip to main content

ModelRealization

Struct ModelRealization 

Source
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>

Source

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?
examples/bench.rs (lines 93-95)
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}
Source

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.

Source

pub fn validate( &self, lattice: &Lattice, n_replicas: usize, n_temps: usize, ) -> Result<(), String>

Validate externally mutable buffers before entering parallel kernels.

Source

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.

Source

pub fn pt_edge_acceptances(&self) -> &[u64]

Source

pub fn pt_round_trips(&self) -> &[u64]

Source

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.

Source

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>
where Vec<<S as Spin>::Value>: Freeze, Vec<S>: Freeze,

§

impl<S> RefUnwindSafe for ModelRealization<S>

§

impl<S> Send for ModelRealization<S>
where Vec<<S as Spin>::Value>: Send, Vec<S>: Send,

§

impl<S> Sync for ModelRealization<S>
where Vec<<S as Spin>::Value>: Sync, Vec<S>: Sync,

§

impl<S> Unpin for ModelRealization<S>
where Vec<<S as Spin>::Value>: Unpin, Vec<S>: Unpin,

§

impl<S> UnsafeUnpin for ModelRealization<S>
where Vec<<S as Spin>::Value>: UnsafeUnpin, Vec<S>: UnsafeUnpin,

§

impl<S> UnwindSafe for ModelRealization<S>
where Vec<<S as Spin>::Value>: UnwindSafe, Vec<S>: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V