use crate::fermions::FermionOperator;
use crate::mappings::JordanWignerSpinToFermion;
use crate::prelude::*;
use crate::spins::{PauliOperator, PlusMinusOperator};
use crate::{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::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};
const INTERNAL_BUG_ADD_OPERATOR_PRODUCT: &str = "Internal bug in add_operator_product.";
#[derive(
Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default,
)]
#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
pub enum SinglePauliOperator {
#[default]
Identity,
X,
Y,
Z,
}
impl FromStr for SinglePauliOperator {
type Err = StruqtureError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"I" => Ok(SinglePauliOperator::Identity),
"X" => Ok(SinglePauliOperator::X),
"Y" => Ok(SinglePauliOperator::Y),
"Z" => Ok(SinglePauliOperator::Z),
_ => Err(StruqtureError::IncorrectPauliEntry {
pauli: s.to_string(),
}),
}
}
}
impl fmt::Display for SinglePauliOperator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SinglePauliOperator::Identity => write!(f, "I"),
SinglePauliOperator::X => write!(f, "X"),
SinglePauliOperator::Y => write!(f, "Y"),
SinglePauliOperator::Z => write!(f, "Z"),
}
}
}
impl SinglePauliOperator {
pub fn multiply(left: SinglePauliOperator, right: SinglePauliOperator) -> (Self, Complex64) {
let (result, coeff): (SinglePauliOperator, Complex64) = match (left, right) {
(SinglePauliOperator::Identity, x) => (x, Complex64::new(1.0, 0.0)),
(x, SinglePauliOperator::Identity) => (x, Complex64::new(1.0, 0.0)),
(SinglePauliOperator::X, SinglePauliOperator::X) => {
(SinglePauliOperator::Identity, Complex64::new(1.0, 0.0))
}
(SinglePauliOperator::X, SinglePauliOperator::Y) => {
(SinglePauliOperator::Z, Complex64::new(0.0, 1.0))
}
(SinglePauliOperator::X, SinglePauliOperator::Z) => {
(SinglePauliOperator::Y, Complex64::new(0.0, -1.0))
}
(SinglePauliOperator::Y, SinglePauliOperator::X) => {
(SinglePauliOperator::Z, Complex64::new(0.0, -1.0))
}
(SinglePauliOperator::Y, SinglePauliOperator::Y) => {
(SinglePauliOperator::Identity, Complex64::new(1.0, 0.0))
}
(SinglePauliOperator::Y, SinglePauliOperator::Z) => {
(SinglePauliOperator::X, Complex64::new(0.0, 1.0))
}
(SinglePauliOperator::Z, SinglePauliOperator::X) => {
(SinglePauliOperator::Y, Complex64::new(0.0, 1.0))
}
(SinglePauliOperator::Z, SinglePauliOperator::Y) => {
(SinglePauliOperator::X, Complex64::new(0.0, -1.0))
}
(SinglePauliOperator::Z, SinglePauliOperator::Z) => {
(SinglePauliOperator::Identity, Complex64::new(1.0, 0.0))
}
};
(result, coeff)
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct PauliProduct {
items: TinyVec<[(usize, SinglePauliOperator); 5]>,
}
impl Serialize for PauliProduct {
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()
}
}
}
#[cfg(feature = "json_schema")]
use schemars;
#[cfg(feature = "json_schema")]
impl schemars::JsonSchema for PauliProduct {
fn schema_name() -> std::borrow::Cow<'static, str> {
"struqture::spins::PauliProduct".into()
}
fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"type": "string",
"description": "Represents products of Pauli Operators by a string of spin numbers followed by pauli operators. E.g. 0X10Y13Z14X."
})
}
}
impl crate::SerializationSupport for PauliProduct {
fn struqture_type() -> crate::StruqtureType {
crate::StruqtureType::PauliProduct
}
}
impl<'de> Deserialize<'de> for PauliProduct {
fn deserialize<D>(deserializer: D) -> Result<PauliProduct, 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 = PauliProduct;
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,
{
PauliProduct::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,
{
PauliProduct::from_str(v).map_err(|err| E::custom(format!("{err:?}")))
}
}
deserializer.deserialize_str(TemporaryVisitor)
} else {
struct PauliProductVisitor;
impl<'de> serde::de::Visitor<'de> for PauliProductVisitor {
type Value = PauliProduct;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
fmt::Formatter::write_str(formatter, "Identifier of PauliProduct variant")
}
fn visit_seq<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: SeqAccess<'de>,
{
let mut pp = PauliProduct::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, SinglePauliOperator));
let pp_visitor = PauliProductVisitor;
deserializer.deserialize_seq(pp_visitor)
}
}
}
impl SpinIndex for PauliProduct {
type SingleSpinType = SinglePauliOperator;
fn new() -> Self {
PauliProduct {
items: TinyVec::<[(usize, SinglePauliOperator); 5]>::with_capacity(5),
}
}
fn set_pauli(self, index: usize, pauli: SinglePauliOperator) -> 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 {
SinglePauliOperator::Identity => {
let _x = pp.items.remove(vecindex);
}
_ => pp.items[vecindex] = (insertindex, pauli),
}
} else {
match pauli {
SinglePauliOperator::Identity => (),
_ => {
pp.items.insert(vecindex, (index, pauli));
}
}
}
} else {
match pauli {
SinglePauliOperator::Identity => (),
_ => {
pp.items.push((index, pauli));
}
}
}
pp
}
fn get(&self, index: &usize) -> Option<&SinglePauliOperator> {
self.items
.iter()
.find_map(|(key, value)| if key == index { Some(value) } else { None })
}
fn iter(&self) -> std::slice::Iter<'_, (usize, SinglePauliOperator)> {
match &self.items {
TinyVec::Heap(x) => x.iter(),
TinyVec::Inline(x) => x.iter(),
}
}
fn remap_qubits(&self, mapping: &HashMap<usize, usize>) -> PauliProduct {
let mut mutable_internal: TinyVec<[(usize, SinglePauliOperator); 5]> =
TinyVec::<[(usize, SinglePauliOperator); 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")
});
PauliProduct {
items: mutable_internal,
}
}
fn multiply(left: PauliProduct, right: PauliProduct) -> (Self, Complex64) {
left * right
}
fn concatenate(&self, other: PauliProduct) -> Result<PauliProduct, 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(PauliProduct { items: return_list })
}
}
impl Ord for PauliProduct {
fn cmp(&self, other: &Self) -> Ordering {
let me: &TinyVec<[(usize, SinglePauliOperator); 5]> = &(self.items);
let them: &TinyVec<[(usize, SinglePauliOperator); 5]> = &(other.items);
match me.len().cmp(&them.len()) {
Ordering::Less => Ordering::Less,
Ordering::Equal => me
.into_iter()
.cloned()
.unzip::<usize, SinglePauliOperator, Vec<usize>, Vec<SinglePauliOperator>>()
.cmp(
&them
.into_iter()
.cloned()
.unzip::<usize, SinglePauliOperator, Vec<usize>, Vec<SinglePauliOperator>>(
),
), Ordering::Greater => Ordering::Greater,
}
}
}
impl PartialOrd for PauliProduct {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl CorrespondsTo<PauliProduct> for PauliProduct {
fn corresponds_to(&self) -> PauliProduct {
self.clone()
}
}
impl SymmetricIndex for PauliProduct {
fn hermitian_conjugate(&self) -> (Self, f64) {
(self.clone(), 1.0)
}
fn is_natural_hermitian(&self) -> bool {
true
}
}
impl Mul<PauliProduct> for PauliProduct {
type Output = (Self, Complex64);
fn mul(self, rhs: PauliProduct) -> Self::Output {
let mut factor = Complex64::new(1.0, 0.0);
let mut return_product = PauliProduct::new();
for (key, left_operator) in self.iter() {
match rhs.get(key) {
Some(right_operator) => {
let (tmp_product, tmp_factor) =
SinglePauliOperator::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.iter().filter(|(key, _)| self.get(key).is_none()) {
return_product = return_product.set_pauli(*key, *right_operator);
}
(return_product, factor)
}
}
impl GetValue<PauliProduct> for PauliProduct {
type ValueIn = CalculatorComplex;
type ValueOut = CalculatorComplex;
fn get_key(index: &PauliProduct) -> Self {
index.clone()
}
fn get_transform(
_index: &PauliProduct,
value: qoqo_calculator::CalculatorComplex,
) -> qoqo_calculator::CalculatorComplex {
value
}
}
impl PauliProduct {
#[cfg(feature = "struqture_1_export")]
pub fn to_struqture_1(&self) -> Result<struqture_1::spins::PauliProduct, StruqtureError> {
let self_string = self.to_string();
let struqture_1_product = struqture_1::spins::PauliProduct::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::PauliProduct,
) -> Result<Self, StruqtureError> {
let value_string = value.to_string();
let pauli_product = Self::from_str(&value_string)?;
Ok(pauli_product)
}
pub fn x(self, index: usize) -> Self {
self.set_pauli(index, SinglePauliOperator::X)
}
pub fn y(self, index: usize) -> Self {
self.set_pauli(index, SinglePauliOperator::Y)
}
pub fn z(self, index: usize) -> Self {
self.set_pauli(index, SinglePauliOperator::Z)
}
pub fn with_capacity(cap: usize) -> Self {
PauliProduct {
items: TinyVec::<[(usize, SinglePauliOperator); 5]>::with_capacity(cap),
}
}
}
impl Default for PauliProduct {
fn default() -> Self {
Self::new()
}
}
impl FromStr for PauliProduct {
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 PauliProduct: {s}"),
});
}
let mut internal: TinyVec<[(usize, SinglePauliOperator); 5]> =
TinyVec::<[(usize, SinglePauliOperator); 5]>::with_capacity(10);
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());
for (index, pauli) in vec_indices.zip(vec_paulis) {
match index.parse() {
Ok(num) => {
let spin: SinglePauliOperator = SinglePauliOperator::from_str(pauli)?;
match spin {
SinglePauliOperator::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",
)
});
match internal.windows(2).all(|w| w[0].0 < w[1].0) {
true => Ok(PauliProduct { items: internal }),
false => Err(StruqtureError::FromStringFailed {
msg: "At least one spin index is used more than once.".to_string(),
}),
}
}
}
}
impl fmt::Display for PauliProduct {
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 PauliProduct {
type Item = (usize, SinglePauliOperator);
type IntoIter = TinyVecIterator<[(usize, SinglePauliOperator); 5]>;
fn into_iter(self) -> Self::IntoIter {
self.items.into_iter()
}
}
impl FromIterator<(usize, SinglePauliOperator)> for PauliProduct {
fn from_iter<I: IntoIterator<Item = (usize, SinglePauliOperator)>>(iter: I) -> Self {
let mut pp = PauliProduct::new();
for (index, pauli) in iter {
pp = pp.set_pauli(index, pauli);
}
pp
}
}
impl Extend<(usize, SinglePauliOperator)> for PauliProduct {
fn extend<I: IntoIterator<Item = (usize, SinglePauliOperator)>>(&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 PauliProduct {
type Output = FermionOperator;
fn jordan_wigner(&self) -> Self::Output {
let mut qubit_operator = PauliOperator::new();
qubit_operator
.add_operator_product(self.clone(), 1.0.into())
.expect(INTERNAL_BUG_ADD_OPERATOR_PRODUCT);
let plus_minus_operator = PlusMinusOperator::from(qubit_operator);
plus_minus_operator.jordan_wigner()
}
}