Skip to content

Commit

Permalink
🐛 Fix dynamic circuit detection (#687)
Browse files Browse the repository at this point in the history
## Description

The previous implementation of the `isDynamicCircuit` check would
falsely report a circuit to not be dynamic if the circuit contains
multiple measurements on one qubit.
This PR fixes the underlying error and refactors the logic of the
respective method to handle more cases.

This was discovered while working on
cda-tum/mqt-qcec#439

## Checklist:

<!---
This checklist serves as a reminder of a couple of things that ensure
your pull request will be merged swiftly.
-->

- [x] The pull request only contains commits that are related to it.
- [x] I have added appropriate tests and documentation.
- [x] I have made sure that all CI jobs on GitHub pass.
- [x] The pull request introduces no new warnings and follows the
project's style guidelines.

---------

Signed-off-by: burgholzer <burgholzer@me.com>
  • Loading branch information
burgholzer authored Sep 9, 2024
1 parent 738d181 commit fd64943
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 84 deletions.
7 changes: 0 additions & 7 deletions include/mqt-core/circuit_optimizer/CircuitOptimizer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,13 @@
#include "Definitions.hpp"
#include "ir/QuantumComputation.hpp"
#include "ir/operations/OpType.hpp"
#include "ir/operations/Operation.hpp"

#include <cstddef>
#include <memory>
#include <unordered_set>

namespace qc {

class CircuitOptimizer {
protected:
static void addToDag(DAG& dag, std::unique_ptr<Operation>* op);
static void addNonStandardOperationToDag(DAG& dag,
std::unique_ptr<Operation>* op);

public:
CircuitOptimizer() = default;

Expand Down
127 changes: 50 additions & 77 deletions src/circuit_optimizer/CircuitOptimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@
#include <vector>

namespace qc {
void addToDag(DAG& dag, std::unique_ptr<Operation>* op) {
const auto usedQubits = (*op)->getUsedQubits();
for (const auto q : usedQubits) {
dag.at(q).push_back(op);
}
}

void CircuitOptimizer::removeIdentities(QuantumComputation& qc) {
// delete the identities from circuit
CircuitOptimizer::removeOperation(qc, {I}, 0);
Expand Down Expand Up @@ -167,13 +174,6 @@ DAG CircuitOptimizer::constructDAG(QuantumComputation& qc) {
return dag;
}

void CircuitOptimizer::addToDag(DAG& dag, std::unique_ptr<Operation>* op) {
const auto usedQubits = (*op)->getUsedQubits();
for (const auto q : usedQubits) {
dag.at(q).push_back(op);
}
}

void CircuitOptimizer::singleQubitGateFusion(QuantumComputation& qc) {
static const std::map<qc::OpType, qc::OpType> INVERSE_MAP = {
{qc::I, qc::I}, {qc::X, qc::X}, {qc::Y, qc::Y},
Expand Down Expand Up @@ -928,90 +928,63 @@ void CircuitOptimizer::deferMeasurements(QuantumComputation& qc) {
qc.initializeIOMapping();
}

bool CircuitOptimizer::isDynamicCircuit(QuantumComputation& qc) {
Qubit highestPhysicalQubit = 0;
for (const auto& q : qc.initialLayout) {
highestPhysicalQubit = std::max(q.first, highestPhysicalQubit);
bool isDynamicCircuit(std::unique_ptr<Operation>* op,
std::vector<bool>& measured, DAG& dag) {
assert(op != nullptr);
auto& it = *op;
// whenever a classic-controlled or a reset operation are encountered
// the circuit has to be dynamic.
if (it->getType() == Reset || it->isClassicControlledOperation()) {
return true;
}

auto dag = DAG(highestPhysicalQubit + 1);

bool hasMeasurements = false;

for (auto& it : qc) {
if (!it->isStandardOperation()) {
if (it->isNonUnitaryOperation()) {
// whenever a reset operation is encountered the circuit has to be
// dynamic
if (it->getType() == Reset) {
return true;
}

// record whether the circuit contains measurements
if (it->getType() == Measure) {
hasMeasurements = true;
}

for (const auto& b : it->getTargets()) {
dag.at(b).push_back(&it);
}
} else if (it->isClassicControlledOperation()) {
// whenever a classic-controlled operation is encountered the circuit
// has to be dynamic
if (it->isStandardOperation()) {
// Whenever a qubit has already been measured, the circuit is dynamic
const auto& usedQubits = it->getUsedQubits();
for (const auto& q : usedQubits) {
if (measured[q]) {
return true;
} else if (it->isCompoundOperation()) {
auto* compOp = dynamic_cast<CompoundOperation*>(it.get());
for (auto& op : *compOp) {
if (op->getType() == Reset || op->isClassicControlledOperation()) {
return true;
}

if (op->getType() == Measure) {
hasMeasurements = true;
}

if (op->isNonUnitaryOperation()) {
for (const auto& b : op->getTargets()) {
dag.at(b).push_back(&op);
}
} else {
addToDag(dag, &op);
}
}
}
} else {
addToDag(dag, &it);
}
addToDag(dag, op);
return false;
}

if (!hasMeasurements) {
if (it->isNonUnitaryOperation()) {
assert(it->getType() == qc::Measure);
for (const auto& b : it->getTargets()) {
dag.at(b).push_back(op);
measured[b] = true;
}
return false;
}

for (const auto& qubitDAG : dag) {
bool operation = false;
bool measurement = false;
for (auto it = qubitDAG.rbegin(); it != qubitDAG.rend(); ++it) {
auto* op = *it;
// once a measurement is encountered the iteration for this qubit can stop
if (op->get()->getType() == qc::Measure) {
measurement = true;
break;
}

if (op->get()->isStandardOperation() ||
op->get()->isClassicControlledOperation() ||
op->get()->isCompoundOperation() || op->get()->getType() == Reset) {
operation = true;
}
}
// there was a measurement and then a non-trivial operation, so the circuit
// is dynamic
if (measurement && operation) {
assert(it->isCompoundOperation());
auto* compOp = dynamic_cast<CompoundOperation*>(it.get());
for (auto& g : *compOp) {
if (isDynamicCircuit(&g, measured, dag)) {
return true;
}
}
return false;
}

bool CircuitOptimizer::isDynamicCircuit(QuantumComputation& qc) {
Qubit highestPhysicalQubit = 0;
for (const auto& q : qc.initialLayout) {
highestPhysicalQubit = std::max(q.first, highestPhysicalQubit);
}

auto dag = DAG(highestPhysicalQubit + 1);

// marks whether a qubit in the DAG has been measured
std::vector<bool> measured(highestPhysicalQubit + 1, false);

for (auto& it : qc) {
if (::qc::isDynamicCircuit(&it, measured, dag)) {
return true;
}
}
return false;
}

Expand Down
11 changes: 11 additions & 0 deletions test/circuit_optimizer/test_defer_measurements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -421,4 +421,15 @@ TEST(DeferMeasurements, preserveOutputPermutationWithoutMeasurements) {
EXPECT_EQ(qc.outputPermutation.size(), 1);
EXPECT_EQ(qc.outputPermutation.at(0), 0);
}

TEST(DeferMeasurements, isDynamicOnRepeatedMeasurements) {
QuantumComputation qc(1, 2);
qc.h(0);
qc.measure(0, 0);
qc.h(0);
qc.measure(0, 1);

EXPECT_TRUE(CircuitOptimizer::isDynamicCircuit(qc));
}

} // namespace qc

0 comments on commit fd64943

Please sign in to comment.