|
| 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 | + ®ISTRY |
| 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], ®ISTRY) |
| 157 | + .unwrap() |
| 158 | + }; |
| 159 | + assert_matches!(hugr.validate(®ISTRY), Ok(_)); |
| 160 | + } |
| 161 | +} |
0 commit comments