1use super::{BosonOperator, BosonProduct, HermitianBosonProduct, ModeIndex, OperateOnBosons};
14use crate::{
15 mappings::BosonToSpin, spins::PauliOperator, GetValue, OperateOnDensityMatrix, OperateOnModes,
16 OperateOnState, StruqtureError, SymmetricIndex,
17};
18use qoqo_calculator::{CalculatorComplex, CalculatorFloat};
19use serde::{Deserialize, Serialize};
20use std::fmt::{self, Write};
21use std::iter::{FromIterator, IntoIterator};
22use std::ops;
23
24use indexmap::map::{Entry, Iter};
25use indexmap::IndexMap;
26
27#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
53#[serde(try_from = "BosonHamiltonianSerialize")]
54#[serde(into = "BosonHamiltonianSerialize")]
55pub struct BosonHamiltonian {
56 internal_map: IndexMap<HermitianBosonProduct, CalculatorComplex>,
58}
59
60impl crate::SerializationSupport for BosonHamiltonian {
61 fn struqture_type() -> crate::StruqtureType {
62 crate::StruqtureType::BosonHamiltonian
63 }
64}
65
66#[cfg(feature = "json_schema")]
67impl schemars::JsonSchema for BosonHamiltonian {
68 fn schema_name() -> std::borrow::Cow<'static, str> {
69 "BosonHamiltonian".into()
70 }
71
72 fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
73 <BosonHamiltonianSerialize>::json_schema(generator)
74 }
75}
76
77#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
78#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
79#[cfg_attr(feature = "json_schema", schemars(deny_unknown_fields))]
80struct BosonHamiltonianSerialize {
81 items: Vec<(HermitianBosonProduct, CalculatorFloat, CalculatorFloat)>,
82 serialisation_meta: crate::StruqtureSerialisationMeta,
83}
84
85impl TryFrom<BosonHamiltonianSerialize> for BosonHamiltonian {
86 type Error = StruqtureError;
87 fn try_from(value: BosonHamiltonianSerialize) -> Result<Self, Self::Error> {
88 let target_serialisation_meta =
89 <Self as crate::SerializationSupport>::target_serialisation_meta();
90 crate::check_can_be_deserialised(&target_serialisation_meta, &value.serialisation_meta)?;
91 let new_noise_op: BosonHamiltonian = value
92 .items
93 .into_iter()
94 .map(|(key, real, imag)| (key, CalculatorComplex { re: real, im: imag }))
95 .collect();
96 Ok(new_noise_op)
97 }
98}
99
100impl From<BosonHamiltonian> for BosonHamiltonianSerialize {
101 fn from(value: BosonHamiltonian) -> Self {
102 let serialisation_meta = crate::SerializationSupport::struqture_serialisation_meta(&value);
103 let new_noise_op: Vec<(HermitianBosonProduct, CalculatorFloat, CalculatorFloat)> = value
104 .into_iter()
105 .map(|(key, val)| (key, val.re, val.im))
106 .collect();
107 Self {
108 items: new_noise_op,
109 serialisation_meta,
110 }
111 }
112}
113
114impl<'a> OperateOnDensityMatrix<'a> for BosonHamiltonian {
115 type Index = HermitianBosonProduct;
116 type Value = CalculatorComplex;
117
118 fn get(&self, key: &Self::Index) -> &Self::Value {
120 match self.internal_map.get(key) {
121 Some(value) => value,
122 None => &CalculatorComplex::ZERO,
123 }
124 }
125
126 fn iter(&'a self) -> impl ExactSizeIterator<Item = (&'a Self::Index, &'a Self::Value)> {
128 self.internal_map.iter()
129 }
130
131 fn keys(&'a self) -> impl ExactSizeIterator<Item = &'a Self::Index> {
133 self.internal_map.keys()
134 }
135
136 fn values(&'a self) -> impl ExactSizeIterator<Item = &'a Self::Value> {
138 self.internal_map.values()
139 }
140
141 fn remove(&mut self, key: &Self::Index) -> Option<Self::Value> {
143 self.internal_map.shift_remove(key)
144 }
145
146 fn empty_clone(&self, capacity: Option<usize>) -> Self {
148 match capacity {
149 Some(cap) => Self::with_capacity(cap),
150 None => Self::new(),
151 }
152 }
153
154 fn set(
167 &mut self,
168 key: Self::Index,
169 value: Self::Value,
170 ) -> Result<Option<Self::Value>, StruqtureError> {
171 if value.re != CalculatorFloat::ZERO || value.im != CalculatorFloat::ZERO {
172 if key.is_natural_hermitian() && value.im != CalculatorFloat::ZERO {
174 Err(StruqtureError::NonHermitianOperator)
175 } else {
176 Ok(self.internal_map.insert(key, value))
177 }
178 } else {
179 match self.internal_map.entry(key) {
180 Entry::Occupied(val) => Ok(Some(val.shift_remove())),
181 Entry::Vacant(_) => Ok(None),
182 }
183 }
184 }
185
186 fn add_operator_product(
198 &mut self,
199 key: Self::Index,
200 value: Self::Value,
201 ) -> Result<(), StruqtureError> {
202 let old = self.get(&key).clone();
203 let new_val = value + old;
204 if key.is_natural_hermitian() && new_val.im != CalculatorFloat::ZERO {
205 Err(StruqtureError::NonHermitianOperator)
206 } else {
207 self.set(key, new_val)?;
208 Ok(())
209 }
210 }
211}
212
213impl OperateOnState<'_> for BosonHamiltonian {
214 fn hermitian_conjugate(&self) -> Self {
216 self.clone()
217 }
218}
219
220impl OperateOnModes<'_> for BosonHamiltonian {
221 fn current_number_modes(&self) -> usize {
227 let mut max_mode: usize = 0;
228 if !self.internal_map.is_empty() {
229 for key in self.internal_map.keys() {
230 if key.current_number_modes() > max_mode {
231 max_mode = key.current_number_modes()
232 }
233 }
234 }
235 max_mode
236 }
237}
238
239impl OperateOnBosons<'_> for BosonHamiltonian {}
240
241impl Default for BosonHamiltonian {
244 fn default() -> Self {
245 Self::new()
246 }
247}
248
249impl BosonHamiltonian {
252 pub fn new() -> Self {
258 BosonHamiltonian {
259 internal_map: IndexMap::new(),
260 }
261 }
262
263 pub fn with_capacity(capacity: usize) -> Self {
273 Self {
274 internal_map: IndexMap::with_capacity(capacity),
275 }
276 }
277
278 #[cfg(feature = "struqture_1_export")]
280 pub fn to_struqture_1(
281 &self,
282 ) -> Result<struqture_1::bosons::BosonHamiltonianSystem, StruqtureError> {
283 let mut new_boson_system = struqture_1::bosons::BosonHamiltonianSystem::new(None);
284 for (key, val) in self.iter() {
285 let one_key = key.to_struqture_1()?;
286 let _ = struqture_1::OperateOnDensityMatrix::set(
287 &mut new_boson_system,
288 one_key,
289 val.clone(),
290 );
291 }
292 Ok(new_boson_system)
293 }
294
295 #[cfg(feature = "struqture_1_import")]
297 pub fn from_struqture_1(
298 value: &struqture_1::bosons::BosonHamiltonianSystem,
299 ) -> Result<Self, StruqtureError> {
300 let mut new_operator = Self::new();
301 for (key, val) in struqture_1::OperateOnDensityMatrix::iter(value) {
302 let self_key = HermitianBosonProduct::from_struqture_1(key)?;
303 let _ = new_operator.set(self_key, val.clone());
304 }
305 Ok(new_operator)
306 }
307}
308
309impl BosonToSpin for BosonHamiltonian {
310 type Output = PauliOperator;
311
312 fn dicke_boson_spin_mapping(
314 &self,
315 number_spins_per_bosonic_mode: usize,
316 ) -> Result<Self::Output, StruqtureError> {
317 let mut pauli_operator = PauliOperator::new();
318 for (key, value) in self.iter() {
319 pauli_operator = pauli_operator
320 + key.dicke_boson_spin_mapping(number_spins_per_bosonic_mode)? * value;
321 }
322
323 Ok(pauli_operator)
324 }
325}
326
327impl TryFrom<BosonOperator> for BosonHamiltonian {
328 type Error = StruqtureError;
329 fn try_from(hamiltonian: BosonOperator) -> Result<Self, StruqtureError> {
341 let mut internal = BosonHamiltonian::new();
342 for (key, value) in hamiltonian.into_iter() {
343 if key.creators().min() > key.annihilators().min() {
344 return Err(StruqtureError::CreatorsAnnihilatorsMinimumIndex {
345 creators_min: key.creators().min().cloned(),
346 annihilators_min: key.annihilators().min().cloned(),
347 });
348 } else {
349 let bp = HermitianBosonProduct::get_key(&key);
350 internal.add_operator_product(bp, value)?;
351 }
352 }
353 Ok(internal)
354 }
355}
356
357impl ops::Neg for BosonHamiltonian {
360 type Output = BosonHamiltonian;
361 fn neg(self) -> Self {
367 let mut internal = self.internal_map.clone();
368 for key in self.keys() {
369 internal.insert(key.clone(), internal[key].clone() * -1.0);
370 }
371 BosonHamiltonian {
372 internal_map: internal,
373 }
374 }
375}
376
377impl<T, V> ops::Add<T> for BosonHamiltonian
380where
381 T: IntoIterator<Item = (HermitianBosonProduct, V)>,
382 V: Into<CalculatorComplex>,
383{
384 type Output = Result<Self, StruqtureError>;
385 fn add(mut self, other: T) -> Self::Output {
396 for (key, value) in other.into_iter() {
397 self.add_operator_product(key.clone(), Into::<CalculatorComplex>::into(value))?;
398 }
399 Ok(self)
400 }
401}
402
403impl<T, V> ops::Sub<T> for BosonHamiltonian
406where
407 T: IntoIterator<Item = (HermitianBosonProduct, V)>,
408 V: Into<CalculatorComplex>,
409{
410 type Output = Result<Self, StruqtureError>;
411 fn sub(mut self, other: T) -> Self::Output {
422 for (key, value) in other.into_iter() {
423 self.add_operator_product(key.clone(), Into::<CalculatorComplex>::into(value) * -1.0)?;
424 }
425 Ok(self)
426 }
427}
428
429impl ops::Mul<CalculatorFloat> for BosonHamiltonian {
432 type Output = Self;
433 fn mul(self, other: CalculatorFloat) -> Self {
443 let mut internal = self.internal_map.clone();
444 for key in self.keys() {
445 internal.insert(key.clone(), internal[key].clone() * other.clone());
446 }
447 BosonHamiltonian {
448 internal_map: internal,
449 }
450 }
451}
452
453impl ops::Mul<CalculatorComplex> for BosonHamiltonian {
456 type Output = BosonOperator;
457 fn mul(self, other: CalculatorComplex) -> BosonOperator {
472 let mut new_out = BosonOperator::with_capacity(self.len());
473 for (key, val) in self {
474 if key.is_natural_hermitian() {
475 let new_key =
476 BosonProduct::new(key.creators().copied(), key.annihilators().copied())
477 .expect("Internal bug in BosonProduct::new");
478 new_out
479 .add_operator_product(new_key, other.clone() * val)
480 .expect("Internal bug in add_operator_product");
481 } else {
482 let new_key =
483 BosonProduct::new(key.creators().copied(), key.annihilators().copied())
484 .expect("Internal bug in BosonProduct::new");
485 new_out
486 .add_operator_product(new_key, other.clone() * val.clone())
487 .expect("Internal bug in add_operator_product");
488 let (key_tmp, prefactor) = key.hermitian_conjugate();
489 let new_key =
490 BosonProduct::new(key_tmp.annihilators().copied(), key_tmp.creators().copied())
491 .expect("Internal bug in BosonProduct::new");
492 new_out
493 .add_operator_product(new_key, other.clone() * val * prefactor)
494 .expect("Internal bug in add_operator_product");
495 }
496 }
497 new_out
498 }
499}
500
501impl ops::Mul<BosonHamiltonian> for BosonHamiltonian {
504 type Output = BosonOperator;
505 fn mul(self, other: BosonHamiltonian) -> BosonOperator {
519 let mut op = BosonOperator::with_capacity(self.len() * other.len());
520 for (bps, vals) in self {
521 for (bpo, valo) in other.iter() {
522 let boson_products = bps.clone() * bpo.clone();
523 let coefficient = Into::<CalculatorComplex>::into(valo) * vals.clone();
524 for b in boson_products {
525 op.add_operator_product(b, coefficient.clone())
526 .expect("Internal bug in add_operator_product");
527 }
528 }
529 }
530 op
531 }
532}
533
534impl IntoIterator for BosonHamiltonian {
537 type Item = (HermitianBosonProduct, CalculatorComplex);
538 type IntoIter = indexmap::map::IntoIter<HermitianBosonProduct, CalculatorComplex>;
539 fn into_iter(self) -> Self::IntoIter {
545 self.internal_map.into_iter()
546 }
547}
548
549impl<'a> IntoIterator for &'a BosonHamiltonian {
552 type Item = (&'a HermitianBosonProduct, &'a CalculatorComplex);
553
554 type IntoIter = Iter<'a, HermitianBosonProduct, CalculatorComplex>;
555
556 fn into_iter(self) -> Self::IntoIter {
562 self.internal_map.iter()
563 }
564}
565
566impl FromIterator<(HermitianBosonProduct, CalculatorComplex)> for BosonHamiltonian {
569 fn from_iter<I: IntoIterator<Item = (HermitianBosonProduct, CalculatorComplex)>>(
583 iter: I,
584 ) -> Self {
585 let mut so = BosonHamiltonian::new();
586 for (pp, cc) in iter {
587 so.add_operator_product(pp, cc)
588 .expect("Internal error in add_operator_product");
589 }
590 so
591 }
592}
593
594impl Extend<(HermitianBosonProduct, CalculatorComplex)> for BosonHamiltonian {
597 fn extend<I: IntoIterator<Item = (HermitianBosonProduct, CalculatorComplex)>>(
607 &mut self,
608 iter: I,
609 ) {
610 for (pp, cc) in iter {
611 self.add_operator_product(pp, cc)
612 .expect("Internal bug in add_operator_product");
613 }
614 }
615}
616
617impl fmt::Display for BosonHamiltonian {
620 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
630 let mut output = "BosonHamiltonian{\n".to_string();
631 for (key, val) in self.iter() {
632 writeln!(output, "{key}: {val},")?;
633 }
634 output.push('}');
635
636 write!(f, "{output}")
637 }
638}
639
640#[cfg(test)]
641mod test {
642 use super::*;
643 use crate::STRUQTURE_VERSION;
644 use serde_test::{assert_tokens, Configure, Token};
645
646 #[test]
648 fn so_from_sos() {
649 let pp: HermitianBosonProduct = HermitianBosonProduct::new([0], [0]).unwrap();
650 let sos = BosonHamiltonianSerialize {
651 items: vec![(pp.clone(), 0.5.into(), 0.0.into())],
652 serialisation_meta: crate::StruqtureSerialisationMeta {
653 type_name: "BosonHamiltonian".to_string(),
654 min_version: (2, 0, 0),
655 version: STRUQTURE_VERSION.to_string(),
656 },
657 };
658 let mut so = BosonHamiltonian::new();
659 so.set(pp, CalculatorComplex::from(0.5)).unwrap();
660
661 assert_eq!(BosonHamiltonian::try_from(sos.clone()).unwrap(), so);
662 assert_eq!(BosonHamiltonianSerialize::from(so), sos);
663 }
664 #[test]
666 fn clone_partial_eq() {
667 let pp: HermitianBosonProduct = HermitianBosonProduct::new([0], [0]).unwrap();
668 let sos = BosonHamiltonianSerialize {
669 items: vec![(pp, 0.5.into(), 0.0.into())],
670 serialisation_meta: crate::StruqtureSerialisationMeta {
671 type_name: "BosonHamiltonian".to_string(),
672 min_version: (2, 0, 0),
673 version: "2.0.0".to_string(),
674 },
675 };
676
677 assert_eq!(sos.clone(), sos);
679
680 let pp_1: HermitianBosonProduct = HermitianBosonProduct::new([0], [0]).unwrap();
682 let sos_1 = BosonHamiltonianSerialize {
683 items: vec![(pp_1, 0.5.into(), 0.0.into())],
684 serialisation_meta: crate::StruqtureSerialisationMeta {
685 type_name: "BosonHamiltonian".to_string(),
686 min_version: (2, 0, 0),
687 version: "2.0.0".to_string(),
688 },
689 };
690 let pp_2: HermitianBosonProduct = HermitianBosonProduct::new([0], [1]).unwrap();
691 let sos_2 = BosonHamiltonianSerialize {
692 items: vec![(pp_2, 0.5.into(), 0.0.into())],
693 serialisation_meta: crate::StruqtureSerialisationMeta {
694 type_name: "BosonHamiltonian".to_string(),
695 min_version: (2, 0, 0),
696 version: "2.0.0".to_string(),
697 },
698 };
699 assert!(sos_1 == sos);
700 assert!(sos == sos_1);
701 assert!(sos_2 != sos);
702 assert!(sos != sos_2);
703 }
704
705 #[test]
707 fn debug() {
708 let pp: HermitianBosonProduct = HermitianBosonProduct::new([0], [0]).unwrap();
709 let sos = BosonHamiltonianSerialize {
710 items: vec![(pp, 0.5.into(), 0.0.into())],
711 serialisation_meta: crate::StruqtureSerialisationMeta {
712 type_name: "BosonHamiltonian".to_string(),
713 min_version: (2, 0, 0),
714 version: "2.0.0".to_string(),
715 },
716 };
717
718 assert_eq!(
719 format!("{sos:?}"),
720 "BosonHamiltonianSerialize { items: [(HermitianBosonProduct { creators: [0], annihilators: [0] }, Float(0.5), Float(0.0))], serialisation_meta: StruqtureSerialisationMeta { type_name: \"BosonHamiltonian\", min_version: (2, 0, 0), version: \"2.0.0\" } }"
721 );
722 }
723
724 #[test]
726 fn serde_readable() {
727 let pp: HermitianBosonProduct = HermitianBosonProduct::new([0], [0]).unwrap();
728 let sos = BosonHamiltonianSerialize {
729 items: vec![(pp, 0.5.into(), 0.0.into())],
730 serialisation_meta: crate::StruqtureSerialisationMeta {
731 type_name: "BosonHamiltonian".to_string(),
732 min_version: (2, 0, 0),
733 version: "2.0.0".to_string(),
734 },
735 };
736
737 assert_tokens(
738 &sos.readable(),
739 &[
740 Token::Struct {
741 name: "BosonHamiltonianSerialize",
742 len: 2,
743 },
744 Token::Str("items"),
745 Token::Seq { len: Some(1) },
746 Token::Tuple { len: 3 },
747 Token::Str("c0a0"),
748 Token::F64(0.5),
749 Token::F64(0.0),
750 Token::TupleEnd,
751 Token::SeqEnd,
752 Token::Str("serialisation_meta"),
753 Token::Struct {
754 name: "StruqtureSerialisationMeta",
755 len: 3,
756 },
757 Token::Str("type_name"),
758 Token::Str("BosonHamiltonian"),
759 Token::Str("min_version"),
760 Token::Tuple { len: 3 },
761 Token::U64(2),
762 Token::U64(0),
763 Token::U64(0),
764 Token::TupleEnd,
765 Token::Str("version"),
766 Token::Str("2.0.0"),
767 Token::StructEnd,
768 Token::StructEnd,
769 ],
770 );
771 }
772
773 #[test]
775 fn serde_compact() {
776 let pp: HermitianBosonProduct = HermitianBosonProduct::new([0], [0]).unwrap();
777 let sos = BosonHamiltonianSerialize {
778 items: vec![(pp, 0.5.into(), 0.0.into())],
779 serialisation_meta: crate::StruqtureSerialisationMeta {
780 type_name: "BosonHamiltonian".to_string(),
781 min_version: (2, 0, 0),
782 version: "2.0.0".to_string(),
783 },
784 };
785
786 assert_tokens(
787 &sos.compact(),
788 &[
789 Token::Struct {
790 name: "BosonHamiltonianSerialize",
791 len: 2,
792 },
793 Token::Str("items"),
794 Token::Seq { len: Some(1) },
795 Token::Tuple { len: 3 },
796 Token::Tuple { len: 2 },
797 Token::Seq { len: Some(1) },
798 Token::U64(0),
799 Token::SeqEnd,
800 Token::Seq { len: Some(1) },
801 Token::U64(0),
802 Token::SeqEnd,
803 Token::TupleEnd,
804 Token::NewtypeVariant {
805 name: "CalculatorFloat",
806 variant: "Float",
807 },
808 Token::F64(0.5),
809 Token::NewtypeVariant {
810 name: "CalculatorFloat",
811 variant: "Float",
812 },
813 Token::F64(0.0),
814 Token::TupleEnd,
815 Token::SeqEnd,
816 Token::Str("serialisation_meta"),
817 Token::Struct {
818 name: "StruqtureSerialisationMeta",
819 len: 3,
820 },
821 Token::Str("type_name"),
822 Token::Str("BosonHamiltonian"),
823 Token::Str("min_version"),
824 Token::Tuple { len: 3 },
825 Token::U64(2),
826 Token::U64(0),
827 Token::U64(0),
828 Token::TupleEnd,
829 Token::Str("version"),
830 Token::Str("2.0.0"),
831 Token::StructEnd,
832 Token::StructEnd,
833 ],
834 );
835 }
836}