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}