microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.25.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/paulimer/src/pauli/generic.rs

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