Skip to content

Commit

Permalink
♻️ Refactor handling of Barrier operations
Browse files Browse the repository at this point in the history
This commit refactors how the quantum barriers are handled across the operations, moving it from nonunitary operations to the standard operations. The changes include updates in the display of the barriers and how they are parsed in the QASMParser, along with modifications in the operation files and test files. This change improves code structure allowing a more consistent handling of operations.

Signed-off-by: Lukas Burgholzer <[email protected]>
  • Loading branch information
burgholzer committed Aug 9, 2023
1 parent 5982ba7 commit ea3842e
Show file tree
Hide file tree
Showing 12 changed files with 76 additions and 103 deletions.
5 changes: 3 additions & 2 deletions include/CircuitOptimizer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
#include <memory>

namespace qc {
static constexpr std::array<qc::OpType, 8> DIAGONAL_GATES = {
qc::I, qc::Z, qc::S, qc::Sdag, qc::T, qc::Tdag, qc::Phase, qc::RZ};
static constexpr std::array<qc::OpType, 10> DIAGONAL_GATES = {
qc::Barrier, qc::I, qc::Z, qc::S, qc::Sdag,
qc::T, qc::Tdag, qc::Phase, qc::RZ, qc::RZZ};

class CircuitOptimizer {
protected:
Expand Down
5 changes: 2 additions & 3 deletions include/QuantumComputation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -915,12 +915,11 @@ class QuantumComputation {

void barrier(const Qubit target) {
checkQubitRange(target);
emplace_back<NonUnitaryOperation>(getNqubits(), std::vector<Qubit>{target},
qc::Barrier);
emplace_back<StandardOperation>(getNqubits(), target, qc::Barrier);
}
void barrier(const std::vector<Qubit>& targets) {
checkQubitRange(targets);
emplace_back<NonUnitaryOperation>(getNqubits(), targets, qc::Barrier);
emplace_back<StandardOperation>(getNqubits(), targets, qc::Barrier);
}

void classicControlled(const OpType op, const Qubit target,
Expand Down
18 changes: 6 additions & 12 deletions include/operations/NonUnitaryOperation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ class NonUnitaryOperation final : public Operation {
void printMeasurement(std::ostream& os, const std::vector<Qubit>& q,
const std::vector<Bit>& c,
const Permutation& permutation) const;
void printResetOrBarrier(std::ostream& os, const std::vector<Qubit>& q,
const Permutation& permutation) const;
void printReset(std::ostream& os, const std::vector<Qubit>& q,
const Permutation& permutation) const;

public:
// Measurement constructor
Expand Down Expand Up @@ -65,26 +65,20 @@ class NonUnitaryOperation final : public Operation {

[[nodiscard]] bool actsOn(Qubit i) const override;

void addDepthContribution(std::vector<std::size_t>& depths) const override;

[[nodiscard]] bool equals(const Operation& op, const Permutation& perm1,
const Permutation& perm2) const override;
[[nodiscard]] bool equals(const Operation& operation) const override {
return equals(operation, {}, {});
}

std::ostream& print(std::ostream& os) const override {
if (type == Measure) {
return printNonUnitary(os, qubits, classics);
}
return printNonUnitary(os, targets);
const auto& qubitArgs = getTargets();
return printNonUnitary(os, qubitArgs, classics);
}
std::ostream& print(std::ostream& os,
const Permutation& permutation) const override {
if (type == Measure) {
return printNonUnitary(os, qubits, classics, permutation);
}
return printNonUnitary(os, targets, {}, permutation);
const auto& qubitArgs = getTargets();
return printNonUnitary(os, qubitArgs, classics, permutation);
}

void dumpOpenQASM(std::ostream& of, const RegisterNames& qreg,
Expand Down
2 changes: 1 addition & 1 deletion include/operations/OpType.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ enum OpType : std::uint8_t {
// Standard Operations
GPhase,
I,
Barrier,
H,
X,
Y,
Expand Down Expand Up @@ -47,7 +48,6 @@ enum OpType : std::uint8_t {
// Non Unitary Operations
Measure,
Reset,
Barrier,
Teleportation,
// Classically-controlled Operation
ClassicControlled,
Expand Down
4 changes: 4 additions & 0 deletions include/operations/Operation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ class Operation {
}

[[nodiscard]] inline virtual bool actsOn(const Qubit i) const {
if (type == Barrier) {
return false;
}

for (const auto& t : targets) {
if (t == i) {
return true;
Expand Down
45 changes: 18 additions & 27 deletions src/CircuitOptimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ void CircuitOptimizer::removeIdentities(QuantumComputation& qc) {
// delete the identities from circuit
auto it = qc.ops.begin();
while (it != qc.ops.end()) {
if ((*it)->getType() == I || (*it)->getType() == Barrier) {
if ((*it)->getType() == I) {
it = qc.ops.erase(it);
} else if ((*it)->isCompoundOperation()) {
auto* compOp = dynamic_cast<qc::CompoundOperation*>((*it).get());
auto cit = compOp->cbegin();
while (cit != compOp->cend()) {
const auto* cop = cit->get();
if (cop->getType() == qc::I || cop->getType() == Barrier) {
if (cop->getType() == qc::I) {
cit = compOp->erase(cit);
} else {
++cit;
Expand Down Expand Up @@ -163,11 +163,6 @@ void CircuitOptimizer::addNonStandardOperationToDag(
}
}
} else if (gate->isNonUnitaryOperation()) {
// barriers are not added to a circuit DAG
if (gate->getType() == Barrier) {
return;
}

for (const auto& b : gate->getTargets()) {
dag.at(b).push_back(op);
}
Expand All @@ -187,10 +182,10 @@ void CircuitOptimizer::addNonStandardOperationToDag(

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},
{qc::Z, qc::Z}, {qc::H, qc::H}, {qc::S, qc::Sdag},
{qc::Sdag, qc::S}, {qc::T, qc::Tdag}, {qc::Tdag, qc::T},
{qc::SX, qc::SXdag}, {qc::SXdag, qc::SX}};
{qc::I, qc::I}, {qc::X, qc::X}, {qc::Y, qc::Y},
{qc::Z, qc::Z}, {qc::H, qc::H}, {qc::S, qc::Sdag},
{qc::Sdag, qc::S}, {qc::T, qc::Tdag}, {qc::Tdag, qc::T},
{qc::SX, qc::SXdag}, {qc::SXdag, qc::SX}, {qc::Barrier, qc::Barrier}};

Qubit highestPhysicalQubit = 0;
for (const auto& q : qc.initialLayout) {
Expand Down Expand Up @@ -387,9 +382,7 @@ void CircuitOptimizer::removeDiagonalGatesBeforeMeasureRecursive(
}
}
auto* op = (*it)->get();
if (op->getType() == Barrier) {
++it;
} else if (op->isStandardOperation()) {
if (op->isStandardOperation()) {
// try removing gate and upon success increase all corresponding iterators
auto onlyDiagonalGates =
removeDiagonalGate(dag, dagIterators, idx, it, op);
Expand Down Expand Up @@ -482,33 +475,34 @@ bool CircuitOptimizer::removeFinalMeasurement(DAG& dag,
qc::Operation* op) {
if (op->getNtargets() != 0) {
// need to check all targets
bool onlyMeasurments = true;
bool onlyMeasurements = true;
for (const auto& target : op->getTargets()) {
if (target == idx) {
continue;
}
if (dagIterators.at(target) == dag.at(target).rend()) {
onlyMeasurments = false;
onlyMeasurements = false;

Check warning on line 484 in src/CircuitOptimizer.cpp

View check run for this annotation

Codecov / codecov/patch

src/CircuitOptimizer.cpp#L484

Added line #L484 was not covered by tests
break;
}
// recursive call at target with this operation as goal
removeFinalMeasurementsRecursive(dag, dagIterators, target, (*it)->get());
// check if iteration of target qubit was successful
if (dagIterators.at(target) == dag.at(target).rend() ||
*dagIterators.at(target) != *it) {
onlyMeasurments = false;
onlyMeasurements = false;
break;
}
}
if (!onlyMeasurments) {
if (!onlyMeasurements) {
// end qubit
dagIterators.at(idx) = dag.at(idx).rend();
} else {
// set operation to identity so that it can be collected by the
// removeIdentities pass
op->setTargets(op->getTargets()); // work-around to properly set targets
op->setGate(qc::I);
}
return onlyMeasurments;
return onlyMeasurements;
}
return false;
}
Expand Down Expand Up @@ -537,9 +531,9 @@ void CircuitOptimizer::removeFinalMeasurementsRecursive(
}
auto* op = (*it)->get();
if (op->getType() == Measure) {
const bool onlyMeasurment =
const bool onlyMeasurement =
removeFinalMeasurement(dag, dagIterators, idx, it, op);
if (onlyMeasurment) {
if (onlyMeasurement) {
for (const auto& target : op->getTargets()) {
if (dagIterators.at(target) == dag.at(target).rend()) {
break;
Expand All @@ -554,6 +548,8 @@ void CircuitOptimizer::removeFinalMeasurementsRecursive(
}
++(dagIterators.at(target));
}
// Barriers at the end of the circuit can be removed
op->setGate(OpType::I);
} else if (op->isCompoundOperation() && op->isNonUnitaryOperation()) {
// iterate over all gates of compound operation and upon success increase
// all corresponding iterators
Expand Down Expand Up @@ -829,7 +825,7 @@ void CircuitOptimizer::deferMeasurements(QuantumComputation& qc) {
// iterate over all subsequent operations
while (opIt != qc.end()) {
const auto* operation = opIt->get();
if (operation->isUnitary() || operation->getType() == qc::Barrier) {
if (operation->isUnitary()) {
// if an operation does not act on the measured qubit, the insert
// location for potential operations has to be updated
if (!operation->actsOn(measurementQubit)) {
Expand Down Expand Up @@ -1097,11 +1093,6 @@ void CircuitOptimizer::reorderOperations(QuantumComputation& qc) {
"circuit contains classically controlled operations\n";
}

if (op->getType() == Barrier) {
++it;
continue;
}

// check whether the gate can be scheduled, i.e. whether all qubits it
// acts on are at this operation
bool executable = true;
Expand Down
16 changes: 8 additions & 8 deletions src/QuantumComputation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -573,12 +573,12 @@ std::ostream& QuantumComputation::print(std::ostream& os) const {
os << logical << "\t";
}
}
os << std::endl;
os << "\n";
size_t i = 0U;
for (const auto& op : ops) {
os << std::setw(width) << ++i << ": \t";
op->print(os, initialLayout);
os << std::endl;
os << "\n";
}
if (!ops.empty()) {
os << std::setw(width) << "o"
Expand All @@ -598,7 +598,7 @@ std::ostream& QuantumComputation::print(std::ostream& os) const {
os << it->second << "\t";
}
}
os << std::endl;
os << "\n";
return os;
}

Expand All @@ -610,11 +610,11 @@ void QuantumComputation::printBin(std::size_t n, std::stringstream& ss) {
}

std::ostream& QuantumComputation::printStatistics(std::ostream& os) const {
os << "QC Statistics:\n";
os << "\tn: " << static_cast<std::size_t>(nqubits) << std::endl;
os << "\tanc: " << static_cast<std::size_t>(nancillae) << std::endl;
os << "\tm: " << ops.size() << std::endl;
os << "--------------" << std::endl;
os << "QC Statistics:";
os << "\n\tn: " << static_cast<std::size_t>(nqubits);
os << "\n\tanc: " << static_cast<std::size_t>(nancillae);
os << "\n\tm: " << ops.size();
os << "\n--------------\n";
return os;
}

Expand Down
49 changes: 9 additions & 40 deletions src/operations/NonUnitaryOperation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ std::ostream& NonUnitaryOperation::printNonUnitary(
printMeasurement(os, q, c, permutation);
break;
case Reset:
case Barrier:
printResetOrBarrier(os, q, permutation);
printReset(os, q, permutation);
break;
default:
break;
Expand Down Expand Up @@ -87,16 +86,9 @@ void NonUnitaryOperation::dumpOpenQASM(std::ostream& of,
}

bool NonUnitaryOperation::actsOn(Qubit i) const {
if (type == Measure) {
return std::any_of(qubits.cbegin(), qubits.cend(),
[&i](const auto& q) { return q == i; });
}
if (type == Reset) {
return std::any_of(targets.cbegin(), targets.cend(),
[&i](const auto& t) { return t == i; });
}
// other non-unitary operations (e.g., barrier statements) may be ignored
return false;
const auto& qubitArgs = getTargets();
return std::any_of(qubitArgs.cbegin(), qubitArgs.cend(),
[&i](const auto& q) { return q == i; });
}

bool NonUnitaryOperation::equals(const Operation& op, const Permutation& perm1,
Expand Down Expand Up @@ -151,13 +143,6 @@ bool NonUnitaryOperation::equals(const Operation& op, const Permutation& perm1,
return false;
}

void NonUnitaryOperation::addDepthContribution(
std::vector<std::size_t>& depths) const {
if (type == Measure || type == Reset) {
Operation::addDepthContribution(depths);
}
}

void NonUnitaryOperation::printMeasurement(
std::ostream& os, const std::vector<Qubit>& q, const std::vector<Bit>& c,
const Permutation& permutation) const {
Expand Down Expand Up @@ -189,23 +174,15 @@ void NonUnitaryOperation::printMeasurement(
}
}

void NonUnitaryOperation::printResetOrBarrier(
std::ostream& os, const std::vector<Qubit>& q,
const Permutation& permutation) const {
void NonUnitaryOperation::printReset(std::ostream& os,
const std::vector<Qubit>& q,
const Permutation& permutation) const {
auto qubitIt = q.cbegin();
os << name << "\t";
if (permutation.empty()) {
for (std::size_t i = 0; i < nqubits; ++i) {
if (qubitIt != q.cend() && *qubitIt == i) {
if (type == Reset) {
os << "\033[31m"
<< "r";
} else {
assert(type == Barrier);
os << "\033[32m"
<< "b";
}
os << "\t\033[0m";
os << "\033[31mr\t\033[0m";
++qubitIt;
} else {
os << "|\t";
Expand All @@ -214,15 +191,7 @@ void NonUnitaryOperation::printResetOrBarrier(
} else {
for (const auto& [physical, logical] : permutation) {
if (qubitIt != q.cend() && *qubitIt == physical) {
if (type == Reset) {
os << "\033[31m"
<< "r";
} else {
assert(type == Barrier);
os << "\033[32m"
<< "b";
}
os << "\t\033[0m";
os << "\033[31mr\t\033[0m";
++qubitIt;
} else {
os << "|\t";
Expand Down
8 changes: 8 additions & 0 deletions src/operations/Operation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ std::ostream& Operation::print(std::ostream& os) const {
if (targetIt != targets.end() && *targetIt == i) {
if (type == ClassicControlled) {
os << "\033[1m\033[35m" << name[2] << name[3];
} else if (type == Barrier) {
os << "\033[1m\033[32mb";
} else {
os << "\033[1m\033[36m" << name[0] << name[1];
}
Expand Down Expand Up @@ -129,6 +131,8 @@ std::ostream& Operation::print(std::ostream& os,
if (targetIt != actualTargets.cend() && *targetIt == physical) {
if (type == ClassicControlled) {
os << "\033[1m\033[35m" << name[2] << name[3];
} else if (type == Barrier) {
os << "\033[1m\033[32mb";
} else {
os << "\033[1m\033[36m" << name[0] << name[1];
}
Expand Down Expand Up @@ -225,6 +229,10 @@ bool Operation::equals(const Operation& op, const Permutation& perm1,
}

void Operation::addDepthContribution(std::vector<std::size_t>& depths) const {
if (type == Barrier) {
return;
}

std::size_t maxDepth = 0;
for (const auto& target : getTargets()) {
maxDepth = std::max(maxDepth, depths[target]);
Expand Down
Loading

0 comments on commit ea3842e

Please sign in to comment.