use num_complex::Complex64;
use qoqo_calculator::CalculatorComplex;
use qoqo_calculator::CalculatorError;
use qoqo_calculator::CalculatorFloat;
use std::collections::HashMap;
use std::fmt::Debug;
use std::ops::Add;
use std::ops::Mul;
use std::ops::Sub;
use std::str::FromStr;
use thiserror::Error;
mod serialisation_meta_information;
pub use serialisation_meta_information::{
check_can_be_deserialised, SerializationSupport, StruqtureSerialisationMeta, StruqtureType,
};
pub const STRUQTURE_VERSION: &str = env!("CARGO_PKG_VERSION");
pub const CURRENT_STRUQTURE_VERSION: (u32, u32, u32) = (2, 0, 0);
pub const MINIMUM_STRUQTURE_VERSION: (u32, u32, u32) = (2, 0, 0);
use tinyvec::TinyVec;
#[derive(Debug, Error, PartialEq)]
pub enum StruqtureError {
#[error("The from_str function failed {msg} string")]
FromStringFailed { msg: String },
#[error("The pauli matrix being set is not in [\"I\", \"X\", \"Y\", \"Z\"] (PauliProduct object), [\"I\", \"X\", \"iY\", \"Z\"] (DecoherenceProduct object) or [\"I\", \"+\", \"-\", \"Z\"] (PlusMinusProduct object): {pauli:?}")]
IncorrectPauliEntry {
pauli: String,
},
#[error("Cannot assign pauli matrix to index {index:?} as it is already occupied")]
ProductIndexAlreadyOccupied {
index: usize,
},
#[error("Number of subsystems does not match. target: {target_number_spin_subsystems} spin {target_number_boson_subsystems} boson {target_number_fermion_subsystems} fermion; actual: {actual_number_spin_subsystems} spin {actual_number_boson_subsystems} boson {actual_number_fermion_subsystems} fermion ")]
MismatchedNumberSubsystems {
target_number_spin_subsystems: usize,
target_number_boson_subsystems: usize,
target_number_fermion_subsystems: usize,
actual_number_spin_subsystems: usize,
actual_number_boson_subsystems: usize,
actual_number_fermion_subsystems: usize,
},
#[error("Indices are not normal ordered: {index_j:?} is larger than index {index_i:?}")]
IndicesNotNormalOrdered {
index_i: usize,
index_j: usize,
},
#[error(
"Indices given in either creators or annihilators contain a double index specification"
)]
IndicesContainDoubles,
#[error(
"Indices given in creators/annihilators are either not normal ordered, or contain a double index specification"
)]
IncorrectlyOrderedIndices,
#[error("The minimum index of the creators {creators_min:?} is larger than the minimum index of the annihilators {annihilators_min:?}")]
CreatorsAnnihilatorsMinimumIndex {
creators_min: Option<usize>,
annihilators_min: Option<usize>,
},
#[error(
"Key is naturally hermitian (on-diagonal term), but its corresponding value is not real."
)]
NonHermitianOperator,
#[error("Error parsing str into {target_type}: {msg}")]
ParsingError { target_type: String, msg: String },
#[error("Version conflict. Data created with struqture version: {data_major_version}.{data_minor_version} could not be deserialized to Library version: {library_major_version}.{library_minor_version}. Please update your libraries to compatible versions or use the data conversion tool.")]
VersionMismatch {
library_major_version: u32,
library_minor_version: u32,
data_major_version: u32,
data_minor_version: u32,
},
#[error("Trying to deserialize a {name_type} with incompatible version of struqture. Library version: {library_major_version}.{library_minor_version} Data version: {data_major_version}.{data_minor_version}.")]
NewVersionMismatch {
library_major_version: u32,
library_minor_version: u32,
data_major_version: u32,
data_minor_version: u32,
name_type: String,
},
#[error("Trying to use a struqture {source_type} object as a struqture {target_type} object. Did you maybe pass the wrong object into a function?")]
TypeMismatch {
source_type: String,
target_type: String,
},
#[error(transparent)]
CalculatorError(#[from] CalculatorError),
#[error("Lindblad operators need to be traceless.")]
InvalidLindbladTerms,
#[error("Error occured: {msg}")]
GenericError {
msg: String,
},
}
pub type CooSparseMatrix = (Vec<Complex64>, (Vec<usize>, Vec<usize>));
pub type CooSparseMatrixReal = (Vec<f64>, (Vec<usize>, Vec<usize>));
pub trait SymmetricIndex:
std::hash::Hash + Eq + Sized + Clone + std::fmt::Debug + std::fmt::Display + FromStr + Default
{
fn hermitian_conjugate(&self) -> (Self, f64);
fn is_natural_hermitian(&self) -> bool;
}
pub trait SpinIndex:
SymmetricIndex
+ std::hash::Hash
+ Eq
+ Sized
+ Clone
+ std::fmt::Debug
+ std::fmt::Display
+ FromStr
+ Default
+ serde::Serialize
where
Self::SingleSpinType: Copy,
{
type SingleSpinType;
fn new() -> Self;
fn set_pauli(self, index: usize, pauli: Self::SingleSpinType) -> Self;
fn get(&self, index: &usize) -> Option<&Self::SingleSpinType>;
fn iter(&self) -> std::slice::Iter<'_, (usize, Self::SingleSpinType)>;
fn current_number_spins(&self) -> usize {
if let Some((max, _)) = self.iter().last() {
*max + 1
} else {
0
}
}
fn len(&self) -> usize {
self.iter().len()
}
fn is_empty(&self) -> bool {
self.iter().len() == 0
}
fn remap_qubits(&self, mapping: &HashMap<usize, usize>) -> Self;
fn multiply(left: Self, right: Self) -> (Self, Complex64);
fn concatenate(&self, other: Self) -> Result<Self, StruqtureError>;
}
pub trait ModeIndex:
SymmetricIndex
+ std::hash::Hash
+ Eq
+ Sized
+ Clone
+ std::fmt::Debug
+ std::fmt::Display
+ FromStr
+ Default
+ serde::Serialize
{
fn new(
creators: impl IntoIterator<Item = usize>,
annihilators: impl IntoIterator<Item = usize>,
) -> Result<Self, StruqtureError>;
fn number_creators(&self) -> usize {
self.creators().len()
}
fn number_annihilators(&self) -> usize {
self.annihilators().len()
}
fn creators(&self) -> std::slice::Iter<'_, usize>;
fn annihilators(&self) -> std::slice::Iter<'_, usize>;
fn create_valid_pair(
creators: impl IntoIterator<Item = usize>,
annihilators: impl IntoIterator<Item = usize>,
value: CalculatorComplex,
) -> Result<(Self, CalculatorComplex), StruqtureError>;
fn current_number_modes(&self) -> usize {
let max_c = match self.creators().max() {
Some(x) => x + 1,
None => 0,
};
let max_a = match self.annihilators().max() {
Some(x) => x + 1,
None => 0,
};
max_c.max(max_a)
}
fn remap_modes(
&self,
reordering_dictionary: &HashMap<usize, usize>,
) -> Result<(Self, CalculatorComplex), StruqtureError> {
let mut keys: Vec<usize> = reordering_dictionary.keys().cloned().collect();
keys.sort();
let mut values: Vec<usize> = reordering_dictionary.values().cloned().collect();
values.sort();
if keys != values {
return Err(StruqtureError::GenericError {
msg: "Input dictionary must be a permutation.".to_string(),
});
}
let mut remapped_creators: Vec<usize> = vec![];
let mut remapped_annihilators: Vec<usize> = vec![];
for creator_index in self.creators() {
remapped_creators.push(match reordering_dictionary.get(creator_index) {
Some(x) => *x,
None => *creator_index,
})
}
for annihilator_index in self.annihilators() {
remapped_annihilators.push(match reordering_dictionary.get(annihilator_index) {
Some(x) => *x,
None => *annihilator_index,
})
}
let (remapped_index, new_coeff) =
Self::create_valid_pair(remapped_creators, remapped_annihilators, 1.0.into()).map_err(
|_| StruqtureError::GenericError {
msg: "Remapping dictionary should be a permutation of the indices.".to_string(),
},
)?;
Ok((remapped_index, new_coeff))
}
}
pub trait GetValue<T> {
type ValueIn;
type ValueOut;
fn get_key(index: &T) -> Self;
fn get_transform(index: &T, value: Self::ValueIn) -> Self::ValueOut;
}
pub trait CorrespondsTo<T> {
fn corresponds_to(&self) -> T;
}
pub trait TruncateTrait: Sized {
fn truncate(&self, threshold: f64) -> Option<Self>;
}
impl TruncateTrait for CalculatorComplex {
fn truncate(&self, threshold: f64) -> Option<Self> {
match (&self.re, &self.im) {
(CalculatorFloat::Str(_), _) => Some(self.clone()),
(_, CalculatorFloat::Str(_)) => Some(self.clone()),
(CalculatorFloat::Float(re), CalculatorFloat::Float(im)) => {
let new_re = if re.abs() >= threshold { *re } else { 0.0 };
let new_im = if im.abs() >= threshold { *im } else { 0.0 };
if Complex64::new(new_re, new_im).norm() >= threshold {
Some(CalculatorComplex::new(new_re, new_im))
} else {
None
}
}
}
}
}
impl TruncateTrait for CalculatorFloat {
fn truncate(&self, threshold: f64) -> Option<Self> {
match &self {
CalculatorFloat::Str(_) => Some(self.clone()),
CalculatorFloat::Float(f) => {
if f.abs() >= threshold {
Some(self.clone())
} else {
None
}
}
}
}
}
pub trait ConjugationTrait: Sized {
fn conjugate(&self) -> Self;
}
impl ConjugationTrait for CalculatorComplex {
fn conjugate(&self) -> Self {
self.conj()
}
}
impl ConjugationTrait for CalculatorFloat {
fn conjugate(&self) -> Self {
self.clone()
}
}
pub trait OperateOnDensityMatrix<'a>:
IntoIterator<Item = (Self::Index, Self::Value)>
+ FromIterator<(Self::Index, Self::Value)>
+ Extend<(Self::Index, Self::Value)>
+ PartialEq
+ Clone
+ Mul<CalculatorFloat>
+ Mul<CalculatorComplex>
+ Add
+ Sub
+ std::fmt::Display
+ serde::Serialize
+ serde::Deserialize<'a>
where
Self: 'a,
&'a Self: IntoIterator,
Self::Index: Clone,
Self::Value: Mul<f64, Output = Self::Value>,
Self::Value: Add<Self::Value, Output = Self::Value>,
Self::Value: Clone,
Self::Value: TruncateTrait,
{
type Index;
type Value;
fn get(&self, key: &Self::Index) -> &Self::Value;
fn iter(&'a self) -> impl ExactSizeIterator<Item = (&'a Self::Index, &'a Self::Value)>;
fn keys(&'a self) -> impl ExactSizeIterator<Item = &'a Self::Index>;
fn values(&'a self) -> impl ExactSizeIterator<Item = &'a Self::Value>;
fn len(&'a self) -> usize {
self.iter().len()
}
fn is_empty(&'a self) -> bool {
self.len() == 0
}
fn remove(&mut self, key: &Self::Index) -> Option<Self::Value>;
fn empty_clone(&self, capacity: Option<usize>) -> Self;
fn set(
&mut self,
key: Self::Index,
value: Self::Value,
) -> Result<Option<Self::Value>, StruqtureError>;
fn add_operator_product(
&mut self,
key: Self::Index,
value: Self::Value,
) -> Result<(), StruqtureError> {
let old = self.get(&key).clone();
self.set(key, value + old)?;
Ok(())
}
fn truncate(&'a self, threshold: f64) -> Self {
let mut new_self = self.empty_clone(Some(self.len()));
new_self.extend(self.iter().filter_map(|(k, v)| {
v.truncate(threshold)
.map(|v_truncated| (k.clone(), v_truncated))
}));
new_self
}
}
pub trait OpenSystem<'a>:
Add + Sub + PartialEq + Clone + std::fmt::Display + serde::Serialize + serde::Deserialize<'a>
where
Self::System: OperateOnState<'a>,
Self::System: 'a,
&'a Self::System: IntoIterator,
<<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Index: Clone,
<<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Index: SymmetricIndex,
<<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Value:
Mul<f64, Output = <<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Value>,
<<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Value: Add<
<<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Value,
Output = <<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Value,
>,
<<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Value: Clone,
<<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Value: TruncateTrait,
<<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Value: ConjugationTrait,
Self::Noise: OperateOnDensityMatrix<'a>,
Self::Noise: 'a,
&'a Self::Noise: IntoIterator,
<<Self as OpenSystem<'a>>::Noise as OperateOnDensityMatrix<'a>>::Index: Clone,
<<Self as OpenSystem<'a>>::Noise as OperateOnDensityMatrix<'a>>::Value:
Mul<f64, Output = <<Self as OpenSystem<'a>>::Noise as OperateOnDensityMatrix<'a>>::Value>,
<<Self as OpenSystem<'a>>::Noise as OperateOnDensityMatrix<'a>>::Value: Add<
<<Self as OpenSystem<'a>>::Noise as OperateOnDensityMatrix<'a>>::Value,
Output = <<Self as OpenSystem<'a>>::Noise as OperateOnDensityMatrix<'a>>::Value,
>,
<<Self as OpenSystem<'a>>::Noise as OperateOnDensityMatrix<'a>>::Value: Clone,
<<Self as OpenSystem<'a>>::Noise as OperateOnDensityMatrix<'a>>::Value: TruncateTrait,
<<Self as OpenSystem<'a>>::Noise as OperateOnDensityMatrix<'a>>::Value: ConjugationTrait,
{
type System;
type Noise;
fn noise(&self) -> &Self::Noise;
fn system(&self) -> &Self::System;
fn noise_mut(&mut self) -> &mut Self::Noise;
fn system_mut(&mut self) -> &mut Self::System;
fn ungroup(self) -> (Self::System, Self::Noise);
fn group(system: Self::System, noise: Self::Noise) -> Result<Self, StruqtureError>;
fn empty_clone(&self) -> Self;
fn truncate(&'a self, threshold: f64) -> Self {
let new_system = self.system().truncate(threshold);
let new_noise = self.noise().truncate(threshold);
Self::group(new_system, new_noise)
.expect("Internal error: System and Noise size unexpectedly do not match")
}
}
pub trait OperateOnState<'a>:
OperateOnDensityMatrix<'a>
+ IntoIterator<Item = (Self::Index, Self::Value)>
+ FromIterator<(Self::Index, Self::Value)>
+ Extend<(Self::Index, Self::Value)>
+ PartialEq
+ Clone
+ Mul<CalculatorFloat>
+ Mul<CalculatorComplex>
+ Add
+ Sub
where
Self: 'a,
&'a Self: IntoIterator,
Self::Index: Clone,
Self::Index: SymmetricIndex,
Self::Value: Mul<f64, Output = Self::Value>,
Self::Value: Add<Self::Value, Output = Self::Value>,
Self::Value: Clone,
Self::Value: TruncateTrait,
Self::Value: ConjugationTrait,
{
fn hermitian_conjugate(&'a self) -> Self {
let mut new_self = self.empty_clone(Some(self.len()));
new_self.extend(self.iter().map(|(k, v)| {
let (new_key, conjugation_prefactor) = k.hermitian_conjugate();
(new_key, v.conjugate() * conjugation_prefactor)
}));
new_self
}
}
pub trait OperateOnModes<'a>: PartialEq + Clone + Mul<CalculatorFloat> + Add + Sub {
fn current_number_modes(&'a self) -> usize;
}
type CreatorsAnnihilators = (TinyVec<[usize; 2]>, TinyVec<[usize; 2]>);
pub mod bosons;
pub mod fermions;
pub mod mappings;
pub mod mixed_systems;
pub mod prelude;
pub mod spins;
#[cfg(test)]
type ModeTinyVec = TinyVec<[usize; 2]>;