struqture/bosons/
bosonic_hamiltonian.rs

1// Copyright © 2021-2023 HQS Quantum Simulations GmbH. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4// in compliance with the License. You may obtain a copy of the License at
5//
6//     http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software distributed under the
9// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
10// express or implied. See the License for the specific language governing permissions and
11// limitations under the License.
12
13use 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/// BosonHamiltonians are combinations of HermitianBosonProducts with specific CalculatorComplex coefficients.
28///
29/// This is a representation of sums of creation and annihilation operators with weightings (and their hermitian conjugates), in order to build a full hamiltonian.
30/// BosonHamiltonian is the hermitian equivalent of BosonOperator.
31///
32/// # Example
33///
34/// ```
35/// use struqture::prelude::*;
36/// use qoqo_calculator::CalculatorComplex;
37/// use struqture::bosons::{ HermitianBosonProduct, BosonHamiltonian};
38/// use struqture::prelude::*;
39///
40/// let mut sh = BosonHamiltonian::new();
41///
42/// let bp_0 = HermitianBosonProduct::new([0], [1]).unwrap();
43/// let bp_1 = HermitianBosonProduct::new([], [0]).unwrap();
44/// sh.set(bp_0.clone(), CalculatorComplex::from(0.5)).unwrap();
45/// sh.set(bp_1.clone(), CalculatorComplex::from(0.2)).unwrap();
46///
47/// // Access what you set:
48/// assert_eq!(sh.get(&bp_0), &CalculatorComplex::from(0.5));
49/// assert_eq!(sh.get(&bp_1), &CalculatorComplex::from(0.2));
50/// ```
51///
52#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
53#[serde(try_from = "BosonHamiltonianSerialize")]
54#[serde(into = "BosonHamiltonianSerialize")]
55pub struct BosonHamiltonian {
56    /// The internal HashMap of HermitianBosonProducts and coefficients (CalculatorComplex)
57    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    // From trait
119    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    // From trait
127    fn iter(&'a self) -> impl ExactSizeIterator<Item = (&'a Self::Index, &'a Self::Value)> {
128        self.internal_map.iter()
129    }
130
131    // From trait
132    fn keys(&'a self) -> impl ExactSizeIterator<Item = &'a Self::Index> {
133        self.internal_map.keys()
134    }
135
136    // From trait
137    fn values(&'a self) -> impl ExactSizeIterator<Item = &'a Self::Value> {
138        self.internal_map.values()
139    }
140
141    // From trait
142    fn remove(&mut self, key: &Self::Index) -> Option<Self::Value> {
143        self.internal_map.shift_remove(key)
144    }
145
146    // From trait
147    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    /// Overwrites an existing entry or sets a new entry in the BosonHamiltonian with the given (HermitianBosonProduct key, CalculatorComplex value) pair.
155    ///
156    /// # Arguments
157    ///
158    /// * `key` - The HermitianBosonProduct key to set in the BosonHamiltonian.
159    /// * `value` - The corresponding CalculatorComplex value to set for the key in the BosonHamiltonian.
160    ///
161    /// # Returns
162    ///
163    /// * `Ok(Some(CalculatorComplex))` - The key existed, this is the value it had before it was set with the value input.
164    /// * `Ok(None)` - The key did not exist, it has been set with its corresponding value.
165    /// * `Err(StruqtureError::NonHermitianOperator)` - Key is naturally hermitian (on-diagonal term), but its corresponding value is not real.
166    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            // Catch on diagonals with non-zero imaginary values
173            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    /// Adds a new (HermitianBosonProduct key, CalculatorComplex value) pair to the BosonHamiltonian.
187    ///
188    /// # Arguments
189    ///
190    /// * `key` - The HermitianBosonProduct key to added to the BosonHamiltonian.
191    /// * `value` - The corresponding CalculatorComplex value to add for the key in the BosonHamiltonian.
192    ///
193    /// # Returns
194    ///
195    /// * `Ok(())` - The (key, value) pair was successfully added.
196    /// * `Err(StruqtureError::NonHermitianOperator)` - Key is naturally hermitian (on-diagonal term), but its corresponding value is not real.
197    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    // From trait
215    fn hermitian_conjugate(&self) -> Self {
216        self.clone()
217    }
218}
219
220impl OperateOnModes<'_> for BosonHamiltonian {
221    /// Gets the maximum index of the BosonHamiltonian.
222    ///
223    /// # Returns
224    ///
225    /// * `usize` - The number of modes in the BosonHamiltonian.
226    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
241/// Implements the default function (Default trait) of BosonHamiltonian (an empty BosonHamiltonian).
242///
243impl Default for BosonHamiltonian {
244    fn default() -> Self {
245        Self::new()
246    }
247}
248
249/// Functions for the BosonHamiltonian
250///
251impl BosonHamiltonian {
252    /// Creates a new BosonHamiltonian.
253    ///
254    /// # Returns
255    ///
256    /// * `Self` - The new (empty) BosonHamiltonian.
257    pub fn new() -> Self {
258        BosonHamiltonian {
259            internal_map: IndexMap::new(),
260        }
261    }
262
263    /// Creates a new BosonHamiltonian with pre-allocated capacity.
264    ///
265    /// # Arguments
266    ///
267    /// * `capacity` - The pre-allocated capacity of the hamiltonian.
268    ///
269    /// # Returns
270    ///
271    /// * `Self` - The new (empty) BosonHamiltonian.
272    pub fn with_capacity(capacity: usize) -> Self {
273        Self {
274            internal_map: IndexMap::with_capacity(capacity),
275        }
276    }
277
278    /// Export to struqture_1 format.
279    #[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    /// Export to struqture_1 format.
296    #[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    // From trait
313    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    /// Tries to convert a BosonOperator into a BosonHamiltonian.
330    ///
331    /// # Arguments
332    ///
333    /// * `hamiltonian` - The BosonOperator to try to convert.
334    ///
335    /// # Returns
336    ///
337    /// * `Ok(Self)` - The BosonOperator converted into a BosonHamiltonian.
338    /// * `Err(StruqtureError::CreatorsAnnihilatorsMinimumIndex)` - The minimum index of the creators is larger than the minimum index of the annihilators.
339    /// * `Err(StruqtureError::NonHermitianOperator)` - Key is naturally hermitian (on-diagonal term), but its corresponding value is not real.
340    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
357/// Implements the negative sign function of BosonHamiltonian.
358///
359impl ops::Neg for BosonHamiltonian {
360    type Output = BosonHamiltonian;
361    /// Implement minus sign for BosonHamiltonian.
362    ///
363    /// # Returns
364    ///
365    /// * `Self` - The BosonHamiltonian * -1.
366    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
377/// Implements the plus function of BosonHamiltonian by BosonHamiltonian.
378///
379impl<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    /// Implements `+` (add) for two BosonHamiltonians.
386    ///
387    /// # Arguments
388    ///
389    /// * `other` - The BosonHamiltonian to be added.
390    ///
391    /// # Returns
392    ///
393    /// * `Ok(Self)` - The two BosonHamiltonians added together.
394    /// * `Err(StruqtureError::NonHermitianOperator)` - Key is naturally hermitian (on-diagonal term), but its corresponding value is not real.
395    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
403/// Implements the minus function of BosonHamiltonian by BosonHamiltonian.
404///
405impl<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    /// Implements `-` (subtract) for two BosonHamiltonians.
412    ///
413    /// # Arguments
414    ///
415    /// * `other` - The BosonHamiltonian to be subtracted.
416    ///
417    /// # Returns
418    ///
419    /// * `Ok(Self)` - The two BosonHamiltonians added together.
420    /// * `Err(StruqtureError::NonHermitianOperator)` - Key is naturally hermitian (on-diagonal term), but its corresponding value is not real.
421    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
429/// Implements the multiplication function of BosonHamiltonian by CalculatorFloat.
430///
431impl ops::Mul<CalculatorFloat> for BosonHamiltonian {
432    type Output = Self;
433    /// Implement `*` for BosonHamiltonian and CalculatorFloat.
434    ///
435    /// # Arguments
436    ///
437    /// * `other` - The CalculatorFloat by which to multiply.
438    ///
439    /// # Returns
440    ///
441    /// * `Self` - The BosonHamiltonian multiplied by the CalculatorFloat.
442    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
453/// Implements the multiplication function of BosonHamiltonian by CalculatorComplex.
454///
455impl ops::Mul<CalculatorComplex> for BosonHamiltonian {
456    type Output = BosonOperator;
457    /// Implement `*` for BosonHamiltonian and CalculatorComplex.
458    ///
459    /// # Arguments
460    ///
461    /// * `other` - The CalculatorComplex by which to multiply.
462    ///
463    /// # Returns
464    ///
465    /// * `BosonOperator` - The BosonHamiltonian multiplied by the CalculatorComplex.
466    ///
467    /// # Panics
468    ///
469    /// * Internal bug in BosonProduct::new.
470    /// * Internal bug in add_operator_product.
471    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
501/// Implements the multiplication function of BosonHamiltonian by BosonHamiltonian.
502///
503impl ops::Mul<BosonHamiltonian> for BosonHamiltonian {
504    type Output = BosonOperator;
505    /// Implement `*` for BosonHamiltonian and BosonHamiltonian.
506    ///
507    /// # Arguments
508    ///
509    /// * `other` - The BosonHamiltonian by which to multiply.
510    ///
511    /// # Returns
512    ///
513    /// * `BosonOperator` - The two BosonHamiltonians multiplied.
514    ///
515    /// # Panics
516    ///
517    /// * Internal bug in add_operator_product.
518    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
534/// Implements the into_iter function (IntoIterator trait) of BosonHamiltonian.
535///
536impl IntoIterator for BosonHamiltonian {
537    type Item = (HermitianBosonProduct, CalculatorComplex);
538    type IntoIter = indexmap::map::IntoIter<HermitianBosonProduct, CalculatorComplex>;
539    /// Returns the BosonHamiltonian in Iterator form.
540    ///
541    /// # Returns
542    ///
543    /// * `Self::IntoIter` - The BosonHamiltonian in Iterator form.
544    fn into_iter(self) -> Self::IntoIter {
545        self.internal_map.into_iter()
546    }
547}
548
549/// Implements the into_iter function (IntoIterator trait) of reference BosonHamiltonian.
550///
551impl<'a> IntoIterator for &'a BosonHamiltonian {
552    type Item = (&'a HermitianBosonProduct, &'a CalculatorComplex);
553
554    type IntoIter = Iter<'a, HermitianBosonProduct, CalculatorComplex>;
555
556    /// Returns the reference BosonHamiltonian in Iterator form.
557    ///
558    /// # Returns
559    ///
560    /// * `Self::IntoIter` - The BosonHamiltonian in Iterator form.
561    fn into_iter(self) -> Self::IntoIter {
562        self.internal_map.iter()
563    }
564}
565
566/// Implements the from_iter function (FromIterator trait) of BosonHamiltonian.
567///
568impl FromIterator<(HermitianBosonProduct, CalculatorComplex)> for BosonHamiltonian {
569    /// Returns the object in BosonHamiltonian form, from an Iterator form of the object.
570    ///
571    /// # Arguments
572    ///
573    /// * `iter` - The iterator containing the information from which to create the BosonHamiltonian.
574    ///
575    /// # Returns
576    ///
577    /// * `Self::IntoIter` - The iterator in BosonHamiltonian form.
578    ///
579    /// # Panics
580    ///
581    /// * Internal bug in add_operator_product.
582    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
594/// Implements the extend function (Extend trait) of BosonHamiltonian.
595///
596impl Extend<(HermitianBosonProduct, CalculatorComplex)> for BosonHamiltonian {
597    /// Extends the BosonHamiltonian by the specified operations (in Iterator form).
598    ///
599    /// # Arguments
600    ///
601    /// * `iter` - The iterator containing the operations by which to extend the BosonHamiltonian.
602    ///
603    /// # Panics
604    ///
605    /// * Internal bug in add_operator_product.
606    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
617/// Implements the format function (Display trait) of BosonHamiltonian.
618///
619impl fmt::Display for BosonHamiltonian {
620    /// Formats the BosonHamiltonian using the given formatter.
621    ///
622    /// # Arguments
623    ///
624    /// * `f` - The formatter to use.
625    ///
626    /// # Returns
627    ///
628    /// * `std::fmt::Result` - The formatted BosonHamiltonian.
629    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 the Clone and PartialEq traits of BosonHamiltonian
647    #[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 the Clone and PartialEq traits of BosonHamiltonian
665    #[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        // Test Clone trait
678        assert_eq!(sos.clone(), sos);
679
680        // Test PartialEq trait
681        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 the Debug trait of BosonHamiltonian
706    #[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 BosonHamiltonian Serialization and Deserialization traits (readable)
725    #[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 BosonHamiltonian Serialization and Deserialization traits (compact)
774    #[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}