roqoqo/
circuit.rs

1// Copyright © 2021-2024 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::operations::{
14    Define, InvolveQubits, InvolvedQubits, Operate, Operation, Substitute, SupportedVersion,
15};
16#[cfg(feature = "overrotate")]
17use crate::operations::{Rotate, Rotation};
18use crate::RoqoqoError;
19use crate::RoqoqoVersion;
20#[cfg(feature = "serialize")]
21use crate::RoqoqoVersionSerializable;
22use qoqo_calculator::Calculator;
23use std::collections::{HashMap, HashSet};
24#[cfg(feature = "overrotate")]
25use std::convert::TryFrom;
26use std::ops;
27use std::{
28    fmt::{Display, Formatter, Write},
29    iter::{FromIterator, IntoIterator},
30};
31
32/// Represents a quantum circuit in roqoqo.
33///
34/// In roqoqo, single operations are collected in a circuit to build up a quantum program.
35/// Roqoqo circuits are strictly linear sequences of operations.
36/// The circuit struct behaves similar to a list and provides several standard
37/// functions of a Vec<Operation>, such as len(), is_empty(), get(), iter() and into_iter().
38///
39/// # Example
40///
41/// ```
42/// use roqoqo::Circuit;
43/// use roqoqo::operations::{Operation, RotateX};
44/// use qoqo_calculator::CalculatorFloat;
45/// // creating circuit
46/// let mut circuit = Circuit::new();
47/// // adding operation to circuit
48/// circuit.add_operation(RotateX::new(0,CalculatorFloat::from(0)));
49/// assert_eq!(circuit.len(), 1);
50/// // iterating over circuit I
51/// let operation_vector: Vec<&Operation>= circuit.iter().collect();
52/// // iterating over circuit II
53/// for op in circuit{
54///    println!("{op:?}");
55/// }
56/// // collecting operations into circuit
57/// let vector = vec![Operation::from(RotateX::new(0,CalculatorFloat::from(0))), Operation::from(RotateX::new(0,CalculatorFloat::from(0)))];
58/// let new_circuit: Circuit = vector.into_iter().collect();
59/// ```
60///
61/// Similarly to single Operations, Circuits can be translated to other frameworks via interfaces.
62///
63/// For Circuits the following functions are defined:
64/// * `new()`: creates an empty Circuit
65/// * `add_operation(operation)`: adds the specified operation to the Circuit
66/// * `get(index)`: returns the operation at the specified index in the Circuit
67/// * `get_mut(index)`: returns mutable reference to the operation at the specified index in the Circuit
68/// * `iter()`: creates an iterator of the Circuit
69/// * `len()`: returns the length of the Circuit
70/// * `is_empty()`: returns a boolean of whether the Circuit contains any definitions and operations or not
71/// * `involved_qubits()`: returns the qubits invovlved in the whole Circuit
72/// * `definitions()`: returns the definitions in the Circuit
73/// * `operations()`: returns the operations in the Circuit
74/// * `substitute_parameters(calculator)`: substitutes any symbolic parameters in (a copy of) the Circuit according to the specified Calculator
75/// * `remap_qubits(mapping)`: remaps the qubits in (a copy of) the Circuit according to the specified mapping
76/// * `count_occurences(operations)`: returns the number of operations in the Circuit with the specified operation tags
77/// * `get_operation_types()`: returns a list of all of the operations in the Circuit (in hqslang)
78/// * `from_iter(iterator)`: creates a Circuit from the items in the specified iterator
79/// * `extend(iterator)`: adds the operations in the specified iterator to the Circuit
80/// * `default()`: creates an empty Circuit
81/// * `[...]`: gets a slice of the Circuit (returned as a vector)
82/// * `+` and `+=`: add two circuits or an operation to the Circuit
83///
84#[derive(Debug, Clone, PartialEq)]
85#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
86#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
87#[cfg_attr(feature = "serialize", serde(try_from = "CircuitSerializable"))]
88#[cfg_attr(feature = "serialize", serde(into = "CircuitSerializable"))]
89pub struct Circuit {
90    /// Definitions in the quantum circuit, must be unique.
91    definitions: Vec<Operation>,
92    /// Operations of the quantum circuit, do not have to be unique.
93    operations: Vec<Operation>,
94    /// The roqoqo version.
95    _roqoqo_version: RoqoqoVersion,
96}
97
98#[cfg(feature = "serialize")]
99#[derive(Clone, PartialEq, Debug, Default)]
100#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
101#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
102#[cfg_attr(feature = "serialize", serde(rename = "Circuit"))]
103struct CircuitSerializable {
104    /// Definitions in the quantum circuit, must be unique.
105    definitions: Vec<Operation>,
106    /// Operations of the quantum circuit, do not have to be unique.
107    operations: Vec<Operation>,
108    /// The roqoqo version.
109    _roqoqo_version: RoqoqoVersionSerializable,
110}
111
112#[cfg(feature = "serialize")]
113impl TryFrom<CircuitSerializable> for Circuit {
114    type Error = RoqoqoError;
115    fn try_from(value: CircuitSerializable) -> Result<Self, Self::Error> {
116        Ok(Circuit {
117            definitions: value.definitions,
118            operations: value.operations,
119            _roqoqo_version: RoqoqoVersion,
120        })
121    }
122}
123
124#[cfg(feature = "serialize")]
125impl From<Circuit> for CircuitSerializable {
126    fn from(value: Circuit) -> Self {
127        let min_version = value.minimum_supported_roqoqo_version();
128        let current_version = RoqoqoVersionSerializable {
129            major_version: min_version.0,
130            minor_version: min_version.1,
131        };
132        Self {
133            definitions: value.definitions,
134            operations: value.operations,
135            _roqoqo_version: current_version,
136        }
137    }
138}
139
140impl Circuit {
141    /// Creates an empty quantum Circuit.
142    ///
143    /// # Returns
144    ///
145    /// * `Self` - The empty Circuit.
146    pub fn new() -> Self {
147        Circuit {
148            definitions: Vec::new(),
149            operations: Vec::new(),
150            _roqoqo_version: RoqoqoVersion,
151        }
152    }
153    /// Adds an Operation to Circuit (self).
154    ///
155    /// # Arguments
156    ///
157    /// * `op` - The Operation to add to the Circuit.
158    pub fn add_operation<T>(&mut self, op: T)
159    where
160        T: Into<Operation>,
161    {
162        let input: Operation = op.into();
163        match &input {
164            Operation::DefinitionBit(_) => self.definitions.push(input),
165            Operation::DefinitionFloat(_) => {
166                self.definitions.push(input);
167            }
168            Operation::DefinitionComplex(_) => {
169                self.definitions.push(input);
170            }
171            Operation::DefinitionUsize(_) => {
172                self.definitions.push(input);
173            }
174            Operation::InputSymbolic(_) => {
175                self.definitions.push(input);
176            }
177            #[cfg(feature = "unstable_operation_definition")]
178            Operation::GateDefinition(_) => {
179                self.definitions.push(input);
180            }
181            _ => self.operations.push(input),
182        }
183    }
184
185    /// Returns a reference to the element at index similar to std::Vec get function.
186    ///
187    /// Contrary to std::Vec get function not implemented for slices  .
188    ///
189    /// # Arguments
190    ///
191    /// * `index` - The index of the Operation to get in the Circuit.
192    ///
193    /// # Returns
194    ///
195    /// * `Option<&Operation>` - The operation at the given index (if it exists).
196    pub fn get(&self, index: usize) -> Option<&Operation> {
197        let def_len = self.definitions.len();
198        if index >= self.definitions.len() {
199            self.operations.get(index - def_len)
200        } else {
201            self.definitions.get(index)
202        }
203    }
204
205    /// Returns a mutable reference to the element at index similar to std::Vec get function.
206    ///
207    /// Contrary to std::Vec get function not implemented for slices.
208    ///
209    /// # Arguments
210    ///
211    /// * `index` - The index of the Operation to get in the Circuit.
212    ///
213    /// # Returns
214    ///
215    /// * `Option<mut &Operation>` - A mutable reference to the operation at the given index (if it exists).
216    pub fn get_mut(&mut self, index: usize) -> Option<&mut Operation> {
217        let def_len = self.definitions.len();
218        if index >= self.definitions.len() {
219            self.operations.get_mut(index - def_len)
220        } else {
221            self.definitions.get_mut(index)
222        }
223    }
224
225    /// Creates an iterator of the Circuit.
226    ///
227    /// # Returns
228    ///
229    /// `Iterator<Item = &Operation>` - The Circuit in iterator form.
230    pub fn iter(&self) -> impl Iterator<Item = &Operation> {
231        self.definitions.iter().chain(self.operations.iter())
232    }
233
234    /// Returns true if the Circuit contains symbolic variables.
235    ///
236    /// # Returns
237    ///
238    /// * `bool` - True if the Circuit contains symbolic values, false if it does not.
239    pub fn is_parametrized(&self) -> bool {
240        self.operations.iter().any(|o| o.is_parametrized())
241            || self.definitions.iter().any(|o| o.is_parametrized())
242    }
243
244    /// Returns the length of the Circuit.
245    ///
246    /// # Returns
247    ///
248    /// * `usize` - The length of the Circuit.
249    pub fn len(&self) -> usize {
250        self.definitions.len() + self.operations.len()
251    }
252
253    /// Returns true if the Circuit does not contain any operations and definitions.
254    ///
255    /// # Returns
256    ///
257    /// * `bool` - True if the Circuit is empty, false if it is not.
258    pub fn is_empty(&self) -> bool {
259        self.definitions.is_empty() && self.operations.is_empty()
260    }
261
262    /// Returns qubits the Circuit acts on.
263    ///
264    /// # Returns
265    ///
266    /// * `InvolvedQubits` - The qubits involved in the Circuit.
267    pub fn involved_qubits(&self) -> InvolvedQubits {
268        let mut temp_involved: HashSet<usize> = HashSet::new();
269        for op in self.operations.iter() {
270            match &op.involved_qubits() {
271                InvolvedQubits::All => {
272                    return InvolvedQubits::All;
273                }
274                InvolvedQubits::None => (),
275                InvolvedQubits::Set(x) => temp_involved = temp_involved.union(x).cloned().collect(),
276            }
277        }
278        match temp_involved.is_empty() {
279            true => InvolvedQubits::None,
280            false => InvolvedQubits::Set(temp_involved),
281        }
282    }
283
284    /// Returns reference to the vector of definitions in Circuit.
285    ///
286    /// Definitions need to be unique.
287    ///
288    /// # Returns
289    ///
290    /// * `&Vec<Operation>` - A vector of the definitions in the Circuit.
291    pub fn definitions(&self) -> &Vec<Operation> {
292        &self.definitions
293    }
294
295    /// Returns reference to the vector of quantum operations in Circuit.
296    ///
297    /// Operations do not need to be unique.
298    ///
299    /// # Returns
300    ///
301    /// * `&Vec<Operation>` - A vector of the operations in the Circuit.
302    pub fn operations(&self) -> &Vec<Operation> {
303        &self.operations
304    }
305
306    /// Substitutes the symbolic parameters in a clone of Circuit according to the calculator input.
307    ///
308    /// # Arguments
309    ///
310    /// * ``calculator` - The Calculator containing the substitutions to use in the Circuit.
311    ///
312    /// # Returns
313    ///
314    /// * `Ok(Self)` -  The Circuit with the parameters substituted.
315    /// * `Err(RoqoqoError)` - The subsitution failed.
316    pub fn substitute_parameters(&self, calculator: &Calculator) -> Result<Self, RoqoqoError> {
317        let mut tmp_calculator = calculator.clone();
318        let mut tmp_def: Vec<Operation> = Vec::new();
319        for def in self.definitions.iter() {
320            let tmp_op = def.substitute_parameters(&tmp_calculator)?;
321            if let Operation::InputSymbolic(x) = &tmp_op {
322                tmp_calculator.set_variable(x.name(), *x.input())
323            }
324            tmp_def.push(tmp_op);
325        }
326        let mut tmp_op: Vec<Operation> = Vec::new();
327        for op in self.operations.iter() {
328            tmp_op.push(op.substitute_parameters(&tmp_calculator)?);
329        }
330        Ok(Self {
331            definitions: tmp_def,
332            operations: tmp_op,
333            _roqoqo_version: RoqoqoVersion,
334        })
335    }
336    /// Remaps the qubits in operations in clone of Circuit.
337    ///
338    /// # Arguments
339    ///
340    /// * ``mapping` - The HashMap containing the {qubit: qubit} mapping to use in the Circuit.
341    ///
342    /// # Returns
343    ///
344    /// * `Ok(Self)` -  The Circuit with the qubits remapped.
345    /// * `Err(RoqoqoError)` - The remapping failed.
346    pub fn remap_qubits(&self, mapping: &HashMap<usize, usize>) -> Result<Self, RoqoqoError> {
347        let mut tmp_op: Vec<Operation> = Vec::new();
348        for op in self.operations.iter() {
349            tmp_op.push(op.remap_qubits(mapping)?);
350        }
351        Ok(Self {
352            definitions: self.definitions.clone(),
353            operations: tmp_op,
354            _roqoqo_version: RoqoqoVersion,
355        })
356    }
357
358    /// Counts the number of occurences of a set of operation tags in the circuit.
359    ///
360    /// # Arguments
361    ///
362    /// `operations` - The list of operation tags that should be counted.
363    ///
364    /// # Returns
365    ///
366    /// * `usize` - The number of occurences of these operation tags.
367    pub fn count_occurences(&self, operations: &[&str]) -> usize {
368        let mut counter: usize = 0;
369        for op in self.iter() {
370            if operations.iter().any(|x| op.tags().contains(x)) {
371                counter += 1
372            }
373        }
374        counter
375    }
376
377    /// Returns a list of the hqslang names of all operations occuring in the circuit.
378    ///
379    /// # Returns
380    ///
381    /// * `HashSet<&str>` - The operation types in the Circuit.
382    pub fn get_operation_types(&self) -> HashSet<&str> {
383        let mut operations: HashSet<&str> = HashSet::new();
384        for op in self.iter() {
385            let _ = operations.insert(op.hqslang());
386        }
387        operations
388    }
389
390    /// Returns clone of the circuit with all Overrotation Pragmas applied.
391    ///
392    /// # Returns
393    ///
394    /// * `Ok(Circuit)` - The Circuit with overrotations applied.
395    /// * `Err(RoqoqoError::OverrotationError)` - Applying overrotations failed.
396    ///
397    /// # Example
398    ///
399    /// ```
400    /// use roqoqo::Circuit;
401    /// use roqoqo::operations::{PragmaOverrotation, RotateX, RotateY};
402    /// let mut circuit = Circuit::new();
403    /// // Adding Overrotation of next RotateY operation acting on qubit 1
404    /// // overrotating parameter theta with a statistical value
405    /// // value is drawn from normal distribution with standard deviation 30.0
406    /// // and multiplied by amplitude 20.0
407    /// circuit += PragmaOverrotation::new("RotateY".to_string(), vec![1], 20.0, 30.0);
408    /// circuit += RotateX::new(0, 0.0.into());
409    /// circuit += RotateY::new(0, 1.0.into());
410    /// circuit += RotateY::new(1, 2.0.into());
411    /// circuit += RotateY::new(1, 3.0.into());
412    ///
413    /// let circuit_overrotated = circuit.overrotate().unwrap();
414    ///
415    /// println!("{}", circuit);
416    /// println!("{}", circuit_overrotated);
417    /// ```
418    ///
419    #[cfg(feature = "overrotate")]
420    pub fn overrotate(&self) -> Result<Self, RoqoqoError> {
421        let mut tmp_vec = self.operations.clone();
422        let mut return_circuit = Circuit {
423            definitions: self.definitions.clone(),
424            operations: Vec::new(),
425            _roqoqo_version: RoqoqoVersion,
426        };
427        let mut length = tmp_vec.len();
428        while length > 0 {
429            match tmp_vec
430                .iter()
431                .enumerate()
432                .find(|(_, op)| op.hqslang() == "PragmaOverrotation")
433                .map(|(i, op)| (i, op.clone()))
434            {
435                Some((index, Operation::PragmaOverrotation(overrotation))) => {
436                    // for op in tmp_vec[..index].iter() {
437                    //     return_circuit.operations.push(op.clone())
438                    // }
439                    let hqslang = overrotation.gate_hqslang();
440                    match tmp_vec[index..].iter().enumerate().find(|(_, op)| {
441                        hqslang == op.hqslang()
442                            && overrotation.involved_qubits() == op.involved_qubits()
443                    }) {
444                        Some((ind, _)) => {
445                            let mut tmp_tmp_vec: Vec<Operation> = Vec::new();
446                            for (mov_ind, op) in tmp_vec.into_iter().enumerate() {
447                                if mov_ind == index + ind {
448                                    tmp_tmp_vec.push(
449                                        Rotation::try_from(op)?
450                                            .overrotate(
451                                                overrotation.amplitude(),
452                                                overrotation.variance(),
453                                            )
454                                            .into(),
455                                    )
456                                } else if index != mov_ind {
457                                    tmp_tmp_vec.push(op)
458                                }
459                            }
460                            tmp_vec = tmp_tmp_vec
461                        }
462                        None => {
463                            let mut tmp_tmp_vec: Vec<Operation> = Vec::new();
464                            for (mov_ind, op) in tmp_vec.into_iter().enumerate() {
465                                if index != mov_ind {
466                                    tmp_tmp_vec.push(op)
467                                }
468                            }
469                            tmp_vec = tmp_tmp_vec
470                        }
471                    }
472                }
473                _ => {
474                    for op in tmp_vec {
475                        return_circuit.operations.push(op)
476                    }
477                    tmp_vec = Vec::new();
478                }
479            }
480            length = tmp_vec.len();
481        }
482        Ok(return_circuit)
483    }
484
485    /// Returns the number of qubits in the circuit.
486    ///
487    /// # Returns
488    /// * `usize` - The number of qubits in the Circuit.
489    pub fn number_of_qubits(&self) -> usize {
490        self.operations
491            .iter()
492            .map(|op| match op.involved_qubits() {
493                InvolvedQubits::All => 0,
494                InvolvedQubits::None => 0,
495                InvolvedQubits::Set(x) => x.into_iter().max().unwrap_or_default() + 1,
496            })
497            .max()
498            .unwrap_or_default()
499    }
500}
501
502/// Implements Index Access for Circuit.
503///
504/// # Panics
505///
506/// Panics when index is out of range of operations in circuit.
507/// This is consistent with standard Vec behaviour
508/// and returning Option or Result enums instead would conflict with definition of Output type.
509impl ops::Index<usize> for Circuit {
510    type Output = Operation;
511
512    /// Returns reference to Operation at index.
513    ///
514    /// # Arguments
515    ///
516    /// * `index` - The index of the operation.
517    ///
518    /// # Panics
519    ///
520    /// Panics when index is out of range of operations in circuit.
521    fn index(&self, index: usize) -> &Self::Output {
522        let def_len = self.definitions.len();
523        if index >= def_len {
524            &self.operations[index - def_len]
525        } else {
526            &self.definitions[index]
527        }
528    }
529}
530
531impl ops::IndexMut<usize> for Circuit {
532    /// Returns reference to Operation at index.
533    ///
534    /// # Arguments
535    ///
536    /// * `index` - The index of the operation.
537    ///
538    /// # Panics
539    ///
540    /// Panics when index is out of range of operations in circuit.
541    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
542        let def_len = self.definitions.len();
543        if index >= def_len {
544            &mut self.operations[index - def_len]
545        } else {
546            &mut self.definitions[index]
547        }
548    }
549}
550
551impl IntoIterator for Circuit {
552    type Item = Operation;
553    type IntoIter = OperationIterator;
554    /// Returns the Circuit in Iterator form.
555    ///
556    /// # Returns
557    ///
558    /// * `Self::IntoIter` - The Circuit in Iterator form.
559    fn into_iter(self) -> Self::IntoIter {
560        Self::IntoIter {
561            definition_iter: self.definitions.into_iter(),
562            operation_iter: self.operations.into_iter(),
563        }
564    }
565}
566
567impl<T> FromIterator<T> for Circuit
568where
569    T: Into<Operation>,
570{
571    /// Returns the circuit in Circuit form, from an Iterator form of the circuit.
572    ///
573    /// # Returns
574    ///
575    /// * `Self::IntoIter` - The Circuit in Circuit form.
576    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
577        let mut circuit = Circuit::new();
578        for op in iter {
579            circuit.add_operation(op.into());
580        }
581        circuit
582    }
583}
584
585impl<T> Extend<T> for Circuit
586where
587    T: Into<Operation>,
588{
589    /// Extends the Circuit by the specified operations (in Iterator form).
590    ///
591    /// # Arguments
592    ///
593    /// * `iter` - The iterator containing the operations by which to extend the Circuit.
594    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
595        for op in iter {
596            self.add_operation(op.into());
597        }
598    }
599}
600
601impl Default for Circuit {
602    /// Creates a default implementation of the Circuit, which is an empty Circuit.
603    ///
604    /// # Returns
605    ///
606    /// * `Self` - The default Circuit (empty).
607    fn default() -> Self {
608        Self::new()
609    }
610}
611
612/// Trait for returning Vectors based on Range structs usually used for Index<> trait and slice access.
613///
614/// Required because Circuit does not have a continuous internal vector representation of the values.
615/// Returns a Vec instead of slices.
616///
617/// # Example
618///
619/// ```
620/// use roqoqo::{Circuit, AsVec};
621/// use roqoqo::operations::{DefinitionFloat, Operation, RotateZ};
622/// use qoqo_calculator::CalculatorFloat;
623///
624/// let mut circuit = Circuit::new();
625/// let definition = DefinitionFloat::new(String::from("ro"), 1, false);
626/// let rotatez0 = RotateZ::new(0, CalculatorFloat::from(0.0));
627/// circuit.add_operation(definition.clone());
628/// circuit.add_operation(rotatez0.clone());
629///
630/// let vec_ops = vec![
631///     Operation::from(definition.clone()),
632///     Operation::from(rotatez0.clone()),
633/// ];
634///
635/// assert_eq!(circuit.as_vec(0..1).clone(), Some(vec![vec_ops[0].clone()])); // Range
636/// assert_eq!(circuit.as_vec(0..).clone(), Some(vec_ops.clone())); // RangeTo
637/// assert_eq!(circuit.as_vec(..1).clone(), Some(vec![vec_ops[0].clone()])); // RangeFrom
638/// ```
639///
640pub trait AsVec<T> {
641    /// Returns slice of Circuit as Vec<Operations>.
642    ///
643    /// # Arguments
644    ///
645    /// * `range` - The indices of the slice of the Circuit to be returned.
646    ///
647    /// # Returns
648    ///
649    /// * `Option<Vec<Operation>>` - A vector of the operations in the Circuit with the specified indices.
650    fn as_vec(&self, range: T) -> Option<Vec<Operation>>;
651}
652
653impl AsVec<std::ops::Range<usize>> for Circuit {
654    /// Returns slice of Circuit as Vec<Operations>.
655    ///
656    /// # Arguments
657    ///
658    /// * `range` - The indices of the slice of the Circuit to be returned.
659    ///
660    /// # Returns
661    ///
662    /// * `Option<Vec<Operation>>` - A vector of the operations in the Circuit with the specified indices.
663    fn as_vec(&self, range: std::ops::Range<usize>) -> Option<Vec<Operation>> {
664        let mut return_vec: Vec<Operation>;
665        let def_len = self.definitions.len();
666        if range.end - def_len >= self.operations.len() {
667            return None;
668        }
669        if range.start < def_len {
670            if range.end < def_len {
671                return_vec = self.definitions[range].to_vec();
672            } else {
673                return_vec = self.definitions[range.start..].to_vec();
674                let mut tmp_vec = self.operations[..range.end - def_len].to_vec();
675                return_vec.append(&mut tmp_vec);
676            }
677        } else {
678            return_vec = self.operations[range.start - def_len..range.end - def_len].to_vec();
679        }
680        Some(return_vec)
681    }
682}
683
684impl AsVec<std::ops::RangeTo<usize>> for Circuit {
685    /// Returns slice of Circuit as Vec<Operations>.
686    ///
687    /// # Arguments
688    ///
689    /// * `range` - The indices of the slice of the Circuit to be returned.
690    ///
691    /// # Returns
692    ///
693    /// * `Option<Vec<Operation>>` - A vector of the operations in the Circuit with the specified indices.
694    fn as_vec(&self, range: std::ops::RangeTo<usize>) -> Option<Vec<Operation>> {
695        let mut return_vec: Vec<Operation>;
696        let def_len = self.definitions.len();
697        if range.end - def_len >= self.operations.len() {
698            return None;
699        }
700        if range.end < def_len {
701            return_vec = self.definitions[range].to_vec();
702        } else {
703            return_vec = self.definitions.clone();
704            let mut tmp_vec = self.operations[..range.end - def_len].to_vec();
705            return_vec.append(&mut tmp_vec);
706        }
707        Some(return_vec)
708    }
709}
710
711impl AsVec<std::ops::RangeFrom<usize>> for Circuit {
712    /// Returns slice of Circuit as Vec<Operations>.
713    ///
714    /// # Arguments
715    ///
716    /// * `range` - The indices of the slice of the Circuit to be returned.
717    ///
718    /// # Returns
719    ///
720    /// * `Option<Vec<Operation>>` - A vector of the operations in the Circuit with the specified indices.
721    fn as_vec(&self, range: std::ops::RangeFrom<usize>) -> Option<Vec<Operation>> {
722        let mut return_vec: Vec<Operation>;
723        let def_len = self.definitions.len();
724        if range.start < def_len {
725            return_vec = self.definitions[range.start..].to_vec();
726            let mut tmp_vec = self.operations.clone();
727            return_vec.append(&mut tmp_vec);
728        } else {
729            return_vec = self.operations[range.start - def_len..].to_vec();
730        }
731        Some(return_vec)
732    }
733}
734
735/// Implements `+` (add) for Circuit and generic type `T`.
736///
737/// # Arguments
738///
739/// * `other` - Any type T that implements Into<Operation> trait.
740impl<T> ops::Add<T> for Circuit
741where
742    T: Into<Operation>,
743{
744    type Output = Self;
745    fn add(self, other: T) -> Self {
746        let mut return_circuit = self;
747        return_circuit.add_operation(other);
748        return_circuit
749    }
750}
751
752/// Implements `+` (add) for two Circuits.
753///
754/// # Arguments
755///
756/// * `other` - The Circuit to be added.
757impl ops::Add<Circuit> for Circuit {
758    type Output = Self;
759    fn add(self, other: Circuit) -> Self {
760        Self {
761            definitions: self
762                .definitions
763                .into_iter()
764                .chain(other.definitions)
765                .collect(),
766            operations: self
767                .operations
768                .into_iter()
769                .chain(other.operations)
770                .collect(),
771            _roqoqo_version: RoqoqoVersion,
772        }
773    }
774}
775
776/// Implements `+` (add) for Circuit and Circuit reference.
777///
778/// # Arguments
779///
780/// * `other` - The Circuit reference to be added.
781impl ops::Add<&Circuit> for Circuit {
782    type Output = Self;
783    fn add(self, other: &Circuit) -> Self {
784        Self {
785            definitions: self
786                .definitions
787                .into_iter()
788                .chain(other.definitions.iter().cloned())
789                .collect(),
790            operations: self
791                .operations
792                .into_iter()
793                .chain(other.operations.iter().cloned())
794                .collect(),
795            _roqoqo_version: RoqoqoVersion,
796        }
797    }
798}
799
800/// Implements `+=` (add) for Circuit and generic type `T`.
801///
802/// # Arguments
803///
804/// * `other` - Any type T that implements Into<Operation> trait.
805impl<T> ops::AddAssign<T> for Circuit
806where
807    T: Into<Operation>,
808{
809    fn add_assign(&mut self, other: T) {
810        self.add_operation(other);
811    }
812}
813
814/// Implements `+=` (add) for two Circuits.
815///
816/// # Arguments
817///
818/// * `other` - The Circuit to be appended.
819impl ops::AddAssign<Circuit> for Circuit {
820    fn add_assign(&mut self, other: Circuit) {
821        self.definitions.extend(other.definitions);
822        self.operations.extend(other.operations)
823    }
824}
825
826/// Implements `+=` (add) for Circuits and Circuit reference.
827///
828/// # Arguments
829///
830/// * `other` - The Circuit to be appended.
831impl ops::AddAssign<&Circuit> for Circuit {
832    fn add_assign(&mut self, other: &Circuit) {
833        self.definitions.extend(other.definitions.iter().cloned());
834        self.operations.extend(other.operations.iter().cloned())
835    }
836}
837
838/// Implements the Display trait for Circuit.
839impl Display for Circuit {
840    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
841        let mut s: String = String::new();
842        for op in self.iter() {
843            _ = writeln!(s, "{op:?}")
844        }
845        write!(f, "{s}")
846    }
847}
848
849/// Iterator over roqoqo operations.
850#[derive(Debug, Clone)]
851pub struct OperationIterator {
852    /// Definitions in the quantum circuit in Iterator form, must be unique.
853    definition_iter: std::vec::IntoIter<Operation>,
854    /// Operations in the quantum circuit in Iterator form, must not be unique.
855    operation_iter: std::vec::IntoIter<Operation>,
856}
857
858impl Iterator for OperationIterator {
859    type Item = Operation;
860    /// Advances the iterator and returns the next value.
861    ///
862    /// Returns None when iteration is finished. Individual iterator implementations may choose to resume iteration,
863    /// and so calling next() again may or may not eventually start returning Some(Operation) again at some point.
864    ///
865    /// # Returns
866    ///
867    /// * `Option<Self::Item>` - The Operation that is next in the Iterator.
868    fn next(&mut self) -> Option<Self::Item> {
869        match self.definition_iter.next() {
870            Some(x) => Some(x),
871            None => self.operation_iter.next(),
872        }
873    }
874}
875
876impl SupportedVersion for Circuit {
877    fn minimum_supported_roqoqo_version(&self) -> (u32, u32, u32) {
878        let mut current_minimum_version = (1, 0, 0);
879        for op in self.iter() {
880            let comparison_version = op.minimum_supported_roqoqo_version();
881            crate::update_roqoqo_version(&mut current_minimum_version, comparison_version);
882        }
883        current_minimum_version
884    }
885}