1use super::{GetValueMixed, HermitianMixedProduct, MixedIndex};
14use crate::bosons::BosonProduct;
15use crate::fermions::FermionProduct;
16use crate::spins::PauliProduct;
17use crate::{CorrespondsTo, StruqtureError, SymmetricIndex};
18use num_complex::Complex64;
19use serde::{
20 de::{Error, SeqAccess, Visitor},
21 ser::SerializeTuple,
22 Deserialize, Deserializer, Serialize, Serializer,
23};
24use std::{ops::Mul, str::FromStr};
25use tinyvec::TinyVec;
26
27#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
51pub struct MixedProduct {
52 pub(crate) spins: TinyVec<[PauliProduct; 2]>,
54 pub(crate) bosons: TinyVec<[BosonProduct; 2]>,
56 pub(crate) fermions: TinyVec<[FermionProduct; 2]>,
58}
59
60#[cfg(feature = "json_schema")]
61impl schemars::JsonSchema for MixedProduct {
62 fn schema_name() -> std::borrow::Cow<'static, str> {
63 "MixedProduct".into()
64 }
65
66 fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
67 schemars::json_schema!({
68 "type": "string",
69 "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:."
70 })
71 }
72}
73
74impl crate::SerializationSupport for MixedProduct {
75 fn struqture_type() -> crate::StruqtureType {
76 crate::StruqtureType::MixedProduct
77 }
78}
79
80impl Serialize for MixedProduct {
81 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
93 where
94 S: Serializer,
95 {
96 let readable = serializer.is_human_readable();
97 if readable {
98 serializer.serialize_str(&self.to_string())
99 } else {
100 let mut tuple = serializer.serialize_tuple(3)?;
101 tuple.serialize_element(&self.spins.as_slice())?;
102 tuple.serialize_element(&self.bosons.as_slice())?;
103 tuple.serialize_element(&self.fermions.as_slice())?;
104 tuple.end()
105 }
106 }
107}
108
109impl<'de> Deserialize<'de> for MixedProduct {
112 fn deserialize<D>(deserializer: D) -> Result<MixedProduct, D::Error>
124 where
125 D: Deserializer<'de>,
126 {
127 let human_readable = deserializer.is_human_readable();
128 if human_readable {
129 struct TemporaryVisitor;
130 impl<'de> Visitor<'de> for TemporaryVisitor {
131 type Value = MixedProduct;
132
133 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
134 formatter.write_str("String")
135 }
136
137 fn visit_str<E>(self, v: &str) -> Result<MixedProduct, E>
138 where
139 E: serde::de::Error,
140 {
141 MixedProduct::from_str(v).map_err(|err| E::custom(format!("{err:?}")))
142 }
143
144 fn visit_borrowed_str<E>(self, v: &'de str) -> Result<MixedProduct, E>
145 where
146 E: serde::de::Error,
147 {
148 MixedProduct::from_str(v).map_err(|err| E::custom(format!("{err:?}")))
149 }
150 }
151
152 deserializer.deserialize_str(TemporaryVisitor)
153 } else {
154 struct MixedProductVisitor;
155 impl<'de> serde::de::Visitor<'de> for MixedProductVisitor {
156 type Value = MixedProduct;
157 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
158 std::fmt::Formatter::write_str(
159 formatter,
160 "Tuple of two sequences of unsigned integers",
161 )
162 }
163 fn visit_seq<M>(self, mut access: M) -> Result<Self::Value, M::Error>
165 where
166 M: SeqAccess<'de>,
167 {
168 let spins: TinyVec<[PauliProduct; 2]> = match access.next_element()? {
169 Some(x) => x,
170 None => {
171 return Err(M::Error::custom("Missing spin sequence".to_string()));
172 }
173 };
174 let bosons: TinyVec<[BosonProduct; 2]> = match access.next_element()? {
175 Some(x) => x,
176 None => {
177 return Err(M::Error::custom("Missing boson sequence".to_string()));
178 }
179 };
180 let fermions: TinyVec<[FermionProduct; 2]> = match access.next_element()? {
181 Some(x) => x,
182 None => {
183 return Err(M::Error::custom("Missing fermion sequence".to_string()));
184 }
185 };
186
187 Ok(MixedProduct {
188 spins,
189 bosons,
190 fermions,
191 })
192 }
193 }
194 let pp_visitor = MixedProductVisitor;
195
196 deserializer.deserialize_tuple(3, pp_visitor)
197 }
198 }
199}
200
201impl MixedProduct {
202 #[cfg(feature = "struqture_1_export")]
204 pub fn to_struqture_1(
205 &self,
206 ) -> Result<struqture_1::mixed_systems::MixedProduct, StruqtureError> {
207 let self_string = self.to_string();
208 let struqture_1_product = struqture_1::mixed_systems::MixedProduct::from_str(&self_string)
209 .map_err(|err| StruqtureError::GenericError {
210 msg: format!("{err}"),
211 })?;
212 Ok(struqture_1_product)
213 }
214
215 #[cfg(feature = "struqture_1_import")]
217 pub fn from_struqture_1(
218 value: &struqture_1::mixed_systems::MixedProduct,
219 ) -> Result<Self, StruqtureError> {
220 let value_string = value.to_string();
221 let pauli_product = Self::from_str(&value_string)?;
222 Ok(pauli_product)
223 }
224}
225
226impl MixedIndex for MixedProduct {
227 type SpinIndexType = PauliProduct;
228 type BosonicIndexType = BosonProduct;
229 type FermionicIndexType = FermionProduct;
230
231 fn new(
243 spins: impl IntoIterator<Item = Self::SpinIndexType>,
244 bosons: impl IntoIterator<Item = Self::BosonicIndexType>,
245 fermions: impl IntoIterator<Item = Self::FermionicIndexType>,
246 ) -> Result<Self, StruqtureError> {
247 Ok(Self {
248 spins: spins.into_iter().collect(),
249 bosons: bosons.into_iter().collect(),
250 fermions: fermions.into_iter().collect(),
251 })
252 }
253
254 fn spins(&self) -> std::slice::Iter<'_, PauliProduct> {
256 self.spins.iter()
257 }
258
259 fn bosons(&self) -> std::slice::Iter<'_, BosonProduct> {
261 self.bosons.iter()
262 }
263
264 fn fermions(&self) -> std::slice::Iter<'_, FermionProduct> {
266 self.fermions.iter()
267 }
268
269 fn create_valid_pair(
285 spins: impl IntoIterator<Item = Self::SpinIndexType>,
286 bosons: impl IntoIterator<Item = Self::BosonicIndexType>,
287 fermions: impl IntoIterator<Item = Self::FermionicIndexType>,
288 value: qoqo_calculator::CalculatorComplex,
289 ) -> Result<(Self, qoqo_calculator::CalculatorComplex), StruqtureError> {
290 let spins: TinyVec<[PauliProduct; 2]> = spins.into_iter().collect();
291 let bosons: TinyVec<[BosonProduct; 2]> = bosons.into_iter().collect();
292 let fermions: TinyVec<[FermionProduct; 2]> = fermions.into_iter().collect();
293 Ok((
294 Self {
295 spins,
296 bosons,
297 fermions,
298 },
299 value,
300 ))
301 }
302}
303
304impl FromStr for MixedProduct {
305 type Err = StruqtureError;
306 fn from_str(s: &str) -> Result<Self, Self::Err> {
317 let mut spins: TinyVec<[PauliProduct; 2]> = TinyVec::<[PauliProduct; 2]>::with_capacity(2);
318 let mut bosons: TinyVec<[BosonProduct; 2]> = TinyVec::<[BosonProduct; 2]>::with_capacity(2);
319 let mut fermions: TinyVec<[FermionProduct; 2]> =
320 TinyVec::<[FermionProduct; 2]>::with_capacity(2);
321 let subsystems = s.split(':').filter(|s| !s.is_empty());
322 for subsystem in subsystems {
323 if let Some(rest) = subsystem.strip_prefix('S') {
324 spins.push(PauliProduct::from_str(rest)?);
325 } else if let Some(rest) = subsystem.strip_prefix('B') {
326 bosons.push(BosonProduct::from_str(rest)?);
327 } else if let Some(rest) = subsystem.strip_prefix('F') {
328 fermions.push(FermionProduct::from_str(rest)?);
329 } else {
330 return Err(StruqtureError::ParsingError {
331 target_type: "MixedIndex".to_string(),
332 msg: format!(
333 "Encountered subsystem that is neither spin, nor boson, nor fermion: {subsystem}"
334 ),
335 });
336 }
337 }
338
339 Ok(Self {
340 spins,
341 bosons,
342 fermions,
343 })
344 }
345}
346
347impl GetValueMixed<'_, MixedProduct> for MixedProduct {
348 fn get_key(index: &MixedProduct) -> Self {
358 index.clone()
359 }
360
361 fn get_transform(
372 _index: &MixedProduct,
373 value: qoqo_calculator::CalculatorComplex,
374 ) -> qoqo_calculator::CalculatorComplex {
375 value
376 }
377}
378
379impl GetValueMixed<'_, HermitianMixedProduct> for MixedProduct {
380 fn get_key(index: &HermitianMixedProduct) -> Self {
390 Self {
391 spins: index.spins().cloned().collect(),
392 bosons: index.bosons().cloned().collect(),
393 fermions: index.fermions().cloned().collect(),
394 }
395 }
396
397 fn get_transform(
408 _index: &HermitianMixedProduct,
409 value: qoqo_calculator::CalculatorComplex,
410 ) -> qoqo_calculator::CalculatorComplex {
411 value
412 }
413}
414
415impl CorrespondsTo<MixedProduct> for MixedProduct {
416 fn corresponds_to(&self) -> MixedProduct {
422 self.clone()
423 }
424}
425
426impl SymmetricIndex for MixedProduct {
427 fn hermitian_conjugate(&self) -> (Self, f64) {
429 let mut coefficient = 1.0;
430
431 let mut new_spins = self.spins.clone();
432 for spin in new_spins.iter_mut() {
433 let (conj_spin, coeff) = spin.hermitian_conjugate();
434 *spin = conj_spin;
435 coefficient *= coeff;
436 }
437 let mut new_bosons = self.bosons.clone();
438 for boson in new_bosons.iter_mut() {
439 let (conj_boson, coeff) = boson.hermitian_conjugate();
440 *boson = conj_boson;
441 coefficient *= coeff;
442 }
443 let mut new_fermions = self.fermions.clone();
444 for fermion in new_fermions.iter_mut() {
445 let (conj_fermion, coeff) = fermion.hermitian_conjugate();
446 *fermion = conj_fermion;
447 coefficient *= coeff;
448 }
449 (
450 Self {
451 spins: new_spins,
452 bosons: new_bosons,
453 fermions: new_fermions,
454 },
455 coefficient,
456 )
457 }
458
459 fn is_natural_hermitian(&self) -> bool {
461 self.bosons.iter().all(|b| b.is_natural_hermitian())
462 && self.fermions.iter().all(|f| f.is_natural_hermitian())
463 }
464}
465
466impl Mul<MixedProduct> for MixedProduct {
469 type Output = Result<Vec<(MixedProduct, Complex64)>, StruqtureError>;
470
471 fn mul(self, rhs: MixedProduct) -> Self::Output {
482 if self.spins().len() != rhs.spins().len()
483 || self.bosons().len() != rhs.bosons().len()
484 || self.fermions().len() != rhs.fermions().len()
485 {
486 return Err(StruqtureError::MismatchedNumberSubsystems {
487 target_number_spin_subsystems: self.spins().len(),
488 target_number_boson_subsystems: self.bosons().len(),
489 target_number_fermion_subsystems: self.fermions().len(),
490 actual_number_spin_subsystems: rhs.spins().len(),
491 actual_number_boson_subsystems: rhs.bosons().len(),
492 actual_number_fermion_subsystems: rhs.fermions().len(),
493 });
494 }
495 let mut coefficient = Complex64::new(1.0, 0.0);
496 let mut result_vec: Vec<(MixedProduct, Complex64)> = Vec::new();
497 let mut tmp_spins: Vec<PauliProduct> = Vec::with_capacity(self.spins().len());
498 let mut tmp_bosons: Vec<Vec<BosonProduct>> = Vec::with_capacity(self.bosons().len());
499 let mut tmp_fermions: Vec<Vec<(FermionProduct, f64)>> =
500 Vec::with_capacity(self.fermions().len());
501 for (left, right) in self.spins.into_iter().zip(rhs.spins.into_iter()) {
502 let (val, coeff) = left * right;
503 tmp_spins.push(val);
504 coefficient *= coeff;
505 }
506 for (left, right) in self.bosons.into_iter().zip(rhs.bosons.into_iter()) {
508 let boson_multiplication = left.clone() * right.clone();
509 if !tmp_bosons.is_empty() {
510 let mut internal_tmp_bosons: Vec<Vec<BosonProduct>> = Vec::new();
511 for bp in boson_multiplication.clone() {
512 for tmp_bp in tmp_bosons.iter() {
513 let mut tmp_entry = tmp_bp.clone();
514 tmp_entry.push(bp.clone());
515 internal_tmp_bosons.push(tmp_entry);
516 }
517 }
518 tmp_bosons.clone_from(&internal_tmp_bosons);
519 } else {
520 for bp in boson_multiplication.clone() {
521 tmp_bosons.push(vec![bp]);
522 }
523 }
524 }
525 for (left, right) in self.fermions.into_iter().zip(rhs.fermions.into_iter()) {
526 let fermion_multiplication = left * right;
527 if !tmp_fermions.is_empty() {
528 let mut internal_tmp_fermions: Vec<Vec<(FermionProduct, f64)>> = Vec::new();
529 for fp in fermion_multiplication {
530 for tmp_fp in tmp_fermions.iter() {
531 let mut tmp_entry = tmp_fp.clone();
532 tmp_entry.push(fp.clone());
533 internal_tmp_fermions.push(tmp_entry);
534 }
535 }
536 tmp_fermions = internal_tmp_fermions;
537 } else {
538 for fp in fermion_multiplication.clone() {
539 tmp_fermions.push(vec![fp]);
540 }
541 }
542 }
543
544 for boson in tmp_bosons.clone() {
546 if !tmp_fermions.is_empty() {
547 for fermion in tmp_fermions.iter() {
548 let mut fermion_vec: Vec<FermionProduct> = Vec::new();
549 let mut sign = Complex64::new(1.0, 0.0);
550 for (f, val) in fermion {
551 fermion_vec.push(f.clone());
552 sign *= val;
553 }
554 result_vec.push((
555 MixedProduct::new(tmp_spins.clone(), boson.clone(), fermion_vec)?,
556 coefficient * sign,
557 ));
558 }
559 } else {
560 result_vec.push((
561 MixedProduct::new(tmp_spins.clone(), boson.clone(), vec![])?,
562 coefficient,
563 ));
564 }
565 }
566 if tmp_bosons.is_empty() && !tmp_fermions.is_empty() {
567 for fermion in tmp_fermions.iter() {
568 let mut fermion_vec: Vec<FermionProduct> = Vec::new();
569 let mut sign = Complex64::new(1.0, 0.0);
570 for (f, val) in fermion {
571 fermion_vec.push(f.clone());
572 sign *= val;
573 }
574 result_vec.push((
575 MixedProduct::new(tmp_spins.clone(), [], fermion_vec)?,
576 coefficient * sign,
577 ));
578 }
579 } else if tmp_bosons.is_empty() && tmp_fermions.is_empty() {
580 result_vec.push((MixedProduct::new(tmp_spins.clone(), [], [])?, coefficient))
581 }
582
583 Ok(result_vec)
584 }
585}
586
587impl Mul<HermitianMixedProduct> for MixedProduct {
588 type Output = Result<Vec<(MixedProduct, Complex64)>, StruqtureError>;
589
590 fn mul(self, rhs: HermitianMixedProduct) -> Self::Output {
605 if self.spins().len() != rhs.spins().len()
606 || self.bosons().len() != rhs.bosons().len()
607 || self.fermions().len() != rhs.fermions().len()
608 {
609 return Err(StruqtureError::MismatchedNumberSubsystems {
610 target_number_spin_subsystems: self.spins().len(),
611 target_number_boson_subsystems: self.bosons().len(),
612 target_number_fermion_subsystems: self.fermions().len(),
613 actual_number_spin_subsystems: rhs.spins().len(),
614 actual_number_boson_subsystems: rhs.bosons().len(),
615 actual_number_fermion_subsystems: rhs.fermions().len(),
616 });
617 }
618 let mut result_vec: Vec<(MixedProduct, Complex64)> = Vec::new();
619
620 let mut right_to_mul: Vec<(MixedProduct, f64)> = Vec::new();
621 let mhp_right = MixedProduct::new(rhs.spins, rhs.bosons, rhs.fermions)
622 .expect("Could not convert rhs into a MixedProduct");
623 right_to_mul.push((mhp_right.clone(), 1.0));
624 if !mhp_right.is_natural_hermitian() {
625 right_to_mul.push(mhp_right.hermitian_conjugate());
626 }
627
628 for (rhs, rsign) in right_to_mul {
629 let mut coefficient = Complex64::new(rsign, 0.0);
630 let mut tmp_spins: Vec<PauliProduct> = Vec::with_capacity(self.spins().len());
631 let mut tmp_bosons: Vec<Vec<BosonProduct>> = Vec::with_capacity(self.bosons().len());
632 let mut tmp_fermions: Vec<Vec<(FermionProduct, f64)>> =
633 Vec::with_capacity(self.fermions().len());
634 for (left, right) in self.clone().spins.into_iter().zip(rhs.spins.into_iter()) {
635 let (val, coeff) = left * right;
636 tmp_spins.push(val);
637 coefficient *= coeff;
638 }
639 for (left, right) in self.clone().bosons.into_iter().zip(rhs.bosons.into_iter()) {
641 let boson_multiplication = left.clone() * right.clone();
642 if !tmp_bosons.is_empty() {
643 let mut internal_tmp_bosons: Vec<Vec<BosonProduct>> = Vec::new();
644 for bp in boson_multiplication.clone() {
645 for tmp_bp in tmp_bosons.iter() {
646 let mut tmp_entry = tmp_bp.clone();
647 tmp_entry.push(bp.clone());
648 internal_tmp_bosons.push(tmp_entry);
649 }
650 }
651 tmp_bosons.clone_from(&internal_tmp_bosons);
652 } else {
653 for bp in boson_multiplication.clone() {
654 tmp_bosons.push(vec![bp]);
655 }
656 }
657 }
658 for (left, right) in self
659 .fermions
660 .clone()
661 .into_iter()
662 .zip(rhs.fermions.into_iter())
663 {
664 let fermion_multiplication = left * right;
665 if !tmp_fermions.is_empty() {
666 let mut internal_tmp_fermions: Vec<Vec<(FermionProduct, f64)>> = Vec::new();
667 for fp in fermion_multiplication {
668 for tmp_fp in tmp_fermions.iter() {
669 let mut tmp_entry = tmp_fp.clone();
670 tmp_entry.push(fp.clone());
671 internal_tmp_fermions.push(tmp_entry);
672 }
673 }
674 tmp_fermions = internal_tmp_fermions;
675 } else {
676 for fp in fermion_multiplication.clone() {
677 tmp_fermions.push(vec![fp]);
678 }
679 }
680 }
681
682 for boson in tmp_bosons.clone() {
684 if !tmp_fermions.is_empty() {
685 for fermion in tmp_fermions.iter() {
686 let mut fermion_vec: Vec<FermionProduct> = Vec::new();
687 let mut sign = Complex64::new(1.0, 0.0);
688 for (f, val) in fermion {
689 fermion_vec.push(f.clone());
690 sign *= val;
691 }
692 result_vec.push((
693 MixedProduct::new(tmp_spins.clone(), boson.clone(), fermion_vec)?,
694 coefficient * sign,
695 ));
696 }
697 } else {
698 result_vec.push((
699 MixedProduct::new(tmp_spins.clone(), boson.clone(), vec![])?,
700 coefficient,
701 ));
702 }
703 }
704 if tmp_bosons.is_empty() && !tmp_fermions.is_empty() {
705 for fermion in tmp_fermions.iter() {
706 let mut fermion_vec: Vec<FermionProduct> = Vec::new();
707 let mut sign = Complex64::new(1.0, 0.0);
708 for (f, val) in fermion {
709 fermion_vec.push(f.clone());
710 sign *= val;
711 }
712 result_vec.push((
713 MixedProduct::new(tmp_spins.clone(), [], fermion_vec)?,
714 coefficient * sign,
715 ));
716 }
717 } else if tmp_bosons.is_empty() && tmp_fermions.is_empty() {
718 result_vec.push((MixedProduct::new(tmp_spins.clone(), [], [])?, coefficient))
719 }
720 }
721
722 Ok(result_vec)
723 }
724}
725
726impl std::fmt::Display for MixedProduct {
729 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
739 let mut string: String = String::new();
740 for spin in self.spins() {
741 string.push_str(format!("S{spin}:").as_str());
742 }
743 for boson in self.bosons() {
744 string.push_str(format!("B{boson}:").as_str());
745 }
746 for fermion in self.fermions() {
747 string.push_str(format!("F{fermion}:").as_str());
748 }
749 write!(f, "{string}")
750 }
751}
752
753#[cfg(test)]
754mod test {
755 use super::*;
756 use itertools::Itertools;
757 use test_case::test_case;
758
759 #[test_case("", &[], &[]; "empty")]
760 #[test_case(":S0X1X:", &[], &[PauliProduct::from_str("0X1X").unwrap()]; "single spin systems")]
761 #[test_case(":S0X1X:S0Z:", &[], &[PauliProduct::from_str("0X1X").unwrap(), PauliProduct::from_str("0Z").unwrap()]; "two spin systems")]
762 #[test_case(":S0X1X:Bc0a1:", &[BosonProduct::from_str("c0a1").unwrap()], &[PauliProduct::from_str("0X1X").unwrap()]; "spin-boson systems")]
763 fn from_string(stringformat: &str, bosons: &[BosonProduct], spins: &[PauliProduct]) {
764 let test_new = <MixedProduct as std::str::FromStr>::from_str(stringformat);
765 assert!(test_new.is_ok());
766 let res = test_new.unwrap();
767 let empty_bosons: Vec<BosonProduct> = bosons.to_vec();
768 let res_bosons: Vec<BosonProduct> = res.bosons.iter().cloned().collect_vec();
769 assert_eq!(res_bosons, empty_bosons);
770 let empty_spins: Vec<PauliProduct> = spins.to_vec();
771 let res_spins: Vec<PauliProduct> = res.spins.iter().cloned().collect_vec();
772 assert_eq!(res_spins, empty_spins);
773 }
774}