Skip to content

✨ 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

Open
wants to merge 53 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
212533c
isCliffordCheck
jannikpflieger Mar 11, 2025
3f64b2f
added isCliffordOperation
jannikpflieger Mar 19, 2025
5db7ac5
edited collectBlocks to handle CliffordBlocks
jannikpflieger Mar 20, 2025
4995e47
Added Tests and redefined if statements in opertaon
jannikpflieger Mar 21, 2025
62004e5
fixed Testcases
jannikpflieger Mar 21, 2025
1656046
Update ci.yml
jannikpflieger Mar 21, 2025
8c811bd
Update ci.yaml again
jannikpflieger Mar 21, 2025
5812c72
Merge branch 'cda-tum:main' into collectCliffordBlocks
jannikpflieger Mar 21, 2025
d9d2132
🎨 pre-commit fixes
pre-commit-ci[bot] Mar 24, 2025
3282c67
more Test Coverage and fix linting errors
jannikpflieger Mar 24, 2025
43d18ae
Merge branch 'cda-tum:main' into collectCliffordBlocks
jannikpflieger Mar 24, 2025
088ada5
removed outcommented code
jannikpflieger Mar 24, 2025
486e225
Added import and fixed variable const
jannikpflieger Mar 24, 2025
66ed0fc
Merge branch 'munich-quantum-toolkit:main' into collectCliffordBlocks
jannikpflieger Apr 29, 2025
e1ffb25
changed parameter name and added docstring
jannikpflieger Apr 29, 2025
0ae9525
Merge branch 'collectCliffordBlocks' of github.com:jannikpflieger/mqt…
jannikpflieger Apr 29, 2025
d7264a0
🎨 pre-commit fixes
pre-commit-ci[bot] Apr 29, 2025
a12f11a
reverted original tests back to normal, changed function header of co…
jannikpflieger Apr 29, 2025
7eb95fc
Merge branch 'collectCliffordBlocks' of github.com:jannikpflieger/mqt…
jannikpflieger Apr 29, 2025
5555b8e
🎨 pre-commit fixes
pre-commit-ci[bot] Apr 29, 2025
e5de02d
Moved IsCliffordfunction to StandardOperation and CompoundOperation
jannikpflieger Apr 29, 2025
6e728cb
Merge branch 'collectCliffordBlocks' of github.com:jannikpflieger/mqt…
jannikpflieger Apr 29, 2025
71e8bc6
🎨 pre-commit fixes
pre-commit-ci[bot] Apr 29, 2025
f771766
Corrected Test for isClifford operations
jannikpflieger Apr 30, 2025
d97eef5
Merge branch 'collectCliffordBlocks' of github.com:jannikpflieger/mqt…
jannikpflieger Apr 30, 2025
227d2ec
🎨 pre-commit fixes
pre-commit-ci[bot] Apr 30, 2025
5ce85b4
Removed usless test added more meanigful ones
jannikpflieger May 1, 2025
326c5b9
Merge branch 'collectCliffordBlocks' of github.com:jannikpflieger/mqt…
jannikpflieger May 1, 2025
ace9eaf
🎨 pre-commit fixes
pre-commit-ci[bot] May 1, 2025
6ff54c3
changed tests a little, some were not correct
jannikpflieger May 12, 2025
479d3ba
Merge branch 'collectCliffordBlocks' of github.com:jannikpflieger/mqt…
jannikpflieger May 12, 2025
e0a4e50
fixed probelsm in collect Blocks
jannikpflieger Jun 6, 2025
a475fab
Merge branch 'munich-quantum-toolkit:main' into collectCliffordBlocks
jannikpflieger Jun 6, 2025
337b2ef
added test for coverage
jannikpflieger Jun 9, 2025
16ff394
Merge branch 'main' into collectCliffordBlocks
jannikpflieger Jun 9, 2025
5ad710c
Merge branch 'collectCliffordBlocks' of github.com:jannikpflieger/mqt…
jannikpflieger Jun 9, 2025
c226bf1
Merge branch 'collectCliffordBlocks' of github.com:jannikpflieger/mqt…
jannikpflieger Jun 9, 2025
b16ac7b
🎨 pre-commit fixes
pre-commit-ci[bot] Jun 9, 2025
e723671
🩹 fix `isClifford` check
burgholzer Jun 10, 2025
de14645
✅ fix and adjust `isClifford` tests
burgholzer Jun 10, 2025
0f4e9fa
Merge branch 'main' into fork/jannikpflieger/collectCliffordBlocks
burgholzer Jun 10, 2025
3d16b8c
✏️ refine docstring
burgholzer Jun 10, 2025
32b333a
reverted changes
jannikpflieger Jun 17, 2025
fcdf42e
Merge branch 'collectCliffordBlocks' of github.com:jannikpflieger/mqt…
jannikpflieger Jun 17, 2025
d3d6c51
added additonal test, used different approach for collectin Blocks
jannikpflieger Jun 19, 2025
801a423
🎨 pre-commit fixes
pre-commit-ci[bot] Jun 19, 2025
54e5b4b
Better tests
jannikpflieger Jun 22, 2025
3fd00a8
Merge branch 'collectCliffordBlocks' of github.com:jannikpflieger/mqt…
jannikpflieger Jun 22, 2025
d715a9c
🎨 pre-commit fixes
pre-commit-ci[bot] Jun 22, 2025
eb463ac
slight test changes
jannikpflieger Jun 23, 2025
ff847a1
Merge branch 'collectCliffordBlocks' of github.com:jannikpflieger/mqt…
jannikpflieger Jun 23, 2025
15ae2b5
chaning loop behaviour
jannikpflieger Jun 30, 2025
5c7f8bc
🎨 pre-commit fixes
pre-commit-ci[bot] Jun 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion include/mqt-core/circuit_optimizer/CircuitOptimizer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,10 @@ class CircuitOptimizer {
* gates and fusing single-qubit gates.
* @param qc the quantum circuit
* @param maxBlockSize the maximum size of a block
* @param onlyCollectCliffords whether to only group Clifford operations
*/
static void collectBlocks(QuantumComputation& qc, std::size_t maxBlockSize);
static void collectBlocks(QuantumComputation& qc, std::size_t maxBlockSize,
bool onlyCollectCliffords = false);

/**
* @brief Elide permutations by propagating them through the circuit.
Expand Down
2 changes: 2 additions & 0 deletions include/mqt-core/ir/operations/CompoundOperation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ class CompoundOperation final : public Operation {

[[nodiscard]] bool isGlobal(size_t nQubits) const noexcept override;

[[nodiscard]] bool isClifford() const override;

void addControl(Control c) override;

void clearControls() override;
Expand Down
3 changes: 3 additions & 0 deletions include/mqt-core/ir/operations/OpType.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ std::string shortName(OpType opType);
}
}

/**
* @brief Checks if given OpType is a single qubit gate
*/
[[nodiscard]] constexpr bool isSingleQubitGate(const OpType type) {
switch (type) {
case I:
Expand Down
2 changes: 2 additions & 0 deletions include/mqt-core/ir/operations/Operation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ class Operation {

[[nodiscard]] virtual bool isControlled() const { return !controls.empty(); }

[[nodiscard]] virtual bool isClifford() const { return false; }

/**
* @brief Checks whether a gate is global.
* @details A StandardOperation is global if it acts on all qubits.
Expand Down
2 changes: 2 additions & 0 deletions include/mqt-core/ir/operations/StandardOperation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ class StandardOperation : public Operation {

[[nodiscard]] bool isGlobal(size_t nQubits) const override;

[[nodiscard]] bool isClifford() const override;

void addControl(const Control c) override {
if (actsOn(c.qubit)) {
throw std::runtime_error("Cannot add control on qubit " +
Expand Down
51 changes: 49 additions & 2 deletions src/circuit_optimizer/CircuitOptimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1418,7 +1418,8 @@
};

void CircuitOptimizer::collectBlocks(QuantumComputation& qc,
const std::size_t maxBlockSize) {
const std::size_t maxBlockSize,
bool onlyCollectCliffords) {
if (qc.size() <= 1) {
return;
}
Expand All @@ -1429,16 +1430,31 @@

// create an empty disjoint set union data structure
DSU dsu{};
DSU nonCliffordDSU{};

Check warning on line 1433 in src/circuit_optimizer/CircuitOptimizer.cpp

View workflow job for this annotation

GitHub Actions / 🇨‌ Lint / 🚨 Lint

src/circuit_optimizer/CircuitOptimizer.cpp:1433:3 [misc-const-correctness]

variable 'nonCliffordDSU' of type 'DSU' can be declared 'const'
for (auto opIt = qc.begin(); opIt != qc.end(); ++opIt) {
auto& op = *opIt;
bool canProcess = true;
bool makesTooBig = false;

// check if the operation can be processed
if (!op->isUnitary()) {
canProcess = false;
}

/*if (onlyCollectCliffords && !op->isClifford()) {
const auto usedQubits = op->getUsedQubits();
std::int64_t prev = -1;
for (const auto& q : usedQubits) {
if (prev != -1) {
dsu.unionBlock(static_cast<Qubit>(prev), q);
}
prev = q;
}
for (auto q : op->getUsedQubits()) {
dsu.finalizeBlock(q);
}
continue;
}*/
Comment on lines +1443 to +1456

Check notice

Code scanning / CodeQL

Commented-out code Note

This comment appears to contain commented-out code.

const auto usedQubits = op->getUsedQubits();

if (canProcess) {
Expand Down Expand Up @@ -1546,6 +1562,37 @@
}
prev = q;
}

if (onlyCollectCliffords && !op->isClifford()) {
// check if next operation which acts on other qubits within the same
// block fits in the block
if (opIt + 1 != qc.end() && (*std::next(opIt))->isUnitary()) {
const auto& nextOp = *(opIt + 1);
const auto nextUsedQubits = nextOp->getUsedQubits();
std::unordered_set<Qubit> nextBlockQubits;
for (const auto& q : nextUsedQubits) {
nextBlockQubits.emplace(dsu.findBlock(q));
}
std::size_t nextTotalSize = 0;
for (const auto& q : nextBlockQubits) {
nextTotalSize += dsu.bitBlocks[q].size();
}
if (nextTotalSize <= maxBlockSize) {
// if the next operation fits in the block, do not finalize the
// current block yet
for (const auto& q : usedQubits) {
dsu.finalizeBlock(q);
}
continue;
}
}

for (auto q : op->getUsedQubits()) {
dsu.finalizeBlock(q);
}
continue;
}

const auto block = dsu.findBlock(static_cast<Qubit>(prev));
const auto empty = dsu.blockEmpty(block);
if (empty) {
Expand Down
5 changes: 5 additions & 0 deletions src/ir/operations/CompoundOperation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ bool CompoundOperation::isGlobal(const size_t nQubits) const noexcept {
});
}

bool CompoundOperation::isClifford() const {
return std::all_of(ops.begin(), ops.end(),
[](const auto& op) { return op->isClifford(); });
}

bool CompoundOperation::isSymbolicOperation() const {
return std::any_of(ops.begin(), ops.end(),
[](const auto& op) { return op->isSymbolicOperation(); });
Expand Down
24 changes: 24 additions & 0 deletions src/ir/operations/StandardOperation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,30 @@ bool StandardOperation::isGlobal(const size_t nQubits) const {
/***
* Public Methods
***/
bool StandardOperation::isClifford() const {
switch (type) {
case I:
case Barrier:
return true;
case X:
case Y:
case Z:
return (getControls().size() <= 1);
case H:
case S:
case Sdg:
case SX:
case SXdg:
case DCX:
case SWAP:
case iSWAP:
case ECR:
return !isControlled();
default:
return false;
}
}

void StandardOperation::dumpOpenQASM(
std::ostream& of, const QubitIndexToRegisterMap& qubitMap,
[[maybe_unused]] const BitIndexToRegisterMap& bitMap, size_t indent,
Expand Down
110 changes: 110 additions & 0 deletions test/circuit_optimizer/test_collect_blocks.cpp
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed up a couple of things in the other parts of the PR, but these tests here are still broken. They do not assert the right behaviour/structure of the resulting circuit and, what makes it worse, the implementation currently does not produce correct results, as can be seen fro example from the nonCliffordOnAll test results

i:   0   1
1:  sx   |
2:   | sxd
3:   t   |
4:   |   t
5:   x   |
6:   |   x
o:   0   1


i:   0   1
1:--------
 :   | sxd
 :   |   x
 ---------
2:--------
 :  sx   |
 :   x   |
 ---------
3:   |   t
4:   t   |
o:   0   1

which clearly shows an incorrect grouping and ordering of the operations.

Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,116 @@ TEST(CollectBlocks, collectMultipleSingleQubitGates) {
EXPECT_TRUE(qc.back()->isCompoundOperation());
}

TEST(CollectBlocks, nonCliffordOnAll) {
QuantumComputation qc(2);
qc.sx(0);
qc.cx(0, 1);
qc.sxdg(1);
qc.t(0);
qc.t(1);
qc.x(0);
qc.x(1);

std::cout << qc << "\n";
qc::CircuitOptimizer::collectBlocks(qc, 2, true);
std::cout << qc << "\n";
EXPECT_EQ(qc.size(), 5);
EXPECT_TRUE(qc.front()->isCompoundOperation());
EXPECT_EQ(dynamic_cast<qc::CompoundOperation&>(*qc.front()).size(), 3);
}

TEST(CollectBlocks, nonCliffordSingleQubit) {
QuantumComputation qc(1);
qc.sdg(0);
qc.h(0);
qc.t(0);
qc.x(0);
std::cout << qc << "\n";
qc::CircuitOptimizer::collectBlocks(qc, 2, true);
std::cout << qc << "\n";
EXPECT_EQ(qc.size(), 3);
EXPECT_TRUE(qc.front()->isCompoundOperation());
}

TEST(CollectBlocks, collectTwoQubitCliffordGates) {
QuantumComputation qc(2);
qc.h(0);
qc.s(1);
qc.cx(0, 1);
qc.rx(0.1, 0);
qc.x(0);
qc.y(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());
EXPECT_TRUE(qc.back()->isStandardOperation());
EXPECT_EQ(dynamic_cast<qc::CompoundOperation&>(*qc.front()).size(), 3);
}

TEST(CollectBlocks, TwoQubitnonClifford) {
QuantumComputation qc(2);
qc.h(0);
qc.s(1);
qc.rxx(0.1, 0, 1);
qc.i(0);
qc.y(1);
std::cout << qc << "\n";
qc::CircuitOptimizer::collectBlocks(qc, 2, true);
std::cout << qc << "\n";
EXPECT_EQ(qc.size(), 3);
EXPECT_TRUE(qc.front()->isCompoundOperation());
}

TEST(CollectBlocks, mergeBlocksnonClifford) {
QuantumComputation qc(3);
qc.h(0);
qc.cx(0, 1);
qc.cx(1, 2);
qc.t(0);
qc.t(1);
qc.cx(0, 1);
qc.cx(1, 2);
std::cout << qc << "\n";
qc::CircuitOptimizer::collectBlocks(qc, 3, true);
std::cout << qc << "\n";
EXPECT_EQ(qc.size(), 4);
EXPECT_TRUE(qc.front()->isCompoundOperation());
EXPECT_TRUE(qc.back()->isCompoundOperation());
}

TEST(CollectBlocks, nonCliffordBeginning) {
QuantumComputation qc(2);
qc.t(0);
qc.t(1);
qc.ecr(0, 1);
qc.x(0);
std::cout << qc << "\n";
qc::CircuitOptimizer::collectBlocks(qc, 2, true);
std::cout << qc << "\n";
EXPECT_EQ(qc.size(), 3);
EXPECT_TRUE(qc.front()->isStandardOperation());
EXPECT_TRUE(qc.back()->isCompoundOperation());
}

TEST(CollectBlocks, threeQubitnonClifford) {
QuantumComputation qc(3);
qc.h(0);
qc.h(1);
qc.h(2);
qc.mcx({0, 1}, 2);
qc.mcz({0, 2}, 1);
qc.dcx(0, 1);
qc.dcx(1, 2);
std::cout << qc << "\n";
qc::CircuitOptimizer::collectBlocks(qc, 3, true);
std::cout << qc << "\n";
EXPECT_EQ(qc.size(), 4);
EXPECT_TRUE(qc.front()->isCompoundOperation());
EXPECT_TRUE(qc.back()->isCompoundOperation());
}

TEST(CollectBlocks, mergeBlocks) {
QuantumComputation qc(2);
qc.h(0);
Expand Down
59 changes: 59 additions & 0 deletions test/ir/test_operation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "ir/Register.hpp"
#include "ir/operations/AodOperation.hpp"
#include "ir/operations/CompoundOperation.hpp"
#include "ir/operations/Control.hpp"
#include "ir/operations/Expression.hpp"
#include "ir/operations/NonUnitaryOperation.hpp"
#include "ir/operations/OpType.hpp"
Expand Down Expand Up @@ -122,6 +123,14 @@ TEST(CompoundOperation, GetNqubits) {
EXPECT_EQ(op.getNqubits(), 3);
}

TEST(CompoundOperation, IsClifford) {
qc::CompoundOperation op1;
op1.emplace_back<qc::StandardOperation>(0, qc::OpType::H);
op1.emplace_back<qc::StandardOperation>(1, qc::OpType::RX,
std::vector{qc::PI_2});
EXPECT_FALSE(op1.isClifford());
}

TEST(OpType, General) {
EXPECT_EQ(qc::toString(qc::RZ), "rz");
std::stringstream ss;
Expand Down Expand Up @@ -157,6 +166,56 @@ TEST(Operation, IsIndividualGate) {
EXPECT_FALSE(op3.isSingleQubitGate());
}

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 ccx({0, 1}, 2, qc::X);
EXPECT_FALSE(ccx.isClifford());

const qc::StandardOperation y(0, qc::Y);
EXPECT_TRUE(y.isClifford());
const qc::StandardOperation cy(0, 1, qc::Y);
EXPECT_TRUE(cy.isClifford());
const qc::StandardOperation ccy({0, 1}, 2, qc::Y);
EXPECT_FALSE(ccy.isClifford());

const qc::StandardOperation z(0, qc::Z);
EXPECT_TRUE(z.isClifford());
const qc::StandardOperation cz(0, 1, qc::Z);
EXPECT_TRUE(cz.isClifford());
const qc::StandardOperation ccz({0, 1}, 2, qc::Z);
EXPECT_FALSE(ccz.isClifford());

const qc::StandardOperation t(0, qc::T);
EXPECT_FALSE(t.isClifford());
const qc::StandardOperation tdg(0, qc::Tdg);
EXPECT_FALSE(tdg.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());
}

TEST(Operation, IsDiagonalGate) {
const qc::StandardOperation op1(0, qc::X);
EXPECT_FALSE(op1.isDiagonalGate());
Expand Down
Loading