Skip to content

Commit f1f2de4

Browse files
ystadeburgholzer
andauthored
🎨 Refactor NAComputation with concrete base classes for every operation and ouput new .naviz format (#846)
## Description The tool [`mqt-naviz`](github.com/cda-tum/mqt-naviz) purposefully introduces a new textual format that allows to execute operations at a specific time stamp. The current output format of NA computation is not really compatible even though `mqt-naviz` supports a legacy feature to read in this old format with some caveats. This issue triggered a wider refactoring of the `NAComputation`. Its entire structure is refactored. Most importantly, concrete Operation have their own class representation inheriting from some super class. At the end, all operations inherit from the class `Op`. As part of the refactoring, there is a straight forward way to test whether an operation is of some particular type and then it can be casted to this type. ## Checklist: - [x] The pull request only contains commits that are related to it. - [x] I have added appropriate tests and documentation. - [x] I have made sure that all CI jobs on GitHub pass. - [x] The pull request introduces no new warnings and follows the project's style guidelines. --------- Signed-off-by: burgholzer <[email protected]> Co-authored-by: Lukas Burgholzer <[email protected]>
1 parent ec0642f commit f1f2de4

38 files changed

+1576
-854
lines changed

‎.clang-tidy

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ CheckOptions:
5555
- key: readability-identifier-naming.MemberCase
5656
value: camelBack
5757
- key: readability-identifier-naming.MemberIgnoredRegexp
58-
value: ".*ZX.*|.*SWAP.*|.*CEX.*|.*DD.*|.*EQ.*"
58+
value: ".*ZX.*|.*SWAP.*|.*CEX.*|.*DD.*|.*EQ.*|.*_"
5959
- key: readability-identifier-naming.MethodCase
6060
value: camelBack
6161
- key: readability-identifier-naming.ParameterCase

‎include/mqt-core/ir/operations/CompoundOperation.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ class CompoundOperation final : public Operation {
5555

5656
[[nodiscard]] bool isCustomGate() const noexcept;
5757

58+
[[nodiscard]] bool isGlobal(size_t nQubits) const noexcept override;
59+
5860
void addControl(Control c) override;
5961

6062
void clearControls() override;

‎include/mqt-core/ir/operations/Operation.hpp

+13
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,19 @@ class Operation {
154154

155155
[[nodiscard]] virtual bool isControlled() const { return !controls.empty(); }
156156

157+
/**
158+
* @brief Checks whether a gate is global.
159+
* @details A StandardOperation is global if it acts on all qubits.
160+
* A CompoundOperation is global if all its sub-operations are
161+
* StandardOperations of the same type with the same parameters acting on all
162+
* qubits. The latter is what a QASM line like `ry(Ï€) q;` is translated to in
163+
* MQT Core. All other operations are not global.
164+
* @return True if the operation is global, false otherwise.
165+
*/
166+
[[nodiscard]] virtual bool isGlobal(size_t /* unused */) const {
167+
return false;
168+
}
169+
157170
[[nodiscard]] virtual bool actsOn(const Qubit i) const {
158171
for (const auto& t : targets) {
159172
if (t == i) {

‎include/mqt-core/ir/operations/StandardOperation.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ class StandardOperation : public Operation {
8181

8282
[[nodiscard]] bool isStandardOperation() const override { return true; }
8383

84+
[[nodiscard]] bool isGlobal(size_t nQubits) const override;
85+
8486
void addControl(const Control c) override {
8587
if (actsOn(c.qubit)) {
8688
throw QFRException("Cannot add control on qubit " +

‎include/mqt-core/na/NAComputation.hpp

+145-65
Original file line numberDiff line numberDiff line change
@@ -7,96 +7,176 @@
77
* Licensed under the MIT License
88
*/
99

10+
/** @file
11+
* @brief Defines a class for representing neutral atom computations.
12+
*/
13+
1014
#pragma once
1115

12-
#include "NADefinitions.hpp"
13-
#include "operations/NAOperation.hpp"
16+
#include "na/entities/Atom.hpp"
17+
#include "na/entities/Location.hpp"
18+
#include "na/entities/Zone.hpp"
19+
#include "na/operations/Op.hpp"
1420

15-
#include <algorithm>
1621
#include <cstddef>
17-
#include <iterator>
1822
#include <memory>
1923
#include <ostream>
2024
#include <string>
25+
#include <unordered_map>
26+
#include <utility>
2127
#include <vector>
2228

2329
namespace na {
24-
class NAComputation {
30+
/// Represents a neutral atom computation.
31+
class NAComputation final {
2532
protected:
26-
std::vector<std::shared_ptr<Point>> initialPositions;
27-
std::vector<std::unique_ptr<NAOperation>> operations;
33+
/// The operations in the NA computation.
34+
std::vector<std::unique_ptr<Op>> operations_;
35+
/// The atoms used in the NA computation.
36+
std::vector<std::unique_ptr<Atom>> atoms_;
37+
/// The zones used in the NA computation.
38+
std::vector<std::unique_ptr<Zone>> zones_;
39+
/// The initial locations of the atoms.
40+
std::unordered_map<const Atom*, Location> initialLocations_;
2841

2942
public:
30-
NAComputation() = default;
31-
NAComputation(NAComputation&& qc) noexcept = default;
32-
NAComputation& operator=(NAComputation&& qc) noexcept = default;
33-
NAComputation(const NAComputation& qc)
34-
: initialPositions(qc.initialPositions) {
35-
operations.reserve(qc.operations.size());
36-
std::transform(qc.operations.cbegin(), qc.operations.cend(),
37-
std::back_inserter(operations),
38-
[](const auto& op) { return op->clone(); });
43+
/// Returns an iterator to the beginning of the operations.
44+
[[nodiscard]] auto begin() -> auto { return operations_.begin(); }
45+
46+
/// Returns an iterator to the beginning of the operations.
47+
[[nodiscard]] auto begin() const -> auto { return operations_.begin(); }
48+
49+
/// Returns an iterator to the end of the operations.
50+
[[nodiscard]] auto end() -> auto { return operations_.end(); }
51+
52+
/// Returns an iterator to the end of the operations.
53+
[[nodiscard]] auto end() const -> auto { return operations_.end(); }
54+
55+
/// Returns the number of operations in the NAComputation.
56+
[[nodiscard]] auto size() const -> std::size_t { return operations_.size(); }
57+
58+
/// Returns a reference to the operation at the given index.
59+
/// @param i The index of the operation.
60+
/// @return A reference to the operation at the given index.
61+
[[nodiscard]] auto operator[](const std::size_t i) -> Op& {
62+
return *operations_[i];
63+
}
64+
65+
/// Returns a const reference to the operation at the given index.
66+
/// @param i The index of the operation.
67+
/// @return A const reference to the operation at the given index.
68+
[[nodiscard]] auto operator[](const std::size_t i) const -> const Op& {
69+
return *operations_[i];
70+
}
71+
72+
/// Clears the operations in the NAComputation.
73+
auto clear() -> void { operations_.clear(); }
74+
75+
/// Returns the number of atoms used in the NAComputation.
76+
[[nodiscard]] auto getAtomsSize() const -> std::size_t {
77+
return atoms_.size();
3978
}
40-
NAComputation& operator=(const NAComputation& qc) {
41-
if (this != &qc) {
42-
initialPositions = qc.initialPositions;
43-
operations.clear();
44-
operations.reserve(qc.operations.size());
45-
std::transform(qc.operations.cbegin(), qc.operations.cend(),
46-
std::back_inserter(operations),
47-
[](const auto& op) { return op->clone(); });
48-
}
49-
return *this;
79+
80+
/// Returns the atoms used in the NAComputation.
81+
[[nodiscard]] auto getAtoms() const -> auto& { return atoms_; }
82+
83+
/// Returns the zones used in global operations within the NAComputation.
84+
[[nodiscard]] auto getZones() const -> auto& { return zones_; }
85+
86+
/// Returns the initial locations of the atoms.
87+
[[nodiscard]] auto getInitialLocations() const -> auto& {
88+
return initialLocations_;
5089
}
51-
virtual ~NAComputation() = default;
52-
template <class T> auto emplaceBack(std::unique_ptr<T>&& op) -> void {
53-
static_assert(std::is_base_of_v<NAOperation, T>,
54-
"T must be a subclass of NAOperation.");
55-
operations.emplace_back(std::move(op));
90+
91+
/// Returns the location of the given atom after the given operation.
92+
/// @param atom The atom to get the location for.
93+
/// @param op The operation to get the location after.
94+
/// @return The location of the atom after the operation.
95+
[[nodiscard]] auto getLocationOfAtomAfterOperation(const Atom& atom,
96+
const Op& op) const
97+
-> Location;
98+
99+
/// Emplaces a new atom with the given name and returns a reference to the
100+
/// newly created atom.
101+
/// @param name The name of the atom.
102+
/// @return A reference to the newly created atom.
103+
auto emplaceBackAtom(std::string name) -> const Atom& {
104+
return *atoms_.emplace_back(std::make_unique<Atom>(std::move(name)));
56105
}
57-
template <class T> auto emplaceBack(const std::unique_ptr<T>& op) -> void {
58-
static_assert(std::is_base_of_v<NAOperation, T>,
59-
"T must be a subclass of NAOperation.");
60-
operations.emplace_back(std::move(op));
106+
107+
/// Emplaces a new zone with the given name and returns a reference to the
108+
/// newly created zone.
109+
/// @param name The name of the zone.
110+
/// @return A reference to the newly created zone.
111+
auto emplaceBackZone(std::string name) -> const Zone& {
112+
return *zones_.emplace_back(std::make_unique<Zone>(std::move(name)));
61113
}
62-
template <class T, class... Args> auto emplaceBack(Args&&... args) -> void {
63-
static_assert(std::is_base_of_v<NAOperation, T>,
64-
"T must be a subclass of NAOperation.");
65-
operations.emplace_back(std::make_unique<T>(std::forward<Args>(args)...));
114+
115+
/// Emplaces a new initial location for the given atom with the given location
116+
/// and returns a reference to the newly created location.
117+
/// @param atom The atom to set the initial location for.
118+
/// @param loc The location of the atom.
119+
/// @return A reference to the newly created location.
120+
auto emplaceInitialLocation(const Atom& atom, const Location& loc)
121+
-> const Location& {
122+
return initialLocations_.emplace(&atom, loc).first->second;
66123
}
67-
auto clear(const bool clearInitialPositions = true) -> void {
68-
operations.clear();
69-
if (clearInitialPositions) {
70-
initialPositions.clear();
71-
}
124+
125+
/// Emplaces a new initial location for the given atom with the given
126+
/// arguments and returns a reference to the newly created location.
127+
/// @param atom The atom to set the initial location for.
128+
/// @param loc The parameters for the location of the atom.
129+
/// @return A reference to the newly created location.
130+
template <typename... Args>
131+
auto emplaceInitialLocation(const Atom& atom, Args&&... loc)
132+
-> const Location& {
133+
return initialLocations_
134+
.emplace(&atom,
135+
Location{static_cast<double>(std::forward<Args>(loc))...})
136+
.first->second;
72137
}
73-
[[nodiscard]] auto size() const -> std::size_t { return operations.size(); }
74-
[[nodiscard]] auto getInitialPositions() const
75-
-> const std::vector<std::shared_ptr<Point>>& {
76-
return initialPositions;
138+
139+
/// Emplaces a new operation of type T with the given operation and returns a
140+
/// reference to the newly created operation.
141+
/// @tparam T The concrete type of the operation.
142+
/// @param op The operation to emplace.
143+
/// @return A reference to the newly created operation.
144+
template <class T> auto emplaceBack(T&& op) -> const Op& {
145+
return *operations_.emplace_back(std::make_unique<T>(std::forward<T>(op)));
77146
}
78-
auto emplaceInitialPosition(std::shared_ptr<Point> p) -> void {
79-
initialPositions.emplace_back(std::move(p));
147+
148+
/// Emplaces a new operation of type T with the given arguments and returns a
149+
/// reference to the newly created operation.
150+
/// @tparam T The concrete type of the operation.
151+
/// @param args The arguments for the operation.
152+
/// @return A reference to the newly created operation.
153+
template <class T, typename... Args>
154+
auto emplaceBack(Args&&... args) -> const Op& {
155+
return *operations_.emplace_back(
156+
std::make_unique<T>(std::forward<Args>(args)...));
80157
}
158+
159+
/// Returns a string representation of the NAComputation.
81160
[[nodiscard]] auto toString() const -> std::string;
161+
/// Outputs the NAComputation to the given output stream, i.e., the string
162+
/// returned by toString().
163+
/// @param os The output stream to print the NAComputation to.
164+
/// @param qc The NAComputation to print.
165+
/// @return The output stream after printing the NAComputation.
82166
friend auto operator<<(std::ostream& os, const NAComputation& qc)
83167
-> std::ostream& {
84168
return os << qc.toString();
85169
}
86-
// Iterators (pass-through)
87-
auto begin() noexcept { return operations.begin(); }
88-
[[nodiscard]] auto begin() const noexcept { return operations.begin(); }
89-
[[nodiscard]] auto cbegin() const noexcept { return operations.cbegin(); }
90-
auto end() noexcept { return operations.end(); }
91-
[[nodiscard]] auto end() const noexcept { return operations.end(); }
92-
[[nodiscard]] auto cend() const noexcept { return operations.cend(); }
93-
auto rbegin() noexcept { return operations.rbegin(); }
94-
[[nodiscard]] auto rbegin() const noexcept { return operations.rbegin(); }
95-
[[nodiscard]] auto crbegin() const noexcept { return operations.crbegin(); }
96-
auto rend() noexcept { return operations.rend(); }
97-
[[nodiscard]] auto rend() const noexcept { return operations.rend(); }
98-
[[nodiscard]] auto crend() const noexcept { return operations.crend(); }
99-
100-
[[nodiscard]] auto validateAODConstraints() const -> bool;
170+
171+
/// Validates the NAComputation and checks whether all AOD constraints are
172+
/// fulfilled.
173+
/// Specifically,
174+
/// - each atom is loaded before it is moved
175+
/// - the relative order of loaded atoms is preserved
176+
/// - each atom is loaded before it is stored
177+
/// - each atom is stored before it is loaded (again)
178+
/// @returns a pair of a Boolean indicating whether the NAComputation is valid
179+
/// and a string containing the error message if the NAComputation is invalid.
180+
[[nodiscard]] auto validate() const -> std::pair<bool, std::string>;
101181
};
102182
} // namespace na

0 commit comments

Comments
 (0)