From 3e8ab4485aefab789aa5a6d899d02698fa9b26a7 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Mon, 3 May 2021 13:11:12 +0200 Subject: [PATCH 01/16] Porting slicing changes to new package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✨ state generation, matrix generation, and multiplication receive an additional parameter `start` (default 0) that indicates the bottom-most qubit the DD is built from 💥 removed the `makeGateDD` constructor taking two `Qubit` arguments for control and target, as it is ambiguous with the new `start` parameter ✨ method for deleting a specific edge of a DD node ✨ `kronecker` receives an additional parameter indicating whether to increase the indices of the top DD ✨ method for tranfering a DD Edge from one package to another ✨ methods for (recursively) exporting and adding amplitudes Signed-off-by: Lukas Burgholzer --- include/dd/Package.hpp | 345 +++++++++++++++++++++++++++++++++-------- test/test_package.cpp | 20 +-- 2 files changed, 293 insertions(+), 72 deletions(-) diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index 51f54947..a31153a4 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -190,21 +190,21 @@ namespace dd { } // generate |0...0> with n qubits - vEdge makeZeroState(QubitCount n) { - assert(n <= nqubits); + vEdge makeZeroState(QubitCount n, std::size_t start = 0) { + assert(n + start <= nqubits); auto f = vEdge::one; - for (std::size_t p = 0; p < n; p++) { + for (std::size_t p = start; p < n + start; p++) { f = makeDDNode(static_cast(p), std::array{f, vEdge::zero}); } return f; } // generate computational basis state |i> with n qubits - vEdge makeBasisState(QubitCount n, const std::vector& state) { - assert(n <= nqubits); + vEdge makeBasisState(QubitCount n, const std::vector& state, std::size_t start = 0) { + assert(n + start <= nqubits); auto f = vEdge::one; - for (std::size_t p = 0; p < n; ++p) { + for (std::size_t p = start; p < n + start; ++p) { if (state[p] == 0) { f = makeDDNode(static_cast(p), std::array{f, vEdge::zero}); } else { @@ -214,15 +214,15 @@ namespace dd { return f; } // generate general basis state with n qubits - vEdge makeBasisState(QubitCount n, const std::vector& state) { - assert(n <= nqubits); + vEdge makeBasisState(QubitCount n, const std::vector& state, std::size_t start = 0) { + assert(n + start <= nqubits); if (state.size() < n) { throw std::invalid_argument("Insufficient qubit states provided. Requested " + std::to_string(n) + ", but received " + std::to_string(state.size())); } auto f = vEdge::one; - for (std::size_t p = 0; p < n; ++p) { + for (std::size_t p = start; p < n + start; ++p) { switch (state[p]) { case BasisStates::zero: f = makeDDNode(static_cast(p), std::array{f, vEdge::zero}); @@ -353,17 +353,14 @@ namespace dd { } // build matrix representation for a single gate on an n-qubit circuit - mEdge makeGateDD(const std::array& mat, QubitCount n, Qubit target) { - return makeGateDD(mat, n, Controls{}, target); + mEdge makeGateDD(const std::array& mat, QubitCount n, Qubit target, std::size_t start = 0) { + return makeGateDD(mat, n, Controls{}, target, start); } - mEdge makeGateDD(const std::array& mat, QubitCount n, const Control& control, Qubit target) { - return makeGateDD(mat, n, Controls{control}, target); + mEdge makeGateDD(const std::array& mat, QubitCount n, const Control& control, Qubit target, std::size_t start = 0) { + return makeGateDD(mat, n, Controls{control}, target, start); } - mEdge makeGateDD(const std::array& mat, QubitCount n, Qubit control, Qubit target) { - return makeGateDD(mat, n, Controls{{control}}, target); - } - mEdge makeGateDD(const std::array& mat, QubitCount n, const Controls& controls, Qubit target) { - assert(n <= nqubits); + mEdge makeGateDD(const std::array& mat, QubitCount n, const Controls& controls, Qubit target, std::size_t start = 0) { + assert(n + start <= nqubits); std::array em{}; auto it = controls.begin(); @@ -376,16 +373,16 @@ namespace dd { } //process lines below target - Qubit z = 0; + auto z = static_cast(start); for (; z < 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 == z) { if (it->type == Control::Type::neg) { // neg. control - em[i] = makeDDNode(z, std::array{em[i], mEdge::zero, mEdge::zero, (i1 == i2) ? makeIdent(z) : mEdge::zero}); + em[i] = makeDDNode(z, std::array{em[i], mEdge::zero, mEdge::zero, (i1 == i2) ? makeIdent(static_cast(start), static_cast(z - 1)) : mEdge::zero}); } else { // pos. control - em[i] = makeDDNode(z, std::array{(i1 == i2) ? makeIdent(z) : mEdge::zero, mEdge::zero, mEdge::zero, em[i]}); + em[i] = makeDDNode(z, std::array{(i1 == i2) ? makeIdent(static_cast(start), static_cast(z - 1)) : mEdge::zero, mEdge::zero, mEdge::zero, em[i]}); } } else { // not connected em[i] = makeDDNode(z, std::array{em[i], mEdge::zero, mEdge::zero, em[i]}); @@ -401,13 +398,13 @@ namespace dd { auto e = makeDDNode(z, em); //process lines above target - for (; z < static_cast(n - 1); z++) { + for (; z < static_cast(n - 1 + start); z++) { auto q = static_cast(z + 1); if (it != controls.end() && it->qubit == q) { if (it->type == Control::Type::neg) { // neg. control - e = makeDDNode(q, std::array{e, mEdge::zero, mEdge::zero, makeIdent(q)}); + e = makeDDNode(q, std::array{e, mEdge::zero, mEdge::zero, makeIdent(static_cast(start), static_cast(q - 1))}); } else { // pos. control - e = makeDDNode(q, std::array{makeIdent(q), mEdge::zero, mEdge::zero, e}); + e = makeDDNode(q, std::array{makeIdent(static_cast(start), static_cast(q - 1)), mEdge::zero, mEdge::zero, e}); } ++it; } else { // not connected @@ -417,57 +414,57 @@ namespace dd { return e; } - mEdge makeSWAPDD(QubitCount n, const Controls& controls, Qubit target0, Qubit target1) { + mEdge makeSWAPDD(QubitCount n, const Controls& controls, Qubit target0, Qubit target1, std::size_t start = 0) { auto c = controls; c.insert(Control{target0}); - mEdge e = makeGateDD(Xmat, n, c, target1); + mEdge e = makeGateDD(Xmat, n, c, target1, start); c.erase(Control{target0}); c.insert(Control{target1}); - e = multiply(e, multiply(makeGateDD(Xmat, n, c, target0), e)); + e = multiply(e, multiply(makeGateDD(Xmat, n, c, target0, start), e)); return e; } - mEdge makePeresDD(QubitCount n, const Controls& controls, Qubit target0, Qubit target1) { + mEdge makePeresDD(QubitCount n, const Controls& controls, Qubit target0, Qubit target1, std::size_t start = 0) { auto c = controls; c.insert(Control{target1}); - mEdge e = makeGateDD(Xmat, n, c, target0); - e = multiply(makeGateDD(Xmat, n, controls, target1), e); + mEdge e = makeGateDD(Xmat, n, c, target0, start); + e = multiply(makeGateDD(Xmat, n, controls, target1, start), e); return e; } - mEdge makePeresdagDD(QubitCount n, const Controls& controls, Qubit target0, Qubit target1) { - mEdge e = makeGateDD(Xmat, n, controls, target1); + mEdge makePeresdagDD(QubitCount n, const Controls& controls, Qubit target0, Qubit target1, std::size_t start = 0) { + mEdge e = makeGateDD(Xmat, n, controls, target1, start); auto c = controls; c.insert(Control{target1}); - e = multiply(makeGateDD(Xmat, n, c, target0), e); + e = multiply(makeGateDD(Xmat, n, c, target0, start), e); return e; } - mEdge makeiSWAPDD(QubitCount n, const Controls& controls, Qubit target0, Qubit target1) { - mEdge e = makeGateDD(Smat, n, controls, target1); // S q[1] - e = multiply(e, makeGateDD(Smat, n, controls, target0)); // S q[0] - e = multiply(e, makeGateDD(Hmat, n, controls, target0)); // H q[0] + mEdge makeiSWAPDD(QubitCount n, const Controls& controls, Qubit target0, Qubit target1, 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(Control{target0}); - e = multiply(e, makeGateDD(Xmat, n, c, target1)); // CX q[0], q[1] + e = multiply(e, makeGateDD(Xmat, n, c, target1, start)); // CX q[0], q[1] c.erase(Control{target0}); c.insert(Control{target1}); - e = multiply(e, makeGateDD(Xmat, n, c, target0)); // CX q[1], q[0] - e = multiply(e, makeGateDD(Hmat, n, controls, target1)); // H q[1] + 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(QubitCount n, const Controls& controls, Qubit target0, Qubit target1) { - mEdge e = makeGateDD(Hmat, n, controls, target1); // H q[1] + mEdge makeiSWAPinvDD(QubitCount n, const Controls& controls, Qubit target0, Qubit target1, std::size_t start = 0) { + mEdge e = makeGateDD(Hmat, n, controls, target1, start); // H q[1] auto c = controls; c.insert(Control{target1}); - e = multiply(e, makeGateDD(Xmat, n, c, target0)); // CX q[1], q[0] + e = multiply(e, makeGateDD(Xmat, n, c, target0, start)); // CX q[1], q[0] c.erase(Control{target1}); c.insert(Control{target0}); - e = multiply(e, makeGateDD(Xmat, n, c, target1)); // CX q[0], q[1] - e = multiply(e, makeGateDD(Hmat, n, controls, target0)); // H q[0] - e = multiply(e, makeGateDD(Sdagmat, n, controls, target0)); // Sdag q[0] - e = multiply(e, makeGateDD(Sdagmat, n, controls, target1)); // Sdag q[1] + 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, makeGateDD(Sdagmat, n, controls, target0, start)); // Sdag q[0] + e = multiply(e, makeGateDD(Sdagmat, n, controls, target1, start)); // Sdag q[1] return e; } @@ -592,6 +589,51 @@ namespace dd { return l; } + template + Edge deleteEdge(const Edge& e, dd::Qubit v, 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, std::unordered_map>& nodes) { + if (e.p == nullptr || e.isTerminal()) { + return e; + } + + const auto& nodeit = nodes.find(e.p); + Edge newedge{}; + if (nodeit != nodes.end()) { + newedge = nodeit->second; + } else { + constexpr std::size_t N = std::tuple_size_ve)>; + std::array, N> edges{}; + if (e.p->v == v) { + for (std::size_t i = 0; i < N; i++) { + edges[i] = i == edgeIdx ? Edge::zero : e.p->e[i]; // optimization -> node cannot occur below again, since dd is assumed to be free + } + } else { + for (std::size_t i = 0; i < N; i++) { + edges[i] = deleteEdge(e.p->e[i], v, edgeIdx, nodes); + } + } + + newedge = makeDDNode(e.p->v, edges); + nodes[e.p] = newedge; + } + + if (newedge.w.approximatelyOne()) { + newedge.w = e.w; + } else { + auto w = cn.getTemporary(); + dd::ComplexNumbers::mul(w, newedge.w, e.w); + newedge.w = cn.lookup(w); + } + + return newedge; + } + /// /// Compute table definitions /// @@ -816,7 +858,7 @@ namespace dd { [[nodiscard]] ComputeTable, Edge, CachedEdge>& getMultiplicationComputeTable(); template - RightOperand multiply(const LeftOperand& x, const RightOperand& y) { + RightOperand multiply(const LeftOperand& x, const RightOperand& y, dd::Qubit start = 0) { [[maybe_unused]] const auto before = cn.cacheCount(); Qubit var = -1; @@ -827,7 +869,7 @@ namespace dd { var = y.p->v; } - auto e = multiply2(x, y, var); + auto e = multiply2(x, y, var, start); if (e.w != Complex::zero && e.w != Complex::one) { cn.returnToCache(e.w); @@ -842,7 +884,7 @@ namespace dd { private: template - Edge multiply2(const Edge& x, const Edge& y, Qubit var) { + Edge multiply2(const Edge& x, const Edge& y, Qubit var, Qubit start = 0) { using LEdge = Edge; using REdge = Edge; using ResultEdge = Edge; @@ -854,7 +896,7 @@ namespace dd { return ResultEdge::zero; } - if (var == -1) { + if (var == start - 1) { return ResultEdge::terminal(cn.mulCached(x.w, y.w)); } @@ -888,7 +930,7 @@ namespace dd { if constexpr (N == NEDGE) { // additionally check if y is the identity in case of matrix multiplication if (y.p->ident) { - e = makeIdent(0, var); + e = makeIdent(start, var); } else { e = yCopy; } @@ -943,7 +985,7 @@ namespace dd { e2 = yCopy; } - auto m = multiply2(e1, e2, static_cast(var - 1)); + auto m = multiply2(e1, e2, static_cast(var - 1), start); if (k == 0 || edge[idx].w == Complex::zero) { edge[idx] = m; @@ -1071,8 +1113,8 @@ namespace dd { [[nodiscard]] ComputeTable, Edge, CachedEdge, 4096>& getKroneckerComputeTable(); template - Edge kronecker(const Edge& x, const Edge& y) { - auto e = kronecker2(x, y); + Edge kronecker(const Edge& x, const Edge& y, bool incIdx = true) { + auto e = kronecker2(x, y, incIdx); if (e.w != Complex::zero && e.w != Complex::one) { cn.returnToCache(e.w); @@ -1091,8 +1133,8 @@ namespace dd { private: template - Edge kronecker2(const Edge& x, const Edge& y) { - if (x.w.approximatelyZero()) + Edge kronecker2(const Edge& x, const Edge& y, bool incIdx = true) { + if (x.w.approximatelyZero() || y.w.approximatelyZero()) return Edge::zero; if (x.isTerminal()) { @@ -1115,9 +1157,11 @@ namespace dd { // special case handling for matrices if constexpr (N == NEDGE) { if (x.p->ident) { - auto e = makeDDNode(static_cast(y.p->v + 1), std::array{y, Edge::zero, Edge::zero, y}); + auto idx = incIdx ? static_cast(y.p->v + 1) : y.p->v; + auto e = makeDDNode(idx, std::array{y, Edge::zero, Edge::zero, y}); for (auto i = 0; i < x.p->v; ++i) { - e = makeDDNode(static_cast(e.p->v + 1), std::array{e, Edge::zero, Edge::zero, e}); + idx = incIdx ? static_cast(e.p->v + 1) : e.p->v; + e = makeDDNode(idx, std::array{e, Edge::zero, Edge::zero, e}); } e.w = cn.getCached(CTEntry::val(y.w.r), CTEntry::val(y.w.i)); @@ -1128,10 +1172,11 @@ namespace dd { std::array, N> edge{}; for (auto i = 0U; i < N; ++i) { - edge[i] = kronecker2(x.p->e[i], y); + edge[i] = kronecker2(x.p->e[i], y, incIdx); } - auto e = makeDDNode(static_cast(y.p->v + x.p->v + 1), edge, true); + auto idx = incIdx ? static_cast(y.p->v + x.p->v + 1) : x.p->v; + auto e = makeDDNode(idx, edge, true); ComplexNumbers::mul(e.w, e.w, x.w); computeTable.insert(x, y, {e.p, e.w}); return e; @@ -1234,7 +1279,7 @@ namespace dd { // create n-qubit identity DD. makeIdent(n) === makeIdent(0, n-1) mEdge makeIdent(QubitCount n) { return makeIdent(0, static_cast(n - 1)); } mEdge makeIdent(Qubit leastSignificantQubit, Qubit mostSignificantQubit) { - if (mostSignificantQubit < 0) + if (mostSignificantQubit < leastSignificantQubit) return mEdge::one; if (leastSignificantQubit == 0 && IdTable[mostSignificantQubit].p != nullptr) { @@ -1710,6 +1755,182 @@ namespace dd { cn.returnToCache(c); } + void exportAmplitudesRec(const dd::Package::vEdge& node, std::ostream& oss, const std::string& path, Complex& amplitude, dd::QubitCount level, bool binary = false) { + if (node.isTerminal()) { + auto amp = cn.getTemporary(); + dd::ComplexNumbers::mul(amp, amplitude, node.w); + for (std::size_t i = 0; i < (1UL << level); i++) { + if (binary) { + amp.writeBinary(oss); + } else { + oss << amp.toString(false, 16) << "\n"; + } + } + + return; + } + + auto a = cn.mulCached(amplitude, node.w); + exportAmplitudesRec(node.p->e[0], oss, path + "0", a, level - 1, binary); + exportAmplitudesRec(node.p->e[1], oss, path + "1", a, level - 1, binary); + cn.returnToCache(a); + } + void exportAmplitudes(const dd::Package::vEdge& node, std::ostream& oss, dd::QubitCount nq, bool binary = false) { + if (node.isTerminal()) { + // TODO special treatment + return; + } + auto weight = cn.getCached(1., 0.); + exportAmplitudesRec(node, oss, "", weight, nq, binary); + cn.returnToCache(weight); + } + void exportAmplitudes(const dd::Package::vEdge& node, const std::string& outputFilename, dd::QubitCount nq, bool binary = false) { + std::ofstream init(outputFilename); + std::ostringstream oss{}; + + exportAmplitudes(node, oss, nq, binary); + + init << oss.str() << std::flush; + init.close(); + } + + void exportAmplitudesRec(const dd::Package::vEdge& node, std::vector& amplitudes, Complex& amplitude, dd::QubitCount level, std::size_t idx) { + if (node.isTerminal()) { + auto amp = cn.getTemporary(); + dd::ComplexNumbers::mul(amp, amplitude, node.w); + idx <<= level; + for (std::size_t i = 0; i < (1UL << level); i++) { + amplitudes[idx++] = dd::ComplexValue{dd::ComplexTable<>::Entry::val(amp.r), dd::ComplexTable<>::Entry::val(amp.i)}; + } + + return; + } + + auto a = cn.mulCached(amplitude, node.w); + exportAmplitudesRec(node.p->e[0], amplitudes, a, level - 1, idx << 1); + exportAmplitudesRec(node.p->e[1], amplitudes, a, level - 1, (idx << 1) | 1); + cn.returnToCache(a); + } + void exportAmplitudes(const dd::Package::vEdge& node, std::vector& amplitudes, dd::QubitCount nq) { + if (node.isTerminal()) { + // TODO special treatment + return; + } + auto weight = cn.getCached(1., 0.); + exportAmplitudesRec(node, amplitudes, weight, nq, 0); + cn.returnToCache(weight); + } + + void addAmplitudesRec(const dd::Package::vEdge& node, std::vector& amplitudes, ComplexValue& amplitude, dd::QubitCount level, std::size_t idx) { + auto ar = dd::ComplexTable<>::Entry::val(node.w.r); + auto ai = dd::ComplexTable<>::Entry::val(node.w.i); + ComplexValue amp{ar * amplitude.r - ai * amplitude.i, ar * amplitude.i + ai * amplitude.r}; + + if (node.isTerminal()) { + idx <<= level; + for (std::size_t i = 0; i < (1UL << level); i++) { + auto temp = dd::ComplexValue{amp.r + amplitudes[idx].r, amp.i + amplitudes[idx].i}; + amplitudes[idx++] = temp; + } + + return; + } + + addAmplitudesRec(node.p->e[0], amplitudes, amp, level - 1, idx << 1); + addAmplitudesRec(node.p->e[1], amplitudes, amp, level - 1, idx << 1 | 1); + } + void addAmplitudes(const dd::Package::vEdge& node, std::vector& amplitudes, dd::QubitCount nq) { + if (node.isTerminal()) { + // TODO special treatment + return; + } + ComplexValue a{1., 0.}; + addAmplitudesRec(node, amplitudes, a, nq, 0); + } + + // transfers a decision diagram from another package to this package + template + Edge transfer(Edge& original) { + // POST ORDER TRAVERSAL USING ONE STACK https://www.geeksforgeeks.org/iterative-postorder-traversal-using-stack/ + Edge root{}; + std::stack stack; + + std::unordered_map mapped_node{}; + + Edge* node = &original; + if (!node->isTerminal()) { + constexpr std::size_t N = std::tuple_size_ve)>; + do { + while (node != nullptr && !node->isTerminal()) { + for (short i = N - 1; i > 0; --i) { + auto& edge = node->p->e[i]; + if (edge.isTerminal()) { + continue; + } + if (edge.w.approximatelyZero()) { + continue; + } + if (mapped_node.find(edge.p) != mapped_node.end()) { + continue; + } + + // non-zero edge to be included + stack.push(&edge); + } + stack.push(node); + node = &node->p->e[0]; + } + node = stack.top(); + stack.pop(); + + bool hasChild = false; + for (std::size_t i = 1; i < N && !hasChild; ++i) { + auto& edge = node->p->e[i]; + if (edge.w.approximatelyZero()) { + continue; + } + if (mapped_node.find(edge.p) != mapped_node.end()) { + continue; + } + hasChild = edge.p == stack.top()->p; + } + + if (hasChild) { + Edge* temp = stack.top(); + stack.pop(); + stack.push(node); + node = temp; + } else { + if (mapped_node.find(node->p) != mapped_node.end()) { + node = nullptr; + continue; + } + std::array edges{}; + for (std::size_t i = 0; i < N; i++) { + if (node->p->e[i].isTerminal()) { + edges[i].p = node->p->e[i].p; + } else { + edges[i].p = mapped_node[node->p->e[i].p]; + } + edges[i].w = cn.lookup(node->p->e[i].w); + } + root = makeDDNode(node->p->v, edges); + mapped_node[node->p] = root.p; + node = nullptr; + } + } while (!stack.empty()); + + auto w = cn.getCached(dd::ComplexTable<>::Entry::val(original.w.r), dd::ComplexTable<>::Entry::val(original.w.i)); + dd::ComplexNumbers::mul(w, root.w, w); + root.w = cn.lookup(w); + cn.returnToCache(w); + } else { + root.p = original.p; // terminal -> static + root.w = cn.lookup(original.w); + } + return root; + } + /// /// Deserialization /// Note: do not rely on the binary format being portable across different architectures/platforms diff --git a/test/test_package.cpp b/test/test_package.cpp index 2e178ecd..dbdbf4a2 100644 --- a/test/test_package.cpp +++ b/test/test_package.cpp @@ -62,7 +62,7 @@ TEST(DDPackageTest, BellState) { auto dd = std::make_unique(2); auto h_gate = dd->makeGateDD(dd::Hmat, 2, 1); - auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 1, 0); + auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 1_pc, 0); auto zero_state = dd->makeZeroState(2); auto bell_state = dd->multiply(dd->multiply(cx_gate, h_gate), zero_state); @@ -148,7 +148,7 @@ TEST(DDPackageTest, VectorSerializationTest) { auto dd = std::make_unique(2); auto h_gate = dd->makeGateDD(dd::Hmat, 2, 1); - auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 1, 0); + auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 1_pc, 0); auto zero_state = dd->makeZeroState(2); auto bell_state = dd->multiply(dd->multiply(cx_gate, h_gate), zero_state); @@ -166,7 +166,7 @@ TEST(DDPackageTest, BellMatrix) { auto dd = std::make_unique(2); auto h_gate = dd->makeGateDD(dd::Hmat, 2, 1); - auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 1, 0); + auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 1_pc, 0); auto bell_matrix = dd->multiply(cx_gate, h_gate); @@ -219,7 +219,7 @@ TEST(DDPackageTest, MatrixSerializationTest) { auto dd = std::make_unique(2); auto h_gate = dd->makeGateDD(dd::Hmat, 2, 1); - auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 1, 0); + auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 1_pc, 0); auto bell_matrix = dd->multiply(cx_gate, h_gate); @@ -236,7 +236,7 @@ TEST(DDPackageTest, SerializationErrors) { auto dd = std::make_unique(2); auto h_gate = dd->makeGateDD(dd::Hmat, 2, 1); - auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 1, 0); + auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 1_pc, 0); auto zero_state = dd->makeZeroState(2); auto bell_state = dd->multiply(dd->multiply(cx_gate, h_gate), zero_state); @@ -282,7 +282,7 @@ TEST(DDPackageTest, TestConsistency) { auto dd = std::make_unique(2); auto h_gate = dd->makeGateDD(dd::Hmat, 2, 1); - auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 1, 0); + auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 1_pc, 0); auto zero_state = dd->makeZeroState(2); auto bell_matrix = dd->multiply(cx_gate, h_gate); @@ -374,7 +374,7 @@ TEST(DDPackageTest, TestLocalInconsistency) { auto dd = std::make_unique(3); auto h_gate = dd->makeGateDD(dd::Hmat, 2, 0); - auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 0, 1); + auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 0_pc, 1); auto zero_state = dd->makeZeroState(2); auto bell_state = dd->multiply(dd->multiply(cx_gate, h_gate), zero_state); @@ -400,7 +400,7 @@ TEST(DDPackageTest, TestLocalInconsistency) { TEST(DDPackageTest, Ancillaries) { auto dd = std::make_unique(4); auto h_gate = dd->makeGateDD(dd::Hmat, 2, 0); - auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 0, 1); + auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 0_pc, 1); auto bell_matrix = dd->multiply(cx_gate, h_gate); dd->incRef(bell_matrix); @@ -437,7 +437,7 @@ TEST(DDPackageTest, Ancillaries) { TEST(DDPackageTest, GarbageVector) { auto dd = std::make_unique(4); auto h_gate = dd->makeGateDD(dd::Hmat, 2, 0); - auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 0, 1); + auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 0_pc, 1); auto zero_state = dd->makeZeroState(2); auto bell_state = dd->multiply(dd->multiply(cx_gate, h_gate), zero_state); dd->printVector(bell_state); @@ -468,7 +468,7 @@ TEST(DDPackageTest, GarbageVector) { TEST(DDPackageTest, GarbageMatrix) { auto dd = std::make_unique(4); auto h_gate = dd->makeGateDD(dd::Hmat, 2, 0); - auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 0, 1); + auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 0_pc, 1); auto bell_matrix = dd->multiply(cx_gate, h_gate); dd->incRef(bell_matrix); From 66020dd91e76d4e4c5b85777e0c2f4b90c1f8598 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Mon, 3 May 2021 13:42:00 +0200 Subject: [PATCH 02/16] Bugfixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ♻️ adjust to removed constructor 🐛 `BM_MakeControlledQubitGateDD_ControlBottom_TargetTop` did not actually construct a controlled operation Signed-off-by: Lukas Burgholzer --- test/bench_package.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/test/bench_package.cpp b/test/bench_package.cpp index 0b30df28..59185a07 100644 --- a/test/bench_package.cpp +++ b/test/bench_package.cpp @@ -117,7 +117,7 @@ static void BM_MakeControlledQubitGateDD_ControlBottom_TargetTop(benchmark::Stat auto nqubits = state.range(0); auto dd = std::make_unique(nqubits); for (auto _: state) { - benchmark::DoNotOptimize(dd->makeGateDD(dd::Xmat, nqubits, static_cast(nqubits - 1))); + benchmark::DoNotOptimize(dd->makeGateDD(dd::Xmat, nqubits, 0_pc, static_cast(nqubits - 1))); } } BENCHMARK(BM_MakeControlledQubitGateDD_ControlBottom_TargetTop)->Apply(QubitRange); @@ -126,7 +126,7 @@ static void BM_MakeControlledQubitGateDD_ControlBottom_TargetMiddle(benchmark::S auto nqubits = state.range(0); auto dd = std::make_unique(nqubits); for (auto _: state) { - benchmark::DoNotOptimize(dd->makeGateDD(dd::Xmat, nqubits, 0, static_cast(nqubits / 2))); + benchmark::DoNotOptimize(dd->makeGateDD(dd::Xmat, nqubits, 0_pc, static_cast(nqubits / 2))); } } BENCHMARK(BM_MakeControlledQubitGateDD_ControlBottom_TargetMiddle)->Apply(QubitRange); @@ -135,7 +135,7 @@ static void BM_MakeControlledQubitGateDD_ControlTop_TargetMiddle(benchmark::Stat auto nqubits = state.range(0); auto dd = std::make_unique(nqubits); for (auto _: state) { - benchmark::DoNotOptimize(dd->makeGateDD(dd::Xmat, nqubits, static_cast(nqubits - 1), static_cast(nqubits / 2))); + benchmark::DoNotOptimize(dd->makeGateDD(dd::Xmat, nqubits, dd::Control{static_cast(nqubits - 1)}, static_cast(nqubits / 2))); } } BENCHMARK(BM_MakeControlledQubitGateDD_ControlTop_TargetMiddle)->Apply(QubitRange); @@ -144,7 +144,7 @@ static void BM_MakeControlledQubitGateDD_ControlTop_TargetBottom(benchmark::Stat auto nqubits = state.range(0); auto dd = std::make_unique(nqubits); for (auto _: state) { - benchmark::DoNotOptimize(dd->makeGateDD(dd::Xmat, nqubits, static_cast(nqubits - 1), 0)); + benchmark::DoNotOptimize(dd->makeGateDD(dd::Xmat, nqubits, dd::Control{static_cast(nqubits - 1)}, 0)); } } BENCHMARK(BM_MakeControlledQubitGateDD_ControlTop_TargetBottom)->Apply(QubitRange); @@ -191,8 +191,8 @@ static void BM_MakeSWAPDD(benchmark::State& state) { auto dd = std::make_unique(nqubits); for (auto _: state) { - auto sv = dd->makeGateDD(dd::Xmat, nqubits, static_cast(nqubits - 1), 0); - sv = dd->multiply(sv, dd->multiply(dd->makeGateDD(dd::Xmat, nqubits, 0, static_cast(nqubits - 1)), sv)); + auto sv = dd->makeGateDD(dd::Xmat, nqubits, dd::Control{static_cast(nqubits - 1)}, 0); + sv = dd->multiply(sv, dd->multiply(dd->makeGateDD(dd::Xmat, nqubits, 0_pc, static_cast(nqubits - 1)), sv)); benchmark::DoNotOptimize(sv); dd->clearComputeTables(); @@ -255,7 +255,7 @@ static void BM_MxV_CX_ControlTop_TargetBottom(benchmark::State& state) { auto basisStates = std::vector{nqubits, dd::BasisStates::zero}; basisStates[nqubits - 1] = dd::BasisStates::plus; auto plus = dd->makeBasisState(nqubits, basisStates); - auto cx = dd->makeGateDD(dd::Xmat, nqubits, static_cast(nqubits - 1), 0); + auto cx = dd->makeGateDD(dd::Xmat, nqubits, dd::Control{static_cast(nqubits - 1)}, 0); for (auto _: state) { auto sim = dd->multiply(cx, plus); @@ -272,7 +272,7 @@ static void BM_MxV_CX_ControlBottom_TargetTop(benchmark::State& state) { auto basisStates = std::vector{nqubits, dd::BasisStates::zero}; basisStates[0] = dd::BasisStates::plus; auto plus = dd->makeBasisState(nqubits, basisStates); - auto cx = dd->makeGateDD(dd::Xmat, nqubits, 0, static_cast(nqubits - 1)); + auto cx = dd->makeGateDD(dd::Xmat, nqubits, 0_pc, static_cast(nqubits - 1)); for (auto _: state) { auto sim = dd->multiply(cx, plus); @@ -309,8 +309,8 @@ static void BM_MxV_GHZ(benchmark::State& state) { for (auto _: state) { auto sv = zero; sv = dd->multiply(h, sv); - for (int i = nqubits - 2; i >= 0; --i) { - auto cx = dd->makeGateDD(dd::Xmat, nqubits, static_cast(nqubits - 1), i); + for (auto i = static_cast(nqubits - 2); i >= 0; --i) { + auto cx = dd->makeGateDD(dd::Xmat, nqubits, dd::Control{static_cast(nqubits - 1)}, static_cast(i)); sv = dd->multiply(cx, sv); } // clear compute table so the next iteration does not find the result cached @@ -323,7 +323,7 @@ static void BM_MxM_Bell(benchmark::State& state) { auto nqubits = state.range(0); auto dd = std::make_unique(nqubits); auto h = dd->makeGateDD(dd::Hmat, nqubits, static_cast(nqubits - 1)); - auto cx = dd->makeGateDD(dd::Xmat, nqubits, static_cast(nqubits - 1), 0); + auto cx = dd->makeGateDD(dd::Xmat, nqubits, dd::Control{static_cast(nqubits - 1)}, 0); for (auto _: state) { auto bell = dd->multiply(cx, h); @@ -340,8 +340,8 @@ static void BM_MxM_GHZ(benchmark::State& state) { for (auto _: state) { auto func = dd->makeGateDD(dd::Hmat, nqubits, static_cast(nqubits - 1)); - for (int i = nqubits - 2; i >= 0; --i) { - auto cx = dd->makeGateDD(dd::Xmat, nqubits, static_cast(nqubits - 1), static_cast(i)); + for (auto i = static_cast(nqubits - 2); i >= 0; --i) { + auto cx = dd->makeGateDD(dd::Xmat, nqubits, dd::Control{static_cast(nqubits - 1)}, static_cast(i)); func = dd->multiply(cx, func); } // clear compute table so the next iteration does not find the result cached From f794ec2687a4cb06883afa37f6914e1e33ead449 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Mon, 3 May 2021 16:59:53 +0200 Subject: [PATCH 03/16] =?UTF-8?q?=F0=9F=90=9B=20fix=20bug=20in=20`transfer?= =?UTF-8?q?`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/Package.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index a31153a4..81f67e94 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -1855,7 +1855,7 @@ namespace dd { Edge root{}; std::stack stack; - std::unordered_map mapped_node{}; + std::unordered_map mapped_node{}; Edge* node = &original; if (!node->isTerminal()) { From 8398a5aa43bc01a806e2d973e36640615ec53825 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 4 May 2021 15:48:56 +0200 Subject: [PATCH 04/16] =?UTF-8?q?=F0=9F=8E=A8=20reduce=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/Export.hpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/include/dd/Export.hpp b/include/dd/Export.hpp index 665f2c00..af40a515 100644 --- a/include/dd/Export.hpp +++ b/include/dd/Export.hpp @@ -138,7 +138,7 @@ namespace dd { return os; } - static std::ostream& modernNode(const Package::mEdge& e, std::ostream& os) { + [[maybe_unused]] static std::ostream& modernNode(const Package::mEdge& e, std::ostream& os) { auto nodelabel = (reinterpret_cast(e.p) & 0x001fffffU) >> 1U; // this allows for 2^20 (roughly 1e6) unique nodes os << nodelabel << "[label=<"; os << R"()"; @@ -155,7 +155,7 @@ namespace dd { os << "
>,tooltip=\"q" << static_cast(e.p->v) << "\"]\n"; return os; } - static std::ostream& modernNode(const Package::vEdge& e, std::ostream& os) { + [[maybe_unused]] static std::ostream& modernNode(const Package::vEdge& e, std::ostream& os) { auto nodelabel = (reinterpret_cast(e.p) & 0x001fffffU) >> 1U; // this allows for 2^20 (roughly 1e6) unique nodes os << nodelabel << "[label=<"; os << R"()"; @@ -165,7 +165,7 @@ namespace dd { os << "
>,tooltip=\"q" << static_cast(e.p->v) << "\"]\n"; return os; } - static std::ostream& classicNode(const Package::mEdge& e, std::ostream& os) { + [[maybe_unused]] static std::ostream& classicNode(const Package::mEdge& e, std::ostream& os) { auto nodelabel = (reinterpret_cast(e.p) & 0x001fffffU) >> 1U; // this allows for 2^20 (roughly 1e6) unique nodes os << nodelabel << "[shape=circle, width=0.53, fixedsize=true, label=<"; os << R"()"; @@ -203,7 +203,7 @@ namespace dd { os << "
>,tooltip=\"q" << static_cast(e.p->v) << "\"]\n"; return os; } - static std::ostream& classicNode(const Package::vEdge& e, std::ostream& os) { + [[maybe_unused]] static std::ostream& classicNode(const Package::vEdge& e, std::ostream& os) { auto nodelabel = (reinterpret_cast(e.p) & 0x001fffffU) >> 1U; // this allows for 2^20 (roughly 1e6) unique nodes os << nodelabel << "[shape=circle, width=0.46, fixedsize=true, label=<"; os << R"()"; @@ -249,7 +249,7 @@ namespace dd { return os; } - static std::ostream& bwEdge(const Package::mEdge& from, const Package::mEdge& to, short idx, std::ostream& os, bool edgeLabels = false, bool classic = false) { + [[maybe_unused]] static std::ostream& bwEdge(const Package::mEdge& from, const Package::mEdge& to, short idx, std::ostream& os, bool edgeLabels = false, bool classic = false) { auto fromlabel = (reinterpret_cast(from.p) & 0x001fffffU) >> 1U; auto tolabel = (reinterpret_cast(to.p) & 0x001fffffU) >> 1U; @@ -288,7 +288,7 @@ namespace dd { return os; } - static std::ostream& bwEdge(const Package::vEdge& from, const Package::vEdge& to, short idx, std::ostream& os, bool edgeLabels = false, [[maybe_unused]] bool classic = false) { + [[maybe_unused]] static std::ostream& bwEdge(const Package::vEdge& from, const Package::vEdge& to, short idx, std::ostream& os, bool edgeLabels = false, [[maybe_unused]] bool classic = false) { auto fromlabel = (reinterpret_cast(from.p) & 0x001fffffU) >> 1U; auto tolabel = (reinterpret_cast(to.p) & 0x001fffffU) >> 1U; @@ -312,7 +312,7 @@ namespace dd { return os; } - static std::ostream& coloredEdge(const Package::mEdge& from, const Package::mEdge& to, short idx, std::ostream& os, bool edgeLabels = false, bool classic = false) { + [[maybe_unused]] static std::ostream& coloredEdge(const Package::mEdge& from, const Package::mEdge& to, short idx, std::ostream& os, bool edgeLabels = false, bool classic = false) { auto fromlabel = (reinterpret_cast(from.p) & 0x001fffffU) >> 1U; auto tolabel = (reinterpret_cast(to.p) & 0x001fffffU) >> 1U; @@ -349,7 +349,7 @@ namespace dd { return os; } - static std::ostream& coloredEdge(const Package::vEdge& from, const Package::vEdge& to, short idx, std::ostream& os, bool edgeLabels = false, [[maybe_unused]] bool classic = false) { + [[maybe_unused]] static std::ostream& coloredEdge(const Package::vEdge& from, const Package::vEdge& to, short idx, std::ostream& os, bool edgeLabels = false, [[maybe_unused]] bool classic = false) { auto fromlabel = (reinterpret_cast(from.p) & 0x001fffffU) >> 1U; auto tolabel = (reinterpret_cast(to.p) & 0x001fffffU) >> 1U; @@ -486,7 +486,7 @@ namespace dd { } template - static void export2Dot(Edge basic, const std::string& outputFilename, bool colored = true, bool edgeLabels = false, bool classic = false, bool memory = false, bool show = true) { + [[maybe_unused]] static void export2Dot(Edge basic, const std::string& outputFilename, bool colored = true, bool edgeLabels = false, bool classic = false, bool memory = false, bool show = true) { std::ofstream init(outputFilename); toDot(basic, init, colored, edgeLabels, classic, memory); init.close(); @@ -629,7 +629,7 @@ namespace dd { } } } - static void serialize(const Package::mEdge& basic, std::ostream& os, bool writeBinary = false) { + [[maybe_unused]] static void serialize(const Package::mEdge& basic, std::ostream& os, bool writeBinary = false) { if (writeBinary) { os.write(reinterpret_cast(&SERIALIZATION_VERSION), sizeof(decltype(SERIALIZATION_VERSION))); basic.w.writeBinary(os); From 3ae3ff6f58afef8e3a70682642914cfde8b6aaad Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 4 May 2021 16:10:30 +0200 Subject: [PATCH 05/16] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20benchmark?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- extern/benchmark | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/benchmark b/extern/benchmark index 264976de..33c133a2 160000 --- a/extern/benchmark +++ b/extern/benchmark @@ -1 +1 @@ -Subproject commit 264976def327306a4a205857d98ec6792a20cae2 +Subproject commit 33c133a2067316e462ad684e8a36207610da1bb6 From 8414a415b68714d1c5e71420dc13a95c8959dd85 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 4 May 2021 20:37:00 +0200 Subject: [PATCH 06/16] =?UTF-8?q?=F0=9F=8E=A8=20silence=20warning?= 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 af40a515..3beb04ef 100644 --- a/include/dd/Export.hpp +++ b/include/dd/Export.hpp @@ -504,7 +504,7 @@ namespace dd { /// Note: do not rely on the binary format being portable across different architectures/platforms /// - static void serialize(const Package::vEdge& basic, std::ostream& os, bool writeBinary = false) { + [[maybe_unused]] static void serialize(const Package::vEdge& basic, std::ostream& os, bool writeBinary = false) { if (writeBinary) { os.write(reinterpret_cast(&SERIALIZATION_VERSION), sizeof(decltype(SERIALIZATION_VERSION))); basic.w.writeBinary(os); From db6968777c20ce219c35d5e92c7a6604505d5908 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Wed, 5 May 2021 15:34:19 +0200 Subject: [PATCH 07/16] =?UTF-8?q?=F0=9F=8F=81=20fix=20binary=20serializati?= =?UTF-8?q?on=20issues=20under=20Windows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/Export.hpp | 7 ++++++- include/dd/Package.hpp | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/include/dd/Export.hpp b/include/dd/Export.hpp index 3beb04ef..8f2bc8cf 100644 --- a/include/dd/Export.hpp +++ b/include/dd/Export.hpp @@ -644,7 +644,12 @@ namespace dd { } template static void serialize(const Edge& basic, const std::string& outputFilename, bool writeBinary = false) { - std::ofstream ofs(outputFilename); + std::ofstream ofs; + if (writeBinary) { + ofs = std::ofstream(outputFilename, std::ios::binary); + } else { + ofs = std::ofstream(outputFilename); + } if (!ofs.good()) { throw std::invalid_argument("Cannot open file: " + outputFilename); diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index 81f67e94..4cbee0cb 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -2034,7 +2034,12 @@ namespace dd { template> Edge deserialize(const std::string& inputFilename, bool readBinary) { - auto ifs = std::ifstream(inputFilename); + std::ifstream ifs; + if (readBinary) { + ifs = std::ifstream(inputFilename, std::ios::binary); + } else { + ifs = std::ifstream(inputFilename); + } if (!ifs.good()) { throw std::invalid_argument("Cannot open serialized file: " + inputFilename); From b547f202a16869c5e222d56c1df70ade6582058b Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Wed, 5 May 2021 15:46:02 +0200 Subject: [PATCH 08/16] =?UTF-8?q?=F0=9F=8E=A8=20always=20use=20`std::ios::?= =?UTF-8?q?binary`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/Export.hpp | 7 +------ include/dd/Package.hpp | 7 +------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/include/dd/Export.hpp b/include/dd/Export.hpp index 8f2bc8cf..a2733fb0 100644 --- a/include/dd/Export.hpp +++ b/include/dd/Export.hpp @@ -644,12 +644,7 @@ namespace dd { } template static void serialize(const Edge& basic, const std::string& outputFilename, bool writeBinary = false) { - std::ofstream ofs; - if (writeBinary) { - ofs = std::ofstream(outputFilename, std::ios::binary); - } else { - ofs = std::ofstream(outputFilename); - } + std::ofstream ofs = std::ofstream(outputFilename, std::ios::binary); if (!ofs.good()) { throw std::invalid_argument("Cannot open file: " + outputFilename); diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index 4cbee0cb..93eacb15 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -2034,12 +2034,7 @@ namespace dd { template> Edge deserialize(const std::string& inputFilename, bool readBinary) { - std::ifstream ifs; - if (readBinary) { - ifs = std::ifstream(inputFilename, std::ios::binary); - } else { - ifs = std::ifstream(inputFilename); - } + auto ifs = std::ifstream(inputFilename, std::ios::binary); if (!ifs.good()) { throw std::invalid_argument("Cannot open serialized file: " + inputFilename); From c73ad271e864825adfb18995e8ae5196b7977ef8 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Thu, 6 May 2021 11:04:52 +0200 Subject: [PATCH 09/16] =?UTF-8?q?=E2=9C=A8=20ComplexValue=20addition=20?= =?UTF-8?q?=E2=AC=86=EF=B8=8F=20benchmark?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- extern/benchmark | 2 +- include/dd/ComplexValue.hpp | 11 +++++++++++ test/test_complex.cpp | 4 ++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/extern/benchmark b/extern/benchmark index 33c133a2..d0c227cc 160000 --- a/extern/benchmark +++ b/extern/benchmark @@ -1 +1 @@ -Subproject commit 33c133a2067316e462ad684e8a36207610da1bb6 +Subproject commit d0c227ccfd87de776f0a6e097b0d6039315608a2 diff --git a/include/dd/ComplexValue.hpp b/include/dd/ComplexValue.hpp index be15ccf9..e24a766e 100644 --- a/include/dd/ComplexValue.hpp +++ b/include/dd/ComplexValue.hpp @@ -194,6 +194,17 @@ namespace dd { return ss.str(); } + + ComplexValue& operator+=(const ComplexValue& rhs) { + r += rhs.r; + i += rhs.i; + return *this; + } + + friend ComplexValue operator+(ComplexValue lhs, const ComplexValue& rhs) { + lhs += rhs; + return lhs; + } }; inline std::ostream& operator<<(std::ostream& os, const ComplexValue& c) { diff --git a/test/test_complex.cpp b/test/test_complex.cpp index ddd20a7a..01deb75e 100644 --- a/test/test_complex.cpp +++ b/test/test_complex.cpp @@ -97,6 +97,10 @@ TEST(DDComplexTest, ComplexNumberArithmetic) { EXPECT_EQ(e, Complex::one); auto f = cn.getTemporary(); ComplexNumbers::div(f, Complex::zero, Complex::one); + + dd::ComplexValue zero{0., 0.}; + dd::ComplexValue one{1., 0.}; + EXPECT_EQ(one + zero, one); } TEST(DDComplexTest, NearZeroLookup) { From 68cbb7c4390aa2aaaca585a7fdb0d62e9e7a6b95 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Thu, 6 May 2021 14:32:29 +0200 Subject: [PATCH 10/16] =?UTF-8?q?=F0=9F=91=8C=20runtime=20exceptions=20ins?= =?UTF-8?q?tead=20of=20assertions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/Package.hpp | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index 93eacb15..e8b53133 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -191,8 +191,13 @@ namespace dd { // generate |0...0> with n qubits vEdge makeZeroState(QubitCount n, std::size_t start = 0) { - assert(n + start <= nqubits); - + if (n + start > nqubits) { + throw std::runtime_error("Requested state with " + + std::to_string(n + start) + + " qubits, but current package configuration only supports up to " + + std::to_string(nqubits) + + " qubits. Please allocate a larger package instance."); + } auto f = vEdge::one; for (std::size_t p = start; p < n + start; p++) { f = makeDDNode(static_cast(p), std::array{f, vEdge::zero}); @@ -201,8 +206,13 @@ namespace dd { } // generate computational basis state |i> with n qubits vEdge makeBasisState(QubitCount n, const std::vector& state, std::size_t start = 0) { - assert(n + start <= nqubits); - + if (n + start > nqubits) { + throw std::runtime_error("Requested state with " + + std::to_string(n + start) + + " qubits, but current package configuration only supports up to " + + std::to_string(nqubits) + + " qubits. Please allocate a larger package instance."); + } auto f = vEdge::one; for (std::size_t p = start; p < n + start; ++p) { if (state[p] == 0) { @@ -215,10 +225,15 @@ namespace dd { } // generate general basis state with n qubits vEdge makeBasisState(QubitCount n, const std::vector& state, std::size_t start = 0) { - assert(n + start <= nqubits); - + if (n + start > nqubits) { + throw std::runtime_error("Requested state with " + + std::to_string(n + start) + + " qubits, but current package configuration only supports up to " + + std::to_string(nqubits) + + " qubits. Please allocate a larger package instance."); + } if (state.size() < n) { - throw std::invalid_argument("Insufficient qubit states provided. Requested " + std::to_string(n) + ", but received " + std::to_string(state.size())); + throw std::runtime_error("Insufficient qubit states provided. Requested " + std::to_string(n) + ", but received " + std::to_string(state.size())); } auto f = vEdge::one; @@ -360,8 +375,13 @@ namespace dd { return makeGateDD(mat, n, Controls{control}, target, start); } mEdge makeGateDD(const std::array& mat, QubitCount n, const Controls& controls, Qubit target, std::size_t start = 0) { - assert(n + start <= nqubits); - + if (n + start > nqubits) { + throw std::runtime_error("Requested gate with " + + std::to_string(n + start) + + " qubits, but current package configuration only supports up to " + + std::to_string(nqubits) + + " qubits. Please allocate a larger package instance."); + } std::array em{}; auto it = controls.begin(); for (auto i = 0U; i < NEDGE; ++i) { From 81d506cbafe264cd0556dbe23027f6ff3589c1ed Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Thu, 6 May 2021 14:37:27 +0200 Subject: [PATCH 11/16] =?UTF-8?q?=E2=9C=85=20add=20tests=20for=20exception?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- test/test_package.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/test_package.cpp b/test/test_package.cpp index dbdbf4a2..4111bcc0 100644 --- a/test/test_package.cpp +++ b/test/test_package.cpp @@ -497,11 +497,15 @@ TEST(DDPackageTest, GarbageMatrix) { EXPECT_TRUE(reduced_bell_matrix.p->e[3].isZeroTerminal()); } -TEST(DDPackageTest, InvalidMakeBasisState) { +TEST(DDPackageTest, InvalidMakeBasisStateAndGate) { auto nqubits = 2; auto dd = std::make_unique(nqubits); auto basisState = std::vector{dd::BasisStates::zero}; - EXPECT_THROW(dd->makeBasisState(nqubits, basisState), std::invalid_argument); + 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(dd->makeBasisState(3, {dd::BasisStates::one, dd::BasisStates::one, dd::BasisStates::one}), std::runtime_error); + EXPECT_THROW(dd->makeGateDD(dd::Xmat, 3, 0), std::runtime_error); } TEST(DDPackageTest, InvalidDecRef) { From be580eb495df8b08db31c19a6515a651e067db85 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Thu, 6 May 2021 14:45:25 +0200 Subject: [PATCH 12/16] =?UTF-8?q?=F0=9F=91=8C=20appropriate=20naming=20`no?= =?UTF-8?q?de`=20->=20`edge`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/Package.hpp | 96 +++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index e8b53133..225bbc25 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -1775,10 +1775,10 @@ namespace dd { cn.returnToCache(c); } - void exportAmplitudesRec(const dd::Package::vEdge& node, std::ostream& oss, const std::string& path, Complex& amplitude, dd::QubitCount level, bool binary = false) { - if (node.isTerminal()) { + void exportAmplitudesRec(const dd::Package::vEdge& edge, std::ostream& oss, const std::string& path, Complex& amplitude, dd::QubitCount level, bool binary = false) { + if (edge.isTerminal()) { auto amp = cn.getTemporary(); - dd::ComplexNumbers::mul(amp, amplitude, node.w); + dd::ComplexNumbers::mul(amp, amplitude, edge.w); for (std::size_t i = 0; i < (1UL << level); i++) { if (binary) { amp.writeBinary(oss); @@ -1790,34 +1790,34 @@ namespace dd { return; } - auto a = cn.mulCached(amplitude, node.w); - exportAmplitudesRec(node.p->e[0], oss, path + "0", a, level - 1, binary); - exportAmplitudesRec(node.p->e[1], oss, path + "1", a, level - 1, binary); + auto a = cn.mulCached(amplitude, edge.w); + exportAmplitudesRec(edge.p->e[0], oss, path + "0", a, level - 1, binary); + exportAmplitudesRec(edge.p->e[1], oss, path + "1", a, level - 1, binary); cn.returnToCache(a); } - void exportAmplitudes(const dd::Package::vEdge& node, std::ostream& oss, dd::QubitCount nq, bool binary = false) { - if (node.isTerminal()) { + void exportAmplitudes(const dd::Package::vEdge& edge, std::ostream& oss, dd::QubitCount nq, bool binary = false) { + if (edge.isTerminal()) { // TODO special treatment return; } auto weight = cn.getCached(1., 0.); - exportAmplitudesRec(node, oss, "", weight, nq, binary); + exportAmplitudesRec(edge, oss, "", weight, nq, binary); cn.returnToCache(weight); } - void exportAmplitudes(const dd::Package::vEdge& node, const std::string& outputFilename, dd::QubitCount nq, bool binary = false) { + void exportAmplitudes(const dd::Package::vEdge& edge, const std::string& outputFilename, dd::QubitCount nq, bool binary = false) { std::ofstream init(outputFilename); std::ostringstream oss{}; - exportAmplitudes(node, oss, nq, binary); + exportAmplitudes(edge, oss, nq, binary); init << oss.str() << std::flush; init.close(); } - void exportAmplitudesRec(const dd::Package::vEdge& node, std::vector& amplitudes, Complex& amplitude, dd::QubitCount level, std::size_t idx) { - if (node.isTerminal()) { + void exportAmplitudesRec(const dd::Package::vEdge& edge, std::vector& amplitudes, Complex& amplitude, dd::QubitCount level, std::size_t idx) { + if (edge.isTerminal()) { auto amp = cn.getTemporary(); - dd::ComplexNumbers::mul(amp, amplitude, node.w); + dd::ComplexNumbers::mul(amp, amplitude, edge.w); idx <<= level; for (std::size_t i = 0; i < (1UL << level); i++) { amplitudes[idx++] = dd::ComplexValue{dd::ComplexTable<>::Entry::val(amp.r), dd::ComplexTable<>::Entry::val(amp.i)}; @@ -1826,27 +1826,27 @@ namespace dd { return; } - auto a = cn.mulCached(amplitude, node.w); - exportAmplitudesRec(node.p->e[0], amplitudes, a, level - 1, idx << 1); - exportAmplitudesRec(node.p->e[1], amplitudes, a, level - 1, (idx << 1) | 1); + auto a = cn.mulCached(amplitude, edge.w); + exportAmplitudesRec(edge.p->e[0], amplitudes, a, level - 1, idx << 1); + exportAmplitudesRec(edge.p->e[1], amplitudes, a, level - 1, (idx << 1) | 1); cn.returnToCache(a); } - void exportAmplitudes(const dd::Package::vEdge& node, std::vector& amplitudes, dd::QubitCount nq) { - if (node.isTerminal()) { + void exportAmplitudes(const dd::Package::vEdge& edge, std::vector& amplitudes, dd::QubitCount nq) { + if (edge.isTerminal()) { // TODO special treatment return; } auto weight = cn.getCached(1., 0.); - exportAmplitudesRec(node, amplitudes, weight, nq, 0); + exportAmplitudesRec(edge, amplitudes, weight, nq, 0); cn.returnToCache(weight); } - void addAmplitudesRec(const dd::Package::vEdge& node, std::vector& amplitudes, ComplexValue& amplitude, dd::QubitCount level, std::size_t idx) { - auto ar = dd::ComplexTable<>::Entry::val(node.w.r); - auto ai = dd::ComplexTable<>::Entry::val(node.w.i); + void addAmplitudesRec(const dd::Package::vEdge& edge, std::vector& amplitudes, ComplexValue& amplitude, dd::QubitCount level, std::size_t idx) { + auto ar = dd::ComplexTable<>::Entry::val(edge.w.r); + auto ai = dd::ComplexTable<>::Entry::val(edge.w.i); ComplexValue amp{ar * amplitude.r - ai * amplitude.i, ar * amplitude.i + ai * amplitude.r}; - if (node.isTerminal()) { + if (edge.isTerminal()) { idx <<= level; for (std::size_t i = 0; i < (1UL << level); i++) { auto temp = dd::ComplexValue{amp.r + amplitudes[idx].r, amp.i + amplitudes[idx].i}; @@ -1856,16 +1856,16 @@ namespace dd { return; } - addAmplitudesRec(node.p->e[0], amplitudes, amp, level - 1, idx << 1); - addAmplitudesRec(node.p->e[1], amplitudes, amp, level - 1, idx << 1 | 1); + addAmplitudesRec(edge.p->e[0], amplitudes, amp, level - 1, idx << 1); + addAmplitudesRec(edge.p->e[1], amplitudes, amp, level - 1, idx << 1 | 1); } - void addAmplitudes(const dd::Package::vEdge& node, std::vector& amplitudes, dd::QubitCount nq) { - if (node.isTerminal()) { + void addAmplitudes(const dd::Package::vEdge& edge, std::vector& amplitudes, dd::QubitCount nq) { + if (edge.isTerminal()) { // TODO special treatment return; } ComplexValue a{1., 0.}; - addAmplitudesRec(node, amplitudes, a, nq, 0); + addAmplitudesRec(edge, amplitudes, a, nq, 0); } // transfers a decision diagram from another package to this package @@ -1877,13 +1877,13 @@ namespace dd { std::unordered_map mapped_node{}; - Edge* node = &original; - if (!node->isTerminal()) { + Edge* currentEdge = &original; + if (!currentEdge->isTerminal()) { constexpr std::size_t N = std::tuple_size_ve)>; do { - while (node != nullptr && !node->isTerminal()) { + while (currentEdge != nullptr && !currentEdge->isTerminal()) { for (short i = N - 1; i > 0; --i) { - auto& edge = node->p->e[i]; + auto& edge = currentEdge->p->e[i]; if (edge.isTerminal()) { continue; } @@ -1897,15 +1897,15 @@ namespace dd { // non-zero edge to be included stack.push(&edge); } - stack.push(node); - node = &node->p->e[0]; + stack.push(currentEdge); + currentEdge = ¤tEdge->p->e[0]; } - node = stack.top(); + currentEdge = stack.top(); stack.pop(); bool hasChild = false; for (std::size_t i = 1; i < N && !hasChild; ++i) { - auto& edge = node->p->e[i]; + auto& edge = currentEdge->p->e[i]; if (edge.w.approximatelyZero()) { continue; } @@ -1918,25 +1918,25 @@ namespace dd { if (hasChild) { Edge* temp = stack.top(); stack.pop(); - stack.push(node); - node = temp; + stack.push(currentEdge); + currentEdge = temp; } else { - if (mapped_node.find(node->p) != mapped_node.end()) { - node = nullptr; + if (mapped_node.find(currentEdge->p) != mapped_node.end()) { + currentEdge = nullptr; continue; } std::array edges{}; for (std::size_t i = 0; i < N; i++) { - if (node->p->e[i].isTerminal()) { - edges[i].p = node->p->e[i].p; + if (currentEdge->p->e[i].isTerminal()) { + edges[i].p = currentEdge->p->e[i].p; } else { - edges[i].p = mapped_node[node->p->e[i].p]; + edges[i].p = mapped_node[currentEdge->p->e[i].p]; } - edges[i].w = cn.lookup(node->p->e[i].w); + edges[i].w = cn.lookup(currentEdge->p->e[i].w); } - root = makeDDNode(node->p->v, edges); - mapped_node[node->p] = root.p; - node = nullptr; + root = makeDDNode(currentEdge->p->v, edges); + mapped_node[currentEdge->p] = root.p; + currentEdge = nullptr; } } while (!stack.empty()); From b315cb94f57a0d9ac7ffac4651d27e58e5e6f87f Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Thu, 6 May 2021 14:46:39 +0200 Subject: [PATCH 13/16] =?UTF-8?q?=F0=9F=94=96=20bump=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 85611503..777368dc 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.14...3.19) # project definition project(DDPackage LANGUAGES CXX - VERSION 2.0.1 + VERSION 2.0.2 DESCRIPTION "JKQ decision diagram package tailored to quantum computing") # enable organization of targets into folders From 04d8a53b0f6e649a5d503d306c981215cdbecfdc Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Thu, 6 May 2021 14:49:05 +0200 Subject: [PATCH 14/16] =?UTF-8?q?=F0=9F=93=84=20updating=20license?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 1e44d430..cbf46617 100755 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 Alwin Zulehner, Stefan Hillmich, Lukas Burgholzer and Robert Wille +Copyright (c) 2021 Hartwig Bauer, Lukas Burgholzer, Thomas Grurl, Stefan Hillmich, Robert Wille, and Alwin Zulehner Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 28bb98a885e1d8c1cc3eecd943d836ca8494096b Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Thu, 6 May 2021 17:08:19 +0200 Subject: [PATCH 15/16] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20benchmark?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- extern/benchmark | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/benchmark b/extern/benchmark index d0c227cc..e50b572e 160000 --- a/extern/benchmark +++ b/extern/benchmark @@ -1 +1 @@ -Subproject commit d0c227ccfd87de776f0a6e097b0d6039315608a2 +Subproject commit e50b572e899e0163ccc6f6bb8282ae4a123a8a92 From 79c0af30173388e81dc332f301c60e2c99256a1b Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Thu, 6 May 2021 17:13:19 +0200 Subject: [PATCH 16/16] =?UTF-8?q?=F0=9F=93=9D=20fix=20readme=20example?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 05f7c8b0..db9d1f67 100755 --- a/README.md +++ b/README.md @@ -23,10 +23,9 @@ A small example shows how to create set a single qubit in superposition. ```c++ #include -#include "DDpackage.h" -#include "GateMatrixDefinitions.h" +#include "dd/Package.hpp" -auto dd = std::make_unique(); // Create new package instance +auto dd = std::make_unique(1); // Create new package instance capable of handling a single qubit auto zero_state = dd->makeZeroState(1) ; // zero_state = |0> /* Creating a DD requires the following inputs: