#[test]
fn support_is_derived_from_a_weight_one_generator() {
let rot = PauliRotation::new(PauliString::<1>::z(7), 0.3);
assert_eq!(Channel::<1>::support(&rot), [1u64 << 7]);
assert_eq!(rot.weight(), 1);
}
#[test]
fn support_is_derived_from_a_weight_two_generator() {
let mut gen = PauliString::<1>::z(2);
gen.mul_assign(&PauliString::<1>::z(5));
let rot = PauliRotation::new(gen, 0.3);
assert_eq!(Channel::<1>::support(&rot), [(1u64 << 2) | (1u64 << 5)]);
assert_eq!(rot.weight(), 2);
}
#[test]
fn a_y_generator_is_one_qubit_of_support_not_two() {
let rot = PauliRotation::new(PauliString::<1>::y(11), 0.3);
assert_eq!(Channel::<1>::support(&rot), [1u64 << 11]);
assert_eq!(rot.weight(), 1);
}
#[test]
fn support_crosses_a_word_boundary_w2() {
let mut gen = PauliString::<2>::x(5);
gen.mul_assign(&PauliString::<2>::z(70));
gen.mul_assign(&PauliString::<2>::y(64));
let rot = PauliRotation::new(gen, 0.3);
assert_eq!(Channel::<2>::support(&rot), [1u64 << 5, (1u64 << 0) | (1u64 << 6)]);
assert_eq!(rot.weight(), 3);
}
#[test]
fn support_is_deduplicated_across_x_and_z() {
let mut gen = PauliString::<1>::x(3);
gen.mul_assign(&PauliString::<1>::z(3));
gen.mul_assign(&PauliString::<1>::x(1));
let rot = PauliRotation::new(gen, 0.3);
assert_eq!(Channel::<1>::support(&rot), [(1u64 << 1) | (1u64 << 3)]);
assert_eq!(rot.weight(), 2);
}
#[test]
fn an_identity_generator_has_empty_support() {
let rot = PauliRotation::new(PauliString::<1>::identity(), 0.3);
assert_eq!(Channel::<1>::support(&rot), [0u64]);
assert_eq!(rot.weight(), 0);
}
#[test]
fn rotation_support_is_generator_mask() {
let mut gen = PauliString::<2>::z(3);
gen.mul_assign(&PauliString::<2>::x(70));
let rot = PauliRotation::new(gen, 0.7);
assert_eq!(Channel::<2>::support(&rot), [gen.x[0] | gen.z[0], gen.x[1] | gen.z[1]]);
}
#[test]
fn rotation_weight_is_popcount() {
for n in 0..=5 {
let mut gen = PauliString::<1>::identity();
for q in 0..n {
gen.mul_assign(&PauliString::<1>::z(q * 10));
}
let rot = PauliRotation::new(gen, 0.1);
let mask = Channel::<1>::support(&rot);
let popcount: u32 = mask.iter().map(|w| w.count_ones()).sum();
assert_eq!(rot.weight(), popcount as usize, "n={n}");
assert_eq!(rot.weight(), n, "n={n}");
}
}
#[test]
fn accessors_round_trip_the_generator_and_angle() {
let gen = PauliString::<2>::y(65);
let rot = PauliRotation::new(gen, -1.25);
assert_eq!(rot.generator(), gen);
assert_eq!(rot.theta(), -1.25);
}
use super::*;
use crate::test_support::{approx_eq, raw_outputs};
const TOL: f64 = 1e-12;
#[test]
fn theta_zero_anticommuting_w1() {
let q = PauliString::<1>::x(0);
let p = PauliString::<1>::z(0);
let rot = PauliRotation::new(p, 0.0);
let c = Complex64::new(2.0, 3.0);
let got = raw_outputs::<1, _>(&rot, false, q, c);
assert_eq!(got.len(), 2);
assert_eq!(got[0].0.x, q.x);
assert_eq!(got[0].0.z, q.z);
assert!(approx_eq(got[0].1, c, TOL));
let y = PauliString::<1>::y(0);
assert_eq!(got[1].0.x, y.x);
assert_eq!(got[1].0.z, y.z);
assert!(approx_eq(got[1].1, Complex64::new(0.0, 0.0), TOL));
}
#[test]
fn theta_zero_commuting_w1() {
let q = PauliString::<1>::z(0);
let p = PauliString::<1>::z(0);
let rot = PauliRotation::new(p, 0.0);
let c = Complex64::new(2.0, 3.0);
let got = raw_outputs::<1, _>(&rot, false, q, c);
assert_eq!(got.len(), 1);
assert_eq!(got[0].0.x, q.x);
assert_eq!(got[0].0.z, q.z);
assert_eq!(got[0].1, c);
}
#[test]
fn pi_z_flips_x_to_minus_x_w1() {
let q = PauliString::<1>::x(0);
let p = PauliString::<1>::z(0);
let rot = PauliRotation::new(p, std::f64::consts::PI);
let c = Complex64::new(1.0, 0.0);
let got = raw_outputs::<1, _>(&rot, false, q, c);
assert_eq!(got.len(), 2);
assert_eq!(got[0].0.x, q.x);
assert_eq!(got[0].0.z, q.z);
assert!(approx_eq(got[0].1, Complex64::new(-1.0, 0.0), TOL));
let y = PauliString::<1>::y(0);
assert_eq!(got[1].0.x, y.x);
assert_eq!(got[1].0.z, y.z);
assert!(approx_eq(got[1].1, Complex64::new(0.0, 0.0), TOL));
}
#[test]
fn commuting_case_is_fanout_one_w1() {
let q = PauliString::<1>::identity();
let p = PauliString::<1>::z(0);
let rot = PauliRotation::new(p, std::f64::consts::FRAC_PI_4);
let c = Complex64::new(0.5, 0.25);
let got = raw_outputs::<1, _>(&rot, false, q, c);
assert_eq!(got.len(), 1);
assert_eq!(got[0].0.x, q.x);
assert_eq!(got[0].0.z, q.z);
assert_eq!(got[0].1, c);
}
#[test]
fn anticommuting_case_is_fanout_two_w1() {
let q = PauliString::<1>::x(0);
let p = PauliString::<1>::z(0);
let theta = std::f64::consts::FRAC_PI_3;
let rot = PauliRotation::new(p, theta);
let c = Complex64::new(1.0, 0.0);
let got = raw_outputs::<1, _>(&rot, false, q, c);
assert_eq!(got.len(), 2);
assert_eq!(got[0].0.x, q.x);
assert_eq!(got[0].0.z, q.z);
assert!(approx_eq(got[0].1, Complex64::new(theta.cos(), 0.0), TOL));
let y = PauliString::<1>::y(0);
assert_eq!(got[1].0.x, y.x);
assert_eq!(got[1].0.z, y.z);
assert!(approx_eq(got[1].1, Complex64::new(theta.sin(), 0.0), TOL));
}
#[test]
fn pi_over_two_x_rotates_z_to_minus_y_w1() {
let q = PauliString::<1>::z(0);
let p = PauliString::<1>::x(0);
let rot = PauliRotation::new(p, std::f64::consts::FRAC_PI_2);
let c = Complex64::new(1.0, 0.0);
let got = raw_outputs::<1, _>(&rot, false, q, c);
assert_eq!(got.len(), 2);
assert_eq!(got[0].0.x, q.x);
assert_eq!(got[0].0.z, q.z);
assert!(approx_eq(got[0].1, Complex64::new(0.0, 0.0), TOL));
let y = PauliString::<1>::y(0);
assert_eq!(got[1].0.x, y.x);
assert_eq!(got[1].0.z, y.z);
assert!(approx_eq(got[1].1, Complex64::new(-1.0, 0.0), TOL));
}
#[test]
fn phase_from_mul_assign_is_folded_w1() {
let q = PauliString::<1>::y(0);
let p = PauliString::<1>::z(0);
let rot = PauliRotation::new(p, std::f64::consts::FRAC_PI_2);
let c = Complex64::new(1.0, 0.0);
let got = raw_outputs::<1, _>(&rot, false, q, c);
assert_eq!(got.len(), 2);
assert_eq!(got[0].0.x, q.x);
assert_eq!(got[0].0.z, q.z);
assert!(approx_eq(got[0].1, Complex64::new(0.0, 0.0), TOL));
let xp = PauliString::<1>::x(0);
assert_eq!(got[1].0.x, xp.x);
assert_eq!(got[1].0.z, xp.z);
assert!(approx_eq(got[1].1, Complex64::new(-1.0, 0.0), TOL));
}
#[test]
fn multi_word_disjoint_support_commutes_w2() {
let q = PauliString::<2>::x(0);
let p = PauliString::<2>::z(64);
let rot = PauliRotation::new(p, std::f64::consts::FRAC_PI_4);
let c = Complex64::new(1.0, 0.0);
let got = raw_outputs::<2, _>(&rot, false, q, c);
assert_eq!(got.len(), 1);
assert_eq!(got[0].0.x, q.x);
assert_eq!(got[0].0.z, q.z);
assert_eq!(got[0].1, c);
}
#[test]
fn multi_word_anticommute_in_word_1_w2() {
let q = PauliString::<2>::x(64);
let p = PauliString::<2>::z(64);
let theta = std::f64::consts::FRAC_PI_3;
let rot = PauliRotation::new(p, theta);
let c = Complex64::new(1.0, 0.0);
let got = raw_outputs::<2, _>(&rot, false, q, c);
assert_eq!(got.len(), 2);
assert_eq!(got[0].0.x, q.x);
assert_eq!(got[0].0.z, q.z);
assert!(approx_eq(got[0].1, Complex64::new(theta.cos(), 0.0), TOL));
let y = PauliString::<2>::y(64);
assert_eq!(got[1].0.x, y.x);
assert_eq!(got[1].0.z, y.z);
assert_eq!(got[1].0.x[0], 0u64);
assert_eq!(got[1].0.z[0], 0u64);
assert!(approx_eq(got[1].1, Complex64::new(theta.sin(), 0.0), TOL));
}
#[test]
fn quarter_turns_are_fanout_one() {
use crate::test_support::KeepAll;
use crate::{propagate, Circuit, Direction, PauliSum};
use std::f64::consts::{FRAC_PI_2, PI};
let (x0, z) = (PauliString::<1>::x(0), PauliString::<1>::z);
let mut zzz = z(0);
zzz.mul_assign(&z(1));
zzz.mul_assign(&z(2));
for gen in [z(0), zzz] {
let mut y = x0;
y.mul_assign(&gen);
for (theta, want, c) in [(FRAC_PI_2, y, 1.0), (PI, x0, -1.0), (-FRAC_PI_2, y, -1.0)] {
let mut circuit = Circuit::<1>::new(3);
circuit.push(PauliRotation::new(gen, theta));
let input = PauliSum::from_strings(&[("XII", Complex64::new(1.0, 0.0))]);
let out = propagate(&circuit, input, &KeepAll, Direction::Forward);
assert_eq!(out.len(), 1, "{gen:?} at {theta}");
assert_eq!(out.get(&want.x, &want.z), Some(Complex64::new(c, 0.0)));
}
}
}