@@ -51,31 +51,49 @@ struct GateDecompositionPattern final
5151 ZSX = 11 ,
5252 };
5353
54+ using QubitId = std::size_t ;
55+ /* *
56+ * Gate sequence of single-qubit and/or two-qubit gates.
57+ */
5458 struct QubitGateSequence {
59+ /* *
60+ * Gate description which should be able to represent every possible
61+ * one-qubit or two-qubit operation.
62+ */
5563 struct Gate {
5664 qc::OpType type{qc::I};
5765 llvm::SmallVector<fp, 3 > parameter;
58- llvm::SmallVector<std:: size_t , 2 > qubitId = {0 };
66+ llvm::SmallVector<QubitId , 2 > qubitId = {0 };
5967 };
68+ /* *
69+ * Container sorting the gate sequence in order.
70+ */
6071 std::vector<Gate> gates;
72+
73+ fp globalPhase{};
74+ [[nodiscard]] bool hasGlobalPhase () const {
75+ return std::abs (globalPhase) > DEFAULT_ATOL;
76+ }
77+
78+ /* *
79+ * Calculate complexity of sequence according to getComplexity().
80+ */
6181 [[nodiscard]] std::size_t complexity () const {
62- // TODO: caching mechanism
82+ // TODO: caching mechanism?
6383 std::size_t c{};
6484 for (auto && gate : gates) {
6585 c += getComplexity (gate.type , gate.qubitId .size ());
6686 }
6787 if (hasGlobalPhase ()) {
68- // need to add two phase gates if a global phase needs to be applied
69- c += 2 * getComplexity (qc::P , 1 );
88+ // need to add a global phase gate if a global phase needs to be applied
89+ c += getComplexity (qc::GPhase , 1 );
7090 }
7191 return c;
7292 }
7393
74- fp globalPhase{};
75- [[nodiscard]] bool hasGlobalPhase () const {
76- return std::abs (globalPhase) > std::numeric_limits<fp>::epsilon ();
77- }
78-
94+ /* *
95+ * Calculate overall unitary matrix of the sequence.
96+ */
7997 [[nodiscard]] matrix4x4 getUnitaryMatrix () const {
8098 matrix4x4 unitaryMatrix =
8199 helpers::kroneckerProduct (IDENTITY_GATE, IDENTITY_GATE);
@@ -91,6 +109,11 @@ struct GateDecompositionPattern final
91109 using OneQubitGateSequence = QubitGateSequence;
92110 using TwoQubitGateSequence = QubitGateSequence;
93111
112+ /* *
113+ * Initialize pattern with a set of basis gates and euler bases.
114+ * The best combination of (basis gate, euler basis) will be evaluated for
115+ * each decomposition.
116+ */
94117 explicit GateDecompositionPattern (
95118 mlir::MLIRContext* context,
96119 llvm::SmallVector<QubitGateSequence::Gate> basisGate,
@@ -119,13 +142,12 @@ struct GateDecompositionPattern final
119142 return mlir::failure ();
120143 }
121144 if (series.isSingleQubitSeries ()) {
122- // only a single-qubit series
145+ // only a single-qubit series;
146+ // single-qubit euler decomposition is more efficient
123147 return mlir::failure ();
124148 }
125149
126150 matrix4x4 unitaryMatrix = series.getUnitaryMatrix ();
127- helpers::print (unitaryMatrix, " UNITARY MATRIX" );
128-
129151 auto targetDecomposition = TwoQubitWeylDecomposition::create (
130152 unitaryMatrix, DEFAULT_FIDELITY, std::nullopt );
131153
@@ -174,14 +196,29 @@ struct GateDecompositionPattern final
174196 }
175197
176198 struct TwoQubitSeries {
199+ /* *
200+ * Complexity of series using getComplexity() for each gate.
201+ */
177202 std::size_t complexity{0 };
203+ /* *
204+ * Qubits that are the input for the series.
205+ * First qubit will always be set, second qubit may be equal to
206+ * mlir::Value{} if the series consists of only single-qubit gates.
207+ *
208+ * All
209+ */
178210 std::array<mlir::Value, 2 > inQubits;
211+ /* *
212+ * Qubits that are the input for the series.
213+ * First qubit will always be set, second qubit may be equal to
214+ * mlir::Value{} if the series consists of only single-qubit gates.
215+ */
179216 std::array<mlir::Value, 2 > outQubits;
180217 fp globalPhase{};
181218
182219 struct Gate {
183220 UnitaryInterface op;
184- llvm::SmallVector<std:: size_t , 2 > qubitIds;
221+ llvm::SmallVector<QubitId , 2 > qubitIds;
185222 };
186223 llvm::SmallVector<Gate, 8 > gates;
187224
@@ -261,7 +298,7 @@ struct GateDecompositionPattern final
261298 complexity +=
262299 getComplexity (helpers::getQcType (initialOperation), in.size ());
263300
264- // TODO: necessary when using non-global phase gates ?
301+ // TODO: necessary?
265302 for (auto && globalPhaseOp :
266303 initialOperation->getBlock ()->getOps <GPhaseOp>()) {
267304 globalPhase += helpers::getParameters (globalPhaseOp)[0 ];
@@ -279,7 +316,7 @@ struct GateDecompositionPattern final
279316 throw std::logic_error{" Operand of single-qubit op and user of "
280317 " qubit is not in current outQubits" };
281318 }
282- std:: size_t qubitId = std::distance (outQubits.begin (), it);
319+ QubitId qubitId = std::distance (outQubits.begin (), it);
283320 *it = nextGate->getResult (0 );
284321
285322 gates.push_back ({.op = nextGate, .qubitIds = {qubitId}});
@@ -309,10 +346,10 @@ struct GateDecompositionPattern final
309346 ? *firstQubitIt
310347 : *secondQubitIt);
311348 // new qubit ID based on position in outQubits
312- auto newInQubitId = std::distance (outQubits.begin (), it);
349+ QubitId newInQubitId = std::distance (outQubits.begin (), it);
313350 // position in operation input; since there are only two qubits, it must
314351 // be the "not old" one
315- auto newOpInQubitId = 1 - std::distance (opInQubits.begin (), it2);
352+ QubitId newOpInQubitId = 1 - std::distance (opInQubits.begin (), it2);
316353
317354 // update inQubit and update dangling iterator, then proceed as usual
318355 inQubits[newInQubitId] = opInQubits[newOpInQubitId];
@@ -323,9 +360,8 @@ struct GateDecompositionPattern final
323360 // possible to collect other single-qubit operations
324361 backtrackSingleQubitSeries (newInQubitId);
325362 }
326- std::size_t firstQubitId = std::distance (outQubits.begin (), firstQubitIt);
327- std::size_t secondQubitId =
328- std::distance (outQubits.begin (), secondQubitIt);
363+ QubitId firstQubitId = std::distance (outQubits.begin (), firstQubitIt);
364+ QubitId secondQubitId = std::distance (outQubits.begin (), secondQubitIt);
329365 *firstQubitIt = nextGate->getResult (0 );
330366 *secondQubitIt = nextGate->getResult (1 );
331367
@@ -342,7 +378,7 @@ struct GateDecompositionPattern final
342378 * gate could have previous single-qubit operations that can be incorporated
343379 * in the series.
344380 */
345- void backtrackSingleQubitSeries (std:: size_t qubitId) {
381+ void backtrackSingleQubitSeries (QubitId qubitId) {
346382 auto prependSingleQubitGate = [&](UnitaryInterface op) {
347383 inQubits[qubitId] = op.getAllInQubits ()[0 ];
348384 gates.insert (gates.begin (), {.op = op, .qubitIds = {qubitId}});
@@ -409,7 +445,7 @@ struct GateDecompositionPattern final
409445
410446 auto inQubits = series.inQubits ;
411447 auto updateInQubits =
412- [&inQubits](const llvm::SmallVector<std:: size_t , 2 >& qubitIds,
448+ [&inQubits](const llvm::SmallVector<QubitId , 2 >& qubitIds,
413449 auto && newGate) {
414450 // TODO: need to handle controls differently?
415451 auto results = newGate.getAllOutQubits ();
@@ -852,11 +888,11 @@ struct GateDecompositionPattern final
852888 if (gate.qubitId .size () == 2 ) {
853889 if (gate.type == qc::X) {
854890 // controlled X (CX)
855- if (gate.qubitId == llvm::SmallVector<std:: size_t , 2 >{0 , 1 }) {
891+ if (gate.qubitId == llvm::SmallVector<QubitId , 2 >{0 , 1 }) {
856892 return matrix4x4{
857893 {1 , 0 , 0 , 0 }, {0 , 1 , 0 , 0 }, {0 , 0 , 0 , 1 }, {0 , 0 , 1 , 0 }};
858894 }
859- if (gate.qubitId == llvm::SmallVector<std:: size_t , 2 >{1 , 0 }) {
895+ if (gate.qubitId == llvm::SmallVector<QubitId , 2 >{1 , 0 }) {
860896 return matrix4x4{
861897 {1 , 0 , 0 , 0 }, {0 , 0 , 0 , 1 }, {0 , 0 , 1 , 0 }, {0 , 1 , 0 , 0 }};
862898 }
@@ -1788,7 +1824,7 @@ struct GateDecompositionPattern final
17881824 gates.globalPhase += qc::PI;
17891825 }
17901826
1791- auto addEulerDecomposition = [&](std::size_t index, std:: size_t qubitId) {
1827+ auto addEulerDecomposition = [&](std::size_t index, QubitId qubitId) {
17921828 if (auto && eulerDecomp = eulerDecompositions[index]) {
17931829 for (auto && gate : eulerDecomp->gates ) {
17941830 gates.gates .push_back ({.type = gate.type ,
@@ -1974,8 +2010,7 @@ struct GateDecompositionPattern final
19742010
19752011 static OneQubitGateSequence unitaryToGateSequenceInner (
19762012 matrix2x2 unitaryMat,
1977- const llvm::SmallVector<EulerBasis>& targetBasisList,
1978- std::size_t /* qubit*/ ,
2013+ const llvm::SmallVector<EulerBasis>& targetBasisList, QubitId /* qubit*/ ,
19792014 const std::vector<std::unordered_map<std::string, fp>>&
19802015 /* error_map*/ , // TODO: remove error_map+qubit for platform
19812016 // independence
0 commit comments