struqture/spins/
decoherence_product.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 crate::fermions::FermionOperator;
14use crate::mappings::JordanWignerSpinToFermion;
15use crate::spins::SinglePauliOperator;
16use crate::{CooSparseMatrix, CorrespondsTo, GetValue, SpinIndex, StruqtureError, SymmetricIndex};
17use num_complex::Complex64;
18use qoqo_calculator::CalculatorComplex;
19use serde::de::{Deserializer, Error, SeqAccess, Visitor};
20use serde::ser::{SerializeSeq, Serializer};
21use serde::{Deserialize, Serialize};
22use std::cmp::{self, Ordering};
23use std::collections::HashMap;
24use std::fmt;
25use std::hash::Hash;
26use std::iter::{FromIterator, IntoIterator};
27use std::ops::Mul;
28use std::str::FromStr;
29use tinyvec::{TinyVec, TinyVecIterator};
30
31use super::PauliProduct;
32
33/// Single Decoherence operators for DecoherenceProducts:
34///
35/// I: identity matrix
36/// $$
37/// \begin{pmatrix}
38/// 1 & 0\\\\
39/// 0 & 1
40/// \end{pmatrix}
41/// $$
42///
43/// X: pauli X matrix
44/// $$
45/// \begin{pmatrix}
46/// 0 & 1\\\\
47/// 1 & 0
48/// \end{pmatrix}
49/// $$
50///
51/// iY: pauli iY matrix
52/// $$
53/// \begin{pmatrix}
54/// 0 & 1 \\\\
55/// -1 & 0
56/// \end{pmatrix}
57/// $$
58///
59/// Z: pauli z matrix
60/// $$
61/// \begin{pmatrix}
62/// 1 & 0\\\\
63/// 0 & -1
64/// \end{pmatrix}
65/// $$
66///
67#[derive(
68    Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default,
69)]
70#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
71pub enum SingleDecoherenceOperator {
72    #[default]
73    Identity,
74    X,
75    IY,
76    Z,
77}
78
79/// Creates a SingleDecoherenceOperator from an &str representation.
80///
81/// # Arguments
82///
83/// * `s` - The string (&str) to be converted to a SingleDecoherenceOperator.
84///
85/// # Returns
86///
87/// * `Ok(Self)` - The SingleDecoherenceOperator of the input string.
88/// * `Err(StruqtureError::IncorrectPauliEntry)` - The pauli matrix being set is not in [\"I\", \"X\", \"iY\", \"Z\"].
89///
90impl FromStr for SingleDecoherenceOperator {
91    type Err = StruqtureError;
92    fn from_str(s: &str) -> Result<Self, Self::Err> {
93        match s {
94            "I" => Ok(SingleDecoherenceOperator::Identity),
95            "X" => Ok(SingleDecoherenceOperator::X),
96            "iY" => Ok(SingleDecoherenceOperator::IY),
97            "Z" => Ok(SingleDecoherenceOperator::Z),
98            _ => Err(StruqtureError::IncorrectPauliEntry {
99                pauli: s.to_string(),
100            }),
101        }
102    }
103}
104
105/// Implements the fmt function (Display trait) of SingleDecoherenceOperator.
106///
107impl fmt::Display for SingleDecoherenceOperator {
108    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109        match self {
110            SingleDecoherenceOperator::Identity => write!(f, "I"),
111            SingleDecoherenceOperator::X => write!(f, "X"),
112            SingleDecoherenceOperator::IY => write!(f, "iY"),
113            SingleDecoherenceOperator::Z => write!(f, "Z"),
114        }
115    }
116}
117
118/// Functions for the SingleDecoherenceOperator
119///
120impl SingleDecoherenceOperator {
121    /// Implements multiplication function for a SingleDecoherenceOperator by a SingleDecoherenceOperator.
122    ///
123    /// # Arguments
124    ///
125    /// * `left` - Left-hand SingleDecoherenceOperator to be multiplied.
126    /// * `right` - Right-hand SingleDecoherenceOperator to be multiplied.
127    pub fn multiply(
128        left: SingleDecoherenceOperator,
129        right: SingleDecoherenceOperator,
130    ) -> (Self, f64) {
131        let result_vec: (SingleDecoherenceOperator, f64) = match (left, right) {
132            (SingleDecoherenceOperator::Identity, x) => (x, 1.0),
133            (x, SingleDecoherenceOperator::Identity) => (x, 1.0),
134            (SingleDecoherenceOperator::X, SingleDecoherenceOperator::X) => {
135                (SingleDecoherenceOperator::Identity, 1.0)
136            }
137            (SingleDecoherenceOperator::X, SingleDecoherenceOperator::IY) => {
138                (SingleDecoherenceOperator::Z, -1.0)
139            }
140            (SingleDecoherenceOperator::X, SingleDecoherenceOperator::Z) => {
141                (SingleDecoherenceOperator::IY, -1.0)
142            }
143            (SingleDecoherenceOperator::IY, SingleDecoherenceOperator::X) => {
144                (SingleDecoherenceOperator::Z, 1.0)
145            }
146            (SingleDecoherenceOperator::IY, SingleDecoherenceOperator::IY) => {
147                (SingleDecoherenceOperator::Identity, -1.0)
148            }
149            (SingleDecoherenceOperator::IY, SingleDecoherenceOperator::Z) => {
150                (SingleDecoherenceOperator::X, -1.0)
151            }
152            (SingleDecoherenceOperator::Z, SingleDecoherenceOperator::X) => {
153                (SingleDecoherenceOperator::IY, 1.0)
154            }
155            (SingleDecoherenceOperator::Z, SingleDecoherenceOperator::IY) => {
156                (SingleDecoherenceOperator::X, 1.0)
157            }
158            (SingleDecoherenceOperator::Z, SingleDecoherenceOperator::Z) => {
159                (SingleDecoherenceOperator::Identity, 1.0)
160            }
161        };
162        result_vec
163    }
164
165    /// Conversion function from SingleDecoherenceOperator to SinglePauliOperator.
166    ///
167    /// # Arguments
168    ///
169    /// * `decoherence` - SingleDecoherenceOperator to convert to SingleDecoherenceOperator type.
170    ///
171    /// # Returns
172    ///
173    /// * `Vec<(SingleDecoherenceOperator, Complex64)>` - Vector of tuples of SingleDecoherenceOperator with a corresponding Complex64 coefficient.
174    pub fn decoherence_to_spin(
175        decoherence: SingleDecoherenceOperator,
176    ) -> (SinglePauliOperator, Complex64) {
177        match decoherence {
178            SingleDecoherenceOperator::Identity => {
179                (SinglePauliOperator::Identity, Complex64::new(1.0, 0.0))
180            }
181            SingleDecoherenceOperator::X => (SinglePauliOperator::X, Complex64::new(1.0, 0.0)),
182            SingleDecoherenceOperator::IY => (SinglePauliOperator::Y, Complex64::new(0.0, 1.0)),
183            SingleDecoherenceOperator::Z => (SinglePauliOperator::Z, Complex64::new(1.0, 0.0)),
184        }
185    }
186
187    /// Conversion function from SinglePauliOperator to SingleDecoherenceOperator.
188    ///
189    /// # Arguments
190    ///
191    /// * `spin` - SingleDecoherenceOperator to convert to SingleDecoherenceOperator type.
192    ///
193    /// # Returns
194    ///
195    /// * `Vec<(SingleDecoherenceOperator, Complex64)>` - Vector of tuples of SingleDecoherenceOperator with a corresponding Complex64 coefficient.
196    pub fn spin_to_decoherence(
197        spin: SinglePauliOperator,
198    ) -> (SingleDecoherenceOperator, Complex64) {
199        match spin {
200            SinglePauliOperator::Identity => (
201                SingleDecoherenceOperator::Identity,
202                Complex64::new(1.0, 0.0),
203            ),
204            SinglePauliOperator::X => (SingleDecoherenceOperator::X, Complex64::new(1.0, 0.0)),
205            SinglePauliOperator::Y => (SingleDecoherenceOperator::IY, Complex64::new(0.0, -1.0)),
206            SinglePauliOperator::Z => (SingleDecoherenceOperator::Z, Complex64::new(1.0, 0.0)),
207        }
208    }
209
210    /// Returns the hermitian conjugate of the DecoherenceOperator.
211    ///
212    /// # Returns
213    ///
214    /// `(SingleDecoherenceOperator, f64)` - Tuple of conjugated SingleDecoherenceOperator and float prefactor due to conjugation
215    ///                                      (SingleDecoherenceOperator::Minus picks up a minus sign).
216    pub fn hermitian_conjugate(&self) -> (Self, f64) {
217        match self {
218            SingleDecoherenceOperator::Identity => (SingleDecoherenceOperator::Identity, 1.0),
219            SingleDecoherenceOperator::Z => (SingleDecoherenceOperator::Z, 1.0),
220            SingleDecoherenceOperator::X => (SingleDecoherenceOperator::X, 1.0),
221            SingleDecoherenceOperator::IY => (SingleDecoherenceOperator::IY, -1.0),
222        }
223    }
224}
225
226/// DecoherenceProducts are combinations of SingleDecoherenceOperators on specific qubits.
227///
228/// This is a representation of products of decoherence matrices acting on qubits, in order to build the terms of a hamiltonian.
229/// For instance, to represent the term $ \sigma_0^{x} \sigma_2^{z} $ :
230/// ` DecoherenceProduct::new().x(0).z(2) `
231///
232/// # Example
233///
234/// ```
235/// use struqture::prelude::*;
236/// use struqture::spins::{DecoherenceProduct, SingleDecoherenceOperator};
237///
238/// let mut dp = DecoherenceProduct::new();
239///
240/// // Method 1 to add to DecoherenceProduct:
241/// dp = dp.set_pauli(0, SingleDecoherenceOperator::X);
242/// // Method 2 to add to DecoherenceProduct
243/// dp = dp.z(1);
244/// // These methods are equal:
245/// assert_eq!(dp.clone().x(2), dp.clone().set_pauli(2, SingleDecoherenceOperator::X));
246///
247/// // Access what you set:
248/// assert_eq!(dp.get(&0).unwrap(), &SingleDecoherenceOperator::X);
249/// ```
250///
251#[derive(Debug, Clone, Hash, PartialEq, Eq)]
252pub struct DecoherenceProduct {
253    /// The internal dictionary of pauli matrices (I, X, Y, Z) and qubits
254    items: TinyVec<[(usize, SingleDecoherenceOperator); 5]>,
255}
256
257#[cfg(feature = "json_schema")]
258impl schemars::JsonSchema for DecoherenceProduct {
259    fn schema_name() -> std::borrow::Cow<'static, str> {
260        "struqture::spins::DecoherenceProduct".into()
261    }
262
263    fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
264        schemars::json_schema!({
265            "type": "string",
266            "description": "Represents products of Decoherence Operators (X, iY, Z) by a string of spin numbers followed by pauli operators. E.g. 0X10iY13Z14X."
267        })
268    }
269}
270
271impl crate::SerializationSupport for DecoherenceProduct {
272    fn struqture_type() -> crate::StruqtureType {
273        crate::StruqtureType::DecoherenceProduct
274    }
275}
276
277/// Implementing serde serialization writing directly to string.
278///
279impl Serialize for DecoherenceProduct {
280    /// Serialization function for DecoherenceProduct according to string type.
281    ///
282    /// # Arguments
283    ///
284    /// * `self` - DecoherenceProduct to be serialized.
285    /// * `serializer` - Serializer used for serialization.
286    ///
287    /// # Returns
288    ///
289    /// `S::Ok` - Serialized instance of DecoherenceProduct.
290    /// `S::Error` - Error in the serialization process.
291    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
292    where
293        S: Serializer,
294    {
295        let readable = serializer.is_human_readable();
296        if readable {
297            serializer.serialize_str(&self.to_string())
298        } else {
299            let mut sequence = serializer.serialize_seq(Some(self.items.len()))?;
300            for item in self.items.iter() {
301                sequence.serialize_element(item)?;
302            }
303            sequence.end()
304        }
305    }
306}
307
308/// Deserializing directly from string.
309///
310impl<'de> Deserialize<'de> for DecoherenceProduct {
311    /// Deserialization function for DecoherenceProduct.
312    ///
313    /// # Arguments
314    ///
315    /// * `self` - Serialized instance of DecoherenceProduct to be deserialized.
316    /// * `deserializer` - Deserializer used for deserialization.
317    ///
318    /// # Returns
319    ///
320    /// `DecoherenceProduct` - Deserialized instance of DecoherenceProduct.
321    /// `D::Error` - Error in the deserialization process.
322    fn deserialize<D>(deserializer: D) -> Result<DecoherenceProduct, D::Error>
323    where
324        D: Deserializer<'de>,
325    {
326        let human_readable = deserializer.is_human_readable();
327        if human_readable {
328            struct TemporaryVisitor;
329            impl<'de> Visitor<'de> for TemporaryVisitor {
330                type Value = DecoherenceProduct;
331
332                fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
333                    formatter.write_str("String")
334                }
335
336                fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
337                where
338                    E: serde::de::Error,
339                {
340                    DecoherenceProduct::from_str(v).map_err(|err| E::custom(format!("{err:?}")))
341                }
342
343                fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
344                where
345                    E: Error,
346                {
347                    DecoherenceProduct::from_str(v).map_err(|err| E::custom(format!("{err:?}")))
348                }
349            }
350
351            deserializer.deserialize_str(TemporaryVisitor)
352        } else {
353            struct DecoherenceProductVisitor;
354            impl<'de> serde::de::Visitor<'de> for DecoherenceProductVisitor {
355                type Value = DecoherenceProduct;
356                fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
357                    fmt::Formatter::write_str(formatter, "Identifier of DecoherenceProduct variant")
358                }
359                // when variants are marked by String values
360                fn visit_seq<M>(self, mut access: M) -> Result<Self::Value, M::Error>
361                where
362                    M: SeqAccess<'de>,
363                {
364                    let mut pp = DecoherenceProduct::new();
365                    while let Some(item) = access.next_element()? {
366                        let entry: Entry = item;
367                        pp = pp.set_pauli(entry.0 .0, entry.0 .1);
368                    }
369                    Ok(pp)
370                }
371            }
372            #[derive(Deserialize)]
373            #[serde(transparent)]
374            struct Entry((usize, SingleDecoherenceOperator));
375            let pp_visitor = DecoherenceProductVisitor;
376
377            deserializer.deserialize_seq(pp_visitor)
378        }
379    }
380}
381
382impl SpinIndex for DecoherenceProduct {
383    type SingleSpinType = SingleDecoherenceOperator;
384
385    // From trait
386    fn new() -> Self {
387        DecoherenceProduct {
388            items: TinyVec::<[(usize, SingleDecoherenceOperator); 5]>::with_capacity(5),
389        }
390    }
391
392    // From trait
393    fn set_pauli(self, index: usize, pauli: SingleDecoherenceOperator) -> Self {
394        let mut pp = self;
395        if let Some((vecindex, insertindex, index_in_use)) =
396            pp.items
397                .iter()
398                .enumerate()
399                .find_map(|(vecindex, (innerindex, _))| {
400                    if innerindex >= &index {
401                        Some((vecindex, *innerindex, innerindex == &index))
402                    } else {
403                        None
404                    }
405                })
406        {
407            if index_in_use {
408                match pauli {
409                    SingleDecoherenceOperator::Identity => {
410                        let _x = pp.items.remove(vecindex);
411                    }
412                    _ => pp.items[vecindex] = (insertindex, pauli),
413                }
414            } else {
415                match pauli {
416                    SingleDecoherenceOperator::Identity => (),
417                    _ => {
418                        pp.items.insert(vecindex, (index, pauli));
419                    }
420                }
421            }
422        } else {
423            match pauli {
424                SingleDecoherenceOperator::Identity => (),
425                _ => {
426                    pp.items.push((index, pauli));
427                }
428            }
429        }
430        pp
431    }
432
433    // From trait
434    fn get(&self, index: &usize) -> Option<&SingleDecoherenceOperator> {
435        self.items
436            .iter()
437            .find_map(|(key, value)| if key == index { Some(value) } else { None })
438    }
439
440    // From trait
441    fn iter(&self) -> std::slice::Iter<'_, (usize, SingleDecoherenceOperator)> {
442        self.items.iter()
443    }
444
445    // From trait
446    fn remap_qubits(&self, mapping: &HashMap<usize, usize>) -> DecoherenceProduct {
447        let mut mutable_internal: TinyVec<[(usize, SingleDecoherenceOperator); 5]> =
448            TinyVec::<[(usize, SingleDecoherenceOperator); 5]>::with_capacity(10);
449
450        for (key, val) in self.iter() {
451            mutable_internal.push(match mapping.get(key) {
452                Some(x) => (*x, *val),
453                None => (*key, *val),
454            });
455        }
456        mutable_internal.sort_by(|(left_index, _), (right_index, _)| {
457            left_index
458                .partial_cmp(right_index)
459                .expect("Cannot compare two unsigned integers internal error in struqture.spins")
460        });
461        DecoherenceProduct {
462            items: mutable_internal,
463        }
464    }
465
466    // From trait
467    fn multiply(left: DecoherenceProduct, right: DecoherenceProduct) -> (Self, Complex64) {
468        left * right
469    }
470
471    // From trait
472    fn concatenate(&self, other: DecoherenceProduct) -> Result<DecoherenceProduct, StruqtureError> {
473        let mut return_list = self.items.clone();
474        for (key, val) in other.iter() {
475            if return_list.iter().any(|(index, _)| index == key) {
476                return Err(StruqtureError::ProductIndexAlreadyOccupied { index: *key });
477            } else {
478                return_list.push((*key, *val));
479            }
480        }
481        return_list.sort_by(|(left_index, _), (right_index, _)| {
482            left_index
483                .partial_cmp(right_index)
484                .expect("Cannot compare two unsigned integers internal error in struqture.spins")
485        });
486        Ok(DecoherenceProduct { items: return_list })
487    }
488}
489
490/// Implements Ord for DecoherenceProduct; length then lexicographic sorting
491///
492/// Using Rust's "Derived" ordering provides only lexicographical ordering.
493/// Here we explicitly augment this to include the length of the DecoherenceProduct
494/// for comparison. For an example operator set: `[1X, 2iY, 1X2iY, 2X3iY, 1X2X3Z]`,
495/// this would be ordered under this definition. Under the old behaviour this
496/// set would order as: `[1X, 1X2X3Z, 1X2iY, 2X3iY, 2iY]` which is less readable.
497///
498/// # Arguments
499///
500/// * `self` - DecoherenceProduct to be ordered.
501///
502/// # Returns
503///
504/// `Ordering` - The ordering result
505impl Ord for DecoherenceProduct {
506    fn cmp(&self, other: &Self) -> Ordering {
507        let me: &TinyVec<[(usize, SingleDecoherenceOperator); 5]> = &(self.items);
508        let them: &TinyVec<[(usize, SingleDecoherenceOperator); 5]> = &(other.items);
509
510        match me.len().cmp(&them.len()) {
511            Ordering::Less => Ordering::Less,
512            Ordering::Equal => me.cmp(them), // If lengths are equal use lexicographic
513            Ordering::Greater => Ordering::Greater,
514        }
515    }
516}
517
518/// This method returns an ordering between `self` and `other` values if one exists.
519impl PartialOrd for DecoherenceProduct {
520    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
521        Some(self.cmp(other))
522    }
523}
524
525impl CorrespondsTo<DecoherenceProduct> for DecoherenceProduct {
526    /// Gets the DecoherenceProduct corresponding to self (here, itself).
527    ///
528    /// # Returns
529    ///
530    /// * `DecoherenceProduct` - The DecoherenceProduct corresponding to Self.
531    fn corresponds_to(&self) -> DecoherenceProduct {
532        self.clone()
533    }
534}
535
536impl SymmetricIndex for DecoherenceProduct {
537    // From trait
538    fn hermitian_conjugate(&self) -> (Self, f64) {
539        // Due to SingleDecoherenceOperator::Minus hermitian_conjugate can produce a constant prefactor
540        // Hermitian conjugation with a basis of X, iY, Z and I only leads to a sign change for iY and leaves everything else as is
541        // if number of iY is even (% 2 == 0) do nothing if odd (%2 == 1) change sign
542        let change_sign: bool = self
543            .items
544            .iter()
545            .map(|(_, b)| match *b {
546                SingleDecoherenceOperator::IY => 1_usize,
547                _ => 0_usize,
548            })
549            .sum::<usize>()
550            % 2
551            == 1;
552        (
553            Self {
554                items: self.items.clone(),
555            },
556            if change_sign { -1_f64 } else { 1_f64 },
557        )
558    }
559
560    // From trait
561    fn is_natural_hermitian(&self) -> bool {
562        let (_, a) = self.hermitian_conjugate();
563        // Return true when applying the hermitian conjugation
564        // does not change the sign.
565        a > 0.0
566    }
567}
568
569/// Implements the multiplication function of DecoherenceProduct by DecoherenceProduct.
570///
571impl Mul<DecoherenceProduct> for DecoherenceProduct {
572    type Output = (Self, Complex64);
573    /// Implement `*` for DecoherenceProduct and DecoherenceProduct.
574    ///
575    /// # Arguments
576    ///
577    /// * `other` - The DecoherenceProduct to multiply by.
578    ///
579    /// # Returns
580    ///
581    /// * `(Self, Complex64)` - The two DecoherenceProducts multiplied and the resulting prefactor.
582    ///
583    /// # Panics
584    ///
585    /// * Unexpectedly failed construction of DecoherenceProduct creation internal struqture bug.
586    fn mul(self, rhs: DecoherenceProduct) -> Self::Output {
587        let mut factor = Complex64::new(1.0, 0.0);
588        let mut return_product = DecoherenceProduct::new();
589        for (key, left_operator) in self.clone().into_iter() {
590            match rhs.get(&key) {
591                Some(right_operator) => {
592                    let (tmp_product, tmp_factor) =
593                        SingleDecoherenceOperator::multiply(left_operator, *right_operator);
594                    factor *= tmp_factor;
595                    return_product = return_product.set_pauli(key, tmp_product);
596                }
597                None => {
598                    return_product = return_product.set_pauli(key, left_operator);
599                }
600            }
601        }
602        for (key, right_operator) in rhs
603            .into_iter()
604            .filter(|(key_internal, _)| self.get(key_internal).is_none())
605        {
606            return_product = return_product.set_pauli(key, right_operator);
607        }
608
609        (return_product, factor)
610    }
611}
612
613impl GetValue<DecoherenceProduct> for DecoherenceProduct {
614    type ValueIn = CalculatorComplex;
615    type ValueOut = CalculatorComplex;
616    /// Gets the key corresponding to the input index (here, itself).
617    ///
618    /// # Arguments
619    ///
620    /// * `index` - The index for which to get the corresponding Product.
621    ///
622    /// # Returns
623    ///
624    /// * `Self` - The corresponding DecoherenceProduct.
625    fn get_key(index: &DecoherenceProduct) -> Self {
626        index.clone()
627    }
628
629    /// Gets the transformed value corresponding to the input index and value (here, itself).
630    ///
631    /// # Arguments
632    ///
633    /// * `index` - The index to transform the value by.
634    /// * `value` - The value to be transformed.
635    ///
636    /// # Returns
637    ///
638    /// * `CalculatorComplex` - The transformed value.
639    fn get_transform(
640        _index: &DecoherenceProduct,
641        value: qoqo_calculator::CalculatorComplex,
642    ) -> qoqo_calculator::CalculatorComplex {
643        value
644    }
645}
646
647impl GetValue<(DecoherenceProduct, DecoherenceProduct)>
648    for (DecoherenceProduct, DecoherenceProduct)
649{
650    type ValueIn = CalculatorComplex;
651    type ValueOut = CalculatorComplex;
652
653    /// Gets the key corresponding to the input index (here, itself).
654    ///
655    /// # Arguments
656    ///
657    /// * `index` - The index for which to get the corresponding (DecoherenceProduct, DecoherenceProduct).
658    ///
659    /// # Returns
660    ///
661    /// * `Self` - The corresponding (DecoherenceProduct, DecoherenceProduct).
662    fn get_key(index: &(DecoherenceProduct, DecoherenceProduct)) -> Self {
663        index.clone()
664    }
665
666    /// Gets the transformed value corresponding to the input index and value (here, itself).
667    ///
668    /// # Arguments
669    ///
670    /// * `index` - The index to transform the value by.
671    /// * `value` - The value to be transformed.
672    ///
673    /// # Returns
674    ///
675    /// * `CalculatorComplex` - The transformed value.
676    fn get_transform(
677        _index: &(DecoherenceProduct, DecoherenceProduct),
678        value: qoqo_calculator::CalculatorComplex,
679    ) -> qoqo_calculator::CalculatorComplex {
680        value
681    }
682}
683
684/// Functions for the DecoherenceProduct
685///
686impl DecoherenceProduct {
687    /// Export to struqture_1 format.
688    #[cfg(feature = "struqture_1_export")]
689    pub fn to_struqture_1(&self) -> Result<struqture_1::spins::DecoherenceProduct, StruqtureError> {
690        let self_string = self.to_string();
691        let struqture_1_product = struqture_1::spins::DecoherenceProduct::from_str(&self_string)
692            .map_err(|err| StruqtureError::GenericError {
693                msg: format!("{err}"),
694            })?;
695        Ok(struqture_1_product)
696    }
697
698    /// Export to struqture_1 format.
699    #[cfg(feature = "struqture_1_import")]
700    pub fn from_struqture_1(
701        value: &struqture_1::spins::DecoherenceProduct,
702    ) -> Result<Self, StruqtureError> {
703        let value_string = value.to_string();
704        let decoh_product = Self::from_str(&value_string)?;
705        Ok(decoh_product)
706    }
707
708    /// Sets a new entry for SingleDecoherenceOperator X in the internal dictionary. This function consumes Self.
709    ///
710    /// # Arguments
711    ///
712    /// * `index` - Index of set object.
713    ///
714    /// # Returns
715    ///
716    /// * `Self` - The entry was correctly set and the DecoherenceProduct is returned.
717    pub fn x(self, index: usize) -> Self {
718        self.set_pauli(index, SingleDecoherenceOperator::X)
719    }
720
721    /// Sets a new entry for SingleDecoherenceOperator IY in the internal dictionary. This function consumes Self.
722    ///
723    /// # Arguments
724    ///
725    /// * `index` - Index of set object.
726    ///
727    /// # Returns
728    ///
729    /// * `Self` - The entry was correctly set and the DecoherenceProduct is returned.
730    pub fn iy(self, index: usize) -> Self {
731        self.set_pauli(index, SingleDecoherenceOperator::IY)
732    }
733
734    /// Sets a new entry for SingleDecoherenceOperator Z in the internal dictionary. This function consumes Self.
735    ///
736    /// # Arguments
737    ///
738    /// * `index` - Index of set object.
739    ///
740    /// # Returns
741    ///
742    /// * `Self` - The entry was correctly set and the DecoherenceProduct is returned.
743    pub fn z(self, index: usize) -> Self {
744        self.set_pauli(index, SingleDecoherenceOperator::Z)
745    }
746
747    /// Creates a new DecoherenceProduct with capacity.
748    ///
749    /// # Returns
750    ///
751    /// * `Self` - The new (empty) DecoherenceProduct.
752    pub fn with_capacity(cap: usize) -> Self {
753        DecoherenceProduct {
754            items: TinyVec::<[(usize, SingleDecoherenceOperator); 5]>::with_capacity(cap),
755        }
756    }
757
758    /// Implements COO output for DecoherenceProduct.
759    ///
760    /// Outputs the DecoherenceProduct as a COO matrix in the form (values, (rows, columns))
761    /// where for DecoherenceProduct the values are +/- 1, and the rows and columns are
762    /// usize.
763    ///
764    /// # Returns
765    ///
766    /// `Result<CooSparseMatrix, StruqtureError>` - The COO matrix or an error.
767    pub fn to_coo(&self, number_spins: usize) -> Result<CooSparseMatrix, StruqtureError> {
768        // Note much of this function inherits the form of the functions it was
769        // based on, the code could probably be shortened but lose readibility.
770
771        // Determine length of the decoherence product:
772        let dimension = 2usize.pow(number_spins as u32);
773
774        // Pre allocate all the arrays:
775        let mut values: Vec<Complex64> = Vec::with_capacity(dimension);
776        let mut rows: Vec<usize> = Vec::with_capacity(dimension);
777        let mut columns: Vec<usize> = Vec::with_capacity(dimension);
778
779        for row in 0..dimension {
780            let (col, val) = self.sparse_matrix_entries_on_row(row)?;
781            rows.push(row);
782            columns.push(col);
783            values.push(val);
784        }
785        Ok((values, (rows, columns)))
786    }
787    /// Constructs the sparse matrix entries for one row of the sparse matrix.
788    ///
789    /// # Arguments
790    ///
791    /// * `row` - The row for which to get the entries.
792    ///
793    /// # Returns
794    ///
795    /// * `Ok((usize, Complex64))` - The matrix representation of the DecoherenceProduct.
796    fn sparse_matrix_entries_on_row(
797        &self,
798        row: usize,
799    ) -> Result<(usize, Complex64), StruqtureError> {
800        let mut column = row;
801        let mut prefac: Complex64 = 1.0.into();
802        for (spin_op_index, pauliop) in self.iter() {
803            match *pauliop {
804                SingleDecoherenceOperator::X => {
805                    match row.div_euclid(2usize.pow(*spin_op_index as u32)) % 2 {
806                        0 => column += 2usize.pow(*spin_op_index as u32),
807                        1 => column -= 2usize.pow(*spin_op_index as u32),
808                        _ => panic!("Internal error in constructing matrix"),
809                    }
810                }
811                SingleDecoherenceOperator::IY => {
812                    match row.div_euclid(2usize.pow(*spin_op_index as u32)) % 2 {
813                        0 => {
814                            column += 2usize.pow(*spin_op_index as u32);
815                            prefac *= Complex64::new(1.0, 0.0);
816                        }
817                        1 => {
818                            column -= 2usize.pow(*spin_op_index as u32);
819                            prefac *= Complex64::new(-1.0, 0.0);
820                        }
821                        _ => panic!("Internal error in constructing matrix"),
822                    };
823                }
824                SingleDecoherenceOperator::Z => {
825                    match row.div_euclid(2usize.pow(*spin_op_index as u32)) % 2 {
826                        0 => {
827                            prefac *= Complex64::new(1.0, 0.0);
828                        }
829                        1 => {
830                            prefac *= Complex64::new(-1.0, 0.0);
831                        }
832                        _ => panic!("Internal error in constructing matrix"),
833                    };
834                }
835                SingleDecoherenceOperator::Identity => (),
836            }
837        }
838        Ok((column, prefac))
839    }
840
841    /// Conversion function from DecoherenceProduct to (PauliProduct, Complex64) tuple.
842    ///
843    /// # Arguments
844    ///
845    /// * `dp` - DecoherenceProduct to convert to (PauliProduct, Complex64) tuple.
846    ///
847    /// # Returns
848    ///
849    /// * `(PauliProduct, Complex64)` - Tuple containing corresponding PauliProduct representation and Complex64 coefficient.
850    pub fn decoherence_to_spin(dp: DecoherenceProduct) -> (PauliProduct, Complex64) {
851        // Original coefficient is unity.
852        let mut coeff = Complex64::new(1.0, 0.0);
853
854        // Capacity will be either original 5 or larger
855        let cap = cmp::max(5_usize, dp.len());
856
857        // // Initialize empty pp
858        let mut new_pp = PauliProduct::with_capacity(cap);
859
860        //Go over each site and populate the Pauli and modify the coefficient:
861        for (site, op) in dp {
862            // If any of the operators are iY, we will pick up a new factor of i.
863            let (pp, newcoeff) = SingleDecoherenceOperator::decoherence_to_spin(op);
864            // Update coefficient, could do a match statement, but I don't think this is the biggest issue.
865            coeff *= newcoeff;
866
867            // Set the pauli directly, no checks needed.
868            new_pp = new_pp.set_pauli(site, pp);
869        }
870
871        (new_pp, coeff)
872    }
873
874    /// Conversion function from PauliProduct to (DecoherenceProduct, Complex64) tuple.
875    ///
876    /// # Arguments
877    ///
878    /// * `pp` - PauliProduct to convert to (DecoherenceProduct, Complex64) tuple.
879    ///
880    /// # Returns
881    ///
882    /// * `(PauliProduct, Complex64)` - Tuple containing corresponding PauliProduct representation and Complex64 coefficient.
883    pub fn spin_to_decoherence(pp: PauliProduct) -> (DecoherenceProduct, Complex64) {
884        // Original coefficient is unity.
885        let mut coeff = Complex64::new(1.0, 0.0);
886
887        // Capacity will be either original 5 or larger
888        let cap = cmp::max(5_usize, pp.len());
889
890        // Initialize empty pp with capacity
891        let mut new_dp = DecoherenceProduct::with_capacity(cap);
892
893        //Go over each site and populate the Pauli and modify the coefficient:
894        for (site, op) in pp {
895            // If any of the operators are iY, we will pick up a new factor of i.
896            let (dp, newcoeff) = SingleDecoherenceOperator::spin_to_decoherence(op);
897            // Update coefficient, could do a match statement, but I don't think this is the biggest issue.
898            coeff *= newcoeff;
899
900            // Set the decoherence operator directly, no checks needed.
901            new_dp = new_dp.set_pauli(site, dp);
902        }
903
904        (new_dp, coeff)
905    }
906}
907
908/// Implements the default function (Default trait) of DecoherenceProduct (an empty DecoherenceProduct).
909///
910impl Default for DecoherenceProduct {
911    fn default() -> Self {
912        Self::new()
913    }
914}
915
916impl FromStr for DecoherenceProduct {
917    type Err = StruqtureError;
918    /// Constructs a DecoherenceProduct from a string.
919    ///
920    /// # Arguments
921    ///
922    /// * `s` - The string to convert.
923    ///
924    /// # Returns
925    ///
926    /// * `Ok(Self)` - The successfully converted DecoherenceProduct.
927    /// * `Err(StruqtureError::IncorrectPauliEntry)` - The pauli matrix being set is not in [\"I\", \"X\", \"Y\", \"Z\"].
928    /// * `Err(StruqtureError::FromStringFailed)` - Using {} instead of unsigned integer as spin index.
929    /// * `Err(StruqtureError::FromStringFailed)` - At least one spin index is used more than once.
930    ///
931    /// # Panics
932    ///
933    /// * Cannot compare two unsigned integers internal error in struqture.spins.
934    fn from_str(s: &str) -> Result<Self, Self::Err> {
935        if s == "I" || s.is_empty() {
936            Ok(Self::new()) // If identity it's just an empty decoherence product.
937        } else {
938            if !s.starts_with(char::is_numeric) {
939                return Err(StruqtureError::FromStringFailed {
940                    msg: format!("Missing spin index in the following DecoherenceProduct: {s}"),
941                });
942            }
943
944            let value = s.to_string();
945            let vec_paulis = value.split(char::is_numeric).filter(|s| !s.is_empty());
946            let vec_indices = value.split(char::is_alphabetic).filter(|s| !s.is_empty());
947            let mut internal: TinyVec<[(usize, SingleDecoherenceOperator); 5]> =
948                TinyVec::<[(usize, SingleDecoherenceOperator); 5]>::with_capacity(10);
949            for (index, pauli) in vec_indices.zip(vec_paulis) {
950                match index.parse() {
951                    Ok(num) => {
952                        let spin: SingleDecoherenceOperator =
953                            SingleDecoherenceOperator::from_str(pauli)?;
954                        match spin {
955                            SingleDecoherenceOperator::Identity => (),
956                            _ => {
957                                internal.push((num, spin));
958                            }
959                        }
960                    }
961                    Err(_) => {
962                        return Err(StruqtureError::FromStringFailed {
963                            msg: format!("Using {index} instead of unsigned integer as spin index"),
964                        })
965                    }
966                }
967            }
968            internal.sort_by(|(left_index, _), (right_index, _)| {
969                left_index.partial_cmp(right_index).expect(
970                    "Cannot compare two unsigned integers internal error in struqture.spins",
971                )
972            });
973            Ok(DecoherenceProduct { items: internal })
974        }
975    }
976}
977
978/// Implements the format function (Display trait) of DecoherenceProduct.
979///
980impl fmt::Display for DecoherenceProduct {
981    /// Formats the DecoherenceProduct using the given formatter.
982    ///
983    /// # Arguments
984    ///
985    /// * `f` - The formatter to use.
986    ///
987    /// # Returns
988    ///
989    /// * `std::fmt::Result` - The formatted DecoherenceProduct.
990    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
991        let mut string: String = String::new();
992        if self.items.is_empty() {
993            string.push('I');
994        } else {
995            for (index, pauli) in self.items.iter() {
996                string.push_str(format!("{index}").as_str());
997                string.push_str(format!("{pauli}").as_str());
998            }
999        }
1000        write!(f, "{string}")
1001    }
1002}
1003
1004/// Implements the into_iter function (IntoIterator trait) of DecoherenceProduct.
1005///
1006impl IntoIterator for DecoherenceProduct {
1007    type Item = (usize, SingleDecoherenceOperator);
1008    type IntoIter = TinyVecIterator<[(usize, SingleDecoherenceOperator); 5]>;
1009    /// Returns the DecoherenceProduct in Iterator form.
1010    ///
1011    /// # Returns
1012    ///
1013    /// * `Self::IntoIter` - The DecoherenceProduct in Iterator form.
1014    fn into_iter(self) -> Self::IntoIter {
1015        self.items.into_iter()
1016    }
1017}
1018
1019/// Implements the from_iter function (FromIterator trait) of DecoherenceProduct.
1020///
1021impl FromIterator<(usize, SingleDecoherenceOperator)> for DecoherenceProduct {
1022    /// Returns the object in DecoherenceProduct form, from an Iterator form of the object.
1023    ///
1024    /// # Arguments
1025    ///
1026    /// * `iter` - The iterator containing the information from which to create the DecoherenceProduct.
1027    ///
1028    /// # Returns
1029    ///
1030    /// * `Self::IntoIter` - The iterator in DecoherenceProduct form.
1031    fn from_iter<I: IntoIterator<Item = (usize, SingleDecoherenceOperator)>>(iter: I) -> Self {
1032        let mut pp = DecoherenceProduct::new();
1033        for (index, pauli) in iter {
1034            pp = pp.set_pauli(index, pauli);
1035        }
1036        pp
1037    }
1038}
1039
1040/// Implements the extend function (Extend trait) of DecoherenceProduct.
1041///
1042impl Extend<(usize, SingleDecoherenceOperator)> for DecoherenceProduct {
1043    /// Extends the DecoherenceProduct by the specified operations (in Iterator form).
1044    ///
1045    /// # Arguments
1046    ///
1047    /// * `iter` - The iterator containing the operations by which to extend the DecoherenceProduct.
1048    fn extend<I: IntoIterator<Item = (usize, SingleDecoherenceOperator)>>(&mut self, iter: I) {
1049        let mut pp = self.clone();
1050        for (index, pauli) in iter {
1051            pp = pp.set_pauli(index, pauli);
1052        }
1053        *self = pp;
1054    }
1055}
1056
1057impl JordanWignerSpinToFermion for DecoherenceProduct {
1058    type Output = FermionOperator;
1059
1060    /// Implements JordanWignerSpinToFermion for a DecoherenceProduct.
1061    ///
1062    /// The convention used is that |0> represents an empty fermionic state (spin-orbital),
1063    /// and |1> represents an occupied fermionic state.
1064    ///
1065    /// # Returns
1066    ///
1067    /// `FermionOperator` - The fermionic operator that results from the transformation.
1068    fn jordan_wigner(&self) -> Self::Output {
1069        let pp = DecoherenceProduct::decoherence_to_spin(self.clone());
1070        pp.0.jordan_wigner() * pp.1
1071    }
1072}