use crate::operations::{
Define, InvolveQubits, InvolvedQubits, Operate, Operation, Substitute, SupportedVersion,
};
#[cfg(feature = "overrotate")]
use crate::operations::{Rotate, Rotation};
use crate::RoqoqoError;
use crate::RoqoqoVersion;
#[cfg(feature = "serialize")]
use crate::RoqoqoVersionSerializable;
use qoqo_calculator::Calculator;
use std::collections::{HashMap, HashSet};
#[cfg(feature = "overrotate")]
use std::convert::TryFrom;
use std::ops;
use std::{
fmt::{Display, Formatter, Write},
iter::{FromIterator, IntoIterator},
};
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "serialize", serde(try_from = "CircuitSerializable"))]
#[cfg_attr(feature = "serialize", serde(into = "CircuitSerializable"))]
pub struct Circuit {
definitions: Vec<Operation>,
operations: Vec<Operation>,
_roqoqo_version: RoqoqoVersion,
}
#[cfg(feature = "serialize")]
#[derive(Clone, PartialEq, Debug, Default)]
#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", serde(rename = "Circuit"))]
struct CircuitSerializable {
definitions: Vec<Operation>,
operations: Vec<Operation>,
_roqoqo_version: RoqoqoVersionSerializable,
}
#[cfg(feature = "serialize")]
impl TryFrom<CircuitSerializable> for Circuit {
type Error = RoqoqoError;
fn try_from(value: CircuitSerializable) -> Result<Self, Self::Error> {
Ok(Circuit {
definitions: value.definitions,
operations: value.operations,
_roqoqo_version: RoqoqoVersion,
})
}
}
#[cfg(feature = "serialize")]
impl From<Circuit> for CircuitSerializable {
fn from(value: Circuit) -> Self {
let min_version = value.minimum_supported_roqoqo_version();
let current_version = RoqoqoVersionSerializable {
major_version: min_version.0,
minor_version: min_version.1,
};
Self {
definitions: value.definitions,
operations: value.operations,
_roqoqo_version: current_version,
}
}
}
impl Circuit {
pub fn new() -> Self {
Circuit {
definitions: Vec::new(),
operations: Vec::new(),
_roqoqo_version: RoqoqoVersion,
}
}
pub fn add_operation<T>(&mut self, op: T)
where
T: Into<Operation>,
{
let input: Operation = op.into();
match &input {
Operation::DefinitionBit(_) => self.definitions.push(input),
Operation::DefinitionFloat(_) => {
self.definitions.push(input);
}
Operation::DefinitionComplex(_) => {
self.definitions.push(input);
}
Operation::DefinitionUsize(_) => {
self.definitions.push(input);
}
Operation::InputSymbolic(_) => {
self.definitions.push(input);
}
#[cfg(feature = "unstable_operation_definition")]
Operation::GateDefinition(_) => {
self.definitions.push(input);
}
_ => self.operations.push(input),
}
}
pub fn get(&self, index: usize) -> Option<&Operation> {
let def_len = self.definitions.len();
if index >= self.definitions.len() {
self.operations.get(index - def_len)
} else {
self.definitions.get(index)
}
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut Operation> {
let def_len = self.definitions.len();
if index >= self.definitions.len() {
self.operations.get_mut(index - def_len)
} else {
self.definitions.get_mut(index)
}
}
pub fn iter(&self) -> impl Iterator<Item = &Operation> {
self.definitions.iter().chain(self.operations.iter())
}
pub fn is_parametrized(&self) -> bool {
self.operations.iter().any(|o| o.is_parametrized())
|| self.definitions.iter().any(|o| o.is_parametrized())
}
pub fn len(&self) -> usize {
self.definitions.len() + self.operations.len()
}
pub fn is_empty(&self) -> bool {
self.definitions.is_empty() && self.operations.is_empty()
}
pub fn involved_qubits(&self) -> InvolvedQubits {
let mut temp_involved: HashSet<usize> = HashSet::new();
for op in self.operations.iter() {
match &op.involved_qubits() {
InvolvedQubits::All => {
return InvolvedQubits::All;
}
InvolvedQubits::None => (),
InvolvedQubits::Set(x) => temp_involved = temp_involved.union(x).cloned().collect(),
}
}
match temp_involved.is_empty() {
true => InvolvedQubits::None,
false => InvolvedQubits::Set(temp_involved),
}
}
pub fn definitions(&self) -> &Vec<Operation> {
&self.definitions
}
pub fn operations(&self) -> &Vec<Operation> {
&self.operations
}
pub fn substitute_parameters(&self, calculator: &Calculator) -> Result<Self, RoqoqoError> {
let mut tmp_calculator = calculator.clone();
let mut tmp_def: Vec<Operation> = Vec::new();
for def in self.definitions.iter() {
let tmp_op = def.substitute_parameters(&tmp_calculator)?;
if let Operation::InputSymbolic(x) = &tmp_op {
tmp_calculator.set_variable(x.name(), *x.input())
}
tmp_def.push(tmp_op);
}
let mut tmp_op: Vec<Operation> = Vec::new();
for op in self.operations.iter() {
tmp_op.push(op.substitute_parameters(&tmp_calculator)?);
}
Ok(Self {
definitions: tmp_def,
operations: tmp_op,
_roqoqo_version: RoqoqoVersion,
})
}
pub fn remap_qubits(&self, mapping: &HashMap<usize, usize>) -> Result<Self, RoqoqoError> {
let mut tmp_op: Vec<Operation> = Vec::new();
for op in self.operations.iter() {
tmp_op.push(op.remap_qubits(mapping)?);
}
Ok(Self {
definitions: self.definitions.clone(),
operations: tmp_op,
_roqoqo_version: RoqoqoVersion,
})
}
pub fn count_occurences(&self, operations: &[&str]) -> usize {
let mut counter: usize = 0;
for op in self.iter() {
if operations.iter().any(|x| op.tags().contains(x)) {
counter += 1
}
}
counter
}
pub fn get_operation_types(&self) -> HashSet<&str> {
let mut operations: HashSet<&str> = HashSet::new();
for op in self.iter() {
let _ = operations.insert(op.hqslang());
}
operations
}
#[cfg(feature = "overrotate")]
pub fn overrotate(&self) -> Result<Self, RoqoqoError> {
let mut tmp_vec = self.operations.clone();
let mut return_circuit = Circuit {
definitions: self.definitions.clone(),
operations: Vec::new(),
_roqoqo_version: RoqoqoVersion,
};
let mut length = tmp_vec.len();
while length > 0 {
match tmp_vec
.iter()
.enumerate()
.find(|(_, op)| op.hqslang() == "PragmaOverrotation")
.map(|(i, op)| (i, op.clone()))
{
Some((index, Operation::PragmaOverrotation(overrotation))) => {
let hqslang = overrotation.gate_hqslang();
match tmp_vec[index..].iter().enumerate().find(|(_, op)| {
hqslang == op.hqslang()
&& overrotation.involved_qubits() == op.involved_qubits()
}) {
Some((ind, _)) => {
let mut tmp_tmp_vec: Vec<Operation> = Vec::new();
for (mov_ind, op) in tmp_vec.into_iter().enumerate() {
if mov_ind == index + ind {
tmp_tmp_vec.push(
Rotation::try_from(op)?
.overrotate(
overrotation.amplitude(),
overrotation.variance(),
)
.into(),
)
} else if index != mov_ind {
tmp_tmp_vec.push(op)
}
}
tmp_vec = tmp_tmp_vec
}
None => {
let mut tmp_tmp_vec: Vec<Operation> = Vec::new();
for (mov_ind, op) in tmp_vec.into_iter().enumerate() {
if index != mov_ind {
tmp_tmp_vec.push(op)
}
}
tmp_vec = tmp_tmp_vec
}
}
}
_ => {
for op in tmp_vec {
return_circuit.operations.push(op)
}
tmp_vec = Vec::new();
}
}
length = tmp_vec.len();
}
Ok(return_circuit)
}
pub fn number_of_qubits(&self) -> usize {
self.operations
.iter()
.map(|op| match op.involved_qubits() {
InvolvedQubits::All => 0,
InvolvedQubits::None => 0,
InvolvedQubits::Set(x) => x.into_iter().max().unwrap_or_default() + 1,
})
.max()
.unwrap_or_default()
}
}
impl ops::Index<usize> for Circuit {
type Output = Operation;
fn index(&self, index: usize) -> &Self::Output {
let def_len = self.definitions.len();
if index >= def_len {
&self.operations[index - def_len]
} else {
&self.definitions[index]
}
}
}
impl ops::IndexMut<usize> for Circuit {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
let def_len = self.definitions.len();
if index >= def_len {
&mut self.operations[index - def_len]
} else {
&mut self.definitions[index]
}
}
}
impl IntoIterator for Circuit {
type Item = Operation;
type IntoIter = OperationIterator;
fn into_iter(self) -> Self::IntoIter {
Self::IntoIter {
definition_iter: self.definitions.into_iter(),
operation_iter: self.operations.into_iter(),
}
}
}
impl<T> FromIterator<T> for Circuit
where
T: Into<Operation>,
{
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut circuit = Circuit::new();
for op in iter {
circuit.add_operation(op.into());
}
circuit
}
}
impl<T> Extend<T> for Circuit
where
T: Into<Operation>,
{
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
for op in iter {
self.add_operation(op.into());
}
}
}
impl Default for Circuit {
fn default() -> Self {
Self::new()
}
}
pub trait AsVec<T> {
fn as_vec(&self, range: T) -> Option<Vec<Operation>>;
}
impl AsVec<std::ops::Range<usize>> for Circuit {
fn as_vec(&self, range: std::ops::Range<usize>) -> Option<Vec<Operation>> {
let mut return_vec: Vec<Operation>;
let def_len = self.definitions.len();
if range.end - def_len >= self.operations.len() {
return None;
}
if range.start < def_len {
if range.end < def_len {
return_vec = self.definitions[range].to_vec();
} else {
return_vec = self.definitions[range.start..].to_vec();
let mut tmp_vec = self.operations[..range.end - def_len].to_vec();
return_vec.append(&mut tmp_vec);
}
} else {
return_vec = self.operations[range.start - def_len..range.end - def_len].to_vec();
}
Some(return_vec)
}
}
impl AsVec<std::ops::RangeTo<usize>> for Circuit {
fn as_vec(&self, range: std::ops::RangeTo<usize>) -> Option<Vec<Operation>> {
let mut return_vec: Vec<Operation>;
let def_len = self.definitions.len();
if range.end - def_len >= self.operations.len() {
return None;
}
if range.end < def_len {
return_vec = self.definitions[range].to_vec();
} else {
return_vec = self.definitions.clone();
let mut tmp_vec = self.operations[..range.end - def_len].to_vec();
return_vec.append(&mut tmp_vec);
}
Some(return_vec)
}
}
impl AsVec<std::ops::RangeFrom<usize>> for Circuit {
fn as_vec(&self, range: std::ops::RangeFrom<usize>) -> Option<Vec<Operation>> {
let mut return_vec: Vec<Operation>;
let def_len = self.definitions.len();
if range.start < def_len {
return_vec = self.definitions[range.start..].to_vec();
let mut tmp_vec = self.operations.clone();
return_vec.append(&mut tmp_vec);
} else {
return_vec = self.operations[range.start - def_len..].to_vec();
}
Some(return_vec)
}
}
impl<T> ops::Add<T> for Circuit
where
T: Into<Operation>,
{
type Output = Self;
fn add(self, other: T) -> Self {
let mut return_circuit = self;
return_circuit.add_operation(other);
return_circuit
}
}
impl ops::Add<Circuit> for Circuit {
type Output = Self;
fn add(self, other: Circuit) -> Self {
Self {
definitions: self
.definitions
.into_iter()
.chain(other.definitions)
.collect(),
operations: self
.operations
.into_iter()
.chain(other.operations)
.collect(),
_roqoqo_version: RoqoqoVersion,
}
}
}
impl ops::Add<&Circuit> for Circuit {
type Output = Self;
fn add(self, other: &Circuit) -> Self {
Self {
definitions: self
.definitions
.into_iter()
.chain(other.definitions.iter().cloned())
.collect(),
operations: self
.operations
.into_iter()
.chain(other.operations.iter().cloned())
.collect(),
_roqoqo_version: RoqoqoVersion,
}
}
}
impl<T> ops::AddAssign<T> for Circuit
where
T: Into<Operation>,
{
fn add_assign(&mut self, other: T) {
self.add_operation(other);
}
}
impl ops::AddAssign<Circuit> for Circuit {
fn add_assign(&mut self, other: Circuit) {
self.definitions.extend(other.definitions);
self.operations.extend(other.operations)
}
}
impl ops::AddAssign<&Circuit> for Circuit {
fn add_assign(&mut self, other: &Circuit) {
self.definitions.extend(other.definitions.iter().cloned());
self.operations.extend(other.operations.iter().cloned())
}
}
impl Display for Circuit {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut s: String = String::new();
for op in self.iter() {
_ = writeln!(s, "{op:?}")
}
write!(f, "{s}")
}
}
#[derive(Debug, Clone)]
pub struct OperationIterator {
definition_iter: std::vec::IntoIter<Operation>,
operation_iter: std::vec::IntoIter<Operation>,
}
impl Iterator for OperationIterator {
type Item = Operation;
fn next(&mut self) -> Option<Self::Item> {
match self.definition_iter.next() {
Some(x) => Some(x),
None => self.operation_iter.next(),
}
}
}
impl SupportedVersion for Circuit {
fn minimum_supported_roqoqo_version(&self) -> (u32, u32, u32) {
let mut current_minimum_version = (1, 0, 0);
for op in self.iter() {
let comparison_version = op.minimum_supported_roqoqo_version();
crate::update_roqoqo_version(&mut current_minimum_version, comparison_version);
}
current_minimum_version
}
}