use super::{
HermitianMixedProduct, HermitianOperateOnMixedSystems, MixedHamiltonian, MixedSystem,
OperateOnMixedSystems,
};
#[cfg(feature = "json_schema")]
use crate::mixed_systems::TinyVecDef;
use crate::prelude::*;
use crate::{OperateOnDensityMatrix, OperateOnState, StruqtureError};
#[cfg(feature = "indexed_map_iterators")]
use indexmap::map::{Iter, Keys, Values};
use qoqo_calculator::CalculatorComplex;
use serde::{Deserialize, Serialize};
#[cfg(not(feature = "indexed_map_iterators"))]
use std::collections::hash_map::{Iter, Keys, Values};
use std::fmt::{self, Write};
use std::iter::{FromIterator, IntoIterator};
use std::ops;
use tinyvec::TinyVec;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct MixedHamiltonianSystem {
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) hamiltonian: MixedHamiltonian,
}
#[cfg(feature = "json_schema")]
impl schemars::JsonSchema for MixedHamiltonianSystem {
fn schema_name() -> std::borrow::Cow<'static, str> {
"MixedHamiltonianSystem".into()
}
fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
<SchemaHelperMixedHamiltonianSystem>::json_schema(generator)
}
}
#[cfg(feature = "json_schema")]
#[derive(schemars::JsonSchema)]
#[schemars(deny_unknown_fields)]
#[allow(dead_code)]
struct SchemaHelperMixedHamiltonianSystem {
#[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) hamiltonian: MixedHamiltonian,
}
impl crate::MinSupportedVersion for MixedHamiltonianSystem {}
impl<'a> OperateOnDensityMatrix<'a> for MixedHamiltonianSystem {
type Index = HermitianMixedProduct;
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.hamiltonian.get(key)
}
fn iter(&'a self) -> Self::IteratorType {
self.hamiltonian.iter()
}
fn keys(&'a self) -> Self::KeyIteratorType {
self.hamiltonian.keys()
}
fn values(&'a self) -> Self::ValueIteratorType {
self.hamiltonian.values()
}
fn remove(&mut self, key: &Self::Index) -> Option<Self::Value> {
self.hamiltonian.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(),
hamiltonian: MixedHamiltonian::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(),
hamiltonian: MixedHamiltonian::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.hamiltonian.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.hamiltonian.add_operator_product(key, value)
}
}
impl OperateOnState<'_> for MixedHamiltonianSystem {
fn hermitian_conjugate(&self) -> Self {
self.clone()
}
}
impl OperateOnMixedSystems<'_> for MixedHamiltonianSystem {
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 MixedHamiltonianSystem {}
impl MixedHamiltonianSystem {
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 hamiltonian = MixedHamiltonian::new(
number_spins.len(),
number_bosons.len(),
number_fermions.len(),
);
MixedHamiltonianSystem {
number_spins,
number_bosons,
number_fermions,
hamiltonian,
}
}
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 hamiltonian = MixedHamiltonian::with_capacity(
number_spins.len(),
number_bosons.len(),
number_fermions.len(),
capacity,
);
MixedHamiltonianSystem {
number_spins,
number_bosons,
number_fermions,
hamiltonian,
}
}
pub fn hamiltonian(&self) -> &MixedHamiltonian {
&self.hamiltonian
}
pub fn from_hamiltonian(
hamiltonian: MixedHamiltonian,
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 hamiltonian
.current_number_spins()
.iter()
.zip(number_spins.iter())
.all(|(current, target)| match target {
Some(x) => current <= x,
None => true,
})
&& hamiltonian
.current_number_bosonic_modes()
.iter()
.zip(number_bosons.iter())
.all(|(current, target)| match target {
Some(x) => current <= x,
None => true,
})
&& hamiltonian
.current_number_fermionic_modes()
.iter()
.zip(number_fermions.iter())
.all(|(current, target)| match target {
Some(x) => current <= x,
None => true,
})
{
Ok(MixedHamiltonianSystem {
number_spins,
number_bosons,
number_fermions,
hamiltonian,
})
} else {
Err(StruqtureError::NumberSpinsExceeded)
}
}
}
impl ops::Neg for MixedHamiltonianSystem {
type Output = Self;
fn neg(mut self) -> Self {
self.hamiltonian = self.hamiltonian.neg();
self
}
}
impl<T, V> ops::Add<T> for MixedHamiltonianSystem
where
T: IntoIterator<Item = (HermitianMixedProduct, 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 MixedHamiltonianSystem
where
T: IntoIterator<Item = (HermitianMixedProduct, 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 MixedHamiltonianSystem
where
T: Into<CalculatorComplex>,
{
type Output = Self;
fn mul(mut self, other: T) -> Self {
let other_cc = Into::<CalculatorComplex>::into(other);
self.hamiltonian = self.hamiltonian * other_cc;
self
}
}
impl ops::Mul<MixedHamiltonianSystem> for MixedHamiltonianSystem {
type Output = Result<MixedSystem, StruqtureError>;
fn mul(self, other: MixedHamiltonianSystem) -> 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 MixedHamiltonianSystem {
type Item = (HermitianMixedProduct, CalculatorComplex);
#[cfg(not(feature = "indexed_map_iterators"))]
type IntoIter = std::collections::hash_map::IntoIter<HermitianMixedProduct, CalculatorComplex>;
#[cfg(feature = "indexed_map_iterators")]
type IntoIter = indexmap::map::IntoIter<HermitianMixedProduct, CalculatorComplex>;
fn into_iter(self) -> Self::IntoIter {
self.hamiltonian.into_iter()
}
}
impl<'a> IntoIterator for &'a MixedHamiltonianSystem {
type Item = (&'a HermitianMixedProduct, &'a CalculatorComplex);
type IntoIter = Iter<'a, HermitianMixedProduct, CalculatorComplex>;
fn into_iter(self) -> Self::IntoIter {
self.hamiltonian.iter()
}
}
impl FromIterator<(HermitianMixedProduct, CalculatorComplex)> for MixedHamiltonianSystem {
fn from_iter<I: IntoIterator<Item = (HermitianMixedProduct, 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 =
MixedHamiltonianSystem::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 => MixedHamiltonianSystem::new([], [], []),
}
}
}
impl Extend<(HermitianMixedProduct, CalculatorComplex)> for MixedHamiltonianSystem {
fn extend<I: IntoIterator<Item = (HermitianMixedProduct, 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 MixedHamiltonianSystem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut output = "MixedHamiltonianSystem(\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<(&HermitianMixedProduct, &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}")
}
}