use super::*;
use crate::pauli_string::PauliString;
use crate::pauli_sum::accumulator::BuildAccumulator;
use crate::pauli_sum::PauliSum;
use crate::truncation::TruncationPolicy;
pub fn and(
a: crate::truncation::BuiltinTruncation,
b: crate::truncation::BuiltinTruncation,
) -> crate::truncation::BuiltinTruncation {
crate::truncation::BuiltinTruncation::And(Box::new(a), Box::new(b))
}
pub fn or(
a: crate::truncation::BuiltinTruncation,
b: crate::truncation::BuiltinTruncation,
) -> crate::truncation::BuiltinTruncation {
crate::truncation::BuiltinTruncation::or(a, b)
}
#[derive(Clone, Default)]
pub struct CallLog(std::sync::Arc<std::sync::Mutex<Vec<&'static str>>>);
impl CallLog {
fn push(&self, call: &'static str) {
self.0.lock().unwrap().push(call);
}
pub fn take(&self) -> Vec<&'static str> {
std::mem::take(&mut *self.0.lock().unwrap())
}
pub fn count(&self, call: &str) -> usize {
self.0.lock().unwrap().iter().filter(|&&c| c == call).count()
}
}
pub struct LoggingTransport {
inner: crate::engine::partitioned::in_process::InProcessTransport,
pub log: CallLog,
}
impl LoggingTransport {
pub fn group(size: u32) -> Vec<Self> {
crate::engine::partitioned::in_process::InProcessTransport::group_with_timeout(
size,
std::time::Duration::from_secs(120),
)
.into_iter()
.map(|inner| Self { inner, log: CallLog::default() })
.collect()
}
}
impl crate::collectives::sealed::Sealed for LoggingTransport {}
impl crate::collectives::Collectives for LoggingTransport {
fn rank(&self) -> u32 {
self.inner.rank()
}
fn size(&self) -> u32 {
self.inner.size()
}
fn allreduce_max_u8(&self, v: u8) -> u8 {
self.log.push("allreduce_max_u8");
self.inner.allreduce_max_u8(v)
}
fn allreduce_sum_u64(&self, buf: &mut [u64]) {
self.log.push("allreduce_sum_u64");
self.inner.allreduce_sum_u64(buf);
}
fn allreduce_sum_f64(&self, buf: &mut [f64]) {
self.log.push("allreduce_sum_f64");
self.inner.allreduce_sum_f64(buf);
}
fn barrier(&self) {
self.log.push("barrier");
self.inner.barrier();
}
}
impl crate::engine::transport::Transport for LoggingTransport {
fn exchange_layer<P, F, R>(
&self,
send: Vec<Option<P>>,
spare: &mut Vec<P>,
map: &crate::engine::transport::ChunkMap,
body: F,
) -> (Vec<Option<P>>, R)
where
P: crate::engine::transport::Payload,
F: FnOnce(&[Option<P>], &dyn crate::engine::transport::ChunkWait) -> R,
{
self.log.push("exchange_layer");
self.inner.exchange_layer(send, spare, map, body)
}
fn exchange<P: crate::engine::transport::Payload>(
&self,
send: Vec<Option<P>>,
spare: &mut Vec<P>,
) -> Vec<Option<P>> {
self.log.push("exchange");
self.inner.exchange(send, spare)
}
}
#[derive(Clone, Copy, Debug)]
pub struct KeepAll;
impl<const W: usize> TruncationPolicy<W> for KeepAll {
fn finalizes_layer(&self) -> bool {
false
}
}
impl From<KeepAll> for crate::truncation::BuiltinTruncation {
fn from(_: KeepAll) -> Self {
Self::Keep
}
}
pub fn x0_terms_identity_on_q63(n: usize, seed: u64) -> PauliSum<1> {
let base = rand_sum::<1>(n, 64, seed);
let mut acc = BuildAccumulator::<1>::new(64);
for (x, z, c) in base.iter() {
let p = PauliString::<1> { x: [(x[0] | 1) & !(1u64 << 63)], z: [z[0] & !(1u64 << 63)] };
acc.add_term(p, c);
}
let out = acc.finalize();
assert_eq!(out.len(), n, "fixture: the terms must stay distinct");
let zz = PauliString::<1> { x: [0], z: [1 | (1u64 << 63)] };
assert!(
out.iter().all(|(x, z, _)| !PauliString::<1> { x: *x, z: *z }.commutes_with(&zz)),
"fixture: every term must anticommute with Z₀Z₆₃ or the layer exports fewer than {n} rows"
);
out
}
pub fn partition_rows<const W: usize>(
num_qubits: usize,
rows_x: Vec<[u64; W]>,
rows_z: Vec<[u64; W]>,
) -> crate::PartitionRows<W> {
crate::PartitionRows::from_rows(num_qubits, rows_x, rows_z)
}
pub fn rows_reading_z63() -> crate::pauli_sum::hash::PartitionRows<1> {
crate::pauli_sum::hash::PartitionRows::<1>::from_rows(64, vec![[0u64]], vec![[1u64 << 63]])
}
pub fn unpinned_partitions(
partitions: usize,
threads: usize,
) -> crate::engine::partitioned::PartitionConfig {
crate::engine::partitioned::PartitionConfig {
placement: crate::engine::partitioned::Placement::Unpinned { partitions },
bind_memory: false,
threads_per_partition: Some(threads),
}
}
pub fn unpinned_scatter<const W: usize>(
partitions: usize,
threads: usize,
row_seed: u64,
) -> crate::engine::partitioned::ScatterOptions<W> {
use crate::engine::partitioned::{PartitionRowPolicy, PartitionRuntime, ScatterRows};
crate::engine::partitioned::ScatterOptions {
runtime: PartitionRuntime::new(&unpinned_partitions(partitions, threads))
.expect("an unpinned placement resolves"),
rows: ScatterRows::Policy(PartitionRowPolicy::Seeded(Some(row_seed))),
}
}