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]>;