use crate::graph::VType;
use crate::hash_graph::{Graph, GraphLike};
use crate::linalg::Mat2;
use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Pauli {
X,
Y,
Z,
}
#[derive(Debug, Default, Clone)]
pub struct PauliWeb {
pub edge_operators: HashMap<(usize, usize), Pauli>,
}
impl PauliWeb {
pub fn new() -> Self {
Self::default()
}
pub fn set_edge(&mut self, from: usize, to: usize, pauli: Pauli) {
self.edge_operators
.insert((from.min(to), from.max(to)), pauli);
}
pub fn edge(&self, from: usize, to: usize) -> Option<Pauli> {
self.edge_operators
.get(&(from.min(to), from.max(to)))
.copied()
}
pub fn edge_color(&self, from: usize, to: usize) -> Option<&'static str> {
self.edge(from, to).map(|pauli| match pauli {
Pauli::X => "green", Pauli::Y => "blue", Pauli::Z => "red", })
}
}
fn ordered_nodes(g: &Graph) -> (Vec<usize>, HashMap<usize, usize>) {
let mut original: Vec<usize> = g.vertices().collect();
original.sort();
let outputs: Vec<usize> = original
.iter()
.filter(|&&v| !g.inputs().contains(&v) && !g.outputs().contains(&v))
.cloned()
.collect();
let mut vertices = outputs.clone();
vertices.extend(
original
.iter()
.filter(|&&v| {
let vtype = g.vertex_type(v);
vtype != VType::B && !outputs.contains(&v)
})
.cloned(),
);
let index_map: HashMap<usize, usize> =
vertices.iter().enumerate().map(|(i, &v)| (i, v)).collect();
(vertices, index_map)
}
pub fn pw(index_map: &HashMap<usize, usize>, v: &Mat2, g: &Graph) -> PauliWeb {
let n_outs = g.inputs().len() + g.outputs().len();
let mut red_edges = BTreeSet::new();
let mut green_edges = BTreeSet::new();
let mut pw = PauliWeb::new();
for col in 0..v.num_cols() {
if v[(0, col)] == 1 {
let node = *index_map
.get(&(col - n_outs))
.expect("Node index not found in index map.");
let node_color = g.vertex_type(node);
for edge in g.edges() {
if node == edge.0 || node == edge.1 {
if node_color == VType::Z {
green_edges.insert(edge);
} else if node_color == VType::X {
red_edges.insert(edge);
} else {
unreachable!("Unexpected Node color: {:?}", node_color);
}
}
}
}
}
for e in &red_edges {
if green_edges.contains(e) {
pw.set_edge(e.0, e.1, Pauli::Y);
} else {
pw.set_edge(e.0, e.1, Pauli::Z);
}
}
for e in green_edges {
if !red_edges.contains(&e) {
pw.set_edge(e.0, e.1, Pauli::X);
}
}
pw
}
fn draw_mat(_name: &str, _mat: &Mat2) {
let _ = env_logger::builder().is_test(true).try_init();
log::debug!(
"Matrix {} ({}x{}):\n{}",
_name,
_mat.num_rows(),
_mat.num_cols(),
_mat
);
}
pub fn detection_webs(g: &mut Graph) -> Vec<PauliWeb> {
let _ = env_logger::builder().is_test(true).try_init();
g.make_bipartite();
let old_inputs = g.inputs().clone();
let old_outputs = g.outputs().clone();
let mut outputs = Vec::new();
for v in g.vertices() {
if g.vertex_type(v) == VType::B {
for w in g.neighbors(v) {
outputs.push(w);
}
}
}
log::debug!("Outputs: {:?}", outputs);
let outs = outputs.len();
g.set_outputs(outputs);
g.set_inputs(vec![]);
let (nodelist, index_map) = ordered_nodes(g);
log::debug!("Ordered nodes: {:?}", nodelist);
log::debug!("outs: {}", outs);
let big_n = g.adjacency_matrix(Some(&nodelist));
draw_mat("N (adjacency)", &big_n);
let i_n = Mat2::id(outs);
draw_mat("I_n", &i_n);
let zeroblock = Mat2::zeros(big_n.num_rows() - outs, outs);
draw_mat("zeroblock", &zeroblock);
let mdl = i_n.vstack(&zeroblock);
draw_mat("mdl", &mdl);
let md = mdl.hstack(&big_n);
draw_mat("md", &md);
let eye_part = Mat2::id(2 * outs);
let zero_part = Mat2::zeros(2 * outs, md.num_cols() - 2 * outs);
let no_output = eye_part.hstack(&zero_part);
let md_no_output = md.vstack(&no_output);
draw_mat("md_no_output", &md_no_output);
let mdnons = md_no_output.nullspace();
let mut pws = Vec::with_capacity(mdnons.len());
for basis in mdnons.into_iter() {
let pw = pw(&index_map, &basis, g);
pws.push(pw);
}
g.set_outputs(old_outputs);
g.set_inputs(old_inputs);
pws
}
#[cfg(test)]
mod tests {
use super::*;
use crate::graph_loader::load_graph;
use crate::graph_to_svg::graph_to_svg_with_pauliweb;
#[test]
fn test_detection_webs() {
let mut g = Graph::new();
let webs = detection_webs(&mut g);
assert_eq!(webs.len(), 0);
}
#[test]
fn test_new_pauliweb() {
let pw = PauliWeb::new();
assert!(pw.edge_operators.is_empty());
}
#[test]
fn test_set_and_get_edge() {
let mut pw = PauliWeb::new();
pw.set_edge(1, 2, Pauli::X);
assert_eq!(pw.edge(1, 2), Some(Pauli::X));
assert_eq!(pw.edge(2, 1), Some(Pauli::X));
pw.set_edge(1, 2, Pauli::Z);
assert_eq!(pw.edge(1, 2), Some(Pauli::Z));
assert_eq!(pw.edge(1, 3), None);
}
#[test]
fn test_get_edge_color() {
let mut pw = PauliWeb::new();
pw.set_edge(1, 2, Pauli::X);
pw.set_edge(2, 3, Pauli::Y);
pw.set_edge(3, 4, Pauli::Z);
assert_eq!(pw.edge_color(1, 2), Some("green"));
assert_eq!(pw.edge_color(2, 3), Some("blue"));
assert_eq!(pw.edge_color(3, 4), Some("red"));
assert_eq!(pw.edge_color(4, 5), None); }
#[test]
fn test_edge_ordering() {
let mut pw = PauliWeb::new();
pw.set_edge(1, 2, Pauli::X);
pw.set_edge(3, 4, Pauli::Z);
pw.set_edge(2, 1, Pauli::X);
assert_eq!(pw.edge(1, 2), Some(Pauli::X));
pw.set_edge(1, 2, Pauli::Z);
assert_eq!(pw.edge(2, 1), Some(Pauli::Z));
}
fn test_file(name: &str) -> String {
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../")
.join("test_files")
.join(name)
.to_str()
.unwrap()
.to_string()
}
#[test]
fn test_visualize_xx_stab_webs() {
let mut g = load_graph(&test_file("xx_stab.zxg"));
let webs = detection_webs(&mut g);
for web in webs.iter() {
let tmpfile = tempfile::NamedTempFile::new().unwrap();
let svg = graph_to_svg_with_pauliweb(&g, Some(web));
std::fs::write(tmpfile, svg).unwrap();
}
}
#[test]
fn test_visualize_steane_style_steane_stabs() {
let mut g = load_graph(&test_file("steane_style_steane_2_rounds.zxg"));
let webs = detection_webs(&mut g);
for web in webs.iter() {
let tmpfile = tempfile::NamedTempFile::new().unwrap();
let svg = graph_to_svg_with_pauliweb(&g, Some(web));
std::fs::write(tmpfile, svg).unwrap();
}
}
}