From 345ef110e7e5e7cabe0e667c3c46dc3a68e6a885 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Sun, 31 Dec 2023 13:34:06 +0100 Subject: [PATCH 1/9] :construction: initial draft for generating random benchmarks --- include/dd/Verification.hpp | 3 ++ src/dd/CMakeLists.txt | 1 + src/dd/Verification.cpp | 77 +++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 src/dd/Verification.cpp diff --git a/include/dd/Verification.hpp b/include/dd/Verification.hpp index 4b65ef9f5..77d26454b 100644 --- a/include/dd/Verification.hpp +++ b/include/dd/Verification.hpp @@ -63,4 +63,7 @@ bool partialEquivalenceCheck(qc::QuantumComputation c1, return dd->partialEquivalenceCheck(u1, u2, static_cast(d1), static_cast(m1)); } + +std::pair +generateRandomBenchmark(Qubit n, Qubit d, Qubit m); } // namespace dd diff --git a/src/dd/CMakeLists.txt b/src/dd/CMakeLists.txt index ce713d7b7..94cedfd6b 100644 --- a/src/dd/CMakeLists.txt +++ b/src/dd/CMakeLists.txt @@ -18,6 +18,7 @@ if(NOT TARGET ${PROJECT_NAME}-dd) RealNumber.cpp RealNumberUniqueTable.cpp Simulation.cpp + Verification.cpp statistics/MemoryManagerStatistics.cpp statistics/Statistics.cpp statistics/TableStatistics.cpp diff --git a/src/dd/Verification.cpp b/src/dd/Verification.cpp new file mode 100644 index 000000000..7bcdf86ae --- /dev/null +++ b/src/dd/Verification.cpp @@ -0,0 +1,77 @@ +#include "dd/Verification.hpp" + +#include "QuantumComputation.hpp" +#include "operations/OpType.hpp" +#include "operations/StandardOperation.hpp" + +#include +#include + +namespace dd { + +void addDecomposedCxxGate(QuantumComputation& circuit, Qubit control1, + Qubit control2, Qubit target) { + circuit.h(target); + circuit.cx(control1, target); + circuit.tdg(target); + circuit.cx(control2, target); + circuit.t(target); + circuit.cx(control1, target); + circuit.t(control1); + circuit.tdg(target); + circuit.cx(control2, target); + circuit.cx(control2, control1); + circuit.t(target); + circuit.t(control2); + circuit.tdg(control1); + circuit.h(target); + circuit.cx(control2, control1); +} + +std::pair +generateRandomBenchmark(Qubit n, Qubit d, Qubit m) { + if (d > n) { + throw std::runtime_error("The number of data or measured qubits can't be " + "bigger than the total number of qubits. n = " + + std::to_string(n) + ";d = " + std::to_string(d) + + "; m = " + std::to_string(m)); + } + qc::QuantumComputation circuit1{n}; + qc::QuantumComputation circuit2{n}; + // H gates + for (Qubit i = 0U; i < d; i++) { + circuit1.h(i); + circuit2.h(i); + } + // Totally equivalent subcircuits + // generate a random subcircuit with d qubits and 3d gates to apply on both + // circuits, but all the Toffoli gates in C2 are decomposed + + for (Qubit i = 0U; i < 3 * d; i++) { + auto randomTarget = static_cast(rand() % d); + auto randomControl1 = static_cast(rand() % (d - 1)); + if (randomControl1 == randomTarget) { + randomControl1 = d - 1; + } + auto randomControl2 = static_cast(rand() % (d - 2)); + if (randomControl2 == randomTarget) { + randomControl2 = d - 1; + } + if (randomControl2 == randomControl1) { + randomControl2 = d - 2; + } + auto randomStandardOperation = static_cast(rand() % Compound); + circuit1.emplace_back(n, randomTarget, + randomStandardOperation); + if (randomStandardOperation == opTypeFromString("mcx")) { + addDecomposedCxxGate(circuit2, randomControl1, randomControl2, + randomTarget); + } else { + circuit2.emplace_back(n, randomTarget, + randomStandardOperation); + } + } + + return std::make_pair(circuit1, circuit2); +} +} // namespace dd From 9b6f20615f4aea981f2b1e6b5e0b45ff8dcd6c50 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Mon, 8 Jan 2024 11:51:22 +0100 Subject: [PATCH 2/9] :construction: first draft of random benchmark generation --- include/dd/Verification.hpp | 2 +- src/dd/Verification.cpp | 193 +++++++++++++++++++++++++++++++----- test/dd/test_package.cpp | 21 ++++ 3 files changed, 191 insertions(+), 25 deletions(-) diff --git a/include/dd/Verification.hpp b/include/dd/Verification.hpp index 77d26454b..98169a121 100644 --- a/include/dd/Verification.hpp +++ b/include/dd/Verification.hpp @@ -65,5 +65,5 @@ bool partialEquivalenceCheck(qc::QuantumComputation c1, } std::pair -generateRandomBenchmark(Qubit n, Qubit d, Qubit m); +generateRandomBenchmark(size_t n, Qubit d, Qubit m); } // namespace dd diff --git a/src/dd/Verification.cpp b/src/dd/Verification.cpp index 7bcdf86ae..d047e7b9e 100644 --- a/src/dd/Verification.cpp +++ b/src/dd/Verification.cpp @@ -9,6 +9,14 @@ namespace dd { +const std::vector> PRE_GENERATED_CIRCUITS_SIZE_1_1{{}}; + +const std::vector> PRE_GENERATED_CIRCUITS_SIZE_1_2{{Z}}; + +const std::vector> PRE_GENERATED_CIRCUITS_SIZE_2_1{{}}; + +const std::vector> PRE_GENERATED_CIRCUITS_SIZE_2_2{{Z}}; + void addDecomposedCxxGate(QuantumComputation& circuit, Qubit control1, Qubit control2, Qubit target) { circuit.h(target); @@ -28,8 +36,93 @@ void addDecomposedCxxGate(QuantumComputation& circuit, Qubit control1, circuit.cx(control2, control1); } +void addRandomGate(QuantumComputation& circuit, size_t n, Qubit d, + Qubit randomControl1, Qubit randomControl2, + Qubit randomTarget, OpType randomStandardOperation, + bool decomposeMcx) { + if (randomStandardOperation == opTypeFromString("mcx")) { + if (d >= 3) { + if (decomposeMcx) { + addDecomposedCxxGate(circuit, randomControl1, randomControl2, + randomTarget); + } else { + const Controls controls{randomControl1, randomControl2}; + StandardOperation op{n, controls, randomTarget, + randomStandardOperation}; + circuit.emplace_back(); + } + } + + } else if (isTwoQubitGate(randomStandardOperation)) { + if (d >= 2) { + circuit.emplace_back(n, randomControl1, randomTarget, + randomStandardOperation); + } + } else { + if (d >= 1) { + circuit.emplace_back(n, randomTarget, + randomStandardOperation); + } + } +} + +void addPreGeneratedCircuits(QuantumComputation& circuit1, + QuantumComputation& circuit2, size_t n, + Qubit groupBeginIndex, Qubit groupSize) { + + const auto& circuits1 = groupSize == 1 ? PRE_GENERATED_CIRCUITS_SIZE_1_1 + : PRE_GENERATED_CIRCUITS_SIZE_2_1; + const auto& circuits2 = groupSize == 1 ? PRE_GENERATED_CIRCUITS_SIZE_1_2 + : PRE_GENERATED_CIRCUITS_SIZE_2_2; + auto nrCircuits = circuits1.size(); + auto randomIndex = static_cast(rand()) % nrCircuits; + auto x1 = circuits1[randomIndex]; + auto x2 = circuits2[randomIndex]; + for (auto gateType : x1) { + if (isTwoQubitGate(gateType)) { + circuit1.emplace_back(n, groupBeginIndex, + groupBeginIndex + 1, gateType); + } + circuit1.emplace_back(n, groupBeginIndex, gateType); + } + for (auto gateType : x2) { + if (isTwoQubitGate(gateType)) { + circuit2.emplace_back(n, groupBeginIndex, + groupBeginIndex + 1, gateType); + } + circuit2.emplace_back(n, groupBeginIndex, gateType); + } +} + +std::tuple threeDiffferentRandomNumbers(Qubit min, + Qubit max) { + auto range = max - min; + auto randomTarget = static_cast(rand() % range) + min; + Qubit randomControl1{0}; + Qubit randomControl2{0}; + if (range > 1) { + randomControl1 = static_cast(rand() % (range - 1)) + min; + if (randomControl1 == randomTarget) { + randomControl1 = static_cast(max + min - 1); + } + if (range > 2) { + randomControl2 = static_cast(rand() % (range - 2)) + min; + if (randomControl2 == randomTarget) { + randomControl2 = static_cast(max + min - 1); + } + if (randomControl2 == randomControl1) { + randomControl2 = static_cast(max + min - 2); + if (randomControl2 == randomTarget) { + randomControl2 = static_cast(max + min - 1); + } + } + } + } + return std::make_tuple(randomTarget, randomControl1, randomControl2); +} + std::pair -generateRandomBenchmark(Qubit n, Qubit d, Qubit m) { +generateRandomBenchmark(size_t n, Qubit d, Qubit m) { if (d > n) { throw std::runtime_error("The number of data or measured qubits can't be " "bigger than the total number of qubits. n = " + @@ -38,39 +131,91 @@ generateRandomBenchmark(Qubit n, Qubit d, Qubit m) { } qc::QuantumComputation circuit1{n}; qc::QuantumComputation circuit2{n}; - // H gates + // 1) H gates for (Qubit i = 0U; i < d; i++) { circuit1.h(i); circuit2.h(i); } - // Totally equivalent subcircuits - // generate a random subcircuit with d qubits and 3d gates to apply on both - // circuits, but all the Toffoli gates in C2 are decomposed + + circuit1.barrier(0); + circuit1.barrier(1); + circuit1.barrier(2); + circuit2.barrier(0); + circuit2.barrier(1); + circuit2.barrier(2); + + // 2) Totally equivalent subcircuits + // generate a random subcircuit with d qubits and 3d gates to apply on both + // circuits, but all the Toffoli gates in C2 are decomposed for (Qubit i = 0U; i < 3 * d; i++) { - auto randomTarget = static_cast(rand() % d); - auto randomControl1 = static_cast(rand() % (d - 1)); - if (randomControl1 == randomTarget) { - randomControl1 = d - 1; - } - auto randomControl2 = static_cast(rand() % (d - 2)); - if (randomControl2 == randomTarget) { - randomControl2 = d - 1; + auto [randomTarget, randomControl1, randomControl2] = + threeDiffferentRandomNumbers(0, d); + auto randomStandardOperation = static_cast(rand() % Compound); + addRandomGate(circuit1, n, d, randomControl1, randomControl2, randomTarget, + randomStandardOperation, false); + addRandomGate(circuit2, n, d, randomControl1, randomControl2, randomTarget, + randomStandardOperation, true); + } + circuit1.barrier(0); + circuit1.barrier(1); + circuit1.barrier(2); + circuit2.barrier(0); + circuit2.barrier(1); + circuit2.barrier(2); + // 3) Partially equivalent subcircuits + + // divide data qubits into groups of size 1 or 2 + Qubit groupBeginIndex = 0; + while (groupBeginIndex < d) { + Qubit groupSize = 1; + if (groupBeginIndex < d - 1) { + groupSize = static_cast(rand() % 2) + 1; } - if (randomControl2 == randomControl1) { - randomControl2 = d - 2; + + addPreGeneratedCircuits(circuit1, circuit2, n, groupBeginIndex, groupSize); + + groupBeginIndex += groupSize; + } + circuit1.barrier(0); + circuit1.barrier(1); + circuit1.barrier(2); + circuit2.barrier(0); + circuit2.barrier(1); + circuit2.barrier(2); + // 4) Arbitrary gates + // arbitrary gates are added to not measured qubits + if (d > m) { + for (Qubit i = 0U; i < d - m; i++) { + auto [randomTarget, randomControl1, randomControl2] = + threeDiffferentRandomNumbers(m, d); + auto randomStandardOperation = static_cast(rand() % Compound); + addRandomGate(circuit1, n, d - m, randomControl1, randomControl2, + randomTarget, randomStandardOperation, false); } - auto randomStandardOperation = static_cast(rand() % Compound); - circuit1.emplace_back(n, randomTarget, - randomStandardOperation); - if (randomStandardOperation == opTypeFromString("mcx")) { - addDecomposedCxxGate(circuit2, randomControl1, randomControl2, - randomTarget); - } else { - circuit2.emplace_back(n, randomTarget, - randomStandardOperation); + for (Qubit i = 0U; i < d - m; i++) { + auto [randomTarget, randomControl1, randomControl2] = + threeDiffferentRandomNumbers(m, d); + auto randomStandardOperation = static_cast(rand() % Compound); + addRandomGate(circuit2, n, d - m, randomControl1, randomControl2, + randomTarget, randomStandardOperation, false); } } + circuit1.barrier(0); + circuit1.barrier(1); + circuit1.barrier(2); + circuit2.barrier(0); + circuit2.barrier(1); + circuit2.barrier(2); + // 5) CNOT gates (if there are ancilla qubits) + Qubit currentDataQubit = 0; + for (Qubit currentAncillaQubit = d; + currentAncillaQubit < static_cast(n); currentAncillaQubit++) { + auto nextDataQubit = static_cast((currentDataQubit + 1) % d); + circuit1.cx(currentAncillaQubit, currentDataQubit); + circuit2.cx(currentAncillaQubit, nextDataQubit); + currentDataQubit = nextDataQubit; + } return std::make_pair(circuit1, circuit2); } diff --git a/test/dd/test_package.cpp b/test/dd/test_package.cpp index 76ac2a168..574504580 100644 --- a/test/dd/test_package.cpp +++ b/test/dd/test_package.cpp @@ -2433,3 +2433,24 @@ TEST(DDPackageTest, DDMPECSliQECPeriodFinding) { c1.setLogicalQubitGarbage(4); EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); } + +TEST(DDPackageTest, DDMPECBenchmark) { + auto dd = std::make_unique>(7); + + auto [c1, c2] = dd::generateRandomBenchmark(5, 3, 1); + + std::cout << "circuit 1: \n"; + c1.print(std::cout); + std::cout << "circuit 2: \n"; + c2.print(std::cout); + + c1.setLogicalQubitAncillary(3); + c1.setLogicalQubitAncillary(4); + + c2.setLogicalQubitGarbage(1); + c2.setLogicalQubitGarbage(2); + c2.setLogicalQubitGarbage(3); + c2.setLogicalQubitGarbage(4); + + EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); +} From 24fa752f81d4fdfb62c5eff4730458de44b7d12d Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Sat, 13 Jan 2024 12:57:59 +0100 Subject: [PATCH 3/9] :bug: fixed bug in partialEquivalenceCheck for m=0 --- include/dd/Verification.hpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/include/dd/Verification.hpp b/include/dd/Verification.hpp index 4bd139aa2..043ca9c67 100644 --- a/include/dd/Verification.hpp +++ b/include/dd/Verification.hpp @@ -38,11 +38,12 @@ bool partialEquivalenceCheck(qc::QuantumComputation c1, auto n2 = static_cast(c2.getNqubits()); auto nextGarbage = getNextGarbage(0, garbage1); // find the first garbage qubit at the end - for (Qubit i = std::min(n1, n2) - 1; i >= static_cast(m1); i--) { - if (!garbage1.at(i)) { + for (std::int64_t i = std::min(n1, n2) - 1; + i >= static_cast(m1); i--) { + if (!garbage1.at(static_cast(i))) { // swap it to the beginning - c1.swap(i, nextGarbage); - c2.swap(i, nextGarbage); + c1.swap(static_cast(i), nextGarbage); + c2.swap(static_cast(i), nextGarbage); ++nextGarbage; nextGarbage = getNextGarbage(nextGarbage, garbage1); } @@ -52,6 +53,15 @@ bool partialEquivalenceCheck(qc::QuantumComputation c1, auto u1 = buildFunctionality(&c1, *dd, false, false); auto u2 = buildFunctionality(&c2, *dd, false, false); + + // std::cout << "u1: \n"; + // u1.printMatrix(); + // std::cout << std::endl; + + // std::cout << "u2: \n"; + // u2.printMatrix(); + // std::cout << std::endl; + if (d1 == n1 && d2 == n2) { // no ancilla qubits return dd->zeroAncillaePartialEquivalenceCheck(u1, u2, From 8ce1564237ee1786279dbbbd846b2ccbfcafcf63 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Sat, 13 Jan 2024 12:59:32 +0100 Subject: [PATCH 4/9] :construction: almost finished implementation of random circuit generation for PEC --- src/dd/Verification.cpp | 276 ++++++++++++++++++++++++--------------- test/dd/test_package.cpp | 102 +++++++++++---- 2 files changed, 250 insertions(+), 128 deletions(-) diff --git a/src/dd/Verification.cpp b/src/dd/Verification.cpp index d047e7b9e..20c359c35 100644 --- a/src/dd/Verification.cpp +++ b/src/dd/Verification.cpp @@ -4,9 +4,6 @@ #include "operations/OpType.hpp" #include "operations/StandardOperation.hpp" -#include -#include - namespace dd { const std::vector> PRE_GENERATED_CIRCUITS_SIZE_1_1{{}}; @@ -25,45 +22,155 @@ void addDecomposedCxxGate(QuantumComputation& circuit, Qubit control1, circuit.cx(control2, target); circuit.t(target); circuit.cx(control1, target); - circuit.t(control1); circuit.tdg(target); circuit.cx(control2, target); - circuit.cx(control2, control1); circuit.t(target); + circuit.t(control1); + circuit.h(target); + circuit.cx(control2, control1); circuit.t(control2); circuit.tdg(control1); - circuit.h(target); circuit.cx(control2, control1); } -void addRandomGate(QuantumComputation& circuit, size_t n, Qubit d, - Qubit randomControl1, Qubit randomControl2, - Qubit randomTarget, OpType randomStandardOperation, - bool decomposeMcx) { - if (randomStandardOperation == opTypeFromString("mcx")) { - if (d >= 3) { - if (decomposeMcx) { - addDecomposedCxxGate(circuit, randomControl1, randomControl2, - randomTarget); - } else { - const Controls controls{randomControl1, randomControl2}; - StandardOperation op{n, controls, randomTarget, - randomStandardOperation}; - circuit.emplace_back(); - } +void addRandomStandardOperation(QuantumComputation& circuit, + StandardOperation op, bool decomposeMcx) { + std::vector controls{}; + for (auto c : op.getControls()) { // they are at most 2 + controls.push_back(static_cast(c.qubit)); + } + std::vector targets{}; + for (auto t : op.getTargets()) { // they are at most 2 + targets.push_back(static_cast(t)); + } + + if (op.getType() == qc::X && controls.size() == 2) { + + if (decomposeMcx) { + addDecomposedCxxGate(circuit, controls[0], controls[1], targets[0]); + return; } + } + + circuit.emplace_back(op); +} + +std::vector fiveDiffferentRandomNumbers(Qubit min, Qubit max) { + std::vector numbers; + + for (Qubit i = min; i < max; i++) { + numbers.push_back(i); + } + unsigned seed = static_cast( + std::chrono::system_clock::now().time_since_epoch().count()); + std::shuffle(numbers.begin(), numbers.end(), + std::default_random_engine(seed)); + + auto lengthOutputVector = std::min(5UL, numbers.size()); + std::vector outputVector(numbers.begin(), + numbers.begin() + + static_cast(lengthOutputVector)); + return outputVector; +} - } else if (isTwoQubitGate(randomStandardOperation)) { - if (d >= 2) { - circuit.emplace_back(n, randomControl1, randomTarget, - randomStandardOperation); +StandardOperation makeRandomStandardOperation(size_t n, Qubit nrQubits, + Qubit min) { + auto randomNumbers = fiveDiffferentRandomNumbers(min, min + nrQubits); + auto randomOpType = static_cast(rand() % Compound); + Qubit randomTarget1 = randomNumbers[0]; + Qubit randomTarget2{min}; + if (randomNumbers.size() > 1) { + randomTarget2 = randomNumbers[1]; + }; + size_t nrControls = + std::min(randomNumbers.size() - 3, static_cast(rand() % 3)); + if (randomNumbers.size() < 3) { + nrControls = 0; + } + if (nrControls == 2) { + // otherwise Cnots are almost never generated + randomOpType = qc::X; + } + Controls randomControls{}; + for (size_t i = 0; i < nrControls; i++) { + randomControls.emplace(randomNumbers[i + 2]); + } + const fp randomParameter1 = + static_cast(rand()) / static_cast(RAND_MAX); + const fp randomParameter2 = + static_cast(rand()) / static_cast(RAND_MAX); + const fp randomParameter3 = + static_cast(rand()) / static_cast(RAND_MAX); + switch (randomOpType) { + // two targets and zero parameters + case qc::SWAP: + case qc::iSWAP: + case qc::iSWAPdg: + case qc::Peres: + case qc::Peresdg: + case qc::DCX: + case qc::ECR: + if (randomNumbers.size() > 1) { + return {n, randomControls, Targets{randomTarget1, randomTarget2}, + randomOpType}; + } + break; + // two targets and one parameter + case qc::RXX: + case qc::RYY: + case qc::RZZ: + case qc::RZX: + if (randomNumbers.size() > 1) { + return {n, randomControls, Targets{randomTarget1, randomTarget2}, + randomOpType, std::vector{randomParameter1}}; } - } else { - if (d >= 1) { - circuit.emplace_back(n, randomTarget, - randomStandardOperation); + break; + + // two targets and two parameters + case qc::XXminusYY: + case qc::XXplusYY: + if (randomNumbers.size() > 1) { + return {n, randomControls, Targets{randomTarget1, randomTarget2}, + randomOpType, + std::vector{randomParameter1, randomParameter2}}; } + break; + + // one target and zero parameters + case qc::I: + case qc::H: + case qc::X: + case qc::Y: + case qc::Z: + case qc::S: + case qc::Sdg: + case qc::T: + case qc::Tdg: + case qc::V: + case qc::Vdg: + case qc::SX: + case qc::SXdg: + return {n, randomControls, randomTarget1, randomOpType}; + // one target and three parameters + case qc::U: + return { + n, randomControls, randomTarget1, randomOpType, + std::vector{randomParameter1, randomParameter2, randomParameter3}}; + // one target and two parameters + case qc::U2: + return {n, randomControls, randomTarget1, randomOpType, + std::vector{randomParameter1, randomParameter2}}; + // one target and one parameter + case qc::P: + case qc::RX: + case qc::RY: + case qc::RZ: + return {n, randomControls, randomTarget1, randomOpType, + std::vector{randomParameter1}}; + default: + return {n, randomTarget1, qc::I}; } + return {n, randomTarget1, qc::I}; } void addPreGeneratedCircuits(QuantumComputation& circuit1, @@ -94,33 +201,6 @@ void addPreGeneratedCircuits(QuantumComputation& circuit1, } } -std::tuple threeDiffferentRandomNumbers(Qubit min, - Qubit max) { - auto range = max - min; - auto randomTarget = static_cast(rand() % range) + min; - Qubit randomControl1{0}; - Qubit randomControl2{0}; - if (range > 1) { - randomControl1 = static_cast(rand() % (range - 1)) + min; - if (randomControl1 == randomTarget) { - randomControl1 = static_cast(max + min - 1); - } - if (range > 2) { - randomControl2 = static_cast(rand() % (range - 2)) + min; - if (randomControl2 == randomTarget) { - randomControl2 = static_cast(max + min - 1); - } - if (randomControl2 == randomControl1) { - randomControl2 = static_cast(max + min - 2); - if (randomControl2 == randomTarget) { - randomControl2 = static_cast(max + min - 1); - } - } - } - } - return std::make_tuple(randomTarget, randomControl1, randomControl2); -} - std::pair generateRandomBenchmark(size_t n, Qubit d, Qubit m) { if (d > n) { @@ -136,33 +216,23 @@ generateRandomBenchmark(size_t n, Qubit d, Qubit m) { circuit1.h(i); circuit2.h(i); } - - circuit1.barrier(0); - circuit1.barrier(1); - circuit1.barrier(2); - circuit2.barrier(0); - circuit2.barrier(1); - circuit2.barrier(2); - + for (Qubit i = 0U; i < static_cast(n); i++) { + circuit1.barrier(i); + circuit2.barrier(i); + } // 2) Totally equivalent subcircuits - // generate a random subcircuit with d qubits and 3d gates to apply on both - // circuits, but all the Toffoli gates in C2 are decomposed + // generate a random subcircuit with d qubits and 3d gates to apply + // on both circuits, but all the Toffoli gates in C2 are decomposed for (Qubit i = 0U; i < 3 * d; i++) { - auto [randomTarget, randomControl1, randomControl2] = - threeDiffferentRandomNumbers(0, d); - auto randomStandardOperation = static_cast(rand() % Compound); - addRandomGate(circuit1, n, d, randomControl1, randomControl2, randomTarget, - randomStandardOperation, false); - addRandomGate(circuit2, n, d, randomControl1, randomControl2, randomTarget, - randomStandardOperation, true); - } - circuit1.barrier(0); - circuit1.barrier(1); - circuit1.barrier(2); - circuit2.barrier(0); - circuit2.barrier(1); - circuit2.barrier(2); + auto op = makeRandomStandardOperation(n, d, 0); + addRandomStandardOperation(circuit1, op, false); + addRandomStandardOperation(circuit2, op, true); + } + for (Qubit i = 0U; i < static_cast(n); i++) { + circuit1.barrier(i); + circuit2.barrier(i); + } // 3) Partially equivalent subcircuits // divide data qubits into groups of size 1 or 2 @@ -177,36 +247,27 @@ generateRandomBenchmark(size_t n, Qubit d, Qubit m) { groupBeginIndex += groupSize; } - circuit1.barrier(0); - circuit1.barrier(1); - circuit1.barrier(2); - circuit2.barrier(0); - circuit2.barrier(1); - circuit2.barrier(2); + for (Qubit i = 0U; i < static_cast(n); i++) { + circuit1.barrier(i); + circuit2.barrier(i); + } // 4) Arbitrary gates // arbitrary gates are added to not measured qubits if (d > m) { - for (Qubit i = 0U; i < d - m; i++) { - auto [randomTarget, randomControl1, randomControl2] = - threeDiffferentRandomNumbers(m, d); - auto randomStandardOperation = static_cast(rand() % Compound); - addRandomGate(circuit1, n, d - m, randomControl1, randomControl2, - randomTarget, randomStandardOperation, false); + Qubit notMQubits = d - m; + for (Qubit i = 0U; i < notMQubits; i++) { + auto op = makeRandomStandardOperation(n, notMQubits, m); + addRandomStandardOperation(circuit1, op, false); } for (Qubit i = 0U; i < d - m; i++) { - auto [randomTarget, randomControl1, randomControl2] = - threeDiffferentRandomNumbers(m, d); - auto randomStandardOperation = static_cast(rand() % Compound); - addRandomGate(circuit2, n, d - m, randomControl1, randomControl2, - randomTarget, randomStandardOperation, false); + auto op = makeRandomStandardOperation(n, notMQubits, m); + addRandomStandardOperation(circuit2, op, false); } } - circuit1.barrier(0); - circuit1.barrier(1); - circuit1.barrier(2); - circuit2.barrier(0); - circuit2.barrier(1); - circuit2.barrier(2); + for (Qubit i = 0U; i < static_cast(n); i++) { + circuit1.barrier(i); + circuit2.barrier(i); + } // 5) CNOT gates (if there are ancilla qubits) Qubit currentDataQubit = 0; for (Qubit currentAncillaQubit = d; @@ -217,6 +278,15 @@ generateRandomBenchmark(size_t n, Qubit d, Qubit m) { currentDataQubit = nextDataQubit; } + for (Qubit i = d; i < static_cast(n); i++) { + circuit1.setLogicalQubitAncillary(i); + circuit2.setLogicalQubitAncillary(i); + } + for (Qubit i = m; i < static_cast(n); i++) { + circuit1.setLogicalQubitGarbage(i); + circuit2.setLogicalQubitGarbage(i); + } + return std::make_pair(circuit1, circuit2); } } // namespace dd diff --git a/test/dd/test_package.cpp b/test/dd/test_package.cpp index 4d64a8931..d524030f7 100644 --- a/test/dd/test_package.cpp +++ b/test/dd/test_package.cpp @@ -1984,6 +1984,21 @@ TEST(DDPackageTest, DDStatistics) { EXPECT_GT(uniqueTableStats["total"]["num_buckets"], 0); } +TEST(DDPackageTest, ReduceAncillaeRegression) { + auto dd = std::make_unique>(2); + const auto inputMatrix = + dd::CMat{{1, 1, 1, 1}, {1, -1, 1, -1}, {1, 1, -1, -1}, {1, -1, -1, 1}}; + auto inputDD = dd->makeDDFromMatrix(inputMatrix); + dd->incRef(inputDD); + const auto outputDD = dd->reduceAncillae(inputDD, {true, false}); + + const auto outputMatrix = outputDD.getMatrix(); + const auto expected = + dd::CMat{{1, 0, 1, 0}, {1, 0, 1, 0}, {1, 0, -1, 0}, {1, 0, -1, 0}}; + + EXPECT_EQ(outputMatrix, expected); +} + TEST(DDPackageTest, DDMShiftAllRows) { const auto nqubits = 2U; auto dd = std::make_unique>(nqubits); @@ -2485,37 +2500,74 @@ TEST(DDPackageTest, DDMPECSliQECPeriodFinding8Qubits) { } TEST(DDPackageTest, DDMPECBenchmark) { - auto dd = std::make_unique>(7); + auto dd = std::make_unique>(20); + size_t minN = 3; + size_t maxN = 9; + std::cout << "Partial equivalence check\n"; + for (size_t n = minN; n < maxN; n++) { + dd::Qubit d = + static_cast(static_cast(rand()) % (n - 1)) + 1; + dd::Qubit m = static_cast(rand()) % d; + auto [c1, c2] = dd::generateRandomBenchmark(n, d, m); + + std::cout << "circuit 1: \n"; + c1.print(std::cout); + std::cout << "circuit 2: \n"; + c2.print(std::cout); + auto start = std::chrono::high_resolution_clock::now(); + EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); + // Get ending timepoint + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = + std::chrono::duration_cast(stop - start); + + std::cout << "\nnumber of qubits = " << n << "; data qubits = " << d + << "; measured qubits = " << m + << "; number of gates = " << c2.size() << "\n"; + std::cout << "time: " << static_cast(duration.count()) / 1000000. + << " seconds\n"; + } +} + +TEST(DDPackageTest, DDMZAPECBenchmark) { + auto dd = std::make_unique>(20); + size_t minN = 3; + size_t maxN = 8; + std::cout << "Zero-ancilla partial equivalence check\n"; + for (size_t n = minN; n < maxN; n++) { + auto d = static_cast(n); + dd::Qubit m = static_cast(rand()) % d; + auto [c1, c2] = dd::generateRandomBenchmark(n, d, m); + + std::cout << "circuit 1: \n"; + c1.print(std::cout); + std::cout << "circuit 2: \n"; + c2.print(std::cout); + auto start = std::chrono::high_resolution_clock::now(); + EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); + // Get ending timepoint + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = + std::chrono::duration_cast(stop - start); + + std::cout << "\nnumber of qubits = " << n << "; data qubits = " << d + << "; measured qubits = " << m + << "; number of gates = " << c2.size() << "\n"; + std::cout << "time: " << static_cast(duration.count()) / 1000000. + << " seconds\n"; + } +} - auto [c1, c2] = dd::generateRandomBenchmark(5, 3, 1); +TEST(DDPackageTest, DDMPECZ) { + auto dd = std::make_unique>(1); + dd::QuantumComputation c1{1}; + dd::QuantumComputation c2{1}; + c2.z(0); std::cout << "circuit 1: \n"; c1.print(std::cout); std::cout << "circuit 2: \n"; c2.print(std::cout); - c1.setLogicalQubitAncillary(3); - c1.setLogicalQubitAncillary(4); - - c2.setLogicalQubitGarbage(1); - c2.setLogicalQubitGarbage(2); - c2.setLogicalQubitGarbage(3); - c2.setLogicalQubitGarbage(4); - EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); } - -TEST(DDPackageTest, ReduceAncillaeRegression) { - auto dd = std::make_unique>(2); - const auto inputMatrix = - dd::CMat{{1, 1, 1, 1}, {1, -1, 1, -1}, {1, 1, -1, -1}, {1, -1, -1, 1}}; - auto inputDD = dd->makeDDFromMatrix(inputMatrix); - dd->incRef(inputDD); - const auto outputDD = dd->reduceAncillae(inputDD, {true, false}); - - const auto outputMatrix = outputDD.getMatrix(); - const auto expected = - dd::CMat{{1, 0, 1, 0}, {1, 0, 1, 0}, {1, 0, -1, 0}, {1, 0, -1, 0}}; - - EXPECT_EQ(outputMatrix, expected); -} From c9ce87aa62e228da4d473ea940ea7b1fc26f0088 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Mon, 22 Jan 2024 10:56:08 +0100 Subject: [PATCH 5/9] :bug: bug fix in circuit generation --- src/dd/Verification.cpp | 8 +-- test/dd/test_package.cpp | 108 +++++++++++++++++---------------------- 2 files changed, 52 insertions(+), 64 deletions(-) diff --git a/src/dd/Verification.cpp b/src/dd/Verification.cpp index 20c359c35..07b5314cc 100644 --- a/src/dd/Verification.cpp +++ b/src/dd/Verification.cpp @@ -61,8 +61,7 @@ std::vector fiveDiffferentRandomNumbers(Qubit min, Qubit max) { for (Qubit i = min; i < max; i++) { numbers.push_back(i); } - unsigned seed = static_cast( - std::chrono::system_clock::now().time_since_epoch().count()); + unsigned seed = 42; std::shuffle(numbers.begin(), numbers.end(), std::default_random_engine(seed)); @@ -76,7 +75,8 @@ std::vector fiveDiffferentRandomNumbers(Qubit min, Qubit max) { StandardOperation makeRandomStandardOperation(size_t n, Qubit nrQubits, Qubit min) { auto randomNumbers = fiveDiffferentRandomNumbers(min, min + nrQubits); - auto randomOpType = static_cast(rand() % Compound); + // choose one of the non-compound operations, but not "None" + auto randomOpType = static_cast(rand() % (Compound - 1) + 1); Qubit randomTarget1 = randomNumbers[0]; Qubit randomTarget2{min}; if (randomNumbers.size() > 1) { @@ -259,7 +259,7 @@ generateRandomBenchmark(size_t n, Qubit d, Qubit m) { auto op = makeRandomStandardOperation(n, notMQubits, m); addRandomStandardOperation(circuit1, op, false); } - for (Qubit i = 0U; i < d - m; i++) { + for (Qubit i = 0U; i < notMQubits; i++) { auto op = makeRandomStandardOperation(n, notMQubits, m); addRandomStandardOperation(circuit2, op, false); } diff --git a/test/dd/test_package.cpp b/test/dd/test_package.cpp index caead1f2b..b80bae9ca 100644 --- a/test/dd/test_package.cpp +++ b/test/dd/test_package.cpp @@ -2572,73 +2572,61 @@ TEST(DDPackageTest, DDMPECSliQECPeriodFinding8Qubits) { TEST(DDPackageTest, DDMPECBenchmark) { auto dd = std::make_unique>(20); - size_t minN = 3; - size_t maxN = 9; + srand(55); + size_t minN = 2; + size_t maxN = 8; + size_t reps = 15; std::cout << "Partial equivalence check\n"; - for (size_t n = minN; n < maxN; n++) { - dd::Qubit d = - static_cast(static_cast(rand()) % (n - 1)) + 1; - dd::Qubit m = static_cast(rand()) % d; - auto [c1, c2] = dd::generateRandomBenchmark(n, d, m); - - std::cout << "circuit 1: \n"; - c1.print(std::cout); - std::cout << "circuit 2: \n"; - c2.print(std::cout); - auto start = std::chrono::high_resolution_clock::now(); - EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); - // Get ending timepoint - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = - std::chrono::duration_cast(stop - start); - - std::cout << "\nnumber of qubits = " << n << "; data qubits = " << d - << "; measured qubits = " << m - << "; number of gates = " << c2.size() << "\n"; - std::cout << "time: " << static_cast(duration.count()) / 1000000. - << " seconds\n"; + for (size_t k = 0; k < reps; k++) { + for (size_t n = minN; n < maxN; n++) { + dd::Qubit d = + static_cast(static_cast(rand()) % (n - 1)) + 1; + dd::Qubit m = static_cast(rand()) % d; + auto [c1, c2] = dd::generateRandomBenchmark(n, d, m); + + auto start = std::chrono::high_resolution_clock::now(); + bool result = dd::partialEquivalenceCheck(c1, c2, dd); + // Get ending timepoint + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = + std::chrono::duration_cast(stop - start); + + EXPECT_TRUE(result); + + std::cout << "\nnumber of qubits = " << n << "; data qubits = " << d + << "; measured qubits = " << m + << "; number of gates = " << c2.size() << "\n"; + std::cout << "time: " << static_cast(duration.count()) / 1000000. + << " seconds\n"; + } } } TEST(DDPackageTest, DDMZAPECBenchmark) { auto dd = std::make_unique>(20); size_t minN = 3; - size_t maxN = 8; + size_t maxN = 12; + size_t reps = 1; std::cout << "Zero-ancilla partial equivalence check\n"; - for (size_t n = minN; n < maxN; n++) { - auto d = static_cast(n); - dd::Qubit m = static_cast(rand()) % d; - auto [c1, c2] = dd::generateRandomBenchmark(n, d, m); - - std::cout << "circuit 1: \n"; - c1.print(std::cout); - std::cout << "circuit 2: \n"; - c2.print(std::cout); - auto start = std::chrono::high_resolution_clock::now(); - EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); - // Get ending timepoint - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = - std::chrono::duration_cast(stop - start); - - std::cout << "\nnumber of qubits = " << n << "; data qubits = " << d - << "; measured qubits = " << m - << "; number of gates = " << c2.size() << "\n"; - std::cout << "time: " << static_cast(duration.count()) / 1000000. - << " seconds\n"; + for (size_t k = 0; k < reps; k++) { + for (size_t n = minN; n < maxN; n++) { + auto d = static_cast(n); + dd::Qubit m = static_cast(rand()) % d; + auto [c1, c2] = dd::generateRandomBenchmark(n, d, m); + auto start = std::chrono::high_resolution_clock::now(); + bool result = dd::partialEquivalenceCheck(c1, c2, dd); + // Get ending timepoint + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = + std::chrono::duration_cast(stop - start); + + EXPECT_TRUE(result); + + std::cout << "\nnumber of qubits = " << n << "; data qubits = " << d + << "; measured qubits = " << m + << "; number of gates = " << c2.size() << "\n"; + std::cout << "time: " << static_cast(duration.count()) / 1000000. + << " seconds\n"; + } } } - -TEST(DDPackageTest, DDMPECZ) { - auto dd = std::make_unique>(1); - - dd::QuantumComputation c1{1}; - dd::QuantumComputation c2{1}; - c2.z(0); - std::cout << "circuit 1: \n"; - c1.print(std::cout); - std::cout << "circuit 2: \n"; - c2.print(std::cout); - - EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); -} From dc81bd6ecc7b56e6763e2ea0d94f471992142a6c Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Wed, 24 Jan 2024 11:39:04 +0100 Subject: [PATCH 6/9] add some pre-generated circuits --- src/dd/Verification.cpp | 87 ++++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 36 deletions(-) diff --git a/src/dd/Verification.cpp b/src/dd/Verification.cpp index 07b5314cc..3ee4fb009 100644 --- a/src/dd/Verification.cpp +++ b/src/dd/Verification.cpp @@ -6,13 +6,17 @@ namespace dd { -const std::vector> PRE_GENERATED_CIRCUITS_SIZE_1_1{{}}; +const std::vector> PRE_GENERATED_CIRCUITS_SIZE_1_1{ + {}, {}, {}, {}}; -const std::vector> PRE_GENERATED_CIRCUITS_SIZE_1_2{{Z}}; +const std::vector> PRE_GENERATED_CIRCUITS_SIZE_1_2{ + {Z}, {Tdg}, {S}, {Sdg}}; -const std::vector> PRE_GENERATED_CIRCUITS_SIZE_2_1{{}}; +const std::vector> PRE_GENERATED_CIRCUITS_SIZE_2_1{ + {}, {}, {}, {}}; -const std::vector> PRE_GENERATED_CIRCUITS_SIZE_2_2{{Z}}; +const std::vector> PRE_GENERATED_CIRCUITS_SIZE_2_2{ + {Z}, {Tdg}, {S}, {Sdg}}; void addDecomposedCxxGate(QuantumComputation& circuit, Qubit control1, Qubit control2, Qubit target) { @@ -72,35 +76,11 @@ std::vector fiveDiffferentRandomNumbers(Qubit min, Qubit max) { return outputVector; } -StandardOperation makeRandomStandardOperation(size_t n, Qubit nrQubits, - Qubit min) { - auto randomNumbers = fiveDiffferentRandomNumbers(min, min + nrQubits); - // choose one of the non-compound operations, but not "None" - auto randomOpType = static_cast(rand() % (Compound - 1) + 1); - Qubit randomTarget1 = randomNumbers[0]; - Qubit randomTarget2{min}; - if (randomNumbers.size() > 1) { - randomTarget2 = randomNumbers[1]; - }; - size_t nrControls = - std::min(randomNumbers.size() - 3, static_cast(rand() % 3)); - if (randomNumbers.size() < 3) { - nrControls = 0; - } - if (nrControls == 2) { - // otherwise Cnots are almost never generated - randomOpType = qc::X; - } - Controls randomControls{}; - for (size_t i = 0; i < nrControls; i++) { - randomControls.emplace(randomNumbers[i + 2]); - } - const fp randomParameter1 = - static_cast(rand()) / static_cast(RAND_MAX); - const fp randomParameter2 = - static_cast(rand()) / static_cast(RAND_MAX); - const fp randomParameter3 = - static_cast(rand()) / static_cast(RAND_MAX); +StandardOperation convertToStandardOperation( + size_t n, size_t nrQubits, OpType randomOpType, Qubit randomTarget1, + Qubit randomTarget2, fp randomParameter1, fp randomParameter2, + fp randomParameter3, const Controls& randomControls) { + switch (randomOpType) { // two targets and zero parameters case qc::SWAP: @@ -110,7 +90,7 @@ StandardOperation makeRandomStandardOperation(size_t n, Qubit nrQubits, case qc::Peresdg: case qc::DCX: case qc::ECR: - if (randomNumbers.size() > 1) { + if (nrQubits > 1) { return {n, randomControls, Targets{randomTarget1, randomTarget2}, randomOpType}; } @@ -120,7 +100,7 @@ StandardOperation makeRandomStandardOperation(size_t n, Qubit nrQubits, case qc::RYY: case qc::RZZ: case qc::RZX: - if (randomNumbers.size() > 1) { + if (nrQubits > 1) { return {n, randomControls, Targets{randomTarget1, randomTarget2}, randomOpType, std::vector{randomParameter1}}; } @@ -129,7 +109,7 @@ StandardOperation makeRandomStandardOperation(size_t n, Qubit nrQubits, // two targets and two parameters case qc::XXminusYY: case qc::XXplusYY: - if (randomNumbers.size() > 1) { + if (nrQubits > 1) { return {n, randomControls, Targets{randomTarget1, randomTarget2}, randomOpType, std::vector{randomParameter1, randomParameter2}}; @@ -173,6 +153,41 @@ StandardOperation makeRandomStandardOperation(size_t n, Qubit nrQubits, return {n, randomTarget1, qc::I}; } +StandardOperation makeRandomStandardOperation(size_t n, Qubit nrQubits, + Qubit min) { + auto randomNumbers = fiveDiffferentRandomNumbers(min, min + nrQubits); + // choose one of the non-compound operations, but not "None" + auto randomOpType = static_cast(rand() % (Vdg - H) + H); + Qubit randomTarget1 = randomNumbers[0]; + Qubit randomTarget2{min}; + if (randomNumbers.size() > 1) { + randomTarget2 = randomNumbers[1]; + }; + size_t nrControls = + std::min(randomNumbers.size() - 3, static_cast(rand() % 3)); + if (randomNumbers.size() < 3) { + nrControls = 0; + } + if (nrControls == 2) { + // otherwise Cnots are almost never generated + randomOpType = qc::X; + } + Controls randomControls{}; + for (size_t i = 0; i < nrControls; i++) { + randomControls.emplace(randomNumbers[i + 2]); + } + const std::vector randomParameters{PI, PI_2, PI_4}; + const fp randomParameter1 = + randomParameters[static_cast(rand()) % randomParameters.size()]; + const fp randomParameter2 = + randomParameters[static_cast(rand()) % randomParameters.size()]; + const fp randomParameter3 = + randomParameters[static_cast(rand()) % randomParameters.size()]; + return convertToStandardOperation( + n, nrQubits, randomOpType, randomTarget1, randomTarget2, randomParameter1, + randomParameter2, randomParameter3, randomControls); +} + void addPreGeneratedCircuits(QuantumComputation& circuit1, QuantumComputation& circuit2, size_t n, Qubit groupBeginIndex, Qubit groupSize) { From 90cbadd6fb695f8d7a89a0e8e6f9b9940441e517 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Mon, 29 Jan 2024 14:36:50 +0100 Subject: [PATCH 7/9] :art: clean up code --- src/dd/Verification.cpp | 116 +++++++++++++++++++++------------------ test/dd/test_package.cpp | 108 +++++++++++++++++++++++------------- 2 files changed, 132 insertions(+), 92 deletions(-) diff --git a/src/dd/Verification.cpp b/src/dd/Verification.cpp index 3ee4fb009..401f77ed7 100644 --- a/src/dd/Verification.cpp +++ b/src/dd/Verification.cpp @@ -13,12 +13,42 @@ const std::vector> PRE_GENERATED_CIRCUITS_SIZE_1_2{ {Z}, {Tdg}, {S}, {Sdg}}; const std::vector> PRE_GENERATED_CIRCUITS_SIZE_2_1{ - {}, {}, {}, {}}; + {}, {}, {}, {}, {X}, {X}}; const std::vector> PRE_GENERATED_CIRCUITS_SIZE_2_2{ - {Z}, {Tdg}, {S}, {Sdg}}; + {Z}, {Tdg}, {S}, {Sdg}, {X, Z}, {Z, X}}; + +void addPreGeneratedCircuits(QuantumComputation& circuit1, + QuantumComputation& circuit2, size_t n, + Qubit groupBeginIndex, Qubit groupSize) { -void addDecomposedCxxGate(QuantumComputation& circuit, Qubit control1, + const auto& circuits1 = groupSize == 1 ? PRE_GENERATED_CIRCUITS_SIZE_1_1 + : PRE_GENERATED_CIRCUITS_SIZE_2_1; + const auto& circuits2 = groupSize == 1 ? PRE_GENERATED_CIRCUITS_SIZE_1_2 + : PRE_GENERATED_CIRCUITS_SIZE_2_2; + auto nrCircuits = circuits1.size(); + auto randomIndex = static_cast(rand()) % nrCircuits; + auto x1 = circuits1[randomIndex]; + auto x2 = circuits2[randomIndex]; + for (auto gateType : x1) { + if (gateType == X) { // add CNOT + circuit1.emplace_back(n, groupBeginIndex, + groupBeginIndex + 1, gateType); + } else { + circuit1.emplace_back(n, groupBeginIndex, gateType); + } + } + for (auto gateType : x2) { + if (gateType == X) { // add CNOT + circuit2.emplace_back(n, groupBeginIndex, + groupBeginIndex + 1, gateType); + } else { + circuit2.emplace_back(n, groupBeginIndex, gateType); + } + } +} + +void addDecomposedCcxGate(QuantumComputation& circuit, Qubit control1, Qubit control2, Qubit target) { circuit.h(target); circuit.cx(control1, target); @@ -37,26 +67,23 @@ void addDecomposedCxxGate(QuantumComputation& circuit, Qubit control1, circuit.cx(control2, control1); } -void addRandomStandardOperation(QuantumComputation& circuit, - StandardOperation op, bool decomposeMcx) { +void addStandardOperationToCircuit(QuantumComputation& circuit, + StandardOperation op, bool decomposeCcx) { std::vector controls{}; - for (auto c : op.getControls()) { // they are at most 2 + for (auto c : op.getControls()) { // the controls are at most 2 controls.push_back(static_cast(c.qubit)); } std::vector targets{}; - for (auto t : op.getTargets()) { // they are at most 2 + for (auto t : op.getTargets()) { // the targets are at most 2 targets.push_back(static_cast(t)); } - if (op.getType() == qc::X && controls.size() == 2) { - - if (decomposeMcx) { - addDecomposedCxxGate(circuit, controls[0], controls[1], targets[0]); - return; - } + if (op.getType() == X && controls.size() == 2 && decomposeCcx) { + // decompose toffoli gate + addDecomposedCcxGate(circuit, controls[0], controls[1], targets[0]); + } else { + circuit.emplace_back(op); } - - circuit.emplace_back(op); } std::vector fiveDiffferentRandomNumbers(Qubit min, Qubit max) { @@ -95,6 +122,7 @@ StandardOperation convertToStandardOperation( randomOpType}; } break; + // two targets and one parameter case qc::RXX: case qc::RYY: @@ -156,20 +184,22 @@ StandardOperation convertToStandardOperation( StandardOperation makeRandomStandardOperation(size_t n, Qubit nrQubits, Qubit min) { auto randomNumbers = fiveDiffferentRandomNumbers(min, min + nrQubits); - // choose one of the non-compound operations, but not "None" - auto randomOpType = static_cast(rand() % (Vdg - H) + H); + // choose one of the non-compound operations, but not "None", and also + // not GPhase or I or Barrier + auto randomOpType = static_cast(rand() % (XXplusYY - H) + H); Qubit randomTarget1 = randomNumbers[0]; Qubit randomTarget2{min}; if (randomNumbers.size() > 1) { randomTarget2 = randomNumbers[1]; }; + // choose random controls, but not more than available qubits size_t nrControls = std::min(randomNumbers.size() - 3, static_cast(rand() % 3)); if (randomNumbers.size() < 3) { nrControls = 0; } if (nrControls == 2) { - // otherwise Cnots are almost never generated + // otherwise toffoli gates are almost never generated randomOpType = qc::X; } Controls randomControls{}; @@ -188,40 +218,12 @@ StandardOperation makeRandomStandardOperation(size_t n, Qubit nrQubits, randomParameter2, randomParameter3, randomControls); } -void addPreGeneratedCircuits(QuantumComputation& circuit1, - QuantumComputation& circuit2, size_t n, - Qubit groupBeginIndex, Qubit groupSize) { - - const auto& circuits1 = groupSize == 1 ? PRE_GENERATED_CIRCUITS_SIZE_1_1 - : PRE_GENERATED_CIRCUITS_SIZE_2_1; - const auto& circuits2 = groupSize == 1 ? PRE_GENERATED_CIRCUITS_SIZE_1_2 - : PRE_GENERATED_CIRCUITS_SIZE_2_2; - auto nrCircuits = circuits1.size(); - auto randomIndex = static_cast(rand()) % nrCircuits; - auto x1 = circuits1[randomIndex]; - auto x2 = circuits2[randomIndex]; - for (auto gateType : x1) { - if (isTwoQubitGate(gateType)) { - circuit1.emplace_back(n, groupBeginIndex, - groupBeginIndex + 1, gateType); - } - circuit1.emplace_back(n, groupBeginIndex, gateType); - } - for (auto gateType : x2) { - if (isTwoQubitGate(gateType)) { - circuit2.emplace_back(n, groupBeginIndex, - groupBeginIndex + 1, gateType); - } - circuit2.emplace_back(n, groupBeginIndex, gateType); - } -} - std::pair generateRandomBenchmark(size_t n, Qubit d, Qubit m) { if (d > n) { throw std::runtime_error("The number of data or measured qubits can't be " "bigger than the total number of qubits. n = " + - std::to_string(n) + ";d = " + std::to_string(d) + + std::to_string(n) + "; d = " + std::to_string(d) + "; m = " + std::to_string(m)); } qc::QuantumComputation circuit1{n}; @@ -231,23 +233,26 @@ generateRandomBenchmark(size_t n, Qubit d, Qubit m) { circuit1.h(i); circuit2.h(i); } + for (Qubit i = 0U; i < static_cast(n); i++) { circuit1.barrier(i); circuit2.barrier(i); } // 2) Totally equivalent subcircuits - // generate a random subcircuit with d qubits and 3d gates to apply - // on both circuits, but all the Toffoli gates in C2 are decomposed + // generate a random subcircuit with d qubits and 3*d gates to apply + // on both circuits, but all the Toffoli gates in circuit2 are decomposed for (Qubit i = 0U; i < 3 * d; i++) { auto op = makeRandomStandardOperation(n, d, 0); - addRandomStandardOperation(circuit1, op, false); - addRandomStandardOperation(circuit2, op, true); + addStandardOperationToCircuit(circuit1, op, false); + addStandardOperationToCircuit(circuit2, op, true); } + for (Qubit i = 0U; i < static_cast(n); i++) { circuit1.barrier(i); circuit2.barrier(i); } + // 3) Partially equivalent subcircuits // divide data qubits into groups of size 1 or 2 @@ -262,6 +267,7 @@ generateRandomBenchmark(size_t n, Qubit d, Qubit m) { groupBeginIndex += groupSize; } + for (Qubit i = 0U; i < static_cast(n); i++) { circuit1.barrier(i); circuit2.barrier(i); @@ -272,17 +278,19 @@ generateRandomBenchmark(size_t n, Qubit d, Qubit m) { Qubit notMQubits = d - m; for (Qubit i = 0U; i < notMQubits; i++) { auto op = makeRandomStandardOperation(n, notMQubits, m); - addRandomStandardOperation(circuit1, op, false); + addStandardOperationToCircuit(circuit1, op, false); } for (Qubit i = 0U; i < notMQubits; i++) { auto op = makeRandomStandardOperation(n, notMQubits, m); - addRandomStandardOperation(circuit2, op, false); + addStandardOperationToCircuit(circuit2, op, false); } } + for (Qubit i = 0U; i < static_cast(n); i++) { circuit1.barrier(i); circuit2.barrier(i); } + // 5) CNOT gates (if there are ancilla qubits) Qubit currentDataQubit = 0; for (Qubit currentAncillaQubit = d; @@ -297,6 +305,7 @@ generateRandomBenchmark(size_t n, Qubit d, Qubit m) { circuit1.setLogicalQubitAncillary(i); circuit2.setLogicalQubitAncillary(i); } + for (Qubit i = m; i < static_cast(n); i++) { circuit1.setLogicalQubitGarbage(i); circuit2.setLogicalQubitGarbage(i); @@ -304,4 +313,5 @@ generateRandomBenchmark(size_t n, Qubit d, Qubit m) { return std::make_pair(circuit1, circuit2); } + } // namespace dd diff --git a/test/dd/test_package.cpp b/test/dd/test_package.cpp index b80bae9ca..da56ef1c1 100644 --- a/test/dd/test_package.cpp +++ b/test/dd/test_package.cpp @@ -2570,18 +2570,25 @@ TEST(DDPackageTest, DDMPECSliQECPeriodFinding8Qubits) { EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); } -TEST(DDPackageTest, DDMPECBenchmark) { - auto dd = std::make_unique>(20); - srand(55); - size_t minN = 2; - size_t maxN = 8; - size_t reps = 15; - std::cout << "Partial equivalence check\n"; - for (size_t k = 0; k < reps; k++) { - for (size_t n = minN; n < maxN; n++) { - dd::Qubit d = - static_cast(static_cast(rand()) % (n - 1)) + 1; - dd::Qubit m = static_cast(rand()) % d; +void partialEquivalencCheckingBenchmarks( + std::unique_ptr>& dd, size_t minN, + size_t maxN, size_t reps, bool addAncilla) { + for (size_t n = minN; n < maxN; n++) { + std::chrono::microseconds totalTime{0}; + std::uint16_t totalGates{0}; + for (size_t k = 0; k < reps; k++) { + dd::Qubit d{0}; + if (addAncilla) { + d = static_cast(rand()) % static_cast(n - 1) + 1; + } else { + d = static_cast(n); + } + dd::Qubit m{0}; + if (d == 1) { + m = 1; + } else { + m = static_cast(rand()) % static_cast(d - 1) + 1; + } auto [c1, c2] = dd::generateRandomBenchmark(n, d, m); auto start = std::chrono::high_resolution_clock::now(); @@ -2593,40 +2600,63 @@ TEST(DDPackageTest, DDMPECBenchmark) { EXPECT_TRUE(result); - std::cout << "\nnumber of qubits = " << n << "; data qubits = " << d - << "; measured qubits = " << m - << "; number of gates = " << c2.size() << "\n"; - std::cout << "time: " << static_cast(duration.count()) / 1000000. - << " seconds\n"; + // std::cout << "\nnumber of qubits = " << n << "; data qubits = " << d + // << "; measured qubits = " << m + // << "; number of gates = " << c2.size() << "\n"; + // std::cout << "time: " << static_cast(duration.count()) / + // 1000000. + // << " seconds\n"; + totalTime += duration; + totalGates += static_cast(c2.size()); } + std::cout << "\nnumber of qubits = " << n << "; number of reps = " << reps + << "; average time = " + << (static_cast(totalTime.count()) / + static_cast(reps) / 1000000.) + << " seconds; average time = " + << (static_cast(totalGates) / static_cast(reps)) + << " seconds\n"; } } -TEST(DDPackageTest, DDMZAPECBenchmark) { +TEST(DDPackageTest, DDMPECBenchmark) { auto dd = std::make_unique>(20); + srand(55); + size_t minN = 2; + size_t maxN = 15; + size_t reps = 10; + std::cout << "Partial equivalence check\n"; + partialEquivalencCheckingBenchmarks(dd, minN, maxN, reps, true); +} + +TEST(DDPackageTest, DDMZAPECBenchmark) { + auto dd = std::make_unique>(30); size_t minN = 3; - size_t maxN = 12; - size_t reps = 1; + size_t maxN = 30; + size_t reps = 10; std::cout << "Zero-ancilla partial equivalence check\n"; - for (size_t k = 0; k < reps; k++) { - for (size_t n = minN; n < maxN; n++) { - auto d = static_cast(n); - dd::Qubit m = static_cast(rand()) % d; - auto [c1, c2] = dd::generateRandomBenchmark(n, d, m); - auto start = std::chrono::high_resolution_clock::now(); - bool result = dd::partialEquivalenceCheck(c1, c2, dd); - // Get ending timepoint - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = - std::chrono::duration_cast(stop - start); + partialEquivalencCheckingBenchmarks(dd, minN, maxN, reps, false); +} - EXPECT_TRUE(result); +TEST(DDPackageTest, DDMPartialEquivalenceCheckingFindEqCircuits) { + const auto nqubits = 4U; + auto dd = std::make_unique>(nqubits); - std::cout << "\nnumber of qubits = " << n << "; data qubits = " << d - << "; measured qubits = " << m - << "; number of gates = " << c2.size() << "\n"; - std::cout << "time: " << static_cast(duration.count()) / 1000000. - << " seconds\n"; - } - } + qc::QuantumComputation c1{2, 1}; + c1.cx(0, 1); + + qc::QuantumComputation c2{2, 1}; + c2.z(1); + c2.cx(0, 1); + + EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); + + qc::QuantumComputation c3{2, 1}; + c3.cx(0, 1); + + qc::QuantumComputation c4{2, 1}; + c4.cx(0, 1); + c4.z(1); + + EXPECT_TRUE(dd::partialEquivalenceCheck(c3, c4, dd)); } From 37b5f69c556184003ca1d0e4a7c6abc2e0ec96ae Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Mon, 29 Jan 2024 14:46:41 +0100 Subject: [PATCH 8/9] :bug: fix printed message --- test/dd/test_package.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/dd/test_package.cpp b/test/dd/test_package.cpp index da56ef1c1..9bf79199a 100644 --- a/test/dd/test_package.cpp +++ b/test/dd/test_package.cpp @@ -2613,9 +2613,9 @@ void partialEquivalencCheckingBenchmarks( << "; average time = " << (static_cast(totalTime.count()) / static_cast(reps) / 1000000.) - << " seconds; average time = " + << " seconds; average number of gates = " << (static_cast(totalGates) / static_cast(reps)) - << " seconds\n"; + << "\n"; } } @@ -2623,7 +2623,7 @@ TEST(DDPackageTest, DDMPECBenchmark) { auto dd = std::make_unique>(20); srand(55); size_t minN = 2; - size_t maxN = 15; + size_t maxN = 8; size_t reps = 10; std::cout << "Partial equivalence check\n"; partialEquivalencCheckingBenchmarks(dd, minN, maxN, reps, true); @@ -2632,7 +2632,7 @@ TEST(DDPackageTest, DDMPECBenchmark) { TEST(DDPackageTest, DDMZAPECBenchmark) { auto dd = std::make_unique>(30); size_t minN = 3; - size_t maxN = 30; + size_t maxN = 15; size_t reps = 10; std::cout << "Zero-ancilla partial equivalence check\n"; partialEquivalencCheckingBenchmarks(dd, minN, maxN, reps, false); From 72b9b2b060fed98e1e64a5176ddc0e0eb8daf296 Mon Sep 17 00:00:00 2001 From: Rebecca Ghidini Date: Mon, 29 Jan 2024 15:09:54 +0100 Subject: [PATCH 9/9] =?UTF-8?q?=F0=9F=9A=9A=20move=20verification=20file?= =?UTF-8?q?=20to=20algorithms/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/{dd => algorithms}/Verification.hpp | 0 src/CMakeLists.txt | 1 + src/{dd => algorithms}/Verification.cpp | 2 +- src/dd/CMakeLists.txt | 1 - test/CMakeLists.txt | 1 + test/algorithms/test_partial_equivalence.cpp | 209 +++++++++++++++++ test/dd/test_package.cpp | 224 ------------------- 7 files changed, 212 insertions(+), 226 deletions(-) rename include/{dd => algorithms}/Verification.hpp (100%) rename src/{dd => algorithms}/Verification.cpp (99%) create mode 100644 test/algorithms/test_partial_equivalence.cpp diff --git a/include/dd/Verification.hpp b/include/algorithms/Verification.hpp similarity index 100% rename from include/dd/Verification.hpp rename to include/algorithms/Verification.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 96e892143..91a9af3ee 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -91,6 +91,7 @@ if(NOT TARGET ${MQT_CORE_TARGET_NAME}) algorithms/QFT.cpp algorithms/QPE.cpp algorithms/RandomCliffordCircuit.cpp + algorithms/Verification.cpp algorithms/WState.cpp CircuitOptimizer.cpp operations/ClassicControlledOperation.cpp diff --git a/src/dd/Verification.cpp b/src/algorithms/Verification.cpp similarity index 99% rename from src/dd/Verification.cpp rename to src/algorithms/Verification.cpp index 401f77ed7..9e2a85517 100644 --- a/src/dd/Verification.cpp +++ b/src/algorithms/Verification.cpp @@ -1,4 +1,4 @@ -#include "dd/Verification.hpp" +#include "algorithms/Verification.hpp" #include "QuantumComputation.hpp" #include "operations/OpType.hpp" diff --git a/src/dd/CMakeLists.txt b/src/dd/CMakeLists.txt index 62809b93e..618fb124d 100644 --- a/src/dd/CMakeLists.txt +++ b/src/dd/CMakeLists.txt @@ -19,7 +19,6 @@ if(NOT TARGET ${MQT_CORE_TARGET_NAME}-dd) RealNumber.cpp RealNumberUniqueTable.cpp Simulation.cpp - Verification.cpp statistics/MemoryManagerStatistics.cpp statistics/Statistics.cpp statistics/TableStatistics.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8e358d581..2aeb5328b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -16,6 +16,7 @@ package_add_test( algorithms/eval_dynamic_circuits.cpp algorithms/test_qft.cpp algorithms/test_grover.cpp + algorithms/test_partial_equivalence.cpp algorithms/test_bernsteinvazirani.cpp algorithms/test_entanglement.cpp algorithms/test_grcs.cpp diff --git a/test/algorithms/test_partial_equivalence.cpp b/test/algorithms/test_partial_equivalence.cpp new file mode 100644 index 000000000..a01567a66 --- /dev/null +++ b/test/algorithms/test_partial_equivalence.cpp @@ -0,0 +1,209 @@ +#include "algorithms/Verification.hpp" +#include "dd/Benchmark.hpp" +#include "dd/Package.hpp" + +#include "gtest/gtest.h" +#include +#include + +using namespace qc::literals; + +TEST(PartialEquivalence, + DDMPartialEquivalenceCheckingExamplePaperDifferentQubitOrder) { + const auto nqubits = 3U; + auto dd = std::make_unique>(nqubits); + + qc::QuantumComputation c1{3, 1}; + c1.cswap(1, 2, 0); + c1.h(2); + c1.z(0); + c1.cswap(1, 2, 0); + + qc::QuantumComputation c2{3, 1}; + c2.x(1); + c2.ch(1, 2); + + c1.setLogicalQubitGarbage(1); + c1.setLogicalQubitGarbage(0); + + c2.setLogicalQubitGarbage(1); + c2.setLogicalQubitGarbage(0); + EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); +} + +TEST(PartialEquivalence, + DDMPartialEquivalenceCheckingExamplePaperDifferentQubitOrderAndNumber) { + const auto nqubits = 4U; + auto dd = std::make_unique>(nqubits); + + qc::QuantumComputation c1{4, 1}; + c1.cswap(1, 2, 0); + c1.h(2); + c1.z(0); + c1.cswap(1, 2, 0); + + qc::QuantumComputation c2{3, 1}; + c2.x(1); + c2.ch(1, 2); + + c1.setLogicalQubitGarbage(1); + c1.setLogicalQubitGarbage(0); + c1.setLogicalQubitGarbage(3); + c1.setLogicalQubitAncillary(3); + + c2.setLogicalQubitGarbage(1); + c2.setLogicalQubitGarbage(0); + EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); + EXPECT_TRUE(dd::partialEquivalenceCheck(c2, c1, dd)); +} + +TEST(PartialEquivalence, DDMZAPECMQTBenchQPE30Qubits) { + // zero ancilla partial equivalence test + auto dd = std::make_unique>(31); + + // 30 qubits + const qc::QuantumComputation c1{ + "./circuits/qpeexact_nativegates_ibm_qiskit_opt0_30.qasm"}; + const qc::QuantumComputation c2{"./circuits/qpeexact_indep_qiskit_30.qasm"}; + // calls zeroAncillaePartialEquivalenceCheck + // buildFunctionality is already very very slow... + // EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); + EXPECT_TRUE(true); +} + +TEST(PartialEquivalence, DDMZAPECSliQEC19Qubits) { + auto dd = std::make_unique>(20); + + // full equivalence, 10 qubits + const qc::QuantumComputation c1{"./circuits/entanglement_1.qasm"}; + const qc::QuantumComputation c2{"./circuits/entanglement_2.qasm"}; + + // calls zeroAncillaePartialEquivalenceCheck + EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); + + // full equivalence, 19 qubits + const qc::QuantumComputation c3{"./circuits/add6_196_1.qasm"}; + const qc::QuantumComputation c4{"./circuits/add6_196_2.qasm"}; + + // calls zeroAncillaePartialEquivalenceCheck + EXPECT_TRUE(dd::partialEquivalenceCheck(c3, c4, dd)); + + // full equivalence, 10 qubits + const qc::QuantumComputation c5{"./circuits/bv_1.qasm"}; + const qc::QuantumComputation c6{"./circuits/bv_2.qasm"}; + + // calls zeroAncillaePartialEquivalenceCheck + EXPECT_TRUE(dd::partialEquivalenceCheck(c5, c6, dd)); +} + +TEST(PartialEquivalence, DDMZAPECSliQECRandomCircuit) { + // doesn't terminate + auto dd = std::make_unique>(20); + // full equivalence, 10 qubits + const qc::QuantumComputation c1{"./circuits/random_1.qasm"}; + const qc::QuantumComputation c2{"./circuits/random_2.qasm"}; + + // calls buildFunctionality for c2^-1 concatenated with c1 + EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); +} + +TEST(PartialEquivalence, DDMPECSliQECPeriodFinding8Qubits) { + auto dd = std::make_unique>(20); + // 8 qubits, 3 data qubits + qc::QuantumComputation c1{"./circuits/period_finding_1.qasm"}; + // 8 qubits, 3 data qubits + qc::QuantumComputation c2{"./circuits/period_finding_2.qasm"}; + + // 3 measured qubits and 3 data qubits + + c2.setLogicalQubitAncillary(7); + c2.setLogicalQubitGarbage(7); + c2.setLogicalQubitAncillary(6); + c2.setLogicalQubitGarbage(6); + c2.setLogicalQubitAncillary(5); + c2.setLogicalQubitGarbage(5); + c2.setLogicalQubitAncillary(3); + c2.setLogicalQubitGarbage(3); + c2.setLogicalQubitAncillary(4); + c2.setLogicalQubitGarbage(4); + + c1.setLogicalQubitAncillary(7); + c1.setLogicalQubitGarbage(7); + c1.setLogicalQubitAncillary(6); + c1.setLogicalQubitGarbage(6); + c1.setLogicalQubitAncillary(5); + c1.setLogicalQubitGarbage(5); + c1.setLogicalQubitAncillary(3); + c1.setLogicalQubitGarbage(3); + c1.setLogicalQubitAncillary(4); + c1.setLogicalQubitGarbage(4); + EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); +} + +void partialEquivalencCheckingBenchmarks( + std::unique_ptr>& dd, size_t minN, + size_t maxN, size_t reps, bool addAncilla) { + for (size_t n = minN; n < maxN; n++) { + std::chrono::microseconds totalTime{0}; + std::uint16_t totalGates{0}; + for (size_t k = 0; k < reps; k++) { + dd::Qubit d{0}; + if (addAncilla) { + d = static_cast(rand()) % static_cast(n - 1) + 1; + } else { + d = static_cast(n); + } + dd::Qubit m{0}; + if (d == 1) { + m = 1; + } else { + m = static_cast(rand()) % static_cast(d - 1) + 1; + } + auto [c1, c2] = dd::generateRandomBenchmark(n, d, m); + + auto start = std::chrono::high_resolution_clock::now(); + bool result = dd::partialEquivalenceCheck(c1, c2, dd); + // Get ending timepoint + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = + std::chrono::duration_cast(stop - start); + + EXPECT_TRUE(result); + + // std::cout << "\nnumber of qubits = " << n << "; data qubits = " << d + // << "; measured qubits = " << m + // << "; number of gates = " << c2.size() << "\n"; + // std::cout << "time: " << static_cast(duration.count()) / + // 1000000. + // << " seconds\n"; + totalTime += duration; + totalGates += static_cast(c2.size()); + } + std::cout << "\nnumber of qubits = " << n << "; number of reps = " << reps + << "; average time = " + << (static_cast(totalTime.count()) / + static_cast(reps) / 1000000.) + << " seconds; average number of gates = " + << (static_cast(totalGates) / static_cast(reps)) + << "\n"; + } +} + +TEST(PartialEquivalence, DDMPECBenchmark) { + auto dd = std::make_unique>(20); + srand(55); + size_t minN = 2; + size_t maxN = 8; + size_t reps = 10; + std::cout << "Partial equivalence check\n"; + partialEquivalencCheckingBenchmarks(dd, minN, maxN, reps, true); +} + +TEST(PartialEquivalence, DDMZAPECBenchmark) { + auto dd = std::make_unique>(30); + size_t minN = 3; + size_t maxN = 15; + size_t reps = 10; + std::cout << "Zero-ancilla partial equivalence check\n"; + partialEquivalencCheckingBenchmarks(dd, minN, maxN, reps, false); +} diff --git a/test/dd/test_package.cpp b/test/dd/test_package.cpp index 9bf79199a..8bc244f67 100644 --- a/test/dd/test_package.cpp +++ b/test/dd/test_package.cpp @@ -5,7 +5,6 @@ #include "dd/FunctionalityConstruction.hpp" #include "dd/GateMatrixDefinitions.hpp" #include "dd/Package.hpp" -#include "dd/Verification.hpp" #include "dd/statistics/PackageStatistics.hpp" #include "operations/Control.hpp" #include "operations/OpType.hpp" @@ -2331,29 +2330,6 @@ TEST(DDPackageTest, DDMPartialEquivalenceCheckingExamplePaperZeroAncillae) { EXPECT_FALSE(dd->zeroAncillaePartialEquivalenceCheck(c3, c4, 1)); } -TEST(DDPackageTest, - DDMPartialEquivalenceCheckingExamplePaperDifferentQubitOrder) { - const auto nqubits = 3U; - auto dd = std::make_unique>(nqubits); - - qc::QuantumComputation c1{3, 1}; - c1.cswap(1, 2, 0); - c1.h(2); - c1.z(0); - c1.cswap(1, 2, 0); - - qc::QuantumComputation c2{3, 1}; - c2.x(1); - c2.ch(1, 2); - - c1.setLogicalQubitGarbage(1); - c1.setLogicalQubitGarbage(0); - - c2.setLogicalQubitGarbage(1); - c2.setLogicalQubitGarbage(0); - EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); -} - TEST(DDPackageTest, DDMPartialEquivalenceWithDifferentNumberOfQubits) { auto dd = std::make_unique>(3); auto controlledSwapGate = dd->makeSWAPDD(3, qc::Controls{1}, 0, 2); @@ -2379,32 +2355,6 @@ TEST(DDPackageTest, DDMPartialEquivalenceWithDifferentNumberOfQubits) { dd->partialEquivalenceCheck(dd::mEdge::one(), dd::mEdge::one(), 0, 0)); } -TEST(DDPackageTest, - DDMPartialEquivalenceCheckingExamplePaperDifferentQubitOrderAndNumber) { - const auto nqubits = 4U; - auto dd = std::make_unique>(nqubits); - - qc::QuantumComputation c1{4, 1}; - c1.cswap(1, 2, 0); - c1.h(2); - c1.z(0); - c1.cswap(1, 2, 0); - - qc::QuantumComputation c2{3, 1}; - c2.x(1); - c2.ch(1, 2); - - c1.setLogicalQubitGarbage(1); - c1.setLogicalQubitGarbage(0); - c1.setLogicalQubitGarbage(3); - c1.setLogicalQubitAncillary(3); - - c2.setLogicalQubitGarbage(1); - c2.setLogicalQubitGarbage(0); - EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); - EXPECT_TRUE(dd::partialEquivalenceCheck(c2, c1, dd)); -} - TEST(DDPackageTest, DDMPartialEquivalenceCheckingComputeTable) { const auto nqubits = 3U; auto dd = std::make_unique>(nqubits); @@ -2454,56 +2404,6 @@ TEST(DDPackageTest, DDMPECMQTBenchGrover7Qubits) { buildFunctionality(&c2, *dd, false, false), 7, 7)); } -TEST(DDPackageTest, DDMZAPECMQTBenchQPE30Qubits) { - // zero ancilla partial equivalence test - auto dd = std::make_unique>(31); - - // 30 qubits - const qc::QuantumComputation c1{ - "./circuits/qpeexact_nativegates_ibm_qiskit_opt0_30.qasm"}; - const qc::QuantumComputation c2{"./circuits/qpeexact_indep_qiskit_30.qasm"}; - // calls zeroAncillaePartialEquivalenceCheck - // buildFunctionality is already very very slow... - // EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); - EXPECT_TRUE(true); -} - -TEST(DDPackageTest, DDMZAPECSliQEC19Qubits) { - auto dd = std::make_unique>(20); - - // full equivalence, 10 qubits - const qc::QuantumComputation c1{"./circuits/entanglement_1.qasm"}; - const qc::QuantumComputation c2{"./circuits/entanglement_2.qasm"}; - - // calls zeroAncillaePartialEquivalenceCheck - EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); - - // full equivalence, 19 qubits - const qc::QuantumComputation c3{"./circuits/add6_196_1.qasm"}; - const qc::QuantumComputation c4{"./circuits/add6_196_2.qasm"}; - - // calls zeroAncillaePartialEquivalenceCheck - EXPECT_TRUE(dd::partialEquivalenceCheck(c3, c4, dd)); - - // full equivalence, 10 qubits - const qc::QuantumComputation c5{"./circuits/bv_1.qasm"}; - const qc::QuantumComputation c6{"./circuits/bv_2.qasm"}; - - // calls zeroAncillaePartialEquivalenceCheck - EXPECT_TRUE(dd::partialEquivalenceCheck(c5, c6, dd)); -} - -TEST(DDPackageTest, DDMZAPECSliQECRandomCircuit) { - // doesn't terminate - auto dd = std::make_unique>(20); - // full equivalence, 10 qubits - const qc::QuantumComputation c1{"./circuits/random_1.qasm"}; - const qc::QuantumComputation c2{"./circuits/random_2.qasm"}; - - // calls buildFunctionality for c2^-1 concatenated with c1 - EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); -} - TEST(DDPackageTest, DDMPECSliQECGrover22Qubits) { // doesn't terminate auto dd = std::make_unique>(22); @@ -2536,127 +2436,3 @@ TEST(DDPackageTest, DDMPECSliQECAdd19Qubits) { // doesn't add ancillary qubits -> total number of qubits is 19 EXPECT_TRUE(dd->partialEquivalenceCheck(c1Dd, c2Dd, 8, 8)); } - -TEST(DDPackageTest, DDMPECSliQECPeriodFinding8Qubits) { - auto dd = std::make_unique>(20); - // 8 qubits, 3 data qubits - qc::QuantumComputation c1{"./circuits/period_finding_1.qasm"}; - // 8 qubits, 3 data qubits - qc::QuantumComputation c2{"./circuits/period_finding_2.qasm"}; - - // 3 measured qubits and 3 data qubits - - c2.setLogicalQubitAncillary(7); - c2.setLogicalQubitGarbage(7); - c2.setLogicalQubitAncillary(6); - c2.setLogicalQubitGarbage(6); - c2.setLogicalQubitAncillary(5); - c2.setLogicalQubitGarbage(5); - c2.setLogicalQubitAncillary(3); - c2.setLogicalQubitGarbage(3); - c2.setLogicalQubitAncillary(4); - c2.setLogicalQubitGarbage(4); - - c1.setLogicalQubitAncillary(7); - c1.setLogicalQubitGarbage(7); - c1.setLogicalQubitAncillary(6); - c1.setLogicalQubitGarbage(6); - c1.setLogicalQubitAncillary(5); - c1.setLogicalQubitGarbage(5); - c1.setLogicalQubitAncillary(3); - c1.setLogicalQubitGarbage(3); - c1.setLogicalQubitAncillary(4); - c1.setLogicalQubitGarbage(4); - EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); -} - -void partialEquivalencCheckingBenchmarks( - std::unique_ptr>& dd, size_t minN, - size_t maxN, size_t reps, bool addAncilla) { - for (size_t n = minN; n < maxN; n++) { - std::chrono::microseconds totalTime{0}; - std::uint16_t totalGates{0}; - for (size_t k = 0; k < reps; k++) { - dd::Qubit d{0}; - if (addAncilla) { - d = static_cast(rand()) % static_cast(n - 1) + 1; - } else { - d = static_cast(n); - } - dd::Qubit m{0}; - if (d == 1) { - m = 1; - } else { - m = static_cast(rand()) % static_cast(d - 1) + 1; - } - auto [c1, c2] = dd::generateRandomBenchmark(n, d, m); - - auto start = std::chrono::high_resolution_clock::now(); - bool result = dd::partialEquivalenceCheck(c1, c2, dd); - // Get ending timepoint - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = - std::chrono::duration_cast(stop - start); - - EXPECT_TRUE(result); - - // std::cout << "\nnumber of qubits = " << n << "; data qubits = " << d - // << "; measured qubits = " << m - // << "; number of gates = " << c2.size() << "\n"; - // std::cout << "time: " << static_cast(duration.count()) / - // 1000000. - // << " seconds\n"; - totalTime += duration; - totalGates += static_cast(c2.size()); - } - std::cout << "\nnumber of qubits = " << n << "; number of reps = " << reps - << "; average time = " - << (static_cast(totalTime.count()) / - static_cast(reps) / 1000000.) - << " seconds; average number of gates = " - << (static_cast(totalGates) / static_cast(reps)) - << "\n"; - } -} - -TEST(DDPackageTest, DDMPECBenchmark) { - auto dd = std::make_unique>(20); - srand(55); - size_t minN = 2; - size_t maxN = 8; - size_t reps = 10; - std::cout << "Partial equivalence check\n"; - partialEquivalencCheckingBenchmarks(dd, minN, maxN, reps, true); -} - -TEST(DDPackageTest, DDMZAPECBenchmark) { - auto dd = std::make_unique>(30); - size_t minN = 3; - size_t maxN = 15; - size_t reps = 10; - std::cout << "Zero-ancilla partial equivalence check\n"; - partialEquivalencCheckingBenchmarks(dd, minN, maxN, reps, false); -} - -TEST(DDPackageTest, DDMPartialEquivalenceCheckingFindEqCircuits) { - const auto nqubits = 4U; - auto dd = std::make_unique>(nqubits); - - qc::QuantumComputation c1{2, 1}; - c1.cx(0, 1); - - qc::QuantumComputation c2{2, 1}; - c2.z(1); - c2.cx(0, 1); - - EXPECT_TRUE(dd::partialEquivalenceCheck(c1, c2, dd)); - - qc::QuantumComputation c3{2, 1}; - c3.cx(0, 1); - - qc::QuantumComputation c4{2, 1}; - c4.cx(0, 1); - c4.z(1); - - EXPECT_TRUE(dd::partialEquivalenceCheck(c3, c4, dd)); -}