Skip to content

Commit

Permalink
feat!: get pauli propagation + examples working with new hugr builder
Browse files Browse the repository at this point in the history
Currently requires some horrible hacks like converting nodes via ints and loading operations from bytes. This will be cleared up by fully using the `hugr` package Node, Wire, ops, types in the `TK2Circuit` interface instead of ad-hoc bindings in a follow up PR.
  • Loading branch information
ss2165 committed Jul 9, 2024
1 parent 601d905 commit db87d5e
Show file tree
Hide file tree
Showing 11 changed files with 601 additions and 638 deletions.
438 changes: 231 additions & 207 deletions poetry.lock

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ packages = [{ include = "tket2-py" }]
[tool.poetry.dependencies]
python = "^3.10"
pytket = "1.29.2"
# hugr = "^0.3.1"
hugr = { git = "https://github.com/CQCL/hugr.git", subdirectory = "hugr-py", branch = "ss/extend" }

[tool.poetry.group.dev.dependencies]
maturin = "^1.7.0"
Expand All @@ -36,7 +38,8 @@ mypy = "^1.10.1"
hypothesis = "^6.105.1"
graphviz = "^0.20"
pre-commit = "^3.7.1"
guppylang = "^0.6.0"
# guppylang = "^0.6.0"
guppylang = { git = "https://github.com/CQCL/guppylang", rev = "3da3936" }

[build-system]
requires = ["maturin~=1.5.1"]
Expand Down
192 changes: 101 additions & 91 deletions tket2-py/examples/1-Getting-Started.ipynb

Large diffs are not rendered by default.

59 changes: 28 additions & 31 deletions tket2-py/examples/2-Rewriting-Circuits.ipynb

Large diffs are not rendered by default.

46 changes: 6 additions & 40 deletions tket2-py/src/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,15 @@ use crate::utils::ConvertPyErr;

pub use self::convert::{try_update_circ, try_with_circ, update_circ, with_circ, CircuitType};
pub use self::cost::PyCircuitCost;
use self::tk2circuit::Dfg;
pub use self::tk2circuit::Tk2Circuit;
pub use tket2::{Pauli, Tk2Op};

/// The module definition
pub fn module(py: Python<'_>) -> PyResult<Bound<'_, PyModule>> {
let m = PyModule::new_bound(py, "circuit")?;
m.add_class::<Tk2Circuit>()?;
m.add_class::<Dfg>()?;
m.add_class::<PyNode>()?;
m.add_class::<PyWire>()?;
m.add_class::<WireIter>()?;
m.add_class::<PyCircuitCost>()?;

m.add_function(wrap_pyfunction!(validate_circuit, &m)?)?;
Expand Down Expand Up @@ -125,49 +122,18 @@ impl fmt::Debug for PyNode {
}
}

#[pyclass]
/// An iterator over the wires of a node.
pub struct WireIter {
node: PyNode,
current: usize,
}

#[pymethods]
impl WireIter {
fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
slf
}

fn __next__(mut slf: PyRefMut<'_, Self>) -> Option<PyWire> {
slf.current += 1;
Some(slf.node.__getitem__(slf.current - 1).unwrap())
}
}

#[pymethods]
impl PyNode {
#[new]
fn new(index: usize) -> Self {
Self {
node: serde_json::from_value(serde_json::Value::Number(index.into())).unwrap(),
}
}
/// A string representation of the pattern.
pub fn __repr__(&self) -> String {
format!("{:?}", self)
}

fn __getitem__(&self, idx: usize) -> PyResult<PyWire> {
Ok(hugr::Wire::new(self.node, idx).into())
}

fn __iter__(slf: PyRef<'_, Self>) -> PyResult<Py<WireIter>> {
let iter = WireIter {
current: 0,
node: *slf,
};
Py::new(slf.py(), iter)
}

fn outs(&self, n: usize) -> Vec<PyWire> {
(0..n)
.map(|i| hugr::Wire::new(self.node, i).into())
.collect()
}
}

/// A [`hugr::Node`] wrapper for Python.
Expand Down
58 changes: 3 additions & 55 deletions tket2-py/src/circuit/tk2circuit.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Rust-backed representation of circuits
use std::borrow::Borrow;
use std::borrow::{Borrow, Cow};

use hugr::builder::{CircuitBuilder, DFGBuilder, Dataflow, DataflowHugr};
use hugr::extension::prelude::QB_T;
Expand Down Expand Up @@ -178,7 +178,7 @@ impl Tk2Circuit {
Ok(self.clone())
}

fn node_op(&self, node: PyNode) -> PyResult<PyCustomOp> {
fn node_op(&self, node: PyNode) -> PyResult<Cow<[u8]>> {
let custom: CustomOp = self
.circ
.hugr()
Expand All @@ -191,7 +191,7 @@ impl Tk2Circuit {
))
})?;

Ok(custom.into())
Ok(serde_json::to_vec(&custom).unwrap().into())
}

fn node_inputs(&self, node: PyNode) -> Vec<PyWire> {
Expand Down Expand Up @@ -226,55 +226,3 @@ impl Tk2Circuit {
circ.extract::<Tk2Circuit>()
}
}

#[pyclass]
#[derive(Clone, Debug, PartialEq, From)]
pub(super) struct Dfg {
/// Rust representation of the circuit.
builder: DFGBuilder<Hugr>,
}
#[pymethods]
impl Dfg {
#[new]
fn new(input_types: Vec<PyHugrType>, output_types: Vec<PyHugrType>) -> PyResult<Self> {
let builder = DFGBuilder::new(FunctionType::new(
into_vec(input_types),
into_vec(output_types),
))
.convert_pyerrs()?;
Ok(Self { builder })
}

fn inputs(&self) -> Vec<PyWire> {
self.builder.input_wires().map_into().collect()
}

fn add_op(&mut self, op: Bound<PyAny>, inputs: Vec<PyWire>) -> PyResult<PyNode> {
// TODO: Once we wrap `Dfg` in a pure python class we can make the conversion there,
// and have a concrete `op: PyCustomOp` argument here.
let custom: PyCustomOp = op
.call_method0("to_custom")
.map_err(|_| {
PyErr::new::<PyValueError, _>(
"The operation must implement the `ToCustomOp` protocol.",
)
})?
.extract()?;
let custom: CustomOp = custom.into();
self.builder
.add_dataflow_op(custom, inputs.into_iter().map_into())
.convert_pyerrs()
.map(|d| d.node().into())
}

fn finish(&mut self, outputs: Vec<PyWire>) -> PyResult<Tk2Circuit> {
Ok(Tk2Circuit {
circ: self
.builder
.clone()
.finish_hugr_with_outputs(outputs.into_iter().map_into(), &REGISTRY)
.convert_pyerrs()?
.into(),
})
}
}
Loading

0 comments on commit db87d5e

Please sign in to comment.