use alloc::{sync::Arc, vec, vec::Vec};
use std::sync::Mutex;
use crate::{
chernoff::ChernoffFunction,
error::SemiflowError,
float::SemiflowFloat,
graph::{Graph, Laplacian},
graph_sensitivity::GeneratorSensitivity,
graph_signal::GraphSignal,
magnus_graph::MagnusGraphHeatChernoff,
scratch::ScratchPool,
};
use super::helpers::{
accumulate_grad_channel, evolve_channel, evolve_magnus6_channel, evolve_magnus_channel,
evolve_vc_channel,
};
pub(super) const MIN_CHANNELS_PARALLEL: usize = 2;
type ErrSlot = Arc<Mutex<Option<SemiflowError>>>;
fn take_err(slot: ErrSlot) -> Result<(), SemiflowError> {
match Arc::try_unwrap(slot) {
Ok(m) => match m.into_inner().unwrap() {
Some(e) => Err(e),
None => Ok(()),
},
Err(_) => Ok(()), }
}
fn store_err(slot: &ErrSlot, e: SemiflowError) {
let mut guard = slot.lock().unwrap();
if guard.is_none() {
*guard = Some(e);
}
}
#[allow(clippy::too_many_arguments)]
pub(super) fn par_evolve_batched<C, F>(
func: &C,
graph: &Arc<Graph<F>>,
tau: F,
n_steps: usize,
src_cols: &[F],
dst_cols: &mut [F],
n: usize,
) -> Result<(), SemiflowError>
where
C: ChernoffFunction<F, S = GraphSignal<F>> + Sync,
F: SemiflowFloat,
{
let err: ErrSlot = Arc::new(Mutex::new(None));
std::thread::scope(|s| {
for (c, dst_c) in dst_cols.chunks_mut(n).enumerate() {
let src_c = &src_cols[c * n..(c + 1) * n];
let g = Arc::clone(graph);
let err_arc = Arc::clone(&err);
s.spawn(move || {
let mut scr = ScratchPool::<F>::new();
let mut a = GraphSignal::zeros(Arc::clone(&g));
let mut b = GraphSignal::zeros(g);
if let Err(e) =
evolve_channel(func, tau, n_steps, src_c, dst_c, &mut a, &mut b, &mut scr)
{
store_err(&err_arc, e);
}
});
}
});
take_err(err)
}
#[allow(clippy::too_many_arguments)]
pub(super) fn par_evolve_magnus<F: SemiflowFloat>(
l1: &Laplacian<F>,
l2: &Laplacian<F>,
tau: F,
n_steps: usize,
src_cols: &[F],
dst_cols: &mut [F],
n: usize,
g: &Arc<Graph<F>>,
) {
std::thread::scope(|s| {
for (c, dst_c) in dst_cols.chunks_mut(n).enumerate() {
let src_c = &src_cols[c * n..(c + 1) * n];
let g = Arc::clone(g);
s.spawn(move || {
let mut scr = ScratchPool::<F>::new();
let mut a = GraphSignal::zeros(Arc::clone(&g));
let mut b = GraphSignal::zeros(g);
evolve_magnus_channel(l1, l2, tau, n_steps, src_c, dst_c, &mut a, &mut b, &mut scr);
});
}
});
}
#[allow(clippy::too_many_arguments)]
pub(super) fn par_evolve_magnus6(
l1: &Laplacian<f64>,
l2: &Laplacian<f64>,
l3: &Laplacian<f64>,
tau: f64,
n_steps: usize,
src_cols: &[f64],
dst_cols: &mut [f64],
n: usize,
g: &Arc<Graph<f64>>,
) {
std::thread::scope(|s| {
for (c, dst_c) in dst_cols.chunks_mut(n).enumerate() {
let src_c = &src_cols[c * n..(c + 1) * n];
let g = Arc::clone(g);
s.spawn(move || {
let mut scr = ScratchPool::<f64>::new();
let mut a = GraphSignal::zeros(Arc::clone(&g));
let mut b = GraphSignal::zeros(g);
evolve_magnus6_channel(
l1, l2, l3, tau, n_steps, src_c, dst_c, &mut a, &mut b, &mut scr,
);
});
}
});
}
#[allow(clippy::too_many_arguments)]
pub(super) fn par_evolve_vc<F: SemiflowFloat>(
l1: &Laplacian<F>,
sqrt_a1: &[F],
l2: &Laplacian<F>,
sqrt_a2: &[F],
tau: F,
n_steps: usize,
src_cols: &[F],
dst_cols: &mut [F],
n: usize,
g: &Arc<Graph<F>>,
) {
std::thread::scope(|s| {
for (c, dst_c) in dst_cols.chunks_mut(n).enumerate() {
let src_c = &src_cols[c * n..(c + 1) * n];
let g = Arc::clone(g);
s.spawn(move || {
let mut scr = ScratchPool::<F>::new();
let mut a = GraphSignal::zeros(Arc::clone(&g));
let mut b = GraphSignal::zeros(g);
evolve_vc_channel(
l1, sqrt_a1, l2, sqrt_a2, tau, n_steps, src_c, dst_c, &mut a, &mut b, &mut scr,
);
});
}
});
}
#[allow(clippy::too_many_arguments)]
#[allow(clippy::too_many_lines)]
pub(super) fn par_grad_batched<F, P>(
mc: &MagnusGraphHeatChernoff<F>,
u0_cols: &[F],
dj_cols: &[F],
n_steps: usize,
tau: F,
param_deriv: &P,
grad_theta: &mut [F],
n_cols: usize,
n: usize,
) -> Result<(), SemiflowError>
where
F: SemiflowFloat,
P: GeneratorSensitivity<F> + Sync,
{
let n_params = param_deriv.n_params();
let err: ErrSlot = Arc::new(Mutex::new(None));
let mut tmp_grads: Vec<Vec<F>> = (0..n_cols).map(|_| vec![F::zero(); n_params]).collect();
let g_arc = Arc::clone(&mc.graph);
std::thread::scope(|s| {
for (c, tmp_c) in tmp_grads.iter_mut().enumerate() {
let u0_c = &u0_cols[c * n..(c + 1) * n];
let dj_c = &dj_cols[c * n..(c + 1) * n];
let g = Arc::clone(&g_arc);
let err_arc = Arc::clone(&err);
s.spawn(move || {
let mut scr = ScratchPool::<F>::new();
if let Err(e) = accumulate_grad_channel(
mc,
u0_c,
dj_c,
&g,
n_steps,
tau,
param_deriv,
n_params,
tmp_c,
&mut scr,
) {
store_err(&err_arc, e);
}
});
}
});
take_err(err)?;
for tmp_c in &tmp_grads {
for (k, &v) in tmp_c.iter().enumerate() {
grad_theta[k] += v;
}
}
Ok(())
}