use crate::fermions::FermionOperator;
use crate::mappings::JordanWignerSpinToFermion;
use crate::spins::SinglePauliOperator;
use crate::{CooSparseMatrix, CorrespondsTo, GetValue, SpinIndex, StruqtureError, SymmetricIndex};
use num_complex::Complex64;
use qoqo_calculator::CalculatorComplex;
use serde::de::{Deserializer, Error, SeqAccess, Visitor};
use serde::ser::{SerializeSeq, Serializer};
use serde::{Deserialize, Serialize};
use std::cmp::{self, Ordering};
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
use std::iter::{FromIterator, IntoIterator};
use std::ops::Mul;
use std::str::FromStr;
use tinyvec::{TinyVec, TinyVecIterator};
use super::PauliProduct;
#[derive(
Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default,
)]
#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
pub enum SingleDecoherenceOperator {
#[default]
Identity,
X,
IY,
Z,
}
impl FromStr for SingleDecoherenceOperator {
type Err = StruqtureError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"I" => Ok(SingleDecoherenceOperator::Identity),
"X" => Ok(SingleDecoherenceOperator::X),
"iY" => Ok(SingleDecoherenceOperator::IY),
"Z" => Ok(SingleDecoherenceOperator::Z),
_ => Err(StruqtureError::IncorrectPauliEntry {
pauli: s.to_string(),
}),
}
}
}
impl fmt::Display for SingleDecoherenceOperator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SingleDecoherenceOperator::Identity => write!(f, "I"),
SingleDecoherenceOperator::X => write!(f, "X"),
SingleDecoherenceOperator::IY => write!(f, "iY"),
SingleDecoherenceOperator::Z => write!(f, "Z"),
}
}
}
impl SingleDecoherenceOperator {
pub fn multiply(
left: SingleDecoherenceOperator,
right: SingleDecoherenceOperator,
) -> (Self, f64) {
let result_vec: (SingleDecoherenceOperator, f64) = match (left, right) {
(SingleDecoherenceOperator::Identity, x) => (x, 1.0),
(x, SingleDecoherenceOperator::Identity) => (x, 1.0),
(SingleDecoherenceOperator::X, SingleDecoherenceOperator::X) => {
(SingleDecoherenceOperator::Identity, 1.0)
}
(SingleDecoherenceOperator::X, SingleDecoherenceOperator::IY) => {
(SingleDecoherenceOperator::Z, -1.0)
}
(SingleDecoherenceOperator::X, SingleDecoherenceOperator::Z) => {
(SingleDecoherenceOperator::IY, -1.0)
}
(SingleDecoherenceOperator::IY, SingleDecoherenceOperator::X) => {
(SingleDecoherenceOperator::Z, 1.0)
}
(SingleDecoherenceOperator::IY, SingleDecoherenceOperator::IY) => {
(SingleDecoherenceOperator::Identity, -1.0)
}
(SingleDecoherenceOperator::IY, SingleDecoherenceOperator::Z) => {
(SingleDecoherenceOperator::X, -1.0)
}
(SingleDecoherenceOperator::Z, SingleDecoherenceOperator::X) => {
(SingleDecoherenceOperator::IY, 1.0)
}
(SingleDecoherenceOperator::Z, SingleDecoherenceOperator::IY) => {
(SingleDecoherenceOperator::X, 1.0)
}
(SingleDecoherenceOperator::Z, SingleDecoherenceOperator::Z) => {
(SingleDecoherenceOperator::Identity, 1.0)
}
};
result_vec
}
pub fn decoherence_to_spin(
decoherence: SingleDecoherenceOperator,
) -> (SinglePauliOperator, Complex64) {
match decoherence {
SingleDecoherenceOperator::Identity => {
(SinglePauliOperator::Identity, Complex64::new(1.0, 0.0))
}
SingleDecoherenceOperator::X => (SinglePauliOperator::X, Complex64::new(1.0, 0.0)),
SingleDecoherenceOperator::IY => (SinglePauliOperator::Y, Complex64::new(0.0, 1.0)),
SingleDecoherenceOperator::Z => (SinglePauliOperator::Z, Complex64::new(1.0, 0.0)),
}
}
pub fn spin_to_decoherence(
spin: SinglePauliOperator,
) -> (SingleDecoherenceOperator, Complex64) {
match spin {
SinglePauliOperator::Identity => (
SingleDecoherenceOperator::Identity,
Complex64::new(1.0, 0.0),
),
SinglePauliOperator::X => (SingleDecoherenceOperator::X, Complex64::new(1.0, 0.0)),
SinglePauliOperator::Y => (SingleDecoherenceOperator::IY, Complex64::new(0.0, -1.0)),
SinglePauliOperator::Z => (SingleDecoherenceOperator::Z, Complex64::new(1.0, 0.0)),
}
}
pub fn hermitian_conjugate(&self) -> (Self, f64) {
match self {
SingleDecoherenceOperator::Identity => (SingleDecoherenceOperator::Identity, 1.0),
SingleDecoherenceOperator::Z => (SingleDecoherenceOperator::Z, 1.0),
SingleDecoherenceOperator::X => (SingleDecoherenceOperator::X, 1.0),
SingleDecoherenceOperator::IY => (SingleDecoherenceOperator::IY, -1.0),
}
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct DecoherenceProduct {
items: TinyVec<[(usize, SingleDecoherenceOperator); 5]>,
}
#[cfg(feature = "json_schema")]
impl schemars::JsonSchema for DecoherenceProduct {
fn schema_name() -> std::borrow::Cow<'static, str> {
"struqture::spins::DecoherenceProduct".into()
}
fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"type": "string",
"description": "Represents products of Decoherence Operators (X, iY, Z) by a string of spin numbers followed by pauli operators. E.g. 0X10iY13Z14X."
})
}
}
impl crate::SerializationSupport for DecoherenceProduct {
fn struqture_type() -> crate::StruqtureType {
crate::StruqtureType::DecoherenceProduct
}
}
impl Serialize for DecoherenceProduct {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let readable = serializer.is_human_readable();
if readable {
serializer.serialize_str(&self.to_string())
} else {
let mut sequence = serializer.serialize_seq(Some(self.items.len()))?;
for item in self.items.iter() {
sequence.serialize_element(item)?;
}
sequence.end()
}
}
}
impl<'de> Deserialize<'de> for DecoherenceProduct {
fn deserialize<D>(deserializer: D) -> Result<DecoherenceProduct, D::Error>
where
D: Deserializer<'de>,
{
let human_readable = deserializer.is_human_readable();
if human_readable {
struct TemporaryVisitor;
impl<'de> Visitor<'de> for TemporaryVisitor {
type Value = DecoherenceProduct;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("String")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
DecoherenceProduct::from_str(v).map_err(|err| E::custom(format!("{err:?}")))
}
fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
where
E: Error,
{
DecoherenceProduct::from_str(v).map_err(|err| E::custom(format!("{err:?}")))
}
}
deserializer.deserialize_str(TemporaryVisitor)
} else {
struct DecoherenceProductVisitor;
impl<'de> serde::de::Visitor<'de> for DecoherenceProductVisitor {
type Value = DecoherenceProduct;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
fmt::Formatter::write_str(formatter, "Identifier of DecoherenceProduct variant")
}
fn visit_seq<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: SeqAccess<'de>,
{
let mut pp = DecoherenceProduct::new();
while let Some(item) = access.next_element()? {
let entry: Entry = item;
pp = pp.set_pauli(entry.0 .0, entry.0 .1);
}
Ok(pp)
}
}
#[derive(Deserialize)]
#[serde(transparent)]
struct Entry((usize, SingleDecoherenceOperator));
let pp_visitor = DecoherenceProductVisitor;
deserializer.deserialize_seq(pp_visitor)
}
}
}
impl SpinIndex for DecoherenceProduct {
type SingleSpinType = SingleDecoherenceOperator;
fn new() -> Self {
DecoherenceProduct {
items: TinyVec::<[(usize, SingleDecoherenceOperator); 5]>::with_capacity(5),
}
}
fn set_pauli(self, index: usize, pauli: SingleDecoherenceOperator) -> Self {
let mut pp = self;
if let Some((vecindex, insertindex, index_in_use)) =
pp.items
.iter()
.enumerate()
.find_map(|(vecindex, (innerindex, _))| {
if innerindex >= &index {
Some((vecindex, *innerindex, innerindex == &index))
} else {
None
}
})
{
if index_in_use {
match pauli {
SingleDecoherenceOperator::Identity => {
let _x = pp.items.remove(vecindex);
}
_ => pp.items[vecindex] = (insertindex, pauli),
}
} else {
match pauli {
SingleDecoherenceOperator::Identity => (),
_ => {
pp.items.insert(vecindex, (index, pauli));
}
}
}
} else {
match pauli {
SingleDecoherenceOperator::Identity => (),
_ => {
pp.items.push((index, pauli));
}
}
}
pp
}
fn get(&self, index: &usize) -> Option<&SingleDecoherenceOperator> {
self.items
.iter()
.find_map(|(key, value)| if key == index { Some(value) } else { None })
}
fn iter(&self) -> std::slice::Iter<'_, (usize, SingleDecoherenceOperator)> {
self.items.iter()
}
fn remap_qubits(&self, mapping: &HashMap<usize, usize>) -> DecoherenceProduct {
let mut mutable_internal: TinyVec<[(usize, SingleDecoherenceOperator); 5]> =
TinyVec::<[(usize, SingleDecoherenceOperator); 5]>::with_capacity(10);
for (key, val) in self.iter() {
mutable_internal.push(match mapping.get(key) {
Some(x) => (*x, *val),
None => (*key, *val),
});
}
mutable_internal.sort_by(|(left_index, _), (right_index, _)| {
left_index
.partial_cmp(right_index)
.expect("Cannot compare two unsigned integers internal error in struqture.spins")
});
DecoherenceProduct {
items: mutable_internal,
}
}
fn multiply(left: DecoherenceProduct, right: DecoherenceProduct) -> (Self, Complex64) {
left * right
}
fn concatenate(&self, other: DecoherenceProduct) -> Result<DecoherenceProduct, StruqtureError> {
let mut return_list = self.items.clone();
for (key, val) in other.iter() {
if return_list.iter().any(|(index, _)| index == key) {
return Err(StruqtureError::ProductIndexAlreadyOccupied { index: *key });
} else {
return_list.push((*key, *val));
}
}
return_list.sort_by(|(left_index, _), (right_index, _)| {
left_index
.partial_cmp(right_index)
.expect("Cannot compare two unsigned integers internal error in struqture.spins")
});
Ok(DecoherenceProduct { items: return_list })
}
}
impl Ord for DecoherenceProduct {
fn cmp(&self, other: &Self) -> Ordering {
let me: &TinyVec<[(usize, SingleDecoherenceOperator); 5]> = &(self.items);
let them: &TinyVec<[(usize, SingleDecoherenceOperator); 5]> = &(other.items);
match me.len().cmp(&them.len()) {
Ordering::Less => Ordering::Less,
Ordering::Equal => me.cmp(them), Ordering::Greater => Ordering::Greater,
}
}
}
impl PartialOrd for DecoherenceProduct {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl CorrespondsTo<DecoherenceProduct> for DecoherenceProduct {
fn corresponds_to(&self) -> DecoherenceProduct {
self.clone()
}
}
impl SymmetricIndex for DecoherenceProduct {
fn hermitian_conjugate(&self) -> (Self, f64) {
let change_sign: bool = self
.items
.iter()
.map(|(_, b)| match *b {
SingleDecoherenceOperator::IY => 1_usize,
_ => 0_usize,
})
.sum::<usize>()
% 2
== 1;
(
Self {
items: self.items.clone(),
},
if change_sign { -1_f64 } else { 1_f64 },
)
}
fn is_natural_hermitian(&self) -> bool {
let (_, a) = self.hermitian_conjugate();
a > 0.0
}
}
impl Mul<DecoherenceProduct> for DecoherenceProduct {
type Output = (Self, Complex64);
fn mul(self, rhs: DecoherenceProduct) -> Self::Output {
let mut factor = Complex64::new(1.0, 0.0);
let mut return_product = DecoherenceProduct::new();
for (key, left_operator) in self.clone().into_iter() {
match rhs.get(&key) {
Some(right_operator) => {
let (tmp_product, tmp_factor) =
SingleDecoherenceOperator::multiply(left_operator, *right_operator);
factor *= tmp_factor;
return_product = return_product.set_pauli(key, tmp_product);
}
None => {
return_product = return_product.set_pauli(key, left_operator);
}
}
}
for (key, right_operator) in rhs
.into_iter()
.filter(|(key_internal, _)| self.get(key_internal).is_none())
{
return_product = return_product.set_pauli(key, right_operator);
}
(return_product, factor)
}
}
impl GetValue<DecoherenceProduct> for DecoherenceProduct {
type ValueIn = CalculatorComplex;
type ValueOut = CalculatorComplex;
fn get_key(index: &DecoherenceProduct) -> Self {
index.clone()
}
fn get_transform(
_index: &DecoherenceProduct,
value: qoqo_calculator::CalculatorComplex,
) -> qoqo_calculator::CalculatorComplex {
value
}
}
impl GetValue<(DecoherenceProduct, DecoherenceProduct)>
for (DecoherenceProduct, DecoherenceProduct)
{
type ValueIn = CalculatorComplex;
type ValueOut = CalculatorComplex;
fn get_key(index: &(DecoherenceProduct, DecoherenceProduct)) -> Self {
index.clone()
}
fn get_transform(
_index: &(DecoherenceProduct, DecoherenceProduct),
value: qoqo_calculator::CalculatorComplex,
) -> qoqo_calculator::CalculatorComplex {
value
}
}
impl DecoherenceProduct {
#[cfg(feature = "struqture_1_export")]
pub fn to_struqture_1(&self) -> Result<struqture_1::spins::DecoherenceProduct, StruqtureError> {
let self_string = self.to_string();
let struqture_1_product = struqture_1::spins::DecoherenceProduct::from_str(&self_string)
.map_err(|err| StruqtureError::GenericError {
msg: format!("{err}"),
})?;
Ok(struqture_1_product)
}
#[cfg(feature = "struqture_1_import")]
pub fn from_struqture_1(
value: &struqture_1::spins::DecoherenceProduct,
) -> Result<Self, StruqtureError> {
let value_string = value.to_string();
let decoh_product = Self::from_str(&value_string)?;
Ok(decoh_product)
}
pub fn x(self, index: usize) -> Self {
self.set_pauli(index, SingleDecoherenceOperator::X)
}
pub fn iy(self, index: usize) -> Self {
self.set_pauli(index, SingleDecoherenceOperator::IY)
}
pub fn z(self, index: usize) -> Self {
self.set_pauli(index, SingleDecoherenceOperator::Z)
}
pub fn with_capacity(cap: usize) -> Self {
DecoherenceProduct {
items: TinyVec::<[(usize, SingleDecoherenceOperator); 5]>::with_capacity(cap),
}
}
pub fn to_coo(&self, number_spins: usize) -> Result<CooSparseMatrix, StruqtureError> {
let dimension = 2usize.pow(number_spins as u32);
let mut values: Vec<Complex64> = Vec::with_capacity(dimension);
let mut rows: Vec<usize> = Vec::with_capacity(dimension);
let mut columns: Vec<usize> = Vec::with_capacity(dimension);
for row in 0..dimension {
let (col, val) = self.sparse_matrix_entries_on_row(row)?;
rows.push(row);
columns.push(col);
values.push(val);
}
Ok((values, (rows, columns)))
}
fn sparse_matrix_entries_on_row(
&self,
row: usize,
) -> Result<(usize, Complex64), StruqtureError> {
let mut column = row;
let mut prefac: Complex64 = 1.0.into();
for (spin_op_index, pauliop) in self.iter() {
match *pauliop {
SingleDecoherenceOperator::X => {
match row.div_euclid(2usize.pow(*spin_op_index as u32)) % 2 {
0 => column += 2usize.pow(*spin_op_index as u32),
1 => column -= 2usize.pow(*spin_op_index as u32),
_ => panic!("Internal error in constructing matrix"),
}
}
SingleDecoherenceOperator::IY => {
match row.div_euclid(2usize.pow(*spin_op_index as u32)) % 2 {
0 => {
column += 2usize.pow(*spin_op_index as u32);
prefac *= Complex64::new(1.0, 0.0);
}
1 => {
column -= 2usize.pow(*spin_op_index as u32);
prefac *= Complex64::new(-1.0, 0.0);
}
_ => panic!("Internal error in constructing matrix"),
};
}
SingleDecoherenceOperator::Z => {
match row.div_euclid(2usize.pow(*spin_op_index as u32)) % 2 {
0 => {
prefac *= Complex64::new(1.0, 0.0);
}
1 => {
prefac *= Complex64::new(-1.0, 0.0);
}
_ => panic!("Internal error in constructing matrix"),
};
}
SingleDecoherenceOperator::Identity => (),
}
}
Ok((column, prefac))
}
pub fn decoherence_to_spin(dp: DecoherenceProduct) -> (PauliProduct, Complex64) {
let mut coeff = Complex64::new(1.0, 0.0);
let cap = cmp::max(5_usize, dp.len());
let mut new_pp = PauliProduct::with_capacity(cap);
for (site, op) in dp {
let (pp, newcoeff) = SingleDecoherenceOperator::decoherence_to_spin(op);
coeff *= newcoeff;
new_pp = new_pp.set_pauli(site, pp);
}
(new_pp, coeff)
}
pub fn spin_to_decoherence(pp: PauliProduct) -> (DecoherenceProduct, Complex64) {
let mut coeff = Complex64::new(1.0, 0.0);
let cap = cmp::max(5_usize, pp.len());
let mut new_dp = DecoherenceProduct::with_capacity(cap);
for (site, op) in pp {
let (dp, newcoeff) = SingleDecoherenceOperator::spin_to_decoherence(op);
coeff *= newcoeff;
new_dp = new_dp.set_pauli(site, dp);
}
(new_dp, coeff)
}
}
impl Default for DecoherenceProduct {
fn default() -> Self {
Self::new()
}
}
impl FromStr for DecoherenceProduct {
type Err = StruqtureError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "I" || s.is_empty() {
Ok(Self::new()) } else {
if !s.starts_with(char::is_numeric) {
return Err(StruqtureError::FromStringFailed {
msg: format!("Missing spin index in the following DecoherenceProduct: {s}"),
});
}
let value = s.to_string();
let vec_paulis = value.split(char::is_numeric).filter(|s| !s.is_empty());
let vec_indices = value.split(char::is_alphabetic).filter(|s| !s.is_empty());
let mut internal: TinyVec<[(usize, SingleDecoherenceOperator); 5]> =
TinyVec::<[(usize, SingleDecoherenceOperator); 5]>::with_capacity(10);
for (index, pauli) in vec_indices.zip(vec_paulis) {
match index.parse() {
Ok(num) => {
let spin: SingleDecoherenceOperator =
SingleDecoherenceOperator::from_str(pauli)?;
match spin {
SingleDecoherenceOperator::Identity => (),
_ => {
internal.push((num, spin));
}
}
}
Err(_) => {
return Err(StruqtureError::FromStringFailed {
msg: format!("Using {index} instead of unsigned integer as spin index"),
})
}
}
}
internal.sort_by(|(left_index, _), (right_index, _)| {
left_index.partial_cmp(right_index).expect(
"Cannot compare two unsigned integers internal error in struqture.spins",
)
});
Ok(DecoherenceProduct { items: internal })
}
}
}
impl fmt::Display for DecoherenceProduct {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut string: String = String::new();
if self.items.is_empty() {
string.push('I');
} else {
for (index, pauli) in self.items.iter() {
string.push_str(format!("{index}").as_str());
string.push_str(format!("{pauli}").as_str());
}
}
write!(f, "{string}")
}
}
impl IntoIterator for DecoherenceProduct {
type Item = (usize, SingleDecoherenceOperator);
type IntoIter = TinyVecIterator<[(usize, SingleDecoherenceOperator); 5]>;
fn into_iter(self) -> Self::IntoIter {
self.items.into_iter()
}
}
impl FromIterator<(usize, SingleDecoherenceOperator)> for DecoherenceProduct {
fn from_iter<I: IntoIterator<Item = (usize, SingleDecoherenceOperator)>>(iter: I) -> Self {
let mut pp = DecoherenceProduct::new();
for (index, pauli) in iter {
pp = pp.set_pauli(index, pauli);
}
pp
}
}
impl Extend<(usize, SingleDecoherenceOperator)> for DecoherenceProduct {
fn extend<I: IntoIterator<Item = (usize, SingleDecoherenceOperator)>>(&mut self, iter: I) {
let mut pp = self.clone();
for (index, pauli) in iter {
pp = pp.set_pauli(index, pauli);
}
*self = pp;
}
}
impl JordanWignerSpinToFermion for DecoherenceProduct {
type Output = FermionOperator;
fn jordan_wigner(&self) -> Self::Output {
let pp = DecoherenceProduct::decoherence_to_spin(self.clone());
pp.0.jordan_wigner() * pp.1
}
}