From 2250ce76f3019deee1d7e1206aa7843a745fe988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Borgna?= <121866228+aborgna-q@users.noreply.github.com> Date: Mon, 30 Sep 2024 15:42:18 +0100 Subject: [PATCH] chore!: Replace thiserror with derive_more 1.0 (#624) `derive_more` 1.0 can now be used as a more granular replacement for `thiserror`. The main changes required were: - Explicit derives for `Display` and `From`, in addition to `Error`. - Use `#[display(...)]` directives instead of `#[error(...)]`. The syntax is sightly different for referencing tuple elements (`_0` vs `0`). `error(transparent)` is also the default behaviour. - The new `From` derive is less intrusive (derives it for every variant non-`from(ignore)`d, unless explicit variants are picked with `#[from]`). Those changes are non-breaking (see first commit). Since the next release will be a breaking one, I included some cleanups too. - Added all missing `#[non_exhaustive]`s to the error enums (this missing is what caused the last breaking change). - Converted unary tuple variants into named structs. `derive_more` assumes unary tuples to be an error source, so we had to add `#[error(ignore)]` in some cases. - Add more contexts to some errors (e.g. adding the node id to errors that only printed the port offset). BREAKING CHANGE: Made all errors `non_exhaustive`, and renamed some fields for clarity. --- Cargo.lock | 7 +- Cargo.toml | 3 +- tket2-hseries/Cargo.toml | 9 ++- tket2-hseries/src/extension/hseries/lower.rs | 46 ++++++------ tket2-hseries/src/lazify_measure.rs | 14 ++-- tket2-hseries/src/lib.rs | 18 ++--- tket2-py/Cargo.toml | 2 +- tket2/Cargo.toml | 8 ++- tket2/src/circuit.rs | 73 +++++++++++++------- tket2/src/circuit/hash.rs | 9 +-- tket2/src/ops.rs | 9 ++- tket2/src/passes/commutation.rs | 36 ++++++---- tket2/src/passes/pytket.rs | 10 +-- tket2/src/portmatching.rs | 22 +++--- tket2/src/portmatching/matcher.rs | 29 ++++---- tket2/src/portmatching/pattern.rs | 15 ++-- tket2/src/rewrite/ecc_rewriter.rs | 22 +++--- tket2/src/serialize/guppy.rs | 40 ++++++----- tket2/src/serialize/pytket.rs | 52 ++++++++------ 19 files changed, 241 insertions(+), 183 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 67e7741c..bdffd826 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1902,7 +1902,7 @@ dependencies = [ "crossbeam-channel", "csv", "delegate 0.13.0", - "derive_more 0.99.18", + "derive_more 1.0.0", "downcast-rs", "fxhash", "hugr", @@ -1924,7 +1924,6 @@ dependencies = [ "smol_str", "strum", "strum_macros", - "thiserror", "tket-json-rs", "tracing", "typetag", @@ -1939,6 +1938,7 @@ version = "0.4.0" dependencies = [ "clap", "cool_asserts", + "derive_more 1.0.0", "hugr", "hugr-cli", "itertools 0.13.0", @@ -1950,7 +1950,6 @@ dependencies = [ "smol_str", "strum", "strum_macros", - "thiserror", "tket2", ] @@ -1959,7 +1958,7 @@ name = "tket2-py" version = "0.0.0" dependencies = [ "cool_asserts", - "derive_more 0.99.18", + "derive_more 1.0.0", "hugr", "hugr-cli", "itertools 0.13.0", diff --git a/Cargo.toml b/Cargo.toml index 5195e962..f3bed2bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,7 @@ criterion = "0.5.1" crossbeam-channel = "0.5.8" csv = "1.2.2" delegate = "0.13.0" -derive_more = "0.99.18" +derive_more = "1.0.0" downcast-rs = "1.2.0" fxhash = "0.2.1" lazy_static = "1.5.0" @@ -61,7 +61,6 @@ serde_json = "1.0" smol_str = "0.2.0" strum = "0.26.3" strum_macros = "0.26.4" -thiserror = "1.0.64" tracing-appender = "0.2.2" tracing-subscriber = "0.3.17" typetag = "0.2.18" diff --git a/tket2-hseries/Cargo.toml b/tket2-hseries/Cargo.toml index d3047b97..f7a88927 100644 --- a/tket2-hseries/Cargo.toml +++ b/tket2-hseries/Cargo.toml @@ -30,10 +30,15 @@ serde_json.workspace = true smol_str.workspace = true strum.workspace = true strum_macros.workspace = true -thiserror.workspace = true itertools.workspace = true -clap = { workspace = true, optional = true} +clap = { workspace = true, optional = true } hugr-cli = { workspace = true, optional = true } +derive_more = { workspace = true, features = [ + "error", + "display", + "from", + "into", +] } [dev-dependencies] cool_asserts.workspace = true diff --git a/tket2-hseries/src/extension/hseries/lower.rs b/tket2-hseries/src/extension/hseries/lower.rs index 5d67f76f..535e88bd 100644 --- a/tket2-hseries/src/extension/hseries/lower.rs +++ b/tket2-hseries/src/extension/hseries/lower.rs @@ -1,3 +1,5 @@ +use derive_more::{Display, Error, From}; +use hugr::ops::NamedOp; use hugr::{ algorithms::validation::{ValidatePassError, ValidationLevel}, builder::{BuildError, DFGBuilder, Dataflow, DataflowHugr}, @@ -11,7 +13,6 @@ use hugr::{ use lazy_static::lazy_static; use std::collections::HashMap; use strum::IntoEnumIterator; -use thiserror::Error; use tket2::{extension::rotation::RotationOpBuilder, Tk2Op}; use crate::extension::hseries::{HSeriesOp, HSeriesOpBuilder}; @@ -35,26 +36,30 @@ fn const_f64(builder: &mut T, value: f64) -> Wire { } /// Errors produced by lowering [Tk2Op]s. -#[derive(Debug, Error)] -#[allow(missing_docs)] +#[derive(Debug, Display, Error, From)] +#[non_exhaustive] pub enum LowerTk2Error { - #[error("Error when building the circuit: {0}")] - BuildError(#[from] BuildError), - - #[error("Unrecognised operation: {0:?} with {1} inputs")] + /// An error raised when building the circuit. + #[display("Error when building the circuit: {_0}")] + BuildError(BuildError), + /// Found an unrecognised operation. + #[display("Unrecognised operation: {} with {_1} inputs", _0.name())] UnknownOp(Tk2Op, usize), - - #[error("Error when replacing op: {0}")] - OpReplacement(#[from] HugrError), - - #[error("Error when lowering ops: {0}")] - CircuitReplacement(#[from] hugr::algorithms::lower::LowerError), - - #[error("Tk2Ops were not lowered: {0:?}")] - Unlowered(Vec), - - #[error(transparent)] - ValidationError(#[from] ValidatePassError), + /// An error raised when replacing an operation. + #[display("Error when replacing op: {_0}")] + OpReplacement(HugrError), + /// An error raised when lowering operations. + #[display("Error when lowering ops: {_0}")] + CircuitReplacement(hugr::algorithms::lower::LowerError), + /// Tk2Ops were not lowered after the pass. + #[display("Tk2Ops were not lowered: {missing_ops:?}")] + #[from(ignore)] + Unlowered { + /// The list of nodes that were not lowered. + missing_ops: Vec, + }, + /// Validation error in the final hugr. + ValidationError(ValidatePassError), } fn op_to_hugr(op: Tk2Op) -> Result { @@ -179,7 +184,8 @@ impl LowerTket2ToHSeriesPass { self.0.run_validated_pass(hugr, registry, |hugr, level| { lower_tk2_op(hugr)?; if *level != ValidationLevel::None { - check_lowered(hugr).map_err(LowerTk2Error::Unlowered)?; + check_lowered(hugr) + .map_err(|missing_ops| LowerTk2Error::Unlowered { missing_ops })?; } Ok(()) }) diff --git a/tket2-hseries/src/lazify_measure.rs b/tket2-hseries/src/lazify_measure.rs index 186e2f18..d17049a8 100644 --- a/tket2-hseries/src/lazify_measure.rs +++ b/tket2-hseries/src/lazify_measure.rs @@ -5,6 +5,7 @@ //! [HSeriesOp::Measure]: crate::extension::hseries::HSeriesOp::Measure use std::collections::{HashMap, HashSet}; +use derive_more::{Display, Error, From}; use hugr::{ algorithms::{ ensure_no_nonlocal_edges, @@ -20,7 +21,6 @@ use hugr::{ types::Signature, Hugr, HugrView, IncomingPort, Node, OutgoingPort, SimpleReplacement, }; -use thiserror::Error; use tket2::Tk2Op; use lazy_static::lazy_static; @@ -39,18 +39,16 @@ use crate::extension::{ #[derive(Default)] pub struct LazifyMeasurePass(ValidationLevel); -#[derive(Error, Debug)] +#[derive(Error, Debug, Display, From)] +#[non_exhaustive] /// An error reported from [LazifyMeasurePass]. pub enum LazifyMeasurePassError { /// The [Hugr] was invalid either before or after a pass ran. - #[error(transparent)] - ValidationError(#[from] ValidatePassError), + ValidationError(ValidatePassError), /// The [Hugr] was found to contain non-local edges. - #[error(transparent)] - NonLocalEdgesError(#[from] NonLocalEdgesError), + NonLocalEdgesError(NonLocalEdgesError), /// A [SimpleReplacement] failed during the running of the pass. - #[error(transparent)] - SimpleReplacementError(#[from] SimpleReplacementError), + SimpleReplacementError(SimpleReplacementError), } impl LazifyMeasurePass { diff --git a/tket2-hseries/src/lib.rs b/tket2-hseries/src/lib.rs index 3c778919..244f7c87 100644 --- a/tket2-hseries/src/lib.rs +++ b/tket2-hseries/src/lib.rs @@ -1,5 +1,6 @@ //! Provides a preparation and validation workflow for Hugrs targeting //! Quantinuum H-series quantum computers. +use derive_more::{Display, Error, From}; use hugr::{ algorithms::{ force_order, @@ -10,8 +11,6 @@ use hugr::{ }; use tket2::Tk2Op; -use thiserror::Error; - use extension::{ futures::FutureOpDef, hseries::{HSeriesOp, LowerTk2Error, LowerTket2ToHSeriesPass}, @@ -33,21 +32,18 @@ pub struct HSeriesPass { validation_level: ValidationLevel, } -#[derive(Error, Debug)] +#[derive(Error, Debug, Display, From)] +#[non_exhaustive] /// An error reported from [HSeriesPass]. pub enum HSeriesPassError { /// The [hugr::Hugr] was invalid either before or after a pass ran. - #[error(transparent)] - ValidationError(#[from] ValidatePassError), + ValidationError(ValidatePassError), /// An error from the component [LazifyMeasurePass]. - #[error(transparent)] - LazyMeasureError(#[from] LazifyMeasurePassError), + LazyMeasureError(LazifyMeasurePassError), /// An error from the component [force_order()] pass. - #[error(transparent)] - ForceOrderError(#[from] HugrError), + ForceOrderError(HugrError), /// An error from the component [LowerTket2ToHSeriesPass] pass. - #[error(transparent)] - LowerTk2Error(#[from] LowerTk2Error), + LowerTk2Error(LowerTk2Error), } impl HSeriesPass { diff --git a/tket2-py/Cargo.toml b/tket2-py/Cargo.toml index afe9657b..2e3267ce 100644 --- a/tket2-py/Cargo.toml +++ b/tket2-py/Cargo.toml @@ -30,7 +30,7 @@ hugr = { workspace = true } portgraph = { workspace = true, features = ["serde"] } pyo3 = { workspace = true } num_cpus = { workspace = true } -derive_more = { workspace = true } +derive_more = { workspace = true, features = ["into", "from"] } itertools = { workspace = true } portmatching = { workspace = true } strum = { workspace = true } diff --git a/tket2/Cargo.toml b/tket2/Cargo.toml index 2de4c6e6..08f465fd 100644 --- a/tket2/Cargo.toml +++ b/tket2/Cargo.toml @@ -40,7 +40,6 @@ num-rational = { workspace = true } num-complex = { workspace = true, optional = true } tket-json-rs = { workspace = true } rayon = { workspace = true } -thiserror = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } downcast-rs = { workspace = true } @@ -50,7 +49,12 @@ typetag = { workspace = true } itertools = { workspace = true } petgraph = { workspace = true } portmatching = { workspace = true, optional = true, features = ["serde"] } -derive_more = { workspace = true } +derive_more = { workspace = true, features = [ + "error", + "display", + "from", + "into", +] } hugr = { workspace = true } hugr-core = { workspace = true } portgraph = { workspace = true, features = ["serde"] } diff --git a/tket2/src/circuit.rs b/tket2/src/circuit.rs index 7f4a332c..d52f6698 100644 --- a/tket2/src/circuit.rs +++ b/tket2/src/circuit.rs @@ -15,6 +15,7 @@ use hugr::extension::prelude::{LiftDef, NoopDef, TupleOpDef}; use hugr::hugr::views::{DescendantsGraph, ExtractHugr, HierarchyView}; use itertools::Either::{Left, Right}; +use derive_more::{Display, Error, From}; use hugr::hugr::hugrmut::HugrMut; use hugr::ops::dataflow::IOTrait; use hugr::ops::{Input, NamedOp, OpName, OpParent, OpTag, OpTrait, Output}; @@ -23,7 +24,6 @@ use hugr::{Hugr, PortIndex}; use hugr::{HugrView, OutgoingPort}; use itertools::Itertools; use lazy_static::lazy_static; -use thiserror::Error; pub use hugr::ops::OpType; pub use hugr::types::{EdgeKind, Type, TypeRow}; @@ -408,15 +408,24 @@ pub(crate) fn remove_empty_wire( let [inp, out] = hugr.get_io(parent).expect("no IO nodes found at parent"); if input_port >= hugr.num_outputs(inp) { - return Err(CircuitMutError::InvalidPortOffset(input_port)); + return Err(CircuitMutError::InvalidPortOffset { + input_port, + dataflow_node: parent, + }); } let input_port = OutgoingPort::from(input_port); let link = hugr .linked_inputs(inp, input_port) .at_most_one() - .map_err(|_| CircuitMutError::DeleteNonEmptyWire(input_port.index()))?; + .map_err(|_| CircuitMutError::DeleteNonEmptyWire { + input_port: input_port.index(), + dataflow_node: parent, + })?; if link.is_some() && link.unwrap().0 != out { - return Err(CircuitMutError::DeleteNonEmptyWire(input_port.index())); + return Err(CircuitMutError::DeleteNonEmptyWire { + input_port: input_port.index(), + dataflow_node: parent, + }); } if link.is_some() { hugr.disconnect(inp, input_port); @@ -444,17 +453,18 @@ pub(crate) fn remove_empty_wire( } /// Errors that can occur when mutating a circuit. -#[derive(Debug, Clone, Error, PartialEq)] +#[derive(Display, Debug, Clone, Error, PartialEq)] +#[non_exhaustive] pub enum CircuitError { /// The parent node for the circuit does not exist in the HUGR. - #[error("{parent} cannot define a circuit as it is not present in the HUGR.")] + #[display("{parent} cannot define a circuit as it is not present in the HUGR.")] MissingParentNode { /// The node that was used as the parent. parent: Node, }, /// Circuit parents must have a concrete signature. - #[error( - "{} node {parent} cannot be used as a circuit parent. Circuits must have a concrete signature, but the node has signature '{}'.", + #[display( + "{parent} with op {} cannot be used as a circuit parent. Circuits must have a concrete signature, but the node has signature '{}'.", optype.name(), signature )] @@ -467,8 +477,8 @@ pub enum CircuitError { signature: PolyFuncType, }, /// The parent node for the circuit has an invalid optype. - #[error( - "{} node {parent} cannot be used as a circuit parent. Only 'DFG', 'DataflowBlock', or 'FuncDefn' operations are allowed.", + #[display( + "{parent} with op {} cannot be used as a circuit parent. Only 'DFG', 'DataflowBlock', or 'FuncDefn' operations are allowed.", optype.name() )] InvalidParentOp { @@ -480,22 +490,31 @@ pub enum CircuitError { } /// Errors that can occur when mutating a circuit. -#[derive(Debug, Clone, Error, PartialEq)] +#[derive(Display, Debug, Clone, Error, PartialEq, From)] +#[non_exhaustive] pub enum CircuitMutError { /// A Hugr error occurred. - #[error("Hugr error: {0:?}")] - HugrError(#[from] hugr::hugr::HugrError), + #[from] + HugrError(hugr::hugr::HugrError), /// A circuit validation error occurred. - #[error("transparent")] - CircuitError(#[from] CircuitError), + #[from] + CircuitError(CircuitError), /// The wire to be deleted is not empty. - #[from(ignore)] - #[error("Wire {0} cannot be deleted: not empty")] - DeleteNonEmptyWire(usize), - /// The wire does not exist. - #[from(ignore)] - #[error("Wire {0} does not exist")] - InvalidPortOffset(usize), + #[display("Tried to delete non-empty input wire with offset {input_port} on dataflow node {dataflow_node}")] + DeleteNonEmptyWire { + /// The input port offset + input_port: usize, + /// The port's node + dataflow_node: Node, + }, + /// Invalid dataflow input offset + #[display("Dataflow node {dataflow_node} does not have an input with offset {input_port}")] + InvalidPortOffset { + /// The input port offset + input_port: usize, + /// The port's node + dataflow_node: Node, + }, } /// Shift ports in range (free_port + 1 .. max_ind) by -1. @@ -720,7 +739,10 @@ mod tests { assert_eq!(circ.qubit_count(), 1); assert_eq!( remove_empty_wire(&mut circ, 0).unwrap_err(), - CircuitMutError::DeleteNonEmptyWire(0) + CircuitMutError::DeleteNonEmptyWire { + input_port: 0, + dataflow_node: circ.parent() + } ); } @@ -747,7 +769,10 @@ mod tests { assert_eq!(circ.units().count(), 0); assert_eq!( remove_empty_wire(&mut circ, 2).unwrap_err(), - CircuitMutError::InvalidPortOffset(2) + CircuitMutError::InvalidPortOffset { + input_port: 2, + dataflow_node: circ.parent() + } ); } } diff --git a/tket2/src/circuit/hash.rs b/tket2/src/circuit/hash.rs index 6dec938a..65cf81ba 100644 --- a/tket2/src/circuit/hash.rs +++ b/tket2/src/circuit/hash.rs @@ -2,12 +2,12 @@ use std::hash::{Hash, Hasher}; +use derive_more::{Display, Error}; use fxhash::{FxHashMap, FxHasher64}; use hugr::hugr::views::{HierarchyView, SiblingGraph}; use hugr::ops::{NamedOp, OpType}; use hugr::{HugrView, Node}; use petgraph::visit::{self as pg, Walker}; -use thiserror::Error; use super::Circuit; @@ -146,13 +146,14 @@ fn hash_node(circ: &impl HugrView, node: Node, state: &mut HashState) -> Result< } /// Errors that can occur while hashing a hugr. -#[derive(Debug, Clone, PartialEq, Eq, Error)] +#[derive(Debug, Display, Clone, PartialEq, Eq, Error)] +#[non_exhaustive] pub enum HashError { /// The circuit contains a cycle. - #[error("The circuit contains a cycle.")] + #[display("The circuit contains a cycle.")] CyclicCircuit, /// The hashed hugr is not a DFG. - #[error("Tried to hash a non-dfg hugr.")] + #[display("Tried to hash a non-dfg hugr.")] NotADfg, } diff --git a/tket2/src/ops.rs b/tket2/src/ops.rs index 4d1798c9..30c90185 100644 --- a/tket2/src/ops.rs +++ b/tket2/src/ops.rs @@ -14,10 +14,9 @@ use hugr::{ types::{type_param::TypeArg, Signature}, }; +use derive_more::{Display, Error}; use serde::{Deserialize, Serialize}; - -use strum_macros::{Display, EnumIter, EnumString, IntoStaticStr}; -use thiserror::Error; +use strum_macros::{EnumIter, EnumString, IntoStaticStr}; use crate::extension::REGISTRY; @@ -92,8 +91,8 @@ pub enum Pauli { Z, } -#[derive(Debug, Error, PartialEq, Clone)] -#[error("{} is not a Tk2Op.", op.name())] +#[derive(Display, Debug, Error, PartialEq, Clone)] +#[display("{} is not a Tk2Op.", op.name())] pub struct NotTk2Op { /// The offending operation. pub op: OpType, diff --git a/tket2/src/passes/commutation.rs b/tket2/src/passes/commutation.rs index 87b203af..cf4e1e8b 100644 --- a/tket2/src/passes/commutation.rs +++ b/tket2/src/passes/commutation.rs @@ -1,5 +1,6 @@ use std::{collections::HashMap, rc::Rc}; +use derive_more::{Display, Error, From}; use hugr::hugr::{hugrmut::HugrMut, HugrError, Rewrite}; use hugr::{CircuitUnit, Direction, HugrView, Node, Port, PortIndex}; use itertools::Itertools; @@ -11,8 +12,6 @@ use crate::{ ops::{Pauli, Tk2Op}, }; -use thiserror::Error; - type Qb = crate::circuit::units::LinearUnit; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -183,18 +182,25 @@ fn commutation_on_port(comms: &[(usize, Pauli)], port: Port) -> Option { } /// Error from a `PullForward` operation. -#[derive(Debug, Clone, Error, PartialEq, Eq)] -#[allow(missing_docs)] +#[derive(Debug, Display, Clone, Error, PartialEq, Eq, From)] +#[non_exhaustive] pub enum PullForwardError { - // Error in hugr mutation. - #[error("Hugr mutation error: {0:?}")] - HugrError(#[from] HugrError), - - #[error("Qubit {0} not found in command.")] - NoQbInCommand(usize), - - #[error("No subsequent command found for qubit {0}")] - NoCommandForQb(usize), + /// Error in hugr mutation. + #[display("Hugr mutation error: {_0}")] + #[from] + HugrError(HugrError), + /// Qubit not found in command. + #[display("Qubit {qubit} not found in command.")] + NoQbInCommand { + /// The qubit index + qubit: usize, + }, + /// No command for qubit + #[display("No subsequent command found for qubit {qubit}")] + NoCommandForQb { + /// The qubit index + qubit: usize, + }, } struct PullForward { @@ -219,7 +225,7 @@ impl Rewrite for PullForward { let qb_port = |command: &ComCommand, qb, direction| { command .port_of_qb(qb, direction) - .ok_or(PullForwardError::NoQbInCommand(qb.index())) + .ok_or(PullForwardError::NoQbInCommand { qubit: qb.index() }) }; // for each qubit, disconnect node and reconnect at destination. for qb in command.qubits() { @@ -238,7 +244,7 @@ impl Rewrite for PullForward { .unwrap(); let Some(new_neighbour_com) = new_nexts.get(&qb) else { - return Err(PullForwardError::NoCommandForQb(qb.index())); + return Err(PullForwardError::NoCommandForQb { qubit: qb.index() }); }; if new_neighbour_com == &command { // do not need to commute along this qubit. diff --git a/tket2/src/passes/pytket.rs b/tket2/src/passes/pytket.rs index 26468dbd..04cba9c0 100644 --- a/tket2/src/passes/pytket.rs +++ b/tket2/src/passes/pytket.rs @@ -3,6 +3,7 @@ //! //! This is a best-effort attempt, and may not always succeed. +use derive_more::{Display, Error, From}; use itertools::Itertools; use crate::serialize::pytket::OpConvertError; @@ -27,15 +28,16 @@ pub fn lower_to_pytket(circ: &Circuit) -> Result { } /// Errors that can occur during the lowering process. -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Display, Error, From)] #[non_exhaustive] pub enum PytketLoweringError { /// An error occurred during the conversion of an operation. - #[error("operation conversion error: {0}")] - OpConversionError(#[from] OpConvertError), + #[display("operation conversion error: {_0}")] + #[from] + OpConversionError(OpConvertError), /// The circuit is not fully-contained in a region. /// Function calls are not supported. - #[error("Non-local operations found. Function calls are not supported.")] + #[display("Non-local operations found. Function calls are not supported.")] NonLocalOperations, } diff --git a/tket2/src/portmatching.rs b/tket2/src/portmatching.rs index 29644b11..ff6f1878 100644 --- a/tket2/src/portmatching.rs +++ b/tket2/src/portmatching.rs @@ -62,12 +62,12 @@ use itertools::Itertools; pub use matcher::{PatternMatch, PatternMatcher}; pub use pattern::CircuitPattern; +use derive_more::{Display, Error}; use hugr::{ ops::{OpTag, OpTrait}, Node, Port, }; use matcher::MatchOp; -use thiserror::Error; use crate::{circuit::Circuit, utils::type_is_linear}; @@ -98,18 +98,18 @@ enum PEdge { InputEdge { src: Port }, } -#[derive(Debug, Clone, Error)] +#[derive(Debug, Clone, Error, Display)] #[non_exhaustive] enum InvalidEdgeProperty { /// The port is linked to multiple edges. - #[error("port {0:?} is linked to multiple edges")] - AmbiguousEdge(Port), + #[display("{port} in {node} is linked to multiple edges")] + AmbiguousEdge { port: Port, node: Node }, /// The port is not linked to any edge. - #[error("port {0:?} is not linked to any edge")] - NoLinkedEdge(Port), + #[display("{port} in {node} is not linked to any edge")] + NoLinkedEdge { port: Port, node: Node }, /// The port does not have a type. - #[error("{0}:{1} does not have a type")] - UntypedPort(Node, Port), + #[display("{port} in {node} does not have a type")] + UntypedPort { port: Port, node: Node }, } impl PEdge { @@ -121,9 +121,9 @@ impl PEdge { .exactly_one() .map_err(|mut e| { if e.next().is_some() { - InvalidEdgeProperty::AmbiguousEdge(src) + InvalidEdgeProperty::AmbiguousEdge { port: src, node } } else { - InvalidEdgeProperty::NoLinkedEdge(src) + InvalidEdgeProperty::NoLinkedEdge { port: src, node } } })?; if hugr.get_optype(dst_node).tag() == OpTag::Input { @@ -134,7 +134,7 @@ impl PEdge { let port_type = match hugr.get_optype(node).port_kind(src) { Some(EdgeKind::Value(typ)) => typ, Some(EdgeKind::Const(typ)) => typ, - _ => return Err(InvalidEdgeProperty::UntypedPort(node, src)), + _ => return Err(InvalidEdgeProperty::UntypedPort { node, port: src }), }; let is_reversible = type_is_linear(&port_type); Ok(Self::InternalEdge { diff --git a/tket2/src/portmatching/matcher.rs b/tket2/src/portmatching/matcher.rs index cb3abac2..bdcc38cd 100644 --- a/tket2/src/portmatching/matcher.rs +++ b/tket2/src/portmatching/matcher.rs @@ -8,6 +8,7 @@ use std::{ }; use super::{CircuitPattern, NodeID, PEdge, PNode}; +use derive_more::{Display, Error, From}; use hugr::hugr::views::sibling_subgraph::{ InvalidReplacement, InvalidSubgraph, InvalidSubgraphBoundary, TopoConvexChecker, }; @@ -21,7 +22,6 @@ use portmatching::{ EdgeProperty, PatternID, }; use smol_str::SmolStr; -use thiserror::Error; use crate::{ circuit::Circuit, @@ -371,42 +371,43 @@ impl PatternMatcher { } /// Errors that can occur when constructing matches. -#[derive(Debug, Clone, PartialEq, Eq, Error)] +#[derive(Debug, Display, Clone, PartialEq, Eq, Error)] +#[non_exhaustive] pub enum InvalidPatternMatch { /// The match is not convex. - #[error("match is not convex")] + #[display("match is not convex")] NotConvex, /// The subcircuit does not match the pattern. - #[error("invalid circuit region")] + #[display("invalid circuit region")] MatchNotFound, /// The subcircuit matched is not valid. /// /// This is typically a logic error in the code. - #[error("invalid circuit region")] + #[display("invalid circuit region")] InvalidSubcircuit, /// Empty matches are not supported. /// /// This should never happen is the pattern is not itself empty (in which /// case an error would have been raised earlier on). - #[error("empty match")] + #[display("empty match")] EmptyMatch, - #[error(transparent)] #[allow(missing_docs)] Other(InvalidSubgraph), } /// Errors that can occur when (de)serialising a matcher. -#[derive(Debug, Error)] +#[derive(Debug, Display, Error, From)] +#[non_exhaustive] pub enum MatcherSerialisationError { /// An IO error occurred - #[error("IO error: {0}")] - Io(#[from] io::Error), + #[display("IO error: {_0}")] + Io(io::Error), /// An error occurred during deserialisation - #[error("Deserialisation error: {0}")] - Deserialisation(#[from] rmp_serde::decode::Error), + #[display("Deserialisation error: {_0}")] + Deserialisation(rmp_serde::decode::Error), /// An error occurred during serialisation - #[error("Serialisation error: {0}")] - Serialisation(#[from] rmp_serde::encode::Error), + #[display("Serialisation error: {_0}")] + Serialisation(rmp_serde::encode::Error), } impl From for InvalidPatternMatch { diff --git a/tket2/src/portmatching/pattern.rs b/tket2/src/portmatching/pattern.rs index fdfdc6b2..53c49e53 100644 --- a/tket2/src/portmatching/pattern.rs +++ b/tket2/src/portmatching/pattern.rs @@ -1,11 +1,11 @@ //! Circuit Patterns for pattern matching +use derive_more::{Display, Error}; use hugr::{HugrView, IncomingPort}; use hugr::{Node, Port}; use itertools::Itertools; use portmatching::{patterns::NoRootFound, HashMap, Pattern, SinglePatternMatcher}; use std::fmt::Debug; -use thiserror::Error; use super::{ matcher::{validate_circuit_edge, validate_circuit_node}, @@ -133,22 +133,25 @@ impl Debug for CircuitPattern { } /// Conversion error from circuit to pattern. -#[derive(Debug, Error, PartialEq, Eq)] +#[derive(Display, Debug, Error, PartialEq, Eq)] #[non_exhaustive] pub enum InvalidPattern { /// An empty circuit cannot be a pattern. - #[error("Empty circuits are not allowed as patterns")] + #[display("Empty circuits are not allowed as patterns")] EmptyCircuit, /// Patterns must be connected circuits. - #[error("The pattern is not connected")] + #[display("The pattern is not connected")] NotConnected, /// Patterns cannot include empty wires. - #[error("The pattern contains an empty wire between {from_node}:{from_port} and {to_node}:{to_port}")] - #[allow(missing_docs)] + #[display("The pattern contains an empty wire between {from_node}, {from_port} and {to_node}, {to_port}")] EmptyWire { + /// The source node from_node: Node, + /// The source port from_port: Port, + /// The target node to_node: Node, + /// The target port to_port: Port, }, } diff --git a/tket2/src/rewrite/ecc_rewriter.rs b/tket2/src/rewrite/ecc_rewriter.rs index c845060a..b602e83a 100644 --- a/tket2/src/rewrite/ecc_rewriter.rs +++ b/tket2/src/rewrite/ecc_rewriter.rs @@ -12,7 +12,7 @@ //! to generate such a file is to use the `gen_ecc_set.sh` script at the root //! of the Quartz repository. -use derive_more::{From, Into}; +use derive_more::{Display, Error, From, Into}; use hugr::ops::custom::{resolve_extension_ops, OpaqueOpError}; use hugr::{Hugr, HugrView, PortIndex}; use itertools::Itertools; @@ -23,7 +23,6 @@ use std::{ io, path::{Path, PathBuf}, }; -use thiserror::Error; use crate::extension::REGISTRY; use crate::{ @@ -205,20 +204,21 @@ impl Rewriter for ECCRewriter { } /// Errors that can occur when (de)serialising an [`ECCRewriter`]. -#[derive(Debug, Error)] +#[derive(Debug, Display, Error, From)] +#[non_exhaustive] pub enum RewriterSerialisationError { /// An IO error occurred - #[error("IO error: {0}")] - Io(#[from] io::Error), + #[display("IO error: {_0}")] + Io(io::Error), /// An error occurred during deserialisation - #[error("Deserialisation error: {0}")] - Deserialisation(#[from] rmp_serde::decode::Error), + #[display("Deserialisation error: {_0}")] + Deserialisation(rmp_serde::decode::Error), /// An error occurred during serialisation - #[error("Serialisation error: {0}")] - Serialisation(#[from] rmp_serde::encode::Error), + #[display("Serialisation error: {_0}")] + Serialisation(rmp_serde::encode::Error), /// An error occurred during resolving extension ops - #[error("Resolving extension ops error: {0}")] - OpaqueOp(#[from] OpaqueOpError), + #[display("Resolving extension ops error: {_0}")] + OpaqueOp(OpaqueOpError), } fn into_targets(rep_sets: Vec) -> Vec { diff --git a/tket2/src/serialize/guppy.rs b/tket2/src/serialize/guppy.rs index c51e4a7d..f1d48a77 100644 --- a/tket2/src/serialize/guppy.rs +++ b/tket2/src/serialize/guppy.rs @@ -3,11 +3,11 @@ use std::path::Path; use std::{fs, io, mem}; +use derive_more::{Display, Error, From}; use hugr::ops::{NamedOp, OpTag, OpTrait, OpType}; use hugr::{Hugr, HugrView}; use hugr_cli::Package; use itertools::Itertools; -use thiserror::Error; use crate::extension::REGISTRY; use crate::{Circuit, CircuitError}; @@ -36,7 +36,7 @@ pub fn load_guppy_json_reader( let pkg: Package = serde_json::from_reader(reader)?; let mut hugrs = pkg.validate(&mut REGISTRY.clone())?; if hugrs.len() != 1 { - return Err(CircuitLoadError::InvalidNumHugrs(hugrs.len())); + return Err(CircuitLoadError::InvalidNumHugrs { count: hugrs.len() }); } let hugr = mem::take(&mut hugrs[0]); find_function(hugr, function) @@ -109,16 +109,19 @@ pub fn find_function(hugr: Hugr, function_name: &str) -> Result, }, /// The function has an invalid control flow structure. - #[error("Function '{function}' has an invalid control flow structure. Currently only flat functions with no control flow primitives are supported.")] + #[display("Function '{function}' has an invalid control flow structure. Currently only flat functions with no control flow primitives are supported.")] InvalidControlFlow { /// The function name. function: String, }, /// Error loading the circuit. - #[error("Error loading the circuit: {0}")] - CircuitLoadError(#[from] CircuitError), + #[display("Error loading the circuit: {_0}")] + #[from] + CircuitLoadError(CircuitError), /// Error validating the loaded circuit. - #[error("{0}")] - ValError(#[from] hugr_cli::validate::ValError), + #[display("{_0}")] + #[from] + ValError(hugr_cli::validate::ValError), /// The encoded HUGR package must have a single HUGR. - #[error("The encoded HUGR package must have a single HUGR, but it has {0} HUGRs.")] - InvalidNumHugrs(usize), + #[display("The encoded HUGR package must have a single HUGR, but it has {count} HUGRs.")] + InvalidNumHugrs { + /// The number of HUGRs encountered in the encoded HUGR package. + count: usize, + }, } diff --git a/tket2/src/serialize/pytket.rs b/tket2/src/serialize/pytket.rs index 7ee3f983..1ffffbc4 100644 --- a/tket2/src/serialize/pytket.rs +++ b/tket2/src/serialize/pytket.rs @@ -22,7 +22,7 @@ use std::{fs, io}; use hugr::ops::{NamedOp, OpType, Value}; -use thiserror::Error; +use derive_more::{Display, Error, From}; use tket_json_rs::circuit_json::{self, SerialCircuit}; use tket_json_rs::optype::OpType as SerialOpType; @@ -156,17 +156,19 @@ pub fn save_tk1_json_str(circ: &Circuit) -> Result { } /// Error type for conversion between `Op` and `OpType`. -#[derive(Debug, Error)] +#[derive(Display, Debug, Error, From)] #[non_exhaustive] pub enum OpConvertError { /// The serialized operation is not supported. - #[error("Unsupported serialized pytket operation: {0:?}")] + #[display("Unsupported serialized pytket operation: {_0:?}")] + #[error(ignore)] // `_0` is not the error source UnsupportedSerializedOp(SerialOpType), /// The serialized operation is not supported. - #[error("Cannot serialize tket2 operation: {0:?}")] + #[display("Cannot serialize tket2 operation: {_0:?}")] + #[error(ignore)] // `_0` is not the error source UnsupportedOpSerialization(OpType), /// The operation has non-serializable inputs. - #[error("Operation {} in {node} has an unsupported input of type {typ}.", optype.name())] + #[display("Operation {} in {node} has an unsupported input of type {typ}.", optype.name())] UnsupportedInputType { /// The unsupported type. typ: Type, @@ -176,7 +178,7 @@ pub enum OpConvertError { node: Node, }, /// The operation has non-serializable outputs. - #[error("Operation {} in {node} has an unsupported output of type {typ}.", optype.name())] + #[display("Operation {} in {node} has an unsupported output of type {typ}.", optype.name())] UnsupportedOutputType { /// The unsupported type. typ: Type, @@ -186,7 +188,7 @@ pub enum OpConvertError { node: Node, }, /// A parameter input could not be evaluated. - #[error("The {typ} parameter input for operation {} in {node} could not be resolved.", optype.name())] + #[display("The {typ} parameter input for operation {} in {node} could not be resolved.", optype.name())] UnresolvedParamInput { /// The parameter type. typ: Type, @@ -197,7 +199,7 @@ pub enum OpConvertError { }, /// The operation has output-only qubits. /// This is not currently supported by the encoder. - #[error("Operation {} in {node} has more output qubits than inputs.", optype.name())] + #[display("Operation {} in {node} has more output qubits than inputs.", optype.name())] TooManyOutputQubits { /// The unsupported type. typ: Type, @@ -207,14 +209,15 @@ pub enum OpConvertError { node: Node, }, /// The opaque tket1 operation had an invalid type parameter. - #[error("Opaque TKET1 operation had an invalid type parameter. {error}")] + #[display("Opaque TKET1 operation had an invalid type parameter. {error}")] + #[from] InvalidOpaqueTypeParam { /// The serialization error. - #[from] + #[error(source)] error: serde_json::Error, }, /// Tried to decode a tket1 operation with not enough parameters. - #[error( + #[display( "Operation {} is missing encoded parameters. Expected at least {expected} but only \"{}\" were specified.", optype.name(), params.iter().join(", "), @@ -228,7 +231,7 @@ pub enum OpConvertError { params: Vec, }, /// Tried to decode a tket1 operation with not enough qubit/bit arguments. - #[error( + #[display( "Operation {} is missing encoded arguments. Expected {expected_qubits} and {expected_bits}, but only \"{args:?}\" were specified.", optype.name(), )] @@ -245,14 +248,14 @@ pub enum OpConvertError { } /// Error type for conversion between `Op` and `OpType`. -#[derive(Debug, Error)] +#[derive(Debug, Display, Error, From)] #[non_exhaustive] pub enum TK1ConvertError { /// Operation conversion error. - #[error(transparent)] - OpConversionError(#[from] OpConvertError), + #[from] + OpConversionError(OpConvertError), /// The circuit has non-serializable inputs. - #[error("Circuit contains non-serializable input of type {typ}.")] + #[display("Circuit contains non-serializable input of type {typ}.")] NonSerializableInputs { /// The unsupported type. typ: Type, @@ -260,20 +263,23 @@ pub enum TK1ConvertError { /// The circuit uses multi-indexed registers. // // This could be supported in the future, if there is a need for it. - #[error("Register {register} in the circuit has multiple indices. Tket2 does not support multi-indexed registers.")] + #[display("Register {register} in the circuit has multiple indices. Tket2 does not support multi-indexed registers.")] MultiIndexedRegister { /// The register name. register: String, }, /// Invalid JSON, - #[error("Invalid pytket JSON. {0}")] - InvalidJson(#[from] serde_json::Error), + #[display("Invalid pytket JSON. {_0}")] + #[from] + InvalidJson(serde_json::Error), /// Invalid JSON, - #[error("Invalid JSON encoding. {0}")] - InvalidJsonEncoding(#[from] std::string::FromUtf8Error), + #[display("Invalid JSON encoding. {_0}")] + #[from] + InvalidJsonEncoding(std::string::FromUtf8Error), /// File not found., - #[error("Unable to load pytket json file. {0}")] - FileLoadError(#[from] io::Error), + #[display("Unable to load pytket json file. {_0}")] + #[from] + FileLoadError(io::Error), } /// Try to interpret a TKET1 parameter as a constant value.