1use super::{ToSparseMatrixOperator, ToSparseMatrixSuperOperator};
14use crate::fermions::FermionOperator;
15use crate::mappings::JordanWignerSpinToFermion;
16use crate::spins::{OperateOnSpins, PauliProduct, SpinHamiltonian, SpinIndex};
17use crate::{
18 CooSparseMatrix, GetValue, OperateOnDensityMatrix, OperateOnState, StruqtureError,
19 StruqtureVersionSerializable, SymmetricIndex, MINIMUM_STRUQTURE_VERSION,
20};
21#[cfg(feature = "indexed_map_iterators")]
22use indexmap::map::{Entry, Iter, Keys, Values};
23#[cfg(feature = "indexed_map_iterators")]
24use indexmap::IndexMap;
25use num_complex::Complex64;
26use qoqo_calculator::{CalculatorComplex, CalculatorFloat};
27use serde::{Deserialize, Serialize};
28#[cfg(not(feature = "indexed_map_iterators"))]
29use std::collections::hash_map::{Entry, Iter, Keys, Values};
30#[cfg(not(feature = "indexed_map_iterators"))]
31use std::collections::HashMap;
32use std::fmt::{self, Write};
33use std::iter::{FromIterator, IntoIterator};
34use std::ops;
35
36#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
61#[serde(from = "SpinOperatorSerialize")]
62#[serde(into = "SpinOperatorSerialize")]
63pub struct SpinOperator {
64 #[cfg(feature = "indexed_map_iterators")]
66 internal_map: IndexMap<PauliProduct, CalculatorComplex>,
67 #[cfg(not(feature = "indexed_map_iterators"))]
68 internal_map: HashMap<PauliProduct, CalculatorComplex>,
69}
70
71impl crate::MinSupportedVersion for SpinOperator {}
72
73#[cfg(feature = "json_schema")]
74impl schemars::JsonSchema for SpinOperator {
75 fn schema_name() -> String {
76 "SpinOperator".to_string()
77 }
78
79 fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
80 <SpinOperatorSerialize>::json_schema(gen)
81 }
82}
83
84#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
85#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
86#[cfg_attr(feature = "json_schema", schemars(deny_unknown_fields))]
87struct SpinOperatorSerialize {
91 items: Vec<(PauliProduct, CalculatorFloat, CalculatorFloat)>,
93 _struqture_version: StruqtureVersionSerializable,
95}
96
97impl From<SpinOperatorSerialize> for SpinOperator {
98 fn from(value: SpinOperatorSerialize) -> Self {
99 let new_noise_op: SpinOperator = value
100 .items
101 .into_iter()
102 .map(|(key, real, imag)| (key, CalculatorComplex { re: real, im: imag }))
103 .collect();
104 new_noise_op
105 }
106}
107
108impl From<SpinOperator> for SpinOperatorSerialize {
109 fn from(value: SpinOperator) -> Self {
110 let new_noise_op: Vec<(PauliProduct, CalculatorFloat, CalculatorFloat)> = value
111 .into_iter()
112 .map(|(key, val)| (key, val.re, val.im))
113 .collect();
114 let current_version = StruqtureVersionSerializable {
115 major_version: MINIMUM_STRUQTURE_VERSION.0,
116 minor_version: MINIMUM_STRUQTURE_VERSION.1,
117 };
118 Self {
119 items: new_noise_op,
120 _struqture_version: current_version,
121 }
122 }
123}
124
125impl<'a> OperateOnDensityMatrix<'a> for SpinOperator {
126 type IteratorType = Iter<'a, Self::Index, Self::Value>;
127 type KeyIteratorType = Keys<'a, Self::Index, Self::Value>;
128 type ValueIteratorType = Values<'a, Self::Index, Self::Value>;
129 type Value = CalculatorComplex;
130 type Index = PauliProduct;
131
132 fn get(&self, key: &Self::Index) -> &Self::Value {
134 match self.internal_map.get(key) {
135 Some(value) => value,
136 None => &CalculatorComplex::ZERO,
137 }
138 }
139
140 fn iter(&'a self) -> Self::IteratorType {
142 self.internal_map.iter()
143 }
144
145 fn keys(&'a self) -> Self::KeyIteratorType {
147 self.internal_map.keys()
148 }
149
150 fn values(&'a self) -> Self::ValueIteratorType {
152 self.internal_map.values()
153 }
154
155 #[cfg(feature = "indexed_map_iterators")]
156 fn remove(&mut self, key: &Self::Index) -> Option<Self::Value> {
158 self.internal_map.shift_remove(key)
159 }
160
161 #[cfg(not(feature = "indexed_map_iterators"))]
162 fn remove(&mut self, key: &Self::Index) -> Option<Self::Value> {
164 self.internal_map.remove(key)
165 }
166
167 fn empty_clone(&self, capacity: Option<usize>) -> Self {
169 match capacity {
170 Some(cap) => Self::with_capacity(cap),
171 None => Self::new(),
172 }
173 }
174
175 fn set(
187 &mut self,
188 key: Self::Index,
189 value: Self::Value,
190 ) -> Result<Option<Self::Value>, StruqtureError> {
191 if value != CalculatorComplex::ZERO {
192 Ok(self.internal_map.insert(key, value))
193 } else {
194 match self.internal_map.entry(key) {
195 #[cfg(feature = "indexed_map_iterators")]
196 Entry::Occupied(val) => Ok(Some(val.shift_remove())),
197 #[cfg(not(feature = "indexed_map_iterators"))]
198 Entry::Occupied(val) => Ok(Some(val.remove())),
199 Entry::Vacant(_) => Ok(None),
200 }
201 }
202 }
203}
204
205impl OperateOnState<'_> for SpinOperator {
206 fn hermitian_conjugate(&self) -> Self {
208 let mut new_operator = Self::with_capacity(self.len());
209 for (pauli_product, value) in self.iter() {
210 let (new_boson_product, prefactor) = pauli_product.hermitian_conjugate();
211 new_operator
212 .add_operator_product(new_boson_product, value.conj() * prefactor)
213 .expect("Internal bug in add_operator_product");
214 }
215 new_operator
216 }
217}
218
219impl OperateOnSpins<'_> for SpinOperator {
220 fn current_number_spins(&self) -> usize {
222 let mut max_mode: usize = 0;
223 if !self.internal_map.is_empty() {
224 for key in self.internal_map.keys() {
225 if key.current_number_spins() > max_mode {
226 max_mode = key.current_number_spins()
227 }
228 }
229 }
230 max_mode
231 }
232
233 fn number_spins(&self) -> usize {
239 self.current_number_spins()
240 }
241}
242
243impl ToSparseMatrixOperator<'_> for SpinOperator {}
244impl<'a> ToSparseMatrixSuperOperator<'a> for SpinOperator {
245 fn sparse_matrix_superoperator_entries_on_row(
247 &'a self,
248 row: usize,
249 number_spins: usize,
250 ) -> Result<std::collections::HashMap<usize, Complex64>, StruqtureError> {
251 <Self as ToSparseMatrixOperator>::sparse_matrix_superoperator_entries_on_row(
252 self,
253 row,
254 number_spins,
255 )
256 }
257
258 fn unitary_sparse_matrix_coo(&'a self) -> Result<CooSparseMatrix, StruqtureError> {
260 self.sparse_matrix_coo(None)
261 }
262
263 fn sparse_lindblad_entries(
265 &'a self,
266 ) -> Result<Vec<(CooSparseMatrix, CooSparseMatrix, Complex64)>, StruqtureError> {
267 let rate = Complex64::default();
268 let left: CooSparseMatrix = (vec![], (vec![], vec![]));
269 let right: CooSparseMatrix = (vec![], (vec![], vec![]));
270 Ok(vec![(left, right, rate)])
271 }
272}
273
274impl Default for SpinOperator {
277 fn default() -> Self {
278 Self::new()
279 }
280}
281
282impl SpinOperator {
285 pub fn new() -> Self {
291 SpinOperator {
292 #[cfg(not(feature = "indexed_map_iterators"))]
293 internal_map: HashMap::new(),
294 #[cfg(feature = "indexed_map_iterators")]
295 internal_map: IndexMap::new(),
296 }
297 }
298
299 pub fn with_capacity(capacity: usize) -> Self {
309 SpinOperator {
310 #[cfg(not(feature = "indexed_map_iterators"))]
311 internal_map: HashMap::with_capacity(capacity),
312 #[cfg(feature = "indexed_map_iterators")]
313 internal_map: IndexMap::with_capacity(capacity),
314 }
315 }
316
317 pub fn separate_into_n_terms(
327 &self,
328 number_spins: usize,
329 ) -> Result<(Self, Self), StruqtureError> {
330 let mut separated = Self::default();
331 let mut remainder = Self::default();
332 for (prod, val) in self.iter() {
333 if prod.len() == number_spins {
334 separated.add_operator_product(prod.clone(), val.clone())?;
335 } else {
336 remainder.add_operator_product(prod.clone(), val.clone())?;
337 }
338 }
339 Ok((separated, remainder))
340 }
341}
342
343impl From<SpinHamiltonian> for SpinOperator {
344 fn from(hamiltonian: SpinHamiltonian) -> Self {
358 let mut internal = SpinOperator::new();
359 for (key, value) in hamiltonian.into_iter() {
360 let bp = PauliProduct::get_key(&key);
361 internal
362 .add_operator_product(bp, CalculatorComplex::from(value))
363 .expect("Internal bug in add_operator_product");
364 }
365 internal
366 }
367}
368
369impl ops::Neg for SpinOperator {
372 type Output = SpinOperator;
373 fn neg(self) -> Self {
379 #[cfg(not(feature = "indexed_map_iterators"))]
380 let mut internal = HashMap::with_capacity(self.len());
381 #[cfg(feature = "indexed_map_iterators")]
382 let mut internal = IndexMap::with_capacity(self.len());
383 for (key, val) in self {
384 internal.insert(key.clone(), val.neg());
385 }
386 SpinOperator {
387 internal_map: internal,
388 }
389 }
390}
391
392impl<T, V> ops::Add<T> for SpinOperator
395where
396 T: IntoIterator<Item = (PauliProduct, V)>,
397 V: Into<CalculatorComplex>,
398{
399 type Output = Self;
400 fn add(mut self, other: T) -> Self {
414 for (key, value) in other.into_iter() {
415 self.add_operator_product(key.clone(), Into::<CalculatorComplex>::into(value))
416 .expect("Internal bug in add_operator_product");
417 }
418 self
419 }
420}
421
422impl<T, V> ops::Sub<T> for SpinOperator
425where
426 T: IntoIterator<Item = (PauliProduct, V)>,
427 V: Into<CalculatorComplex>,
428{
429 type Output = Self;
430 fn sub(mut self, other: T) -> Self {
444 for (key, value) in other.into_iter() {
445 self.add_operator_product(key.clone(), Into::<CalculatorComplex>::into(value) * -1.0)
446 .expect("Internal bug in add_operator_product");
447 }
448 self
449 }
450}
451
452impl<T> ops::Mul<T> for SpinOperator
455where
456 T: Into<CalculatorComplex>,
457{
458 type Output = Self;
459 fn mul(self, other: T) -> Self {
469 let other_cc = Into::<CalculatorComplex>::into(other);
470 let mut internal = self.internal_map.clone();
471 for (key, val) in self {
472 internal.insert(key, val * other_cc.clone());
473 }
474 SpinOperator {
475 internal_map: internal,
476 }
477 }
478}
479
480impl ops::Mul<SpinOperator> for SpinOperator {
483 type Output = Self;
484 fn mul(self, other: SpinOperator) -> Self {
498 let mut spin_op = SpinOperator::with_capacity(self.len() * other.len());
499 for (pps, vals) in self {
500 for (ppo, valo) in other.iter() {
501 let (ppp, coefficient) = pps.clone() * ppo.clone();
502 let coefficient =
503 Into::<CalculatorComplex>::into(valo) * coefficient * vals.clone();
504 spin_op
505 .add_operator_product(ppp, coefficient)
506 .expect("Internal bug in add_operator_product");
507 }
508 }
509 spin_op
510 }
511}
512
513impl ops::Mul<PauliProduct> for SpinOperator {
516 type Output = Self;
517 fn mul(self, ppo: PauliProduct) -> Self {
531 let mut spin_op = SpinOperator::with_capacity(self.len());
532 for (pps, vals) in self {
533 let (ppp, coefficient) = pps.clone() * ppo.clone();
534 let coefficient = CalculatorComplex::from(coefficient) * vals.clone();
535 spin_op
536 .add_operator_product(ppp, coefficient)
537 .expect("Internal bug in add_operator_product");
538 }
539 spin_op
540 }
541}
542
543impl ops::Mul<SpinOperator> for PauliProduct {
546 type Output = SpinOperator;
547 fn mul(self, other: SpinOperator) -> SpinOperator {
561 let mut spin_op = SpinOperator::with_capacity(other.len());
562 for (ppo, valo) in other.iter() {
563 let (ppp, coefficient) = self.clone() * ppo.clone();
564 let coefficient = valo.clone() * CalculatorComplex::from(coefficient);
565 spin_op
566 .add_operator_product(ppp, coefficient)
567 .expect("Internal bug in add_operator_product");
568 }
569 spin_op
570 }
571}
572
573impl IntoIterator for SpinOperator {
576 type Item = (PauliProduct, CalculatorComplex);
577 #[cfg(not(feature = "indexed_map_iterators"))]
578 type IntoIter = std::collections::hash_map::IntoIter<PauliProduct, CalculatorComplex>;
579 #[cfg(feature = "indexed_map_iterators")]
580 type IntoIter = indexmap::map::IntoIter<PauliProduct, CalculatorComplex>;
581 fn into_iter(self) -> Self::IntoIter {
587 self.internal_map.into_iter()
588 }
589}
590
591impl<'a> IntoIterator for &'a SpinOperator {
594 type Item = (&'a PauliProduct, &'a CalculatorComplex);
595 type IntoIter = Iter<'a, PauliProduct, CalculatorComplex>;
596
597 fn into_iter(self) -> Self::IntoIter {
603 self.internal_map.iter()
604 }
605}
606
607impl FromIterator<(PauliProduct, CalculatorComplex)> for SpinOperator {
610 fn from_iter<I: IntoIterator<Item = (PauliProduct, CalculatorComplex)>>(iter: I) -> Self {
624 let mut so = SpinOperator::new();
625 for (pp, cc) in iter {
626 so.add_operator_product(pp, cc)
627 .expect("Internal bug in add_operator_product");
628 }
629 so
630 }
631}
632
633impl Extend<(PauliProduct, CalculatorComplex)> for SpinOperator {
636 fn extend<I: IntoIterator<Item = (PauliProduct, CalculatorComplex)>>(&mut self, iter: I) {
646 for (pp, cc) in iter {
647 self.add_operator_product(pp, cc)
648 .expect("Internal bug in add_operator_product");
649 }
650 }
651}
652
653impl fmt::Display for SpinOperator {
656 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
666 let mut output = "SpinOperator{\n".to_string();
667 for (key, val) in self.iter() {
668 writeln!(output, "{}: {},", key, val)?;
669 }
670 output.push('}');
671
672 write!(f, "{}", output)
673 }
674}
675
676impl JordanWignerSpinToFermion for SpinOperator {
677 type Output = FermionOperator;
678
679 fn jordan_wigner(&self) -> Self::Output {
688 let mut out = FermionOperator::new();
689 for pp in self.keys() {
690 out = out + pp.jordan_wigner() * self.get(pp);
691 }
692 out
693 }
694}
695
696#[cfg(test)]
697mod test {
698 use super::*;
699 use serde_test::{assert_tokens, Configure, Token};
700
701 #[test]
703 fn so_from_sos() {
704 let pp: PauliProduct = PauliProduct::new().z(0);
705 let sos = SpinOperatorSerialize {
706 items: vec![(pp.clone(), 0.5.into(), 0.0.into())],
707 _struqture_version: StruqtureVersionSerializable {
708 major_version: 1,
709 minor_version: 0,
710 },
711 };
712 let mut so = SpinOperator::new();
713 so.set(pp, CalculatorComplex::from(0.5)).unwrap();
714
715 assert_eq!(SpinOperator::from(sos.clone()), so);
716 assert_eq!(SpinOperatorSerialize::from(so), sos);
717 }
718 #[test]
720 fn clone_partial_eq() {
721 let pp: PauliProduct = PauliProduct::new().z(0);
722 let sos = SpinOperatorSerialize {
723 items: vec![(pp, 0.5.into(), 0.0.into())],
724 _struqture_version: StruqtureVersionSerializable {
725 major_version: 1,
726 minor_version: 0,
727 },
728 };
729
730 assert_eq!(sos.clone(), sos);
732
733 let pp_1: PauliProduct = PauliProduct::new().z(0);
735 let sos_1 = SpinOperatorSerialize {
736 items: vec![(pp_1, 0.5.into(), 0.0.into())],
737 _struqture_version: StruqtureVersionSerializable {
738 major_version: 1,
739 minor_version: 0,
740 },
741 };
742 let pp_2: PauliProduct = PauliProduct::new().z(2);
743 let sos_2 = SpinOperatorSerialize {
744 items: vec![(pp_2, 0.5.into(), 0.0.into())],
745 _struqture_version: StruqtureVersionSerializable {
746 major_version: 1,
747 minor_version: 0,
748 },
749 };
750 assert!(sos_1 == sos);
751 assert!(sos == sos_1);
752 assert!(sos_2 != sos);
753 assert!(sos != sos_2);
754 }
755
756 #[test]
758 fn debug() {
759 let pp: PauliProduct = PauliProduct::new().z(0);
760 let sos = SpinOperatorSerialize {
761 items: vec![(pp, 0.5.into(), 0.0.into())],
762 _struqture_version: StruqtureVersionSerializable {
763 major_version: 1,
764 minor_version: 0,
765 },
766 };
767
768 assert_eq!(
769 format!("{:?}", sos),
770 "SpinOperatorSerialize { items: [(PauliProduct { items: [(0, Z)] }, Float(0.5), Float(0.0))], _struqture_version: StruqtureVersionSerializable { major_version: 1, minor_version: 0 } }"
771 );
772 }
773
774 #[test]
776 fn serde_readable() {
777 let pp = PauliProduct::new().x(0);
778 let sos = SpinOperatorSerialize {
779 items: vec![(pp, 0.5.into(), 0.0.into())],
780 _struqture_version: StruqtureVersionSerializable {
781 major_version: 1,
782 minor_version: 0,
783 },
784 };
785
786 assert_tokens(
787 &sos.readable(),
788 &[
789 Token::Struct {
790 name: "SpinOperatorSerialize",
791 len: 2,
792 },
793 Token::Str("items"),
794 Token::Seq { len: Some(1) },
795 Token::Tuple { len: 3 },
796 Token::Str("0X"),
797 Token::F64(0.5),
798 Token::F64(0.0),
799 Token::TupleEnd,
800 Token::SeqEnd,
801 Token::Str("_struqture_version"),
802 Token::Struct {
803 name: "StruqtureVersionSerializable",
804 len: 2,
805 },
806 Token::Str("major_version"),
807 Token::U32(1),
808 Token::Str("minor_version"),
809 Token::U32(0),
810 Token::StructEnd,
811 Token::StructEnd,
812 ],
813 );
814 }
815
816 #[test]
818 fn serde_compact() {
819 let pp = PauliProduct::new().x(0);
820 let sos = SpinOperatorSerialize {
821 items: vec![(pp, 0.5.into(), 0.0.into())],
822 _struqture_version: StruqtureVersionSerializable {
823 major_version: 1,
824 minor_version: 0,
825 },
826 };
827
828 assert_tokens(
829 &sos.compact(),
830 &[
831 Token::Struct {
832 name: "SpinOperatorSerialize",
833 len: 2,
834 },
835 Token::Str("items"),
836 Token::Seq { len: Some(1) },
837 Token::Tuple { len: 3 },
838 Token::Seq { len: Some(1) },
839 Token::Tuple { len: 2 },
840 Token::U64(0),
841 Token::UnitVariant {
842 name: "SingleSpinOperator",
843 variant: "X",
844 },
845 Token::TupleEnd,
846 Token::SeqEnd,
847 Token::NewtypeVariant {
848 name: "CalculatorFloat",
849 variant: "Float",
850 },
851 Token::F64(0.5),
852 Token::NewtypeVariant {
853 name: "CalculatorFloat",
854 variant: "Float",
855 },
856 Token::F64(0.0),
857 Token::TupleEnd,
858 Token::SeqEnd,
859 Token::Str("_struqture_version"),
860 Token::Struct {
861 name: "StruqtureVersionSerializable",
862 len: 2,
863 },
864 Token::Str("major_version"),
865 Token::U32(1),
866 Token::Str("minor_version"),
867 Token::U32(0),
868 Token::StructEnd,
869 Token::StructEnd,
870 ],
871 );
872 }
873}