1use super::{GetValueMixed, MixedIndex, MixedProduct};
14use crate::fermions::FermionProduct;
15use crate::prelude::*;
16use crate::CorrespondsTo;
17use crate::{bosons::BosonProduct, spins::PauliProduct, StruqtureError, SymmetricIndex};
18use num_complex::Complex64;
19use qoqo_calculator::CalculatorFloat;
20use serde::{
21 de::{Error, SeqAccess, Visitor},
22 ser::SerializeTuple,
23 Deserialize, Deserializer, Serialize, Serializer,
24};
25use std::{ops::Mul, str::FromStr};
26use tinyvec::TinyVec;
27
28#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
52pub struct HermitianMixedProduct {
53 pub(crate) spins: TinyVec<[PauliProduct; 2]>,
55 pub(crate) bosons: TinyVec<[BosonProduct; 2]>,
57 pub(crate) fermions: TinyVec<[FermionProduct; 2]>,
59}
60
61#[cfg(feature = "json_schema")]
62impl schemars::JsonSchema for HermitianMixedProduct {
63 fn schema_name() -> std::borrow::Cow<'static, str> {
64 "HermitianMixedProduct".into()
65 }
66
67 fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
68 schemars::json_schema!({
69 "type": "string",
70 "description": "Represents products of Spin operators and Bosonic and Fermionic creators and annhilators by a string. Spin Operators X, Y and Z are preceeded and creators (c) and annihilators (a) are followed by the modes they are acting on. E.g. :S0X1Y:Bc0a0:Fc0a0:."
71 })
72 }
73}
74
75impl crate::SerializationSupport for HermitianMixedProduct {
76 fn struqture_type() -> crate::StruqtureType {
77 crate::StruqtureType::HermitianMixedProduct
78 }
79}
80
81impl Serialize for HermitianMixedProduct {
82 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
94 where
95 S: Serializer,
96 {
97 let readable = serializer.is_human_readable();
98 if readable {
99 serializer.serialize_str(&self.to_string())
100 } else {
101 let mut tuple = serializer.serialize_tuple(3)?;
102 tuple.serialize_element(&self.spins.as_slice())?;
103 tuple.serialize_element(&self.bosons.as_slice())?;
104 tuple.serialize_element(&self.fermions.as_slice())?;
105 tuple.end()
106 }
107 }
108}
109
110impl<'de> Deserialize<'de> for HermitianMixedProduct {
113 fn deserialize<D>(deserializer: D) -> Result<HermitianMixedProduct, D::Error>
125 where
126 D: Deserializer<'de>,
127 {
128 let human_readable = deserializer.is_human_readable();
129 if human_readable {
130 struct TemporaryVisitor;
131 impl<'de> Visitor<'de> for TemporaryVisitor {
132 type Value = HermitianMixedProduct;
133
134 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
135 formatter.write_str("String")
136 }
137
138 fn visit_str<E>(self, v: &str) -> Result<HermitianMixedProduct, E>
139 where
140 E: serde::de::Error,
141 {
142 HermitianMixedProduct::from_str(v).map_err(|err| E::custom(format!("{err:?}")))
143 }
144
145 fn visit_borrowed_str<E>(self, v: &'de str) -> Result<HermitianMixedProduct, E>
146 where
147 E: serde::de::Error,
148 {
149 HermitianMixedProduct::from_str(v).map_err(|err| E::custom(format!("{err:?}")))
150 }
151 }
152
153 deserializer.deserialize_str(TemporaryVisitor)
154 } else {
155 struct HermitianMixedProductVisitor;
156 impl<'de> serde::de::Visitor<'de> for HermitianMixedProductVisitor {
157 type Value = HermitianMixedProduct;
158 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
159 std::fmt::Formatter::write_str(
160 formatter,
161 "Tuple of two sequences of unsigned integers",
162 )
163 }
164 fn visit_seq<M>(self, mut access: M) -> Result<Self::Value, M::Error>
166 where
167 M: SeqAccess<'de>,
168 {
169 let spins: TinyVec<[PauliProduct; 2]> = match access.next_element()? {
170 Some(x) => x,
171 None => {
172 return Err(M::Error::custom("Missing creator sequence".to_string()));
173 }
174 };
175 let bosons: TinyVec<[BosonProduct; 2]> = match access.next_element()? {
176 Some(x) => x,
177 None => {
178 return Err(M::Error::custom(
179 "Missing annihilator sequence".to_string(),
180 ));
181 }
182 };
183 let fermions: TinyVec<[FermionProduct; 2]> = match access.next_element()? {
184 Some(x) => x,
185 None => {
186 return Err(M::Error::custom(
187 "Missing annihilator sequence".to_string(),
188 ));
189 }
190 };
191
192 Ok(HermitianMixedProduct {
193 spins,
194 bosons,
195 fermions,
196 })
197 }
198 }
199 let pp_visitor = HermitianMixedProductVisitor;
200
201 deserializer.deserialize_tuple(3, pp_visitor)
202 }
203 }
204}
205
206impl HermitianMixedProduct {
207 #[cfg(feature = "struqture_1_export")]
209 pub fn to_struqture_1(
210 &self,
211 ) -> Result<struqture_1::mixed_systems::HermitianMixedProduct, StruqtureError> {
212 let self_string = self.to_string();
213 let struqture_1_product = struqture_1::mixed_systems::HermitianMixedProduct::from_str(
214 &self_string,
215 )
216 .map_err(|err| StruqtureError::GenericError {
217 msg: format!("{err}"),
218 })?;
219 Ok(struqture_1_product)
220 }
221
222 #[cfg(feature = "struqture_1_import")]
224 pub fn from_struqture_1(
225 value: &struqture_1::mixed_systems::HermitianMixedProduct,
226 ) -> Result<Self, StruqtureError> {
227 let value_string = value.to_string();
228 let pauli_product = Self::from_str(&value_string)?;
229 Ok(pauli_product)
230 }
231}
232
233impl MixedIndex for HermitianMixedProduct {
234 type SpinIndexType = PauliProduct;
235 type BosonicIndexType = BosonProduct;
236 type FermionicIndexType = FermionProduct;
237
238 fn new(
252 spins: impl IntoIterator<Item = Self::SpinIndexType>,
253 bosons: impl IntoIterator<Item = Self::BosonicIndexType>,
254 fermions: impl IntoIterator<Item = Self::FermionicIndexType>,
255 ) -> Result<Self, StruqtureError> {
256 let spins: TinyVec<[PauliProduct; 2]> = spins.into_iter().collect();
257 let bosons: TinyVec<[BosonProduct; 2]> = bosons.into_iter().collect();
258 let fermions: TinyVec<[FermionProduct; 2]> = fermions.into_iter().collect();
259 let mut hermitian_decision_made = false;
270 for b in bosons.iter() {
271 let mut number_equal_indices = 0;
272 for (creator, annihilator) in b.creators().zip(b.annihilators()) {
273 match annihilator.cmp(creator) {
274 std::cmp::Ordering::Less => {
275 return Err(StruqtureError::CreatorsAnnihilatorsMinimumIndex {
276 creators_min: Some(*creator),
277 annihilators_min: Some(*annihilator),
278 });
279 }
280 std::cmp::Ordering::Greater => {
281 hermitian_decision_made = true;
282 break;
283 }
284 _ => {
285 number_equal_indices += 1;
286 }
287 }
288 }
289 if b.creators().len() > number_equal_indices
290 && b.annihilators().len() == number_equal_indices
291 {
292 return Err(StruqtureError::CreatorsAnnihilatorsMinimumIndex {
293 creators_min: b.creators().nth(number_equal_indices).copied(),
294 annihilators_min: None,
295 });
296 }
297
298 if hermitian_decision_made {
299 break;
300 }
301 }
302 if !hermitian_decision_made {
303 for f in fermions.iter() {
304 let mut number_equal_indices = 0;
305 for (creator, annihilator) in f.creators().zip(f.annihilators()) {
306 match annihilator.cmp(creator) {
307 std::cmp::Ordering::Less => {
308 return Err(StruqtureError::CreatorsAnnihilatorsMinimumIndex {
309 creators_min: Some(*creator),
310 annihilators_min: Some(*annihilator),
311 });
312 }
313 std::cmp::Ordering::Greater => {
314 hermitian_decision_made = true;
315 break;
316 }
317 _ => {
318 number_equal_indices += 1;
319 }
320 }
321 }
322 if f.creators().len() > number_equal_indices
323 && f.annihilators().len() == number_equal_indices
324 {
325 return Err(StruqtureError::CreatorsAnnihilatorsMinimumIndex {
326 creators_min: f.creators().nth(number_equal_indices).copied(),
327 annihilators_min: None,
328 });
329 }
330 if hermitian_decision_made {
331 break;
332 }
333 }
334 }
335 Ok(Self {
336 spins,
337 bosons,
338 fermions,
339 })
340 }
341
342 fn spins(&self) -> std::slice::Iter<'_, PauliProduct> {
344 self.spins.iter()
345 }
346
347 fn bosons(&self) -> std::slice::Iter<'_, BosonProduct> {
349 self.bosons.iter()
350 }
351
352 fn fermions(&self) -> std::slice::Iter<'_, FermionProduct> {
354 self.fermions.iter()
355 }
356
357 fn create_valid_pair(
374 spins: impl IntoIterator<Item = Self::SpinIndexType>,
375 bosons: impl IntoIterator<Item = Self::BosonicIndexType>,
376 fermions: impl IntoIterator<Item = Self::FermionicIndexType>,
377 value: qoqo_calculator::CalculatorComplex,
378 ) -> Result<(Self, qoqo_calculator::CalculatorComplex), StruqtureError> {
379 let spins: TinyVec<[PauliProduct; 2]> = spins.into_iter().collect();
380 let bosons: TinyVec<[BosonProduct; 2]> = bosons.into_iter().collect();
381 let fermions: TinyVec<[FermionProduct; 2]> = fermions.into_iter().collect();
382
383 let mut hermitian_conjugate = false;
384 let mut hermitian_decision_made = false;
385 for b in bosons.iter() {
386 let mut number_equal_indices = 0;
387 for (creator, annihilator) in b.creators().zip(b.annihilators()) {
388 match annihilator.cmp(creator) {
389 std::cmp::Ordering::Less => {
390 hermitian_conjugate = true;
391 hermitian_decision_made = true;
392 break;
393 }
394 std::cmp::Ordering::Greater => {
395 hermitian_decision_made = true;
396 break;
397 }
398 _ => {
399 number_equal_indices += 1;
400 }
401 }
402 }
403 if b.creators().len() > number_equal_indices
404 && b.annihilators().len() == number_equal_indices
405 {
406 hermitian_conjugate = true;
407 hermitian_decision_made = true;
408 }
409 if hermitian_decision_made {
410 break;
411 }
412 }
413 if !hermitian_decision_made {
414 for f in fermions.iter() {
415 let mut number_equal_indices = 0;
416 for (creator, annihilator) in f.creators().zip(f.annihilators()) {
417 match annihilator.cmp(creator) {
418 std::cmp::Ordering::Less => {
419 hermitian_conjugate = true;
420 break;
421 }
422 std::cmp::Ordering::Greater => {
423 hermitian_decision_made = true;
424 break;
425 }
426 _ => {
427 number_equal_indices += 1;
428 }
429 }
430 }
431 if f.creators().len() > number_equal_indices
432 && f.annihilators().len() == number_equal_indices
433 {
434 hermitian_conjugate = true;
435 hermitian_decision_made = true;
436 }
437 if hermitian_decision_made {
438 break;
439 }
440 }
441 }
442 let (new_index, new_val) = if hermitian_conjugate {
443 let mut new_spins: TinyVec<[PauliProduct; 2]> = TinyVec::<[PauliProduct; 2]>::new();
444 let mut prefactor = 1.0;
445 for s in spins {
446 let (new_s, pf) = s.hermitian_conjugate();
447 new_spins.push(new_s);
448 prefactor *= pf;
449 }
450 let mut new_bosons: TinyVec<[BosonProduct; 2]> = TinyVec::<[BosonProduct; 2]>::new();
451 for b in bosons {
452 let (new_b, pf) = b.hermitian_conjugate();
453 new_bosons.push(new_b);
454 prefactor *= pf;
455 }
456 let mut new_fermions: TinyVec<[FermionProduct; 2]> =
457 TinyVec::<[FermionProduct; 2]>::new();
458 for f in fermions {
459 let (new_f, pf) = f.hermitian_conjugate();
460 new_fermions.push(new_f);
461 prefactor *= pf;
462 }
463 (
464 Self {
465 spins: new_spins,
466 bosons: new_bosons,
467 fermions: new_fermions,
468 },
469 value.conj() * prefactor,
470 )
471 } else {
472 (
473 Self {
474 spins,
475 bosons,
476 fermions,
477 },
478 value,
479 )
480 };
481
482 if new_index.is_natural_hermitian() && new_val.im != CalculatorFloat::ZERO {
483 Err(StruqtureError::NonHermitianOperator)
484 } else {
485 Ok((new_index, new_val))
486 }
487 }
488}
489
490impl FromStr for HermitianMixedProduct {
491 type Err = StruqtureError;
492
493 fn from_str(s: &str) -> Result<Self, Self::Err> {
504 let mut spins: TinyVec<[PauliProduct; 2]> = TinyVec::<[PauliProduct; 2]>::with_capacity(2);
505 let mut bosons: TinyVec<[BosonProduct; 2]> = TinyVec::<[BosonProduct; 2]>::with_capacity(2);
506 let mut fermions: TinyVec<[FermionProduct; 2]> =
507 TinyVec::<[FermionProduct; 2]>::with_capacity(2);
508 let subsystems = s.split(':').filter(|s| !s.is_empty());
509 for subsystem in subsystems {
510 if let Some(rest) = subsystem.strip_prefix('S') {
511 spins.push(PauliProduct::from_str(rest)?);
512 } else if let Some(rest) = subsystem.strip_prefix('B') {
513 bosons.push(BosonProduct::from_str(rest)?);
514 } else if let Some(rest) = subsystem.strip_prefix('F') {
515 fermions.push(FermionProduct::from_str(rest)?);
516 } else {
517 return Err(StruqtureError::ParsingError {
518 target_type: "MixedIndex".to_string(),
519 msg: format!(
520 "Encountered subsystem that is neither spin nor boson: {subsystem}"
521 ),
522 });
523 }
524 }
525
526 HermitianMixedProduct::new(spins, bosons, fermions)
527 }
528}
529
530impl GetValueMixed<'_, HermitianMixedProduct> for HermitianMixedProduct {
531 fn get_key(index: &HermitianMixedProduct) -> Self {
541 index.clone()
542 }
543
544 fn get_transform(
555 _index: &HermitianMixedProduct,
556 value: qoqo_calculator::CalculatorComplex,
557 ) -> qoqo_calculator::CalculatorComplex {
558 value
559 }
560}
561
562impl CorrespondsTo<HermitianMixedProduct> for HermitianMixedProduct {
563 fn corresponds_to(&self) -> HermitianMixedProduct {
569 self.clone()
570 }
571}
572
573impl SymmetricIndex for HermitianMixedProduct {
574 fn hermitian_conjugate(&self) -> (Self, f64) {
576 (self.clone(), 1.0)
577 }
578
579 fn is_natural_hermitian(&self) -> bool {
581 self.bosons.iter().all(|b| b.is_natural_hermitian())
582 && self.fermions.iter().all(|f| f.is_natural_hermitian())
583 }
584}
585
586impl Mul<HermitianMixedProduct> for HermitianMixedProduct {
589 type Output = Result<Vec<(MixedProduct, Complex64)>, StruqtureError>;
590
591 fn mul(self, rhs: HermitianMixedProduct) -> Self::Output {
607 if self.spins().len() != rhs.spins().len()
608 || self.bosons().len() != rhs.bosons().len()
609 || self.fermions().len() != rhs.fermions().len()
610 {
611 return Err(StruqtureError::MismatchedNumberSubsystems {
612 target_number_spin_subsystems: self.spins().len(),
613 target_number_boson_subsystems: self.bosons().len(),
614 target_number_fermion_subsystems: self.fermions().len(),
615 actual_number_spin_subsystems: rhs.spins().len(),
616 actual_number_boson_subsystems: rhs.bosons().len(),
617 actual_number_fermion_subsystems: rhs.fermions().len(),
618 });
619 }
620 let mut result_vec: Vec<(MixedProduct, Complex64)> = Vec::new();
621
622 let mut left_to_mul: Vec<(MixedProduct, f64)> = Vec::new();
623 let mhp_left = MixedProduct::new(self.spins, self.bosons, self.fermions)
624 .expect("Could not convert self into a MixedProduct");
625 left_to_mul.push((mhp_left.clone(), 1.0));
626 if !mhp_left.is_natural_hermitian() {
627 left_to_mul.push(mhp_left.hermitian_conjugate());
628 }
629
630 let mut right_to_mul: Vec<(MixedProduct, f64)> = Vec::new();
631 let mhp_right = MixedProduct::new(rhs.spins, rhs.bosons, rhs.fermions)
632 .expect("Could not convert rhs into a MixedProduct");
633 right_to_mul.push((mhp_right.clone(), 1.0));
634 if !mhp_right.is_natural_hermitian() {
635 right_to_mul.push(mhp_right.hermitian_conjugate());
636 }
637
638 for (lhs, lsign) in left_to_mul {
639 for (rhs, rsign) in right_to_mul.clone() {
640 let mut coefficient = Complex64::new(lsign * rsign, 0.0);
641 let mut tmp_spins: Vec<PauliProduct> = Vec::with_capacity(lhs.spins().len());
642 let mut tmp_bosons: Vec<Vec<BosonProduct>> = Vec::with_capacity(lhs.bosons().len());
643 let mut tmp_fermions: Vec<Vec<(FermionProduct, f64)>> =
644 Vec::with_capacity(lhs.fermions().len());
645 for (left, right) in lhs.clone().spins.into_iter().zip(rhs.spins.into_iter()) {
646 let (val, coeff) = left * right;
647 tmp_spins.push(val);
648 coefficient *= coeff;
649 }
650 for (left, right) in lhs.clone().bosons.into_iter().zip(rhs.bosons.into_iter()) {
652 let boson_multiplication = left.clone() * right.clone();
653 if !tmp_bosons.is_empty() {
654 let mut internal_tmp_bosons: Vec<Vec<BosonProduct>> = Vec::new();
655 for bp in boson_multiplication.clone() {
656 for tmp_bp in tmp_bosons.iter() {
657 let mut tmp_entry = tmp_bp.clone();
658 tmp_entry.push(bp.clone());
659 internal_tmp_bosons.push(tmp_entry);
660 }
661 }
662 tmp_bosons.clone_from(&internal_tmp_bosons);
663 } else {
664 for bp in boson_multiplication.clone() {
665 tmp_bosons.push(vec![bp]);
666 }
667 }
668 }
669 for (left, right) in lhs
670 .fermions
671 .clone()
672 .into_iter()
673 .zip(rhs.fermions.into_iter())
674 {
675 let fermion_multiplication = left * right;
676 if !tmp_fermions.is_empty() {
677 let mut internal_tmp_fermions: Vec<Vec<(FermionProduct, f64)>> = Vec::new();
678 for fp in fermion_multiplication {
679 for tmp_fp in tmp_fermions.iter() {
680 let mut tmp_entry = tmp_fp.clone();
681 tmp_entry.push(fp.clone());
682 internal_tmp_fermions.push(tmp_entry);
683 }
684 }
685 tmp_fermions = internal_tmp_fermions;
686 } else {
687 for fp in fermion_multiplication.clone() {
688 tmp_fermions.push(vec![fp]);
689 }
690 }
691 }
692
693 for boson in tmp_bosons.clone() {
695 if !tmp_fermions.is_empty() {
696 for fermion in tmp_fermions.iter() {
697 let mut fermion_vec: Vec<FermionProduct> = Vec::new();
698 let mut sign = Complex64::new(1.0, 0.0);
699 for (f, val) in fermion {
700 fermion_vec.push(f.clone());
701 sign *= val;
702 }
703 result_vec.push((
704 MixedProduct::new(tmp_spins.clone(), boson.clone(), fermion_vec)?,
705 coefficient * sign,
706 ));
707 }
708 } else {
709 result_vec.push((
710 MixedProduct::new(tmp_spins.clone(), boson.clone(), vec![])?,
711 coefficient,
712 ));
713 }
714 }
715 if tmp_bosons.is_empty() && !tmp_fermions.is_empty() {
716 for fermion in tmp_fermions.iter() {
717 let mut fermion_vec: Vec<FermionProduct> = Vec::new();
718 let mut sign = Complex64::new(1.0, 0.0);
719 for (f, val) in fermion {
720 fermion_vec.push(f.clone());
721 sign *= val;
722 }
723 result_vec.push((
724 MixedProduct::new(tmp_spins.clone(), [], fermion_vec)?,
725 coefficient * sign,
726 ));
727 }
728 } else if tmp_bosons.is_empty() && tmp_fermions.is_empty() {
729 result_vec.push((MixedProduct::new(tmp_spins.clone(), [], [])?, coefficient))
730 }
731 }
732 }
733
734 Ok(result_vec)
735 }
736}
737
738impl Mul<MixedProduct> for HermitianMixedProduct {
739 type Output = Result<Vec<(MixedProduct, Complex64)>, StruqtureError>;
740
741 fn mul(self, rhs: MixedProduct) -> Self::Output {
756 if self.spins().len() != rhs.spins().len()
757 || self.bosons().len() != rhs.bosons().len()
758 || self.fermions().len() != rhs.fermions().len()
759 {
760 return Err(StruqtureError::MismatchedNumberSubsystems {
761 target_number_spin_subsystems: self.spins().len(),
762 target_number_boson_subsystems: self.bosons().len(),
763 target_number_fermion_subsystems: self.fermions().len(),
764 actual_number_spin_subsystems: rhs.spins().len(),
765 actual_number_boson_subsystems: rhs.bosons().len(),
766 actual_number_fermion_subsystems: rhs.fermions().len(),
767 });
768 }
769 let mut result_vec: Vec<(MixedProduct, Complex64)> = Vec::new();
770
771 let mut left_to_mul: Vec<(MixedProduct, f64)> = Vec::new();
772 let mhp_left = MixedProduct::new(self.spins, self.bosons, self.fermions)
773 .expect("Could not convert self into a MixedProduct");
774 left_to_mul.push((mhp_left.clone(), 1.0));
775 if !mhp_left.is_natural_hermitian() {
776 left_to_mul.push(mhp_left.hermitian_conjugate());
777 }
778
779 for (lhs, lsign) in left_to_mul {
780 let mut coefficient = Complex64::new(lsign, 0.0);
781 let mut tmp_spins: Vec<PauliProduct> = Vec::with_capacity(lhs.spins().len());
782 let mut tmp_bosons: Vec<Vec<BosonProduct>> = Vec::with_capacity(lhs.bosons().len());
783 let mut tmp_fermions: Vec<Vec<(FermionProduct, f64)>> =
784 Vec::with_capacity(lhs.fermions().len());
785 for (left, right) in lhs
786 .clone()
787 .spins
788 .into_iter()
789 .zip(rhs.clone().spins.into_iter())
790 {
791 let (val, coeff) = left * right;
792 tmp_spins.push(val);
793 coefficient *= coeff;
794 }
795 for (left, right) in lhs
797 .clone()
798 .bosons
799 .into_iter()
800 .zip(rhs.clone().bosons.into_iter())
801 {
802 let boson_multiplication = left.clone() * right.clone();
803 if !tmp_bosons.is_empty() {
804 let mut internal_tmp_bosons: Vec<Vec<BosonProduct>> = Vec::new();
805 for bp in boson_multiplication.clone() {
806 for tmp_bp in tmp_bosons.iter() {
807 let mut tmp_entry = tmp_bp.clone();
808 tmp_entry.push(bp.clone());
809 internal_tmp_bosons.push(tmp_entry);
810 }
811 }
812 tmp_bosons.clone_from(&internal_tmp_bosons);
813 } else {
814 for bp in boson_multiplication.clone() {
815 tmp_bosons.push(vec![bp]);
816 }
817 }
818 }
819 for (left, right) in lhs
820 .fermions
821 .clone()
822 .into_iter()
823 .zip(rhs.clone().fermions.into_iter())
824 {
825 let fermion_multiplication = left * right;
826 if !tmp_fermions.is_empty() {
827 let mut internal_tmp_fermions: Vec<Vec<(FermionProduct, f64)>> = Vec::new();
828 for fp in fermion_multiplication {
829 for tmp_fp in tmp_fermions.iter() {
830 let mut tmp_entry = tmp_fp.clone();
831 tmp_entry.push(fp.clone());
832 internal_tmp_fermions.push(tmp_entry);
833 }
834 }
835 tmp_fermions = internal_tmp_fermions;
836 } else {
837 for fp in fermion_multiplication.clone() {
838 tmp_fermions.push(vec![fp]);
839 }
840 }
841 }
842
843 for boson in tmp_bosons.clone() {
845 if !tmp_fermions.is_empty() {
846 for fermion in tmp_fermions.iter() {
847 let mut fermion_vec: Vec<FermionProduct> = Vec::new();
848 let mut sign = Complex64::new(1.0, 0.0);
849 for (f, val) in fermion {
850 fermion_vec.push(f.clone());
851 sign *= val;
852 }
853 result_vec.push((
854 MixedProduct::new(tmp_spins.clone(), boson.clone(), fermion_vec)?,
855 coefficient * sign,
856 ));
857 }
858 } else {
859 result_vec.push((
860 MixedProduct::new(tmp_spins.clone(), boson.clone(), vec![])?,
861 coefficient,
862 ));
863 }
864 }
865 if tmp_bosons.is_empty() && !tmp_fermions.is_empty() {
866 for fermion in tmp_fermions.iter() {
867 let mut fermion_vec: Vec<FermionProduct> = Vec::new();
868 let mut sign = Complex64::new(1.0, 0.0);
869 for (f, val) in fermion {
870 fermion_vec.push(f.clone());
871 sign *= val;
872 }
873 result_vec.push((
874 MixedProduct::new(tmp_spins.clone(), [], fermion_vec)?,
875 coefficient * sign,
876 ));
877 }
878 } else if tmp_bosons.is_empty() && tmp_fermions.is_empty() {
879 result_vec.push((MixedProduct::new(tmp_spins.clone(), [], [])?, coefficient))
880 }
881 }
882
883 Ok(result_vec)
884 }
885}
886
887impl std::fmt::Display for HermitianMixedProduct {
890 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
900 let mut string: String = String::new();
901 for spin in self.spins() {
902 string.push_str(format!("S{spin}:").as_str());
903 }
904 for boson in self.bosons() {
905 string.push_str(format!("B{boson}:").as_str());
906 }
907 for fermion in self.fermions() {
908 string.push_str(format!("F{fermion}:").as_str());
909 }
910 write!(f, "{string}")
911 }
912}
913
914#[cfg(test)]
915mod test {
916 use super::*;
917 use itertools::Itertools;
918 use test_case::test_case;
919
920 #[test_case("", &[], &[]; "empty")]
921 #[test_case(":S0X1X:", &[], &[PauliProduct::from_str("0X1X").unwrap()]; "single spin systems")]
922 #[test_case(":S0X1X:S0Z:", &[], &[PauliProduct::from_str("0X1X").unwrap(), PauliProduct::from_str("0Z").unwrap()]; "two spin systems")]
923 #[test_case(":S0X1X:Bc0a1:", &[BosonProduct::from_str("c0a1").unwrap()], &[PauliProduct::from_str("0X1X").unwrap()]; "spin-boson systems")]
924 fn from_string(stringformat: &str, bosons: &[BosonProduct], spins: &[PauliProduct]) {
925 let test_new = <HermitianMixedProduct as std::str::FromStr>::from_str(stringformat);
926 assert!(test_new.is_ok());
927 let res = test_new.unwrap();
928 let empty_bosons: Vec<BosonProduct> = bosons.to_vec();
929 let res_bosons: Vec<BosonProduct> = res.bosons.iter().cloned().collect_vec();
930 assert_eq!(res_bosons, empty_bosons);
931 let empty_spins: Vec<PauliProduct> = spins.to_vec();
932 let res_spins: Vec<PauliProduct> = res.spins.iter().cloned().collect_vec();
933 assert_eq!(res_spins, empty_spins);
934 }
935}