From 7b8344f91201e3f83e13a55bae266b5ff3ddda90 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sat, 15 Jul 2023 00:30:15 -0700 Subject: [PATCH 01/14] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20fix=20a=20typo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/Export.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/dd/Export.hpp b/include/dd/Export.hpp index 9dc681422..f08294278 100644 --- a/include/dd/Export.hpp +++ b/include/dd/Export.hpp @@ -695,7 +695,7 @@ static void toDot(const Edge& e, std::ostream& os, bool colored = true, modernNode(*node, oss, formatAsPolar); } - // iterate over edges in reverse to guarantee correct proceossing order + // iterate over edges in reverse to guarantee correct processing order for (auto i = static_cast(node->p->e.size() - 1); i >= 0; --i) { auto& edge = node->p->e[static_cast(i)]; From f76bba596125c5a1e941b729d527267373af029d Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sat, 15 Jul 2023 00:44:17 -0700 Subject: [PATCH 02/14] =?UTF-8?q?=F0=9F=9A=A8=20small=20linter=20and=20for?= =?UTF-8?q?mat=20improvements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/Node.hpp | 1 - include/dd/NoiseFunctionality.hpp | 152 +++++----- include/dd/Operations.hpp | 101 ++++--- include/dd/UniqueTable.hpp | 4 +- src/dd/FunctionalityConstruction.cpp | 37 +-- src/dd/Simulation.cpp | 32 +-- test/algorithms/eval_dynamic_circuits.cpp | 64 ++--- test/algorithms/test_bernsteinvazirani.cpp | 316 ++++++++++----------- test/algorithms/test_grcs.cpp | 8 +- test/algorithms/test_grover.cpp | 2 +- test/algorithms/test_qft.cpp | 4 +- test/algorithms/test_qpe.cpp | 34 +-- test/algorithms/test_random_clifford.cpp | 7 +- 13 files changed, 378 insertions(+), 384 deletions(-) diff --git a/include/dd/Node.hpp b/include/dd/Node.hpp index b3ef465ef..743de2245 100644 --- a/include/dd/Node.hpp +++ b/include/dd/Node.hpp @@ -55,7 +55,6 @@ struct mNode { [[nodiscard]] inline bool isSymmetric() const noexcept { return (flags & static_cast(32U)) != 0; } - inline void setIdentity(const bool identity) noexcept { if (identity) { flags = (flags | static_cast(16U)); diff --git a/include/dd/NoiseFunctionality.hpp b/include/dd/NoiseFunctionality.hpp index 4e1c15173..d814a24a5 100644 --- a/include/dd/NoiseFunctionality.hpp +++ b/include/dd/NoiseFunctionality.hpp @@ -26,7 +26,7 @@ template class StochasticNoiseFunctionality { dd::QubitCount nq, double gateNoiseProbability, double amplitudeDampingProb, double multiQubitGateFactor, - std::vector effects) + std::vector effects) : package(dd), nQubits(nq), dist(0.0, 1.0L), noiseProbability(gateNoiseProbability), noiseProbabilityMulti(gateNoiseProbability * multiQubitGateFactor), @@ -38,17 +38,16 @@ template class StochasticNoiseFunctionality { oneMinusSqrtAmplitudeDampingProbabilityMulti( {std::sqrt(1 - multiQubitGateFactor * amplitudeDampingProb), 0}), ampDampingTrue( - dd::GateMatrix({dd::complex_zero, sqrtAmplitudeDampingProbability, - dd::complex_zero, dd::complex_zero})), - ampDampingTrueMulti(dd::GateMatrix( - {dd::complex_zero, sqrtAmplitudeDampingProbabilityMulti, - dd::complex_zero, dd::complex_zero})), - ampDampingFalse( - dd::GateMatrix({dd::complex_one, dd::complex_zero, dd::complex_zero, - oneMinusSqrtAmplitudeDampingProbability})), + GateMatrix({complex_zero, sqrtAmplitudeDampingProbability, + complex_zero, complex_zero})), + ampDampingTrueMulti( + GateMatrix({complex_zero, sqrtAmplitudeDampingProbabilityMulti, + complex_zero, complex_zero})), + ampDampingFalse(GateMatrix({complex_one, complex_zero, complex_zero, + oneMinusSqrtAmplitudeDampingProbability})), ampDampingFalseMulti( - dd::GateMatrix({dd::complex_one, dd::complex_zero, dd::complex_zero, - oneMinusSqrtAmplitudeDampingProbabilityMulti})), + GateMatrix({complex_one, complex_zero, complex_zero, + oneMinusSqrtAmplitudeDampingProbabilityMulti})), noiseEffects(std::move(effects)), identityDD(package->makeIdent(nQubits)) { package->incRef(identityDD); @@ -64,16 +63,16 @@ template class StochasticNoiseFunctionality { double noiseProbability; double noiseProbabilityMulti; - dd::ComplexValue sqrtAmplitudeDampingProbability; - dd::ComplexValue oneMinusSqrtAmplitudeDampingProbability; - dd::ComplexValue sqrtAmplitudeDampingProbabilityMulti; - dd::ComplexValue oneMinusSqrtAmplitudeDampingProbabilityMulti; - dd::GateMatrix ampDampingTrue{}; - dd::GateMatrix ampDampingTrueMulti{}; - dd::GateMatrix ampDampingFalse{}; - dd::GateMatrix ampDampingFalseMulti{}; - std::vector noiseEffects; - dd::mEdge identityDD; + ComplexValue sqrtAmplitudeDampingProbability; + ComplexValue oneMinusSqrtAmplitudeDampingProbability; + ComplexValue sqrtAmplitudeDampingProbabilityMulti; + ComplexValue oneMinusSqrtAmplitudeDampingProbabilityMulti; + GateMatrix ampDampingTrue{}; + GateMatrix ampDampingTrueMulti{}; + GateMatrix ampDampingFalse{}; + GateMatrix ampDampingFalseMulti{}; + std::vector noiseEffects; + mEdge identityDD; [[nodiscard]] dd::QubitCount getNumberOfQubits() const { return nQubits; } [[nodiscard]] double getNoiseProbability(bool multiQubitNoiseFlag) const { @@ -89,7 +88,7 @@ template class StochasticNoiseFunctionality { return multiQubitNoiseFlag ? qc::MultiAFalse : qc::AFalse; } - [[nodiscard]] dd::GateMatrix + [[nodiscard]] GateMatrix getAmplitudeDampingOperationMatrix(bool multiQubitNoiseFlag, bool amplitudeDampingFlag) const { if (amplitudeDampingFlag) { @@ -99,14 +98,13 @@ template class StochasticNoiseFunctionality { } public: - [[nodiscard]] dd::mEdge getIdentityDD() const { return identityDD; } - void setNoiseEffects(std::vector newNoiseEffects) { + [[nodiscard]] mEdge getIdentityDD() const { return identityDD; } + void setNoiseEffects(std::vector newNoiseEffects) { noiseEffects = std::move(newNoiseEffects); } - void applyNoiseOperation(const std::set& targets, - dd::mEdge operation, dd::vEdge& state, - std::mt19937_64& generator) { + void applyNoiseOperation(const std::set& targets, mEdge operation, + vEdge& state, std::mt19937_64& generator) { const bool multiQubitOperation = targets.size() > 1; for (const auto& target : targets) { @@ -115,7 +113,7 @@ template class StochasticNoiseFunctionality { generator, false, multiQubitOperation); auto tmp = package->multiply(stackedOperation, state); - if (dd::ComplexNumbers::mag2(tmp.w) < dist(generator)) { + if (ComplexNumbers::mag2(tmp.w) < dist(generator)) { // The probability of amplitude damping does not only depend on the // noise probability, but also the quantum state. Due to the // normalization constraint of decision diagrams the probability for @@ -157,7 +155,7 @@ template class StochasticNoiseFunctionality { bool amplitudeDamping, bool multiQubitOperation) { for (const auto& noiseType : noiseEffects) { - const auto effect = noiseType == dd::AmplitudeDamping + const auto effect = noiseType == AmplitudeDamping ? getAmplitudeDampingOperationType( multiQubitOperation, amplitudeDamping) : returnNoiseOperation(noiseType, dist(generator), @@ -204,10 +202,10 @@ template class StochasticNoiseFunctionality { } [[nodiscard]] qc::OpType - returnNoiseOperation(dd::NoiseOperations noiseOperation, double prob, + returnNoiseOperation(NoiseOperations noiseOperation, double prob, bool multiQubitNoiseFlag) const { switch (noiseOperation) { - case dd::NoiseOperations::Depolarization: { + case NoiseOperations::Depolarization: { if (prob >= (getNoiseProbability(multiQubitNoiseFlag) * 0.75)) { // prob > prob apply qc::I, also 25 % of the time when depolarization is // applied nothing happens @@ -227,13 +225,13 @@ template class StochasticNoiseFunctionality { // apply qc::Z return qc::Z; } - case dd::NoiseOperations::PhaseFlip: { + case NoiseOperations::PhaseFlip: { if (prob > getNoiseProbability(multiQubitNoiseFlag)) { return qc::I; } return qc::Z; } - case dd::NoiseOperations::Identity: { + case NoiseOperations::Identity: { return qc::I; } default: @@ -270,16 +268,16 @@ template class DeterministicNoiseFunctionality { double ampDampingProbSingleQubit; double ampDampingProbMultiQubit; - std::vector noiseEffects; + std::vector noiseEffects; bool useDensityMatrixType; bool sequentiallyApplyNoise; - inline static const std::map + inline static const std::map SEQUENTIAL_NOISE_MAP = { - {dd::Identity, 1}, // Identity Noise - {dd::PhaseFlip, 2}, // Phase-flip - {dd::AmplitudeDamping, 2}, // Amplitude Damping - {dd::Depolarization, 4}, // Depolarisation + {Identity, 1}, // Identity Noise + {PhaseFlip, 2}, // Phase-flip + {AmplitudeDamping, 2}, // Amplitude Damping + {Depolarization, 4}, // Depolarisation }; [[nodiscard]] dd::QubitCount getNumberOfQubits() const { return nQubits; } @@ -355,22 +353,22 @@ template class DeterministicNoiseFunctionality { })) { for (auto const& type : noiseEffects) { switch (type) { - case dd::AmplitudeDamping: + case AmplitudeDamping: applyAmplitudeDampingToEdges( newEdges, (usedQubits.size() == 1) ? ampDampingProbSingleQubit : ampDampingProbMultiQubit); break; - case dd::PhaseFlip: + case PhaseFlip: applyPhaseFlipToEdges(newEdges, (usedQubits.size() == 1) ? noiseProbSingleQubit : noiseProbMultiQubit); break; - case dd::Depolarization: + case Depolarization: applyDepolarisationToEdges(newEdges, (usedQubits.size() == 1) ? noiseProbSingleQubit : noiseProbMultiQubit); break; - case dd::Identity: + case Identity: continue; } } @@ -407,7 +405,7 @@ template class DeterministicNoiseFunctionality { } void applyAmplitudeDampingToEdges(ArrayOfEdges& e, double probability) { - dd::Complex complexProb = package->cn.getCached(0., 0.); + Complex complexProb = package->cn.getCached(0., 0.); // e[0] = e[0] + p*e[3] if (!e[3].w.exactlyZero()) { @@ -461,7 +459,7 @@ template class DeterministicNoiseFunctionality { void applyDepolarisationToEdges(ArrayOfEdges& e, double probability) { std::array helperEdge{}; - dd::Complex complexProb = package->cn.getCached(); + Complex complexProb = package->cn.getCached(); complexProb.i->value = 0; qc::DensityMatrixDD oldE0Edge{e[0].p, package->cn.getCached(e[0].w)}; @@ -474,7 +472,7 @@ template class DeterministicNoiseFunctionality { complexProb.r->value = (2 - probability) * 0.5; helperEdge[0].w = package->cn.mulCached(e[0].w, complexProb); } else { - helperEdge[0].w = dd::Complex::zero; + helperEdge[0].w = Complex::zero; } // helperEdge[1] = 0.5*p*e[3] @@ -483,7 +481,7 @@ template class DeterministicNoiseFunctionality { complexProb.r->value = probability * 0.5; helperEdge[1].w = package->cn.mulCached(e[3].w, complexProb); } else { - helperEdge[1].w = dd::Complex::zero; + helperEdge[1].w = Complex::zero; } // e[0] = helperEdge[0] + helperEdge[1] @@ -522,7 +520,7 @@ template class DeterministicNoiseFunctionality { complexProb.r->value = (2 - probability) * 0.5; helperEdge[0].w = package->cn.mulCached(e[3].w, complexProb); } else { - helperEdge[0].w = dd::Complex::zero; + helperEdge[0].w = Complex::zero; } // helperEdge[1] = 0.5*p*e[0] @@ -531,7 +529,7 @@ template class DeterministicNoiseFunctionality { complexProb.r->value = probability * 0.5; helperEdge[1].w = package->cn.mulCached(oldE0Edge.w, complexProb); } else { - helperEdge[1].w = dd::Complex::zero; + helperEdge[1].w = Complex::zero; } package->cn.returnToCache(e[3].w); @@ -560,10 +558,10 @@ template class DeterministicNoiseFunctionality { for (std::size_t m = 0; m < SEQUENTIAL_NOISE_MAP.find(type)->second; m++) { auto tmp0 = package->conjugateTranspose(idleOperation.at(m)); - auto tmp1 = package->multiply( - originalEdge, dd::densityFromMatrixEdge(tmp0), 0, false); + auto tmp1 = package->multiply(originalEdge, + densityFromMatrixEdge(tmp0), 0, false); auto tmp2 = - package->multiply(dd::densityFromMatrixEdge(idleOperation.at(m)), + package->multiply(densityFromMatrixEdge(idleOperation.at(m)), tmp1, 0, useDensityMatrixType); if (tmp.p == nullptr) { tmp = tmp2; @@ -590,43 +588,43 @@ template class DeterministicNoiseFunctionality { idleNoiseGate{}; dd::ComplexValue tmp = {}; - tmp.r = std::sqrt(1 - ((3 * probability) / 4)) * dd::complex_one.r; + tmp.r = std::sqrt(1 - ((3 * probability) / 4)) * complex_one.r; // (1 0) // sqrt(1- ((3p)/4))*(0 1) idleNoiseGate[0][0] = idleNoiseGate[0][3] = tmp; - idleNoiseGate[0][1] = idleNoiseGate[0][2] = dd::complex_zero; + idleNoiseGate[0][1] = idleNoiseGate[0][2] = complex_zero; pointerForMatrices[0] = package->makeGateDD(idleNoiseGate[0], getNumberOfQubits(), target); // (0 1) // sqrt(probability/4))*(1 0) - tmp.r = std::sqrt(probability / 4) * dd::complex_one.r; + tmp.r = std::sqrt(probability / 4) * complex_one.r; idleNoiseGate[1][1] = idleNoiseGate[1][2] = tmp; - idleNoiseGate[1][0] = idleNoiseGate[1][3] = dd::complex_zero; + idleNoiseGate[1][0] = idleNoiseGate[1][3] = complex_zero; pointerForMatrices[1] = package->makeGateDD(idleNoiseGate[1], getNumberOfQubits(), target); // (1 0) // sqrt(probability/4))*(0 -1) - tmp.r = std::sqrt(probability / 4) * dd::complex_one.r; + tmp.r = std::sqrt(probability / 4) * complex_one.r; idleNoiseGate[2][0] = tmp; tmp.r = tmp.r * -1; idleNoiseGate[2][3] = tmp; - idleNoiseGate[2][1] = idleNoiseGate[2][2] = dd::complex_zero; + idleNoiseGate[2][1] = idleNoiseGate[2][2] = complex_zero; pointerForMatrices[3] = package->makeGateDD(idleNoiseGate[2], getNumberOfQubits(), target); // (0 -i) // sqrt(probability/4))*(i 0) - tmp.r = dd::complex_zero.r; + tmp.r = complex_zero.r; tmp.i = std::sqrt(probability / 4) * 1; idleNoiseGate[3][2] = tmp; tmp.i = tmp.i * -1; idleNoiseGate[3][1] = tmp; - idleNoiseGate[3][0] = idleNoiseGate[3][3] = dd::complex_zero; + idleNoiseGate[3][0] = idleNoiseGate[3][3] = complex_zero; pointerForMatrices[2] = package->makeGateDD(idleNoiseGate[3], getNumberOfQubits(), target); @@ -645,9 +643,9 @@ template class DeterministicNoiseFunctionality { // identity noise (for testing) // (1 0) // (0 1), - case dd::Identity: { - idleNoiseGate[0][0] = idleNoiseGate[0][3] = dd::complex_one; - idleNoiseGate[0][1] = idleNoiseGate[0][2] = dd::complex_zero; + case Identity: { + idleNoiseGate[0][0] = idleNoiseGate[0][3] = complex_one; + idleNoiseGate[0][1] = idleNoiseGate[0][2] = complex_zero; pointerForMatrices[0] = package->makeGateDD(idleNoiseGate[0], getNumberOfQubits(), target); @@ -657,15 +655,15 @@ template class DeterministicNoiseFunctionality { // phase flip // (1 0) (1 0) // e0= sqrt(1-probability)*(0 1), e1= sqrt(probability)*(0 -1) - case dd::PhaseFlip: { - tmp.r = std::sqrt(1 - probability) * dd::complex_one.r; + case PhaseFlip: { + tmp.r = std::sqrt(1 - probability) * complex_one.r; idleNoiseGate[0][0] = idleNoiseGate[0][3] = tmp; - idleNoiseGate[0][1] = idleNoiseGate[0][2] = dd::complex_zero; - tmp.r = std::sqrt(probability) * dd::complex_one.r; + idleNoiseGate[0][1] = idleNoiseGate[0][2] = complex_zero; + tmp.r = std::sqrt(probability) * complex_one.r; idleNoiseGate[1][0] = tmp; tmp.r *= -1; idleNoiseGate[1][3] = tmp; - idleNoiseGate[1][1] = idleNoiseGate[1][2] = dd::complex_zero; + idleNoiseGate[1][1] = idleNoiseGate[1][2] = complex_zero; pointerForMatrices[0] = package->makeGateDD(idleNoiseGate[0], getNumberOfQubits(), target); @@ -677,15 +675,15 @@ template class DeterministicNoiseFunctionality { // amplitude damping // (1 0) (0 sqrt(probability)) // e0= (0 sqrt(1-probability), e1= (0 0) - case dd::AmplitudeDamping: { - tmp.r = std::sqrt(1 - probability) * dd::complex_one.r; - idleNoiseGate[0][0] = dd::complex_one; - idleNoiseGate[0][1] = idleNoiseGate[0][2] = dd::complex_zero; + case AmplitudeDamping: { + tmp.r = std::sqrt(1 - probability) * complex_one.r; + idleNoiseGate[0][0] = complex_one; + idleNoiseGate[0][1] = idleNoiseGate[0][2] = complex_zero; idleNoiseGate[0][3] = tmp; - tmp.r = std::sqrt(probability) * dd::complex_one.r; + tmp.r = std::sqrt(probability) * complex_one.r; idleNoiseGate[1][0] = idleNoiseGate[1][3] = idleNoiseGate[1][2] = - dd::complex_zero; + complex_zero; idleNoiseGate[1][1] = tmp; pointerForMatrices[0] = @@ -695,7 +693,7 @@ template class DeterministicNoiseFunctionality { break; } // depolarization - case dd::Depolarization: + case Depolarization: generateDepolarizationGate(pointerForMatrices, target, probability); break; default: @@ -703,9 +701,9 @@ template class DeterministicNoiseFunctionality { } } - double getNoiseProbability(const dd::NoiseOperations type, + double getNoiseProbability(const NoiseOperations type, const std::set& targets) { - if (type == dd::AmplitudeDamping) { + if (type == AmplitudeDamping) { return (targets.size() == 1) ? ampDampingProbSingleQubit : ampDampingProbMultiQubit; } diff --git a/include/dd/Operations.hpp b/include/dd/Operations.hpp index 8a75a5137..241bd07ab 100644 --- a/include/dd/Operations.hpp +++ b/include/dd/Operations.hpp @@ -13,7 +13,7 @@ namespace dd { // single-target Operations template qc::MatrixDD getStandardOperationDD(const qc::StandardOperation* op, - std::unique_ptr>& dd, + std::unique_ptr>& dd, const qc::Controls& controls, dd::Qubit target, bool inverse) { GateMatrix gm; @@ -25,64 +25,63 @@ qc::MatrixDD getStandardOperationDD(const qc::StandardOperation* op, switch (type) { case qc::I: - gm = dd::Imat; + gm = Imat; break; case qc::H: - gm = dd::Hmat; + gm = Hmat; break; - case qc::X: { - gm = dd::Xmat; + case qc::X: + gm = Xmat; break; - } case qc::Y: - gm = dd::Ymat; + gm = Ymat; break; case qc::Z: - gm = dd::Zmat; + gm = Zmat; break; case qc::S: - gm = inverse ? dd::Sdagmat : dd::Smat; + gm = inverse ? Sdagmat : Smat; break; case qc::Sdag: - gm = inverse ? dd::Smat : dd::Sdagmat; + gm = inverse ? Smat : Sdagmat; break; case qc::T: - gm = inverse ? dd::Tdagmat : dd::Tmat; + gm = inverse ? Tdagmat : Tmat; break; case qc::Tdag: - gm = inverse ? dd::Tmat : dd::Tdagmat; + gm = inverse ? Tmat : Tdagmat; break; case qc::V: - gm = inverse ? dd::Vdagmat : dd::Vmat; + gm = inverse ? Vdagmat : Vmat; break; case qc::Vdag: - gm = inverse ? dd::Vmat : dd::Vdagmat; + gm = inverse ? Vmat : Vdagmat; break; case qc::U3: - gm = inverse ? dd::U3mat(-parameter[1U], -parameter[2U], -parameter[0U]) - : dd::U3mat(parameter[2U], parameter[1U], parameter[0U]); + gm = inverse ? U3mat(-parameter[1U], -parameter[2U], -parameter[0U]) + : U3mat(parameter[2U], parameter[1U], parameter[0U]); break; case qc::U2: - gm = inverse ? dd::U2mat(-parameter[0U] + dd::PI, -parameter[1U] - dd::PI) - : dd::U2mat(parameter[1U], parameter[0U]); + gm = inverse ? U2mat(-parameter[0U] + PI, -parameter[1U] - PI) + : U2mat(parameter[1U], parameter[0U]); break; case qc::Phase: - gm = inverse ? dd::Phasemat(-parameter[0U]) : dd::Phasemat(parameter[0U]); + gm = inverse ? Phasemat(-parameter[0U]) : Phasemat(parameter[0U]); break; case qc::SX: - gm = inverse ? dd::SXdagmat : dd::SXmat; + gm = inverse ? SXdagmat : SXmat; break; case qc::SXdag: - gm = inverse ? dd::SXmat : dd::SXdagmat; + gm = inverse ? SXmat : SXdagmat; break; case qc::RX: - gm = inverse ? dd::RXmat(-parameter[0U]) : dd::RXmat(parameter[0U]); + gm = inverse ? RXmat(-parameter[0U]) : RXmat(parameter[0U]); break; case qc::RY: - gm = inverse ? dd::RYmat(-parameter[0U]) : dd::RYmat(parameter[0U]); + gm = inverse ? RYmat(-parameter[0U]) : RYmat(parameter[0U]); break; case qc::RZ: - gm = inverse ? dd::RZmat(-parameter[0U]) : dd::RZmat(parameter[0U]); + gm = inverse ? RZmat(-parameter[0U]) : RZmat(parameter[0U]); break; default: std::ostringstream oss{}; @@ -116,36 +115,36 @@ qc::MatrixDD getStandardOperationDD(const qc::StandardOperation* op, bool definitionFound = true; switch (type) { case qc::SWAP: - gm = dd::SWAPmat; + gm = SWAPmat; break; case qc::iSWAP: - gm = inverse ? dd::iSWAPinvmat : dd::iSWAPmat; + gm = inverse ? iSWAPinvmat : iSWAPmat; break; case qc::DCX: - gm = dd::DCXmat; + gm = DCXmat; break; case qc::ECR: - gm = dd::ECRmat; + gm = ECRmat; break; case qc::RXX: - gm = inverse ? dd::RXXmat(-parameter[0U]) : dd::RXXmat(parameter[0U]); + gm = inverse ? RXXmat(-parameter[0U]) : RXXmat(parameter[0U]); break; case qc::RYY: - gm = inverse ? dd::RYYmat(-parameter[0U]) : dd::RYYmat(parameter[0U]); + gm = inverse ? RYYmat(-parameter[0U]) : RYYmat(parameter[0U]); break; case qc::RZZ: - gm = inverse ? dd::RZZmat(-parameter[0U]) : dd::RZZmat(parameter[0U]); + gm = inverse ? RZZmat(-parameter[0U]) : RZZmat(parameter[0U]); break; case qc::RZX: - gm = inverse ? dd::RZXmat(-parameter[0U]) : dd::RZXmat(parameter[0U]); + gm = inverse ? RZXmat(-parameter[0U]) : RZXmat(parameter[0U]); break; case qc::XXminusYY: - gm = inverse ? dd::XXMinusYYmat(-parameter[0U], parameter[1U]) - : dd::XXMinusYYmat(parameter[0U], parameter[1U]); + gm = inverse ? XXMinusYYmat(-parameter[0U], parameter[1U]) + : XXMinusYYmat(parameter[0U], parameter[1U]); break; case qc::XXplusYY: - gm = inverse ? dd::XXPlusYYmat(-parameter[0U], parameter[1U]) - : dd::XXPlusYYmat(parameter[0U], parameter[1U]); + gm = inverse ? XXPlusYYmat(-parameter[0U], parameter[1U]) + : XXPlusYYmat(parameter[0U], parameter[1U]); break; default: definitionFound = false; @@ -243,22 +242,22 @@ qc::MatrixDD getStandardOperationDD(const qc::StandardOperation* op, template qc::MatrixDD getDD(const qc::Operation* op, - std::unique_ptr>& dd, - qc::Permutation& permutation, bool inverse = false) { + std::unique_ptr>& dd, + qc::Permutation& permutation, const bool inverse = false) { const auto type = op->getType(); const auto nqubits = op->getNqubits(); // check whether the operation can be handled by the underlying DD package - if (nqubits > dd::Package::MAX_POSSIBLE_QUBITS) { + if (nqubits > Package::MAX_POSSIBLE_QUBITS) { throw qc::QFRException( "Requested too many qubits to be handled by the DD package. Qubit " "datatype only allows up to " + - std::to_string(dd::Package::MAX_POSSIBLE_QUBITS) + + std::to_string(Package::MAX_POSSIBLE_QUBITS) + " qubits, while " + std::to_string(nqubits) + " were requested. If you want to use more than " + - std::to_string(dd::Package::MAX_POSSIBLE_QUBITS) + + std::to_string(Package::MAX_POSSIBLE_QUBITS) + " qubits, you have to recompile the package with a wider Qubit type in " - "`export/dd_package/include/dd/Definitions.hpp!`"); + "`include/dd/DDDefinitions.hpp!`"); } // if a permutation is provided and the current operation is a SWAP, this @@ -266,8 +265,8 @@ qc::MatrixDD getDD(const qc::Operation* op, if (!permutation.empty() && type == qc::SWAP && !op->isControlled()) { const auto& targets = op->getTargets(); - const auto target0 = targets.at(0U); - const auto target1 = targets.at(1U); + const auto target0 = targets[0U]; + const auto target1 = targets[1U]; // update permutation std::swap(permutation.at(target0), permutation.at(target1)); return dd->makeIdent(static_cast(nqubits)); @@ -333,21 +332,21 @@ qc::MatrixDD getDD(const qc::Operation* op, template qc::MatrixDD getDD(const qc::Operation* op, - std::unique_ptr>& dd, - bool inverse = false) { + std::unique_ptr>& dd, + const bool inverse = false) { qc::Permutation perm{}; return getDD(op, dd, perm, inverse); } template qc::MatrixDD getInverseDD(const qc::Operation* op, - std::unique_ptr>& dd) { + std::unique_ptr>& dd) { return getDD(op, dd, true); } template qc::MatrixDD getInverseDD(const qc::Operation* op, - std::unique_ptr>& dd, + std::unique_ptr>& dd, qc::Permutation& permutation) { return getDD(op, dd, permutation, true); } @@ -355,15 +354,15 @@ qc::MatrixDD getInverseDD(const qc::Operation* op, template void dumpTensor(qc::Operation* op, std::ostream& of, std::vector& inds, std::size_t& gateIdx, - std::unique_ptr>& dd); + std::unique_ptr>& dd); // apply swaps 'on' DD in order to change 'from' to 'to' // where |from| >= |to| template void changePermutation(DDType& on, qc::Permutation& from, const qc::Permutation& to, - std::unique_ptr>& dd, - bool regular = true) { + std::unique_ptr>& dd, + const bool regular = true) { assert(from.size() >= to.size()); // iterate over (k,v) pairs of second permutation diff --git a/include/dd/UniqueTable.hpp b/include/dd/UniqueTable.hpp index edfd77dd4..25c5dde19 100644 --- a/include/dd/UniqueTable.hpp +++ b/include/dd/UniqueTable.hpp @@ -255,7 +255,7 @@ template class UniqueTable { using Table = std::array; /// The number of variables - std::size_t nvars = 0; + std::size_t nvars = 0U; /** * @brief The actual tables (one for each variable) * @details Each hash table is an array of buckets. Each bucket is a linked @@ -274,7 +274,7 @@ template class UniqueTable { * @brief the number of active nodes for each variable * @note A node is considered active if it has a non-zero reference count. */ - std::vector active{std::vector(nvars, 0)}; + std::vector active{std::vector(nvars, 0U)}; /// The initial garbage collection limit std::size_t initialGCLimit; diff --git a/src/dd/FunctionalityConstruction.cpp b/src/dd/FunctionalityConstruction.cpp index 4e0000437..d9317eba8 100644 --- a/src/dd/FunctionalityConstruction.cpp +++ b/src/dd/FunctionalityConstruction.cpp @@ -35,7 +35,7 @@ MatrixDD buildFunctionality(const QuantumComputation* qc, template MatrixDD buildFunctionalityRecursive(const QuantumComputation* qc, - std::unique_ptr>& dd) { + std::unique_ptr>& dd) { if (qc->getNqubits() == 0U) { return MatrixDD::one; } @@ -71,7 +71,7 @@ bool buildFunctionalityRecursive(const QuantumComputation* qc, std::size_t depth, std::size_t opIdx, std::stack& s, Permutation& permutation, - std::unique_ptr>& dd) { + std::unique_ptr>& dd) { // base case if (depth == 1U) { auto e = getDD(qc->at(opIdx).get(), dd, permutation); @@ -119,7 +119,7 @@ bool buildFunctionalityRecursive(const QuantumComputation* qc, template MatrixDD buildFunctionality(const qc::Grover* qc, - std::unique_ptr>& dd) { + std::unique_ptr>& dd) { QuantumComputation groverIteration(qc->getNqubits()); qc->oracle(groverIteration); qc->diffusion(groverIteration); @@ -152,7 +152,7 @@ MatrixDD buildFunctionality(const qc::Grover* qc, template MatrixDD buildFunctionalityRecursive(const qc::Grover* qc, - std::unique_ptr>& dd) { + std::unique_ptr>& dd) { QuantumComputation groverIteration(qc->getNqubits()); qc->oracle(groverIteration); qc->diffusion(groverIteration); @@ -227,23 +227,24 @@ MatrixDD buildFunctionality(GoogleRandomCircuitSampling* qc, template MatrixDD buildFunctionality(const qc::QuantumComputation* qc, - std::unique_ptr>& dd); -template MatrixDD buildFunctionalityRecursive( - const qc::QuantumComputation* qc, - std::unique_ptr>& dd); -template bool buildFunctionalityRecursive( - const qc::QuantumComputation* qc, const std::size_t depth, - const std::size_t opIdx, std::stack& s, - qc::Permutation& permutation, - std::unique_ptr>& dd); + std::unique_ptr>& dd); +template MatrixDD +buildFunctionalityRecursive(const qc::QuantumComputation* qc, + std::unique_ptr>& dd); +template bool +buildFunctionalityRecursive(const qc::QuantumComputation* qc, + const std::size_t depth, const std::size_t opIdx, + std::stack& s, + qc::Permutation& permutation, + std::unique_ptr>& dd); template MatrixDD buildFunctionality(const qc::Grover* qc, - std::unique_ptr>& dd); -template MatrixDD buildFunctionalityRecursive( - const qc::Grover* qc, - std::unique_ptr>& dd); + std::unique_ptr>& dd); +template MatrixDD +buildFunctionalityRecursive(const qc::Grover* qc, + std::unique_ptr>& dd); template MatrixDD buildFunctionality(GoogleRandomCircuitSampling* qc, - std::unique_ptr>& dd, + std::unique_ptr>& dd, const std::optional ncycles); } // namespace dd diff --git a/src/dd/Simulation.cpp b/src/dd/Simulation.cpp index 687994afc..15d8fb052 100644 --- a/src/dd/Simulation.cpp +++ b/src/dd/Simulation.cpp @@ -4,7 +4,7 @@ namespace dd { template std::map simulate(const QuantumComputation* qc, const VectorDD& in, - std::unique_ptr>& dd, std::size_t shots, + std::unique_ptr>& dd, std::size_t shots, std::size_t seed) { bool isDynamicCircuit = false; bool hasMeasurements = false; @@ -139,8 +139,7 @@ simulate(const QuantumComputation* qc, const VectorDD& in, const auto& bits = nonunitary->getClassics(); for (std::size_t j = 0U; j < qubits.size(); ++j) { measurements[bits.at(j)] = dd->measureOneCollapsing( - e, static_cast(permutation.at(qubits.at(j))), true, - mt); + e, static_cast(permutation.at(qubits.at(j))), true, mt); } continue; } @@ -149,7 +148,7 @@ simulate(const QuantumComputation* qc, const VectorDD& in, const auto& qubits = nonunitary->getTargets(); for (const auto& qubit : qubits) { auto bit = dd->measureOneCollapsing( - e, static_cast(permutation.at(qubit)), true, mt); + e, static_cast(permutation.at(qubit)), true, mt); // apply an X operation whenever the measured result is one if (bit == '1') { const auto x = @@ -208,7 +207,7 @@ simulate(const QuantumComputation* qc, const VectorDD& in, template void extractProbabilityVector(const QuantumComputation* qc, const VectorDD& in, ProbabilityVector& probVector, - std::unique_ptr>& dd) { + std::unique_ptr>& dd) { // ! initial layout, output permutation and garbage qubits are currently not // supported here dd->incRef(in); @@ -301,7 +300,7 @@ void extractProbabilityVectorRecursive( // determine probabilities for this measurement auto [pzero, pone] = dd->determineMeasurementProbabilities( - state, static_cast(targets[0]), true); + state, static_cast(targets[0]), true); // normalize probabilities const auto norm = pzero + pone; @@ -372,7 +371,7 @@ void extractProbabilityVectorRecursive( qc::VectorDD measuredState = dd->multiply(measurementGate, state); auto c = dd->cn.getTemporary(1. / std::sqrt(pzero), 0); - dd::ComplexNumbers::mul(c, measuredState.w, c); + ComplexNumbers::mul(c, c, measuredState.w); measuredState.w = dd->cn.lookup(c); dd->incRef(measuredState); dd->decRef(state); @@ -391,16 +390,15 @@ void extractProbabilityVectorRecursive( // determine the next iteration point auto nextIt = it + 1; // actually collapse the state - const dd::GateMatrix measurementMatrix{ - dd::complex_zero, dd::complex_zero, dd::complex_zero, - dd::complex_one}; + const GateMatrix measurementMatrix{complex_zero, complex_zero, + complex_zero, complex_one}; const qc::MatrixDD measurementGate = dd->makeGateDD( measurementMatrix, static_cast(state.p->v + 1), static_cast(targets[0])); qc::VectorDD measuredState = dd->multiply(measurementGate, state); auto c = dd->cn.getTemporary(1. / std::sqrt(pone), 0); - dd::ComplexNumbers::mul(c, measuredState.w, c); + ComplexNumbers::mul(c, measuredState.w, c); measuredState.w = dd->cn.lookup(c); dd->incRef(measuredState); dd->decRef(state); @@ -426,7 +424,7 @@ void extractProbabilityVectorRecursive( template VectorDD simulate(GoogleRandomCircuitSampling* qc, const VectorDD& in, - std::unique_ptr>& dd, + std::unique_ptr>& dd, const std::optional ncycles) { if (ncycles.has_value() && (*ncycles < qc->cycles.size() - 2U)) { qc->removeCycles(qc->cycles.size() - 2U - *ncycles); @@ -449,19 +447,19 @@ VectorDD simulate(GoogleRandomCircuitSampling* qc, const VectorDD& in, template std::map simulate(const QuantumComputation* qc, const VectorDD& in, - std::unique_ptr>& dd, + std::unique_ptr>& dd, std::size_t shots, std::size_t seed); template void extractProbabilityVector( const QuantumComputation* qc, const VectorDD& in, ProbabilityVector& probVector, - std::unique_ptr>& dd); + std::unique_ptr>& dd); template void extractProbabilityVectorRecursive( const QuantumComputation* qc, const VectorDD& in, decltype(qc->begin()) currentIt, std::map measurements, - dd::fp commonFactor, dd::ProbabilityVector& probVector, - std::unique_ptr>& dd); + fp commonFactor, ProbabilityVector& probVector, + std::unique_ptr>& dd); template VectorDD simulate(GoogleRandomCircuitSampling* qc, const VectorDD& in, - std::unique_ptr>& dd, + std::unique_ptr>& dd, const std::optional ncycles); } // namespace dd diff --git a/test/algorithms/eval_dynamic_circuits.cpp b/test/algorithms/eval_dynamic_circuits.cpp index a94786856..ccb75d5bf 100644 --- a/test/algorithms/eval_dynamic_circuits.cpp +++ b/test/algorithms/eval_dynamic_circuits.cpp @@ -41,11 +41,11 @@ class DynamicCircuitEvalExactQPE : public testing::TestWithParam { iqpeNgates = iqpe->getNindividualOps(); std::cout << "Estimating lambda = " << lambda << "π up to " << precision - << "-bit precision." << std::endl; + << "-bit precision.\n"; theta = lambda / 2; - std::cout << "Expected theta=" << theta << std::endl; + std::cout << "Expected theta=" << theta << "\n"; std::bitset<64> binaryExpansion{}; dd::fp expansion = theta * 2; std::size_t index = 0; @@ -75,9 +75,9 @@ class DynamicCircuitEvalExactQPE : public testing::TestWithParam { expectedResultRepresentation = ss.str(); std::cout << "Theta is exactly representable using " << precision - << " bits." << std::endl; + << " bits.\n"; std::cout << "The expected output state is |" - << expectedResultRepresentation << ">." << std::endl; + << expectedResultRepresentation << ">.\n"; } }; @@ -160,9 +160,9 @@ TEST_P(DynamicCircuitEvalExactQPE, UnitaryTransformation) { std::stringstream ss{}; ss << "qpe_exact,transformation," << qpe->getNqubits() << "," << qpeNgates << ",2," << iqpeNgates << "," << preprocessing << "," << verification; - std::cout << ss.str() << std::endl; + std::cout << ss.str() << "\n"; ofs.open("results_exact.csv", std::ios_base::app); - ofs << ss.str() << std::endl; + ofs << ss.str() << "\n"; EXPECT_TRUE(e.p->isIdentity()); } @@ -207,9 +207,9 @@ TEST_P(DynamicCircuitEvalExactQPE, ProbabilityExtraction) { ss << "qpe_exact,extraction," << qpe->getNqubits() << "," << qpeNgates << ",2," << iqpeNgates << "," << simulation << "," << extraction << "," << comparison << "," << total; - std::cout << ss.str() << std::endl; + std::cout << ss.str() << "\n"; ofs.open("results_exact_prob.csv", std::ios_base::app); - ofs << ss.str() << std::endl; + ofs << ss.str() << "\n"; EXPECT_NEAR(fidelity, 1.0, 1e-4); } @@ -246,11 +246,11 @@ class DynamicCircuitEvalInexactQPE iqpeNgates = iqpe->getNindividualOps(); std::cout << "Estimating lambda = " << lambda << "π up to " << precision - << "-bit precision." << std::endl; + << "-bit precision.\n"; theta = lambda / 2; - std::cout << "Expected theta=" << theta << std::endl; + std::cout << "Expected theta=" << theta << "\n"; std::bitset<64> binaryExpansion{}; dd::fp expansion = theta * 2; std::size_t index = 0; @@ -270,8 +270,8 @@ class DynamicCircuitEvalInexactQPE } } std::stringstream ss{}; - for (auto i = precision - 1; i >= 0; --i) { - if ((expectedResult & (1ULL << i)) != 0) { + for (auto i = precision; i > 0; --i) { + if ((expectedResult & (1ULL << (i - 1))) != 0) { ss << 1; } else { ss << 0; @@ -281,8 +281,8 @@ class DynamicCircuitEvalInexactQPE secondExpectedResult = expectedResult + 1; ss.str(""); - for (auto i = precision - 1; i >= 0; --i) { - if ((secondExpectedResult & (1ULL << i)) != 0) { + for (auto i = precision; i > 0; --i) { + if ((secondExpectedResult & (1ULL << (i - 1))) != 0) { ss << 1; } else { ss << 0; @@ -291,10 +291,10 @@ class DynamicCircuitEvalInexactQPE secondExpectedResultRepresentation = ss.str(); std::cout << "Theta is not exactly representable using " << precision - << " bits." << std::endl; + << " bits.\n"; std::cout << "Most probable output states are |" << expectedResultRepresentation << "> and |" - << secondExpectedResultRepresentation << ">." << std::endl; + << secondExpectedResultRepresentation << ">.\n"; } }; @@ -377,9 +377,9 @@ TEST_P(DynamicCircuitEvalInexactQPE, UnitaryTransformation) { std::stringstream ss{}; ss << "qpe_inexact,transformation," << qpe->getNqubits() << "," << qpeNgates << ",2," << iqpeNgates << "," << preprocessing << "," << verification; - std::cout << ss.str() << std::endl; + std::cout << ss.str() << "\n"; ofs.open("results_inexact.csv", std::ios_base::app); - ofs << ss.str() << std::endl; + ofs << ss.str() << "\n"; EXPECT_TRUE(e.p->isIdentity()); } @@ -393,14 +393,14 @@ TEST_P(DynamicCircuitEvalInexactQPE, ProbabilityExtraction) { dd->makeZeroState(static_cast(iqpe->getNqubits())), probs, dd); const auto extractionEnd = std::chrono::steady_clock::now(); - std::cout << "---- extraction done ----" << std::endl; + std::cout << "---- extraction done ----\n"; // generate DD of QPE circuit via simulation auto e = simulate( qpe.get(), dd->makeZeroState(static_cast(qpe->getNqubits())), dd); const auto simulationEnd = std::chrono::steady_clock::now(); - std::cout << "---- sim done ----" << std::endl; + std::cout << "---- sim done ----\n"; // extend to account for 0 qubit auto stub = dd::ProbabilityVector{}; @@ -426,9 +426,9 @@ TEST_P(DynamicCircuitEvalInexactQPE, ProbabilityExtraction) { ss << "qpe_inexact,extraction," << qpe->getNqubits() << "," << qpeNgates << ",2," << iqpeNgates << "," << simulation << "," << extraction << "," << comparison << "," << total; - std::cout << ss.str() << std::endl; + std::cout << ss.str() << "\n"; ofs.open("results_inexact_prob.csv", std::ios_base::app); - ofs << ss.str() << std::endl; + ofs << ss.str() << "\n"; EXPECT_NEAR(fidelity, 1.0, 1e-4); } @@ -461,7 +461,7 @@ class DynamicCircuitEvalBV : public testing::TestWithParam { const auto expected = dynamic_cast(bv.get())->expected; std::cout << "Hidden bitstring: " << expected << " (" << bitwidth - << " qubits)" << std::endl; + << " qubits)\n"; } }; @@ -544,9 +544,9 @@ TEST_P(DynamicCircuitEvalBV, UnitaryTransformation) { std::stringstream ss{}; ss << "bv,transformation," << bv->getNqubits() << "," << bvNgates << ",2," << dbvNgates << "," << preprocessing << "," << verification; - std::cout << ss.str() << std::endl; + std::cout << ss.str() << "\n"; ofs.open("results_bv.csv", std::ios_base::app); - ofs << ss.str() << std::endl; + ofs << ss.str() << "\n"; EXPECT_TRUE(e.p->isIdentity()); } @@ -591,9 +591,9 @@ TEST_P(DynamicCircuitEvalBV, ProbabilityExtraction) { ss << "bv,extraction," << bv->getNqubits() << "," << bvNgates << ",2," << dbvNgates << "," << simulation << "," << extraction << "," << comparison << "," << total; - std::cout << ss.str() << std::endl; + std::cout << ss.str() << "\n"; ofs.open("results_bv_prob.csv", std::ios_base::app); - ofs << ss.str() << std::endl; + ofs << ss.str() << "\n"; EXPECT_NEAR(fidelity, 1.0, 1e-4); } @@ -703,9 +703,9 @@ TEST_P(DynamicCircuitEvalQFT, UnitaryTransformation) { std::stringstream ss{}; ss << "qft,transformation," << qft->getNqubits() << "," << qftNgates << ",1," << dqftNgates << "," << preprocessing << "," << verification; - std::cout << ss.str() << std::endl; + std::cout << ss.str() << "\n"; ofs.open("results_qft.csv", std::ios_base::app); - ofs << ss.str() << std::endl; + ofs << ss.str() << "\n"; EXPECT_TRUE(e.p->isIdentity()); } @@ -743,14 +743,14 @@ TEST_P(DynamicCircuitEvalQFT, ProbabilityExtraction) { ss << "qft,extraction," << qft->getNqubits() << "," << qftNgates << ",1," << dqftNgates << "," << extraction << "," << simulation << "," << comparison << "," << total; - std::cout << ss.str() << std::endl; + std::cout << ss.str() << "\n"; } else { ss << "qft,extraction," << qft->getNqubits() << "," << qftNgates << ",1," << dqftNgates << ",," << simulation << ",,,"; - std::cout << ss.str() << std::endl; + std::cout << ss.str() << "\n"; } ofs.open("results_qft_prob.csv", std::ios_base::app); - ofs << ss.str() << std::endl; + ofs << ss.str() << "\n"; } diff --git a/test/algorithms/test_bernsteinvazirani.cpp b/test/algorithms/test_bernsteinvazirani.cpp index 0e7bc47af..e527b011a 100644 --- a/test/algorithms/test_bernsteinvazirani.cpp +++ b/test/algorithms/test_bernsteinvazirani.cpp @@ -1,158 +1,158 @@ -#include "CircuitOptimizer.hpp" -#include "algorithms/BernsteinVazirani.hpp" -#include "dd/Simulation.hpp" - -#include "gtest/gtest.h" - -class BernsteinVazirani : public testing::TestWithParam { -protected: - void TearDown() override {} - void SetUp() override {} -}; - -INSTANTIATE_TEST_SUITE_P( - BernsteinVazirani, BernsteinVazirani, - testing::Values(0ULL, // Zero-Value - 3ULL, 63ULL, 170ULL, // 0-bit < hInt <= 8-bit - 819ULL, 4032ULL, 33153ULL, // 8-bit < hInt <= 16-bit - 87381ULL, 16777215ULL, - 1234567891011ULL // 16-bit < hInt <= 32-bit - ), - [](const testing::TestParamInfo& inf) { - // Generate names for test cases - const auto s = inf.param; - std::stringstream ss{}; - ss << "bv_" << s; - return ss.str(); - }); - -TEST_P(BernsteinVazirani, FunctionTest) { - // get hidden bitstring - auto s = qc::BitString(GetParam()); - - // construct Bernstein Vazirani circuit - auto qc = std::make_unique(s); - qc->printStatistics(std::cout); - - // simulate the circuit - auto dd = std::make_unique>(qc->getNqubits()); - const std::size_t shots = 1024; - auto measurements = - simulate(qc.get(), - dd->makeZeroState(static_cast(qc->getNqubits())), - dd, shots); - - for (const auto& [state, count] : measurements) { - std::cout << state << ": " << count << std::endl; - } - - // expect to obtain the hidden bitstring with certainty - EXPECT_EQ(measurements[qc->expected], shots); -} - -TEST_P(BernsteinVazirani, FunctionTestDynamic) { - // get hidden bitstring - auto s = qc::BitString(GetParam()); - - // construct Bernstein Vazirani circuit - auto qc = std::make_unique(s, true); - qc->printStatistics(std::cout); - - // simulate the circuit - auto dd = std::make_unique>(qc->getNqubits()); - const std::size_t shots = 1024; - auto measurements = - simulate(qc.get(), - dd->makeZeroState(static_cast(qc->getNqubits())), - dd, shots); - - for (const auto& [state, count] : measurements) { - std::cout << state << ": " << count << std::endl; - } - - // expect to obtain the hidden bitstring with certainty - EXPECT_EQ(measurements[qc->expected], shots); -} - -TEST_F(BernsteinVazirani, LargeCircuit) { - const std::size_t nq = 127; - auto qc = std::make_unique(nq); - qc->printStatistics(std::cout); - - // simulate the circuit - auto dd = std::make_unique>(qc->getNqubits()); - const std::size_t shots = 1024; - auto measurements = - simulate(qc.get(), - dd->makeZeroState(static_cast(qc->getNqubits())), - dd, shots); - - for (const auto& [state, count] : measurements) { - std::cout << state << ": " << count << std::endl; - } - - // expect to obtain the hidden bitstring with certainty - EXPECT_EQ(measurements[qc->expected], shots); -} - -TEST_F(BernsteinVazirani, DynamicCircuit) { - const std::size_t nq = 127; - auto qc = std::make_unique(nq, true); - qc->printStatistics(std::cout); - - // simulate the circuit - auto dd = std::make_unique>(qc->getNqubits()); - const std::size_t shots = 1024; - auto measurements = - simulate(qc.get(), - dd->makeZeroState(static_cast(qc->getNqubits())), - dd, shots); - - for (const auto& [state, count] : measurements) { - std::cout << state << ": " << count << std::endl; - } - - // expect to obtain the hidden bitstring with certainty - EXPECT_EQ(measurements[qc->expected], shots); -} - -TEST_P(BernsteinVazirani, DynamicEquivalenceSimulation) { - // get hidden bitstring - auto s = qc::BitString(GetParam()); - - // create standard BV circuit - auto bv = std::make_unique(s); - - auto dd = std::make_unique>(bv->getNqubits()); - - // remove final measurements to obtain statevector - qc::CircuitOptimizer::removeFinalMeasurements(*bv); - - // simulate circuit - auto e = simulate( - bv.get(), - dd->makeZeroState(static_cast(bv->getNqubits())), dd); - - // create dynamic BV circuit - auto dbv = std::make_unique(s, true); - - // transform dynamic circuits by first eliminating reset operations and - // afterwards deferring measurements - qc::CircuitOptimizer::eliminateResets(*dbv); - - qc::CircuitOptimizer::deferMeasurements(*dbv); - - // remove final measurements to obtain statevector - qc::CircuitOptimizer::removeFinalMeasurements(*dbv); - - // simulate circuit - auto f = simulate( - dbv.get(), - dd->makeZeroState(static_cast(dbv->getNqubits())), dd); - - // calculate fidelity between both results - auto fidelity = dd->fidelity(e, f); - std::cout << "Fidelity of both circuits: " << fidelity << std::endl; - - EXPECT_NEAR(fidelity, 1.0, 1e-4); -} +#include "CircuitOptimizer.hpp" +#include "algorithms/BernsteinVazirani.hpp" +#include "dd/Simulation.hpp" + +#include "gtest/gtest.h" + +class BernsteinVazirani : public testing::TestWithParam { +protected: + void TearDown() override {} + void SetUp() override {} +}; + +INSTANTIATE_TEST_SUITE_P( + BernsteinVazirani, BernsteinVazirani, + testing::Values(0ULL, // Zero-Value + 3ULL, 63ULL, 170ULL, // 0-bit < hInt <= 8-bit + 819ULL, 4032ULL, 33153ULL, // 8-bit < hInt <= 16-bit + 87381ULL, 16777215ULL, + 1234567891011ULL // 16-bit < hInt <= 32-bit + ), + [](const testing::TestParamInfo& inf) { + // Generate names for test cases + const auto s = inf.param; + std::stringstream ss{}; + ss << "bv_" << s; + return ss.str(); + }); + +TEST_P(BernsteinVazirani, FunctionTest) { + // get hidden bitstring + auto s = qc::BitString(GetParam()); + + // construct Bernstein Vazirani circuit + auto qc = std::make_unique(s); + qc->printStatistics(std::cout); + + // simulate the circuit + auto dd = std::make_unique>(qc->getNqubits()); + const std::size_t shots = 1024; + auto measurements = + simulate(qc.get(), + dd->makeZeroState(static_cast(qc->getNqubits())), + dd, shots); + + for (const auto& [state, count] : measurements) { + std::cout << state << ": " << count << "\n"; + } + + // expect to obtain the hidden bitstring with certainty + EXPECT_EQ(measurements[qc->expected], shots); +} + +TEST_P(BernsteinVazirani, FunctionTestDynamic) { + // get hidden bitstring + auto s = qc::BitString(GetParam()); + + // construct Bernstein Vazirani circuit + auto qc = std::make_unique(s, true); + qc->printStatistics(std::cout); + + // simulate the circuit + auto dd = std::make_unique>(qc->getNqubits()); + const std::size_t shots = 1024; + auto measurements = + simulate(qc.get(), + dd->makeZeroState(static_cast(qc->getNqubits())), + dd, shots); + + for (const auto& [state, count] : measurements) { + std::cout << state << ": " << count << "\n"; + } + + // expect to obtain the hidden bitstring with certainty + EXPECT_EQ(measurements[qc->expected], shots); +} + +TEST_F(BernsteinVazirani, LargeCircuit) { + const std::size_t nq = 127; + auto qc = std::make_unique(nq); + qc->printStatistics(std::cout); + + // simulate the circuit + auto dd = std::make_unique>(qc->getNqubits()); + const std::size_t shots = 1024; + auto measurements = + simulate(qc.get(), + dd->makeZeroState(static_cast(qc->getNqubits())), + dd, shots); + + for (const auto& [state, count] : measurements) { + std::cout << state << ": " << count << "\n"; + } + + // expect to obtain the hidden bitstring with certainty + EXPECT_EQ(measurements[qc->expected], shots); +} + +TEST_F(BernsteinVazirani, DynamicCircuit) { + const std::size_t nq = 127; + auto qc = std::make_unique(nq, true); + qc->printStatistics(std::cout); + + // simulate the circuit + auto dd = std::make_unique>(qc->getNqubits()); + const std::size_t shots = 1024; + auto measurements = + simulate(qc.get(), + dd->makeZeroState(static_cast(qc->getNqubits())), + dd, shots); + + for (const auto& [state, count] : measurements) { + std::cout << state << ": " << count << "\n"; + } + + // expect to obtain the hidden bitstring with certainty + EXPECT_EQ(measurements[qc->expected], shots); +} + +TEST_P(BernsteinVazirani, DynamicEquivalenceSimulation) { + // get hidden bitstring + auto s = qc::BitString(GetParam()); + + // create standard BV circuit + auto bv = std::make_unique(s); + + auto dd = std::make_unique>(bv->getNqubits()); + + // remove final measurements to obtain statevector + qc::CircuitOptimizer::removeFinalMeasurements(*bv); + + // simulate circuit + auto e = simulate( + bv.get(), + dd->makeZeroState(static_cast(bv->getNqubits())), dd); + + // create dynamic BV circuit + auto dbv = std::make_unique(s, true); + + // transform dynamic circuits by first eliminating reset operations and + // afterwards deferring measurements + qc::CircuitOptimizer::eliminateResets(*dbv); + + qc::CircuitOptimizer::deferMeasurements(*dbv); + + // remove final measurements to obtain statevector + qc::CircuitOptimizer::removeFinalMeasurements(*dbv); + + // simulate circuit + auto f = simulate( + dbv.get(), + dd->makeZeroState(static_cast(dbv->getNqubits())), dd); + + // calculate fidelity between both results + auto fidelity = dd->fidelity(e, f); + std::cout << "Fidelity of both circuits: " << fidelity << "\n"; + + EXPECT_NEAR(fidelity, 1.0, 1e-4); +} diff --git a/test/algorithms/test_grcs.cpp b/test/algorithms/test_grcs.cpp index 1f03ec35e..13c2aa9d4 100644 --- a/test/algorithms/test_grcs.cpp +++ b/test/algorithms/test_grcs.cpp @@ -14,12 +14,12 @@ TEST_F(GRCS, import) { auto qcBris = qc::GoogleRandomCircuitSampling("./circuits/grcs/bris_4_40_9_v2.txt"); qcBris.printStatistics(std::cout); - std::cout << qcBris << std::endl; + std::cout << qcBris << "\n"; auto qcInst = qc::GoogleRandomCircuitSampling("./circuits/grcs/inst_4x4_80_9_v2.txt"); qcInst.printStatistics(std::cout); - std::cout << qcInst << std::endl; + std::cout << qcInst << "\n"; } TEST_F(GRCS, simulate) { @@ -30,7 +30,7 @@ TEST_F(GRCS, simulate) { auto in = dd->makeZeroState(static_cast(qcBris.getNqubits())); const std::optional ncycles = 4; ASSERT_NO_THROW({ simulate(&qcBris, in, dd, ncycles); }); - std::cout << qcBris << std::endl; + std::cout << qcBris << "\n"; qcBris.printStatistics(std::cout); } @@ -40,6 +40,6 @@ TEST_F(GRCS, buildFunctionality) { auto dd = std::make_unique>(qcBris.getNqubits()); ASSERT_NO_THROW({ buildFunctionality(&qcBris, dd, 4); }); - std::cout << qcBris << std::endl; + std::cout << qcBris << "\n"; qcBris.printStatistics(std::cout); } diff --git a/test/algorithms/test_grover.cpp b/test/algorithms/test_grover.cpp index cdd3c9883..0ed25c422 100644 --- a/test/algorithms/test_grover.cpp +++ b/test/algorithms/test_grover.cpp @@ -119,7 +119,7 @@ TEST_P(Grover, Simulation) { auto measurements = simulate(qc.get(), in, dd, shots); for (const auto& [state, count] : measurements) { - std::cout << state << ": " << count << std::endl; + std::cout << state << ": " << count << "\n"; } auto correctShots = measurements[qc->expected]; diff --git a/test/algorithms/test_qft.cpp b/test/algorithms/test_qft.cpp index e541e73dd..72cc14a7d 100644 --- a/test/algorithms/test_qft.cpp +++ b/test/algorithms/test_qft.cpp @@ -87,7 +87,7 @@ TEST_P(QFT, Functionality) { // since only positive real values are stored in the complex table // this number has to be divided by 4 ASSERT_EQ(dd->cn.realCount(), - static_cast(std::ceil(std::pow(2, nqubits) / 4))); + static_cast(std::ceil(std::pow(2, nqubits) / 4))); // top edge weight should equal sqrt(0.5)^n EXPECT_NEAR(dd::RealNumber::val(func.w.r), @@ -128,7 +128,7 @@ TEST_P(QFT, FunctionalityRecursive) { // since only positive real values are stored in the complex table // this number has to be divided by 4 ASSERT_EQ(dd->cn.realCount(), - static_cast(std::ceil(std::pow(2, nqubits) / 4))); + static_cast(std::ceil(std::pow(2, nqubits) / 4))); // top edge weight should equal sqrt(0.5)^n EXPECT_NEAR(dd::RealNumber::val(func.w.r), diff --git a/test/algorithms/test_qpe.cpp b/test/algorithms/test_qpe.cpp index 7ff2352d0..576805cb6 100644 --- a/test/algorithms/test_qpe.cpp +++ b/test/algorithms/test_qpe.cpp @@ -26,11 +26,11 @@ class QPE : public testing::TestWithParam> { precision = GetParam().second; std::cout << "Estimating lambda = " << lambda << "π up to " << precision - << "-bit precision." << std::endl; + << "-bit precision.\n"; theta = lambda / 2; - std::cout << "Expected theta=" << theta << std::endl; + std::cout << "Expected theta=" << theta << "\n"; std::bitset<64> binaryExpansion{}; auto expansion = theta * 2; std::size_t index = 0; @@ -69,9 +69,9 @@ class QPE : public testing::TestWithParam> { if (exactlyRepresentable) { std::cout << "Theta is exactly representable using " << precision - << " bits." << std::endl; + << " bits.\n"; std::cout << "The expected output state is |" - << expectedResultRepresentation << ">." << std::endl; + << expectedResultRepresentation << ">.\n"; } else { secondExpectedResult = expectedResult + 1; ss.str(""); @@ -85,10 +85,10 @@ class QPE : public testing::TestWithParam> { secondExpectedResultRepresentation = ss.str(); std::cout << "Theta is not exactly representable using " << precision - << " bits." << std::endl; + << " bits.\n"; std::cout << "Most probable output states are |" << expectedResultRepresentation << "> and |" - << secondExpectedResultRepresentation << ">." << std::endl; + << secondExpectedResultRepresentation << ">.\n"; } } }; @@ -129,7 +129,7 @@ TEST_P(QPE, QPETest) { auto amplitude = dd->getValueByPath(e, (expectedResult << 1) + 1); auto probability = amplitude.r * amplitude.r + amplitude.i * amplitude.i; std::cout << "Obtained probability for |" << expectedResultRepresentation - << ">: " << probability << std::endl; + << ">: " << probability << "\n"; if (exactlyRepresentable) { EXPECT_NEAR(probability, 1.0, 1e-8); @@ -143,7 +143,7 @@ TEST_P(QPE, QPETest) { secondAmplitude.i * secondAmplitude.i; std::cout << "Obtained probability for |" << secondExpectedResultRepresentation - << ">: " << secondProbability << std::endl; + << ">: " << secondProbability << "\n"; EXPECT_GT(probability, threshold); EXPECT_GT(secondProbability, threshold); @@ -175,10 +175,10 @@ TEST_P(QPE, IQPETest) { const std::set ordered(measurements.begin(), measurements.end(), comp); - std::cout << "Obtained measurements: " << std::endl; + std::cout << "Obtained measurements: \n"; for (const auto& measurement : ordered) { std::cout << "\t" << measurement.first << ": " << measurement.second << " (" - << (measurement.second * 100) / shots << "%)" << std::endl; + << (measurement.second * 100) / shots << "%)\n"; } const auto& mostLikely = *ordered.begin(); @@ -235,7 +235,7 @@ TEST_P(QPE, DynamicEquivalenceSimulation) { // calculate fidelity between both results auto fidelity = dd->fidelity(e, f); - std::cout << "Fidelity of both circuits: " << fidelity << std::endl; + std::cout << "Fidelity of both circuits: " << fidelity << "\n"; EXPECT_NEAR(fidelity, 1.0, 1e-4); } @@ -285,7 +285,7 @@ TEST_P(QPE, ProbabilityExtraction) { for (const auto& [state, prob] : probs) { std::stringstream ss{}; qc::QuantumComputation::printBin(state, ss); - std::cout << ss.str() << ": " << prob << std::endl; + std::cout << ss.str() << ": " << prob << "\n"; } if (exactlyRepresentable) { @@ -311,9 +311,9 @@ TEST_P(QPE, DynamicEquivalenceSimulationProbabilityExtraction) { qpe.get(), dd->makeZeroState(static_cast(qpe->getNqubits())), dd); const auto vec = dd->getVector(e); - std::cout << "QPE: " << std::endl; + std::cout << "QPE:\n"; for (const auto& amp : vec) { - std::cout << std::norm(amp) << std::endl; + std::cout << std::norm(amp) << "\n"; } // create standard IQPE circuit @@ -333,17 +333,17 @@ TEST_P(QPE, DynamicEquivalenceSimulationProbabilityExtraction) { stub[2 * state + 1] = prob; } - std::cout << "IQPE: " << std::endl; + std::cout << "IQPE:\n"; for (const auto& [state, prob] : stub) { std::stringstream ss{}; qc::QuantumComputation::printBin(state, ss); - std::cout << ss.str() << ": " << prob << std::endl; + std::cout << ss.str() << ": " << prob << "\n"; } // calculate fidelity between both results auto fidelity = dd->fidelityOfMeasurementOutcomes(e, stub); std::cout << "Fidelity of both circuits' measurement outcomes: " << fidelity - << std::endl; + << "\n"; EXPECT_NEAR(fidelity, 1.0, 1e-4); } diff --git a/test/algorithms/test_random_clifford.cpp b/test/algorithms/test_random_clifford.cpp index 16990d44f..eb5b896e5 100644 --- a/test/algorithms/test_random_clifford.cpp +++ b/test/algorithms/test_random_clifford.cpp @@ -12,8 +12,7 @@ class RandomClifford : public testing::TestWithParam { }; INSTANTIATE_TEST_SUITE_P( - RandomClifford, RandomClifford, - testing::Range(static_cast(1), static_cast(9)), + RandomClifford, RandomClifford, testing::Range(1U, 9U), [](const testing::TestParamInfo& inf) { // Generate names for test cases const auto nqubits = inf.param; @@ -29,7 +28,7 @@ TEST_P(RandomClifford, simulate) { auto qc = qc::RandomCliffordCircuit(nq, nq * nq, 12345); auto in = dd->makeZeroState(static_cast(nq)); - std::cout << qc << std::endl; + std::cout << qc << "\n"; ASSERT_NO_THROW({ simulate(&qc, in, dd); }); qc.printStatistics(std::cout); } @@ -39,7 +38,7 @@ TEST_P(RandomClifford, buildFunctionality) { auto dd = std::make_unique>(nq); auto qc = qc::RandomCliffordCircuit(nq, nq * nq, 12345); - std::cout << qc << std::endl; + std::cout << qc << "\n"; ASSERT_NO_THROW({ buildFunctionality(&qc, dd); }); qc.printStatistics(std::cout); } From fc7d2c170269bcad12cc29615b9254810df9c0cc Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sat, 15 Jul 2023 00:44:54 -0700 Subject: [PATCH 03/14] =?UTF-8?q?=F0=9F=A9=B9=20fix=20unique=20table=20sta?= =?UTF-8?q?ts=20JSON=20output?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- src/dd/UniqueTableStatistics.cpp | 34 +++++++++++++++++++------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/dd/UniqueTableStatistics.cpp b/src/dd/UniqueTableStatistics.cpp index cb2680bdb..36dded5ab 100644 --- a/src/dd/UniqueTableStatistics.cpp +++ b/src/dd/UniqueTableStatistics.cpp @@ -29,20 +29,26 @@ void UniqueTableStatistics::reset() noexcept { } nlohmann::json UniqueTableStatistics::json() const { - return nlohmann::json{ - {"table_performance", - {"collisions", collisions}, - {"hits", hits}, - {"lookups", lookups}, - {"inserts", inserts}, - {"hit_ratio", hitRatio()}, - {"col_ratio", colRatio()}}, - {"entry_statistics", - {"entry_count", entryCount}, - {"peak_entry_count", peakActiveEntryCount}, - {"active_entry_count", activeEntryCount}, - {"peak_active_entry_count", peakActiveEntryCount}}, - {"garbage_collection_statistics", {"calls", gcCalls}, {"runs", gcRuns}}}; + nlohmann::json j{}; + auto& perf = j["table_performance"]; + perf["collisions"] = collisions; + perf["hits"] = hits; + perf["lookups"] = lookups; + perf["inserts"] = inserts; + perf["hit_ratio"] = hitRatio(); + perf["col_ratio"] = colRatio(); + + auto& entry = j["entry_statistics"]; + entry["entry_count"] = entryCount; + entry["peak_entry_count"] = peakEntryCount; + entry["active_entry_count"] = activeEntryCount; + entry["peak_active_entry_count"] = peakActiveEntryCount; + + auto& garbage = j["garbage_collection_statistics"]; + garbage["calls"] = gcCalls; + garbage["runs"] = gcRuns; + + return j; } std::string UniqueTableStatistics::toString() const { return json().dump(2U); } From 777ab42930a8102ec1fc847912e3dd7b1cdfbe99 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sat, 15 Jul 2023 01:06:43 -0700 Subject: [PATCH 04/14] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20eliminate=20DD=20ter?= =?UTF-8?q?minals?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit completely eliminates the static DD node terminals and replaces them by using nullptr instead. For one, this eliminates some global static variables. Furthermore, DD terminal nodes are no longer associated with the index -1. This allows changing the qubit index to an unsigned type and unlocks double the number of qubits. Finally, analysis has shown that the DD node size does not increase when using 16 bits for the qubit index. Hence, the package now directly supports up to 65536 qubits. Signed-off-by: Lukas Burgholzer --- include/dd/DDDefinitions.hpp | 26 +- include/dd/Export.hpp | 12 + include/dd/Node.hpp | 79 +- include/dd/NoiseFunctionality.hpp | 102 ++- include/dd/Operations.hpp | 40 +- include/dd/Package.hpp | 718 +++++++++---------- include/dd/StochasticNoiseOperationTable.hpp | 9 +- include/dd/UniqueTable.hpp | 31 +- src/dd/Edge.cpp | 7 +- src/dd/FunctionalityConstruction.cpp | 14 +- src/dd/Node.cpp | 31 +- src/dd/Simulation.cpp | 43 +- 12 files changed, 535 insertions(+), 577 deletions(-) diff --git a/include/dd/DDDefinitions.hpp b/include/dd/DDDefinitions.hpp index aadadd495..dd43df9ff 100644 --- a/include/dd/DDDefinitions.hpp +++ b/include/dd/DDDefinitions.hpp @@ -8,21 +8,25 @@ #include namespace dd { -// integer type used for indexing qubits -// needs to be a signed type to encode -1 as the index for the terminal -// std::int8_t can address up to 128 qubits as [0, ..., 127] -using Qubit = std::int8_t; -static_assert(std::is_signed_v, "Type Qubit must be signed."); - -// integer type used for specifying numbers of qubits -using QubitCount = std::make_unsigned_t; +/** + * @brief Integer type used for indexing qubits + * @details `std::uint16_t` can address up to 65536 qubits as [0, ..., 65535]. + * @note If you need even more qubits, this can be increased to `std::uint32_t`. + * Beware of the increased memory footprint of matrix nodes. + */ +using Qubit = std::uint16_t; -// integer type used for reference counting -// 32bit suffice for a max ref count of around 4 billion +/** + * @brief Integer type used for reference counting + * @details Allows a maximum reference count of roughly 4 billion. + */ using RefCount = std::uint32_t; static_assert(std::is_unsigned_v, "RefCount should be unsigned."); -// floating point type to use +/** + * @brief Floating point type to use for computations + * @note Adjusting the precision might lead to unexpected results. + */ using fp = double; static_assert( std::is_floating_point_v, diff --git a/include/dd/Export.hpp b/include/dd/Export.hpp index f08294278..24c48ff4c 100644 --- a/include/dd/Export.hpp +++ b/include/dd/Export.hpp @@ -662,6 +662,12 @@ static void toDot(const Edge& e, std::ostream& os, bool colored = true, std::unordered_set nodes{}; auto priocmp = [](const Edge* left, const Edge* right) { + if (left->p == nullptr) { + return true; + } + if (right->p == nullptr) { + return false; + } return left->p->v < right->p->v; }; @@ -936,6 +942,12 @@ template static void exportEdgeWeights(const Edge& edge, std::ostream& stream) { struct Priocmp { bool operator()(const Edge* left, const Edge* right) { + if (left->p == nullptr) { + return true; + } + if (right->p == nullptr) { + return false; + } return left->p->v < right->p->v; } }; diff --git a/include/dd/Node.hpp b/include/dd/Node.hpp index 743de2245..ed23acd1b 100644 --- a/include/dd/Node.hpp +++ b/include/dd/Node.hpp @@ -9,30 +9,34 @@ #include namespace dd { -// NOLINTNEXTLINE(readability-identifier-naming) -struct vNode { + +/** + * @brief A vector DD node + * @details Data Layout |24|24|8|4|2| = 62B (space for two more bytes) + */ +struct vNode { // NOLINT(readability-identifier-naming) std::array, RADIX> e{}; // edges out of this node vNode* next{}; // used to link nodes in unique table RefCount ref{}; // reference count - Qubit v{}; // variable index (nonterminal) value (-1 for terminal) - - // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables - static vNode terminal; + Qubit v{}; // variable index static constexpr bool isTerminal(const vNode* p) noexcept { - return p == &terminal; + return p == nullptr; } - static constexpr vNode* getTerminal() noexcept { return &terminal; } + static constexpr vNode* getTerminal() noexcept { return nullptr; } }; using vEdge = Edge; using vCachedEdge = CachedEdge; -// NOLINTNEXTLINE(readability-identifier-naming) -struct mNode { +/** + * @brief A matrix DD node + * @details Data Layout |24|24|24|24|8|4|2|1| = 111B (space for one more byte) + */ +struct mNode { // NOLINT(readability-identifier-naming) std::array, NEDGE> e{}; // edges out of this node mNode* next{}; // used to link nodes in unique table RefCount ref{}; // reference count - Qubit v{}; // variable index (nonterminal) value (-1 for terminal) + Qubit v{}; // variable index std::uint8_t flags = 0; // 32 = marks a node with is symmetric. // 16 = marks a node resembling identity @@ -41,19 +45,32 @@ struct mNode { // 2 = mark first path edge (tmp flag), // 1 = mark path is conjugated (tmp flag)) - // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) - static mNode terminal; + [[nodiscard]] static constexpr bool isTerminal(const mNode* p) noexcept { + return p == nullptr; + } + [[nodiscard]] static constexpr mNode* getTerminal() noexcept { + return nullptr; + } - static constexpr bool isTerminal(const mNode* p) noexcept { - return p == &terminal; + [[nodiscard]] inline bool isSymmetric() const noexcept { + return (flags & static_cast(32U)) != 0; + } + [[nodiscard]] static constexpr bool isSymmetric(const mNode* p) noexcept { + return p == nullptr || p->isSymmetric(); + } + inline void setSymmetric(const bool symmetric) noexcept { + if (symmetric) { + flags = (flags | static_cast(32U)); + } else { + flags = (flags & static_cast(~32U)); + } } - static constexpr mNode* getTerminal() noexcept { return &terminal; } [[nodiscard]] inline bool isIdentity() const noexcept { return (flags & static_cast(16U)) != 0; } - [[nodiscard]] inline bool isSymmetric() const noexcept { - return (flags & static_cast(32U)) != 0; + [[nodiscard]] static constexpr bool isIdentity(const mNode* p) noexcept { + return p == nullptr || p->isIdentity(); } inline void setIdentity(const bool identity) noexcept { if (identity) { @@ -62,23 +79,19 @@ struct mNode { flags = (flags & static_cast(~16U)); } } - inline void setSymmetric(const bool symmetric) noexcept { - if (symmetric) { - flags = (flags | static_cast(32U)); - } else { - flags = (flags & static_cast(~32U)); - } - } }; using mEdge = Edge; using mCachedEdge = CachedEdge; -// NOLINTNEXTLINE(readability-identifier-naming) -struct dNode { +/** + * @brief A density matrix DD node + * @details Data Layout |24|24|24|24|8|4|2|1| = 111B (space for one more byte) + */ +struct dNode { // NOLINT(readability-identifier-naming) std::array, NEDGE> e{}; // edges out of this node dNode* next{}; // used to link nodes in unique table RefCount ref{}; // reference count - Qubit v{}; // variable index (nonterminal) value (-1 for terminal) + Qubit v{}; // variable index std::uint8_t flags = 0; // 32 = marks a node with is symmetric. // 16 = marks a node resembling identity @@ -87,13 +100,10 @@ struct dNode { // 2 = mark first path edge (tmp flag), // 1 = mark path is conjugated (tmp flag)) - // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) - static dNode terminal; - static constexpr bool isTerminal(const dNode* p) noexcept { - return p == &terminal; + return p == nullptr; } - static constexpr dNode* getTerminal() noexcept { return &terminal; } + static constexpr dNode* getTerminal() noexcept { return nullptr; } [[nodiscard]] [[maybe_unused]] static inline bool tempDensityMatrixFlagsEqual(const std::uint8_t a, @@ -191,8 +201,7 @@ static inline dEdge densityFromMatrixEdge(const mEdge& e) { template [[nodiscard]] static inline bool noRefCountingNeeded(const Node* const p) noexcept { - return p == nullptr || Node::isTerminal(p) || - p->ref == std::numeric_limits::max(); + return Node::isTerminal(p) || p->ref == std::numeric_limits::max(); } /** diff --git a/include/dd/NoiseFunctionality.hpp b/include/dd/NoiseFunctionality.hpp index d814a24a5..208761f74 100644 --- a/include/dd/NoiseFunctionality.hpp +++ b/include/dd/NoiseFunctionality.hpp @@ -5,12 +5,12 @@ #include #include -using CN = dd::ComplexNumbers; -using ArrayOfEdges = - std::array>; - namespace dd { +using CN = ComplexNumbers; +using NrEdges = std::tuple_size; +using ArrayOfEdges = std::array; + // noise operations available for deterministic noise aware quantum circuit // simulation enum NoiseOperations : std::uint8_t { @@ -22,8 +22,9 @@ enum NoiseOperations : std::uint8_t { template class StochasticNoiseFunctionality { public: - StochasticNoiseFunctionality(const std::unique_ptr>& dd, - dd::QubitCount nq, double gateNoiseProbability, + StochasticNoiseFunctionality(const std::unique_ptr>& dd, + const std::size_t nq, + double gateNoiseProbability, double amplitudeDampingProb, double multiQubitGateFactor, std::vector effects) @@ -57,9 +58,9 @@ template class StochasticNoiseFunctionality { protected: // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members) - const std::unique_ptr>& package; - dd::QubitCount nQubits; - std::uniform_real_distribution dist; + const std::unique_ptr>& package; + std::size_t nQubits; + std::uniform_real_distribution dist; double noiseProbability; double noiseProbabilityMulti; @@ -74,7 +75,7 @@ template class StochasticNoiseFunctionality { std::vector noiseEffects; mEdge identityDD; - [[nodiscard]] dd::QubitCount getNumberOfQubits() const { return nQubits; } + [[nodiscard]] std::size_t getNumberOfQubits() const { return nQubits; } [[nodiscard]] double getNoiseProbability(bool multiQubitNoiseFlag) const { return multiQubitNoiseFlag ? noiseProbabilityMulti : noiseProbability; } @@ -108,9 +109,8 @@ template class StochasticNoiseFunctionality { const bool multiQubitOperation = targets.size() > 1; for (const auto& target : targets) { - auto stackedOperation = - generateNoiseOperation(operation, static_cast(target), - generator, false, multiQubitOperation); + auto stackedOperation = generateNoiseOperation( + operation, target, generator, false, multiQubitOperation); auto tmp = package->multiply(stackedOperation, state); if (ComplexNumbers::mag2(tmp.w) < dist(generator)) { @@ -119,12 +119,11 @@ template class StochasticNoiseFunctionality { // normalization constraint of decision diagrams the probability for // applying amplitude damping stands in the root edge weight, of the dd // after the noise has been applied - stackedOperation = - generateNoiseOperation(operation, static_cast(target), - generator, true, multiQubitOperation); + stackedOperation = generateNoiseOperation(operation, target, generator, + true, multiQubitOperation); tmp = package->multiply(stackedOperation, state); } - tmp.w = dd::Complex::one; + tmp.w = Complex::one; package->incRef(tmp); package->decRef(state); @@ -136,13 +135,12 @@ template class StochasticNoiseFunctionality { } protected: - [[nodiscard]] dd::mEdge stackOperation(dd::mEdge operation, - const dd::Qubit target, - const qc::OpType noiseOperation, - const GateMatrix matrix) { + [[nodiscard]] mEdge stackOperation(mEdge operation, const qc::Qubit target, + const qc::OpType noiseOperation, + const GateMatrix matrix) { auto tmpOperation = package->stochasticNoiseOperationCache.lookup(noiseOperation, target); - if (tmpOperation.p == nullptr) { + if (tmpOperation.isTerminal()) { tmpOperation = package->makeGateDD(matrix, getNumberOfQubits(), target); package->stochasticNoiseOperationCache.insert(noiseOperation, target, tmpOperation); @@ -150,10 +148,10 @@ template class StochasticNoiseFunctionality { return package->multiply(tmpOperation, operation); } - dd::mEdge generateNoiseOperation(dd::mEdge operation, dd::Qubit target, - std::mt19937_64& generator, - bool amplitudeDamping, - bool multiQubitOperation) { + mEdge generateNoiseOperation(mEdge operation, qc::Qubit target, + std::mt19937_64& generator, + bool amplitudeDamping, + bool multiQubitOperation) { for (const auto& noiseType : noiseEffects) { const auto effect = noiseType == AmplitudeDamping ? getAmplitudeDampingOperationType( @@ -243,12 +241,14 @@ template class StochasticNoiseFunctionality { template class DeterministicNoiseFunctionality { public: - DeterministicNoiseFunctionality( - const std::unique_ptr>& dd, dd::QubitCount nq, - double noiseProbabilitySingleQubit, double noiseProbabilityMultiQubit, - double ampDampProbSingleQubit, double ampDampProbMultiQubit, - std::vector effects, bool useDensityMatType, - bool seqApplyNoise) + DeterministicNoiseFunctionality(const std::unique_ptr>& dd, + const std::size_t nq, + double noiseProbabilitySingleQubit, + double noiseProbabilityMultiQubit, + double ampDampProbSingleQubit, + double ampDampProbMultiQubit, + std::vector effects, + bool useDensityMatType, bool seqApplyNoise) : package(dd), nQubits(nq), noiseProbSingleQubit(noiseProbabilitySingleQubit), noiseProbMultiQubit(noiseProbabilityMultiQubit), @@ -260,8 +260,8 @@ template class DeterministicNoiseFunctionality { protected: // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members) - const std::unique_ptr>& package; - dd::QubitCount nQubits; + const std::unique_ptr>& package; + std::size_t nQubits; double noiseProbSingleQubit; double noiseProbMultiQubit; @@ -280,7 +280,7 @@ template class DeterministicNoiseFunctionality { {Depolarization, 4}, // Depolarisation }; - [[nodiscard]] dd::QubitCount getNumberOfQubits() const { return nQubits; } + [[nodiscard]] std::size_t getNumberOfQubits() const { return nQubits; } public: void applyNoiseEffects(qc::DensityMatrixDD& originalEdge, @@ -318,7 +318,7 @@ template class DeterministicNoiseFunctionality { qc::DensityMatrixDD applyNoiseEffects(qc::DensityMatrixDD& originalEdge, const std::set& usedQubits, bool firstPathEdge) { - if (originalEdge.p->v < static_cast(*usedQubits.begin())) { + if (originalEdge.isTerminal() || originalEdge.p->v < *usedQubits.begin()) { if (ComplexNumbers::isStaticComplex(originalEdge.w)) { return originalEdge; } @@ -349,7 +349,7 @@ template class DeterministicNoiseFunctionality { qc::DensityMatrixDD e = {}; if (std::any_of(usedQubits.begin(), usedQubits.end(), [originalEdge](const qc::Qubit qubit) { - return originalEdge.p->v == static_cast(qubit); + return originalEdge.p->v == qubit; })) { for (auto const& type : noiseEffects) { switch (type) { @@ -545,13 +545,12 @@ template class DeterministicNoiseFunctionality { const std::set& targets) { qc::DensityMatrixDD tmp = {}; - std::array> - idleOperation{}; + std::array idleOperation{}; // Iterate over qubits and check if the qubit had been used for (const auto targetQubit : targets) { for (auto const& type : noiseEffects) { - generateGate(idleOperation, type, static_cast(targetQubit), + generateGate(idleOperation, type, targetQubit, getNoiseProbability(type, targets)); tmp.p = nullptr; // Apply all noise matrices of the current noise effect @@ -581,12 +580,10 @@ template class DeterministicNoiseFunctionality { } void generateDepolarizationGate( - std::array>& - pointerForMatrices, - const dd::Qubit target, const double probability) { - std::array> - idleNoiseGate{}; - dd::ComplexValue tmp = {}; + std::array& pointerForMatrices, + const qc::Qubit target, const double probability) { + std::array idleNoiseGate{}; + ComplexValue tmp = {}; tmp.r = std::sqrt(1 - ((3 * probability) / 4)) * complex_one.r; // (1 0) @@ -630,14 +627,11 @@ template class DeterministicNoiseFunctionality { package->makeGateDD(idleNoiseGate[3], getNumberOfQubits(), target); } - void - generateGate(std::array>& - pointerForMatrices, - const dd::NoiseOperations noiseType, const dd::Qubit target, - const double probability) { - std::array> - idleNoiseGate{}; - dd::ComplexValue tmp = {}; + void generateGate(std::array& pointerForMatrices, + const NoiseOperations noiseType, const qc::Qubit target, + const double probability) { + std::array idleNoiseGate{}; + ComplexValue tmp = {}; switch (noiseType) { // identity noise (for testing) diff --git a/include/dd/Operations.hpp b/include/dd/Operations.hpp index 241bd07ab..ceab605b1 100644 --- a/include/dd/Operations.hpp +++ b/include/dd/Operations.hpp @@ -15,12 +15,13 @@ template qc::MatrixDD getStandardOperationDD(const qc::StandardOperation* op, std::unique_ptr>& dd, const qc::Controls& controls, - dd::Qubit target, bool inverse) { + const qc::Qubit target, + const bool inverse) { GateMatrix gm; const auto type = op->getType(); - const auto nqubits = static_cast(op->getNqubits()); - const auto startQubit = static_cast(op->getStartingQubit()); + const auto nqubits = op->getNqubits(); + const auto startQubit = op->getStartingQubit(); const auto& parameter = op->getParameter(); switch (type) { @@ -94,13 +95,13 @@ qc::MatrixDD getStandardOperationDD(const qc::StandardOperation* op, // two-target Operations template qc::MatrixDD getStandardOperationDD(const qc::StandardOperation* op, - std::unique_ptr>& dd, + std::unique_ptr>& dd, const qc::Controls& controls, - dd::Qubit target0, dd::Qubit target1, - bool inverse) { + qc::Qubit target0, qc::Qubit target1, + const bool inverse) { const auto type = op->getType(); - const auto nqubits = static_cast(op->getNqubits()); - const auto startQubit = static_cast(op->getStartingQubit()); + const auto nqubits = op->getNqubits(); + const auto startQubit = op->getStartingQubit(); const auto& parameter = op->getParameter(); if (type == qc::DCX && inverse) { @@ -269,12 +270,12 @@ qc::MatrixDD getDD(const qc::Operation* op, const auto target1 = targets[1U]; // update permutation std::swap(permutation.at(target0), permutation.at(target1)); - return dd->makeIdent(static_cast(nqubits)); + return dd->makeIdent(nqubits); } if (type == qc::ShowProbabilities || type == qc::Barrier || type == qc::Snapshot) { - return dd->makeIdent(static_cast(nqubits)); + return dd->makeIdent(nqubits); } if (type == qc::GPhase) { @@ -282,7 +283,7 @@ qc::MatrixDD getDD(const qc::Operation* op, if (inverse) { phase = -phase; } - auto id = dd->makeIdent(static_cast(nqubits)); + auto id = dd->makeIdent(nqubits); id.w = dd->cn.lookup(std::cos(phase), std::sin(phase)); return id; } @@ -297,18 +298,16 @@ qc::MatrixDD getDD(const qc::Operation* op, if (qc::isTwoQubitGate(type)) { assert(targets.size() == 2); - const auto target0 = static_cast(targets[0U]); - const auto target1 = static_cast(targets[1U]); - return getStandardOperationDD(standardOp, dd, controls, target0, target1, - inverse); + return getStandardOperationDD(standardOp, dd, controls, targets[0U], + targets[1U], inverse); } assert(targets.size() == 1); - const auto target0 = static_cast(targets[0U]); - return getStandardOperationDD(standardOp, dd, controls, target0, inverse); + return getStandardOperationDD(standardOp, dd, controls, targets[0U], + inverse); } if (const auto* compoundOp = dynamic_cast(op)) { - auto e = dd->makeIdent(static_cast(op->getNqubits())); + auto e = dd->makeIdent(op->getNqubits()); if (inverse) { for (const auto& operation : *compoundOp) { e = dd->multiply(e, getInverseDD(operation.get(), dd, permutation)); @@ -392,9 +391,8 @@ void changePermutation(DDType& on, qc::Permutation& from, // swap i and j auto saved = on; - const auto swapDD = dd->makeSWAPDD( - static_cast(on.p->v + 1), qc::Controls{}, - static_cast(from.at(i)), static_cast(from.at(j))); + const auto swapDD = dd->makeSWAPDD(static_cast(on.p->v) + 1, + qc::Controls{}, from.at(i), from.at(j)); if constexpr (std::is_same_v) { on = dd->multiply(swapDD, on); } else { diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index 3fc58bebf..f8b05c050 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -50,10 +50,8 @@ template class Package { /// public: static constexpr std::size_t MAX_POSSIBLE_QUBITS = - static_cast>( - std::numeric_limits::max()) + - 1U; - static constexpr std::size_t DEFAULT_QUBITS = 128; + static_cast(std::numeric_limits::max()) + 1U; + static constexpr std::size_t DEFAULT_QUBITS = 32U; explicit Package(std::size_t nq = DEFAULT_QUBITS) : nqubits(nq) { resize(nq); }; @@ -145,14 +143,11 @@ template class Package { } /// The unique table used for vector nodes - UniqueTable vUniqueTable{nqubits, - vMemoryManager}; + UniqueTable vUniqueTable{0U, vMemoryManager}; /// The unique table used for matrix nodes - UniqueTable mUniqueTable{nqubits, - mMemoryManager}; + UniqueTable mUniqueTable{0U, mMemoryManager}; /// The unique table used for density matrix nodes - UniqueTable dUniqueTable{nqubits, - dMemoryManager}; + UniqueTable dUniqueTable{0U, dMemoryManager}; /** * @brief The unique table used for complex numbers * @note The table actually only stores real numbers in the interval [0, 1], @@ -392,7 +387,7 @@ template class Package { return r; } - dEdge makeZeroDensityOperator(QubitCount n) { + dEdge makeZeroDensityOperator(const std::size_t n) { auto f = dEdge::one; for (std::size_t p = 0; p < n; p++) { f = makeDDNode(static_cast(p), @@ -402,7 +397,7 @@ template class Package { } // generate |0...0> with n qubits - vEdge makeZeroState(QubitCount n, std::size_t start = 0) { + vEdge makeZeroState(const std::size_t n, const std::size_t start = 0) { if (n + start > nqubits) { throw std::runtime_error{ "Requested state with " + std::to_string(n + start) + @@ -417,8 +412,8 @@ template class Package { return f; } // generate computational basis state |i> with n qubits - vEdge makeBasisState(QubitCount n, const std::vector& state, - std::size_t start = 0) { + vEdge makeBasisState(const std::size_t n, const std::vector& state, + const std::size_t start = 0) { if (n + start > nqubits) { throw std::runtime_error{ "Requested state with " + std::to_string(n + start) + @@ -437,8 +432,9 @@ template class Package { return f; } // generate general basis state with n qubits - vEdge makeBasisState(QubitCount n, const std::vector& state, - std::size_t start = 0) { + vEdge makeBasisState(const std::size_t n, + const std::vector& state, + const std::size_t start = 0) { if (n + start > nqubits) { throw std::runtime_error{ "Requested state with " + std::to_string(n + start) + @@ -656,18 +652,19 @@ template class Package { } // build matrix representation for a single gate on an n-qubit circuit - mEdge makeGateDD(const std::array& mat, QubitCount n, - Qubit target, std::size_t start = 0) { + mEdge makeGateDD(const std::array& mat, + const std::size_t n, const qc::Qubit target, + const std::size_t start = 0) { return makeGateDD(mat, n, qc::Controls{}, target, start); } - mEdge makeGateDD(const std::array& mat, QubitCount n, - const qc::Control& control, Qubit target, - std::size_t start = 0) { + mEdge makeGateDD(const std::array& mat, + const std::size_t n, const qc::Control& control, + const qc::Qubit target, const std::size_t start = 0) { return makeGateDD(mat, n, qc::Controls{control}, target, start); } - mEdge makeGateDD(const std::array& mat, QubitCount n, - const qc::Controls& controls, Qubit target, - std::size_t start = 0) { + mEdge makeGateDD(const std::array& mat, + const std::size_t n, const qc::Controls& controls, + const qc::Qubit target, const std::size_t start = 0) { if (n + start > nqubits) { throw std::runtime_error{ "Requested gate with " + std::to_string(n + start) + @@ -688,33 +685,40 @@ template class Package { // process lines below target auto z = static_cast(start); - for (; z < target; z++) { - for (auto i1 = 0U; i1 < RADIX; i1++) { - for (auto i2 = 0U; i2 < RADIX; i2++) { + for (; z < static_cast(target); ++z) { + for (auto i1 = 0U; i1 < RADIX; ++i1) { + for (auto i2 = 0U; i2 < RADIX; ++i2) { auto i = i1 * RADIX + i2; - if (it != controls.end() && it->qubit == static_cast(z)) { + if (it != controls.end() && it->qubit == z) { + auto edges = + std::array{mEdge::zero, mEdge::zero, mEdge::zero, mEdge::zero}; if (it->type == qc::Control::Type::Neg) { // neg. control - em[i] = makeDDNode( - z, - std::array{em[i], mEdge::zero, mEdge::zero, - (i1 == i2) ? makeIdent(static_cast(start), - static_cast(z - 1)) - : mEdge::zero}); + edges[0] = em[i]; + if (i1 == i2) { + if (z == 0U) { + edges[3] = mEdge::one; + } else { + edges[3] = makeIdent(start, z - 1U); + } + } } else { // pos. control - em[i] = makeDDNode( - z, - std::array{(i1 == i2) ? makeIdent(static_cast(start), - static_cast(z - 1)) - : mEdge::zero, - mEdge::zero, mEdge::zero, em[i]}); + edges[3] = em[i]; + if (i1 == i2) { + if (z == 0U) { + edges[0] = mEdge::one; + } else { + edges[0] = makeIdent(start, z - 1U); + } + } } + em[i] = makeDDNode(z, edges); } else { // not connected em[i] = makeDDNode( z, std::array{em[i], mEdge::zero, mEdge::zero, em[i]}); } } } - if (it != controls.end() && it->qubit == static_cast(z)) { + if (it != controls.end() && it->qubit == z) { ++it; } } @@ -728,12 +732,10 @@ template class Package { if (it != controls.end() && it->qubit == static_cast(q)) { if (it->type == qc::Control::Type::Neg) { // neg. control e = makeDDNode(q, std::array{e, mEdge::zero, mEdge::zero, - makeIdent(static_cast(start), - static_cast(q - 1))}); + makeIdent(start, q - 1U)}); } else { // pos. control - e = makeDDNode(q, std::array{makeIdent(static_cast(start), - static_cast(q - 1)), - mEdge::zero, mEdge::zero, e}); + e = makeDDNode(q, std::array{makeIdent(start, q - 1U), mEdge::zero, + mEdge::zero, e}); } ++it; } else { // not connected @@ -756,7 +758,7 @@ template class Package { **/ mEdge makeTwoQubitGateDD( const std::array, NEDGE>& mat, - const QubitCount n, const Qubit target0, const Qubit target1, + const std::size_t n, const qc::Qubit target0, const qc::Qubit target1, const std::size_t start = 0) { // sanity check if (n + start > nqubits) { @@ -834,82 +836,82 @@ template class Package { auto e = makeDDNode(z, em0); // process lines above the larger target (by creating identity structures) - for (++z; z < static_cast(n + start); ++z) { + for (++z; z < n + start; ++z) { e = makeDDNode(z, std::array{e, mEdge::zero, mEdge::zero, e}); } return e; } - mEdge makeSWAPDD(const QubitCount n, const Qubit target0, const Qubit target1, - const std::size_t start = 0) { + mEdge makeSWAPDD(const std::size_t n, const qc::Qubit target0, + const qc::Qubit target1, const std::size_t start = 0) { return makeTwoQubitGateDD(SWAPmat, n, target0, target1, start); } - mEdge makeSWAPDD(const QubitCount n, const qc::Controls& controls, - const Qubit target0, const Qubit target1, + mEdge makeSWAPDD(const std::size_t n, const qc::Controls& controls, + const qc::Qubit target0, const qc::Qubit target1, const std::size_t start = 0) { auto c = controls; - c.insert(qc::Control{static_cast(target0)}); + c.insert(qc::Control{target0}); mEdge e = makeGateDD(Xmat, n, c, target1, start); - c.erase(qc::Control{static_cast(target0)}); - c.insert(qc::Control{static_cast(target1)}); + c.erase(qc::Control{target0}); + c.insert(qc::Control{target1}); e = multiply(e, multiply(makeGateDD(Xmat, n, c, target0, start), e)); return e; } - mEdge makePeresDD(const QubitCount n, const qc::Controls& controls, - const Qubit target0, const Qubit target1, + mEdge makePeresDD(const std::size_t n, const qc::Controls& controls, + const qc::Qubit target0, const qc::Qubit target1, const std::size_t start = 0) { auto c = controls; - c.insert(qc::Control{static_cast(target1)}); + c.insert(qc::Control{target1}); mEdge e = makeGateDD(Xmat, n, c, target0, start); e = multiply(makeGateDD(Xmat, n, controls, target1, start), e); return e; } - mEdge makePeresdagDD(const QubitCount n, const qc::Controls& controls, - const Qubit target0, const Qubit target1, + mEdge makePeresdagDD(const std::size_t n, const qc::Controls& controls, + const qc::Qubit target0, const qc::Qubit target1, const std::size_t start = 0) { mEdge e = makeGateDD(Xmat, n, controls, target1, start); auto c = controls; - c.insert(qc::Control{static_cast(target1)}); + c.insert(qc::Control{target1}); e = multiply(makeGateDD(Xmat, n, c, target0, start), e); return e; } - mEdge makeiSWAPDD(const QubitCount n, const Qubit target0, - const Qubit target1, const std::size_t start = 0) { + mEdge makeiSWAPDD(const std::size_t n, const qc::Qubit target0, + const qc::Qubit target1, const std::size_t start = 0) { return makeTwoQubitGateDD(iSWAPmat, n, target0, target1, start); } - mEdge makeiSWAPDD(const QubitCount n, const qc::Controls& controls, - const Qubit target0, const Qubit target1, + mEdge makeiSWAPDD(const std::size_t n, const qc::Controls& controls, + const qc::Qubit target0, const qc::Qubit target1, const std::size_t start = 0) { mEdge e = makeGateDD(Smat, n, controls, target1, start); // S q[1] e = multiply(e, makeGateDD(Smat, n, controls, target0, start)); // S q[0] e = multiply(e, makeGateDD(Hmat, n, controls, target0, start)); // H q[0] auto c = controls; - c.insert(qc::Control{static_cast(target0)}); + c.insert(qc::Control{target0}); e = multiply(e, makeGateDD(Xmat, n, c, target1, start)); // CX q[0], q[1] - c.erase(qc::Control{static_cast(target0)}); - c.insert(qc::Control{static_cast(target1)}); + c.erase(qc::Control{target0}); + c.insert(qc::Control{target1}); e = multiply(e, makeGateDD(Xmat, n, c, target0, start)); // CX q[1], q[0] e = multiply(e, makeGateDD(Hmat, n, controls, target1, start)); // H q[1] return e; } - mEdge makeiSWAPinvDD(const QubitCount n, const Qubit target0, - const Qubit target1, const std::size_t start = 0) { + mEdge makeiSWAPinvDD(const std::size_t n, const qc::Qubit target0, + const qc::Qubit target1, const std::size_t start = 0) { return makeTwoQubitGateDD(iSWAPinvmat, n, target0, target1, start); } - mEdge makeiSWAPinvDD(const QubitCount n, const qc::Controls& controls, - const Qubit target0, const Qubit target1, + mEdge makeiSWAPinvDD(const std::size_t n, const qc::Controls& controls, + const qc::Qubit target0, const qc::Qubit target1, const std::size_t start = 0) { mEdge e = makeGateDD(Hmat, n, controls, target1, start); // H q[1] auto c = controls; - c.insert(qc::Control{static_cast(target1)}); + c.insert(qc::Control{target1}); e = multiply(e, makeGateDD(Xmat, n, c, target0, start)); // CX q[1], q[0] - c.erase(qc::Control{static_cast(target1)}); - c.insert(qc::Control{static_cast(target0)}); + c.erase(qc::Control{target1}); + c.insert(qc::Control{target0}); e = multiply(e, makeGateDD(Xmat, n, c, target1, start)); // CX q[0], q[1] e = multiply(e, makeGateDD(Hmat, n, controls, target0, start)); // H q[0] e = multiply(e, @@ -919,46 +921,48 @@ template class Package { return e; } - mEdge makeDCXDD(const QubitCount n, const Qubit target0, const Qubit target1, - const std::size_t start = 0) { + mEdge makeDCXDD(const std::size_t n, const qc::Qubit target0, + const qc::Qubit target1, const std::size_t start = 0) { return makeTwoQubitGateDD(DCXmat, n, target0, target1, start); } - mEdge makeDCXDD(const QubitCount n, const qc::Controls& controls, - const Qubit target0, const Qubit target1, + mEdge makeDCXDD(const std::size_t n, const qc::Controls& controls, + const qc::Qubit target0, const qc::Qubit target1, const std::size_t start = 0) { auto c = controls; - c.insert(qc::Control{static_cast(target0)}); + c.insert(qc::Control{target0}); mEdge e = makeGateDD(Xmat, n, c, target1, start); - c.erase(qc::Control{static_cast(target0)}); - c.insert(qc::Control{static_cast(target1)}); + c.erase(qc::Control{target0}); + c.insert(qc::Control{target1}); e = multiply(e, makeGateDD(Xmat, n, c, target0, start)); return e; } - mEdge makeRZZDD(const QubitCount n, const Qubit target0, const Qubit target1, - const fp theta, const std::size_t start = 0) { + mEdge makeRZZDD(const std::size_t n, const qc::Qubit target0, + const qc::Qubit target1, const fp theta, + const std::size_t start = 0) { return makeTwoQubitGateDD(RZZmat(theta), n, target0, target1, start); } - mEdge makeRZZDD(const QubitCount n, const qc::Controls& controls, - const Qubit target0, const Qubit target1, const fp theta, - const std::size_t start = 0) { + mEdge makeRZZDD(const std::size_t n, const qc::Controls& controls, + const qc::Qubit target0, const qc::Qubit target1, + const fp theta, const std::size_t start = 0) { auto c = controls; - c.insert(qc::Control{static_cast(target0)}); + c.insert(qc::Control{target0}); auto e = makeGateDD(Xmat, n, c, target1, start); - c.erase(qc::Control{static_cast(target0)}); + c.erase(qc::Control{target0}); e = multiply(e, makeGateDD(RZmat(theta), n, c, target1, start)); - c.insert(qc::Control{static_cast(target0)}); + c.insert(qc::Control{target0}); e = multiply(e, makeGateDD(Xmat, n, c, target1, start)); return e; } - mEdge makeRYYDD(const QubitCount n, const Qubit target0, const Qubit target1, - const fp theta, const std::size_t start = 0) { + mEdge makeRYYDD(const std::size_t n, const qc::Qubit target0, + const qc::Qubit target1, const fp theta, + const std::size_t start = 0) { return makeTwoQubitGateDD(RYYmat(theta), n, target0, target1, start); } - mEdge makeRYYDD(const QubitCount n, const qc::Controls& controls, - const Qubit target0, const Qubit target1, const fp theta, - const std::size_t start = 0) { + mEdge makeRYYDD(const std::size_t n, const qc::Controls& controls, + const qc::Qubit target0, const qc::Qubit target1, + const fp theta, const std::size_t start = 0) { // no controls are necessary on the RX gates since they cancel if the // controls are 0. auto e = makeGateDD(RXmat(PI_2), n, qc::Controls{}, target0, start); @@ -971,13 +975,14 @@ template class Package { return e; } - mEdge makeRXXDD(const QubitCount n, const Qubit target0, const Qubit target1, - const fp theta, const std::size_t start = 0) { + mEdge makeRXXDD(const std::size_t n, const qc::Qubit target0, + const qc::Qubit target1, const fp theta, + const std::size_t start = 0) { return makeTwoQubitGateDD(RXXmat(theta), n, target0, target1, start); } - mEdge makeRXXDD(const QubitCount n, const qc::Controls& controls, - const Qubit target0, const Qubit target1, const fp theta, - const std::size_t start = 0) { + mEdge makeRXXDD(const std::size_t n, const qc::Controls& controls, + const qc::Qubit target0, const qc::Qubit target1, + const fp theta, const std::size_t start = 0) { // no controls are necessary on the H gates since they cancel if the // controls are 0. auto e = makeGateDD(Hmat, n, qc::Controls{}, target0, start); @@ -988,13 +993,14 @@ template class Package { return e; } - mEdge makeRZXDD(const QubitCount n, const Qubit target0, const Qubit target1, - const fp theta, const std::size_t start = 0) { + mEdge makeRZXDD(const std::size_t n, const qc::Qubit target0, + const qc::Qubit target1, const fp theta, + const std::size_t start = 0) { return makeTwoQubitGateDD(RZXmat(theta), n, target0, target1, start); } - mEdge makeRZXDD(const QubitCount n, const qc::Controls& controls, - const Qubit target0, const Qubit target1, const fp theta, - const std::size_t start = 0) { + mEdge makeRZXDD(const std::size_t n, const qc::Controls& controls, + const qc::Qubit target0, const qc::Qubit target1, + const fp theta, const std::size_t start = 0) { // no controls are necessary on the H gates since they cancel if the // controls are 0. auto e = makeGateDD(Hmat, n, qc::Controls{}, target1, start); @@ -1003,12 +1009,12 @@ template class Package { return e; } - mEdge makeECRDD(const QubitCount n, const Qubit target0, const Qubit target1, - const std::size_t start = 0) { + mEdge makeECRDD(const std::size_t n, const qc::Qubit target0, + const qc::Qubit target1, const std::size_t start = 0) { return makeTwoQubitGateDD(ECRmat, n, target0, target1, start); } - mEdge makeECRDD(const QubitCount n, const qc::Controls& controls, - const Qubit target0, const Qubit target1, + mEdge makeECRDD(const std::size_t n, const qc::Controls& controls, + const qc::Qubit target0, const qc::Qubit target1, const std::size_t start = 0) { auto e = makeRZXDD(n, controls, target0, target1, -PI_4, start); e = multiply(e, makeGateDD(Xmat, n, controls, target0, start)); @@ -1016,14 +1022,14 @@ template class Package { return e; } - mEdge makeXXMinusYYDD(const QubitCount n, const Qubit target0, - const Qubit target1, const fp theta, const fp beta = 0., - const std::size_t start = 0) { + mEdge makeXXMinusYYDD(const std::size_t n, const qc::Qubit target0, + const qc::Qubit target1, const fp theta, + const fp beta = 0., const std::size_t start = 0) { return makeTwoQubitGateDD(XXMinusYYmat(theta, beta), n, target0, target1, start); } - mEdge makeXXMinusYYDD(const QubitCount n, const qc::Controls& controls, - const Qubit target0, const Qubit target1, + mEdge makeXXMinusYYDD(const std::size_t n, const qc::Controls& controls, + const qc::Qubit target0, const qc::Qubit target1, const fp theta, const fp beta = 0., const std::size_t start = 0) { auto e = makeGateDD(RZmat(-beta), n, qc::Controls{}, target1, start); @@ -1032,18 +1038,14 @@ template class Package { e = multiply(e, makeGateDD(SXmat, n, qc::Controls{}, target0, start)); e = multiply(e, makeGateDD(RZmat(PI_2), n, qc::Controls{}, target0, start)); e = multiply(e, makeGateDD(Smat, n, qc::Controls{}, target1, start)); - e = multiply(e, makeGateDD(Xmat, n, - qc::Control{static_cast(target0)}, - target1, start)); + e = multiply(e, makeGateDD(Xmat, n, qc::Control{target0}, target1, start)); // only the following two gates need to be controlled by the controls since // the other gates cancel if the controls are 0. e = multiply(e, makeGateDD(RYmat(-theta / 2.), n, controls, target0, start)); e = multiply(e, makeGateDD(RYmat(theta / 2.), n, controls, target1, start)); - e = multiply(e, makeGateDD(Xmat, n, - qc::Control{static_cast(target0)}, - target1, start)); + e = multiply(e, makeGateDD(Xmat, n, qc::Control{target0}, target1, start)); e = multiply(e, makeGateDD(Sdagmat, n, qc::Controls{}, target1, start)); e = multiply(e, makeGateDD(RZmat(-PI_2), n, qc::Controls{}, target0, start)); @@ -1053,32 +1055,29 @@ template class Package { return e; } - mEdge makeXXPlusYYDD(const QubitCount n, const Qubit target0, - const Qubit target1, const fp theta, const fp beta = 0., - const std::size_t start = 0) { + mEdge makeXXPlusYYDD(const std::size_t n, const qc::Qubit target0, + const qc::Qubit target1, const fp theta, + const fp beta = 0., const std::size_t start = 0) { return makeTwoQubitGateDD(XXPlusYYmat(theta, beta), n, target0, target1, start); } - mEdge makeXXPlusYYDD(const QubitCount n, const qc::Controls& controls, - const Qubit target0, const Qubit target1, const fp theta, - const fp beta = 0., const std::size_t start = 0) { + mEdge makeXXPlusYYDD(const std::size_t n, const qc::Controls& controls, + const qc::Qubit target0, const qc::Qubit target1, + const fp theta, const fp beta = 0., + const std::size_t start = 0) { auto e = makeGateDD(RZmat(beta), n, qc::Controls{}, target1, start); e = multiply(e, makeGateDD(RZmat(-PI_2), n, qc::Controls{}, target0, start)); e = multiply(e, makeGateDD(SXmat, n, qc::Controls{}, target0, start)); e = multiply(e, makeGateDD(RZmat(PI_2), n, qc::Controls{}, target0, start)); e = multiply(e, makeGateDD(Smat, n, qc::Controls{}, target1, start)); - e = multiply(e, makeGateDD(Xmat, n, - qc::Control{static_cast(target0)}, - target1, start)); + e = multiply(e, makeGateDD(Xmat, n, qc::Control{target0}, target1, start)); // only the following two gates need to be controlled by the controls since // the other gates cancel if the controls are 0. e = multiply(e, makeGateDD(RYmat(theta / 2.), n, controls, target0, start)); e = multiply(e, makeGateDD(RYmat(theta / 2.), n, controls, target1, start)); - e = multiply(e, makeGateDD(Xmat, n, - qc::Control{static_cast(target0)}, - target1, start)); + e = multiply(e, makeGateDD(Xmat, n, qc::Control{target0}, target1, start)); e = multiply(e, makeGateDD(Sdagmat, n, qc::Controls{}, target1, start)); e = multiply(e, makeGateDD(RZmat(-PI_2), n, qc::Controls{}, target0, start)); @@ -1092,7 +1091,7 @@ template class Package { private: // check whether node represents a symmetric matrix or the identity void checkSpecialMatrices(mNode* p) { - if (p->v == -1) { + if (mNode::isTerminal(p)) { return; } @@ -1100,7 +1099,9 @@ template class Package { p->setSymmetric(false); // check if matrix is symmetric - if (!p->e[0].p->isSymmetric() || !p->e[3].p->isSymmetric()) { + const auto& e0 = p->e[0]; + const auto& e3 = p->e[3]; + if (!mNode::isSymmetric(e0.p) || !mNode::isSymmetric(e3.p)) { return; } if (transpose(p->e[1]) != p->e[2]) { @@ -1109,9 +1110,11 @@ template class Package { p->setSymmetric(true); // check if matrix resembles identity - if (!(p->e[0].p->isIdentity()) || !p->e[1].w.exactlyZero() || - !p->e[2].w.exactlyZero() || !p->e[0].w.exactlyOne() || - !p->e[3].w.exactlyOne() || !(p->e[3].p->isIdentity())) { + const auto& e1 = p->e[1]; + const auto& e2 = p->e[2]; + if (!mNode::isIdentity(e0.p) || !e1.w.exactlyZero() || + !e2.w.exactlyZero() || !e0.w.exactlyOne() || !e3.w.exactlyOne() || + !mNode::isIdentity(e3.p)) { return; } p->setIdentity(true); @@ -1120,7 +1123,7 @@ template class Package { vEdge makeStateFromVector(const CVec::const_iterator& begin, const CVec::const_iterator& end, const Qubit level) { - if (level == 0) { + if (level == 0U) { assert(std::distance(begin, end) == 2); const auto& zeroWeight = cn.getCached(*begin); const auto& oneWeight = cn.getCached(*std::next(begin)); @@ -1147,7 +1150,6 @@ template class Package { @param colStart The starting column of the quadrant being processed. @param colEnd The ending column of the quadrant being processed. @return An mEdge representing the root node of the created DD. - @throw std::invalid_argument If level is negative. @details This function recursively breaks down the matrix into quadrants until each quadrant has only one element. At each level of recursion, four new edges are created, one for each quadrant of the matrix. The four resulting decision @@ -1161,10 +1163,18 @@ template class Package { const std::size_t rowStart, const std::size_t rowEnd, const std::size_t colStart, const std::size_t colEnd) { // base case - if (level == -1) { - assert(rowEnd - rowStart == 1); - assert(colEnd - colStart == 1); - return {mNode::getTerminal(), cn.getCached(matrix[rowStart][colStart])}; + if (level == 0U) { + assert(rowEnd - rowStart == 2); + assert(colEnd - colStart == 2); + const auto w0 = cn.getCached(matrix[rowStart][colStart]); + const auto e0 = mEdge{mNode::getTerminal(), w0}; + const auto w1 = cn.getCached(matrix[rowStart + 1][colStart]); + const auto e1 = mEdge{mNode::getTerminal(), w1}; + const auto w2 = cn.getCached(matrix[rowStart][colStart + 1]); + const auto e2 = mEdge{mNode::getTerminal(), w2}; + const auto w3 = cn.getCached(matrix[rowStart + 1][colStart + 1]); + const auto e3 = mEdge{mNode::getTerminal(), w3}; + return makeDDNode(0U, {e0, e1, e2, e3}, true); } // recursively call the function on all quadrants @@ -1188,9 +1198,9 @@ template class Package { // not recreated if it already exists. template Edge makeDDNode( - Qubit var, + const Qubit var, const std::array, std::tuple_size_v>& edges, - bool cached = false, + const bool cached = false, [[maybe_unused]] const bool generateDensityMatrix = false) { auto& memoryManager = getMemoryManager(); Edge e{memoryManager.get(), Complex::one}; @@ -1208,17 +1218,17 @@ template class Package { for ([[maybe_unused]] const auto& edge : edges) { // an error here indicates that cached nodes are assigned multiple times. // Check if garbage collect correctly resets the cache tables! - assert(edge.p->v == var - 1 || edge.isTerminal()); + assert(edge.isTerminal() || edge.p->v == var - 1); } // normalize it e = normalize(e, cached); - assert(e.p->v == var || e.isTerminal()); + assert(e.isTerminal() || e.p->v == var); // look it up in the unique tables auto& uniqueTable = getUniqueTable(); auto l = uniqueTable.lookup(e, false); - assert(l.p->v == var || l.isTerminal()); + assert(l.isTerminal() || l.p->v == var); // set specific node properties for matrices if constexpr (std::is_same_v) { @@ -1230,16 +1240,18 @@ template class Package { } template - Edge deleteEdge(const Edge& e, dd::Qubit v, std::size_t edgeIdx) { + Edge deleteEdge(const Edge& e, const Qubit v, + const std::size_t edgeIdx) { std::unordered_map> nodes{}; return deleteEdge(e, v, edgeIdx, nodes); } private: template - Edge deleteEdge(const Edge& e, dd::Qubit v, std::size_t edgeIdx, + Edge deleteEdge(const Edge& e, const Qubit v, + const std::size_t edgeIdx, std::unordered_map>& nodes) { - if (e.p == nullptr || e.isTerminal()) { + if (e.isTerminal()) { return e; } @@ -1303,7 +1315,7 @@ template class Package { /// Measurements from state decision diagrams /// std::string measureAll(vEdge& rootEdge, const bool collapse, - std::mt19937_64& mt, fp epsilon = 0.001) { + std::mt19937_64& mt, const fp epsilon = 0.001) { if (std::abs(ComplexNumbers::mag2(rootEdge.w) - 1.0) > epsilon) { if (rootEdge.w.approximatelyZero()) { throw std::runtime_error( @@ -1314,14 +1326,18 @@ template class Package { << ComplexNumbers::mag2(rootEdge.w) << ", but should be 1!\n"; } + if (rootEdge.isTerminal()) { + return ""; + } + vEdge cur = rootEdge; - const auto numberOfQubits = static_cast(rootEdge.p->v + 1); + const auto numberOfQubits = static_cast(rootEdge.p->v) + 1U; std::string result(numberOfQubits, '0'); std::uniform_real_distribution dist(0.0, 1.0L); - for (Qubit i = rootEdge.p->v; i >= 0; --i) { + for (auto i = numberOfQubits; i > 0; --i) { fp p0 = ComplexNumbers::mag2(cur.p->e.at(0).w); const fp p1 = ComplexNumbers::mag2(cur.p->e.at(1).w); const fp tmp = p0 + p1; @@ -1336,7 +1352,7 @@ template class Package { if (threshold < p0) { cur = cur.p->e.at(0); } else { - result[static_cast(cur.p->v)] = '1'; + result[cur.p->v] = '1'; cur = cur.p->e.at(1); } } @@ -1347,15 +1363,15 @@ template class Package { vEdge e = vEdge::one; std::array edges{}; - for (Qubit p = 0; p < numberOfQubits; p++) { - if (result[static_cast(p)] == '0') { + for (std::size_t p = 0U; p < numberOfQubits; ++p) { + if (result[p] == '0') { edges[0] = e; edges[1] = vEdge::zero; } else { edges[0] = vEdge::zero; edges[1] = e; } - e = makeDDNode(p, edges, false); + e = makeDDNode(static_cast(p), edges, false); } incRef(e); rootEdge = e; @@ -1367,15 +1383,15 @@ template class Package { private: double assignProbabilities(const vEdge& edge, - std::unordered_map& probs) { + std::unordered_map& probs) { auto it = probs.find(edge.p); if (it != probs.end()) { return ComplexNumbers::mag2(edge.w) * it->second; } double sum{1}; if (!edge.isTerminal()) { - sum = assignProbabilities(edge.p->e.at(0), probs) + - assignProbabilities(edge.p->e.at(1), probs); + sum = assignProbabilities(edge.p->e[0], probs) + + assignProbabilities(edge.p->e[1], probs); } probs.insert({edge.p, sum}); @@ -1387,40 +1403,42 @@ template class Package { std::pair determineMeasurementProbabilities(const vEdge& rootEdge, const Qubit index, const bool assumeProbabilityNormalization) { - std::map probsMone; - std::set visited; - std::queue q; + std::map probsMone; + std::set visited; + std::queue q; probsMone[rootEdge.p] = ComplexNumbers::mag2(rootEdge.w); visited.insert(rootEdge.p); q.push(rootEdge.p); while (q.front()->v != index) { - vNode* ptr = q.front(); + const auto* ptr = q.front(); q.pop(); const fp prob = probsMone[ptr]; - if (!ptr->e.at(0).w.approximatelyZero()) { - const fp tmp1 = prob * ComplexNumbers::mag2(ptr->e.at(0).w); + const auto& s0 = ptr->e[0]; + if (!s0.w.approximatelyZero()) { + const fp tmp1 = prob * ComplexNumbers::mag2(s0.w); - if (visited.find(ptr->e.at(0).p) != visited.end()) { - probsMone[ptr->e.at(0).p] = probsMone[ptr->e.at(0).p] + tmp1; + if (visited.find(s0.p) != visited.end()) { + probsMone[s0.p] = probsMone[s0.p] + tmp1; } else { - probsMone[ptr->e.at(0).p] = tmp1; - visited.insert(ptr->e.at(0).p); - q.push(ptr->e.at(0).p); + probsMone[s0.p] = tmp1; + visited.insert(s0.p); + q.push(s0.p); } } - if (!ptr->e.at(1).w.approximatelyZero()) { - const fp tmp1 = prob * ComplexNumbers::mag2(ptr->e.at(1).w); + const auto& s1 = ptr->e[1]; + if (!s1.w.approximatelyZero()) { + const fp tmp1 = prob * ComplexNumbers::mag2(s1.w); - if (visited.find(ptr->e.at(1).p) != visited.end()) { - probsMone[ptr->e.at(1).p] = probsMone[ptr->e.at(1).p] + tmp1; + if (visited.find(s1.p) != visited.end()) { + probsMone[s1.p] = probsMone[s1.p] + tmp1; } else { - probsMone[ptr->e.at(1).p] = tmp1; - visited.insert(ptr->e.at(1).p); - q.push(ptr->e.at(1).p); + probsMone[s1.p] = tmp1; + visited.insert(s1.p); + q.push(s1.p); } } } @@ -1430,33 +1448,32 @@ template class Package { if (assumeProbabilityNormalization) { while (!q.empty()) { - vNode* ptr = q.front(); + const auto* ptr = q.front(); q.pop(); - - if (!ptr->e.at(0).w.approximatelyZero()) { - pzero += probsMone[ptr] * ComplexNumbers::mag2(ptr->e.at(0).w); + const auto& s0 = ptr->e[0]; + if (!s0.w.approximatelyZero()) { + pzero += probsMone[ptr] * ComplexNumbers::mag2(s0.w); } - - if (!ptr->e.at(1).w.approximatelyZero()) { - pone += probsMone[ptr] * ComplexNumbers::mag2(ptr->e.at(1).w); + const auto& s1 = ptr->e[1]; + if (!s1.w.approximatelyZero()) { + pone += probsMone[ptr] * ComplexNumbers::mag2(s1.w); } } } else { - std::unordered_map probs; + std::unordered_map probs; assignProbabilities(rootEdge, probs); while (!q.empty()) { - vNode* ptr = q.front(); + const auto* ptr = q.front(); q.pop(); - if (!ptr->e.at(0).w.approximatelyZero()) { - pzero += probsMone[ptr] * probs[ptr->e.at(0).p] * - ComplexNumbers::mag2(ptr->e.at(0).w); + const auto& s0 = ptr->e[0]; + if (!s0.w.approximatelyZero()) { + pzero += probsMone[ptr] * probs[s0.p] * ComplexNumbers::mag2(s0.w); } - - if (!ptr->e.at(1).w.approximatelyZero()) { - pone += probsMone[ptr] * probs[ptr->e.at(1).p] * - ComplexNumbers::mag2(ptr->e.at(1).w); + const auto& s1 = ptr->e[1]; + if (!s1.w.approximatelyZero()) { + pone += probsMone[ptr] * probs[s1.p] * ComplexNumbers::mag2(s1.w); } } } @@ -1522,8 +1539,7 @@ template class Package { } const auto measurementGate = - makeGateDD(measurementMatrix, - static_cast(rootEdge.p->v + 1), index); + makeGateDD(measurementMatrix, rootEdge.p->v + 1U, index); vEdge e = multiply(measurementGate, rootEdge); @@ -1572,13 +1588,6 @@ template class Package { template Edge add2(const Edge& x, const Edge& y) { - if (x.p == nullptr) { - return y; - } - if (y.p == nullptr) { - return x; - } - if (x.w.exactlyZero()) { if (y.w.exactlyZero()) { return Edge::zero; @@ -1600,9 +1609,7 @@ template class Package { auto& computeTable = getAddComputeTable(); auto r = computeTable.lookup({x.p, x.w}, {y.p, y.w}); - // if (r.p != nullptr && false) { // activate for debugging - // caching only - if (r.p != nullptr) { + if (!Node::isTerminal(r.p)) { if (r.w.approximatelyZero()) { return Edge::zero; } @@ -1625,8 +1632,8 @@ template class Package { } } else { e1 = x; - if (y.p->e[i].p == nullptr) { - e1 = {nullptr, Complex::zero}; + if (y.p->e[i].isTerminal()) { + e1 = Edge::zero; } } Edge e2{}; @@ -1638,8 +1645,8 @@ template class Package { } } else { e2 = y; - if (x.p->e[i].p == nullptr) { - e2 = {nullptr, Complex::zero}; + if (x.p->e[i].isTerminal()) { + e2 = Edge::zero; } } @@ -1662,11 +1669,6 @@ template class Package { auto e = makeDDNode(w, edge, true); - // if (r.p != nullptr && e.p != r.p){ // activate for debugging - // caching only - // std::cout << "Caching error detected in add" << std::endl; - // } - computeTable.insert({x.p, x.w}, {y.p, y.w}, {e.p, e.w}); return e; } @@ -1680,13 +1682,13 @@ template class Package { conjugateMatrixTranspose{}; mEdge transpose(const mEdge& a) { - if (a.p == nullptr || a.isTerminal() || a.p->isSymmetric()) { + if (a.isTerminal() || a.p->isSymmetric()) { return a; } // check in compute table auto r = matrixTranspose.lookup(a); - if (r.p != nullptr) { + if (!r.isTerminal()) { return r; } @@ -1707,18 +1709,13 @@ template class Package { return r; } mEdge conjugateTranspose(const mEdge& a) { - if (a.p == nullptr) { - return a; - } if (a.isTerminal()) { // terminal case - auto r = a; - r.w = ComplexNumbers::conj(a.w); - return r; + return {a.p, ComplexNumbers::conj(a.w)}; } // check if in compute table auto r = conjugateMatrixTranspose.lookup(a); - if (r.p != nullptr) { + if (!r.isTerminal()) { return r; } @@ -1762,7 +1759,7 @@ template class Package { } dEdge applyOperationToDensity(dEdge& e, const mEdge& operation, - bool generateDensityMatrix = false) { + const bool generateDensityMatrix = false) { [[maybe_unused]] const auto before = cn.cacheCount(); auto tmp0 = conjugateTranspose(operation); auto tmp1 = multiply(e, densityFromMatrixEdge(tmp0), 0, false); @@ -1781,12 +1778,12 @@ template class Package { } template - RightOperand multiply(const LeftOperand& x, const RightOperand& y, - dd::Qubit start = 0, - [[maybe_unused]] bool generateDensityMatrix = false) { + RightOperand + multiply(const LeftOperand& x, const RightOperand& y, const Qubit start = 0, + [[maybe_unused]] const bool generateDensityMatrix = false) { [[maybe_unused]] const auto before = cn.cacheCount(); - Qubit var = -1; + Qubit var{}; RightOperand e; if constexpr (std::is_same_v) { @@ -1795,25 +1792,19 @@ template class Package { dEdge::applyDmChangesToEdges(xCopy, yCopy); if (!xCopy.isTerminal()) { - assert(xCopy.p != nullptr); var = xCopy.p->v; } if (!y.isTerminal() && yCopy.p->v > var) { - assert(xCopy.p != nullptr); var = yCopy.p->v; } e = multiply2(xCopy, yCopy, var, start, generateDensityMatrix); - dEdge::revertDmChangesToEdges(xCopy, yCopy); - } else { if (!x.isTerminal()) { - assert(x.p != nullptr); var = x.p->v; } if (!y.isTerminal() && (y.p->v) > var) { - assert(y.p != nullptr); var = y.p->v; } e = multiply2(x, y, var, start); @@ -1831,24 +1822,17 @@ template class Package { template Edge multiply2(const Edge& x, const Edge& y, - Qubit var, Qubit start = 0, - [[maybe_unused]] bool generateDensityMatrix = false) { + const Qubit var, const Qubit start = 0, + [[maybe_unused]] const bool generateDensityMatrix = false) { using LEdge = Edge; using REdge = Edge; using ResultEdge = Edge; - if (x.p == nullptr) { - return {nullptr, Complex::zero}; - } - if (y.p == nullptr) { - return y; - } - if (x.w.exactlyZero() || y.w.exactlyZero()) { return ResultEdge::zero; } - if (var == start - 1) { + if (x.isTerminal() && y.isTerminal()) { return ResultEdge::terminal(cn.mulCached(x.w, y.w)); } @@ -1858,9 +1842,7 @@ template class Package { auto& computeTable = getMultiplicationComputeTable(); auto r = computeTable.lookup(xCopy, yCopy, generateDensityMatrix); - // if (r.p != nullptr && false) { // activate for debugging - // caching only - if (r.p != nullptr) { + if (!RightOperandNode::isTerminal(r.p)) { if (r.w.approximatelyZero()) { return ResultEdge::zero; } @@ -1875,7 +1857,6 @@ template class Package { } constexpr std::size_t n = std::tuple_size_ve)>; - ResultEdge e{}; if constexpr (std::is_same_v) { // This branch is only taken for matrices @@ -1994,12 +1975,6 @@ template class Package { } } e = makeDDNode(var, edge, true, generateDensityMatrix); - - // if (r.p != nullptr && e.p != r.p) { // activate for debugging - // caching - // std::cout << "Caching error detected in mul" << std::endl; - // } - computeTable.insert(xCopy, yCopy, {e.p, e.w}); if (!e.w.exactlyZero()) { @@ -2031,7 +2006,7 @@ template class Package { @return a complex number representing the scalar product of the DDs **/ ComplexValue innerProduct(const vEdge& x, const vEdge& y) { - if (x.p == nullptr || y.p == nullptr || x.w.approximatelyZero() || + if (x.isTerminal() || y.isTerminal() || x.w.approximatelyZero() || y.w.approximatelyZero()) { // the 0 case return {0, 0}; } @@ -2045,7 +2020,7 @@ template class Package { // Overall normalization factor needs to be conjugated // before input into recursive private function auto xCopy = vEdge{x.p, ComplexNumbers::conj(x.w)}; - const ComplexValue ip = innerProduct(xCopy, y, static_cast(w + 1)); + const ComplexValue ip = innerProduct(xCopy, y, w + 1U); [[maybe_unused]] const auto after = cn.cacheCount(); assert(after == before); @@ -2091,8 +2066,7 @@ template class Package { fidelityOfMeasurementOutcomesRecursive(e.p->e[1], probs, rightIdx); } - const dd::fp fidelity = topw * (leftContribution + rightContribution); - return fidelity; + return topw * (leftContribution + rightContribution); } private: @@ -2107,8 +2081,7 @@ template class Package { decreases each time to traverse the DDs. **/ ComplexValue innerProduct(const vEdge& x, const vEdge& y, Qubit var) { - if (x.p == nullptr || y.p == nullptr || x.w.approximatelyZero() || - y.w.approximatelyZero()) { // the 0 case + if (x.w.approximatelyZero() || y.w.approximatelyZero()) { // the 0 case return {0.0, 0.0}; } @@ -2121,15 +2094,14 @@ template class Package { auto xCopy = vEdge{x.p, Complex::one}; auto yCopy = vEdge{y.p, Complex::one}; auto r = vectorInnerProduct.lookup(xCopy, yCopy); - if (r.p != nullptr) { + if (!vNode::isTerminal(r.p)) { auto c = cn.getTemporary(r.w); ComplexNumbers::mul(c, c, x.w); ComplexNumbers::mul(c, c, y.w); - return {RealNumber::val(c.r), RealNumber::val(c.i)}; + return {c.r->value, c.i->value}; } - auto w = static_cast(var - 1); - + auto w = static_cast(var - 1U); ComplexValue sum{0.0, 0.0}; // Iterates through edge weights recursively until terminal for (auto i = 0U; i < RADIX; i++) { @@ -2150,14 +2122,13 @@ template class Package { sum.r += cv.r; sum.i += cv.i; } - r.p = vNode::getTerminal(); r.w = sum; vectorInnerProduct.insert(xCopy, yCopy, r); auto c = cn.getTemporary(sum); ComplexNumbers::mul(c, c, x.w); ComplexNumbers::mul(c, c, y.w); - return {RealNumber::val(c.r), RealNumber::val(c.i)}; + return {c.r->value, c.i->value}; } public: @@ -2177,7 +2148,7 @@ template class Package { memory. **/ fp expectationValue(const mEdge& x, const vEdge& y) { - assert(x.p != nullptr && y.p != nullptr); + assert(!x.isTerminal() && !y.isTerminal()); if (x.p->v != y.p->v) { throw std::invalid_argument( "Observable and state must act on the same number of qubits to " @@ -2212,7 +2183,7 @@ template class Package { } template - Edge kronecker(const Edge& x, const Edge& y, bool incIdx = true) { + Edge kronecker(const Edge& x, const Edge& y, const bool incIdx = true) { if constexpr (std::is_same_v) { throw std::invalid_argument( "Kronecker is currently not supported for density matrices"); @@ -2226,18 +2197,16 @@ template class Package { // extent the DD pointed to by `e` with `h` identities on top and `l` // identities at the bottom - mEdge extend(const mEdge& e, Qubit h, Qubit l = 0) { - auto f = - (l > 0) ? kronecker(e, makeIdent(static_cast(l))) : e; - auto g = - (h > 0) ? kronecker(makeIdent(static_cast(h)), f) : f; + mEdge extend(const mEdge& e, const Qubit h, const Qubit l = 0) { + auto f = (l > 0) ? kronecker(e, makeIdent(l)) : e; + auto g = (h > 0) ? kronecker(makeIdent(h), f) : f; return g; } private: template Edge kronecker2(const Edge& x, const Edge& y, - bool incIdx = true) { + const bool incIdx = true) { if (x.w.approximatelyZero() || y.w.approximatelyZero()) { return Edge::zero; } @@ -2248,7 +2217,7 @@ template class Package { auto& computeTable = getKroneckerComputeTable(); auto r = computeTable.lookup(x, y); - if (r.p != nullptr) { + if (!Node::isTerminal(r.p)) { if (r.w.approximatelyZero()) { return Edge::zero; } @@ -2263,7 +2232,7 @@ template class Package { auto e = makeDDNode( idx, std::array{y, Edge::zero, Edge::zero, y}); for (auto i = 0; i < x.p->v; ++i) { - idx = incIdx ? static_cast(e.p->v + 1) : e.p->v; + idx = incIdx ? (e.p->v + 1) : e.p->v; e = makeDDNode(idx, std::array{e, Edge::zero, Edge::zero, e}); } @@ -2279,8 +2248,8 @@ template class Package { edge[i] = kronecker2(x.p->e[i], y, incIdx); } - auto idx = incIdx ? static_cast(y.p->v + x.p->v + 1) : x.p->v; - auto e = makeDDNode(idx, edge, true); + auto idx = incIdx ? (y.p->v + x.p->v + 1) : x.p->v; + auto e = makeDDNode(static_cast(idx), edge, true); ComplexNumbers::mul(e.w, e.w, x.w); computeTable.insert(x, y, {e.p, e.w}); return e; @@ -2298,14 +2267,14 @@ template class Package { return result; } ComplexValue trace(const mEdge& a) { - auto eliminate = std::vector(nqubits, true); + const auto eliminate = std::vector(nqubits, true); [[maybe_unused]] const auto before = cn.cacheCount(); const auto res = partialTrace(a, eliminate); [[maybe_unused]] const auto after = cn.cacheCount(); assert(before == after); return {RealNumber::val(res.w.r), RealNumber::val(res.w.i)}; } - bool isCloseToIdentity(const mEdge& m, dd::fp tol = 1e-10) { + bool isCloseToIdentity(const mEdge& m, const dd::fp tol = 1e-10) { std::unordered_set visited{}; visited.reserve(mUniqueTable.getStats().activeEntryCount); return isCloseToIdentityRecursive(m, visited, tol); @@ -2319,28 +2288,21 @@ template class Package { return mEdge::zero; } - if (std::none_of(eliminate.begin(), eliminate.end(), - [](bool v) { return v; })) { + if (a.isTerminal() || std::none_of(eliminate.begin(), eliminate.end(), + [](bool v) { return v; })) { return a; } - auto v = a.p->v; - // Base case - if (v == -1) { - if (a.isTerminal()) { - return a; - } - throw std::runtime_error("Expected terminal node in trace."); - } - if (eliminate[static_cast(v)]) { - auto elims = alreadyEliminated + 1; + const auto v = a.p->v; + if (eliminate[v]) { + const auto elims = alreadyEliminated + 1; auto r = mEdge::zero; - auto t0 = trace(a.p->e[0], eliminate, elims); + const auto t0 = trace(a.p->e[0], eliminate, elims); r = add2(r, t0); auto r1 = r; - auto t1 = trace(a.p->e[3], eliminate, elims); + const auto t1 = trace(a.p->e[3], eliminate, elims); r = add2(r, t1); auto r2 = r; @@ -2364,13 +2326,12 @@ template class Package { [this, &eliminate, &alreadyEliminated](const mEdge& e) -> mEdge { return trace(e, eliminate, alreadyEliminated); }); - auto adjustedV = + const auto adjustedV = static_cast(static_cast(a.p->v) - (static_cast(std::count( eliminate.begin(), eliminate.end(), true)) - alreadyEliminated)); auto r = makeDDNode(adjustedV, edge); - if (r.w.exactlyOne()) { r.w = a.w; } else { @@ -2381,14 +2342,14 @@ template class Package { bool isCloseToIdentityRecursive(const mEdge& m, std::unordered_set& visited, - dd::fp tol) { + const dd::fp tol) { // 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 - if (m.p->isIdentity()) { + if (m.isTerminal() || m.p->isIdentity()) { return true; } @@ -2446,42 +2407,42 @@ template class Package { /// Identity matrices /// // create n-qubit identity DD. makeIdent(n) === makeIdent(0, n-1) - mEdge makeIdent(QubitCount n) { - return makeIdent(0, static_cast(n - 1)); + mEdge makeIdent(const std::size_t n) { + if (n == 0U) { + return mEdge::one; + } + return makeIdent(0, n - 1); } - mEdge makeIdent(Qubit leastSignificantQubit, Qubit mostSignificantQubit) { + mEdge makeIdent(const std::size_t leastSignificantQubit, + const std::size_t mostSignificantQubit) { if (mostSignificantQubit < leastSignificantQubit) { return mEdge::one; } - const auto& entry = - idTable.at(static_cast(mostSignificantQubit)); + const auto& entry = idTable.at(mostSignificantQubit); if (leastSignificantQubit == 0 && entry.p != nullptr) { return entry; } if (mostSignificantQubit >= 1) { - const auto& prevEntry = - idTable.at(static_cast(mostSignificantQubit - 1)); - if (prevEntry.p != nullptr) { - idTable.at(static_cast(mostSignificantQubit)) = makeDDNode( - mostSignificantQubit, + const auto& prevEntry = idTable.at(mostSignificantQubit - 1); + if (!prevEntry.isTerminal()) { + idTable.at(mostSignificantQubit) = makeDDNode( + static_cast(mostSignificantQubit), std::array{prevEntry, mEdge::zero, mEdge::zero, prevEntry}); - return idTable[static_cast(mostSignificantQubit)]; + return idTable[mostSignificantQubit]; } } - auto e = - makeDDNode(leastSignificantQubit, std::array{mEdge::one, mEdge::zero, - mEdge::zero, mEdge::one}); - for (auto k = static_cast(leastSignificantQubit + 1); - k <= static_cast>(mostSignificantQubit); - k++) { + auto e = makeDDNode( + static_cast(leastSignificantQubit), + std::array{mEdge::one, mEdge::zero, mEdge::zero, mEdge::one}); + for (auto k = leastSignificantQubit + 1; k <= mostSignificantQubit; ++k) { e = makeDDNode(static_cast(k), std::array{e, mEdge::zero, mEdge::zero, e}); } if (leastSignificantQubit == 0) { - idTable.at(static_cast(mostSignificantQubit)) = e; + idTable.at(mostSignificantQubit) = e; } return e; } @@ -2495,7 +2456,7 @@ template class Package { } } - mEdge createInitialMatrix(dd::QubitCount n, + mEdge createInitialMatrix(const std::size_t n, const std::vector& ancillary) { auto e = makeIdent(n); incRef(e); @@ -2516,8 +2477,8 @@ template class Package { /// /// Decision diagram size /// - template unsigned int size(const Edge& e) { - static constexpr unsigned int NODECOUNT_BUCKETS = 200000; + template std::size_t size(const Edge& e) { + static constexpr std::size_t NODECOUNT_BUCKETS = 200000U; static std::unordered_set visited{NODECOUNT_BUCKETS}; // 2e6 visited.max_load_factor(10); visited.clear(); @@ -2526,13 +2487,13 @@ template class Package { private: template - unsigned int nodeCount(const Edge& e, - std::unordered_set& v) const { + std::size_t nodeCount(const Edge& e, + std::unordered_set& v) const { v.insert(e.p); - unsigned int sum = 1; + std::size_t sum = 1U; if (!e.isTerminal()) { for (const auto& edge : e.p->e) { - if (edge.p != nullptr && !v.count(edge.p)) { + if (!v.count(edge.p)) { sum += nodeCount(edge, v); } } @@ -2545,11 +2506,11 @@ template class Package { /// public: mEdge reduceAncillae(mEdge& e, const std::vector& ancillary, - bool regular = true) { + const bool regular = true) { // return if no more garbage left if (std::none_of(ancillary.begin(), ancillary.end(), [](bool v) { return v; }) || - e.p == nullptr) { + e.isTerminal()) { return e; } Qubit lowerbound = 0; @@ -2574,7 +2535,7 @@ template class Package { // return if no more garbage left if (std::none_of(garbage.begin(), garbage.end(), [](bool v) { return v; }) || - e.p == nullptr) { + e.isTerminal()) { return e; } Qubit lowerbound = 0; @@ -2593,11 +2554,11 @@ template class Package { return f; } mEdge reduceGarbage(mEdge& e, const std::vector& garbage, - bool regular = true) { + const bool regular = true) { // return if no more garbage left if (std::none_of(garbage.begin(), garbage.end(), [](bool v) { return v; }) || - e.p == nullptr) { + e.isTerminal()) { return e; } Qubit lowerbound = 0; @@ -2618,7 +2579,8 @@ template class Package { private: mEdge reduceAncillaeRecursion(mEdge& e, const std::vector& ancillary, - Qubit lowerbound, bool regular = true) { + const Qubit lowerbound, + const bool regular = true) { if (e.p->v < lowerbound) { return e; } @@ -2647,7 +2609,7 @@ template class Package { f = makeDDNode(f.p->v, edges); // something to reduce for this qubit - if (f.p->v >= 0 && ancillary[static_cast(f.p->v)]) { + if (f.p->v >= 0 && ancillary[f.p->v]) { if (regular) { if (f.p->e[1].w != Complex::zero || f.p->e[3].w != Complex::zero) { f = makeDDNode(f.p->v, std::array{f.p->e[0], mEdge::zero, f.p->e[2], @@ -2665,7 +2627,7 @@ template class Package { } vEdge reduceGarbageRecursion(vEdge& e, const std::vector& garbage, - Qubit lowerbound) { + const Qubit lowerbound) { if (e.p->v < lowerbound) { return e; } @@ -2693,7 +2655,7 @@ template class Package { f = makeDDNode(f.p->v, edges); // something to reduce for this qubit - if (f.p->v >= 0 && garbage[static_cast(f.p->v)]) { + if (f.p->v >= 0 && garbage[f.p->v]) { if (f.p->e[1].w != Complex::zero) { vEdge g{}; if (f.p->e[0].w == Complex::zero && f.p->e[1].w != Complex::zero) { @@ -2716,7 +2678,8 @@ template class Package { return f; } mEdge reduceGarbageRecursion(mEdge& e, const std::vector& garbage, - Qubit lowerbound, bool regular = true) { + const Qubit lowerbound, + const bool regular = true) { if (e.p->v < lowerbound) { return e; } @@ -2745,7 +2708,7 @@ template class Package { f = makeDDNode(f.p->v, edges); // something to reduce for this qubit - if (f.p->v >= 0 && garbage[static_cast(f.p->v)]) { + if (f.p->v >= 0 && garbage[f.p->v]) { if (regular) { if (f.p->e[2].w != Complex::zero || f.p->e[3].w != Complex::zero) { mEdge g{}; @@ -2821,8 +2784,7 @@ template class Package { // NOLINTNEXTLINE(cppcoreguidelines-avoid-do-while) do { ComplexNumbers::mul(c, c, r.w); - auto tmp = static_cast( - elements.at(static_cast(r.p->v)) - '0'); + auto tmp = static_cast(elements.at(r.p->v) - '0'); assert(tmp <= r.p->e.size()); r = r.p->e.at(tmp); } while (!r.isTerminal()); @@ -2830,14 +2792,14 @@ template class Package { return {RealNumber::val(c.r), RealNumber::val(c.i)}; } - ComplexValue getValueByPath(const vEdge& e, std::size_t i) { + ComplexValue getValueByPath(const vEdge& e, const std::size_t i) { if (e.isTerminal()) { return {RealNumber::val(e.w.r), RealNumber::val(e.w.i)}; } return getValueByPath(e, Complex::one, i); } ComplexValue getValueByPath(const vEdge& e, const Complex& amp, - std::size_t i) { + const std::size_t i) { auto c = cn.mulCached(e.w, amp); if (e.isTerminal()) { @@ -2856,14 +2818,15 @@ template class Package { cn.returnToCache(c); return r; } - ComplexValue getValueByPath(const mEdge& e, std::size_t i, std::size_t j) { + ComplexValue getValueByPath(const mEdge& e, const std::size_t i, + const std::size_t j) { if (e.isTerminal()) { return {RealNumber::val(e.w.r), RealNumber::val(e.w.i)}; } return getValueByPath(e, Complex::one, i, j); } - ComplexValue getValueByPath(const mEdge& e, const Complex& amp, std::size_t i, - std::size_t j) { + ComplexValue getValueByPath(const mEdge& e, const Complex& amp, + const std::size_t i, const std::size_t j) { auto c = cn.mulCached(e.w, amp); if (e.isTerminal()) { @@ -2889,7 +2852,7 @@ template class Package { } std::map - getProbVectorFromDensityMatrix(dEdge e, double measurementThreshold) { + getProbVectorFromDensityMatrix(dEdge e, const double measurementThreshold) { dEdge::alignDensityEdge(e); if (std::pow(2, e.p->v + 1) >= static_cast(std::numeric_limits::max())) { @@ -2905,7 +2868,7 @@ template class Package { auto resultString = intToString(m, '1', e.p->v + 1); dEdge cur = e; for (dd::Qubit i = 0; i < e.p->v + 1; ++i) { - if (cur.p->v == -1 || globalProbability <= measurementThreshold) { + if (cur.isTerminal() || globalProbability <= measurementThreshold) { globalProbability = 0; break; } @@ -2930,12 +2893,13 @@ template class Package { return measuredResult; } - [[nodiscard]] std::string intToString(std::size_t targetNumber, char value, - dd::Qubit size) const { - std::string path(static_cast(size), '0'); - for (auto i = 1; i <= size; i++) { + [[nodiscard]] std::string intToString(std::size_t targetNumber, + const char value, + const Qubit size) const { + std::string path(size, '0'); + for (auto i = 1U; i <= size; ++i) { if ((targetNumber % 2) != 0U) { - path[static_cast(size - i)] = value; + path[size - i] = value; } targetNumber = targetNumber >> 1U; } @@ -2949,7 +2913,8 @@ template class Package { getVector(e, Complex::one, 0, vec); return vec; } - void getVector(const vEdge& e, const Complex& amp, std::size_t i, CVec& vec) { + void getVector(const vEdge& e, const Complex& amp, const std::size_t i, + CVec& vec) { // calculate new accumulated amplitude auto c = cn.mulCached(e.w, amp); @@ -2976,8 +2941,9 @@ template class Package { const std::size_t element = 2ULL << e.p->v; for (auto i = 0ULL; i < element; i++) { const auto amplitude = getValueByPath(e, i); - for (Qubit j = e.p->v; j >= 0; j--) { - std::cout << ((i >> j) & 1ULL); + const auto n = static_cast(e.p->v) + 1U; + for (auto j = n; j > 0; --j) { + std::cout << ((i >> (j - 1)) & 1ULL); } constexpr auto precision = 3; // set fixed width to maximum of a printed number @@ -3017,8 +2983,8 @@ template class Package { getMatrix(e, Complex::one, 0, 0, mat); return mat; } - void getMatrix(const mEdge& e, const Complex& amp, std::size_t i, - std::size_t j, CMat& mat) { + void getMatrix(const mEdge& e, const Complex& amp, const std::size_t i, + const std::size_t j, CMat& mat) { // calculate new accumulated amplitude auto c = cn.mulCached(e.w, amp); @@ -3058,8 +3024,8 @@ template class Package { return mat; } - void getDensityMatrix(dEdge& e, const Complex& amp, std::size_t i, - std::size_t j, CMat& mat) { + void getDensityMatrix(dEdge& e, const Complex& amp, const std::size_t i, + const std::size_t j, CMat& mat) { // calculate new accumulated amplitude auto c = cn.mulCached(e.w, amp); @@ -3100,7 +3066,7 @@ template class Package { void exportAmplitudesRec(const vEdge& edge, std::ostream& oss, const std::string& path, Complex& amplitude, - dd::QubitCount level, bool binary = false) { + const std::size_t level, const bool binary = false) { if (edge.isTerminal()) { auto amp = cn.getTemporary(); dd::ComplexNumbers::mul(amp, amplitude, edge.w); @@ -3120,8 +3086,8 @@ template class Package { exportAmplitudesRec(edge.p->e[1], oss, path + "1", a, level - 1, binary); cn.returnToCache(a); } - void exportAmplitudes(const vEdge& edge, std::ostream& oss, dd::QubitCount nq, - bool binary = false) { + void exportAmplitudes(const vEdge& edge, std::ostream& oss, + const std::size_t nq, const bool binary = false) { if (edge.isTerminal()) { // TODO special treatment return; @@ -3131,7 +3097,7 @@ template class Package { cn.returnToCache(weight); } void exportAmplitudes(const vEdge& edge, const std::string& outputFilename, - dd::QubitCount nq, bool binary = false) { + const std::size_t nq, const bool binary = false) { std::ofstream init(outputFilename); std::ostringstream oss{}; @@ -3143,7 +3109,7 @@ template class Package { void exportAmplitudesRec(const vEdge& edge, std::vector>& amplitudes, - Complex& amplitude, dd::QubitCount level, + Complex& amplitude, const std::size_t level, std::size_t idx) { if (edge.isTerminal()) { auto amp = cn.getTemporary(); @@ -3165,7 +3131,7 @@ template class Package { } void exportAmplitudes(const vEdge& edge, std::vector>& amplitudes, - dd::QubitCount nq) { + const std::size_t nq) { if (edge.isTerminal()) { // TODO special treatment return; @@ -3177,7 +3143,7 @@ template class Package { void addAmplitudesRec(const vEdge& edge, std::vector>& amplitudes, - ComplexValue& amplitude, dd::QubitCount level, + ComplexValue& amplitude, const std::size_t level, std::size_t idx) { const auto ar = RealNumber::val(edge.w.r); const auto ai = RealNumber::val(edge.w.i); @@ -3200,7 +3166,7 @@ template class Package { } void addAmplitudes(const vEdge& edge, std::vector>& amplitudes, - dd::QubitCount nq) { + const std::size_t nq) { if (edge.isTerminal()) { // TODO special treatment return; @@ -3297,7 +3263,7 @@ template class Package { template , std::size_t N = std::tuple_size_v> - Edge deserialize(std::istream& is, bool readBinary = false) { + Edge deserialize(std::istream& is, const bool readBinary = false) { auto result = Edge::zero; ComplexValue rootweight{}; @@ -3409,7 +3375,7 @@ template class Package { } template > - Edge deserialize(const std::string& inputFilename, bool readBinary) { + Edge deserialize(const std::string& inputFilename, const bool readBinary) { auto ifs = std::ifstream(inputFilename, std::ios::binary); if (!ifs.good()) { @@ -3423,9 +3389,9 @@ template class Package { private: template , std::size_t N = std::tuple_size_v> - Edge deserializeNode(std::int64_t index, Qubit v, + Edge deserializeNode(const std::int64_t index, const Qubit v, std::array& edgeIdx, - std::array& edgeWeight, + const std::array& edgeWeight, std::unordered_map& nodes) { if (index == -1) { return Edge::zero; @@ -3526,7 +3492,7 @@ template class Package { } for (const auto& child : e.p->e) { - if (child.p->v + 1 != e.p->v && !child.isTerminal()) { + if (!child.isTerminal() && child.p->v + 1 != e.p->v) { std::clog << "\nLOCAL INCONSISTENCY FOUND: Wrong V\n"; debugnode(e.p); return false; @@ -3642,6 +3608,10 @@ template class Package { << alignof(mEdge) << " bytes)" << "\n mNode size: " << sizeof(mNode) << " bytes (aligned " << alignof(mNode) << " bytes)" + << "\n dEdge size: " << sizeof(dEdge) << " bytes (aligned " + << alignof(dEdge) << " bytes)" + << "\n dNode size: " << sizeof(dNode) << " bytes (aligned " + << alignof(dNode) << " bytes)" << "\n CT Vector Add size: " << sizeof(typename decltype(vectorAdd)::Entry) << " bytes (aligned " @@ -3691,10 +3661,10 @@ template class Package { // print unique and compute table statistics void statistics() { std::cout << "DD statistics:\n"; - std::cout << "[vUniqueTable] " << vUniqueTable.getStats(); - std::cout << "[mUniqueTable] " << mUniqueTable.getStats(); - std::cout << "[dUniqueTable] " << dUniqueTable.getStats(); - std::cout << "[cUniqueTable] " << cUniqueTable.getStats(); + std::cout << "[vUniqueTable] " << vUniqueTable.getStats() << "\n"; + std::cout << "[mUniqueTable] " << mUniqueTable.getStats() << "\n"; + std::cout << "[dUniqueTable] " << dUniqueTable.getStats() << "\n"; + std::cout << "[cUniqueTable] " << cUniqueTable.getStats() << "\n"; std::cout << "[CT Vector Add] "; vectorAdd.printStatistics(); std::cout << "[CT Matrix Add] "; diff --git a/include/dd/StochasticNoiseOperationTable.hpp b/include/dd/StochasticNoiseOperationTable.hpp index 5b0c0d35c..5400ae925 100644 --- a/include/dd/StochasticNoiseOperationTable.hpp +++ b/include/dd/StochasticNoiseOperationTable.hpp @@ -1,5 +1,6 @@ #pragma once +#include "Definitions.hpp" #include "dd/DDDefinitions.hpp" #include @@ -25,23 +26,23 @@ class StochasticNoiseOperationTable { table.resize(nvars); } - void insert(std::uint8_t kind, Qubit target, const Edge& r) { + void insert(std::uint8_t kind, qc::Qubit target, const Edge& r) { assert(kind < numberOfStochasticOperations); // There are new operations in OpType. // Increase the value of // numberOfOperations accordingly - table.at(static_cast(target)).at(kind) = r; + table.at(target).at(kind) = r; ++count; } - Edge lookup(std::uint8_t kind, Qubit target) { + Edge lookup(std::uint8_t kind, qc::Qubit target) { assert(kind < numberOfStochasticOperations); // There are new operations in OpType. // Increase the value of // numberOfOperations accordingly lookups++; Edge r{}; - auto entry = table.at(static_cast(target)).at(kind); + auto entry = table.at(target).at(kind); if (entry.p == nullptr) { return r; } diff --git a/include/dd/UniqueTable.hpp b/include/dd/UniqueTable.hpp index 25c5dde19..976bd87e5 100644 --- a/include/dd/UniqueTable.hpp +++ b/include/dd/UniqueTable.hpp @@ -109,8 +109,8 @@ template class UniqueTable { } // if node not found -> add it to front of unique table bucket - e.p->next = tables[static_cast(v)][key]; - tables[static_cast(v)][key] = e.p; + e.p->next = tables[v][key]; + tables[v][key] = e.p; stats.trackInsert(); return e; @@ -128,12 +128,9 @@ template class UniqueTable { */ [[nodiscard]] bool incRef(Node* p) noexcept { const auto inc = ::dd::incRef(p); - if (inc) { - assert(p != nullptr); - if (p->ref == 1U) { - stats.trackActiveEntry(); - ++active[static_cast(p->v)]; - } + if (inc && p->ref == 1U) { + stats.trackActiveEntry(); + ++active[p->v]; } return inc; } @@ -150,12 +147,9 @@ template class UniqueTable { */ [[nodiscard]] bool decRef(Node* p) noexcept { const auto dec = ::dd::decRef(p); - if (dec) { - assert(p != nullptr); - if (p->ref == 0U) { - --stats.activeEntryCount; - --active[static_cast(p->v)]; - } + if (dec && p->ref == 0U) { + --stats.activeEntryCount; + --active[p->v]; } return dec; } @@ -178,7 +172,6 @@ template class UniqueTable { Node* lastp = nullptr; while (p != nullptr) { if (p->ref == 0) { - assert(!Node::isTerminal(p)); Node* next = p->next; if (lastp == nullptr) { bucket = next; @@ -221,10 +214,10 @@ template class UniqueTable { }; void print() { - auto q = static_cast(nvars - 1); + auto q = nvars - 1U; for (auto it = tables.rbegin(); it != tables.rend(); ++it) { auto& table = *it; - std::cout << "\tq" << static_cast(q) << ":" + std::cout << "\tq" << q << ":" << "\n"; for (std::size_t key = 0; key < table.size(); ++key) { auto p = table[key]; @@ -292,9 +285,7 @@ template class UniqueTable { **/ Edge searchTable(const Edge& e, const std::size_t& key, const bool keepNode = false) { - const auto v = e.p->v; - - Node* p = tables[static_cast(v)][key]; + Node* p = tables[e.p->v][key]; while (p != nullptr) { if (nodesAreEqual(e.p, p)) { // Match found diff --git a/src/dd/Edge.cpp b/src/dd/Edge.cpp index fa32998d1..9ca45bdd6 100644 --- a/src/dd/Edge.cpp +++ b/src/dd/Edge.cpp @@ -99,10 +99,11 @@ hash>::operator()(const dd::Edge& e) const noexcept { const auto h2 = std::hash{}(e.w); auto h3 = dd::combineHash(h1, h2); if constexpr (std::is_same_v) { - assert(e.p != nullptr); + if (e.isTerminal()) { + return h3; + } assert((dd::dNode::isDensityMatrixTempFlagSet(e.p)) == false); - const auto h4 = std::hash{}(static_cast( - dd::dNode::getDensityMatrixTempFlags(e.p->flags))); + const auto h4 = dd::dNode::getDensityMatrixTempFlags(e.p->flags); h3 = dd::combineHash(h3, h4); } return h3; diff --git a/src/dd/FunctionalityConstruction.cpp b/src/dd/FunctionalityConstruction.cpp index d9317eba8..3767688b5 100644 --- a/src/dd/FunctionalityConstruction.cpp +++ b/src/dd/FunctionalityConstruction.cpp @@ -3,9 +3,9 @@ namespace dd { template MatrixDD buildFunctionality(const QuantumComputation* qc, - std::unique_ptr>& dd) { - const auto nqubits = static_cast(qc->getNqubits()); - if (nqubits == 0U) { + std::unique_ptr>& dd) { + const auto nq = qc->getNqubits(); + if (nq == 0U) { return MatrixDD::one; } @@ -14,7 +14,7 @@ MatrixDD buildFunctionality(const QuantumComputation* qc, } auto permutation = qc->initialLayout; - auto e = dd->createInitialMatrix(nqubits, qc->ancillary); + auto e = dd->createInitialMatrix(nq, qc->ancillary); for (const auto& op : *qc) { auto tmp = dd->multiply(getDD(op.get(), dd, permutation), e); @@ -207,12 +207,12 @@ MatrixDD buildFunctionality(GoogleRandomCircuitSampling* qc, qc->removeCycles(qc->cycles.size() - 2U - *ncycles); } - const auto nqubits = static_cast(qc->getNqubits()); + const auto nq = qc->getNqubits(); Permutation permutation = qc->initialLayout; - auto e = dd->makeIdent(nqubits); + auto e = dd->makeIdent(nq); dd->incRef(e); for (const auto& cycle : qc->cycles) { - auto f = dd->makeIdent(nqubits); + auto f = dd->makeIdent(nq); for (const auto& op : cycle) { f = dd->multiply(getDD(op.get(), dd, permutation), f); } diff --git a/src/dd/Node.cpp b/src/dd/Node.cpp index 1eb727599..db8828ec1 100644 --- a/src/dd/Node.cpp +++ b/src/dd/Node.cpp @@ -4,30 +4,6 @@ #include "dd/RealNumber.hpp" namespace dd { -// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables, -// cppcoreguidelines-interfaces-global-init) -vNode vNode::terminal{ - {{{nullptr, Complex::zero}, {nullptr, Complex::zero}}}, nullptr, 0U, -1}; - -mNode mNode::terminal{{{{nullptr, Complex::zero}, - {nullptr, Complex::zero}, - {nullptr, Complex::zero}, - {nullptr, Complex::zero}}}, - nullptr, - 0U, - -1, - 32 + 16}; - -dNode dNode::terminal{{{{nullptr, Complex::zero}, - {nullptr, Complex::zero}, - {nullptr, Complex::zero}, - {nullptr, Complex::zero}}}, - nullptr, - 0, - -1, - 0}; -// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables, -// cppcoreguidelines-interfaces-global-init) void dNode::setDensityMatrixNodeFlag(const bool densityMatrix) noexcept { if (densityMatrix) { @@ -42,7 +18,7 @@ std::uint8_t dNode::alignDensityNodeNode(dNode*& p) noexcept { // Get an aligned node alignDensityNode(p); - if (p == nullptr || dNode::isTerminal(p)) { + if (dNode::isTerminal(p)) { return 0U; } @@ -99,13 +75,16 @@ void dNode::getAlignedNodeRevertModificationsOnSubEdges(dNode* p) noexcept { void dNode::applyDmChangesToNode(dNode*& p) noexcept { if (isDensityMatrixTempFlagSet(p)) { const auto tmp = alignDensityNodeNode(p); + if (p == nullptr) { + return; + } assert(getDensityMatrixTempFlags(p->flags) == 0); p->flags = p->flags | tmp; } } void dNode::revertDmChangesToNode(dNode*& p) noexcept { - if (isDensityMatrixTempFlagSet(p->flags)) { + if (!dNode::isTerminal(p) && isDensityMatrixTempFlagSet(p->flags)) { getAlignedNodeRevertModificationsOnSubEdges(p); p->unsetTempDensityMatrixFlags(); } diff --git a/src/dd/Simulation.cpp b/src/dd/Simulation.cpp index 15d8fb052..845d85e54 100644 --- a/src/dd/Simulation.cpp +++ b/src/dd/Simulation.cpp @@ -24,7 +24,7 @@ simulate(const QuantumComputation* qc, const VectorDD& in, mt.seed(seeds); } - std::map measurementMap{}; + std::map measurementMap{}; // rudimentary check whether circuit is dynamic for (const auto& op : *qc) { @@ -44,7 +44,7 @@ simulate(const QuantumComputation* qc, const VectorDD& in, const auto& classic = measure->getClassics(); for (std::size_t i = 0; i < quantum.size(); ++i) { - measurementMap[static_cast(quantum.at(i))] = classic.at(i); + measurementMap[quantum.at(i)] = classic.at(i); } } @@ -105,16 +105,14 @@ simulate(const QuantumComputation* qc, const VectorDD& in, // measurement map specifies that the circuit `qubit` is measured into // a certain `bit` measurement[qc->getNcbits() - 1U - bit] = - bitstring[bitstring.size() - 1U - - static_cast(qubit)]; + bitstring[bitstring.size() - 1U - qubit]; } } else { // otherwise, we consider the output permutation for determining where // to measure the qubits to for (const auto& [qubit, bit] : qc->outputPermutation) { measurement[qc->getNcbits() - 1 - bit] = - bitstring[bitstring.size() - 1U - - static_cast(qubit)]; + bitstring[bitstring.size() - 1U - qubit]; } } actualCounts[measurement] += count; @@ -216,11 +214,13 @@ void extractProbabilityVector(const QuantumComputation* qc, const VectorDD& in, } template -void extractProbabilityVectorRecursive( - const QuantumComputation* qc, const VectorDD& currentState, - decltype(qc->begin()) currentIt, std::map measurements, - dd::fp commonFactor, ProbabilityVector& probVector, - std::unique_ptr>& dd) { +void extractProbabilityVectorRecursive(const QuantumComputation* qc, + const VectorDD& currentState, + decltype(qc->begin()) currentIt, + std::map measurements, + fp commonFactor, + ProbabilityVector& probVector, + std::unique_ptr>& dd) { auto state = currentState; for (auto it = currentIt; it != qc->end(); ++it) { const auto& op = (*it); @@ -260,7 +260,7 @@ void extractProbabilityVectorRecursive( } auto [pzero, pone] = dd->determineMeasurementProbabilities( - state, static_cast(targets[0]), true); + state, static_cast(targets[0]), true); // normalize probabilities const auto norm = pzero + pone; @@ -268,9 +268,9 @@ void extractProbabilityVectorRecursive( pone /= norm; if (RealNumber::approximatelyOne(pone)) { - const qc::MatrixDD xGate = dd->makeGateDD( - dd::Xmat, static_cast(state.p->v + 1), - static_cast(targets[0U])); + const qc::MatrixDD xGate = + dd->makeGateDD(Xmat, static_cast(state.p->v) + 1U, + static_cast(targets[0U])); const qc::VectorDD resetState = dd->multiply(xGate, state); dd->incRef(resetState); dd->decRef(state); @@ -362,12 +362,11 @@ void extractProbabilityVectorRecursive( // determine the next iteration point auto nextIt = it + 1; // actually collapse the state - const dd::GateMatrix measurementMatrix{ - dd::complex_one, dd::complex_zero, dd::complex_zero, - dd::complex_zero}; + const GateMatrix measurementMatrix{complex_one, complex_zero, + complex_zero, complex_zero}; const qc::MatrixDD measurementGate = dd->makeGateDD( - measurementMatrix, static_cast(state.p->v + 1), - static_cast(targets[0])); + measurementMatrix, static_cast(state.p->v) + 1U, + targets[0]); qc::VectorDD measuredState = dd->multiply(measurementGate, state); auto c = dd->cn.getTemporary(1. / std::sqrt(pzero), 0); @@ -393,8 +392,8 @@ void extractProbabilityVectorRecursive( const GateMatrix measurementMatrix{complex_zero, complex_zero, complex_zero, complex_one}; const qc::MatrixDD measurementGate = dd->makeGateDD( - measurementMatrix, static_cast(state.p->v + 1), - static_cast(targets[0])); + measurementMatrix, static_cast(state.p->v) + 1U, + targets[0]); qc::VectorDD measuredState = dd->multiply(measurementGate, state); auto c = dd->cn.getTemporary(1. / std::sqrt(pone), 0); From 4ee6017f64fc741b65060554aeec3bf41c288698 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sat, 15 Jul 2023 01:07:13 -0700 Subject: [PATCH 05/14] =?UTF-8?q?=E2=9C=85=20test=20adjustments=20and=20im?= =?UTF-8?q?provements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- test/algorithms/eval_dynamic_circuits.cpp | 50 ++++------ test/algorithms/test_bernsteinvazirani.cpp | 71 ++++++-------- test/algorithms/test_entanglement.cpp | 21 ++-- test/algorithms/test_grcs.cpp | 2 +- test/algorithms/test_grover.cpp | 10 +- test/algorithms/test_qft.cpp | 49 ++++----- test/algorithms/test_qpe.cpp | 93 +++++++----------- test/algorithms/test_random_clifford.cpp | 2 +- test/dd/test_dd_functionality.cpp | 5 +- test/dd/test_dd_noise_functionality.cpp | 20 ++-- test/dd/test_package.cpp | 109 +++++++++------------ 11 files changed, 173 insertions(+), 259 deletions(-) diff --git a/test/algorithms/eval_dynamic_circuits.cpp b/test/algorithms/eval_dynamic_circuits.cpp index ccb75d5bf..8e08a2e69 100644 --- a/test/algorithms/eval_dynamic_circuits.cpp +++ b/test/algorithms/eval_dynamic_circuits.cpp @@ -109,7 +109,7 @@ TEST_P(DynamicCircuitEvalExactQPE, UnitaryTransformation) { qc::CircuitOptimizer::reorderOperations(*iqpe); const auto finishedTransformation = std::chrono::steady_clock::now(); - qc::MatrixDD e = dd->makeIdent(static_cast(precision + 1)); + qc::MatrixDD e = dd->makeIdent(precision + 1); dd->incRef(e); auto leftIt = qpe->begin(); @@ -170,17 +170,13 @@ TEST_P(DynamicCircuitEvalExactQPE, UnitaryTransformation) { TEST_P(DynamicCircuitEvalExactQPE, ProbabilityExtraction) { // generate DD of QPE circuit via simulation const auto start = std::chrono::steady_clock::now(); - auto e = simulate( - qpe.get(), - dd->makeZeroState(static_cast(qpe->getNqubits())), dd); + auto e = simulate(qpe.get(), dd->makeZeroState(qpe->getNqubits()), dd); const auto simulationEnd = std::chrono::steady_clock::now(); // extract measurement probabilities from IQPE simulations dd::ProbabilityVector probs{}; - extractProbabilityVector( - iqpe.get(), - dd->makeZeroState(static_cast(iqpe->getNqubits())), probs, - dd); + extractProbabilityVector(iqpe.get(), dd->makeZeroState(iqpe->getNqubits()), + probs, dd); const auto extractionEnd = std::chrono::steady_clock::now(); // extend to account for 0 qubit @@ -217,7 +213,7 @@ TEST_P(DynamicCircuitEvalExactQPE, ProbabilityExtraction) { class DynamicCircuitEvalInexactQPE : public testing::TestWithParam { protected: - dd::QubitCount precision{}; + std::size_t precision{}; dd::fp theta{}; std::size_t expectedResult{}; std::string expectedResultRepresentation{}; @@ -232,7 +228,7 @@ class DynamicCircuitEvalInexactQPE void TearDown() override {} void SetUp() override { - precision = static_cast(GetParam()); + precision = GetParam(); dd = std::make_unique>(precision + 1); @@ -388,17 +384,13 @@ TEST_P(DynamicCircuitEvalInexactQPE, ProbabilityExtraction) { const auto start = std::chrono::steady_clock::now(); // extract measurement probabilities from IQPE simulations dd::ProbabilityVector probs{}; - extractProbabilityVector( - iqpe.get(), - dd->makeZeroState(static_cast(iqpe->getNqubits())), probs, - dd); + extractProbabilityVector(iqpe.get(), dd->makeZeroState(iqpe->getNqubits()), + probs, dd); const auto extractionEnd = std::chrono::steady_clock::now(); std::cout << "---- extraction done ----\n"; // generate DD of QPE circuit via simulation - auto e = simulate( - qpe.get(), - dd->makeZeroState(static_cast(qpe->getNqubits())), dd); + auto e = simulate(qpe.get(), dd->makeZeroState(qpe->getNqubits()), dd); const auto simulationEnd = std::chrono::steady_clock::now(); std::cout << "---- sim done ----\n"; @@ -493,7 +485,7 @@ TEST_P(DynamicCircuitEvalBV, UnitaryTransformation) { qc::CircuitOptimizer::reorderOperations(*dbv); const auto finishedTransformation = std::chrono::steady_clock::now(); - qc::MatrixDD e = dd->makeIdent(static_cast(bitwidth + 1)); + qc::MatrixDD e = dd->makeIdent(bitwidth + 1); dd->incRef(e); auto leftIt = bv->begin(); @@ -554,17 +546,13 @@ TEST_P(DynamicCircuitEvalBV, UnitaryTransformation) { TEST_P(DynamicCircuitEvalBV, ProbabilityExtraction) { // generate DD of QPE circuit via simulation const auto start = std::chrono::steady_clock::now(); - auto e = simulate( - bv.get(), - dd->makeZeroState(static_cast(bv->getNqubits())), dd); + auto e = simulate(bv.get(), dd->makeZeroState(bv->getNqubits()), dd); const auto simulationEnd = std::chrono::steady_clock::now(); // extract measurement probabilities from IQPE simulations dd::ProbabilityVector probs{}; - extractProbabilityVector( - dbv.get(), - dd->makeZeroState(static_cast(dbv->getNqubits())), probs, - dd); + extractProbabilityVector(dbv.get(), dd->makeZeroState(dbv->getNqubits()), + probs, dd); const auto extractionEnd = std::chrono::steady_clock::now(); // extend to account for 0 qubit @@ -652,7 +640,7 @@ TEST_P(DynamicCircuitEvalQFT, UnitaryTransformation) { qc::CircuitOptimizer::reorderOperations(*dqft); const auto finishedTransformation = std::chrono::steady_clock::now(); - qc::MatrixDD e = dd->makeIdent(static_cast(precision)); + qc::MatrixDD e = dd->makeIdent(precision); dd->incRef(e); auto leftIt = qft->begin(); @@ -713,9 +701,7 @@ TEST_P(DynamicCircuitEvalQFT, UnitaryTransformation) { TEST_P(DynamicCircuitEvalQFT, ProbabilityExtraction) { // generate DD of QPE circuit via simulation const auto start = std::chrono::steady_clock::now(); - auto e = simulate( - qft.get(), - dd->makeZeroState(static_cast(qft->getNqubits())), dd); + auto e = simulate(qft.get(), dd->makeZeroState(qft->getNqubits()), dd); const auto simulationEnd = std::chrono::steady_clock::now(); const auto simulation = std::chrono::duration(simulationEnd - start).count(); @@ -724,10 +710,8 @@ TEST_P(DynamicCircuitEvalQFT, ProbabilityExtraction) { // extract measurement probabilities from IQPE simulations if (qft->getNqubits() <= 15) { dd::ProbabilityVector probs{}; - extractProbabilityVector( - dqft.get(), - dd->makeZeroState(static_cast(dqft->getNqubits())), - probs, dd); + extractProbabilityVector(dqft.get(), dd->makeZeroState(dqft->getNqubits()), + probs, dd); const auto extractionEnd = std::chrono::steady_clock::now(); // compare outcomes diff --git a/test/algorithms/test_bernsteinvazirani.cpp b/test/algorithms/test_bernsteinvazirani.cpp index e527b011a..c9fb10d51 100644 --- a/test/algorithms/test_bernsteinvazirani.cpp +++ b/test/algorithms/test_bernsteinvazirani.cpp @@ -31,23 +31,21 @@ TEST_P(BernsteinVazirani, FunctionTest) { auto s = qc::BitString(GetParam()); // construct Bernstein Vazirani circuit - auto qc = std::make_unique(s); - qc->printStatistics(std::cout); + auto qc = qc::BernsteinVazirani(s); + qc.printStatistics(std::cout); // simulate the circuit - auto dd = std::make_unique>(qc->getNqubits()); + auto dd = std::make_unique>(qc.getNqubits()); const std::size_t shots = 1024; auto measurements = - simulate(qc.get(), - dd->makeZeroState(static_cast(qc->getNqubits())), - dd, shots); + simulate(&qc, dd->makeZeroState(qc.getNqubits()), dd, shots); for (const auto& [state, count] : measurements) { std::cout << state << ": " << count << "\n"; } // expect to obtain the hidden bitstring with certainty - EXPECT_EQ(measurements[qc->expected], shots); + EXPECT_EQ(measurements[qc.expected], shots); } TEST_P(BernsteinVazirani, FunctionTestDynamic) { @@ -55,65 +53,59 @@ TEST_P(BernsteinVazirani, FunctionTestDynamic) { auto s = qc::BitString(GetParam()); // construct Bernstein Vazirani circuit - auto qc = std::make_unique(s, true); - qc->printStatistics(std::cout); + auto qc = qc::BernsteinVazirani(s, true); + qc.printStatistics(std::cout); // simulate the circuit - auto dd = std::make_unique>(qc->getNqubits()); + auto dd = std::make_unique>(qc.getNqubits()); const std::size_t shots = 1024; auto measurements = - simulate(qc.get(), - dd->makeZeroState(static_cast(qc->getNqubits())), - dd, shots); + simulate(&qc, dd->makeZeroState(qc.getNqubits()), dd, shots); for (const auto& [state, count] : measurements) { std::cout << state << ": " << count << "\n"; } // expect to obtain the hidden bitstring with certainty - EXPECT_EQ(measurements[qc->expected], shots); + EXPECT_EQ(measurements[qc.expected], shots); } TEST_F(BernsteinVazirani, LargeCircuit) { const std::size_t nq = 127; - auto qc = std::make_unique(nq); - qc->printStatistics(std::cout); + auto qc = qc::BernsteinVazirani(nq); + qc.printStatistics(std::cout); // simulate the circuit - auto dd = std::make_unique>(qc->getNqubits()); + auto dd = std::make_unique>(qc.getNqubits()); const std::size_t shots = 1024; auto measurements = - simulate(qc.get(), - dd->makeZeroState(static_cast(qc->getNqubits())), - dd, shots); + simulate(&qc, dd->makeZeroState(qc.getNqubits()), dd, shots); for (const auto& [state, count] : measurements) { std::cout << state << ": " << count << "\n"; } // expect to obtain the hidden bitstring with certainty - EXPECT_EQ(measurements[qc->expected], shots); + EXPECT_EQ(measurements[qc.expected], shots); } TEST_F(BernsteinVazirani, DynamicCircuit) { const std::size_t nq = 127; - auto qc = std::make_unique(nq, true); - qc->printStatistics(std::cout); + auto qc = qc::BernsteinVazirani(nq, true); + qc.printStatistics(std::cout); // simulate the circuit - auto dd = std::make_unique>(qc->getNqubits()); + auto dd = std::make_unique>(qc.getNqubits()); const std::size_t shots = 1024; auto measurements = - simulate(qc.get(), - dd->makeZeroState(static_cast(qc->getNqubits())), - dd, shots); + simulate(&qc, dd->makeZeroState(qc.getNqubits()), dd, shots); for (const auto& [state, count] : measurements) { std::cout << state << ": " << count << "\n"; } // expect to obtain the hidden bitstring with certainty - EXPECT_EQ(measurements[qc->expected], shots); + EXPECT_EQ(measurements[qc.expected], shots); } TEST_P(BernsteinVazirani, DynamicEquivalenceSimulation) { @@ -121,34 +113,29 @@ TEST_P(BernsteinVazirani, DynamicEquivalenceSimulation) { auto s = qc::BitString(GetParam()); // create standard BV circuit - auto bv = std::make_unique(s); + auto bv = qc::BernsteinVazirani(s); - auto dd = std::make_unique>(bv->getNqubits()); + auto dd = std::make_unique>(bv.getNqubits()); // remove final measurements to obtain statevector - qc::CircuitOptimizer::removeFinalMeasurements(*bv); + qc::CircuitOptimizer::removeFinalMeasurements(bv); // simulate circuit - auto e = simulate( - bv.get(), - dd->makeZeroState(static_cast(bv->getNqubits())), dd); + auto e = simulate(&bv, dd->makeZeroState(bv.getNqubits()), dd); // create dynamic BV circuit - auto dbv = std::make_unique(s, true); + auto dbv = qc::BernsteinVazirani(s, true); // transform dynamic circuits by first eliminating reset operations and // afterwards deferring measurements - qc::CircuitOptimizer::eliminateResets(*dbv); - - qc::CircuitOptimizer::deferMeasurements(*dbv); + qc::CircuitOptimizer::eliminateResets(dbv); + qc::CircuitOptimizer::deferMeasurements(dbv); // remove final measurements to obtain statevector - qc::CircuitOptimizer::removeFinalMeasurements(*dbv); + qc::CircuitOptimizer::removeFinalMeasurements(dbv); // simulate circuit - auto f = simulate( - dbv.get(), - dd->makeZeroState(static_cast(dbv->getNqubits())), dd); + auto f = simulate(&dbv, dd->makeZeroState(dbv.getNqubits()), dd); // calculate fidelity between both results auto fidelity = dd->fidelity(e, f); diff --git a/test/algorithms/test_entanglement.cpp b/test/algorithms/test_entanglement.cpp index c6a8be75b..4058706b5 100644 --- a/test/algorithms/test_entanglement.cpp +++ b/test/algorithms/test_entanglement.cpp @@ -4,35 +4,30 @@ #include "gtest/gtest.h" #include -class Entanglement : public testing::TestWithParam { +class Entanglement : public testing::TestWithParam { protected: void TearDown() override {} void SetUp() override {} }; INSTANTIATE_TEST_SUITE_P( - Entanglement, Entanglement, - testing::Range(static_cast(2), - static_cast(90), 7), + Entanglement, Entanglement, testing::Range(2U, 90U, 7U), [](const testing::TestParamInfo& inf) { // Generate names for test cases - const dd::QubitCount nqubits = inf.param; + const auto nqubits = inf.param; std::stringstream ss{}; - ss << static_cast(nqubits) << "_qubits"; + ss << nqubits << "_qubits"; return ss.str(); }); TEST_P(Entanglement, FunctionTest) { - const dd::QubitCount nq = GetParam(); + const auto nq = GetParam(); auto dd = std::make_unique>(nq); - std::unique_ptr qc; - qc::MatrixDD e{}; + auto qc = qc::Entanglement(nq); + auto e = buildFunctionality(&qc, dd); - ASSERT_NO_THROW({ qc = std::make_unique(nq); }); - ASSERT_NO_THROW({ e = buildFunctionality(qc.get(), dd); }); - - ASSERT_EQ(qc->getNops(), nq); + ASSERT_EQ(qc.getNops(), nq); const qc::VectorDD r = dd->multiply(e, dd->makeZeroState(nq)); ASSERT_EQ(dd->getValueByPath(r, std::string(nq, '0')), diff --git a/test/algorithms/test_grcs.cpp b/test/algorithms/test_grcs.cpp index 13c2aa9d4..c2a361bf9 100644 --- a/test/algorithms/test_grcs.cpp +++ b/test/algorithms/test_grcs.cpp @@ -27,7 +27,7 @@ TEST_F(GRCS, simulate) { qc::GoogleRandomCircuitSampling("./circuits/grcs/bris_4_40_9_v2.txt"); auto dd = std::make_unique>(qcBris.getNqubits()); - auto in = dd->makeZeroState(static_cast(qcBris.getNqubits())); + auto in = dd->makeZeroState(qcBris.getNqubits()); const std::optional ncycles = 4; ASSERT_NO_THROW({ simulate(&qcBris, in, dd, ncycles); }); std::cout << qcBris << "\n"; diff --git a/test/algorithms/test_grover.cpp b/test/algorithms/test_grover.cpp index 0ed25c422..542395551 100644 --- a/test/algorithms/test_grover.cpp +++ b/test/algorithms/test_grover.cpp @@ -10,12 +10,8 @@ class Grover : public testing::TestWithParam> { protected: void TearDown() override { - if (sim.p != nullptr) { - dd->decRef(sim); - } - if (func.p != nullptr) { - dd->decRef(func); - } + dd->decRef(sim); + dd->decRef(func); dd->garbageCollect(true); // number of complex table entries after clean-up should equal initial @@ -113,7 +109,7 @@ TEST_P(Grover, Simulation) { ASSERT_NO_THROW({ qc = std::make_unique(nqubits, seed); }); qc->printStatistics(std::cout); - auto in = dd->makeZeroState(static_cast(nqubits + 1)); + auto in = dd->makeZeroState(nqubits + 1U); // there should be no error simulating the circuit const std::size_t shots = 1024; auto measurements = simulate(qc.get(), in, dd, shots); diff --git a/test/algorithms/test_qft.cpp b/test/algorithms/test_qft.cpp index 72cc14a7d..aae356064 100644 --- a/test/algorithms/test_qft.cpp +++ b/test/algorithms/test_qft.cpp @@ -6,15 +6,11 @@ #include #include -class QFT : public testing::TestWithParam { +class QFT : public testing::TestWithParam { protected: void TearDown() override { - if (sim.p != nullptr) { - dd->decRef(sim); - } - if (func.p != nullptr) { - dd->decRef(func); - } + dd->decRef(sim); + dd->decRef(func); dd->garbageCollect(true); // number of complex table entries after clean-up should equal initial @@ -32,7 +28,7 @@ class QFT : public testing::TestWithParam { initialComplexCount = dd->cn.realCount(); } - dd::QubitCount nqubits = 0; + std::size_t nqubits = 0; std::unique_ptr> dd; std::unique_ptr qc; std::size_t initialCacheCount = 0; @@ -52,23 +48,22 @@ class QFT : public testing::TestWithParam { /// The accuracy of double floating points allows for a minimal CN::TOLERANCE /// value of 10e-15 /// Utilizing more qubits requires the use of fp=long double -constexpr dd::QubitCount QFT_MAX_QUBITS = 20; - -INSTANTIATE_TEST_SUITE_P( - QFT, QFT, - testing::Range(static_cast(0), - static_cast(QFT_MAX_QUBITS + 1), 3), - [](const testing::TestParamInfo& inf) { - const auto nqubits = inf.param; - std::stringstream ss{}; - ss << static_cast(nqubits); - if (nqubits == 1) { - ss << "_qubit"; - } else { - ss << "_qubits"; - } - return ss.str(); - }); +constexpr std::size_t QFT_MAX_QUBITS = 20U; + +INSTANTIATE_TEST_SUITE_P(QFT, QFT, + testing::Range(0U, QFT_MAX_QUBITS + 1U, + 3U), + [](const testing::TestParamInfo& inf) { + const auto nqubits = inf.param; + std::stringstream ss{}; + ss << nqubits; + if (nqubits == 1) { + ss << "_qubit"; + } else { + ss << "_qubits"; + } + return ss.str(); + }); TEST_P(QFT, Functionality) { // there should be no error constructing the circuit @@ -207,9 +202,7 @@ TEST_P(QFT, DynamicSimulation) { // simulate the circuit std::size_t shots = 8192U; auto measurements = - simulate(qc.get(), - dd->makeZeroState(static_cast(qc->getNqubits())), - dd, shots); + simulate(qc.get(), dd->makeZeroState(qc->getNqubits()), dd, shots); for (const auto& [state, count] : measurements) { std::cout << state << ": " << count << "\n"; diff --git a/test/algorithms/test_qpe.cpp b/test/algorithms/test_qpe.cpp index 576805cb6..1dbe2a908 100644 --- a/test/algorithms/test_qpe.cpp +++ b/test/algorithms/test_qpe.cpp @@ -109,20 +109,13 @@ INSTANTIATE_TEST_SUITE_P( TEST_P(QPE, QPETest) { auto dd = std::make_unique>(precision + 1); - std::unique_ptr qc; - qc::VectorDD e{}; - - ASSERT_NO_THROW({ qc = std::make_unique(lambda, precision); }); - - ASSERT_EQ(qc->getNqubits(), precision + 1); - - ASSERT_NO_THROW({ qc::CircuitOptimizer::removeFinalMeasurements(*qc); }); + auto qc = qc::QPE(lambda, precision); + ASSERT_EQ(qc.getNqubits(), precision + 1); + ASSERT_NO_THROW({ qc::CircuitOptimizer::removeFinalMeasurements(qc); }); - ASSERT_NO_THROW({ - e = simulate( - qc.get(), - dd->makeZeroState(static_cast(qc->getNqubits())), dd); - }); + qc::VectorDD e{}; + ASSERT_NO_THROW( + { e = simulate(&qc, dd->makeZeroState(qc.getNqubits()), dd); }); // account for the eigenstate qubit in the expected result by shifting and // adding 1 @@ -152,17 +145,12 @@ TEST_P(QPE, QPETest) { TEST_P(QPE, IQPETest) { auto dd = std::make_unique>(precision + 1); - std::unique_ptr qc; - - ASSERT_NO_THROW({ qc = std::make_unique(lambda, precision, true); }); - - ASSERT_EQ(qc->getNqubits(), 2U); + auto qc = qc::QPE(lambda, precision, true); + ASSERT_EQ(qc.getNqubits(), 2U); constexpr auto shots = 8192U; auto measurements = - simulate(qc.get(), - dd->makeZeroState(static_cast(qc->getNqubits())), - dd, shots); + simulate(&qc, dd->makeZeroState(qc.getNqubits()), dd, shots); // sort the measurements using Measurement = std::pair; @@ -206,32 +194,27 @@ TEST_P(QPE, DynamicEquivalenceSimulation) { auto dd = std::make_unique>(precision + 1); // create standard QPE circuit - auto qpe = std::make_unique(lambda, precision); + auto qpe = qc::QPE(lambda, precision); // remove final measurements to obtain statevector - qc::CircuitOptimizer::removeFinalMeasurements(*qpe); + qc::CircuitOptimizer::removeFinalMeasurements(qpe); // simulate circuit - auto e = simulate( - qpe.get(), - dd->makeZeroState(static_cast(qpe->getNqubits())), dd); + auto e = simulate(&qpe, dd->makeZeroState(qpe.getNqubits()), dd); // create standard IQPE circuit - auto iqpe = std::make_unique(lambda, precision, true); + auto iqpe = qc::QPE(lambda, precision, true); // transform dynamic circuits by first eliminating reset operations and // afterwards deferring measurements - qc::CircuitOptimizer::eliminateResets(*iqpe); - - qc::CircuitOptimizer::deferMeasurements(*iqpe); + qc::CircuitOptimizer::eliminateResets(iqpe); + qc::CircuitOptimizer::deferMeasurements(iqpe); // remove final measurements to obtain statevector - qc::CircuitOptimizer::removeFinalMeasurements(*iqpe); + qc::CircuitOptimizer::removeFinalMeasurements(iqpe); // simulate circuit - auto f = simulate( - iqpe.get(), - dd->makeZeroState(static_cast(iqpe->getNqubits())), dd); + auto f = simulate(&iqpe, dd->makeZeroState(iqpe.getNqubits()), dd); // calculate fidelity between both results auto fidelity = dd->fidelity(e, f); @@ -244,27 +227,27 @@ TEST_P(QPE, DynamicEquivalenceFunctionality) { auto dd = std::make_unique>(precision + 1); // create standard QPE circuit - auto qpe = std::make_unique(lambda, precision); + auto qpe = qc::QPE(lambda, precision); // remove final measurements to obtain statevector - qc::CircuitOptimizer::removeFinalMeasurements(*qpe); + qc::CircuitOptimizer::removeFinalMeasurements(qpe); // simulate circuit - auto e = buildFunctionality(qpe.get(), dd); + auto e = buildFunctionality(&qpe, dd); // create standard IQPE circuit - auto iqpe = std::make_unique(lambda, precision, true); + auto iqpe = qc::QPE(lambda, precision, true); // transform dynamic circuits by first eliminating reset operations and // afterwards deferring measurements - qc::CircuitOptimizer::eliminateResets(*iqpe); - qc::CircuitOptimizer::deferMeasurements(*iqpe); + qc::CircuitOptimizer::eliminateResets(iqpe); + qc::CircuitOptimizer::deferMeasurements(iqpe); // remove final measurements to obtain statevector - qc::CircuitOptimizer::removeFinalMeasurements(*iqpe); + qc::CircuitOptimizer::removeFinalMeasurements(iqpe); // simulate circuit - auto f = buildFunctionality(iqpe.get(), dd); + auto f = buildFunctionality(&iqpe, dd); EXPECT_EQ(e, f); } @@ -273,14 +256,12 @@ TEST_P(QPE, ProbabilityExtraction) { auto dd = std::make_unique>(precision + 1); // create standard QPE circuit - auto iqpe = std::make_unique(lambda, precision, true); + auto iqpe = qc::QPE(lambda, precision, true); - std::cout << *iqpe << std::endl; + std::cout << iqpe << "\n"; dd::ProbabilityVector probs{}; - extractProbabilityVector( - iqpe.get(), - dd->makeZeroState(static_cast(iqpe->getNqubits())), probs, - dd); + extractProbabilityVector(&iqpe, dd->makeZeroState(iqpe.getNqubits()), probs, + dd); for (const auto& [state, prob] : probs) { std::stringstream ss{}; @@ -301,15 +282,13 @@ TEST_P(QPE, DynamicEquivalenceSimulationProbabilityExtraction) { auto dd = std::make_unique>(precision + 1); // create standard QPE circuit - auto qpe = std::make_unique(lambda, precision); + auto qpe = qc::QPE(lambda, precision); // remove final measurements to obtain statevector - qc::CircuitOptimizer::removeFinalMeasurements(*qpe); + qc::CircuitOptimizer::removeFinalMeasurements(qpe); // simulate circuit - auto e = simulate( - qpe.get(), - dd->makeZeroState(static_cast(qpe->getNqubits())), dd); + auto e = simulate(&qpe, dd->makeZeroState(qpe.getNqubits()), dd); const auto vec = dd->getVector(e); std::cout << "QPE:\n"; for (const auto& amp : vec) { @@ -317,14 +296,12 @@ TEST_P(QPE, DynamicEquivalenceSimulationProbabilityExtraction) { } // create standard IQPE circuit - auto iqpe = std::make_unique(lambda, precision, true); + auto iqpe = qc::QPE(lambda, precision, true); // extract measurement probabilities from IQPE simulations dd::ProbabilityVector probs{}; - extractProbabilityVector( - iqpe.get(), - dd->makeZeroState(static_cast(iqpe->getNqubits())), probs, - dd); + extractProbabilityVector(&iqpe, dd->makeZeroState(iqpe.getNqubits()), probs, + dd); // extend to account for 0 qubit auto stub = dd::ProbabilityVector{}; diff --git a/test/algorithms/test_random_clifford.cpp b/test/algorithms/test_random_clifford.cpp index eb5b896e5..e5a61361b 100644 --- a/test/algorithms/test_random_clifford.cpp +++ b/test/algorithms/test_random_clifford.cpp @@ -26,7 +26,7 @@ TEST_P(RandomClifford, simulate) { auto dd = std::make_unique>(nq); auto qc = qc::RandomCliffordCircuit(nq, nq * nq, 12345); - auto in = dd->makeZeroState(static_cast(nq)); + auto in = dd->makeZeroState(nq); std::cout << qc << "\n"; ASSERT_NO_THROW({ simulate(&qc, in, dd); }); diff --git a/test/dd/test_dd_functionality.cpp b/test/dd/test_dd_functionality.cpp index 789d9c59c..8de4a4eff 100644 --- a/test/dd/test_dd_functionality.cpp +++ b/test/dd/test_dd_functionality.cpp @@ -43,7 +43,7 @@ class DDFunctionality : public testing::TestWithParam { dist = std::uniform_real_distribution(0.0, 2. * dd::PI); } - dd::QubitCount nqubits = 4U; + std::size_t nqubits = 4U; std::size_t initialCacheCount = 0U; std::size_t initialComplexCount = 0U; qc::MatrixDD e{}, ident{}; @@ -244,8 +244,7 @@ TEST_F(DDFunctionality, changePermutation) { << "qreg q[2];" << "x q[0];\n"; qc.import(ss, qc::Format::OpenQASM); - auto sim = simulate( - &qc, dd->makeZeroState(static_cast(qc.getNqubits())), dd); + auto sim = simulate(&qc, dd->makeZeroState(qc.getNqubits()), dd); EXPECT_TRUE(sim.p->e[0].isZeroTerminal()); EXPECT_TRUE(sim.p->e[1].w.approximatelyOne()); EXPECT_TRUE(sim.p->e[1].p->e[1].isZeroTerminal()); diff --git a/test/dd/test_dd_noise_functionality.cpp b/test/dd/test_dd_noise_functionality.cpp index 6e95efcc2..2b54cf7dd 100644 --- a/test/dd/test_dd_noise_functionality.cpp +++ b/test/dd/test_dd_noise_functionality.cpp @@ -93,8 +93,7 @@ TEST_F(DDNoiseFunctionalityTest, DetSimulateAdder4TrackAPD) { for (const auto applyNoiseSequentially : {false, true}) { auto dd = std::make_unique(qc.getNqubits()); - auto rootEdge = dd->makeZeroDensityOperator( - static_cast(qc.getNqubits())); + auto rootEdge = dd->makeZeroDensityOperator(qc.getNqubits()); dd->incRef(rootEdge); const auto noiseEffects = {dd::AmplitudeDamping, dd::PhaseFlip, @@ -102,9 +101,8 @@ TEST_F(DDNoiseFunctionalityTest, DetSimulateAdder4TrackAPD) { auto deterministicNoiseFunctionality = dd::DeterministicNoiseFunctionality( - dd, static_cast(qc.getNqubits()), 0.01, 0.02, - 0.02, 0.04, noiseEffects, useDensityMatrixType, - applyNoiseSequentially); + dd, qc.getNqubits(), 0.01, 0.02, 0.02, 0.04, noiseEffects, + useDensityMatrixType, applyNoiseSequentially); for (auto const& op : qc) { dd->applyOperationToDensity(rootEdge, dd::getDD(op.get(), dd), @@ -140,12 +138,10 @@ TEST_F(DDNoiseFunctionalityTest, StochSimulateAdder4TrackAPD) { dd::Depolarization}; auto stochasticNoiseFunctionality = dd::StochasticNoiseFunctionality( - dd, static_cast(qc.getNqubits()), 0.01, 0.02, 2., - noiseEffects); + dd, qc.getNqubits(), 0.01, 0.02, 2., noiseEffects); for (size_t i = 0U; i < stochRuns; i++) { - auto rootEdge = - dd->makeZeroState(static_cast(qc.getNqubits())); + auto rootEdge = dd->makeZeroState(qc.getNqubits()); dd->incRef(rootEdge); for (auto const& op : qc) { @@ -194,12 +190,10 @@ TEST_F(DDNoiseFunctionalityTest, StochSimulateAdder4IdentiyError) { const auto noiseEffects = {dd::Identity}; auto stochasticNoiseFunctionality = dd::StochasticNoiseFunctionality( - dd, static_cast(qc.getNqubits()), 0.01, 0.02, 2., - noiseEffects); + dd, qc.getNqubits(), 0.01, 0.02, 2., noiseEffects); for (size_t i = 0U; i < stochRuns; i++) { - auto rootEdge = - dd->makeZeroState(static_cast(qc.getNqubits())); + auto rootEdge = dd->makeZeroState(qc.getNqubits()); dd->incRef(rootEdge); for (auto const& op : qc) { diff --git a/test/dd/test_package.cpp b/test/dd/test_package.cpp index ab8308493..10e416c73 100644 --- a/test/dd/test_package.cpp +++ b/test/dd/test_package.cpp @@ -13,7 +13,7 @@ using namespace qc::literals; TEST(DDPackageTest, RequestInvalidPackageSize) { EXPECT_THROW(auto dd = std::make_unique>( - std::numeric_limits::max() + 2), + dd::Package<>::MAX_POSSIBLE_QUBITS + 2), std::invalid_argument); } @@ -232,8 +232,8 @@ TEST(DDPackageTest, StateGenerationManipulation) { auto dd = std::make_unique>(nqubits); auto b = std::vector(nqubits, false); b[0] = b[1] = true; - auto e = dd->makeBasisState(static_cast(nqubits), b); - auto f = dd->makeBasisState(static_cast(nqubits), + auto e = dd->makeBasisState(nqubits, b); + auto f = dd->makeBasisState(nqubits, {dd::BasisStates::zero, dd::BasisStates::one, dd::BasisStates::plus, dd::BasisStates::minus, dd::BasisStates::left, dd::BasisStates::right}); @@ -455,7 +455,6 @@ TEST(DDPackageTest, Identity) { auto dd = std::make_unique>(4); EXPECT_TRUE(dd->makeIdent(0).isOneTerminal()); - EXPECT_TRUE(dd->makeIdent(0, -1).isOneTerminal()); auto id3 = dd->makeIdent(3); EXPECT_EQ(dd->makeIdent(0, 2), id3); @@ -610,12 +609,10 @@ TEST(DDPackageTest, GarbageMatrix) { } TEST(DDPackageTest, InvalidMakeBasisStateAndGate) { - auto nqubits = 2; + auto nqubits = 2U; auto dd = std::make_unique>(nqubits); auto basisState = std::vector{dd::BasisStates::zero}; - EXPECT_THROW( - dd->makeBasisState(static_cast(nqubits), basisState), - std::runtime_error); + EXPECT_THROW(dd->makeBasisState(nqubits, basisState), std::runtime_error); EXPECT_THROW(dd->makeZeroState(3), std::runtime_error); EXPECT_THROW(dd->makeBasisState(3, {true, true, true}), std::runtime_error); EXPECT_THROW( @@ -731,9 +728,9 @@ TEST(DDPackageTest, MatrixTranspose) { TEST(DDPackageTest, SpecialCaseTerminal) { auto dd = std::make_unique>(2); auto one = dd::vEdge::one; - dd::export2Dot(one, "oneColored.dot", true); - dd::export2Dot(one, "oneClassic.dot", false); - dd::export2Dot(one, "oneMemory.dot", true, true, false, true); + dd::export2Dot(one, "oneColored.dot", true, false, false, false, false); + dd::export2Dot(one, "oneClassic.dot", false, false, false, false, false); + dd::export2Dot(one, "oneMemory.dot", true, true, false, true, false); EXPECT_EQ(dd->vUniqueTable.lookup(one), one); @@ -1138,21 +1135,16 @@ using DensityMatrixPackageTest = TEST(DDPackageTest, dNodeMultiply) { // Multiply dNode with mNode (MxMxM) - const dd::Qubit nrQubits = 3; + const auto nrQubits = 3U; auto dd = std::make_unique(nrQubits); // Make zero density matrix - auto state = - dd->makeZeroDensityOperator(static_cast(dd->qubits())); + auto state = dd->makeZeroDensityOperator(dd->qubits()); dd->incRef(state); std::vector operations = {}; - operations.emplace_back( - dd->makeGateDD(dd::Hmat, static_cast(nrQubits), 0)); - operations.emplace_back( - dd->makeGateDD(dd::Hmat, static_cast(nrQubits), 1)); - operations.emplace_back( - dd->makeGateDD(dd::Hmat, static_cast(nrQubits), 2)); - operations.emplace_back( - dd->makeGateDD(dd::Zmat, static_cast(nrQubits), 2)); + operations.emplace_back(dd->makeGateDD(dd::Hmat, nrQubits, 0)); + operations.emplace_back(dd->makeGateDD(dd::Hmat, nrQubits, 1)); + operations.emplace_back(dd->makeGateDD(dd::Hmat, nrQubits, 2)); + operations.emplace_back(dd->makeGateDD(dd::Zmat, nrQubits, 2)); for (const auto& op : operations) { dd->applyOperationToDensity(state, op, true); @@ -1190,11 +1182,10 @@ TEST(DDPackageTest, dNodeMultiply) { TEST(DDPackageTest, dNodeMultiply2) { // Multiply dNode with mNode (MxMxM) - const dd::Qubit nrQubits = 3; + const auto nrQubits = 3U; auto dd = std::make_unique(nrQubits); // Make zero density matrix - auto state = - dd->makeZeroDensityOperator(static_cast(dd->qubits())); + auto state = dd->makeZeroDensityOperator(dd->qubits()); dd->incRef(state); std::vector operations = {}; operations.emplace_back(dd->makeGateDD(dd::Hmat, nrQubits, 0)); @@ -1231,7 +1222,7 @@ TEST(DDPackageTest, dNodeMultiply2) { TEST(DDPackageTest, dNodeMulCache1) { // Make caching test with dNodes - const dd::QubitCount nrQubits = 1U; + const auto nrQubits = 1U; auto dd = std::make_unique(nrQubits); // Make zero density matrix auto state = dd->makeZeroDensityOperator(nrQubits); @@ -1276,7 +1267,7 @@ TEST(DDPackageTest, dNodeMulCache1) { TEST(DDPackageTest, dNoiseCache) { // Test the flags for dnode, vnode and mnodes - const dd::QubitCount nrQubits = 1U; + const auto nrQubits = 1U; auto dd = std::make_unique>(nrQubits); // Make zero density matrix const auto initialState = dd->makeZeroDensityOperator(nrQubits); @@ -1304,7 +1295,7 @@ TEST(DDPackageTest, dNoiseCache) { } TEST(DDPackageTest, calCulpDistance) { - const dd::Qubit nrQubits = 1; + const auto nrQubits = 1U; auto dd = std::make_unique>(nrQubits); auto tmp0 = dd::ulpDistance(1 + 1e-12, 1); auto tmp1 = dd::ulpDistance(1, 1); @@ -1319,7 +1310,7 @@ struct StochPackageConfig : public dd::DDPackageConfig { using stochPackage = dd::Package; TEST(DDPackageTest, dStochCache) { - const dd::Qubit nrQubits = 4; + const auto nrQubits = 4U; auto dd = std::make_unique(nrQubits); std::vector operations = {}; @@ -1393,15 +1384,15 @@ TEST(DDPackageTest, stateFromVectorNoPowerOfTwo) { TEST(DDPackageTest, stateFromScalar) { auto dd = std::make_unique>(1); auto s = dd->makeStateFromVector({1}); - EXPECT_EQ(s.p->v, -1); + EXPECT_TRUE(s.isTerminal()); EXPECT_EQ(s.w.r->value, 1); EXPECT_EQ(s.w.i->value, 0); } TEST(DDPackageTest, expectationValueGlobalOperators) { - const dd::QubitCount maxQubits = 3; + const dd::Qubit maxQubits = 3; auto dd = std::make_unique>(maxQubits); - for (dd::QubitCount nrQubits = 1; nrQubits < maxQubits + 1; ++nrQubits) { + for (dd::Qubit nrQubits = 1; nrQubits < maxQubits + 1; ++nrQubits) { const auto zeroState = dd->makeZeroState(nrQubits); // Definition global operators @@ -1414,7 +1405,7 @@ TEST(DDPackageTest, expectationValueGlobalOperators) { const auto singleSiteHadamard = dd->makeGateDD(dd::Hmat, 1, 0); auto globalHadamard = singleSiteHadamard; - for (dd::QubitCount i = 1; i < nrQubits; ++i) { + for (dd::Qubit i = 1; i < nrQubits; ++i) { globalX = dd->kronecker(globalX, singleSiteX); globalZ = dd->kronecker(globalZ, singleSiteZ); globalHadamard = dd->kronecker(globalHadamard, singleSiteHadamard); @@ -1429,14 +1420,13 @@ TEST(DDPackageTest, expectationValueGlobalOperators) { } TEST(DDPackageTest, expectationValueLocalOperators) { - const dd::QubitCount maxQubits = 3; + const dd::Qubit maxQubits = 3; auto dd = std::make_unique>(maxQubits); - for (dd::QubitCount nrQubits = 1; nrQubits < maxQubits + 1; ++nrQubits) { + for (dd::Qubit nrQubits = 1; nrQubits < maxQubits + 1; ++nrQubits) { const auto zeroState = dd->makeZeroState(nrQubits); // Local expectation values at each site - for (dd::Qubit site = 0; site < static_cast(nrQubits) - 1; - ++site) { + for (dd::Qubit site = 0; site < nrQubits - 1; ++site) { // Definition local operators auto xGate = dd->makeGateDD(dd::Xmat, nrQubits, site); auto zGate = dd->makeGateDD(dd::Zmat, nrQubits, site); @@ -1450,11 +1440,10 @@ TEST(DDPackageTest, expectationValueLocalOperators) { } TEST(DDPackageTest, expectationValueExceptions) { - const dd::QubitCount nrQubits = 2; + const auto nrQubits = 2U; auto dd = std::make_unique>(nrQubits); - const auto zeroState = - dd->makeZeroState(static_cast(nrQubits - 1)); + const auto zeroState = dd->makeZeroState(nrQubits - 1); const auto xGate = dd->makeGateDD(dd::Xmat, nrQubits, 0); EXPECT_ANY_THROW(dd->expectationValue(xGate, zeroState)); @@ -1464,7 +1453,7 @@ TEST(DDPackageTest, DDFromSingleQubitMatrix) { const auto inputMatrix = dd::CMat{{dd::SQRT2_2, dd::SQRT2_2}, {dd::SQRT2_2, -dd::SQRT2_2}}; - const dd::QubitCount nrQubits = 1; + const auto nrQubits = 1U; const auto dd = std::make_unique>(nrQubits); const auto matDD = dd->makeDDFromMatrix(inputMatrix); @@ -1477,7 +1466,7 @@ TEST(DDPackageTest, DDFromTwoQubitMatrix) { const auto inputMatrix = dd::CMat{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}}; - const dd::QubitCount nrQubits = 2; + const auto nrQubits = 2U; const auto dd = std::make_unique>(nrQubits); const auto matDD = dd->makeDDFromMatrix(inputMatrix); const auto outputMatrix = dd->getMatrix(matDD); @@ -1492,7 +1481,7 @@ TEST(DDPackageTest, DDFromThreeQubitMatrix) { {0, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 1, 0}}; - const dd::QubitCount nrQubits = 3; + const auto nrQubits = 3U; const auto dd = std::make_unique>(nrQubits); const auto matDD = dd->makeDDFromMatrix(inputMatrix); @@ -1504,7 +1493,7 @@ TEST(DDPackageTest, DDFromThreeQubitMatrix) { TEST(DDPackageTest, DDFromEmptyMatrix) { const auto inputMatrix = dd::CMat{}; - const dd::QubitCount nrQubits = 3; + const auto nrQubits = 3U; const auto dd = std::make_unique>(nrQubits); EXPECT_EQ(dd->makeDDFromMatrix(inputMatrix), dd::mEdge::one); } @@ -1512,7 +1501,7 @@ TEST(DDPackageTest, DDFromEmptyMatrix) { TEST(DDPackageTest, DDFromNonPowerOfTwoMatrix) { auto inputMatrix = dd::CMat{{0, 1, 2}, {3, 4, 5}, {6, 7, 8}}; - const dd::QubitCount nrQubits = 3; + const auto nrQubits = 3U; const auto dd = std::make_unique>(nrQubits); EXPECT_THROW(dd->makeDDFromMatrix(inputMatrix), std::invalid_argument); } @@ -1520,7 +1509,7 @@ TEST(DDPackageTest, DDFromNonPowerOfTwoMatrix) { TEST(DDPackageTest, DDFromNonSquareMatrix) { const auto inputMatrix = dd::CMat{{0, 1, 2, 3}, {4, 5, 6, 7}}; - const dd::QubitCount nrQubits = 3; + const auto nrQubits = 3U; const auto dd = std::make_unique>(nrQubits); EXPECT_THROW(dd->makeDDFromMatrix(inputMatrix), std::invalid_argument); } @@ -1528,14 +1517,14 @@ TEST(DDPackageTest, DDFromNonSquareMatrix) { TEST(DDPackageTest, DDFromSingleElementMatrix) { const auto inputMatrix = dd::CMat{{1}}; - const dd::QubitCount nrQubits = 1; + const auto nrQubits = 1U; const auto dd = std::make_unique>(nrQubits); EXPECT_EQ(dd->makeDDFromMatrix(inputMatrix), dd::mEdge::one); } TEST(DDPackageTest, TwoQubitControlledGateDDConstruction) { - const dd::QubitCount nrQubits = 5; + const auto nrQubits = 5U; const auto dd = std::make_unique>(nrQubits); const auto gateMatrices = std::vector{std::pair{dd::Xmat, dd::CXmat}, @@ -1562,7 +1551,7 @@ TEST(DDPackageTest, TwoQubitControlledGateDDConstruction) { } TEST(DDPackageTest, SWAPGateDDConstruction) { - const dd::QubitCount nrQubits = 5; + const auto nrQubits = 5U; const auto dd = std::make_unique>(nrQubits); for (dd::Qubit control = 0; control < nrQubits; ++control) { @@ -1579,7 +1568,7 @@ TEST(DDPackageTest, SWAPGateDDConstruction) { } TEST(DDPackageTest, iSWAPGateDDConstruction) { - const dd::QubitCount nrQubits = 5; + const auto nrQubits = 5U; const auto dd = std::make_unique>(nrQubits); for (dd::Qubit control = 0; control < nrQubits; ++control) { @@ -1601,7 +1590,7 @@ TEST(DDPackageTest, iSWAPGateDDConstruction) { } TEST(DDPackageTest, DCXGateDDConstruction) { - const dd::QubitCount nrQubits = 5; + const auto nrQubits = 5U; const auto dd = std::make_unique>(nrQubits); for (dd::Qubit control = 0; control < nrQubits; ++control) { @@ -1618,7 +1607,7 @@ TEST(DDPackageTest, DCXGateDDConstruction) { } TEST(DDPackageTest, RZZGateDDConstruction) { - const dd::QubitCount nrQubits = 5; + const auto nrQubits = 5U; const auto dd = std::make_unique>(nrQubits); const auto params = {0., dd::PI_2, dd::PI, 2 * dd::PI}; @@ -1652,7 +1641,7 @@ TEST(DDPackageTest, RZZGateDDConstruction) { } TEST(DDPackageTest, RYYGateDDConstruction) { - const dd::QubitCount nrQubits = 5; + const auto nrQubits = 5U; const auto dd = std::make_unique>(nrQubits); const auto params = {0., dd::PI_2, dd::PI}; @@ -1682,7 +1671,7 @@ TEST(DDPackageTest, RYYGateDDConstruction) { } TEST(DDPackageTest, RXXGateDDConstruction) { - const dd::QubitCount nrQubits = 5; + const auto nrQubits = 5U; const auto dd = std::make_unique>(nrQubits); const auto params = {0., dd::PI_2, dd::PI}; @@ -1712,7 +1701,7 @@ TEST(DDPackageTest, RXXGateDDConstruction) { } TEST(DDPackageTest, RZXGateDDConstruction) { - const dd::QubitCount nrQubits = 5; + const auto nrQubits = 5U; const auto dd = std::make_unique>(nrQubits); const auto params = {0., dd::PI_2, dd::PI}; @@ -1742,7 +1731,7 @@ TEST(DDPackageTest, RZXGateDDConstruction) { } TEST(DDPackageTest, ECRGateDDConstruction) { - const dd::QubitCount nrQubits = 5; + const auto nrQubits = 5U; const auto dd = std::make_unique>(nrQubits); for (dd::Qubit control = 0; control < nrQubits; ++control) { @@ -1760,7 +1749,7 @@ TEST(DDPackageTest, ECRGateDDConstruction) { } TEST(DDPackageTest, XXMinusYYGateDDConstruction) { - const dd::QubitCount nrQubits = 5; + const auto nrQubits = 5U; const auto dd = std::make_unique>(nrQubits); const auto thetaAngles = {0., dd::PI_2, dd::PI}; @@ -1786,7 +1775,7 @@ TEST(DDPackageTest, XXMinusYYGateDDConstruction) { } TEST(DDPackageTest, XXPlusYYGateDDConstruction) { - const dd::QubitCount nrQubits = 5; + const auto nrQubits = 5U; const auto dd = std::make_unique>(nrQubits); const auto thetaAngles = {0., dd::PI_2, dd::PI}; @@ -1812,7 +1801,7 @@ TEST(DDPackageTest, XXPlusYYGateDDConstruction) { } TEST(DDPackageTest, TwoQubitGateCreationFailure) { - const dd::QubitCount nrQubits = 1; + const auto nrQubits = 1U; const auto dd = std::make_unique>(nrQubits); EXPECT_THROW(dd->makeTwoQubitGateDD(dd::CXmat, 2, 0, 1), std::runtime_error); @@ -1822,7 +1811,7 @@ TEST(DDPackageTest, InnerProductTopNodeConjugation) { // Test comes from experimental results // 2 qubit state is rotated Rxx(-2) equivalent to // Ising model evolution up to a time T=1 - const dd::QubitCount nrQubits = 2; + const auto nrQubits = 2U; const auto dd = std::make_unique>(nrQubits); const auto zeroState = dd->makeZeroState(nrQubits); const auto rxx = dd->makeRXXDD(nrQubits, 0, 1, -2); From 2aeb41ca8dcabc60fdb4d2b51031eba3498e167e Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sat, 15 Jul 2023 14:23:39 +0200 Subject: [PATCH 06/14] =?UTF-8?q?=F0=9F=A9=B9=20fix=20ECC=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- test/unittests/test_ecc_functionality.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/test/unittests/test_ecc_functionality.cpp b/test/unittests/test_ecc_functionality.cpp index 14efd0a17..f7699e498 100644 --- a/test/unittests/test_ecc_functionality.cpp +++ b/test/unittests/test_ecc_functionality.cpp @@ -134,7 +134,7 @@ class DDECCFunctionalityTest : public ::testing::Test { auto mapper = std::make_unique(qcOriginal, 0); mapper->apply(); circuitCounter++; - std::cout << "Testing circuit " << circuitCounter << std::endl; + std::cout << "Testing circuit " << circuitCounter << "\n"; bool const success = testErrorCorrectionCircuit( mapper->getOriginalCircuit(), mapper->getMappedCircuit(), testParameter.testNoise, mapper->getDataQubits(), @@ -183,16 +183,14 @@ class DDECCFunctionalityTest : public ::testing::Test { (static_cast(shots) / 100.0) * (tolerance * 100.0); auto ddOriginal = std::make_unique>(qcOriginal->getNqubits()); - auto originalRootEdge = ddOriginal->makeZeroState( - static_cast(qcOriginal->getNqubits())); + auto originalRootEdge = ddOriginal->makeZeroState(qcOriginal->getNqubits()); ddOriginal->incRef(originalRootEdge); auto measurementsOriginal = simulate(qcOriginal.get(), originalRootEdge, ddOriginal, shots); auto ddEcc = std::make_unique>(qcMapped->getNqubits()); - auto eccRootEdge = ddEcc->makeZeroState( - static_cast(qcMapped->getNqubits())); + auto eccRootEdge = ddEcc->makeZeroState(qcMapped->getNqubits()); ddEcc->incRef(eccRootEdge); auto measurementsProtected = @@ -210,13 +208,13 @@ class DDECCFunctionalityTest : public ::testing::Test { auto difference = std::max(eccHits, hits) - std::min(eccHits, hits); std::cout << "Diff/tolerance " << difference << "/" << toleranceAbsolute << " Original register: " << hits - << " ecc register: " << eccHits << std::endl; + << " ecc register: " << eccHits << "\n"; if (simulateWithErrors) { std::cout << " Simulating an error in qubit " << target << " after " - << insertErrorAfterNGates << " gates." << std::endl; + << insertErrorAfterNGates << " gates.\n"; } if (static_cast(difference) > toleranceAbsolute) { - std::cout << "Error is too large!" << std::endl; + std::cout << "Error is too large!\n"; return false; } } From f9eb6d797859b119ea7c5be9c9834146b7e34a3d Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sat, 15 Jul 2023 14:43:32 +0200 Subject: [PATCH 07/14] =?UTF-8?q?=F0=9F=A9=B9=20fix=20CodeQL=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/Package.hpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index f8b05c050..05d135471 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -836,7 +836,8 @@ template class Package { auto e = makeDDNode(z, em0); // process lines above the larger target (by creating identity structures) - for (++z; z < n + start; ++z) { + const auto end = static_cast(n + start); + for (++z; z < end; ++z) { e = makeDDNode(z, std::array{e, mEdge::zero, mEdge::zero, e}); } @@ -2609,7 +2610,7 @@ template class Package { f = makeDDNode(f.p->v, edges); // something to reduce for this qubit - if (f.p->v >= 0 && ancillary[f.p->v]) { + if (ancillary[f.p->v]) { if (regular) { if (f.p->e[1].w != Complex::zero || f.p->e[3].w != Complex::zero) { f = makeDDNode(f.p->v, std::array{f.p->e[0], mEdge::zero, f.p->e[2], @@ -2655,7 +2656,7 @@ template class Package { f = makeDDNode(f.p->v, edges); // something to reduce for this qubit - if (f.p->v >= 0 && garbage[f.p->v]) { + if (garbage[f.p->v]) { if (f.p->e[1].w != Complex::zero) { vEdge g{}; if (f.p->e[0].w == Complex::zero && f.p->e[1].w != Complex::zero) { @@ -2708,7 +2709,7 @@ template class Package { f = makeDDNode(f.p->v, edges); // something to reduce for this qubit - if (f.p->v >= 0 && garbage[f.p->v]) { + if (garbage[f.p->v]) { if (regular) { if (f.p->e[2].w != Complex::zero || f.p->e[3].w != Complex::zero) { mEdge g{}; From f7f224933ba697dbb625cd7d6f37038e0eaba14e Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sat, 15 Jul 2023 14:47:40 +0200 Subject: [PATCH 08/14] =?UTF-8?q?=F0=9F=A9=B9=20fix=20comparators?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/Export.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/dd/Export.hpp b/include/dd/Export.hpp index 24c48ff4c..ec8da35ad 100644 --- a/include/dd/Export.hpp +++ b/include/dd/Export.hpp @@ -663,7 +663,7 @@ static void toDot(const Edge& e, std::ostream& os, bool colored = true, auto priocmp = [](const Edge* left, const Edge* right) { if (left->p == nullptr) { - return true; + return right->p != nullptr; } if (right->p == nullptr) { return false; @@ -943,7 +943,7 @@ static void exportEdgeWeights(const Edge& edge, std::ostream& stream) { struct Priocmp { bool operator()(const Edge* left, const Edge* right) { if (left->p == nullptr) { - return true; + return right->p != nullptr; } if (right->p == nullptr) { return false; From 455f214989a9ce9a2048d0a62dcbb29899eecc1d Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sat, 15 Jul 2023 15:01:58 +0200 Subject: [PATCH 09/14] =?UTF-8?q?=E2=9A=97=EF=B8=8F=20maybe=20these=20stat?= =?UTF-8?q?ic=20asserts=20help=20clang-tidy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/Package.hpp | 8 ++++++++ include/dd/UniqueTable.hpp | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index 05d135471..9adb92a0f 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -1782,6 +1782,14 @@ template class Package { RightOperand multiply(const LeftOperand& x, const RightOperand& y, const Qubit start = 0, [[maybe_unused]] const bool generateDensityMatrix = false) { + static_assert(std::disjunction_v, + std::is_same>, + "Left operand must be a matrix or density matrix"); + static_assert(std::disjunction_v, + std::is_same, + std::is_same>, + "Right operand must be a vector, matrix or density matrix"); + [[maybe_unused]] const auto before = cn.cacheCount(); Qubit var{}; diff --git a/include/dd/UniqueTable.hpp b/include/dd/UniqueTable.hpp index 976bd87e5..fa3fc3e47 100644 --- a/include/dd/UniqueTable.hpp +++ b/include/dd/UniqueTable.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace dd { @@ -23,6 +24,11 @@ namespace dd { */ template class UniqueTable { + static_assert( + std::disjunction_v, std::is_same, + std::is_same>, + "Node type must be one of vNode, mNode, dNode"); + public: /** * @brief The initial garbage collection limit. From 534bd01cc113f779142e028ec20a8c7e1368fd26 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sat, 15 Jul 2023 15:26:40 +0200 Subject: [PATCH 10/14] =?UTF-8?q?=F0=9F=A9=B9=20re-add=20assertions=20to?= =?UTF-8?q?=20please=20clang-tidy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/Package.hpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index 9adb92a0f..d6c75d8a1 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -200,9 +200,12 @@ template class Package { cn.incRef(e.w); const auto& p = e.p; const auto inc = getUniqueTable().incRef(p); - if (inc && p->ref == 1U) { - for (const auto& child : p->e) { - incRef(child); + if (inc) { + assert(p != nullptr); + if (p->ref == 1U) { + for (const auto& child : p->e) { + incRef(child); + } } } } @@ -221,9 +224,12 @@ template class Package { cn.decRef(e.w); const auto& p = e.p; const auto dec = getUniqueTable().decRef(p); - if (dec && p->ref == 0U) { - for (const auto& child : p->e) { - decRef(child); + if (dec) { + assert(p != nullptr); + if (p->ref == 0U) { + for (const auto& child : p->e) { + decRef(child); + } } } } @@ -1811,9 +1817,11 @@ template class Package { dEdge::revertDmChangesToEdges(xCopy, yCopy); } else { if (!x.isTerminal()) { + assert(x.p != nullptr); var = x.p->v; } if (!y.isTerminal() && (y.p->v) > var) { + assert(y.p != nullptr); var = y.p->v; } e = multiply2(x, y, var, start); From 8d2e24df4ab6678b882af499d8444da9eaebd1b6 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sun, 16 Jul 2023 10:44:21 +0200 Subject: [PATCH 11/14] Update include/dd/Package.hpp --- include/dd/Package.hpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index d6c75d8a1..98ce3a2d0 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -200,12 +200,9 @@ template class Package { cn.incRef(e.w); const auto& p = e.p; const auto inc = getUniqueTable().incRef(p); - if (inc) { - assert(p != nullptr); - if (p->ref == 1U) { - for (const auto& child : p->e) { - incRef(child); - } + if (inc && p->ref == 1U) { + for (const auto& child : p->e) { + incRef(child); } } } From 1f8ea3f423bbcc5a04c4ee95722fc241608ae9ba Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sun, 16 Jul 2023 10:44:40 +0200 Subject: [PATCH 12/14] Update include/dd/Package.hpp --- include/dd/Package.hpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index 98ce3a2d0..81233602f 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -221,12 +221,9 @@ template class Package { cn.decRef(e.w); const auto& p = e.p; const auto dec = getUniqueTable().decRef(p); - if (dec) { - assert(p != nullptr); - if (p->ref == 0U) { - for (const auto& child : p->e) { - decRef(child); - } + if (dec && p->ref == 0U) { + for (const auto& child : p->e) { + decRef(child); } } } From a353114a8a2b7ba3bd04e582ed914929f30793f5 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sun, 16 Jul 2023 10:45:08 +0200 Subject: [PATCH 13/14] Update include/dd/UniqueTable.hpp --- include/dd/UniqueTable.hpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/include/dd/UniqueTable.hpp b/include/dd/UniqueTable.hpp index fa3fc3e47..b7b529ccf 100644 --- a/include/dd/UniqueTable.hpp +++ b/include/dd/UniqueTable.hpp @@ -134,9 +134,12 @@ template class UniqueTable { */ [[nodiscard]] bool incRef(Node* p) noexcept { const auto inc = ::dd::incRef(p); - if (inc && p->ref == 1U) { - stats.trackActiveEntry(); - ++active[p->v]; + if (inc) { + assert(p != nullptr); + if (p->ref == 1U) { + stats.trackActiveEntry(); + ++active[p->v]; + } } return inc; } From 6eb2b563c4a28f6559c7a04e8aa747151f252863 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sun, 16 Jul 2023 10:45:21 +0200 Subject: [PATCH 14/14] Update include/dd/UniqueTable.hpp --- include/dd/UniqueTable.hpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/include/dd/UniqueTable.hpp b/include/dd/UniqueTable.hpp index b7b529ccf..a9595c31b 100644 --- a/include/dd/UniqueTable.hpp +++ b/include/dd/UniqueTable.hpp @@ -156,9 +156,12 @@ template class UniqueTable { */ [[nodiscard]] bool decRef(Node* p) noexcept { const auto dec = ::dd::decRef(p); - if (dec && p->ref == 0U) { - --stats.activeEntryCount; - --active[p->v]; + if (dec) { + assert(p != nullptr); + if (p->ref == 0U) { + --stats.activeEntryCount; + --active[p->v]; + } } return dec; }