Skip to content

Commit f8daa7f

Browse files
authored
Zero-ancilla partial equivalence checking (#532)
1 parent b043e28 commit f8daa7f

File tree

2 files changed

+140
-31
lines changed

2 files changed

+140
-31
lines changed

include/dd/Package.hpp

+61-31
Original file line numberDiff line numberDiff line change
@@ -1903,10 +1903,26 @@ template <class Config> class Package {
19031903
const auto eliminate = std::vector<bool>(nqubits, true);
19041904
return trace(a, eliminate).w;
19051905
}
1906-
bool isCloseToIdentity(const mEdge& m, const dd::fp tol = 1e-10) {
1906+
1907+
/**
1908+
Checks if a given matrix is close to the identity matrix, while ignoring
1909+
any potential garbage qubits and ignoring the diagonal weights if
1910+
`checkCloseToOne` is set to false.
1911+
@param m An mEdge that represents the DD of the matrix.
1912+
@param tol The accepted tolerance for the edge weights of the DD.
1913+
@param garbage A vector of boolean values that defines which qubits are
1914+
considered garbage qubits. If it's empty, then no qubit is considered to be
1915+
a garbage qubit.
1916+
@param checkCloseToOne If false, the function only checks if the matrix
1917+
is close to a diagonal matrix.
1918+
**/
1919+
bool isCloseToIdentity(const mEdge& m, const dd::fp tol = 1e-10,
1920+
const std::vector<bool>& garbage = {},
1921+
const bool checkCloseToOne = true) {
19071922
std::unordered_set<decltype(m.p)> visited{};
19081923
visited.reserve(mUniqueTable.getNumActiveEntries());
1909-
return isCloseToIdentityRecursive(m, visited, tol);
1924+
return isCloseToIdentityRecursive(m, visited, tol, garbage,
1925+
checkCloseToOne);
19101926
}
19111927

19121928
private:
@@ -1952,62 +1968,76 @@ template <class Config> class Package {
19521968

19531969
bool isCloseToIdentityRecursive(const mEdge& m,
19541970
std::unordered_set<decltype(m.p)>& visited,
1955-
const dd::fp tol) {
1971+
const dd::fp tol,
1972+
const std::vector<bool>& garbage,
1973+
const bool checkCloseToOne) {
19561974
// immediately return if this node has already been visited
19571975
if (visited.find(m.p) != visited.end()) {
19581976
return true;
19591977
}
19601978

1961-
// immediately return of this node is identical to the identity
1979+
// immediately return if this node is identical to the identity
19621980
if (m.isTerminal() || m.p->isIdentity()) {
19631981
return true;
19641982
}
19651983

1984+
const auto n = m.p->v;
1985+
1986+
if (garbage.size() > n && garbage[n]) {
1987+
return isCloseToIdentityRecursive(m.p->e[0U], visited, tol, garbage,
1988+
checkCloseToOne) &&
1989+
isCloseToIdentityRecursive(m.p->e[1U], visited, tol, garbage,
1990+
checkCloseToOne) &&
1991+
isCloseToIdentityRecursive(m.p->e[2U], visited, tol, garbage,
1992+
checkCloseToOne) &&
1993+
isCloseToIdentityRecursive(m.p->e[3U], visited, tol, garbage,
1994+
checkCloseToOne);
1995+
;
1996+
}
1997+
19661998
// check whether any of the middle successors is non-zero, i.e., m = [ x 0 0
19671999
// y ]
19682000
const auto mag1 = dd::ComplexNumbers::mag2(m.p->e[1U].w);
19692001
const auto mag2 = dd::ComplexNumbers::mag2(m.p->e[2U].w);
19702002
if (mag1 > tol || mag2 > tol) {
1971-
visited.insert(m.p);
1972-
return false;
1973-
}
1974-
1975-
// check whether m = [ ~1 0 0 y ]
1976-
const auto mag0 = dd::ComplexNumbers::mag2(m.p->e[0U].w);
1977-
if (std::abs(mag0 - 1.0) > tol) {
1978-
visited.insert(m.p);
1979-
return false;
1980-
}
1981-
const auto arg0 = dd::ComplexNumbers::arg(m.p->e[0U].w);
1982-
if (std::abs(arg0) > tol) {
1983-
visited.insert(m.p);
19842003
return false;
19852004
}
19862005

1987-
// check whether m = [ x 0 0 ~1 ] or m = [ x 0 0 ~0 ] (the last case is true
1988-
// for an ancillary qubit)
1989-
const auto mag3 = dd::ComplexNumbers::mag2(m.p->e[3U].w);
1990-
if (mag3 > tol) {
1991-
if (std::abs(mag3 - 1.0) > tol) {
1992-
visited.insert(m.p);
2006+
if (checkCloseToOne) {
2007+
// check whether m = [ ~1 0 0 y ]
2008+
const auto mag0 = dd::ComplexNumbers::mag2(m.p->e[0U].w);
2009+
if (std::abs(mag0 - 1.0) > tol) {
19932010
return false;
19942011
}
1995-
const auto arg3 = dd::ComplexNumbers::arg(m.p->e[3U].w);
1996-
if (std::abs(arg3) > tol) {
1997-
visited.insert(m.p);
2012+
const auto arg0 = dd::ComplexNumbers::arg(m.p->e[0U].w);
2013+
if (std::abs(arg0) > tol) {
19982014
return false;
19992015
}
2000-
}
20012016

2017+
// check whether m = [ x 0 0 ~1 ] or m = [ x 0 0 ~0 ] (the last case is
2018+
// true for an ancillary qubit)
2019+
const auto mag3 = dd::ComplexNumbers::mag2(m.p->e[3U].w);
2020+
if (mag3 > tol) {
2021+
if (std::abs(mag3 - 1.0) > tol) {
2022+
return false;
2023+
}
2024+
const auto arg3 = dd::ComplexNumbers::arg(m.p->e[3U].w);
2025+
if (std::abs(arg3) > tol) {
2026+
return false;
2027+
}
2028+
}
2029+
}
20022030
// m either has the form [ ~1 0 0 ~1 ] or [ ~1 0 0 ~0 ]
2003-
const auto ident0 = isCloseToIdentityRecursive(m.p->e[0U], visited, tol);
2031+
const auto ident0 = isCloseToIdentityRecursive(m.p->e[0U], visited, tol,
2032+
garbage, checkCloseToOne);
2033+
20042034
if (!ident0) {
2005-
visited.insert(m.p);
20062035
return false;
20072036
}
2008-
20092037
// m either has the form [ I 0 0 ~1 ] or [ I 0 0 ~0 ]
2010-
const auto ident3 = isCloseToIdentityRecursive(m.p->e[3U], visited, tol);
2038+
const auto ident3 = isCloseToIdentityRecursive(m.p->e[3U], visited, tol,
2039+
garbage, checkCloseToOne);
2040+
20112041
visited.insert(m.p);
20122042
return ident3;
20132043
}

test/dd/test_package.cpp

+79
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,85 @@ TEST(DDPackageTest, CloseToIdentity) {
11401140
EXPECT_FALSE(dd->isCloseToIdentity(notClose3));
11411141
}
11421142

1143+
TEST(DDPackageTest, CloseToIdentityWithGarbageAtTheBeginning) {
1144+
const dd::fp tol = 1.0E-10;
1145+
const auto nqubits = 3U;
1146+
auto dd = std::make_unique<dd::Package<>>(nqubits);
1147+
auto controlledSwapGate = dd->makeSWAPDD(nqubits, qc::Controls{1}, 0, 2);
1148+
auto hGate = dd->makeGateDD(dd::H_MAT, nqubits, 0);
1149+
auto zGate = dd->makeGateDD(dd::Z_MAT, nqubits, 2);
1150+
auto xGate = dd->makeGateDD(dd::X_MAT, nqubits, 1);
1151+
auto controlledHGate = dd->makeGateDD(dd::H_MAT, nqubits, qc::Controls{1}, 0);
1152+
1153+
auto c1 = dd->multiply(
1154+
controlledSwapGate,
1155+
dd->multiply(hGate, dd->multiply(zGate, controlledSwapGate)));
1156+
auto c2 = dd->multiply(controlledHGate, xGate);
1157+
1158+
auto c1MultipliedWithC2 = dd->multiply(c1, dd->conjugateTranspose(c2));
1159+
1160+
EXPECT_TRUE(dd->isCloseToIdentity(c1MultipliedWithC2, tol,
1161+
{false, true, true}, false));
1162+
EXPECT_FALSE(dd->isCloseToIdentity(c1MultipliedWithC2, tol,
1163+
{false, false, true}, false));
1164+
}
1165+
1166+
TEST(DDPackageTest, CloseToIdentityWithGarbageAtTheEnd) {
1167+
const dd::fp tol = 1.0E-10;
1168+
const auto nqubits = 3U;
1169+
auto dd = std::make_unique<dd::Package<>>(nqubits);
1170+
1171+
auto controlledSwapGate = dd->makeSWAPDD(nqubits, qc::Controls{1}, 0, 2);
1172+
auto xGate = dd->makeGateDD(dd::X_MAT, nqubits, 1);
1173+
1174+
auto hGate2 = dd->makeGateDD(dd::H_MAT, nqubits, 2);
1175+
auto zGate2 = dd->makeGateDD(dd::Z_MAT, nqubits, 0);
1176+
auto controlledHGate2 =
1177+
dd->makeGateDD(dd::H_MAT, nqubits, qc::Controls{1}, 2);
1178+
1179+
auto c3 = dd->multiply(
1180+
controlledSwapGate,
1181+
dd->multiply(hGate2, dd->multiply(zGate2, controlledSwapGate)));
1182+
auto c4 = dd->multiply(controlledHGate2, xGate);
1183+
1184+
auto c3MultipliedWithC4 = dd->multiply(c3, dd->conjugateTranspose(c4));
1185+
1186+
EXPECT_FALSE(dd->isCloseToIdentity(c3MultipliedWithC4, tol,
1187+
{false, true, true}, false));
1188+
EXPECT_FALSE(dd->isCloseToIdentity(c3MultipliedWithC4, tol,
1189+
{true, false, true}, false));
1190+
EXPECT_TRUE(dd->isCloseToIdentity(c3MultipliedWithC4, tol,
1191+
{true, true, false}, false));
1192+
}
1193+
1194+
TEST(DDPackageTest, CloseToIdentityWithGarbageInTheMiddle) {
1195+
const dd::fp tol = 1.0E-10;
1196+
const auto nqubits = 3U;
1197+
auto dd = std::make_unique<dd::Package<>>(nqubits);
1198+
1199+
auto zGate = dd->makeGateDD(dd::Z_MAT, nqubits, 2);
1200+
1201+
auto controlledSwapGate3 = dd->makeSWAPDD(nqubits, qc::Controls{0}, 1, 2);
1202+
auto hGate3 = dd->makeGateDD(dd::H_MAT, nqubits, 1);
1203+
auto xGate3 = dd->makeGateDD(dd::X_MAT, nqubits, 0);
1204+
auto controlledHGate3 =
1205+
dd->makeGateDD(dd::H_MAT, nqubits, qc::Controls{0}, 1);
1206+
1207+
auto c5 = dd->multiply(
1208+
controlledSwapGate3,
1209+
dd->multiply(hGate3, dd->multiply(zGate, controlledSwapGate3)));
1210+
auto c6 = dd->multiply(controlledHGate3, xGate3);
1211+
1212+
auto c5MultipliedWithC6 = dd->multiply(c5, dd->conjugateTranspose(c6));
1213+
1214+
EXPECT_FALSE(dd->isCloseToIdentity(c5MultipliedWithC6, tol,
1215+
{false, true, true}, false));
1216+
EXPECT_FALSE(dd->isCloseToIdentity(c5MultipliedWithC6, tol,
1217+
{true, true, false}, false));
1218+
EXPECT_TRUE(dd->isCloseToIdentity(c5MultipliedWithC6, tol,
1219+
{true, false, true}, false));
1220+
}
1221+
11431222
struct DensityMatrixSimulatorDDPackageConfigTesting
11441223
: public dd::DDPackageConfig {
11451224
static constexpr std::size_t UT_DM_NBUCKET = 65536U;

0 commit comments

Comments
 (0)