Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ This project adheres to [Semantic Versioning], with the exception that minor rel

_If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#unreleased)._

### Added

- ✨ Add a (dummy) decomposer component to the zoned neutral atom compiler ([#902]) ([**@ystade**])

## [3.5.0]

_If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#350)._
Expand Down Expand Up @@ -178,6 +182,7 @@ _📚 Refer to the [GitHub Release Notes] for previous changelogs._

<!-- PR links -->

[#902]: https://github.com/munich-quantum-toolkit/qmap/pull/902
[#874]: https://github.com/munich-quantum-toolkit/qmap/pull/874
[#862]: https://github.com/munich-quantum-toolkit/qmap/pull/862
[#859]: https://github.com/munich-quantum-toolkit/qmap/pull/859
Expand Down
47 changes: 35 additions & 12 deletions include/na/zoned/Compiler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "Architecture.hpp"
#include "code_generator/CodeGenerator.hpp"
#include "decomposer/NoOpDecomposer.hpp"
#include "ir/QuantumComputation.hpp"
#include "ir/operations/Operation.hpp"
#include "layout_synthesizer/PlaceAndRouteSynthesizer.hpp"
Expand All @@ -25,7 +26,6 @@
#include <cassert>
#include <chrono>
#include <functional>
#include <iostream>
#include <memory>
#include <nlohmann/json.hpp>
#include <spdlog/spdlog.h>
Expand All @@ -43,9 +43,10 @@ namespace na::zoned {
* allowing for better performance than having the components as members of the
* compiler and setting them at runtime.
*/
template <class ConcreteType, class Scheduler, class ReuseAnalyzer,
class LayoutSynthesizer, class CodeGenerator>
template <class ConcreteType, class Scheduler, class Decomposer,
class ReuseAnalyzer, class LayoutSynthesizer, class CodeGenerator>
class Compiler : protected Scheduler,
protected Decomposer,
protected ReuseAnalyzer,
protected LayoutSynthesizer,
protected CodeGenerator {
Expand All @@ -59,6 +60,8 @@ class Compiler : protected Scheduler,
struct Config {
/// Configuration for the scheduler
typename Scheduler::Config schedulerConfig{};
/// Configuration for the decomposer
typename Decomposer::Config decomposerConfig{};
/// Configuration for the reuse analyzer
typename ReuseAnalyzer::Config reuseAnalyzerConfig{};
/// Configuration for the layout synthesizer
Expand All @@ -68,28 +71,29 @@ class Compiler : protected Scheduler,
/// Log level for the compiler
spdlog::level::level_enum logLevel = spdlog::level::info;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Config, schedulerConfig,
decomposerConfig,
reuseAnalyzerConfig,
layoutSynthesizerConfig,
codeGeneratorConfig, logLevel);
};

/**
* Collection of statistics collected during the compilation process for the
* different components.
*/
struct Statistics {
int64_t schedulingTime; ///< Time taken for scheduling in us
int64_t decomposingTime; ///< Time taken for decomposing in us
int64_t reuseAnalysisTime; ///< Time taken for reuse analysis in us
/// Statistics collected during layout synthesis.
typename LayoutSynthesizer::Statistics layoutSynthesizerStatistics;
int64_t layoutSynthesisTime; ///< Time taken for layout synthesis in us
int64_t codeGenerationTime; ///< Time taken for code generation in us
int64_t totalTime; ///< Total time taken for the compilation in us
NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Statistics, schedulingTime,
reuseAnalysisTime,
layoutSynthesizerStatistics,
layoutSynthesisTime,
codeGenerationTime,
totalTime);
NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(
Statistics, schedulingTime, decomposingTime, reuseAnalysisTime,
layoutSynthesizerStatistics, layoutSynthesisTime, codeGenerationTime,
totalTime);
};

private:
Expand All @@ -106,6 +110,7 @@ class Compiler : protected Scheduler,
*/
Compiler(const Architecture& architecture, const Config& config)
: Scheduler(architecture, config.schedulerConfig),
Decomposer(architecture, config.decomposerConfig),
ReuseAnalyzer(architecture, config.reuseAnalyzerConfig),
LayoutSynthesizer(architecture, config.layoutSynthesizerConfig),
CodeGenerator(architecture, config.codeGeneratorConfig),
Expand Down Expand Up @@ -195,6 +200,17 @@ class Compiler : protected Scheduler,
}
#endif // SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_DEBUG

SPDLOG_DEBUG("Decomposing...");
const auto decomposingStart = std::chrono::system_clock::now();
const auto& decomposedSingleQubitGateLayers =
SELF.decompose(singleQubitGateLayers);
const auto decomposingEnd = std::chrono::system_clock::now();
statistics_.decomposingTime =
std::chrono::duration_cast<std::chrono::microseconds>(decomposingEnd -
decomposingStart)
.count();
SPDLOG_INFO("Time for decomposing: {}us", statistics_.decomposingTime);

SPDLOG_DEBUG("Analyzing reuse...");
const auto reuseAnalysisStart = std::chrono::system_clock::now();
const auto& reuseQubits = SELF.analyzeReuse(twoQubitGateLayers);
Expand Down Expand Up @@ -222,7 +238,7 @@ class Compiler : protected Scheduler,
SPDLOG_DEBUG("Generating code...");
const auto codeGenerationStart = std::chrono::system_clock::now();
NAComputation code =
SELF.generate(singleQubitGateLayers, placement, routing);
SELF.generate(decomposedSingleQubitGateLayers, placement, routing);
const auto codeGenerationEnd = std::chrono::system_clock::now();
assert(code.validate().first);
statistics_.codeGenerationTime =
Expand All @@ -239,6 +255,7 @@ class Compiler : protected Scheduler,
SPDLOG_INFO("Total time: {}us", statistics_.totalTime);
return code;
}

/// @return the statistics collected during the compilation process.
[[nodiscard]] auto getStatistics() const -> const Statistics& {
return statistics_;
Expand All @@ -253,17 +270,20 @@ class RoutingAgnosticSynthesizer
RoutingAgnosticSynthesizer(const Architecture& architecture,
const Config& config)
: PlaceAndRouteSynthesizer(architecture, config) {}

explicit RoutingAgnosticSynthesizer(const Architecture& architecture)
: PlaceAndRouteSynthesizer(architecture) {}
};

class RoutingAgnosticCompiler final
: public Compiler<RoutingAgnosticCompiler, ASAPScheduler,
: public Compiler<RoutingAgnosticCompiler, ASAPScheduler, NoOpDecomposer,
VertexMatchingReuseAnalyzer, RoutingAgnosticSynthesizer,
CodeGenerator> {
public:
RoutingAgnosticCompiler(const Architecture& architecture,
const Config& config)
: Compiler(architecture, config) {}

explicit RoutingAgnosticCompiler(const Architecture& architecture)
: Compiler(architecture) {}
};
Expand All @@ -275,16 +295,19 @@ class RoutingAwareSynthesizer
RoutingAwareSynthesizer(const Architecture& architecture,
const Config& config)
: PlaceAndRouteSynthesizer(architecture, config) {}

explicit RoutingAwareSynthesizer(const Architecture& architecture)
: PlaceAndRouteSynthesizer(architecture) {}
};

class RoutingAwareCompiler final
: public Compiler<RoutingAwareCompiler, ASAPScheduler,
: public Compiler<RoutingAwareCompiler, ASAPScheduler, NoOpDecomposer,
VertexMatchingReuseAnalyzer, RoutingAwareSynthesizer,
CodeGenerator> {
public:
RoutingAwareCompiler(const Architecture& architecture, const Config& config)
: Compiler(architecture, config) {}

explicit RoutingAwareCompiler(const Architecture& architecture)
: Compiler(architecture) {}
};
Expand Down
8 changes: 6 additions & 2 deletions include/na/zoned/Types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,18 @@
#include "na/zoned/Architecture.hpp"

#include <array>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <vector>

namespace na::zoned {
/// A list of single-qubit gates representing a single-qubit gate layer.
using SingleQubitGateLayer =
/// A non-owning reference layer of single-qubit gates (used for scheduling
/// output).
using SingleQubitGateRefLayer =
std::vector<std::reference_wrapper<const qc::Operation>>;
/// An owning layer of single-qubit gates (used after decomposition).
using SingleQubitGateLayer = std::vector<std::unique_ptr<const qc::Operation>>;
/// A pair of qubits as an array that allows iterating over the qubits.
using QubitPair = std::array<qc::Qubit, 2>;
/// A list of two-qubit gates representing a two-qubit gate layer.
Expand Down
35 changes: 35 additions & 0 deletions include/na/zoned/decomposer/DecomposerBase.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
* Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
* All rights reserved.
*
* SPDX-License-Identifier: MIT
*
* Licensed under the MIT License
*/

#pragma once

#include "na/zoned/Types.hpp"

#include <vector>

namespace na::zoned {
/**
* The Abstract Base Class for the Decomposer of the MQT's Zoned Neutral Atom
* Compiler.
*/
class DecomposerBase {
public:
virtual ~DecomposerBase() = default;
/**
* This function defines the interface of the decomposer.
* @param singleQubitGateLayers are the layers of single-qubit gates that are
* meant to be first decomposed into the native gate set.
* @return the new single-qubit gate layers
*/
[[nodiscard]] virtual auto decompose(
const std::vector<SingleQubitGateRefLayer>& singleQubitGateLayers) const
-> std::vector<SingleQubitGateLayer> = 0;
};
} // namespace na::zoned
55 changes: 55 additions & 0 deletions include/na/zoned/decomposer/NoOpDecomposer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
* Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
* All rights reserved.
*
* SPDX-License-Identifier: MIT
*
* Licensed under the MIT License
*/

#pragma once

#include "na/zoned/Architecture.hpp"
#include "na/zoned/Types.hpp"
#include "na/zoned/decomposer/DecomposerBase.hpp"

#include <nlohmann/json.hpp>
#include <vector>

namespace na::zoned {
/**
* The class NoOpDecomposer implements a dummy no-op decomposer that just copies
* every operation.
*/
class NoOpDecomposer : public DecomposerBase {

public:
/// The configuration of the NoOpDecomposer
struct Config {
template <
typename BasicJsonType,
nlohmann::detail::enable_if_t<
nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0>
friend void to_json(BasicJsonType& /* unused */,
const Config& /* unused */) {}

template <
typename BasicJsonType,
nlohmann::detail::enable_if_t<
nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0>
friend void from_json(const BasicJsonType& /* unused */,
Config& /* unused */) {}
};

/**
* Create a new NoOpDecomposer.
*/
NoOpDecomposer(const Architecture& /* unused */, const Config& /* unused */) {
}

[[nodiscard]] auto decompose(
const std::vector<SingleQubitGateRefLayer>& singleQubitGateLayers) const
-> std::vector<SingleQubitGateLayer> override;
};
} // namespace na::zoned
2 changes: 1 addition & 1 deletion include/na/zoned/scheduler/ASAPScheduler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class ASAPScheduler : public SchedulerBase {
* operations. A pair of qubits represents every two-qubit operation.
*/
[[nodiscard]] auto schedule(const qc::QuantumComputation& qc) const
-> std::pair<std::vector<SingleQubitGateLayer>,
-> std::pair<std::vector<SingleQubitGateRefLayer>,
std::vector<TwoQubitGateLayer>>;
};
} // namespace na::zoned
2 changes: 1 addition & 1 deletion include/na/zoned/scheduler/SchedulerBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class SchedulerBase {
* operations. A pair of qubits represents every two-qubit operation.
*/
[[nodiscard]] virtual auto schedule(const qc::QuantumComputation& qc) const
-> std::pair<std::vector<SingleQubitGateLayer>,
-> std::pair<std::vector<SingleQubitGateRefLayer>,
std::vector<TwoQubitGateLayer>> = 0;
};
} // namespace na::zoned
Loading