Skip to content

✨ Migrate and Refactor Approximation Functionality from DDSim Package #908

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

Open
wants to merge 58 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
804f39b
Add Approximation.hpp
MatthiasReumann Apr 1, 2025
454d5bc
Add NodeContributions struct to Approximation.hpp
MatthiasReumann Apr 3, 2025
afb6b6f
Update vectorize function to iterate breadth-first
MatthiasReumann Apr 6, 2025
182b48a
Add Approximation to simulate function
MatthiasReumann Apr 6, 2025
2a7b5cd
Slim down NodeContributions class
MatthiasReumann Apr 6, 2025
080774b
Implement applyApproximation
MatthiasReumann Apr 6, 2025
43b9569
Add preliminary unit tests
MatthiasReumann Apr 6, 2025
82580a7
🎨 pre-commit fixes
pre-commit-ci[bot] Apr 6, 2025
3919879
Merge branch 'main' into feat/dd-sim-approximations
MatthiasReumann Apr 6, 2025
1f841ad
Update doxygen for simulate function
MatthiasReumann Apr 6, 2025
22c4e55
Make Approximation structs constexpr
MatthiasReumann Apr 7, 2025
1d3a7e5
Show in code that MemoryDriven is FidelityDriven + threshold
MatthiasReumann Apr 7, 2025
472958c
Add rebuildWithout function
MatthiasReumann Apr 7, 2025
017628b
Merge branch 'main' into feat/dd-sim-approximations
MatthiasReumann Apr 7, 2025
5dbef62
Remove faulty lookup
MatthiasReumann Apr 8, 2025
4a6a156
Add renormalization to root edge
MatthiasReumann Apr 8, 2025
14eb307
Merge branch 'main' into feat/dd-sim-approximations
MatthiasReumann Apr 10, 2025
378800a
1-traversal approximation
MatthiasReumann Apr 11, 2025
0c77295
Apply correct edge weights and update tests
MatthiasReumann Apr 12, 2025
7dce932
Fix includes
MatthiasReumann Apr 12, 2025
40f63c3
Add single traversal breadth-first algorithm
MatthiasReumann Apr 20, 2025
92dba38
Fix linting issues
MatthiasReumann Apr 20, 2025
052f6f6
Add doxygen
MatthiasReumann Apr 20, 2025
014f369
Remove includes in Simulation.hpp and Simulation.cpp
MatthiasReumann Apr 24, 2025
3780739
Use std::forward_list instead of std::deque
MatthiasReumann Apr 24, 2025
bf74fe7
Add OneQubitApproximation test with ry gate
MatthiasReumann Apr 24, 2025
1543950
Add rebuild function
MatthiasReumann Apr 24, 2025
544b81b
Update tests
MatthiasReumann Apr 24, 2025
b2273d1
Fix linting issues
MatthiasReumann Apr 25, 2025
ce1ad59
Update doxygen for approximate function
MatthiasReumann Apr 25, 2025
f415fb1
Add <utility> to Approximation.cpp
MatthiasReumann Apr 25, 2025
964d2e2
Adjust tests
MatthiasReumann Apr 25, 2025
23bd171
Update test description
MatthiasReumann Apr 25, 2025
349f166
Return std::pair with fidelity as second
MatthiasReumann Apr 28, 2025
044a9d4
Clean up approximate function
MatthiasReumann Apr 28, 2025
1c8c556
Add std::find for nextEdge
MatthiasReumann Apr 28, 2025
5c8489e
Merge branch 'main' into feat/dd-sim-approximations
MatthiasReumann Apr 28, 2025
6b17acf
Remove checks with pre-approximation state after decRef and garbageCo…
MatthiasReumann Apr 28, 2025
25f7de9
Update doxygen
MatthiasReumann Apr 28, 2025
84a9f06
Remove unused postFidelity in tests
MatthiasReumann Apr 28, 2025
a8dc8b9
Comment out rebuild function
MatthiasReumann Apr 28, 2025
ecdb253
Uncomment contribution map
MatthiasReumann Apr 28, 2025
3b234b7
Comment all but simplest test
MatthiasReumann Apr 29, 2025
a7cc5fd
Comment out approximate function in simplest test
MatthiasReumann Apr 29, 2025
4fa3524
Initialize contributions map value explicitly
MatthiasReumann Apr 29, 2025
e642827
Comment approximate function
MatthiasReumann Apr 29, 2025
ab939de
Uncomment code
MatthiasReumann Apr 29, 2025
90bb242
Use unique_ptr for dd::Package
MatthiasReumann Apr 29, 2025
ae89cdf
Eliminate contribution map
MatthiasReumann Apr 29, 2025
f95f388
Remove unused import of unordered_map
MatthiasReumann Apr 29, 2025
4e638b7
Formatting
MatthiasReumann Apr 30, 2025
7fa8900
Update tests, its descriptions, and add ascii dds
MatthiasReumann May 1, 2025
6015b69
Remove 0. in dd vis
MatthiasReumann May 1, 2025
42ac8df
De-nest if statements
MatthiasReumann May 1, 2025
9d79391
Extract global phase
MatthiasReumann May 1, 2025
4a8d67b
Use unordered_map for layer
MatthiasReumann May 1, 2025
5676056
Remove edges without a second top-down traversal
MatthiasReumann May 5, 2025
bf5d09c
Merge branch 'main' into feat/dd-sim-approximations
MatthiasReumann May 5, 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
33 changes: 33 additions & 0 deletions include/mqt-core/dd/Approximation.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2023 - 2025 Chair for Design Automation, TUM
* Copyright (c) 2025 Munich Quantum Software Company GmbH
* All rights reserved.
*
* SPDX-License-Identifier: MIT
*
* Licensed under the MIT License
*/

#pragma once

#include "dd/Node.hpp"
#include "dd/Package.hpp"

namespace dd {

/**
* @brief Approximate the @p state based on fidelity. The fidelity of the
* approximated state will be at least @p fidelity.
*
* @details Traverses the decision diagram layer by layer in a breadth-first
* manner (iterative deepening algorithm) and eliminates edges greedily until
* the budget (1 - @p fidelity) is exhausted.
Comment on lines +22 to +24
Copy link
Member

Choose a reason for hiding this comment

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

Once all the details of the implementation are clarified, I believe it would be helpful to add a brief description of the complexity of the approximation operation to the docstring here.

*
* @param state The DD to approximate.
* @param fidelity The desired minimum fidelity after approximation.
* @param dd The DD package to use for the approximation.
* @return The fidelity between the source and the approximated state.
*/
double approximate(VectorDD& state, double fidelity, Package& dd);

} // namespace dd
116 changes: 116 additions & 0 deletions src/dd/Approximation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* Copyright (c) 2023 - 2025 Chair for Design Automation, TUM
* Copyright (c) 2025 Munich Quantum Software Company GmbH
* All rights reserved.
*
* SPDX-License-Identifier: MIT
*
* Licensed under the MIT License
*/

#include "dd/Approximation.hpp"

#include "dd/ComplexNumbers.hpp"
#include "dd/DDDefinitions.hpp"
#include "dd/Node.hpp"
#include "dd/Package.hpp"

#include <array>
#include <forward_list>
#include <unordered_map>
#include <utility>

namespace dd {
namespace {
using UpwardsItem = std::pair<std::forward_list<vEdge*>, std::size_t>;

Check warning on line 25 in src/dd/Approximation.cpp

View workflow job for this annotation

GitHub Actions / 🇨‌ Lint / 🚨 Lint

src/dd/Approximation.cpp:25:63 [misc-include-cleaner]

no header providing "std::size_t" is directly included
using Upwards = std::unordered_map<vEdge*, UpwardsItem>;
using Layer = std::unordered_map<vEdge*, double>;

/**
* @brief Sets the edge specified by the @p item to `vEdge::zero()` and
* multiplies the weights of the parent's edges with the weight of the
* new edge.
* @note The resulting DD is not normalized.
*/
void zeroEdge(UpwardsItem& item, Package& dd) {
const auto& [parents, i] = item;
const vNode* parentNode = parents.front()->p;

std::array<vEdge, RADIX> edges = parentNode->e;
edges[i] = vEdge::zero();

vEdge newEdge = dd.makeDDNode(parentNode->v, edges);
Complex w = newEdge.w;

Check warning on line 43 in src/dd/Approximation.cpp

View workflow job for this annotation

GitHub Actions / 🇨‌ Lint / 🚨 Lint

src/dd/Approximation.cpp:43:3 [misc-include-cleaner]

no header providing "dd::Complex" is directly included

Check warning on line 43 in src/dd/Approximation.cpp

View workflow job for this annotation

GitHub Actions / 🇨‌ Lint / 🚨 Lint

src/dd/Approximation.cpp:43:3 [misc-const-correctness]

variable 'w' of type 'Complex' can be declared 'const'

for (const auto& edge : parents) {
newEdge.w = dd.cn.lookup(w * edge->w);

dd.decRef(*edge);
dd.incRef(newEdge);
*edge = newEdge;
}
}
}; // namespace

double approximate(VectorDD& state, const double fidelity, Package& dd) {
const Complex phase = state.w;

Upwards upwards{};
Layer curr{{&state, ComplexNumbers::mag2(state.w)}};

double budget = 1 - fidelity;
while (!curr.empty() && budget > 0) {
Layer next{};
Upwards upwardsNext{};

for (const auto& [edge, contribution] : curr) {
if (contribution <= budget) {
zeroEdge(upwards[edge], dd);
budget -= contribution;
continue;
}

if (edge->isTerminal()) {
continue;
}

for (std::size_t i = 0; i < RADIX; ++i) {
vEdge* eChild = &edge->p->e[i];
if (eChild->w.exactlyZero()) {
continue;
}

// Implicit: If `next` doesn't contain `eChild`, it will be initialized
// with 0. See `operator[]`.
next[eChild] += contribution * ComplexNumbers::mag2(eChild->w);

// Link the child with the parent's edge and its associated index.
if (upwardsNext.find(eChild) == upwardsNext.end()) {
upwardsNext[eChild] = {{edge}, i};
continue;
}
upwardsNext[eChild].first.emplace_front(edge);
}
}

curr = std::move(next);
upwards = std::move(upwardsNext);
}

// Rebuild only the root state to normalize all nodes of the DD.
// Then: Apply global phase.
VectorDD approx = dd.makeDDNode(state.p->v, state.p->e);
approx.w = phase;

// Make sure to correctly update the reference counts and clean up.
dd.incRef(approx);
dd.decRef(state);
dd.garbageCollect();

// Finally, apply approximation to source state.
state = approx;

return fidelity + budget;
}

} // namespace dd
Loading
Loading