use num_complex::Complex64;
#[cfg(feature = "cuda")]
use super::ZERO;
use crate::pauli_string::PauliString;
use crate::pauli_sum::accumulator::BuildAccumulator;
use crate::pauli_sum::PauliSum;
pub struct Xs64(u64);
impl Xs64 {
pub fn new(seed: u64) -> Self {
Self(seed | 1)
}
#[inline]
pub fn next_u64(&mut self) -> u64 {
let mut x = self.0;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.0 = x;
x
}
pub fn next_array<const W: usize>(&mut self) -> [u64; W] {
let mut a = [0u64; W];
for slot in a.iter_mut() {
*slot = self.next_u64();
}
a
}
}
pub fn word_mask(num_qubits: usize, word: usize) -> u64 {
let lo = 64 * word;
if num_qubits >= lo + 64 {
!0u64
} else if num_qubits <= lo {
0
} else {
(1u64 << (num_qubits - lo)) - 1
}
}
pub fn rand_sum<const W: usize>(n: usize, num_qubits: usize, seed: u64) -> PauliSum<W> {
let mut rng = Xs64::new(seed);
let mut acc = BuildAccumulator::<W>::with_capacity(num_qubits, n);
for _ in 0..n {
let mut p = PauliString::<W> { x: [0u64; W], z: [0u64; W] };
for w in 0..W {
let m = word_mask(num_qubits, w);
p.x[w] = rng.next_u64() & m;
p.z[w] = rng.next_u64() & m;
}
let re = (rng.next_u64() as i64 as f64) / (i64::MAX as f64);
let im = (rng.next_u64() as i64 as f64) / (i64::MAX as f64);
acc.add_term(p, Complex64::new(re, im));
}
acc.finalize()
}
pub fn rand_sum_real<const W: usize>(n: usize, num_qubits: usize, seed: u64) -> PauliSum<W> {
let mut rng = Xs64::new(seed);
let mut acc = BuildAccumulator::<W>::with_capacity(num_qubits, n);
for _ in 0..n {
let mut p = PauliString::<W> { x: [0u64; W], z: [0u64; W] };
for w in 0..W {
let m = word_mask(num_qubits, w);
p.x[w] = rng.next_u64() & m;
p.z[w] = rng.next_u64() & m;
}
let re = (rng.next_u64() as i64 as f64) / (i64::MAX as f64);
acc.add_term(p, Complex64::new(re, 0.0));
}
acc.finalize()
}
pub fn rand_pauli<const W: usize>(rng: &mut Xs64) -> PauliString<W> {
PauliString::<W> { x: rng.next_array::<W>(), z: rng.next_array::<W>() }
}
pub fn rand_sum_unmasked<const W: usize>(n: usize, num_qubits: usize, seed: u64) -> PauliSum<W> {
let mut rng = Xs64::new(seed);
let mut acc = BuildAccumulator::<W>::with_capacity(num_qubits, n);
for _ in 0..n {
let p = rand_pauli::<W>(&mut rng);
let re = (rng.next_u64() as i64 as f64) / (i64::MAX as f64);
let im = (rng.next_u64() as i64 as f64) / (i64::MAX as f64);
acc.add_term(p, Complex64::new(re, im));
}
acc.finalize()
}
fn low_weight_pauli<const W: usize>(
rng: &mut Xs64,
num_qubits: usize,
weight: usize,
) -> PauliString<W> {
let mut p = PauliString::<W> { x: [0u64; W], z: [0u64; W] };
for _ in 0..weight {
let q = (rng.next_u64() as usize) % num_qubits;
let word = q / 64;
let bit = 1u64 << (q % 64);
match rng.next_u64() % 3 {
0 => p.x[word] |= bit,
1 => p.z[word] |= bit,
_ => {
p.x[word] |= bit;
p.z[word] |= bit;
}
}
}
p
}
pub fn low_weight_sum<const W: usize>(
n: usize,
num_qubits: usize,
weight: usize,
seed: u64,
) -> PauliSum<W> {
let mut rng = Xs64::new(seed);
let mut acc = BuildAccumulator::<W>::with_capacity(num_qubits, n);
for _ in 0..n {
let p = low_weight_pauli::<W>(&mut rng, num_qubits, weight);
let re = (rng.next_u64() as i64 as f64) / (i64::MAX as f64);
let im = (rng.next_u64() as i64 as f64) / (i64::MAX as f64);
acc.add_term(p, Complex64::new(re, im));
}
acc.finalize()
}
pub fn tie_heavy_sum<const W: usize>(n: usize, num_qubits: usize, seed: u64) -> PauliSum<W> {
let base = rand_sum::<W>(n, num_qubits, seed);
let mut acc = BuildAccumulator::<W>::with_capacity(num_qubits, n);
for (i, (x, z, _)) in base.iter().enumerate() {
let mag = [1.0f64, 0.5, 0.25, 0.125][i % 4];
acc.add_term(PauliString::<W> { x: *x, z: *z }, Complex64::new(mag, 0.0));
}
acc.finalize()
}
pub fn tie_heavy_sum_unmasked<const W: usize>(
n: usize,
num_qubits: usize,
seed: u64,
) -> PauliSum<W> {
let mut rng = Xs64::new(seed);
let mut acc = BuildAccumulator::<W>::with_capacity(num_qubits, n);
for i in 0..n {
let p = rand_pauli::<W>(&mut rng);
let mag = [1.0f64, 0.5, 0.25, 0.125][i % 4];
acc.add_term(p, Complex64::new(mag, 0.0));
}
acc.finalize()
}
pub fn spread_magnitudes<const W: usize>(
sum: &PauliSum<W>,
octaves: u32,
seed: u64,
) -> PauliSum<W> {
let mut rng = Xs64::new(seed);
let mut acc = BuildAccumulator::<W>::with_capacity(sum.num_qubits(), sum.len());
for (x, z, c) in sum.iter() {
let k = (rng.next_u64() % u64::from(octaves)) as i32;
acc.add_term(PauliString::<W> { x: *x, z: *z }, c * 2f64.powi(-k));
}
acc.finalize()
}
#[cfg(feature = "cuda")]
pub fn with_zero_coefficients<const W: usize>(sum: &PauliSum<W>, every: usize) -> PauliSum<W> {
let mut i = 0usize;
let buckets = (0..sum.num_buckets())
.map(|b| {
let (x, z, c) = sum.bucket(b);
let mut columns = crate::pauli_sum::storage::BucketColumns::<W>::default();
columns.x.extend_from_slice(x);
columns.z.extend_from_slice(z);
for &c in c {
columns.coeff.push(if i.is_multiple_of(every) { ZERO } else { c });
i += 1;
}
columns
})
.collect();
PauliSum::from_buckets(buckets, sum.hash().clone(), sum.num_qubits())
}
pub fn cancellation_sum<const W: usize>(num_qubits: usize) -> PauliSum<W> {
let mut acc = BuildAccumulator::<W>::new(num_qubits);
let identity = PauliString::<W> { x: [0u64; W], z: [0u64; W] };
acc.add_term(identity, Complex64::new(-0.5, 0.0));
acc.add_term(PauliString::<W>::z(0), Complex64::new(1.0, 0.0));
acc.add_term(PauliString::<W>::x(0), Complex64::new(0.25, 0.25));
acc.add_term(PauliString::<W>::y(1), Complex64::new(0.75, -0.5));
acc.add_term(PauliString::<W>::z(2), Complex64::new(0.125, 0.0));
acc.finalize()
}
pub fn z0_sum() -> PauliSum<1> {
let mut acc = BuildAccumulator::<1>::new(8);
acc.add_term(PauliString::<1>::z(0), Complex64::new(1.0, 0.0));
acc.finalize()
}