use rand_xoshiro::Xoshiro256StarStar;
use rayon::prelude::*;
use std::marker::PhantomData;
pub(crate) fn for_each_task(n_tasks: usize, sequential: bool, work: impl Fn(usize) + Send + Sync) {
if sequential {
(0..n_tasks).for_each(work);
return;
}
(0..n_tasks).into_par_iter().for_each(work);
}
pub(crate) struct Slots<'a, T> {
ptr: *mut T,
len: usize,
_slice: PhantomData<&'a mut [T]>,
}
unsafe impl<T: Send> Sync for Slots<'_, T> {}
impl<'a, T> Slots<'a, T> {
pub(crate) fn new(out: Option<&'a mut [T]>) -> Self {
let (ptr, len) = out.map_or((std::ptr::null_mut(), 0), |out| {
(out.as_mut_ptr(), out.len())
});
Self {
ptr,
len,
_slice: PhantomData,
}
}
pub(crate) fn is_some(&self) -> bool {
!self.ptr.is_null()
}
#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn get(&self, slot: usize) -> Option<&mut T> {
if self.ptr.is_null() {
return None;
}
assert!(slot < self.len, "output slot out of range");
Some(&mut *self.ptr.add(slot))
}
#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn at(&self, slot: usize) -> &mut T {
self.get(slot).expect("slot storage is always present")
}
}
pub(crate) struct ReplicaSlot<'a, S, T, O> {
pub spins: &'a mut [S],
pub rng: &'a mut Xoshiro256StarStar,
pub temp: T,
pub temp_id: usize,
pub system_id: usize,
pub output: &'a mut O,
}
pub(crate) fn par_over_replicas<S: Copy + Send + Sync, T: Copy + Send + Sync>(
spins: &mut [S],
rngs: &mut [Xoshiro256StarStar],
temperatures: &[T],
system_ids: &[usize],
n_spins: usize,
sequential: bool,
body: impl Fn(&mut [S], &mut Xoshiro256StarStar, T, usize, usize) + Send + Sync,
) {
let mut outputs = vec![(); rngs.len()];
par_over_replica_groups(
spins,
rngs,
temperatures,
system_ids,
n_spins,
sequential,
1,
&mut outputs,
|slots| {
let slot = &mut slots[0];
body(
slot.spins,
slot.rng,
slot.temp,
slot.temp_id,
slot.system_id,
);
},
);
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn par_over_replica_groups<S: Copy + Send + Sync, T: Copy + Send + Sync, O: Send>(
spins: &mut [S],
rngs: &mut [Xoshiro256StarStar],
temperatures: &[T],
system_ids: &[usize],
n_spins: usize,
sequential: bool,
group: usize,
outputs: &mut [O],
body: impl Fn(&mut [ReplicaSlot<'_, S, T, O>]) + Send + Sync,
) {
let n_systems = rngs.len();
assert!(
group > 0
&& n_spins > 0
&& system_ids.len() <= n_systems
&& temperatures.len() >= system_ids.len()
&& outputs.len() == n_systems
&& spins.len() >= n_systems * n_spins,
"replica buffers disagree with the system count"
);
let mut spin_blocks: Vec<_> = spins
.chunks_exact_mut(n_spins)
.take(n_systems)
.map(Some)
.collect();
let mut rngs: Vec<_> = rngs.iter_mut().map(Some).collect();
let mut outputs: Vec<_> = outputs.iter_mut().map(Some).collect();
let mut slots: Vec<_> = system_ids
.iter()
.enumerate()
.map(|(temp_id, &system_id)| {
assert!(system_id < n_systems, "system id out of range");
let repeated = "system id repeated";
ReplicaSlot {
spins: spin_blocks[system_id].take().expect(repeated),
rng: rngs[system_id].take().expect(repeated),
temp: temperatures[temp_id],
temp_id,
system_id,
output: outputs[system_id].take().expect(repeated),
}
})
.collect();
if sequential {
slots.chunks_mut(group).for_each(body);
return;
}
slots.par_chunks_mut(group).for_each(body);
}
#[cfg(test)]
mod tests {
use super::*;
use rand::SeedableRng;
#[test]
#[should_panic(expected = "system id repeated")]
fn repeated_system_ids_panic_instead_of_aliasing() {
let mut spins = vec![1i8; 8];
let mut rngs = vec![Xoshiro256StarStar::seed_from_u64(0); 2];
par_over_replicas(
&mut spins,
&mut rngs,
&[1.0f32, 2.0],
&[0, 0],
4,
true,
|_, _, _, _, _| {},
);
}
}