use super::{HermitianOperateOnMixedSystems, MixedOperator, MixedProduct, OperateOnMixedSystems};
#[cfg(feature = "json_schema")]
use crate::mixed_systems::TinyVecDef;
use crate::prelude::*;
use crate::{OperateOnDensityMatrix, OperateOnState, StruqtureError};
use qoqo_calculator::CalculatorComplex;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Write};
use std::iter::{FromIterator, IntoIterator};
use std::ops;
use tinyvec::TinyVec;
#[cfg(feature = "indexed_map_iterators")]
use indexmap::map::{Iter, Keys, Values};
#[cfg(not(feature = "indexed_map_iterators"))]
use std::collections::hash_map::{Iter, Keys, Values};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct MixedSystem {
pub(crate) number_spins: TinyVec<[Option<usize>; 2]>,
pub(crate) number_bosons: TinyVec<[Option<usize>; 2]>,
pub(crate) number_fermions: TinyVec<[Option<usize>; 2]>,
pub(crate) operator: MixedOperator,
}
#[cfg(feature = "json_schema")]
impl schemars::JsonSchema for MixedSystem {
fn schema_name() -> std::borrow::Cow<'static, str> {
"MixedSystem".into()
}
fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
<SchemaHelperMixedSystem>::json_schema(generator)
}
}
#[cfg(feature = "json_schema")]
#[derive(schemars::JsonSchema)]
#[schemars(deny_unknown_fields)]
#[allow(dead_code)]
struct SchemaHelperMixedSystem {
#[serde(with = "TinyVecDef")]
number_spins: TinyVec<[Option<usize>; 2]>,
#[serde(with = "TinyVecDef")]
number_bosons: TinyVec<[Option<usize>; 2]>,
#[serde(with = "TinyVecDef")]
number_fermions: TinyVec<[Option<usize>; 2]>,
pub(crate) operator: MixedOperator,
}
impl crate::MinSupportedVersion for MixedSystem {}
impl<'a> OperateOnDensityMatrix<'a> for MixedSystem {
type Index = MixedProduct;
type Value = CalculatorComplex;
type IteratorType = Iter<'a, Self::Index, Self::Value>;
type KeyIteratorType = Keys<'a, Self::Index, Self::Value>;
type ValueIteratorType = Values<'a, Self::Index, Self::Value>;
fn get(&self, key: &Self::Index) -> &Self::Value {
self.operator.get(key)
}
fn iter(&'a self) -> Self::IteratorType {
self.operator.iter()
}
fn keys(&'a self) -> Self::KeyIteratorType {
self.operator.keys()
}
fn values(&'a self) -> Self::ValueIteratorType {
self.operator.values()
}
fn remove(&mut self, key: &Self::Index) -> Option<Self::Value> {
self.operator.remove(key)
}
fn empty_clone(&self, capacity: Option<usize>) -> Self {
match capacity {
Some(cap) => Self {
number_spins: self.number_spins.clone(),
number_bosons: self.number_bosons.clone(),
number_fermions: self.number_fermions.clone(),
operator: MixedOperator::with_capacity(
self.number_spins.len(),
self.number_bosons.len(),
self.number_fermions.len(),
cap,
),
},
None => Self {
number_spins: self.number_spins.clone(),
number_bosons: self.number_bosons.clone(),
number_fermions: self.number_fermions.clone(),
operator: MixedOperator::new(
self.number_spins.len(),
self.number_bosons.len(),
self.number_fermions.len(),
),
},
}
}
fn set(
&mut self,
key: Self::Index,
value: Self::Value,
) -> Result<Option<Self::Value>, StruqtureError> {
if key.spins().len() != self.number_spins.len()
|| key.bosons().len() != self.number_bosons.len()
|| key.fermions().len() != self.number_fermions.len()
{
return Err(StruqtureError::MissmatchedNumberSubsystems {
target_number_spin_subsystems: self.number_spins.len(),
target_number_boson_subsystems: self.number_bosons.len(),
target_number_fermion_subsystems: self.number_fermions.len(),
actual_number_spin_subsystems: key.spins().len(),
actual_number_boson_subsystems: key.bosons().len(),
actual_number_fermion_subsystems: key.fermions().len(),
});
}
for (x, y) in key.bosons().zip(self.number_bosons.clone()) {
if let Some(max_number) = y {
if x.current_number_modes() > max_number {
return Err(StruqtureError::MissmatchedNumberModes);
}
}
}
for (x, y) in key.fermions().zip(self.number_fermions.clone()) {
if let Some(max_number) = y {
if x.current_number_modes() > max_number {
return Err(StruqtureError::MissmatchedNumberModes);
}
}
}
for (x, y) in key.spins().zip(self.number_spins.clone()) {
if let Some(max_number) = y {
if x.current_number_spins() > max_number {
return Err(StruqtureError::MissmatchedNumberSpins);
}
}
}
self.operator.set(key, value)
}
fn add_operator_product(
&mut self,
key: Self::Index,
value: Self::Value,
) -> Result<(), StruqtureError> {
if key.spins().len() != self.number_spins.len()
|| key.bosons().len() != self.number_bosons.len()
|| key.fermions().len() != self.number_fermions.len()
{
return Err(StruqtureError::MissmatchedNumberSubsystems {
target_number_spin_subsystems: self.number_spins.len(),
target_number_boson_subsystems: self.number_bosons.len(),
target_number_fermion_subsystems: self.number_fermions.len(),
actual_number_spin_subsystems: key.spins().len(),
actual_number_boson_subsystems: key.bosons().len(),
actual_number_fermion_subsystems: key.fermions().len(),
});
}
for (x, y) in key.bosons().zip(self.number_bosons.clone()) {
if let Some(max_number) = y {
if x.current_number_modes() > max_number {
return Err(StruqtureError::MissmatchedNumberModes);
}
}
}
for (x, y) in key.fermions().zip(self.number_fermions.clone()) {
if let Some(max_number) = y {
if x.current_number_modes() > max_number {
return Err(StruqtureError::MissmatchedNumberModes);
}
}
}
for (x, y) in key.spins().zip(self.number_spins.clone()) {
if let Some(max_number) = y {
if x.current_number_spins() > max_number {
return Err(StruqtureError::MissmatchedNumberSpins);
}
}
}
self.operator.add_operator_product(key, value)
}
}
impl OperateOnState<'_> for MixedSystem {
fn hermitian_conjugate(&self) -> Self {
self.clone()
}
}
impl OperateOnMixedSystems<'_> for MixedSystem {
fn number_spins(&self) -> Vec<usize> {
self.number_spins
.iter()
.zip(self.current_number_spins())
.map(|(target, current)| target.unwrap_or_else(|| current))
.collect()
}
fn current_number_spins(&self) -> Vec<usize> {
let mut number_spins: Vec<usize> = (0..self.number_spins.len()).map(|_| 0).collect();
for key in self.keys() {
for (index, s) in key.spins().enumerate() {
let maxk = s.current_number_spins();
if maxk > number_spins[index] {
number_spins[index] = maxk
}
}
}
number_spins
}
fn number_bosonic_modes(&self) -> Vec<usize> {
self.number_bosons
.iter()
.zip(self.current_number_bosonic_modes())
.map(|(target, current)| target.unwrap_or_else(|| current))
.collect()
}
fn current_number_bosonic_modes(&self) -> Vec<usize> {
let mut number_bosons: Vec<usize> = (0..self.number_bosons.len()).map(|_| 0).collect();
for key in self.keys() {
for (index, s) in key.bosons().enumerate() {
let maxk = s.current_number_modes();
if maxk > number_bosons[index] {
number_bosons[index] = maxk
}
}
}
number_bosons
}
fn number_fermionic_modes(&self) -> Vec<usize> {
self.number_fermions
.iter()
.zip(self.current_number_fermionic_modes())
.map(|(target, current)| target.unwrap_or_else(|| current))
.collect()
}
fn current_number_fermionic_modes(&self) -> Vec<usize> {
let mut number_fermions: Vec<usize> = (0..self.number_fermions.len()).map(|_| 0).collect();
for key in self.keys() {
for (index, s) in key.fermions().enumerate() {
let maxk = s.current_number_modes();
if maxk > number_fermions[index] {
number_fermions[index] = maxk
}
}
}
number_fermions
}
}
impl HermitianOperateOnMixedSystems<'_> for MixedSystem {}
impl MixedSystem {
pub fn new(
number_spins: impl IntoIterator<Item = Option<usize>>,
number_bosons: impl IntoIterator<Item = Option<usize>>,
number_fermions: impl IntoIterator<Item = Option<usize>>,
) -> Self {
let number_spins: TinyVec<[Option<usize>; 2]> = number_spins.into_iter().collect();
let number_bosons: TinyVec<[Option<usize>; 2]> = number_bosons.into_iter().collect();
let number_fermions: TinyVec<[Option<usize>; 2]> = number_fermions.into_iter().collect();
let operator = MixedOperator::new(
number_spins.len(),
number_bosons.len(),
number_fermions.len(),
);
MixedSystem {
number_spins,
number_bosons,
number_fermions,
operator,
}
}
pub fn with_capacity(
number_spins: impl IntoIterator<Item = Option<usize>>,
number_bosons: impl IntoIterator<Item = Option<usize>>,
number_fermions: impl IntoIterator<Item = Option<usize>>,
capacity: usize,
) -> Self {
let number_spins: TinyVec<[Option<usize>; 2]> = number_spins.into_iter().collect();
let number_bosons: TinyVec<[Option<usize>; 2]> = number_bosons.into_iter().collect();
let number_fermions: TinyVec<[Option<usize>; 2]> = number_fermions.into_iter().collect();
let operator = MixedOperator::with_capacity(
number_spins.len(),
number_bosons.len(),
number_fermions.len(),
capacity,
);
MixedSystem {
number_spins,
number_bosons,
number_fermions,
operator,
}
}
pub fn operator(&self) -> &MixedOperator {
&self.operator
}
pub fn from_operator(
operator: MixedOperator,
number_spins: impl IntoIterator<Item = Option<usize>>,
number_bosons: impl IntoIterator<Item = Option<usize>>,
number_fermions: impl IntoIterator<Item = Option<usize>>,
) -> Result<Self, StruqtureError> {
let number_spins: TinyVec<[Option<usize>; 2]> = number_spins.into_iter().collect();
let number_bosons: TinyVec<[Option<usize>; 2]> = number_bosons.into_iter().collect();
let number_fermions: TinyVec<[Option<usize>; 2]> = number_fermions.into_iter().collect();
if operator
.current_number_spins()
.iter()
.zip(number_spins.iter())
.all(|(current, target)| match target {
Some(x) => current <= x,
None => true,
})
&& operator
.current_number_bosonic_modes()
.iter()
.zip(number_bosons.iter())
.all(|(current, target)| match target {
Some(x) => current <= x,
None => true,
})
&& operator
.current_number_fermionic_modes()
.iter()
.zip(number_fermions.iter())
.all(|(current, target)| match target {
Some(x) => current <= x,
None => true,
})
{
Ok(MixedSystem {
number_spins,
number_bosons,
number_fermions,
operator,
})
} else {
Err(StruqtureError::NumberSpinsExceeded)
}
}
}
impl ops::Neg for MixedSystem {
type Output = Self;
fn neg(mut self) -> Self {
self.operator = self.operator.neg();
self
}
}
impl<T, V> ops::Add<T> for MixedSystem
where
T: IntoIterator<Item = (MixedProduct, V)>,
V: Into<CalculatorComplex>,
{
type Output = Result<Self, StruqtureError>;
fn add(mut self, other: T) -> Self::Output {
for (key, value) in other.into_iter() {
self.add_operator_product(key.clone(), Into::<CalculatorComplex>::into(value))?;
}
Ok(self)
}
}
impl<T, V> ops::Sub<T> for MixedSystem
where
T: IntoIterator<Item = (MixedProduct, V)>,
V: Into<CalculatorComplex>,
{
type Output = Result<Self, StruqtureError>;
fn sub(mut self, other: T) -> Self::Output {
for (key, value) in other.into_iter() {
self.add_operator_product(key.clone(), Into::<CalculatorComplex>::into(value) * -1.0)?;
}
Ok(self)
}
}
impl<T> ops::Mul<T> for MixedSystem
where
T: Into<CalculatorComplex>,
{
type Output = Self;
fn mul(mut self, other: T) -> Self {
let other_cc = Into::<CalculatorComplex>::into(other);
self.operator = self.operator * other_cc;
self
}
}
impl ops::Mul<MixedSystem> for MixedSystem {
type Output = Result<MixedSystem, StruqtureError>;
fn mul(self, other: MixedSystem) -> Self::Output {
if self.number_spins.len() != other.number_spins.len()
|| self.number_bosons.len() != other.number_bosons.len()
|| self.number_fermions.len() != other.number_fermions.len()
{
return Err(StruqtureError::MissmatchedNumberSubsystems {
target_number_spin_subsystems: self.number_spins.len(),
target_number_boson_subsystems: self.number_bosons.len(),
target_number_fermion_subsystems: self.number_fermions.len(),
actual_number_spin_subsystems: other.number_spins.len(),
actual_number_boson_subsystems: other.number_bosons.len(),
actual_number_fermion_subsystems: other.number_fermions.len(),
});
}
let capacity = self.len() * other.len();
let mut spin_op = MixedSystem::with_capacity(
self.number_spins.clone(),
self.number_bosons.clone(),
self.number_fermions.clone(),
capacity,
);
for (pps, vals) in self {
for (ppo, valo) in other.iter() {
let products = (pps.clone() * ppo.clone())?;
for (ppp, coefficient) in products {
let coefficient =
Into::<CalculatorComplex>::into(valo) * vals.clone() * coefficient;
spin_op.add_operator_product(ppp, coefficient)?;
}
}
}
Ok(spin_op)
}
}
impl IntoIterator for MixedSystem {
type Item = (MixedProduct, CalculatorComplex);
#[cfg(not(feature = "indexed_map_iterators"))]
type IntoIter = std::collections::hash_map::IntoIter<MixedProduct, CalculatorComplex>;
#[cfg(feature = "indexed_map_iterators")]
type IntoIter = indexmap::map::IntoIter<MixedProduct, CalculatorComplex>;
fn into_iter(self) -> Self::IntoIter {
self.operator.into_iter()
}
}
impl<'a> IntoIterator for &'a MixedSystem {
type Item = (&'a MixedProduct, &'a CalculatorComplex);
type IntoIter = Iter<'a, MixedProduct, CalculatorComplex>;
fn into_iter(self) -> Self::IntoIter {
self.operator.iter()
}
}
impl FromIterator<(MixedProduct, CalculatorComplex)> for MixedSystem {
fn from_iter<I: IntoIterator<Item = (MixedProduct, CalculatorComplex)>>(iter: I) -> Self {
let mut iterator = iter.into_iter();
match iterator.next() {
Some(first_element) => {
let number_spins: Vec<Option<usize>> =
(0..first_element.0.spins().len()).map(|_| None).collect();
let number_bosons: Vec<Option<usize>> =
(0..first_element.0.bosons().len()).map(|_| None).collect();
let number_fermions: Vec<Option<usize>> = (0..first_element.0.fermions().len())
.map(|_| None)
.collect();
let mut slno = MixedSystem::new(number_spins, number_bosons, number_fermions);
slno.set(first_element.0, first_element.1)
.expect("Internal error in set");
for (pair, cc) in iterator {
slno.add_operator_product(pair, cc)
.expect("Internal error in add_operator_product");
}
slno
}
None => MixedSystem::new([], [], []),
}
}
}
impl Extend<(MixedProduct, CalculatorComplex)> for MixedSystem {
fn extend<I: IntoIterator<Item = (MixedProduct, CalculatorComplex)>>(&mut self, iter: I) {
for (pp, cc) in iter {
self.add_operator_product(pp, cc)
.expect("Internal error in add_operator_product");
}
}
}
impl fmt::Display for MixedSystem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut output = "MixedSystem(\n".to_string();
output.push_str("number_spins: ");
for n in self.number_spins() {
write!(output, "{n}, ")?;
}
output.push('\n');
output.push_str("number_bosons: ");
for n in self.number_bosonic_modes() {
write!(output, "{n}, ")?;
}
output.push('\n');
output.push_str("number_fermions: ");
for n in self.number_fermionic_modes() {
write!(output, "{n}, ")?;
}
output.push_str(")\n");
output.push('{');
let mut vec: Vec<(&MixedProduct, &CalculatorComplex)> = self.iter().collect();
vec.sort_unstable_by(|(left_index, _), (right_index, _)| {
left_index
.partial_cmp(right_index)
.expect("Cannot compare two unsigned integers internal error in struqture.spins")
});
for (key, val) in vec {
writeln!(output, "{key}: {val},")?;
}
output.push('}');
write!(f, "{output}")
}
}