Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
bb95636
Add helper function in PauliString to calculate hamming weight
Jul 4, 2025
55ae0b7
Update connectivity to fix unintended behavior
Jul 4, 2025
eb01544
Correct pivot selection for custom callback
Jul 4, 2025
3099c2d
Add more helper functions to CliffordTableau
Jul 4, 2025
7260616
Add more comments on structure of CliffordTableau
Jul 4, 2025
d203c63
Add helper functions for PermRowCol
Jul 4, 2025
4fe69af
Implement PermRowCol for CliffordTableau
Jul 4, 2025
f84c0de
Add tests for PermRowCol for CliffordTableau
Jul 4, 2025
98a504c
Update benchmark code
Jul 4, 2025
29f2a00
Use alias for complex type in CliffordCallback
Jul 4, 2025
3892013
Remove unused lifetime in CallbackCliffordSynthesizer
Jul 4, 2025
a6fd07c
Remove unused import
Jul 4, 2025
dd3601a
Remove println statement
Jul 4, 2025
3ac86d0
Remove unused imports and print statements
Jul 4, 2025
4492cb8
Update test for architecture
Aug 12, 2025
4bde6d9
Set function as public
Aug 14, 2025
7ac2de6
Rename edge_ and node_bound to edge_ and node_count
Sep 20, 2025
05539f8
Add clearer error messages calling Clifford Tableau functions
Sep 20, 2025
d8cc290
Replace clean_naive_pivot with clean_pivot
Oct 6, 2025
dd42947
Add example from syn into synpy integration tests
Oct 6, 2025
d684b90
Add PermRowCol as strategy in pauli_exponential.rs
Oct 6, 2025
f0cfd71
Add ability to modify row and column selection strategy for PermRowCo…
Oct 6, 2025
99a5ac9
Convert from syn to synir
Oct 7, 2025
b0f56e8
Update formatting of clifford_tableau/naive.rs
Oct 7, 2025
13235f7
Formatted test_pauliopt.py
Oct 7, 2025
fc94cd1
Remove derived Default for Connectivity and PermRowCol synthesizer
Oct 11, 2025
a0ff746
Remove unneeded dollar signs from assert message
Oct 11, 2025
eba8342
Add comments on gate direction back to clean_z_pivot
Oct 11, 2025
91f3c40
Remove unused import
Oct 11, 2025
d2848df
Shift permrowcol tests to separate file, move common.rs to common/mod…
Oct 11, 2025
3c59e50
Update permrowcol `pick_column` to not add weight of chosen pivot row
Oct 11, 2025
7ebc5e3
Shift prc synthesis to permrowcol test file
Oct 11, 2025
951bcfe
Add more tests to ensure prc column selection is correct
Oct 11, 2025
4bae383
Remove unused imports
Oct 11, 2025
8db28a8
Reorder prc synthesis tests to be uniform
Oct 11, 2025
67beeb6
Merge branch 'main' into perm_row_col
Aerylia Oct 15, 2025
54f6dd0
Fix rwlock stuff
Aerylia Oct 15, 2025
a1b74e2
Test refactor and bugfixes
Aerylia Oct 15, 2025
25c2c98
Custom Callback rewrite
Aerylia Oct 15, 2025
bee7694
Remove extra scope
Aerylia Oct 15, 2025
ada9158
Refactor integration tests to use macros
Aerylia Oct 21, 2025
5047359
Add check to ensure pivot qubit is non-identity
Oct 24, 2025
08f9939
Format file
Oct 24, 2025
0104944
Modify pick_column to penalize identity on row
Oct 24, 2025
701951f
Allow identity_2qb_ct test for custom callback to return a permuted CT
Oct 24, 2025
c32da81
Remove print statement
Oct 24, 2025
3f16f1a
Add comments for permutation direction
Oct 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions synir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ petgraph = { version = "0.8.2", features = ["stable_graph"], git = "https://gith
[dev-dependencies]
criterion = "0.5.1"
rand = "0.9.0"
paste = "1.0.15"

[[bench]]
name = "clifford_tableau"
Expand Down
2 changes: 1 addition & 1 deletion synir/benches/clifford_tableau.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use bitvec::prelude::Lsb0;
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
use synir::data_structures::CliffordTableau;
use synir::data_structures::PauliString;
use synir::ir::clifford_tableau::naive::NaiveCliffordSynthesizer;
use synir::ir::clifford_tableau::CallbackCliffordSynthesizer;
use synir::ir::clifford_tableau::NaiveCliffordSynthesizer;
use synir::ir::CliffordGates;
use synir::ir::Synthesizer;
use synir::IndexType;
Expand Down
11 changes: 5 additions & 6 deletions synir/benches/connectivity.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use criterion::{black_box, criterion_group, Criterion};
use petgraph::visit::Walker;
use rand::prelude::IndexedRandom;
use rand::seq::SliceRandom;
use rand::Rng;
use rand::{rng, Rng};
Comment thread
Aerylia marked this conversation as resolved.
use synir::architecture::connectivity::Connectivity;
use synir::architecture::Architecture;

Expand All @@ -11,7 +10,7 @@ fn random_connected_connectivity(
extra_edges: usize,
Comment thread
Aerylia marked this conversation as resolved.
subset_length: usize,
) -> (Connectivity, Vec<usize>, usize) {
let mut rng = rand::thread_rng();
let mut rng = rng();
let mut edges = Vec::new();

let mut nodes: Vec<usize> = (0..num_nodes).collect();
Expand All @@ -22,8 +21,8 @@ fn random_connected_connectivity(

// Add extra random edges:
while edges.len() < (num_nodes - 1) + extra_edges {
let a = rng.gen_range(0..num_nodes);
let b = rng.gen_range(0..num_nodes);
let a = rng.random_range(0..num_nodes);
let b = rng.random_range(0..num_nodes);
if a != b && !edges.contains(&(a, b)) && !edges.contains(&(b, a)) {
edges.push((a, b));
}
Expand All @@ -39,7 +38,7 @@ fn random_connected_connectivity(
}

fn get_cx_ladder_connectivity((connectivity, nodes, root): &(Connectivity, Vec<usize>, usize)) {
let _ = connectivity.get_cx_ladder(&nodes, &root);
let _ = connectivity.get_cx_ladder(nodes, root);
}

pub fn connectivity_bench(c: &mut Criterion) {
Expand Down
2 changes: 1 addition & 1 deletion synir/src/architecture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub trait Architecture {
fn best_path(&self, i: GraphIndex, j: GraphIndex) -> Vec<GraphIndex>;
fn distance(&self, i: GraphIndex, j: GraphIndex) -> usize;
fn neighbors(&self, i: GraphIndex) -> Vec<GraphIndex>;
fn non_cutting(&mut self) -> &Vec<GraphIndex>;
fn non_cutting(&self) -> &Vec<GraphIndex>;
fn get_cx_ladder(
&self,
nodes: &[GraphIndex],
Expand Down
81 changes: 41 additions & 40 deletions synir/src/architecture/connectivity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,11 @@ pub struct Connectivity {

impl Connectivity {
pub fn new(num_qubits: usize) -> Self {
let graph = StableUnGraph::with_capacity(num_qubits, 0);

Connectivity {
graph,
non_cutting: Default::default(),
prev: Default::default(),
distance: HashMap::new(),
let mut graph = StableUnGraph::with_capacity(num_qubits, 0);
for _ in 0..num_qubits {
graph.add_node(());
}
Connectivity::from_graph(graph)
}

pub fn line(num_qubits: usize) -> Self {
Expand Down Expand Up @@ -80,13 +77,22 @@ impl Connectivity {
}

pub fn from_edges(edges: &[(GraphIndex, GraphIndex)]) -> Self {
let graph = StableUnGraph::from_edges(edges);
Connectivity::from_graph(graph)
if edges.len() > 0 {
let mut graph = StableUnGraph::from_edges(edges);
graph.edge_weights_mut().for_each(|weight| *weight = 1); // Default weight of 1 for unweighted edges
Connectivity::from_graph(graph)
} else {
Connectivity::new(1)
}
}

pub fn from_weighted_edges(edges: &[(GraphIndex, GraphIndex, EdgeWeight)]) -> Self {
let graph = StableUnGraph::from_edges(edges);
Connectivity::from_graph(graph)
if edges.len() > 0 {
let graph = StableUnGraph::from_edges(edges);
Connectivity::from_graph(graph)
} else {
Connectivity::new(1)
}
}

pub fn from_graph(graph: StableUnGraph<NodeWeight, EdgeWeight, GraphIndex>) -> Self {
Expand All @@ -109,6 +115,14 @@ impl Connectivity {
.collect()
}

pub fn node_count(&self) -> usize {
self.graph.node_count()
}

pub fn edge_count(&self) -> usize {
self.graph.edge_count()
}

pub fn edges(&self) -> Vec<(GraphIndex, GraphIndex)> {
self.graph
.edge_references()
Expand Down Expand Up @@ -158,41 +172,21 @@ impl Connectivity {

impl Architecture for Connectivity {
fn best_path(&self, i: GraphIndex, j: GraphIndex) -> Vec<GraphIndex> {
assert!(
Comment thread
Aerylia marked this conversation as resolved.
i < self.graph.node_count(),
"architecture does not contain node {i}"
);
assert!(
j < self.graph.node_count(),
"architecture does not contain node {j}"
);
self.path_from_shortest_path_tree(i, j)
}

fn distance(&self, i: GraphIndex, j: GraphIndex) -> usize {
assert!(
i < self.graph.node_count(),
"architecture does not contain node {i}"
);
assert!(
j < self.graph.node_count(),
"architecture does not contain node {j}"
);
self.distance[&(self.graph.from_index(i), self.graph.from_index(j))]
}

fn neighbors(&self, i: GraphIndex) -> Vec<GraphIndex> {
assert!(
i < self.graph.node_count(),
"architecture does not contain node {i}"
);
self.graph
.neighbors(self.graph.from_index(i))
.map(|neighbor| neighbor.index())
.collect()
}

fn non_cutting(&mut self) -> &Vec<GraphIndex> {
fn non_cutting(&self) -> &Vec<GraphIndex> {
&self.non_cutting
}

Expand Down Expand Up @@ -429,7 +423,7 @@ mod tests {
fn test_best_simple_path() {
let new_architecture = Connectivity::from_edges(&setup_simple());

assert_eq!(new_architecture.best_path(0, 4), vec![0, 1, 2, 4]);
assert_eq!(new_architecture.best_path(0, 4), vec![0, 5, 4]);
}

#[test]
Expand All @@ -440,7 +434,7 @@ mod tests {
}

#[test]
#[should_panic = "architecture does not contain node 6"]
#[should_panic = "index out of bounds: the len is 6 but the index is 6"]
fn test_best_path_missing() {
let new_architecture = Connectivity::from_edges(&setup_simple());
new_architecture.best_path(5, 6);
Expand All @@ -455,7 +449,14 @@ mod tests {
}

#[test]
#[should_panic = "architecture does not contain node 6"]
fn test_simple_distance() {
let new_architecture = Connectivity::from_edges(&setup_simple());
assert_eq!(1, new_architecture.distance(0, 1));
assert_eq!(2, new_architecture.distance(1, 4));
}

#[test]
#[should_panic = "no entry found for key"]
fn test_distance_missing() {
let new_architecture = Connectivity::from_edges(&setup_simple());
new_architecture.distance(5, 6);
Expand All @@ -468,27 +469,27 @@ mod tests {
}

#[test]
#[should_panic = "architecture does not contain node 7"]
#[should_panic = "no entry found for key"]
fn test_neighbor_missing() {
let new_architecture = Connectivity::from_edges(&setup_simple());
new_architecture.distance(2, 7);
}

#[test]
fn test_non_cutting() {
let mut new_architecture = Connectivity::from_edges(&setup_simple());
let new_architecture = Connectivity::from_edges(&setup_simple());
assert_eq!(&new_architecture.nodes(), new_architecture.non_cutting());
}

#[test]
fn test_non_cutting_line() {
let mut line_architecture = Connectivity::line(5);
let line_architecture = Connectivity::line(5);
assert_eq!(*line_architecture.non_cutting(), vec![0, 4]);
}

#[test]
fn test_non_cutting_grid() {
let mut line_architecture = Connectivity::grid(3, 3);
let line_architecture = Connectivity::grid(3, 3);
assert_eq!(
*line_architecture.non_cutting(),
vec![0, 1, 2, 3, 4, 5, 6, 7, 8]
Expand All @@ -497,7 +498,7 @@ mod tests {

#[test]
fn test_non_cutting_complete() {
let mut line_architecture = Connectivity::complete(3);
let line_architecture = Connectivity::complete(3);
assert_eq!(*line_architecture.non_cutting(), vec![0, 1, 2]);
}

Expand Down
54 changes: 53 additions & 1 deletion synir/src/data_structures/clifford_tableau.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use bitvec::prelude::BitVec;
use bitvec::vec;
use itertools::{izip, Itertools};
use std::fmt;
use std::iter::zip;
Expand All @@ -9,10 +10,12 @@ use super::{
pauli_string::{cx, PauliString},
IndexType, PropagateClifford,
};
use crate::data_structures::PauliLetter;

#[derive(PartialEq, Eq, Debug, Clone, Default)]
pub struct CliffordTableau {
// We keep track of the pauli letters per qubit not per stabilizer
// Each Pauli Column contains 2 BitStrings of length 2 * n and corresponds to all operators on one qubit (vertical)
pauli_columns: Vec<PauliString>,
signs: BitVec,
size: usize, // https://quantumcomputing.stackexchange.com/questions/28740/tracking-the-signs-of-the-inverse-tableau
Expand Down Expand Up @@ -51,10 +54,40 @@ impl CliffordTableau {
self.signs[n..].to_bitvec()
}

pub(crate) fn destabilizer(&self, qubit: usize, index: usize) -> PauliLetter {
let n = self.size();
assert!(
index < n && qubit < n,
"Given index: {index} or qubit: {qubit} out of bounds for clifford tableau of size {n}"
);

PauliLetter::new(
self.pauli_columns[qubit].x(index),
self.pauli_columns[qubit].z(index),
)
}

pub(crate) fn stabilizer(&self, qubit: usize, index: usize) -> PauliLetter {
let n = self.size();
assert!(
index < n && qubit < n,
"Given index: {index} or qubit: {qubit} out of bounds for clifford tableau of size {n}"
);

PauliLetter::new(
self.pauli_columns[qubit].x(index + n),
self.pauli_columns[qubit].z(index + n),
)
}

pub(crate) fn column(&self, i: usize) -> &PauliString {
&self.pauli_columns[i]
}

pub(crate) fn columns(&self) -> &Vec<PauliString> {
&self.pauli_columns
}

pub fn compose(&self, rhs: &Self) -> Self {
rhs.prepend(self)
}
Expand Down Expand Up @@ -158,7 +191,7 @@ impl CliffordTableau {
}
}

pub fn permute(&mut self, permutation_vector: &[usize]) {
pub fn permute(&mut self, permutation_vector: Vec<usize>) {
assert_eq!(
permutation_vector
.iter()
Expand All @@ -174,6 +207,25 @@ impl CliffordTableau {
.collect::<Vec<_>>();
self.pauli_columns = sorted_pauli_columns;
}

/// Calculates the row permutation of a Clifford tableau if it is a permutation of identity.
/// The permutation indicates perm[physical] = logical, such that the for [1, 0, 2], physical qubit 0 stores logical qubit 1.
pub fn get_permutation(&self) -> Option<Vec<usize>> {
let mut row_permutation = Vec::new();
//let mut col_permutation = Vec::new();
for pauli_column in self.pauli_columns.iter() {
if pauli_column.x_weight() != 1 || pauli_column.z_weight() != 1 {
return None;
}
//col_permutation.push(pauli_column.z.first_one().unwrap() - self.size);
row_permutation.push(pauli_column.x.first_one().unwrap());
}
let _ = (0..self.size)
.map(|i| row_permutation.iter().find_position(|&&x| x == i))
.map(|x| x.unwrap().0)
.collect_vec();
Some(row_permutation)
}
}

impl HasAdjoint for CliffordTableau {
Expand Down
8 changes: 8 additions & 0 deletions synir/src/data_structures/pauli_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ impl PauliString {
self.x[i]
}

pub fn x_weight(&self) -> usize {
self.x.count_ones()
}

pub fn z_weight(&self) -> usize {
self.z.count_ones()
}

pub fn z(&self, i: usize) -> bool {
self.z[i]
}
Expand Down
Loading
Loading