use std::mem::size_of;
use num_complex::Complex64;
use crate::bits::is_odd_popcount;
use crate::errors::{Result, TicitError};
use crate::pauli::{PauliString, pauli_squares_to_identity};
pub const INV_SQRT2: f64 = std::f64::consts::FRAC_1_SQRT_2;
pub fn active_length(k: usize) -> Result<usize> {
if k >= 62 {
return Err(TicitError::new(
"active qubit count is too large for machine basis indices",
));
}
Ok(1usize << k)
}
pub fn phase_factor(phase: i32) -> Complex64 {
match phase & 3 {
0 => Complex64::new(1.0, 0.0),
1 => Complex64::new(0.0, 1.0),
2 => Complex64::new(-1.0, 0.0),
_ => Complex64::new(0.0, -1.0),
}
}
#[inline]
pub fn insert_zero_bit(packed: usize, bit: usize) -> usize {
let low_mask = (1usize << bit) - 1;
(packed & low_mask) | ((packed & !low_mask) << 1)
}
fn is_near_zero(value: f64) -> bool {
value.abs() < 1e-14
}
fn active_mask_x(pauli: &PauliString) -> u64 {
pauli.x.first().copied().unwrap_or(0)
}
fn active_mask_z(pauli: &PauliString) -> u64 {
pauli.z.first().copied().unwrap_or(0)
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ActivePauliAction {
pub nqubits: usize,
pub xmask: u64,
pub zmask: u64,
pub even_phase: Complex64,
pub odd_phase: Complex64,
pub xz_overlap_odd: bool,
}
impl Default for ActivePauliAction {
fn default() -> Self {
Self {
nqubits: 0,
xmask: 0,
zmask: 0,
even_phase: Complex64::new(1.0, 0.0),
odd_phase: Complex64::new(-1.0, 0.0),
xz_overlap_odd: false,
}
}
}
impl ActivePauliAction {
pub fn new(pauli: &PauliString) -> Result<Self> {
if pauli.nqubits >= 63 {
return Err(TicitError::new(
"Pauli string has too many qubits for active-state basis indexing",
));
}
if !pauli_squares_to_identity(pauli) {
return Err(TicitError::new("Pauli rotation requires P^2 == I"));
}
let xmask = active_mask_x(pauli);
let zmask = active_mask_z(pauli);
let even_phase = phase_factor(pauli.phase_exponent());
Ok(Self {
nqubits: pauli.nqubits,
xmask,
zmask,
even_phase,
odd_phase: -even_phase,
xz_overlap_odd: is_odd_popcount(xmask & zmask),
})
}
#[inline]
pub fn phase_odd(&self, basis: usize) -> bool {
is_odd_popcount(basis as u64 & self.zmask)
}
}
fn can_rotate_real_pair_flip(action: &ActivePauliAction) -> bool {
if action.zmask == 0 || !action.xz_overlap_odd {
return false;
}
is_near_zero(action.even_phase.re) && !is_near_zero(action.even_phase.im)
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct PrecomputedActivePauliRotationKernel {
pub action: ActivePauliAction,
pub is_diagonal: bool,
pub uniform_imag_pairs: bool,
pub real_pair_flip: bool,
pub pair_bit: u32,
pub pair_count: usize,
pub kernel_angle: f64,
pub cos_kernel_angle: f64,
pub sin_kernel_angle: f64,
pub minus_even_coefficient: Complex64,
}
impl Default for PrecomputedActivePauliRotationKernel {
fn default() -> Self {
Self {
action: ActivePauliAction::default(),
is_diagonal: true,
uniform_imag_pairs: false,
real_pair_flip: false,
pair_bit: 0,
pair_count: 0,
kernel_angle: 0.0,
cos_kernel_angle: 1.0,
sin_kernel_angle: 0.0,
minus_even_coefficient: Complex64::new(0.0, 0.0),
}
}
}
impl PrecomputedActivePauliRotationKernel {
pub fn new(action: &ActivePauliAction, kernel_angle: f64) -> Result<Self> {
let dim = active_length(action.nqubits)?;
let sin_kernel_angle = kernel_angle.sin();
let mut kernel = Self {
action: *action,
is_diagonal: action.xmask == 0,
uniform_imag_pairs: false,
real_pair_flip: false,
pair_bit: 0,
pair_count: 0,
kernel_angle,
cos_kernel_angle: kernel_angle.cos(),
sin_kernel_angle,
minus_even_coefficient: Complex64::new(0.0, -sin_kernel_angle) * action.even_phase,
};
if kernel.is_diagonal {
return Ok(kernel);
}
kernel.uniform_imag_pairs = action.zmask == 0;
kernel.real_pair_flip = can_rotate_real_pair_flip(action);
kernel.pair_bit = 63 - action.xmask.leading_zeros();
kernel.pair_count = dim >> 1;
Ok(kernel)
}
#[inline]
pub fn coefficient(&self, source: usize, sign: bool) -> Complex64 {
if sign != self.action.phase_odd(source) {
-self.minus_even_coefficient
} else {
self.minus_even_coefficient
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct PrecomputedActivePauliMeasurementKernel {
pub action: ActivePauliAction,
pub pivot: usize,
pub is_diagonal: bool,
pub diagonal_phase_bit: i32,
pub z_without_pivot: u64,
pub out_dim: usize,
pub nondiagonal_coefficient1_even: Complex64,
}
impl Default for PrecomputedActivePauliMeasurementKernel {
fn default() -> Self {
Self {
action: ActivePauliAction::default(),
pivot: 0,
is_diagonal: true,
diagonal_phase_bit: 0,
z_without_pivot: 0,
out_dim: 0,
nondiagonal_coefficient1_even: Complex64::new(0.0, 0.0),
}
}
}
impl PrecomputedActivePauliMeasurementKernel {
pub fn from_pauli(pauli: &PauliString) -> Result<Self> {
Self::from_action(&ActivePauliAction::new(pauli)?)
}
pub fn from_action(action: &ActivePauliAction) -> Result<Self> {
let pivot = if action.xmask != 0 {
63 - action.xmask.leading_zeros()
} else if action.zmask != 0 {
63 - action.zmask.leading_zeros()
} else {
return Err(TicitError::new(
"cannot build an active measurement kernel for identity Pauli",
));
};
Self::with_pivot(action, pivot as usize)
}
pub fn with_pivot(action: &ActivePauliAction, pivot: usize) -> Result<Self> {
if action.nqubits == 0 {
return Err(TicitError::new(
"cannot build an active measurement kernel for k == 0",
));
}
if pivot >= action.nqubits {
return Err(TicitError::new("active measurement pivot is out of range"));
}
let mut kernel = Self {
action: *action,
pivot,
out_dim: active_length(action.nqubits)? >> 1,
nondiagonal_coefficient1_even: action.even_phase.conj() * INV_SQRT2,
..Self::default()
};
let pivot_bit = 1u64 << pivot;
if action.xmask != 0 {
if action.xmask & pivot_bit == 0 {
return Err(TicitError::new(
"nondiagonal active measurement pivot must have an X component",
));
}
kernel.is_diagonal = false;
} else if action.zmask != 0 {
if action.zmask & pivot_bit == 0 {
return Err(TicitError::new(
"diagonal active measurement pivot must have a Z component",
));
}
kernel.is_diagonal = true;
let negative_phase =
(action.even_phase.re + 1.0).abs() < 1e-12 && action.even_phase.im.abs() < 1e-12;
let positive_phase =
(action.even_phase.re - 1.0).abs() < 1e-12 && action.even_phase.im.abs() < 1e-12;
if !negative_phase && !positive_phase {
return Err(TicitError::new(
"diagonal active measurement Pauli must have real eigenvalues",
));
}
kernel.diagonal_phase_bit = i32::from(negative_phase);
kernel.z_without_pivot = action.zmask & !pivot_bit;
} else {
return Err(TicitError::new(
"cannot build an active measurement kernel for identity Pauli",
));
}
Ok(kernel)
}
#[inline]
pub fn diagonal_source(&self, packed: usize, branch: bool) -> usize {
let without_pivot = insert_zero_bit(packed, self.pivot);
let parity = if self.z_without_pivot == 0 {
0
} else {
(without_pivot as u64 & self.z_without_pivot).count_ones() as i32 & 1
};
let pivot_value = self.diagonal_phase_bit ^ parity ^ i32::from(branch);
without_pivot | ((pivot_value as usize) << self.pivot)
}
#[inline]
pub fn nondiagonal_source0(&self, packed: usize) -> usize {
insert_zero_bit(packed, self.pivot)
}
#[inline]
pub fn nondiagonal_source1(&self, packed: usize) -> usize {
self.nondiagonal_source0(packed) ^ self.action.xmask as usize
}
#[inline]
pub fn nondiagonal_coefficient1(&self, packed: usize, branch: bool) -> Complex64 {
let odd = self.action.phase_odd(self.nondiagonal_source0(packed));
if branch != odd {
-self.nondiagonal_coefficient1_even
} else {
self.nondiagonal_coefficient1_even
}
}
}
const _: () = assert!(size_of::<PrecomputedActivePauliRotationKernel>() <= 128);
const _: () = assert!(size_of::<PrecomputedActivePauliMeasurementKernel>() <= 128);
#[cfg(test)]
mod tests {
use super::*;
use crate::pauli::{pauli_x, pauli_z};
#[test]
fn active_length_rejects_oversized_registers() {
assert_eq!(active_length(0), Ok(1));
assert_eq!(active_length(61), Ok(1usize << 61));
assert!(active_length(62).is_err());
}
#[test]
fn insert_zero_bit_opens_a_gap() {
assert_eq!(insert_zero_bit(0b1011, 0), 0b10110);
assert_eq!(insert_zero_bit(0b1011, 2), 0b10011);
assert_eq!(insert_zero_bit(0b1011, 4), 0b01011);
}
#[test]
fn action_rejects_non_hermitian_paulis() {
let mut pauli = pauli_x(1, 0);
pauli.set_phase(1);
assert!(ActivePauliAction::new(&pauli).is_err());
}
#[test]
fn measurement_kernel_picks_the_highest_pivot() {
let diagonal = &pauli_z(4, 0) * &pauli_z(4, 3);
let kernel = PrecomputedActivePauliMeasurementKernel::from_pauli(&diagonal)
.expect("Z0*Z3 is a valid diagonal measurement");
assert!(kernel.is_diagonal);
assert_eq!(kernel.pivot, 3);
assert_eq!(kernel.z_without_pivot, 0b0001);
let nondiagonal = &(&pauli_x(4, 1) * &pauli_z(4, 2)) * &pauli_x(4, 3);
let kernel = PrecomputedActivePauliMeasurementKernel::from_pauli(&nondiagonal)
.expect("X1*Z2*X3 is a valid measurement");
assert!(!kernel.is_diagonal);
assert_eq!(kernel.pivot, 3);
}
#[test]
fn identity_has_no_measurement_kernel() {
let action = ActivePauliAction::new(&PauliString::new(3)).expect("identity is Hermitian");
assert!(PrecomputedActivePauliMeasurementKernel::from_action(&action).is_err());
}
#[test]
fn diagonal_measurement_needs_real_eigenvalues() {
let action = ActivePauliAction {
nqubits: 2,
zmask: 0b10,
even_phase: Complex64::new(0.0, 1.0),
odd_phase: Complex64::new(0.0, -1.0),
..ActivePauliAction::default()
};
assert!(PrecomputedActivePauliMeasurementKernel::from_action(&action).is_err());
}
}