Skip to content

Commit f5ae2b0

Browse files
committed
more cleanup
1 parent 2078571 commit f5ae2b0

File tree

3 files changed

+64
-932
lines changed

3 files changed

+64
-932
lines changed

mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ struct GateDecomposition final
4040
config.setUseTopDownTraversal(true);
4141

4242
// Apply patterns in an iterative and greedy manner.
43-
if (mlir::failed(mlir::applyPatternsGreedily(op, std::move(patterns), config))) {
43+
if (mlir::failed(
44+
mlir::applyPatternsGreedily(op, std::move(patterns), config))) {
4445
signalPassFailure();
4546
}
4647
}

mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp

Lines changed: 62 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)