1use super::{MixedIndex, MixedProduct};
14use crate::bosons::BosonProduct;
15use crate::fermions::FermionProduct;
16use crate::spins::{PauliProduct, PlusMinusProduct};
17use crate::{ModeIndex, StruqtureError, SymmetricIndex};
18use itertools::Itertools;
19use num_complex::Complex64;
20use serde::{
21 de::{Error, SeqAccess, Visitor},
22 ser::SerializeTuple,
23 Deserialize, Deserializer, Serialize, Serializer,
24};
25use std::str::FromStr;
26use tinyvec::TinyVec;
27
28#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
52pub struct MixedPlusMinusProduct {
53 pub(crate) spins: TinyVec<[PlusMinusProduct; 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 MixedPlusMinusProduct {
63 fn schema_name() -> std::borrow::Cow<'static, str> {
64 "MixedPlusMinusProduct".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 +, - and Z are preceeded and creators (c) and annihilators (a) are followed by the modes they are acting on. E.g. :S0+1+:Bc0a1:Fc0a2:."
71 })
72 }
73}
74
75impl crate::SerializationSupport for MixedPlusMinusProduct {
76 fn struqture_type() -> crate::StruqtureType {
77 crate::StruqtureType::MixedPlusMinusProduct
78 }
79}
80
81impl Serialize for MixedPlusMinusProduct {
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 MixedPlusMinusProduct {
113 fn deserialize<D>(deserializer: D) -> Result<MixedPlusMinusProduct, 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 = MixedPlusMinusProduct;
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<MixedPlusMinusProduct, E>
139 where
140 E: serde::de::Error,
141 {
142 MixedPlusMinusProduct::from_str(v).map_err(|err| E::custom(format!("{err:?}")))
143 }
144
145 fn visit_borrowed_str<E>(self, v: &'de str) -> Result<MixedPlusMinusProduct, E>
146 where
147 E: serde::de::Error,
148 {
149 MixedPlusMinusProduct::from_str(v).map_err(|err| E::custom(format!("{err:?}")))
150 }
151 }
152
153 deserializer.deserialize_str(TemporaryVisitor)
154 } else {
155 struct MixedProductVisitor;
156 impl<'de> serde::de::Visitor<'de> for MixedProductVisitor {
157 type Value = MixedPlusMinusProduct;
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<[PlusMinusProduct; 2]> = match access.next_element()? {
170 Some(x) => x,
171 None => {
172 return Err(M::Error::custom("Missing spin 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("Missing boson sequence".to_string()));
179 }
180 };
181 let fermions: TinyVec<[FermionProduct; 2]> = match access.next_element()? {
182 Some(x) => x,
183 None => {
184 return Err(M::Error::custom("Missing fermion sequence".to_string()));
185 }
186 };
187
188 Ok(MixedPlusMinusProduct {
189 spins,
190 bosons,
191 fermions,
192 })
193 }
194 }
195 let pp_visitor = MixedProductVisitor;
196
197 deserializer.deserialize_tuple(3, pp_visitor)
198 }
199 }
200}
201
202impl MixedPlusMinusProduct {
203 pub fn new(
215 spins: impl IntoIterator<Item = PlusMinusProduct>,
216 bosons: impl IntoIterator<Item = BosonProduct>,
217 fermions: impl IntoIterator<Item = FermionProduct>,
218 ) -> Self {
219 Self {
220 spins: spins.into_iter().collect(),
221 bosons: bosons.into_iter().collect(),
222 fermions: fermions.into_iter().collect(),
223 }
224 }
225
226 pub fn spins(&self) -> std::slice::Iter<'_, PlusMinusProduct> {
232 self.spins.iter()
233 }
234
235 pub fn bosons(&self) -> std::slice::Iter<'_, BosonProduct> {
241 self.bosons.iter()
242 }
243
244 pub fn fermions(&self) -> std::slice::Iter<'_, FermionProduct> {
250 self.fermions.iter()
251 }
252
253 pub fn current_number_spins(&self) -> Vec<usize> {
259 self.spins().map(|s| s.current_number_spins()).collect()
260 }
261
262 pub fn current_number_bosonic_modes(&self) -> Vec<usize> {
268 self.bosons().map(|b| b.current_number_modes()).collect()
269 }
270
271 pub fn current_number_fermionic_modes(&self) -> Vec<usize> {
277 self.fermions().map(|f| f.current_number_modes()).collect()
278 }
279
280 #[cfg(feature = "struqture_1_export")]
282 pub fn to_struqture_1(
283 &self,
284 ) -> Result<struqture_1::mixed_systems::MixedPlusMinusProduct, StruqtureError> {
285 let self_string = self.to_string();
286 let struqture_1_product = struqture_1::mixed_systems::MixedPlusMinusProduct::from_str(
287 &self_string,
288 )
289 .map_err(|err| StruqtureError::GenericError {
290 msg: format!("{err}"),
291 })?;
292 Ok(struqture_1_product)
293 }
294
295 #[cfg(feature = "struqture_1_import")]
297 pub fn from_struqture_1(
298 value: &struqture_1::mixed_systems::MixedPlusMinusProduct,
299 ) -> Result<Self, StruqtureError> {
300 let value_string = value.to_string();
301 let pauli_product = Self::from_str(&value_string)?;
302 Ok(pauli_product)
303 }
304}
305
306impl From<MixedProduct> for Vec<(MixedPlusMinusProduct, Complex64)> {
307 fn from(value: MixedProduct) -> Self {
317 let mut return_vec: Vec<(MixedPlusMinusProduct, Complex64)> = Vec::new();
318 let mut spins_vec: Vec<Vec<(PlusMinusProduct, Complex64)>> = Vec::new();
319 for mixed_product in value.spins() {
320 let conversion = Vec::<(PlusMinusProduct, Complex64)>::from(mixed_product.clone());
321 spins_vec.push(conversion);
322 }
323
324 let mut converted: Vec<(Vec<PlusMinusProduct>, Complex64)> = Vec::new();
326 for (mp, prefactor) in spins_vec[0].clone() {
327 converted.push((vec![mp], prefactor))
328 }
329 for element in spins_vec.iter().skip(1) {
330 let mut new_converted = Vec::new();
331 for ((left, prefactor), (right, right_factor)) in
332 converted.iter().cartesian_product(element)
333 {
334 let mut new_vec = left.clone();
335 new_vec.push(right.clone());
336 new_converted.push((new_vec, prefactor * right_factor))
337 }
338 converted = new_converted;
339 }
340
341 for (vec_mp, cc) in converted {
342 return_vec.push((
343 MixedPlusMinusProduct::new(
344 vec_mp,
345 value.bosons().cloned(),
346 value.fermions().cloned(),
347 ),
348 cc,
349 ));
350 }
351 return_vec
352 }
353}
354
355impl TryFrom<MixedPlusMinusProduct> for Vec<(MixedProduct, Complex64)> {
356 type Error = StruqtureError;
357 fn try_from(value: MixedPlusMinusProduct) -> Result<Self, Self::Error> {
367 let mut return_vec: Vec<(MixedProduct, Complex64)> = Vec::new();
368 let mut spins_vec: Vec<Vec<(PauliProduct, Complex64)>> = Vec::new();
369 for mixed_product in value.spins() {
370 let conversion = Vec::<(PauliProduct, Complex64)>::from(mixed_product.clone());
371 spins_vec.push(conversion);
372 }
373
374 let mut converted: Vec<(Vec<PauliProduct>, Complex64)> = Vec::new();
376 for (mp, prefactor) in spins_vec[0].clone() {
377 converted.push((vec![mp], prefactor))
378 }
379 for element in spins_vec.iter().skip(1) {
380 let mut new_converted = Vec::new();
381 for ((left, prefactor), (right, right_factor)) in
382 converted.iter().cartesian_product(element)
383 {
384 let mut new_vec = left.clone();
385 new_vec.push(right.clone());
386 new_converted.push((new_vec, prefactor * right_factor))
387 }
388 converted = new_converted;
389 }
390
391 for (vec_mp, cc) in converted {
392 return_vec.push((
393 MixedProduct::new(vec_mp, value.bosons().cloned(), value.fermions().cloned())?,
394 cc,
395 ));
396 }
397 Ok(return_vec)
398 }
399}
400
401impl FromStr for MixedPlusMinusProduct {
402 type Err = StruqtureError;
403 fn from_str(s: &str) -> Result<Self, Self::Err> {
414 let mut spins: TinyVec<[PlusMinusProduct; 2]> =
415 TinyVec::<[PlusMinusProduct; 2]>::with_capacity(2);
416 let mut bosons: TinyVec<[BosonProduct; 2]> = TinyVec::<[BosonProduct; 2]>::with_capacity(2);
417 let mut fermions: TinyVec<[FermionProduct; 2]> =
418 TinyVec::<[FermionProduct; 2]>::with_capacity(2);
419 let subsystems = s.split(':').filter(|s| !s.is_empty());
420 for subsystem in subsystems {
421 if let Some(rest) = subsystem.strip_prefix('S') {
422 spins.push(PlusMinusProduct::from_str(rest)?);
423 } else if let Some(rest) = subsystem.strip_prefix('B') {
424 bosons.push(BosonProduct::from_str(rest)?);
425 } else if let Some(rest) = subsystem.strip_prefix('F') {
426 fermions.push(FermionProduct::from_str(rest)?);
427 } else {
428 return Err(StruqtureError::ParsingError {
429 target_type: "MixedPlusMinusProduct".to_string(),
430 msg: format!(
431 "Encountered subsystem that is neither spin, nor boson, nor fermion: {subsystem}"
432 ),
433 });
434 }
435 }
436
437 Ok(Self {
438 spins,
439 bosons,
440 fermions,
441 })
442 }
443}
444
445impl SymmetricIndex for MixedPlusMinusProduct {
446 fn hermitian_conjugate(&self) -> (Self, f64) {
448 let mut coefficient = 1.0;
449
450 let mut new_spins = self.spins.clone();
451 for spin in new_spins.iter_mut() {
452 let (conj_spin, coeff) = spin.hermitian_conjugate();
453 *spin = conj_spin;
454 coefficient *= coeff;
455 }
456 let mut new_bosons = self.bosons.clone();
457 for boson in new_bosons.iter_mut() {
458 let (conj_boson, coeff) = boson.hermitian_conjugate();
459 *boson = conj_boson;
460 coefficient *= coeff;
461 }
462 let mut new_fermions = self.fermions.clone();
463 for fermion in new_fermions.iter_mut() {
464 let (conj_fermion, coeff) = fermion.hermitian_conjugate();
465 *fermion = conj_fermion;
466 coefficient *= coeff;
467 }
468 (
469 Self {
470 spins: new_spins,
471 bosons: new_bosons,
472 fermions: new_fermions,
473 },
474 coefficient,
475 )
476 }
477
478 fn is_natural_hermitian(&self) -> bool {
480 self.bosons.iter().all(|b| b.is_natural_hermitian())
481 && self.fermions.iter().all(|f| f.is_natural_hermitian())
482 }
483}
484
485impl std::fmt::Display for MixedPlusMinusProduct {
488 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
498 let mut string: String = String::new();
499 for spin in self.spins() {
500 string.push_str(format!("S{spin}:").as_str());
501 }
502 for boson in self.bosons() {
503 string.push_str(format!("B{boson}:").as_str());
504 }
505 for fermion in self.fermions() {
506 string.push_str(format!("F{fermion}:").as_str());
507 }
508 write!(f, "{string}")
509 }
510}
511
512#[cfg(test)]
513mod test {
514 use super::*;
515 use itertools::Itertools;
516 use test_case::test_case;
517
518 #[test_case("", &[], &[]; "empty")]
519 #[test_case(":S0+1+:", &[], &[PlusMinusProduct::from_str("0+1+").unwrap()]; "single spin systems")]
520 #[test_case(":S0+1+:S0Z:", &[], &[PlusMinusProduct::from_str("0+1+").unwrap(), PlusMinusProduct::from_str("0Z").unwrap()]; "two spin systems")]
521 #[test_case(":S0+1+:Bc0a1:", &[BosonProduct::from_str("c0a1").unwrap()], &[PlusMinusProduct::from_str("0+1+").unwrap()]; "spin-boson systems")]
522 fn from_string(stringformat: &str, bosons: &[BosonProduct], spins: &[PlusMinusProduct]) {
523 let test_new = <MixedPlusMinusProduct as std::str::FromStr>::from_str(stringformat);
524 assert!(test_new.is_ok());
525 let res = test_new.unwrap();
526 let empty_bosons: Vec<BosonProduct> = bosons.to_vec();
527 let res_bosons: Vec<BosonProduct> = res.bosons.iter().cloned().collect_vec();
528 assert_eq!(res_bosons, empty_bosons);
529 let empty_spins: Vec<PlusMinusProduct> = spins.to_vec();
530 let res_spins: Vec<PlusMinusProduct> = res.spins.iter().cloned().collect_vec();
531 assert_eq!(res_spins, empty_spins);
532 }
533}