struqture/spins/
spin_operator.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::{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/// SpinOperators are combinations of PauliProducts with specific CalculatorComplex coefficients.
37///
38/// This is a representation of sums of pauli products with weightings, in order to build a full hamiltonian.
39///
40/// # Example
41///
42/// ```
43/// use struqture::prelude::*;
44/// use qoqo_calculator::CalculatorComplex;
45/// use struqture::spins::{OperateOnSpins, PauliProduct, SpinOperator};
46///
47/// let mut so = SpinOperator::new();
48///
49/// // Representing the hamiltonian $ 1/2 \sigma_0^{x} \sigma_1^{x} + 1/5 \sigma_0^{z} $
50/// let pp_0x1x = PauliProduct::new().x(0).x(1);
51/// let pp_0z = PauliProduct::new().z(0);
52/// so.add_operator_product(pp_0x1x.clone(), CalculatorComplex::from(0.5)).unwrap();
53/// so.add_operator_product(pp_0z.clone(), CalculatorComplex::from(0.2)).unwrap();
54///
55/// // Access what you set:
56/// assert_eq!(so.get(&pp_0x1x), &CalculatorComplex::from(0.5));
57/// assert_eq!(so.get(&pp_0z), &CalculatorComplex::from(0.2));
58/// ```
59///
60#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
61#[serde(from = "SpinOperatorSerialize")]
62#[serde(into = "SpinOperatorSerialize")]
63pub struct SpinOperator {
64    // The internal HashMap of PauliProducts and coefficients (CalculatorComplex)
65    #[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))]
87///# SpinOperator
88///
89/// This is a representation of sums of pauli products with weightings, in order to build a full hamiltonian.
90struct SpinOperatorSerialize {
91    /// List of all non-zero entries in the SpinOperator in the form (PauliProduct, real part of weight, imaginary part of weight).
92    items: Vec<(PauliProduct, CalculatorFloat, CalculatorFloat)>,
93    /// Minimum struqture version required to de-serialize object
94    _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    // From trait
133    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    // From trait
141    fn iter(&'a self) -> Self::IteratorType {
142        self.internal_map.iter()
143    }
144
145    // From trait
146    fn keys(&'a self) -> Self::KeyIteratorType {
147        self.internal_map.keys()
148    }
149
150    // From trait
151    fn values(&'a self) -> Self::ValueIteratorType {
152        self.internal_map.values()
153    }
154
155    #[cfg(feature = "indexed_map_iterators")]
156    // From trait
157    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    // From trait
163    fn remove(&mut self, key: &Self::Index) -> Option<Self::Value> {
164        self.internal_map.remove(key)
165    }
166
167    // From trait
168    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    /// Overwrites an existing entry or sets a new entry in the SpinOperator with the given (PauliProduct key, CalculatorComplex value) pair.
176    ///
177    /// # Arguments
178    ///
179    /// * `key` - The PauliProduct key to set in the SpinOperator.
180    /// * `value` - The corresponding CalculatorComplex value to set for the key in the SpinOperator.
181    ///
182    /// # Returns
183    ///
184    /// * `Ok(Some(CalculatorComplex))` - The key existed, this is the value it had before it was set with the value input.
185    /// * `Ok(None)` - The key did not exist, it has been set with its corresponding value.
186    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    // From trait
207    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    // From trait
221    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    /// Gets the maximum index of the SpinOperator.
234    ///
235    /// # Returns
236    ///
237    /// * `usize` - The number of spins in the SpinOperator.
238    fn number_spins(&self) -> usize {
239        self.current_number_spins()
240    }
241}
242
243impl ToSparseMatrixOperator<'_> for SpinOperator {}
244impl<'a> ToSparseMatrixSuperOperator<'a> for SpinOperator {
245    // From trait
246    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    // From trait
259    fn unitary_sparse_matrix_coo(&'a self) -> Result<CooSparseMatrix, StruqtureError> {
260        self.sparse_matrix_coo(None)
261    }
262
263    // From trait
264    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
274/// Implements the default function (Default trait) of SpinOperator (an empty SpinOperator).
275///
276impl Default for SpinOperator {
277    fn default() -> Self {
278        Self::new()
279    }
280}
281
282/// Functions for the SpinOperator
283///
284impl SpinOperator {
285    /// Creates a new SpinOperator.
286    ///
287    /// # Returns
288    ///
289    /// * `Self` - The new (empty) SpinOperator.
290    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    /// Creates a new SpinOperator with pre-allocated capacity.
300    ///
301    /// # Arguments
302    ///
303    /// * `capacity` - The pre-allocated capacity of the system.
304    ///
305    /// # Returns
306    ///
307    /// * `Self` - The new (empty) SpinOperator.
308    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    /// Separate self into an operator with the terms of given number of spins and an operator with the remaining operations
318    ///
319    /// # Arguments
320    ///
321    /// * `number_spins` - Number of spins to filter for in the keys.
322    ///
323    /// # Returns
324    ///
325    /// `Ok((separated, remainder))` - Operator with the noise terms where number_spins matches the number of spins the operator product acts on and Operator with all other contributions.
326    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    /// Converts a SpinHamiltonian into a SpinOperator.
345    ///
346    /// # Arguments
347    ///
348    /// * `hamiltonian` - The SpinHamiltonian to convert.
349    ///
350    /// # Returns
351    ///
352    /// * `Self` - The SpinHamiltonian converted into a SpinOperator.
353    ///
354    /// # Panics
355    ///
356    /// * Internal error in add_operator_product.
357    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
369/// Implements the negative sign function of SpinOperator.
370///
371impl ops::Neg for SpinOperator {
372    type Output = SpinOperator;
373    /// Implement minus sign for SpinOperator.
374    ///
375    /// # Returns
376    ///
377    /// * `Self` - The SpinOperator * -1.
378    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
392/// Implements the plus function of SpinOperator by SpinOperator.
393///
394impl<T, V> ops::Add<T> for SpinOperator
395where
396    T: IntoIterator<Item = (PauliProduct, V)>,
397    V: Into<CalculatorComplex>,
398{
399    type Output = Self;
400    /// Implements `+` (add) for two SpinOperators.
401    ///
402    /// # Arguments
403    ///
404    /// * `other` - The SpinOperator to be added.
405    ///
406    /// # Returns
407    ///
408    /// * `Self` - The two SpinOperators added together.
409    ///
410    /// # Panics
411    ///
412    /// * Internal error in add_operator_product.
413    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
422/// Implements the minus function of SpinOperator by SpinOperator.
423///
424impl<T, V> ops::Sub<T> for SpinOperator
425where
426    T: IntoIterator<Item = (PauliProduct, V)>,
427    V: Into<CalculatorComplex>,
428{
429    type Output = Self;
430    /// Implements `-` (subtract) for two SpinOperators.
431    ///
432    /// # Arguments
433    ///
434    /// * `other` - The SpinOperator to be subtracted.
435    ///
436    /// # Returns
437    ///
438    /// * `Self` - The two SpinOperators subtracted.
439    ///
440    /// # Panics
441    ///
442    /// * Internal error in add_operator_product.
443    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
452/// Implements the multiplication function of SpinOperator by CalculatorComplex/CalculatorFloat.
453///
454impl<T> ops::Mul<T> for SpinOperator
455where
456    T: Into<CalculatorComplex>,
457{
458    type Output = Self;
459    /// Implement `*` for SpinOperator and CalculatorComplex/CalculatorFloat.
460    ///
461    /// # Arguments
462    ///
463    /// * `other` - The CalculatorComplex or CalculatorFloat by which to multiply.
464    ///
465    /// # Returns
466    ///
467    /// * `Self` - The SpinOperator multiplied by the CalculatorComplex/CalculatorFloat.
468    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
480/// Implements the multiplication function of SpinOperator by SpinOperator.
481///
482impl ops::Mul<SpinOperator> for SpinOperator {
483    type Output = Self;
484    /// Implement `*` for SpinOperator and SpinOperator.
485    ///
486    /// # Arguments
487    ///
488    /// * `other` - The SpinOperator to multiply by.
489    ///
490    /// # Returns
491    ///
492    /// * `Self` - The two SpinOperators multiplied.
493    ///
494    /// # Panics
495    ///
496    /// * Internal error in add_operator_product.
497    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
513/// Implements the multiplication function of SpinOperator by PauliProduct.
514///
515impl ops::Mul<PauliProduct> for SpinOperator {
516    type Output = Self;
517    /// Implement `*` for SpinOperator and PauliProduct.
518    ///
519    /// # Arguments
520    ///
521    /// * `other` - PauliProduct
522    ///
523    /// # Returns
524    ///
525    /// * `Self` - The SpinOperator multiplied by the PauliProduct.
526    ///
527    /// # Panics
528    ///
529    /// * Internal error in add_operator_product.
530    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
543/// Implements the multiplication function of PauliProduct by SpinOperator.
544///
545impl ops::Mul<SpinOperator> for PauliProduct {
546    type Output = SpinOperator;
547    /// Implement `*` for PauliProduct and SpinOperator.
548    ///
549    /// # Arguments
550    ///
551    /// * `other` - The SpinOperator to multiply by.
552    ///
553    /// # Returns
554    ///
555    /// * `Self` - A SpinOperator derived from the PauliProduct, SpinOperator multiplication.
556    ///
557    /// # Panics
558    ///
559    /// * Internal error in add_operator_product.
560    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
573/// Implements the into_iter function (IntoIterator trait) of SpinOperator.
574///
575impl 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    /// Returns the SpinOperator in Iterator form.
582    ///
583    /// # Returns
584    ///
585    /// * `Self::IntoIter` - The SpinOperator in Iterator form.
586    fn into_iter(self) -> Self::IntoIter {
587        self.internal_map.into_iter()
588    }
589}
590
591/// Implements the into_iter function (IntoIterator trait) of reference SpinOperator.
592///
593impl<'a> IntoIterator for &'a SpinOperator {
594    type Item = (&'a PauliProduct, &'a CalculatorComplex);
595    type IntoIter = Iter<'a, PauliProduct, CalculatorComplex>;
596
597    /// Returns the reference SpinOperator in Iterator form.
598    ///
599    /// # Returns
600    ///
601    /// * `Self::IntoIter` - The reference SpinOperator in Iterator form.
602    fn into_iter(self) -> Self::IntoIter {
603        self.internal_map.iter()
604    }
605}
606
607/// Implements the from_iter function (FromIterator trait) of SpinOperator.
608///
609impl FromIterator<(PauliProduct, CalculatorComplex)> for SpinOperator {
610    /// Returns the object in SpinOperator form, from an Iterator form of the object.
611    ///
612    /// # Arguments
613    ///
614    /// * `iter` - The iterator containing the information from which to create the SpinOperator.
615    ///
616    /// # Returns
617    ///
618    /// * `Self::IntoIter` - The iterator in SpinOperator form.
619    ///
620    /// # Panics
621    ///
622    /// * Internal error in add_operator_product.
623    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
633/// Implements the extend function (Extend trait) of SpinOperator.
634///
635impl Extend<(PauliProduct, CalculatorComplex)> for SpinOperator {
636    /// Extends the SpinOperator by the specified operations (in Iterator form).
637    ///
638    /// # Arguments
639    ///
640    /// * `iter` - The iterator containing the operations by which to extend the SpinOperator.
641    ///
642    /// # Panics
643    ///
644    /// * Internal error in add_operator_product.
645    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
653/// Implements the format function (Display trait) of SpinOperator.
654///
655impl fmt::Display for SpinOperator {
656    /// Formats the SpinOperator using the given formatter.
657    ///
658    /// # Arguments
659    ///
660    /// * `f` - The formatter to use.
661    ///
662    /// # Returns
663    ///
664    /// * `std::fmt::Result` - The formatted SpinOperator.
665    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    /// Implements JordanWignerSpinToFermion for a SpinOperator.
680    ///
681    /// The convention used is that |0> represents an empty fermionic state (spin-orbital),
682    /// and |1> represents an occupied fermionic state.
683    ///
684    /// # Returns
685    ///
686    /// `FermionOperator` - The fermionic operator that results from the transformation.
687    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 the Clone and PartialEq traits of SpinOperator
702    #[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 the Clone and PartialEq traits of SpinOperator
719    #[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        // Test Clone trait
731        assert_eq!(sos.clone(), sos);
732
733        // Test PartialEq trait
734        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 the Debug trait of SpinOperator
757    #[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 SpinOperator Serialization and Deserialization traits (readable)
775    #[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 SpinOperator Serialization and Deserialization traits (compact)
817    #[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}