microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.25.1

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/paulimer/tests/clifford_test.rs

1274lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use paulimer::bits::{BitMatrix, BitVec, Bitwise, IndexSet};
5use paulimer::clifford::generic_algos::{clifford_from_images, clifford_to_prepare_bell_states};
6use paulimer::clifford::{
7 apply_qubit_clifford_by_axis, group_encoding_clifford_of, prepare_all_plus, prepare_all_zero,
8 random_clifford_via_operations_sampling, split_clifford_encoder_mod_pauli, split_phased_css,
9 split_qubit_cliffords_and_css, split_qubit_tensor_product_encoder, Clifford, CliffordMutable,
10 CliffordStringParsingError, MutablePreImages, PreimageViews, Swap, XOrZ,
11};
12type CliffordUnitary = paulimer::clifford::CliffordUnitary;
13type CliffordUnitaryModPauli = paulimer::clifford::CliffordUnitaryModPauli;
14
15use paulimer::pauli::{
16 anti_commutes_with, apply_pauli_exponent, apply_root_x, apply_root_y, apply_root_z,
17 pauli_random, pauli_random_order_two, DensePauli, DensePauliProjective, PauliMutable,
18 SparsePauliProjective,
19};
20use paulimer::pauli::{commutes_with, Pauli, PauliBinaryOps, PauliUnitary, Phase, SparsePauli};
21
22use paulimer::operations::{css_operations, diagonal_operations};
23use paulimer::quantum_core::{x, y, z, PositionedPauliObservable};
24use proptest::prelude::*;
25use rand::prelude::*;
26use std::borrow::Borrow;
27use std::ops::Range;
28use std::str::FromStr;
29
30pub trait TestableClifford:
31 Clifford<
32 DensePauli: for<'life, 'life1> PartialEq<&'life [PositionedPauliObservable]>
33 + PartialEq
34 + std::fmt::Display
35 + std::fmt::Debug,
36 > + CliffordMutable<PhaseExponentValue = <Self as Clifford>::PhaseExponentValue>
37 + PartialEq
38 + Eq
39 + PreimageViews<PhaseExponentValue = <Self as Clifford>::PhaseExponentValue>
40 + MutablePreImages<PhaseExponentValue = <Self as Clifford>::PhaseExponentValue>
41 + FromStr<Err = CliffordStringParsingError>
42 + std::fmt::Display
43 + std::fmt::Debug
44{
45 type SparsePauli: Pauli<PhaseExponentValue = <Self as Clifford>::PhaseExponentValue>
46 + for<'life> From<&'life [PositionedPauliObservable]>
47 + std::fmt::Display;
48 type DensePauli: Pauli<PhaseExponentValue = <Self as Clifford>::PhaseExponentValue>
49 + for<'life> PartialEq<&'life [PositionedPauliObservable]>
50 + std::fmt::Display;
51}
52
53impl TestableClifford for CliffordUnitary {
54 type SparsePauli = SparsePauli;
55 type DensePauli = DensePauli;
56}
57
58impl TestableClifford for CliffordUnitaryModPauli {
59 type SparsePauli = SparsePauliProjective;
60 type DensePauli = DensePauliProjective;
61}
62
63fn is_pauli_x_up_to_phase(pauli: &impl Pauli, qubit_id: usize) -> bool {
64 pauli.z_bits().is_zero() && pauli.x_bits().weight() == 1 && pauli.x_bits().index(qubit_id)
65}
66
67fn is_pauli_z_up_to_phase(pauli: &impl Pauli, qubit_id: usize) -> bool {
68 pauli.x_bits().is_zero() && pauli.z_bits().weight() == 1 && pauli.z_bits().index(qubit_id)
69}
70
71fn are_identity_preimages_up_to_phase(clifford: &impl Clifford) -> bool {
72 for j in 0..clifford.num_qubits() {
73 if !is_pauli_x_up_to_phase(&clifford.preimage_x(j), j) {
74 return false;
75 }
76 if !is_pauli_z_up_to_phase(&clifford.preimage_z(j), j) {
77 return false;
78 }
79 }
80 true
81}
82
83fn identity_preimages_with_dimension(dimension: usize) {
84 let id = CliffordUnitary::identity(dimension);
85 assert!(id.is_identity());
86 let id_mod_pauli = CliffordUnitaryModPauli::identity(dimension);
87 assert!(are_identity_preimages_up_to_phase(&id_mod_pauli));
88}
89
90#[test]
91fn identity_preimages() {
92 for dimension in 1..4 {
93 identity_preimages_with_dimension(dimension);
94 }
95}
96
97proptest! {
98 #[test]
99 fn from_images(clifford in arbitrary_clifford(0..1)) {
100 let images = images_of(&clifford);
101 let from_images : CliffordUnitary = clifford_from_images(images.as_slice().iter());
102 assert_eq!(images, images_of(&from_images));
103 }
104
105 #[test]
106 fn format_string_roundtrip(clifford in arbitrary_clifford(1..10)) {
107 format_string_roundtrip_generic_test(&clifford);
108 format_string_roundtrip_generic_test::<CliffordUnitaryModPauli>(&clifford.into());
109 }
110
111 #[test]
112 fn clone(clifford in arbitrary_clifford(0..10)) {
113 let cloned = clifford.clone();
114 assert_eq!(images_of(&cloned), images_of(&clifford));
115 }
116
117 #[test]
118 fn pauli_exponent_multiply(clifford in arbitrary_clifford(0..10)) {
119 let pauli = pauli_random_order_two::<<paulimer::clifford::CliffordUnitary as Clifford>::DensePauli>(clifford.num_qubits(),&mut thread_rng());
120 let mut product = clifford.clone();
121 product.left_mul_pauli_exp(&pauli);
122 let ipauli = pauli.clone() * Phase::from_exponent(1u8);
123
124 let assert_image = |indicator: PauliUnitary<Vec<bool>, u8>| {
125 let power_image = product.image(&indicator);
126 let clifford_image = clifford.image(&indicator);
127 // println!("indicator={}, pauli={}, power_image={}, clifford_image={}", indicator, pauli, power_image, clifford_image);
128 if commutes_with(&clifford_image,&pauli) {
129 assert_eq!(clifford_image, power_image);
130 } else {
131 assert_eq!(ipauli.clone() * &clifford_image, power_image);
132 }
133 };
134
135 for index in 0..clifford.num_qubits() {
136 assert_image(x_at(index, clifford.num_qubits()));
137 assert_image(z_at(index, clifford.num_qubits()));
138 }
139 }
140
141 #[test]
142 fn controlled_pauli_multiply(clifford in arbitrary_clifford(2..3)) {
143
144 assert!(clifford.num_qubits() >= 2);
145 let mut control : <paulimer::clifford::CliffordUnitary as Clifford>::DensePauli = pauli_random_order_two(clifford.num_qubits(),&mut thread_rng());
146 while control.x_bits().is_zero() && control.z_bits().is_zero() {
147 control = pauli_random_order_two(clifford.num_qubits(),&mut thread_rng());
148 }
149 let mut target : <paulimer::clifford::CliffordUnitary as Clifford>::DensePauli = pauli_random_order_two(clifford.num_qubits(),&mut thread_rng());
150 while !commutes_with(&control,&target) || (control.x_bits().is_zero() && control.z_bits().is_zero()) {
151 target = pauli_random_order_two(clifford.num_qubits(),&mut thread_rng());
152 }
153
154 let mut product = clifford.clone();
155 assert!(control.is_order_two());
156 assert!(target.is_order_two());
157 product.left_mul_controlled_pauli(&control,&target);
158
159 let images = images_of(&clifford);
160 let product_images = images_of(&product);
161 for (image, product_image) in images.iter().zip(product_images.iter()) {
162 let mut expected_image = image.clone();
163 if anti_commutes_with(image,&control) {
164 expected_image.mul_assign_right(&target);
165 }
166 if anti_commutes_with(image, &target) {
167 expected_image.mul_assign_left(&control);
168 }
169 // println!("Q={}, P1={}, P2={}, expected={}, actual={}", image, control, target, expected_image, *product_image);
170 assert_eq!(expected_image, *product_image);
171 }
172 }
173
174 #[test]
175 fn pauli_multiply(clifford in arbitrary_clifford(0..1)) {
176 let pauli = arbitrary_pauli_of_length(clifford.num_qubits());
177 let product = &pauli * clifford.clone();
178
179 let assert_image_sign = |indicator: PauliUnitary<Vec<bool>, u8>| {
180 let product_image = product.image(&indicator);
181 let clifford_image = clifford.image(&indicator);
182 // println!("indicator={}, pauli={}, product_image={}, clifford_image={}", indicator, pauli, product_image, clifford_image);
183 if commutes_with(&clifford_image, &pauli) {
184 assert_eq!(clifford_image, product_image);
185 } else {
186 assert_eq!(-clifford_image, product_image);
187 }
188 };
189
190 for index in 0..clifford.num_qubits() {
191 assert_image_sign(x_at(index, clifford.num_qubits()));
192 assert_image_sign(z_at(index, clifford.num_qubits()));
193 }
194 }
195
196 #[test]
197 fn composition((left, right) in composable_cliffords(0..10)) {
198 let composed = left.multiply_with(&right);
199 for index in 0..left.num_qubits() {
200 let x = x_at(index, left.num_qubits());
201 let z = z_at(index, left.num_qubits());
202 assert_eq!(composed.preimage(&x), right.preimage(&left.preimage(&x)));
203 assert_eq!(composed.preimage(&z), right.preimage(&left.preimage(&z)));
204 }
205 }
206
207 #[test]
208 fn right_swap(clifford in arbitrary_clifford(2..10), mut index0 in 0..10usize, mut index1 in 0..10usize) {
209 index0 %= clifford.num_qubits();
210 index1 %= clifford.num_qubits();
211 let swapped = clifford.clone() * Swap(index0, index1);
212 let clifford_images = images_of(&clifford);
213 let swapped_images = images_of(&swapped);
214 assert_eq!(swapped_images[2*index0], clifford_images[2*index1]);
215 assert_eq!(swapped_images[2*index0+1], clifford_images[2*index1+1]);
216 assert_eq!(swapped_images[2*index1], clifford_images[2*index0]);
217 assert_eq!(swapped_images[2*index1+1], clifford_images[2*index0+1]);
218 for index in 0..clifford.num_qubits() {
219 if index != index0 && index != index1 {
220 assert_eq!(swapped_images[2*index], clifford_images[2*index]);
221 assert_eq!(swapped_images[2*index+1], clifford_images[2*index+1]);
222 }
223 }
224 }
225
226 #[test]
227 fn left_swap(clifford in arbitrary_clifford(2..3), mut index0 in 0..10usize, mut index1 in 0..10usize) {
228 index0 %= clifford.num_qubits();
229 index1 %= clifford.num_qubits();
230 let mut swapped = clifford.clone();
231 swapped.left_mul_swap(index0, index1);
232 let clifford_images = preimages_of(&clifford);
233 let swapped_images = preimages_of(&swapped);
234 assert_eq!(swapped_images[2*index0], clifford_images[2*index1]);
235 assert_eq!(swapped_images[2*index0+1], clifford_images[2*index1+1]);
236 assert_eq!(swapped_images[2*index1], clifford_images[2*index0]);
237 assert_eq!(swapped_images[2*index1+1], clifford_images[2*index0+1]);
238 for index in 0..clifford.num_qubits() {
239 if index != index0 && index != index1 {
240 assert_eq!(swapped_images[2*index], clifford_images[2*index]);
241 assert_eq!(swapped_images[2*index+1], clifford_images[2*index+1]);
242 }
243 }
244 }
245
246 #[test]
247 fn preimage_inverts_image(clifford in arbitrary_clifford(0..10)) {
248 let clifford = CliffordUnitary::identity(clifford.num_qubits());
249 for index in 0..clifford.num_qubits() {
250 let x = &x_at(index, clifford.num_qubits());
251 let z = &z_at(index, clifford.num_qubits());
252 let x_image = clifford.image(x);
253 let z_image = clifford.image(z);
254 let x_image_preimage = clifford.preimage(&x_image);
255 let z_image_preimage = clifford.preimage(&z_image);
256 assert!( x == x_image_preimage);
257 assert!( z == z_image_preimage);
258 }
259 }
260
261 #[test]
262 fn split_clifford(clifford1 in arbitrary_clifford(1..5), clifford2 in arbitrary_clifford(1..5)) {
263 let c1 = CliffordUnitaryModPauli::from(clifford1);
264 let c2 = CliffordUnitaryModPauli::from(clifford2);
265 let qubit_count = c1.num_qubits() + c2.num_qubits();
266 let mut c3 = random_diagonal_clifford::<CliffordUnitaryModPauli>(qubit_count).multiply_with(&random_css_clifford(qubit_count));
267
268 let support = c1.qubits().collect::<Vec<_>>();
269 let support_complement = (c1.num_qubits()..c3.num_qubits()).collect::<Vec<_>>();
270
271 c3.left_mul_clifford(&c1, &support);
272 c3.left_mul_clifford(&c2, &support_complement);
273
274 if let Some((split_clifford1, split_clifford2)) = split_clifford_encoder_mod_pauli(&c3, &support, &support_complement) {
275 assert!(split_clifford1.is_valid());
276 assert!(split_clifford2.is_valid());
277
278 assert_eq!(split_clifford1.num_qubits(), c1.num_qubits());
279 assert_eq!(split_clifford2.num_qubits(), c2.num_qubits());
280
281 for qubit_index in split_clifford1.qubits() {
282 assert!(c3.preimage(&split_clifford1.image_z(qubit_index)).x_bits().is_zero());
283 }
284
285 // tensor product of split_clifford1, split_clifford2 encode the same state as c4
286 let mut c4 = CliffordUnitaryModPauli::identity(qubit_count);
287 c4.left_mul_clifford(&split_clifford1, &support);
288 c4.left_mul_clifford(&split_clifford2, &support_complement);
289 for qubit_index in c4.qubits() {
290 assert!(c3.preimage(&c4.image_z(qubit_index)).x_bits().is_zero());
291 }
292
293 }
294 else {
295 panic!("Clifford should split")
296 }
297 }
298
299 #[test]
300 fn identity_dimension(dimension in 0..1000usize) {
301 let identity = CliffordUnitary::identity(dimension);
302 assert_eq!(dimension, identity.num_qubits());
303 }
304
305 #[test]
306 fn identity_multiplication_is_trivial(clifford in arbitrary_clifford(0..10)) {
307 let identity = CliffordUnitary::identity(clifford.num_qubits());
308 assert_eq!(clifford, clifford.multiply_with(&identity));
309 assert_eq!(clifford, identity.multiply_with(&clifford));
310 }
311
312 #[test]
313 fn group_encoding_clifford_of_test(clifford in arbitrary_clifford(1..20), qubit_count in 1usize..20 ) {
314 let num_images = qubit_count.min(clifford.num_qubits());
315 let images = (0 .. num_images).map(|id| clifford.image_z(id) ).collect::<Vec<_>>();
316 let encoding_clifford = group_encoding_clifford_of(&images,clifford.num_qubits());
317 for image in images {
318 let preimage = encoding_clifford.preimage(&image);
319 assert!(preimage.x_bits().is_zero());
320 assert!(preimage.z_bits().max_bit_id().unwrap() < num_images);
321 assert_eq!(preimage.xz_phase_exponent(),0);
322 }
323 }
324
325 #[test]
326 fn left_mul_root_and_apply_root_are_consistent(qubit_count in 1..10usize) {
327 check_left_mul_root_and_apply_root_are_consistent(qubit_count, <CliffordUnitary as CliffordMutable>::left_mul_root_x, apply_root_x::<DensePauli>);
328 check_left_mul_root_and_apply_root_are_consistent(qubit_count, <CliffordUnitary as CliffordMutable>::left_mul_root_y, apply_root_y::<DensePauli>);
329 check_left_mul_root_and_apply_root_are_consistent(qubit_count, <CliffordUnitary as CliffordMutable>::left_mul_root_z, apply_root_z::<DensePauli>);
330 }
331
332 #[test]
333 fn left_mul_pauli_exp_and_apply_pauli_exp_are_consistent(clifford in arbitrary_clifford(1..20)) {
334 let identity = CliffordUnitary::identity(clifford.num_qubits());
335 let mut pauli_exp = CliffordUnitary::identity(clifford.num_qubits());
336 let exp = clifford.image_z(0);
337 pauli_exp.left_mul_pauli_exp(&exp);
338 for qubit_index in 0 .. clifford.num_qubits() {
339 let mut image_z = identity.image_z(qubit_index);
340 apply_pauli_exponent(&mut image_z, &exp);
341 assert_eq!(image_z, pauli_exp.image_z(qubit_index));
342
343 let mut image_x = identity.image_x(qubit_index);
344 apply_pauli_exponent(&mut image_x, &exp);
345 assert_eq!(image_x, pauli_exp.image_x(qubit_index));
346 }
347 }
348
349 #[test]
350 fn inverse(clifford in arbitrary_clifford(1..2)) {
351 let inverse = clifford.inverse();
352 let identity = CliffordUnitary::identity(clifford.num_qubits());
353 assert_eq!(identity, clifford.multiply_with(&inverse));
354 }
355
356 #[test]
357 fn is_diagonal(clifford in arbitrary_diagonal_clifford(1..15usize)) {
358 generic_diagonal_clifford_test::<CliffordUnitaryModPauli>(&clifford.clone().into());
359 generic_diagonal_clifford_test::<CliffordUnitary>(&clifford);
360
361 }
362
363 #[test]
364 fn diagonal_resource_state_encoder_test(qubit_count in 1..15usize) {
365 assert!(prepare_all_plus(qubit_count).is_diagonal_resource_encoder(XOrZ::Z));
366 assert!(prepare_all_zero(qubit_count).is_diagonal_resource_encoder(XOrZ::X));
367 assert!(prepare_all_plus(qubit_count).unitary_from_diagonal_resource_state(XOrZ::Z).unwrap().is_identity());
368 assert!(prepare_all_zero(qubit_count).unitary_from_diagonal_resource_state(XOrZ::X).unwrap().is_identity());
369 }
370
371 #[test]
372 fn is_css(clifford in arbitrary_css_clifford(2..10usize)) {
373 generic_is_css_clifford_test::<CliffordUnitary>(&clifford);
374 let clifford_mod_pauli: CliffordUnitaryModPauli = clifford.into();
375 generic_is_css_clifford_test::<CliffordUnitaryModPauli>(&clifford_mod_pauli);
376 let qubit_count = clifford_mod_pauli.num_qubits();
377 assert!(prepare_all_plus(qubit_count).is_diagonal_resource_encoder(XOrZ::Z));
378 assert!(clifford_mod_pauli.multiply_with(&prepare_all_plus(qubit_count)).is_diagonal_resource_encoder(XOrZ::Z));
379 assert!(clifford_mod_pauli.multiply_with(&prepare_all_zero(qubit_count)).is_diagonal_resource_encoder(XOrZ::X));
380 }
381
382 #[test]
383 fn is_phased_css_test( (css,diagonal) in composable_css_diagonal_cliffords(2..10usize)) {
384 let c1 : CliffordUnitaryModPauli = css.multiply_with(&diagonal).into();
385 let c2 : CliffordUnitaryModPauli = diagonal.multiply_with(&css).into();
386 assert!(split_phased_css(&css.clone().into()).is_some());
387 assert!(split_phased_css(&diagonal.clone().into()).is_some());
388 if let Some((diag,extracted_css)) = split_phased_css(&c2) {
389 assert_eq!(CliffordUnitaryModPauli::from(diagonal),diag);
390 assert_eq!(CliffordUnitaryModPauli::from(css),extracted_css);
391 }
392 assert!(split_phased_css(&c1).is_some());
393 }
394
395 #[test]
396 fn is_qubit_css_test( (css,qubit) in composable_css_qubit_cliffords(2..10usize)) {
397 let c2 : CliffordUnitaryModPauli = qubit.multiply_with(&css).into();
398 assert!(split_qubit_cliffords_and_css(&css.clone().into()).is_some());
399 assert!(split_qubit_cliffords_and_css(&qubit.clone().into()).is_some());
400 if let Some((extracted_qubit,extracted_css)) = split_qubit_cliffords_and_css(&c2) {
401 assert_eq!(CliffordUnitaryModPauli::from(qubit),extracted_qubit);
402 assert_eq!(CliffordUnitaryModPauli::from(css),extracted_css);
403 }
404 }
405
406 #[test]
407 fn qubit_cliffords_recognition_test( qubit_cliffords in arbitrary_qubit_cliffords(1..10usize)) {
408 let qubit_count = qubit_cliffords.num_qubits();
409 let c : CliffordUnitaryModPauli = qubit_cliffords.into();
410 let mut r = CliffordUnitaryModPauli::identity(qubit_count);
411 let plus = prepare_all_plus(qubit_count);
412 let zero = prepare_all_zero(qubit_count);
413 let plus_axes = split_qubit_tensor_product_encoder(&c.multiply_with(&plus)).unwrap();
414 let zero_axes = split_qubit_tensor_product_encoder(&c.multiply_with(&zero)).unwrap();
415 for (qubit_index,(zero,plus)) in std::iter::zip(zero_axes, plus_axes).enumerate() {
416 apply_qubit_clifford_by_axis(&mut r, qubit_index, zero, plus);
417 }
418 assert_eq!(c,r);
419 }
420}
421
422prop_compose! {
423 fn arbitrary_clifford(dimension_range: Range<usize>)(dimension in dimension_range) -> CliffordUnitary {
424 arbitrary_clifford_of_dimension(dimension)
425 }
426}
427
428prop_compose! {
429 fn arbitrary_css_clifford(dimension_range: Range<usize>)(dimension in dimension_range) -> CliffordUnitary {
430 let mut clifford: CliffordUnitary = random_css_clifford(dimension);
431 let pauli = pauli_random_order_two::<<paulimer::clifford::CliffordUnitary as Clifford>::DensePauli>(clifford.num_qubits(),&mut thread_rng());
432 clifford.left_mul_pauli(&pauli);
433 clifford
434 }
435}
436
437prop_compose! {
438 fn arbitrary_diagonal_clifford(dimension_range: Range<usize>)(dimension in dimension_range) -> CliffordUnitary {
439 let mut clifford: CliffordUnitary= random_diagonal_clifford(dimension);
440 let pauli = pauli_random_order_two::<<paulimer::clifford::CliffordUnitary as Clifford>::DensePauli>(clifford.num_qubits(),&mut thread_rng());
441 clifford.left_mul_pauli(&pauli);
442 clifford
443 }
444}
445
446prop_compose! {
447 fn composable_cliffords(dimension_range: Range<usize>)(dimension in dimension_range) -> (CliffordUnitary, CliffordUnitary) {
448 (arbitrary_clifford_of_dimension(dimension), arbitrary_clifford_of_dimension(dimension))
449 }
450}
451
452prop_compose! {
453 fn composable_css_diagonal_cliffords(dimension_range: Range<usize>)(dimension in dimension_range) -> (CliffordUnitary, CliffordUnitary) {
454 (arbitrary_css_clifford_of_dimension(dimension), arbitrary_diagonal_clifford_of_dimension(dimension))
455 }
456}
457
458prop_compose! {
459 fn composable_css_qubit_cliffords(dimension_range: Range<usize>)(dimension in dimension_range) -> (CliffordUnitary, CliffordUnitary) {
460 (arbitrary_css_clifford_of_dimension(dimension), arbitrary_qubit_cliffords_of_dimension(dimension))
461 }
462}
463
464prop_compose! {
465 fn arbitrary_qubit_cliffords(dimension_range: Range<usize>)(dimension in dimension_range) -> CliffordUnitary {
466 arbitrary_qubit_cliffords_of_dimension(dimension)
467 }
468}
469
470prop_compose! {
471 fn arbitrary_images(max_dimension: usize)(dimension in 0..=max_dimension) -> Vec<PauliUnitary<BitVec, u8>> {
472 let images: Vec<PauliUnitary<BitVec, u8>> = std::iter::from_fn(|| Some(arbitrary_pauli_of_length(dimension))).take(dimension*2).collect();
473 images
474 }
475}
476
477prop_compose! {
478 fn arbitrary_pauli(max_dimension: usize)(dimension in 0..=max_dimension) -> PauliUnitary<BitVec, u8> {
479 arbitrary_pauli_of_length(dimension)
480 }
481}
482
483fn arbitrary_clifford_of_dimension(dimension: usize) -> CliffordUnitary {
484 CliffordUnitary::random(dimension, &mut thread_rng())
485}
486
487fn arbitrary_css_clifford_of_dimension(dimension: usize) -> CliffordUnitary {
488 let mut clifford: CliffordUnitary = random_css_clifford(dimension);
489 let pauli = pauli_random_order_two::<
490 <paulimer::clifford::CliffordUnitary as Clifford>::DensePauli,
491 >(clifford.num_qubits(), &mut thread_rng());
492 clifford.left_mul_pauli(&pauli);
493 clifford
494}
495
496fn arbitrary_qubit_cliffords_of_dimension(dimension: usize) -> CliffordUnitary {
497 let mut clifford: CliffordUnitary = CliffordUnitary::identity(dimension);
498 for qubit_index in clifford.qubits() {
499 let qubit_random_clifford = arbitrary_clifford_of_dimension(1);
500 clifford.left_mul_clifford(&qubit_random_clifford, &[qubit_index]);
501 }
502 clifford
503}
504
505fn arbitrary_diagonal_clifford_of_dimension(dimension: usize) -> CliffordUnitary {
506 let mut clifford: CliffordUnitary = random_diagonal_clifford(dimension);
507 let pauli = pauli_random_order_two::<
508 <paulimer::clifford::CliffordUnitary as Clifford>::DensePauli,
509 >(clifford.num_qubits(), &mut thread_rng());
510 clifford.left_mul_pauli(&pauli);
511 clifford
512}
513
514fn arbitrary_pauli_of_length(length: usize) -> PauliUnitary<BitVec, u8> {
515 pauli_random(length, &mut thread_rng())
516}
517
518fn images_of<CliffordLike: Clifford>(clifford: &CliffordLike) -> Vec<CliffordLike::DensePauli> {
519 let mut images = vec![];
520 for qubit_index in clifford.qubits() {
521 images.push(clifford.image_x(qubit_index));
522 images.push(clifford.image_z(qubit_index));
523 }
524 images
525}
526
527fn preimages_of<CliffordLike: Clifford>(clifford: &CliffordLike) -> Vec<CliffordLike::DensePauli> {
528 let mut preimages = vec![];
529 for qubit_index in clifford.qubits() {
530 preimages.push(clifford.preimage_x(qubit_index));
531 preimages.push(clifford.preimage_z(qubit_index));
532 }
533 preimages
534}
535
536fn x_at(index: usize, length: usize) -> PauliUnitary<Vec<bool>, u8> {
537 let zeros = vec![false; length];
538 let mut bits = zeros.clone();
539 bits[index] = true;
540 PauliUnitary::from_bits(bits, zeros, 0u8)
541}
542
543fn z_at(index: usize, length: usize) -> PauliUnitary<Vec<bool>, u8> {
544 let zeros = vec![false; length];
545 let mut bits = zeros.clone();
546 bits[index] = true;
547 PauliUnitary::from_bits(zeros, bits, 0u8)
548}
549
550/// One and two-qubit Clifford gates tests
551macro_rules! generic_qubit_unitary_test_macro {
552 ($func:ident, $image_func:expr) => {
553 for num_qubits in 0..6 {
554 for qubit_index in 0..num_qubits {
555 generic_qubit_unitary_test(
556 num_qubits,
557 qubit_index,
558 CliffordUnitary::$func,
559 $image_func,
560 );
561 generic_qubit_unitary_test(
562 num_qubits,
563 qubit_index,
564 CliffordUnitaryModPauli::$func,
565 $image_func,
566 );
567 }
568 }
569 };
570}
571
572macro_rules! generic_two_qubit_unitary_test_macro {
573 ($func:ident, $image_func:expr) => {
574 for num_qubits in 0..6 {
575 for qubit_index1 in 0..num_qubits {
576 for qubit_index2 in 0..num_qubits {
577 if qubit_index1 != qubit_index2 {
578 generic_two_qubit_unitary_test(
579 num_qubits,
580 qubit_index1,
581 qubit_index2,
582 CliffordUnitary::$func,
583 $image_func,
584 );
585 generic_two_qubit_unitary_test(
586 num_qubits,
587 qubit_index1,
588 qubit_index2,
589 CliffordUnitaryModPauli::$func,
590 $image_func,
591 );
592 }
593 }
594 }
595 }
596 };
597}
598
599#[test]
600fn hadamard_test() {
601 generic_qubit_unitary_test_macro!(left_mul_hadamard, h_images);
602}
603
604#[test]
605fn root_x_test() {
606 generic_qubit_unitary_test_macro!(left_mul_root_x, root_x_images);
607 generic_qubit_unitary_test_macro!(left_mul_root_x_inverse, root_x_inv_images);
608}
609
610#[test]
611fn root_z_test() {
612 generic_qubit_unitary_test_macro!(left_mul_root_z, root_z_images);
613 generic_qubit_unitary_test_macro!(left_mul_root_z_inverse, root_z_inv_images);
614}
615
616#[test]
617fn root_y_test() {
618 generic_qubit_unitary_test_macro!(left_mul_root_y, root_y_images);
619 generic_qubit_unitary_test_macro!(left_mul_root_y_inverse, root_y_inv_images);
620}
621
622#[test]
623fn xyz_test() {
624 generic_qubit_unitary_test_macro!(left_mul_x, x_images);
625 generic_qubit_unitary_test_macro!(left_mul_y, y_images);
626 generic_qubit_unitary_test_macro!(left_mul_z, z_images);
627}
628
629#[test]
630fn cx_test() {
631 generic_two_qubit_unitary_test_macro!(left_mul_cx, cx_images);
632}
633
634#[test]
635fn cz_test() {
636 generic_two_qubit_unitary_test_macro!(left_mul_cz, cz_images);
637}
638
639#[test]
640fn swap_test() {
641 generic_two_qubit_unitary_test_macro!(left_mul_swap, swap_images);
642}
643
644#[test]
645fn prepare_bell_test() {
646 generic_two_qubit_unitary_test_macro!(left_mul_prepare_bell, prepare_bell_images);
647}
648
649type ImageTable = Vec<(
650 Vec<PositionedPauliObservable>,
651 Vec<PositionedPauliObservable>,
652)>;
653
654fn cx_images(c: usize, t: usize) -> ImageTable {
655 vec![
656 (vec![x(c)], vec![x(c), x(t)]),
657 (vec![z(c)], vec![z(c)]),
658 (vec![x(t)], vec![x(t)]),
659 (vec![z(t)], vec![z(c), z(t)]),
660 (vec![y(t)], vec![z(c), y(t)]),
661 (vec![y(c)], vec![y(c), x(t)]),
662 ]
663}
664
665fn cz_images(c: usize, t: usize) -> ImageTable {
666 vec![
667 (vec![x(c)], vec![x(c), z(t)]),
668 (vec![z(c)], vec![z(c)]),
669 (vec![x(t)], vec![z(c), x(t)]),
670 (vec![z(t)], vec![z(t)]),
671 (vec![y(t)], vec![z(c), y(t)]),
672 (vec![y(c)], vec![y(c), z(t)]),
673 ]
674}
675
676fn swap_images(q1: usize, q2: usize) -> ImageTable {
677 vec![
678 (vec![x(q1)], vec![x(q2)]),
679 (vec![z(q1)], vec![z(q2)]),
680 (vec![y(q1)], vec![y(q2)]),
681 (vec![-x(q1), z(q2)], vec![-x(q2), z(q1)]),
682 ]
683}
684
685fn prepare_bell_images(q1: usize, q2: usize) -> ImageTable {
686 vec![
687 (vec![z(q1)], vec![x(q1), x(q2)]),
688 (vec![x(q1)], vec![z(q1)]),
689 (vec![z(q2)], vec![z(q1), z(q2)]),
690 (vec![x(q2)], vec![x(q2)]),
691 (vec![z(q1), z(q2)], vec![-y(q1), y(q2)]),
692 ]
693}
694
695fn x_images(q: usize) -> ImageTable {
696 vec![
697 (vec![z(q)], vec![-z(q)]),
698 (vec![x(q)], vec![x(q)]),
699 (vec![y(q)], vec![-y(q)]),
700 ]
701}
702
703fn y_images(q: usize) -> ImageTable {
704 vec![
705 (vec![z(q)], vec![-z(q)]),
706 (vec![x(q)], vec![-x(q)]),
707 (vec![y(q)], vec![y(q)]),
708 ]
709}
710
711fn z_images(q: usize) -> ImageTable {
712 vec![
713 (vec![z(q)], vec![z(q)]),
714 (vec![x(q)], vec![-x(q)]),
715 (vec![y(q)], vec![-y(q)]),
716 ]
717}
718
719fn h_images(q: usize) -> ImageTable {
720 vec![
721 (vec![x(q)], vec![z(q)]),
722 (vec![z(q)], vec![x(q)]),
723 (vec![y(q)], vec![-y(q)]),
724 ]
725}
726
727fn root_z_images(q: usize) -> ImageTable {
728 vec![
729 (vec![z(q)], vec![z(q)]),
730 (vec![x(q)], vec![y(q)]),
731 (vec![y(q)], vec![-x(q)]),
732 ]
733}
734
735fn root_z_inv_images(q: usize) -> ImageTable {
736 vec![
737 (vec![z(q)], vec![z(q)]),
738 (vec![x(q)], vec![-y(q)]),
739 (vec![y(q)], vec![x(q)]),
740 ]
741}
742
743fn root_x_images(q: usize) -> ImageTable {
744 vec![
745 (vec![z(q)], vec![-y(q)]),
746 (vec![x(q)], vec![x(q)]),
747 (vec![y(q)], vec![z(q)]),
748 ]
749}
750
751fn root_x_inv_images(q: usize) -> ImageTable {
752 vec![
753 (vec![z(q)], vec![y(q)]),
754 (vec![x(q)], vec![x(q)]),
755 (vec![y(q)], vec![-z(q)]),
756 ]
757}
758
759fn root_y_images(q: usize) -> ImageTable {
760 vec![
761 (vec![z(q)], vec![x(q)]),
762 (vec![x(q)], vec![-z(q)]),
763 (vec![y(q)], vec![y(q)]),
764 ]
765}
766
767fn root_y_inv_images(q: usize) -> ImageTable {
768 vec![
769 (vec![z(q)], vec![-x(q)]),
770 (vec![x(q)], vec![z(q)]),
771 (vec![y(q)], vec![y(q)]),
772 ]
773}
774
775fn check_images<CliffordLike: TestableClifford>(c: &CliffordLike, image_table: &ImageTable) {
776 let sparse = sparse::<CliffordLike>;
777 for (p, im_p) in image_table {
778 assert!(c.image(&sparse(p)) == im_p.as_slice());
779 assert!(c.preimage(&sparse(im_p)) == p.as_slice());
780 }
781}
782
783fn generic_qubit_unitary_test<CliffordLike: TestableClifford>(
784 num_qubits: usize,
785 qubit_index: usize,
786 apply_transformation: impl FnOnce(&mut CliffordLike, usize),
787 images: impl Fn(usize) -> ImageTable,
788) {
789 let mut c = CliffordLike::identity(num_qubits);
790 apply_transformation(&mut c, qubit_index);
791 assert!(c.is_valid());
792 check_images(&c, &images(qubit_index));
793 for j in c.qubits() {
794 if j != qubit_index {
795 assert_identity_on(&c, j);
796 }
797 }
798}
799
800fn generic_two_qubit_unitary_test<CliffordLike: TestableClifford>(
801 num_qubits: usize,
802 qubit_index1: usize,
803 qubit_index2: usize,
804 apply_transformation: impl FnOnce(&mut CliffordLike, usize, usize),
805 images: impl Fn(usize, usize) -> ImageTable,
806) {
807 let mut c = CliffordLike::identity(num_qubits);
808 apply_transformation(&mut c, qubit_index1, qubit_index2);
809 assert!(c.is_valid());
810 check_images(&c, &images(qubit_index1, qubit_index2));
811 for j in c.qubits() {
812 if j != qubit_index1 && j != qubit_index2 {
813 assert_identity_on(&c, j);
814 }
815 }
816}
817
818fn assert_identity_on(c: &impl Clifford, qubit_index: usize) {
819 assert!(c.image_x(qubit_index).is_pauli_x(qubit_index));
820 assert!(c.image_z(qubit_index).is_pauli_z(qubit_index));
821 assert!(c.preimage_x(qubit_index).is_pauli_x(qubit_index));
822 assert!(c.preimage_z(qubit_index).is_pauli_z(qubit_index));
823}
824
825fn generic_prepare_bell_states_test<CliffordLike: TestableClifford>() {
826 let c = clifford_to_prepare_bell_states::<CliffordLike>(1);
827 assert!(c.is_valid());
828 assert!(c.image_z(0) == [x(0), x(1)].borrow());
829 assert!(c.image_z(1) == [z(0), z(1)].borrow());
830}
831
832#[test]
833fn clifford_to_prepare_bell_states_test() {
834 generic_prepare_bell_states_test::<CliffordUnitaryModPauli>();
835 generic_prepare_bell_states_test::<CliffordUnitary>();
836}
837
838fn is_identity_by_images(clifford: &impl Clifford) -> bool {
839 for qubit_id in 0..clifford.num_qubits() {
840 if !clifford.image_z(qubit_id).is_pauli_z(qubit_id) {
841 return false;
842 }
843 if !clifford.image_x(qubit_id).is_pauli_x(qubit_id) {
844 return false;
845 }
846 }
847 true
848}
849
850fn generic_clifford_identity_test<CliffordLike: Clifford>(max_qubits: usize) {
851 for j in 0..max_qubits {
852 let id = CliffordLike::identity(j);
853 assert!(id.is_valid());
854 assert!(id.is_identity());
855 assert!(is_identity_by_images(&id));
856 }
857}
858
859#[test]
860fn clifford_identity_test() {
861 let max_qubits = 10usize;
862 generic_clifford_identity_test::<CliffordUnitaryModPauli>(max_qubits);
863 generic_clifford_identity_test::<CliffordUnitary>(max_qubits);
864}
865
866fn two_qubit_clifford<CliffordLike: TestableClifford>(
867 transformation: impl FnOnce(&mut CliffordLike, usize, usize),
868) -> CliffordLike {
869 let mut res = CliffordLike::identity(2);
870 transformation(&mut res, 0, 1);
871 res
872}
873
874fn one_qubit_clifford<CliffordLike: TestableClifford>(
875 transformation: impl FnOnce(&mut CliffordLike, usize),
876) -> CliffordLike {
877 let mut res = CliffordLike::identity(1);
878 transformation(&mut res, 0);
879 res
880}
881
882fn clifford_examples<CliffordLike: TestableClifford>() -> Vec<CliffordLike> {
883 vec![
884 one_qubit_clifford(CliffordLike::left_mul_x),
885 one_qubit_clifford(CliffordLike::left_mul_y),
886 one_qubit_clifford(CliffordLike::left_mul_z),
887 one_qubit_clifford(CliffordLike::left_mul_hadamard),
888 one_qubit_clifford(CliffordLike::left_mul_root_x),
889 one_qubit_clifford(CliffordLike::left_mul_root_y),
890 one_qubit_clifford(CliffordLike::left_mul_root_z),
891 one_qubit_clifford(CliffordLike::left_mul_root_x_inverse),
892 one_qubit_clifford(CliffordLike::left_mul_root_y_inverse),
893 one_qubit_clifford(CliffordLike::left_mul_root_z_inverse),
894 two_qubit_clifford(CliffordLike::left_mul_cx),
895 two_qubit_clifford(CliffordLike::left_mul_cz),
896 two_qubit_clifford(CliffordLike::left_mul_swap),
897 two_qubit_clifford(CliffordLike::left_mul_prepare_bell),
898 ]
899}
900
901fn clifford_order2_examples<CliffordLike: TestableClifford>() -> Vec<CliffordLike> {
902 vec![
903 one_qubit_clifford(CliffordLike::left_mul_x),
904 one_qubit_clifford(CliffordLike::left_mul_y),
905 one_qubit_clifford(CliffordLike::left_mul_z),
906 one_qubit_clifford(CliffordLike::left_mul_hadamard),
907 two_qubit_clifford(CliffordLike::left_mul_cx),
908 two_qubit_clifford(CliffordLike::left_mul_cz),
909 two_qubit_clifford(CliffordLike::left_mul_swap),
910 ]
911}
912
913fn assert_images_consistent<CliffordLike: TestableClifford>(clifford: &CliffordLike) {
914 let sparse = sparse::<CliffordLike>;
915 for qubit_index in clifford.qubits() {
916 let im_x = clifford.image_x(qubit_index);
917 let im_z = clifford.image_z(qubit_index);
918 assert!(clifford.image_x_bits(&IndexSet::singleton(qubit_index)) == im_x);
919 assert!(clifford.image_z_bits(&IndexSet::singleton(qubit_index)) == im_z);
920 assert!(clifford.image(&sparse(&[x(qubit_index)])) == im_x);
921 assert!(clifford.image(&sparse(&[z(qubit_index)])) == im_z);
922 }
923}
924
925fn assert_preimages_consistent<CliffordLike: TestableClifford>(clifford: &CliffordLike) {
926 let sparse = sparse::<CliffordLike>;
927 for qubit_index in clifford.qubits() {
928 let pre_im_x = clifford.preimage_x(qubit_index);
929 let pre_im_z = clifford.preimage_z(qubit_index);
930 assert!(clifford.preimage_x_bits(&IndexSet::singleton(qubit_index)) == pre_im_x);
931 assert!(clifford.preimage_z_bits(&IndexSet::singleton(qubit_index)) == pre_im_z);
932 assert!(clifford.preimage(&sparse(&[x(qubit_index)])) == pre_im_x);
933 assert!(clifford.preimage(&sparse(&[z(qubit_index)])) == pre_im_z);
934 }
935}
936
937fn assert_inverse_and_multiply_are_consistent<CliffordLike: TestableClifford>(
938 clifford: &CliffordLike,
939) {
940 let inv = clifford.inverse();
941 assert!(inv.multiply_with(clifford).is_identity());
942 assert!(clifford.multiply_with(&inv).is_identity());
943}
944
945fn generic_consistency_test<CliffordLike: TestableClifford>() {
946 let test_cases = clifford_examples::<CliffordLike>();
947 for c in test_cases {
948 assert!(c.is_valid());
949 assert_images_consistent(&c);
950 assert_preimages_consistent(&c);
951 assert_inverse_and_multiply_are_consistent(&c);
952 }
953}
954
955#[test]
956pub fn clifford_consistency_test() {
957 generic_consistency_test::<CliffordUnitary>();
958 generic_consistency_test::<CliffordUnitaryModPauli>();
959}
960
961fn generic_multiply_test<CliffordLike: TestableClifford>() {
962 let examples = clifford_order2_examples::<CliffordLike>();
963 for clifford in examples {
964 let r = clifford.multiply_with(&clifford);
965 assert!(r.is_valid());
966 assert!(r.is_identity());
967 }
968}
969
970#[test]
971pub fn clifford_multiply_test() {
972 generic_multiply_test::<CliffordUnitary>();
973 generic_multiply_test::<CliffordUnitaryModPauli>();
974}
975
976fn compare_clifford_transformations<CliffordLike: TestableClifford>(
977 num_qubits: usize,
978 apply_transformation1: impl FnOnce(&mut CliffordLike),
979 apply_transformation2: impl FnOnce(&mut CliffordLike),
980) {
981 let mut c1 = CliffordLike::identity(num_qubits);
982 let mut c2 = CliffordLike::identity(num_qubits);
983 apply_transformation1(&mut c1);
984 apply_transformation2(&mut c2);
985 assert!(c1.is_valid());
986 assert!(c2.is_valid());
987 assert!(c1 == c2);
988}
989
990fn sparse<CliffordLike: TestableClifford>(
991 observable: &[PositionedPauliObservable],
992) -> <CliffordLike as TestableClifford>::SparsePauli {
993 CliffordLike::SparsePauli::from(observable)
994}
995
996fn apply_exp_zz<CliffordLike: TestableClifford>(clifford: &mut CliffordLike) {
997 clifford.left_mul_pauli_exp(&sparse::<CliffordLike>(&[z(0), z(1)]));
998}
999
1000fn apply_exp_xx<CliffordLike: TestableClifford>(clifford: &mut CliffordLike) {
1001 clifford.left_mul_pauli_exp(&sparse::<CliffordLike>(&[x(0), x(1)]));
1002}
1003
1004fn apply_cz<CliffordLike: TestableClifford>(clifford: &mut CliffordLike) {
1005 clifford.left_mul_cz(0, 1);
1006}
1007
1008fn apply_cz2<CliffordLike: TestableClifford>(clifford: &mut CliffordLike) {
1009 let z = |j| CliffordLike::SparsePauli::from(&[z(j)]);
1010 clifford.left_mul_controlled_pauli(&z(0), &z(1));
1011}
1012
1013fn apply_cz3<CliffordLike: TestableClifford>(clifford: &mut CliffordLike) {
1014 let sparse = sparse::<CliffordLike>;
1015 clifford.left_mul_pauli_exp(&sparse(&[-z(0)]));
1016 clifford.left_mul_pauli_exp(&sparse(&[z(0), z(1)]));
1017 clifford.left_mul_pauli_exp(&sparse(&[-z(1)]));
1018}
1019
1020fn apply_exp_zz_via_cx(clifford: &mut impl TestableClifford) {
1021 clifford.left_mul_cx(1, 0);
1022 clifford.left_mul_root_z_inverse(0);
1023 clifford.left_mul_cx(1, 0);
1024}
1025
1026fn apply_exp_xx_via_cx(clifford: &mut impl TestableClifford) {
1027 clifford.left_mul_cx(1, 0);
1028 clifford.left_mul_root_x_inverse(1);
1029 clifford.left_mul_cx(1, 0);
1030}
1031
1032fn root_xyz_identities<CliffordLike: TestableClifford>() {
1033 let mut clifford = CliffordLike::identity(3);
1034 let sparse = sparse::<CliffordLike>;
1035 clifford.left_mul_pauli_exp(&sparse(&[-z(0)]));
1036 clifford.left_mul_root_z_inverse(0);
1037 assert!(clifford.is_identity());
1038 clifford.left_mul_pauli_exp(&sparse(&[-x(1)]));
1039 clifford.left_mul_root_x_inverse(1);
1040 assert!(clifford.is_identity());
1041 clifford.left_mul_pauli_exp(&sparse(&[-y(2)]));
1042 clifford.left_mul_root_y_inverse(2);
1043 assert!(clifford.is_identity());
1044 clifford.left_mul_hadamard(2);
1045 clifford.left_mul_pauli_exp(&sparse(&[-z(2)]));
1046 clifford.left_mul_hadamard(2);
1047 clifford.left_mul_root_x_inverse(2);
1048 assert!(clifford.is_identity());
1049}
1050
1051fn generic_clifford_identities_test<CliffordLike: TestableClifford>() {
1052 compare_clifford_transformations::<CliffordLike>(2, apply_exp_zz_via_cx, apply_exp_zz);
1053 compare_clifford_transformations::<CliffordLike>(2, apply_exp_xx_via_cx, apply_exp_xx);
1054 compare_clifford_transformations::<CliffordLike>(2, apply_cz, apply_cz2);
1055 compare_clifford_transformations::<CliffordLike>(2, apply_cz, apply_cz3);
1056 root_xyz_identities::<CliffordLike>();
1057}
1058
1059fn controlled_pauli_via_pauli_exp_test(
1060 control: &[PositionedPauliObservable],
1061 target: &[PositionedPauliObservable],
1062) {
1063 let mut control_sparse: SparsePauli = control.into();
1064 let mut target_sparse: SparsePauli = target.into();
1065
1066 let mut p1p2 = control_sparse.clone();
1067 p1p2.mul_assign_right(&target_sparse);
1068
1069 let num_qubits =
1070 std::cmp::max(control_sparse.max_qubit_id(), target_sparse.max_qubit_id()).unwrap() + 1;
1071 let mut clifford1 = CliffordUnitary::identity(num_qubits);
1072 let mut clifford2 = CliffordUnitary::identity(num_qubits);
1073 clifford1.left_mul_controlled_pauli(&control_sparse, &target_sparse);
1074 clifford2.left_mul_pauli_exp(&p1p2);
1075 control_sparse.add_assign_phase_exp(2);
1076 clifford2.left_mul_pauli_exp(&control_sparse);
1077 target_sparse.add_assign_phase_exp(2);
1078 clifford2.left_mul_pauli_exp(&target_sparse);
1079 assert_eq!(clifford1, clifford2);
1080}
1081
1082#[test]
1083fn clifford_identities_test() {
1084 controlled_pauli_via_pauli_exp_test(&[z(0)], &[z(1)]);
1085 controlled_pauli_via_pauli_exp_test(&[z(0)], &[x(1)]);
1086 controlled_pauli_via_pauli_exp_test(&[y(0), x(1)], &[z(0), z(1)]);
1087 controlled_pauli_via_pauli_exp_test(&[x(0), x(1)], &[z(0), z(1)]);
1088 generic_clifford_identities_test::<CliffordUnitaryModPauli>();
1089 generic_clifford_identities_test::<CliffordUnitary>();
1090}
1091
1092fn generic_random_tensor_test<CliffordLike: TestableClifford>(
1093 num_qubits1: usize,
1094 num_qubits2: usize,
1095) {
1096 let id1 = CliffordLike::identity(num_qubits1);
1097 let id2 = CliffordLike::identity(num_qubits2);
1098 let r1 = CliffordLike::random(num_qubits1, &mut thread_rng());
1099 let r2 = CliffordLike::random(num_qubits2, &mut thread_rng());
1100 assert!((r1.tensor(&id2)).multiply_with(&(id1.tensor(&r2))) == r1.tensor(&r2));
1101}
1102
1103fn generic_tensor_test<CliffordLike: TestableClifford>() {
1104 let mut c1 = CliffordLike::identity(2);
1105 c1.left_mul_cx(0, 1);
1106 let mut c2 = CliffordLike::identity(2);
1107 c2.left_mul_cz(0, 1);
1108 let mut c1xc2 = CliffordLike::identity(4);
1109 c1xc2.left_mul_cx(0, 1);
1110 c1xc2.left_mul_cz(2, 3);
1111 assert!(c1xc2 == c1.tensor(&c2));
1112
1113 for _ in 0..10 {
1114 generic_random_tensor_test::<CliffordLike>(5, 10);
1115 }
1116}
1117
1118#[test]
1119fn tensor_test() {
1120 generic_tensor_test::<CliffordUnitary>();
1121 generic_tensor_test::<CliffordUnitaryModPauli>();
1122}
1123
1124fn are_bits_equal_to_col(bitstring: &impl Bitwise, matrix: &BitMatrix, col: usize) -> bool {
1125 for j in 0..matrix.columncount() {
1126 if matrix[(j, col)] != bitstring.index(j) {
1127 return false;
1128 }
1129 }
1130 true
1131}
1132
1133/// # Panics
1134///
1135/// Will panic
1136pub fn random_bitmatrix(rowcount: usize, columncount: usize) -> BitMatrix {
1137 let mut matrix = BitMatrix::with_shape(rowcount, columncount);
1138 let mut bits = std::iter::from_fn(move || Some(rand::Rng::gen::<bool>(&mut thread_rng())));
1139 for row_index in 0..rowcount {
1140 for column_index in 0..columncount {
1141 matrix.set((row_index, column_index), bits.next().expect("boom"));
1142 }
1143 }
1144 matrix
1145}
1146
1147#[test]
1148fn css_clifford_test() {
1149 let mut num_tests = 0;
1150 while num_tests < 100 {
1151 let num_qubits = 10;
1152 let a = random_bitmatrix(num_qubits, num_qubits);
1153 if a.rank() == a.rowcount() {
1154 num_tests += 1;
1155 let a_inv_t = a.inverted().transposed();
1156 let c = CliffordUnitary::from_css_preimage_indicators(&a, &a_inv_t);
1157 assert!(c.is_valid());
1158 for k in c.qubits() {
1159 assert!(c.preimage_z(k).x_bits().is_zero());
1160 assert!(c.preimage_z(k).z_bits() == &a_inv_t.row(k));
1161 assert!(c.image_z(k).x_bits().is_zero());
1162 assert!(are_bits_equal_to_col(c.image_z(k).z_bits(), &a, k));
1163
1164 assert!(c.preimage_x(k).z_bits().is_zero());
1165 assert!(c.preimage_x(k).x_bits() == &a.row(k));
1166 assert!(c.image_x(k).z_bits().is_zero());
1167 assert!(are_bits_equal_to_col(c.image_x(k).x_bits(), &a_inv_t, k));
1168 }
1169 }
1170 }
1171}
1172
1173fn left_mul_clifford_generic_test<CliffordLike: TestableClifford>() {
1174 let mut clifford1 = CliffordLike::identity(4);
1175 let mut clifford2 = CliffordLike::identity(2);
1176 clifford1.left_mul_cx(1, 2);
1177 clifford2.left_mul_cx(0, 1);
1178 clifford1.left_mul_clifford(&clifford2, &[1, 2]);
1179 assert! {clifford1.is_identity()}
1180 clifford1.left_mul_cx(2, 3);
1181 clifford1.left_mul_clifford(&clifford2, &[2, 3]);
1182}
1183
1184#[test]
1185fn left_mul_clifford_test() {
1186 left_mul_clifford_generic_test::<CliffordUnitary>();
1187 left_mul_clifford_generic_test::<CliffordUnitaryModPauli>();
1188}
1189
1190fn left_mul_permutation_generic_test<CliffordLike: TestableClifford>() {
1191 let mut clifford1 = CliffordLike::identity(3);
1192 clifford1.left_mul_swap(0, 1);
1193 clifford1.left_mul_swap(1, 2);
1194 clifford1.left_mul_permutation(&[2, 0, 1], &[0, 1, 2]);
1195 assert!(clifford1.is_identity());
1196}
1197
1198#[test]
1199fn left_mul_permutation_test() {
1200 left_mul_permutation_generic_test::<CliffordUnitary>();
1201 left_mul_permutation_generic_test::<CliffordUnitaryModPauli>();
1202}
1203
1204fn format_string_roundtrip_generic_test<CliffordLike: TestableClifford>(clifford: &CliffordLike) {
1205 let sparse_str = format!("{clifford}");
1206 let dense_str = format!("{clifford:#}");
1207 let clifford1 = sparse_str.parse::<CliffordLike>().expect(&sparse_str);
1208 let clifford2 = dense_str.parse::<CliffordLike>().expect(&dense_str);
1209 assert_eq!(clifford, &clifford1);
1210 assert_eq!(clifford, &clifford2);
1211}
1212
1213fn random_diagonal_clifford<CliffordLike: TestableClifford>(qubit_count: usize) -> CliffordLike {
1214 let generators = diagonal_operations(qubit_count);
1215 random_clifford_via_operations_sampling(qubit_count, qubit_count * qubit_count, &generators)
1216}
1217
1218fn random_css_clifford<CliffordLike: TestableClifford>(qubit_count: usize) -> CliffordLike {
1219 let generators = css_operations(qubit_count);
1220 random_clifford_via_operations_sampling(qubit_count, qubit_count * qubit_count, &generators)
1221}
1222
1223fn generic_diagonal_clifford_test<CliffordLike: TestableClifford>(c: &CliffordLike) {
1224 use XOrZ::{X, Z};
1225 assert!(c.is_diagonal(Z));
1226 assert!(c.inverse().is_diagonal(Z));
1227
1228 let qubit_count = c.num_qubits();
1229 let mut c2 = CliffordLike::identity(qubit_count);
1230 transverse_h(&mut c2);
1231 c2.left_mul_clifford(c, &c.qubits().collect::<Vec<_>>());
1232 assert!(c2.is_diagonal_resource_encoder(Z));
1233 let c3 = c2.unitary_from_diagonal_resource_state(Z).unwrap();
1234 assert!(c3.is_valid());
1235 assert!(c3.is_diagonal(Z));
1236 for qubit_index in c3.qubits() {
1237 assert_eq!(c3.image_x(qubit_index), c2.image_z(qubit_index));
1238 }
1239
1240 transverse_h(&mut c2);
1241 assert!(c2.is_diagonal(X));
1242 assert!(c2.inverse().is_diagonal(X));
1243 assert!(c2.is_diagonal_resource_encoder(X));
1244 let c4 = c2.unitary_from_diagonal_resource_state(X).unwrap();
1245 assert!(c4.is_valid());
1246 assert!(c4.is_diagonal(X));
1247 for qubit_index in c4.qubits() {
1248 assert_eq!(c4.image_z(qubit_index), c2.image_z(qubit_index));
1249 }
1250}
1251
1252fn generic_is_css_clifford_test<CliffordLike: TestableClifford>(c: &CliffordLike) {
1253 assert!(c.is_css());
1254}
1255
1256fn transverse_h<CliffordLike: TestableClifford>(clifford: &mut CliffordLike) {
1257 for qubit_index in clifford.qubits() {
1258 clifford.left_mul_hadamard(qubit_index);
1259 }
1260}
1261
1262fn check_left_mul_root_and_apply_root_are_consistent(
1263 qubit_count: usize,
1264 left_mul_root: fn(&mut CliffordUnitary, usize),
1265 apply_root: fn(&mut DensePauli, usize),
1266) {
1267 let mut clifford = CliffordUnitary::identity(qubit_count);
1268 for target_qubit in 0..qubit_count {
1269 let mut image = clifford.image_z(target_qubit);
1270 left_mul_root(&mut clifford, target_qubit);
1271 apply_root(&mut image, target_qubit);
1272 assert_eq!(image, clifford.image_z(target_qubit));
1273 }
1274}