tket_json_rs/opbox.rs
1//! Data definition for box operations.
2//!
3//! These definitions correspond to the operations' `box` attribute in the
4//! [`circuit_v1`](https://github.com/Quantinuum/tket/blob/develop/schemas/circuit_v1.json)
5//! schema.
6
7use std::collections::HashMap;
8use std::str::FromStr;
9
10use crate::circuit_json::{
11 ClassicalExp, CustomGate, Matrix, Operation, Permutation, SerialCircuit,
12};
13use crate::optype::OpType;
14use crate::register::{Bitstring, Qubit};
15#[cfg(feature = "schemars")]
16use schemars::JsonSchema;
17use serde::{Deserialize, Serialize};
18
19/// Unique identifier for an [`OpBox`].
20#[cfg_attr(feature = "schemars", derive(JsonSchema))]
21#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq, Hash)]
22pub struct BoxID(uuid::Uuid);
23
24impl BoxID {
25 /// Create a new [`BoxID`] with a random UUID.
26 pub fn new() -> Self {
27 BoxID(uuid::Uuid::new_v4())
28 }
29}
30
31impl FromStr for BoxID {
32 type Err = String;
33
34 fn from_str(s: &str) -> Result<Self, Self::Err> {
35 let uuid = uuid::Uuid::from_str(s).map_err(|e| e.to_string())?;
36 Ok(BoxID(uuid))
37 }
38}
39
40impl std::fmt::Display for BoxID {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 write!(f, "{}", self.0)
43 }
44}
45
46impl Default for BoxID {
47 fn default() -> Self {
48 BoxID::new()
49 }
50}
51
52/// Box for an operation, the enum variant names come from the names
53/// of the C++ operations and are renamed if the string corresponding
54/// to the operation is differently named when serializing.
55#[cfg_attr(feature = "schemars", derive(JsonSchema))]
56#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
57#[serde(tag = "type")]
58#[allow(missing_docs)]
59#[non_exhaustive]
60pub enum OpBox {
61 /// Operation defined as a circuit.
62 CircBox {
63 id: BoxID,
64 /// The circuit defining the operation.
65 circuit: SerialCircuit,
66 },
67 /// One-qubit operation defined as a unitary matrix.
68 Unitary1qBox {
69 id: BoxID,
70 /// 2x2 matrix of complex numbers
71 matrix: [[(f64, f64); 2]; 2],
72 },
73 /// Two-qubit operation defined as a unitary matrix.
74 Unitary2qBox {
75 id: BoxID,
76 /// 4x4 matrix of complex numbers
77 matrix: [[(f64, f64); 4]; 4],
78 },
79 /// Three-qubit operation defined as a unitary matrix.
80 Unitary3qBox {
81 id: BoxID,
82 /// 8x8 matrix of complex numbers
83 matrix: Box<[[(f64, f64); 8]; 8]>,
84 },
85 /// Two-qubit operation defined in terms of a hermitian matrix and a phase.
86 ExpBox {
87 id: BoxID,
88 /// 4x4 matrix of complex numbers
89 matrix: [[(f64, f64); 4]; 4],
90 /// Phase of the operation.
91 phase: f64,
92 },
93 /// Operation defined as the exponential of a tensor of Pauli operators.
94 PauliExpBox {
95 id: BoxID,
96 /// List of Pauli operators.
97 paulis: Vec<String>,
98 /// Symengine expression.
99 phase: String,
100 /// Config param for decomposition of Pauli exponentials.
101 #[serde(default)]
102 cx_config: String,
103 },
104 /// A pair of (not necessarily commuting) Pauli exponentials performed in sequence.
105 PauliExpPairBox {
106 id: BoxID,
107 /// List of List of Pauli operators.
108 paulis_pair: Vec<Vec<String>>,
109 /// List of Symengine expressions.
110 phase_pair: Vec<String>,
111 /// Config param for decomposition of Pauli exponentials.
112 cx_config: String,
113 },
114 /// Operation defined as a set of commuting exponentials of a tensor of Pauli operators.
115 PauliExpCommutingSetBox {
116 id: BoxID,
117 /// List of Symengine expressions.
118 pauli_gadgets: Vec<(Vec<String>, String)>,
119 /// Config param for decomposition of Pauli exponentials.
120 cx_config: String,
121 },
122 /// An unordered collection of Pauli exponentials that can be synthesised in
123 /// any order, causing a change in the unitary operation. Synthesis order
124 /// depends on the synthesis strategy chosen only.
125 TermSequenceBox {
126 id: BoxID,
127 /// List of Symengine expressions.
128 pauli_gadgets: Vec<(Vec<String>, String)>,
129 /// Synthesis strategy. See [`PauliSynthStrat`].
130 #[serde(default)]
131 synth_strategy: PauliSynthStrat,
132 /// Partition strategy. See [`PauliPartitionStrat`].
133 #[serde(default)]
134 partition_strategy: PauliPartitionStrat,
135 /// Graph colouring method. See [`GraphColourMethod`].
136 #[serde(default)]
137 graph_colouring: GraphColourMethod,
138 /// Configurations for CXs upon decompose phase gadgets.
139 #[serde(default)]
140 cx_config: CXConfigType,
141 },
142 /// An operation capable of representing arbitrary Circuits made up of CNOT
143 /// and RZ, as a PhasePolynomial plus a boolean matrix representing an
144 /// additional linear transformation.
145 PhasePolyBox {
146 id: BoxID,
147 /// Number of qubits.
148 n_qubits: u32,
149 /// Map from qubits to inputs.
150 qubit_indices: Vec<(Qubit, u32)>,
151 /// The phase polynomial definition.
152 /// Represented by a map from bitstring to expression of coefficient.
153 phase_polynomial: Vec<Vec<(Bitstring, String)>>,
154 /// The additional linear transformation.
155 linear_transformation: Matrix,
156 },
157 /// A user-defined assertion specified by a list of Pauli stabilisers.
158 StabiliserAssertionBox {
159 id: BoxID,
160 stabilisers: Vec<PauliStabiliser>,
161 },
162 /// A user-defined assertion specified by a 2x2, 4x4, or 8x8 projector matrix.
163 ProjectorAssertionBox { id: BoxID, matrix: Matrix },
164 /// A user-defined gate defined by a parametrised Circuit.
165 #[serde(alias = "Composite")]
166 CustomGate {
167 id: BoxID,
168 /// The gate defined as a circuit.
169 gate: CustomGate,
170 // Vec of Symengine Expr
171 params: Vec<String>,
172 },
173 /// Wraps another quantum op, adding control qubits.
174 QControlBox {
175 id: BoxID,
176 /// Number of control qubits.
177 n_controls: u32,
178 /// The operation to be controlled.
179 op: Box<Operation>,
180 /// The state of the control.
181 #[serde(default)]
182 control_state: u32,
183 },
184 /// Holding box for abstract expressions on Bits.
185 ///
186 /// Deprecated in favour of [`OpType::ClExpr`].
187 ClassicalExpBox {
188 id: BoxID,
189 n_i: u32,
190 n_io: u32,
191 n_o: u32,
192 exp: ClassicalExp,
193 },
194 /// Binary matrix form of a stabilizer tableau for unitary Clifford circuits.
195 UnitaryTableauBox {
196 id: BoxID,
197 /// The tableau.
198 tab: UnitaryTableau,
199 },
200 /// A user-defined multiplexor specified by a map from bitstrings to Operations.
201 MultiplexorBox {
202 id: BoxID,
203 op_map: Vec<(Bitstring, Operation)>,
204 },
205 /// A user-defined multiplexed rotation gate specified by a map from
206 /// bitstrings to Operations.
207 MultiplexedRotationBox {
208 id: BoxID,
209 op_map: Vec<(Bitstring, Operation)>,
210 },
211 /// A user-defined multiplexed rotation gate specified by a map from
212 /// bitstrings to Operations.
213 MultiplexedU2Box {
214 id: BoxID,
215 op_map: Vec<(Bitstring, Operation)>,
216 #[serde(default = "default_true")]
217 impl_diag: bool,
218 },
219 /// A user-defined multiplexed tensor product of U2 gates specified by a map
220 /// from bitstrings to lists of Op or a list of bitstring-list(Op s) pairs.
221 MultiplexedTensoredU2Box {
222 id: BoxID,
223 op_map: Vec<(Bitstring, Operation)>,
224 },
225 /// An operation that constructs a circuit to implement the specified
226 /// permutation of classical basis states.
227 ToffoliBox {
228 id: BoxID,
229 /// The classical basis state permutation.
230 permutation: Permutation,
231 // Synthesis strategy. See [`ToffoliBoxSynthStrat`].
232 strat: ToffoliBoxSynthStrat,
233 // The rotation axis of the multiplexors used in the decomposition. Can
234 // be either `Rx` or `Ry`. Only applicable to the
235 // [`ToffoliBoxSynthStrat::Matching`] strategy. Default to `Ry`.
236 #[serde(skip_serializing_if = "Option::is_none")]
237 #[serde(default)]
238 rotation_axis: Option<OpType>,
239 },
240 /// An operation composed of ‘action’, ‘compute’ and ‘uncompute’ circuits
241 ConjugationBox {
242 id: BoxID,
243 /// Reversible computation.
244 compute: Box<Operation>,
245 /// Internal operation to be applied.
246 action: Box<Operation>,
247 /// Reverse uncomputation.
248 #[serde(default)]
249 uncompute: Option<Box<Operation>>,
250 },
251 /// A placeholder operation that holds resource data. This box type cannot
252 /// be decomposed into a circuit. It only serves to record resource data for
253 /// a region of a circuit: for example, upper and lower bounds on gate
254 /// counts and depth. A circuit containing such a box cannot be executed.
255 DummyBox {
256 id: BoxID,
257 /// Number of qubits.
258 n_qubits: u32,
259 /// Number of bits.
260 n_bits: u32,
261 /// Dummy resource data.
262 resource_data: ResourceData,
263 },
264 /// A box for preparing quantum states using multiplexed-Ry and multiplexed-Rz gates.
265 StatePreparationBox {
266 id: BoxID,
267 /// Normalised statevector of complex numbers
268 statevector: Matrix,
269 /// Whether to implement the dagger of the state preparation circuit, default to false.
270 #[serde(default)]
271 is_inverse: bool,
272 /// Whether to explicitly set the state to zero initially (by default
273 /// the initial zero state is assumed and no explicit reset is applied).
274 #[serde(default)]
275 with_initial_reset: bool,
276 },
277 /// A box for synthesising a diagonal unitary matrix into a sequence of multiplexed-Rz gates.
278 DiagonalBox {
279 id: BoxID,
280 /// Diagonal entries.
281 diagonal: Matrix,
282 /// Indicates whether the multiplexed-Rz gates take the shape of an upper triangle or a lower triangle. Default to true.
283 #[serde(default = "default_true")]
284 upper_triangle: bool,
285 },
286}
287
288fn default_true() -> bool {
289 true
290}
291
292/// Strategies for synthesising ToffoliBoxes.
293#[cfg_attr(feature = "schemars", derive(JsonSchema))]
294#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
295#[non_exhaustive]
296pub enum ToffoliBoxSynthStrat {
297 /// Use multiplexors to perform parallel swaps on hypercubes.
298 Matching,
299 /// Use CnX gates to perform transpositions.
300 Cycle,
301}
302
303/// Strategies for synthesising PauliBoxes.
304#[cfg_attr(feature = "schemars", derive(JsonSchema))]
305#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Default)]
306#[non_exhaustive]
307pub enum PauliSynthStrat {
308 /// Synthesise gadgets individually.
309 Individual,
310 /// Synthesise gadgets using an efficient pairwise strategy from Cowtan et
311 /// al <https://arxiv.org/abs/1906.01734>.
312 Pairwise,
313 /// Synthesise gadgets in commuting sets.
314 #[default]
315 Sets,
316}
317
318/// Strategies for partitioning Pauli tensors.
319#[cfg_attr(feature = "schemars", derive(JsonSchema))]
320#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Default)]
321#[non_exhaustive]
322pub enum PauliPartitionStrat {
323 /// Build sets of Pauli tensors in which each qubit has the same Pauli or
324 /// Pauli.I. Requires no additional CX gates for diagonalisation.
325 NonConflictingSets,
326 /// Build sets of mutually commuting Pauli tensors. Requires O(n^2) CX gates to diagonalise.
327 #[default]
328 CommutingSets,
329}
330
331/// Available methods to perform graph colouring.
332#[cfg_attr(feature = "schemars", derive(JsonSchema))]
333#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Default)]
334#[non_exhaustive]
335pub enum GraphColourMethod {
336 /// Does not build the graph before performing the colouring; partitions
337 /// while iterating through the Pauli tensors in the input order.
338 #[default]
339 Lazy,
340 /// Builds the graph and then greedily colours by iterating through the
341 /// vertices, with the highest degree first.
342 LargestFirst,
343 /// Builds the graph and then systematically checks all possibilities until
344 /// it finds a colouring with the minimum possible number of colours. Such
345 /// colourings need not be unique. Exponential time in the worst case, but
346 /// often runs much faster.
347 Exhaustive,
348}
349
350/// Available configurations for CXs upon decompose phase gadgets
351#[cfg_attr(feature = "schemars", derive(JsonSchema))]
352#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Default)]
353#[non_exhaustive]
354pub enum CXConfigType {
355 /// Linear nearest neighbour CX sequence. Linear depth.
356 Snake,
357 /// Every CX has same target, linear depth, good for gate cancellation.
358 Star,
359 /// Balanced tree: logarithmic depth, harder to route.
360 #[default]
361 Tree,
362 /// Support for multi-qubit architectures, decomposing to 3-qubit XXPhase3
363 /// gates instead of CXs where possible.
364 MultiQGate,
365}
366
367/// A simple struct for Pauli strings with +/- phase, used to represent Pauli
368/// strings in a stabiliser subgroup.
369#[cfg_attr(feature = "schemars", derive(JsonSchema))]
370#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq, Hash)]
371pub struct PauliStabiliser {
372 coeff: bool,
373 string: Vec<String>,
374}
375
376/// An object holding resource data for use in a [`OpType::DummyBox`].
377#[cfg_attr(feature = "schemars", derive(JsonSchema))]
378#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
379pub struct ResourceData {
380 /// Dictionary of counts of selected [`OpType`]s.
381 pub op_type_count: HashMap<OpType, ResourceBounds>,
382 /// Overall gate depth.
383 pub gate_depth: ResourceBounds,
384 /// Dictionary of depths of selected [`OpType`]s.
385 pub op_type_depth: HashMap<OpType, ResourceBounds>,
386 /// Overall two-qubit-gate depth.
387 pub two_qubit_gate_depth: ResourceBounds,
388}
389
390/// Structure holding a minimum and maximum value of some resource, where both
391/// values are unsigned integers.
392#[cfg_attr(feature = "schemars", derive(JsonSchema))]
393#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
394pub struct ResourceBounds {
395 /// Minimum value.
396 pub min: u32,
397 /// Maximum value.
398 pub max: u32,
399}
400
401/// Binary matrix form of a stabilizer tableau for unitary Clifford circuits.
402#[cfg_attr(feature = "schemars", derive(JsonSchema))]
403#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq, Hash)]
404pub struct UnitaryTableau {
405 /// A symplectic tableau.
406 pub tab: SymplecticTableau,
407 /// Ordered naming of qubits in the tableau.
408 pub qubits: Vec<Qubit>,
409}
410
411/// Binary matrix form of a collection of Pauli strings.
412#[cfg_attr(feature = "schemars", derive(JsonSchema))]
413#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq, Hash)]
414pub struct SymplecticTableau {
415 /// Number of rows in the tableau.
416 #[serde(default)]
417 pub nrows: u32,
418 /// Number of columns in the tableau.
419 #[serde(default)]
420 pub nqubits: u32,
421 /// The X matrix.
422 pub xmat: Matrix<bool>,
423 /// The Z matrix.
424 pub zmat: Matrix<bool>,
425 /// The phase matrix.
426 pub phase: Matrix<bool>,
427}