-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Use tket1 and tket2 circuits interchangeably everywhere #243
Changes from 4 commits
1ab8420
0a80938
19cf309
214e775
0a2123c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,11 @@ | ||
//! Utilities for calling Hugr functions on generic python objects. | ||
|
||
use pyo3::exceptions::PyAttributeError; | ||
use pyo3::{prelude::*, PyTypeInfo}; | ||
|
||
use derive_more::From; | ||
use hugr::{Hugr, HugrView}; | ||
use serde::Serialize; | ||
use tket2::extension::REGISTRY; | ||
use tket2::json::TKETDecode; | ||
use tket2::passes::CircuitChunks; | ||
|
@@ -14,78 +16,149 @@ use crate::pattern::rewrite::PyCircuitRewrite; | |
/// A manager for tket 2 operations on a tket 1 Circuit. | ||
#[pyclass] | ||
#[derive(Clone, Debug, PartialEq, From)] | ||
pub struct T2Circuit { | ||
pub struct Tk2Circuit { | ||
/// Rust representation of the circuit. | ||
pub hugr: Hugr, | ||
} | ||
|
||
#[pymethods] | ||
impl T2Circuit { | ||
impl Tk2Circuit { | ||
/// Cast a tket1 circuit to a [`Tk2Circuit`]. | ||
#[new] | ||
fn from_circuit(circ: PyObject) -> PyResult<Self> { | ||
pub fn from_tket1(circ: &PyAny) -> PyResult<Self> { | ||
Ok(Self { | ||
hugr: with_hugr(circ, |hugr| hugr)?, | ||
hugr: with_hugr(circ, |hugr, _| hugr)?, | ||
}) | ||
} | ||
|
||
fn finish(&self) -> PyResult<PyObject> { | ||
SerialCircuit::encode(&self.hugr)?.to_tket1_with_gil() | ||
/// Cast the [`Tk2Circuit`] to a tket1 circuit. | ||
pub fn to_tket1<'py>(&self, py: Python<'py>) -> PyResult<&'py PyAny> { | ||
SerialCircuit::encode(&self.hugr)?.to_tket1(py) | ||
} | ||
|
||
fn apply_match(&mut self, rw: PyCircuitRewrite) { | ||
/// Apply a rewrite on the circuit. | ||
pub fn apply_match(&mut self, rw: PyCircuitRewrite) { | ||
rw.rewrite.apply(&mut self.hugr).expect("Apply error."); | ||
} | ||
|
||
/// Encode the circuit as a HUGR json string. | ||
// | ||
// TODO: Bind a messagepack encoder/decoder too. | ||
pub fn to_hugr_json(&self) -> PyResult<String> { | ||
Ok(serde_json::to_string(&self.hugr).unwrap()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. a note that previously we have used messagepack (and the behaviour between json and messagepack is not always identical), but I'm ok avoiding the extra python dependency here for now - at least until the serialized format gets a bit more stable (hopefully soon) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a TODO |
||
} | ||
|
||
/// Decode a HUGR json string to a circuit. | ||
#[staticmethod] | ||
pub fn from_hugr_json(json: &str) -> PyResult<Self> { | ||
let hugr = serde_json::from_str(json) | ||
.map_err(|e| PyErr::new::<PyAttributeError, _>(format!("Invalid encoded HUGR: {e}")))?; | ||
Ok(Tk2Circuit { hugr }) | ||
} | ||
|
||
/// Encode the circuit as a tket1 json string. | ||
/// | ||
/// FIXME: Currently the encoded circuit cannot be loaded back due to | ||
/// [https://github.com/CQCL/hugr/issues/683] | ||
pub fn to_tket1_json(&self) -> PyResult<String> { | ||
Ok(serde_json::to_string(&SerialCircuit::encode(&self.hugr)?).unwrap()) | ||
} | ||
|
||
/// Decode a tket1 json string to a circuit. | ||
#[staticmethod] | ||
pub fn from_tket1_json(json: &str) -> PyResult<Self> { | ||
let tk1: SerialCircuit = serde_json::from_str(json) | ||
.map_err(|e| PyErr::new::<PyAttributeError, _>(format!("Invalid encoded HUGR: {e}")))?; | ||
Ok(Tk2Circuit { | ||
hugr: tk1.decode()?, | ||
}) | ||
} | ||
} | ||
impl T2Circuit { | ||
/// Tries to extract a T2Circuit from a python object. | ||
impl Tk2Circuit { | ||
/// Tries to extract a Tk2Circuit from a python object. | ||
/// | ||
/// Returns an error if the py object is not a T2Circuit. | ||
pub fn try_extract(circ: Py<PyAny>) -> PyResult<Self> { | ||
Python::with_gil(|py| circ.as_ref(py).extract::<T2Circuit>()) | ||
/// Returns an error if the py object is not a Tk2Circuit. | ||
pub fn try_extract(circ: &PyAny) -> PyResult<Self> { | ||
circ.extract::<Tk2Circuit>() | ||
} | ||
} | ||
|
||
/// A flag to indicate the encoding of a circuit. | ||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
pub enum CircuitType { | ||
/// A `pytket` `Circuit`. | ||
Tket1, | ||
/// A tket2 `Tk2Circuit`, represented as a HUGR. | ||
Tket2, | ||
} | ||
|
||
impl CircuitType { | ||
/// Converts a `Hugr` into the format indicated by the flag. | ||
pub fn convert(self, py: Python, hugr: Hugr) -> PyResult<&PyAny> { | ||
match self { | ||
CircuitType::Tket1 => SerialCircuit::encode(&hugr)?.to_tket1(py), | ||
CircuitType::Tket2 => Ok(Py::new(py, Tk2Circuit { hugr })?.into_ref(py)), | ||
} | ||
} | ||
} | ||
|
||
/// Apply a fallible function expecting a hugr on a pytket circuit. | ||
pub fn try_with_hugr<T, E, F>(circ: Py<PyAny>, f: F) -> PyResult<T> | ||
/// Apply a fallible function expecting a hugr on a python circuit. | ||
/// | ||
/// This method supports both `pytket.Circuit` and `Tk2Circuit` python objects. | ||
pub fn try_with_hugr<T, E, F>(circ: &PyAny, f: F) -> PyResult<T> | ||
where | ||
E: Into<PyErr>, | ||
F: FnOnce(Hugr) -> Result<T, E>, | ||
F: FnOnce(Hugr, CircuitType) -> Result<T, E>, | ||
{ | ||
let hugr = Python::with_gil(|py| -> PyResult<Hugr> { | ||
let circ = circ.as_ref(py); | ||
match T2Circuit::extract(circ) { | ||
// hugr circuit | ||
Ok(t2circ) => Ok(t2circ.hugr), | ||
// tket1 circuit | ||
Err(_) => Ok(SerialCircuit::from_tket1(circ)?.decode()?), | ||
} | ||
})?; | ||
(f)(hugr).map_err(|e| e.into()) | ||
let (hugr, typ) = match Tk2Circuit::extract(circ) { | ||
// hugr circuit | ||
Ok(t2circ) => (t2circ.hugr, CircuitType::Tket2), | ||
// tket1 circuit | ||
Err(_) => ( | ||
SerialCircuit::from_tket1(circ)?.decode()?, | ||
CircuitType::Tket1, | ||
), | ||
}; | ||
(f)(hugr, typ).map_err(|e| e.into()) | ||
} | ||
|
||
/// Apply a function expecting a hugr on a pytket circuit. | ||
pub fn with_hugr<T, F>(circ: Py<PyAny>, f: F) -> PyResult<T> | ||
/// Apply a function expecting a hugr on a python circuit. | ||
/// | ||
/// This method supports both `pytket.Circuit` and `Tk2Circuit` python objects. | ||
pub fn with_hugr<T, F>(circ: &PyAny, f: F) -> PyResult<T> | ||
where | ||
F: FnOnce(Hugr) -> T, | ||
F: FnOnce(Hugr, CircuitType) -> T, | ||
{ | ||
try_with_hugr(circ, |hugr| Ok::<T, PyErr>((f)(hugr))) | ||
try_with_hugr(circ, |hugr, typ| Ok::<T, PyErr>((f)(hugr, typ))) | ||
} | ||
|
||
/// Apply a hugr-to-hugr function on a pytket circuit, and return the modified circuit. | ||
pub fn try_update_hugr<E, F>(circ: Py<PyAny>, f: F) -> PyResult<Py<PyAny>> | ||
/// Apply a fallible hugr-to-hugr function on a python circuit, and return the modified circuit. | ||
/// | ||
/// This method supports both `pytket.Circuit` and `Tk2Circuit` python objects. | ||
/// The returned Hugr is converted to the matching python object. | ||
pub fn try_update_hugr<E, F>(circ: &PyAny, f: F) -> PyResult<&PyAny> | ||
where | ||
E: Into<PyErr>, | ||
F: FnOnce(Hugr) -> Result<Hugr, E>, | ||
F: FnOnce(Hugr, CircuitType) -> Result<Hugr, E>, | ||
{ | ||
let hugr = try_with_hugr(circ, f)?; | ||
SerialCircuit::encode(&hugr)?.to_tket1_with_gil() | ||
let py = circ.py(); | ||
try_with_hugr(circ, |hugr, typ| { | ||
let hugr = f(hugr, typ).map_err(|e| e.into())?; | ||
typ.convert(py, hugr) | ||
}) | ||
} | ||
|
||
/// Apply a hugr-to-hugr function on a pytket circuit, and return the modified circuit. | ||
pub fn update_hugr<F>(circ: Py<PyAny>, f: F) -> PyResult<Py<PyAny>> | ||
/// Apply a hugr-to-hugr function on a python circuit, and return the modified circuit. | ||
/// | ||
/// This method supports both `pytket.Circuit` and `Tk2Circuit` python objects. | ||
/// The returned Hugr is converted to the matching python object. | ||
pub fn update_hugr<F>(circ: &PyAny, f: F) -> PyResult<&PyAny> | ||
where | ||
F: FnOnce(Hugr) -> Hugr, | ||
F: FnOnce(Hugr, CircuitType) -> Hugr, | ||
{ | ||
let hugr = with_hugr(circ, f)?; | ||
SerialCircuit::encode(&hugr)?.to_tket1_with_gil() | ||
let py = circ.py(); | ||
try_with_hugr(circ, |hugr, typ| { | ||
let hugr = f(hugr, typ); | ||
typ.convert(py, hugr) | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
still a couple mentions of "cast"?