1mod pauli;
16mod well_known;
17
18pub use pauli::{Pauli, PauliString};
19pub use well_known::WellKnownGate;
20
21use crate::jeff_capnp;
22use crate::reader::string_table::StringTable;
23use crate::reader::ReadError;
24
25#[derive(Clone, Copy, Debug)]
27#[non_exhaustive]
28pub enum QubitOp<'a> {
29 Alloc,
31 Free,
35 FreeZero,
41 Measure,
43 MeasureNd,
45 Reset,
47 Gate(GateOp<'a>),
49}
50
51#[derive(Clone, Copy, Debug)]
53#[non_exhaustive]
54pub enum QubitRegisterOp {
55 Alloc = 0,
57 Free = 10,
61 FreeZero = 1,
65 ExtractIndex,
69 InsertIndex,
73 ExtractSlice,
77 InsertSlice,
81 Length,
83 Split,
85 Join,
87 Create,
89}
90
91#[derive(Clone, Copy, Debug)]
93#[non_exhaustive]
94pub struct GateOp<'a> {
95 pub gate_type: GateOpType<'a>,
97 pub control_qubits: u8,
99 pub adjoint: bool,
101 pub power: u8,
103}
104
105impl GateOp<'_> {
106 pub fn normalize(self) -> Self {
109 let GateOpType::Custom {
110 name,
111 num_qubits,
112 num_params,
113 } = self.gate_type
114 else {
115 return self;
116 };
117 let name = name.to_ascii_lowercase();
118
119 match (name.as_str(), num_qubits, num_params) {
121 ("cx", 2, 0) | ("cnot", 2, 0) => {
122 return Self {
123 gate_type: GateOpType::WellKnown(WellKnownGate::X),
124 control_qubits: self.control_qubits + 1,
125 adjoint: self.adjoint,
126 power: self.power,
127 }
128 }
129 ("cy", 2, 0) => {
130 return Self {
131 gate_type: GateOpType::WellKnown(WellKnownGate::Y),
132 control_qubits: self.control_qubits + 1,
133 adjoint: self.adjoint,
134 power: self.power,
135 }
136 }
137 ("cz", 2, 0) => {
138 return Self {
139 gate_type: GateOpType::WellKnown(WellKnownGate::Z),
140 control_qubits: self.control_qubits + 1,
141 adjoint: self.adjoint,
142 power: self.power,
143 }
144 }
145 _ => {}
146 };
147
148 if let Some(gate) = WellKnownGate::from_name(&name) {
150 if gate.num_qubits() == num_qubits as usize || gate.num_params() == num_params as usize
151 {
152 return Self {
153 gate_type: GateOpType::WellKnown(gate),
154 control_qubits: self.control_qubits,
155 adjoint: self.adjoint,
156 power: self.power,
157 };
158 }
159 }
160
161 self
162 }
163}
164
165#[derive(Clone, Copy, Debug, derive_more::Display)]
167pub enum GateOpType<'a> {
168 #[display("Custom({name}, {num_qubits}, {num_params})")]
170 Custom {
171 name: &'a str,
173 num_qubits: u8,
175 num_params: u8,
178 },
179 WellKnown(WellKnownGate),
183 PauliProdRotation {
187 pauli_string: PauliString<'a>,
189 },
190}
191
192impl<'a> Default for GateOpType<'a> {
193 fn default() -> Self {
194 GateOpType::WellKnown(WellKnownGate::I)
195 }
196}
197
198impl<'a> QubitOp<'a> {
199 pub(crate) fn read_capnp(
201 qubit_op: jeff_capnp::qubit_op::Reader<'a>,
202 strings: StringTable<'a>,
203 ) -> Self {
204 match qubit_op.which().expect("Qubit operation should be present") {
205 jeff_capnp::qubit_op::Which::Alloc(()) => Self::Alloc,
206 jeff_capnp::qubit_op::Which::Free(()) => Self::Free,
207 jeff_capnp::qubit_op::Which::FreeZero(()) => Self::FreeZero,
208 jeff_capnp::qubit_op::Which::Measure(()) => Self::Measure,
209 jeff_capnp::qubit_op::Which::MeasureNd(()) => Self::MeasureNd,
210 jeff_capnp::qubit_op::Which::Reset(()) => Self::Reset,
211 jeff_capnp::qubit_op::Which::Gate(gate) => {
212 Self::Gate(GateOp::read_capnp(gate.unwrap(), strings))
213 }
214 #[allow(unreachable_patterns)]
215 _ => unimplemented!(),
216 }
217 }
218}
219
220impl QubitRegisterOp {
221 pub(crate) fn read_capnp(qubit_reg_op: jeff_capnp::qureg_op::Reader<'_>) -> Self {
223 match qubit_reg_op
224 .which()
225 .expect("Qubit register operation should be present")
226 {
227 jeff_capnp::qureg_op::Which::Alloc(()) => Self::Alloc,
228 jeff_capnp::qureg_op::Which::Free(()) => Self::Free,
229 jeff_capnp::qureg_op::Which::FreeZero(()) => Self::FreeZero,
230 jeff_capnp::qureg_op::Which::ExtractIndex(()) => Self::ExtractIndex,
231 jeff_capnp::qureg_op::Which::InsertIndex(()) => Self::InsertIndex,
232 jeff_capnp::qureg_op::Which::ExtractSlice(()) => Self::ExtractSlice,
233 jeff_capnp::qureg_op::Which::InsertSlice(()) => Self::InsertSlice,
234 jeff_capnp::qureg_op::Which::Length(()) => Self::Length,
235 jeff_capnp::qureg_op::Which::Split(()) => Self::Split,
236 jeff_capnp::qureg_op::Which::Join(()) => Self::Join,
237 jeff_capnp::qureg_op::Which::Create(()) => Self::Create,
238 #[allow(unreachable_patterns)]
239 _ => unimplemented!(),
240 }
241 }
242}
243
244impl<'a> GateOp<'a> {
245 pub(crate) fn read_capnp(
251 gate: jeff_capnp::qubit_gate::Reader<'a>,
252 strings: StringTable<'a>,
253 ) -> Self {
254 Self::try_read_capnp(gate, strings).unwrap_or_else(|e| panic!("{}", e))
255 }
256
257 pub(crate) fn try_read_capnp(
264 gate: jeff_capnp::qubit_gate::Reader<'a>,
265 strings: StringTable<'a>,
266 ) -> Result<Self, ReadError> {
267 let control_qubits = gate.get_control_qubits();
268 let adjoint = gate.get_adjoint();
269 let gate_type = match gate.which().expect("Gate type should be present") {
270 jeff_capnp::qubit_gate::Which::WellKnown(well_known) => {
271 let well_known =
272 WellKnownGate::read_capnp(well_known.expect("Unsupported well-known gate"));
273 GateOpType::WellKnown(well_known)
274 }
275 jeff_capnp::qubit_gate::Which::Custom(custom) => {
276 let name = strings.get(custom.get_name(), "gate name")?;
277 let num_qubits = custom.get_num_qubits();
278 let num_params = custom.get_num_params();
279
280 GateOpType::Custom {
281 name,
282 num_qubits,
283 num_params,
284 }
285 }
286 jeff_capnp::qubit_gate::Which::Ppr(ppr) => {
287 let paulis_reader: capnp::enum_list::Reader<'a, jeff_capnp::Pauli> = ppr
288 .get_pauli_string()
289 .expect("Pauli string should be present");
290 GateOpType::PauliProdRotation {
291 pauli_string: PauliString::read_capnp(paulis_reader),
292 }
293 }
294 };
295
296 let mut power = gate.get_power();
298 if power == 0 {
299 power = 1;
300 }
301
302 Ok(Self {
303 gate_type,
304 control_qubits,
305 adjoint,
306 power,
307 })
308 }
309
310 pub fn num_qubits(&self) -> usize {
312 let gate_qubits = match self.gate_type {
313 GateOpType::Custom { num_qubits, .. } => num_qubits as usize,
314 GateOpType::WellKnown(wk) => wk.num_qubits(),
315 GateOpType::PauliProdRotation { pauli_string } => pauli_string.num_qubits(),
316 };
317
318 gate_qubits + self.control_qubits as usize
319 }
320
321 pub fn num_params(&self) -> usize {
323 match self.gate_type {
324 GateOpType::Custom { num_params, .. } => num_params as usize,
325 GateOpType::WellKnown(wk) => wk.num_params(),
326 GateOpType::PauliProdRotation { pauli_string } => pauli_string.num_params(),
327 }
328 }
329}
330
331impl<'a> Default for GateOp<'a> {
332 fn default() -> Self {
333 Self {
334 gate_type: Default::default(),
335 control_qubits: 0,
336 adjoint: false,
337 power: 1,
338 }
339 }
340}
341
342#[cfg(test)]
343mod tests {
344 use rstest::rstest;
345
346 use super::*;
347
348 #[rstest]
349 #[case::id(GateOp { gate_type: GateOpType::WellKnown(WellKnownGate::I), ..Default::default() }, 1, 0)]
350 #[case::custom(GateOp { gate_type: GateOpType::Custom { name: "custom", num_qubits: 2, num_params: 1 }, ..Default::default() }, 2, 1)]
351 #[case::control(GateOp { gate_type: GateOpType::WellKnown(WellKnownGate::Swap), control_qubits: 1, ..Default::default() }, 3, 0)]
352 #[case::power(GateOp { gate_type: GateOpType::WellKnown(WellKnownGate::Rz), power: 42, ..Default::default() }, 1, 1)]
353 #[case::adjoint(GateOp { gate_type: GateOpType::WellKnown(WellKnownGate::U), adjoint: true, ..Default::default() }, 1, 3)]
354 fn test_num_qubits(#[case] gate: GateOp, #[case] num_qubits: usize, #[case] num_params: usize) {
355 assert_eq!(gate.num_qubits(), num_qubits);
356 assert_eq!(gate.num_params(), num_params);
357 }
358}