struqture/
lib.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
13// #![deny(missing_docs)]
14// #![warn(private_intra_doc_links)]
15// #![warn(missing_crate_level_docs)]
16// #![warn(missing_doc_code_examples)]
17// #![warn(private_doc_tests)]
18// #![deny(missing_debug_implementations)]
19
20use num_complex::Complex64;
21use qoqo_calculator::CalculatorComplex;
22use qoqo_calculator::CalculatorError;
23use qoqo_calculator::CalculatorFloat;
24use std::collections::HashMap;
25use std::fmt::Debug;
26use std::ops::Add;
27use std::ops::Mul;
28use std::ops::Sub;
29use std::str::FromStr;
30use thiserror::Error;
31
32mod serialisation_meta_information;
33pub use serialisation_meta_information::{
34    check_can_be_deserialised, SerializationSupport, StruqtureSerialisationMeta, StruqtureType,
35};
36
37pub const STRUQTURE_VERSION: &str = env!("CARGO_PKG_VERSION");
38// if it should be up
39pub const CURRENT_STRUQTURE_VERSION: (u32, u32, u32) = (2, 0, 0);
40pub const MINIMUM_STRUQTURE_VERSION: (u32, u32, u32) = (2, 0, 0);
41use tinyvec::TinyVec;
42
43/// Errors that can occur in struqture.
44#[derive(Debug, Error, PartialEq)]
45pub enum StruqtureError {
46    /// Error when using from_str.
47    #[error("The from_str function failed {msg} string")]
48    FromStringFailed { msg: String },
49    /// Error the Pauli matrix set is not in the allowed matrices.
50    #[error("The pauli matrix being set is not in [\"I\", \"X\", \"Y\", \"Z\"] (PauliProduct object), [\"I\", \"X\", \"iY\", \"Z\"] (DecoherenceProduct object) or [\"I\", \"+\", \"-\", \"Z\"] (PlusMinusProduct object): {pauli:?}")]
51    IncorrectPauliEntry {
52        /// Incorrect Pauli matrix trying to be added.
53        pauli: String,
54    },
55    /// Error when adding a key to an object as the index key already exists.
56    #[error("Cannot assign pauli matrix to index {index:?} as it is already occupied")]
57    ProductIndexAlreadyOccupied {
58        /// Index that is occupied.
59        index: usize,
60    },
61    /// Error when the number of subsystems in a mixed system does not match.
62    #[error("Number of subsystems does not match. target: {target_number_spin_subsystems} spin {target_number_boson_subsystems} boson {target_number_fermion_subsystems} fermion; actual: {actual_number_spin_subsystems} spin {actual_number_boson_subsystems} boson {actual_number_fermion_subsystems} fermion ")]
63    MismatchedNumberSubsystems {
64        target_number_spin_subsystems: usize,
65        target_number_boson_subsystems: usize,
66        target_number_fermion_subsystems: usize,
67        actual_number_spin_subsystems: usize,
68        actual_number_boson_subsystems: usize,
69        actual_number_fermion_subsystems: usize,
70    },
71    /// Error when the indices of the object being added are not Normal Ordered.
72    #[error("Indices are not normal ordered: {index_j:?} is larger than index {index_i:?}")]
73    IndicesNotNormalOrdered {
74        /// Index i of the object.
75        index_i: usize,
76        /// Index j of the object.
77        index_j: usize,
78    },
79    /// Error when the indices of the object being added are not Normal Ordered.
80    #[error(
81        "Indices given in either creators or annihilators contain a double index specification"
82    )]
83    IndicesContainDoubles,
84    /// Error when the creator indices of the object being added are not Normal Ordered or contain a double.
85    #[error(
86        "Indices given in creators/annihilators are either not normal ordered, or contain a double index specification"
87    )]
88    IncorrectlyOrderedIndices,
89    /// Error when the minimum index of the creators of the object is larger than the minimum index of the annihilators object.
90    #[error("The minimum index of the creators {creators_min:?} is larger than the minimum index of the annihilators {annihilators_min:?}")]
91    CreatorsAnnihilatorsMinimumIndex {
92        /// Minimum index of the creators.
93        creators_min: Option<usize>,
94        /// Minimum index of the annihilators.
95        annihilators_min: Option<usize>,
96    },
97    /// Error when the key is naturally hermitian (on-diagonal term), but its corresponding value is not real.
98    #[error(
99        "Key is naturally hermitian (on-diagonal term), but its corresponding value is not real."
100    )]
101    NonHermitianOperator,
102    /// Error when parsing from str
103    #[error("Error parsing str into {target_type}: {msg}")]
104    ParsingError { target_type: String, msg: String },
105    /// Error when trying to deserialize struqture data created with an incompatible version of struqture
106    #[error("Version conflict. Data created with struqture version: {data_major_version}.{data_minor_version} could not be deserialized to Library version: {library_major_version}.{library_minor_version}. Please update your libraries to compatible versions or use the data conversion tool.")]
107    VersionMismatch {
108        /// Major version of the library
109        library_major_version: u32,
110        /// Minor version of the library
111        library_minor_version: u32,
112        /// Major version of the data
113        data_major_version: u32,
114        /// Minor version of the data
115        data_minor_version: u32,
116    },
117    #[error("Trying to deserialize a {name_type} with incompatible version of struqture. Library version: {library_major_version}.{library_minor_version} Data version: {data_major_version}.{data_minor_version}.")]
118    NewVersionMismatch {
119        /// Major version of the library
120        library_major_version: u32,
121        /// Minor version of the library
122        library_minor_version: u32,
123        /// Major version of the data
124        data_major_version: u32,
125        /// Minor version of the data
126        data_minor_version: u32,
127        /// Type of object trying to be deserialized.
128        name_type: String,
129    },
130    #[error("Trying to use a struqture {source_type} object as a struqture {target_type} object. Did you maybe pass the wrong object into a function?")]
131    TypeMismatch {
132        /// The type of the data that is deserialized.
133        source_type: String,
134        /// The type of the target that the data is supposed to be deserialized to.
135        target_type: String,
136    },
137    /// Transparent propagation of CalculatorError.
138    #[error(transparent)]
139    CalculatorError(#[from] CalculatorError),
140    /// Error when trying to insert identities into noise operators
141    #[error("Lindblad operators need to be traceless.")]
142    InvalidLindbladTerms,
143    /// Gerneric Error in struqture.
144    #[error("Error occured: {msg}")]
145    GenericError {
146        /// Error message
147        msg: String,
148    },
149}
150
151/// Complex sparse matrix in coordinate (COO) format.
152///
153/// Input in the form (value_vector, (row_index_vector, column_index_vector))
154pub type CooSparseMatrix = (Vec<Complex64>, (Vec<usize>, Vec<usize>));
155
156/// Real sparse matrix in coordinate (COO) format.
157///
158/// Input in the form (value_vector, (row_index_vector, column_index_vector))
159pub type CooSparseMatrixReal = (Vec<f64>, (Vec<usize>, Vec<usize>));
160
161/// Trait for all hermitian indices
162pub trait SymmetricIndex:
163    std::hash::Hash + Eq + Sized + Clone + std::fmt::Debug + std::fmt::Display + FromStr + Default
164{
165    /// Returns the hermitian conjugate of Self and its prefactor.
166    ///
167    /// # Returns
168    ///
169    /// * `(Self, f64)` - The hermitian conjugate of Self and its prefactor.
170    fn hermitian_conjugate(&self) -> (Self, f64);
171
172    /// Returns whether Self is naturally hermitian.
173    ///
174    /// For spin objects, this is true when applying the hermitian conjugation does not change the sign.
175    /// For bosonic and fermionic objects, this is true when creators == annihilators.
176    /// For mixed objects, this is true when all of the spin, bosonic and fermionic parts' `is_naturally_hermitian` functions evaluate to true.
177    ///
178    /// # Returns
179    ///
180    /// * `bool` - Whether Self is naturally hermitian or not.
181    fn is_natural_hermitian(&self) -> bool;
182}
183
184/// Trait for all index types requires converting between index types
185pub trait SpinIndex:
186    SymmetricIndex
187    + std::hash::Hash
188    + Eq
189    + Sized
190    + Clone
191    + std::fmt::Debug
192    + std::fmt::Display
193    + FromStr
194    + Default
195    + serde::Serialize
196where
197    Self::SingleSpinType: Copy,
198{
199    /// Type of operators on single spin in a SpinIndex.
200    ///
201    /// This can either be a [crate::spins::SinglePauliOperator] (`I`, `X`, `Y` or `Z`)
202    /// or a [crate::spins::SingleOperator/Hamiltonian] (`I`, `X`, `iY` or `Z`)
203    type SingleSpinType;
204
205    /// Creates a new Self typed object.
206    ///
207    /// # Returns
208    ///
209    /// * `Self` - The new (empty) instance of type `Self`.
210    fn new() -> Self;
211
212    /// Sets a new entry in Self. This function consumes Self.
213    ///
214    /// # Arguments
215    ///
216    /// * `index` - Index of set object.
217    /// * `pauli` - Value of set object.
218    ///
219    /// # Returns
220    ///
221    /// * `Self` - The entry was correctly set and the new object is returned.
222    fn set_pauli(self, index: usize, pauli: Self::SingleSpinType) -> Self;
223
224    /// Gets the pauli matrix corresponding to the index.
225    ///
226    /// # Arguments
227    ///
228    /// * `index` - The index of qubit to get the pauli matrix for.
229    ///
230    /// # Returns
231    ///
232    /// * `Some(&SingleSpinType)` - The key exists and its corresponding value is returned.
233    /// * `None` - The key does not exist in Self.
234    fn get(&self, index: &usize) -> Option<&Self::SingleSpinType>;
235
236    /// Returns the iterator form of Self.
237    ///
238    /// # Returns
239    ///
240    /// * `Iter<usize, SingleSpinType>` - The iterator form of Self.
241    fn iter(&self) -> std::slice::Iter<'_, (usize, Self::SingleSpinType)>;
242
243    /// Returns maximum index in Self.
244    ///
245    /// # Returns
246    ///
247    /// * `usize` - Maximum index.
248    fn current_number_spins(&self) -> usize {
249        if let Some((max, _)) = self.iter().last() {
250            *max + 1
251        } else {
252            0
253        }
254    }
255
256    /// Returns the length of the SpinIndex object.
257    ///
258    /// # Returns
259    ///
260    /// * `usize` - The length of the SpinIndex object.
261    fn len(&self) -> usize {
262        self.iter().len()
263    }
264
265    /// Returns whether the SpinIndex object is empty or not.
266    ///
267    /// # Returns
268    ///
269    /// * `bool` - Whether the SpinIndex object is empty or not.
270    fn is_empty(&self) -> bool {
271        self.iter().len() == 0
272    }
273
274    /// Remaps the qubits in a clone instance of Self.
275    ///
276    /// # Arguments
277    ///
278    /// * `mapping` - The map containing the {qubit: qubit} mapping to use.
279    ///
280    /// # Returns
281    ///
282    /// * `Self` -  The new object with the qubits remapped from Self.
283    fn remap_qubits(&self, mapping: &HashMap<usize, usize>) -> Self;
284
285    /// Implements multiplication function for a Self typed object by a Self typed object.
286    ///
287    /// # Arguments
288    ///
289    /// * `left` - Left-hand Self typed object to be multiplied.
290    /// * `right` - Right-hand Self typed object to be multiplied.
291    ///
292    /// Returns
293    ///
294    /// * `(Self, Complex64)` - The multiplied objects and the resulting prefactor.
295    fn multiply(left: Self, right: Self) -> (Self, Complex64);
296
297    /// Returns the concatenation of two Self typed objects with no overlapping qubits.
298    ///
299    /// # Arguments
300    ///
301    /// * `other` - The object to concatenate Self with.
302    ///
303    /// Returns
304    ///
305    /// * `Ok(Self)` - The concatenated objects.
306    /// * `Err(StruqtureError::ProductIndexAlreadyOccupied)` - Cannot assign pauli matrix to index as it is already occupied.
307    fn concatenate(&self, other: Self) -> Result<Self, StruqtureError>;
308}
309
310/// Trait for all index types requires converting between index types
311pub trait ModeIndex:
312    SymmetricIndex
313    + std::hash::Hash
314    + Eq
315    + Sized
316    + Clone
317    + std::fmt::Debug
318    + std::fmt::Display
319    + FromStr
320    + Default
321    + serde::Serialize
322{
323    // Document locally
324    fn new(
325        creators: impl IntoIterator<Item = usize>,
326        annihilators: impl IntoIterator<Item = usize>,
327    ) -> Result<Self, StruqtureError>;
328
329    /// Gets the number of creator indices of Self.
330    ///
331    /// # Returns
332    ///
333    /// * `usize` - The number of creator indices in Self.
334    fn number_creators(&self) -> usize {
335        self.creators().len()
336    }
337
338    /// Gets the number of annihilator indices of Self.
339    ///
340    /// # Returns
341    ///
342    /// * `usize` - The number of annihilator indices in Self.
343    fn number_annihilators(&self) -> usize {
344        self.annihilators().len()
345    }
346
347    /// Gets the creator indices of Self.
348    ///
349    /// # Returns
350    ///
351    /// * `Iter<usize>` - The creator indices in Self.
352    fn creators(&self) -> std::slice::Iter<'_, usize>;
353
354    /// Gets the annihilator indices of Self.
355    ///
356    /// # Returns
357    ///
358    /// * `Iter<usize>` - The annihilator indices in Self.
359    fn annihilators(&self) -> std::slice::Iter<'_, usize>;
360
361    // Document locally
362    fn create_valid_pair(
363        creators: impl IntoIterator<Item = usize>,
364        annihilators: impl IntoIterator<Item = usize>,
365        value: CalculatorComplex,
366    ) -> Result<(Self, CalculatorComplex), StruqtureError>;
367
368    /// Returns the maximal number of modes the Index (operator product) acts on.
369    ///
370    /// A ModeIndex acts on a state space of unknown dimension.
371    /// There is only a lower bound of the dimension or number of modes based on the
372    /// maximal mode the product of operators in the index acts on.
373    ///
374    /// For example an index consisting of one creator acting on mode 0 would have
375    /// a current_number_modes of one. An index consisting of one annhihilator acting on 3
376    /// would have current_number_modes of four.
377    fn current_number_modes(&self) -> usize {
378        let max_c = match self.creators().max() {
379            Some(x) => x + 1,
380            None => 0,
381        };
382        let max_a = match self.annihilators().max() {
383            Some(x) => x + 1,
384            None => 0,
385        };
386        max_c.max(max_a)
387    }
388
389    /// Remap modes according to an input dictionary.
390    ///
391    /// # Arguments
392    ///
393    /// `reordering_dictionary` - The dictionary specifying the remapping. It must represent a permutation.
394    ///
395    /// # Returns
396    ///
397    /// `(Self, CalculatorComplex)` - The instance of Self with modes remapped, and the sign resulting from symmetry/antisymmetry.
398    fn remap_modes(
399        &self,
400        reordering_dictionary: &HashMap<usize, usize>,
401    ) -> Result<(Self, CalculatorComplex), StruqtureError> {
402        let mut keys: Vec<usize> = reordering_dictionary.keys().cloned().collect();
403        keys.sort();
404        let mut values: Vec<usize> = reordering_dictionary.values().cloned().collect();
405        values.sort();
406
407        if keys != values {
408            return Err(StruqtureError::GenericError {
409                msg: "Input dictionary must be a permutation.".to_string(),
410            });
411        }
412
413        let mut remapped_creators: Vec<usize> = vec![];
414        let mut remapped_annihilators: Vec<usize> = vec![];
415
416        for creator_index in self.creators() {
417            remapped_creators.push(match reordering_dictionary.get(creator_index) {
418                Some(x) => *x,
419                None => *creator_index,
420            })
421        }
422        for annihilator_index in self.annihilators() {
423            remapped_annihilators.push(match reordering_dictionary.get(annihilator_index) {
424                Some(x) => *x,
425                None => *annihilator_index,
426            })
427        }
428        let (remapped_index, new_coeff) =
429            Self::create_valid_pair(remapped_creators, remapped_annihilators, 1.0.into()).map_err(
430                |_| StruqtureError::GenericError {
431                    msg: "Remapping dictionary should be a permutation of the indices.".to_string(),
432                },
433            )?;
434        Ok((remapped_index, new_coeff))
435    }
436}
437
438/// Trait for transforming value stored at index I when using index of different type T to read out value
439/// e.g. Hermitian Hamiltonian H but we access H[NOIndex(2,1)] -> H[HermitianIndex(1,2)].conj()
440pub trait GetValue<T> {
441    type ValueIn;
442    type ValueOut;
443
444    // Document locally
445    fn get_key(index: &T) -> Self;
446
447    // Document locally
448    fn get_transform(index: &T, value: Self::ValueIn) -> Self::ValueOut;
449}
450
451/// Trait linking indices of lower symmetry to one with higher symmetry
452///
453/// Several indices of a lower symmetry (e.g. normal ordered products of creators and annihilators)
454/// can correspond to the same index of a higher symmetry (e.g. Hermitian products where
455/// a pair of hermitian conjugated products of creators and annihilators is indexed by a single index)
456///
457/// ```
458/// use struqture::prelude::*;
459/// use struqture::CorrespondsTo;
460/// use struqture::bosons::{BosonProduct, HermitianBosonProduct};
461///
462/// let hbp = HermitianBosonProduct::new([0, 2, 4], [1, 3, 5]).unwrap();
463/// let bp_1 = BosonProduct::new([0, 2, 4], [1, 3, 5]).unwrap();
464/// let bp_2 = BosonProduct::new([1, 3, 5], [0, 2, 4]).unwrap();
465/// let bp1_coresponds: HermitianBosonProduct = bp_1.corresponds_to();
466/// let bp2_coresponds: HermitianBosonProduct = bp_2.corresponds_to();
467///
468/// assert_eq!(hbp, bp1_coresponds);
469/// assert_eq!(hbp, bp2_coresponds);
470/// ```
471pub trait CorrespondsTo<T> {
472    // Document locally
473    fn corresponds_to(&self) -> T;
474}
475
476/// Helper trait to allow truncation of values below threshold.
477/// Should eventually be ported to qoqo_calculator like this
478/// and be implemented for CalculatorFloat, CaclulatorComplex, f64 and Complexf64
479pub trait TruncateTrait: Sized {
480    /// Truncates values mapping discarded values to None.
481    ///
482    /// Values with an absolute value under the threshold are mapped to None.
483    /// Values that are not removed are mapped to Some(value).
484    /// For complex values the threshold is applied to real and imaginary part separately.
485    /// All symbolic values are considered to be above the threshold.
486    ///
487    /// # Arguments
488    ///
489    /// * `threshold` - The threshold for inclusion.
490    ///
491    /// # Returns
492    ///
493    /// * `Some(Self)` - The truncated version of Self.
494    /// * `None` - Nothing was left in Self below the threshold.
495    fn truncate(&self, threshold: f64) -> Option<Self>;
496}
497
498impl TruncateTrait for CalculatorComplex {
499    fn truncate(&self, threshold: f64) -> Option<Self> {
500        match (&self.re, &self.im) {
501            (CalculatorFloat::Str(_), _) => Some(self.clone()),
502            (_, CalculatorFloat::Str(_)) => Some(self.clone()),
503            (CalculatorFloat::Float(re), CalculatorFloat::Float(im)) => {
504                let new_re = if re.abs() >= threshold { *re } else { 0.0 };
505                let new_im = if im.abs() >= threshold { *im } else { 0.0 };
506                if Complex64::new(new_re, new_im).norm() >= threshold {
507                    Some(CalculatorComplex::new(new_re, new_im))
508                } else {
509                    None
510                }
511            }
512        }
513    }
514}
515
516impl TruncateTrait for CalculatorFloat {
517    fn truncate(&self, threshold: f64) -> Option<Self> {
518        match &self {
519            CalculatorFloat::Str(_) => Some(self.clone()),
520            CalculatorFloat::Float(f) => {
521                if f.abs() >= threshold {
522                    Some(self.clone())
523                } else {
524                    None
525                }
526            }
527        }
528    }
529}
530
531/// Helper trait to allow hermitian conjugation of values
532/// Should eventually be ported to qoqo_calculator like this
533/// and be implemented for CalculatorFloat, CaclulatorComplex, f64 and Complexf64
534pub trait ConjugationTrait: Sized {
535    /// Conjugates all values in Self.
536    ///
537    /// # Returns
538    ///
539    /// * `Self` - The conjugated version of Self.
540    fn conjugate(&self) -> Self;
541}
542
543impl ConjugationTrait for CalculatorComplex {
544    fn conjugate(&self) -> Self {
545        self.conj()
546    }
547}
548
549impl ConjugationTrait for CalculatorFloat {
550    fn conjugate(&self) -> Self {
551        self.clone()
552    }
553}
554
555/// Trait for all objects that can act on a quantum density matrix like a superoperator.
556///
557/// # Example
558/// ```
559/// use qoqo_calculator::CalculatorComplex;
560/// use std::collections::HashMap;
561/// use struqture::prelude::*;
562/// use struqture::spins::{OperateOnSpins, PauliProduct, PauliOperator};
563///
564/// let mut so = PauliOperator::new();
565/// let pp_0z = PauliProduct::new().z(0);
566/// so.add_operator_product(pp_0z.clone(), CalculatorComplex::from(0.2)).unwrap();
567/// let mut mapping: HashMap<PauliProduct, CalculatorComplex> = HashMap::new();
568/// mapping.insert(pp_0z.clone(), CalculatorComplex::from(0.2));
569///
570/// // Functions provided in this :
571/// assert_eq!(so.get(&pp_0z), &CalculatorComplex::from(0.2));
572/// for (item_so, item_map) in so.iter().zip(mapping.iter()) {
573///     assert_eq!(item_so, item_map);
574/// }
575/// for (key_so, key_map) in so.keys().zip(mapping.keys()) {
576///     assert_eq!(key_so, key_map);
577/// }
578/// for (val_so, val_map) in so.values().zip(mapping.values()) {
579///     assert_eq!(val_so, val_map);
580/// }
581/// assert_eq!(so.len(), 1_usize);
582/// assert_eq!(so.is_empty(), false);
583/// ```
584///
585pub trait OperateOnDensityMatrix<'a>:
586    IntoIterator<Item = (Self::Index, Self::Value)>
587    + FromIterator<(Self::Index, Self::Value)>
588    + Extend<(Self::Index, Self::Value)>
589    + PartialEq
590    + Clone
591    + Mul<CalculatorFloat>
592    + Mul<CalculatorComplex>
593    + Add
594    + Sub
595    + std::fmt::Display
596    + serde::Serialize
597    + serde::Deserialize<'a>
598where
599    Self: 'a,
600    &'a Self: IntoIterator,
601    Self::Index: Clone,
602    Self::Value: Mul<f64, Output = Self::Value>,
603    Self::Value: Add<Self::Value, Output = Self::Value>,
604    Self::Value: Clone,
605    Self::Value: TruncateTrait,
606{
607    type Index;
608    type Value;
609
610    /// Gets the Self::Value typed coefficient corresponding to the key.
611    ///
612    /// # Arguments
613    ///
614    /// * `key` - The Self::Index key for which to retrieve the value.
615    ///
616    /// # Returns
617    ///
618    /// *  Value at key (or 0.0).
619    fn get(&self, key: &Self::Index) -> &Self::Value;
620
621    /// Returns the iterator form of Self.
622    ///
623    /// # Returns
624    ///
625    /// * `Iter<'_, Self::Index, Self::Value>` - Self in iterator form.
626    fn iter(&'a self) -> impl ExactSizeIterator<Item = (&'a Self::Index, &'a Self::Value)>;
627
628    /// Returns the unsorted keys in Self.
629    ///
630    /// # Returns
631    ///
632    /// * `Keys<'_, Self::Index, Self::Value>` - The sequence of keys of Self.
633    fn keys(&'a self) -> impl ExactSizeIterator<Item = &'a Self::Index>;
634
635    /// Returns the unsorted values in Self.
636    ///
637    /// # Returns
638    ///
639    /// * `Values<'_, Self::Index, Self::Value>` - The sequence of values of Self.
640    fn values(&'a self) -> impl ExactSizeIterator<Item = &'a Self::Value>;
641
642    /// Returns number of entries in object.
643    ///
644    /// # Returns
645    ///
646    /// * `usize` - The length of the object's internal_map.
647    fn len(&'a self) -> usize {
648        self.iter().len()
649    }
650
651    /// Returns true if object contains no values.
652    ///
653    /// # Returns
654    ///
655    /// * `bool` - Whether the object is empty or not.
656    fn is_empty(&'a self) -> bool {
657        self.len() == 0
658    }
659
660    /// Removes the value of the Self::Index object key.
661    ///
662    /// # Arguments
663    ///
664    /// * `key` - The Self::Index object to remove from Self.
665    ///
666    /// # Returns
667    ///
668    /// * `Some(Self::Value)` - Key existed, this is the value it had before it was removed.
669    /// * `None` - Key did not exist.
670    fn remove(&mut self, key: &Self::Index) -> Option<Self::Value>;
671
672    /// Returns an instance of Self that has no entries but clones all other properties, with the given capacity.
673    ///
674    /// # Arguments
675    ///
676    /// * `capacity` - The capacity of the object to create.
677    ///
678    /// # Returns
679    ///
680    /// * `Self` - An empty clone with the same properties as Self, with the given capacity.
681    fn empty_clone(&self, capacity: Option<usize>) -> Self;
682
683    // Document locally
684    fn set(
685        &mut self,
686        key: Self::Index,
687        value: Self::Value,
688    ) -> Result<Option<Self::Value>, StruqtureError>;
689
690    // Document locally
691    fn add_operator_product(
692        &mut self,
693        key: Self::Index,
694        value: Self::Value,
695    ) -> Result<(), StruqtureError> {
696        let old = self.get(&key).clone();
697        self.set(key, value + old)?;
698        Ok(())
699    }
700
701    /// Truncates Self by returning a copy without entries under a threshold.
702    ///
703    /// Entries with an absolute value under the threshold are removed from the copy of the object that is returned.
704    /// For entries with complex coefficients the threshold is applied to real and imaginary part separately.
705    /// All symbolic values are considered to be above the threshold.
706    ///
707    /// # Arguments
708    ///
709    /// * `threshold` - The threshold for inclusion.
710    ///
711    /// # Returns
712    ///
713    /// * `Self` - The truncated version of Self.
714    fn truncate(&'a self, threshold: f64) -> Self {
715        let mut new_self = self.empty_clone(Some(self.len()));
716        new_self.extend(self.iter().filter_map(|(k, v)| {
717            v.truncate(threshold)
718                .map(|v_truncated| (k.clone(), v_truncated))
719        }));
720        new_self
721    }
722}
723
724/// Trait for representing complete open systems
725pub trait OpenSystem<'a>:
726    Add + Sub + PartialEq + Clone + std::fmt::Display + serde::Serialize + serde::Deserialize<'a>
727where
728    Self::System: OperateOnState<'a>,
729    Self::System: 'a,
730    &'a Self::System: IntoIterator,
731    <<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Index: Clone,
732    <<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Index: SymmetricIndex,
733    <<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Value:
734        Mul<f64, Output = <<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Value>,
735    <<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Value: Add<
736        <<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Value,
737        Output = <<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Value,
738    >,
739    <<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Value: Clone,
740    <<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Value: TruncateTrait,
741    <<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Value: ConjugationTrait,
742    // <<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::IteratorType:
743    //     ExactSizeIterator<
744    //         Item = (
745    //             &'a <<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Index,
746    //             &'a <<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Value,
747    //         ),
748    //     >,
749    // <<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::KeyIteratorType:
750    //     ExactSizeIterator<
751    //         Item = &'a <<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Index,
752    //     >,
753    // <<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::ValueIteratorType:
754    //     ExactSizeIterator<
755    //         Item = &'a <<Self as OpenSystem<'a>>::System as OperateOnDensityMatrix<'a>>::Value,
756    //     >,
757    Self::Noise: OperateOnDensityMatrix<'a>,
758    Self::Noise: 'a,
759    &'a Self::Noise: IntoIterator,
760    <<Self as OpenSystem<'a>>::Noise as OperateOnDensityMatrix<'a>>::Index: Clone,
761    <<Self as OpenSystem<'a>>::Noise as OperateOnDensityMatrix<'a>>::Value:
762        Mul<f64, Output = <<Self as OpenSystem<'a>>::Noise as OperateOnDensityMatrix<'a>>::Value>,
763    <<Self as OpenSystem<'a>>::Noise as OperateOnDensityMatrix<'a>>::Value: Add<
764        <<Self as OpenSystem<'a>>::Noise as OperateOnDensityMatrix<'a>>::Value,
765        Output = <<Self as OpenSystem<'a>>::Noise as OperateOnDensityMatrix<'a>>::Value,
766    >,
767    <<Self as OpenSystem<'a>>::Noise as OperateOnDensityMatrix<'a>>::Value: Clone,
768    <<Self as OpenSystem<'a>>::Noise as OperateOnDensityMatrix<'a>>::Value: TruncateTrait,
769    <<Self as OpenSystem<'a>>::Noise as OperateOnDensityMatrix<'a>>::Value: ConjugationTrait,
770{
771    type System;
772    type Noise;
773
774    /// Returns the Self::Noise of the OpenSystem object.
775    ///
776    /// # Returns
777    ///
778    /// * `&Self::Noise` - The Self::Noise of the OpenSystem object.
779    fn noise(&self) -> &Self::Noise;
780
781    /// Returns the Self::System of the OpenSystem object.
782    ///
783    /// # Returns
784    ///
785    /// * `&Self::System` - The Self::System of the OpenSystem object.
786    fn system(&self) -> &Self::System;
787
788    /// Returns a mutable reference of the Self::Noise of the OpenSystem object.
789    ///
790    /// # Returns
791    ///
792    /// * `&Self::Noise` - The Self::Noise of the OpenSystem object.
793    fn noise_mut(&mut self) -> &mut Self::Noise;
794
795    /// Returns a mutable reference of the Self::System of the OpenSystem object.
796    ///
797    /// # Returns
798    ///
799    /// * `&Self::System` - The Self::System of the OpenSystem object.
800    fn system_mut(&mut self) -> &mut Self::System;
801
802    /// Returns a tuple of the system (Self::System) and the noise (Self::Noise) of the OpenSystem.
803    ///
804    /// # Returns
805    ///
806    /// * `(Self::System, Self::Noise)` - The system and noise of the OpenSystem.
807    fn ungroup(self) -> (Self::System, Self::Noise);
808
809    // Document locally
810    fn group(system: Self::System, noise: Self::Noise) -> Result<Self, StruqtureError>;
811
812    /// Returns an instance of Self that has no entries but clones all other properties, with the given capacity.
813    ///
814    /// # Arguments
815    ///
816    /// * `capacity` - The capacity of the object to create.
817    ///
818    /// # Returns
819    ///
820    /// * `Self` - An empty clone with the same properties as Self, with the given capacity.
821    fn empty_clone(&self) -> Self;
822
823    /// Truncates Self by returning a copy without entries under a threshold.
824    ///
825    /// Entries with an absolute value under the threshold are removed from the copy of the object that is returned.
826    /// For entries with complex coefficients the threshold is applied to real and imaginary part separately.
827    /// All symbolic values are considered to be above the threshold.
828    ///
829    /// # Arguments
830    ///
831    /// * `threshold` - The threshold for inclusion.
832    ///
833    /// # Returns
834    ///
835    /// * `Self` - The truncated version of Self.
836    fn truncate(&'a self, threshold: f64) -> Self {
837        let new_system = self.system().truncate(threshold);
838        let new_noise = self.noise().truncate(threshold);
839        Self::group(new_system, new_noise)
840            .expect("Internal error: System and Noise size unexpectedly do not match")
841    }
842}
843
844/// Trait for all objects that can act on a quantum state like an operator.
845///
846/// # Example
847/// ```
848/// use qoqo_calculator::CalculatorComplex;
849/// use std::collections::HashMap;
850/// use struqture::prelude::*;
851/// use struqture::spins::{OperateOnSpins, PauliProduct, PauliOperator};
852///
853/// let mut so = PauliOperator::new();
854/// let pp_0z = PauliProduct::new().z(0);
855/// so.add_operator_product(pp_0z.clone(), CalculatorComplex::from(0.2)).unwrap();
856///
857/// // Functions provided in this :
858/// assert_eq!(so.hermitian_conjugate(), so);
859/// ```
860///
861pub trait OperateOnState<'a>:
862    OperateOnDensityMatrix<'a>
863    + IntoIterator<Item = (Self::Index, Self::Value)>
864    + FromIterator<(Self::Index, Self::Value)>
865    + Extend<(Self::Index, Self::Value)>
866    + PartialEq
867    + Clone
868    + Mul<CalculatorFloat>
869    + Mul<CalculatorComplex>
870    + Add
871    + Sub
872where
873    Self: 'a,
874    &'a Self: IntoIterator,
875    Self::Index: Clone,
876    Self::Index: SymmetricIndex,
877    Self::Value: Mul<f64, Output = Self::Value>,
878    Self::Value: Add<Self::Value, Output = Self::Value>,
879    Self::Value: Clone,
880    Self::Value: TruncateTrait,
881    Self::Value: ConjugationTrait,
882{
883    /// Returns the hermitian conjugate of Self.
884    ///
885    /// # Returns
886    ///
887    /// * `Self` - The hermitian conjugate of Self.
888    fn hermitian_conjugate(&'a self) -> Self {
889        let mut new_self = self.empty_clone(Some(self.len()));
890        new_self.extend(self.iter().map(|(k, v)| {
891            let (new_key, conjugation_prefactor) = k.hermitian_conjugate();
892            (new_key, v.conjugate() * conjugation_prefactor)
893        }));
894        new_self
895    }
896}
897
898/// Trait for bosonic or fermionic modes.
899///
900/// # Example
901/// ```
902/// use qoqo_calculator::CalculatorComplex;
903/// use struqture::prelude::*;
904/// use std::collections::HashMap;
905/// use struqture::bosons::{HermitianBosonProduct, BosonHamiltonian};
906///
907/// let mut sh = BosonHamiltonian::new();
908///
909/// // Functions provided in this :
910/// assert_eq!(sh.current_number_modes(), 0);
911///
912/// let pp_0z = HermitianBosonProduct::new([0], [0]).unwrap();
913/// sh.add_operator_product(pp_0z.clone(), CalculatorComplex::from(0.2)).unwrap();
914///
915/// assert_eq!(sh.current_number_modes(), 1);
916/// ```
917///
918pub trait OperateOnModes<'a>: PartialEq + Clone + Mul<CalculatorFloat> + Add + Sub {
919    // Document locally
920    fn current_number_modes(&'a self) -> usize;
921}
922
923/// Shorthand type notation for a tuple of lists of indices of creators and annihilators
924type CreatorsAnnihilators = (TinyVec<[usize; 2]>, TinyVec<[usize; 2]>);
925
926pub mod bosons;
927pub mod fermions;
928pub mod mappings;
929pub mod mixed_systems;
930pub mod prelude;
931pub mod spins;
932
933/// Shorhand type for TinyVec representation of creators or annihilators
934#[cfg(test)]
935type ModeTinyVec = TinyVec<[usize; 2]>;