microsoft/qdk
Publicmirrored from https://github.com/microsoft/qdkAvailable
source/paulimer/src/pauli/sparse.rs
172lines · modecode
| 1 | // Copyright (c) Microsoft Corporation. |
| 2 | // Licensed under the MIT License. |
| 3 | |
| 4 | use rustc_hash::FxHashMap; |
| 5 | |
| 6 | use crate::bits::{self, Bitwise, IndexAssignable, IndexSet}; |
| 7 | use crate::pauli::generic::{PauliCharacterError, PauliUnitary}; |
| 8 | use crate::quantum_core::{self, PositionedPauliObservable}; |
| 9 | |
| 10 | use super::{Pauli, PauliUnitaryProjective}; |
| 11 | |
| 12 | pub type SparsePauli = PauliUnitary<IndexSet, u8>; |
| 13 | pub type SparsePauliProjective = PauliUnitaryProjective<IndexSet>; |
| 14 | |
| 15 | // Note: Can be improved using PauliMutable trait |
| 16 | // Question: Should 'i' be interpreted as I or complex phase ? |
| 17 | impl TryFrom<FxHashMap<usize, char>> for SparsePauli { |
| 18 | type Error = PauliCharacterError; |
| 19 | |
| 20 | fn try_from(characters: FxHashMap<usize, char>) -> Result<Self, Self::Error> { |
| 21 | let mut x_bits = IndexSet::new(); |
| 22 | let mut z_bits = IndexSet::new(); |
| 23 | let mut exponent: u8 = 0; |
| 24 | for (index, character) in characters { |
| 25 | match character { |
| 26 | 'X' | 'x' => x_bits.assign_index(index, true), |
| 27 | 'Z' | 'z' => z_bits.assign_index(index, true), |
| 28 | 'Y' | 'y' => { |
| 29 | exponent += 1; |
| 30 | x_bits.assign_index(index, true); |
| 31 | z_bits.assign_index(index, true); |
| 32 | } |
| 33 | 'I' => {} |
| 34 | _ => return Err(PauliCharacterError {}), |
| 35 | } |
| 36 | } |
| 37 | Ok(SparsePauli::from_bits(x_bits, z_bits, exponent)) |
| 38 | } |
| 39 | } |
| 40 | |
| 41 | // Note: Allow repeated indicies so that conversion never fails and replace with generic that relies on PauliMutable |
| 42 | impl From<&[PositionedPauliObservable]> for SparsePauli { |
| 43 | fn from(pauli_observable: &[PositionedPauliObservable]) -> Self { |
| 44 | let mut obs_copy = Vec::from(pauli_observable); |
| 45 | obs_copy.sort_unstable(); |
| 46 | if obs_copy.len() > 1 { |
| 47 | for j in 0..obs_copy.len() - 1 { |
| 48 | assert!( |
| 49 | obs_copy[j].qubit_id < obs_copy[j + 1].qubit_id, |
| 50 | "Repeated qubit positions" |
| 51 | ); |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | let mut x_indices = IndexSet::new(); |
| 56 | let mut z_indices = IndexSet::new(); |
| 57 | let mut phase = 0u8; |
| 58 | |
| 59 | for quantum_core::PositionedPauliObservable { |
| 60 | qubit_id, |
| 61 | observable, |
| 62 | } in obs_copy |
| 63 | { |
| 64 | match observable { |
| 65 | quantum_core::PauliObservable::PlusI => (), |
| 66 | quantum_core::PauliObservable::MinusI => phase += 2, |
| 67 | quantum_core::PauliObservable::PlusX => x_indices.assign_index(qubit_id, true), |
| 68 | quantum_core::PauliObservable::PlusZ => z_indices.assign_index(qubit_id, true), |
| 69 | quantum_core::PauliObservable::MinusX => { |
| 70 | x_indices.assign_index(qubit_id, true); |
| 71 | phase += 2; |
| 72 | } |
| 73 | quantum_core::PauliObservable::MinusZ => { |
| 74 | z_indices.assign_index(qubit_id, true); |
| 75 | phase += 2; |
| 76 | } |
| 77 | quantum_core::PauliObservable::PlusY => { |
| 78 | x_indices.assign_index(qubit_id, true); |
| 79 | z_indices.assign_index(qubit_id, true); |
| 80 | phase += 1; |
| 81 | } |
| 82 | quantum_core::PauliObservable::MinusY => { |
| 83 | x_indices.assign_index(qubit_id, true); |
| 84 | z_indices.assign_index(qubit_id, true); |
| 85 | phase += 3; |
| 86 | } |
| 87 | } |
| 88 | } |
| 89 | PauliUnitary::from_bits(x_indices, z_indices, phase) |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | impl From<&[PositionedPauliObservable]> for SparsePauliProjective { |
| 94 | fn from(pauli_observable: &[PositionedPauliObservable]) -> Self { |
| 95 | let mut obs_copy = Vec::from(pauli_observable); |
| 96 | obs_copy.sort_unstable(); |
| 97 | if obs_copy.len() > 1 { |
| 98 | for j in 0..obs_copy.len() - 1 { |
| 99 | assert!( |
| 100 | obs_copy[j].qubit_id < obs_copy[j + 1].qubit_id, |
| 101 | "Repeated qubit positions" |
| 102 | ); |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | let mut x_indices = IndexSet::new(); |
| 107 | let mut z_indices = IndexSet::new(); |
| 108 | |
| 109 | for quantum_core::PositionedPauliObservable { |
| 110 | qubit_id, |
| 111 | observable, |
| 112 | } in obs_copy |
| 113 | { |
| 114 | match observable { |
| 115 | quantum_core::PauliObservable::PlusI | quantum_core::PauliObservable::MinusI => (), |
| 116 | quantum_core::PauliObservable::PlusX | quantum_core::PauliObservable::MinusX => { |
| 117 | x_indices.assign_index(qubit_id, true); |
| 118 | } |
| 119 | quantum_core::PauliObservable::PlusZ | quantum_core::PauliObservable::MinusZ => { |
| 120 | z_indices.assign_index(qubit_id, true); |
| 121 | } |
| 122 | quantum_core::PauliObservable::PlusY | quantum_core::PauliObservable::MinusY => { |
| 123 | x_indices.assign_index(qubit_id, true); |
| 124 | z_indices.assign_index(qubit_id, true); |
| 125 | } |
| 126 | } |
| 127 | } |
| 128 | PauliUnitaryProjective::from_bits(x_indices, z_indices) |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | impl<const LENGTH: usize> From<[PositionedPauliObservable; LENGTH]> for SparsePauli { |
| 133 | fn from(pauli_observable: [PositionedPauliObservable; LENGTH]) -> Self { |
| 134 | pauli_observable.as_slice().into() |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | impl From<Vec<PositionedPauliObservable>> for SparsePauli { |
| 139 | fn from(value: Vec<PositionedPauliObservable>) -> Self { |
| 140 | value.as_slice().into() |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | impl<const LENGTH: usize> From<[PositionedPauliObservable; LENGTH]> for SparsePauliProjective { |
| 145 | fn from(pauli_observable: [PositionedPauliObservable; LENGTH]) -> Self { |
| 146 | pauli_observable.as_slice().into() |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | impl From<Vec<PositionedPauliObservable>> for SparsePauliProjective { |
| 151 | fn from(value: Vec<PositionedPauliObservable>) -> Self { |
| 152 | value.as_slice().into() |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | pub fn remapped_sparse(pauli: &SparsePauli, support: &[usize]) -> SparsePauli { |
| 157 | let x_bits: IndexSet = bits::remapped(pauli.x_bits(), support); |
| 158 | let z_bits: IndexSet = bits::remapped(pauli.z_bits(), support); |
| 159 | SparsePauli::from_bits(x_bits, z_bits, pauli.xz_phase_exponent()) |
| 160 | } |
| 161 | |
| 162 | pub fn as_sparse(pauli: &impl Pauli<PhaseExponentValue = u8>) -> SparsePauli { |
| 163 | let x_bits = pauli.x_bits().support().into(); |
| 164 | let z_bits = pauli.z_bits().support().into(); |
| 165 | SparsePauli::from_bits(x_bits, z_bits, pauli.xz_phase_exponent()) |
| 166 | } |
| 167 | |
| 168 | pub fn as_sparse_projective(pauli: &impl Pauli) -> SparsePauliProjective { |
| 169 | let x_bits = pauli.x_bits().support().into(); |
| 170 | let z_bits = pauli.z_bits().support().into(); |
| 171 | SparsePauliProjective::from_bits(x_bits, z_bits) |
| 172 | } |
| 173 | |