Skip to content
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

⚡ Eliminate garbage reduction in alternating checker #356

Merged
merged 7 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ filterwarnings = [
'ignore:.*qiskit.utils.algorithm_globals.QiskitAlgorithmGlobals*:DeprecationWarning:qiskit',
'ignore:.*Building a flow controller with keyword arguments is going to be deprecated*:PendingDeprecationWarning:qiskit',
'ignore:.*encountered in det.*:RuntimeWarning:numpy.linalg:',
'ignore:.*datetime\.datetime\.utcfromtimestamp.*:DeprecationWarning:',
]

[tool.coverage]
Expand Down
46 changes: 18 additions & 28 deletions src/checker/dd/DDAlternatingChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,39 +93,29 @@
if (isDone()) {
return;
}

// sum up the contributions of garbage qubits
taskManager1.reduceGarbage(functionality);
if (isDone()) {
return;
}
taskManager2.reduceGarbage(functionality);
if (isDone()) {
return;
}
}

EquivalenceCriterion DDAlternatingChecker::checkEquivalence() {
// create the full identity matrix
auto goalMatrix = dd->makeIdent(nqubits);
dd->incRef(goalMatrix);

// account for any garbage
taskManager1.reduceGarbage(goalMatrix);
taskManager2.reduceGarbage(goalMatrix);

taskManager1.reduceAncillae(goalMatrix);
taskManager2.reduceAncillae(goalMatrix);
std::vector<bool> garbage(nqubits);
for (qc::Qubit q = 0U; q < nqubits; ++q) {
garbage[static_cast<std::size_t>(q)] =
qc1.logicalQubitIsGarbage(q) && qc2.logicalQubitIsGarbage(q);
}

// the resulting goal matrix is
// [1 0] if the qubit is no ancillary
// [0 1]
//
// [1 0] (= |0><0>|) for an ancillary that is present in either circuit
// [0 0]
// if partial equivalence is being checked instead of total equivalence, it
// suffices to change the last parameter of isCloseToIdentity to `false`
const bool isClose = dd->isCloseToIdentity(
functionality, configuration.functionality.traceThreshold, garbage, true);

// compare the obtained functionality to the goal matrix
return equals(functionality, goalMatrix);
if (isClose) {
// whenever the top edge weight is not one, both decision diagrams are only
// equivalent up to a global phase
if (!functionality.w.approximatelyEquals(dd::Complex::one())) {
return EquivalenceCriterion::EquivalentUpToGlobalPhase;
}
return EquivalenceCriterion::Equivalent;
}
return EquivalenceCriterion::NotEquivalent;
}

[[nodiscard]] bool DDAlternatingChecker::gatesAreIdentical() const {
Expand Down Expand Up @@ -156,8 +146,8 @@
continue;
}

const auto isIdle1 = found1 && qc1.isIdleQubit(*physical1);

Check warning on line 149 in src/checker/dd/DDAlternatingChecker.cpp

View workflow job for this annotation

GitHub Actions / 🇨‌ Lint / 🚨 Lint

src/checker/dd/DDAlternatingChecker.cpp:149:55 [bugprone-unchecked-optional-access]

unchecked access to optional value
const auto isIdle2 = found2 && qc2.isIdleQubit(*physical2);

Check warning on line 150 in src/checker/dd/DDAlternatingChecker.cpp

View workflow job for this annotation

GitHub Actions / 🇨‌ Lint / 🚨 Lint

src/checker/dd/DDAlternatingChecker.cpp:150:55 [bugprone-unchecked-optional-access]

unchecked access to optional value

// if an ancillary qubit is acted on in both circuits,
// the alternating checker cannot be used.
Expand Down
3 changes: 2 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ package_add_test(
test_gate_cost_application_scheme.cpp
test_equality.cpp
test_zx.cpp
test_symbolic.cpp)
test_symbolic.cpp
test_partial_equivalence.cpp)
140 changes: 140 additions & 0 deletions test/test_partial_equivalence.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
//
// This file is part of the MQT QCEC library released under the MIT license.
// See README.md or go to https://github.com/cda-tum/qcec for more information.
//

#include "EquivalenceCheckingManager.hpp"

#include "gtest/gtest.h"

class PartialEqualityTest : public testing::Test {
void SetUp() override {
qc1 = qc::QuantumComputation(nqubits);
qc2 = qc::QuantumComputation(nqubits);
config.optimizations.fuseSingleQubitGates = false;
config.optimizations.reorderOperations = false;
config.optimizations.reconstructSWAPs = false;
config.optimizations.fixOutputPermutationMismatch = true;

config.execution.runSimulationChecker = false;
config.execution.runAlternatingChecker = false;
config.execution.runConstructionChecker = false;
config.execution.runZXChecker = false;
}

protected:
std::size_t nqubits = 3U;
qc::QuantumComputation qc1;
qc::QuantumComputation qc2;
ec::Configuration config{};
};

TEST_F(PartialEqualityTest, AlternatingCheckerGarbage) {
// these circuits have the same gates acting on the measured qubit
// and random gates acting on the two garbage qubits
qc1.cswap(1, 0, 2);
qc1.cx(2, 0);
qc1.h(0);
qc1.tdg(1);
qc1.s(1);
qc1.z(2);

qc2.cswap(1, 0, 2);
qc2.cx(2, 0);
qc2.h(0);
qc2.h(2);
qc2.rz(dd::PI_4, 1);
qc2.ry(0.1, 1);
qc2.cx(1, 2);

qc1.setLogicalQubitGarbage(2);
qc1.setLogicalQubitGarbage(1);
qc2.setLogicalQubitGarbage(2);
qc2.setLogicalQubitGarbage(1);

config.execution.runAlternatingChecker = true;
ec::EquivalenceCheckingManager ecm(qc1, qc2, config);
ecm.setApplicationScheme(ec::ApplicationSchemeType::Proportional);
ecm.run();
EXPECT_EQ(ecm.equivalence(),
ec::EquivalenceCriterion::EquivalentUpToGlobalPhase);
}

TEST_F(PartialEqualityTest, AlternatingCheckerGarbage2) {
// measured qubit: 1
qc1.setLogicalQubitGarbage(2);
qc1.setLogicalQubitGarbage(0);

qc1.h(1);
qc1.tdg(0);
qc1.s(0);
qc1.z(2);

// measured qubit: 1
qc2.setLogicalQubitGarbage(0);
qc2.setLogicalQubitGarbage(2);

qc2.h(1);
qc2.h(0);
qc2.rz(dd::PI_4, 2);
qc2.ry(0.1, 2);
qc2.cx(2, 0);

config.execution.runAlternatingChecker = true;
ec::EquivalenceCheckingManager ecm(qc1, qc2, config);
ecm.setApplicationScheme(ec::ApplicationSchemeType::Proportional);
ecm.run();
EXPECT_EQ(ecm.equivalence(),
ec::EquivalenceCriterion::EquivalentUpToGlobalPhase);
}

TEST_F(PartialEqualityTest, AlternatingCheckerGarbageAndAncillary) {
qc1.setLogicalQubitGarbage(2);
qc1.setLogicalQubitGarbage(1);
qc1.setLogicalQubitAncillary(2);

qc1.h(0);
qc1.tdg(1);
qc1.s(1);
// ancillary qubits are initialized to zero, therefore this gate doesn't
// change the functionality of the circuit
qc1.cx(2, 0);

qc::QuantumComputation qc3(nqubits - 1);
qc3.setLogicalQubitGarbage(1);

qc3.h(0);
qc3.rz(dd::PI_4, 1);
qc3.ry(0.1, 1);

config.execution.runAlternatingChecker = true;
ec::EquivalenceCheckingManager ecm(qc1, qc3, config);
ecm.setApplicationScheme(ec::ApplicationSchemeType::Proportional);
ecm.run();
EXPECT_EQ(ecm.equivalence(),
ec::EquivalenceCriterion::EquivalentUpToGlobalPhase);
}

TEST_F(PartialEqualityTest, AlternatingCheckerGarbageNotEquivalent) {
// example from the paper https://arxiv.org/abs/2208.07564
// these two circuits are only partially equivalent,
// therefore the equivalence checker returns NotEquivalent
qc1.cswap(1, 0, 2);
qc1.h(0);
qc1.z(2);
qc1.cswap(1, 0, 2);

qc2.x(1);
qc2.ch(1, 0);

qc1.setLogicalQubitGarbage(2);
qc1.setLogicalQubitGarbage(1);
qc2.setLogicalQubitGarbage(2);
qc2.setLogicalQubitGarbage(1);

config.execution.runAlternatingChecker = true;
ec::EquivalenceCheckingManager ecm(qc1, qc2, config);
ecm.setApplicationScheme(ec::ApplicationSchemeType::Proportional);
ecm.run();
EXPECT_EQ(ecm.equivalence(), ec::EquivalenceCriterion::NotEquivalent);
}
Loading