Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion src/data_structures/clifford_tableau.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ impl CliffordTableau {
&self.pauli_columns[i]
}

pub(crate) fn compose(&self, rhs: &Self) -> Self {
pub fn compose(&self, rhs: &Self) -> Self {
rhs.prepend(self)
}

Expand Down
1 change: 1 addition & 0 deletions synpy/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 synpy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ name = "synpy_rust"
crate-type = ["cdylib"]

[dependencies]
bitvec = "1.0.1"
pyo3 = "0.23.3"
syn = { path = "../" }
17 changes: 17 additions & 0 deletions synpy/integration_tests/test_qiskit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from qiskit.quantum_info import Clifford
from qiskit import QuantumCircuit

from synpy.qiskit.plugin import SynPyCliffordPlugin


def test_qiskit_bell() -> None:
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
cliff = Clifford(qc)

plugin = SynPyCliffordPlugin()
circ = plugin.run(cliff, None, None, [])
# circ.draw()

assert circ == qc
28 changes: 28 additions & 0 deletions synpy/python/synpy/qiskit/plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from qiskit.transpiler import CouplingMap, Target
from qiskit.transpiler.passes.synthesis.plugin import HighLevelSynthesisPlugin
from qiskit.quantum_info import Clifford
from qiskit import QuantumCircuit

from synpy.synpy_rust import PyCliffordTableau
from synpy.utils import pycommand_to_qasm


class SynPyCliffordPlugin(HighLevelSynthesisPlugin):
def __init__(self) -> None:
super().__init__()

def run(self, clifford: Clifford, coupling_map: CouplingMap, target: Target, qubits: list) -> QuantumCircuit:
n = clifford.num_qubits
tableau_x = clifford.tableau[:, :n]
tableau_z = clifford.tableau[:, n : 2 * n]

pauli_columns = ["".join(["IZXY"[2 * x + z] for x, z in zip(col_x, col_z)]) for col_x, col_z in zip(tableau_x.T, tableau_z.T)]
signs = clifford.tableau[:, -1].tolist()

# Convert Clifford to CliffordTableau
synpy_tableau = PyCliffordTableau.from_parts(pauli_columns, signs)

# Synthesize CliffordTableau
commands = synpy_tableau.synthesize()
qasm = pycommand_to_qasm(n, commands)
return QuantumCircuit.from_qasm_str(qasm)
10 changes: 10 additions & 0 deletions synpy/python/synpy/synpy_rust.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,17 @@ class PyPauliString(object):
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
def __new__(*args: Any, **kwargs: Any) -> Any: ...

class PyCliffordTableau(object):
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
def __new__(*args: Any, **kwargs: Any) -> Any: ...
@classmethod
def from_parts(cls, *args: Any, **kwargs: Any) -> Any: ...
def synthesize(self, *args: Any, **kwargs: Any) -> Any: ...
def size(self, *args: Any, **kwargs: Any) -> Any: ...
def compose(self, *args: Any, **kwargs: Any) -> Any: ...

__all__ = [
"PyCliffordTableau",
"PyPauliString",
"PyCommand",
"synthesize_pauli_exponential",
Expand Down
17 changes: 17 additions & 0 deletions synpy/python/synpy/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,20 @@ def pycommand_to_tuple(cmd: PyCommand) -> tuple:
return "CZ", cmd[0], cmd[1]
else:
raise ValueError("Unhandled PyCommand variant: " + str(type(cmd)))


def pycommand_to_qasm(n_qubits: int, commands: list[PyCommand]) -> str:
out = [
"OPENQASM 2.0;",
'include "qelib1.inc";',
f"qreg q[{n_qubits}];",
]

for command in commands:
op, *args = pycommand_to_tuple(command)
op = op.lower()
args = [f"q[{arg}]" for arg in args]
s = f'{op} {", ".join(args)};'
out.append(s)

return "\n".join(out)
9 changes: 5 additions & 4 deletions synpy/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
mod synthesis;
mod tableau;
mod validation;

use crate::synthesis::synthesize_pauli_exponential;
use crate::synthesis::{PyCommand, PyPauliString};
use crate::tableau::PyCliffordTableau;
use pyo3::prelude::{PyModule, PyModuleMethods};
use pyo3::{pymodule, wrap_pyfunction, Bound, FromPyObject, PyRef, PyResult, Python};
use std::ops::Deref;
use syn::ir::Synthesizer;
use pyo3::{pymodule, wrap_pyfunction, Bound, PyResult};

#[pymodule]
#[pyo3(name = "synpy_rust")]
fn synpy_rust(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<PyPauliString>()?;
m.add_class::<PyCommand>()?;
let _ = m.add_function(wrap_pyfunction!(synthesize_pauli_exponential, m)?);
m.add_class::<PyCliffordTableau>()?;

Ok(())
}
}
9 changes: 5 additions & 4 deletions synpy/src/synthesis.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use std::ops::Deref;
use pyo3::{pyfunction, Py};
use crate::PyRef;
use crate::Python;
use pyo3::{pyfunction, PyRef, PyResult};
use pyo3::exceptions::PyException;
use pyo3::{pyclass, pymethods, PyErr};
use syn::data_structures::{CliffordTableau, PauliPolynomial};
Expand All @@ -11,7 +9,6 @@ use syn::ir::CliffordGates;
use syn::ir::Gates;
use syn::ir::pauli_exponential::{PauliExponential, PauliExponentialSynthesizer};
use syn::ir::pauli_polynomial::PauliPolynomialSynthStrategy;
use crate::PyResult;
use crate::validation::validate;
use syn::ir::Synthesizer;

Expand Down Expand Up @@ -99,6 +96,10 @@ pub fn parse_clifford_commands(
Ok(tableau)
}

pub trait Synthesize {
fn synthesize(&self) -> Vec<PyCommand>;
}

#[pyclass]
#[derive(Clone)]
pub struct PyPauliString {
Expand Down
87 changes: 87 additions & 0 deletions synpy/src/tableau.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use crate::synthesis::{CommandCollector, PyCommand, Synthesize};

use bitvec::prelude::BitVec;
use pyo3::basic::CompareOp;
use pyo3::prelude::*;
use pyo3::PyResult;
use syn::data_structures::CliffordTableau as CliffordTableau;
use syn::data_structures::PauliString;
use syn::data_structures::PropagateClifford;

use syn::ir::clifford_tableau::NaiveCliffordSynthesizer;
use syn::ir::Synthesizer;

#[pyclass(unsendable)]
pub struct PyCliffordTableau {
tableau: CliffordTableau,
}

#[pymethods]
impl PyCliffordTableau {
#[new]
fn new(n: usize) -> Self {
PyCliffordTableau {
tableau: CliffordTableau::new(n),
}
}

#[staticmethod]
pub fn from_parts(pauli_strings: Vec<String>, signs: Vec<bool>) -> Self {
let pauli_columns: Vec<PauliString> = pauli_strings
.iter()
.map(|pauli_string| PauliString::from_text(pauli_string))
.collect();
let signs_bitvec: BitVec = signs.iter().copied().collect();
let tableau = CliffordTableau::from_parts(pauli_columns, signs_bitvec);

PyCliffordTableau { tableau }
}

pub fn size(&self) -> usize {
self.tableau.size()
}

pub(crate) fn compose(&self, rhs: &Self) -> Self {
PyCliffordTableau {
tableau: self.tableau.compose(&rhs.tableau),
}
}

fn __richcmp__(&self, other: &PyCliffordTableau, op: CompareOp) -> PyResult<bool> {
match op {
CompareOp::Eq => Ok(self.tableau == other.tableau),
CompareOp::Ne => Ok(self.tableau != other.tableau),
_ => Ok(false),
}
}

pub fn synthesize(&self) -> Vec<PyCommand> {
Comment thread
keefehuang marked this conversation as resolved.
<Self as Synthesize>::synthesize(self)
}
}

impl PropagateClifford for PyCliffordTableau {
fn cx(&mut self, control: usize, target: usize) -> &mut Self {
self.tableau.cx(control, target);
self
}

fn s(&mut self, target: usize) -> &mut Self {
self.tableau.s(target);
self
}

fn v(&mut self, target: usize) -> &mut Self {
self.tableau.v(target);
self
}
}

impl Synthesize for PyCliffordTableau {
fn synthesize(&self) -> Vec<PyCommand> {
let mut tracker = CommandCollector::new();
let mut synthesizer = NaiveCliffordSynthesizer::default();
synthesizer.synthesize(self.tableau.clone(), &mut tracker);
tracker.commands()
}
}
Loading