use super::FermionIndex;
use crate::mappings::JordanWignerFermionToSpin;
use crate::prelude::*;
use crate::spins::{PauliHamiltonian, PauliOperator, PauliProduct, SinglePauliOperator};
use crate::{
CorrespondsTo, CreatorsAnnihilators, GetValue, ModeIndex, StruqtureError, SymmetricIndex,
};
use qoqo_calculator::*;
use serde::{
de::{Error, SeqAccess, Visitor},
ser::SerializeTuple,
Deserialize, Deserializer, Serialize, Serializer,
};
use std::cmp::Ordering;
use std::{ops::Mul, str::FromStr};
use tinyvec::TinyVec;
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct FermionProduct {
creators: TinyVec<[usize; 2]>,
annihilators: TinyVec<[usize; 2]>,
}
#[cfg(feature = "json_schema")]
impl schemars::JsonSchema for FermionProduct {
fn schema_name() -> std::borrow::Cow<'static, str> {
"FermionProduct".into()
}
fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"type": "string",
"description": "Represents products of Fermionic creators and annhilators by a string creators (c) or annihilators (a) followed by the modes they are acting on. E.g. c0a1."
})
}
}
impl crate::SerializationSupport for FermionProduct {
fn struqture_type() -> crate::StruqtureType {
crate::StruqtureType::FermionProduct
}
}
impl Serialize for FermionProduct {
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 tuple = serializer.serialize_tuple(2)?;
tuple.serialize_element(&self.creators)?;
tuple.serialize_element(&self.annihilators)?;
tuple.end()
}
}
}
impl<'de> Deserialize<'de> for FermionProduct {
fn deserialize<D>(deserializer: D) -> Result<FermionProduct, 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 = FermionProduct;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("String")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
FermionProduct::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: serde::de::Error,
{
FermionProduct::from_str(v).map_err(|err| E::custom(format!("{err:?}")))
}
}
deserializer.deserialize_str(TemporaryVisitor)
} else {
struct FermionProductVisitor;
impl<'de> serde::de::Visitor<'de> for FermionProductVisitor {
type Value = FermionProduct;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Formatter::write_str(
formatter,
"Tuple of two sequences of unsigned integers",
)
}
fn visit_seq<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: SeqAccess<'de>,
{
let creators: TinyVec<[usize; 2]> = match access.next_element()? {
Some(x) => x,
None => {
return Err(M::Error::custom("Missing creator sequence".to_string()));
}
};
let annihilators: TinyVec<[usize; 2]> = match access.next_element()? {
Some(x) => x,
None => {
return Err(M::Error::custom(
"Missing annihilator sequence".to_string(),
));
}
};
FermionProduct::new(creators, annihilators).map_err(M::Error::custom)
}
}
let pp_visitor = FermionProductVisitor;
deserializer.deserialize_tuple(2, pp_visitor)
}
}
}
impl ModeIndex for FermionProduct {
fn new(
creators: impl IntoIterator<Item = usize>,
annihilators: impl IntoIterator<Item = usize>,
) -> Result<Self, StruqtureError> {
let creators: TinyVec<[usize; 2]> = creators.into_iter().collect();
match creators.windows(2).all(|w| w[0] < w[1]) {
true => {}
false => return Err(StruqtureError::IncorrectlyOrderedIndices),
}
let annihilators: TinyVec<[usize; 2]> = annihilators.into_iter().collect();
match annihilators.windows(2).all(|w| w[0] < w[1]) {
true => {}
false => return Err(StruqtureError::IncorrectlyOrderedIndices),
}
Ok(Self {
creators,
annihilators,
})
}
fn creators(&self) -> std::slice::Iter<'_, usize> {
self.creators.iter()
}
fn annihilators(&self) -> std::slice::Iter<'_, usize> {
self.annihilators.iter()
}
fn create_valid_pair(
creators: impl IntoIterator<Item = usize>,
annihilators: impl IntoIterator<Item = usize>,
value: qoqo_calculator::CalculatorComplex,
) -> Result<(Self, qoqo_calculator::CalculatorComplex), StruqtureError> {
let creators: TinyVec<[usize; 2]> = creators.into_iter().collect();
let (new_creators, contains_double, parity_c) = sort_and_signal(creators);
if contains_double {
return Err(StruqtureError::IndicesContainDoubles {});
}
let annihilators: TinyVec<[usize; 2]> = annihilators.into_iter().collect();
let (new_annihilators, contains_double, parity_a) = sort_and_signal(annihilators);
if contains_double {
return Err(StruqtureError::IndicesContainDoubles {});
}
let value = if (parity_c + parity_a) % 2 != 0 {
value * -1.0
} else {
value
};
Ok((
Self {
creators: new_creators,
annihilators: new_annihilators,
},
value,
))
}
}
impl FermionIndex for FermionProduct {}
impl FermionProduct {
#[cfg(feature = "struqture_1_export")]
pub fn to_struqture_1(&self) -> Result<struqture_1::fermions::FermionProduct, StruqtureError> {
let self_string = self.to_string();
let struqture_1_product = struqture_1::fermions::FermionProduct::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::fermions::FermionProduct,
) -> Result<Self, StruqtureError> {
let value_string = value.to_string();
let pauli_product = Self::from_str(&value_string)?;
Ok(pauli_product)
}
}
impl CorrespondsTo<FermionProduct> for FermionProduct {
fn corresponds_to(&self) -> FermionProduct {
self.clone()
}
}
impl CorrespondsTo<HermitianFermionProduct> for FermionProduct {
fn corresponds_to(&self) -> HermitianFermionProduct {
if self.creators().min() > self.annihilators().min() {
HermitianFermionProduct {
creators: self.annihilators.clone(),
annihilators: self.creators.clone(),
}
} else {
HermitianFermionProduct {
creators: self.creators.clone(),
annihilators: self.annihilators.clone(),
}
}
}
}
impl SymmetricIndex for FermionProduct {
fn hermitian_conjugate(&self) -> (Self, f64) {
let mut creators = self.annihilators.clone();
creators.reverse();
let mut annihilators = self.creators.clone();
annihilators.reverse();
let (new, value) = FermionProduct::create_valid_pair(creators, annihilators, 1.0.into())
.expect("Bug: somehow commuted through and got a complex value");
(
new,
*value
.re
.float()
.expect("Bug: somehow commuted through and got a complex value"),
)
}
fn is_natural_hermitian(&self) -> bool {
self.creators == self.annihilators
}
}
impl Mul<FermionProduct> for FermionProduct {
type Output = Vec<(FermionProduct, f64)>;
fn mul(self, rhs: FermionProduct) -> Self::Output {
let mut output_vec: Vec<(FermionProduct, f64)> = Vec::new();
let commuted_creators_annihilators =
commute_creator_annihilator_fermionic(&self.annihilators, &rhs.creators);
for ((new_creators, mut new_annihilators), prefac) in commuted_creators_annihilators {
let mut tmp_creators = self.creators.clone();
tmp_creators.extend(new_creators);
new_annihilators.extend(rhs.annihilators().copied());
match FermionProduct::create_valid_pair(tmp_creators, new_annihilators, prefac.into()) {
Ok((tmp_fermion_product, sign)) => {
output_vec.push((
tmp_fermion_product,
*sign
.re
.float()
.expect("Bug: somehow commuted through and got a complex value"),
));
}
Err(StruqtureError::IndicesContainDoubles) => continue,
_ => panic!("Internal bug in `create_valid_pair`"),
}
}
output_vec
}
}
impl Mul<Vec<FermionProduct>> for FermionProduct {
type Output = Vec<(FermionProduct, f64)>;
fn mul(self, rhs: Vec<FermionProduct>) -> Self::Output {
let mut output_vec: Vec<(FermionProduct, f64)> = Vec::new();
for rh_bp in rhs.iter() {
output_vec.append(&mut (self.clone() * rh_bp.clone()))
}
output_vec
}
}
impl Mul<FermionProduct> for Vec<FermionProduct> {
type Output = Vec<(FermionProduct, f64)>;
fn mul(self, rhs: FermionProduct) -> Self::Output {
let mut output_vec: Vec<(FermionProduct, f64)> = Vec::new();
for lh_bp in self {
output_vec.append(&mut (lh_bp * rhs.clone()))
}
output_vec
}
}
impl Mul<HermitianFermionProduct> for FermionProduct {
type Output = Vec<(FermionProduct, f64)>;
fn mul(self, rhs: HermitianFermionProduct) -> Self::Output {
let mut output_vec: Vec<(FermionProduct, f64)> = Vec::new();
let mut right_to_mul: Vec<(FermionProduct, f64)> = Vec::new();
let hfp_to_fp = FermionProduct::new(rhs.creators, rhs.annihilators)
.expect("Could not convert rhs into a FermionProduct");
right_to_mul.push((hfp_to_fp.clone(), 1.0));
if !hfp_to_fp.is_natural_hermitian() {
right_to_mul.push(hfp_to_fp.hermitian_conjugate());
}
for (right, rsign) in right_to_mul {
let res_vec: Vec<(FermionProduct, f64)> = self.clone() * right;
for (fp, val) in res_vec.iter() {
output_vec.push((fp.clone(), val * rsign * 1.0));
}
}
output_vec
}
}
impl GetValue<FermionProduct> for FermionProduct {
type ValueIn = CalculatorComplex;
type ValueOut = CalculatorComplex;
fn get_key(index: &FermionProduct) -> Self {
index.clone()
}
fn get_transform(
_index: &FermionProduct,
value: qoqo_calculator::CalculatorComplex,
) -> qoqo_calculator::CalculatorComplex {
value
}
}
impl std::fmt::Display for FermionProduct {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut string: String = String::new();
if self.creators.is_empty() & self.annihilators.is_empty() {
string.push('I'); } else {
for index in self.creators() {
string.push_str(format!("c{index}").as_str());
}
for index in self.annihilators() {
string.push_str(format!("a{index}").as_str());
}
}
write!(f, "{string}")
}
}
impl FromStr for FermionProduct {
type Err = StruqtureError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "I" {
Self::new([], [])
} else {
let mut creators: TinyVec<[usize; 2]> = TinyVec::<[usize; 2]>::with_capacity(2);
let mut annihilators: TinyVec<[usize; 2]> = TinyVec::<[usize; 2]>::with_capacity(2);
let operators = s.split(char::is_numeric).filter(|s| !s.is_empty());
let indices = s.split(char::is_alphabetic).filter(|s| !s.is_empty());
let mut parsing_creators: bool = true;
for (index, op) in indices.zip(operators) {
match index.parse() {
Ok(num) => {
match op{
"c" => {if parsing_creators{ creators.push(num);} else{return Err(StruqtureError::IndicesNotNormalOrdered{index_i: num, index_j: num+1})}}
"a" => {annihilators.push(num); parsing_creators = false;}
_ => return Err(StruqtureError::FromStringFailed{msg: format!("Used operator {op} that is neither 'c' nor 'a' in FermionProduct::from_str")})
}
}
Err(_) => return Err(StruqtureError::FromStringFailed{msg: format!("Index of Fermion operator {index} is not a FermionProduct::from_str")}),
}
}
Self::new(creators, annihilators)
}
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct HermitianFermionProduct {
creators: TinyVec<[usize; 2]>,
annihilators: TinyVec<[usize; 2]>,
}
#[cfg(feature = "json_schema")]
impl schemars::JsonSchema for HermitianFermionProduct {
fn schema_name() -> std::borrow::Cow<'static, str> {
"HermitianFermionProduct".into()
}
fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"type": "string",
"description": "Represents products of Fermionic creators and annhilators by a string creators (c) or annihilators (a) followed by the modes they are acting on. E.g. c0a1."
})
}
}
impl crate::SerializationSupport for HermitianFermionProduct {
fn struqture_type() -> crate::StruqtureType {
crate::StruqtureType::HermitianFermionProduct
}
}
impl Serialize for HermitianFermionProduct {
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 tuple = serializer.serialize_tuple(2)?;
tuple.serialize_element(&self.creators)?;
tuple.serialize_element(&self.annihilators)?;
tuple.end()
}
}
}
impl<'de> Deserialize<'de> for HermitianFermionProduct {
fn deserialize<D>(deserializer: D) -> Result<HermitianFermionProduct, 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 = HermitianFermionProduct;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("String")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
HermitianFermionProduct::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: serde::de::Error,
{
HermitianFermionProduct::from_str(v)
.map_err(|err| E::custom(format!("{err:?}")))
}
}
deserializer.deserialize_str(TemporaryVisitor)
} else {
struct FermionProductVisitor;
impl<'de> serde::de::Visitor<'de> for FermionProductVisitor {
type Value = HermitianFermionProduct;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Formatter::write_str(
formatter,
"Tuple of two sequences of unsigned integers",
)
}
fn visit_seq<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: SeqAccess<'de>,
{
let creators: TinyVec<[usize; 2]> = match access.next_element()? {
Some(x) => x,
None => {
return Err(M::Error::custom("Missing creator sequence".to_string()));
}
};
let annihilators: TinyVec<[usize; 2]> = match access.next_element()? {
Some(x) => x,
None => {
return Err(M::Error::custom(
"Missing annihilator sequence".to_string(),
));
}
};
HermitianFermionProduct::new(creators, annihilators).map_err(M::Error::custom)
}
}
let pp_visitor = FermionProductVisitor;
deserializer.deserialize_tuple(2, pp_visitor)
}
}
}
impl ModeIndex for HermitianFermionProduct {
fn new(
creators: impl IntoIterator<Item = usize>,
annihilators: impl IntoIterator<Item = usize>,
) -> Result<Self, StruqtureError> {
let creators: TinyVec<[usize; 2]> = creators.into_iter().collect();
match creators.windows(2).all(|w| w[0] < w[1]) {
true => {}
false => return Err(StruqtureError::IncorrectlyOrderedIndices),
}
let annihilators: TinyVec<[usize; 2]> = annihilators.into_iter().collect();
match annihilators.windows(2).all(|w| w[0] < w[1]) {
true => {}
false => return Err(StruqtureError::IncorrectlyOrderedIndices),
}
let mut number_equal_indices = 0;
for (creator, annihilator) in creators.iter().zip(annihilators.iter()) {
match annihilator.cmp(creator) {
std::cmp::Ordering::Less => {
return Err(StruqtureError::CreatorsAnnihilatorsMinimumIndex {
creators_min: Some(*creator),
annihilators_min: Some(*annihilator),
});
}
std::cmp::Ordering::Greater => break,
_ => {
number_equal_indices += 1;
}
}
}
if creators.len() > number_equal_indices && annihilators.len() == number_equal_indices {
return Err(StruqtureError::CreatorsAnnihilatorsMinimumIndex {
creators_min: creators.get(number_equal_indices).copied(),
annihilators_min: None,
});
}
Ok(Self {
creators,
annihilators,
})
}
fn creators(&self) -> std::slice::Iter<'_, usize> {
self.creators.iter()
}
fn annihilators(&self) -> std::slice::Iter<'_, usize> {
self.annihilators.iter()
}
fn create_valid_pair(
creators: impl IntoIterator<Item = usize>,
annihilators: impl IntoIterator<Item = usize>,
value: qoqo_calculator::CalculatorComplex,
) -> Result<(Self, qoqo_calculator::CalculatorComplex), StruqtureError> {
let creators: TinyVec<[usize; 2]> = creators.into_iter().collect();
let (new_creators, contains_double, parity_c) = sort_and_signal(creators);
if contains_double {
return Err(StruqtureError::IndicesContainDoubles {});
}
let annihilators: TinyVec<[usize; 2]> = annihilators.into_iter().collect();
let (new_annihilators, contains_double, parity_a) = sort_and_signal(annihilators);
if contains_double {
return Err(StruqtureError::IndicesContainDoubles {});
}
let value = if (parity_c + parity_a) % 2 != 0 {
value * -1.0
} else {
value
};
let mut hermitian_conjugate = false;
let mut number_equal_indices = 0;
for (creator, annihilator) in new_creators.iter().zip(new_annihilators.iter()) {
match annihilator.cmp(creator) {
std::cmp::Ordering::Less => {
hermitian_conjugate = true;
break;
}
std::cmp::Ordering::Greater => break,
_ => {
number_equal_indices += 1;
}
}
}
if new_creators.len() > number_equal_indices
&& new_annihilators.len() == number_equal_indices
{
hermitian_conjugate = true;
}
if hermitian_conjugate {
Ok((
Self {
creators: new_annihilators,
annihilators: new_creators,
},
value.conj(),
))
} else {
Ok((
Self {
creators: new_creators,
annihilators: new_annihilators,
},
value,
))
}
}
}
impl FermionIndex for HermitianFermionProduct {}
impl HermitianFermionProduct {
#[cfg(feature = "struqture_1_export")]
pub fn to_struqture_1(
&self,
) -> Result<struqture_1::fermions::HermitianFermionProduct, StruqtureError> {
let self_string = self.to_string();
let struqture_1_product = struqture_1::fermions::HermitianFermionProduct::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::fermions::HermitianFermionProduct,
) -> Result<Self, StruqtureError> {
let value_string = value.to_string();
let pauli_product = Self::from_str(&value_string)?;
Ok(pauli_product)
}
}
impl CorrespondsTo<HermitianFermionProduct> for HermitianFermionProduct {
fn corresponds_to(&self) -> HermitianFermionProduct {
self.clone()
}
}
impl CorrespondsTo<FermionProduct> for HermitianFermionProduct {
fn corresponds_to(&self) -> FermionProduct {
FermionProduct {
creators: self.creators.clone(),
annihilators: self.annihilators.clone(),
}
}
}
impl SymmetricIndex for HermitianFermionProduct {
fn hermitian_conjugate(&self) -> (Self, f64) {
(self.clone(), 1.0)
}
fn is_natural_hermitian(&self) -> bool {
self.creators == self.annihilators
}
}
impl Mul<HermitianFermionProduct> for HermitianFermionProduct {
type Output = Vec<(FermionProduct, f64)>;
fn mul(self, rhs: HermitianFermionProduct) -> Self::Output {
let mut output_vec: Vec<(FermionProduct, f64)> = Vec::new();
let mut left_to_mul: Vec<(FermionProduct, f64)> = Vec::new();
let fp_left = FermionProduct::new(self.creators, self.annihilators)
.expect("Could not convert self into a FermionProduct");
left_to_mul.push((fp_left.clone(), 1.0));
if !fp_left.is_natural_hermitian() {
left_to_mul.push(fp_left.hermitian_conjugate());
}
let mut right_to_mul: Vec<(FermionProduct, f64)> = Vec::new();
let fp_right = FermionProduct::new(rhs.creators, rhs.annihilators)
.expect("Could not convert rhs into a FermionProduct");
right_to_mul.push((fp_right.clone(), 1.0));
if !fp_right.is_natural_hermitian() {
right_to_mul.push(fp_right.hermitian_conjugate());
}
for (left, lsign) in left_to_mul {
for (right, rsign) in right_to_mul.clone() {
let res_vec: Vec<(FermionProduct, f64)> = left.clone() * right;
for (fp, val) in res_vec.iter() {
output_vec.push((fp.clone(), val * rsign * lsign));
}
}
}
output_vec
}
}
impl Mul<HermitianFermionProduct> for Vec<FermionProduct> {
type Output = Vec<(FermionProduct, f64)>;
fn mul(self, rhs: HermitianFermionProduct) -> Self::Output {
let mut output_vec: Vec<(FermionProduct, f64)> = Vec::new();
for lh_bp in self {
output_vec.append(&mut (lh_bp * rhs.clone()))
}
output_vec
}
}
impl Mul<&FermionProduct> for HermitianFermionProduct {
type Output = Vec<(FermionProduct, f64)>;
fn mul(self, rhs: &FermionProduct) -> Self::Output {
let mut output_vec: Vec<(FermionProduct, f64)> = Vec::new();
let mut left_to_mul: Vec<(FermionProduct, f64)> = Vec::new();
let hfp_to_fp = FermionProduct::new(self.creators, self.annihilators)
.expect("Could not convert self into a FermionProduct");
left_to_mul.push((hfp_to_fp.clone(), 1.0));
if !hfp_to_fp.is_natural_hermitian() {
left_to_mul.push(hfp_to_fp.hermitian_conjugate());
}
for (left, lsign) in left_to_mul {
let res_vec: Vec<(FermionProduct, f64)> = left.clone() * rhs.clone();
for (fp, val) in res_vec.iter() {
output_vec.push((fp.clone(), val * 1.0 * lsign));
}
}
output_vec
}
}
impl Mul<Vec<FermionProduct>> for HermitianFermionProduct {
type Output = Vec<(FermionProduct, f64)>;
fn mul(self, rhs: Vec<FermionProduct>) -> Self::Output {
let mut output_vec: Vec<(FermionProduct, f64)> = Vec::new();
for rh_bp in rhs.iter() {
output_vec.append(&mut (self.clone() * rh_bp))
}
output_vec
}
}
impl GetValue<HermitianFermionProduct> for HermitianFermionProduct {
type ValueIn = CalculatorComplex;
type ValueOut = CalculatorComplex;
fn get_key(index: &HermitianFermionProduct) -> Self {
index.clone()
}
fn get_transform(
_index: &HermitianFermionProduct,
value: qoqo_calculator::CalculatorComplex,
) -> qoqo_calculator::CalculatorComplex {
value
}
}
impl GetValue<FermionProduct> for HermitianFermionProduct {
type ValueIn = CalculatorComplex;
type ValueOut = CalculatorComplex;
fn get_key(index: &FermionProduct) -> Self {
if index.creators().min() > index.annihilators().min() {
Self {
creators: index.annihilators.clone(),
annihilators: index.creators.clone(),
}
} else {
Self {
creators: index.creators.clone(),
annihilators: index.annihilators.clone(),
}
}
}
fn get_transform(
index: &FermionProduct,
value: qoqo_calculator::CalculatorComplex,
) -> qoqo_calculator::CalculatorComplex {
if index.creators().min() > index.annihilators().min() {
value.conj()
} else {
value
}
}
}
impl GetValue<HermitianFermionProduct> for FermionProduct {
type ValueIn = CalculatorComplex;
type ValueOut = CalculatorComplex;
fn get_key(index: &HermitianFermionProduct) -> Self {
Self {
creators: index.creators.clone(),
annihilators: index.annihilators.clone(),
}
}
fn get_transform(
_index: &HermitianFermionProduct,
value: qoqo_calculator::CalculatorComplex,
) -> qoqo_calculator::CalculatorComplex {
value
}
}
impl std::fmt::Display for HermitianFermionProduct {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut string: String = String::new();
if self.creators.is_empty() & self.annihilators.is_empty() {
string.push('I'); } else {
for index in self.creators() {
string.push_str(format!("c{index}").as_str());
}
for index in self.annihilators() {
string.push_str(format!("a{index}").as_str());
}
}
write!(f, "{string}")
}
}
impl FromStr for HermitianFermionProduct {
type Err = StruqtureError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "I" {
Self::new([], [])
} else {
let mut creators: TinyVec<[usize; 2]> = TinyVec::<[usize; 2]>::with_capacity(2);
let mut annihilators: TinyVec<[usize; 2]> = TinyVec::<[usize; 2]>::with_capacity(2);
let operators = s.split(char::is_numeric).filter(|s| !s.is_empty());
let indices = s.split(char::is_alphabetic).filter(|s| !s.is_empty());
let mut parsing_creators: bool = true;
for (index, op) in indices.zip(operators) {
match index.parse() {
Ok(num) => {
match op{
"c" => {if parsing_creators{ creators.push(num);} else{return Err(StruqtureError::IndicesNotNormalOrdered{index_i: num, index_j: num+1})}}
"a" => {annihilators.push(num); parsing_creators = false;}
_ => return Err(StruqtureError::FromStringFailed{msg: format!("Used operator {op} that is neither 'c' nor 'a' in HermitianFermionProduct::from_str")})
}
}
Err(_) => return Err(StruqtureError::FromStringFailed{msg: format!("Index of Fermion operator {index} is not a HermitianFermionProduct::from_str")}),
}
}
Self::new(creators, annihilators)
}
}
}
fn sort_and_signal(indices: TinyVec<[usize; 2]>) -> (TinyVec<[usize; 2]>, bool, usize) {
let mut parity: usize = 0;
let mut contain_double = false;
let mut local_indices = indices;
for outer_counter in 0..local_indices.len() {
for inner_counter in (0..outer_counter).rev() {
match local_indices[inner_counter].cmp(&local_indices[inner_counter + 1]) {
Ordering::Greater => {
local_indices.swap(inner_counter, inner_counter + 1);
parity += 1;
}
Ordering::Equal => {
contain_double = true;
break;
}
Ordering::Less => {
break;
}
}
}
}
(local_indices, contain_double, parity)
}
impl JordanWignerFermionToSpin for FermionProduct {
type Output = PauliOperator;
fn jordan_wigner(&self) -> Self::Output {
let number_creators = self.number_creators();
let number_annihilators = self.number_annihilators();
let mut qubit_operator = PauliOperator::new();
let mut id = PauliProduct::new();
id = id.set_pauli(0, SinglePauliOperator::Identity);
qubit_operator
.add_operator_product(id, CalculatorComplex::new(1.0, 0.0))
.expect("Internal bug in add_operator_product.");
let mut previous = 0;
for (index, site) in self.creators().enumerate() {
if index % 2 != number_creators % 2 {
for i in previous..*site {
qubit_operator = qubit_operator * PauliProduct::new().z(i)
}
}
qubit_operator = qubit_operator * _lowering_operator(site);
previous = *site;
}
previous = 0;
for (index, site) in self.annihilators().enumerate() {
if index % 2 != number_annihilators % 2 {
for i in previous..*site {
qubit_operator = qubit_operator * PauliProduct::new().z(i)
}
}
qubit_operator = qubit_operator * _raising_operator(site);
previous = *site;
}
qubit_operator
}
}
impl JordanWignerFermionToSpin for HermitianFermionProduct {
type Output = PauliHamiltonian;
fn jordan_wigner(&self) -> Self::Output {
let number_creators = self.number_creators();
let number_annihilators = self.number_annihilators();
let mut qubit_operator = PauliOperator::new();
let mut id = PauliProduct::new();
id = id.set_pauli(0, SinglePauliOperator::Identity);
qubit_operator
.add_operator_product(id, CalculatorComplex::new(1.0, 0.0))
.expect("Internal bug in add_operator_product.");
let mut previous = 0;
for (index, site) in self.creators().enumerate() {
if index % 2 != number_creators % 2 {
for i in previous..*site {
qubit_operator = qubit_operator * PauliProduct::new().z(i)
}
}
qubit_operator = qubit_operator * _lowering_operator(site);
previous = *site;
}
previous = 0;
for (index, site) in self.annihilators().enumerate() {
if index % 2 != number_annihilators % 2 {
for i in previous..*site {
qubit_operator = qubit_operator * PauliProduct::new().z(i)
}
}
qubit_operator = qubit_operator * _raising_operator(site);
previous = *site;
}
if !self.is_natural_hermitian() {
let mut out = PauliHamiltonian::new();
for (product, coeff) in qubit_operator.iter() {
if coeff.im == 0.0.into() {
out.add_operator_product(product.clone(), coeff.re.clone() * 2)
.expect("Internal bug in add_operator_product.");
}
}
return out;
}
PauliHamiltonian::try_from(qubit_operator).expect(
"Error in conversion from PauliOperator to
PauliHamiltonian, despite the internal check that the HermitianFermionProduct in the jordan-wigner
transform is hermitian.",
)
}
}
fn _lowering_operator(i: &usize) -> PauliOperator {
let mut out = PauliOperator::new();
out.add_operator_product(PauliProduct::new().x(*i), CalculatorComplex::new(0.5, 0.0))
.expect("Internal bug in add_operator_product.");
out.add_operator_product(PauliProduct::new().y(*i), CalculatorComplex::new(0.0, -0.5))
.expect("Internal bug in add_operator_product.");
out
}
fn _raising_operator(i: &usize) -> PauliOperator {
let mut out = PauliOperator::new();
out.add_operator_product(PauliProduct::new().x(*i), CalculatorComplex::new(0.5, 0.0))
.expect("Internal bug in add_operator_product.");
out.add_operator_product(PauliProduct::new().y(*i), CalculatorComplex::new(0.0, 0.5))
.expect("Internal bug in add_operator_product.");
out
}
type MulVec = Vec<((TinyVec<[usize; 2]>, TinyVec<[usize; 2]>), f64)>;
#[allow(unused)]
fn commute_creator_annihilator_fermionic(
annihilators_left: &[usize],
creators_right: &[usize],
) -> Vec<(CreatorsAnnihilators, f64)> {
let mut result: Vec<(CreatorsAnnihilators, f64)> = Vec::new();
let orig_parity = if (creators_right.len() * annihilators_left.len()) % 2 == 0 {
1.0
} else {
-1.0
};
let mut found = false;
for (cindex, creator) in creators_right.iter().enumerate() {
if let Some((aindex, _)) = annihilators_left
.iter()
.enumerate()
.find(|(_, an)| *an == creator)
{
let offset_parity = if (annihilators_left.len() - aindex + cindex - 1) % 2 == 0 {
1.0
} else {
-1.0
};
let recurse_creators: TinyVec<[usize; 2]> = creators_right
.iter()
.enumerate()
.filter(|(index, _)| *index != cindex)
.map(|(_, rc)| rc)
.copied()
.collect();
let recurse_annihilators: TinyVec<[usize; 2]> = annihilators_left
.iter()
.enumerate()
.filter(|(index, _)| *index != aindex)
.map(|(_, rc)| rc)
.copied()
.collect();
let recursed_result: MulVec =
commute_creator_annihilator_fermionic(&recurse_annihilators, &recurse_creators)
.into_iter()
.map(|((c, a), parity)| ((c, a), parity * offset_parity))
.collect();
result.extend(recursed_result.iter().cloned());
let commuted_parity =
if (2 * annihilators_left.len() + creators_right.len() - aindex + cindex - 2) % 2
== 0
{
1.0
} else {
-1.0
};
for ((mut c, mut a), p) in recursed_result.clone() {
c.insert(0, *creator);
a.push(*creator);
result.push(((c, a), p * offset_parity * commuted_parity))
}
found = true;
break;
}
}
if !found {
result.push((
(
creators_right.iter().copied().collect(),
annihilators_left.iter().copied().collect(),
),
orig_parity,
));
}
result
}
#[cfg(test)]
mod test {
use crate::ModeTinyVec;
use super::*;
use test_case::test_case;
use tinyvec::tiny_vec;
#[test_case(tiny_vec!([usize; 2] => 0, 2, 4), tiny_vec!([usize; 2] => 1, 3, 5),
vec![((tiny_vec!([usize; 2] => 1, 3, 5), tiny_vec!([usize; 2] => 0, 2, 4)), -1.0)]; "0,2,4 - 1,3,5")]
#[test_case(tiny_vec!([usize; 2] => 0), tiny_vec!([usize; 2] => 0),
vec![((tiny_vec!([usize; 2] => 0), tiny_vec!([usize; 2] => 0)), -1.0), ((tiny_vec!([usize; 2]), tiny_vec!([usize; 2])), 1.0)]; "0, - 0")]
#[test_case(tiny_vec!([usize; 2] => 20), tiny_vec!([usize; 2]),
vec![((tiny_vec!([usize; 2]), tiny_vec!([usize; 2] => 20)), 1.0)]; "20 - empty")]
#[test_case(tiny_vec!([usize; 2] => 1,20), tiny_vec!([usize; 2] => 1,30),
vec![((tiny_vec!([usize; 2] => 1,30), tiny_vec!([usize; 2] => 20,1)), -1.0), ((tiny_vec!([usize; 2] => 30), tiny_vec!([usize; 2] => 20)), 1.0)]; "1,20 - 1,30")]
#[test_case(tiny_vec!([usize; 2] => 1,2,20), tiny_vec!([usize; 2] => 1,2,30),
vec![((tiny_vec!([usize; 2] => 30), tiny_vec!([usize; 2] => 20)), 1.0), ((tiny_vec!([usize; 2] => 2,30), tiny_vec!([usize; 2] => 20,2)), -1.0),
((tiny_vec!([usize; 2] => 1,30), tiny_vec!([usize; 2] => 20,1)), -1.0), ((tiny_vec!([usize; 2] => 1,2,30), tiny_vec!([usize; 2] => 20,2,1)), 1.0)]; "1,2,20 - 1,2,30")]
#[test_case(tiny_vec!([usize; 2] => 10,20,30), tiny_vec!([usize; 2] => 10,30),
vec![((tiny_vec!([usize; 2]), tiny_vec!([usize; 2] => 20)), 1.0), ((tiny_vec!([usize; 2] => 30), tiny_vec!([usize; 2] => 20,30)), 1.0),
((tiny_vec!([usize; 2] => 10), tiny_vec!([usize; 2] => 20,10)), 1.0), ((tiny_vec!([usize; 2] => 10,30), tiny_vec!([usize; 2] => 20,30,10)), 1.0)]; "10,20,30 - 10,30")]
#[test_case(tiny_vec!([usize; 2] => 10,20,30), tiny_vec!([usize; 2] => 10,30,40),
vec![((tiny_vec!([usize; 2] => 40), tiny_vec!([usize; 2] => 20)), -1.0), ((tiny_vec!([usize; 2] => 30,40), tiny_vec!([usize; 2] => 20,30)), 1.0),
((tiny_vec!([usize; 2] => 10,40), tiny_vec!([usize; 2] => 20,10)), 1.0), ((tiny_vec!([usize; 2] => 10,30,40), tiny_vec!([usize; 2] => 20,30,10)), -1.0)]; "10,20,30 - 10,30,40")]
fn commute_fermionic(
annihilators_left: TinyVec<[usize; 2]>,
creators_right: TinyVec<[usize; 2]>,
expected: Vec<((ModeTinyVec, ModeTinyVec), f64)>,
) {
let result = commute_creator_annihilator_fermionic(&annihilators_left, &creators_right);
assert_eq!(result.len(), expected.len());
for pair in expected {
assert!(result.contains(&pair));
}
}
}