// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. use rustc_hash::FxHashMap; use crate::bits::{self, Bitwise, IndexAssignable, IndexSet}; use crate::pauli::generic::{PauliCharacterError, PauliUnitary}; use crate::quantum_core::{self, PositionedPauliObservable}; use super::{Pauli, PauliUnitaryProjective}; pub type SparsePauli = PauliUnitary; pub type SparsePauliProjective = PauliUnitaryProjective; // Note: Can be improved using PauliMutable trait // Question: Should 'i' be interpreted as I or complex phase ? impl TryFrom> for SparsePauli { type Error = PauliCharacterError; fn try_from(characters: FxHashMap) -> Result { let mut x_bits = IndexSet::new(); let mut z_bits = IndexSet::new(); let mut exponent: u8 = 0; for (index, character) in characters { match character { 'X' | 'x' => x_bits.assign_index(index, true), 'Z' | 'z' => z_bits.assign_index(index, true), 'Y' | 'y' => { exponent += 1; x_bits.assign_index(index, true); z_bits.assign_index(index, true); } 'I' => {} _ => return Err(PauliCharacterError {}), } } Ok(SparsePauli::from_bits(x_bits, z_bits, exponent)) } } // Note: Allow repeated indicies so that conversion never fails and replace with generic that relies on PauliMutable impl From<&[PositionedPauliObservable]> for SparsePauli { fn from(pauli_observable: &[PositionedPauliObservable]) -> Self { let mut obs_copy = Vec::from(pauli_observable); obs_copy.sort_unstable(); if obs_copy.len() > 1 { for j in 0..obs_copy.len() - 1 { assert!( obs_copy[j].qubit_id < obs_copy[j + 1].qubit_id, "Repeated qubit positions" ); } } let mut x_indices = IndexSet::new(); let mut z_indices = IndexSet::new(); let mut phase = 0u8; for quantum_core::PositionedPauliObservable { qubit_id, observable, } in obs_copy { match observable { quantum_core::PauliObservable::PlusI => (), quantum_core::PauliObservable::MinusI => phase += 2, quantum_core::PauliObservable::PlusX => x_indices.assign_index(qubit_id, true), quantum_core::PauliObservable::PlusZ => z_indices.assign_index(qubit_id, true), quantum_core::PauliObservable::MinusX => { x_indices.assign_index(qubit_id, true); phase += 2; } quantum_core::PauliObservable::MinusZ => { z_indices.assign_index(qubit_id, true); phase += 2; } quantum_core::PauliObservable::PlusY => { x_indices.assign_index(qubit_id, true); z_indices.assign_index(qubit_id, true); phase += 1; } quantum_core::PauliObservable::MinusY => { x_indices.assign_index(qubit_id, true); z_indices.assign_index(qubit_id, true); phase += 3; } } } PauliUnitary::from_bits(x_indices, z_indices, phase) } } impl From<&[PositionedPauliObservable]> for SparsePauliProjective { fn from(pauli_observable: &[PositionedPauliObservable]) -> Self { let mut obs_copy = Vec::from(pauli_observable); obs_copy.sort_unstable(); if obs_copy.len() > 1 { for j in 0..obs_copy.len() - 1 { assert!( obs_copy[j].qubit_id < obs_copy[j + 1].qubit_id, "Repeated qubit positions" ); } } let mut x_indices = IndexSet::new(); let mut z_indices = IndexSet::new(); for quantum_core::PositionedPauliObservable { qubit_id, observable, } in obs_copy { match observable { quantum_core::PauliObservable::PlusI | quantum_core::PauliObservable::MinusI => (), quantum_core::PauliObservable::PlusX | quantum_core::PauliObservable::MinusX => { x_indices.assign_index(qubit_id, true); } quantum_core::PauliObservable::PlusZ | quantum_core::PauliObservable::MinusZ => { z_indices.assign_index(qubit_id, true); } quantum_core::PauliObservable::PlusY | quantum_core::PauliObservable::MinusY => { x_indices.assign_index(qubit_id, true); z_indices.assign_index(qubit_id, true); } } } PauliUnitaryProjective::from_bits(x_indices, z_indices) } } impl From<[PositionedPauliObservable; LENGTH]> for SparsePauli { fn from(pauli_observable: [PositionedPauliObservable; LENGTH]) -> Self { pauli_observable.as_slice().into() } } impl From> for SparsePauli { fn from(value: Vec) -> Self { value.as_slice().into() } } impl From<[PositionedPauliObservable; LENGTH]> for SparsePauliProjective { fn from(pauli_observable: [PositionedPauliObservable; LENGTH]) -> Self { pauli_observable.as_slice().into() } } impl From> for SparsePauliProjective { fn from(value: Vec) -> Self { value.as_slice().into() } } pub fn remapped_sparse(pauli: &SparsePauli, support: &[usize]) -> SparsePauli { let x_bits: IndexSet = bits::remapped(pauli.x_bits(), support); let z_bits: IndexSet = bits::remapped(pauli.z_bits(), support); SparsePauli::from_bits(x_bits, z_bits, pauli.xz_phase_exponent()) } pub fn as_sparse(pauli: &impl Pauli) -> SparsePauli { let x_bits = pauli.x_bits().support().into(); let z_bits = pauli.z_bits().support().into(); SparsePauli::from_bits(x_bits, z_bits, pauli.xz_phase_exponent()) } pub fn as_sparse_projective(pauli: &impl Pauli) -> SparsePauliProjective { let x_bits = pauli.x_bits().support().into(); let z_bits = pauli.z_bits().support().into(); SparsePauliProjective::from_bits(x_bits, z_bits) }