From 0294446d7fd298939a1d249a755725ad5bc7afbe Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 20 Apr 2021 16:48:10 +0200 Subject: [PATCH 01/48] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20googletest=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 +- extern/googletest | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extern/benchmark b/extern/benchmark index 39b5a298..69054ae5 160000 --- a/extern/benchmark +++ b/extern/benchmark @@ -1 +1 @@ -Subproject commit 39b5a298a7a502f9b8620127030ba318babdcb53 +Subproject commit 69054ae50e07e9de7cb27f9e2d1d355f74605524 diff --git a/extern/googletest b/extern/googletest index 8d664b94..eaf9a3fd 160000 --- a/extern/googletest +++ b/extern/googletest @@ -1 +1 @@ -Subproject commit 8d664b94bebc86a9d3c6272bb41039310c550e58 +Subproject commit eaf9a3fd77869cf95befb87455a2e2a2e85044ff From a934959ae80c6c058c14275e462e426e8f4eb46e Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 20 Apr 2021 16:48:39 +0200 Subject: [PATCH 02/48] =?UTF-8?q?=F0=9F=93=9D=20adapted=20README.md?= 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, 5 deletions(-) diff --git a/README.md b/README.md index 6bd6d999..8a03dc56 100755 --- a/README.md +++ b/README.md @@ -9,11 +9,6 @@ A DD package tailored to quantum computing by the [Institute for Integrated Circuits](http://iic.jku.at/eda/) at the [Johannes Kepler University Linz](https://jku.at). This package is part of the [JKQ toolset](https://github.com/iic-jku/jkq). -Developers: Alwin Zulehner, Stefan Hillmich, Lukas Burgholzer, and Robert Wille - -With code from the QMDD implementation provided by Michael Miller (University of Victoria, Canada) -and Philipp Niemann (University of Bremen, Germany). - For more information, please visit [iic.jku.at/eda/research/quantum_dd](http://iic.jku.at/eda/research/quantum_dd). If you have any questions, feel free to contact us via [iic-quantum@jku.at](mailto:iic-quantum@jku.at) or by creating an issue on [GitHub](https://github.com/iic-jku/dd_package/issues). From 4c3475844b3761b6df3a7f815a5958319590337d Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 20 Apr 2021 16:50:15 +0200 Subject: [PATCH 03/48] =?UTF-8?q?=F0=9F=8E=A8=20moved=20number=20printing?= =?UTF-8?q?=20code=20from=20`Complex`=20to=20`ComplexValue`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/Complex.hpp | 40 +----------------------------------- include/dd/ComplexValue.hpp | 41 +++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 39 deletions(-) diff --git a/include/dd/Complex.hpp b/include/dd/Complex.hpp index 92208204..744ab0b5 100644 --- a/include/dd/Complex.hpp +++ b/include/dd/Complex.hpp @@ -49,45 +49,7 @@ namespace dd { } [[nodiscard]] std::string toString(bool formatted = true, int precision = -1) const { - std::ostringstream ss{}; - - if (precision >= 0) ss << std::setprecision(precision); - - auto real = r->val(); - auto imag = i->val(); - - if (real != 0.) { - if (formatted) { - ComplexValue::printFormatted(ss, real); - } else { - ss << real; - } - } - if (imag != 0.) { - if (formatted) { - if (real == imag) { - ss << "(1+i)"; - return ss.str(); - } else if (imag == -real) { - ss << "(1-i)"; - return ss.str(); - } - ComplexValue::printFormatted(ss, imag, true); - } else { - if (real == 0.) { - ss << imag; - } else { - if (imag > 0.) { - ss << "+"; - } - ss << imag; - } - ss << "i"; - } - } - if (real == 0. && imag == 0.) return "0"; - - return ss.str(); + return ComplexValue::toString(r->val(), i->val(), formatted, precision); } void writeBinary(std::ostream& os) const { diff --git a/include/dd/ComplexValue.hpp b/include/dd/ComplexValue.hpp index 1727c9c4..99db69c5 100644 --- a/include/dd/ComplexValue.hpp +++ b/include/dd/ComplexValue.hpp @@ -13,7 +13,9 @@ #include #include #include +#include #include +#include #include #include @@ -153,6 +155,45 @@ namespace dd { } else os << r; } + + static std::string toString(const fp& real, const fp& imag, bool formatted = true, int precision = -1) { + std::ostringstream ss{}; + + if (precision >= 0) ss << std::setprecision(precision); + + if (real != 0.) { + if (formatted) { + printFormatted(ss, real); + } else { + ss << real; + } + } + if (imag != 0.) { + if (formatted) { + if (real == imag) { + ss << "(1+i)"; + return ss.str(); + } else if (imag == -real) { + ss << "(1-i)"; + return ss.str(); + } + printFormatted(ss, imag, true); + } else { + if (real == 0.) { + ss << imag; + } else { + if (imag > 0.) { + ss << "+"; + } + ss << imag; + } + ss << "i"; + } + } + if (real == 0. && imag == 0.) return "0"; + + return ss.str(); + } }; inline std::ostream& operator<<(std::ostream& os, const ComplexValue& c) { From 2cd4cf20a869e5d3a70c78326446fb274dae3c71 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 20 Apr 2021 16:52:28 +0200 Subject: [PATCH 04/48] =?UTF-8?q?=E2=9A=A1=20emplace=20instead=20of=20push?= =?UTF-8?q?=20in=20all=20table=20return=20methods=20=E2=9A=A1=20reference?= =?UTF-8?q?=20instead=20of=20copy=20on=20getting=20nodes=20from=20any=20ta?= =?UTF-8?q?ble?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/ComplexCache.hpp | 4 ++-- include/dd/ComplexTable.hpp | 4 ++-- include/dd/UniqueTable.hpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/dd/ComplexCache.hpp b/include/dd/ComplexCache.hpp index 933f873b..2071e965 100644 --- a/include/dd/ComplexCache.hpp +++ b/include/dd/ComplexCache.hpp @@ -42,7 +42,7 @@ namespace dd { [[nodiscard]] Complex getCachedComplex() { // an entry is available on the stack if (!available.empty()) { - auto entry = available.top(); + auto& entry = available.top(); available.pop(); count += 2; return entry; @@ -95,7 +95,7 @@ namespace dd { assert(c != Complex::one); assert(c.r->refCount == 0); assert(c.i->refCount == 0); - available.push(c); + available.emplace(c); count -= 2; } diff --git a/include/dd/ComplexTable.hpp b/include/dd/ComplexTable.hpp index 5426f89d..16461b34 100644 --- a/include/dd/ComplexTable.hpp +++ b/include/dd/ComplexTable.hpp @@ -180,7 +180,7 @@ namespace dd { [[nodiscard]] Entry* getEntry() { // an entry is available on the stack if (!available.empty()) { - auto entry = available.top(); + auto& entry = available.top(); available.pop(); // returned entries could have a ref count != 0 entry->refCount = 0; @@ -203,7 +203,7 @@ namespace dd { } void returnEntry(Entry* entry) { - available.push(entry); + available.emplace(entry); } // increment reference count for corresponding entry diff --git a/include/dd/UniqueTable.hpp b/include/dd/UniqueTable.hpp index 2ea8721d..b35d49b8 100644 --- a/include/dd/UniqueTable.hpp +++ b/include/dd/UniqueTable.hpp @@ -126,7 +126,7 @@ namespace dd { [[nodiscard]] Node* getNode() { // a node is available on the stack if (!available.empty()) { - auto p = available.top(); + auto& p = available.top(); available.pop(); // returned nodes could have a ref count != 0 p->ref = 0; @@ -149,7 +149,7 @@ namespace dd { } void returnNode(Node* p) { - available.push(p); + available.emplace(p); } // increment reference counter for node e points to From 6fd0c0a15b15551e93d45376c0ac7b418e374eb7 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 20 Apr 2021 16:53:35 +0200 Subject: [PATCH 05/48] =?UTF-8?q?=F0=9F=90=9B=20nullptr=20check=20in=20inc?= =?UTF-8?q?Ref/decRef?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/ComplexTable.hpp | 6 ++++++ include/dd/UniqueTable.hpp | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/dd/ComplexTable.hpp b/include/dd/ComplexTable.hpp index 16461b34..ce90b2e4 100644 --- a/include/dd/ComplexTable.hpp +++ b/include/dd/ComplexTable.hpp @@ -208,6 +208,9 @@ namespace dd { // increment reference count for corresponding entry static void incRef(Entry* entry) { + if (entry == nullptr) + return; + // get valid pointer auto entryPtr = entry->getAlignedPointer(); @@ -225,6 +228,9 @@ namespace dd { // decrement reference count for corresponding entry static void decRef(Entry* entry) { + if (entry == nullptr) + return; + // get valid pointer auto entryPtr = entry->getAlignedPointer(); diff --git a/include/dd/UniqueTable.hpp b/include/dd/UniqueTable.hpp index b35d49b8..615c0ff1 100644 --- a/include/dd/UniqueTable.hpp +++ b/include/dd/UniqueTable.hpp @@ -157,7 +157,7 @@ namespace dd { // each child if this is the first reference void incRef(const Edge& e) { dd::ComplexNumbers::incRef(e.w); - if (e.isTerminal()) + if (e.p == nullptr || e.isTerminal()) return; if (e.p->ref == std::numeric_limitsref)>::max()) { @@ -184,7 +184,7 @@ namespace dd { // each child if this is the last reference void decRef(const Edge& e) { dd::ComplexNumbers::decRef(e.w); - if (e.isTerminal()) return; + if (e.p == nullptr || e.isTerminal()) return; if (e.p->ref == std::numeric_limitsref)>::max()) return; if (e.p->ref == 0) { From 6f0d29494e07aa656893976d7d175cf1dff1b7a4 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 20 Apr 2021 16:54:17 +0200 Subject: [PATCH 06/48] =?UTF-8?q?=F0=9F=90=9B=20fixed=20colorFromPhase=20b?= =?UTF-8?q?ug=20when=20encountering=20a=20negative=20phase?= 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, 2 insertions(+) diff --git a/include/dd/Export.hpp b/include/dd/Export.hpp index d1acbcc4..d403b807 100644 --- a/include/dd/Export.hpp +++ b/include/dd/Export.hpp @@ -31,6 +31,8 @@ namespace dd { auto phase = dd::ComplexNumbers::arg(a); auto twopi = 2 * dd::PI; phase = (phase) / twopi; + if (phase < 0) + phase += 1.; std::ostringstream oss{}; oss << std::fixed << std::setprecision(3) << phase << " " << 0.667 << " " << 0.75; return oss.str(); From 700aa07cb81cee184598dfad7c4da8e5305bdb94 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 20 Apr 2021 16:55:02 +0200 Subject: [PATCH 07/48] =?UTF-8?q?=F0=9F=90=9B=20fixed=20unique/complex=20t?= =?UTF-8?q?able=20garbage=20collection=20iteration=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/ComplexTable.hpp | 9 +-------- include/dd/UniqueTable.hpp | 9 +-------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/include/dd/ComplexTable.hpp b/include/dd/ComplexTable.hpp index ce90b2e4..e8f24a8c 100644 --- a/include/dd/ComplexTable.hpp +++ b/include/dd/ComplexTable.hpp @@ -257,15 +257,8 @@ namespace dd { while (it != bucket.end()) { if ((*it)->refCount == 0) { auto entry = (*it); - bucket.erase_after(lastit); // erases the element at `it` + it = bucket.erase_after(lastit); // erases the element at `it` returnEntry(entry); - if (lastit == bucket.before_begin()) { - // first entry was removed - it = bucket.begin(); - } else { - // entry in middle of list was removed - it = ++lastit; - } collected++; } else { ++it; diff --git a/include/dd/UniqueTable.hpp b/include/dd/UniqueTable.hpp index 615c0ff1..d301d687 100644 --- a/include/dd/UniqueTable.hpp +++ b/include/dd/UniqueTable.hpp @@ -221,15 +221,8 @@ namespace dd { assert(!Node::isTerminal(*it)); auto node = (*it); - bucket.erase_after(lastit); // erases the element at `it` + it = bucket.erase_after(lastit); // erases the element at `it` returnNode(node); - if (lastit == bucket.before_begin()) { - // first entry was removed - it = bucket.begin(); - } else { - // entry in middle of list was removed - it = ++lastit; - } collected++; } else { ++it; From 1fc497d53e444e8993cdc5086b5c7ef26ab766b7 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 20 Apr 2021 16:55:34 +0200 Subject: [PATCH 08/48] =?UTF-8?q?=F0=9F=8E=A8=20add=20additional=20constan?= =?UTF-8?q?ts=20to=20Definitions.hpp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/Definitions.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/dd/Definitions.hpp b/include/dd/Definitions.hpp index 709579b1..f768e205 100644 --- a/include/dd/Definitions.hpp +++ b/include/dd/Definitions.hpp @@ -46,6 +46,8 @@ namespace dd { static constexpr fp SQRT2_2 = 0.707106781186547524400844362104849039284835937688474036588L; static constexpr fp PI = 3.141592653589793238462643383279502884197169399375105820974L; + static constexpr fp PI_2 = 1.570796326794896619231321691639751442098584699687552910487L; + static constexpr fp PI_4 = 0.785398163397448309615660845819875721049292349843776455243L; using CVec = std::vector>; using CMat = std::vector; From 5e8da945bef852d76b1bd2e8cd84332a3c14c9db Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 20 Apr 2021 16:57:12 +0200 Subject: [PATCH 09/48] =?UTF-8?q?=F0=9F=8E=A8=20define=20`dd::Controls`=20?= =?UTF-8?q?as=20a=20set=20of=20controls=20that=20may=20also=20be=20searche?= =?UTF-8?q?d=20for=20by=20a=20`dd::Qubit`=20alone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/Control.hpp | 21 +++++++++++++++++++++ include/dd/Package.hpp | 8 ++++---- include/dd/ToffoliTable.hpp | 14 +++++++------- test/bench_package.cpp | 6 +++--- 4 files changed, 35 insertions(+), 14 deletions(-) diff --git a/include/dd/Control.hpp b/include/dd/Control.hpp index 364bf023..751a51f9 100644 --- a/include/dd/Control.hpp +++ b/include/dd/Control.hpp @@ -8,6 +8,8 @@ #include "Definitions.hpp" +#include + namespace dd { struct Control { enum class Type : bool { pos = true, @@ -29,6 +31,25 @@ namespace dd { return !(lhs == rhs); } + // this allows a set of controls to be indexed by a `Qubit` + struct CompareControl { + using is_transparent = void; + + inline bool operator()(const Control& lhs, const Control& rhs) const { + return lhs < rhs; + } + + inline bool operator()(Qubit lhs, const Control& rhs) const { + return lhs < rhs.qubit; + } + + inline bool operator()(const Control& lhs, Qubit rhs) const { + return lhs.qubit < rhs; + } + + }; + using Controls = std::set; + inline namespace literals { inline Control operator""_pc(unsigned long long int q) { return {static_cast(q)}; } inline Control operator""_nc(unsigned long long int q) { return {static_cast(q), Control::Type::neg}; } diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index 997d8db4..0ed44c4f 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -345,15 +345,15 @@ 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, std::set{}, target); + return makeGateDD(mat, n, Controls{}, target); } mEdge makeGateDD(const std::array& mat, QubitCount n, const Control& control, Qubit target) { - return makeGateDD(mat, n, std::set{control}, target); + return makeGateDD(mat, n, Controls{control}, target); } mEdge makeGateDD(const std::array& mat, QubitCount n, Qubit control, Qubit target) { - return makeGateDD(mat, n, std::set{{control}}, target); + return makeGateDD(mat, n, Controls{{control}}, target); } - mEdge makeGateDD(const std::array& mat, QubitCount n, const std::set& controls, Qubit target) { + mEdge makeGateDD(const std::array& mat, QubitCount n, const Controls& controls, Qubit target) { assert(n <= nqubits); std::array em{}; diff --git a/include/dd/ToffoliTable.hpp b/include/dd/ToffoliTable.hpp index c7f91d23..45a7caac 100644 --- a/include/dd/ToffoliTable.hpp +++ b/include/dd/ToffoliTable.hpp @@ -22,10 +22,10 @@ namespace dd { ToffoliTable() = default; struct Entry { - QubitCount n = 0; - std::set controls{}; - Qubit target = 0; - Edge e; + QubitCount n = 0; + Controls controls{}; + Qubit target = 0; + Edge e; }; static constexpr std::size_t MASK = NBUCKET - 1; @@ -33,13 +33,13 @@ namespace dd { // access functions [[nodiscard]] const auto& getTable() const { return table; } - void insert(QubitCount n, const std::set& controls, Qubit target, const Edge& e) { + void insert(QubitCount n, const Controls& controls, Qubit target, const Edge& e) { const auto key = hash(controls, target); table[key] = {n, controls, target, e}; ++count; } - Edge lookup(QubitCount n, const std::set& controls, Qubit target) { + Edge lookup(QubitCount n, const Controls& controls, Qubit target) { lookups++; Edge r{}; const auto key = hash(controls, target); @@ -52,7 +52,7 @@ namespace dd { return entry.e; } - static std::size_t hash(const std::set& controls, Qubit target) { + static std::size_t hash(const Controls& controls, Qubit target) { auto key = static_cast(std::make_unsigned::type(target)); for (const auto& control: controls) { if (control.type == dd::Control::Type::pos) { diff --git a/test/bench_package.cpp b/test/bench_package.cpp index a3403f9b..9e29beec 100644 --- a/test/bench_package.cpp +++ b/test/bench_package.cpp @@ -152,7 +152,7 @@ BENCHMARK(BM_MakeControlledQubitGateDD_ControlTop_TargetBottom)->Apply(QubitRang static void BM_MakeFullControlledToffoliDD_TargetTop(benchmark::State& state) { unsigned short nqubits = state.range(0); auto dd = std::make_unique(nqubits); - std::set controls; + dd::Controls controls; for (std::size_t i = 0; i < static_cast(nqubits - 1); i++) controls.insert({static_cast(i)}); for (auto _: state) { @@ -164,7 +164,7 @@ BENCHMARK(BM_MakeFullControlledToffoliDD_TargetTop)->Apply(QubitRange); static void BM_MakeFullControlledToffoliDD_TargetMiddle(benchmark::State& state) { unsigned short nqubits = state.range(0); auto dd = std::make_unique(nqubits); - std::set controls; + dd::Controls controls; for (std::size_t i = 0; i < nqubits; i++) if (i != nqubits / 2) controls.insert({static_cast(i)}); @@ -177,7 +177,7 @@ BENCHMARK(BM_MakeFullControlledToffoliDD_TargetMiddle)->Apply(QubitRange); static void BM_MakeFullControlledToffoliDD_TargetBottom(benchmark::State& state) { unsigned short nqubits = state.range(0); auto dd = std::make_unique(nqubits); - std::set controls; + dd::Controls controls; for (std::size_t i = 1; i < nqubits; i++) controls.insert({static_cast(i)}); for (auto _: state) { From 8b5347e0acdc63eaaf08c8aef78bd319ae7587e8 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 20 Apr 2021 16:58:07 +0200 Subject: [PATCH 10/48] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20moved=20DD=20operati?= =?UTF-8?q?on=20creation=20routined=20from=20JKQ=20QFR=20to=20Package?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/Package.hpp | 55 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index 0ed44c4f..cba0aa06 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -15,6 +15,7 @@ #include "Control.hpp" #include "Definitions.hpp" #include "Edge.hpp" +#include "GateMatrixDefinitions.hpp" #include "NoiseOperationTable.hpp" #include "ToffoliTable.hpp" #include "UnaryComputeTable.hpp" @@ -408,6 +409,60 @@ namespace dd { return e; } + mEdge makeSWAPDD(QubitCount n, const Controls& controls, Qubit target0, Qubit target1) { + auto c = controls; + c.insert(Control{target0}); + mEdge e = makeGateDD(Xmat, n, c, target1); + c.erase(Control{target0}); + c.insert(Control{target1}); + e = multiply(e, multiply(makeGateDD(Xmat, n, c, target0), e)); + return e; + } + + mEdge makePeresDD(QubitCount n, const Controls& controls, Qubit target0, Qubit target1) { + auto c = controls; + c.insert(Control{target1}); + mEdge e = makeGateDD(Xmat, n, c, target0); + e = multiply(makeGateDD(Xmat, n, controls, target1), e); + return e; + } + + mEdge makePeresdagDD(QubitCount n, const Controls& controls, Qubit target0, Qubit target1) { + mEdge e = makeGateDD(Xmat, n, controls, target1); + auto c = controls; + c.insert(Control{target1}); + e = multiply(makeGateDD(Xmat, n, c, target0), 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] + auto c = controls; + c.insert(Control{target0}); + e = multiply(e, makeGateDD(Xmat, n, c, target1)); // 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] + return e; + } + + mEdge makeiSWAPinvDD(QubitCount n, const Controls& controls, Qubit target0, Qubit target1) { + mEdge e = makeGateDD(Hmat, n, controls, target1); // H q[1] + auto c = controls; + c.insert(Control{target1}); + e = multiply(e, makeGateDD(Xmat, n, c, target0)); // 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] + return e; + } + private: // check whether node represents a symmetric matrix or the identity void checkSpecialMatrices(mNode* p) { From f0786f8a834b1c14edb7e535bd74a122e7e9d294 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 20 Apr 2021 16:59:06 +0200 Subject: [PATCH 11/48] =?UTF-8?q?=F0=9F=8E=A8=20improved=20vector=20printi?= =?UTF-8?q?ng=20=E2=9C=A8=20formatted=20matrix=20printing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/Package.hpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index cba0aa06..0db8f29d 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -1605,7 +1605,27 @@ namespace dd { for (Qubit j = e.p->v; j >= 0; j--) { std::cout << ((i >> j) & 1u); } - std::cout << ": " << amplitude << "\n"; + constexpr auto precision = 3; + // set fixed width to maximum of a printed number + // (-) 0.precision plus/minus 0.precision i + constexpr auto width = 1 + 2 + precision + 1 + 2 + precision + 1; + std::cout << ": " << std::setw (width) << ComplexValue::toString(amplitude.r, amplitude.i, false, precision) << "\n"; + } + std::cout << std::flush; + } + + void printMatrix(const mEdge& e) { + unsigned long long element = 2u << e.p->v; + for (unsigned long long i = 0; i < element; i++) { + for (unsigned long long j = 0; j < element; j++) { + auto amplitude = getValueByPath(e, i, j); + constexpr auto precision = 3; + // set fixed width to maximum of a printed number + // (-) 0.precision plus/minus 0.precision i + constexpr auto width = 1 + 2 + precision + 1 + 2 + precision + 1; + std::cout << std::setw (width) << ComplexValue::toString(amplitude.r, amplitude.i, false, precision) << " "; + } + std::cout << "\n"; } std::cout << std::flush; } From 06e2c036b274b755edac7bb74d9b7dfcabb9fc35 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 20 Apr 2021 16:59:37 +0200 Subject: [PATCH 12/48] =?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 697a8076..55c6e022 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.0 + VERSION 2.0.1 DESCRIPTION "JKQ decision diagram package tailored to quantum computing") # enable organization of targets into folders From 607fe3271208540ccaf04fd4f79aaea4505c0dde Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 20 Apr 2021 17:25:37 +0200 Subject: [PATCH 13/48] =?UTF-8?q?=F0=9F=8E=A8=20code=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/ComplexTable.hpp | 2 +- include/dd/Control.hpp | 1 - include/dd/Package.hpp | 6 +++--- include/dd/UniqueTable.hpp | 2 +- test/bench_package.cpp | 18 +++++++++--------- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/include/dd/ComplexTable.hpp b/include/dd/ComplexTable.hpp index e8f24a8c..707611e5 100644 --- a/include/dd/ComplexTable.hpp +++ b/include/dd/ComplexTable.hpp @@ -257,7 +257,7 @@ namespace dd { while (it != bucket.end()) { if ((*it)->refCount == 0) { auto entry = (*it); - it = bucket.erase_after(lastit); // erases the element at `it` + it = bucket.erase_after(lastit); // erases the element at `it` returnEntry(entry); collected++; } else { diff --git a/include/dd/Control.hpp b/include/dd/Control.hpp index 751a51f9..0773925b 100644 --- a/include/dd/Control.hpp +++ b/include/dd/Control.hpp @@ -46,7 +46,6 @@ namespace dd { inline bool operator()(const Control& lhs, Qubit rhs) const { return lhs.qubit < rhs; } - }; using Controls = std::set; diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index 0db8f29d..8caf60c7 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -1609,7 +1609,7 @@ namespace dd { // set fixed width to maximum of a printed number // (-) 0.precision plus/minus 0.precision i constexpr auto width = 1 + 2 + precision + 1 + 2 + precision + 1; - std::cout << ": " << std::setw (width) << ComplexValue::toString(amplitude.r, amplitude.i, false, precision) << "\n"; + std::cout << ": " << std::setw(width) << ComplexValue::toString(amplitude.r, amplitude.i, false, precision) << "\n"; } std::cout << std::flush; } @@ -1618,12 +1618,12 @@ namespace dd { unsigned long long element = 2u << e.p->v; for (unsigned long long i = 0; i < element; i++) { for (unsigned long long j = 0; j < element; j++) { - auto amplitude = getValueByPath(e, i, j); + auto amplitude = getValueByPath(e, i, j); constexpr auto precision = 3; // set fixed width to maximum of a printed number // (-) 0.precision plus/minus 0.precision i constexpr auto width = 1 + 2 + precision + 1 + 2 + precision + 1; - std::cout << std::setw (width) << ComplexValue::toString(amplitude.r, amplitude.i, false, precision) << " "; + std::cout << std::setw(width) << ComplexValue::toString(amplitude.r, amplitude.i, false, precision) << " "; } std::cout << "\n"; } diff --git a/include/dd/UniqueTable.hpp b/include/dd/UniqueTable.hpp index d301d687..6a5edf26 100644 --- a/include/dd/UniqueTable.hpp +++ b/include/dd/UniqueTable.hpp @@ -221,7 +221,7 @@ namespace dd { assert(!Node::isTerminal(*it)); auto node = (*it); - it = bucket.erase_after(lastit); // erases the element at `it` + it = bucket.erase_after(lastit); // erases the element at `it` returnNode(node); collected++; } else { diff --git a/test/bench_package.cpp b/test/bench_package.cpp index 9e29beec..0b30df28 100644 --- a/test/bench_package.cpp +++ b/test/bench_package.cpp @@ -150,9 +150,9 @@ static void BM_MakeControlledQubitGateDD_ControlTop_TargetBottom(benchmark::Stat BENCHMARK(BM_MakeControlledQubitGateDD_ControlTop_TargetBottom)->Apply(QubitRange); static void BM_MakeFullControlledToffoliDD_TargetTop(benchmark::State& state) { - unsigned short nqubits = state.range(0); - auto dd = std::make_unique(nqubits); - dd::Controls controls; + unsigned short nqubits = state.range(0); + auto dd = std::make_unique(nqubits); + dd::Controls controls; for (std::size_t i = 0; i < static_cast(nqubits - 1); i++) controls.insert({static_cast(i)}); for (auto _: state) { @@ -162,9 +162,9 @@ static void BM_MakeFullControlledToffoliDD_TargetTop(benchmark::State& state) { BENCHMARK(BM_MakeFullControlledToffoliDD_TargetTop)->Apply(QubitRange); static void BM_MakeFullControlledToffoliDD_TargetMiddle(benchmark::State& state) { - unsigned short nqubits = state.range(0); - auto dd = std::make_unique(nqubits); - dd::Controls controls; + unsigned short nqubits = state.range(0); + auto dd = std::make_unique(nqubits); + dd::Controls controls; for (std::size_t i = 0; i < nqubits; i++) if (i != nqubits / 2) controls.insert({static_cast(i)}); @@ -175,9 +175,9 @@ static void BM_MakeFullControlledToffoliDD_TargetMiddle(benchmark::State& state) BENCHMARK(BM_MakeFullControlledToffoliDD_TargetMiddle)->Apply(QubitRange); static void BM_MakeFullControlledToffoliDD_TargetBottom(benchmark::State& state) { - unsigned short nqubits = state.range(0); - auto dd = std::make_unique(nqubits); - dd::Controls controls; + unsigned short nqubits = state.range(0); + auto dd = std::make_unique(nqubits); + dd::Controls controls; for (std::size_t i = 1; i < nqubits; i++) controls.insert({static_cast(i)}); for (auto _: state) { From 704c32e850be189188375ff6a4f0114e9ab0846e Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Wed, 21 Apr 2021 16:14:10 +0200 Subject: [PATCH 14/48] =?UTF-8?q?=F0=9F=90=9B=20fix=20misaligned=20access?= =?UTF-8?q?=20on=20ComplexTable::Entries?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/Complex.hpp | 26 ++++++++------- include/dd/ComplexNumbers.hpp | 46 ++++++++++++------------- include/dd/ComplexTable.hpp | 63 +++++++++++++++++------------------ include/dd/Edge.hpp | 4 +-- include/dd/Package.hpp | 52 ++++++++++++++--------------- test/test_complex.cpp | 36 ++++++++++---------- test/test_package.cpp | 2 +- 7 files changed, 115 insertions(+), 114 deletions(-) diff --git a/include/dd/Complex.hpp b/include/dd/Complex.hpp index 744ab0b5..7099b737 100644 --- a/include/dd/Complex.hpp +++ b/include/dd/Complex.hpp @@ -16,28 +16,30 @@ #include namespace dd { + using CTEntry = ComplexTable<>::Entry; + struct Complex { - ComplexTable<>::Entry* r; - ComplexTable<>::Entry* i; + CTEntry* r; + CTEntry* i; static Complex zero; static Complex one; void setVal(const Complex& c) const { - r->value = c.r->val(); - i->value = c.i->val(); + r->value = CTEntry::val(c.r); + i->value = CTEntry::val(c.i); } [[nodiscard]] bool approximatelyEquals(const Complex& c) const { - return r->approximatelyEquals(c.r) && i->approximatelyEquals(c.i); + return CTEntry::approximatelyEquals(r, c.r) && CTEntry::approximatelyEquals(i, c.i); }; [[nodiscard]] bool approximatelyZero() const { - return r->approximatelyZero() && i->approximatelyZero(); + return CTEntry::approximatelyZero(r) && CTEntry::approximatelyZero(i); } [[nodiscard]] bool approximatelyOne() const { - return r->approximatelyOne() && i->approximatelyZero(); + return CTEntry::approximatelyOne(r) && CTEntry::approximatelyZero(i); } bool operator==(const Complex& other) const { @@ -49,18 +51,18 @@ namespace dd { } [[nodiscard]] std::string toString(bool formatted = true, int precision = -1) const { - return ComplexValue::toString(r->val(), i->val(), formatted, precision); + return ComplexValue::toString(CTEntry::val(r), CTEntry::val(i), formatted, precision); } void writeBinary(std::ostream& os) const { - r->writeBinary(os); - i->writeBinary(os); + CTEntry::writeBinary(r, os); + CTEntry::writeBinary(i, os); } }; inline std::ostream& operator<<(std::ostream& os, const Complex& c) { - auto r = c.r->val(); - auto i = c.i->val(); + auto r = CTEntry::val(c.r); + auto i = CTEntry::val(c.i); if (r != 0) { ComplexValue::printFormatted(os, r); diff --git a/include/dd/ComplexNumbers.hpp b/include/dd/ComplexNumbers.hpp index 7a63fbf0..9786bbc4 100644 --- a/include/dd/ComplexNumbers.hpp +++ b/include/dd/ComplexNumbers.hpp @@ -38,14 +38,14 @@ namespace dd { static void add(Complex& r, const Complex& a, const Complex& b) { assert(r != Complex::zero); assert(r != Complex::one); - r.r->value = a.r->val() + b.r->val(); - r.i->value = a.i->val() + b.i->val(); + r.r->value = CTEntry::val(a.r) + CTEntry::val(b.r); + r.i->value = CTEntry::val(a.i) + CTEntry::val(b.i); } static void sub(Complex& r, const Complex& a, const Complex& b) { assert(r != Complex::zero); assert(r != Complex::one); - r.r->value = a.r->val() - b.r->val(); - r.i->value = a.i->val() - b.i->val(); + r.r->value = CTEntry::val(a.r) - CTEntry::val(b.r); + r.i->value = CTEntry::val(a.i) - CTEntry::val(b.i); } static void mul(Complex& r, const Complex& a, const Complex& b) { assert(r != Complex::zero); @@ -58,10 +58,10 @@ namespace dd { r.r->value = 0.; r.i->value = 0.; } else { - auto ar = a.r->val(); - auto ai = a.i->val(); - auto br = b.r->val(); - auto bi = b.i->val(); + auto ar = CTEntry::val(a.r); + auto ai = CTEntry::val(a.i); + auto br = CTEntry::val(b.r); + auto bi = CTEntry::val(b.i); r.r->value = ar * br - ai * bi; r.i->value = ar * bi + ai * br; @@ -79,10 +79,10 @@ namespace dd { } else if (b.approximatelyOne()) { r.setVal(a); } else { - auto ar = a.r->val(); - auto ai = a.i->val(); - auto br = b.r->val(); - auto bi = b.i->val(); + auto ar = CTEntry::val(a.r); + auto ai = CTEntry::val(a.i); + auto br = CTEntry::val(b.r); + auto bi = CTEntry::val(b.i); auto cmag = br * br + bi * bi; @@ -91,8 +91,8 @@ namespace dd { } } static inline fp mag2(const Complex& a) { - auto ar = a.r->val(); - auto ai = a.i->val(); + auto ar = CTEntry::val(a.r); + auto ai = CTEntry::val(a.i); return ar * ar + ai * ai; } @@ -100,24 +100,24 @@ namespace dd { return std::sqrt(mag2(a)); } static inline fp arg(const Complex& a) { - auto ar = a.r->val(); - auto ai = a.i->val(); + auto ar = CTEntry::val(a.r); + auto ai = CTEntry::val(a.i); return std::atan2(ai, ar); } static Complex conj(const Complex& a) { auto ret = a; if (a.i != Complex::zero.i) { - ret.i = a.i->flipPointerSign(); + ret.i = CTEntry::flipPointerSign(a.i); } return ret; } static Complex neg(const Complex& a) { auto ret = a; if (a.i != Complex::zero.i) { - ret.i = a.i->flipPointerSign(); + ret.i = CTEntry::flipPointerSign(a.i); } if (a.r != Complex::zero.i) { - ret.r = a.r->flipPointerSign(); + ret.r = CTEntry::flipPointerSign(a.r); } return ret; } @@ -155,8 +155,8 @@ namespace dd { return Complex::one; } - auto valr = c.r->val(); - auto vali = c.i->val(); + auto valr = CTEntry::val(c.r); + auto vali = CTEntry::val(c.i); return lookup(valr, vali); } Complex lookup(const fp& r, const fp& i) { @@ -165,7 +165,7 @@ namespace dd { auto sign_r = std::signbit(r); if (sign_r) { auto absr = std::abs(r); - ret.r = complexTable.lookup(absr)->getNegativePointer(); + ret.r = CTEntry::getNegativePointer(complexTable.lookup(absr)); } else { ret.r = complexTable.lookup(r); } @@ -173,7 +173,7 @@ namespace dd { auto sign_i = std::signbit(i); if (sign_i) { auto absi = std::abs(i); - ret.i = complexTable.lookup(absi)->getNegativePointer(); + ret.i = CTEntry::getNegativePointer(complexTable.lookup(absi)); } else { ret.i = complexTable.lookup(i); } diff --git a/include/dd/ComplexTable.hpp b/include/dd/ComplexTable.hpp index 707611e5..a9339000 100644 --- a/include/dd/ComplexTable.hpp +++ b/include/dd/ComplexTable.hpp @@ -33,48 +33,48 @@ namespace dd { /// If not handled properly, this causes misaligned access /// These routines allow to obtain safe pointers /// - [[nodiscard]] inline Entry* getAlignedPointer() const { - return reinterpret_cast(reinterpret_cast(this) & (~1ULL)); + [[nodiscard]] static inline Entry* getAlignedPointer(const Entry* e) { + return reinterpret_cast(reinterpret_cast(e) & (~1ULL)); } - [[nodiscard]] inline Entry* getNegativePointer() const { - return reinterpret_cast(reinterpret_cast(this) | 1ULL); + [[nodiscard]] static inline Entry* getNegativePointer(const Entry* e) { + return reinterpret_cast(reinterpret_cast(e) | 1ULL); } - [[nodiscard]] inline Entry* flipPointerSign() const { - return reinterpret_cast(reinterpret_cast(this) ^ 1ULL); + [[nodiscard]] static inline Entry* flipPointerSign(const Entry* e) { + return reinterpret_cast(reinterpret_cast(e) ^ 1ULL); } - [[nodiscard]] inline bool isNegativePointer() const { - return reinterpret_cast(this) & 1ULL; + [[nodiscard]] static inline bool isNegativePointer(const Entry* e) { + return reinterpret_cast(e) & 1ULL; } - [[nodiscard]] inline fp val() const { - if (isNegativePointer()) { - return -getAlignedPointer()->value; + [[nodiscard]] static inline fp val(const Entry* e) { + if (isNegativePointer(e)) { + return -getAlignedPointer(e)->value; } - return value; + return e->value; } - [[nodiscard]] inline RefCount ref() const { - if (isNegativePointer()) { - return -getAlignedPointer()->refCount; + [[nodiscard]] static inline RefCount ref(const Entry* e) { + if (isNegativePointer(e)) { + return -getAlignedPointer(e)->refCount; } - return refCount; + return e->refCount; } - [[nodiscard]] inline bool approximatelyEquals(const Entry* entry) const { - return std::abs(val() - entry->val()) < TOLERANCE; + [[nodiscard]] static inline bool approximatelyEquals(const Entry* left, const Entry* right) { + return std::abs(val(left) - val(right)) < TOLERANCE; } - [[nodiscard]] inline bool approximatelyZero() const { - return this == &zero || std::abs(val()) < TOLERANCE; + [[nodiscard]] static inline bool approximatelyZero(const Entry* e) { + return e == &zero || std::abs(val(e)) < TOLERANCE; } - [[nodiscard]] inline bool approximatelyOne() const { - return this == &one || std::abs(val() - 1) < TOLERANCE; + [[nodiscard]] static inline bool approximatelyOne(const Entry* e) { + return e == &one || std::abs(val(e) - 1) < TOLERANCE; } - void writeBinary(std::ostream& os) const { - auto temp = val(); - os.write(reinterpret_cast(&temp), sizeof(decltype(value))); + static void writeBinary(const Entry* e, std::ostream& os) { + auto temp = val(e); + os.write(reinterpret_cast(&temp), sizeof(decltype(temp))); } }; @@ -169,12 +169,11 @@ namespace dd { } // value was not found in the table -> get a new entry and add it to the central bucket - auto entry = getEntry(); - entry->value = val; - table[key].push_front(entry); + table[key].emplace_front(getEntry()); + table[key].front()->value = val; count++; peakCount = std::max(peakCount, count); - return entry; + return table[key].front(); } [[nodiscard]] Entry* getEntry() { @@ -212,7 +211,7 @@ namespace dd { return; // get valid pointer - auto entryPtr = entry->getAlignedPointer(); + auto entryPtr = Entry::getAlignedPointer(entry); // `zero` and `one` are static and never altered if (entryPtr != &one && entryPtr != &zero) { @@ -232,7 +231,7 @@ namespace dd { return; // get valid pointer - auto entryPtr = entry->getAlignedPointer(); + auto entryPtr = Entry::getAlignedPointer(entry); // `zero` and `one` are static and never altered if (entryPtr != &one && entryPtr != &zero) { @@ -357,7 +356,7 @@ namespace dd { std::size_t gcRuns = 0; std::size_t gcLimit = 250000; - typename Bucket::const_iterator find(const Bucket& bucket, const fp& val) { + inline typename Bucket::const_iterator find(const Bucket& bucket, const fp& val) { auto it = bucket.cbegin(); while (it != bucket.cend()) { if (std::abs((*it)->value - val) < TOLERANCE) { diff --git a/include/dd/Edge.hpp b/include/dd/Edge.hpp index 9e9b80ab..bf2525a7 100644 --- a/include/dd/Edge.hpp +++ b/include/dd/Edge.hpp @@ -50,8 +50,8 @@ namespace dd { p(p), w(w) {} CachedEdge(Node* p, const Complex& c): p(p) { - w.r = c.r->val(); - w.i = c.i->val(); + w.r = CTEntry::val(c.r); + w.i = CTEntry::val(c.i); } /// Comparing two DD edges with another involves comparing the respective pointers diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index 8caf60c7..68f690fb 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -152,7 +152,7 @@ namespace dd { r.w.r->value *= sum; r.w.i->value *= sum; } else { - r.w = cn.lookup(max.w.r->val() * sum, max.w.i->val() * sum); + r.w = cn.lookup(CTEntry::val(max.w.r) * sum, CTEntry::val(max.w.i) * sum); if (r.w.approximatelyZero()) { return vEdge::zero; } @@ -605,12 +605,12 @@ namespace dd { if (x.w == Complex::zero) { if (y.w == Complex::zero) return y; auto r = y; - r.w = cn.getCached(y.w.r->val(), y.w.i->val()); + r.w = cn.getCached(CTEntry::val(y.w.r), CTEntry::val(y.w.i)); return r; } if (y.w == Complex::zero) { auto r = x; - r.w = cn.getCached(x.w.r->val(), x.w.i->val()); + r.w = cn.getCached(CTEntry::val(x.w.r), CTEntry::val(x.w.i)); return r; } if (x.p == y.p) { @@ -981,7 +981,7 @@ namespace dd { auto c = cn.getTemporary(r.w); ComplexNumbers::mul(c, c, x.w); ComplexNumbers::mul(c, c, y.w); - return {c.r->val(), c.i->val()}; + return {CTEntry::val(c.r), CTEntry::val(c.i)}; } auto w = static_cast(var - 1); @@ -1012,7 +1012,7 @@ namespace dd { auto c = cn.getTemporary(sum); ComplexNumbers::mul(c, c, x.w); ComplexNumbers::mul(c, c, y.w); - return {c.r->val(), c.i->val()}; + return {CTEntry::val(c.r), CTEntry::val(c.i)}; } /// @@ -1075,7 +1075,7 @@ namespace dd { e = makeDDNode(static_cast(e.p->v + 1), std::array{e, Edge::zero, Edge::zero, e}); } - e.w = cn.getCached(y.w.r->val(), y.w.i->val()); + e.w = cn.getCached(CTEntry::val(y.w.r), CTEntry::val(y.w.i)); computeTable.insert(x, y, {e.p, e.w}); return e; } @@ -1109,7 +1109,7 @@ namespace dd { const auto res = partialTrace(a, eliminate); [[maybe_unused]] const auto after = cn.cacheCount(); assert(before == after); - return {res.w.r->val(), res.w.i->val()}; + return {CTEntry::val(res.w.r), CTEntry::val(res.w.i)}; } private: @@ -1500,7 +1500,7 @@ namespace dd { template ComplexValue getValueByPath(const Edge& e, const std::string& elements) { if (e.isTerminal()) { - return {e.w.r->val(), e.w.i->val()}; + return {CTEntry::val(e.w.r), CTEntry::val(e.w.i)}; } auto c = cn.getTemporary(1, 0); @@ -1513,11 +1513,11 @@ namespace dd { } while (!r.isTerminal()); ComplexNumbers::mul(c, c, r.w); - return {c.r->val(), c.i->val()}; + return {CTEntry::val(c.r), CTEntry::val(c.i)}; } ComplexValue getValueByPath(const vEdge& e, std::size_t i) { if (e.isTerminal()) { - return {e.w.r->val(), e.w.i->val()}; + return {CTEntry::val(e.w.r), CTEntry::val(e.w.i)}; } return getValueByPath(e, Complex::one, i); } @@ -1526,7 +1526,7 @@ namespace dd { if (e.isTerminal()) { cn.returnToCache(c); - return {c.r->val(), c.i->val()}; + return {CTEntry::val(c.r), CTEntry::val(c.i)}; } bool one = i & (1 << e.p->v); @@ -1542,7 +1542,7 @@ namespace dd { } ComplexValue getValueByPath(const mEdge& e, std::size_t i, std::size_t j) { if (e.isTerminal()) { - return {e.w.r->val(), e.w.i->val()}; + return {CTEntry::val(e.w.r), CTEntry::val(e.w.i)}; } return getValueByPath(e, Complex::one, i, j); } @@ -1551,7 +1551,7 @@ namespace dd { if (e.isTerminal()) { cn.returnToCache(c); - return {c.r->val(), c.i->val()}; + return {CTEntry::val(c.r), CTEntry::val(c.i)}; } bool row = i & (1 << e.p->v); @@ -1584,7 +1584,7 @@ namespace dd { // base case if (e.isTerminal()) { - vec.at(i) = {c.r->val(), c.i->val()}; + vec.at(i) = {CTEntry::val(c.r), CTEntry::val(c.i)}; cn.returnToCache(c); return; } @@ -1643,7 +1643,7 @@ namespace dd { // base case if (e.isTerminal()) { - mat.at(i).at(j) = {c.r->val(), c.i->val()}; + mat.at(i).at(j) = {CTEntry::val(c.r), CTEntry::val(c.i)}; cn.returnToCache(c); return; } @@ -1818,8 +1818,8 @@ namespace dd { std::clog << "Debug node: " << debugnode_line(p) << "\n"; for (const auto& edge: p->e) { std::clog << " " << std::hexfloat - << std::setw(22) << edge.w.r->val() << " " - << std::setw(22) << edge.w.i->val() << std::defaultfloat + << std::setw(22) << CTEntry::val(edge.w.r) << " " + << std::setw(22) << CTEntry::val(edge.w.i) << std::defaultfloat << "i --> " << debugnode_line(edge.p) << "\n"; } std::clog << std::flush; @@ -1865,8 +1865,8 @@ namespace dd { private: template bool isLocallyConsistent2(const Edge& e) { - const auto ptr_r = e.w.r->getAlignedPointer(); - const auto ptr_i = e.w.i->getAlignedPointer(); + const auto ptr_r = CTEntry::getAlignedPointer(e.w.r); + const auto ptr_i = CTEntry::getAlignedPointer(e.w.i); if ((ptr_r->refCount == 0 || ptr_i->refCount == 0) && e.w != Complex::one && e.w != Complex::zero) { std::clog << "\nLOCAL INCONSISTENCY FOUND\nOffending Number: " << e.w << " (" << ptr_r->refCount << ", " << ptr_i->refCount << ")\n\n"; @@ -1904,8 +1904,8 @@ namespace dd { template void fillConsistencyCounter(const Edge& edge, std::map::Entry*, std::size_t>& weight_map, std::map& node_map) { - weight_map[edge.w.r->getAlignedPointer()]++; - weight_map[edge.w.i->getAlignedPointer()]++; + weight_map[CTEntry::getAlignedPointer(edge.w.r)]++; + weight_map[CTEntry::getAlignedPointer(edge.w.i)]++; if (edge.isTerminal()) { return; @@ -1916,20 +1916,20 @@ namespace dd { fillConsistencyCounter(child, weight_map, node_map); } else { node_map[child.p]++; - weight_map[child.w.r->getAlignedPointer()]++; - weight_map[child.w.i->getAlignedPointer()]++; + weight_map[CTEntry::getAlignedPointer(child.w.r)]++; + weight_map[CTEntry::getAlignedPointer(child.w.i)]++; } } } template void checkConsistencyCounter(const Edge& edge, const std::map::Entry*, std::size_t>& weight_map, const std::map& node_map) { - auto* r_ptr = edge.w.r->getAlignedPointer(); - auto* i_ptr = edge.w.i->getAlignedPointer(); + auto* r_ptr = CTEntry::getAlignedPointer(edge.w.r); + auto* i_ptr = CTEntry::getAlignedPointer(edge.w.i); if (weight_map.at(r_ptr) > r_ptr->refCount && r_ptr != Complex::one.r && r_ptr != Complex::zero.i) { std::clog << "\nOffending weight: " << edge.w << "\n"; - std::clog << "Bits: " << std::hexfloat << edge.w.r->val() << " " << edge.w.i->val() << std::defaultfloat << "\n"; + std::clog << "Bits: " << std::hexfloat << CTEntry::val(edge.w.r) << " " << CTEntry::val(edge.w.i) << std::defaultfloat << "\n"; debugnode(edge.p); throw std::runtime_error("Ref-Count mismatch for " + std::to_string(r_ptr->value) + "(r): " + std::to_string(weight_map.at(r_ptr)) + " occurences in DD but Ref-Count is only " + std::to_string(r_ptr->refCount)); } diff --git a/test/test_complex.cpp b/test/test_complex.cpp index f725dbbd..c9fc2834 100644 --- a/test/test_complex.cpp +++ b/test/test_complex.cpp @@ -41,26 +41,26 @@ TEST(DDComplexTest, ComplexNumberCreation) { EXPECT_EQ(cn.lookup(Complex::zero), Complex::zero); EXPECT_EQ(cn.lookup(Complex::one), Complex::one); EXPECT_EQ(cn.lookup(1e-14, 0.), Complex::zero); - EXPECT_EQ(cn.lookup(1e-14, 1.).r->val(), 0.); - EXPECT_EQ(cn.lookup(1e-14, 1.).i->val(), 1.); - EXPECT_EQ(cn.lookup(1e-14, -1.).r->val(), 0.); - EXPECT_EQ(cn.lookup(1e-14, -1.).i->val(), -1.); - EXPECT_EQ(cn.lookup(-1., -1.).r->val(), -1.); - EXPECT_EQ(cn.lookup(-1., -1.).i->val(), -1.); + EXPECT_EQ(CTEntry::val(cn.lookup(1e-14, 1.).r), 0.); + EXPECT_EQ(CTEntry::val(cn.lookup(1e-14, 1.).i), 1.); + EXPECT_EQ(CTEntry::val(cn.lookup(1e-14, -1.).r), 0.); + EXPECT_EQ(CTEntry::val(cn.lookup(1e-14, -1.).i), -1.); + EXPECT_EQ(CTEntry::val(cn.lookup(-1., -1.).r), -1.); + EXPECT_EQ(CTEntry::val(cn.lookup(-1., -1.).i), -1.); auto c = cn.lookup(0., -1.); std::cout << c << std::endl; - EXPECT_EQ(cn.lookup(c).r->val(), 0.); - EXPECT_EQ(cn.lookup(c).i->val(), -1.); + EXPECT_EQ(CTEntry::val(cn.lookup(c).r), 0.); + EXPECT_EQ(CTEntry::val(cn.lookup(c).i), -1.); c = cn.lookup(0., 1.); - EXPECT_EQ(cn.lookup(c).r->val(), 0.); - EXPECT_EQ(cn.lookup(c).i->val(), 1.); + EXPECT_EQ(CTEntry::val(cn.lookup(c).r), 0.); + EXPECT_EQ(CTEntry::val(cn.lookup(c).i), 1.); c = cn.lookup(0., -0.5); std::cout << c << std::endl; - EXPECT_EQ(cn.lookup(c).r->val(), 0.); - EXPECT_EQ(cn.lookup(c).i->val(), -0.5); + EXPECT_EQ(CTEntry::val(cn.lookup(c).r), 0.); + EXPECT_EQ(CTEntry::val(cn.lookup(c).i), -0.5); c = cn.lookup(-1., -1.); - EXPECT_EQ(cn.lookup(c).r->val(), -1.); - EXPECT_EQ(cn.lookup(c).i->val(), -1.); + EXPECT_EQ(CTEntry::val(cn.lookup(c).r), -1.); + EXPECT_EQ(CTEntry::val(cn.lookup(c).i), -1.); std::cout << c << std::endl; auto e = cn.lookup(1., -1.); @@ -76,12 +76,12 @@ TEST(DDComplexTest, ComplexNumberArithmetic) { auto cn = ComplexNumbers(); auto c = cn.lookup(0., 1.); auto d = ComplexNumbers::conj(c); - EXPECT_EQ(d.r->val(), 0.); - EXPECT_EQ(d.i->val(), -1.); + EXPECT_EQ(CTEntry::val(d.r), 0.); + EXPECT_EQ(CTEntry::val(d.i), -1.); c = cn.lookup(-1., -1.); d = ComplexNumbers::neg(c); - EXPECT_EQ(d.r->val(), 1.); - EXPECT_EQ(d.i->val(), 1.); + EXPECT_EQ(CTEntry::val(d.r), 1.); + EXPECT_EQ(CTEntry::val(d.i), 1.); c = cn.lookup(0.5, 0.5); ComplexNumbers::incRef(c); d = cn.lookup(-0.5, 0.5); diff --git a/test/test_package.cpp b/test/test_package.cpp index cf706fb8..b728a690 100644 --- a/test/test_package.cpp +++ b/test/test_package.cpp @@ -117,7 +117,7 @@ TEST(DDPackageTest, PartialIdentityTrace) { auto dd = std::make_unique(2); auto tr = dd->partialTrace(dd->makeIdent(2), {false, true}); auto mul = dd->multiply(tr, tr); - EXPECT_EQ(mul.w.r->val(), 4.0); + EXPECT_EQ(dd::CTEntry::val(mul.w.r), 4.0); } TEST(DDPackageTest, StateGenerationManipulation) { From 1b9deeb794f3486c29bf69b76704cca527923fc7 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Wed, 21 Apr 2021 16:16:47 +0200 Subject: [PATCH 15/48] =?UTF-8?q?=F0=9F=90=9B=20require=20standard=20and?= =?UTF-8?q?=20disable=20cxx=20extensions=20for=20test=20executables?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- test/CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4b874166..7c0880e5 100755 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -29,6 +29,7 @@ endif() add_executable(${PROJECT_NAME}_example ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp) target_link_libraries(${PROJECT_NAME}_example PRIVATE ${PROJECT_NAME}) +set_target_properties(${PROJECT_NAME}_example PROPERTIES FOLDER tests CMAKE_CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) macro(package_add_test TESTNAME) # create an exectuable in which the tests will be stored @@ -37,7 +38,7 @@ macro(package_add_test TESTNAME) target_link_libraries(${TESTNAME} PRIVATE ${PROJECT_NAME} gtest_main) # discover tests gtest_discover_tests(${TESTNAME} WORKING_DIRECTORY ${PROJECT_DIR} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_DIR}") - set_target_properties(${TESTNAME} PROPERTIES FOLDER tests) + set_target_properties(${TESTNAME} PROPERTIES FOLDER tests CMAKE_CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) # enable interprocedural optimization if it is supported include(CheckIPOSupported) @@ -64,7 +65,7 @@ add_test(NAME ${PROJECT_NAME}_example COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${PROJ add_executable(${PROJECT_NAME}_bench EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/bench_package.cpp) # link the Google benchmark infrastructure and a default main fuction to the benchmark executable. target_link_libraries(${PROJECT_NAME}_bench PRIVATE ${PROJECT_NAME} benchmark::benchmark_main Threads::Threads) -set_target_properties(${PROJECT_NAME}_bench PROPERTIES FOLDER tests) +set_target_properties(${PROJECT_NAME}_bench PROPERTIES FOLDER tests CMAKE_CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) # enable interprocedural optimization if it is supported include(CheckIPOSupported) From 7cf3c863b7e50b2e2ecdcf18fa96e678172c2513 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Wed, 21 Apr 2021 17:31:55 +0200 Subject: [PATCH 16/48] =?UTF-8?q?=F0=9F=9A=91=20temporary=20workaround=20f?= =?UTF-8?q?or=20long=20compile=20times=20under=20gcc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/ComplexTable.hpp | 8 +++++--- include/dd/UniqueTable.hpp | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/include/dd/ComplexTable.hpp b/include/dd/ComplexTable.hpp index a9339000..5bbbf9a7 100644 --- a/include/dd/ComplexTable.hpp +++ b/include/dd/ComplexTable.hpp @@ -9,7 +9,6 @@ #include "Definitions.hpp" #include -#include #include #include #include @@ -328,9 +327,12 @@ namespace dd { private: using Bucket = std::forward_list; - using Table = std::array; + /// gcc is having serious troubles compiling this using std::array (compilation times >15min). + /// std::vector shouldn't be any less efficient in our application scenario + /// TODO: revisit this in the future + using Table = std::vector; - Table table{}; + Table table{NBUCKET}; // numerical tolerance to be used for floating point values static inline fp TOLERANCE = 1e-13; diff --git a/include/dd/UniqueTable.hpp b/include/dd/UniqueTable.hpp index 6a5edf26..10f20bb2 100644 --- a/include/dd/UniqueTable.hpp +++ b/include/dd/UniqueTable.hpp @@ -11,7 +11,6 @@ #include "Edge.hpp" #include -#include #include #include #include @@ -318,11 +317,14 @@ namespace dd { private: using NodeBucket = std::forward_list; - using Table = std::array; + /// gcc is having serious troubles compiling this using std::array (compilation times >15min). + /// std::vector shouldn't be any less efficient in our application scenario + /// TODO: revisit this in the future + using Table = std::vector; // unique tables (one per input variable) std::size_t nvars = 0; - std::vector tables{nvars}; + std::vector
tables{nvars, std::vector{NBUCKET}}; std::stack available{}; std::vector> chunks{}; From d732d692ee67736e15cc2b60f51cde9fec820423 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Wed, 21 Apr 2021 17:32:16 +0200 Subject: [PATCH 17/48] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20googletest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- extern/googletest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/googletest b/extern/googletest index eaf9a3fd..23ef2955 160000 --- a/extern/googletest +++ b/extern/googletest @@ -1 +1 @@ -Subproject commit eaf9a3fd77869cf95befb87455a2e2a2e85044ff +Subproject commit 23ef29555ef4789f555f1ba8c51b4c52975f0907 From f887f7b3290f6f22b6391a9dd1db994334a742fc Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Thu, 22 Apr 2021 12:59:26 +0200 Subject: [PATCH 18/48] =?UTF-8?q?=E2=9A=A1=20emplace=5Ffront=20instead=20o?= =?UTF-8?q?f=20push=5Ffront?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/ComplexTable.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/dd/ComplexTable.hpp b/include/dd/ComplexTable.hpp index 5bbbf9a7..84efcbb1 100644 --- a/include/dd/ComplexTable.hpp +++ b/include/dd/ComplexTable.hpp @@ -90,8 +90,8 @@ namespace dd { chunkEndIt = chunks[0].end(); // emplace static zero and one in the table - table[0].push_front(&zero); - table[NBUCKET - 1].push_front(&one); + table[0].emplace_front(&zero); + table[NBUCKET - 1].emplace_front(&one); count = 2; peakCount = 2; From 0b3a64b74e380a1457a924634a3987bb08d0be66 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Thu, 22 Apr 2021 13:00:14 +0200 Subject: [PATCH 19/48] =?UTF-8?q?=E2=9A=A1=20add=20IPO=20for=20DDPackage?= =?UTF-8?q?=5Fexample?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- test/CMakeLists.txt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7c0880e5..0f6fca35 100755 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -24,12 +24,19 @@ if (NOT TARGET benchmark::benchmark) if (BINDINGS) # adjust visibility settings for building Python bindings target_compile_options(benchmark PUBLIC -fvisibility=hidden) - endif() -endif() + endif () +endif () add_executable(${PROJECT_NAME}_example ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp) target_link_libraries(${PROJECT_NAME}_example PRIVATE ${PROJECT_NAME}) set_target_properties(${PROJECT_NAME}_example PROPERTIES FOLDER tests CMAKE_CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +# enable interprocedural optimization if it is supported +include(CheckIPOSupported) +check_ipo_supported(RESULT ipo_supported) +if (ipo_supported) + set_target_properties(${PROJECT_NAME}_example PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) +endif () + macro(package_add_test TESTNAME) # create an exectuable in which the tests will be stored From 95c461a8faa1c607da4006a8f0be6a9387f8dd63 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Thu, 22 Apr 2021 13:29:42 +0200 Subject: [PATCH 20/48] =?UTF-8?q?=F0=9F=90=A7=20disable=20LTO=20in=20case?= =?UTF-8?q?=20clang=20is=20used=20under=20linux?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- CMakeLists.txt | 9 +++++++++ test/CMakeLists.txt | 25 +++---------------------- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 55c6e022..e4ead553 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,6 +71,15 @@ endif () # add JKQ alias add_library(JKQ::DDPackage ALIAS ${PROJECT_NAME}) +macro(enable_lto TARGET_NAME) + # enable interprocedural optimization if it is supported (Clang's ThinLTO does not work with Ubuntu 20.04's default linker at the moment) + include(CheckIPOSupported) + check_ipo_supported(RESULT ipo_supported) + if (ipo_supported AND NOT ((${CMAKE_SYSTEM_NAME} MATCHES "Linux") AND (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang"))) + set_target_properties(${TARGETNAME} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) + endif () +endmacro() + # add test code if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME OR BUILD_DD_PACKAGE_TESTS) if (NOT EXISTS "${PROJECT_SOURCE_DIR}/extern/googletest/CMakeLists.txt") diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0f6fca35..1ede0b30 100755 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -30,13 +30,7 @@ endif () add_executable(${PROJECT_NAME}_example ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp) target_link_libraries(${PROJECT_NAME}_example PRIVATE ${PROJECT_NAME}) set_target_properties(${PROJECT_NAME}_example PROPERTIES FOLDER tests CMAKE_CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) -# enable interprocedural optimization if it is supported -include(CheckIPOSupported) -check_ipo_supported(RESULT ipo_supported) -if (ipo_supported) - set_target_properties(${PROJECT_NAME}_example PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) -endif () - +enable_lto(${PROJECT_NAME}_example) macro(package_add_test TESTNAME) # create an exectuable in which the tests will be stored @@ -46,14 +40,7 @@ macro(package_add_test TESTNAME) # discover tests gtest_discover_tests(${TESTNAME} WORKING_DIRECTORY ${PROJECT_DIR} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_DIR}") set_target_properties(${TESTNAME} PROPERTIES FOLDER tests CMAKE_CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) - - # enable interprocedural optimization if it is supported - include(CheckIPOSupported) - check_ipo_supported(RESULT ipo_supported) - if (ipo_supported) - set_target_properties(${TESTNAME} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) - endif () - + enable_lto(${TESTNAME}) endmacro() # add unit tests @@ -73,13 +60,7 @@ add_executable(${PROJECT_NAME}_bench EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR # link the Google benchmark infrastructure and a default main fuction to the benchmark executable. target_link_libraries(${PROJECT_NAME}_bench PRIVATE ${PROJECT_NAME} benchmark::benchmark_main Threads::Threads) set_target_properties(${PROJECT_NAME}_bench PROPERTIES FOLDER tests CMAKE_CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) - -# enable interprocedural optimization if it is supported -include(CheckIPOSupported) -check_ipo_supported(RESULT ipo_supported) -if (ipo_supported) - set_target_properties(${PROJECT_NAME}_bench PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) -endif () +enable_lto(${PROJECT_NAME}_bench) add_custom_command(TARGET ${PROJECT_NAME}_bench POST_BUILD From cf40cea9c07f419572dd8cad59249e93634f5be9 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Thu, 22 Apr 2021 13:36:10 +0200 Subject: [PATCH 21/48] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20clang-format=2012?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- .clang-format | 44 ++++++++++++++++++++-------------------- .github/workflows/ci.yml | 6 +++--- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/.clang-format b/.clang-format index 7660791c..8b7dcaa4 100644 --- a/.clang-format +++ b/.clang-format @@ -1,25 +1,25 @@ # Generated from CLion C/C++ Code Style settings -BasedOnStyle: LLVM -AccessModifierOffset: -4 -AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: true -AlignConsecutiveDeclarations: true -AlignEscapedNewlines: Left -AlignOperands: AlignAfterOperator -AlignTrailingComments: true -AllowAllArgumentsOnNextLine: true +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: Consecutive +AlignConsecutiveDeclarations: Consecutive +AlignEscapedNewlines: Left +AlignOperands: AlignAfterOperator +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true AllowAllConstructorInitializersOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true -AllowShortBlocksOnASingleLine: Empty -AllowShortCaseLabelsOnASingleLine: true -AllowShortEnumsOnASingleLine: true -AllowShortFunctionsOnASingleLine: Inline -AllowShortIfStatementsOnASingleLine: WithoutElse -AllowShortLambdasOnASingleLine: All -AllowShortLoopsOnASingleLine: true -AlwaysBreakAfterReturnType: None -AlwaysBreakTemplateDeclarations: Yes -BreakBeforeBraces: Custom +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeBraces: Custom BraceWrapping: AfterCaseLabel: false AfterClass: false @@ -42,7 +42,7 @@ CompactNamespaces: false ContinuationIndentWidth: 8 Cpp11BracedListStyle: true DeriveLineEnding: true -#EmptyLineBeforeAccessModifier: LogicalBlock +EmptyLineBeforeAccessModifier: LogicalBlock IncludeBlocks: Regroup IndentCaseBlocks: false IndentCaseLabels: true @@ -56,12 +56,12 @@ ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true PointerAlignment: Left ReflowComments: false -SortIncludes: true +SortIncludes: CaseSensitive SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: false SpaceBeforeAssignmentOperators: true -#SpaceBeforeCaseColon: false +SpaceBeforeCaseColon: false SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: false SpaceBeforeInheritanceColon: false diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c5d87b40..5ae3b894 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,10 +100,10 @@ jobs: - uses: actions/checkout@v2 with: submodules: recursive - - uses: DoozyX/clang-format-lint-action@v0.11 + - uses: DoozyX/clang-format-lint-action@v0.12 with: - source: 'include test' - clangFormatVersion: 11 + source: 'include test' + clangFormatVersion: 12 # inplace: True # - uses: EndBug/add-and-commit@v4 # with: From 48d3d00893a8f458e2f8cb1d913f888c14ce8b3e Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Thu, 22 Apr 2021 13:40:55 +0200 Subject: [PATCH 22/48] =?UTF-8?q?=F0=9F=8E=A8=20reformat=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- .clang-format | 112 +++++++++++++++++++++++++------------------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/.clang-format b/.clang-format index 8b7dcaa4..2d06f8f1 100644 --- a/.clang-format +++ b/.clang-format @@ -21,62 +21,62 @@ AlwaysBreakAfterReturnType: None AlwaysBreakTemplateDeclarations: Yes BreakBeforeBraces: Custom BraceWrapping: - AfterCaseLabel: false - AfterClass: false + AfterCaseLabel: false + AfterClass: false AfterControlStatement: Never - AfterEnum: false - AfterFunction: false - AfterNamespace: false - AfterUnion: false - BeforeCatch: false - BeforeElse: false - IndentBraces: false - SplitEmptyFunction: false - SplitEmptyRecord: false -BreakBeforeBinaryOperators: None -BreakBeforeTernaryOperators: false -BreakConstructorInitializers: AfterColon -BreakInheritanceList: AfterColon -ColumnLimit: 0 -CompactNamespaces: false -ContinuationIndentWidth: 8 -Cpp11BracedListStyle: true -DeriveLineEnding: true + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterColon +ColumnLimit: 0 +CompactNamespaces: false +ContinuationIndentWidth: 8 +Cpp11BracedListStyle: true +DeriveLineEnding: true EmptyLineBeforeAccessModifier: LogicalBlock -IncludeBlocks: Regroup -IndentCaseBlocks: false -IndentCaseLabels: true -IndentPPDirectives: BeforeHash -IndentWidth: 4 -KeepEmptyLinesAtTheStartOfBlocks: false -Language: Cpp -MaxEmptyLinesToKeep: 1 -NamespaceIndentation: All -ObjCSpaceAfterProperty: false -ObjCSpaceBeforeProtocolList: true -PointerAlignment: Left -ReflowComments: false -SortIncludes: CaseSensitive -SpaceAfterCStyleCast: false -SpaceAfterLogicalNot: false -SpaceAfterTemplateKeyword: false -SpaceBeforeAssignmentOperators: true +IncludeBlocks: Regroup +IndentCaseBlocks: false +IndentCaseLabels: true +IndentPPDirectives: BeforeHash +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: false +Language: Cpp +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PointerAlignment: Left +ReflowComments: false +SortIncludes: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true SpaceBeforeCaseColon: false -SpaceBeforeCpp11BracedList: false -SpaceBeforeCtorInitializerColon: false -SpaceBeforeInheritanceColon: false -SpaceBeforeParens: ControlStatements -SpaceBeforeRangeBasedForLoopColon: false -SpaceBeforeSquareBrackets: false -SpaceInEmptyBlock: false -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 1 -SpacesInAngles: false -SpacesInConditionalStatement: false -SpacesInCStyleCastParentheses: false -SpacesInContainerLiterals: false -SpacesInParentheses: false -SpacesInSquareBrackets: false -Standard: c++17 -TabWidth: 4 -UseTab: Never +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: false +SpaceBeforeInheritanceColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: false +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: c++17 +TabWidth: 4 +UseTab: Never From 0060b9e2bcdf7ae6d4fc6fd45b77b2937de42abf Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Thu, 22 Apr 2021 16:47:45 +0200 Subject: [PATCH 23/48] =?UTF-8?q?=E2=9C=85=20increase=20patch=20coverage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- test/test_package.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_package.cpp b/test/test_package.cpp index b728a690..a135e0b0 100644 --- a/test/test_package.cpp +++ b/test/test_package.cpp @@ -606,6 +606,8 @@ TEST(DDPackageTest, SpecialCaseTerminal) { dd->debugnode(one.p); dd::ComplexValue cOne{1.0, 0.0}; EXPECT_EQ(dd->getValueByPath(one, ""), cOne); + EXPECT_EQ(dd->getValueByPath(one, 0), cOne); + EXPECT_EQ(dd->getValueByPath(dd::Package::mEdge::one, 0, 0), cOne); } TEST(DDPackageTest, GarbageCollectSomeButNotAll) { From a3795320ba82cebe003994bbe111f27fa4e4d829 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Thu, 22 Apr 2021 16:50:28 +0200 Subject: [PATCH 24/48] =?UTF-8?q?=F0=9F=8D=8E=20switch=20to=20gcc=20for=20?= =?UTF-8?q?macOS=20CI=20build=20due=20to=20performance=20regressions=20whe?= =?UTF-8?q?n=20using=20(Apple)Clang=20under=20macOS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- .github/workflows/ci.yml | 186 +++++++++++++++++++++------------------ CMakeLists.txt | 6 +- README.md | 17 ++-- 3 files changed, 116 insertions(+), 93 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ae3b894..bc810dd7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,10 +1,10 @@ name: CI -on: +on: push: - branches: [master] + branches: [ master ] pull_request: - branches: [master] + branches: [ master ] workflow_dispatch: env: @@ -15,86 +15,100 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + os: [ ubuntu-latest, macos-latest, windows-latest ] steps: - - uses: actions/checkout@v2 - with: - submodules: recursive - - - name: Configure CMake - shell: bash - run: | - if [ "$RUNNER_OS" == "Windows" ]; then - cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -T "ClangCl" - else - cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE - fi - - - name: Build - shell: bash - run: cmake --build "${{github.workspace}}/build" --config $BUILD_TYPE + - uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Install gcc@10 + if: ${{ runner.os == 'macOS' }} + shell: bash + run: brew install gcc@10 + + - name: Configure CMake + shell: bash + run: | + if [ "$RUNNER_OS" == "Windows" ]; then + cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -T "ClangCl" + elif [ "$RUNNER_OS" == "macOS" ]; then + cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=g++-10 + else + cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE + fi + + - name: Build + shell: bash + run: cmake --build "${{github.workspace}}/build" --config $BUILD_TYPE test: - needs: build + needs: build runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + os: [ ubuntu-latest, macos-latest, windows-latest ] steps: - - uses: actions/checkout@v2 - with: - submodules: recursive - - - name: Configure CMake - shell: bash - run: | - if [ "$RUNNER_OS" == "Windows" ]; then - cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -T "ClangCl" - else - cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE - fi - - - name: Build - shell: bash - run: cmake --build "${{github.workspace}}/build" --config $BUILD_TYPE - - - name: Test - working-directory: ${{github.workspace}}/build - shell: bash - run: ctest -C $BUILD_TYPE - + - uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Install gcc@10 + if: ${{ runner.os == 'macOS' }} + shell: bash + run: brew install gcc@10 + + - name: Configure CMake + shell: bash + run: | + if [ "$RUNNER_OS" == "Windows" ]; then + cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -T "ClangCl" + elif [ "$RUNNER_OS" == "macOS" ]; then + cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=g++-10 + else + cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE + fi + + - name: Build + shell: bash + run: cmake --build "${{github.workspace}}/build" --config $BUILD_TYPE + + - name: Test + working-directory: ${{github.workspace}}/build + shell: bash + run: ctest -C $BUILD_TYPE + coverage: - needs: test + needs: test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - with: - submodules: recursive - - - name: Configure CMake - shell: bash - run: cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=Release -DCOVERAGE=1 - - - name: Build - shell: bash - run: cmake --build "${{github.workspace}}/build" --config Release --target DDPackage_test - - - name: Test - working-directory: ${{github.workspace}}/build/test - shell: bash - run: ./DDPackage_test - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1 - with: - fail_ci_if_error: true + - uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Configure CMake + shell: bash + run: cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=Release -DCOVERAGE=1 + + - name: Build + shell: bash + run: cmake --build "${{github.workspace}}/build" --config Release --target DDPackage_test + + - name: Test + working-directory: ${{github.workspace}}/build/test + shell: bash + run: ./DDPackage_test + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v1 + with: + fail_ci_if_error: true codestyle: -# needs: test + # needs: test runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -104,50 +118,50 @@ jobs: with: source: 'include test' clangFormatVersion: 12 -# inplace: True -# - uses: EndBug/add-and-commit@v4 -# with: -# author_name: github-actions -# author_email: 41898282+github-actions[bot]@users.noreply.github.com -# message: '🎨 clang-format changes' + # inplace: True + # - uses: EndBug/add-and-commit@v4 + # with: + # author_name: github-actions + # author_email: 41898282+github-actions[bot]@users.noreply.github.com + # message: '🎨 clang-format changes' benchmark: - needs: test + needs: test runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: submodules: recursive - - name: Configure CMake + - name: Configure CMake shell: bash - run: cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=Release + run: cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=Release - - name: Build + - name: Build shell: bash - run: cmake --build "${{github.workspace}}/build" --config Release --target DDPackage_bench + run: cmake --build "${{github.workspace}}/build" --config Release --target DDPackage_bench - - name: Run Benchmarks + - name: Run Benchmarks working-directory: ${{github.workspace}}/build - shell: bash - run: ./DDPackage_bench --benchmark_format=json | tee benchmark_result.json + shell: bash + run: ./DDPackage_bench --benchmark_format=json | tee benchmark_result.json - name: Store benchmark result # for pushes uses: rhysd/github-action-benchmark@v1 # if: github.event_name == 'push' with: # What benchmark tool the output.txt came from - tool: 'googlecpp' + tool: 'googlecpp' # Where the output from the benchmark tool is stored output-file-path: build/benchmark_result.json # Workflow will fail when an alert happens - fail-on-alert: true + fail-on-alert: true # GitHub API token to make a commit comment - github-token: ${{ secrets.GITHUB_TOKEN }} + github-token: ${{ secrets.GITHUB_TOKEN }} # Enable alert commit comment comment-on-alert: true # Set alert threshold - alert-threshold: 500% + alert-threshold: 500% # Mention @burgholzer in the commit comment alert-comment-cc-users: '@burgholzer' # Push and deploy GitHub pages branch automatically - auto-push: true + auto-push: true diff --git a/CMakeLists.txt b/CMakeLists.txt index e4ead553..4ca4b9ac 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ set_property(GLOBAL PROPERTY USE_FOLDERS ON) option(DEPLOY "Configure for deployment") option(BINDINGS "Configure for building Python bindings") option(COVERAGE "Configure for coverage report generation") -option(BUILD_DD_PACKAGE_TESTS "Also build tests for DD package" ) +option(BUILD_DD_PACKAGE_TESTS "Also build tests for DD package") # build type settings set(default_build_type "Release") @@ -24,6 +24,10 @@ if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif () +if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang" AND ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + message(WARNING "Using (Apple)Clang under macOS may lead to severe performance degradation. Consider using gcc to compile the project!") +endif () + # add main library code include(GNUInstallDirs) add_library(${PROJECT_NAME} INTERFACE) diff --git a/README.md b/README.md index 8a03dc56..a1e51520 100755 --- a/README.md +++ b/README.md @@ -41,22 +41,27 @@ auto h_op = dd->makeGateDD(dd::Hmat, 1, 0); auto superposition = dd->multiply(h_op, zero_state); ``` -For implementing more complex functionality which requires garbage collection, be advised that you have to do the reference counting by hand. +For implementing more complex functionality which requires garbage collection, be advised that you have to do the reference counting by hand. ### System Requirements -Building (and running) is continuously tested under Linux, MacOS, and Windows using the [latest available system versions for GitHub Actions](https://github.com/actions/virtual-environments). -However, the implementation should be compatible with any current C++ compiler supporting C++17 and a minimum CMake version of 3.14. +Building (and running) is continuously tested under Linux, MacOS, and Windows using the [latest available system versions for GitHub Actions](https://github.com/actions/virtual-environments). However, the implementation should be compatible +with any current C++ compiler supporting C++17 and a minimum CMake version of 3.14. + +Note that using (Apple)Clang to compile the library under macOS might lead to severe performance degradation. Consider using gcc to compile the project! It is recommended (although not required) to have [GraphViz](https://www.graphviz.org) installed for visualization purposes. - -### Setup, Build, and Run + +### Setup, Build, and Run To start off, clone this repository using + ```shell git clone --recurse-submodules -j8 https://github.com/iic-jku/dd_package ``` -Note the `--recurse-submodules` flag. It is required to also clone all the required submodules. If you happen to forget passing the flag on your initial clone, you can initialize all the submodules by executing `git submodule update --init --recursive` in the main project directory. + +Note the `--recurse-submodules` flag. It is required to also clone all the required submodules. If you happen to forget passing the flag on your initial clone, you can initialize all the submodules by +executing `git submodule update --init --recursive` in the main project directory. Our projects use CMake as the main build configuration tool. Building a project using CMake is a two-stage process. First, CMake needs to be *configured* by calling ```shell From 6bb56109646569e6ae07b7de98dc2c9c73244885 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Thu, 22 Apr 2021 16:53:40 +0200 Subject: [PATCH 25/48] =?UTF-8?q?=F0=9F=8D=8E=20=F0=9F=92=9A=20brew=20gcc?= =?UTF-8?q?=20is=20installed=20per=20default=20on=20GitHub=20action=20work?= =?UTF-8?q?ers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- .github/workflows/ci.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc810dd7..2a693df9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,11 +22,6 @@ jobs: with: submodules: recursive - - name: Install gcc@10 - if: ${{ runner.os == 'macOS' }} - shell: bash - run: brew install gcc@10 - - name: Configure CMake shell: bash run: | @@ -55,11 +50,6 @@ jobs: with: submodules: recursive - - name: Install gcc@10 - if: ${{ runner.os == 'macOS' }} - shell: bash - run: brew install gcc@10 - - name: Configure CMake shell: bash run: | From a430012cc70c5ed3194724e903f859cc82181e9b Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Thu, 22 Apr 2021 16:55:36 +0200 Subject: [PATCH 26/48] =?UTF-8?q?=F0=9F=8D=8E=20=F0=9F=92=9A=20correct=20c?= =?UTF-8?q?=20compiler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a693df9..7695d6f8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: if [ "$RUNNER_OS" == "Windows" ]; then cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -T "ClangCl" elif [ "$RUNNER_OS" == "macOS" ]; then - cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=g++-10 + cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 else cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE fi @@ -56,7 +56,7 @@ jobs: if [ "$RUNNER_OS" == "Windows" ]; then cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -T "ClangCl" elif [ "$RUNNER_OS" == "macOS" ]; then - cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=g++-10 + cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 else cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE fi From cd5f044020e0c89b60c1480caaa10c848525bdec Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Fri, 23 Apr 2021 10:31:01 +0200 Subject: [PATCH 27/48] =?UTF-8?q?=E2=9A=A1=20improve=20memory=20handling?= =?UTF-8?q?=20by=20reverting=20from=20using=20`stack`=20and=20`forward=5Fl?= =?UTF-8?q?ist`=20to=20explicitly=20batch-allocated=20`next`=20members=20i?= =?UTF-8?q?n=20the=20respective=20table=20entry=20classes=20=F0=9F=8E=A8?= =?UTF-8?q?=20optimize=20header=20includes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/Complex.hpp | 2 - include/dd/ComplexCache.hpp | 28 +++++---- include/dd/ComplexTable.hpp | 109 +++++++++++++++++++----------------- include/dd/Edge.hpp | 1 - include/dd/Export.hpp | 1 + include/dd/Package.hpp | 12 +++- include/dd/UniqueTable.hpp | 76 +++++++++++++------------ test/test_complex.cpp | 13 ++--- test/test_package.cpp | 20 +++---- 9 files changed, 134 insertions(+), 128 deletions(-) diff --git a/include/dd/Complex.hpp b/include/dd/Complex.hpp index 7099b737..47c55640 100644 --- a/include/dd/Complex.hpp +++ b/include/dd/Complex.hpp @@ -10,9 +10,7 @@ #include "ComplexValue.hpp" #include -#include #include -#include #include namespace dd { diff --git a/include/dd/ComplexCache.hpp b/include/dd/ComplexCache.hpp index 2071e965..797ad2ad 100644 --- a/include/dd/ComplexCache.hpp +++ b/include/dd/ComplexCache.hpp @@ -11,7 +11,6 @@ #include #include -#include #include namespace dd { @@ -41,9 +40,10 @@ namespace dd { [[nodiscard]] Complex getCachedComplex() { // an entry is available on the stack - if (!available.empty()) { - auto& entry = available.top(); - available.pop(); + if (available != nullptr) { + assert(available->next != nullptr); + auto entry = Complex{available, available->next}; + available = entry.i->next; count += 2; return entry; } @@ -69,8 +69,9 @@ namespace dd { [[nodiscard]] Complex getTemporaryComplex() { // an entry is available on the stack - if (!available.empty()) { - return available.top(); + if (available != nullptr) { + assert(available->next != nullptr); + return {available, available->next}; } // new chunk has to be allocated @@ -82,11 +83,7 @@ namespace dd { chunkIt = chunks[chunkID].begin(); chunkEndIt = chunks[chunkID].end(); } - - Complex c{}; - c.r = &(*chunkIt); - c.i = &(*(chunkIt + 1)); - return c; + return {&(*chunkIt), &(*(chunkIt + 1))}; } void returnToCache(Complex& c) { @@ -95,14 +92,15 @@ namespace dd { assert(c != Complex::one); assert(c.r->refCount == 0); assert(c.i->refCount == 0); - available.emplace(c); + c.i->next = available; + c.r->next = c.i; + available = c.r; count -= 2; } void clear() { // clear available stack - while (!available.empty()) - available.pop(); + available = nullptr; // release memory of all but the first chunk TODO: it could be desirable to keep the memory while (chunkID > 0) { @@ -120,7 +118,7 @@ namespace dd { }; private: - std::stack available{}; + Entry* available{}; std::vector> chunks{}; std::size_t chunkID; typename std::vector::iterator chunkIt; diff --git a/include/dd/ComplexTable.hpp b/include/dd/ComplexTable.hpp index 84efcbb1..996952fe 100644 --- a/include/dd/ComplexTable.hpp +++ b/include/dd/ComplexTable.hpp @@ -10,13 +10,10 @@ #include #include -#include #include #include -#include #include #include -#include #include namespace dd { @@ -25,6 +22,7 @@ namespace dd { public: struct Entry { fp value{}; + Entry* next{}; RefCount refCount{}; /// @@ -77,8 +75,8 @@ namespace dd { } }; - static inline Entry zero{0., 1}; - static inline Entry one{1., 1}; + static inline Entry zero{0., nullptr, 1}; + static inline Entry one{1., nullptr, 1}; ComplexTable(): chunkID(0), allocationSize(INITIAL_ALLOCATION_SIZE), gcLimit(INITIAL_GC_LIMIT) { @@ -90,10 +88,10 @@ namespace dd { chunkEndIt = chunks[0].end(); // emplace static zero and one in the table - table[0].emplace_front(&zero); - table[NBUCKET - 1].emplace_front(&one); - count = 2; - peakCount = 2; + table[0] = &zero; + table[NBUCKET - 1] = &one; + count = 2; + peakCount = 2; // add 1/2 and 1/sqrt(2) to the complex table and increase their ref count (so that they are not collected) lookup(0.5L)->refCount++; @@ -140,9 +138,9 @@ namespace dd { // search in intended bucket const auto key = hash(val); const auto& bucket = table[key]; - auto it = find(bucket, val); - if (it != bucket.end()) { - return (*it); + auto p = find(bucket, val); + if (p != nullptr) { + return p; } // search in (potentially) lower bucket @@ -150,9 +148,9 @@ namespace dd { const auto lowerKey = hash(val - TOLERANCE); if (lowerKey != key) { const auto& lowerBucket = table[lowerKey]; - it = find(lowerBucket, val); - if (it != lowerBucket.end()) { - return (*it); + p = find(lowerBucket, val); + if (p != nullptr) { + return p; } } } @@ -161,25 +159,27 @@ namespace dd { const auto upperKey = hash(val - TOLERANCE); if (upperKey != key) { const auto& upperBucket = table[upperKey]; - it = find(upperBucket, val); - if (it != upperBucket.end()) { - return (*it); + p = find(upperBucket, val); + if (p != nullptr) { + return p; } } // value was not found in the table -> get a new entry and add it to the central bucket - table[key].emplace_front(getEntry()); - table[key].front()->value = val; + Entry* entry = getEntry(); + entry->value = val; + entry->next = table[key]; + table[key] = entry; count++; peakCount = std::max(peakCount, count); - return table[key].front(); + return entry; } [[nodiscard]] Entry* getEntry() { // an entry is available on the stack - if (!available.empty()) { - auto& entry = available.top(); - available.pop(); + if (available != nullptr) { + Entry* entry = available; + available = entry->next; // returned entries could have a ref count != 0 entry->refCount = 0; return entry; @@ -201,7 +201,8 @@ namespace dd { } void returnEntry(Entry* entry) { - available.emplace(entry); + entry->next = available; + available = entry; } // increment reference count for corresponding entry @@ -250,17 +251,22 @@ namespace dd { std::size_t collected = 0; std::size_t remaining = 0; for (auto& bucket: table) { - auto it = bucket.begin(); - auto lastit = bucket.before_begin(); - while (it != bucket.end()) { - if ((*it)->refCount == 0) { - auto entry = (*it); - it = bucket.erase_after(lastit); // erases the element at `it` - returnEntry(entry); + Entry* p = bucket; + Entry* lastp = nullptr; + while (p != nullptr) { + if (p->refCount == 0) { + Entry* next = p->next; + if (lastp == nullptr) { + bucket = next; + } else { + lastp->next = next; + } + returnEntry(p); + p = next; collected++; } else { - ++it; - ++lastit; + lastp = p; + p = p->next; remaining++; } } @@ -273,12 +279,11 @@ namespace dd { void clear() { // clear table buckets for (auto& bucket: table) { - bucket.clear(); + bucket = nullptr; } // clear available stack - while (!available.empty()) - available.pop(); + available = nullptr; // release memory of all but the first chunk TODO: it could be desirable to keep the memory while (chunkID > 0) { @@ -305,14 +310,16 @@ namespace dd { void print() { for (std::size_t key = 0; key < table.size(); ++key) { - auto& bucket = table[key]; - if (!bucket.empty()) + auto& p = table[key]; + if (p != nullptr) std::cout << key << ": "; - for (const auto& node: bucket) - std::cout << "\t\t" << reinterpret_cast(node) << " " << node->refCount << "\t"; + while (p != nullptr) { + std::cout << "\t\t" << reinterpret_cast(p) << " " << p->refCount << "\t"; + p = p->next; + } - if (!bucket.empty()) + if (table[key] != nullptr) std::cout << "\n"; } } @@ -326,7 +333,7 @@ namespace dd { } private: - using Bucket = std::forward_list; + using Bucket = Entry*; /// gcc is having serious troubles compiling this using std::array (compilation times >15min). /// std::vector shouldn't be any less efficient in our application scenario /// TODO: revisit this in the future @@ -337,7 +344,7 @@ namespace dd { // numerical tolerance to be used for floating point values static inline fp TOLERANCE = 1e-13; - std::stack available{}; + Entry* available{}; std::vector> chunks{}; std::size_t chunkID; typename std::vector::iterator chunkIt; @@ -358,17 +365,17 @@ namespace dd { std::size_t gcRuns = 0; std::size_t gcLimit = 250000; - inline typename Bucket::const_iterator find(const Bucket& bucket, const fp& val) { - auto it = bucket.cbegin(); - while (it != bucket.cend()) { - if (std::abs((*it)->value - val) < TOLERANCE) { + inline Entry* find(const Bucket& bucket, const fp& val) { + auto p = bucket; + while (p != nullptr) { + if (std::abs(p->value - val) < TOLERANCE) { ++hits; - return it; + return p; } ++collisions; - ++it; + p = p->next; } - return it; + return p; } }; } // namespace dd diff --git a/include/dd/Edge.hpp b/include/dd/Edge.hpp index bf2525a7..cd0bd932 100644 --- a/include/dd/Edge.hpp +++ b/include/dd/Edge.hpp @@ -7,7 +7,6 @@ #define DD_PACKAGE_EDGE_HPP #include "Complex.hpp" -#include "ComplexTable.hpp" #include "ComplexValue.hpp" #include diff --git a/include/dd/Export.hpp b/include/dd/Export.hpp index d403b807..3c9bd38e 100644 --- a/include/dd/Export.hpp +++ b/include/dd/Export.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index 68f690fb..ac66d50d 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -21,9 +21,11 @@ #include "UnaryComputeTable.hpp" #include "UniqueTable.hpp" +#include #include #include #include +#include #include #include #include @@ -93,9 +95,10 @@ namespace dd { /// public: struct vNode { - std::array, RADIX> e{}; // edges out of this node - RefCount ref{}; // reference count - Qubit v{}; // variable index (nonterminal) value (-1 for terminal) + 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) static vNode terminalNode; constexpr static vNode* terminal{&terminalNode}; @@ -248,6 +251,7 @@ namespace dd { public: struct mNode { 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) bool symm = false; // node is symmetric @@ -2024,11 +2028,13 @@ namespace dd { }; inline Package::vNode Package::vNode::terminalNode{{{{nullptr, Complex::zero}, {nullptr, Complex::zero}}}, + nullptr, 0, -1}; inline Package::mNode Package::mNode::terminalNode{ {{{nullptr, Complex::zero}, {nullptr, Complex::zero}, {nullptr, Complex::zero}, {nullptr, Complex::zero}}}, + nullptr, 0, -1, true, diff --git a/include/dd/UniqueTable.hpp b/include/dd/UniqueTable.hpp index 10f20bb2..47c41e80 100644 --- a/include/dd/UniqueTable.hpp +++ b/include/dd/UniqueTable.hpp @@ -14,11 +14,9 @@ #include #include #include -#include #include #include #include -#include #include #include @@ -90,32 +88,32 @@ namespace dd { for ([[maybe_unused]] const auto& edge: e.p->e) assert(edge.p->v == v - 1 || edge.isTerminal()); - auto& bucket = tables[v][key]; - auto it = bucket.begin(); - while (it != bucket.end()) { - if (e.p->e == (*it)->e) { + Node* p = tables[v][key]; + while (p != nullptr) { + if (e.p->e == p->e) { // Match found - if (e.p != (*it) && !keepNode) { + if (e.p != p && !keepNode) { // put node pointed to by e.p on available chain returnNode(e.p); } hits++; // variables should stay the same - assert((*it)->v == e.p->v); + assert(p->v == e.p->v); // successors of a node shall either have successive variable numbers or be terminals for ([[maybe_unused]] const auto& edge: e.p->e) assert(edge.p->v == v - 1 || edge.isTerminal()); - return {(*it), e.w}; + return {p, e.w}; } collisions++; - ++it; + p = p->next; } // node was not found -> add it to front of unique table bucket - bucket.push_front(e.p); + e.p->next = tables[v][key]; + tables[v][key] = e.p; nodeCount++; peakNodeCount = std::max(peakNodeCount, nodeCount); @@ -124,9 +122,9 @@ namespace dd { [[nodiscard]] Node* getNode() { // a node is available on the stack - if (!available.empty()) { - auto& p = available.top(); - available.pop(); + if (available != nullptr) { + Node* p = available; + available = p->next; // returned nodes could have a ref count != 0 p->ref = 0; return p; @@ -148,7 +146,8 @@ namespace dd { } void returnNode(Node* p) { - available.emplace(p); + p->next = available; + available = p; } // increment reference counter for node e points to @@ -213,19 +212,23 @@ namespace dd { std::size_t remaining = 0; for (auto& table: tables) { for (auto& bucket: table) { - auto it = bucket.begin(); - auto lastit = bucket.before_begin(); - while (it != bucket.end()) { - if ((*it)->ref == 0) { - assert(!Node::isTerminal(*it)); - - auto node = (*it); - it = bucket.erase_after(lastit); // erases the element at `it` - returnNode(node); + Node* p = bucket; + Node* lastp = nullptr; + while (p != nullptr) { + if (p->ref == 0) { + assert(!Node::isTerminal(p)); + Node* next = p->next; + if (lastp == nullptr) { + bucket = next; + } else { + lastp->next = next; + } + returnNode(p); + p = next; collected++; } else { - ++it; - ++lastit; + lastp = p; + p = p->next; remaining++; } } @@ -240,12 +243,11 @@ namespace dd { // clear unique table buckets for (auto& table: tables) { for (auto& bucket: table) { - bucket.clear(); + bucket = nullptr; } } // clear available stack - while (!available.empty()) - available.pop(); + available = nullptr; // release memory of all but the first chunk TODO: it could be desirable to keep the memory while (chunkID > 0) { @@ -281,14 +283,16 @@ namespace dd { std::cout << "\t" << static_cast(q) << ":" << "\n"; for (std::size_t key = 0; key < table.size(); ++key) { - auto& bucket = table[key]; - if (!bucket.empty()) + auto& p = table[key]; + if (p != nullptr) std::cout << key << ": "; - for (const auto& node: bucket) - std::cout << "\t\t" << reinterpret_cast(node) << " " << node->ref << "\t"; + while (p != nullptr) { + std::cout << "\t\t" << reinterpret_cast(p) << " " << p->ref << "\t"; + p = p->next; + } - if (!bucket.empty()) + if (table[key] != nullptr) std::cout << "\n"; } --q; @@ -316,7 +320,7 @@ namespace dd { } private: - using NodeBucket = std::forward_list; + using NodeBucket = Node*; /// gcc is having serious troubles compiling this using std::array (compilation times >15min). /// std::vector shouldn't be any less efficient in our application scenario /// TODO: revisit this in the future @@ -326,7 +330,7 @@ namespace dd { std::size_t nvars = 0; std::vector
tables{nvars, std::vector{NBUCKET}}; - std::stack available{}; + Node* available{}; std::vector> chunks{}; std::size_t chunkID; typename std::vector::iterator chunkIt; diff --git a/test/test_complex.cpp b/test/test_complex.cpp index c9fc2834..250e5336 100644 --- a/test/test_complex.cpp +++ b/test/test_complex.cpp @@ -113,16 +113,13 @@ TEST(DDComplexTest, GarbageCollectSomeInBucket) { fp num2 = num + 2. * ComplexTable<>::tolerance(); ComplexNumbers::incRef(cn.lookup(num2, 0.0)); // num2 should be placed in same bucket as num - auto key = ComplexTable<>::hash(num); - auto& bucket = cn.complexTable.getTable()[key]; - auto it = bucket.begin(); - EXPECT_NEAR((*it)->value, num2, ComplexTable<>::tolerance()); - auto it1 = it; - it1++; - EXPECT_NEAR((*it1)->value, num, ComplexTable<>::tolerance()); + auto key = ComplexTable<>::hash(num); + auto* p = cn.complexTable.getTable()[key]; + EXPECT_NEAR(p->value, num2, ComplexTable<>::tolerance()); + EXPECT_NEAR((p->next)->value, num, ComplexTable<>::tolerance()); cn.garbageCollect(true); // num should be collected - EXPECT_NEAR(bucket.front()->value, num2, ComplexTable<>::tolerance()); + EXPECT_NEAR(cn.complexTable.getTable()[key]->value, num2, ComplexTable<>::tolerance()); } TEST(DDComplexTest, LookupInNeighbouringBuckets) { diff --git a/test/test_package.cpp b/test/test_package.cpp index a135e0b0..6cd6c306 100644 --- a/test/test_package.cpp +++ b/test/test_package.cpp @@ -501,15 +501,15 @@ TEST(DDPackageTest, PackageReset) { const auto& unique = dd->mUniqueTable.getTables(); const auto& table = unique[0]; auto ihash = dd->mUniqueTable.hash(i_gate.p); - const auto& node = table[ihash].front(); + const auto* node = table[ihash]; std::cout << ihash << ": " << reinterpret_cast(i_gate.p) << std::endl; // node should be the first in this unique table bucket EXPECT_EQ(node, i_gate.p); dd->reset(); // after clearing the tables, they should be empty - EXPECT_TRUE(table[ihash].empty()); + EXPECT_EQ(table[ihash], nullptr); i_gate = dd->makeIdent(1); - const auto& node2 = table[ihash].front(); + const auto* node2 = table[ihash]; // after recreating the DD, it should receive the same node EXPECT_EQ(node2, node); @@ -518,13 +518,11 @@ TEST(DDPackageTest, PackageReset) { auto zhash = dd->mUniqueTable.hash(z_gate.p); std::cout << zhash << ": " << reinterpret_cast(z_gate.p) << std::endl; // both nodes should reside in the same bucket - EXPECT_EQ(table[ihash].front(), z_gate.p); - auto it = table[ihash].begin(); - std::advance(it, 1); - EXPECT_EQ(*it, i_gate.p); + EXPECT_EQ(table[ihash], z_gate.p); + EXPECT_EQ(table[ihash]->next, i_gate.p); dd->reset(); // after clearing the tables, they should be empty - EXPECT_TRUE(table[ihash].empty()); + EXPECT_EQ(table[ihash], nullptr); auto z_gate2 = dd->makeGateDD(dd::Zmat, 1, 0); auto i_gate2 = dd->makeIdent(1); // recreating the decision diagrams in reverse order should use the same pointers as before @@ -633,10 +631,8 @@ TEST(DDPackageTest, GarbageCollectSomeButNotAll) { // garbage collection should only collect the I gate and leave the Z gate at the front of the bucket dd->garbageCollect(true); - EXPECT_EQ(table[Zhash].front(), Z.p); - auto it = table[Zhash].begin(); - ++it; - EXPECT_EQ(it, table[Zhash].end()); + EXPECT_EQ(table[Zhash], Z.p); + EXPECT_EQ(table[Zhash]->next, nullptr); } TEST(DDPackageTest, KroneckerProduct) { From fa44a430055b89e4846c56d9e21d6d638b3ea969 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Fri, 23 Apr 2021 10:58:15 +0200 Subject: [PATCH 28/48] =?UTF-8?q?=E2=8F=AA=20revert=20changes=20made=20for?= =?UTF-8?q?=20reducing=20gcc=20compile=20time=20as=20they=20are=20no=20lon?= =?UTF-8?q?ger=20needed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/ComplexTable.hpp | 9 ++++----- include/dd/UniqueTable.hpp | 8 +++----- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/include/dd/ComplexTable.hpp b/include/dd/ComplexTable.hpp index 996952fe..518e8f97 100644 --- a/include/dd/ComplexTable.hpp +++ b/include/dd/ComplexTable.hpp @@ -9,7 +9,9 @@ #include "Definitions.hpp" #include +#include #include +#include #include #include #include @@ -334,12 +336,9 @@ namespace dd { private: using Bucket = Entry*; - /// gcc is having serious troubles compiling this using std::array (compilation times >15min). - /// std::vector shouldn't be any less efficient in our application scenario - /// TODO: revisit this in the future - using Table = std::vector; + using Table = std::array; - Table table{NBUCKET}; + Table table{}; // numerical tolerance to be used for floating point values static inline fp TOLERANCE = 1e-13; diff --git a/include/dd/UniqueTable.hpp b/include/dd/UniqueTable.hpp index 47c41e80..b9c0888b 100644 --- a/include/dd/UniqueTable.hpp +++ b/include/dd/UniqueTable.hpp @@ -11,6 +11,7 @@ #include "Edge.hpp" #include +#include #include #include #include @@ -321,14 +322,11 @@ namespace dd { private: using NodeBucket = Node*; - /// gcc is having serious troubles compiling this using std::array (compilation times >15min). - /// std::vector shouldn't be any less efficient in our application scenario - /// TODO: revisit this in the future - using Table = std::vector; + using Table = std::array; // unique tables (one per input variable) std::size_t nvars = 0; - std::vector
tables{nvars, std::vector{NBUCKET}}; + std::vector
tables{nvars}; Node* available{}; std::vector> chunks{}; From 1a43d1fe7534347f69c470ea46682adbd82a2792 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Fri, 23 Apr 2021 11:47:25 +0200 Subject: [PATCH 29/48] =?UTF-8?q?=E2=9A=A1=20only=20clear=20compute=20tabl?= =?UTF-8?q?es=20if=20anything=20has=20been=20collected=20by=20the=20garbag?= =?UTF-8?q?e=20collection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/ComputeTable.hpp | 4 ++-- include/dd/Package.hpp | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/dd/ComputeTable.hpp b/include/dd/ComputeTable.hpp index 0bc25765..a8e07bfb 100644 --- a/include/dd/ComputeTable.hpp +++ b/include/dd/ComputeTable.hpp @@ -33,9 +33,9 @@ namespace dd { static constexpr std::size_t MASK = NBUCKET - 1; - static std::size_t hash(const LeftOperandType& leftOperand, const RightOperandType& b) { + static std::size_t hash(const LeftOperandType& leftOperand, const RightOperandType& rightOperand) { const auto h1 = std::hash{}(leftOperand); - const auto h2 = std::hash{}(b); + const auto h2 = std::hash{}(rightOperand); const auto hash = h1 ^ (h2 << 1); return hash & MASK; } diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index ac66d50d..8c89841b 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -509,12 +509,13 @@ namespace dd { UniqueTable mUniqueTable{nqubits}; void garbageCollect(bool force = false) { - [[maybe_unused]] auto vCollect = vUniqueTable.garbageCollect(force); - [[maybe_unused]] auto mCollect = mUniqueTable.garbageCollect(force); - [[maybe_unused]] auto cCollect = cn.garbageCollect(force); + auto vCollect = vUniqueTable.garbageCollect(force); + auto mCollect = mUniqueTable.garbageCollect(force); + auto cCollect = cn.garbageCollect(force); - // IMPORTANT clear all compute tables - clearComputeTables(); + // IMPORTANT clear all compute tables if anything has been collected + if (vCollect > 0 || mCollect > 0 || cCollect > 0) + clearComputeTables(); } void clearUniqueTables() { From 68f542f2bdafd943a722b9c38ce3e2c54e23b2b7 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Fri, 23 Apr 2021 17:08:05 +0200 Subject: [PATCH 30/48] Garbage Collection Improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ⚡ if any unique/complex table needs to be garbage collected, collect all of them at once in order to minimize compute table invalidations ⚡ adjust garbage collection limit depending on the number of remaining entries ⚡ reduce initial garbage collection limits 🔥 remove unused GC_INCREMENT Signed-off-by: Lukas Burgholzer --- include/dd/ComplexTable.hpp | 19 +++++++++++++++---- include/dd/Package.hpp | 16 +++++++++++++--- include/dd/UniqueTable.hpp | 17 ++++++++++++++--- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/include/dd/ComplexTable.hpp b/include/dd/ComplexTable.hpp index 518e8f97..a41f6422 100644 --- a/include/dd/ComplexTable.hpp +++ b/include/dd/ComplexTable.hpp @@ -19,7 +19,7 @@ #include namespace dd { - template + template class ComplexTable { public: struct Entry { @@ -244,9 +244,11 @@ namespace dd { } } + [[nodiscard]] bool needsCollection() const { return count >= gcLimit; } + std::size_t garbageCollect(bool force = false) { gcCalls++; - if (!force && count < gcLimit) + if ((!force && count < gcLimit) || count == 0) return 0; gcRuns++; @@ -273,7 +275,16 @@ namespace dd { } } } - gcLimit += GC_INCREMENT; + // The garbage collection limit changes dynamically depending on the number of remaining (active) nodes. + // If it were not changed, garbage collection would run through the complete table on each successive call + // once the number of remaining entries reaches the garbage collection limit. It is increased whenever the + // number of remaining entries is rather close to the garbage collection threshold and decreased if the + // number of remaining entries is much lower than the current limit. + if (remaining > gcLimit * 9 / 10) { + gcLimit = remaining + INITIAL_GC_LIMIT; + } else if (remaining < gcLimit / 16) { + gcLimit /= 8; + } count = remaining; return collected; } @@ -362,7 +373,7 @@ namespace dd { // garbage collection std::size_t gcCalls = 0; std::size_t gcRuns = 0; - std::size_t gcLimit = 250000; + std::size_t gcLimit = 100000; inline Entry* find(const Bucket& bucket, const fp& val) { auto p = bucket; diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index 8c89841b..0ddc94e0 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -509,9 +509,19 @@ namespace dd { UniqueTable mUniqueTable{nqubits}; void garbageCollect(bool force = false) { - auto vCollect = vUniqueTable.garbageCollect(force); - auto mCollect = mUniqueTable.garbageCollect(force); - auto cCollect = cn.garbageCollect(force); + // return immediately if no table needs collection + if (!force && + !vUniqueTable.needsCollection() && + !mUniqueTable.needsCollection() && + !cn.complexTable.needsCollection()) { + return; + } + + // if any table needs collection, enforce garbage collection on all of them + // this way compute tables are invalidated less frequently + auto vCollect = vUniqueTable.garbageCollect(true); + auto mCollect = mUniqueTable.garbageCollect(true); + auto cCollect = cn.garbageCollect(true); // IMPORTANT clear all compute tables if anything has been collected if (vCollect > 0 || mCollect > 0 || cCollect > 0) diff --git a/include/dd/UniqueTable.hpp b/include/dd/UniqueTable.hpp index b9c0888b..a01da9a4 100644 --- a/include/dd/UniqueTable.hpp +++ b/include/dd/UniqueTable.hpp @@ -30,7 +30,7 @@ namespace dd { /// \tparam GROWTH_PERCENTAGE percentage that the allocations' size shall grow over time /// \tparam INITIAL_GC_LIMIT number of nodes initially used as garbage collection threshold /// \tparam GC_INCREMENT absolute number of nodes to increase the garbage collection threshold after garbage collection has been performed - template + template class UniqueTable { public: explicit UniqueTable(std::size_t nvars): @@ -203,9 +203,11 @@ namespace dd { } } + [[nodiscard]] bool needsCollection() const { return nodeCount >= gcLimit; } + std::size_t garbageCollect(bool force = false) { gcCalls++; - if (!force && nodeCount < gcLimit) + if ((!force && nodeCount < gcLimit) || nodeCount == 0) return 0; gcRuns++; @@ -235,7 +237,16 @@ namespace dd { } } } - gcLimit += GC_INCREMENT; + // The garbage collection limit changes dynamically depending on the number of remaining (active) nodes. + // If it were not changed, garbage collection would run through the complete table on each successive call + // once the number of remaining entries reaches the garbage collection limit. It is increased whenever the + // number of remaining entries is rather close to the garbage collection threshold and decreased if the + // number of remaining entries is much lower than the current limit. + if (remaining > gcLimit * 9 / 10) { + gcLimit = remaining + INITIAL_GC_LIMIT; + } else if (remaining < gcLimit / 16) { + gcLimit /= 8; + } nodeCount = remaining; return collected; } From 680b70ed2698bd223d870a571f39549762e1d644 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Fri, 23 Apr 2021 17:17:32 +0200 Subject: [PATCH 31/48] =?UTF-8?q?=F0=9F=91=B7=20a=20rather=20optimistic=20?= =?UTF-8?q?try=20of=20using=20MSVC=20again=20under=20Windows=20instead=20o?= =?UTF-8?q?f=20Clang?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7695d6f8..58e8d510 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: shell: bash run: | if [ "$RUNNER_OS" == "Windows" ]; then - cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -T "ClangCl" + cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE elif [ "$RUNNER_OS" == "macOS" ]; then cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 else @@ -54,7 +54,7 @@ jobs: shell: bash run: | if [ "$RUNNER_OS" == "Windows" ]; then - cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -T "ClangCl" + cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE elif [ "$RUNNER_OS" == "macOS" ]; then cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 else From da2a948b52611261b40de93d835e78215026581a Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Fri, 23 Apr 2021 17:22:33 +0200 Subject: [PATCH 32/48] =?UTF-8?q?=F0=9F=8F=81=20revert=20to=20clang=20for?= =?UTF-8?q?=20windows=20CI=20=E2=9C=85=20small=20coverage=20increase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- .github/workflows/ci.yml | 4 ++-- test/test_package.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 58e8d510..7695d6f8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: shell: bash run: | if [ "$RUNNER_OS" == "Windows" ]; then - cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE + cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -T "ClangCl" elif [ "$RUNNER_OS" == "macOS" ]; then cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 else @@ -54,7 +54,7 @@ jobs: shell: bash run: | if [ "$RUNNER_OS" == "Windows" ]; then - cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE + cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -T "ClangCl" elif [ "$RUNNER_OS" == "macOS" ]; then cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 else diff --git a/test/test_package.cpp b/test/test_package.cpp index 6cd6c306..f5d2bb07 100644 --- a/test/test_package.cpp +++ b/test/test_package.cpp @@ -544,7 +544,7 @@ TEST(DDPackageTest, Inverse) { auto x = dd->makeGateDD(dd::Xmat, 1, 0); auto xdag = dd->conjugateTranspose(x); EXPECT_EQ(x, xdag); - dd->garbageCollect(true); + dd->garbageCollect(); } TEST(DDPackageTest, UniqueTableAllocation) { From 76d6c48a43f216a1f323fec92481ef6c4109de29 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sun, 25 Apr 2021 01:23:50 +0200 Subject: [PATCH 33/48] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor=20all=20has?= =?UTF-8?q?h=20functions=20in=20order=20to=20avoid=20standard=20library=20?= =?UTF-8?q?dependence.=20llvm=20stdlib=20used=20IEEE=20FP=20representation?= =?UTF-8?q?=20as=20hash=20for=20double,=20which=20turns=20out=20to=20be=20?= =?UTF-8?q?a=20bad=20idea.=20So=20is=20hashing=20floating=20points=20in=20?= =?UTF-8?q?general.=20=E2=9A=A1=20further=20tweak=20garbage=20collection?= =?UTF-8?q?=20=E2=9A=A1=20reduce=20default=20size=20of=20some=20compute=20?= =?UTF-8?q?tables=20in=20order=20to=20conserve=20space=20=F0=9F=8D=8E=20re?= =?UTF-8?q?vert=20to=20using=20Clang=20for=20CI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- .github/workflows/ci.yml | 4 --- CMakeLists.txt | 4 --- README.md | 2 -- include/dd/Complex.hpp | 6 ++-- include/dd/ComplexTable.hpp | 6 ++-- include/dd/ComplexValue.hpp | 6 ++-- include/dd/ComputeTable.hpp | 2 +- include/dd/Definitions.hpp | 14 +++++++++ include/dd/Edge.hpp | 8 ++--- include/dd/Package.hpp | 61 ++++++++++++++++++++++++++----------- include/dd/UniqueTable.hpp | 19 ++++++------ 11 files changed, 82 insertions(+), 50 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7695d6f8..6769b43f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,8 +27,6 @@ jobs: run: | if [ "$RUNNER_OS" == "Windows" ]; then cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -T "ClangCl" - elif [ "$RUNNER_OS" == "macOS" ]; then - cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 else cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE fi @@ -55,8 +53,6 @@ jobs: run: | if [ "$RUNNER_OS" == "Windows" ]; then cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -T "ClangCl" - elif [ "$RUNNER_OS" == "macOS" ]; then - cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 else cmake -S "${{github.workspace}}" -B "${{github.workspace}}/build" -DCMAKE_BUILD_TYPE=$BUILD_TYPE fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ca4b9ac..85611503 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,10 +24,6 @@ if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif () -if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang" AND ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - message(WARNING "Using (Apple)Clang under macOS may lead to severe performance degradation. Consider using gcc to compile the project!") -endif () - # add main library code include(GNUInstallDirs) add_library(${PROJECT_NAME} INTERFACE) diff --git a/README.md b/README.md index a1e51520..05f7c8b0 100755 --- a/README.md +++ b/README.md @@ -48,8 +48,6 @@ For implementing more complex functionality which requires garbage collection, b Building (and running) is continuously tested under Linux, MacOS, and Windows using the [latest available system versions for GitHub Actions](https://github.com/actions/virtual-environments). However, the implementation should be compatible with any current C++ compiler supporting C++17 and a minimum CMake version of 3.14. -Note that using (Apple)Clang to compile the library under macOS might lead to severe performance degradation. Consider using gcc to compile the project! - It is recommended (although not required) to have [GraphViz](https://www.graphviz.org) installed for visualization purposes. ### Setup, Build, and Run diff --git a/include/dd/Complex.hpp b/include/dd/Complex.hpp index 47c55640..efaeb939 100644 --- a/include/dd/Complex.hpp +++ b/include/dd/Complex.hpp @@ -88,9 +88,9 @@ namespace std { template<> struct hash { std::size_t operator()(dd::Complex const& c) const noexcept { - auto h1 = std::hash::Entry*>{}(c.r); - auto h2 = std::hash::Entry*>{}(c.i); - return h1 ^ (h2 << 1); + auto h1 = dd::murmur64(reinterpret_cast(c.r)); + auto h2 = dd::murmur64(reinterpret_cast(c.i)); + return dd::combineHash(h1, h2); } }; } // namespace std diff --git a/include/dd/ComplexTable.hpp b/include/dd/ComplexTable.hpp index a41f6422..cce5cd30 100644 --- a/include/dd/ComplexTable.hpp +++ b/include/dd/ComplexTable.hpp @@ -19,7 +19,7 @@ #include namespace dd { - template + template class ComplexTable { public: struct Entry { @@ -282,8 +282,8 @@ namespace dd { // number of remaining entries is much lower than the current limit. if (remaining > gcLimit * 9 / 10) { gcLimit = remaining + INITIAL_GC_LIMIT; - } else if (remaining < gcLimit / 16) { - gcLimit /= 8; + } else if (remaining < gcLimit / 128) { + gcLimit /= 2; } count = remaining; return collected; diff --git a/include/dd/ComplexValue.hpp b/include/dd/ComplexValue.hpp index 99db69c5..f0b7b457 100644 --- a/include/dd/ComplexValue.hpp +++ b/include/dd/ComplexValue.hpp @@ -220,9 +220,9 @@ namespace std { template<> struct hash { std::size_t operator()(dd::ComplexValue const& c) const noexcept { - auto h1 = std::hash{}(c.r); - auto h2 = std::hash{}(c.i); - return h1 ^ (h2 << 1); + auto h1 = dd::murmur64(static_cast(std::round(c.r * 1000))); + auto h2 = dd::murmur64(static_cast(std::round(c.i * 1000))); + return dd::combineHash(h1, h2); } }; } // namespace std diff --git a/include/dd/ComputeTable.hpp b/include/dd/ComputeTable.hpp index a8e07bfb..28d7d251 100644 --- a/include/dd/ComputeTable.hpp +++ b/include/dd/ComputeTable.hpp @@ -36,7 +36,7 @@ namespace dd { static std::size_t hash(const LeftOperandType& leftOperand, const RightOperandType& rightOperand) { const auto h1 = std::hash{}(leftOperand); const auto h2 = std::hash{}(rightOperand); - const auto hash = h1 ^ (h2 << 1); + const auto hash = dd::combineHash(h1, h2); return hash & MASK; } diff --git a/include/dd/Definitions.hpp b/include/dd/Definitions.hpp index f768e205..f298658a 100644 --- a/include/dd/Definitions.hpp +++ b/include/dd/Definitions.hpp @@ -54,5 +54,19 @@ namespace dd { static constexpr std::uint_least64_t SERIALIZATION_VERSION = 1; + constexpr std::size_t murmur64(std::size_t k) { + k ^= k >> 33; + k *= 0xff51afd7ed558ccdUL; + k ^= k >> 33; + k *= 0xc4ceb9fe1a85ec53UL; + k ^= k >> 33; + return k; + } + + constexpr std::size_t combineHash(std::size_t lhs, std::size_t rhs) { + lhs ^= rhs + 0x9e3779b97f4a7c15 + (lhs << 6) + (lhs >> 2); + return lhs; + } + } // namespace dd #endif //DDpackage_DATATYPES_HPP diff --git a/include/dd/Edge.hpp b/include/dd/Edge.hpp index cd0bd932..0fdc27f3 100644 --- a/include/dd/Edge.hpp +++ b/include/dd/Edge.hpp @@ -69,18 +69,18 @@ namespace std { template struct hash> { std::size_t operator()(dd::Edge const& e) const noexcept { - auto h1 = std::hash{}(e.p); + auto h1 = dd::murmur64(reinterpret_cast(e.p)); auto h2 = std::hash{}(e.w); - return h1 ^ (h2 << 1); + return dd::combineHash(h1, h2); } }; template struct hash> { std::size_t operator()(dd::CachedEdge const& e) const noexcept { - auto h1 = std::hash{}(e.p); + auto h1 = dd::murmur64(reinterpret_cast(e.p)); auto h2 = std::hash{}(e.w); - return h1 ^ (h2 << 1); + return dd::combineHash(h1, h2); } }; } // namespace std diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index 0ddc94e0..bec621ef 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -517,15 +517,42 @@ namespace dd { return; } - // if any table needs collection, enforce garbage collection on all of them - // this way compute tables are invalidated less frequently - auto vCollect = vUniqueTable.garbageCollect(true); - auto mCollect = mUniqueTable.garbageCollect(true); - auto cCollect = cn.garbageCollect(true); - - // IMPORTANT clear all compute tables if anything has been collected - if (vCollect > 0 || mCollect > 0 || cCollect > 0) - clearComputeTables(); + auto vCollect = vUniqueTable.garbageCollect(force); + auto mCollect = mUniqueTable.garbageCollect(force); + auto cCollect = cn.garbageCollect(force); + + // invalidate all compute tables involving vectors if any vector node has been collected + if (vCollect > 0) { + vectorAdd.clear(); + vectorInnerProduct.clear(); + vectorKronecker.clear(); + matrixVectorMultiplication.clear(); + } + // invalidate all compute tables involving matrices if any matrix node has been collected + if (mCollect > 0) { + matrixAdd.clear(); + matrixTranspose.clear(); + conjugateMatrixTranspose.clear(); + matrixKronecker.clear(); + matrixVectorMultiplication.clear(); + matrixMatrixMultiplication.clear(); + toffoliTable.clear(); + clearIdentityTable(); + noiseOperationTable.clear(); + } + // invalidate all compute tables where any component of the entry contains numbers from the complex table if any complex numbers were collected + if (cCollect > 0) { + matrixVectorMultiplication.clear(); + matrixMatrixMultiplication.clear(); + matrixTranspose.clear(); + conjugateMatrixTranspose.clear(); + vectorInnerProduct.clear(); + vectorKronecker.clear(); + matrixKronecker.clear(); + toffoliTable.clear(); + clearIdentityTable(); + noiseOperationTable.clear(); + } } void clearUniqueTables() { @@ -708,8 +735,8 @@ namespace dd { /// Matrix (conjugate) transpose /// public: - UnaryComputeTable matrixTranspose{}; - UnaryComputeTable conjugateMatrixTranspose{}; + UnaryComputeTable matrixTranspose{}; + UnaryComputeTable conjugateMatrixTranspose{}; mEdge transpose(const mEdge& a) { if (a.p == nullptr || a.isTerminal() || a.p->symm) { @@ -949,7 +976,7 @@ namespace dd { /// Inner product and fidelity /// public: - ComputeTable vectorInnerProduct{}; + ComputeTable vectorInnerProduct{}; ComplexValue innerProduct(const vEdge& x, const vEdge& y) { if (x.p == nullptr || y.p == nullptr || x.w.approximatelyZero() || y.w.approximatelyZero()) { // the 0 case @@ -1034,11 +1061,11 @@ namespace dd { /// Kronecker/tensor product /// public: - ComputeTable vectorKronecker{}; - ComputeTable matrixKronecker{}; + ComputeTable vectorKronecker{}; + ComputeTable matrixKronecker{}; template - [[nodiscard]] ComputeTable, Edge, CachedEdge>& getKroneckerComputeTable(); + [[nodiscard]] ComputeTable, Edge, CachedEdge, 4096>& getKroneckerComputeTable(); template Edge kronecker(const Edge& x, const Edge& y) { @@ -2070,9 +2097,9 @@ namespace dd { [[nodiscard]] inline ComputeTable& Package::getMultiplicationComputeTable() { return matrixMatrixMultiplication; } template<> - [[nodiscard]] inline ComputeTable& Package::getKroneckerComputeTable() { return vectorKronecker; } + [[nodiscard]] inline ComputeTable& Package::getKroneckerComputeTable() { return vectorKronecker; } template<> - [[nodiscard]] inline ComputeTable& Package::getKroneckerComputeTable() { return matrixKronecker; } + [[nodiscard]] inline ComputeTable& Package::getKroneckerComputeTable() { return matrixKronecker; } } // namespace dd #endif diff --git a/include/dd/UniqueTable.hpp b/include/dd/UniqueTable.hpp index a01da9a4..2d60c91e 100644 --- a/include/dd/UniqueTable.hpp +++ b/include/dd/UniqueTable.hpp @@ -30,7 +30,7 @@ namespace dd { /// \tparam GROWTH_PERCENTAGE percentage that the allocations' size shall grow over time /// \tparam INITIAL_GC_LIMIT number of nodes initially used as garbage collection threshold /// \tparam GC_INCREMENT absolute number of nodes to increase the garbage collection threshold after garbage collection has been performed - template + template class UniqueTable { public: explicit UniqueTable(std::size_t nvars): @@ -56,14 +56,15 @@ namespace dd { } static std::size_t hash(const Node* p) { - std::uintptr_t key = 0; + std::size_t key = 0; for (std::size_t i = 0; i < p->e.size(); ++i) { - key += ((reinterpret_cast(p->e[i].p) >> i) + - (reinterpret_cast(p->e[i].w.r) >> i) + - (reinterpret_cast(p->e[i].w.i) >> (i + 1))) & - MASK; - key &= MASK; + key = dd::combineHash(key, std::hash>{}(p->e[i])); + // old hash function: + // key += ((reinterpret_cast(p->e[i].p) >> i) + + // (reinterpret_cast(p->e[i].w.r) >> i) + + // (reinterpret_cast(p->e[i].w.i) >> (i + 1))) & MASK; } + key &= MASK; return key; } @@ -244,8 +245,8 @@ namespace dd { // number of remaining entries is much lower than the current limit. if (remaining > gcLimit * 9 / 10) { gcLimit = remaining + INITIAL_GC_LIMIT; - } else if (remaining < gcLimit / 16) { - gcLimit /= 8; + } else if (remaining < gcLimit / 32) { + gcLimit /= 4; } nodeCount = remaining; return collected; From ca6faf6fc612b82992b6293cd7fce0d69315c341 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sun, 25 Apr 2021 01:33:51 +0200 Subject: [PATCH 34/48] =?UTF-8?q?=F0=9F=94=A5=20(temporarily)=20remove=20t?= =?UTF-8?q?ests=20that=20do=20not=20work=20any=20more=20because=20of=20the?= =?UTF-8?q?=20changed=20hash=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- test/test_package.cpp | 124 +++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/test/test_package.cpp b/test/test_package.cpp index f5d2bb07..f5670e8a 100644 --- a/test/test_package.cpp +++ b/test/test_package.cpp @@ -493,42 +493,42 @@ TEST(DDPackageTest, InvalidDecRef) { EXPECT_THROW(dd->decRef(e), std::runtime_error); } -TEST(DDPackageTest, PackageReset) { - auto dd = std::make_unique(1); - - // one node in unique table of variable 0 - auto i_gate = dd->makeIdent(1); - const auto& unique = dd->mUniqueTable.getTables(); - const auto& table = unique[0]; - auto ihash = dd->mUniqueTable.hash(i_gate.p); - const auto* node = table[ihash]; - std::cout << ihash << ": " << reinterpret_cast(i_gate.p) << std::endl; - // node should be the first in this unique table bucket - EXPECT_EQ(node, i_gate.p); - dd->reset(); - // after clearing the tables, they should be empty - EXPECT_EQ(table[ihash], nullptr); - i_gate = dd->makeIdent(1); - const auto* node2 = table[ihash]; - // after recreating the DD, it should receive the same node - EXPECT_EQ(node2, node); - - // two nodes in same unique table bucket of variable 0 - auto z_gate = dd->makeGateDD(dd::Zmat, 1, 0); - auto zhash = dd->mUniqueTable.hash(z_gate.p); - std::cout << zhash << ": " << reinterpret_cast(z_gate.p) << std::endl; - // both nodes should reside in the same bucket - EXPECT_EQ(table[ihash], z_gate.p); - EXPECT_EQ(table[ihash]->next, i_gate.p); - dd->reset(); - // after clearing the tables, they should be empty - EXPECT_EQ(table[ihash], nullptr); - auto z_gate2 = dd->makeGateDD(dd::Zmat, 1, 0); - auto i_gate2 = dd->makeIdent(1); - // recreating the decision diagrams in reverse order should use the same pointers as before - EXPECT_EQ(z_gate2.p, i_gate.p); - EXPECT_EQ(i_gate2.p, z_gate.p); -} +//TEST(DDPackageTest, PackageReset) { +// auto dd = std::make_unique(1); +// +// // one node in unique table of variable 0 +// auto i_gate = dd->makeIdent(1); +// const auto& unique = dd->mUniqueTable.getTables(); +// const auto& table = unique[0]; +// auto ihash = dd->mUniqueTable.hash(i_gate.p); +// const auto* node = table[ihash]; +// std::cout << ihash << ": " << reinterpret_cast(i_gate.p) << std::endl; +// // node should be the first in this unique table bucket +// EXPECT_EQ(node, i_gate.p); +// dd->reset(); +// // after clearing the tables, they should be empty +// EXPECT_EQ(table[ihash], nullptr); +// i_gate = dd->makeIdent(1); +// const auto* node2 = table[ihash]; +// // after recreating the DD, it should receive the same node +// EXPECT_EQ(node2, node); +// +// // two nodes in same unique table bucket of variable 0 +// auto z_gate = dd->makeGateDD(dd::Zmat, 1, 0); +// auto zhash = dd->mUniqueTable.hash(z_gate.p); +// std::cout << zhash << ": " << reinterpret_cast(z_gate.p) << std::endl; +// // both nodes should reside in the same bucket +// EXPECT_EQ(table[ihash], z_gate.p); +// EXPECT_EQ(table[ihash]->next, i_gate.p); +// dd->reset(); +// // after clearing the tables, they should be empty +// EXPECT_EQ(table[ihash], nullptr); +// auto z_gate2 = dd->makeGateDD(dd::Zmat, 1, 0); +// auto i_gate2 = dd->makeIdent(1); +// // recreating the decision diagrams in reverse order should use the same pointers as before +// EXPECT_EQ(z_gate2.p, i_gate.p); +// EXPECT_EQ(i_gate2.p, z_gate.p); +//} TEST(DDPackageTest, MaxRefCount) { auto dd = std::make_unique(1); @@ -608,32 +608,32 @@ TEST(DDPackageTest, SpecialCaseTerminal) { EXPECT_EQ(dd->getValueByPath(dd::Package::mEdge::one, 0, 0), cOne); } -TEST(DDPackageTest, GarbageCollectSomeButNotAll) { - auto dd = std::make_unique(1); - - // one node in unique table of variable 0 - const auto& unique = dd->mUniqueTable.getTables(); - const auto& table = unique[0]; - - auto I = dd->makeIdent(1); - auto Ihash = dd->mUniqueTable.hash(I.p); - - // two nodes in same unique table bucket of variable 0 - auto Z = dd->makeGateDD(dd::Zmat, 1, 0); - auto Zhash = dd->mUniqueTable.hash(Z.p); - - // I and Z should be placed in the same bucket - EXPECT_EQ(Ihash, Zhash); - - // increase the reference count of the Z gate, but not the I gate - dd->incRef(Z); - - // garbage collection should only collect the I gate and leave the Z gate at the front of the bucket - dd->garbageCollect(true); - - EXPECT_EQ(table[Zhash], Z.p); - EXPECT_EQ(table[Zhash]->next, nullptr); -} +//TEST(DDPackageTest, GarbageCollectSomeButNotAll) { +// auto dd = std::make_unique(1); +// +// // one node in unique table of variable 0 +// const auto& unique = dd->mUniqueTable.getTables(); +// const auto& table = unique[0]; +// +// auto I = dd->makeIdent(1); +// auto Ihash = dd->mUniqueTable.hash(I.p); +// +// // two nodes in same unique table bucket of variable 0 +// auto Z = dd->makeGateDD(dd::Zmat, 1, 0); +// auto Zhash = dd->mUniqueTable.hash(Z.p); +// +// // I and Z should be placed in the same bucket +// EXPECT_EQ(Ihash, Zhash); +// +// // increase the reference count of the Z gate, but not the I gate +// dd->incRef(Z); +// +// // garbage collection should only collect the I gate and leave the Z gate at the front of the bucket +// dd->garbageCollect(true); +// +// EXPECT_EQ(table[Zhash], Z.p); +// EXPECT_EQ(table[Zhash]->next, nullptr); +//} TEST(DDPackageTest, KroneckerProduct) { auto dd = std::make_unique(2); From b29a7ef5324644651f93970f40e14d249241f6e6 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sun, 25 Apr 2021 17:58:57 +0200 Subject: [PATCH 35/48] =?UTF-8?q?=E2=9C=85=20increase=20coverage=20?= =?UTF-8?q?=F0=9F=92=A1=20documentation=20for=20hash=20functions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/Definitions.hpp | 19 +++++++++++-- test/test_package.cpp | 56 ++++++++++++++------------------------ 2 files changed, 36 insertions(+), 39 deletions(-) diff --git a/include/dd/Definitions.hpp b/include/dd/Definitions.hpp index f298658a..da6ed3ff 100644 --- a/include/dd/Definitions.hpp +++ b/include/dd/Definitions.hpp @@ -54,19 +54,32 @@ namespace dd { static constexpr std::uint_least64_t SERIALIZATION_VERSION = 1; + // 64bit mixing hash (from MurmurHash3, https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp) constexpr std::size_t murmur64(std::size_t k) { k ^= k >> 33; - k *= 0xff51afd7ed558ccdUL; + k *= 0xff51afd7ed558ccdULL; k ^= k >> 33; - k *= 0xc4ceb9fe1a85ec53UL; + k *= 0xc4ceb9fe1a85ec53ULL; k ^= k >> 33; return k; } + // combine two 64bit hashes into one 64bit hash (boost::hash_combine, https://www.boost.org/LICENSE_1_0.txt) constexpr std::size_t combineHash(std::size_t lhs, std::size_t rhs) { - lhs ^= rhs + 0x9e3779b97f4a7c15 + (lhs << 6) + (lhs >> 2); + lhs ^= rhs + 0x9e3779b97f4a7c15ULL + (lhs << 6) + (lhs >> 2); return lhs; } + // // alternative hash combinator (from Google's city hash, https://github.com/google/cityhash/blob/master/COPYING) + // constexpr std::size_t hash128to64(std::size_t lhs, std::size_t rhs) { + // const std::size_t kMul = 0x9ddfea08eb382d69ULL; + // std::size_t a = (lhs ^ rhs) * kMul; + // a ^= (a >> 47); + // std::size_t b = (rhs ^ a) * kMul; + // b ^= (b >> 47); + // b *= kMul; + // return b; + // } + } // namespace dd #endif //DDpackage_DATATYPES_HPP diff --git a/test/test_package.cpp b/test/test_package.cpp index f5670e8a..eab7299a 100644 --- a/test/test_package.cpp +++ b/test/test_package.cpp @@ -493,42 +493,26 @@ TEST(DDPackageTest, InvalidDecRef) { EXPECT_THROW(dd->decRef(e), std::runtime_error); } -//TEST(DDPackageTest, PackageReset) { -// auto dd = std::make_unique(1); -// -// // one node in unique table of variable 0 -// auto i_gate = dd->makeIdent(1); -// const auto& unique = dd->mUniqueTable.getTables(); -// const auto& table = unique[0]; -// auto ihash = dd->mUniqueTable.hash(i_gate.p); -// const auto* node = table[ihash]; -// std::cout << ihash << ": " << reinterpret_cast(i_gate.p) << std::endl; -// // node should be the first in this unique table bucket -// EXPECT_EQ(node, i_gate.p); -// dd->reset(); -// // after clearing the tables, they should be empty -// EXPECT_EQ(table[ihash], nullptr); -// i_gate = dd->makeIdent(1); -// const auto* node2 = table[ihash]; -// // after recreating the DD, it should receive the same node -// EXPECT_EQ(node2, node); -// -// // two nodes in same unique table bucket of variable 0 -// auto z_gate = dd->makeGateDD(dd::Zmat, 1, 0); -// auto zhash = dd->mUniqueTable.hash(z_gate.p); -// std::cout << zhash << ": " << reinterpret_cast(z_gate.p) << std::endl; -// // both nodes should reside in the same bucket -// EXPECT_EQ(table[ihash], z_gate.p); -// EXPECT_EQ(table[ihash]->next, i_gate.p); -// dd->reset(); -// // after clearing the tables, they should be empty -// EXPECT_EQ(table[ihash], nullptr); -// auto z_gate2 = dd->makeGateDD(dd::Zmat, 1, 0); -// auto i_gate2 = dd->makeIdent(1); -// // recreating the decision diagrams in reverse order should use the same pointers as before -// EXPECT_EQ(z_gate2.p, i_gate.p); -// EXPECT_EQ(i_gate2.p, z_gate.p); -//} +TEST(DDPackageTest, PackageReset) { + auto dd = std::make_unique(1); + + // one node in unique table of variable 0 + auto i_gate = dd->makeIdent(1); + const auto& unique = dd->mUniqueTable.getTables(); + const auto& table = unique[0]; + auto ihash = dd->mUniqueTable.hash(i_gate.p); + const auto* node = table[ihash]; + std::cout << ihash << ": " << reinterpret_cast(i_gate.p) << std::endl; + // node should be the first in this unique table bucket + EXPECT_EQ(node, i_gate.p); + dd->reset(); + // after clearing the tables, they should be empty + EXPECT_EQ(table[ihash], nullptr); + i_gate = dd->makeIdent(1); + const auto* node2 = table[ihash]; + // after recreating the DD, it should receive the same node + EXPECT_EQ(node2, node); +} TEST(DDPackageTest, MaxRefCount) { auto dd = std::make_unique(1); From 9a1b19f23bc38a4543c4dc1536e4bb6d8ec35807 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sun, 25 Apr 2021 18:02:57 +0200 Subject: [PATCH 36/48] =?UTF-8?q?=E2=9A=97=EF=B8=8F=20try=20if=20disabling?= =?UTF-8?q?=20standard=20extensions=20caused=20benchmark=20performance=20t?= =?UTF-8?q?o=20be=20slightly=20altered?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1ede0b30..1a0749f6 100755 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -59,7 +59,7 @@ add_test(NAME ${PROJECT_NAME}_example COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${PROJ add_executable(${PROJECT_NAME}_bench EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/bench_package.cpp) # link the Google benchmark infrastructure and a default main fuction to the benchmark executable. target_link_libraries(${PROJECT_NAME}_bench PRIVATE ${PROJECT_NAME} benchmark::benchmark_main Threads::Threads) -set_target_properties(${PROJECT_NAME}_bench PROPERTIES FOLDER tests CMAKE_CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +set_target_properties(${PROJECT_NAME}_bench PROPERTIES FOLDER tests CMAKE_CXX_STANDARD_REQUIRED ON) enable_lto(${PROJECT_NAME}_bench) add_custom_command(TARGET ${PROJECT_NAME}_bench From 47eade540ae79c0e11695809ea1a3d58230568ba Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sun, 25 Apr 2021 18:23:31 +0200 Subject: [PATCH 37/48] =?UTF-8?q?=E2=9A=97=EF=B8=8F=20allowing=20some=20up?= =?UTF-8?q?=20and=20downs=20in=20project=20coverage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- .github/codecov.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/codecov.yml b/.github/codecov.yml index 76d15e5e..e645efc6 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -4,5 +4,12 @@ ignore: - "test/bench_package.cpp" coverage: - range: 60..90 + range: 60..90 precision: 1 + status: + project: + default: + threshold: 0.5% + patch: + default: + threshold: 1% From 89eee180b2513306956c46027bacf4e4017253a4 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sun, 25 Apr 2021 18:25:29 +0200 Subject: [PATCH 38/48] =?UTF-8?q?=E2=8F=AA=20standard=20extensions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1a0749f6..1ede0b30 100755 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -59,7 +59,7 @@ add_test(NAME ${PROJECT_NAME}_example COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${PROJ add_executable(${PROJECT_NAME}_bench EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/bench_package.cpp) # link the Google benchmark infrastructure and a default main fuction to the benchmark executable. target_link_libraries(${PROJECT_NAME}_bench PRIVATE ${PROJECT_NAME} benchmark::benchmark_main Threads::Threads) -set_target_properties(${PROJECT_NAME}_bench PROPERTIES FOLDER tests CMAKE_CXX_STANDARD_REQUIRED ON) +set_target_properties(${PROJECT_NAME}_bench PROPERTIES FOLDER tests CMAKE_CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) enable_lto(${PROJECT_NAME}_bench) add_custom_command(TARGET ${PROJECT_NAME}_bench From 39775210e430753734a95818c834bdafb339c014 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sun, 25 Apr 2021 18:57:14 +0200 Subject: [PATCH 39/48] =?UTF-8?q?=E2=9C=85=20increase=20coverage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- test/test_package.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/test_package.cpp b/test/test_package.cpp index eab7299a..38eb087f 100644 --- a/test/test_package.cpp +++ b/test/test_package.cpp @@ -51,6 +51,8 @@ TEST(DDPackageTest, TrivialTest) { auto h_state = dd->multiply(h_gate, zero_state); auto one_state = dd->multiply(x_gate, zero_state); + ASSERT_EQ(dd->fidelity(zero_state, one_state), 0.0); + // repeat the same calculation - triggering compute table hit ASSERT_EQ(dd->fidelity(zero_state, one_state), 0.0); ASSERT_NEAR(dd->fidelity(zero_state, h_state), 0.5, dd::ComplexTable<>::tolerance()); ASSERT_NEAR(dd->fidelity(one_state, h_state), 0.5, dd::ComplexTable<>::tolerance()); @@ -590,6 +592,9 @@ TEST(DDPackageTest, SpecialCaseTerminal) { EXPECT_EQ(dd->getValueByPath(one, ""), cOne); EXPECT_EQ(dd->getValueByPath(one, 0), cOne); EXPECT_EQ(dd->getValueByPath(dd::Package::mEdge::one, 0, 0), cOne); + + dd::ComplexValue cZero{0.0, 0.0}; + EXPECT_EQ(dd->innerProduct(zero, zero), cZero); } //TEST(DDPackageTest, GarbageCollectSomeButNotAll) { From 26d46a4448dd8d919364223f94f92d4f95211cae Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sun, 25 Apr 2021 19:41:15 +0200 Subject: [PATCH 40/48] =?UTF-8?q?=E2=9C=85=20increase=20coverage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/ComplexTable.hpp | 3 ++- test/test_complex.cpp | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/include/dd/ComplexTable.hpp b/include/dd/ComplexTable.hpp index cce5cd30..d94a8cd1 100644 --- a/include/dd/ComplexTable.hpp +++ b/include/dd/ComplexTable.hpp @@ -124,6 +124,7 @@ namespace dd { [[nodiscard]] std::size_t getAllocations() const { return allocations; } [[nodiscard]] std::size_t getGrowthFactor() const { return GROWTH_FACTOR; } [[nodiscard]] const auto& getTable() const { return table; } + [[nodiscard]] bool availableEmpty() const { return available == nullptr; }; Entry* lookup(const fp& val) { assert(!std::isnan(val)); @@ -179,7 +180,7 @@ namespace dd { [[nodiscard]] Entry* getEntry() { // an entry is available on the stack - if (available != nullptr) { + if (!availableEmpty()) { Entry* entry = available; available = entry->next; // returned entries could have a ref count != 0 diff --git a/test/test_complex.cpp b/test/test_complex.cpp index 250e5336..580eb4c9 100644 --- a/test/test_complex.cpp +++ b/test/test_complex.cpp @@ -34,6 +34,9 @@ TEST(DDComplexTest, TrivialTest) { // since lookup does not increase the ref count, garbage collection removes the new values unsigned int end_count = cn->cacheCount(); ASSERT_EQ(before_count, end_count); + + EXPECT_NO_THROW(cn->incRef({nullptr, nullptr})); + EXPECT_NO_THROW(cn->decRef({nullptr, nullptr})); } TEST(DDComplexTest, ComplexNumberCreation) { @@ -286,6 +289,16 @@ TEST(DDComplexTest, ComplexTableAllocation) { // clearing the complex table should reduce the allocated size to the original size cn.complexTable.clear(); EXPECT_EQ(cn.complexTable.getAllocations(), allocs); + + EXPECT_TRUE(cn.complexTable.availableEmpty()); + // obtain entry + auto entry = cn.complexTable.getEntry(); + // immediately return entry + cn.complexTable.returnEntry(entry); + EXPECT_FALSE(cn.complexTable.availableEmpty()); + // obtain the same entry again, but this time from the available stack + auto entry2 = cn.complexTable.getEntry(); + EXPECT_EQ(entry, entry2); } TEST(DDComplexTest, ComplexCacheAllocation) { From 3e644471247f097c9b5b97e654902e01659df8a4 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Mon, 26 Apr 2021 17:36:49 +0200 Subject: [PATCH 41/48] =?UTF-8?q?=F0=9F=90=9B=20fix=20reference=20counting?= =?UTF-8?q?=20bug=20in=20`reduceAncillary`=20and=20`reduceGarbage`=20?= =?UTF-8?q?=E2=9A=A1=20complex=20table=20hash=20now=20rounds=20instead=20o?= =?UTF-8?q?f=20truncates=20the=20calculated=20value=20=E2=9A=A1=20introduc?= =?UTF-8?q?e=20additional=201/sqrt(2)=20static=20member=20in=20complex=20t?= =?UTF-8?q?able=20=E2=9A=A1=F0=9F=90=9B=20always=20check=200,=201/sqrt(2),?= =?UTF-8?q?=20and=201=20first=20upon=20accessing=20the=20respective=20buck?= =?UTF-8?q?ets=20in=20the=20complex=20table?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/ComplexTable.hpp | 59 ++++++++++++++++++++++++++++--------- include/dd/Definitions.hpp | 4 +-- include/dd/Package.hpp | 22 +++++++------- test/test_complex.cpp | 2 +- test/test_package.cpp | 15 +++++++++- 5 files changed, 74 insertions(+), 28 deletions(-) diff --git a/include/dd/ComplexTable.hpp b/include/dd/ComplexTable.hpp index d94a8cd1..b0c6722c 100644 --- a/include/dd/ComplexTable.hpp +++ b/include/dd/ComplexTable.hpp @@ -78,6 +78,7 @@ namespace dd { }; static inline Entry zero{0., nullptr, 1}; + static inline Entry sqrt2_2{SQRT2_2, nullptr, 1}; static inline Entry one{1., nullptr, 1}; ComplexTable(): @@ -89,15 +90,15 @@ namespace dd { chunkIt = chunks[0].begin(); chunkEndIt = chunks[0].end(); - // emplace static zero and one in the table + // emplace static zero, 1/sqrt(2), and one in the table table[0] = &zero; + table[sqrt2_2Key] = &sqrt2_2; table[NBUCKET - 1] = &one; - count = 2; - peakCount = 2; + count = 3; + peakCount = 3; - // add 1/2 and 1/sqrt(2) to the complex table and increase their ref count (so that they are not collected) + // add 1/2 to the complex table and increase its ref count (so that it is not collected) lookup(0.5L)->refCount++; - lookup(SQRT2_2)->refCount++; } ~ComplexTable() = default; @@ -112,12 +113,16 @@ namespace dd { static constexpr std::size_t MASK = NBUCKET - 1; // linear (clipped) hash function - static std::size_t hash(const fp val) { + static constexpr std::size_t hash(const fp val) { assert(val >= 0); - auto key = static_cast(val * MASK); + auto key = static_cast(val * MASK + 0.5L); return std::min(key, MASK); } + static constexpr std::size_t zeroKey = hash(0.); + static constexpr std::size_t sqrt2_2Key = hash(SQRT2_2); + static constexpr std::size_t oneKey = hash(1.); + // access functions [[nodiscard]] std::size_t getCount() const { return count; } [[nodiscard]] std::size_t getPeakCount() const { return peakCount; } @@ -139,7 +144,32 @@ namespace dd { lookups++; // search in intended bucket - const auto key = hash(val); + const auto key = hash(val); + + // ensure that the exact value of important constants is checked first on all occasions + if (key == zeroKey) { + if (std::abs(val) < TOLERANCE) { + ++hits; + return &zero; + } else { + ++collisions; + } + } else if (key == oneKey) { + if (std::abs(val - 1.) < TOLERANCE) { + ++hits; + return &one; + } else { + ++collisions; + } + } else if (key == sqrt2_2Key) { + if (std::abs(val - SQRT2_2) < TOLERANCE) { + ++hits; + return &sqrt2_2; + } else { + ++collisions; + } + } + const auto& bucket = table[key]; auto p = find(bucket, val); if (p != nullptr) { @@ -216,8 +246,8 @@ namespace dd { // get valid pointer auto entryPtr = Entry::getAlignedPointer(entry); - // `zero` and `one` are static and never altered - if (entryPtr != &one && entryPtr != &zero) { + // important (static) numbers are never altered + if (entryPtr != &one && entryPtr != &zero && entryPtr != &sqrt2_2) { if (entryPtr->refCount == std::numeric_limits::max()) { std::clog << "[WARN] MAXREFCNT reached for " << entryPtr->value << ". Number will never be collected." << std::endl; return; @@ -236,8 +266,8 @@ namespace dd { // get valid pointer auto entryPtr = Entry::getAlignedPointer(entry); - // `zero` and `one` are static and never altered - if (entryPtr != &one && entryPtr != &zero) { + // important (static) numbers are never altered + if (entryPtr != &one && entryPtr != &zero && entryPtr != &sqrt2_2) { assert(entryPtr->refCount > 0); // decrease reference count @@ -326,10 +356,11 @@ namespace dd { for (std::size_t key = 0; key < table.size(); ++key) { auto& p = table[key]; if (p != nullptr) - std::cout << key << ": "; + std::cout << key << ": " + << "\n"; while (p != nullptr) { - std::cout << "\t\t" << reinterpret_cast(p) << " " << p->refCount << "\t"; + std::cout << "\t\t" << p->value << " " << reinterpret_cast(p) << " " << p->refCount << "\n"; p = p->next; } diff --git a/include/dd/Definitions.hpp b/include/dd/Definitions.hpp index da6ed3ff..ce0bf396 100644 --- a/include/dd/Definitions.hpp +++ b/include/dd/Definitions.hpp @@ -70,8 +70,8 @@ namespace dd { return lhs; } - // // alternative hash combinator (from Google's city hash, https://github.com/google/cityhash/blob/master/COPYING) - // constexpr std::size_t hash128to64(std::size_t lhs, std::size_t rhs) { + // alternative hash combinator (from Google's city hash, https://github.com/google/cityhash/blob/master/COPYING) + // constexpr std::size_t combineHash(std::size_t lhs, std::size_t rhs) { // const std::size_t kMul = 0x9ddfea08eb382d69ULL; // std::size_t a = (lhs ^ rhs) * kMul; // a ^= (a >> 47); diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index bec621ef..cfafc94f 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -1314,7 +1314,10 @@ namespace dd { } } if (e.p->v < lowerbound) return e; - return reduceAncillaeRecursion(e, ancillary, lowerbound, regular); + auto f = reduceAncillaeRecursion(e, ancillary, lowerbound, regular); + decRef(e); + incRef(f); + return f; } // Garbage reduction works for reversible circuits --- to be thoroughly tested for quantum circuits @@ -1329,7 +1332,10 @@ namespace dd { } } if (e.p->v < lowerbound) return e; - return reduceGarbageRecursion(e, garbage, lowerbound); + auto f = reduceGarbageRecursion(e, garbage, lowerbound); + decRef(e); + incRef(f); + return f; } mEdge reduceGarbage(mEdge& e, const std::vector& garbage, bool regular = true) { // return if no more garbage left @@ -1342,7 +1348,10 @@ namespace dd { } } if (e.p->v < lowerbound) return e; - return reduceGarbageRecursion(e, garbage, lowerbound, regular); + auto f = reduceGarbageRecursion(e, garbage, lowerbound, regular); + decRef(e); + incRef(f); + return f; } private: @@ -1387,9 +1396,6 @@ namespace dd { auto c = cn.mulCached(f.w, e.w); f.w = cn.lookup(c); cn.returnToCache(c); - - // increasing the ref count for safety. TODO: find out if this is necessary - incRef(f); return f; } @@ -1441,8 +1447,6 @@ namespace dd { if (ComplexNumbers::mag2(f.w) > 1.0) f.w = Complex::one; - // increasing the ref count for safety. TODO: find out if this is necessary - incRef(f); return f; } mEdge reduceGarbageRecursion(mEdge& e, const std::vector& garbage, Qubit lowerbound, bool regular = true) { @@ -1523,8 +1527,6 @@ namespace dd { if (ComplexNumbers::mag2(f.w) > 1.0) f.w = Complex::one; - // increasing the ref count for safety. TODO: find out if this is necessary - incRef(f); return f; } diff --git a/test/test_complex.cpp b/test/test_complex.cpp index 580eb4c9..f10ed0ef 100644 --- a/test/test_complex.cpp +++ b/test/test_complex.cpp @@ -130,7 +130,7 @@ TEST(DDComplexTest, LookupInNeighbouringBuckets) { constexpr std::size_t NBUCKET = ComplexTable<>::MASK + 1; // lower border of a bucket - fp bucketBorder = 0.25 * NBUCKET / (NBUCKET - 1); + fp bucketBorder = (0.25 * NBUCKET - 0.5) / (NBUCKET - 1); // insert a number slightly away from the border fp num = bucketBorder + 2 * ComplexTable<>::tolerance(); diff --git a/test/test_package.cpp b/test/test_package.cpp index 38eb087f..36602b7a 100644 --- a/test/test_package.cpp +++ b/test/test_package.cpp @@ -401,13 +401,16 @@ TEST(DDPackageTest, Ancillaries) { auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 0, 1); auto bell_matrix = dd->multiply(cx_gate, h_gate); + dd->incRef(bell_matrix); auto reduced_bell_matrix = dd->reduceAncillae(bell_matrix, {false, false, false, false}); EXPECT_EQ(bell_matrix, reduced_bell_matrix); + dd->incRef(bell_matrix); reduced_bell_matrix = dd->reduceAncillae(bell_matrix, {false, false, true, true}); EXPECT_EQ(bell_matrix, reduced_bell_matrix); auto extended_bell_matrix = dd->extend(bell_matrix, 2); - reduced_bell_matrix = dd->reduceAncillae(extended_bell_matrix, {false, false, true, true}); + dd->incRef(extended_bell_matrix); + reduced_bell_matrix = dd->reduceAncillae(extended_bell_matrix, {false, false, true, true}); EXPECT_TRUE(reduced_bell_matrix.p->e[1].isZeroTerminal()); EXPECT_TRUE(reduced_bell_matrix.p->e[2].isZeroTerminal()); EXPECT_TRUE(reduced_bell_matrix.p->e[3].isZeroTerminal()); @@ -417,6 +420,7 @@ TEST(DDPackageTest, Ancillaries) { EXPECT_TRUE(reduced_bell_matrix.p->e[0].p->e[2].isZeroTerminal()); EXPECT_TRUE(reduced_bell_matrix.p->e[0].p->e[3].isZeroTerminal()); + dd->incRef(extended_bell_matrix); reduced_bell_matrix = dd->reduceAncillae(extended_bell_matrix, {false, false, true, true}, false); EXPECT_TRUE(reduced_bell_matrix.p->e[1].isZeroTerminal()); EXPECT_TRUE(reduced_bell_matrix.p->e[2].isZeroTerminal()); @@ -436,11 +440,14 @@ TEST(DDPackageTest, GarbageVector) { auto bell_state = dd->multiply(dd->multiply(cx_gate, h_gate), zero_state); dd->printVector(bell_state); + dd->incRef(bell_state); auto reduced_bell_state = dd->reduceGarbage(bell_state, {false, false, false, false}); EXPECT_EQ(bell_state, reduced_bell_state); + dd->incRef(bell_state); reduced_bell_state = dd->reduceGarbage(bell_state, {false, false, true, false}); EXPECT_EQ(bell_state, reduced_bell_state); + dd->incRef(bell_state); reduced_bell_state = dd->reduceGarbage(bell_state, {false, true, false, false}); auto vec = dd->getVector(reduced_bell_state); dd->printVector(reduced_bell_state); @@ -448,6 +455,7 @@ TEST(DDPackageTest, GarbageVector) { EXPECT_EQ(vec[2], zero); EXPECT_EQ(vec[3], zero); + dd->incRef(bell_state); reduced_bell_state = dd->reduceGarbage(bell_state, {true, false, false, false}); dd->printVector(reduced_bell_state); vec = dd->getVector(reduced_bell_state); @@ -461,22 +469,27 @@ TEST(DDPackageTest, GarbageMatrix) { auto cx_gate = dd->makeGateDD(dd::Xmat, 2, 0, 1); auto bell_matrix = dd->multiply(cx_gate, h_gate); + dd->incRef(bell_matrix); auto reduced_bell_matrix = dd->reduceGarbage(bell_matrix, {false, false, false, false}); EXPECT_EQ(bell_matrix, reduced_bell_matrix); + dd->incRef(bell_matrix); reduced_bell_matrix = dd->reduceGarbage(bell_matrix, {false, false, true, false}); EXPECT_EQ(bell_matrix, reduced_bell_matrix); + dd->incRef(bell_matrix); reduced_bell_matrix = dd->reduceGarbage(bell_matrix, {false, true, false, false}); auto mat = dd->getMatrix(reduced_bell_matrix); auto zero = dd::CVec{{0., 0.}, {0., 0.}, {0., 0.}, {0., 0.}}; EXPECT_EQ(mat[2], zero); EXPECT_EQ(mat[3], zero); + dd->incRef(bell_matrix); reduced_bell_matrix = dd->reduceGarbage(bell_matrix, {true, false, false, false}); mat = dd->getMatrix(reduced_bell_matrix); EXPECT_EQ(mat[1], zero); EXPECT_EQ(mat[3], zero); + dd->incRef(bell_matrix); reduced_bell_matrix = dd->reduceGarbage(bell_matrix, {false, true, false, false}, false); EXPECT_TRUE(reduced_bell_matrix.p->e[1].isZeroTerminal()); EXPECT_TRUE(reduced_bell_matrix.p->e[3].isZeroTerminal()); From 0ced61b7e62e24f42cad4cca9be5aa133649b819 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Mon, 26 Apr 2021 17:48:09 +0200 Subject: [PATCH 42/48] fix global consistency check Signed-off-by: Lukas Burgholzer --- include/dd/Package.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index cfafc94f..c41fa895 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -1971,14 +1971,14 @@ namespace dd { auto* r_ptr = CTEntry::getAlignedPointer(edge.w.r); auto* i_ptr = CTEntry::getAlignedPointer(edge.w.i); - if (weight_map.at(r_ptr) > r_ptr->refCount && r_ptr != Complex::one.r && r_ptr != Complex::zero.i) { + if (weight_map.at(r_ptr) > r_ptr->refCount && r_ptr != Complex::one.r && r_ptr != Complex::zero.i && r_ptr != &ComplexTable<>::sqrt2_2) { std::clog << "\nOffending weight: " << edge.w << "\n"; std::clog << "Bits: " << std::hexfloat << CTEntry::val(edge.w.r) << " " << CTEntry::val(edge.w.i) << std::defaultfloat << "\n"; debugnode(edge.p); throw std::runtime_error("Ref-Count mismatch for " + std::to_string(r_ptr->value) + "(r): " + std::to_string(weight_map.at(r_ptr)) + " occurences in DD but Ref-Count is only " + std::to_string(r_ptr->refCount)); } - if (weight_map.at(i_ptr) > i_ptr->refCount && i_ptr != Complex::zero.i && i_ptr != Complex::one.r) { + if (weight_map.at(i_ptr) > i_ptr->refCount && i_ptr != Complex::zero.i && i_ptr != Complex::one.r && r_ptr != &ComplexTable<>::sqrt2_2) { std::clog << edge.w << "\n"; debugnode(edge.p); throw std::runtime_error("Ref-Count mismatch for " + std::to_string(i_ptr->value) + "(i): " + std::to_string(weight_map.at(i_ptr)) + " occurences in DD but Ref-Count is only " + std::to_string(i_ptr->refCount)); From 228ef414cc71c45662e53bbb2231170393a615cf Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Mon, 26 Apr 2021 18:51:44 +0200 Subject: [PATCH 43/48] =?UTF-8?q?=E2=9C=85=20increase=20coverage=20of=20`C?= =?UTF-8?q?ontrol`=20struct?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- test/test_package.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/test_package.cpp b/test/test_package.cpp index 36602b7a..e24187af 100644 --- a/test/test_package.cpp +++ b/test/test_package.cpp @@ -702,3 +702,16 @@ TEST(DDPackageTest, NearZeroNormalize) { auto meNormalized = dd->normalize(me, false); EXPECT_EQ(meNormalized, dd::Package::mEdge::zero); } + +TEST(DDPackageTest, Controls) { + dd::Control cpos{0}; + dd::Control cneg{0, dd::Control::Type::neg}; + + EXPECT_NE(cpos, cneg); + + dd::Controls controls{}; + controls.insert(cpos); + controls.insert(cneg); + EXPECT_EQ(controls.begin()->type, dd::Control::Type::neg); + EXPECT_EQ(controls.count(0), 2); +} From 4168e1a74ec22e8bc28ee2cabb46cb2d5ecffa5e Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Mon, 26 Apr 2021 18:58:18 +0200 Subject: [PATCH 44/48] =?UTF-8?q?=F0=9F=90=9B=20fix=20bug=20that=20cleared?= =?UTF-8?q?=20the=20unique=20and=20complex=20table=20upon=20printing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/ComplexTable.hpp | 2 +- include/dd/UniqueTable.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/dd/ComplexTable.hpp b/include/dd/ComplexTable.hpp index b0c6722c..32fc5e89 100644 --- a/include/dd/ComplexTable.hpp +++ b/include/dd/ComplexTable.hpp @@ -354,7 +354,7 @@ namespace dd { void print() { for (std::size_t key = 0; key < table.size(); ++key) { - auto& p = table[key]; + auto p = table[key]; if (p != nullptr) std::cout << key << ": " << "\n"; diff --git a/include/dd/UniqueTable.hpp b/include/dd/UniqueTable.hpp index 2d60c91e..58f586fa 100644 --- a/include/dd/UniqueTable.hpp +++ b/include/dd/UniqueTable.hpp @@ -296,7 +296,7 @@ namespace dd { std::cout << "\t" << static_cast(q) << ":" << "\n"; for (std::size_t key = 0; key < table.size(); ++key) { - auto& p = table[key]; + auto p = table[key]; if (p != nullptr) std::cout << key << ": "; From 73a8713524b588c75bdfe40316ded450e5d72edb Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Mon, 26 Apr 2021 19:07:10 +0200 Subject: [PATCH 45/48] =?UTF-8?q?=F0=9F=90=9B=20fix=20complex=20table=20ga?= =?UTF-8?q?rbage=20collection=20threshold=20for=20quickly=20returning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/ComplexTable.hpp | 4 +++- test/test_complex.cpp | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/dd/ComplexTable.hpp b/include/dd/ComplexTable.hpp index 32fc5e89..c1cb48e6 100644 --- a/include/dd/ComplexTable.hpp +++ b/include/dd/ComplexTable.hpp @@ -279,7 +279,9 @@ namespace dd { std::size_t garbageCollect(bool force = false) { gcCalls++; - if ((!force && count < gcLimit) || count == 0) + // nothing to be done if garbage collection is not forced and the limit has not been reached + // or the current count is minimal (the complex table always contains at least 0, 0.5, 1/sqrt(2), and 1) + if ((!force && count < gcLimit) || count <= 4) return 0; gcRuns++; diff --git a/test/test_complex.cpp b/test/test_complex.cpp index f10ed0ef..ddd20a7a 100644 --- a/test/test_complex.cpp +++ b/test/test_complex.cpp @@ -109,6 +109,7 @@ TEST(DDComplexTest, NearZeroLookup) { TEST(DDComplexTest, GarbageCollectSomeInBucket) { auto cn = ComplexNumbers(); + EXPECT_EQ(cn.garbageCollect(), 0); fp num = 0.25; cn.lookup(num, 0.0); From 9823975ff6f5fd6993668f5efa64394eda4ef756 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Mon, 26 Apr 2021 19:15:17 +0200 Subject: [PATCH 46/48] =?UTF-8?q?=E2=9C=85=20add=20test=20for=20unique=20t?= =?UTF-8?q?able=20garbage=20collection=20and=20compute=20table=20invalidat?= =?UTF-8?q?ion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- test/test_package.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/test_package.cpp b/test/test_package.cpp index e24187af..569f562c 100644 --- a/test/test_package.cpp +++ b/test/test_package.cpp @@ -544,6 +544,16 @@ TEST(DDPackageTest, Inverse) { auto xdag = dd->conjugateTranspose(x); EXPECT_EQ(x, xdag); dd->garbageCollect(); + // nothing should have been collected since the threshold is not reached + EXPECT_EQ(dd->mUniqueTable.getNodeCount(), 1); + dd->incRef(x); + dd->garbageCollect(true); + // nothing should have been collected since the lone node has a non-zero ref count + EXPECT_EQ(dd->mUniqueTable.getNodeCount(), 1); + dd->decRef(x); + dd->garbageCollect(true); + // now the node should have been collected + EXPECT_EQ(dd->mUniqueTable.getNodeCount(), 0); } TEST(DDPackageTest, UniqueTableAllocation) { From 0c56bca1e726949652a303b0d402e58a88dc1bbb Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Mon, 26 Apr 2021 19:52:22 +0200 Subject: [PATCH 47/48] =?UTF-8?q?=E2=9A=A1=20add=20`inline`=20specifier=20?= =?UTF-8?q?to=20some=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/dd/Complex.hpp | 10 +++++----- include/dd/ComplexValue.hpp | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/dd/Complex.hpp b/include/dd/Complex.hpp index efaeb939..6e40eedb 100644 --- a/include/dd/Complex.hpp +++ b/include/dd/Complex.hpp @@ -28,23 +28,23 @@ namespace dd { i->value = CTEntry::val(c.i); } - [[nodiscard]] bool approximatelyEquals(const Complex& c) const { + [[nodiscard]] inline bool approximatelyEquals(const Complex& c) const { return CTEntry::approximatelyEquals(r, c.r) && CTEntry::approximatelyEquals(i, c.i); }; - [[nodiscard]] bool approximatelyZero() const { + [[nodiscard]] inline bool approximatelyZero() const { return CTEntry::approximatelyZero(r) && CTEntry::approximatelyZero(i); } - [[nodiscard]] bool approximatelyOne() const { + [[nodiscard]] inline bool approximatelyOne() const { return CTEntry::approximatelyOne(r) && CTEntry::approximatelyZero(i); } - bool operator==(const Complex& other) const { + inline bool operator==(const Complex& other) const { return r == other.r && i == other.i; } - bool operator!=(const Complex& other) const { + inline bool operator!=(const Complex& other) const { return !operator==(other); } diff --git a/include/dd/ComplexValue.hpp b/include/dd/ComplexValue.hpp index f0b7b457..a56ca77b 100644 --- a/include/dd/ComplexValue.hpp +++ b/include/dd/ComplexValue.hpp @@ -23,26 +23,26 @@ namespace dd { struct ComplexValue { fp r, i; - [[nodiscard]] bool approximatelyEquals(const ComplexValue& c) const { + [[nodiscard]] inline bool approximatelyEquals(const ComplexValue& c) const { return std::abs(r - c.r) < ComplexTable<>::tolerance() && std::abs(i - c.i) < ComplexTable<>::tolerance(); } - [[nodiscard]] bool approximatelyZero() const { + [[nodiscard]] inline bool approximatelyZero() const { return std::abs(r) < ComplexTable<>::tolerance() && std::abs(i) < ComplexTable<>::tolerance(); } - [[nodiscard]] bool approximatelyOne() const { + [[nodiscard]] inline bool approximatelyOne() const { return std::abs(r - 1.) < ComplexTable<>::tolerance() && std::abs(i) < ComplexTable<>::tolerance(); } - bool operator==(const ComplexValue& other) const { + inline bool operator==(const ComplexValue& other) const { return r == other.r && i == other.i; } - bool operator!=(const ComplexValue& other) const { + inline bool operator!=(const ComplexValue& other) const { return !operator==(other); } From a1b81f34bdba13ea67dac34f14155a67912c1e73 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 27 Apr 2021 09:59:59 +0200 Subject: [PATCH 48/48] Code review changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 👌 change garbage collection limit calculation to avoid overflow 👌 renaming `needsCollection` to `possiblyNeedsCollection` ⬆️ googletest ⬆️ benchmark ⚡ toffoli table and identity table need not be cleared when complex numbers have been collected as the just consist of edges with weight 1 Signed-off-by: Lukas Burgholzer --- extern/benchmark | 2 +- extern/googletest | 2 +- include/dd/ComplexTable.hpp | 4 ++-- include/dd/Package.hpp | 8 +++----- include/dd/UniqueTable.hpp | 4 ++-- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/extern/benchmark b/extern/benchmark index 69054ae5..264976de 160000 --- a/extern/benchmark +++ b/extern/benchmark @@ -1 +1 @@ -Subproject commit 69054ae50e07e9de7cb27f9e2d1d355f74605524 +Subproject commit 264976def327306a4a205857d98ec6792a20cae2 diff --git a/extern/googletest b/extern/googletest index 23ef2955..252ce9c5 160000 --- a/extern/googletest +++ b/extern/googletest @@ -1 +1 @@ -Subproject commit 23ef29555ef4789f555f1ba8c51b4c52975f0907 +Subproject commit 252ce9c52d304659eff6be558209c811b7191963 diff --git a/include/dd/ComplexTable.hpp b/include/dd/ComplexTable.hpp index c1cb48e6..80d127b5 100644 --- a/include/dd/ComplexTable.hpp +++ b/include/dd/ComplexTable.hpp @@ -275,7 +275,7 @@ namespace dd { } } - [[nodiscard]] bool needsCollection() const { return count >= gcLimit; } + [[nodiscard]] bool possiblyNeedsCollection() const { return count >= gcLimit; } std::size_t garbageCollect(bool force = false) { gcCalls++; @@ -313,7 +313,7 @@ namespace dd { // once the number of remaining entries reaches the garbage collection limit. It is increased whenever the // number of remaining entries is rather close to the garbage collection threshold and decreased if the // number of remaining entries is much lower than the current limit. - if (remaining > gcLimit * 9 / 10) { + if (remaining > gcLimit / 10 * 9) { gcLimit = remaining + INITIAL_GC_LIMIT; } else if (remaining < gcLimit / 128) { gcLimit /= 2; diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index c41fa895..b944521f 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -511,9 +511,9 @@ namespace dd { void garbageCollect(bool force = false) { // return immediately if no table needs collection if (!force && - !vUniqueTable.needsCollection() && - !mUniqueTable.needsCollection() && - !cn.complexTable.needsCollection()) { + !vUniqueTable.possiblyNeedsCollection() && + !mUniqueTable.possiblyNeedsCollection() && + !cn.complexTable.possiblyNeedsCollection()) { return; } @@ -549,8 +549,6 @@ namespace dd { vectorInnerProduct.clear(); vectorKronecker.clear(); matrixKronecker.clear(); - toffoliTable.clear(); - clearIdentityTable(); noiseOperationTable.clear(); } } diff --git a/include/dd/UniqueTable.hpp b/include/dd/UniqueTable.hpp index 58f586fa..bd191384 100644 --- a/include/dd/UniqueTable.hpp +++ b/include/dd/UniqueTable.hpp @@ -204,7 +204,7 @@ namespace dd { } } - [[nodiscard]] bool needsCollection() const { return nodeCount >= gcLimit; } + [[nodiscard]] bool possiblyNeedsCollection() const { return nodeCount >= gcLimit; } std::size_t garbageCollect(bool force = false) { gcCalls++; @@ -243,7 +243,7 @@ namespace dd { // once the number of remaining entries reaches the garbage collection limit. It is increased whenever the // number of remaining entries is rather close to the garbage collection threshold and decreased if the // number of remaining entries is much lower than the current limit. - if (remaining > gcLimit * 9 / 10) { + if (remaining > gcLimit / 10 * 9) { gcLimit = remaining + INITIAL_GC_LIMIT; } else if (remaining < gcLimit / 32) { gcLimit /= 4;