Skip to content

Zero-ancilla partial equivalence checking #532

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

Merged
Merged
Show file tree
Hide file tree
Changes from 3 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
82 changes: 54 additions & 28 deletions include/dd/Package.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1903,10 +1903,16 @@ template <class Config> class Package {
const auto eliminate = std::vector<bool>(nqubits, true);
return trace(a, eliminate).w;
}
bool isCloseToIdentity(const mEdge& m, const dd::fp tol = 1e-10) {

// if checkCloseToOne is false, it only checks if it's close to a diagonal
// matrix
bool isCloseToIdentity(const mEdge& m, const dd::fp tol = 1e-10,
const std::vector<bool>& garbage = {},
const bool checkCloseToOne = true) {
std::unordered_set<decltype(m.p)> visited{};
visited.reserve(mUniqueTable.getNumActiveEntries());
return isCloseToIdentityRecursive(m, visited, tol);
return isCloseToIdentityRecursive(m, visited, tol, garbage,
checkCloseToOne);
}

private:
Expand Down Expand Up @@ -1952,62 +1958,82 @@ template <class Config> class Package {

bool isCloseToIdentityRecursive(const mEdge& m,
std::unordered_set<decltype(m.p)>& visited,
const dd::fp tol) {
const dd::fp tol,
const std::vector<bool>& garbage,
const bool checkCloseToOne) {
// immediately return if this node has already been visited
if (visited.find(m.p) != visited.end()) {
return true;
}

// immediately return of this node is identical to the identity
// immediately return if this node is identical to the identity
if (m.isTerminal() || m.p->isIdentity()) {
return true;
}

const auto n = m.p->v;

if (garbage.size() > n && garbage[n]) {
return isCloseToIdentityRecursive(m.p->e[0U], visited, tol, garbage,
checkCloseToOne) &&
isCloseToIdentityRecursive(m.p->e[1U], visited, tol, garbage,
checkCloseToOne) &&
isCloseToIdentityRecursive(m.p->e[2U], visited, tol, garbage,
checkCloseToOne) &&
isCloseToIdentityRecursive(m.p->e[3U], visited, tol, garbage,
checkCloseToOne);
;
}

// check whether any of the middle successors is non-zero, i.e., m = [ x 0 0
// y ]
const auto mag1 = dd::ComplexNumbers::mag2(m.p->e[1U].w);
const auto mag2 = dd::ComplexNumbers::mag2(m.p->e[2U].w);
if (mag1 > tol || mag2 > tol) {
visited.insert(m.p);
return false;
}

// check whether m = [ ~1 0 0 y ]
const auto mag0 = dd::ComplexNumbers::mag2(m.p->e[0U].w);
if (std::abs(mag0 - 1.0) > tol) {
visited.insert(m.p);
return false;
}
const auto arg0 = dd::ComplexNumbers::arg(m.p->e[0U].w);
if (std::abs(arg0) > tol) {
visited.insert(m.p);
visited.insert(m.p); // is that not superfluous?
return false;
}

// check whether m = [ x 0 0 ~1 ] or m = [ x 0 0 ~0 ] (the last case is true
// for an ancillary qubit)
const auto mag3 = dd::ComplexNumbers::mag2(m.p->e[3U].w);
if (mag3 > tol) {
if (std::abs(mag3 - 1.0) > tol) {
if (checkCloseToOne) {
// check whether m = [ ~1 0 0 y ]
const auto mag0 = dd::ComplexNumbers::mag2(m.p->e[0U].w);
if (std::abs(mag0 - 1.0) > tol) {
visited.insert(m.p);
return false;
}
const auto arg3 = dd::ComplexNumbers::arg(m.p->e[3U].w);
if (std::abs(arg3) > tol) {
const auto arg0 = dd::ComplexNumbers::arg(m.p->e[0U].w);
if (std::abs(arg0) > tol) {
visited.insert(m.p);
return false;
}
}

// check whether m = [ x 0 0 ~1 ] or m = [ x 0 0 ~0 ] (the last case is
// true for an ancillary qubit)
const auto mag3 = dd::ComplexNumbers::mag2(m.p->e[3U].w);
if (mag3 > tol) {
if (std::abs(mag3 - 1.0) > tol) {
visited.insert(m.p);
return false;
}
const auto arg3 = dd::ComplexNumbers::arg(m.p->e[3U].w);
if (std::abs(arg3) > tol) {
visited.insert(m.p);
return false;
}
}
}
// m either has the form [ ~1 0 0 ~1 ] or [ ~1 0 0 ~0 ]
const auto ident0 = isCloseToIdentityRecursive(m.p->e[0U], visited, tol);
const auto ident0 = isCloseToIdentityRecursive(m.p->e[0U], visited, tol,
garbage, checkCloseToOne);

if (!ident0) {
visited.insert(m.p);
return false;
}

// m either has the form [ I 0 0 ~1 ] or [ I 0 0 ~0 ]
const auto ident3 = isCloseToIdentityRecursive(m.p->e[3U], visited, tol);
const auto ident3 = isCloseToIdentityRecursive(m.p->e[3U], visited, tol,
garbage, checkCloseToOne);

visited.insert(m.p);
return ident3;
}
Expand Down
63 changes: 63 additions & 0 deletions test/dd/test_package.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,69 @@ TEST(DDPackageTest, CloseToIdentity) {
EXPECT_FALSE(dd->isCloseToIdentity(notClose3));
}

TEST(DDPackageTest, CloseToIdentityWithGarbage) {
const dd::fp tol = 1.0E-10;
const auto nqubits = 3U;
auto dd = std::make_unique<dd::Package<>>(nqubits);
auto controlledSwapGate = dd->makeSWAPDD(nqubits, qc::Controls{1}, 0, 2);
auto hGate = dd->makeGateDD(dd::H_MAT, nqubits, 0);
auto zGate = dd->makeGateDD(dd::Z_MAT, nqubits, 2);
auto xGate = dd->makeGateDD(dd::X_MAT, nqubits, 1);
auto controlledHGate = dd->makeGateDD(dd::H_MAT, nqubits, qc::Controls{1}, 0);

auto c1 = dd->multiply(
controlledSwapGate,
dd->multiply(hGate, dd->multiply(zGate, controlledSwapGate)));
auto c2 = dd->multiply(controlledHGate, xGate);

auto c1MultipliedWithC2 = dd->multiply(c1, dd->conjugateTranspose(c2));

EXPECT_TRUE(dd->isCloseToIdentity(c1MultipliedWithC2, tol,
{false, true, true}, false));
EXPECT_FALSE(dd->isCloseToIdentity(c1MultipliedWithC2, tol,
{false, false, true}, false));

auto hGate2 = dd->makeGateDD(dd::H_MAT, nqubits, 2);
auto zGate2 = dd->makeGateDD(dd::Z_MAT, nqubits, 0);
auto controlledHGate2 =
dd->makeGateDD(dd::H_MAT, nqubits, qc::Controls{1}, 2);

// different order of qubits
auto c3 = dd->multiply(
controlledSwapGate,
dd->multiply(hGate2, dd->multiply(zGate2, controlledSwapGate)));
auto c4 = dd->multiply(controlledHGate2, xGate);

auto c3MultipliedWithC4 = dd->multiply(c3, dd->conjugateTranspose(c4));

EXPECT_FALSE(dd->isCloseToIdentity(c3MultipliedWithC4, tol,
{false, true, true}, false));
EXPECT_FALSE(dd->isCloseToIdentity(c3MultipliedWithC4, tol,
{true, false, true}, false));
EXPECT_TRUE(dd->isCloseToIdentity(c3MultipliedWithC4, tol,
{true, true, false}, false));
// yet another different order of qubits
auto controlledSwapGate3 = dd->makeSWAPDD(nqubits, qc::Controls{0}, 1, 2);
auto hGate3 = dd->makeGateDD(dd::H_MAT, nqubits, 1);
auto xGate3 = dd->makeGateDD(dd::X_MAT, nqubits, 0);
auto controlledHGate3 =
dd->makeGateDD(dd::H_MAT, nqubits, qc::Controls{0}, 1);

auto c5 = dd->multiply(
controlledSwapGate3,
dd->multiply(hGate3, dd->multiply(zGate, controlledSwapGate3)));
auto c6 = dd->multiply(controlledHGate3, xGate3);

auto c5MultipliedWithC6 = dd->multiply(c5, dd->conjugateTranspose(c6));

EXPECT_FALSE(dd->isCloseToIdentity(c5MultipliedWithC6, tol,
{false, true, true}, false));
EXPECT_FALSE(dd->isCloseToIdentity(c5MultipliedWithC6, tol,
{true, true, false}, false));
EXPECT_TRUE(dd->isCloseToIdentity(c5MultipliedWithC6, tol,
{true, false, true}, false));
}

struct DensityMatrixSimulatorDDPackageConfigTesting
: public dd::DDPackageConfig {
static constexpr std::size_t UT_DM_NBUCKET = 65536U;
Expand Down