use std::collections::{HashMap, HashSet};
use crate::operations::*;
use crate::Circuit;
use crate::RoqoqoVersionSerializable;
use crate::{RoqoqoError, RoqoqoVersion};
use petgraph::adj::NodeIndex;
use petgraph::algo;
use petgraph::algo::toposort;
use petgraph::graph::{Graph, Neighbors};
use petgraph::visit::Dfs;
use petgraph::Directed;
use petgraph::Direction::{Incoming, Outgoing};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct CircuitDag {
pub(crate) graph: Graph<Operation, (), Directed, usize>,
pub(crate) commuting_operations: Vec<NodeIndex<usize>>,
pub(crate) first_parallel_block: HashSet<NodeIndex<usize>>,
pub(crate) last_parallel_block: HashSet<NodeIndex<usize>>,
pub(crate) first_all: Option<NodeIndex<usize>>,
pub(crate) last_all: Option<NodeIndex<usize>>,
pub(crate) first_operation_involving_qubit: HashMap<usize, NodeIndex<usize>>,
pub(crate) last_operation_involving_qubit: HashMap<usize, NodeIndex<usize>>,
pub(crate) first_operation_involving_classical: HashMap<(String, usize), NodeIndex<usize>>,
pub(crate) last_operation_involving_classical: HashMap<(String, usize), NodeIndex<usize>>,
_roqoqo_version: RoqoqoVersion,
}
#[cfg(feature = "serialize")]
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", serde(rename = "CircuitDag"))]
struct CircuitDagSerializable {
graph: Graph<Operation, (), Directed, usize>,
commuting_operations: Vec<NodeIndex<usize>>,
first_parallel_block: HashSet<NodeIndex<usize>>,
last_parallel_block: HashSet<NodeIndex<usize>>,
first_all: Option<NodeIndex<usize>>,
last_all: Option<NodeIndex<usize>>,
first_operation_involving_qubit: HashMap<usize, NodeIndex<usize>>,
last_operation_involving_qubit: HashMap<usize, NodeIndex<usize>>,
first_operation_involving_classical: HashMap<(String, usize), NodeIndex<usize>>,
last_operation_involving_classical: HashMap<(String, usize), NodeIndex<usize>>,
_roqoqo_version: RoqoqoVersionSerializable,
}
#[cfg(feature = "serialize")]
impl TryFrom<CircuitDagSerializable> for CircuitDag {
type Error = RoqoqoError;
fn try_from(value: CircuitDagSerializable) -> Result<Self, Self::Error> {
Ok(CircuitDag {
_roqoqo_version: RoqoqoVersion,
graph: value.graph,
commuting_operations: value.commuting_operations,
first_parallel_block: value.first_parallel_block,
last_parallel_block: value.last_parallel_block,
first_all: value.first_all,
last_all: value.last_all,
first_operation_involving_qubit: value.first_operation_involving_qubit,
last_operation_involving_qubit: value.last_operation_involving_qubit,
first_operation_involving_classical: value.first_operation_involving_classical,
last_operation_involving_classical: value.last_operation_involving_classical,
})
}
}
#[cfg(feature = "serialize")]
impl From<CircuitDag> for CircuitDagSerializable {
fn from(value: CircuitDag) -> Self {
let min_version = value.minimum_supported_roqoqo_version();
let current_version = RoqoqoVersionSerializable {
major_version: min_version.0,
minor_version: min_version.1,
};
Self {
_roqoqo_version: current_version,
graph: value.graph,
commuting_operations: value.commuting_operations,
first_parallel_block: value.first_parallel_block,
last_parallel_block: value.last_parallel_block,
first_all: value.first_all,
last_all: value.last_all,
first_operation_involving_qubit: value.first_operation_involving_qubit,
last_operation_involving_qubit: value.last_operation_involving_qubit,
first_operation_involving_classical: value.first_operation_involving_classical,
last_operation_involving_classical: value.last_operation_involving_classical,
}
}
}
#[derive(Debug)]
pub struct ParallelBlocks<'a> {
dag: &'a CircuitDag,
parallel_block: Vec<NodeIndex<usize>>,
already_executed: Vec<NodeIndex<usize>>,
}
impl PartialEq for CircuitDag {
fn eq(&self, other: &Self) -> bool {
let nodes = |a: &Operation, b: &Operation| a.eq(b);
let edges = |_: &(), _: &()| true;
algo::is_isomorphic_matching(&self.graph, &other.graph, nodes, edges)
}
}
impl CircuitDag {
pub fn with_capacity(node_number: usize, edge_number: usize) -> Self {
CircuitDag {
graph: Graph::<Operation, (), Directed, usize>::with_capacity(node_number, edge_number),
commuting_operations: Vec::<NodeIndex<usize>>::new(),
first_parallel_block: HashSet::<NodeIndex<usize>>::new(),
last_parallel_block: HashSet::<NodeIndex<usize>>::new(),
first_all: Option::<NodeIndex<usize>>::None,
last_all: Option::<NodeIndex<usize>>::None,
first_operation_involving_qubit: HashMap::<usize, NodeIndex<usize>>::new(),
last_operation_involving_qubit: HashMap::<usize, NodeIndex<usize>>::new(),
first_operation_involving_classical: HashMap::<(String, usize), NodeIndex<usize>>::new(
),
last_operation_involving_classical: HashMap::<(String, usize), NodeIndex<usize>>::new(),
_roqoqo_version: RoqoqoVersion,
}
}
pub fn add_to_back(&mut self, operation: Operation) -> Option<usize> {
let node = self.graph.add_node(operation.clone());
match operation {
Operation::DefinitionBit(_) => self.commuting_operations.push(node.index()),
Operation::DefinitionFloat(_) => self.commuting_operations.push(node.index()),
Operation::DefinitionUsize(_) => self.commuting_operations.push(node.index()),
Operation::DefinitionComplex(_) => self.commuting_operations.push(node.index()),
_ => {
if let (InvolvedQubits::None, InvolvedClassical::None) =
(operation.involved_qubits(), operation.involved_classical())
{
self.commuting_operations.push(node.index());
} else {
self.add_to_back_involved(node.index());
}
}
}
if !self.is_definition_classical_populate(node.index(), operation.clone()) {
self.update_classical_back(node.index(), operation);
}
Some(node.index())
}
fn add_to_back_involved(&mut self, node: NodeIndex<usize>) {
let node_involved_qubits: InvolvedQubits = self
.graph
.node_weight(node.into())
.unwrap()
.involved_qubits();
if let InvolvedQubits::Set(x) = node_involved_qubits {
for qubit in x {
self.update_from_qubit_back(node, qubit);
}
if self
.graph
.neighbors_directed(node.into(), Incoming)
.next()
.is_none()
{
self.first_parallel_block.insert(node);
}
} else if let InvolvedQubits::All = node_involved_qubits {
self.update_from_all_operation_back(node);
}
}
fn update_from_qubit_back(&mut self, node: NodeIndex<usize>, qubit: usize) {
if let Some(&i) = self.last_operation_involving_qubit.get(&qubit) {
self.graph.update_edge(i.into(), node.into(), ());
self.last_parallel_block.remove(&i);
} else if let Some(la) = self.last_all {
self.graph
.update_edge(self.last_all.unwrap().into(), node.into(), ());
self.last_parallel_block.remove(&la);
}
let qubit_presence = self.last_operation_involving_qubit.insert(qubit, node);
self.last_parallel_block.insert(node);
if qubit_presence.is_none() {
if let Some(last_all) = self.last_all {
self.first_operation_involving_qubit.insert(qubit, last_all);
} else {
self.first_operation_involving_qubit.insert(qubit, node);
}
}
}
fn update_from_all_operation_back(&mut self, node: NodeIndex<usize>) {
if self.first_all.is_none() {
self.first_all = Some(node);
}
self.last_all = Some(node);
self.last_parallel_block.clear();
self.last_parallel_block.insert(node);
if self.first_parallel_block.is_empty() {
self.first_parallel_block.insert(node);
}
if self.first_operation_involving_qubit.is_empty()
&& self.last_operation_involving_qubit.is_empty()
{
self.first_operation_involving_qubit.insert(0, node);
self.last_operation_involving_qubit.insert(0, node);
} else {
let mut temp_map: HashMap<usize, NodeIndex<usize>> =
HashMap::with_capacity(self.last_operation_involving_qubit.capacity());
for (&qubit, &old_node) in &self.last_operation_involving_qubit {
self.graph.update_edge(old_node.into(), node.into(), ());
temp_map.insert(qubit, node);
}
self.last_operation_involving_qubit = temp_map;
}
}
pub fn add_to_front(&mut self, operation: Operation) -> Option<usize> {
let node = self.graph.add_node(operation.clone());
match operation {
Operation::DefinitionBit(_) => self.commuting_operations.push(node.index()),
Operation::DefinitionFloat(_) => self.commuting_operations.push(node.index()),
Operation::DefinitionUsize(_) => self.commuting_operations.push(node.index()),
Operation::DefinitionComplex(_) => self.commuting_operations.push(node.index()),
_ => {
if let (InvolvedQubits::None, InvolvedClassical::None) =
(operation.involved_qubits(), operation.involved_classical())
{
self.commuting_operations.push(node.index());
} else {
self.add_to_front_involved(node.index());
}
}
}
if !self.is_definition_classical_populate(node.index(), operation.clone()) {
self.update_classical_front(node.index(), operation);
}
Some(node.index())
}
fn add_to_front_involved(&mut self, node: NodeIndex<usize>) {
let node_involved_qubits: InvolvedQubits = self
.graph
.node_weight(node.into())
.unwrap()
.involved_qubits();
if let InvolvedQubits::Set(x) = node_involved_qubits {
for qubit in x {
self.update_from_qubit_front(node, qubit);
}
if self
.graph
.neighbors_directed(node.into(), Outgoing)
.next()
.is_none()
{
self.last_parallel_block.insert(node);
}
} else if let InvolvedQubits::All = node_involved_qubits {
self.update_from_all_operation_front(node);
}
}
fn update_from_qubit_front(&mut self, node: NodeIndex<usize>, qubit: usize) {
if let Some(&i) = self.first_operation_involving_qubit.get(&qubit) {
self.graph.update_edge(node.into(), i.into(), ());
self.first_parallel_block.remove(&i);
} else if let Some(fa) = self.first_all {
self.graph
.update_edge(node.into(), self.first_all.unwrap().into(), ());
self.first_parallel_block.remove(&fa);
}
let qubit_presence = self.first_operation_involving_qubit.insert(qubit, node);
self.first_parallel_block.insert(node);
if qubit_presence.is_none() {
if let Some(first_all) = self.first_all {
self.last_operation_involving_qubit.insert(qubit, first_all);
} else {
self.last_operation_involving_qubit.insert(qubit, node);
}
}
}
fn update_from_all_operation_front(&mut self, node: NodeIndex<usize>) {
if self.last_all.is_none() {
self.last_all = Some(node);
}
self.first_all = Some(node);
self.first_parallel_block.clear();
self.first_parallel_block.insert(node);
if self.last_parallel_block.is_empty() {
self.last_parallel_block.insert(node);
}
if self.first_operation_involving_qubit.is_empty()
&& self.last_operation_involving_qubit.is_empty()
{
self.first_operation_involving_qubit.insert(0, node);
self.last_operation_involving_qubit.insert(0, node);
} else {
let mut temp_map: HashMap<usize, NodeIndex<usize>> =
HashMap::with_capacity(self.first_operation_involving_qubit.capacity());
for (&qubit, &old_node) in &self.first_operation_involving_qubit {
self.graph.update_edge(node.into(), old_node.into(), ());
temp_map.insert(qubit, node);
}
self.first_operation_involving_qubit = temp_map;
}
}
fn is_definition_classical_populate(
&mut self,
node: NodeIndex<usize>,
operation: Operation,
) -> bool {
match &operation {
Operation::DefinitionBit(_) => {
let new_op: DefinitionBit = operation.clone().try_into().unwrap();
for i in 0..*new_op.length() {
self.first_operation_involving_classical
.insert((String::from(new_op.name()), i), node);
self.last_operation_involving_classical
.insert((String::from(new_op.name()), i), node);
}
true
}
Operation::DefinitionComplex(_) => {
let new_op: DefinitionComplex = operation.clone().try_into().unwrap();
for i in 0..*new_op.length() {
self.first_operation_involving_classical
.insert((String::from(new_op.name()), i), node);
self.last_operation_involving_classical
.insert((String::from(new_op.name()), i), node);
}
true
}
Operation::DefinitionFloat(_) => {
let new_op: DefinitionFloat = operation.clone().try_into().unwrap();
for i in 0..*new_op.length() {
self.first_operation_involving_classical
.insert((String::from(new_op.name()), i), node);
self.last_operation_involving_classical
.insert((String::from(new_op.name()), i), node);
}
true
}
Operation::DefinitionUsize(_) => {
let new_op: DefinitionUsize = operation.clone().try_into().unwrap();
for i in 0..*new_op.length() {
self.first_operation_involving_classical
.insert((String::from(new_op.name()), i), node);
self.last_operation_involving_classical
.insert((String::from(new_op.name()), i), node);
}
true
}
_ => false,
}
}
fn update_classical_back(&mut self, node: NodeIndex<usize>, operation: Operation) {
match operation.involved_classical() {
InvolvedClassical::Set(x) => {
for (name, readout) in &x {
if self
.last_operation_involving_classical
.insert((String::clone(name), *readout), node)
.is_none()
{
self.first_operation_involving_classical
.insert((String::clone(name), *readout), node);
}
}
}
InvolvedClassical::All(x) | InvolvedClassical::AllQubits(x) => {
let mut temp_map: HashMap<(String, usize), NodeIndex<usize>> =
HashMap::with_capacity(self.last_operation_involving_classical.capacity());
for (name, readout) in self.last_operation_involving_classical.keys() {
if *name == x {
temp_map.insert((String::clone(name), *readout), node);
}
}
self.last_operation_involving_classical
.clone_from(&temp_map);
}
InvolvedClassical::None => (),
}
}
fn update_classical_front(&mut self, node: NodeIndex<usize>, operation: Operation) {
match operation.involved_classical() {
InvolvedClassical::Set(x) => {
for (name, readout) in &x {
if self
.first_operation_involving_classical
.insert((String::clone(name), *readout), node)
.is_none()
{
self.last_operation_involving_classical
.insert((String::clone(name), *readout), node);
}
}
}
InvolvedClassical::All(x) | InvolvedClassical::AllQubits(x) => {
let mut temp_map: HashMap<(String, usize), NodeIndex<usize>> =
HashMap::with_capacity(self.first_operation_involving_classical.capacity());
for (name, readout) in self.first_operation_involving_classical.keys() {
if *name == x {
temp_map.insert((String::clone(name), *readout), node);
}
}
self.first_operation_involving_classical
.clone_from(&temp_map);
}
InvolvedClassical::None => (),
}
}
pub fn execution_blocked(
&self,
already_executed: &[NodeIndex<usize>],
to_be_executed: &NodeIndex<usize>,
) -> Vec<NodeIndex<usize>> {
let mut blocking_elements: Vec<NodeIndex<usize>> = vec![];
let mut rev_graph: Graph<Operation, (), Directed, usize> = self.graph.clone();
rev_graph.reverse();
let mut dfs = Dfs::new(&rev_graph, (*to_be_executed).into());
dfs.next(&rev_graph);
while let Some(nxt) = dfs.next(&rev_graph) {
if !already_executed.contains(&nxt.index()) {
blocking_elements.push(nxt.index());
}
}
blocking_elements.sort_unstable();
blocking_elements
}
pub fn blocking_predecessors(
&self,
already_executed: &[NodeIndex<usize>],
to_be_executed: &NodeIndex<usize>,
) -> Vec<NodeIndex<usize>> {
let mut blocking_elements: Vec<NodeIndex<usize>> = vec![];
let neighbor_iter = self
.graph
.neighbors_directed((*to_be_executed).into(), Incoming);
for nxt in neighbor_iter {
if !already_executed.contains(&nxt.index()) {
blocking_elements.push(nxt.index());
}
}
blocking_elements.sort_unstable();
blocking_elements
}
pub fn new_front_layer(
&self,
already_executed: &[NodeIndex<usize>],
current_front_layer: &[NodeIndex<usize>],
to_be_executed: &NodeIndex<usize>,
) -> Result<Vec<NodeIndex<usize>>, RoqoqoError> {
if !current_front_layer.contains(to_be_executed) {
Err(RoqoqoError::GenericError {
msg: "The Operation to be executed is not in the current front layer.".to_string(),
})
} else {
let mut current_front_layer = current_front_layer.to_vec();
let mut added: bool = false;
let neighbor_iter = self
.graph
.neighbors_directed((*to_be_executed).into(), Outgoing);
let empty: bool = neighbor_iter.clone().next().is_none();
let mut extended_a_e: Vec<NodeIndex<usize>> = Vec::from(already_executed);
extended_a_e.push(*to_be_executed);
for nxt in neighbor_iter {
if self
.execution_blocked(&extended_a_e, &nxt.index())
.is_empty()
{
current_front_layer.push(nxt.index());
added = true;
}
}
if added || empty {
current_front_layer.remove(
current_front_layer
.iter()
.position(|&x| x == *to_be_executed)
.unwrap(),
);
}
Ok(current_front_layer)
}
}
pub fn parallel_blocks(&'_ self) -> ParallelBlocks<'_> {
ParallelBlocks {
dag: self,
parallel_block: Vec::<NodeIndex<usize>>::new(),
already_executed: Vec::<NodeIndex<usize>>::new(),
}
}
pub fn successors(&'_ self, node: NodeIndex<usize>) -> Neighbors<'_, (), usize> {
self.graph.neighbors_directed(node.into(), Outgoing)
}
pub fn commuting_operations(&self) -> &Vec<usize> {
&self.commuting_operations
}
pub fn first_parallel_block(&self) -> &HashSet<usize> {
&self.first_parallel_block
}
pub fn last_parallel_block(&self) -> &HashSet<usize> {
&self.last_parallel_block
}
pub fn first_operation_involving_qubit(&self) -> &HashMap<usize, usize> {
&self.first_operation_involving_qubit
}
pub fn last_operation_involving_qubit(&self) -> &HashMap<usize, usize> {
&self.last_operation_involving_qubit
}
pub fn first_operation_involving_classical(&self) -> &HashMap<(String, usize), usize> {
&self.first_operation_involving_classical
}
pub fn last_operation_involving_classical(&self) -> &HashMap<(String, usize), usize> {
&self.last_operation_involving_classical
}
pub fn get(&self, node: NodeIndex<usize>) -> Option<&Operation> {
self.graph.node_weight(node.into())
}
}
impl From<Circuit> for CircuitDag {
fn from(circuit: Circuit) -> Self {
let mut new_dag = CircuitDag {
graph: Graph::<Operation, (), Directed, usize>::with_capacity(
circuit.len(),
circuit.operations().len(),
),
commuting_operations: Vec::<NodeIndex<usize>>::new(),
first_parallel_block: HashSet::<NodeIndex<usize>>::new(),
last_parallel_block: HashSet::<NodeIndex<usize>>::new(),
first_all: Option::<NodeIndex<usize>>::None,
last_all: Option::<NodeIndex<usize>>::None,
first_operation_involving_qubit: HashMap::<usize, NodeIndex<usize>>::new(),
last_operation_involving_qubit: HashMap::<usize, NodeIndex<usize>>::new(),
first_operation_involving_classical: HashMap::<(String, usize), NodeIndex<usize>>::new(
),
last_operation_involving_classical: HashMap::<(String, usize), NodeIndex<usize>>::new(),
_roqoqo_version: RoqoqoVersion,
};
for operation in circuit.iter() {
new_dag.add_to_back(operation.clone());
}
new_dag
}
}
impl From<CircuitDag> for Circuit {
fn from(dag: CircuitDag) -> Circuit {
let mut circuit: Circuit = Circuit::new();
match toposort(&dag.graph, None) {
Ok(order) => {
for i in order {
circuit.add_operation(dag.graph.node_weight(i).unwrap().clone());
}
}
Err(_) => {
println!("Error: graph not acyclic");
}
}
circuit
}
}
impl Iterator for ParallelBlocks<'_> {
type Item = Vec<NodeIndex<usize>>;
fn next(&mut self) -> Option<Self::Item> {
if self.parallel_block.is_empty() && self.already_executed.is_empty() {
for node in &self.dag.first_parallel_block {
self.parallel_block.push(*node);
}
return Some(self.parallel_block.clone());
}
for node in &self.parallel_block {
self.already_executed.push(*node);
}
let mut new_parallel_block: Vec<NodeIndex<usize>> = Vec::new();
for node in &self.parallel_block {
let neighbor_iter = self.dag.graph.neighbors_directed((*node).into(), Outgoing);
for nxt in neighbor_iter {
if self
.dag
.execution_blocked(self.already_executed.as_slice(), &nxt.index())
.is_empty()
&& !new_parallel_block.iter().any(|id| *id == nxt.index())
{
new_parallel_block.push(nxt.index());
}
}
}
self.parallel_block.clone_from(&new_parallel_block);
if new_parallel_block.is_empty() {
return None;
}
Some(new_parallel_block)
}
}
impl crate::operations::SupportedVersion for CircuitDag {
fn minimum_supported_roqoqo_version(&self) -> (u32, u32, u32) {
let mut current_minimum_version = (1, 0, 0);
for index in self.graph.node_indices() {
let node_op = self.get(index.index()).unwrap();
let comparison_version = node_op.minimum_supported_roqoqo_version();
crate::update_roqoqo_version(&mut current_minimum_version, comparison_version);
}
current_minimum_version
}
}
#[cfg(test)]
mod tests {
use crate::operations::*;
use crate::{Circuit, CircuitDag};
use test_case::test_case;
static DEFAULT_NODE_NUMBER: usize = 10;
static DEFAULT_EDGE_NUMBER: usize = 30;
#[test_case(Operation::from(PauliX::new(0)))]
#[test_case(Operation::from(PauliY::new(1)))]
#[test_case(Operation::from(ControlledPauliZ::new(0, 1)))]
fn check_node_existance(operation: Operation) {
let mut dag: CircuitDag =
CircuitDag::with_capacity(DEFAULT_NODE_NUMBER, DEFAULT_EDGE_NUMBER);
dag.add_to_back(operation.clone());
assert!(dag.graph.node_count() == 1);
dag.add_to_front(operation);
dag.add_to_back(Operation::from(CNOT::new(0, 1)));
assert!(dag.graph.node_count() == 3);
}
#[test_case(Operation::from(PauliX::new(0)), Operation::from(PauliY::new(0)))]
#[test_case(Operation::from(PauliZ::new(0)), Operation::from(CNOT::new(0, 1)))]
fn check_node_count(operation1: Operation, operation2: Operation) {
let mut dag: CircuitDag =
CircuitDag::with_capacity(DEFAULT_NODE_NUMBER, DEFAULT_EDGE_NUMBER);
dag.add_to_back(operation1.clone());
dag.add_to_front(operation2);
assert!(dag.graph.node_count() == 2);
dag.add_to_back(operation1);
assert!(dag.graph.node_count() == 3);
}
#[test_case(Operation::from(PauliX::new(0)), Operation::from(PauliY::new(0)))]
#[test_case(
Operation::from(PauliX::new(0)),
Operation::from(PragmaRepeatedMeasurement::new(String::from("ro"), 1, None,))
)]
#[test_case(
Operation::from(PragmaRepeatedMeasurement::new(String::from("ro"), 1, None,)),
Operation::from(PauliX::new(0))
)]
#[test_case(
Operation::from(PragmaRepeatedMeasurement::new(String::from("ro"), 1, None,)),
Operation::from(PragmaRepeatedMeasurement::new(String::from("ro"), 1, None,))
)]
fn check_edge(operation1: Operation, operation2: Operation) {
let mut dag: CircuitDag =
CircuitDag::with_capacity(DEFAULT_NODE_NUMBER, DEFAULT_EDGE_NUMBER);
dag.add_to_back(Operation::from(DefinitionBit::new(
"ro".to_string(),
4,
false,
)));
let ind1 = dag.add_to_back(operation1);
let ind2 = dag.add_to_back(operation2);
assert!(dag
.graph
.contains_edge(ind1.unwrap().into(), ind2.unwrap().into()));
}
#[test_case(Operation::from(PragmaRepeatedMeasurement::new(String::from("ro"), 1, None)))]
#[test_case(Operation::from(PragmaRepeatedMeasurement::new(String::from("ri"), 2, None)))]
fn check_first_last_all_existence(operation: Operation) {
let mut dag: CircuitDag =
CircuitDag::with_capacity(DEFAULT_NODE_NUMBER, DEFAULT_EDGE_NUMBER);
assert!(dag.first_all.is_none());
assert!(dag.last_all.is_none());
let ind_back = dag.add_to_back(operation.clone());
let ind_front = dag.add_to_front(operation);
assert!(dag.first_all.is_some());
assert!(dag.last_all.is_some());
assert!(dag.first_all.unwrap() == ind_front.unwrap());
assert!(dag.last_all.unwrap() == ind_back.unwrap());
}
#[test_case(
Operation::from(PragmaRepeatedMeasurement::new(String::from("ro"), 1, None)),
Operation::from(PragmaRepeatedMeasurement::new(String::from("ri"), 2, None))
)]
fn check_first_last_all_order(operation1: Operation, operation2: Operation) {
let mut dag: CircuitDag =
CircuitDag::with_capacity(DEFAULT_NODE_NUMBER, DEFAULT_EDGE_NUMBER);
dag.add_to_back(operation1);
dag.add_to_front(operation2);
assert!(dag.first_all.is_some());
assert!(dag.last_all.is_some());
assert_ne!(
dag.graph.node_weight(dag.first_all.unwrap().into()),
dag.graph.node_weight(dag.last_all.unwrap().into())
);
}
#[test_case(Operation::from(PragmaRepeatedMeasurement::new(String::from("ro"), 1, None,)))]
#[test_case(Operation::from(PragmaRepeatedMeasurement::new(String::from("ri"), 2, None,)))]
fn check_operation_involving_qubits_all(operation: Operation) {
let mut dag: CircuitDag =
CircuitDag::with_capacity(DEFAULT_NODE_NUMBER, DEFAULT_EDGE_NUMBER);
assert!(dag.first_operation_involving_qubit().is_empty());
assert!(dag.last_operation_involving_qubit().is_empty());
dag.add_to_front(operation.clone());
let back = dag.add_to_back(Operation::from(PauliX::new(0)));
assert_eq!(dag.last_operation_involving_qubit().get(&0), back.as_ref());
let front = dag.add_to_front(Operation::from(CNOT::new(0, 1)));
assert_eq!(dag.last_operation_involving_qubit().get(&0), back.as_ref());
assert_eq!(
dag.first_operation_involving_qubit().get(&0),
front.as_ref()
);
assert_ne!(dag.last_operation_involving_qubit().get(&0), front.as_ref());
assert_ne!(dag.first_operation_involving_qubit().get(&0), back.as_ref());
let new_front_all = dag.add_to_front(operation.clone());
let new_back_all = dag.add_to_back(operation);
assert!(dag
.graph
.contains_edge(new_front_all.unwrap().into(), front.unwrap().into()));
assert!(dag
.graph
.contains_edge(back.unwrap().into(), new_back_all.unwrap().into()));
}
#[test_case(vec![Operation::from(CNOT::new(0,1)), Operation::from(PauliX::new(0)), Operation::from(PauliY::new(1))])]
#[test_case(vec![Operation::from(PauliZ::new(0)), Operation::from(ControlledPauliZ::new(1,2))])]
fn test_new_from_circuit(op_vec: Vec<Operation>) {
let mut circuit: Circuit = Circuit::new();
for op in &op_vec {
circuit.add_operation((*op).clone());
}
let dag: CircuitDag = CircuitDag::from(circuit);
assert!(!dag.first_operation_involving_qubit().is_empty());
assert!(!dag.last_operation_involving_qubit().is_empty());
assert_eq!(dag.graph.node_count(), op_vec.len());
}
#[test_case(vec![Operation::from(CNOT::new(0,1)), Operation::from(PauliX::new(0)), Operation::from(PauliY::new(0)), Operation::from(PauliZ::new(0))])]
#[test_case(vec![Operation::from(PauliZ::new(0)), Operation::from(ControlledPauliZ::new(0,1))])]
fn test_from_circuitdag(op_vec: Vec<Operation>) {
let mut dag: CircuitDag =
CircuitDag::with_capacity(DEFAULT_NODE_NUMBER, DEFAULT_EDGE_NUMBER);
for op in &op_vec {
dag.add_to_back(op.clone());
}
let circuit = Circuit::from(dag.clone());
assert_eq!(circuit.len(), dag.graph.node_count());
circuit.iter().enumerate().for_each(|(ind, oper)| {
assert_eq!(*oper, *dag.graph.node_weight(ind.into()).unwrap());
});
}
}