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
…#465)

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.

BREAKING CHANGE: `Dfg` binding removed, replaced with builder from
`hugr` package.
  • Loading branch information
ss2165 authored Jul 10, 2024
1 parent c40e1a9 commit cab0d87
Show file tree
Hide file tree
Showing 12 changed files with 634 additions and 709 deletions.
168 changes: 84 additions & 84 deletions Cargo.lock

Large diffs are not rendered by default.

412 changes: 214 additions & 198 deletions poetry.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ packages = [{ include = "tket2-py" }]
[tool.poetry.dependencies]
python = "^3.10"
pytket = "1.29.2"
hugr = "^0.4.0"

[tool.poetry.group.dev.dependencies]
maturin = "^1.7.0"
Expand All @@ -36,7 +37,7 @@ mypy = "^1.10.1"
hypothesis = "^6.105.1"
graphviz = "^0.20"
pre-commit = "^3.7.1"
guppylang = "^0.6.0"
guppylang = "^0.6.2"

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

Large diffs are not rendered by default.

61 changes: 29 additions & 32 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 cab0d87

Please sign in to comment.