-
Notifications
You must be signed in to change notification settings - Fork 35
✨ Collect clifford blocks #885
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?
✨ Collect clifford blocks #885
Conversation
Signed-off-by: Jannik Pflieger <[email protected]>
Codecov ReportAttention: Patch coverage is 📢 Thoughts on this report? Let us know! |
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.
Thanks for this initial PR 🙏🏼
I just went through everything and left a couple of inline comments. This still needs a bit of work before it is ready to be merged.
Don't hesitate to ask if you should have any questions.
@@ -90,7 +90,8 @@ class CircuitOptimizer { | |||
* @param qc the quantum circuit | |||
* @param maxBlockSize the maximum size of a block | |||
*/ | |||
static void collectBlocks(QuantumComputation& qc, std::size_t maxBlockSize); | |||
static void collectBlocks(QuantumComputation& qc, std::size_t maxBlockSize, | |||
bool collectCliffords); |
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.
bool collectCliffords); | |
bool onlyCollectCliffords = false); |
Maybe that name would be a little more suitable?
Also, this parameter is missing a description in the corresponding docstring.
Finally, this should default to false, otherwise this would be a breaking change.
[[nodiscard]] virtual bool isClifford() const { | ||
const OpType opType = this->getType(); | ||
const bool controlled = this->isControlled(); | ||
const std::size_t numberOfControls = this->getNcontrols(); | ||
if ((controlled) && ((numberOfControls >= 2))) { | ||
return false; | ||
} | ||
switch (opType) { | ||
case I: | ||
case X: | ||
case Y: | ||
case Z: | ||
case H: | ||
case S: | ||
case Sdg: | ||
case SX: | ||
case SXdg: | ||
case DCX: | ||
case SWAP: | ||
case iSWAP: | ||
case ECR: | ||
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.
This is not yet correct. As it stands, this would, e.g., classify a controlled-S gate as Clifford.
While the aspiration of this function is not to yield absolutely no false-negatives, it must not yield false positives.
- you also do not need the
this->
in the member function. - the function here should actually simply return
false
and an overload of the method should be implemented in theStandardOperation
class that contains this implementation. TheCompoundOperation
class will also need an overload that checks if every operation in the compound operation is Clifford. Other similar functions are already implemented in the respective classes and serve as a blueprint. - while we are at it, would you mind adding a docstring to any method that you add to the code base?
- you can tweak the code slightly to improve the checks. Identity gates as well as barriers are supported with arbitrary numbers of qubits. thus, you should check for these first. Then maybe check for the gates that allow a single control (X, Y, Z) and then for the gates that must have no controls (the rest from the list above).
bool isClifford = false; | ||
|
||
// check if the operation can be processed | ||
if (!op->isUnitary()) { | ||
canProcess = false; | ||
} | ||
|
||
// check if the operation is a Clifford operation | ||
if (collectCliffords && op->isClifford()) { | ||
isClifford = true; | ||
} | ||
|
||
if (collectCliffords && !isClifford) { | ||
canProcess = 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.
bool isClifford = false; | |
// check if the operation can be processed | |
if (!op->isUnitary()) { | |
canProcess = false; | |
} | |
// check if the operation is a Clifford operation | |
if (collectCliffords && op->isClifford()) { | |
isClifford = true; | |
} | |
if (collectCliffords && !isClifford) { | |
canProcess = false; | |
} | |
// check if the operation can be processed | |
if (!op->isUnitary() || (collectCliffords && !op->isClifford()) { | |
canProcess = false; | |
} |
much simpler
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.
All the changes in this file where
qc::CircuitOptimizer::collectBlocks(..., ..., false);
Should be reverted to remain unchanged by this PR based on the default argument for the pass.
TEST(CollectBlocks, emptyCliffordCircuit) { | ||
QuantumComputation qc(1); | ||
qc::CircuitOptimizer::collectBlocks(qc, 1, true); |
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.
This is a pretty useless test. The new flag has no influence at all on empty circuits.
TEST(CollectBlocks, singleCliffordGate) { | ||
QuantumComputation qc(1); | ||
qc.t(0); | ||
std::cout << qc << "\n"; | ||
qc::CircuitOptimizer::collectBlocks(qc, 1, true); |
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.
The T gate is not a Clifford gate. This seems to be a non-sense test. Please adjust to make this a reasonable test.
TEST(CollectBlocks, collectMultipleSingleQubitCliffordGates) { | ||
QuantumComputation qc(2); | ||
qc.h(0); | ||
qc.h(1); | ||
qc.t(0); | ||
qc.t(1); | ||
qc.x(0); | ||
qc.x(1); | ||
std::cout << qc << "\n"; | ||
qc::CircuitOptimizer::collectBlocks(qc, 1, true); | ||
std::cout << qc << "\n"; | ||
EXPECT_EQ(qc.size(), 6); | ||
} |
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.
The test name implies that this tests some kind of collection. However, the actual test is set up to not change a single thing. Please revise the test so that it is actually useful.
TEST(CollectBlocks, collectMultipleCliffordGates) { | ||
QuantumComputation qc(2); | ||
qc.h(0); | ||
qc.h(1); | ||
qc.t(0); | ||
qc.x(0); | ||
qc.x(1); | ||
std::cout << qc << "\n"; | ||
qc::CircuitOptimizer::collectBlocks(qc, 2, true); | ||
std::cout << qc << "\n"; | ||
EXPECT_EQ(qc.size(), 4); | ||
EXPECT_TRUE(qc.front()->isCompoundOperation()); | ||
} |
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.
This test does not seem quite right.
Looking at the circuit, I would have imagined there to be 3 blocks in the resulting circuit (H(0), H(1), X(1)), (T(0)), and (X(0)).
Please check the implementation again. and devise reasonable tests.
Instead of simply asserting the number of operations, assert what the actual operations are to make sure that the pass is doing what it is supposed to do.
TEST(CollectBlocks, collectTwoQubitCliffordGates) { | ||
QuantumComputation qc(2); | ||
qc.h(0); | ||
qc.h(1); | ||
qc.cx(0, 1); | ||
qc.t(0); | ||
qc.x(0); | ||
qc.x(1); | ||
std::cout << qc << "\n"; | ||
qc::CircuitOptimizer::collectBlocks(qc, 2, true); | ||
std::cout << qc << "\n"; | ||
EXPECT_EQ(qc.size(), 4); | ||
EXPECT_TRUE(qc.front()->isCompoundOperation()); | ||
} |
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.
A couple more tests are in order. Tests should cover at least every Clifford gate once and every non-Clifford gate once.
TEST(Operation, IsClifford) { | ||
const qc::StandardOperation x(0, qc::X); | ||
EXPECT_TRUE(x.isClifford()); | ||
const qc::StandardOperation cx(0, 1, qc::X); | ||
EXPECT_TRUE(cx.isClifford()); | ||
const qc::StandardOperation y(0, 1, qc::Y); | ||
EXPECT_TRUE(y.isClifford()); | ||
const qc::StandardOperation z(0, 1, qc::Z); | ||
EXPECT_TRUE(z.isClifford()); | ||
const qc::StandardOperation t(0, qc::T); | ||
EXPECT_FALSE(t.isClifford()); | ||
const qc::Controls controls = {qc::Control(0), qc::Control(1)}; | ||
const qc::StandardOperation ccx(controls, 2, qc::X); | ||
EXPECT_FALSE(ccx.isClifford()); | ||
const qc::StandardOperation ccy(controls, 2, qc::Y); | ||
EXPECT_FALSE(ccy.isClifford()); | ||
const qc::StandardOperation ccz(controls, 2, qc::Z); | ||
EXPECT_FALSE(ccz.isClifford()); | ||
const qc::StandardOperation h(0, qc::H); | ||
EXPECT_TRUE(h.isClifford()); | ||
const qc::StandardOperation s(0, qc::S); | ||
EXPECT_TRUE(s.isClifford()); | ||
const qc::StandardOperation sdg(0, qc::Sdg); | ||
EXPECT_TRUE(sdg.isClifford()); | ||
const qc::StandardOperation sx(0, qc::SX); | ||
EXPECT_TRUE(sx.isClifford()); | ||
const qc::StandardOperation sxdg(0, qc::SXdg); | ||
EXPECT_TRUE(sxdg.isClifford()); | ||
const qc::StandardOperation dcx(0, 1, qc::DCX); | ||
EXPECT_TRUE(dcx.isClifford()); | ||
const qc::StandardOperation swap(0, 1, qc::SWAP); | ||
EXPECT_TRUE(swap.isClifford()); | ||
const qc::StandardOperation iswap(0, 1, qc::iSWAP); | ||
EXPECT_TRUE(iswap.isClifford()); | ||
const qc::StandardOperation ecr(0, 1, qc::ECR); | ||
EXPECT_TRUE(ecr.isClifford()); | ||
} |
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 are not really conclusive (see comment where the function is defined).
@jannikpflieger any updates on this? it's been three weeks since the review above 😌 |
@burgholzer still doing it, since I was on vacation + exam, but I'm on it! I'm back since yesterday evening :D |
…-core-cliffordblockfinder into collectCliffordBlocks
…llect blocks, changed isClifford function, added docstring
…-core-cliffordblockfinder into collectCliffordBlocks
…-core-cliffordblockfinder into collectCliffordBlocks
…-core-cliffordblockfinder into collectCliffordBlocks
…-core-cliffordblockfinder into collectCliffordBlocks
Description
Added the ability to separate the collection of Clifford and non Clifford Blocks by adding a new function parameter. For that a isCliffordCheck was added as well. The isClifford check is very rough but includes all common Clifford gates. However, it does not include rotation gates that with the right angle could be considered Clifford. It does not include custom gates that could be Clifford as well.
The separation is done by finalizing a block when we iterate over a non-Clifford Block. When we want to collect Clifford and non Clifford gates separately then the function handles non-Clifford gates as "cannot be processed" and the canProcess variable is set to false. This ensures that we still have large CliffordBlocks but we have only single non CliffordBlocks. Essentially separating the non-CliffordBlocks from all Clifford and other non-CliffordBlocks.
Checklist: