Skip to content
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

🎨 Refactor NAComputation with concrete base classes for every operation and ouput new .naviz format #846

Merged
merged 40 commits into from
Mar 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
c570770
♻️ Refactor NACOmputation, output new format
ystade Feb 8, 2025
3404c85
🧪 Work on tests
ystade Feb 8, 2025
a8758e7
🐛 Use unique pointers to avoid invalidated pointers
ystade Feb 8, 2025
5d28e53
🎨 Add hash for pair replacing FullOpType
ystade Feb 8, 2025
2975642
🐛 Fix bug in validate function
ystade Feb 8, 2025
1ab3262
🎨 Improve validation
ystade Feb 10, 2025
45514c8
🎨 Improve inteface of NAComputation
ystade Feb 10, 2025
35f8683
🎨 Pre-commit fixes
ystade Feb 10, 2025
c7b542d
🎨 Change function name
ystade Feb 10, 2025
bb59ce8
🎨 Implement clone function
ystade Feb 10, 2025
6bb958f
🎨 Add getter for initial locations
ystade Feb 10, 2025
9e8bf8c
🎨 Good stuff from try to make it copyable
ystade Feb 11, 2025
fce9539
🎨 Add getZones
ystade Feb 11, 2025
3b65ad3
🔥 Remove clone()
ystade Feb 11, 2025
ad6bb26
🎨 Remove superfluous link targets
ystade Feb 11, 2025
71a5fd1
🎨 Offer more constructors for locations
ystade Feb 11, 2025
dbcf43f
🐛 Fix creation of Location
ystade Feb 12, 2025
cd8ad9c
✅ Cover GlobalCZOp with tests
ystade Mar 3, 2025
779abd6
🐛 Add missing header
ystade Mar 3, 2025
db68ced
🐛 Link against missing target
ystade Mar 3, 2025
efe341d
🎨 Add missing header
ystade Mar 3, 2025
7caeb30
🎨 Add missing header
ystade Mar 3, 2025
fe38f0f
🩹 Fix header includes
ystade Mar 3, 2025
0bf5318
💚 Work on ci warnings
ystade Mar 3, 2025
878c01a
📝 Add more comments
ystade Mar 3, 2025
d1efe8f
💚 Work on CI warnings
ystade Mar 3, 2025
6552eb1
🎨 WIP incorporate feedback
ystade Mar 5, 2025
21f311e
📝 Add mostly docs
ystade Mar 5, 2025
a8aa02a
🐛 Fix all open construction sites
ystade Mar 5, 2025
4edea4a
🎨 Move isGLobal to qc::Operation
ystade Mar 5, 2025
4279aab
🐛 Fix test
ystade Mar 5, 2025
f1f05a7
🚨 Update clang-tidy
ystade Mar 5, 2025
21030b8
🔧 Handle empty quantum and classical registers in qiskit to MQT trans…
burgholzer Mar 4, 2025
ed9a5a6
🔧 Fix clang-tidy again
ystade Mar 5, 2025
3f3aea5
Merge branch 'refs/heads/main' into refactor-na-computation
burgholzer Mar 6, 2025
9689ac3
🔥 remove redundant test file
burgholzer Mar 6, 2025
bd59ac3
🚨 fix shadowing compiler warning
burgholzer Mar 6, 2025
6d71857
🔥 remove redundant utils file
burgholzer Mar 6, 2025
9ff5af2
📝🎨 Enhance documentation for NAComputation and related classes
burgholzer Mar 6, 2025
493a509
🎨 small refinements in the NA computation
burgholzer Mar 6, 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
2 changes: 1 addition & 1 deletion .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ CheckOptions:
- key: readability-identifier-naming.MemberCase
value: camelBack
- key: readability-identifier-naming.MemberIgnoredRegexp
value: ".*ZX.*|.*SWAP.*|.*CEX.*|.*DD.*|.*EQ.*"
value: ".*ZX.*|.*SWAP.*|.*CEX.*|.*DD.*|.*EQ.*|.*_"
- key: readability-identifier-naming.MethodCase
value: camelBack
- key: readability-identifier-naming.ParameterCase
Expand Down
2 changes: 2 additions & 0 deletions include/mqt-core/ir/operations/CompoundOperation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ class CompoundOperation final : public Operation {

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

[[nodiscard]] bool isGlobal(size_t nQubits) const noexcept override;

void addControl(Control c) override;

void clearControls() override;
Expand Down
13 changes: 13 additions & 0 deletions include/mqt-core/ir/operations/Operation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,19 @@ class Operation {

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

/**
* @brief Checks whether a gate is global.
* @details A StandardOperation is global if it acts on all qubits.
* A CompoundOperation is global if all its sub-operations are
* StandardOperations of the same type with the same parameters acting on all
* qubits. The latter is what a QASM line like `ry(π) q;` is translated to in
* MQT Core. All other operations are not global.
* @return True if the operation is global, false otherwise.
*/
[[nodiscard]] virtual bool isGlobal(size_t /* unused */) const {
return false;
}

[[nodiscard]] virtual bool actsOn(const Qubit i) const {
for (const auto& t : targets) {
if (t == i) {
Expand Down
2 changes: 2 additions & 0 deletions include/mqt-core/ir/operations/StandardOperation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ class StandardOperation : public Operation {

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

[[nodiscard]] bool isGlobal(size_t nQubits) const override;

void addControl(const Control c) override {
if (actsOn(c.qubit)) {
throw QFRException("Cannot add control on qubit " +
Expand Down
210 changes: 145 additions & 65 deletions include/mqt-core/na/NAComputation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,96 +7,176 @@
* Licensed under the MIT License
*/

/** @file
* @brief Defines a class for representing neutral atom computations.
*/

#pragma once

#include "NADefinitions.hpp"
#include "operations/NAOperation.hpp"
#include "na/entities/Atom.hpp"
#include "na/entities/Location.hpp"
#include "na/entities/Zone.hpp"
#include "na/operations/Op.hpp"

#include <algorithm>
#include <cstddef>
#include <iterator>
#include <memory>
#include <ostream>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

namespace na {
class NAComputation {
/// Represents a neutral atom computation.
class NAComputation final {
protected:
std::vector<std::shared_ptr<Point>> initialPositions;
std::vector<std::unique_ptr<NAOperation>> operations;
/// The operations in the NA computation.
std::vector<std::unique_ptr<Op>> operations_;
/// The atoms used in the NA computation.
std::vector<std::unique_ptr<Atom>> atoms_;
/// The zones used in the NA computation.
std::vector<std::unique_ptr<Zone>> zones_;
/// The initial locations of the atoms.
std::unordered_map<const Atom*, Location> initialLocations_;

public:
NAComputation() = default;
NAComputation(NAComputation&& qc) noexcept = default;
NAComputation& operator=(NAComputation&& qc) noexcept = default;
NAComputation(const NAComputation& qc)
: initialPositions(qc.initialPositions) {
operations.reserve(qc.operations.size());
std::transform(qc.operations.cbegin(), qc.operations.cend(),
std::back_inserter(operations),
[](const auto& op) { return op->clone(); });
/// Returns an iterator to the beginning of the operations.
[[nodiscard]] auto begin() -> auto { return operations_.begin(); }

/// Returns an iterator to the beginning of the operations.
[[nodiscard]] auto begin() const -> auto { return operations_.begin(); }

/// Returns an iterator to the end of the operations.
[[nodiscard]] auto end() -> auto { return operations_.end(); }

/// Returns an iterator to the end of the operations.
[[nodiscard]] auto end() const -> auto { return operations_.end(); }

/// Returns the number of operations in the NAComputation.
[[nodiscard]] auto size() const -> std::size_t { return operations_.size(); }

/// Returns a reference to the operation at the given index.
/// @param i The index of the operation.
/// @return A reference to the operation at the given index.
[[nodiscard]] auto operator[](const std::size_t i) -> Op& {
return *operations_[i];
}

/// Returns a const reference to the operation at the given index.
/// @param i The index of the operation.
/// @return A const reference to the operation at the given index.
[[nodiscard]] auto operator[](const std::size_t i) const -> const Op& {
return *operations_[i];
}

/// Clears the operations in the NAComputation.
auto clear() -> void { operations_.clear(); }

/// Returns the number of atoms used in the NAComputation.
[[nodiscard]] auto getAtomsSize() const -> std::size_t {
return atoms_.size();
}
NAComputation& operator=(const NAComputation& qc) {
if (this != &qc) {
initialPositions = qc.initialPositions;
operations.clear();
operations.reserve(qc.operations.size());
std::transform(qc.operations.cbegin(), qc.operations.cend(),
std::back_inserter(operations),
[](const auto& op) { return op->clone(); });
}
return *this;

/// Returns the atoms used in the NAComputation.
[[nodiscard]] auto getAtoms() const -> auto& { return atoms_; }

/// Returns the zones used in global operations within the NAComputation.
[[nodiscard]] auto getZones() const -> auto& { return zones_; }

/// Returns the initial locations of the atoms.
[[nodiscard]] auto getInitialLocations() const -> auto& {
return initialLocations_;
}
virtual ~NAComputation() = default;
template <class T> auto emplaceBack(std::unique_ptr<T>&& op) -> void {
static_assert(std::is_base_of_v<NAOperation, T>,
"T must be a subclass of NAOperation.");
operations.emplace_back(std::move(op));

/// Returns the location of the given atom after the given operation.
/// @param atom The atom to get the location for.
/// @param op The operation to get the location after.
/// @return The location of the atom after the operation.
[[nodiscard]] auto getLocationOfAtomAfterOperation(const Atom& atom,
const Op& op) const
-> Location;

/// Emplaces a new atom with the given name and returns a reference to the
/// newly created atom.
/// @param name The name of the atom.
/// @return A reference to the newly created atom.
auto emplaceBackAtom(std::string name) -> const Atom& {
return *atoms_.emplace_back(std::make_unique<Atom>(std::move(name)));
}
template <class T> auto emplaceBack(const std::unique_ptr<T>& op) -> void {
static_assert(std::is_base_of_v<NAOperation, T>,
"T must be a subclass of NAOperation.");
operations.emplace_back(std::move(op));

/// Emplaces a new zone with the given name and returns a reference to the
/// newly created zone.
/// @param name The name of the zone.
/// @return A reference to the newly created zone.
auto emplaceBackZone(std::string name) -> const Zone& {
return *zones_.emplace_back(std::make_unique<Zone>(std::move(name)));
}
template <class T, class... Args> auto emplaceBack(Args&&... args) -> void {
static_assert(std::is_base_of_v<NAOperation, T>,
"T must be a subclass of NAOperation.");
operations.emplace_back(std::make_unique<T>(std::forward<Args>(args)...));

/// Emplaces a new initial location for the given atom with the given location
/// and returns a reference to the newly created location.
/// @param atom The atom to set the initial location for.
/// @param loc The location of the atom.
/// @return A reference to the newly created location.
auto emplaceInitialLocation(const Atom& atom, const Location& loc)
-> const Location& {
return initialLocations_.emplace(&atom, loc).first->second;
}
auto clear(const bool clearInitialPositions = true) -> void {
operations.clear();
if (clearInitialPositions) {
initialPositions.clear();
}

/// Emplaces a new initial location for the given atom with the given
/// arguments and returns a reference to the newly created location.
/// @param atom The atom to set the initial location for.
/// @param loc The parameters for the location of the atom.
/// @return A reference to the newly created location.
template <typename... Args>
auto emplaceInitialLocation(const Atom& atom, Args&&... loc)
-> const Location& {
return initialLocations_
.emplace(&atom,
Location{static_cast<double>(std::forward<Args>(loc))...})
.first->second;
}
[[nodiscard]] auto size() const -> std::size_t { return operations.size(); }
[[nodiscard]] auto getInitialPositions() const
-> const std::vector<std::shared_ptr<Point>>& {
return initialPositions;

/// Emplaces a new operation of type T with the given operation and returns a
/// reference to the newly created operation.
/// @tparam T The concrete type of the operation.
/// @param op The operation to emplace.
/// @return A reference to the newly created operation.
template <class T> auto emplaceBack(T&& op) -> const Op& {
return *operations_.emplace_back(std::make_unique<T>(std::forward<T>(op)));
}
auto emplaceInitialPosition(std::shared_ptr<Point> p) -> void {
initialPositions.emplace_back(std::move(p));

/// Emplaces a new operation of type T with the given arguments and returns a
/// reference to the newly created operation.
/// @tparam T The concrete type of the operation.
/// @param args The arguments for the operation.
/// @return A reference to the newly created operation.
template <class T, typename... Args>
auto emplaceBack(Args&&... args) -> const Op& {
return *operations_.emplace_back(
std::make_unique<T>(std::forward<Args>(args)...));
}

/// Returns a string representation of the NAComputation.
[[nodiscard]] auto toString() const -> std::string;
/// Outputs the NAComputation to the given output stream, i.e., the string
/// returned by toString().
/// @param os The output stream to print the NAComputation to.
/// @param qc The NAComputation to print.
/// @return The output stream after printing the NAComputation.
friend auto operator<<(std::ostream& os, const NAComputation& qc)
-> std::ostream& {
return os << qc.toString();
}
// Iterators (pass-through)
auto begin() noexcept { return operations.begin(); }
[[nodiscard]] auto begin() const noexcept { return operations.begin(); }
[[nodiscard]] auto cbegin() const noexcept { return operations.cbegin(); }
auto end() noexcept { return operations.end(); }
[[nodiscard]] auto end() const noexcept { return operations.end(); }
[[nodiscard]] auto cend() const noexcept { return operations.cend(); }
auto rbegin() noexcept { return operations.rbegin(); }
[[nodiscard]] auto rbegin() const noexcept { return operations.rbegin(); }
[[nodiscard]] auto crbegin() const noexcept { return operations.crbegin(); }
auto rend() noexcept { return operations.rend(); }
[[nodiscard]] auto rend() const noexcept { return operations.rend(); }
[[nodiscard]] auto crend() const noexcept { return operations.crend(); }

[[nodiscard]] auto validateAODConstraints() const -> bool;

/// Validates the NAComputation and checks whether all AOD constraints are
/// fulfilled.
/// Specifically,
/// - each atom is loaded before it is moved
/// - the relative order of loaded atoms is preserved
/// - each atom is loaded before it is stored
/// - each atom is stored before it is loaded (again)
/// @returns a pair of a Boolean indicating whether the NAComputation is valid
/// and a string containing the error message if the NAComputation is invalid.
[[nodiscard]] auto validate() const -> std::pair<bool, std::string>;
};
} // namespace na
Loading
Loading