-
-
Notifications
You must be signed in to change notification settings - Fork 54
Add multi-control functionality #1380
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 45 commits
2134c2e
0ae2b61
f8c4503
894add1
fb32afb
2375b74
275e832
c873318
068560c
53032bb
5c68a2f
3f46cd3
33b48a7
453e00a
b81e3e3
2505f27
9ab82f3
06a40af
92ed957
0911f7c
3d74d1b
9915b6a
cdc0ec3
ee9b279
cbda9d6
a8af5a7
8236fbf
14e1955
9de9ac9
826829e
93bf49d
48caa99
8cbe448
e10767e
9816218
ef2f343
77dae77
6460a92
4af8da7
63f2325
eed43bf
bf5e23e
3f1fa28
340bad9
4780212
ff77fa4
297078e
9ab3177
553ab8f
67b8fc2
440ff42
e82defd
1f224ce
18a8aa1
c4c524c
4a51d10
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -307,6 +307,123 @@ | |
| addCnot(diag, ctrl0, ctrl1, qubits); | ||
| } | ||
|
|
||
| void FunctionalityConstruction::addCcz(ZXDiagram& diag, const Qubit ctrl0, | ||
| const Qubit ctrl1, const Qubit target, | ||
| std::vector<Vertex>& qubits) { | ||
|
|
||
| addCnot(diag, ctrl1, target, qubits); | ||
| addZSpider(diag, target, qubits, PiExpression(PiRational(-1, 4))); | ||
| addCnot(diag, ctrl0, target, qubits); | ||
| addZSpider(diag, target, qubits, PiExpression(PiRational(1, 4))); | ||
| addCnot(diag, ctrl1, target, qubits); | ||
| addZSpider(diag, ctrl1, qubits, PiExpression(PiRational(1, 4))); | ||
| addZSpider(diag, target, qubits, PiExpression(PiRational(-1, 4))); | ||
| addCnot(diag, ctrl0, target, qubits); | ||
| addZSpider(diag, target, qubits, PiExpression(PiRational(1, 4))); | ||
| addCnot(diag, ctrl0, ctrl1, qubits); | ||
| addZSpider(diag, ctrl0, qubits, PiExpression(PiRational(1, 4))); | ||
| addZSpider(diag, ctrl1, qubits, PiExpression(PiRational(-1, 4))); | ||
| addZSpider(diag, target, qubits, PiExpression(PiRational(0, 1)), | ||
| EdgeType::Hadamard); | ||
| addCnot(diag, ctrl0, ctrl1, qubits); | ||
| addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); | ||
| } | ||
|
|
||
| void FunctionalityConstruction::addCrz(ZXDiagram& diag, | ||
| const PiExpression& phase, | ||
| const Qubit q0, const Qubit q1, | ||
| std::vector<Vertex>& qubits) { | ||
| // CRZ decomposition uses reversed CNOT direction | ||
| addCnot(diag, q1, q0, qubits); | ||
| addZSpider(diag, q0, qubits, -phase / 2); | ||
| addZSpider(diag, q1, qubits, phase / 2); | ||
| addCnot(diag, q1, q0, qubits); | ||
| } | ||
|
|
||
| void FunctionalityConstruction::addMcrz(ZXDiagram& diag, | ||
| const PiExpression& phase, | ||
| std::vector<Qubit> controls, | ||
| const Qubit target, | ||
| std::vector<Vertex>& qubits) { | ||
|
|
||
| const Qubit nextControl = controls.back(); | ||
| controls.pop_back(); | ||
|
|
||
| addCrz(diag, phase / 2, nextControl, target, qubits); | ||
| addMcx(diag, controls, target, qubits); | ||
| addCrz(diag, -phase / 2, nextControl, target, qubits); | ||
| addMcx(diag, controls, target, qubits); | ||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| void FunctionalityConstruction::addMcx(ZXDiagram& diag, | ||
| std::vector<Qubit> controls, | ||
| const Qubit target, | ||
| std::vector<Vertex>& qubits) { | ||
|
|
||
| switch (controls.size()) { | ||
| case 0: | ||
| addXSpider(diag, target, qubits, PiExpression(PiRational(1, 1))); | ||
| return; | ||
| case 1: | ||
| addCnot(diag, controls.front(), target, qubits); | ||
| return; | ||
| case 2: | ||
| addCcx(diag, controls.front(), controls.back(), target, qubits); | ||
| return; | ||
| default: | ||
| const auto half = static_cast<std::ptrdiff_t>((controls.size() + 1) / 2); | ||
| const std::vector<Qubit> first(controls.begin(), controls.begin() + half); | ||
| std::vector<Qubit> second(controls.begin() + half, controls.end()); | ||
|
|
||
| if (qubits.size() > controls.size() + 1) { | ||
| std::vector<Qubit> blocked = controls; | ||
| blocked.emplace_back(target); | ||
| std::ranges::sort(blocked); | ||
| std::ranges::sort(qubits); | ||
|
|
||
| std::vector<Qubit> available; | ||
| available.reserve(qubits.size()); | ||
| std::ranges::set_difference(qubits, blocked, | ||
| std::back_inserter(available)); | ||
|
|
||
| second.emplace_back(available.front()); | ||
|
|
||
| addMcx(diag, first, available.front(), qubits); | ||
| addMcx(diag, second, target, qubits); | ||
|
|
||
| addMcx(diag, first, available.front(), qubits); | ||
| addMcx(diag, second, target, qubits); | ||
| } else { | ||
| addRx(diag, PiExpression(PiRational(1, 4)), target, qubits); | ||
| addMcz(diag, second, target, qubits); | ||
| addRx(diag, PiExpression(-PiRational(1, 4)), target, qubits); | ||
| addMcx(diag, first, target, qubits); | ||
|
|
||
| addRx(diag, PiExpression(PiRational(1, 4)), target, qubits); | ||
| addMcz(diag, second, target, qubits); | ||
| addRx(diag, PiExpression(-PiRational(1, 4)), target, qubits); | ||
| addMcx(diag, first, target, qubits); | ||
| const Qubit lastControl = controls.back(); | ||
| controls.pop_back(); | ||
| addMcrz(diag, PiExpression(PiRational(1, 2)), controls, lastControl, | ||
| qubits); | ||
| } | ||
| } | ||
| } | ||
|
Comment on lines
+433
to
+474
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial
Static analysis flags that Proposed fix- std::vector<Qubit> second(controls.begin() + half, controls.end());
+ const std::vector<Qubit> second(controls.begin() + half, controls.end());🧰 Tools🪛 GitHub Check: 🇨 Lint / 🚨 Lint[warning] 451-451: src/zx/FunctionalityConstruction.cpp:451:5 [misc-const-correctness] 🤖 Prompt for AI Agents |
||
|
|
||
| void FunctionalityConstruction::addMcz(ZXDiagram& diag, | ||
| std::vector<Qubit> controls, | ||
| const Qubit target, | ||
| std::vector<Vertex>& qubits) { | ||
| const Qubit nextControl = controls.back(); | ||
| controls.pop_back(); | ||
|
|
||
| addCrz(diag, PiExpression(PiRational(1, 2)), nextControl, target, qubits); | ||
| addMcx(diag, controls, target, qubits); | ||
| addCrz(diag, PiExpression(-PiRational(1, 2)), nextControl, target, qubits); | ||
| addMcx(diag, controls, target, qubits); | ||
burgholzer marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
keefehuang marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+476
to
+483
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Move Same as Proposed fix void FunctionalityConstruction::addMcz(ZXDiagram& diag,
std::vector<Qubit> controls,
const Qubit target,
std::vector<Vertex>& qubits) {
addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard);
- addMcx(diag, controls, target, qubits);
+ addMcx(diag, std::move(controls), target, qubits);
addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard);
}🧰 Tools🪛 GitHub Check: 🇨 Lint / 🚨 Lint[warning] 481-481: src/zx/FunctionalityConstruction.cpp:481:16 [performance-unnecessary-value-param] 🤖 Prompt for AI Agents |
||
|
|
||
| FunctionalityConstruction::op_it | ||
| FunctionalityConstruction::parseOp(ZXDiagram& diag, op_it it, op_it end, | ||
| std::vector<Vertex>& qubits, | ||
|
|
@@ -538,6 +655,9 @@ | |
| qubits[static_cast<std::size_t>(target)], | ||
| EdgeType::Hadamard); | ||
| break; | ||
| case qc::OpType::RZ: | ||
| addCrz(diag, parseParam(op.get(), 0), ctrl, target, qubits); | ||
| break; | ||
|
|
||
| case qc::OpType::I: | ||
| break; | ||
|
|
@@ -582,11 +702,32 @@ | |
| case qc::OpType::X: | ||
| addCcx(diag, ctrl0, ctrl1, target, qubits); | ||
| break; | ||
|
|
||
| case qc::OpType::Z: | ||
| addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); | ||
| addCcx(diag, ctrl0, ctrl1, target, qubits); | ||
| addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); | ||
| addCcz(diag, ctrl0, ctrl1, target, qubits); | ||
| break; | ||
| case qc::OpType::RZ: | ||
| addMcrz(diag, parseParam(op.get(), 0), {ctrl0, ctrl1}, target, qubits); | ||
| break; | ||
| default: | ||
| throw ZXException("Unsupported Multi-control operation: " + | ||
| qc::toString(op->getType())); | ||
| } | ||
| } else if (op->getNtargets() == 1) { | ||
| const auto target = static_cast<Qubit>(p.at(op->getTargets().front())); | ||
| std::vector<Qubit> controls; | ||
| controls.reserve(op->getNcontrols()); | ||
| for (const auto& ctrl : op->getControls()) { | ||
| controls.emplace_back(static_cast<Qubit>(p.at(ctrl.qubit))); | ||
| } | ||
burgholzer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| switch (op->getType()) { | ||
| case qc::OpType::X: | ||
| addMcx(diag, controls, target, qubits); | ||
| break; | ||
| case qc::OpType::Z: | ||
| addMcz(diag, controls, target, qubits); | ||
| break; | ||
| case qc::OpType::RZ: | ||
| addMcrz(diag, parseParam(op.get(), 0), controls, target, qubits); | ||
| break; | ||
| default: | ||
| throw ZXException("Unsupported Multi-control operation: " + | ||
|
|
@@ -701,15 +842,16 @@ | |
| case qc::OpType::S: | ||
| case qc::OpType::Tdg: | ||
| case qc::OpType::Sdg: | ||
| case qc::OpType::RZ: | ||
| return true; | ||
|
|
||
| default: | ||
| return false; | ||
| } | ||
| } else if (op->getNcontrols() == 2) { | ||
| } else if (op->getNtargets() == 1) { | ||
| switch (op->getType()) { | ||
| case qc::OpType::X: | ||
| case qc::OpType::Z: | ||
| case qc::OpType::RZ: | ||
| return true; | ||
| default: | ||
| return false; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These commute, so it does not really matter too much, but for consistency with the
mcrzpatter, I'd prefer this order.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These don't actually commute. However, this is an implementation that allows for more gate parallelism. (You can check that moving this around breaks the equality).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am slightly confused here. By the rules of the ZX-calculus, one of these Z-spiders has to commute with the ZX, doesn't it?
This is also a standard circuit rewrite rule (commuting diagonal operators through control qubits).