From dcc562d27c2b436000a1a4e673bfec680d7c7d95 Mon Sep 17 00:00:00 2001
From: Seyon Sivarajah
Date: Fri, 6 Sep 2024 11:35:42 +0100
Subject: [PATCH 1/4] feat: dataflow builder methods for angle ops (#596)
---
tket2/src/extension/angle.rs | 109 ++++++++++++++++++++++++++++++++++-
1 file changed, 107 insertions(+), 2 deletions(-)
diff --git a/tket2/src/extension/angle.rs b/tket2/src/extension/angle.rs
index 315c7f87..44da13aa 100644
--- a/tket2/src/extension/angle.rs
+++ b/tket2/src/extension/angle.rs
@@ -1,9 +1,10 @@
+use hugr::builder::{BuildError, Dataflow};
use hugr::extension::prelude::{sum_with_error, BOOL_T, USIZE_T};
use hugr::extension::simple_op::{MakeOpDef, MakeRegisteredOp};
use hugr::extension::{ExtensionId, ExtensionSet, Version};
use hugr::ops::constant::{downcast_equal_consts, CustomConst};
use hugr::std_extensions::arithmetic::float_types::FLOAT64_TYPE;
-use hugr::type_row;
+use hugr::{type_row, Wire};
use hugr::{
types::{ConstTypeError, CustomType, Signature, Type, TypeBound},
Extension,
@@ -261,11 +262,94 @@ pub(super) fn add_to_extension(extension: &mut Extension) {
AngleOp::load_all_ops(extension).expect("add fail");
}
+/// An extension trait for [Dataflow] providing methods to add
+/// "tket2.angle" operations.
+pub trait AngleOpBuilder: Dataflow {
+ /// Add a "tket2.angle.atrunc" op.
+ fn add_atrunc(&mut self, angle: Wire, log_denom: Wire) -> Result {
+ Ok(self
+ .add_dataflow_op(AngleOp::atrunc, [angle, log_denom])?
+ .out_wire(0))
+ }
+ /// Add a "tket2.angle.aadd" op.
+ fn add_aadd(&mut self, angle1: Wire, angle2: Wire) -> Result {
+ Ok(self
+ .add_dataflow_op(AngleOp::aadd, [angle1, angle2])?
+ .out_wire(0))
+ }
+
+ /// Add a "tket2.angle.asub" op.
+ fn add_asub(&mut self, angle1: Wire, angle2: Wire) -> Result {
+ Ok(self
+ .add_dataflow_op(AngleOp::asub, [angle1, angle2])?
+ .out_wire(0))
+ }
+
+ /// Add a "tket2.angle.aneg" op.
+ fn add_aneg(&mut self, angle: Wire) -> Result {
+ Ok(self.add_dataflow_op(AngleOp::aneg, [angle])?.out_wire(0))
+ }
+
+ /// Add a "tket2.angle.anew" op.
+ fn add_anew(&mut self, numerator: Wire, log_denominator: Wire) -> Result {
+ Ok(self
+ .add_dataflow_op(AngleOp::anew, [numerator, log_denominator])?
+ .out_wire(0))
+ }
+
+ /// Add a "tket2.angle.aparts" op.
+ fn add_aparts(&mut self, angle: Wire) -> Result<[Wire; 2], BuildError> {
+ Ok(self
+ .add_dataflow_op(AngleOp::aparts, [angle])?
+ .outputs_arr())
+ }
+
+ /// Add a "tket2.angle.afromrad" op.
+ fn add_afromrad(&mut self, log_denominator: Wire, radians: Wire) -> Result {
+ Ok(self
+ .add_dataflow_op(AngleOp::afromrad, [log_denominator, radians])?
+ .out_wire(0))
+ }
+
+ /// Add a "tket2.angle.atorad" op.
+ fn add_atorad(&mut self, angle: Wire) -> Result {
+ Ok(self.add_dataflow_op(AngleOp::atorad, [angle])?.out_wire(0))
+ }
+
+ /// Add a "tket2.angle.aeq" op.
+ fn add_aeq(&mut self, angle1: Wire, angle2: Wire) -> Result {
+ Ok(self
+ .add_dataflow_op(AngleOp::aeq, [angle1, angle2])?
+ .out_wire(0))
+ }
+
+ /// Add a "tket2.angle.amul" op.
+ fn add_amul(&mut self, angle: Wire, scalar: Wire) -> Result {
+ Ok(self
+ .add_dataflow_op(AngleOp::amul, [angle, scalar])?
+ .out_wire(0))
+ }
+
+ /// Add a "tket2.angle.adiv" op.
+ fn add_adiv(&mut self, angle: Wire, scalar: Wire) -> Result {
+ Ok(self
+ .add_dataflow_op(AngleOp::adiv, [angle, scalar])?
+ .out_wire(0))
+ }
+}
+
+impl AngleOpBuilder for D {}
+
#[cfg(test)]
mod test {
- use hugr::ops::OpType;
+ use hugr::{
+ builder::{DFGBuilder, DataflowHugr},
+ ops::OpType,
+ };
use strum::IntoEnumIterator;
+ use crate::extension::REGISTRY;
+
use super::*;
#[test]
@@ -306,4 +390,25 @@ mod test {
assert_eq!(optype.cast(), Some(op));
}
}
+
+ #[test]
+ fn test_builder() {
+ let mut builder =
+ DFGBuilder::new(Signature::new(vec![ANGLE_TYPE, USIZE_T], vec![BOOL_T])).unwrap();
+
+ let [angle, scalar] = builder.input_wires_arr();
+ let radians = builder.add_atorad(angle).unwrap();
+ let angle = builder.add_afromrad(scalar, radians).unwrap();
+ let angle = builder.add_amul(angle, scalar).unwrap();
+ let angle = builder.add_adiv(angle, scalar).unwrap();
+ let angle = builder.add_aadd(angle, angle).unwrap();
+ let angle = builder.add_asub(angle, angle).unwrap();
+ let [num, log_denom] = builder.add_aparts(angle).unwrap();
+ let _angle_sum = builder.add_anew(num, log_denom).unwrap();
+ let angle = builder.add_aneg(angle).unwrap();
+ let angle = builder.add_atrunc(angle, log_denom).unwrap();
+ let bool = builder.add_aeq(angle, angle).unwrap();
+
+ let _hugr = builder.finish_hugr_with_outputs([bool], ®ISTRY).unwrap();
+ }
}
From 7ac02063365e7bbf56a0d3a94145bf394438381d Mon Sep 17 00:00:00 2001
From: Seyon Sivarajah
Date: Fri, 6 Sep 2024 14:22:18 +0100
Subject: [PATCH 2/4] feat: lowering tk2ops -> hseriesops (#579)
Closes #490
Using definitions in hqslib1
https://github.com/CQCL/pytket-quantinuum/blob/main/pytket/extensions/quantinuum/backends/hqslib1.inc
---
Cargo.lock | 26 ++-
Cargo.toml | 4 +-
tket2-hseries/Cargo.toml | 1 +
tket2-hseries/src/extension/hseries.rs | 203 ++++++++++++++--
tket2-hseries/src/extension/hseries/lower.rs | 231 +++++++++++++++++++
tket2-hseries/src/lib.rs | 2 +-
tket2/src/passes.rs | 1 +
7 files changed, 434 insertions(+), 34 deletions(-)
create mode 100644 tket2-hseries/src/extension/hseries/lower.rs
diff --git a/Cargo.lock b/Cargo.lock
index 75265692..88128dda 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -473,6 +473,17 @@ dependencies = [
"syn 2.0.71",
]
+[[package]]
+name = "delegate"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5060bb0febb73fa907273f8a7ed17ab4bf831d585eac835b28ec24a1e2460956"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.71",
+]
+
[[package]]
name = "deranged"
version = "0.3.11"
@@ -741,9 +752,9 @@ dependencies = [
[[package]]
name = "hugr"
-version = "0.12.0"
+version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41def210e277099199acba8a9f5d74938612607a0309cf695dc147b0f5870c74"
+checksum = "34ee4f66c9add4abc4b1ed5895b8f4ca1ee3727a0aacbb011696bb0a5946be01"
dependencies = [
"hugr-core",
"hugr-passes",
@@ -751,14 +762,14 @@ dependencies = [
[[package]]
name = "hugr-core"
-version = "0.9.0"
+version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dce2072c663e82ec9cc43282ff555c90fa139f3e5396a693f2b2a14d72e694e0"
+checksum = "b9cadea7900319ff43c7ee211a28e7de26a3d3f3b1d1bdd4c3de3dfee1199d3e"
dependencies = [
"bitvec",
"cgmath",
"context-iterators",
- "delegate 0.12.0",
+ "delegate 0.13.0",
"derive_more 1.0.0",
"downcast-rs",
"enum_dispatch",
@@ -782,9 +793,9 @@ dependencies = [
[[package]]
name = "hugr-passes"
-version = "0.8.0"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c47d41300b40e1dfd53f17382b2fe4dc815c9707d7507dc68846562ea7ee78b9"
+checksum = "6636bd4e828751880354ea8000bcc0be0d753aed9062783b72c78b668b813aa8"
dependencies = [
"hugr-core",
"itertools 0.13.0",
@@ -1842,6 +1853,7 @@ dependencies = [
"itertools 0.13.0",
"lazy_static",
"petgraph",
+ "rstest",
"serde",
"serde_json",
"smol_str",
diff --git a/Cargo.toml b/Cargo.toml
index dc4a5db4..f1a178fd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -26,8 +26,8 @@ missing_docs = "warn"
[workspace.dependencies]
# Make sure to run `just recompile-eccs` if the hugr serialisation format changes.
-hugr = "0.12.0"
-hugr-core = "0.9.0"
+hugr = "0.12.1"
+hugr-core = "0.9.1"
portgraph = "0.12"
pyo3 = "0.21.2"
itertools = "0.13.0"
diff --git a/tket2-hseries/Cargo.toml b/tket2-hseries/Cargo.toml
index dd3ec6be..5534a985 100644
--- a/tket2-hseries/Cargo.toml
+++ b/tket2-hseries/Cargo.toml
@@ -28,6 +28,7 @@ itertools.workspace = true
[dev-dependencies]
cool_asserts.workspace = true
petgraph.workspace = true
+rstest.workspace = true
[lints]
workspace = true
diff --git a/tket2-hseries/src/extension/hseries.rs b/tket2-hseries/src/extension/hseries.rs
index 96ec8145..f1d7a898 100644
--- a/tket2-hseries/src/extension/hseries.rs
+++ b/tket2-hseries/src/extension/hseries.rs
@@ -9,9 +9,13 @@ use hugr::{
extension::{
prelude::{BOOL_T, QB_T},
simple_op::{try_from_name, MakeOpDef, MakeRegisteredOp},
- ExtensionId, ExtensionRegistry, OpDef, SignatureFunc, Version, PRELUDE,
+ ExtensionId, ExtensionRegistry, ExtensionSet, OpDef, SignatureFunc, Version, PRELUDE,
+ },
+ ops::Value,
+ std_extensions::arithmetic::{
+ float_ops::FloatOps,
+ float_types::{ConstF64, EXTENSION as FLOAT_TYPES, FLOAT64_TYPE},
},
- std_extensions::arithmetic::float_types::{EXTENSION as FLOAT_TYPES, FLOAT64_TYPE},
type_row,
types::Signature,
Extension, Wire,
@@ -24,6 +28,10 @@ use crate::extension::futures;
use super::futures::future_type;
+mod lower;
+use lower::pi_mul_f64;
+pub use lower::{check_lowered, lower_tk2_op};
+
/// The "tket2.hseries" extension id.
pub const EXTENSION_ID: ExtensionId = ExtensionId::new_unchecked("tket2.hseries");
/// The "tket2.hseries" extension version.
@@ -32,7 +40,12 @@ pub const EXTENSION_VERSION: Version = Version::new(0, 1, 0);
lazy_static! {
/// The "tket2.hseries" extension.
pub static ref EXTENSION: Extension = {
- let mut ext = Extension::new(EXTENSION_ID, EXTENSION_VERSION);
+ let mut ext = Extension::new(EXTENSION_ID, EXTENSION_VERSION).with_reqs(ExtensionSet::from_iter([
+ futures::EXTENSION.name(),
+ PRELUDE.name(),
+ FLOAT_TYPES.name(),
+ tket2::extension::angle::ANGLE_EXTENSION.name(),
+ ].into_iter().cloned()));
HSeriesOp::load_all_ops(&mut ext).unwrap();
ext
};
@@ -40,10 +53,11 @@ lazy_static! {
/// Extension registry including the "tket2.hseries" extension and
/// dependencies.
pub static ref REGISTRY: ExtensionRegistry = ExtensionRegistry::try_new([
+ EXTENSION.to_owned(),
futures::EXTENSION.to_owned(),
PRELUDE.to_owned(),
- EXTENSION.to_owned(),
FLOAT_TYPES.to_owned(),
+ tket2::extension::angle::ANGLE_EXTENSION.to_owned(),
]).unwrap();
}
@@ -133,11 +147,7 @@ pub trait HSeriesOpBuilder: Dataflow {
/// Add a "tket2.hseries.Reset" op.
fn add_reset(&mut self, qb: Wire) -> Result {
- Ok(self
- .add_dataflow_op(HSeriesOp::Reset, [qb])?
- .outputs()
- .next()
- .unwrap())
+ Ok(self.add_dataflow_op(HSeriesOp::Reset, [qb])?.out_wire(0))
}
/// Add a "tket2.hseries.ZZMax" op.
@@ -158,27 +168,19 @@ pub trait HSeriesOpBuilder: Dataflow {
fn add_phased_x(&mut self, qb: Wire, angle1: Wire, angle2: Wire) -> Result {
Ok(self
.add_dataflow_op(HSeriesOp::PhasedX, [qb, angle1, angle2])?
- .outputs()
- .next()
- .unwrap())
+ .out_wire(0))
}
/// Add a "tket2.hseries.Rz" op.
fn add_rz(&mut self, qb: Wire, angle: Wire) -> Result {
Ok(self
.add_dataflow_op(HSeriesOp::Rz, [qb, angle])?
- .outputs()
- .next()
- .unwrap())
+ .out_wire(0))
}
/// Add a "tket2.hseries.QAlloc" op.
fn add_qalloc(&mut self) -> Result {
- Ok(self
- .add_dataflow_op(HSeriesOp::QAlloc, [])?
- .outputs()
- .next()
- .unwrap())
+ Ok(self.add_dataflow_op(HSeriesOp::QAlloc, [])?.out_wire(0))
}
/// Add a "tket2.hseries.QFree" op.
@@ -186,6 +188,161 @@ pub trait HSeriesOpBuilder: Dataflow {
self.add_dataflow_op(HSeriesOp::QFree, [qb])?;
Ok(())
}
+
+ /// Build a hadamard gate in terms of HSeries primitives.
+ fn build_h(&mut self, qb: Wire) -> Result {
+ let pi = pi_mul_f64(self, 1.0);
+ let pi_2 = pi_mul_f64(self, 0.5);
+ let pi_minus_2 = pi_mul_f64(self, -0.5);
+
+ let q = self.add_phased_x(qb, pi_2, pi_minus_2)?;
+ self.add_rz(q, pi)
+ }
+
+ /// Build an X gate in terms of HSeries primitives.
+ fn build_x(&mut self, qb: Wire) -> Result {
+ let pi = pi_mul_f64(self, 1.0);
+ let zero = pi_mul_f64(self, 0.0);
+ self.add_phased_x(qb, pi, zero)
+ }
+
+ /// Build a Y gate in terms of HSeries primitives.
+ fn build_y(&mut self, qb: Wire) -> Result {
+ let pi = pi_mul_f64(self, 1.0);
+ let pi_2 = pi_mul_f64(self, 0.5);
+ self.add_phased_x(qb, pi, pi_2)
+ }
+
+ /// Build a Z gate in terms of HSeries primitives.
+ fn build_z(&mut self, qb: Wire) -> Result {
+ let pi = pi_mul_f64(self, 1.0);
+ self.add_rz(qb, pi)
+ }
+
+ /// Build an S gate in terms of HSeries primitives.
+ fn build_s(&mut self, qb: Wire) -> Result {
+ let pi_2 = pi_mul_f64(self, 0.5);
+ self.add_rz(qb, pi_2)
+ }
+
+ /// Build an Sdg gate in terms of HSeries primitives.
+ fn build_sdg(&mut self, qb: Wire) -> Result {
+ let pi_minus_2 = pi_mul_f64(self, -0.5);
+ self.add_rz(qb, pi_minus_2)
+ }
+
+ /// Build a T gate in terms of HSeries primitives.
+ fn build_t(&mut self, qb: Wire) -> Result {
+ let pi_4 = pi_mul_f64(self, 0.25);
+ self.add_rz(qb, pi_4)
+ }
+
+ /// Build a Tdg gate in terms of HSeries primitives.
+ fn build_tdg(&mut self, qb: Wire) -> Result {
+ let pi_minus_4 = pi_mul_f64(self, -0.25);
+ self.add_rz(qb, pi_minus_4)
+ }
+
+ /// Build a CNOT gate in terms of HSeries primitives.
+ fn build_cx(&mut self, c: Wire, t: Wire) -> Result<[Wire; 2], BuildError> {
+ let pi = pi_mul_f64(self, 1.0);
+ let pi_2 = pi_mul_f64(self, 0.5);
+ let pi_minus_2 = pi_mul_f64(self, -0.5);
+
+ let t = self.add_phased_x(t, pi_minus_2, pi_2)?;
+ let [c, t] = self.add_zz_max(c, t)?;
+ let c = self.add_rz(c, pi_minus_2)?;
+ let t = self.add_phased_x(t, pi_2, pi)?;
+ let t = self.add_rz(t, pi_minus_2)?;
+
+ Ok([c, t])
+ }
+
+ /// Build a CY gate in terms of HSeries primitives.
+ fn build_cy(&mut self, a: Wire, b: Wire) -> Result<[Wire; 2], BuildError> {
+ let pi_2 = pi_mul_f64(self, 0.5);
+ let pi_minus_2 = pi_mul_f64(self, -0.5);
+ let zero = pi_mul_f64(self, 0.0);
+
+ let a = self.add_phased_x(a, pi_minus_2, zero)?;
+ let [a, b] = self.add_zz_max(a, b)?;
+ let a = self.add_rz(a, pi_2)?;
+ let b = self.add_phased_x(b, pi_2, pi_2)?;
+ let b = self.add_rz(b, pi_minus_2)?;
+ Ok([a, b])
+ }
+
+ /// Build a CZ gate in terms of HSeries primitives.
+ fn build_cz(&mut self, a: Wire, b: Wire) -> Result<[Wire; 2], BuildError> {
+ let pi = pi_mul_f64(self, 1.0);
+ let pi_2 = pi_mul_f64(self, 0.5);
+ let pi_minus_2 = pi_mul_f64(self, -0.5);
+
+ let a = self.add_phased_x(a, pi, pi)?;
+ let [a, b] = self.add_zz_max(a, b)?;
+ let a = self.add_phased_x(a, pi, pi_2)?;
+ let b = self.add_rz(b, pi_2)?;
+ let a = self.add_rz(a, pi_minus_2)?;
+
+ Ok([a, b])
+ }
+
+ /// Build a RX gate in terms of HSeries primitives.
+ fn build_rx(&mut self, qb: Wire, theta: Wire) -> Result {
+ let zero = pi_mul_f64(self, 0.0);
+ self.add_phased_x(qb, theta, zero)
+ }
+
+ /// Build a RY gate in terms of HSeries primitives.
+ fn build_ry(&mut self, qb: Wire, theta: Wire) -> Result {
+ let pi_2 = pi_mul_f64(self, 0.5);
+ self.add_phased_x(qb, theta, pi_2)
+ }
+
+ /// Build a CRZ gate in terms of HSeries primitives.
+ fn build_crz(&mut self, a: Wire, b: Wire, lambda: Wire) -> Result<[Wire; 2], BuildError> {
+ let two = self.add_load_const(Value::from(ConstF64::new(2.0)));
+ let lambda_2 = self
+ .add_dataflow_op(FloatOps::fdiv, [lambda, two])?
+ .out_wire(0);
+ let lambda_minus_2 = self
+ .add_dataflow_op(FloatOps::fneg, [lambda_2])?
+ .out_wire(0);
+
+ let [a, b] = self.add_zz_phase(a, b, lambda_minus_2)?;
+ let b = self.add_rz(b, lambda_2)?;
+ Ok([a, b])
+ }
+
+ /// Build a Toffoli (CCX) gate in terms of HSeries primitives.
+ fn build_toffoli(&mut self, a: Wire, b: Wire, c: Wire) -> Result<[Wire; 3], BuildError> {
+ let pi = pi_mul_f64(self, 1.0);
+ let pi_2 = pi_mul_f64(self, 0.5);
+ let pi_minus_2 = pi_mul_f64(self, -0.5);
+ let pi_4 = pi_mul_f64(self, 0.25);
+ let pi_minus_4 = pi_mul_f64(self, -0.25);
+ let pi_minus_3_4 = pi_mul_f64(self, -0.75);
+ let zero = pi_mul_f64(self, 0.0);
+
+ let c = self.add_phased_x(c, pi, pi)?;
+ let [b, c] = self.add_zz_max(b, c)?;
+ let c = self.add_phased_x(c, pi_4, pi_minus_2)?;
+ let [a, c] = self.add_zz_max(a, c)?;
+ let c = self.add_phased_x(c, pi_minus_4, zero)?;
+ let [b, c] = self.add_zz_max(b, c)?;
+ let b = self.add_phased_x(b, pi_minus_2, pi_4)?;
+ let c = self.add_phased_x(c, pi_4, pi_2)?;
+ let [a, c] = self.add_zz_max(a, c)?;
+ let [a, b] = self.add_zz_max(a, b)?;
+ let c = self.add_phased_x(c, pi_minus_3_4, zero)?;
+ let b = self.add_phased_x(b, pi_4, pi_4)?;
+ let [a, b] = self.add_zz_max(a, b)?;
+ let a = self.add_rz(a, pi_4)?;
+ let b = self.add_phased_x(b, pi_minus_2, pi_4)?;
+ let b = self.add_rz(b, pi_4)?;
+
+ Ok([a, b, c])
+ }
}
impl HSeriesOpBuilder for D {}
@@ -196,10 +353,8 @@ mod test {
use cool_asserts::assert_matches;
use futures::FutureOpBuilder as _;
- use hugr::{
- builder::{DataflowHugr, FunctionBuilder},
- ops::{NamedOp, OpType},
- };
+ use hugr::builder::{DataflowHugr, FunctionBuilder};
+ use hugr::ops::{NamedOp, OpType};
use strum::IntoEnumIterator as _;
use super::*;
diff --git a/tket2-hseries/src/extension/hseries/lower.rs b/tket2-hseries/src/extension/hseries/lower.rs
new file mode 100644
index 00000000..a7f755cb
--- /dev/null
+++ b/tket2-hseries/src/extension/hseries/lower.rs
@@ -0,0 +1,231 @@
+use std::collections::HashMap;
+
+use hugr::{
+ builder::{BuildError, DFGBuilder, Dataflow, DataflowHugr},
+ hugr::{hugrmut::HugrMut, HugrError},
+ ops::{self, DataflowOpTrait},
+ std_extensions::arithmetic::float_types::ConstF64,
+ types::Signature,
+ Hugr, HugrView, Node, Wire,
+};
+use strum::IntoEnumIterator;
+use thiserror::Error;
+use tket2::{extension::angle::AngleOpBuilder, Tk2Op};
+
+use crate::extension::hseries::{HSeriesOp, HSeriesOpBuilder};
+
+use super::REGISTRY;
+
+pub(super) fn pi_mul_f64(builder: &mut T, multiplier: f64) -> Wire {
+ const_f64(builder, multiplier * std::f64::consts::PI)
+}
+
+fn const_f64(builder: &mut T, value: f64) -> Wire {
+ builder.add_load_const(ops::Const::new(ConstF64::new(value).into()))
+}
+
+/// Errors produced by lowering [Tk2Op]s.
+#[derive(Debug, Error)]
+pub enum LowerTk2Error {
+ #[error("Error when building the circuit: {0}")]
+ BuildError(#[from] BuildError),
+
+ #[error("Unrecognised operation: {0:?} with {1} inputs")]
+ UnknownOp(Tk2Op, usize),
+
+ #[error("Error when replacing op: {0}")]
+ OpReplacement(#[from] HugrError),
+
+ #[error("Error when lowering ops: {0}")]
+ CircuitReplacement(#[from] hugr::algorithms::lower::LowerError),
+}
+
+fn op_to_hugr(op: Tk2Op) -> Result {
+ let sig = op.into_extension_op().signature();
+ let sig = Signature::new(sig.input, sig.output); // ignore extension delta
+ let mut b = DFGBuilder::new(sig)?;
+ let inputs: Vec<_> = b.input_wires().collect();
+
+ let outputs = match (op, inputs.as_slice()) {
+ (Tk2Op::H, [q]) => vec![b.build_h(*q)?],
+ (Tk2Op::X, [q]) => vec![b.build_x(*q)?],
+ (Tk2Op::Y, [q]) => vec![b.build_y(*q)?],
+ (Tk2Op::Z, [q]) => vec![b.build_z(*q)?],
+ (Tk2Op::S, [q]) => vec![b.build_s(*q)?],
+ (Tk2Op::Sdg, [q]) => vec![b.build_sdg(*q)?],
+ (Tk2Op::T, [q]) => vec![b.build_t(*q)?],
+ (Tk2Op::Tdg, [q]) => vec![b.build_tdg(*q)?],
+ (Tk2Op::CX, [c, t]) => b.build_cx(*c, *t)?.into(),
+ (Tk2Op::CY, [c, t]) => b.build_cy(*c, *t)?.into(),
+ (Tk2Op::CZ, [c, t]) => b.build_cz(*c, *t)?.into(),
+ (Tk2Op::Rx, [q, angle]) => {
+ let float = b.add_atorad(*angle)?;
+ vec![b.build_rx(*q, float)?]
+ }
+ (Tk2Op::Ry, [q, angle]) => {
+ let float = b.add_atorad(*angle)?;
+ vec![b.build_ry(*q, float)?]
+ }
+ (Tk2Op::Rz, [q, angle]) => {
+ let float = b.add_atorad(*angle)?;
+ vec![b.add_rz(*q, float)?]
+ }
+ (Tk2Op::CRz, [c, t, angle]) => {
+ let float = b.add_atorad(*angle)?;
+ b.build_crz(*c, *t, float)?.into()
+ }
+ (Tk2Op::Toffoli, [a, b_, c]) => b.build_toffoli(*a, *b_, *c)?.into(),
+ _ => return Err(LowerTk2Error::UnknownOp(op, inputs.len())), // non-exhaustive
+ };
+ Ok(b.finish_hugr_with_outputs(outputs, ®ISTRY)?)
+}
+
+/// Lower `Tk2Op` operations to `HSeriesOp` operations.
+pub fn lower_tk2_op(hugr: &mut impl HugrMut) -> Result, LowerTk2Error> {
+ let replaced_nodes = lower_direct(hugr)?;
+ let mut hugr_map: HashMap = HashMap::new();
+ for op in Tk2Op::iter() {
+ match op_to_hugr(op) {
+ Ok(h) => hugr_map.insert(op, h),
+ // filter out unknown ops, includes those covered by direct lowering
+ Err(LowerTk2Error::UnknownOp(_, _)) => continue,
+ Err(e) => return Err(e),
+ };
+ }
+
+ let lowered_nodes = hugr::algorithms::lower_ops(hugr, |op| hugr_map.get(&op.cast()?).cloned())?;
+
+ Ok([replaced_nodes, lowered_nodes].concat())
+}
+
+fn lower_direct(hugr: &mut impl HugrMut) -> Result, LowerTk2Error> {
+ Ok(hugr::algorithms::replace_many_ops(hugr, |op| {
+ let op: Tk2Op = op.cast()?;
+ Some(match op {
+ Tk2Op::QAlloc => HSeriesOp::QAlloc,
+ Tk2Op::QFree => HSeriesOp::QFree,
+ Tk2Op::Reset => HSeriesOp::Reset,
+ Tk2Op::Measure => HSeriesOp::Measure,
+ _ => return None,
+ })
+ })?
+ .into_iter()
+ .map(|(node, _)| node)
+ .collect())
+}
+
+/// Check there are no "tket2.quantum" ops left in the HUGR.
+///
+/// # Errors
+/// Returns vector of nodes that are not lowered.
+pub fn check_lowered(hugr: &impl HugrView) -> Result<(), Vec> {
+ let unlowered: Vec = hugr
+ .nodes()
+ .filter_map(|node| {
+ let optype = hugr.get_optype(node);
+ optype.as_extension_op().and_then(|ext| {
+ (ext.def().extension() == &tket2::extension::TKET2_EXTENSION_ID).then_some(node)
+ })
+ })
+ .collect();
+
+ if unlowered.is_empty() {
+ Ok(())
+ } else {
+ Err(unlowered)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use hugr::{builder::FunctionBuilder, type_row, HugrView};
+ use tket2::{extension::angle::ANGLE_TYPE, Circuit};
+
+ use super::*;
+ use rstest::rstest;
+
+ #[test]
+ fn test_lower_direct() {
+ let mut b = FunctionBuilder::new("circuit", Signature::new_endo(type_row![])).unwrap();
+ let [q] = b.add_dataflow_op(Tk2Op::QAlloc, []).unwrap().outputs_arr();
+ let [q] = b.add_dataflow_op(Tk2Op::Reset, [q]).unwrap().outputs_arr();
+ let [q, _] = b
+ .add_dataflow_op(Tk2Op::Measure, [q])
+ .unwrap()
+ .outputs_arr();
+ b.add_dataflow_op(Tk2Op::QFree, [q]).unwrap();
+ let mut h = b.finish_hugr_with_outputs([], ®ISTRY).unwrap();
+
+ let lowered = lower_direct(&mut h).unwrap();
+ assert_eq!(lowered.len(), 4);
+ let circ = Circuit::new(&h, h.root());
+ let ops: Vec = circ
+ .commands()
+ .map(|com| com.optype().cast().unwrap())
+ .collect();
+ assert_eq!(
+ ops,
+ vec![
+ HSeriesOp::QAlloc,
+ HSeriesOp::Reset,
+ HSeriesOp::Measure,
+ HSeriesOp::QFree
+ ]
+ );
+ assert_eq!(check_lowered(&h), Ok(()));
+ }
+
+ #[rstest]
+ #[case(Tk2Op::H, Some(vec![HSeriesOp::PhasedX, HSeriesOp::Rz]))]
+ #[case(Tk2Op::X, Some(vec![HSeriesOp::PhasedX]))]
+ #[case(Tk2Op::Y, Some(vec![HSeriesOp::PhasedX]))]
+ #[case(Tk2Op::Z, Some(vec![HSeriesOp::Rz]))]
+ #[case(Tk2Op::S, Some(vec![HSeriesOp::Rz]))]
+ #[case(Tk2Op::Sdg, Some(vec![HSeriesOp::Rz]))]
+ #[case(Tk2Op::T, Some(vec![HSeriesOp::Rz]))]
+ #[case(Tk2Op::Tdg, Some(vec![HSeriesOp::Rz]))]
+ #[case(Tk2Op::Rx, Some(vec![HSeriesOp::PhasedX]))]
+ #[case(Tk2Op::Ry, Some(vec![HSeriesOp::PhasedX]))]
+ #[case(Tk2Op::Rz, Some(vec![HSeriesOp::Rz]))]
+ // multi qubit ordering is not deterministic
+ #[case(Tk2Op::CX, None)]
+ #[case(Tk2Op::CY, None)]
+ #[case(Tk2Op::CZ, None)]
+ #[case(Tk2Op::CRz, None)]
+ #[case(Tk2Op::Toffoli, None)]
+ fn test_lower(#[case] t2op: Tk2Op, #[case] hseries_ops: Option>) {
+ // build dfg with just the op
+
+ let h = op_to_hugr(t2op).unwrap();
+ let circ = Circuit::new(&h, h.root());
+ let ops: Vec = circ
+ .commands()
+ .filter_map(|com| com.optype().cast())
+ .collect();
+ if let Some(hseries_ops) = hseries_ops {
+ assert_eq!(ops, hseries_ops);
+ }
+
+ assert_eq!(check_lowered(&h), Ok(()));
+ }
+
+ #[test]
+ fn test_mixed() {
+ let mut b = DFGBuilder::new(Signature::new(type_row![ANGLE_TYPE], type_row![])).unwrap();
+ let [angle] = b.input_wires_arr();
+ let [q] = b.add_dataflow_op(Tk2Op::QAlloc, []).unwrap().outputs_arr();
+ let [q] = b.add_dataflow_op(Tk2Op::H, [q]).unwrap().outputs_arr();
+ let [q] = b
+ .add_dataflow_op(Tk2Op::Rx, [q, angle])
+ .unwrap()
+ .outputs_arr();
+ b.add_dataflow_op(Tk2Op::QFree, [q]).unwrap();
+ let mut h = b.finish_hugr_with_outputs([], ®ISTRY).unwrap();
+
+ let lowered = lower_tk2_op(&mut h).unwrap();
+ assert_eq!(lowered.len(), 4);
+ // dfg, input, output, alloc, phasedx, rz, atorad, phasedx, free + 4x(float + load)
+ assert_eq!(h.node_count(), 17);
+ assert_eq!(check_lowered(&h), Ok(()));
+ }
+}
diff --git a/tket2-hseries/src/lib.rs b/tket2-hseries/src/lib.rs
index e8e759da..f53fb643 100644
--- a/tket2-hseries/src/lib.rs
+++ b/tket2-hseries/src/lib.rs
@@ -22,7 +22,7 @@ pub mod lazify_measure;
/// Modify a [hugr::Hugr] into a form that is acceptable for ingress into an H-series.
/// Returns an error if this cannot be done.
///
-/// To constuct a `HSeriesPass` use [Default::default].
+/// To construct a `HSeriesPass` use [Default::default].
#[derive(Debug, Clone, Copy, Default)]
pub struct HSeriesPass {
validation_level: ValidationLevel,
diff --git a/tket2/src/passes.rs b/tket2/src/passes.rs
index 2b3d7ec9..f560fc26 100644
--- a/tket2/src/passes.rs
+++ b/tket2/src/passes.rs
@@ -1,6 +1,7 @@
//! Optimisation passes and related utilities for circuits.
mod commutation;
+
pub use commutation::{apply_greedy_commutation, PullForwardError};
pub mod chunks;
From 58e162644ecd779dc100c21c3c2edde29ec98d7d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 9 Sep 2024 08:31:41 +0100
Subject: [PATCH 3/4] chore(deps-rs): bump the minor group with 2 updates
(#600)
Bumps the minor group with 2 updates:
[bytemuck](https://github.com/Lokathor/bytemuck) and
[delegate](https://github.com/kobzol/rust-delegate).
Updates `bytemuck` from 1.17.1 to 1.18.0
Changelog
Sourced from bytemuck's
changelog.
bytemuck
changelog
1.18
- Adds the
latest_stable_rust
cargo feature, which is a
blanket feature that turns all other features on that are both sound and
compatible with Stable rust.
Commits
860c391
chore: Release bytemuck version 1.18.0
4c535f9
Clean up Cargo.toml (#271)
374d184
Update changelog.md
1906570
Add a convenience feature indicating you're on the latest stable version
of R...
2d2b397
update the changelog to be less dismissive because we don't need that
negativ...
- See full diff in compare
view
Updates `delegate` from 0.12.0 to 0.13.0
Changelog
Sourced from delegate's
changelog.
0.13.0 (2. 9. 2024)
- Generalize match arms handling. You can now combine a match
expression target with annotations like
#[into]
and
others:
struct A;
impl A {
pub fn callable(self) -> Self {
self
}
}
struct B;
impl B {
pub fn callable(self) -> Self {
self
}
}
enum Common {
A(A),
B(B),
}
impl From<A> for Common {
fn from(inner: A) -> Self {
Self::A(inner)
}
}
impl From<B> for Common {
fn from(inner: B) -> Self {
Self::B(inner)
}
}
impl Common {
delegate! {
to match self {
// ---------- match
arms have incompatible types
Common::A(inner) => inner;
Common::B(inner) => inner;
} {
#[into]
pub fn callable(self) -> Self;
}
}
</tr></table>
... (truncated)
Commits
0f61120
Bump version to 0.13.0
d36262b
Modify changelog
3b21055
Make #[into]
and #[try_into]
generate
no_std
-compatible code
868eaee
Fix doc indentation
c681324
Fix doc indentation
eb03baf
Add example of delegating associated functions
556adea
Generalize handling of match expressions
- See full diff in compare
view
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore ` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore ` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore ` will
remove the ignore condition of the specified dependency and ignore
conditions
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
Cargo.lock | 6 +++---
Cargo.toml | 4 ++--
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 88128dda..87570247 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -155,9 +155,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "bytemuck"
-version = "1.17.1"
+version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "773d90827bc3feecfb67fab12e24de0749aad83c74b9504ecde46237b5cd24e2"
+checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae"
[[package]]
name = "byteorder"
@@ -1813,7 +1813,7 @@ dependencies = [
"criterion",
"crossbeam-channel",
"csv",
- "delegate 0.12.0",
+ "delegate 0.13.0",
"derive_more 0.99.18",
"downcast-rs",
"fxhash",
diff --git a/Cargo.toml b/Cargo.toml
index f1a178fd..bd1a0b6a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -34,14 +34,14 @@ itertools = "0.13.0"
tket-json-rs = "0.5.1"
tracing = "0.1.37"
portmatching = "0.3.1"
-bytemuck = "1.17.1"
+bytemuck = "1.18.0"
cgmath = "0.18.0"
chrono = "0.4.30"
clap = "4.5.16"
criterion = "0.5.1"
crossbeam-channel = "0.5.8"
csv = "1.2.2"
-delegate = "0.12.0"
+delegate = "0.13.0"
derive_more = "0.99.18"
downcast-rs = "1.2.0"
fxhash = "0.2.1"
From d9deacbc9a9e63c52efc57979dd8028397c2b466 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 9 Sep 2024 09:04:24 +0100
Subject: [PATCH 4/4] chore(deps-rs): bump the patch group with 3 updates
(#599)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps the patch group with 3 updates:
[clap](https://github.com/clap-rs/clap),
[serde](https://github.com/serde-rs/serde) and
[serde_json](https://github.com/serde-rs/json).
Updates `clap` from 4.5.16 to 4.5.17
Release notes
Sourced from clap's
releases.
v4.5.17
[4.5.17] - 2024-09-04
Fixes
- (help) Style required argument groups
- (derive) Improve error messages when unsupported fields are
used
Changelog
Sourced from clap's
changelog.
[4.5.17] - 2024-09-04
Fixes
- (help) Style required argument groups
- (derive) Improve error messages when unsupported fields are
used
Commits
6013ad4
chore: Release
f98e3ee
docs: Update changelog
addec17
Merge pull request #5681
from epage/static
3c69aaa
docs(complete): Add stdout warning to env
e46263a
docs(complete): Redistribute dynamic's documentation
de723aa
fix(complete)!: Flatten in prep for stabilization
6727c15
fix(complete): Section off existing completions
6842ed9
refactor(complete): Remove low-value w macro
17d6d24
Merge pull request #5680
from epage/unstable
23fb056
Merge pull request #5679
from epage/api
- Additional commits viewable in compare
view
Updates `serde` from 1.0.209 to 1.0.210
Release notes
Sourced from serde's
releases.
v1.0.210
- Support serializing and deserializing
IpAddr
and
SocketAddr
in no-std mode on Rust 1.77+ (#2816,
thanks @​MathiasKoch
)
- Make
serde::ser::StdError
and
serde::de::StdError
equivalent to
core::error::Error
on Rust 1.81+ (#2818)
Commits
89c4b02
Release 1.0.210
eeb8e44
Merge pull request #2818
from dtolnay/coreerror
785c2d9
Stabilize no-std StdError trait
d549f04
Reformat parse_ip_impl definition and calls
4c0dd63
Delete attr support from core::net deserialization macros
26fb134
Relocate cfg attrs out of parse_ip_impl and parse_socket_impl
07e614b
Merge pull request #2817
from dtolnay/corenet
b1f899f
Delete doc(cfg) attribute from impls that are supported in no-std
b4f860e
Merge pull request #2816
from MathiasKoch/chore/core-net
d940fe1
Reuse existing Buf wrapper as replacement for std::io::Write
- Additional commits viewable in compare
view
Updates `serde_json` from 1.0.127 to 1.0.128
Release notes
Sourced from serde_json's
releases.
1.0.128
- Support serializing maps containing 128-bit integer keys to
serde_json::Value (#1188,
thanks
@​Mrreadiness
)
Commits
d96b1d9
Release 1.0.128
599228d
Merge pull request #1188
from Mrreadiness/feat/add-hashmap-key-128-serializer
5416cee
feat: add support for 128 bit HashMap key serialization
27a4ca9
Upload CI Cargo.lock for reproducing failures
- See full diff in compare
view
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore ` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore ` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore ` will
remove the ignore condition of the specified dependency and ignore
conditions
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
Cargo.lock | 20 ++++++++++----------
Cargo.toml | 2 +-
2 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 87570247..79cc6812 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -253,9 +253,9 @@ dependencies = [
[[package]]
name = "clap"
-version = "4.5.16"
+version = "4.5.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019"
+checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac"
dependencies = [
"clap_builder",
"clap_derive",
@@ -263,9 +263,9 @@ dependencies = [
[[package]]
name = "clap_builder"
-version = "4.5.15"
+version = "4.5.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6"
+checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73"
dependencies = [
"anstream",
"anstyle",
@@ -1554,18 +1554,18 @@ dependencies = [
[[package]]
name = "serde"
-version = "1.0.209"
+version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
+checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.209"
+version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
+checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
dependencies = [
"proc-macro2",
"quote",
@@ -1574,9 +1574,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.127"
+version = "1.0.128"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad"
+checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
dependencies = [
"itoa",
"memchr",
diff --git a/Cargo.toml b/Cargo.toml
index bd1a0b6a..7d503b6a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -37,7 +37,7 @@ portmatching = "0.3.1"
bytemuck = "1.18.0"
cgmath = "0.18.0"
chrono = "0.4.30"
-clap = "4.5.16"
+clap = "4.5.17"
criterion = "0.5.1"
crossbeam-channel = "0.5.8"
csv = "1.2.2"