#[allow(unused_imports)]
use crate::mwpf::shim::prelude::*;
use crate::mwpf::util::*;
use num_traits::{One, Zero};
pub type VarIndex = usize;
pub type RowIndex = usize;
pub type ColumnIndex = usize;
pub trait MatrixBasic {
fn add_variable(&mut self, edge_index: EdgeIndex) -> Option<VarIndex>;
fn add_constraint(
&mut self,
vertex_index: VertexIndex,
incident_edges: &[EdgeIndex],
parity: bool,
) -> Option<Vec<VarIndex>>;
fn xor_row(&mut self, target: RowIndex, source: RowIndex);
fn swap_row(&mut self, a: RowIndex, b: RowIndex);
fn get_lhs(&self, row: RowIndex, var_index: VarIndex) -> bool;
fn get_rhs(&self, row: RowIndex) -> bool;
fn var_to_edge_index(&self, var_index: VarIndex) -> EdgeIndex;
fn edge_to_var_index(&self, edge_index: EdgeIndex) -> Option<VarIndex>;
fn exists_edge(&self, edge_index: EdgeIndex) -> bool {
self.edge_to_var_index(edge_index).is_some()
}
fn get_edges(&self) -> FastIterSet<EdgeIndex>;
fn get_vertices(&self) -> FastIterSet<VertexIndex>;
}
pub trait MatrixView: MatrixBasic {
fn columns(&mut self) -> usize;
fn column_to_var_index(&self, column: ColumnIndex) -> VarIndex;
fn column_to_edge_index(&self, column: ColumnIndex) -> EdgeIndex {
let var_index = self.column_to_var_index(column);
self.var_to_edge_index(var_index)
}
fn rows(&mut self) -> usize;
fn get_view_edges(&mut self) -> Vec<EdgeIndex> {
(0..self.columns())
.map(|column: usize| self.column_to_edge_index(column))
.collect()
}
fn var_to_column_index(&mut self, var_index: VarIndex) -> Option<ColumnIndex> {
(0..self.columns()).find(|&column| self.column_to_var_index(column) == var_index)
}
fn edge_to_column_index(&mut self, edge_index: EdgeIndex) -> Option<ColumnIndex> {
let var_index = self.edge_to_var_index(edge_index)?;
self.var_to_column_index(var_index)
}
}
pub trait MatrixTight: MatrixView {
fn update_edge_tightness(&mut self, edge_index: EdgeIndex, is_tight: bool);
fn is_tight(&self, edge_index: usize) -> bool;
fn get_tight_edges(&self) -> &FastIterSet<EdgeIndex>;
fn add_variable_with_tightness(&mut self, edge_index: EdgeIndex, is_tight: bool) {
self.add_variable(edge_index);
self.update_edge_tightness(edge_index, is_tight);
}
fn add_tight_variable(&mut self, edge_index: EdgeIndex) {
self.add_variable_with_tightness(edge_index, true)
}
}
pub trait MatrixTail {
fn get_tail_edges(&self) -> &FastIterSet<EdgeIndex>;
fn get_tail_edges_mut(&mut self) -> &mut FastIterSet<EdgeIndex>;
fn set_tail_edges<EdgeIter>(&mut self, edges: EdgeIter)
where
EdgeIter: Iterator<Item = EdgeIndex>,
{
let tail_edges = self.get_tail_edges_mut();
tail_edges.clear();
for edge_index in edges {
tail_edges.insert(edge_index);
}
}
fn get_tail_edges_vec(&self) -> Vec<EdgeIndex> {
let mut edges: Vec<EdgeIndex> = self.get_tail_edges().iter().cloned().collect();
edges.sort();
edges
}
}
pub trait MatrixEchelonTail {
fn get_tail_start_index(&mut self) -> Option<ColumnIndex>;
fn get_corner_row_index(&mut self, tail_start_index: ColumnIndex) -> RowIndex;
}
pub trait MatrixEchelon: MatrixView {
fn get_echelon_info(&mut self) -> &EchelonInfo;
fn get_echelon_info_immutable(&self) -> &EchelonInfo;
fn get_solution(&mut self) -> Option<Subgraph> {
self.get_echelon_info(); let info = self.get_echelon_info_immutable();
if !info.satisfiable {
return None; }
let mut solution = vec![];
for (row, row_info) in info.rows.iter().enumerate() {
debug_assert!(row_info.has_leading());
if self.get_rhs(row) {
let column = row_info.column;
let edge_index = self.column_to_edge_index(column);
solution.push(edge_index);
}
}
Some(solution)
}
fn get_solution_local_minimum<F>(&mut self, weight_of: F) -> Option<Subgraph>
where
F: Fn(EdgeIndex) -> Weight,
{
self.get_echelon_info(); let info = self.get_echelon_info_immutable();
if !info.satisfiable {
return None; }
let mut solution = FastIterSet::new();
for (row, row_info) in info.rows.iter().enumerate() {
debug_assert!(row_info.has_leading());
if self.get_rhs(row) {
let column = row_info.column;
let edge_index = self.column_to_edge_index(column);
solution.insert(edge_index);
}
}
let mut independent_columns = vec![];
for (column, column_info) in info.columns.iter().enumerate() {
if !column_info.is_dependent() {
independent_columns.push(column);
}
}
let mut total_weight = Rational::zero();
for &edge_index in solution.iter() {
total_weight += weight_of(edge_index);
}
let mut pending_flip_edge_indices = vec![];
let mut is_local_minimum = false;
while !is_local_minimum {
is_local_minimum = true;
for &column in independent_columns.iter() {
pending_flip_edge_indices.clear();
let var_index = self.column_to_var_index(column);
let edge_index = self.var_to_edge_index(var_index);
let mut primal_delta = (weight_of(edge_index))
* if solution.contains(&edge_index) {
-Rational::one()
} else {
Rational::one()
};
pending_flip_edge_indices.push(edge_index);
for row in 0..info.rows.len() {
if self.get_lhs(row, var_index) {
debug_assert!(info.rows[row].has_leading());
let flip_column = info.rows[row].column;
debug_assert!(flip_column < column);
let flip_edge_index = self.column_to_edge_index(flip_column);
primal_delta += (weight_of(flip_edge_index))
* if solution.contains(&flip_edge_index) {
-Rational::one()
} else {
Rational::one()
};
pending_flip_edge_indices.push(flip_edge_index);
}
}
if primal_delta < Rational::zero() {
total_weight = total_weight + primal_delta;
for &edge_index in pending_flip_edge_indices.iter() {
if solution.contains(&edge_index) {
solution.remove(&edge_index);
} else {
solution.insert(edge_index);
}
}
is_local_minimum = false;
break; }
}
}
Some(solution.into_iter().collect())
}
}
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "python", pyclass(module = "stabilizer_qec.mwpf", get_all, set_all))]
pub struct EchelonInfo {
pub satisfiable: bool,
pub columns: Vec<ColumnInfo>,
pub effective_rows: usize,
pub rows: Vec<RowInfo>,
}
#[cfg(feature = "python")]
#[pymethods]
impl EchelonInfo {
fn __repr__(&self) -> String {
format!("{:?}", self)
}
fn __str__(&self) -> String {
self.__repr__()
}
}
#[derive(Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "python", pyclass(module = "stabilizer_qec.mwpf", get_all, set_all))]
pub struct ColumnInfo {
pub row: RowIndex,
}
#[cfg(feature = "python")]
#[pymethods]
impl ColumnInfo {
fn __repr__(&self) -> String {
format!("{:?}", self)
}
fn __str__(&self) -> String {
self.__repr__()
}
#[pyo3(name = "is_dependent")]
fn py_is_dependent(&self) -> bool {
self.row != RowIndex::MAX
}
}
impl EchelonInfo {
pub fn new() -> Self {
Self::default()
}
}
impl RowInfo {
pub fn new() -> Self {
Self::default()
}
}
impl ColumnInfo {
pub fn new() -> Self {
Self::default()
}
pub fn not_dependent() -> Self {
Self { row: RowIndex::MAX }
}
pub fn set(&mut self, row: RowIndex) {
debug_assert!(row != RowIndex::MAX);
self.row = row;
}
pub fn is_dependent(&self) -> bool {
self.row != RowIndex::MAX
}
pub fn set_not_dependent(&mut self) {
self.row = RowIndex::MAX;
}
}
impl std::fmt::Debug for ColumnInfo {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if !self.is_dependent() {
write!(f, "Row(*)")
} else {
write!(f, "Row({})", self.row)
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "python", pyclass(module = "stabilizer_qec.mwpf", get_all, set_all))]
pub struct RowInfo {
pub column: ColumnIndex,
}
#[cfg(feature = "python")]
#[pymethods]
impl RowInfo {
fn __repr__(&self) -> String {
format!("{:?}", self)
}
fn __str__(&self) -> String {
self.__repr__()
}
}
impl RowInfo {
pub fn no_leading() -> Self {
Self {
column: ColumnIndex::MAX,
}
}
pub fn set(&mut self, column: ColumnIndex) {
debug_assert!(column != ColumnIndex::MAX);
self.column = column;
}
pub fn has_leading(&self) -> bool {
self.column != ColumnIndex::MAX
}
pub fn set_no_leading(&mut self) {
self.column = ColumnIndex::MAX;
}
}
impl std::fmt::Debug for RowInfo {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if !self.has_leading() {
write!(f, "Col(*)")
} else {
write!(f, "Col({})", self.column)
}
}
}
#[cfg(test)]
pub mod tests {
use super::super::*;
use super::*;
type TightMatrix = Tight<BasicMatrix>;
#[test]
fn matrix_interface_simple() {
let mut matrix = TightMatrix::new();
matrix.add_tight_variable(233);
matrix.add_tight_variable(14);
matrix.add_variable(68);
matrix.add_tight_variable(75);
matrix.printstd();
assert_eq!(matrix.get_view_edges(), [233, 14, 75]);
assert_eq!(matrix.var_to_column_index(0), Some(0));
assert_eq!(matrix.var_to_column_index(1), Some(1));
assert_eq!(matrix.var_to_column_index(2), None);
assert_eq!(matrix.var_to_column_index(3), Some(2));
assert_eq!(matrix.edge_to_column_index(233), Some(0));
assert_eq!(matrix.edge_to_column_index(14), Some(1));
assert_eq!(matrix.edge_to_column_index(68), None);
assert_eq!(matrix.edge_to_column_index(75), Some(2));
assert_eq!(matrix.edge_to_column_index(666), None);
}
#[test]
fn matrix_interface_echelon_info() {
let mut column_info = ColumnInfo::new();
column_info.set(13);
assert_eq!(format!("{column_info:?}"), "Row(13)");
column_info.set_not_dependent();
assert_eq!(format!("{column_info:?}"), "Row(*)");
assert_eq!(format!("{:?}", column_info.clone()), "Row(*)");
let mut row_info = RowInfo::new();
row_info.set(13);
assert_eq!(format!("{row_info:?}"), "Col(13)");
row_info.set_no_leading();
assert_eq!(format!("{row_info:?}"), "Col(*)");
assert_eq!(format!("{:?}", row_info.clone()), "Col(*)");
let echelon_info = EchelonInfo::new();
println!("echelon_info: {echelon_info:?}");
}
#[derive(Default)]
struct TestEdgeWeights {
pub weights: FastIterMap<EdgeIndex, Weight>,
}
impl TestEdgeWeights {
fn new(weights: &[(EdgeIndex, Weight)]) -> Self {
let mut result: TestEdgeWeights = Default::default();
for (edge_index, weight) in weights {
result.weights.insert(edge_index.clone(), weight.clone());
}
result
}
fn get_solution_local_minimum(&self, matrix: &mut Echelon<Tail<BasicMatrix>>) -> Option<Subgraph> {
matrix.get_solution_local_minimum(|edge_index| {
if let Some(weight) = self.weights.get(&edge_index) {
weight.clone()
} else {
Rational::from_float(1.).unwrap()
}
})
}
}
#[test]
fn matrix_interface_echelon_solution() {
let mut matrix = Echelon::<Tail<BasicMatrix>>::new();
let parity_checks = vec![
(vec![0, 1], false),
(vec![1, 2], false),
(vec![2, 3, 7, 8], false),
(vec![3, 4], false),
(vec![4, 5], true),
(vec![5, 6, 7], false),
(vec![6, 9], false),
(vec![0, 8, 9], true),
];
for (vertex_index, (incident_edges, parity)) in parity_checks.iter().enumerate() {
matrix.add_constraint(vertex_index, incident_edges, *parity);
}
matrix.printstd();
assert_eq!(matrix.get_solution(), Some(vec![0, 1, 2, 3, 4]));
let weights = TestEdgeWeights::new(&[
(3, Rational::from_float(10.).unwrap()),
(9, Rational::from_float(10.).unwrap()),
]);
assert_eq!(
sorted_vec_option(weights.get_solution_local_minimum(&mut matrix)),
Some(vec![5, 7, 8])
);
let weights = TestEdgeWeights::new(&[
(7, Rational::from_float(10.).unwrap()),
(9, Rational::from_float(10.).unwrap()),
]);
assert_eq!(
sorted_vec_option(weights.get_solution_local_minimum(&mut matrix)),
Some(vec![3, 4, 8])
);
let weights = TestEdgeWeights::new(&[
(3, Rational::from_float(10.).unwrap()),
(4, Rational::from_float(10.).unwrap()),
(7, Rational::from_float(10.).unwrap()),
]);
assert_eq!(
sorted_vec_option(weights.get_solution_local_minimum(&mut matrix)),
Some(vec![5, 6, 9])
);
}
#[test]
fn matrix_interface_echelon_no_solution() {
let mut matrix = Echelon::<Tail<BasicMatrix>>::new();
let parity_checks = [(vec![0, 1], false), (vec![0, 1], true)];
for (vertex_index, (incident_edges, parity)) in parity_checks.iter().enumerate() {
matrix.add_constraint(vertex_index, incident_edges, *parity);
}
assert_eq!(matrix.get_solution(), None);
let weights = TestEdgeWeights::new(&[]);
assert_eq!(weights.get_solution_local_minimum(&mut matrix), None);
}
}