Skip to content

Commit 7f5d90d

Browse files
doug-qcqc-alecmark-koch
authored
feat: Add lazify-measure pass (#482)
Co-authored-by: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> Co-authored-by: Mark Koch <48097969+mark-koch@users.noreply.github.com>
1 parent a83a1af commit 7f5d90d

File tree

5 files changed

+449
-1
lines changed

5 files changed

+449
-1
lines changed

.github/change-filters.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ rust-core: &rust-core
88

99
rust:
1010
- *rust-core
11+
- "tket2-hseries/**"
1112
- "badger-optimiser/**"
1213
- "compile-rewriter/**"
1314

tket2-hseries/src/extension.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
//! This module defines the Hugr extensions used by tket2-hseries.
2-
32
pub mod futures;
3+
pub mod quantum_lazy;
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
//! This module defines the Hugr extension used to represent Lazy Quantum
2+
//! Operations.
3+
//!
4+
//! Lazyness is represented by returning `tket2.futures.Future` classical
5+
//! values. Qubits are never lazy.
6+
use hugr::{
7+
builder::{BuildError, Dataflow},
8+
extension::{
9+
prelude::{BOOL_T, QB_T},
10+
simple_op::{try_from_name, MakeExtensionOp, MakeOpDef, MakeRegisteredOp},
11+
ExtensionId, ExtensionRegistry, OpDef, SignatureFunc, PRELUDE,
12+
},
13+
ops::{CustomOp, OpType},
14+
types::FunctionType,
15+
Extension, Wire,
16+
};
17+
18+
use lazy_static::lazy_static;
19+
use strum_macros::{EnumIter, EnumString, IntoStaticStr};
20+
21+
use crate::extension::futures;
22+
23+
use super::futures::future_type;
24+
25+
/// The "tket2.quantum.lazy" extension id.
26+
pub const EXTENSION_ID: ExtensionId = ExtensionId::new_unchecked("tket2.quantum.lazy");
27+
28+
lazy_static! {
29+
/// The "tket2.quantum.lazy" extension.
30+
pub static ref EXTENSION: Extension = {
31+
let mut ext = Extension::new(EXTENSION_ID);
32+
LazyQuantumOp::load_all_ops(&mut ext).unwrap();
33+
ext
34+
};
35+
36+
/// Extension registry including the "tket2.quantum.lazy" extension and
37+
/// dependencies.
38+
pub static ref REGISTRY: ExtensionRegistry = ExtensionRegistry::try_new([
39+
futures::EXTENSION.to_owned(),
40+
PRELUDE.to_owned(),
41+
EXTENSION.to_owned()
42+
]).unwrap();
43+
}
44+
45+
#[derive(
46+
Clone,
47+
Copy,
48+
Debug,
49+
serde::Serialize,
50+
serde::Deserialize,
51+
Hash,
52+
PartialEq,
53+
Eq,
54+
PartialOrd,
55+
Ord,
56+
EnumIter,
57+
IntoStaticStr,
58+
EnumString,
59+
)]
60+
#[allow(missing_docs)]
61+
#[non_exhaustive]
62+
pub enum LazyQuantumOp {
63+
Measure,
64+
}
65+
66+
impl MakeOpDef for LazyQuantumOp {
67+
fn signature(&self) -> SignatureFunc {
68+
match self {
69+
Self::Measure => FunctionType::new(QB_T, vec![QB_T, future_type(BOOL_T)]).into(),
70+
}
71+
}
72+
73+
fn from_def(op_def: &OpDef) -> Result<Self, hugr::extension::simple_op::OpLoadError> {
74+
try_from_name(op_def.name(), &EXTENSION_ID)
75+
}
76+
77+
fn extension(&self) -> ExtensionId {
78+
EXTENSION_ID
79+
}
80+
}
81+
82+
impl MakeRegisteredOp for LazyQuantumOp {
83+
fn extension_id(&self) -> ExtensionId {
84+
EXTENSION_ID
85+
}
86+
87+
fn registry<'s, 'r: 's>(&'s self) -> &'r ExtensionRegistry {
88+
&REGISTRY
89+
}
90+
}
91+
92+
impl TryFrom<&OpType> for LazyQuantumOp {
93+
type Error = ();
94+
fn try_from(value: &OpType) -> Result<Self, Self::Error> {
95+
let Some(custom_op) = value.as_custom_op() else {
96+
Err(())?
97+
};
98+
match custom_op {
99+
CustomOp::Extension(ext) => Self::from_extension_op(ext).ok(),
100+
CustomOp::Opaque(opaque) => try_from_name(opaque.name(), &EXTENSION_ID).ok(),
101+
}
102+
.ok_or(())
103+
}
104+
}
105+
106+
/// An extension trait for [Dataflow] providing methods to add
107+
/// "tket2.quantum.lazy" operations.
108+
pub trait LazyQuantumOpBuilder: Dataflow {
109+
/// Add a "tket2.quantum.lazy.Measure" op.
110+
fn add_lazy_measure(&mut self, qb: Wire) -> Result<[Wire; 2], BuildError> {
111+
Ok(self
112+
.add_dataflow_op(LazyQuantumOp::Measure, [qb])?
113+
.outputs_arr())
114+
}
115+
}
116+
117+
impl<D: Dataflow> LazyQuantumOpBuilder for D {}
118+
119+
#[cfg(test)]
120+
mod test {
121+
use std::sync::Arc;
122+
123+
use cool_asserts::assert_matches;
124+
use futures::FutureOpBuilder as _;
125+
use hugr::{
126+
builder::{DataflowHugr, FunctionBuilder},
127+
ops::NamedOp,
128+
};
129+
use strum::IntoEnumIterator as _;
130+
131+
use super::*;
132+
133+
fn get_opdef(op: impl NamedOp) -> Option<&'static Arc<OpDef>> {
134+
EXTENSION.get_op(&op.name())
135+
}
136+
137+
#[test]
138+
fn create_extension() {
139+
assert_eq!(EXTENSION.name(), &EXTENSION_ID);
140+
141+
for o in LazyQuantumOp::iter() {
142+
assert_eq!(LazyQuantumOp::from_def(get_opdef(o).unwrap()), Ok(o));
143+
}
144+
}
145+
146+
#[test]
147+
fn circuit() {
148+
let hugr = {
149+
let mut func_builder =
150+
FunctionBuilder::new("circuit", FunctionType::new(QB_T, vec![QB_T, BOOL_T]))
151+
.unwrap();
152+
let [qb] = func_builder.input_wires_arr();
153+
let [qb, lazy_b] = func_builder.add_lazy_measure(qb).unwrap();
154+
let [b] = func_builder.add_read(lazy_b, BOOL_T).unwrap();
155+
func_builder
156+
.finish_hugr_with_outputs([qb, b], &REGISTRY)
157+
.unwrap()
158+
};
159+
assert_matches!(hugr.validate(&REGISTRY), Ok(_));
160+
}
161+
}

0 commit comments

Comments
 (0)