microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/replace-qsharp-with-qdk-python-tests

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/paulimer/src/clifford/clifford_impl.rs

2216lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use crate::quantum_core::{y, Axis};
5use sorted_iter::assume::AssumeSortedByItemExt;
6use sorted_iter::SortedIterator;
7
8use super::generic_algos::support_restricted_z_images_from_support_complement;
9use super::{
10 Bitwise, Clifford, CliffordModPauliBatch, CliffordMutable, CliffordStringParsingError,
11 CliffordUnitary, CliffordUnitaryModPauli, ControlledPauli, Hadamard, MutablePreImages,
12 PauliExponent, PreimageViews, Swap, XOrZ,
13};
14
15use crate::bits::bitmatrix::{
16 are_zero_rows, is_zero_padded_identity, is_zero_padded_symmetric, BitMatrix, Column,
17};
18use crate::bits::{
19 BitVec, BitView, BitwiseBinaryOps, IndexAssignable, IndexSet, MutableBitView,
20 WORD_COUNT_DEFAULT,
21};
22use crate::pauli::generic::PhaseExponent;
23use crate::pauli::{
24 apply_pauli_exponent, apply_root_x, are_mutually_commuting, dense_from, remapped_sparse,
25 DensePauli, DensePauliProjective, Pauli, PauliBinaryOps, PauliBits, PauliMutable, PauliUnitary,
26 PauliUnitaryProjective, SparsePauli, SparsePauliProjective,
27};
28use crate::{assert_1q_gate, assert_2q_gate, UnitaryOp};
29use crate::{subscript_digits, NeutralElement, Tuple2x2, Tuple4, Tuple4x2, Tuple8};
30
31use core::fmt;
32use std::collections::BTreeSet;
33use std::fmt::{Debug, Display};
34use std::iter::{zip, IntoIterator};
35use std::ops::Mul;
36use std::str::FromStr;
37use std::vec;
38
39// Utils
40
41fn concat2<T>(ab: Tuple2x2<T>) -> Tuple4<T> {
42 (ab.0 .0, ab.0 .1, ab.1 .0, ab.1 .1)
43}
44
45fn split2<T>(ab: Tuple4<T>) -> Tuple2x2<T> {
46 ((ab.0, ab.1), (ab.2, ab.3))
47}
48
49fn concat4<T>(a: Tuple4x2<T>) -> Tuple8<T> {
50 (
51 a.0 .0, a.0 .1, a.1 .0, a.1 .1, a.2 .0, a.2 .1, a.3 .0, a.3 .1,
52 )
53}
54
55fn split4<T>(abcd: Tuple8<T>) -> Tuple4x2<T> {
56 (
57 (abcd.0, abcd.1),
58 (abcd.2, abcd.3),
59 (abcd.4, abcd.5),
60 (abcd.6, abcd.7),
61 )
62}
63
64/// Does not check if indices are distinct
65unsafe fn tuple2_from_vec<T>(vec: &mut Vec<T>, index: (usize, usize)) -> (&mut T, &mut T) {
66 let ptr = vec.as_mut_ptr();
67 unsafe { (&mut *ptr.add(index.0), &mut *ptr.add(index.1)) }
68}
69
70/// Does not check if indices are distinct
71unsafe fn tuple4_from_vec<T>(
72 vec: &mut Vec<T>,
73 index: (usize, usize, usize, usize),
74) -> (&mut T, &mut T, &mut T, &mut T) {
75 let ptr = vec.as_mut_ptr();
76 unsafe {
77 (
78 &mut *ptr.add(index.0),
79 &mut *ptr.add(index.1),
80 &mut *ptr.add(index.2),
81 &mut *ptr.add(index.3),
82 )
83 }
84}
85
86// Neutral element trait
87
88fn set_identity_pre_images<PauliLike: Pauli, CliffordLike: Clifford + MutablePreImages>(
89 clifford: &mut CliffordLike,
90) where
91 for<'life> <CliffordLike as MutablePreImages>::PreImageViewMut<'life>:
92 PauliBinaryOps<PauliLike>,
93{
94 for index in 0..clifford.num_qubits() {
95 debug_assert!(clifford.preimage_x_view_mut(index).is_identity());
96 clifford.preimage_x_view_mut(index).mul_assign_left_x(index);
97 debug_assert!(clifford.preimage_z_view_mut(index).is_identity());
98 clifford
99 .preimage_z_view_mut(index)
100 .mul_assign_right_z(index);
101 }
102}
103
104impl NeutralElement for CliffordUnitary {
105 type NeutralElementType = CliffordUnitary;
106
107 fn neutral_element(&self) -> Self::NeutralElementType {
108 Self::identity(self.num_qubits())
109 }
110
111 fn default_size_neutral_element() -> Self::NeutralElementType {
112 Self::identity(0)
113 }
114
115 fn neutral_element_of_size(size: usize) -> Self::NeutralElementType {
116 Self::identity(size)
117 }
118}
119
120impl NeutralElement for CliffordUnitaryModPauli {
121 type NeutralElementType = CliffordUnitaryModPauli;
122
123 fn neutral_element(&self) -> Self::NeutralElementType {
124 Self::identity(self.num_qubits())
125 }
126
127 fn default_size_neutral_element() -> Self::NeutralElementType {
128 Self::identity(0)
129 }
130
131 fn neutral_element_of_size(size: usize) -> Self::NeutralElementType {
132 Self::identity(size)
133 }
134}
135
136// Clifford trait
137
138fn projective_image_at<const WORD_COUNT: usize>(
139 bits: &BitMatrix<WORD_COUNT>,
140 dimension: usize,
141 qubit_index: usize,
142 x_bits_start: usize,
143 z_bits_start: usize,
144) -> PauliUnitaryProjective<Column<'_, WORD_COUNT>> {
145 let column = bits.column(qubit_index);
146 let x_bits = column.slice(x_bits_start..dimension + x_bits_start);
147 let z_bits = column.slice(z_bits_start..dimension + z_bits_start);
148 PauliUnitaryProjective::<Column<WORD_COUNT>>::from_bits(x_bits, z_bits)
149}
150
151fn projective_x_image_at<const WORD_COUNT: usize>(
152 bits: &BitMatrix<WORD_COUNT>,
153 dimension: usize,
154 qubit_index: usize,
155) -> PauliUnitaryProjective<Column<'_, WORD_COUNT>> {
156 projective_image_at(
157 bits,
158 dimension,
159 qubit_index,
160 z_of_preimage_z_offset(dimension),
161 z_of_preimage_x_offset(dimension),
162 )
163}
164
165fn projective_z_image_at<const WORD_COUNT: usize>(
166 bits: &BitMatrix<WORD_COUNT>,
167 dimension: usize,
168 qubit_index: usize,
169) -> PauliUnitaryProjective<Column<'_, WORD_COUNT>> {
170 projective_image_at(
171 bits,
172 dimension,
173 qubit_index,
174 x_of_preimage_z_offset(dimension),
175 x_of_preimage_x_offset(dimension),
176 )
177}
178
179/// index of `preimage_phase_exponents` that describes phase of preimage x_`index`
180#[inline]
181fn phase_of_preimage_x(index: usize) -> usize {
182 2 * index
183}
184
185/// index of `preimage_phase_exponents` that describes phase of preimage z_`index`
186#[inline]
187fn phase_of_preimage_z(index: usize) -> usize {
188 2 * index + 1
189}
190
191/// first row where x bits of preimage of x of a clifford unitary are stored
192#[inline]
193fn x_of_preimage_x_offset(_dimension: usize) -> usize {
194 0
195}
196
197/// first row where z bits of preimage of x of a clifford unitary are stored
198#[inline]
199fn z_of_preimage_x_offset(dimension: usize) -> usize {
200 dimension
201}
202
203/// first row where x bits of preimage of z of a clifford unitary are stored
204#[inline]
205fn x_of_preimage_z_offset(dimension: usize) -> usize {
206 2 * dimension
207}
208
209/// first row where z bits of preimage of z of a clifford unitary are stored
210#[inline]
211fn z_of_preimage_z_offset(dimension: usize) -> usize {
212 3 * dimension
213}
214
215/// index of row of bits that describes `z_bits` of preimage z_`index`
216#[inline]
217fn z_of_pz(dimension: usize, index: usize) -> usize {
218 index + z_of_preimage_z_offset(dimension)
219}
220
221/// index of row of bits that describes `x_bits` of preimage z_`index`
222#[inline]
223fn x_of_pz(dimension: usize, index: usize) -> usize {
224 index + x_of_preimage_z_offset(dimension)
225}
226
227/// index of row of bits that describes `z_bits` of preimage x_`index`
228#[inline]
229fn z_of_px(dimension: usize, index: usize) -> usize {
230 index + z_of_preimage_x_offset(dimension)
231}
232
233/// index of row of bits that describes `x_bits` of preimage x_`index`
234#[inline]
235fn x_of_px(dimension: usize, index: usize) -> usize {
236 index + x_of_preimage_x_offset(dimension)
237}
238
239#[inline]
240fn x_preimage_rows_ids(dimension: usize, qubit_id: usize) -> (usize, usize) {
241 (x_of_px(dimension, qubit_id), z_of_px(dimension, qubit_id))
242}
243
244#[inline]
245fn z_preimage_rows_ids(dimension: usize, qubit_id: usize) -> (usize, usize) {
246 (x_of_pz(dimension, qubit_id), z_of_pz(dimension, qubit_id))
247}
248
249#[inline]
250fn xz_preimage_rows_ids(dimension: usize, qubit_id: usize) -> ((usize, usize), (usize, usize)) {
251 (
252 x_preimage_rows_ids(dimension, qubit_id),
253 z_preimage_rows_ids(dimension, qubit_id),
254 )
255}
256
257macro_rules! clifford_common_impl {
258 () => {
259 fn preimage_x_bits(&self, x_bits: &impl Bitwise) -> Self::DensePauli {
260 let mut res = Self::DensePauli::neutral_element_of_size(self.num_qubits());
261 super::generic_algos::mul_assign_right_clifford_preimage_x_bits(&mut res, self, x_bits);
262 res
263 }
264
265 fn preimage_z_bits(&self, z_bits: &impl Bitwise) -> Self::DensePauli {
266 let mut res = Self::DensePauli::neutral_element_of_size(self.num_qubits());
267 super::generic_algos::mul_assign_right_clifford_preimage_z_bits(&mut res, self, z_bits);
268 res
269 }
270
271 fn preimage<PauliLike: Pauli<PhaseExponentValue = Self::PhaseExponentValue>>(
272 &self,
273 pauli: &PauliLike,
274 ) -> Self::DensePauli {
275 let mut res = Self::DensePauli::neutral_element_of_size(self.num_qubits());
276 super::generic_algos::mul_assign_right_clifford_preimage(&mut res, self, pauli);
277 res
278 }
279
280 fn num_qubits(&self) -> usize {
281 self.bits.columncount()
282 }
283
284 fn is_valid(&self) -> bool {
285 super::generic_algos::is_valid_clifford(self)
286 }
287
288 fn is_identity(&self) -> bool {
289 super::generic_algos::clifford_is_identity(self)
290 }
291
292 fn multiply_with(&self, rhs: &Self) -> Self {
293 super::generic_algos::clifford_multiply_with(self, &rhs)
294 }
295
296 fn from_preimages(preimages: &[Self::DensePauli]) -> Self {
297 super::generic_algos::clifford_from_preimages(preimages.into_iter())
298 }
299
300 fn preimage_x(&self, qubit_index: usize) -> Self::DensePauli {
301 self.preimage_x_view(qubit_index).into()
302 }
303
304 fn preimage_z(&self, qubit_index: usize) -> Self::DensePauli {
305 self.preimage_z_view(qubit_index).into()
306 }
307
308 fn random(num_qubits: usize, random_number_generator: &mut impl rand::Rng) -> Self {
309 let mut res = Self::identity(num_qubits);
310 let mut random_pauli: Self::DensePauli =
311 Self::DensePauli::neutral_element_of_size(num_qubits);
312 for _ in 0..2 * num_qubits + 1 {
313 random_pauli.set_random_order_two(num_qubits, random_number_generator);
314 res.left_mul_pauli_exp(&random_pauli);
315 }
316 res
317 }
318
319 fn identity(num_qubits: usize) -> Self {
320 let mut res = Self::zero(num_qubits);
321 set_identity_pre_images::<Self::DensePauli, Self>(&mut res);
322 res
323 }
324
325 fn from_css_preimage_indicators(
326 x_indicators: &BitMatrix,
327 z_indicators: &BitMatrix,
328 ) -> Self {
329 super::generic_algos::clifford_from_css_preimage_indicators(x_indicators, z_indicators)
330 }
331
332 fn tensor(&self, rhs: &Self) -> Self {
333 super::generic_algos::clifford_tensored(self, rhs)
334 }
335
336 fn is_diagonal(&self, axis: XOrZ) -> bool {
337 match axis {
338 XOrZ::X => is_x_diagonal(self),
339 XOrZ::Z => is_z_diagonal(self),
340 }
341 }
342
343 fn is_diagonal_resource_encoder(&self, axis: XOrZ) -> bool {
344 match axis {
345 XOrZ::X => is_x_diagonal_resource_encoder(self).is_some(),
346 XOrZ::Z => is_z_diagonal_resource_encoder(self).is_some(),
347 }
348 }
349
350 fn is_css(&self) -> bool {
351 is_css_clifford(self)
352 }
353
354 fn symplectic_matrix(&self) -> BitMatrix {
355 let qubit_count = self.num_qubits();
356 let mut res = BitMatrix::zeros(2 * qubit_count, 2 * qubit_count);
357 for qubit in self.qubits() {
358 let (x, z) = (self.preimage_x_view(qubit), self.preimage_z_view(qubit));
359 res.row_mut(qubit)
360 .assign_with_offset(x.x_bits(), 0, qubit_count);
361 res.row_mut(qubit)
362 .assign_with_offset(x.z_bits(), qubit_count, qubit_count);
363 res.row_mut(qubit + qubit_count)
364 .assign_with_offset(z.x_bits(), 0, qubit_count);
365 res.row_mut(qubit + qubit_count).assign_with_offset(
366 z.z_bits(),
367 qubit_count,
368 qubit_count,
369 );
370 }
371 res
372 }
373 };
374}
375
376impl Clifford for CliffordUnitary {
377 type PhaseExponentValue = u8;
378 type DensePauli = PauliUnitary<BitVec<WORD_COUNT_DEFAULT>, u8>;
379
380 fn image_x(&self, qubit_index: usize) -> Self::DensePauli {
381 let mut image_up_to_phase = self.x_image_view_up_to_phase(0).neutral_element();
382 image_up_to_phase.mul_assign_left(&self.x_image_view_up_to_phase(qubit_index));
383 super::generic_algos::clifford_image_with_phase(self, image_up_to_phase.to_xz_bits())
384 }
385
386 fn image_z(&self, qubit_index: usize) -> Self::DensePauli {
387 let mut image_up_to_phase = self.z_image_view_up_to_phase(0).neutral_element();
388 image_up_to_phase.mul_assign_left(&self.z_image_view_up_to_phase(qubit_index));
389 super::generic_algos::clifford_image_with_phase(self, image_up_to_phase.to_xz_bits())
390 }
391
392 fn image_x_bits(&self, x_bits: &impl Bitwise) -> Self::DensePauli {
393 let mut image_up_to_phase = self.x_image_view_up_to_phase(0).neutral_element();
394 super::generic_algos::mul_assign_right_clifford_image_x_bits_up_to_phase(
395 &mut image_up_to_phase,
396 self,
397 x_bits,
398 );
399 super::generic_algos::clifford_image_with_phase(self, image_up_to_phase.to_xz_bits())
400 }
401
402 fn image_z_bits(&self, z_bits: &impl Bitwise) -> Self::DensePauli {
403 let mut image_up_to_phase = self.x_image_view_up_to_phase(0).neutral_element();
404 super::generic_algos::mul_assign_right_clifford_image_z_bits_up_to_phase(
405 &mut image_up_to_phase,
406 self,
407 z_bits,
408 );
409 super::generic_algos::clifford_image_with_phase(self, image_up_to_phase.to_xz_bits())
410 }
411
412 fn image<PauliLike: Pauli<PhaseExponentValue = Self::PhaseExponentValue>>(
413 &self,
414 pauli: &PauliLike,
415 ) -> Self::DensePauli {
416 let mut image_up_to_phase = self.x_image_view_up_to_phase(0).neutral_element();
417 super::generic_algos::mul_assign_right_clifford_image_up_to_phase(
418 &mut image_up_to_phase,
419 self,
420 pauli,
421 );
422 let mut res =
423 super::generic_algos::clifford_image_with_phase(self, image_up_to_phase.to_xz_bits());
424 res.mul_assign_phase_from(pauli);
425 res
426 }
427
428 fn unitary_from_diagonal_resource_state(&self, axis: XOrZ) -> Option<Self> {
429 if let Some(mut res) = blocks_from_diagonal_resource_state(self, axis) {
430 // make sure pre-images are hermitian
431 for qubit_index in self.qubits() {
432 if !res.preimage_z(qubit_index).is_order_two() {
433 res.preimage_z_view_mut(qubit_index).add_assign_phase_exp(1);
434 }
435 if !res.preimage_x(qubit_index).is_order_two() {
436 res.preimage_x_view_mut(qubit_index).add_assign_phase_exp(1);
437 }
438 }
439 // make sure images signs match
440 debug_assert!(res.is_valid());
441 match axis {
442 XOrZ::X => {
443 for qubit_index in self.qubits() {
444 let mut im_z = self.image_z(qubit_index);
445 im_z.mul_assign_left(&res.image_z(qubit_index));
446 if im_z.xz_phase_exponent() != 0 {
447 res.left_mul_pauli(&res.image_x(qubit_index));
448 }
449 }
450 }
451 XOrZ::Z => {
452 for qubit_index in self.qubits() {
453 let mut im_z = self.image_z(qubit_index);
454 im_z.mul_assign_left(&res.image_x(qubit_index));
455 if im_z.xz_phase_exponent() != 0 {
456 res.left_mul_pauli(&res.image_z(qubit_index));
457 }
458 }
459 }
460 }
461 Some(res)
462 } else {
463 None
464 }
465 }
466
467 fn zero(num_qubits: usize) -> Self {
468 CliffordUnitary {
469 bits: BitMatrix::<WORD_COUNT_DEFAULT>::zeros(num_qubits * 4, num_qubits),
470 preimage_phase_exponents: vec![0u8; 2 * num_qubits],
471 }
472 }
473
474 clifford_common_impl! {}
475
476 fn inverse(&self) -> Self {
477 inverse_with_signs(self)
478 }
479}
480
481impl PreimageViews for CliffordUnitary {
482 type PhaseExponentValue = u8;
483 type PreImageView<'life> = PauliUnitary<BitView<'life, WORD_COUNT_DEFAULT>, &'life u8>;
484 type ImageViewUpToPhase<'life> = PauliUnitaryProjective<Column<'life, WORD_COUNT_DEFAULT>>;
485
486 fn preimage_x_view(&self, qubit_index: usize) -> Self::PreImageView<'_> {
487 let xz_bits = self
488 .bits
489 .rows2(x_preimage_rows_ids(self.num_qubits(), qubit_index));
490 Self::PreImageView::from_bits_tuple(
491 xz_bits,
492 &self.preimage_phase_exponents[phase_of_preimage_x(qubit_index)],
493 )
494 }
495
496 fn preimage_z_view(&self, qubit_index: usize) -> Self::PreImageView<'_> {
497 let xz_bits = self
498 .bits
499 .rows2(z_preimage_rows_ids(self.num_qubits(), qubit_index));
500 Self::PreImageView::from_bits_tuple(
501 xz_bits,
502 &self.preimage_phase_exponents[phase_of_preimage_z(qubit_index)],
503 )
504 }
505
506 fn x_image_view_up_to_phase(&self, qubit_index: usize) -> Self::ImageViewUpToPhase<'_> {
507 projective_x_image_at(&self.bits, self.num_qubits(), qubit_index)
508 }
509
510 fn z_image_view_up_to_phase(&self, qubit_index: usize) -> Self::ImageViewUpToPhase<'_> {
511 projective_z_image_at(&self.bits, self.num_qubits(), qubit_index)
512 }
513}
514
515fn inverse_with_signs<CliffordLikeFrom: Clifford, CliffordLikeTo>(
516 from: &CliffordLikeFrom,
517) -> CliffordLikeTo
518where
519 CliffordLikeTo: Clifford + PreimageViews + MutablePreImages,
520 for<'life> <CliffordLikeTo as MutablePreImages>::PreImageViewMut<'life>:
521 PauliBinaryOps<CliffordLikeFrom::DensePauli>,
522{
523 let mut res = CliffordLikeTo::identity(from.num_qubits());
524 for qubit_index in 0..from.num_qubits() {
525 res.preimage_x_view_mut(qubit_index)
526 .assign(&from.image_x(qubit_index));
527 res.preimage_z_view_mut(qubit_index)
528 .assign(&from.image_z(qubit_index));
529 }
530 res
531}
532
533impl Clifford for CliffordUnitaryModPauli {
534 type PhaseExponentValue = ();
535 type DensePauli = PauliUnitaryProjective<BitVec<WORD_COUNT_DEFAULT>>;
536 // type SparsePauli = PauliUnitaryProjective<IndexSet>;
537
538 fn image_x(&self, qubit_index: usize) -> Self::DensePauli {
539 let mut res = Self::DensePauli::neutral_element_of_size(self.num_qubits());
540 res.assign(&self.x_image_view_up_to_phase(qubit_index));
541 res
542 }
543
544 fn image_z(&self, qubit_index: usize) -> Self::DensePauli {
545 let mut res = Self::DensePauli::neutral_element_of_size(self.num_qubits());
546 res.assign(&self.z_image_view_up_to_phase(qubit_index));
547 res
548 }
549
550 fn image_x_bits(&self, x_bits: &impl Bitwise) -> Self::DensePauli {
551 let mut res = Self::DensePauli::neutral_element_of_size(self.num_qubits());
552 super::generic_algos::mul_assign_right_clifford_image_x_bits_up_to_phase(
553 &mut res, self, x_bits,
554 );
555 res
556 }
557
558 fn image_z_bits(&self, z_bits: &impl Bitwise) -> Self::DensePauli {
559 let mut res = Self::DensePauli::neutral_element_of_size(self.num_qubits());
560 super::generic_algos::mul_assign_right_clifford_image_z_bits_up_to_phase(
561 &mut res, self, z_bits,
562 );
563 res
564 }
565
566 fn image<PauliLike: Pauli<PhaseExponentValue = Self::PhaseExponentValue>>(
567 &self,
568 pauli: &PauliLike,
569 ) -> Self::DensePauli {
570 let mut res = Self::DensePauli::neutral_element_of_size(self.num_qubits());
571 super::generic_algos::mul_assign_right_clifford_image_up_to_phase(&mut res, self, pauli);
572 res
573 }
574
575 fn zero(num_qubits: usize) -> Self {
576 CliffordUnitaryModPauli {
577 bits: BitMatrix::<WORD_COUNT_DEFAULT>::zeros(num_qubits * 4, num_qubits),
578 }
579 }
580
581 clifford_common_impl! {}
582
583 fn inverse(&self) -> Self {
584 super::generic_algos::clifford_inverse_up_to_signs(self)
585 }
586
587 fn unitary_from_diagonal_resource_state(&self, axis: XOrZ) -> Option<Self> {
588 blocks_from_diagonal_resource_state(self, axis)
589 }
590}
591
592impl PreimageViews for CliffordUnitaryModPauli {
593 type PreImageView<'life> = PauliUnitaryProjective<BitView<'life, WORD_COUNT_DEFAULT>>;
594 type ImageViewUpToPhase<'life> = PauliUnitaryProjective<Column<'life, WORD_COUNT_DEFAULT>>;
595
596 fn preimage_x_view(&self, qubit_index: usize) -> Self::PreImageView<'_> {
597 let xz_bits = self
598 .bits
599 .rows2(x_preimage_rows_ids(self.num_qubits(), qubit_index));
600 Self::PreImageView::from_bits_tuple(xz_bits)
601 }
602
603 fn preimage_z_view(&self, qubit_index: usize) -> Self::PreImageView<'_> {
604 let xz_bits = self
605 .bits
606 .rows2(z_preimage_rows_ids(self.num_qubits(), qubit_index));
607 Self::PreImageView::from_bits_tuple(xz_bits)
608 }
609
610 fn x_image_view_up_to_phase(&self, qubit_index: usize) -> Self::ImageViewUpToPhase<'_> {
611 projective_x_image_at(&self.bits, self.num_qubits(), qubit_index)
612 }
613
614 fn z_image_view_up_to_phase(&self, qubit_index: usize) -> Self::ImageViewUpToPhase<'_> {
615 projective_z_image_at(&self.bits, self.num_qubits(), qubit_index)
616 }
617
618 type PhaseExponentValue = ();
619}
620
621// CliffordMutable trait
622
623fn swap_clifford_bits<const WORD_COUNT: usize>(
624 dimension: usize,
625 qubit1_id: usize,
626 qubit2_id: usize,
627 bits: &mut BitMatrix<WORD_COUNT>,
628) {
629 let ((a1, b1), (c1, d1)) = xz_preimage_rows_ids(dimension, qubit1_id);
630 let ((a2, b2), (c2, d2)) = xz_preimage_rows_ids(dimension, qubit2_id);
631 bits.swap_rows(a1, a2);
632 bits.swap_rows(b1, b2);
633 bits.swap_rows(c1, c2);
634 bits.swap_rows(d1, d2);
635}
636
637fn hadamard_clifford_bits<const WORD_COUNT: usize>(
638 dimension: usize,
639 qubit_id: usize,
640 bits: &mut BitMatrix<WORD_COUNT>,
641) {
642 let (x1, x2) = x_preimage_rows_ids(dimension, qubit_id);
643 let (z1, z2) = z_preimage_rows_ids(dimension, qubit_id);
644 bits.swap_rows(x1, z1);
645 bits.swap_rows(x2, z2);
646}
647
648impl MutablePreImages for CliffordUnitaryModPauli
649where
650 for<'life> PauliUnitaryProjective<MutableBitView<'life, WORD_COUNT_DEFAULT>>:
651 PauliBinaryOps + Pauli<PhaseExponentValue = ()>,
652{
653 type PhaseExponentValue = ();
654 type PreImageViewMut<'life> = PauliUnitaryProjective<MutableBitView<'life, WORD_COUNT_DEFAULT>>;
655
656 fn preimage_x_view_mut(&mut self, index: usize) -> Self::PreImageViewMut<'_> {
657 let xz_bits = self
658 .bits
659 .rows2_mut(x_preimage_rows_ids(self.num_qubits(), index));
660 Self::PreImageViewMut::from_bits_tuple(xz_bits)
661 }
662
663 fn preimage_z_view_mut(&mut self, index: usize) -> Self::PreImageViewMut<'_> {
664 let xz_bits = self
665 .bits
666 .rows2_mut(z_preimage_rows_ids(self.num_qubits(), index));
667 Self::PreImageViewMut::from_bits_tuple(xz_bits)
668 }
669
670 fn preimage_xz_views_mut(
671 &mut self,
672 index: usize,
673 ) -> (Self::PreImageViewMut<'_>, Self::PreImageViewMut<'_>) {
674 unsafe {
675 let xz_ids = xz_preimage_rows_ids(self.num_qubits(), index);
676 let (xz_of_x, xz_of_z) = split2(self.bits.rows4_mut(concat2(xz_ids)));
677 (
678 Self::PreImageViewMut::from_bits_tuple(xz_of_x),
679 Self::PreImageViewMut::from_bits_tuple(xz_of_z),
680 )
681 }
682 }
683
684 #[allow(clippy::similar_names)]
685 fn preimage_xz_views_mut_distinct(
686 &mut self,
687 index: (usize, usize),
688 ) -> (
689 (Self::PreImageViewMut<'_>, Self::PreImageViewMut<'_>),
690 (Self::PreImageViewMut<'_>, Self::PreImageViewMut<'_>),
691 ) {
692 assert_ne!(index.0, index.1);
693 let (xz_of_x0_ids, xz_of_z0_ids) = xz_preimage_rows_ids(self.num_qubits(), index.0);
694 let (xz_of_x1_ids, xz_of_z1_ids) = xz_preimage_rows_ids(self.num_qubits(), index.1);
695 unsafe {
696 let (xz_of_x0, xz_of_z0, xz_of_x1, xz_of_z1) = split4(self.bits.rows8_mut(concat4((
697 xz_of_x0_ids,
698 xz_of_z0_ids,
699 xz_of_x1_ids,
700 xz_of_z1_ids,
701 ))));
702 (
703 (
704 Self::PreImageViewMut::from_bits_tuple(xz_of_x0),
705 Self::PreImageViewMut::from_bits_tuple(xz_of_z0),
706 ),
707 (
708 Self::PreImageViewMut::from_bits_tuple(xz_of_x1),
709 Self::PreImageViewMut::from_bits_tuple(xz_of_z1),
710 ),
711 )
712 }
713 }
714}
715
716macro_rules! clifford_mutable_common_impl {
717 () => {
718 fn left_mul_root_z(&mut self, qubit_id: usize) {
719 super::generic_algos::clifford_left_mul_eq_root_z(self, qubit_id)
720 }
721
722 fn left_mul_root_z_inverse(&mut self, qubit_id: usize) {
723 super::generic_algos::clifford_left_mul_eq_root_z_inverse(self, qubit_id)
724 }
725
726 fn left_mul_root_x(&mut self, qubit_id: usize) {
727 super::generic_algos::clifford_left_mul_eq_root_x(self, qubit_id)
728 }
729
730 fn left_mul_root_x_inverse(&mut self, qubit_id: usize) {
731 super::generic_algos::clifford_left_mul_eq_root_x_inverse(self, qubit_id)
732 }
733
734 fn left_mul_root_y(&mut self, qubit_id: usize) {
735 super::generic_algos::clifford_left_mul_eq_root_y(self, qubit_id)
736 }
737
738 fn left_mul_root_y_inverse(&mut self, qubit_id: usize) {
739 super::generic_algos::clifford_left_mul_eq_root_y_inverse(self, qubit_id)
740 }
741
742 fn left_mul_cx(&mut self, control_qubit_id: usize, target_qubit_id: usize) {
743 super::generic_algos::clifford_left_mul_eq_cnot(self, control_qubit_id, target_qubit_id)
744 }
745
746 fn left_mul_cz(&mut self, control_qubit_id: usize, target_qubit_id: usize) {
747 super::generic_algos::clifford_left_mul_eq_cz(self, control_qubit_id, target_qubit_id)
748 }
749
750 fn left_mul_prepare_bell(&mut self, control_qubit_id: usize, target_qubit_id: usize) {
751 super::generic_algos::clifford_left_mul_eq_prepare_bell(
752 self,
753 control_qubit_id,
754 target_qubit_id,
755 )
756 }
757
758 fn left_mul(&mut self, unitary_op: UnitaryOp, support: &[usize]) {
759 use crate::UnitaryOp::*;
760 match unitary_op {
761 I => {}
762 X => {
763 assert_1q_gate!(support);
764 self.left_mul_x(support[0]);
765 }
766 Y => {
767 assert_1q_gate!(support);
768 self.left_mul_y(support[0]);
769 }
770 Z => {
771 assert_1q_gate!(support);
772 self.left_mul_z(support[0]);
773 }
774 SqrtX => {
775 assert_1q_gate!(support);
776 self.left_mul_root_x(support[0]);
777 }
778 SqrtXInv => {
779 assert_1q_gate!(support);
780 self.left_mul_root_x_inverse(support[0]);
781 }
782 SqrtY => {
783 assert_1q_gate!(support);
784 self.left_mul_root_y(support[0]);
785 }
786 SqrtYInv => {
787 assert_1q_gate!(support);
788 self.left_mul_root_y_inverse(support[0]);
789 }
790 SqrtZ => {
791 assert_1q_gate!(support);
792 self.left_mul_root_z(support[0]);
793 }
794 SqrtZInv => {
795 assert_1q_gate!(support);
796 self.left_mul_root_z_inverse(support[0]);
797 }
798 Hadamard => {
799 assert_1q_gate!(support);
800 self.left_mul_hadamard(support[0]);
801 }
802 Swap => {
803 assert_2q_gate!(support);
804 self.left_mul_swap(support[0], support[1]);
805 }
806 ControlledX => {
807 assert_2q_gate!(support);
808 self.left_mul_cx(support[0], support[1]);
809 }
810 ControlledZ => {
811 assert_2q_gate!(support);
812 self.left_mul_cz(support[0], support[1]);
813 }
814 PrepareBell => {
815 assert_2q_gate!(support);
816 self.left_mul_prepare_bell(support[0], support[1]);
817 }
818 }
819 }
820 };
821}
822
823macro_rules! clifford_mutable_common_multi_qubit_impl {
824 ($DensePauli:ty) => {
825 fn left_mul_pauli_exp<PauliLike: Pauli<PhaseExponentValue = Self::PhaseExponentValue>>(
826 &mut self,
827 pauli: &PauliLike,
828 ) {
829 if self.num_qubits() > 0 {
830 super::generic_algos::clifford_left_mul_eq_pauli_exp(self, pauli);
831 }
832 }
833
834 fn left_mul_pauli<PauliLike: Pauli>(&mut self, pauli: &PauliLike) {
835 for qubit_x_index in pauli.x_bits().support() {
836 self.left_mul_x(qubit_x_index)
837 }
838 for qubit_z_index in pauli.z_bits().support() {
839 self.left_mul_z(qubit_z_index)
840 }
841 }
842
843 fn left_mul_controlled_pauli<
844 PauliLike: Pauli<PhaseExponentValue = Self::PhaseExponentValue>,
845 >(
846 &mut self,
847 control: &PauliLike,
848 target: &PauliLike,
849 ) {
850 if self.num_qubits() > 0 {
851 super::generic_algos::clifford_left_mul_eq_controlled_pauli(self, control, target);
852 }
853 }
854
855 fn left_mul_permutation(&mut self, permutation: &[usize], support: &[usize]) {
856 assert! {is_permutation(permutation)};
857 assert! {has_no_duplicates(support)};
858 assert_eq! {permutation.len(), support.len()};
859 let mut new_preimages = Vec::<($DensePauli, $DensePauli)>::with_capacity(support.len());
860 for elt_index in 0..support.len() {
861 new_preimages.push((
862 self.preimage_x_view(support[permutation[elt_index]]).into(),
863 self.preimage_z_view(support[permutation[elt_index]]).into(),
864 ));
865 }
866 for (elt_index, elt) in support.into_iter().enumerate() {
867 <Self as MutablePreImages>::preimage_x_view_mut(self, *elt)
868 .assign(&new_preimages[elt_index].0);
869 <Self as MutablePreImages>::preimage_z_view_mut(self, *elt)
870 .assign(&new_preimages[elt_index].1);
871 }
872 }
873 };
874}
875
876fn reindexed_support(
877 new_index: &[usize],
878 bit_support: impl sorted_iter::SortedIterator<Item = usize>,
879) -> IndexSet {
880 bit_support.map(|bit| new_index[bit]).collect()
881}
882
883fn sparse_projective_pauli_on_support(
884 pauli: &impl Pauli,
885 support: &[usize],
886) -> PauliUnitaryProjective<IndexSet> {
887 PauliUnitaryProjective::<IndexSet>::from_bits(
888 reindexed_support(support, pauli.x_bits().support()),
889 reindexed_support(support, pauli.z_bits().support()),
890 )
891}
892
893fn sparse_pauli_on_support<PauliLike: Pauli>(pauli: &PauliLike, support: &[usize]) -> SparsePauli
894where
895 SparsePauli: Pauli<PhaseExponentValue = PauliLike::PhaseExponentValue>,
896{
897 let mut res = SparsePauli::from_bits(
898 reindexed_support(support, pauli.x_bits().support()),
899 reindexed_support(support, pauli.z_bits().support()),
900 0,
901 );
902 res.assign_phase_from(pauli);
903 res
904}
905
906fn is_permutation(sequence: &[usize]) -> bool {
907 let mut seq = sequence.to_vec();
908 seq.sort_unstable();
909 if seq[0] != 0 {
910 return false;
911 }
912 for j in 0..seq.len() - 1 {
913 if seq[j] + 1 != seq[j + 1] {
914 return false;
915 }
916 }
917 true
918}
919
920fn has_no_duplicates(sequence: &[usize]) -> bool {
921 let mut seq = sequence.to_vec();
922 seq.sort_unstable();
923 for j in 0..seq.len() - 1 {
924 if seq[j] == seq[j + 1] {
925 return false;
926 }
927 }
928 true
929}
930
931impl CliffordMutable for CliffordUnitaryModPauli {
932 clifford_mutable_common_impl!();
933 clifford_mutable_common_multi_qubit_impl!(<Self as Clifford>::DensePauli);
934
935 fn left_mul_hadamard(&mut self, qubit_id: usize) {
936 hadamard_clifford_bits(self.num_qubits(), qubit_id, &mut self.bits);
937 }
938
939 fn left_mul_swap(&mut self, qubit1_id: usize, qubit2_id: usize) {
940 swap_clifford_bits(self.num_qubits(), qubit1_id, qubit2_id, &mut self.bits);
941 }
942
943 fn left_mul_x(&mut self, _qubit_index: usize) {}
944
945 fn left_mul_y(&mut self, _qubit_index: usize) {}
946
947 fn left_mul_z(&mut self, _qubit_index: usize) {}
948
949 #[allow(clippy::similar_names)]
950 fn left_mul_clifford<CliffordLike: Clifford + PreimageViews>(
951 &mut self,
952 clifford: &CliffordLike,
953 support: &[usize],
954 ) {
955 assert_eq! {support.len(),clifford.num_qubits()};
956 assert!(has_no_duplicates(support));
957
958 let mut new_preimages = Vec::<(
959 <Self as Clifford>::DensePauli,
960 <Self as Clifford>::DensePauli,
961 )>::with_capacity(support.len());
962 for elt_index in 0..support.len() {
963 let px_on_support =
964 sparse_projective_pauli_on_support(&clifford.preimage_x_view(elt_index), support);
965 let pz_on_support =
966 sparse_projective_pauli_on_support(&clifford.preimage_z_view(elt_index), support);
967 new_preimages.push((self.preimage(&px_on_support), self.preimage(&pz_on_support)));
968 }
969
970 for (elt_index, elt) in support.iter().enumerate() {
971 self.preimage_x_view_mut(*elt)
972 .assign(&new_preimages[elt_index].0);
973 self.preimage_z_view_mut(*elt)
974 .assign(&new_preimages[elt_index].1);
975 }
976 }
977
978 type PhaseExponentValue = ();
979}
980
981impl MutablePreImages for CliffordUnitary
982where
983 for<'life> PauliUnitary<MutableBitView<'life, WORD_COUNT_DEFAULT>, &'life mut u8>:
984 PauliBinaryOps + Pauli<PhaseExponentValue = u8>,
985{
986 type PreImageViewMut<'life> =
987 PauliUnitary<MutableBitView<'life, WORD_COUNT_DEFAULT>, &'life mut u8>;
988
989 fn preimage_x_view_mut(&mut self, index: usize) -> Self::PreImageViewMut<'_> {
990 let xz_bits = self
991 .bits
992 .rows2_mut(x_preimage_rows_ids(self.num_qubits(), index));
993 Self::PreImageViewMut::from_bits_tuple(
994 xz_bits,
995 &mut self.preimage_phase_exponents[phase_of_preimage_x(index)],
996 )
997 }
998
999 fn preimage_z_view_mut(&mut self, index: usize) -> Self::PreImageViewMut<'_> {
1000 let xz_bits = self
1001 .bits
1002 .rows2_mut(z_preimage_rows_ids(self.num_qubits(), index));
1003 Self::PreImageViewMut::from_bits_tuple(
1004 xz_bits,
1005 &mut self.preimage_phase_exponents[phase_of_preimage_z(index)],
1006 )
1007 }
1008
1009 fn preimage_xz_views_mut(
1010 &mut self,
1011 index: usize,
1012 ) -> (Self::PreImageViewMut<'_>, Self::PreImageViewMut<'_>) {
1013 unsafe {
1014 let (xz_of_x, xz_of_z) = split2(
1015 self.bits
1016 .rows4_mut(concat2(xz_preimage_rows_ids(self.num_qubits(), index))),
1017 );
1018 let (px, pz) = tuple2_from_vec(
1019 &mut self.preimage_phase_exponents,
1020 (phase_of_preimage_x(index), phase_of_preimage_z(index)),
1021 );
1022 (
1023 Self::PreImageViewMut::from_bits_tuple(xz_of_x, px),
1024 Self::PreImageViewMut::from_bits_tuple(xz_of_z, pz),
1025 )
1026 }
1027 }
1028
1029 #[allow(clippy::similar_names)]
1030 fn preimage_xz_views_mut_distinct(
1031 &mut self,
1032 index: (usize, usize),
1033 ) -> (
1034 (Self::PreImageViewMut<'_>, Self::PreImageViewMut<'_>),
1035 (Self::PreImageViewMut<'_>, Self::PreImageViewMut<'_>),
1036 ) {
1037 let (xz_of_x0_ids, xz_of_z0_ids) = xz_preimage_rows_ids(self.num_qubits(), index.0);
1038 let (xz_of_x1_ids, xz_of_z1_ids) = xz_preimage_rows_ids(self.num_qubits(), index.1);
1039 unsafe {
1040 let (xz_of_x0, xz_of_z0, xz_of_x1, xz_of_z1) = split4(self.bits.rows8_mut(concat4((
1041 xz_of_x0_ids,
1042 xz_of_z0_ids,
1043 xz_of_x1_ids,
1044 xz_of_z1_ids,
1045 ))));
1046 let (px0, pz0, px1, pz1) = tuple4_from_vec(
1047 &mut self.preimage_phase_exponents,
1048 (
1049 phase_of_preimage_x(index.0),
1050 phase_of_preimage_z(index.0),
1051 phase_of_preimage_x(index.1),
1052 phase_of_preimage_z(index.1),
1053 ),
1054 );
1055 (
1056 (
1057 Self::PreImageViewMut::from_bits_tuple(xz_of_x0, px0),
1058 Self::PreImageViewMut::from_bits_tuple(xz_of_z0, pz0),
1059 ),
1060 (
1061 Self::PreImageViewMut::from_bits_tuple(xz_of_x1, px1),
1062 Self::PreImageViewMut::from_bits_tuple(xz_of_z1, pz1),
1063 ),
1064 )
1065 }
1066 }
1067
1068 type PhaseExponentValue = u8;
1069}
1070
1071impl CliffordMutable for CliffordUnitary {
1072 fn left_mul_hadamard(&mut self, qubit_id: usize) {
1073 hadamard_clifford_bits(self.num_qubits(), qubit_id, &mut self.bits);
1074 self.preimage_phase_exponents
1075 .swap(phase_of_preimage_x(qubit_id), phase_of_preimage_z(qubit_id));
1076 }
1077
1078 fn left_mul_swap(&mut self, qubit1_id: usize, qubit2_id: usize) {
1079 swap_clifford_bits(self.num_qubits(), qubit1_id, qubit2_id, &mut self.bits);
1080 self.preimage_phase_exponents.swap(
1081 phase_of_preimage_x(qubit1_id),
1082 phase_of_preimage_x(qubit2_id),
1083 );
1084 self.preimage_phase_exponents.swap(
1085 phase_of_preimage_z(qubit1_id),
1086 phase_of_preimage_z(qubit2_id),
1087 );
1088 }
1089
1090 fn left_mul_x(&mut self, qubit_id: usize) {
1091 super::generic_algos::clifford_left_mul_eq_x(self, qubit_id);
1092 }
1093
1094 fn left_mul_y(&mut self, qubit_id: usize) {
1095 super::generic_algos::clifford_left_mul_eq_y(self, qubit_id);
1096 }
1097
1098 fn left_mul_z(&mut self, qubit_id: usize) {
1099 super::generic_algos::clifford_left_mul_eq_z(self, qubit_id);
1100 }
1101
1102 clifford_mutable_common_impl!();
1103 clifford_mutable_common_multi_qubit_impl!(<Self as Clifford>::DensePauli);
1104
1105 #[allow(clippy::similar_names)]
1106 fn left_mul_clifford<
1107 CliffordLike: Clifford<PhaseExponentValue = Self::PhaseExponentValue>
1108 + PreimageViews<PhaseExponentValue = Self::PhaseExponentValue>,
1109 >(
1110 &mut self,
1111 clifford: &CliffordLike,
1112 support: &[usize],
1113 ) {
1114 assert_eq! {support.len(),clifford.num_qubits()};
1115 assert! {has_no_duplicates(support)};
1116
1117 let mut new_preimages = Vec::<(
1118 <Self as Clifford>::DensePauli,
1119 <Self as Clifford>::DensePauli,
1120 )>::with_capacity(support.len());
1121 for elt_index in 0..support.len() {
1122 let px_on_support =
1123 sparse_pauli_on_support(&clifford.preimage_x_view(elt_index), support);
1124 let pz_on_support =
1125 sparse_pauli_on_support(&clifford.preimage_z_view(elt_index), support);
1126 new_preimages.push((self.preimage(&px_on_support), self.preimage(&pz_on_support)));
1127 }
1128
1129 for (elt_index, elt) in support.iter().enumerate() {
1130 self.preimage_x_view_mut(*elt)
1131 .assign(&new_preimages[elt_index].0);
1132 self.preimage_z_view_mut(*elt)
1133 .assign(&new_preimages[elt_index].1);
1134 }
1135 }
1136
1137 type PhaseExponentValue = u8;
1138}
1139
1140fn clifford_display_fmt<'life, CliffordLike: Clifford + PreimageViews>(
1141 clifford: &'life CliffordLike,
1142 f: &mut std::fmt::Formatter<'_>,
1143) -> std::fmt::Result
1144where
1145 CliffordLike::PreImageView<'life>: fmt::Display,
1146 CliffordLike::DensePauli: fmt::Display,
1147{
1148 if f.alternate() {
1149 for index in 0..clifford.num_qubits() {
1150 let index_str = subscript_digits(index);
1151 write!(f, "Z{}→{:#}, ", index_str, clifford.image_z(index))?;
1152 }
1153 for index in 0..clifford.num_qubits() {
1154 let index_str = subscript_digits(index);
1155 write!(f, "X{}→{:#}, ", index_str, clifford.image_x(index))?;
1156 }
1157 Ok(())
1158 } else {
1159 for index in 0..clifford.num_qubits() {
1160 let index_str = subscript_digits(index);
1161 write!(f, "Z{}→{}, ", index_str, clifford.image_z(index))?;
1162 }
1163 for index in 0..clifford.num_qubits() {
1164 let index_str = subscript_digits(index);
1165 write!(f, "X{}→{}, ", index_str, clifford.image_x(index))?;
1166 }
1167 Ok(())
1168 }
1169}
1170
1171impl Display for CliffordUnitary {
1172 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1173 clifford_display_fmt(self, f)
1174 }
1175}
1176
1177impl Display for CliffordUnitaryModPauli {
1178 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1179 clifford_display_fmt(self, f)
1180 }
1181}
1182
1183impl Debug for CliffordUnitary {
1184 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1185 clifford_display_fmt(self, f)
1186 }
1187}
1188
1189impl Debug for CliffordUnitaryModPauli {
1190 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1191 clifford_display_fmt(self, f)
1192 }
1193}
1194
1195fn clifford_from_str<DensePauliLike, SparsePauliLike, CliffordLike>(
1196 s: &str,
1197) -> Result<CliffordLike, CliffordStringParsingError>
1198where
1199 DensePauliLike: Pauli
1200 + NeutralElement<NeutralElementType = DensePauliLike>
1201 + Clone
1202 + PauliBinaryOps<SparsePauliLike>
1203 + fmt::Display,
1204 SparsePauliLike: Pauli + std::str::FromStr,
1205 CliffordLike: Clifford<DensePauli = DensePauliLike>,
1206{
1207 let trimmed = s.trim().trim_end_matches(',');
1208 let pauli_images = trimmed.split(['\n', ',']);
1209 let mut image_pairs = Vec::new();
1210 for pauli_image in pauli_images {
1211 let image_parts = pauli_image.split([':', '→']).collect::<Vec<_>>();
1212 if image_parts.len() == 2 {
1213 let from = image_parts[0].parse::<SparsePauliLike>();
1214 let to = image_parts[1].parse::<SparsePauliLike>();
1215 if let (Ok(pauli_from), Ok(pauli_to)) = (from, to) {
1216 image_pairs.push((pauli_from, pauli_to));
1217 } else {
1218 return Err(CliffordStringParsingError);
1219 }
1220 } else {
1221 return Err(CliffordStringParsingError);
1222 }
1223 }
1224 if image_pairs.len() % 2 == 0 {
1225 let qubit_count = image_pairs.len() / 2;
1226 let mut preimages =
1227 vec![DensePauliLike::neutral_element_of_size(qubit_count); 2 * qubit_count];
1228 for (pauli_from, pauli_to) in image_pairs {
1229 if pauli_from.weight() == 1 {
1230 if let Some(qubit_id) = pauli_from.support().next() {
1231 if pauli_from.is_pauli_x(qubit_id) {
1232 preimages[2 * qubit_id].assign(&pauli_to);
1233 } else if pauli_from.is_pauli_z(qubit_id) {
1234 preimages[2 * qubit_id + 1].assign(&pauli_to);
1235 } else {
1236 return Err(CliffordStringParsingError);
1237 }
1238 } else {
1239 return Err(CliffordStringParsingError);
1240 }
1241 } else {
1242 return Err(CliffordStringParsingError);
1243 }
1244 }
1245 let clifford = CliffordLike::from_preimages(&preimages);
1246 if !clifford.is_valid() {
1247 return Err(CliffordStringParsingError);
1248 }
1249 Ok(clifford.inverse())
1250 } else {
1251 Err(CliffordStringParsingError)
1252 }
1253}
1254
1255impl FromStr for CliffordUnitary {
1256 type Err = CliffordStringParsingError;
1257
1258 fn from_str(s: &str) -> Result<Self, Self::Err> {
1259 clifford_from_str::<DensePauli, SparsePauli, CliffordUnitary>(s)
1260 }
1261}
1262
1263impl FromStr for CliffordUnitaryModPauli {
1264 type Err = CliffordStringParsingError;
1265
1266 fn from_str(s: &str) -> Result<Self, Self::Err> {
1267 clifford_from_str::<DensePauliProjective, SparsePauliProjective, CliffordUnitaryModPauli>(s)
1268 }
1269}
1270
1271impl<T: Clifford<PhaseExponentValue = ()>> From<T> for CliffordUnitary
1272where
1273 <CliffordUnitary as Clifford>::DensePauli: From<T::DensePauli>,
1274{
1275 fn from(value: T) -> Self {
1276 let mut preimages = Vec::new();
1277 for j in value.qubits() {
1278 preimages.push(value.preimage_x(j).into());
1279 preimages.push(value.preimage_z(j).into());
1280 }
1281 Self::from_preimages(&preimages)
1282 }
1283}
1284
1285impl<T: Clifford<PhaseExponentValue = u8>> From<T> for CliffordUnitaryModPauli
1286where
1287 <CliffordUnitaryModPauli as Clifford>::DensePauli: From<T::DensePauli>,
1288{
1289 fn from(value: T) -> Self {
1290 let mut preimages = Vec::new();
1291 for j in value.qubits() {
1292 preimages.push(value.preimage_x(j).into());
1293 preimages.push(value.preimage_z(j).into());
1294 }
1295 Self::from_preimages(&preimages)
1296 }
1297}
1298
1299/// Multiplication traits
1300impl Mul for &CliffordUnitary {
1301 type Output = CliffordUnitary;
1302
1303 fn mul(self, other: Self) -> CliffordUnitary {
1304 self.multiply_with(other)
1305 }
1306}
1307
1308impl<'life, Bits: PauliBits, _Phase: PhaseExponent> Mul<&'life mut CliffordUnitary>
1309 for &PauliUnitary<Bits, _Phase>
1310{
1311 type Output = ();
1312
1313 fn mul(self, clifford: &'life mut CliffordUnitary) -> Self::Output {
1314 clifford.left_mul_pauli(self);
1315 // for qubit_index in self.x_bits().support() {
1316 // let mut clifford_preimage = clifford.z_preimage_at_mut(qubit_index);
1317 // clifford_preimage *= Phase::from_exponent(2u8);
1318 // }
1319 // for qubit_index in self.z_bits().support() {
1320 // let mut clifford_preimage = clifford.x_preimage_at_mut(qubit_index);
1321 // clifford_preimage *= Phase::from_exponent(2u8);
1322 // }
1323 }
1324}
1325
1326impl<Bits: PauliBits, Phase: PhaseExponent> Mul<&mut CliffordUnitary>
1327 for &ControlledPauli<Bits, Phase>
1328{
1329 type Output = ();
1330
1331 fn mul(self, clifford: &mut CliffordUnitary) -> Self::Output {
1332 clifford.left_mul_controlled_pauli(&self.0, &self.1);
1333 }
1334}
1335
1336impl<Bits: PauliBits, _Phase: PhaseExponent> Mul<&mut CliffordUnitary>
1337 for &PauliExponent<Bits, _Phase>
1338{
1339 type Output = ();
1340
1341 fn mul(self, clifford: &mut CliffordUnitary) -> Self::Output {
1342 clifford.left_mul_pauli_exp(&self.0);
1343 }
1344}
1345
1346impl Mul<&Swap> for CliffordUnitary {
1347 type Output = CliffordUnitary;
1348
1349 fn mul(mut self, swap: &Swap) -> CliffordUnitary {
1350 self.bits.swap_columns(swap.0, swap.1);
1351 self
1352 }
1353}
1354
1355impl Mul<Swap> for CliffordUnitary {
1356 type Output = CliffordUnitary;
1357
1358 fn mul(self, swap: Swap) -> CliffordUnitary {
1359 self * &swap
1360 }
1361}
1362
1363impl Mul<&mut CliffordUnitary> for &Swap {
1364 type Output = ();
1365
1366 fn mul(self, clifford: &mut CliffordUnitary) -> Self::Output {
1367 clifford.left_mul_swap(self.0, self.1);
1368 }
1369}
1370
1371impl Mul<&mut CliffordUnitary> for &Hadamard {
1372 type Output = ();
1373
1374 fn mul(self, clifford: &mut CliffordUnitary) -> Self::Output {
1375 clifford.left_mul_hadamard(self.0);
1376 }
1377}
1378
1379impl<const WORD_COUNT: usize, const QUBIT_COUNT: usize>
1380 CliffordModPauliBatch<WORD_COUNT, QUBIT_COUNT>
1381{
1382 #[must_use]
1383 pub fn num_qubits(&self) -> usize {
1384 QUBIT_COUNT
1385 }
1386
1387 pub fn preimage_bits_mut(
1388 &mut self,
1389 qubit_index: usize,
1390 axis_index: usize,
1391 preimage_index: usize,
1392 ) -> &mut [u64; WORD_COUNT] {
1393 &mut self.preimages[2 * preimage_index + axis_index][qubit_index]
1394 }
1395
1396 #[must_use]
1397 pub fn preimage_bits(
1398 &self,
1399 qubit_index: usize,
1400 axis_index: usize,
1401 preimage_index: usize,
1402 ) -> &[u64; WORD_COUNT] {
1403 &self.preimages[2 * preimage_index + axis_index][qubit_index]
1404 }
1405
1406 fn preimage<PauliLike: Pauli<PhaseExponentValue = ()>>(
1407 &self,
1408 pauli: &PauliLike,
1409 ) -> PauliUnitaryProjective<[u64; WORD_COUNT]> {
1410 let mut res =
1411 PauliUnitaryProjective::<[u64; WORD_COUNT]>::neutral_element_of_size(self.num_qubits());
1412 super::generic_algos::mul_assign_right_clifford_preimage(&mut res, self, pauli);
1413 res
1414 }
1415
1416 pub fn clear(&mut self) {
1417 unsafe {
1418 std::ptr::write_bytes(self.preimages.as_mut_ptr(), 0, 4);
1419 }
1420 }
1421}
1422
1423impl<const WORD_COUNT: usize, const QUBIT_COUNT: usize> Default
1424 for CliffordModPauliBatch<WORD_COUNT, QUBIT_COUNT>
1425{
1426 fn default() -> Self {
1427 Self {
1428 preimages: [[[0u64; WORD_COUNT]; QUBIT_COUNT]; 4],
1429 }
1430 }
1431}
1432
1433unsafe fn get_pair_mut_unsafe<T>(v: &mut [T; 4], i: usize) -> (&mut T, &mut T) {
1434 let ptr = v as *mut [T; 4];
1435 (&mut (*ptr)[i], &mut (*ptr)[i + 1])
1436}
1437
1438unsafe fn get_quad_mut_unsafe<T>(v: &mut [T; 4]) -> (&mut T, &mut T, &mut T, &mut T) {
1439 let ptr = v as *mut [T; 4];
1440 (
1441 &mut (*ptr)[0],
1442 &mut (*ptr)[1],
1443 &mut (*ptr)[2],
1444 &mut (*ptr)[3],
1445 )
1446}
1447
1448unsafe fn get_tuple_mut_unsafe<T, const SIZE: usize>(
1449 v: &mut [T; SIZE],
1450 i: (usize, usize),
1451) -> (&mut T, &mut T) {
1452 let ptr = v as *mut [T; SIZE];
1453 (&mut (*ptr)[i.0], &mut (*ptr)[i.1])
1454}
1455
1456impl<const WORD_COUNT: usize, const QUBIT_COUNT: usize> MutablePreImages
1457 for CliffordModPauliBatch<WORD_COUNT, QUBIT_COUNT>
1458{
1459 type PreImageViewMut<'life> = PauliUnitaryProjective<&'life mut [u64; WORD_COUNT]>;
1460
1461 fn preimage_x_view_mut(&mut self, qubit_index: usize) -> Self::PreImageViewMut<'_> {
1462 unsafe {
1463 let (x, z) = get_pair_mut_unsafe(&mut self.preimages, 0);
1464 PauliUnitaryProjective::from_bits(&mut x[qubit_index], &mut z[qubit_index])
1465 }
1466 }
1467
1468 fn preimage_z_view_mut(&mut self, qubit_index: usize) -> Self::PreImageViewMut<'_> {
1469 unsafe {
1470 let (x, z) = get_pair_mut_unsafe(&mut self.preimages, 2);
1471 PauliUnitaryProjective::from_bits(&mut x[qubit_index], &mut z[qubit_index])
1472 }
1473 }
1474
1475 fn preimage_xz_views_mut(
1476 &mut self,
1477 index: usize,
1478 ) -> (Self::PreImageViewMut<'_>, Self::PreImageViewMut<'_>) {
1479 unsafe {
1480 let (xx, xz, zx, zz) = get_quad_mut_unsafe(&mut self.preimages);
1481 (
1482 PauliUnitaryProjective::from_bits(&mut xx[index], &mut xz[index]),
1483 PauliUnitaryProjective::from_bits(&mut zx[index], &mut zz[index]),
1484 )
1485 }
1486 }
1487
1488 #[allow(clippy::similar_names)]
1489 fn preimage_xz_views_mut_distinct(
1490 &mut self,
1491 index: (usize, usize),
1492 ) -> crate::Tuple2x2<Self::PreImageViewMut<'_>> {
1493 debug_assert!(index.0 != index.1);
1494 unsafe {
1495 let (xx, xz, zx, zz) = get_quad_mut_unsafe(&mut self.preimages);
1496 let (xx0, xx1) = get_tuple_mut_unsafe(xx, index);
1497 let (xz0, xz1) = get_tuple_mut_unsafe(xz, index);
1498 let (zx0, zx1) = get_tuple_mut_unsafe(zx, index);
1499 let (zz0, zz1) = get_tuple_mut_unsafe(zz, index);
1500 (
1501 (
1502 PauliUnitaryProjective::from_bits(xx0, xz0),
1503 PauliUnitaryProjective::from_bits(zx0, zz0),
1504 ),
1505 (
1506 PauliUnitaryProjective::from_bits(xx1, xz1),
1507 PauliUnitaryProjective::from_bits(zx1, zz1),
1508 ),
1509 )
1510 }
1511 }
1512
1513 type PhaseExponentValue = ();
1514}
1515
1516impl<const WORD_COUNT: usize, const QUBIT_COUNT: usize> PreimageViews
1517 for CliffordModPauliBatch<WORD_COUNT, QUBIT_COUNT>
1518{
1519 type PreImageView<'life> = PauliUnitaryProjective<&'life [u64; WORD_COUNT]>;
1520 type ImageViewUpToPhase<'life> = PauliUnitaryProjective<&'life [u64; WORD_COUNT]>;
1521
1522 fn preimage_x_view(&self, index: usize) -> Self::PreImageView<'_> {
1523 PauliUnitaryProjective::from_bits(&self.preimages[0][index], &self.preimages[1][index])
1524 }
1525
1526 fn preimage_z_view(&self, index: usize) -> Self::PreImageView<'_> {
1527 PauliUnitaryProjective::from_bits(&self.preimages[2][index], &self.preimages[3][index])
1528 }
1529
1530 fn x_image_view_up_to_phase(&self, _qubit_index: usize) -> Self::ImageViewUpToPhase<'_> {
1531 todo!()
1532 }
1533
1534 fn z_image_view_up_to_phase(&self, _qubit_index: usize) -> Self::ImageViewUpToPhase<'_> {
1535 todo!()
1536 }
1537
1538 type PhaseExponentValue = ();
1539}
1540
1541impl<const WORD_COUNT: usize, const QUBIT_COUNT: usize> CliffordMutable
1542 for CliffordModPauliBatch<WORD_COUNT, QUBIT_COUNT>
1543{
1544 clifford_mutable_common_impl!();
1545
1546 fn left_mul_x(&mut self, _qubit_index: usize) {}
1547
1548 fn left_mul_y(&mut self, _qubit_index: usize) {}
1549
1550 fn left_mul_z(&mut self, _qubit_index: usize) {}
1551
1552 #[allow(clippy::similar_names)]
1553 fn left_mul_hadamard(&mut self, qubit_index: usize) {
1554 unsafe {
1555 let (xx, xz, zx, zz) = get_quad_mut_unsafe(&mut self.preimages);
1556 std::mem::swap(&mut xx[qubit_index], &mut zx[qubit_index]);
1557 std::mem::swap(&mut xz[qubit_index], &mut zz[qubit_index]);
1558 }
1559 }
1560
1561 #[allow(clippy::similar_names)]
1562 fn left_mul_swap(&mut self, qubit_index1: usize, qubit_index2: usize) {
1563 unsafe {
1564 let (xx, xz, zx, zz) = get_quad_mut_unsafe(&mut self.preimages);
1565 let index = (qubit_index1, qubit_index2);
1566 let (xx0, xx1) = get_tuple_mut_unsafe(xx, index);
1567 std::mem::swap(xx0, xx1);
1568 let (xz0, xz1) = get_tuple_mut_unsafe(xz, index);
1569 std::mem::swap(xz0, xz1);
1570 let (zx0, zx1) = get_tuple_mut_unsafe(zx, index);
1571 std::mem::swap(zx0, zx1);
1572 let (zz0, zz1) = get_tuple_mut_unsafe(zz, index);
1573 std::mem::swap(zz0, zz1);
1574 }
1575 }
1576
1577 #[allow(clippy::similar_names)]
1578 fn left_mul_clifford<CliffordLike: Clifford + PreimageViews>(
1579 &mut self,
1580 clifford: &CliffordLike,
1581 support: &[usize],
1582 ) {
1583 assert_eq! {support.len(),clifford.num_qubits()};
1584 assert!(has_no_duplicates(support));
1585
1586 let mut new_preimages = Vec::<(
1587 PauliUnitaryProjective<[u64; WORD_COUNT]>,
1588 PauliUnitaryProjective<[u64; WORD_COUNT]>,
1589 )>::with_capacity(support.len());
1590 for elt_index in 0..support.len() {
1591 let px_on_support =
1592 sparse_projective_pauli_on_support(&clifford.preimage_x_view(elt_index), support);
1593 let pz_on_support =
1594 sparse_projective_pauli_on_support(&clifford.preimage_z_view(elt_index), support);
1595 new_preimages.push((self.preimage(&px_on_support), self.preimage(&pz_on_support)));
1596 }
1597
1598 for (elt_index, elt) in support.iter().enumerate() {
1599 self.preimage_x_view_mut(*elt)
1600 .assign(&new_preimages[elt_index].0);
1601 self.preimage_z_view_mut(*elt)
1602 .assign(&new_preimages[elt_index].1);
1603 }
1604 }
1605
1606 clifford_mutable_common_multi_qubit_impl!(PauliUnitaryProjective<[u64; WORD_COUNT]>);
1607
1608 type PhaseExponentValue = ();
1609}
1610
1611fn image_block_range(qubit_count: usize, bits: XOrZ, image: XOrZ) -> std::ops::Range<usize> {
1612 let offset = block_offset(qubit_count, bits, image);
1613 offset..offset + qubit_count
1614}
1615
1616fn image_block_iterator(
1617 qubit_count: usize,
1618 bits: XOrZ,
1619 image: XOrZ,
1620 iter: impl ExactSizeIterator<Item = usize>,
1621) -> impl ExactSizeIterator<Item = usize> {
1622 let offset: usize = block_offset(qubit_count, bits, image);
1623 iter.map(move |x| x + offset)
1624}
1625
1626fn block_offset(qubit_count: usize, bits: XOrZ, image: XOrZ) -> usize {
1627 use XOrZ::{X, Z};
1628 match (bits, image) {
1629 (X, X) => z_of_preimage_z_offset(qubit_count),
1630 (X, Z) => x_of_preimage_z_offset(qubit_count),
1631 (Z, X) => z_of_preimage_x_offset(qubit_count),
1632 (Z, Z) => x_of_preimage_x_offset(qubit_count),
1633 }
1634}
1635
1636trait CliffordBitBlocks {
1637 type Column<'life>: Bitwise
1638 where
1639 Self: 'life;
1640 type ColumnMutable<'life>: Bitwise + BitwiseBinaryOps<Column<'life>>
1641 where
1642 Self: 'life;
1643 fn block(&self, bits: XOrZ, image: XOrZ) -> impl ExactSizeIterator<Item = Self::Column<'_>>;
1644 fn block_restriction(
1645 &self,
1646 bits: XOrZ,
1647 image: XOrZ,
1648 iter: impl ExactSizeIterator<Item = usize>,
1649 ) -> impl ExactSizeIterator<Item = Self::Column<'_>>;
1650 fn block_mut(
1651 &mut self,
1652 bits: XOrZ,
1653 image: XOrZ,
1654 ) -> impl ExactSizeIterator<Item = Self::ColumnMutable<'_>>;
1655 // fn block_restriction_mut(
1656 // &mut self,
1657 // bits: XOrZ,
1658 // image: XOrZ,
1659 // iter: impl ExactSizeIterator<Item = usize>,
1660 // ) -> impl ExactSizeIterator<Item = Self::ColumnMutable<'_>>;
1661}
1662
1663macro_rules! clifford_bit_blocks_common {
1664 () => {
1665 fn block(
1666 &self,
1667 bits: XOrZ,
1668 image: XOrZ,
1669 ) -> impl ExactSizeIterator<Item = Self::Column<'_>> {
1670 self.bits
1671 .row_iterator(image_block_range(self.num_qubits(), bits, image))
1672 }
1673
1674 fn block_restriction(
1675 &self,
1676 bits: XOrZ,
1677 image: XOrZ,
1678 iter: impl ExactSizeIterator<Item = usize>,
1679 ) -> impl ExactSizeIterator<Item = Self::Column<'_>> {
1680 self.bits
1681 .row_iterator(image_block_iterator(self.num_qubits(), bits, image, iter))
1682 }
1683
1684 fn block_mut(
1685 &mut self,
1686 bits: XOrZ,
1687 image: XOrZ,
1688 ) -> impl ExactSizeIterator<Item = Self::ColumnMutable<'_>> {
1689 self.bits
1690 .row_iterator_mut(image_block_range(self.num_qubits(), bits, image))
1691 }
1692
1693 // fn block_restriction_mut(
1694 // &mut self,
1695 // bits: XOrZ,
1696 // image: XOrZ,
1697 // iter: impl ExactSizeIterator<Item = usize>,
1698 // ) -> impl ExactSizeIterator<Item = Self::ColumnMutable<'_>> {
1699 // self.bits
1700 // .row_iterator_mut(image_block_iterator(self.num_qubits(), bits, image, iter))
1701 // }
1702 };
1703}
1704
1705impl CliffordBitBlocks for CliffordUnitaryModPauli {
1706 type Column<'life> = BitView<'life, WORD_COUNT_DEFAULT>;
1707 type ColumnMutable<'life> = MutableBitView<'life, WORD_COUNT_DEFAULT>;
1708 clifford_bit_blocks_common!();
1709}
1710
1711impl CliffordBitBlocks for CliffordUnitary {
1712 type Column<'life> = BitView<'life, WORD_COUNT_DEFAULT>;
1713 type ColumnMutable<'life> = MutableBitView<'life, WORD_COUNT_DEFAULT>;
1714 clifford_bit_blocks_common!();
1715}
1716
1717fn is_x_diagonal(clifford: &impl CliffordBitBlocks) -> bool {
1718 use XOrZ::{X, Z};
1719 is_zero_padded_identity(clifford.block(X, X))
1720 & is_zero_padded_identity(clifford.block(Z, Z))
1721 & are_zero_rows(&mut clifford.block(Z, X))
1722}
1723
1724fn is_z_diagonal(clifford: &impl CliffordBitBlocks) -> bool {
1725 use XOrZ::{X, Z};
1726 is_zero_padded_identity(clifford.block(X, X))
1727 & is_zero_padded_identity(clifford.block(Z, Z))
1728 & are_zero_rows(&mut clifford.block(X, Z))
1729}
1730
1731fn is_css_clifford(clifford: &impl CliffordBitBlocks) -> bool {
1732 use XOrZ::{X, Z};
1733 are_zero_rows(&mut clifford.block(X, Z)) & are_zero_rows(&mut clifford.block(Z, X))
1734}
1735
1736// fn is_reduced_z_diagonal_resource_encoder<'life, CliffordLike>(clifford: &'life CliffordLike) -> bool
1737// where
1738// CliffordLike: CliffordBitBlocks<Column<'life> = BitView<'life, WORD_COUNT_DEFAULT>> + Clifford,
1739// {
1740// use XOrZ::{X, Z};
1741// is_zero_padded_identity(clifford.block(X, Z))
1742// & is_zero_padded_symmetric(clifford.block(Z, Z), clifford.num_qubits())
1743// }
1744
1745fn is_z_diagonal_resource_encoder<'life, CliffordLike>(
1746 clifford: &'life CliffordLike,
1747) -> Option<BitMatrix>
1748where
1749 CliffordLike: CliffordBitBlocks<Column<'life> = BitView<'life, WORD_COUNT_DEFAULT>> + Clifford,
1750{
1751 use XOrZ::{X, Z};
1752 let qubit_count = clifford.num_qubits();
1753 let chain = clifford
1754 .block(X, Z)
1755 .chain(clifford.block(Z, Z))
1756 .collect::<Vec<_>>();
1757 is_reduced_symmetric(qubit_count, chain)
1758}
1759
1760fn is_reduced_symmetric(
1761 qubit_count: usize,
1762 chain: Vec<BitView<'_, WORD_COUNT_DEFAULT>>,
1763) -> Option<BitMatrix> {
1764 let mut matrix = BitMatrix::from_row_iter(chain.into_iter(), qubit_count).transposed();
1765 matrix.echelonize();
1766 let transposed_rref = matrix.transposed();
1767 let (top_block, bottom_block) = split_blocks(qubit_count, &transposed_rref);
1768 if is_zero_padded_identity(top_block) & is_zero_padded_symmetric(bottom_block, qubit_count) {
1769 Some(transposed_rref)
1770 } else {
1771 None
1772 }
1773}
1774
1775fn split_blocks(
1776 qubit_count: usize,
1777 transposed_rref: &BitMatrix<WORD_COUNT_DEFAULT>,
1778) -> (
1779 impl ExactSizeIterator<Item = BitView<'_, WORD_COUNT_DEFAULT>>,
1780 impl ExactSizeIterator<Item = BitView<'_, WORD_COUNT_DEFAULT>>,
1781) {
1782 let top_block = transposed_rref.row_iterator(0..qubit_count);
1783 let bottom_block = transposed_rref.row_iterator(qubit_count..2 * qubit_count);
1784 (top_block, bottom_block)
1785}
1786
1787fn is_x_diagonal_resource_encoder<'life, CliffordLike>(
1788 clifford: &'life CliffordLike,
1789) -> Option<BitMatrix>
1790where
1791 CliffordLike: CliffordBitBlocks<Column<'life> = BitView<'life, WORD_COUNT_DEFAULT>> + Clifford,
1792{
1793 use XOrZ::{X, Z};
1794 let qubit_count = clifford.num_qubits();
1795 let chain = clifford
1796 .block(Z, Z)
1797 .chain(clifford.block(X, Z))
1798 .collect::<Vec<_>>();
1799 is_reduced_symmetric(qubit_count, chain)
1800}
1801
1802// fn is_reduced_x_diagonal_resource_encoder<'life, CliffordLike>(clifford: &'life CliffordLike) -> bool
1803// where
1804// CliffordLike: CliffordBitBlocks<Column<'life> = BitView<'life, WORD_COUNT_DEFAULT>> + Clifford,
1805// {
1806// use XOrZ::{X, Z};
1807// is_zero_padded_identity(clifford.block(Z, Z))
1808// & is_zero_padded_symmetric(clifford.block(X, Z), clifford.num_qubits())
1809// }
1810
1811fn blocks_from_diagonal_resource_state<CliffordLike>(
1812 encoder: &CliffordLike,
1813 axis: XOrZ,
1814) -> Option<CliffordLike>
1815where
1816 for<'life1> CliffordLike:
1817 CliffordBitBlocks<Column<'life1> = BitView<'life1, 8>> + Clifford + 'life1,
1818 for<'life1, 'life2> <CliffordLike as CliffordBitBlocks>::ColumnMutable<'life1>:
1819 BitwiseBinaryOps<<CliffordLike as CliffordBitBlocks>::Column<'life2>>,
1820{
1821 use XOrZ::{X, Z};
1822 let some_blocks = match axis {
1823 X => is_x_diagonal_resource_encoder(encoder),
1824 Z => is_z_diagonal_resource_encoder(encoder),
1825 };
1826
1827 if let Some(blocks) = some_blocks {
1828 let (_, symmetric_block) = split_blocks(encoder.num_qubits(), &blocks);
1829 let mut res = CliffordLike::identity(encoder.num_qubits());
1830 match axis {
1831 X => {
1832 for (mut row_to, row_from) in std::iter::zip(res.block_mut(X, Z), symmetric_block) {
1833 row_to.assign(&row_from);
1834 }
1835 }
1836 Z => {
1837 for (mut row_to, row_from) in std::iter::zip(res.block_mut(Z, X), symmetric_block) {
1838 row_to.assign(&row_from);
1839 }
1840 }
1841 }
1842 debug_assert!(res.is_diagonal(axis));
1843 Some(res)
1844 } else {
1845 None
1846 }
1847}
1848
1849#[must_use]
1850pub fn split_clifford_mod_pauli_with_transforms(
1851 clifford: &CliffordUnitaryModPauli,
1852 support: &[usize],
1853 support_complement: &[usize],
1854) -> Option<(
1855 CliffordUnitaryModPauli,
1856 CliffordUnitaryModPauli,
1857 BitMatrix,
1858 BitMatrix,
1859)> {
1860 use XOrZ::{X, Z};
1861
1862 let qubit_count = clifford.num_qubits();
1863 let restriction_transform = support_restricted_z_images_from_support_complement::<
1864 CliffordUnitaryModPauli,
1865 >(clifford, support_complement);
1866 let restriction_transform_complement = support_restricted_z_images_from_support_complement::<
1867 CliffordUnitaryModPauli,
1868 >(clifford, support);
1869 if restriction_transform.rowcount() + restriction_transform_complement.rowcount() != qubit_count
1870 {
1871 return None;
1872 }
1873 let stacked_rows = restriction_transform
1874 .rows()
1875 .chain(restriction_transform_complement.rows())
1876 .collect::<Vec<_>>();
1877 let stacked = BitMatrix::from_row_iter(stacked_rows.into_iter(), clifford.num_qubits());
1878 let stacked_inv_transpose = stacked.inverted().transposed();
1879 let split_transform = CliffordUnitaryModPauli::from_css_preimage_indicators(
1880 &stacked.transposed(),
1881 &stacked.inverted(),
1882 );
1883 let split_clifford = clifford.multiply_with(&split_transform);
1884
1885 let size1 = support.len();
1886 let size2 = support_complement.len();
1887 let mut split_clifford1 = CliffordUnitaryModPauli::zero(size1);
1888 let mut split_clifford2 = CliffordUnitaryModPauli::zero(size2);
1889 for image_axis in [X, Z] {
1890 for bits_axis in [X, Z] {
1891 let block_from_1 =
1892 split_clifford.block_restriction(bits_axis, image_axis, support.iter().copied());
1893 let block_to_1 = split_clifford1.block_mut(bits_axis, image_axis);
1894 for (mut row_to, row_from) in zip(block_to_1, block_from_1) {
1895 row_to.assign_from_interval(&row_from, 0, size1);
1896 }
1897
1898 let block_from_2 = split_clifford.block_restriction(
1899 bits_axis,
1900 image_axis,
1901 support_complement.iter().copied(),
1902 );
1903 let block_to_2 = split_clifford2.block_mut(bits_axis, image_axis);
1904 for (mut row_to, row_from) in zip(block_to_2, block_from_2) {
1905 row_to.assign_from_interval(&row_from, size1, size2);
1906 }
1907 }
1908 }
1909 Some((
1910 split_clifford1,
1911 split_clifford2,
1912 stacked,
1913 stacked_inv_transpose,
1914 ))
1915}
1916
1917#[must_use]
1918pub fn split_clifford_encoder_mod_pauli(
1919 clifford: &CliffordUnitaryModPauli,
1920 support: &[usize],
1921 support_complement: &[usize],
1922) -> Option<(CliffordUnitaryModPauli, CliffordUnitaryModPauli)> {
1923 if let Some((clifford1, clifford2, _, _)) =
1924 split_clifford_mod_pauli_with_transforms(clifford, support, support_complement)
1925 {
1926 Some((clifford1, clifford2))
1927 } else {
1928 None
1929 }
1930}
1931
1932pub fn recover_z_images_phases(
1933 clifford_up_to_phases: &mut CliffordUnitary,
1934 support: &[usize],
1935 reference_unitary: &CliffordUnitary,
1936) {
1937 for qubit_index in clifford_up_to_phases.qubits() {
1938 let stabilizer: SparsePauli = clifford_up_to_phases.image_z(qubit_index).into();
1939 let remapped_stabilizer = remapped_sparse(&stabilizer, support);
1940 let preimage = reference_unitary.preimage(&remapped_stabilizer);
1941 if preimage.xz_phase_exponent().wrapping_neg() != 0 {
1942 clifford_up_to_phases.left_mul_pauli(&clifford_up_to_phases.preimage_x(qubit_index));
1943 }
1944 debug_assert!(preimage.x_bits().is_zero());
1945 }
1946}
1947
1948#[must_use]
1949pub fn split_clifford_encoder(
1950 first_part_qubit_count: usize,
1951 tensor_product_encoder: &CliffordUnitary,
1952) -> Option<(CliffordUnitary, CliffordUnitary)> {
1953 let first_part_qubits = (0..first_part_qubit_count).collect::<Vec<_>>();
1954 let second_part_qubits =
1955 (first_part_qubit_count..tensor_product_encoder.num_qubits()).collect::<Vec<_>>();
1956 if let Some((first_part_encoder_mod_pauli, second_part_encoder_mod_pauli)) =
1957 split_clifford_encoder_mod_pauli(
1958 &tensor_product_encoder.clone().into(),
1959 &first_part_qubits,
1960 &second_part_qubits,
1961 )
1962 {
1963 let mut first_part_encoder: CliffordUnitary = first_part_encoder_mod_pauli.into();
1964 let mut second_part_encoder: CliffordUnitary = second_part_encoder_mod_pauli.into();
1965 recover_z_images_phases(
1966 &mut first_part_encoder,
1967 &first_part_qubits,
1968 tensor_product_encoder,
1969 );
1970 recover_z_images_phases(
1971 &mut second_part_encoder,
1972 &second_part_qubits,
1973 tensor_product_encoder,
1974 );
1975 Some((first_part_encoder, second_part_encoder))
1976 } else {
1977 None
1978 }
1979}
1980
1981pub fn prepare_all_zero(qubit_count: usize) -> CliffordUnitaryModPauli {
1982 CliffordUnitaryModPauli::identity(qubit_count)
1983}
1984
1985pub fn prepare_all_plus(qubit_count: usize) -> CliffordUnitaryModPauli {
1986 prepare_zero_plus(qubit_count, &(0..qubit_count).collect::<Vec<_>>())
1987}
1988
1989pub fn prepare_zero_plus(qubit_count: usize, plus_indicies: &[usize]) -> CliffordUnitaryModPauli {
1990 let mut result = CliffordUnitaryModPauli::identity(qubit_count);
1991 for qubit_index in plus_indicies {
1992 result.left_mul_hadamard(*qubit_index);
1993 }
1994 result
1995}
1996
1997#[must_use]
1998pub fn split_phased_css(
1999 clifford: &CliffordUnitaryModPauli,
2000) -> Option<(CliffordUnitaryModPauli, CliffordUnitaryModPauli)> {
2001 let qubit_count = clifford.num_qubits();
2002 let plus_resource = clifford.multiply_with(&prepare_all_plus(qubit_count));
2003 if let Some(diagonal_part) = plus_resource.unitary_from_diagonal_resource_state(XOrZ::Z) {
2004 // assert!(diagonal_part.multiply_with(&diagonal_part).is_identity());
2005 let css_remainder = diagonal_part.multiply_with(clifford);
2006 if css_remainder.is_css() {
2007 return Some((diagonal_part, css_remainder));
2008 }
2009 return None;
2010 }
2011 None
2012}
2013
2014#[must_use]
2015pub fn split_qubit_tensor_product_encoder(clifford: &CliffordUnitaryModPauli) -> Option<Vec<Axis>> {
2016 let mut res = Vec::new();
2017 for qubit_index in clifford.qubits() {
2018 if clifford.preimage_x(qubit_index).x_bits().is_zero() {
2019 res.push(Axis::X);
2020 } else if clifford
2021 .preimage::<SparsePauliProjective>(&[y(qubit_index)].into())
2022 .x_bits()
2023 .is_zero()
2024 {
2025 res.push(Axis::Y);
2026 } else if clifford.preimage_z(qubit_index).x_bits().is_zero() {
2027 res.push(Axis::Z);
2028 } else {
2029 return None;
2030 }
2031 }
2032 Some(res)
2033}
2034
2035#[must_use]
2036pub fn split_qubit_cliffords_and_css(
2037 clifford: &CliffordUnitaryModPauli,
2038) -> Option<(CliffordUnitaryModPauli, CliffordUnitaryModPauli)> {
2039 let qubit_count = clifford.num_qubits();
2040 let plus_resource = clifford.multiply_with(&prepare_all_plus(qubit_count));
2041 let zero_resource = clifford.multiply_with(&prepare_all_zero(qubit_count));
2042 if let (Some(plus_axes), Some(zero_axes)) = (
2043 split_qubit_tensor_product_encoder(&plus_resource),
2044 split_qubit_tensor_product_encoder(&zero_resource),
2045 ) {
2046 let mut qubit_product = CliffordUnitaryModPauli::identity(clifford.num_qubits());
2047 for (qubit_index, (zero_image, plus_image)) in zip(zero_axes, plus_axes).enumerate() {
2048 apply_qubit_clifford_by_axis(&mut qubit_product, qubit_index, zero_image, plus_image)?;
2049 }
2050 let css_remainder = qubit_product.inverse().multiply_with(clifford);
2051 if css_remainder.is_css() {
2052 return Some((qubit_product, css_remainder));
2053 }
2054 return None;
2055 }
2056 None
2057}
2058
2059pub fn apply_qubit_clifford_by_axis(
2060 qubit_product: &mut CliffordUnitaryModPauli,
2061 qubit_index: usize,
2062 zero_image: Axis,
2063 plus_image: Axis,
2064) -> Option<()> {
2065 match (zero_image, plus_image) {
2066 (Axis::Y, Axis::Y) | (Axis::Z, Axis::Z) | (Axis::X, Axis::X) => {
2067 return None;
2068 }
2069 (Axis::Z, Axis::X) => {}
2070 (Axis::Z, Axis::Y) => {
2071 qubit_product.left_mul_root_z(qubit_index);
2072 }
2073 (Axis::X, Axis::Z) => {
2074 qubit_product.left_mul_root_y(qubit_index);
2075 }
2076 (Axis::X, Axis::Y) => {
2077 qubit_product.left_mul_root_y(qubit_index);
2078 qubit_product.left_mul_root_x(qubit_index);
2079 }
2080 (Axis::Y, Axis::X) => {
2081 qubit_product.left_mul_root_x(qubit_index);
2082 }
2083 (Axis::Y, Axis::Z) => {
2084 qubit_product.left_mul_root_y(qubit_index);
2085 qubit_product.left_mul_root_z(qubit_index);
2086 }
2087 }
2088 Some(())
2089}
2090
2091#[must_use]
2092pub fn random_clifford_via_operations_sampling<CliffordLike: Clifford + CliffordMutable>(
2093 qubit_count: usize,
2094 num_random_generators: usize,
2095 operations: &crate::operations::Operations,
2096) -> CliffordLike {
2097 let mut random_clifford = CliffordLike::identity(qubit_count);
2098 for _ in 0..num_random_generators {
2099 let (unitary_operation, support) = &operations[rand::random::<usize>() % operations.len()];
2100 random_clifford.left_mul(*unitary_operation, support);
2101 }
2102 random_clifford
2103}
2104
2105pub fn dense_restriction_of(
2106 pauli: &impl Pauli<PhaseExponentValue = u8>,
2107 support: impl SortedIterator<Item = usize> + Clone,
2108 qubit_count: usize,
2109) -> DensePauli {
2110 let (mut x_bits, mut z_bits) = DensePauli::neutral_element_of_size(qubit_count).to_xz_bits();
2111 for index in pauli
2112 .x_bits()
2113 .support()
2114 .intersection(support.clone().assume_sorted_by_item())
2115 {
2116 x_bits.assign_index(index, true);
2117 }
2118 for index in pauli
2119 .z_bits()
2120 .support()
2121 .intersection(support.assume_sorted_by_item())
2122 {
2123 z_bits.assign_index(index, true);
2124 }
2125 DensePauli::from_bits(x_bits, z_bits, pauli.xz_phase_exponent())
2126}
2127
2128/// # Panics
2129/// If the generators are not mutually commuting.
2130pub fn group_encoding_clifford_of<PauliLike: Pauli<PhaseExponentValue = u8>>(
2131 generators: &[PauliLike],
2132 qubit_count: usize,
2133) -> CliffordUnitary
2134where
2135 DensePauli: PauliBinaryOps<PauliLike>,
2136{
2137 assert!(are_mutually_commuting(generators));
2138 let mut current_support = (0..qubit_count).collect::<BTreeSet<_>>();
2139 let mut current_images = generators
2140 .iter()
2141 .map(|sparse| dense_from(sparse, qubit_count))
2142 .collect::<Vec<_>>();
2143 let mut result = CliffordUnitary::identity(qubit_count);
2144 let mut pivots = Vec::new();
2145 for index in 0..current_images.len() {
2146 let mut remainder = dense_restriction_of(
2147 &current_images[index],
2148 current_support.iter().copied().assume_sorted_by_item(),
2149 qubit_count,
2150 );
2151 let support_first = remainder.support().next();
2152 if let Some(non_identity_index) = support_first {
2153 let x_bit = remainder.x_bits().index(non_identity_index);
2154 // ensure that x_bit is true
2155 if !x_bit {
2156 apply_root_x(&mut remainder, non_identity_index);
2157 result.left_mul_root_x(non_identity_index);
2158 for current_image in current_images.iter_mut().skip(index) {
2159 apply_root_x(current_image, non_identity_index);
2160 }
2161 }
2162
2163 remainder.mul_assign_left_z(non_identity_index);
2164 remainder.add_assign_phase_exp(1);
2165
2166 result.left_mul_pauli_exp(&remainder);
2167 for current_image in current_images.iter_mut().skip(index) {
2168 apply_pauli_exponent(current_image, &remainder);
2169 }
2170
2171 current_support.remove(&non_identity_index);
2172 pivots.push(non_identity_index);
2173 } else {
2174 panic!("Group generators are not independent")
2175 }
2176 }
2177 let new_order = pivots
2178 .iter()
2179 .chain(current_support.iter())
2180 .copied()
2181 .collect::<Vec<_>>();
2182 result.left_mul_permutation(&new_order, &(0..qubit_count).collect::<Vec<_>>());
2183 result.inverse()
2184}
2185
2186// PartialEq trait
2187
2188impl PartialEq for CliffordUnitaryModPauli {
2189 fn eq(&self, other: &Self) -> bool {
2190 self.bits == other.bits
2191 }
2192}
2193
2194impl PartialEq for CliffordUnitary {
2195 fn eq(&self, other: &Self) -> bool {
2196 zip(
2197 &self.preimage_phase_exponents,
2198 &other.preimage_phase_exponents,
2199 )
2200 .all(|(x, y)| <u8 as PhaseExponent>::raw_eq(*x, *y))
2201 && (self.bits == other.bits)
2202 }
2203}
2204
2205impl std::hash::Hash for CliffordUnitary {
2206 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2207 self.bits.hash(state);
2208 todo!("not implemented yet");
2209 }
2210}
2211
2212impl std::hash::Hash for CliffordUnitaryModPauli {
2213 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2214 self.bits.hash(state);
2215 }
2216}
2217