Skip to content

✨ Implement mark-and-sweep garbage collection #980

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
a9d5ffe
✨ Implement mark-and-sweep garbage collection and update reference ma…
q-inho Jun 2, 2025
8a3646e
✨ Refactor garbage collection root management to use unordered_set fo…
q-inho Jun 2, 2025
46d7a71
Merge branch 'main' into mark_and_sweep_garbage_DD_package
q-inho Jun 2, 2025
e03554c
Update include/mqt-core/dd/Node.hpp
q-inho Jun 2, 2025
73ef0ff
✨ Enhance mark-and-sweep garbage collection with explicit reference c…
q-inho Jun 4, 2025
2f216ef
Merge branch 'main' into mark_and_sweep_garbage_DD_package
q-inho Jun 4, 2025
ad64bf9
Fix RealNumber reference count assertion
q-inho Jun 4, 2025
46e7aa9
Update include/mqt-core/dd/Package.hpp
q-inho Jun 4, 2025
c88a319
Refactor RealNumber, ComplexNumber and UniqueTable for Improved Memor…
q-inho Jun 13, 2025
143c1db
constexpr pointer operations in `RealNumber`
q-inho Jun 13, 2025
6671038
RealNumber with mark and unmark functions
q-inho Jun 13, 2025
f5877d0
Update tests to use makeBasisState instead of makeZeroState for bette…
q-inho Jun 13, 2025
bed77c0
Revert changes
q-inho Jun 13, 2025
4a1a121
Merge remote-tracking branch 'upstream/main' into mark_and_sweep_garb…
q-inho Jun 13, 2025
7e768b6
Change the call to `makeZeroState` to the `dd` namespace
q-inho Jun 13, 2025
ce4afda
add `bit_cast` support for clearMark function in RealNumber for Build…
q-inho Jun 14, 2025
c51a5de
Merge branch 'main' into mark_and_sweep_garbage_DD_package
q-inho Jun 14, 2025
d75f141
Replace constexp to inline due to build failure on Window
q-inho Jun 15, 2025
d6dec27
Remove constexpr from exactlyZero and exactlyOne methods in Complex s…
q-inho Jun 15, 2025
8344696
Remove constexpr from isStaticComplex method in ComplexNumbers class
q-inho Jun 15, 2025
26ba8d5
🚧 work-in-progress cleanup
burgholzer Jun 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions include/mqt-core/dd/Edge.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,23 @@ template <class Node> struct Edge {
};
} // namespace dd

namespace dd {
template <class Node> struct EdgePtrHash {
std::size_t operator()(const Edge<Node>& e) const noexcept {
const auto h1 = murmur64(reinterpret_cast<std::size_t>(e.p));
const auto h2 = murmur64(reinterpret_cast<std::size_t>(e.w.r));
const auto h3 = murmur64(reinterpret_cast<std::size_t>(e.w.i));
return qc::combineHash(qc::combineHash(h1, h2), h3);
}
};

template <class Node> struct EdgePtrEqual {
bool operator()(const Edge<Node>& lhs, const Edge<Node>& rhs) const noexcept {
return lhs.p == rhs.p && lhs.w.r == rhs.w.r && lhs.w.i == rhs.w.i;
}
};
} // namespace dd

Comment on lines +404 to +420
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change should not be necessary if I am not overlooking things.
The Edge class defines a hash function at the very end of this file.

And the equality operator defined for the Edge class should also be sufficient here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. Edge already provides them.

template <class Node> struct std::hash<dd::Edge<Node>> {
std::size_t operator()(dd::Edge<Node> const& e) const noexcept;
};
3 changes: 1 addition & 2 deletions include/mqt-core/dd/Export.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,7 @@ static std::ostream& memoryNode(const Edge<Node>& e, std::ostream& os) {
os << nodelabel << "[label=<";
os << R"(<font point-size="10"><table border="1" cellspacing="0" cellpadding="2" style="rounded">)";
os << R"(<tr><td colspan=")" << n << R"(" border="1" sides="B">)" << std::hex
<< reinterpret_cast<std::uintptr_t>(e.p) << std::dec
<< " ref: " << e.p->ref << "</td></tr>";
<< reinterpret_cast<std::uintptr_t>(e.p) << std::dec << "</td></tr>";
os << "<tr>";
for (std::size_t i = 0; i < n; ++i) {
os << "<td port=\"" << i << R"(" href="javascript:;" border="0" tooltip=")"
Expand Down
66 changes: 18 additions & 48 deletions include/mqt-core/dd/Node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,9 @@ namespace dd {
* @details This class is used to store common information for all DD nodes.
* The `flags` makes the implicit padding explicit and can be used for storing
* node properties.
* Data Layout (8)|(4|2|2) = 16B.
* Data Layout (8)|(2|2|4) = 16B.
*/
struct NodeBase : LLBase {
/// Reference count
RefCount ref = 0;
/// Variable index
Qubit v{};

Expand All @@ -40,13 +38,23 @@ struct NodeBase : LLBase {
* @details Not required for all node types, but padding is required either
* way.
*
* 0b1000 = marks a reduced dm node,
* 0b100 = marks a dm (tmp flag),
* 0b10 = mark first path edge (tmp flag),
* 0b1 = mark path is conjugated (tmp flag)
* 0b10000 = mark flag used for mark-and-sweep garbage collection,
* 0b1000 = marks a reduced dm node,
* 0b100 = marks a dm (tmp flag),
* 0b10 = mark first path edge (tmp flag),
* 0b1 = mark path is conjugated (tmp flag)
*/
std::uint16_t flags = 0;

/// Mark flag used for mark-and-sweep garbage collection
static constexpr std::uint16_t MARK_FLAG = 0b10000U;

[[nodiscard]] bool marked() const noexcept {
return (flags & MARK_FLAG) != 0U;
}
void mark() noexcept { flags |= MARK_FLAG; }
void unmark() noexcept { flags &= static_cast<std::uint16_t>(~MARK_FLAG); }

/// Getter for the next object.
[[nodiscard]] NodeBase* next() const noexcept {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast)
Expand All @@ -70,7 +78,7 @@ struct NodeBase : LLBase {

/**
* @brief A vector DD node
* @details Data Layout (8)|(4|2|2)|(24|24) = 64B
* @details Data Layout (8)|(2|2|4)|(24|24) = 64B
*/
struct vNode final : NodeBase { // NOLINT(readability-identifier-naming)
std::array<Edge<vNode>, RADIX> e{}; // edges out of this node
Expand All @@ -89,7 +97,7 @@ using VectorDD = vEdge;

/**
* @brief A matrix DD node
* @details Data Layout (8)|(4|2|2)|(24|24|24|24) = 112B
* @details Data Layout (8)|(2|2|4)|(24|24|24|24) = 112B
*/
struct mNode final : NodeBase { // NOLINT(readability-identifier-naming)
std::array<Edge<mNode>, NEDGE> e{}; // edges out of this node
Expand All @@ -108,7 +116,7 @@ using MatrixDD = mEdge;

/**
* @brief A density matrix DD node
* @details Data Layout (8)|(4|2|2)|(24|24|24|24) = 112B
* @details Data Layout (8)|(2|2|4)|(24|24|24|24) = 112B
*/
struct dNode final : NodeBase { // NOLINT(readability-identifier-naming)
std::array<Edge<dNode>, NEDGE> e{}; // edges out of this node
Expand Down Expand Up @@ -205,42 +213,4 @@ static inline dEdge densityFromMatrixEdge(const mEdge& e) {
return dEdge{reinterpret_cast<dNode*>(e.p), e.w};
}

/**
* @brief Increment the reference count of a node.
* @details This function increments the reference count of a node. If the
* reference count has saturated (i.e. reached the maximum value of RefCount)
* the reference count is not incremented.
* @param p A pointer to the node to increment the reference count of.
* @returns Whether the reference count was incremented.
* @note Typically, you do not want to call this function directly. Instead,
* use the UniqueTable::incRef(Node*) function.
*/
[[nodiscard]] static constexpr bool incRef(NodeBase* p) noexcept {
if (p == nullptr || p->ref == std::numeric_limits<RefCount>::max()) {
return false;
}
++p->ref;
return true;
}

/**
* @brief Decrement the reference count of a node.
* @details This function decrements the reference count of a node. If the
* reference count has saturated (i.e. reached the maximum value of RefCount)
* the reference count is not decremented.
* @param p A pointer to the node to decrement the reference count of.
* @returns Whether the reference count was decremented.
* @note Typically, you do not want to call this function directly. Instead,
* use the UniqueTable::decRef(Node*) function.
*/
[[nodiscard]] static constexpr bool decRef(NodeBase* p) noexcept {
if (p == nullptr || p->ref == std::numeric_limits<RefCount>::max()) {
return false;
}
assert(p->ref != 0 &&
"Reference count of Node must not be zero before decrement");
--p->ref;
return true;
}

} // namespace dd
17 changes: 8 additions & 9 deletions include/mqt-core/dd/Operations.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ MatrixDD getInverseDD(const qc::Operation& op, Package& dd,
* @brief Apply a unitary operation to a given vector DD.
*
* @details This is a convenience function that realizes @p op times @p in and
* correctly accounts for the permutation of the operation's qubits as well as
* automatically handles reference counting.
* correctly accounts for the permutation of the operation's qubits and
* automatically manages root-set membership.
*
* @param op The operation to apply
* @param in The input DD
Expand All @@ -119,8 +119,8 @@ VectorDD applyUnitaryOperation(const qc::Operation& op, const VectorDD& in,
* @brief Apply a unitary operation to a given matrix DD.
*
* @details This is a convenience function that realizes @p op times @p in and
* correctly accounts for the permutation of the operation's qubits as well as
* automatically handles reference counting.
* correctly accounts for the permutation of the operation's qubits and
* automatically manages root-set membership.
*
* @param op The operation to apply
* @param in The input DD
Expand All @@ -142,8 +142,8 @@ MatrixDD applyUnitaryOperation(const qc::Operation& op, const MatrixDD& in,
* @details This is a convenience function that realizes the measurement @p op
* on @p in and stores the measurement results in @p measurements. The result is
* determined based on the RNG @p rng. The function correctly accounts for the
* permutation of the operation's qubits as well as automatically handles
* reference counting.
* permutation of the operation's qubits and automatically manages root-set
* membership.
*
* @param op The measurement operation to apply
* @param in The input DD
Expand All @@ -166,7 +166,7 @@ VectorDD applyMeasurement(const qc::NonUnitaryOperation& op, VectorDD in,
* in. To this end, it measures the qubit and applies an X operation if the
* measurement result is one. The result is determined based on the RNG @p rng.
* The function correctly accounts for the permutation of the operation's
* qubits as well as automatically handles reference counting.
* qubits and automatically manages root-set membership.
*
* @param op The reset operation to apply
* @param in The input DD
Expand All @@ -187,8 +187,7 @@ VectorDD applyReset(const qc::NonUnitaryOperation& op, VectorDD in, Package& dd,
* operation @p op on @p in. It applies the underlying operation if the actual
* value stored in the measurement results matches the expected value according
* to the comparison kind. The function correctly accounts for the permutation
* of the operation's qubits as well as automatically handles reference
* counting.
* of the operation's qubits and automatically manages root-set membership.
*
* @param op The classic controlled operation to apply
* @param in The input DD
Expand Down
Loading
Loading