use num_complex::Complex64;
use smallvec::SmallVec;
use super::kernels::{rowmul_words, xor_words};
use crate::backend::{
NORM_CLAMP_MIN, dense_probability_len, dense_statevector_len, reserve_dense_output,
};
use crate::error::Result;
fn tableau_support(
xz: &[u64],
phase: &[bool],
n: usize,
nw: usize,
stride: usize,
) -> (usize, Vec<usize>) {
let (stab_x, stab_z, stab_phase, diag) = gauss_eliminate_x(xz, phase, n, nw, stride);
let seed = solve_diagonal_seed(&stab_z, &stab_phase, &diag, nw, n);
let mut non_diag_set = vec![true; n];
for &di in &diag {
non_diag_set[di] = false;
}
let coset_gens: Vec<usize> = (0..n)
.filter(|&i| non_diag_set[i])
.map(|i| {
let mut x = 0usize;
#[allow(clippy::needless_range_loop)]
for w in 0..nw {
let shift = w * 64;
if shift < usize::BITS as usize {
x |= (stab_x[i * nw + w] as usize) << shift;
}
}
x
})
.collect();
(min_coset_member(seed, &coset_gens), coset_gens)
}
fn min_coset_member(seed: usize, gens: &[usize]) -> usize {
let mut by_top_bit = [0usize; usize::BITS as usize];
for &g in gens {
let mut v = g;
while v != 0 {
let top = v.ilog2() as usize;
if by_top_bit[top] == 0 {
by_top_bit[top] = v;
break;
}
v ^= by_top_bit[top];
}
}
let mut min = seed;
for top in (0..by_top_bit.len()).rev() {
if (min >> top) & 1 == 1 {
min ^= by_top_bit[top];
}
}
min
}
pub(crate) fn dense_probabilities(
xz: &[u64],
phase: &[bool],
n: usize,
nw: usize,
stride: usize,
backend: &str,
) -> Result<Vec<f64>> {
let dim = dense_probability_len(backend, n)?;
let mut probs = Vec::new();
reserve_dense_output(&mut probs, dim, backend, "probabilities")?;
let (seed, coset_gens) = tableau_support(xz, phase, n, nw, stride);
let k = coset_gens.len();
let amplitude_sq = 1.0 / (1u64 << k) as f64;
if k == n {
probs.resize(dim, amplitude_sq);
return Ok(probs);
}
probs.resize(dim, 0.0);
let coset_size = 1usize << k;
let mut current = seed;
probs[current] = amplitude_sq;
for i in 1..coset_size {
let bit = i.trailing_zeros() as usize;
current ^= coset_gens[bit];
probs[current] = amplitude_sq;
}
Ok(probs)
}
pub(crate) fn dense_statevector(
xz: &[u64],
phase: &[bool],
n: usize,
nw: usize,
stride: usize,
backend: &str,
operation: &str,
) -> Result<Vec<Complex64>> {
let dim = dense_statevector_len(backend, operation, n)?;
let mut state = Vec::new();
reserve_dense_output(&mut state, dim, backend, operation)?;
let mut visited_gen = Vec::new();
reserve_dense_output(&mut visited_gen, dim, backend, operation)?;
let (seed, _) = tableau_support(xz, phase, n, nw, stride);
state.resize(dim, Complex64::new(0.0, 0.0));
state[seed] = Complex64::new(1.0, 0.0);
visited_gen.resize(dim, 0u32);
project_generators(&mut state, &mut visited_gen, xz, phase, n, nw, stride);
Ok(state)
}
#[allow(clippy::type_complexity)]
fn gauss_eliminate_x(
xz: &[u64],
phase: &[bool],
n: usize,
nw: usize,
stride: usize,
) -> (Vec<u64>, Vec<u64>, Vec<bool>, Vec<usize>) {
let mut stab_x = vec![0u64; n * nw];
let mut stab_z = vec![0u64; n * nw];
let mut stab_phase = vec![false; n];
#[allow(clippy::needless_range_loop)]
for i in 0..n {
let src = (i + n) * stride;
let dst = i * nw;
stab_x[dst..dst + nw].copy_from_slice(&xz[src..src + nw]);
stab_z[dst..dst + nw].copy_from_slice(&xz[src + nw..src + nw + nw]);
stab_phase[i] = phase[i + n];
}
let mut remaining: Vec<usize> = (0..n).collect();
for col in 0..n {
let w = col / 64;
let b = col % 64;
let mut pivot_idx = None;
for (ri, &row) in remaining.iter().enumerate() {
if (stab_x[row * nw + w] >> b) & 1 == 1 {
pivot_idx = Some(ri);
break;
}
}
if let Some(ri) = pivot_idx {
let pr = remaining.swap_remove(ri);
let pr_off = pr * nw;
for row in 0..n {
if row == pr {
continue;
}
let row_off = row * nw;
if (stab_x[row_off + w] >> b) & 1 == 1 {
let initial_sum = if stab_phase[pr] { 2u64 } else { 0 }
+ if stab_phase[row] { 2u64 } else { 0 };
let (dst_x, dst_z, src_x, src_z) = unsafe {
let xp = stab_x.as_mut_ptr();
let zp = stab_z.as_mut_ptr();
(
std::slice::from_raw_parts_mut(xp.add(row_off), nw),
std::slice::from_raw_parts_mut(zp.add(row_off), nw),
std::slice::from_raw_parts(xp.add(pr_off) as *const u64, nw),
std::slice::from_raw_parts(zp.add(pr_off) as *const u64, nw),
)
};
let sum = rowmul_words(dst_x, dst_z, src_x, src_z, initial_sum);
stab_phase[row] = (sum & 3) >= 2;
}
}
}
}
(stab_x, stab_z, stab_phase, remaining)
}
fn solve_diagonal_seed(
stab_z: &[u64],
stab_phase: &[bool],
diag: &[usize],
nw: usize,
n: usize,
) -> usize {
let d = diag.len();
if d == 0 {
return 0;
}
let mut z_rows: Vec<u64> = Vec::with_capacity(d * nw);
let mut phases: Vec<bool> = Vec::with_capacity(d);
for &di in diag {
z_rows.extend_from_slice(&stab_z[di * nw..(di + 1) * nw]);
phases.push(stab_phase[di]);
}
let mut pivot_col = vec![usize::MAX; d];
let mut available_cols: Vec<usize> = (0..n).collect();
for row in 0..d {
let row_off = row * nw;
let mut found = None;
for (ci, &col) in available_cols.iter().enumerate() {
if (z_rows[row_off + col / 64] >> (col % 64)) & 1 == 1 {
found = Some(ci);
break;
}
}
if let Some(ci) = found {
let col = available_cols.swap_remove(ci);
pivot_col[row] = col;
let w = col / 64;
let b = col % 64;
let pivot_z: SmallVec<[u64; 16]> = SmallVec::from_slice(&z_rows[row_off..row_off + nw]);
let pivot_phase = phases[row];
#[allow(clippy::needless_range_loop)]
for other in 0..d {
if other == row {
continue;
}
let other_off = other * nw;
if (z_rows[other_off + w] >> b) & 1 == 1 {
unsafe {
xor_words(z_rows.as_mut_ptr().add(other_off), pivot_z.as_ptr(), nw);
}
phases[other] ^= pivot_phase;
}
}
}
}
let mut seed = 0usize;
for row in 0..d {
if pivot_col[row] != usize::MAX && phases[row] {
seed |= 1 << pivot_col[row];
}
}
seed
}
fn project_generators(
sv: &mut [Complex64],
visited_gen: &mut [u32],
xz: &[u64],
phase: &[bool],
n: usize,
nw: usize,
stride: usize,
) {
let dim = sv.len();
let zero = Complex64::new(0.0, 0.0);
let powers_of_i = [
Complex64::new(1.0, 0.0),
Complex64::new(0.0, 1.0),
Complex64::new(-1.0, 0.0),
Complex64::new(0.0, -1.0),
];
let mut current_gen = 0u32;
for i in 0..n {
let row = i + n;
let base = row * stride;
let mut x_bits = 0usize;
let mut z_bits = 0usize;
for w in 0..nw {
let shift = w * 64;
if shift < usize::BITS as usize {
x_bits |= (xz[base + w] as usize) << shift;
z_bits |= (xz[base + nw + w] as usize) << shift;
}
}
let r = phase[row];
let m = (x_bits & z_bits).count_ones() as usize;
let i_factor = powers_of_i[m & 3];
let base_sign = if r { -1.0 } else { 1.0 };
if x_bits == 0 {
for (y, s) in sv.iter_mut().enumerate() {
let dot_parity = (z_bits & y).count_ones() & 1;
let phase_val = if dot_parity == 0 {
base_sign
} else {
-base_sign
};
if phase_val < 0.0 {
*s = zero;
}
}
} else {
current_gen += 1;
for y in 0..dim {
if visited_gen[y] == current_gen {
continue;
}
let partner = y ^ x_bits;
visited_gen[partner] = current_gen;
let a = sv[y];
let b = sv[partner];
let dot_y = (z_bits & y).count_ones() & 1;
let real_y = if dot_y == 0 { base_sign } else { -base_sign };
let gy_phase = i_factor * real_y;
let dot_p = (z_bits & partner).count_ones() & 1;
let real_p = if dot_p == 0 { base_sign } else { -base_sign };
let gp_phase = i_factor * real_p;
sv[y] = (a + b * gp_phase) * 0.5;
sv[partner] = (b + a * gy_phase) * 0.5;
}
}
}
let norm_sq: f64 = sv.iter().map(Complex64::norm_sqr).sum();
if norm_sq > NORM_CLAMP_MIN {
let inv_norm = 1.0 / norm_sq.sqrt();
for amp in sv {
*amp *= inv_norm;
}
}
}