microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
billt/mac-intel-cryptography

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/paulimer/src/pauli/generic.rs

1293lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use crate::quantum_core::PositionedPauliObservable;
5
6use crate::bits::{self, BitVec, BitView, FromBits, IndexSet};
7use crate::bits::{
8 Bitwise, BitwiseBinaryOps, BitwiseNeutralElement, Dot, IndexAssignable, OverlapWeight,
9};
10use crate::{subscript_digits, NeutralElement};
11
12use std::collections::btree_map::Entry;
13use std::fmt::Debug;
14use std::num::ParseIntError;
15use std::str::FromStr;
16use std::{collections::BTreeMap, fmt::Display};
17
18use super::sparse::SparsePauliProjective;
19use super::{
20 Pauli, PauliBinaryOps, PauliBits, PauliMutable, PauliMutableBits, PauliNeutralElement,
21 SparsePauli,
22};
23
24pub trait PhaseExponent {
25 fn raw_value(&self) -> u8;
26
27 fn value(&self) -> u8 {
28 self.raw_value() % 4
29 }
30
31 fn is_even(&self) -> bool {
32 self.raw_value() & 1 == 0
33 }
34
35 fn is_odd(&self) -> bool {
36 self.raw_value() & 1 != 0
37 }
38
39 #[must_use]
40 fn raw_eq(raw_value1: u8, raw_value2: u8) -> bool {
41 raw_value1.wrapping_sub(raw_value2).is_multiple_of(4)
42 }
43
44 fn eq(&self, other: &Self) -> bool {
45 Self::raw_eq(self.raw_value(), other.raw_value())
46 }
47
48 fn is_zero(&self) -> bool {
49 self.raw_value().trailing_zeros() >= 2
50 }
51}
52
53pub trait PhaseExponentMutable: PhaseExponent {
54 fn add_assign(&mut self, value: u8);
55 fn assign(&mut self, value: u8);
56 fn complex_conjugate_in_place(&mut self) {
57 self.assign(4u8 - self.raw_value() % 4);
58 }
59 fn set_random(&mut self, random_number_generator: &mut impl rand::Rng);
60}
61
62pub trait PhaseNeutralElement:
63 PhaseExponent + NeutralElement<NeutralElementType: PhaseExponentMutable>
64{
65}
66
67impl PhaseExponent for u8 {
68 fn raw_value(&self) -> u8 {
69 *self
70 }
71}
72
73impl PhaseExponent for &u8 {
74 fn raw_value(&self) -> u8 {
75 **self
76 }
77}
78
79impl PhaseExponent for &mut u8 {
80 fn raw_value(&self) -> u8 {
81 **self
82 }
83}
84
85impl PhaseExponentMutable for u8 {
86 fn add_assign(&mut self, value: u8) {
87 *self = self.wrapping_add(value);
88 }
89
90 fn assign(&mut self, value: u8) {
91 *self = value;
92 }
93
94 fn set_random(&mut self, random_number_generator: &mut impl rand::Rng) {
95 use rand::RngExt;
96 *self = random_number_generator.random::<u8>();
97 }
98}
99
100impl PhaseExponentMutable for &mut u8 {
101 fn add_assign(&mut self, value: u8) {
102 **self = self.wrapping_add(value);
103 }
104
105 fn assign(&mut self, value: u8) {
106 **self = value;
107 }
108
109 fn set_random(&mut self, random_number_generator: &mut impl rand::Rng) {
110 use rand::RngExt;
111 **self = random_number_generator.random::<u8>();
112 }
113}
114
115impl NeutralElement for u8 {
116 type NeutralElementType = u8;
117
118 #[inline]
119 fn neutral_element(&self) -> Self::NeutralElementType {
120 0u8
121 }
122
123 #[inline]
124 fn default_size_neutral_element() -> Self::NeutralElementType {
125 0u8
126 }
127
128 #[inline]
129 fn neutral_element_of_size(_size: usize) -> Self::NeutralElementType {
130 0u8
131 }
132}
133
134impl NeutralElement for &u8 {
135 type NeutralElementType = u8;
136
137 #[inline]
138 fn neutral_element(&self) -> Self::NeutralElementType {
139 0u8
140 }
141
142 #[inline]
143 fn default_size_neutral_element() -> Self::NeutralElementType {
144 0u8
145 }
146
147 #[inline]
148 fn neutral_element_of_size(_size: usize) -> Self::NeutralElementType {
149 0u8
150 }
151}
152
153impl NeutralElement for &mut u8 {
154 type NeutralElementType = u8;
155
156 #[inline]
157 fn neutral_element(&self) -> Self::NeutralElementType {
158 0u8
159 }
160
161 #[inline]
162 fn default_size_neutral_element() -> Self::NeutralElementType {
163 0u8
164 }
165
166 #[inline]
167 fn neutral_element_of_size(_size: usize) -> Self::NeutralElementType {
168 0u8
169 }
170}
171
172impl PhaseNeutralElement for u8 {}
173impl PhaseNeutralElement for &u8 {}
174impl PhaseNeutralElement for &mut u8 {}
175
176// PauliUnitary & PauliUnitaryProjective structs
177
178#[must_use]
179#[derive(Clone, Eq, Hash)]
180pub struct PauliUnitary<Bits: PauliBits, Phase: PhaseExponent> {
181 x_bits: Bits,
182 z_bits: Bits,
183 xz_phase_exp: Phase,
184}
185
186#[must_use]
187#[derive(Clone, Eq, Hash)]
188pub struct PauliUnitaryProjective<Bits: PauliBits> {
189 x_bits: Bits,
190 z_bits: Bits,
191}
192
193// Pauli
194
195impl<Bits: PauliBits + OverlapWeight> Pauli for PauliUnitaryProjective<Bits> {
196 type Bits = Bits;
197 type PhaseExponentValue = ();
198
199 fn x_bits(&self) -> &Self::Bits {
200 &self.x_bits
201 }
202
203 fn z_bits(&self) -> &Self::Bits {
204 &self.z_bits
205 }
206
207 fn is_order_two(&self) -> bool {
208 true
209 }
210
211 fn is_identity(&self) -> bool {
212 self.x_bits.is_zero() && self.z_bits.is_zero()
213 }
214
215 fn is_pauli_x(&self, qubit: usize) -> bool {
216 self.x_bits.is_one_bit(qubit) && self.z_bits.is_zero()
217 }
218
219 fn is_pauli_z(&self, qubit: usize) -> bool {
220 self.x_bits.is_zero() && self.z_bits.is_one_bit(qubit)
221 }
222
223 fn is_pauli_y(&self, qubit: usize) -> bool {
224 self.x_bits.is_one_bit(qubit) && self.z_bits.is_one_bit(qubit)
225 }
226
227 fn equals_to(&self, rhs: &Self) -> bool {
228 self == rhs
229 }
230
231 fn to_xz_bits(self) -> (Self::Bits, Self::Bits) {
232 (self.x_bits, self.z_bits)
233 }
234
235 fn xz_phase_exponent(&self) -> Self::PhaseExponentValue {}
236}
237
238impl<Bits: PauliBits + OverlapWeight, PhExp: PhaseExponent> Pauli for PauliUnitary<Bits, PhExp> {
239 type Bits = Bits;
240 type PhaseExponentValue = u8;
241
242 fn x_bits(&self) -> &Bits {
243 &self.x_bits
244 }
245
246 fn z_bits(&self) -> &Bits {
247 &self.z_bits
248 }
249
250 fn is_order_two(&self) -> bool {
251 self.y_parity() ^ self.xz_phase_exp.is_even()
252 }
253
254 fn is_identity(&self) -> bool {
255 self.x_bits.is_zero() && self.z_bits.is_zero() && self.xz_phase_exp.is_zero()
256 }
257
258 fn is_pauli_x(&self, qubit: usize) -> bool {
259 self.x_bits.is_one_bit(qubit) && self.z_bits.is_zero() && self.xz_phase_exp.is_zero()
260 }
261
262 fn is_pauli_z(&self, qubit: usize) -> bool {
263 self.x_bits.is_zero() && self.z_bits.is_one_bit(qubit) && self.xz_phase_exp.is_zero()
264 }
265
266 fn is_pauli_y(&self, qubit: usize) -> bool {
267 self.x_bits.is_one_bit(qubit)
268 && self.z_bits.is_one_bit(qubit)
269 && self.xz_phase_exp.value() == 1
270 }
271
272 fn equals_to(&self, rhs: &Self) -> bool {
273 self == rhs
274 }
275
276 fn to_xz_bits(self) -> (Self::Bits, Self::Bits) {
277 (self.x_bits, self.z_bits)
278 }
279
280 fn xz_phase_exponent(&self) -> Self::PhaseExponentValue {
281 self.xz_phase_exp.value()
282 }
283}
284
285// PauliMutableBits
286
287impl<OtherBits: Bitwise, Bits: BitwiseBinaryOps<OtherBits> + PauliBits> PauliMutableBits<OtherBits>
288 for PauliUnitaryProjective<Bits>
289{
290 type BitsMutable = Bits;
291
292 fn x_bits_mut(&mut self) -> &mut Self::BitsMutable {
293 &mut self.x_bits
294 }
295
296 fn z_bits_mut(&mut self) -> &mut Self::BitsMutable {
297 &mut self.z_bits
298 }
299}
300
301impl<
302 OtherBits: Bitwise,
303 Bits: BitwiseBinaryOps<OtherBits> + PauliBits,
304 PhExp: PhaseExponentMutable,
305 > PauliMutableBits<OtherBits> for PauliUnitary<Bits, PhExp>
306{
307 type BitsMutable = Bits;
308
309 fn x_bits_mut(&mut self) -> &mut Self::BitsMutable {
310 &mut self.x_bits
311 }
312
313 fn z_bits_mut(&mut self) -> &mut Self::BitsMutable {
314 &mut self.z_bits
315 }
316}
317
318// PauliOps
319
320impl<Bits: PauliBits + IndexAssignable, Exponent: PhaseExponentMutable> PauliMutable
321 for PauliUnitary<Bits, Exponent>
322{
323 fn assign_phase_exp(&mut self, rhs: u8) {
324 self.xz_phase_exp.assign(rhs);
325 }
326
327 fn add_assign_phase_exp(&mut self, rhs: u8) {
328 self.xz_phase_exp.add_assign(rhs);
329 }
330
331 fn complex_conjugate(&mut self) {
332 self.xz_phase_exp.complex_conjugate_in_place();
333 }
334
335 fn invert(&mut self) {
336 self.complex_conjugate();
337 if self.y_parity() {
338 self.negate();
339 }
340 }
341
342 fn negate(&mut self) {
343 self.xz_phase_exp.add_assign(2u8);
344 }
345
346 fn assign_phase_from<PauliLike: Pauli<PhaseExponentValue = Self::PhaseExponentValue>>(
347 &mut self,
348 other: &PauliLike,
349 ) {
350 self.xz_phase_exp.assign(other.xz_phase_exponent());
351 }
352
353 fn mul_assign_phase_from<PauliLike: Pauli<PhaseExponentValue = Self::PhaseExponentValue>>(
354 &mut self,
355 other: &PauliLike,
356 ) {
357 self.xz_phase_exp.add_assign(other.xz_phase_exponent());
358 }
359
360 fn mul_assign_left_x(&mut self, qubit_id: usize) {
361 self.x_bits.negate_index(qubit_id);
362 }
363
364 fn mul_assign_right_x(&mut self, qubit_id: usize) {
365 self.x_bits.negate_index(qubit_id);
366 if self.z_bits().index(qubit_id) {
367 self.xz_phase_exponent().add_assign(2);
368 }
369 }
370
371 fn mul_assign_left_z(&mut self, qubit_id: usize) {
372 self.z_bits.negate_index(qubit_id);
373 if self.x_bits().index(qubit_id) {
374 self.xz_phase_exponent().add_assign(2);
375 }
376 }
377
378 fn mul_assign_right_z(&mut self, qubit_id: usize) {
379 self.z_bits.negate_index(qubit_id);
380 }
381
382 fn set_identity(&mut self) {
383 self.x_bits.clear_bits();
384 self.z_bits.clear_bits();
385 self.xz_phase_exp.assign(0);
386 }
387
388 fn set_random(&mut self, num_qubits: usize, random_number_generator: &mut impl rand::Rng) {
389 self.x_bits.set_random(num_qubits, random_number_generator);
390 self.z_bits.set_random(num_qubits, random_number_generator);
391 self.xz_phase_exp.set_random(random_number_generator);
392 }
393
394 fn set_random_order_two(
395 &mut self,
396 num_qubits: usize,
397 random_number_generator: &mut impl rand::Rng,
398 ) {
399 self.set_random(num_qubits, random_number_generator);
400 if !self.is_order_two() {
401 self.xz_phase_exp.add_assign(1u8);
402 }
403 debug_assert!(self.is_order_two());
404 }
405}
406
407// PauliBinaryOps
408
409impl<Bits: PauliBits + IndexAssignable> PauliMutable for PauliUnitaryProjective<Bits> {
410 fn assign_phase_exp(&mut self, _rhs: u8) {}
411
412 fn add_assign_phase_exp(&mut self, _rhs: u8) {}
413
414 fn complex_conjugate(&mut self) {}
415
416 fn invert(&mut self) {}
417
418 fn negate(&mut self) {}
419
420 fn assign_phase_from<PauliLike: Pauli<PhaseExponentValue = Self::PhaseExponentValue>>(
421 &mut self,
422 _other: &PauliLike,
423 ) {
424 }
425
426 fn mul_assign_phase_from<PauliLike: Pauli<PhaseExponentValue = Self::PhaseExponentValue>>(
427 &mut self,
428 _other: &PauliLike,
429 ) {
430 }
431
432 fn mul_assign_left_x(&mut self, qubit_id: usize) {
433 self.x_bits.negate_index(qubit_id);
434 }
435
436 fn mul_assign_right_x(&mut self, qubit_id: usize) {
437 self.x_bits.negate_index(qubit_id);
438 }
439
440 fn mul_assign_left_z(&mut self, qubit_id: usize) {
441 self.z_bits.negate_index(qubit_id);
442 }
443
444 fn mul_assign_right_z(&mut self, qubit_id: usize) {
445 self.z_bits.negate_index(qubit_id);
446 }
447
448 fn set_identity(&mut self) {
449 self.x_bits.clear_bits();
450 self.z_bits.clear_bits();
451 }
452
453 fn set_random(&mut self, num_qubits: usize, random_number_generator: &mut impl rand::Rng) {
454 self.x_bits.set_random(num_qubits, random_number_generator);
455 self.z_bits.set_random(num_qubits, random_number_generator);
456 }
457
458 fn set_random_order_two(
459 &mut self,
460 num_qubits: usize,
461 random_number_generator: &mut impl rand::Rng,
462 ) {
463 self.set_random(num_qubits, random_number_generator);
464 }
465}
466
467pub fn add_assign_bits<T, U>(to: &mut T, from: &U)
468where
469 T: PauliMutableBits<U::Bits>,
470 U: Pauli,
471{
472 to.x_bits_mut().bitxor_assign(from.x_bits());
473 to.z_bits_mut().bitxor_assign(from.z_bits());
474}
475
476fn assign_bits<T, U>(to: &mut T, from: &U)
477where
478 T: PauliMutableBits<U::Bits>,
479 U: Pauli,
480{
481 to.x_bits_mut().assign(from.x_bits());
482 to.z_bits_mut().assign(from.z_bits());
483}
484
485fn assign_bits_with_offset<T, U>(to: &mut T, from: &U, start_qubit_index: usize, num_qubits: usize)
486where
487 T: PauliMutableBits<U::Bits>,
488 U: Pauli,
489{
490 to.x_bits_mut()
491 .assign_with_offset(from.x_bits(), start_qubit_index, num_qubits);
492 to.z_bits_mut()
493 .assign_with_offset(from.z_bits(), start_qubit_index, num_qubits);
494}
495
496fn bits_eq<T, U>(x: &T, z: &T, b: &U) -> bool
497where
498 U: Pauli,
499 T: PartialEq<U::Bits>,
500{
501 x == b.x_bits() && z == b.z_bits()
502}
503
504impl<Bits, OtherPauli: Pauli<PhaseExponentValue = ()>> PauliBinaryOps<OtherPauli>
505 for PauliUnitaryProjective<Bits>
506where
507 Bits: BitwiseBinaryOps<OtherPauli::Bits> + PauliBits,
508 OtherPauli: Pauli,
509{
510 fn mul_assign_right(&mut self, rhs: &OtherPauli) {
511 add_assign_bits(self, rhs);
512 }
513
514 fn mul_assign_left(&mut self, lhs: &OtherPauli) {
515 add_assign_bits(self, lhs);
516 }
517
518 fn assign(&mut self, rhs: &OtherPauli) {
519 assign_bits(self, rhs);
520 }
521
522 fn assign_with_offset(
523 &mut self,
524 rhs: &OtherPauli,
525 start_qubit_index: usize,
526 num_qubits: usize,
527 ) {
528 assign_bits_with_offset(self, rhs, start_qubit_index, num_qubits);
529 }
530}
531
532// impl<Bits, OtherPauli : Pauli<PhaseExponentValue = ()>> PauliPhaseBinaryOps<OtherPauli> for PauliUnitaryProjective<Bits>
533// where
534// Bits: PauliBits,
535// {
536// fn assign_phase_from(&mut self, _other: &OtherPauli) {}
537// fn mul_assign_phase_from(&mut self, _other: &OtherPauli) {}
538// }
539
540impl<Bits, Exponent, OtherPauli: Pauli<PhaseExponentValue = u8>> PauliBinaryOps<OtherPauli>
541 for PauliUnitary<Bits, Exponent>
542where
543 Bits: BitwiseBinaryOps<OtherPauli::Bits> + Dot<OtherPauli::Bits> + PauliBits + IndexAssignable,
544 Exponent: PhaseExponentMutable,
545{
546 fn mul_assign_right(&mut self, rhs: &OtherPauli) {
547 let cross: u8 = if self.z_bits().dot(rhs.x_bits()) {
548 2u8
549 } else {
550 0u8
551 };
552 add_assign_bits(self, rhs);
553 self.add_assign_phase_exp(cross.wrapping_add(rhs.xz_phase_exponent()));
554 }
555
556 fn mul_assign_left(&mut self, lhs: &OtherPauli) {
557 let cross: u8 = if self.x_bits().dot(lhs.z_bits()) {
558 2u8
559 } else {
560 0u8
561 };
562 add_assign_bits(self, lhs);
563 self.add_assign_phase_exp(cross.wrapping_add(lhs.xz_phase_exponent()));
564 }
565
566 fn assign(&mut self, rhs: &OtherPauli) {
567 self.assign_phase_exp(rhs.xz_phase_exponent());
568 assign_bits(self, rhs);
569 }
570
571 fn assign_with_offset(
572 &mut self,
573 rhs: &OtherPauli,
574 start_qubit_index: usize,
575 num_qubits: usize,
576 ) {
577 self.assign_phase_exp(rhs.xz_phase_exponent());
578 assign_bits_with_offset(self, rhs, start_qubit_index, num_qubits);
579 }
580}
581
582// impl<Bits, Exponent, OtherPauli : Pauli<PhaseExponentValue = u8> > PauliPhaseBinaryOps<OtherPauli> for PauliUnitary<Bits, Exponent>
583// where
584// Bits: PauliBits,
585// Exponent: PhaseExponentMutable,
586// {
587// fn assign_phase_from(&mut self, other: &OtherPauli) {
588// self.assign_phase_exp(other.xz_phase_exponent());
589// }
590
591// fn mul_assign_phase_from(&mut self, other: &OtherPauli) {
592// self.add_assign_phase_exp(other.xz_phase_exponent());
593// }
594// }
595
596impl<Bits: PauliBits, Exponent: PhaseExponent> PauliUnitary<Bits, Exponent> {
597 pub fn from_bits(x_bits: Bits, z_bits: Bits, phase: Exponent) -> PauliUnitary<Bits, Exponent> {
598 PauliUnitary {
599 x_bits,
600 z_bits,
601 xz_phase_exp: phase,
602 }
603 }
604
605 pub fn from_bits_tuple(bits: (Bits, Bits), phase: Exponent) -> PauliUnitary<Bits, Exponent> {
606 PauliUnitary {
607 x_bits: bits.0,
608 z_bits: bits.1,
609 xz_phase_exp: phase,
610 }
611 }
612}
613
614impl<Bits: PauliBits> PauliUnitaryProjective<Bits> {
615 pub fn from_bits(x_bits: Bits, z_bits: Bits) -> PauliUnitaryProjective<Bits> {
616 PauliUnitaryProjective { x_bits, z_bits }
617 }
618
619 pub fn from_bits_tuple(xz_bits: (Bits, Bits)) -> PauliUnitaryProjective<Bits> {
620 PauliUnitaryProjective {
621 x_bits: xz_bits.0,
622 z_bits: xz_bits.1,
623 }
624 }
625}
626
627impl<Exponent: PhaseExponent> PauliUnitary<crate::bits::BitVec, Exponent> {
628 pub fn size(&self) -> usize {
629 self.x_bits.len()
630 }
631}
632
633impl PauliUnitaryProjective<crate::bits::BitVec> {
634 #[must_use]
635 pub fn size(&self) -> usize {
636 self.x_bits.len()
637 }
638}
639
640// Partial and Full equality
641
642impl<LeftBits, LeftPhase, RightBits, RightPhase> PartialEq<PauliUnitary<RightBits, RightPhase>>
643 for PauliUnitary<LeftBits, LeftPhase>
644where
645 LeftBits: PartialEq<RightBits> + PauliBits,
646 RightBits: PauliBits,
647 LeftPhase: PhaseExponent,
648 RightPhase: PhaseExponent,
649{
650 fn eq(&self, other: &PauliUnitary<RightBits, RightPhase>) -> bool {
651 (self.xz_phase_exponent() == other.xz_phase_exponent())
652 && bits_eq(&self.x_bits, &self.z_bits, other)
653 }
654}
655
656impl<LeftBits, LeftPhase, RightBits, RightPhase> PartialEq<PauliUnitary<RightBits, RightPhase>>
657 for &PauliUnitary<LeftBits, LeftPhase>
658where
659 LeftBits: PartialEq<RightBits> + PauliBits,
660 RightBits: PauliBits,
661 LeftPhase: PhaseExponent,
662 RightPhase: PhaseExponent,
663{
664 fn eq(&self, other: &PauliUnitary<RightBits, RightPhase>) -> bool {
665 *self == other
666 }
667}
668
669impl<LeftBits, LeftPhase, RightBits, RightPhase> PartialEq<&PauliUnitary<RightBits, RightPhase>>
670 for PauliUnitary<LeftBits, LeftPhase>
671where
672 LeftBits: PartialEq<RightBits> + PauliBits,
673 RightBits: PauliBits,
674 LeftPhase: PhaseExponent,
675 RightPhase: PhaseExponent,
676{
677 fn eq(&self, other: &&PauliUnitary<RightBits, RightPhase>) -> bool {
678 self == *other
679 }
680}
681
682// impl<LeftBits: PauliBits, RightPauli: Pauli + ProjectivePauli> PartialEq<RightPauli>
683// for PauliUnitaryProjective<LeftBits>
684// where
685// LeftBits: Bitwise + PartialEq<RightPauli::Bits>,
686// {
687// fn eq(&self, other: &RightPauli) -> bool {
688// bits_eq(&self.x_bits, &self.z_bits, other)
689// }
690// }
691
692impl<LeftBits, RightBits> PartialEq<PauliUnitaryProjective<RightBits>>
693 for PauliUnitaryProjective<LeftBits>
694where
695 LeftBits: PartialEq<RightBits> + PauliBits,
696 RightBits: PauliBits,
697{
698 fn eq(&self, other: &PauliUnitaryProjective<RightBits>) -> bool {
699 bits_eq(&self.x_bits, &self.z_bits, other)
700 }
701}
702
703impl<LeftBits, RightBits> PartialEq<PauliUnitaryProjective<RightBits>>
704 for &PauliUnitaryProjective<LeftBits>
705where
706 LeftBits: PartialEq<RightBits> + PauliBits,
707 RightBits: PauliBits,
708{
709 fn eq(&self, other: &PauliUnitaryProjective<RightBits>) -> bool {
710 *self == other
711 }
712}
713
714impl<LeftBits, RightBits> PartialEq<&PauliUnitaryProjective<RightBits>>
715 for PauliUnitaryProjective<LeftBits>
716where
717 LeftBits: PartialEq<RightBits> + PauliBits,
718 RightBits: PauliBits,
719{
720 fn eq(&self, other: &&PauliUnitaryProjective<RightBits>) -> bool {
721 self == *other
722 }
723}
724
725fn string_map(pauli: &impl Pauli) -> (u8, BTreeMap<usize, char>) {
726 let mut phase = 0;
727 let mut support = BTreeMap::new();
728 for index in pauli.x_bits().support() {
729 support.insert(index, 'X');
730 }
731 for index in pauli.z_bits().support() {
732 match support.entry(index) {
733 Entry::Occupied(mut e) => {
734 e.insert('Y');
735 phase = (phase + 3) % 4;
736 }
737 Entry::Vacant(e) => {
738 e.insert('Z');
739 }
740 }
741 }
742 (phase, support)
743}
744
745fn pauli_string(
746 pauli: &impl Pauli,
747 phase: u8,
748 add_phase: bool,
749 sign_plus: bool,
750 dense: bool,
751) -> String {
752 if let Some(last_index) = pauli.max_qubit_id() {
753 let mut string = String::new();
754 let (extra_phase, id_to_character) = string_map(pauli);
755 if add_phase {
756 string.push_str(&phase_to_string(
757 (phase.wrapping_add(extra_phase)) % 4u8,
758 sign_plus,
759 ));
760 }
761 if dense {
762 for index in 0..=last_index {
763 if let Some(character) = id_to_character.get(&index) {
764 string.push(*character);
765 } else {
766 string.push('I');
767 }
768 }
769 } else {
770 for (index, character) in &id_to_character {
771 string.push(*character);
772 string.push_str(&subscript_digits(*index));
773 }
774 }
775 string
776 } else {
777 "I".to_owned()
778 }
779}
780
781// Display
782
783impl<Bits: PauliBits, Phase: PhaseExponent> Display for PauliUnitary<Bits, Phase> {
784 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
785 if f.alternate() {
786 // dense
787 f.pad(&pauli_string(
788 self,
789 self.xz_phase_exp.value(),
790 true,
791 f.sign_plus(),
792 true,
793 ))
794 } else {
795 // sparse
796 f.pad(&pauli_string(
797 self,
798 self.xz_phase_exp.value(),
799 true,
800 f.sign_plus(),
801 false,
802 ))
803 }
804 }
805}
806
807impl<Bits: PauliBits, Phase: PhaseExponent> Debug for PauliUnitary<Bits, Phase> {
808 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
809 <Self as Display>::fmt(self, f)
810 }
811}
812
813impl<Bits: PauliBits> Display for PauliUnitaryProjective<Bits> {
814 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
815 if f.alternate() {
816 f.pad(&pauli_string(self, 0, false, f.sign_plus(), true))
817 } else {
818 f.pad(&pauli_string(self, 0, false, f.sign_plus(), false))
819 }
820 }
821}
822
823impl<Bits: PauliBits> Debug for PauliUnitaryProjective<Bits> {
824 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
825 <Self as Display>::fmt(self, f)
826 }
827}
828
829/// # Panics
830///
831/// Will panic
832#[must_use]
833pub fn phase_to_string(phase: u8, with_plus: bool) -> String {
834 let s = match phase {
835 0 => {
836 if with_plus {
837 "+"
838 } else {
839 ""
840 }
841 }
842 1 => {
843 if with_plus {
844 "+𝑖"
845 } else {
846 "𝑖"
847 }
848 }
849 2 => "-",
850 3 => "-𝑖",
851 _ => panic!("Unexpected phase"),
852 };
853 String::from(s)
854}
855
856impl<Bits, Phase> NeutralElement for PauliUnitary<Bits, Phase>
857where
858 Bits: BitwiseNeutralElement + PauliBits,
859 Bits::NeutralElementType: PauliBits,
860 Phase: PhaseNeutralElement,
861{
862 type NeutralElementType = PauliUnitary<Bits::NeutralElementType, Phase::NeutralElementType>;
863
864 fn neutral_element(&self) -> Self::NeutralElementType {
865 PauliUnitary::from_bits(
866 self.x_bits.neutral_element(),
867 self.z_bits.neutral_element(),
868 self.xz_phase_exp.neutral_element(),
869 )
870 }
871
872 fn default_size_neutral_element() -> Self::NeutralElementType {
873 PauliUnitary::from_bits(
874 <Bits as NeutralElement>::default_size_neutral_element(),
875 <Bits as NeutralElement>::default_size_neutral_element(),
876 <Phase as NeutralElement>::default_size_neutral_element(),
877 )
878 }
879
880 fn neutral_element_of_size(size: usize) -> Self::NeutralElementType {
881 PauliUnitary::from_bits(
882 <Bits as NeutralElement>::neutral_element_of_size(size),
883 <Bits as NeutralElement>::neutral_element_of_size(size),
884 <Phase as NeutralElement>::default_size_neutral_element(),
885 )
886 }
887}
888
889impl<Bits> NeutralElement for PauliUnitaryProjective<Bits>
890where
891 Bits: BitwiseNeutralElement + PauliBits,
892 Bits::NeutralElementType: PauliBits,
893{
894 type NeutralElementType = PauliUnitaryProjective<Bits::NeutralElementType>;
895
896 fn neutral_element(&self) -> Self::NeutralElementType {
897 PauliUnitaryProjective::from_bits(
898 self.x_bits.neutral_element(),
899 self.z_bits.neutral_element(),
900 )
901 }
902
903 fn default_size_neutral_element() -> Self::NeutralElementType {
904 PauliUnitaryProjective::from_bits(
905 <Bits as NeutralElement>::default_size_neutral_element(),
906 <Bits as NeutralElement>::default_size_neutral_element(),
907 )
908 }
909
910 fn neutral_element_of_size(size: usize) -> Self::NeutralElementType {
911 PauliUnitaryProjective::from_bits(
912 <Bits as NeutralElement>::neutral_element_of_size(size),
913 <Bits as NeutralElement>::neutral_element_of_size(size),
914 )
915 }
916}
917
918impl<Bits> PauliNeutralElement for PauliUnitaryProjective<Bits>
919where
920 Bits: BitwiseNeutralElement + PauliBits,
921 Bits::NeutralElementType: PauliBits + IndexAssignable,
922{
923}
924
925impl<Bits, Phase> PauliNeutralElement for PauliUnitary<Bits, Phase>
926where
927 Bits: BitwiseNeutralElement + PauliBits,
928 Phase: PhaseNeutralElement,
929 Bits::NeutralElementType: PauliBits + Dot<Bits> + IndexAssignable,
930{
931}
932
933impl<BitsFrom: PauliBits, Bits: PauliBits> FromBits<PauliUnitaryProjective<BitsFrom>>
934 for PauliUnitaryProjective<Bits>
935where
936 Self: PauliNeutralElement<NeutralElementType = Self>,
937 Bits: FromBits<BitsFrom>,
938{
939 fn from_bits(other: &PauliUnitaryProjective<BitsFrom>) -> Self {
940 let x = Bits::from_bits(other.x_bits());
941 let z = Bits::from_bits(other.z_bits());
942 PauliUnitaryProjective::<Bits>::from_bits(x, z)
943 }
944}
945
946impl<
947 BitsFrom: PauliBits,
948 Bits: PauliBits,
949 PhaseFrom: PhaseExponent,
950 Phase: PhaseExponentMutable + NeutralElement<NeutralElementType = Phase>,
951 > FromBits<PauliUnitary<BitsFrom, PhaseFrom>> for PauliUnitary<Bits, Phase>
952where
953 Self: PauliNeutralElement<NeutralElementType = Self>,
954 Bits: FromBits<BitsFrom>,
955 PauliUnitary<Bits, Phase>: Pauli<PhaseExponentValue = u8>,
956{
957 fn from_bits(other: &PauliUnitary<BitsFrom, PhaseFrom>) -> Self {
958 let x = Bits::from_bits(other.x_bits());
959 let z = Bits::from_bits(other.z_bits());
960 let mut res =
961 PauliUnitary::<Bits, Phase>::from_bits(x, z, Phase::default_size_neutral_element());
962 res.add_assign_phase_exp(other.xz_phase_exponent());
963 res
964 }
965}
966
967fn digits_to_int(digits: &[u32]) -> Result<u32, ParseIntError> {
968 let mut normal_digits = String::with_capacity(digits.len());
969 for digit_value in digits {
970 let digit_char = std::char::from_digit(*digit_value, 10).expect("expected a digit");
971 normal_digits.push(digit_char);
972 }
973 normal_digits.parse()
974}
975
976fn pauli_from_str<T>(pauli_string: &str) -> Result<T, PauliStringParsingError>
977where
978 T: PauliMutable + NeutralElement<NeutralElementType = T>,
979{
980 let no_whitespace = pauli_string.trim();
981 let allowed_chars = "IXYZxyz +-i𝑖 ₀₁₂₃₄₅₆₇₈₉0123456789{}_";
982 let index_chars = "₀₁₂₃₄₅₆₇₈₉0123456789";
983 let phase_prefix_options = ["+i", "i", "-i", "+𝑖", "𝑖", "-𝑖", "+", "-"];
984
985 if no_whitespace.chars().all(|ch| allowed_chars.contains(ch)) {
986 let (no_whitespace, phase_exp) = parse_phase(no_whitespace, phase_prefix_options);
987
988 if no_whitespace.chars().any(|ch| index_chars.contains(ch)) {
989 // Sparse string
990 parse_sparse_pauli(no_whitespace, phase_exp)
991 } else {
992 // Dense string
993 let mut res: T = <T as NeutralElement>::neutral_element_of_size(pauli_string.len());
994 res.add_assign_phase_exp(phase_exp);
995 for (index, character) in no_whitespace.chars().enumerate() {
996 match character {
997 'X' | 'x' => res.mul_assign_right_x(index),
998 'Z' | 'z' => res.mul_assign_right_z(index),
999 'Y' | 'y' => res.mul_assign_right_y(index),
1000 'I' | ' ' => {}
1001 _ => {
1002 return Err(PauliStringParsingError);
1003 }
1004 }
1005 }
1006 Ok(res)
1007 }
1008 } else {
1009 Err(PauliStringParsingError)
1010 }
1011}
1012
1013fn parse_sparse_pauli<T>(no_whitespace: &str, phase_exp: u8) -> Result<T, PauliStringParsingError>
1014where
1015 T: PauliMutable + NeutralElement<NeutralElementType = T>,
1016{
1017 let mut character_and_positions = Vec::new();
1018 let mut digit_group = Vec::<u32>::new();
1019 let mut pauli_char: char = 'I';
1020
1021 for character in no_whitespace.chars() {
1022 match character {
1023 'X' | 'x' | 'Z' | 'z' | 'Y' | 'y' => {
1024 if pauli_char != 'I' {
1025 character_and_positions.push((pauli_char, digit_group.clone()));
1026 digit_group.clear();
1027 }
1028 pauli_char = character;
1029 }
1030 '₀'..='₉' => {
1031 digit_group.push(character as u32 - '₀' as u32);
1032 }
1033 '0'..='9' => {
1034 digit_group.push(character as u32 - '0' as u32);
1035 }
1036 '{' | '}' | ' ' | '_' => {}
1037 _ => {
1038 return Err(PauliStringParsingError);
1039 }
1040 }
1041 }
1042 if pauli_char != 'I' {
1043 character_and_positions.push((pauli_char, digit_group.clone()));
1044 }
1045
1046 let mut max_index = 0;
1047 for (_, digits) in &character_and_positions {
1048 if let Ok(index) = digits_to_int(digits) {
1049 if let Ok(index_usize) = usize::try_from(index) {
1050 max_index = usize::max(max_index, index_usize);
1051 } else {
1052 return Err(PauliStringParsingError);
1053 }
1054 } else {
1055 return Err(PauliStringParsingError);
1056 }
1057 }
1058 let mut res: T = <T as NeutralElement>::neutral_element_of_size(no_whitespace.len());
1059 res.add_assign_phase_exp(phase_exp);
1060 for (pauli_char, digits) in &character_and_positions {
1061 if let Ok(index) = digits_to_int(digits) {
1062 if let Ok(index_usize) = usize::try_from(index) {
1063 match pauli_char {
1064 'X' | 'x' => res.mul_assign_left_x(index_usize),
1065 'Z' | 'z' => res.mul_assign_left_z(index_usize),
1066 'Y' | 'y' => res.mul_assign_left_y(index_usize),
1067 _ => {
1068 return Err(PauliStringParsingError);
1069 }
1070 }
1071 } else {
1072 return Err(PauliStringParsingError);
1073 }
1074 } else {
1075 return Err(PauliStringParsingError);
1076 }
1077 }
1078 Ok(res)
1079}
1080
1081fn parse_phase<'life>(
1082 no_whitespace: &'life str,
1083 phase_prefix_options: [&'static str; 8],
1084) -> (&'life str, u8) {
1085 for phase_prefix in phase_prefix_options {
1086 if no_whitespace.starts_with(phase_prefix) {
1087 let (phase_string, remainder) = no_whitespace.split_at(phase_prefix.len());
1088 let phase_exp = match phase_string {
1089 "-" => 2,
1090 "+i" | "+𝑖" | "i" | "𝑖" => 1,
1091 "-i" | "-𝑖" => 3,
1092 "+" => 0,
1093 _ => {
1094 unreachable!();
1095 }
1096 };
1097 return (remainder, phase_exp);
1098 }
1099 }
1100 (no_whitespace, 0)
1101}
1102
1103impl<Bits: PauliBits + BitwiseNeutralElement> FromStr for PauliUnitaryProjective<Bits>
1104where
1105 Self: PauliNeutralElement<NeutralElementType = Self>,
1106{
1107 type Err = PauliStringParsingError;
1108
1109 fn from_str(characters: &str) -> Result<Self, Self::Err> {
1110 pauli_from_str(characters)
1111 }
1112}
1113
1114impl<Bits, Phase> FromStr for PauliUnitary<Bits, Phase>
1115where
1116 Bits: BitwiseNeutralElement + PauliBits,
1117 Phase: PhaseNeutralElement,
1118 Self: PauliNeutralElement<NeutralElementType = Self>,
1119{
1120 type Err = PauliStringParsingError;
1121
1122 fn from_str(characters: &str) -> Result<Self, Self::Err> {
1123 pauli_from_str(characters)
1124 }
1125}
1126
1127impl<Bits, Phase> PartialEq<&[PositionedPauliObservable]> for PauliUnitary<Bits, Phase>
1128where
1129 Bits: PauliBits + std::cmp::PartialEq<bits::IndexSet>,
1130 Phase: PhaseNeutralElement,
1131{
1132 fn eq(&self, other: &&[PositionedPauliObservable]) -> bool {
1133 self == <&[PositionedPauliObservable] as Into<SparsePauli>>::into(other)
1134 }
1135}
1136
1137impl<Bits, Phase, const LENGTH: usize> PartialEq<[PositionedPauliObservable; LENGTH]>
1138 for PauliUnitary<Bits, Phase>
1139where
1140 Bits: PauliBits + std::cmp::PartialEq<bits::IndexSet>,
1141 Phase: PhaseNeutralElement,
1142{
1143 fn eq(&self, other: &[PositionedPauliObservable; LENGTH]) -> bool {
1144 self == <&[PositionedPauliObservable] as Into<SparsePauli>>::into(other)
1145 }
1146}
1147
1148impl<Bits, Phase> PartialEq<Vec<PositionedPauliObservable>> for PauliUnitary<Bits, Phase>
1149where
1150 Bits: PauliBits + std::cmp::PartialEq<bits::IndexSet>,
1151 Phase: PhaseNeutralElement,
1152{
1153 fn eq(&self, other: &Vec<PositionedPauliObservable>) -> bool {
1154 self == <&[PositionedPauliObservable] as Into<SparsePauli>>::into(other)
1155 }
1156}
1157
1158impl<Bits> PartialEq<&[PositionedPauliObservable]> for PauliUnitaryProjective<Bits>
1159where
1160 Bits: PauliBits + std::cmp::PartialEq<bits::IndexSet>,
1161{
1162 fn eq(&self, other: &&[PositionedPauliObservable]) -> bool {
1163 self == <&[PositionedPauliObservable] as Into<SparsePauliProjective>>::into(other)
1164 }
1165}
1166
1167impl<Bits, const LENGTH: usize> PartialEq<[PositionedPauliObservable; LENGTH]>
1168 for PauliUnitaryProjective<Bits>
1169where
1170 Bits: PauliBits + std::cmp::PartialEq<bits::IndexSet>,
1171{
1172 fn eq(&self, other: &[PositionedPauliObservable; LENGTH]) -> bool {
1173 self == <&[PositionedPauliObservable] as Into<SparsePauliProjective>>::into(other)
1174 }
1175}
1176
1177impl<Bits> PartialEq<Vec<PositionedPauliObservable>> for PauliUnitaryProjective<Bits>
1178where
1179 Bits: PauliBits + std::cmp::PartialEq<bits::IndexSet>,
1180{
1181 fn eq(&self, other: &Vec<PositionedPauliObservable>) -> bool {
1182 self == <&[PositionedPauliObservable] as Into<SparsePauliProjective>>::into(other)
1183 }
1184}
1185
1186#[derive(Debug, PartialEq, Eq, Default)]
1187pub struct PauliCharacterError;
1188
1189#[derive(Debug, PartialEq, Eq, Default)]
1190pub struct PauliStringParsingError;
1191
1192impl<Bits: PauliBits> From<(Bits, Bits)> for PauliUnitaryProjective<Bits> {
1193 fn from(value: (Bits, Bits)) -> Self {
1194 PauliUnitaryProjective::<Bits>::from_bits_tuple(value)
1195 }
1196}
1197
1198impl<Bits: PauliBits, Phase: PhaseExponent + NeutralElement<NeutralElementType = Phase>>
1199 From<(Bits, Bits)> for PauliUnitary<Bits, Phase>
1200{
1201 fn from(value: (Bits, Bits)) -> Self {
1202 PauliUnitary::<Bits, Phase>::from_bits_tuple(value, Phase::default_size_neutral_element())
1203 }
1204}
1205
1206impl<'life, const WORD_COUNT: usize> From<PauliUnitaryProjective<BitView<'life, WORD_COUNT>>>
1207 for PauliUnitaryProjective<BitVec<WORD_COUNT>>
1208{
1209 fn from(value: PauliUnitaryProjective<BitView<'life, WORD_COUNT>>) -> Self {
1210 Self::from_bits(value.x_bits.into(), value.z_bits.into())
1211 }
1212}
1213
1214impl<Bits: PauliBits, T: Pauli<PhaseExponentValue = u8, Bits = Bits>, const WORD_COUNT: usize>
1215 From<T> for PauliUnitaryProjective<BitVec<WORD_COUNT>>
1216where
1217 BitVec<WORD_COUNT>: for<'life> From<&'life Bits>,
1218{
1219 fn from(value: T) -> Self {
1220 Self::from_bits(value.x_bits().into(), value.z_bits().into())
1221 }
1222}
1223
1224impl<Bits: PauliBits, T: Pauli<PhaseExponentValue = (), Bits = Bits>, const WORD_COUNT: usize>
1225 From<T> for PauliUnitary<BitVec<WORD_COUNT>, u8>
1226where
1227 BitVec<WORD_COUNT>: for<'life> From<&'life Bits>,
1228{
1229 fn from(value: T) -> Self {
1230 let weight = value.x_bits().and_weight(value.z_bits());
1231 Self::from_bits(
1232 value.x_bits().into(),
1233 value.z_bits().into(),
1234 (weight % 4).try_into().unwrap(),
1235 )
1236 }
1237}
1238
1239impl<Bits: PauliBits, T: Pauli<PhaseExponentValue = u8, Bits = Bits>> From<T>
1240 for PauliUnitary<IndexSet, u8>
1241where
1242 IndexSet: for<'life> From<&'life Bits>,
1243{
1244 fn from(value: T) -> Self {
1245 Self::from_bits(
1246 value.x_bits().into(),
1247 value.z_bits().into(),
1248 value.xz_phase_exponent(),
1249 )
1250 }
1251}
1252
1253impl<'life, const WORD_COUNT: usize> From<PauliUnitaryProjective<&'life [u64; WORD_COUNT]>>
1254 for PauliUnitaryProjective<[u64; WORD_COUNT]>
1255{
1256 fn from(value: PauliUnitaryProjective<&'life [u64; WORD_COUNT]>) -> Self {
1257 Self::from_bits(value.x_bits.to_owned(), value.z_bits.to_owned())
1258 }
1259}
1260
1261impl<'life, const WORD_COUNT: usize> From<PauliUnitary<BitView<'life, WORD_COUNT>, &u8>>
1262 for PauliUnitary<BitVec<WORD_COUNT>, u8>
1263{
1264 fn from(value: PauliUnitary<BitView<'life, WORD_COUNT>, &u8>) -> Self {
1265 Self::from_bits(
1266 value.x_bits.into(),
1267 value.z_bits.into(),
1268 *value.xz_phase_exp,
1269 )
1270 }
1271}
1272
1273pub fn pauli_random<PauliLike: NeutralElement<NeutralElementType = PauliLike> + PauliMutable>(
1274 num_qubits: usize,
1275 random_number_generator: &mut impl rand::Rng,
1276) -> PauliLike {
1277 let mut res = PauliLike::neutral_element_of_size(num_qubits);
1278 res.set_random(num_qubits, random_number_generator);
1279 res
1280}
1281
1282/// # Example
1283/// `pauli_random_order_two(6, &mut thread_rng());`
1284pub fn pauli_random_order_two<
1285 PauliLike: NeutralElement<NeutralElementType = PauliLike> + PauliMutable,
1286>(
1287 num_qubits: usize,
1288 random_number_generator: &mut impl rand::Rng,
1289) -> PauliLike {
1290 let mut res = PauliLike::neutral_element_of_size(num_qubits);
1291 res.set_random_order_two(num_qubits, random_number_generator);
1292 res
1293}
1294