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}