1use std::sync::{Arc, Weak};
2
3use crate::extension::bool::bool_type;
4use crate::extension::rotation::rotation_type;
5use crate::extension::sympy::SympyOpDef;
6use crate::extension::{TKET_EXTENSION, TKET_EXTENSION_ID as EXTENSION_ID};
7use hugr::ops::custom::ExtensionOp;
8use hugr::types::Type;
9use hugr::{
10 extension::{
11 prelude::{bool_t, option_type, qb_t},
12 simple_op::{try_from_name, MakeOpDef, MakeRegisteredOp},
13 ExtensionId, OpDef, SignatureFunc,
14 },
15 ops::OpType,
16 type_row,
17 types::Signature,
18};
19
20use derive_more::Display;
21use serde::{Deserialize, Serialize};
22use smol_str::ToSmolStr;
23use strum::{EnumIter, EnumString, IntoStaticStr};
24
25#[derive(
26 Clone,
27 Copy,
28 Debug,
29 Serialize,
30 Deserialize,
31 Hash,
32 PartialEq,
33 Eq,
34 PartialOrd,
35 Ord,
36 EnumIter,
37 IntoStaticStr,
38 EnumString,
39)]
40#[allow(missing_docs)]
41#[non_exhaustive]
42pub enum TketOp {
44 H,
45 CX,
46 CY,
47 CZ,
48 CRz,
49 T,
50 Tdg,
51 S,
52 Sdg,
53 X,
54 Y,
55 Z,
56 Rx,
57 Ry,
58 Rz,
59 Toffoli,
60 Measure,
61 MeasureFree,
62 QAlloc,
63 TryQAlloc,
64 QFree,
65 Reset,
66 V,
67 Vdg,
68}
69
70impl TketOp {
71 pub fn exposed_name(&self) -> smol_str::SmolStr {
73 <TketOp as Into<OpType>>::into(*self).to_smolstr()
74 }
75
76 pub fn into_extension_op(self) -> ExtensionOp {
78 <Self as MakeRegisteredOp>::to_extension_op(self)
79 .expect("Failed to convert to extension op.")
80 }
81}
82
83pub fn op_matches(op: &OpType, tket_op: TketOp) -> bool {
85 op.to_string() == tket_op.exposed_name()
86}
87
88#[derive(
89 Clone, Copy, Debug, Serialize, Deserialize, EnumIter, Display, PartialEq, PartialOrd, EnumString,
90)]
91#[allow(missing_docs)]
92pub enum Pauli {
94 I,
95 X,
96 Y,
97 Z,
98}
99
100impl Pauli {
101 pub fn commutes_with(&self, other: Self) -> bool {
103 *self == Pauli::I || other == Pauli::I || *self == other
104 }
105}
106impl MakeOpDef for TketOp {
107 fn opdef_id(&self) -> hugr::ops::OpName {
108 <&'static str>::from(self).into()
109 }
110
111 fn init_signature(&self, _extension_ref: &std::sync::Weak<hugr::Extension>) -> SignatureFunc {
112 use TketOp::*;
113 match self {
114 H | T | S | V | X | Y | Z | Tdg | Sdg | Vdg | Reset => Signature::new_endo(qb_t()),
115 CX | CZ | CY => Signature::new_endo(vec![qb_t(); 2]),
116 Toffoli => Signature::new_endo(vec![qb_t(); 3]),
117 Measure => Signature::new(qb_t(), vec![qb_t(), bool_t()]),
118 MeasureFree => Signature::new(qb_t(), bool_type()),
119 Rz | Rx | Ry => Signature::new(vec![qb_t(), rotation_type()], qb_t()),
120 CRz => Signature::new(vec![qb_t(), qb_t(), rotation_type()], vec![qb_t(); 2]),
121 QAlloc => Signature::new(type_row![], qb_t()),
122 TryQAlloc => Signature::new(type_row![], Type::from(option_type(qb_t()))),
123 QFree => Signature::new(qb_t(), type_row![]),
124 }
125 .into()
126 }
127
128 fn extension(&self) -> ExtensionId {
129 EXTENSION_ID.to_owned()
130 }
131
132 fn post_opdef(&self, def: &mut OpDef) {
133 def.add_misc(
134 "commutation",
135 serde_json::to_value(self.qubit_commutation()).unwrap(),
136 );
137 }
138
139 fn from_def(op_def: &OpDef) -> Result<Self, hugr::extension::simple_op::OpLoadError> {
140 try_from_name(op_def.name(), op_def.extension_id())
141 }
142
143 fn extension_ref(&self) -> Weak<hugr::Extension> {
144 Arc::downgrade(&TKET_EXTENSION)
145 }
146}
147
148impl MakeRegisteredOp for TketOp {
149 fn extension_id(&self) -> ExtensionId {
150 EXTENSION_ID.to_owned()
151 }
152
153 fn extension_ref(&self) -> Weak<hugr::Extension> {
154 Arc::<hugr::Extension>::downgrade(&TKET_EXTENSION)
155 }
156}
157
158impl TketOp {
159 pub(crate) fn qubit_commutation(&self) -> Vec<(usize, Pauli)> {
160 use TketOp::*;
161
162 match self {
163 X | V | Vdg | Rx => vec![(0, Pauli::X)],
164 Y => vec![(0, Pauli::Y)],
165 T | Z | S | Tdg | Sdg | Rz | Measure => vec![(0, Pauli::Z)],
166 CX => vec![(0, Pauli::Z), (1, Pauli::X)],
167 CZ => vec![(0, Pauli::Z), (1, Pauli::Z)],
168 _ => vec![],
170 }
171 }
172
173 pub fn is_quantum(&self) -> bool {
175 use TketOp::*;
176 match self {
177 H | CX | T | S | V | X | Y | Z | Tdg | Sdg | Vdg | Rz | Rx | Toffoli | Ry | CZ | CY
178 | CRz => true,
179 Measure | MeasureFree | QAlloc | TryQAlloc | QFree | Reset => false,
180 }
181 }
182}
183
184pub fn symbolic_constant_op(arg: String) -> OpType {
186 SympyOpDef.with_expr(arg).into()
187}
188
189#[cfg(test)]
190pub(crate) mod test {
191
192 use std::str::FromStr;
193 use std::sync::Arc;
194
195 use hugr::builder::{DFGBuilder, Dataflow, DataflowHugr};
196 use hugr::extension::prelude::{option_type, qb_t};
197 use hugr::extension::simple_op::{MakeExtensionOp, MakeOpDef};
198 use hugr::extension::{prelude::UnwrapBuilder as _, OpDef};
199 use hugr::types::Signature;
200 use hugr::{type_row, CircuitUnit, HugrView};
201 use itertools::Itertools;
202 use rstest::{fixture, rstest};
203 use strum::IntoEnumIterator;
204
205 use super::TketOp;
206 use crate::circuit::Circuit;
207 use crate::extension::bool::bool_type;
208 use crate::extension::{TKET_EXTENSION as EXTENSION, TKET_EXTENSION_ID as EXTENSION_ID};
209 use crate::utils::build_simple_circuit;
210 use crate::Pauli;
211 fn get_opdef(op: TketOp) -> Option<&'static Arc<OpDef>> {
212 EXTENSION.get_op(&op.op_id())
213 }
214 #[test]
215 fn create_extension() {
216 assert_eq!(EXTENSION.name(), &EXTENSION_ID);
217
218 for o in TketOp::iter() {
219 assert_eq!(TketOp::from_def(get_opdef(o).unwrap()), Ok(o));
220 }
221 }
222
223 #[fixture]
224 pub(crate) fn t2_bell_circuit() -> Circuit {
225 let h = build_simple_circuit(2, |circ| {
226 circ.append(TketOp::H, [0])?;
227 circ.append(TketOp::CX, [0, 1])?;
228 Ok(())
229 });
230
231 h.unwrap()
232 }
233
234 #[rstest]
235 fn check_t2_bell(t2_bell_circuit: Circuit) {
236 assert_eq!(t2_bell_circuit.commands().count(), 2);
237 }
238
239 #[test]
240 fn ancilla_circ() {
241 let h = build_simple_circuit(1, |circ| {
242 let empty: [CircuitUnit; 0] = []; let ancilla = circ.append_with_outputs(TketOp::QAlloc, empty)?[0];
244 let ancilla = circ.append_with_outputs(TketOp::Reset, [ancilla])?[0];
245
246 let ancilla = circ.append_with_outputs(
247 TketOp::CX,
248 [CircuitUnit::Linear(0), CircuitUnit::Wire(ancilla)],
249 )?[0];
250 let ancilla = circ.append_with_outputs(TketOp::Measure, [ancilla])?[0];
251 circ.append_and_consume(TketOp::QFree, [ancilla])?;
252
253 Ok(())
254 })
255 .unwrap();
256
257 assert_eq!(h.commands().count(), 5);
259 }
260
261 #[test]
262 fn try_qalloc_measure_free() {
263 let mut b = DFGBuilder::new(Signature::new(type_row![], bool_type())).unwrap();
264
265 let try_q = b
266 .add_dataflow_op(TketOp::TryQAlloc, [])
267 .unwrap()
268 .out_wire(0);
269 let [q] = b.build_unwrap_sum(1, option_type(qb_t()), try_q).unwrap();
270 let measured = b
271 .add_dataflow_op(TketOp::MeasureFree, [q])
272 .unwrap()
273 .out_wire(0);
274 let h = b.finish_hugr_with_outputs([measured]).unwrap();
275
276 let top_ops = h
277 .children(h.entrypoint())
278 .map(|n| h.get_optype(n))
279 .collect_vec();
280
281 assert_eq!(top_ops.len(), 5);
282 assert_eq!(
284 TketOp::from_op(top_ops[2].as_extension_op().unwrap()).unwrap(),
285 TketOp::TryQAlloc
286 );
287 assert!(top_ops[3].is_conditional());
288 assert_eq!(
289 TketOp::from_op(top_ops[4].as_extension_op().unwrap()).unwrap(),
290 TketOp::MeasureFree
291 );
292 }
293 #[test]
294 fn tket_op_properties() {
295 for op in TketOp::iter() {
296 assert!(op.exposed_name().starts_with(&EXTENSION_ID.to_string()));
298
299 let ext_op = op.into_extension_op();
300 assert_eq!(ext_op.args(), &[]);
301 assert_eq!(ext_op.def().extension_id(), &EXTENSION_ID);
302 let name = ext_op.def().name();
303 assert_eq!(TketOp::from_str(name), Ok(op));
304 }
305
306 assert!(TketOp::H.is_quantum());
308 assert!(!TketOp::Measure.is_quantum());
309
310 for (op, pauli) in [
311 (TketOp::X, Pauli::X),
312 (TketOp::Y, Pauli::Y),
313 (TketOp::Z, Pauli::Z),
314 ]
315 .iter()
316 {
317 assert_eq!(op.qubit_commutation(), &[(0, *pauli)]);
318 }
319 }
320}