mqt-core 3.0.2__cp313-cp313t-win_amd64.whl → 3.1.0__cp313-cp313t-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of mqt-core might be problematic. Click here for more details.

Files changed (105) hide show
  1. mqt/core/__init__.py +3 -3
  2. mqt/core/_version.py +2 -2
  3. mqt/core/bin/mqt-core-algorithms.dll +0 -0
  4. mqt/core/bin/mqt-core-circuit-optimizer.dll +0 -0
  5. mqt/core/bin/mqt-core-dd.dll +0 -0
  6. mqt/core/bin/mqt-core-ds.dll +0 -0
  7. mqt/core/bin/mqt-core-ir.dll +0 -0
  8. mqt/core/bin/mqt-core-na.dll +0 -0
  9. mqt/core/bin/mqt-core-qasm.dll +0 -0
  10. mqt/core/bin/mqt-core-zx.dll +0 -0
  11. mqt/core/dd.cp313t-win_amd64.pyd +0 -0
  12. mqt/core/include/mqt-core/dd/Approximation.hpp +45 -0
  13. mqt/core/include/mqt-core/dd/Complex.hpp +6 -0
  14. mqt/core/include/mqt-core/dd/ComplexNumbers.hpp +0 -18
  15. mqt/core/include/mqt-core/dd/ComputeTable.hpp +4 -1
  16. mqt/core/include/mqt-core/dd/DDDefinitions.hpp +0 -7
  17. mqt/core/include/mqt-core/dd/Edge.hpp +16 -0
  18. mqt/core/include/mqt-core/dd/Export.hpp +1 -2
  19. mqt/core/include/mqt-core/dd/Node.hpp +26 -49
  20. mqt/core/include/mqt-core/dd/Operations.hpp +27 -0
  21. mqt/core/include/mqt-core/dd/Package.hpp +138 -120
  22. mqt/core/include/mqt-core/dd/RealNumber.hpp +49 -66
  23. mqt/core/include/mqt-core/dd/RealNumberUniqueTable.hpp +14 -25
  24. mqt/core/include/mqt-core/dd/StateGeneration.hpp +143 -0
  25. mqt/core/include/mqt-core/dd/UnaryComputeTable.hpp +6 -4
  26. mqt/core/include/mqt-core/dd/UniqueTable.hpp +13 -39
  27. mqt/core/include/mqt-core/dd/statistics/PackageStatistics.hpp +8 -7
  28. mqt/core/include/mqt-core/dd/statistics/UniqueTableStatistics.hpp +0 -10
  29. mqt/core/include/mqt-core/ir/QuantumComputation.hpp +3 -0
  30. mqt/core/include/mqt-core/ir/operations/CompoundOperation.hpp +2 -0
  31. mqt/core/include/mqt-core/ir/operations/Operation.hpp +2 -2
  32. mqt/core/ir.cp313t-win_amd64.pyd +0 -0
  33. mqt/core/lib/mqt-core-algorithms.lib +0 -0
  34. mqt/core/lib/mqt-core-circuit-optimizer.lib +0 -0
  35. mqt/core/lib/mqt-core-dd.lib +0 -0
  36. mqt/core/lib/mqt-core-ds.lib +0 -0
  37. mqt/core/lib/mqt-core-ir.lib +0 -0
  38. mqt/core/lib/mqt-core-na.lib +0 -0
  39. mqt/core/lib/mqt-core-qasm.lib +0 -0
  40. mqt/core/lib/mqt-core-zx.lib +0 -0
  41. mqt/core/plugins/__init__.py +0 -8
  42. mqt/core/plugins/qiskit/qiskit_to_mqt.py +6 -0
  43. mqt/core/share/cmake/mqt-core/AddMQTPythonBinding.cmake +52 -0
  44. mqt/core/share/cmake/mqt-core/PackageAddTest.cmake +4 -4
  45. mqt/core/share/cmake/mqt-core/mqt-core-config-version.cmake +3 -3
  46. mqt/core/share/cmake/mqt-core/mqt-core-config.cmake +1 -0
  47. mqt/core/share/cmake/mqt-core/mqt-core-targets.cmake +1 -1
  48. mqt_core-3.1.0.dist-info/DELVEWHEEL +2 -0
  49. {mqt_core-3.0.2.dist-info → mqt_core-3.1.0.dist-info}/METADATA +46 -21
  50. {mqt_core-3.0.2.dist-info → mqt_core-3.1.0.dist-info}/RECORD +54 -101
  51. {mqt_core-3.0.2.dist-info → mqt_core-3.1.0.dist-info}/WHEEL +1 -1
  52. mqt_core.libs/msvcp140.dll +0 -0
  53. mqt/core/include/nlohmann/adl_serializer.hpp +0 -55
  54. mqt/core/include/nlohmann/byte_container_with_subtype.hpp +0 -103
  55. mqt/core/include/nlohmann/detail/abi_macros.hpp +0 -100
  56. mqt/core/include/nlohmann/detail/conversions/from_json.hpp +0 -497
  57. mqt/core/include/nlohmann/detail/conversions/to_chars.hpp +0 -1118
  58. mqt/core/include/nlohmann/detail/conversions/to_json.hpp +0 -446
  59. mqt/core/include/nlohmann/detail/exceptions.hpp +0 -257
  60. mqt/core/include/nlohmann/detail/hash.hpp +0 -129
  61. mqt/core/include/nlohmann/detail/input/binary_reader.hpp +0 -3009
  62. mqt/core/include/nlohmann/detail/input/input_adapters.hpp +0 -492
  63. mqt/core/include/nlohmann/detail/input/json_sax.hpp +0 -727
  64. mqt/core/include/nlohmann/detail/input/lexer.hpp +0 -1633
  65. mqt/core/include/nlohmann/detail/input/parser.hpp +0 -519
  66. mqt/core/include/nlohmann/detail/input/position_t.hpp +0 -37
  67. mqt/core/include/nlohmann/detail/iterators/internal_iterator.hpp +0 -35
  68. mqt/core/include/nlohmann/detail/iterators/iter_impl.hpp +0 -751
  69. mqt/core/include/nlohmann/detail/iterators/iteration_proxy.hpp +0 -242
  70. mqt/core/include/nlohmann/detail/iterators/iterator_traits.hpp +0 -61
  71. mqt/core/include/nlohmann/detail/iterators/json_reverse_iterator.hpp +0 -130
  72. mqt/core/include/nlohmann/detail/iterators/primitive_iterator.hpp +0 -132
  73. mqt/core/include/nlohmann/detail/json_custom_base_class.hpp +0 -39
  74. mqt/core/include/nlohmann/detail/json_pointer.hpp +0 -988
  75. mqt/core/include/nlohmann/detail/json_ref.hpp +0 -78
  76. mqt/core/include/nlohmann/detail/macro_scope.hpp +0 -482
  77. mqt/core/include/nlohmann/detail/macro_unscope.hpp +0 -45
  78. mqt/core/include/nlohmann/detail/meta/call_std/begin.hpp +0 -17
  79. mqt/core/include/nlohmann/detail/meta/call_std/end.hpp +0 -17
  80. mqt/core/include/nlohmann/detail/meta/cpp_future.hpp +0 -171
  81. mqt/core/include/nlohmann/detail/meta/detected.hpp +0 -70
  82. mqt/core/include/nlohmann/detail/meta/identity_tag.hpp +0 -21
  83. mqt/core/include/nlohmann/detail/meta/is_sax.hpp +0 -159
  84. mqt/core/include/nlohmann/detail/meta/std_fs.hpp +0 -29
  85. mqt/core/include/nlohmann/detail/meta/type_traits.hpp +0 -795
  86. mqt/core/include/nlohmann/detail/meta/void_t.hpp +0 -24
  87. mqt/core/include/nlohmann/detail/output/binary_writer.hpp +0 -1838
  88. mqt/core/include/nlohmann/detail/output/output_adapters.hpp +0 -147
  89. mqt/core/include/nlohmann/detail/output/serializer.hpp +0 -988
  90. mqt/core/include/nlohmann/detail/string_concat.hpp +0 -146
  91. mqt/core/include/nlohmann/detail/string_escape.hpp +0 -72
  92. mqt/core/include/nlohmann/detail/value_t.hpp +0 -118
  93. mqt/core/include/nlohmann/json.hpp +0 -5258
  94. mqt/core/include/nlohmann/json_fwd.hpp +0 -75
  95. mqt/core/include/nlohmann/ordered_map.hpp +0 -359
  96. mqt/core/include/nlohmann/thirdparty/hedley/hedley.hpp +0 -2045
  97. mqt/core/include/nlohmann/thirdparty/hedley/hedley_undef.hpp +0 -158
  98. mqt/core/nlohmann_json.natvis +0 -278
  99. mqt/core/share/cmake/nlohmann_json/nlohmann_jsonConfig.cmake +0 -15
  100. mqt/core/share/cmake/nlohmann_json/nlohmann_jsonConfigVersion.cmake +0 -20
  101. mqt/core/share/cmake/nlohmann_json/nlohmann_jsonTargets.cmake +0 -110
  102. mqt/core/share/pkgconfig/nlohmann_json.pc +0 -4
  103. mqt_core-3.0.2.dist-info/DELVEWHEEL +0 -2
  104. {mqt_core-3.0.2.dist-info → mqt_core-3.1.0.dist-info}/entry_points.txt +0 -0
  105. {mqt_core-3.0.2.dist-info → mqt_core-3.1.0.dist-info}/licenses/LICENSE.md +0 -0
mqt/core/__init__.py CHANGED
@@ -12,14 +12,14 @@ from __future__ import annotations
12
12
 
13
13
 
14
14
  # start delvewheel patch
15
- def _delvewheel_patch_1_10_0():
15
+ def _delvewheel_patch_1_10_1():
16
16
  import os
17
17
  if os.path.isdir(libs_dir := os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, 'mqt_core.libs'))):
18
18
  os.add_dll_directory(libs_dir)
19
19
 
20
20
 
21
- _delvewheel_patch_1_10_0()
22
- del _delvewheel_patch_1_10_0
21
+ _delvewheel_patch_1_10_1()
22
+ del _delvewheel_patch_1_10_1
23
23
  # end delvewheel patch
24
24
 
25
25
  import os
mqt/core/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '3.0.2'
21
- __version_tuple__ = version_tuple = (3, 0, 2)
20
+ __version__ = version = '3.1.0'
21
+ __version_tuple__ = version_tuple = (3, 1, 0)
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,45 @@
1
+ /*
2
+ * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM
3
+ * Copyright (c) 2025 Munich Quantum Software Company GmbH
4
+ * All rights reserved.
5
+ *
6
+ * SPDX-License-Identifier: MIT
7
+ *
8
+ * Licensed under the MIT License
9
+ */
10
+
11
+ #pragma once
12
+
13
+ #include "dd/Node.hpp"
14
+ #include "dd/Package.hpp"
15
+
16
+ namespace dd {
17
+
18
+ /**
19
+ * @brief Useful metadata of an approximation run.
20
+ */
21
+ struct ApproximationMetadata {
22
+ /// @brief The fidelity between the source and the approximated state.
23
+ double fidelity;
24
+ /// @brief The number of nodes visited during the mark stage.
25
+ std::size_t nodesVisited;
26
+ /// @brief The lowest qubit number that requires rebuilding.
27
+ Qubit min;
28
+ };
29
+
30
+ /**
31
+ * @brief Approximate the @p state based on fidelity. The fidelity of the
32
+ * approximated state will be at least @p fidelity.
33
+ * @details Traverses the decision diagram layer by layer in a breadth-first
34
+ * manner (iterative deepening algorithm) and eliminates edges greedily until
35
+ * the budget (1 - @p fidelity) is exhausted.
36
+ *
37
+ * @param state The DD to approximate.
38
+ * @param fidelity The desired minimum fidelity after approximation.
39
+ * @param dd The DD package to use for the approximation.
40
+ * @return Metadata about the approximation.
41
+ */
42
+ ApproximationMetadata approximate(VectorDD& state, double fidelity,
43
+ Package& dd);
44
+
45
+ } // namespace dd
@@ -88,6 +88,12 @@ struct Complex {
88
88
  */
89
89
  [[nodiscard]] bool approximatelyZero() const noexcept;
90
90
 
91
+ /// @brief Mark the complex number as used.
92
+ void mark() const noexcept;
93
+
94
+ /// @brief Unmark the complex number.
95
+ void unmark() const noexcept;
96
+
91
97
  /**
92
98
  * @brief Convert the complex number to a string.
93
99
  * @param formatted Whether to apply special formatting to the numbers.
@@ -128,24 +128,6 @@ public:
128
128
  return e;
129
129
  }
130
130
 
131
- /**
132
- * @brief Increment the reference count of a complex number.
133
- * @details This is a pass-through function that increments the reference
134
- * count of the real and imaginary parts of the given complex number.
135
- * @param c The complex number
136
- * @see RealNumberUniqueTable::incRef(RealNumber*)
137
- */
138
- void incRef(const Complex& c) const noexcept;
139
-
140
- /**
141
- * @brief Decrement the reference count of a complex number.
142
- * @details This is a pass-through function that decrements the reference
143
- * count of the real and imaginary parts of the given complex number.
144
- * @param c The complex number
145
- * @see RealNumberUniqueTable::decRef(RealNumber*)
146
- */
147
- void decRef(const Complex& c) const noexcept;
148
-
149
131
  /**
150
132
  * @brief Check whether a complex number is one of the static ones.
151
133
  * @param c The complex number.
@@ -36,11 +36,14 @@ namespace dd {
36
36
  template <class LeftOperandType, class RightOperandType, class ResultType>
37
37
  class ComputeTable {
38
38
  public:
39
+ /// Default number of buckets for the compute table
40
+ static constexpr std::size_t DEFAULT_NUM_BUCKETS = 16384U;
41
+
39
42
  /**
40
43
  * Default constructor
41
44
  * @param numBuckets Number of hash table buckets. Must be a power of two.
42
45
  */
43
- explicit ComputeTable(const size_t numBuckets = 16384U) {
46
+ explicit ComputeTable(const size_t numBuckets = DEFAULT_NUM_BUCKETS) {
44
47
  // numBuckets must be a power of two
45
48
  if ((numBuckets & (numBuckets - 1)) != 0) {
46
49
  throw std::invalid_argument("Number of buckets must be a power of two.");
@@ -32,13 +32,6 @@ namespace dd {
32
32
  */
33
33
  using Qubit = std::uint16_t;
34
34
 
35
- /**
36
- * @brief Integer type used for reference counting
37
- * @details Allows a maximum reference count of roughly 4 billion.
38
- */
39
- using RefCount = std::uint32_t;
40
- static_assert(std::is_unsigned_v<RefCount>, "RefCount should be unsigned.");
41
-
42
35
  /**
43
36
  * @brief Floating point type to use for computations
44
37
  * @note Adjusting the precision might lead to unexpected results.
@@ -88,6 +88,16 @@ template <class Node> struct Edge {
88
88
  return Edge{Node::getTerminal(), w};
89
89
  }
90
90
 
91
+ /**
92
+ * @brief Check whether an edge requires tracking.
93
+ * @param e The edge to check.
94
+ * @return Whether the edge requires tracking.
95
+ */
96
+ [[nodiscard]] static constexpr bool trackingRequired(const Edge& e) {
97
+ return !e.isTerminal() || !constants::isStaticNumber(e.w.r) ||
98
+ !constants::isStaticNumber(e.w.i);
99
+ }
100
+
91
101
  /**
92
102
  * @brief Check whether this is a terminal
93
103
  * @return whether this is a terminal
@@ -131,6 +141,12 @@ template <class Node> struct Edge {
131
141
  */
132
142
  [[nodiscard]] std::size_t size() const;
133
143
 
144
+ /// @brief Mark the edge as used.
145
+ void mark() const noexcept;
146
+
147
+ /// @brief Unmark the edge.
148
+ void unmark() const noexcept;
149
+
134
150
  private:
135
151
  /**
136
152
  * @brief Recursively traverse the DD and count the number of nodes
@@ -163,8 +163,7 @@ static std::ostream& memoryNode(const Edge<Node>& e, std::ostream& os) {
163
163
  os << nodelabel << "[label=<";
164
164
  os << R"(<font point-size="10"><table border="1" cellspacing="0" cellpadding="2" style="rounded">)";
165
165
  os << R"(<tr><td colspan=")" << n << R"(" border="1" sides="B">)" << std::hex
166
- << reinterpret_cast<std::uintptr_t>(e.p) << std::dec
167
- << " ref: " << e.p->ref << "</td></tr>";
166
+ << reinterpret_cast<std::uintptr_t>(e.p) << std::dec << "</td></tr>";
168
167
  os << "<tr>";
169
168
  for (std::size_t i = 0; i < n; ++i) {
170
169
  os << "<td port=\"" << i << R"(" href="javascript:;" border="0" tooltip=")"
@@ -18,7 +18,6 @@
18
18
  #include <array>
19
19
  #include <cassert>
20
20
  #include <cstdint>
21
- #include <limits>
22
21
 
23
22
  namespace dd {
24
23
 
@@ -27,11 +26,9 @@ namespace dd {
27
26
  * @details This class is used to store common information for all DD nodes.
28
27
  * The `flags` makes the implicit padding explicit and can be used for storing
29
28
  * node properties.
30
- * Data Layout (8)|(4|2|2) = 16B.
29
+ * Data Layout (8)|(2|2|4) = 16B.
31
30
  */
32
31
  struct NodeBase : LLBase {
33
- /// Reference count
34
- RefCount ref = 0;
35
32
  /// Variable index
36
33
  Qubit v{};
37
34
 
@@ -40,13 +37,28 @@ struct NodeBase : LLBase {
40
37
  * @details Not required for all node types, but padding is required either
41
38
  * way.
42
39
  *
43
- * 0b1000 = marks a reduced dm node,
44
- * 0b100 = marks a dm (tmp flag),
45
- * 0b10 = mark first path edge (tmp flag),
46
- * 0b1 = mark path is conjugated (tmp flag)
40
+ * 0b10000 = mark flag used for mark-and-sweep garbage collection,
41
+ * 0b1000 = marks a reduced dm node,
42
+ * 0b100 = marks a dm (tmp flag),
43
+ * 0b10 = mark first path edge (tmp flag),
44
+ * 0b1 = mark path is conjugated (tmp flag)
47
45
  */
48
46
  std::uint16_t flags = 0;
49
47
 
48
+ /// Mark flag used for mark-and-sweep garbage collection
49
+ static constexpr std::uint16_t MARK_FLAG = 0b10000U;
50
+
51
+ /// @brief Whether a node is marked as used.
52
+ [[nodiscard]] bool isMarked() const noexcept {
53
+ return (flags & MARK_FLAG) != 0U;
54
+ }
55
+
56
+ /// @brief Mark the node as used.
57
+ void mark() noexcept { flags |= MARK_FLAG; }
58
+
59
+ /// @brief Unmark the node.
60
+ void unmark() noexcept { flags &= ~MARK_FLAG; }
61
+
50
62
  /// Getter for the next object.
51
63
  [[nodiscard]] NodeBase* next() const noexcept {
52
64
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast)
@@ -68,9 +80,12 @@ struct NodeBase : LLBase {
68
80
  static constexpr NodeBase* getTerminal() noexcept { return nullptr; }
69
81
  };
70
82
 
83
+ static_assert(sizeof(NodeBase) == 16);
84
+ static_assert(alignof(NodeBase) == 8);
85
+
71
86
  /**
72
87
  * @brief A vector DD node
73
- * @details Data Layout (8)|(4|2|2)|(24|24) = 64B
88
+ * @details Data Layout (8)|(2|2|4)|(24|24) = 64B
74
89
  */
75
90
  struct vNode final : NodeBase { // NOLINT(readability-identifier-naming)
76
91
  std::array<Edge<vNode>, RADIX> e{}; // edges out of this node
@@ -89,7 +104,7 @@ using VectorDD = vEdge;
89
104
 
90
105
  /**
91
106
  * @brief A matrix DD node
92
- * @details Data Layout (8)|(4|2|2)|(24|24|24|24) = 112B
107
+ * @details Data Layout (8)|(2|2|4)|(24|24|24|24) = 112B
93
108
  */
94
109
  struct mNode final : NodeBase { // NOLINT(readability-identifier-naming)
95
110
  std::array<Edge<mNode>, NEDGE> e{}; // edges out of this node
@@ -108,7 +123,7 @@ using MatrixDD = mEdge;
108
123
 
109
124
  /**
110
125
  * @brief A density matrix DD node
111
- * @details Data Layout (8)|(4|2|2)|(24|24|24|24) = 112B
126
+ * @details Data Layout (8)|(2|2|4)|(24|24|24|24) = 112B
112
127
  */
113
128
  struct dNode final : NodeBase { // NOLINT(readability-identifier-naming)
114
129
  std::array<Edge<dNode>, NEDGE> e{}; // edges out of this node
@@ -205,42 +220,4 @@ static inline dEdge densityFromMatrixEdge(const mEdge& e) {
205
220
  return dEdge{reinterpret_cast<dNode*>(e.p), e.w};
206
221
  }
207
222
 
208
- /**
209
- * @brief Increment the reference count of a node.
210
- * @details This function increments the reference count of a node. If the
211
- * reference count has saturated (i.e. reached the maximum value of RefCount)
212
- * the reference count is not incremented.
213
- * @param p A pointer to the node to increment the reference count of.
214
- * @returns Whether the reference count was incremented.
215
- * @note Typically, you do not want to call this function directly. Instead,
216
- * use the UniqueTable::incRef(Node*) function.
217
- */
218
- [[nodiscard]] static constexpr bool incRef(NodeBase* p) noexcept {
219
- if (p == nullptr || p->ref == std::numeric_limits<RefCount>::max()) {
220
- return false;
221
- }
222
- ++p->ref;
223
- return true;
224
- }
225
-
226
- /**
227
- * @brief Decrement the reference count of a node.
228
- * @details This function decrements the reference count of a node. If the
229
- * reference count has saturated (i.e. reached the maximum value of RefCount)
230
- * the reference count is not decremented.
231
- * @param p A pointer to the node to decrement the reference count of.
232
- * @returns Whether the reference count was decremented.
233
- * @note Typically, you do not want to call this function directly. Instead,
234
- * use the UniqueTable::decRef(Node*) function.
235
- */
236
- [[nodiscard]] static constexpr bool decRef(NodeBase* p) noexcept {
237
- if (p == nullptr || p->ref == std::numeric_limits<RefCount>::max()) {
238
- return false;
239
- }
240
- assert(p->ref != 0 &&
241
- "Reference count of Node must not be zero before decrement");
242
- --p->ref;
243
- return true;
244
- }
245
-
246
223
  } // namespace dd
@@ -204,6 +204,33 @@ applyClassicControlledOperation(const qc::ClassicControlledOperation& op,
204
204
  const std::vector<bool>& measurements,
205
205
  const qc::Permutation& permutation = {});
206
206
 
207
+ /**
208
+ * @brief Check whether @p op is virtually executable.
209
+ *
210
+ * @param op The operation in question.
211
+ * @return Whether @p op is virtually executable.
212
+ */
213
+ bool isExecutableVirtually(const qc::Operation& op) noexcept;
214
+
215
+ /**
216
+ * @brief Apply virtual operation @p op.
217
+ *
218
+ * @param op The virtual operation to apply.
219
+ * @param permutation If suitable, the to be updated permutation.
220
+ */
221
+ void applyVirtualOperation(const qc::Operation& op,
222
+ qc::Permutation& permutation) noexcept;
223
+
224
+ /**
225
+ * @brief Apply global phase to a given DD.
226
+ *
227
+ * @param in The input DD
228
+ * @param phase The phase to apply
229
+ * @param dd The DD package to use
230
+ * @return The output DD
231
+ */
232
+ VectorDD applyGlobalPhase(VectorDD& in, const fp& phase, Package& dd);
233
+
207
234
  /**
208
235
  * @brief Change the permutation of a given DD.
209
236
  *
@@ -194,63 +194,160 @@ public:
194
194
  void clearUniqueTables();
195
195
 
196
196
  /**
197
- * @brief Increment the reference count of an edge
198
- * @details This is the main function for increasing reference counts within
199
- * the DD package. It increases the reference count of the complex edge weight
200
- * as well as the DD node itself. If the node newly becomes active, meaning
201
- * that it had a reference count of zero beforehand, the reference count of
202
- * all children is recursively increased.
197
+ * @brief Add the DD to a tracking hashset and update its reference count.
203
198
  * @tparam Node The node type of the edge.
204
- * @param e The edge to increase the reference count of
199
+ * @param e The edge to increase the reference count of.
205
200
  */
206
201
  template <class Node> void incRef(const Edge<Node>& e) noexcept {
207
- cn.incRef(e.w);
208
- const auto& p = e.p;
209
- const auto inc = getUniqueTable<Node>().incRef(p);
210
- if (inc && p->ref == 1U) {
211
- for (const auto& child : p->e) {
212
- incRef(child);
213
- }
202
+ if (Edge<Node>::trackingRequired(e)) {
203
+ roots.addToRoots(e);
214
204
  }
215
205
  }
216
206
 
217
207
  /**
218
- * @brief Decrement the reference count of an edge
219
- * @details This is the main function for decreasing reference counts within
220
- * the DD package. It decreases the reference count of the complex edge weight
221
- * as well as the DD node itself. If the node newly becomes dead, meaning
222
- * that its reference count hit zero, the reference count of all children is
223
- * recursively decreased.
208
+ * @brief Decrease the DD's reference count and remove it from the tracking
209
+ * hashset if the count hits zero.
224
210
  * @tparam Node The node type of the edge.
225
- * @param e The edge to decrease the reference count of
211
+ * @param e The edge to decrease the reference count of.
212
+ * @throws std::invalid_argument If the edge is not part of the tracking
213
+ * hashset.
226
214
  */
227
- template <class Node> void decRef(const Edge<Node>& e) noexcept {
228
- cn.decRef(e.w);
229
- const auto& p = e.p;
230
- const auto dec = getUniqueTable<Node>().decRef(p);
231
- if (dec && p->ref == 0U) {
232
- for (const auto& child : p->e) {
233
- decRef(child);
234
- }
215
+ template <class Node> void decRef(const Edge<Node>& e) {
216
+ if (Edge<Node>::trackingRequired(e)) {
217
+ roots.removeFromRoots(e);
235
218
  }
236
219
  }
237
220
 
221
+ template <class Node> [[nodiscard]] auto& getRootSet() noexcept {
222
+ return roots.getRoots<Node>();
223
+ }
224
+
225
+ private:
226
+ struct RootSetManager {
227
+ public:
228
+ template <class Node>
229
+ using RootSet = std::unordered_map<Edge<Node>, std::size_t>;
230
+
231
+ /// @brief Add to respective root set.
232
+ template <class Node> void addToRoots(const Edge<Node>& e) noexcept {
233
+ getRoots<Node>()[e]++;
234
+ }
235
+
236
+ /// @brief Remove from respective root set.
237
+ template <class Node> void removeFromRoots(const Edge<Node>& e) {
238
+ auto& set = getRoots<Node>();
239
+ auto it = set.find(e);
240
+ if (it == set.end()) {
241
+ throw std::invalid_argument("Edge is not part of the root set.");
242
+ }
243
+ if (--it->second == 0U) {
244
+ set.erase(it);
245
+ }
246
+ }
247
+
248
+ /// @brief Execute mark() -> op() -> unmark().
249
+ template <class Result, typename Fn> Result execute(Fn& op) noexcept {
250
+ mark();
251
+ Result res = op();
252
+ unmark();
253
+ return res;
254
+ }
255
+
256
+ /// @brief Clear all root sets.
257
+ void reset() {
258
+ vRoots.clear();
259
+ mRoots.clear();
260
+ dRoots.clear();
261
+ }
262
+
263
+ private:
264
+ /// @brief Mark edges contained in @p roots.
265
+ template <class Node> static void mark(const RootSet<Node>& roots) {
266
+ for (auto& [edge, _] : roots) {
267
+ edge.mark();
268
+ }
269
+ }
270
+
271
+ /// @brief Unmark edges contained in @p roots.
272
+ template <class Node> static void unmark(const RootSet<Node>& roots) {
273
+ for (auto& [edge, _] : roots) {
274
+ edge.unmark();
275
+ }
276
+ }
277
+
278
+ /// @brief Mark edges contained in all root sets.
279
+ void mark() noexcept {
280
+ RootSetManager::mark(vRoots);
281
+ RootSetManager::mark(mRoots);
282
+ RootSetManager::mark(dRoots);
283
+ }
284
+
285
+ /// @brief Unmark edges contained in all root sets.
286
+ void unmark() noexcept {
287
+ RootSetManager::unmark(vRoots);
288
+ RootSetManager::unmark(mRoots);
289
+ RootSetManager::unmark(dRoots);
290
+ }
291
+
292
+ /// @brief Return vector roots.
293
+ template <class Node,
294
+ std::enable_if_t<std::is_same_v<Node, vNode>, bool> = true>
295
+ auto& getRoots() noexcept {
296
+ return vRoots;
297
+ }
298
+
299
+ /// @brief Return matrix roots.
300
+ template <class Node,
301
+ std::enable_if_t<std::is_same_v<Node, mNode>, bool> = true>
302
+ auto& getRoots() noexcept {
303
+ return mRoots;
304
+ }
305
+
306
+ /// @brief Return density roots.
307
+ template <class Node,
308
+ std::enable_if_t<std::is_same_v<Node, dNode>, bool> = true>
309
+ auto& getRoots() noexcept {
310
+ return dRoots;
311
+ }
312
+
313
+ RootSet<vNode> vRoots;
314
+ RootSet<mNode> mRoots;
315
+ RootSet<dNode> dRoots;
316
+
317
+ template <class Node> friend auto& Package::getRootSet() noexcept;
318
+ };
319
+
320
+ RootSetManager roots;
321
+
322
+ public:
238
323
  /**
239
- * @brief Trigger garbage collection in all unique tables
240
- *
241
- * @details Garbage collection is the process of removing all nodes from the
242
- * unique tables that have a reference count of zero.
243
- * Such nodes are considered "dead" and they can be safely removed from the
244
- * unique tables. This process is necessary to free up memory that is no
245
- * longer needed. By default, garbage collection is only triggered if the
246
- * unique table indicates that it possibly needs collection. Whenever some
247
- * nodes are recollected, some compute tables need to be invalidated as well.
248
- *
249
- * @param force
250
- * @return
324
+ * @brief Trigger garbage collection on all unique tables.
325
+ * @details Mark-and-sweep algorithm: First, mark all nodes and complex
326
+ * numbers tracked in @p roots. Second, remove any unmarked nodes and numbers
327
+ * from the respective unique tables. Lastly, unmark all nodes and complex
328
+ * numbers again.
329
+ * @note By default, garbage collection is only triggered if the unique tables
330
+ * report that a collection might be necessary.
331
+ *
332
+ * @param force Force garbage collect, regardless of whether any
333
+ * table reports that it may need collecting.
334
+ * @returns Whether at least one vector, matrix, density-matrix node, or
335
+ * any complex number was reclaimed.
251
336
  */
252
337
  bool garbageCollect(bool force = false);
253
338
 
339
+ struct ActiveCounts {
340
+ std::size_t vector = 0U;
341
+ std::size_t matrix = 0U;
342
+ std::size_t density = 0U;
343
+ std::size_t reals = 0U;
344
+ };
345
+ /**
346
+ * @brief Compute the active number of nodes and numbers
347
+ * @note This traverses every currently tracked DD twice.
348
+ */
349
+ [[nodiscard]] ActiveCounts computeActiveCounts();
350
+
254
351
  ///
255
352
  /// Vector nodes, edges and quantum states
256
353
  ///
@@ -263,60 +360,6 @@ public:
263
360
  */
264
361
  dEdge makeZeroDensityOperator(std::size_t n);
265
362
 
266
- /**
267
- * @brief Construct the all-zero state \f$|0...0\rangle\f$
268
- * @param n The number of qubits
269
- * @param start The starting qubit index. Default is 0.
270
- * @return A decision diagram for the all-zero state
271
- */
272
- vEdge makeZeroState(std::size_t n, std::size_t start = 0);
273
-
274
- /**
275
- * @brief Construct a computational basis state \f$|b_{n-1}...b_0\rangle\f$
276
- * @param n The number of qubits
277
- * @param state The state to construct
278
- * @param start The starting qubit index. Default is 0.
279
- * @return A decision diagram for the computational basis state
280
- */
281
- vEdge makeBasisState(std::size_t n, const std::vector<bool>& state,
282
- std::size_t start = 0);
283
-
284
- /**
285
- * @brief Construct a product state out of
286
- * \f$\{0, 1, +, -, R, L\}^{\otimes n}\f$.
287
- * @param n The number of qubits
288
- * @param state The state to construct
289
- * @param start The starting qubit index. Default is 0.
290
- * @return A decision diagram for the product state
291
- */
292
- vEdge makeBasisState(std::size_t n, const std::vector<BasisStates>& state,
293
- std::size_t start = 0);
294
-
295
- /**
296
- * @brief Construct a GHZ state \f$|0...0\rangle + |1...1\rangle\f$
297
- * @param n The number of qubits
298
- * @return A decision diagram for the GHZ state
299
- */
300
- vEdge makeGHZState(std::size_t n);
301
-
302
- /**
303
- * @brief Construct a W state
304
- * @details The W state is defined as
305
- * \f[
306
- * |0...01\rangle + |0...10\rangle + |10...0\rangle
307
- * \f]
308
- * @param n The number of qubits
309
- * @return A decision diagram for the W state
310
- */
311
- vEdge makeWState(std::size_t n);
312
-
313
- /**
314
- * @brief Construct a decision diagram from an arbitrary state vector
315
- * @param stateVector The state vector to convert to a DD
316
- * @return A decision diagram for the state
317
- */
318
- vEdge makeStateFromVector(const CVec& stateVector);
319
-
320
363
  ///
321
364
  /// Matrix nodes, edges and quantum gates
322
365
  ///
@@ -399,30 +442,6 @@ public:
399
442
  mEdge makeDDFromMatrix(const CMat& matrix);
400
443
 
401
444
  private:
402
- /**
403
- * @brief Constructs a decision diagram (DD) from a state vector using a
404
- * recursive algorithm.
405
- *
406
- * @param begin Iterator pointing to the beginning of the state vector.
407
- * @param end Iterator pointing to the end of the state vector.
408
- * @param level The current level of recursion. Starts at the highest level of
409
- * the state vector (log base 2 of the vector size - 1).
410
- * @return A vCachedEdge representing the root node of the created DD.
411
- *
412
- * @details This function recursively breaks down the state vector into halves
413
- * until each half has only one element. At each level of recursion, two new
414
- * edges are created, one for each half of the state vector. The two resulting
415
- * decision diagram edges are used to create a new decision diagram node at
416
- * the current level, and this node is returned as the result of the current
417
- * recursive call. At the base case of recursion, the state vector has only
418
- * two elements, which are converted into terminal nodes of the decision
419
- * diagram.
420
- *
421
- * @note This function assumes that the state vector size is a power of two.
422
- */
423
- vCachedEdge makeStateFromVector(const CVec::const_iterator& begin,
424
- const CVec::const_iterator& end, Qubit level);
425
-
426
445
  /**
427
446
  * @brief Constructs a decision diagram (DD) from a complex matrix using a
428
447
  * recursive algorithm.
@@ -477,7 +496,6 @@ public:
477
496
  [[maybe_unused]] const bool generateDensityMatrix = false) {
478
497
  auto& memoryManager = getMemoryManager<Node>();
479
498
  auto p = memoryManager.template get<Node>();
480
- assert(p->ref == 0U);
481
499
 
482
500
  p->v = var;
483
501
  if constexpr (std::is_same_v<Node, mNode> || std::is_same_v<Node, dNode>) {