diff --git a/src/circuit.rs b/src/circuit.rs index deadac69..b09731d1 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -78,6 +78,9 @@ pub trait Circuit<'circ>: HugrView { fn command_optype(&self, command: &Command) -> &OpType { self.get_optype(command.node()) } + + /// The number of gates in the circuit. + fn num_gates(&self) -> usize; } impl<'circ, T> Circuit<'circ> for T @@ -143,4 +146,49 @@ where fn output(&self) -> Node { return self.children(self.root()).nth(1).unwrap(); } + + #[inline] + fn num_gates(&self) -> usize { + self.children(self.root()).count() - 2 + } +} + +#[cfg(test)] +mod tests { + use std::sync::OnceLock; + + use hugr::{ + hugr::views::{DescendantsGraph, HierarchyView}, + ops::handle::DfgID, + Hugr, HugrView, + }; + + use crate::{circuit::Circuit, json::load_tk1_json_str}; + + static CIRC: OnceLock = OnceLock::new(); + + fn test_circuit() -> DescendantsGraph<'static, DfgID> { + let hugr = CIRC.get_or_init(|| { + load_tk1_json_str( + r#"{ + "phase": "0", + "bits": [], + "qubits": [["q", [0]], ["q", [1]]], + "commands": [ + {"args": [["q", [0]]], "op": {"type": "H"}}, + {"args": [["q", [0]], ["q", [1]]], "op": {"type": "CX"}} + ], + "implicit_permutation": [[["q", [0]], ["q", [0]]], [["q", [1]], ["q", [1]]]] + }"#, + ) + .unwrap() + }); + DescendantsGraph::new(hugr, hugr.root()) + } + + #[test] + fn test_num_gates() { + let circ = test_circuit(); + assert_eq!(circ.num_gates(), 2); + } } diff --git a/src/json.rs b/src/json.rs index a968fde0..9da0f879 100644 --- a/src/json.rs +++ b/src/json.rs @@ -7,6 +7,8 @@ pub mod op; #[cfg(test)] mod tests; +use std::{fs, io}; + use hugr::ops::OpType; use hugr::std_extensions::arithmetic::float_types::ConstF64; use hugr::values::Value; @@ -81,6 +83,62 @@ pub enum OpConvertError { NonSerializableInputs(OpType), } +/// Load a TKET1 circuit from a JSON file. +pub fn load_tk1_json_file(path: &str) -> Result { + let file = fs::File::open(path)?; + let reader = io::BufReader::new(file); + let ser: SerialCircuit = serde_json::from_reader(reader)?; + Ok(ser.decode()?) +} + +/// Load a TKET1 circuit from a JSON string. +pub fn load_tk1_json_str(json: &str) -> Result { + let ser: SerialCircuit = serde_json::from_str(json)?; + Ok(ser.decode()?) +} + +/// Error type for conversion between `Op` and `OpType`. +#[derive(Debug, Error)] +pub enum TK1LoadError { + /// The serialized operation is not supported. + #[error("unsupported serialized operation: {0:?}")] + UnsupportedSerializedOp(JsonOpType), + /// The serialized operation is not supported. + #[error("cannot serialize operation: {0:?}")] + UnsupportedOpSerialization(OpType), + /// The serialized operation is not supported. + #[error("cannot serialize operation: {0:?}")] + NonSerializableInputs(OpType), + /// Invalid JSON, + #[error("invalid JSON")] + InvalidJson, + /// File not found., + #[error("unable to load file")] + FileLoadError, +} + +impl From for TK1LoadError { + fn from(_: serde_json::Error) -> Self { + Self::InvalidJson + } +} + +impl From for TK1LoadError { + fn from(_: io::Error) -> Self { + Self::FileLoadError + } +} + +impl From for TK1LoadError { + fn from(value: OpConvertError) -> Self { + match value { + OpConvertError::UnsupportedSerializedOp(op) => Self::UnsupportedSerializedOp(op), + OpConvertError::UnsupportedOpSerialization(op) => Self::UnsupportedOpSerialization(op), + OpConvertError::NonSerializableInputs(op) => Self::NonSerializableInputs(op), + } + } +} + /// Try to interpret a TKET1 parameter as a constant value. #[inline] fn try_param_to_constant(param: &str) -> Option {