use approx::abs_diff_eq;
use num::Complex;
use crate::circuit::Circuit;
use crate::graph::GraphLike;
use crate::simplify::full_simp;
use crate::tensor::ToTensor;
use crate::vec_graph::Graph;
pub fn equal_graph_dim(g1: &Graph, g2: &Graph) -> bool {
if g1.inputs().len() != g2.inputs().len() {
return false;
}
if g1.outputs().len() != g2.outputs().len() {
return false;
}
true
}
pub fn equal_circuit_dim(c1: &Circuit, c2: &Circuit) -> bool {
let g1: Graph = c1.to_graph();
let g2: Graph = c2.to_graph();
equal_graph_dim(&g1, &g2)
}
pub fn equal_graph_tensor(g1: &Graph, g2: &Graph) -> bool {
if !equal_graph_dim(g1, g2) {
return false;
}
g1.to_tensorf() == g2.to_tensorf()
}
pub fn equal_circuit_tensor(c1: &Circuit, c2: &Circuit) -> bool {
let g1: Graph = c1.to_graph();
let g2: Graph = c2.to_graph();
equal_graph_tensor(&g1, &g2)
}
pub fn equal_graph_with_options(g1: &Graph, g2: &Graph, up_to_global_phase: bool) -> Option<bool> {
if !equal_graph_dim(g1, g2) {
return Some(false);
}
let mut g = g1.to_adjoint();
g.plug(g2);
full_simp(&mut g);
if g.is_identity() {
if !up_to_global_phase {
let c: Complex<f64> = g.scalar().into();
return Some(abs_diff_eq!(c.arg(), 0.0));
}
return Some(true);
}
None
}
pub fn equal_graph(g1: &Graph, g2: &Graph) -> Option<bool> {
equal_graph_with_options(g1, g2, true)
}
pub fn equal_circuit_with_options(
c1: &Circuit,
c2: &Circuit,
up_to_global_phase: bool,
) -> Option<bool> {
let g1: Graph = c1.to_graph();
let g2: Graph = c2.to_graph();
equal_graph_with_options(&g1, &g2, up_to_global_phase)
}
pub fn equal_circuit(c1: &Circuit, c2: &Circuit) -> Option<bool> {
equal_circuit_with_options(c1, c2, true)
}
#[cfg(test)]
mod tests {
use num::Rational64;
use super::equal_circuit_tensor;
use super::equal_circuit_with_options;
use crate::circuit::Circuit;
#[test]
fn both_circuits_empty() {
let c1 = Circuit::new(1);
let c2 = Circuit::new(1);
assert!(equal_circuit_tensor(&c1, &c2));
assert!(equal_circuit_with_options(&c1, &c2, false).unwrap());
}
#[test]
fn close_but_not_equal() {
let mut c1 = Circuit::new(1);
let mut c2 = Circuit::new(1);
c1.add_gate("x", vec![0]);
c2.add_gate("x", vec![0]);
c2.add_gate_with_phase("rz", vec![0], Rational64::new(1, 1024));
assert!(!equal_circuit_tensor(&c1, &c2));
assert!(equal_circuit_with_options(&c1, &c2, true).is_none());
}
#[test]
fn cx_with_ancilla_as_x() {
let mut c1 = Circuit::new(1);
c1.add_gate("x", vec![0]);
let mut c2 = Circuit::new(2);
c2.add_gate("init_anc", vec![1]); c2.add_gate("x", vec![1]); c2.add_gate("cx", vec![1, 0]); c2.add_gate("x", vec![1]); c2.add_gate("post_sel", vec![1]);
assert!(equal_circuit_tensor(&c1, &c2));
assert!(equal_circuit_with_options(&c1, &c2, true).unwrap());
}
#[test]
fn global_phase() {
let mut c1 = Circuit::new(1);
let mut c2 = Circuit::new(1);
c1.add_gate("x", vec![0]);
c2.add_gate("x", vec![0]);
c2.add_gate("z", vec![0]);
c2.add_gate("x", vec![0]);
c2.add_gate("z", vec![0]);
c2.add_gate("x", vec![0]);
assert!(equal_circuit_with_options(&c1, &c2, true).unwrap());
assert!(!equal_circuit_tensor(&c1, &c2));
assert!(!equal_circuit_with_options(&c1, &c2, false).unwrap());
}
#[test]
fn more_than_64_qubits() {
let mut c1 = Circuit::new(65);
c1.add_gate("h", vec![0]);
for i in 1..65 {
c1.add_gate("cx", vec![0, i]);
}
let c2 = c1.clone();
assert!(equal_circuit_with_options(&c1, &c2, false).unwrap());
}
}