diff --git a/include/na/zoned/Compiler.hpp b/include/na/zoned/Compiler.hpp index 4f1b013b0..1a7bb7c58 100644 --- a/include/na/zoned/Compiler.hpp +++ b/include/na/zoned/Compiler.hpp @@ -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" @@ -25,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -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 +template class Compiler : protected Scheduler, + protected Decomposer, protected ReuseAnalyzer, protected LayoutSynthesizer, protected CodeGenerator { @@ -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 @@ -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: @@ -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), @@ -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(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); @@ -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 = @@ -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_; @@ -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 { public: RoutingAgnosticCompiler(const Architecture& architecture, const Config& config) : Compiler(architecture, config) {} + explicit RoutingAgnosticCompiler(const Architecture& architecture) : Compiler(architecture) {} }; @@ -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 { public: RoutingAwareCompiler(const Architecture& architecture, const Config& config) : Compiler(architecture, config) {} + explicit RoutingAwareCompiler(const Architecture& architecture) : Compiler(architecture) {} }; diff --git a/include/na/zoned/Types.hpp b/include/na/zoned/Types.hpp index c30560aeb..9227484c3 100644 --- a/include/na/zoned/Types.hpp +++ b/include/na/zoned/Types.hpp @@ -14,14 +14,18 @@ #include "na/zoned/Architecture.hpp" #include +#include #include #include #include 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>; +/// An owning layer of single-qubit gates (used after decomposition). +using SingleQubitGateLayer = std::vector>; /// A pair of qubits as an array that allows iterating over the qubits. using QubitPair = std::array; /// A list of two-qubit gates representing a two-qubit gate layer. diff --git a/include/na/zoned/decomposer/DecomposerBase.hpp b/include/na/zoned/decomposer/DecomposerBase.hpp new file mode 100644 index 000000000..997dd095a --- /dev/null +++ b/include/na/zoned/decomposer/DecomposerBase.hpp @@ -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 + +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& singleQubitGateLayers) const + -> std::vector = 0; +}; +} // namespace na::zoned diff --git a/include/na/zoned/decomposer/NoOpDecomposer.hpp b/include/na/zoned/decomposer/NoOpDecomposer.hpp new file mode 100644 index 000000000..65b8c4f07 --- /dev/null +++ b/include/na/zoned/decomposer/NoOpDecomposer.hpp @@ -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 +#include + +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::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::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& singleQubitGateLayers) const + -> std::vector override; +}; +} // namespace na::zoned diff --git a/include/na/zoned/scheduler/ASAPScheduler.hpp b/include/na/zoned/scheduler/ASAPScheduler.hpp index ed4c3052b..38884dbbb 100644 --- a/include/na/zoned/scheduler/ASAPScheduler.hpp +++ b/include/na/zoned/scheduler/ASAPScheduler.hpp @@ -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::pair, std::vector>; }; } // namespace na::zoned diff --git a/include/na/zoned/scheduler/SchedulerBase.hpp b/include/na/zoned/scheduler/SchedulerBase.hpp index 95b6c7fb9..74c0bbaac 100644 --- a/include/na/zoned/scheduler/SchedulerBase.hpp +++ b/include/na/zoned/scheduler/SchedulerBase.hpp @@ -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::pair, std::vector> = 0; }; } // namespace na::zoned diff --git a/src/na/zoned/code_generator/CodeGenerator.cpp b/src/na/zoned/code_generator/CodeGenerator.cpp index 5b5eebc65..0cc00751d 100644 --- a/src/na/zoned/code_generator/CodeGenerator.cpp +++ b/src/na/zoned/code_generator/CodeGenerator.cpp @@ -62,12 +62,11 @@ auto CodeGenerator::appendSingleQubitGates( // This flag is used for circuit consisting of only one qubit since in this // case, global and local gates are the same. bool singleQubitGate = false; - if (op.get().isGlobal(nQubits)) { + if (op->isGlobal(nQubits)) { // a global operation can be wrapped in a compound operation or a standard // operation acting on all qubits - if (op.get().isCompoundOperation()) { - const auto& compOp = - dynamic_cast(op.get()); + if (op->isCompoundOperation()) { + const auto& compOp = dynamic_cast(*op); const auto opType = compOp.front()->getType(); if (opType == qc::RY) { code.emplaceBack(globalZone, @@ -80,9 +79,8 @@ auto CodeGenerator::appendSingleQubitGates( assert(false); } } else { - if (const auto opType = op.get().getType(); opType == qc::RY) { - code.emplaceBack(globalZone, - op.get().getParameter().front()); + if (const auto opType = op->getType(); opType == qc::RY) { + code.emplaceBack(globalZone, op->getParameter().front()); } else if (opType == qc::Y) { code.emplaceBack(globalZone, qc::PI); } else if (nQubits == 1) { @@ -101,21 +99,20 @@ auto CodeGenerator::appendSingleQubitGates( if (singleQubitGate) { // one qubit gates act exactly on one qubit and are converted to local // gates - assert(op.get().getNqubits() == 1); - const qc::Qubit qubit = op.get().getTargets().front(); + assert(op->getNqubits() == 1); + const qc::Qubit qubit = op->getTargets().front(); // By default, all variants of rotational z-gates are supported - if (op.get().getType() == qc::RZ || op.get().getType() == qc::P) { - code.emplaceBack(atoms[qubit], - op.get().getParameter().front()); - } else if (op.get().getType() == qc::Z) { + if (op->getType() == qc::RZ || op->getType() == qc::P) { + code.emplaceBack(atoms[qubit], op->getParameter().front()); + } else if (op->getType() == qc::Z) { code.emplaceBack(atoms[qubit], qc::PI); - } else if (op.get().getType() == qc::S) { + } else if (op->getType() == qc::S) { code.emplaceBack(atoms[qubit], qc::PI_2); - } else if (op.get().getType() == qc::Sdg) { + } else if (op->getType() == qc::Sdg) { code.emplaceBack(atoms[qubit], -qc::PI_2); - } else if (op.get().getType() == qc::T) { + } else if (op->getType() == qc::T) { code.emplaceBack(atoms[qubit], qc::PI_4); - } else if (op.get().getType() == qc::Tdg) { + } else if (op->getType() == qc::Tdg) { code.emplaceBack(atoms[qubit], -qc::PI_4); } else { // in this case, the gate is not any variant of a rotational z-gate. @@ -123,37 +120,35 @@ auto CodeGenerator::appendSingleQubitGates( if (config_.warnUnsupportedGates) { SPDLOG_WARN( "Gate not part of basis gates will be inserted as U3 gate: {}", - qc::toString(op.get().getType())); + qc::toString(op->getType())); } - if (op.get().getType() == qc::U) { - code.emplaceBack( - atoms[qubit], op.get().getParameter().front(), - op.get().getParameter().at(1), op.get().getParameter().at(2)); - } else if (op.get().getType() == qc::U2) { + if (op->getType() == qc::U) { + code.emplaceBack(atoms[qubit], op->getParameter().front(), + op->getParameter().at(1), + op->getParameter().at(2)); + } else if (op->getType() == qc::U2) { code.emplaceBack(atoms[qubit], qc::PI_2, - op.get().getParameter().front(), - op.get().getParameter().at(1)); - } else if (op.get().getType() == qc::RX) { - code.emplaceBack(atoms[qubit], - op.get().getParameter().front(), -qc::PI_2, - qc::PI_2); - } else if (op.get().getType() == qc::RY) { - code.emplaceBack(atoms[qubit], - op.get().getParameter().front(), 0, 0); - } else if (op.get().getType() == qc::H) { + op->getParameter().front(), + op->getParameter().at(1)); + } else if (op->getType() == qc::RX) { + code.emplaceBack(atoms[qubit], op->getParameter().front(), + -qc::PI_2, qc::PI_2); + } else if (op->getType() == qc::RY) { + code.emplaceBack(atoms[qubit], op->getParameter().front(), + 0, 0); + } else if (op->getType() == qc::H) { code.emplaceBack(atoms[qubit], qc::PI_2, 0, qc::PI); - } else if (op.get().getType() == qc::X) { + } else if (op->getType() == qc::X) { code.emplaceBack(atoms[qubit], qc::PI, 0, qc::PI); - } else if (op.get().getType() == qc::Y) { + } else if (op->getType() == qc::Y) { code.emplaceBack(atoms[qubit], qc::PI, qc::PI_2, qc::PI_2); - } else if (op.get().getType() == qc::Vdg) { + } else if (op->getType() == qc::Vdg) { code.emplaceBack(atoms[qubit], -qc::PI_2, qc::PI_2, -qc::PI_2); - } else if (op.get().getType() == qc::SX) { + } else if (op->getType() == qc::SX) { code.emplaceBack(atoms[qubit], qc::PI_2, -qc::PI_2, qc::PI_2); - } else if (op.get().getType() == qc::SXdg || - op.get().getType() == qc::V) { + } else if (op->getType() == qc::SXdg || op->getType() == qc::V) { code.emplaceBack(atoms[qubit], -qc::PI_2, -qc::PI_2, qc::PI_2); } else { @@ -161,7 +156,7 @@ auto CodeGenerator::appendSingleQubitGates( // gate is not included in the output. std::ostringstream oss; oss << "\033[1;31m[ERROR]\033[0m Unsupported single-qubit gate: " - << op.get().getType() << "\n"; + << op->getType() << "\n"; throw std::invalid_argument(oss.str()); } } diff --git a/src/na/zoned/decomposer/NoOpDecomposer.cpp b/src/na/zoned/decomposer/NoOpDecomposer.cpp new file mode 100644 index 000000000..6c8ae3973 --- /dev/null +++ b/src/na/zoned/decomposer/NoOpDecomposer.cpp @@ -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 + */ + +#include "na/zoned/decomposer/NoOpDecomposer.hpp" + +#include "ir/QuantumComputation.hpp" +#include "na/zoned/Architecture.hpp" + +#include +#include + +namespace na::zoned { +auto NoOpDecomposer::decompose( + const std::vector& singleQubitGateLayers) const + -> std::vector { + std::vector result; + result.reserve(singleQubitGateLayers.size()); + for (const auto& layer : singleQubitGateLayers) { + SingleQubitGateLayer newLayer; + newLayer.reserve(layer.size()); + for (const auto& opRef : layer) { + newLayer.emplace_back(opRef.get().clone()); + } + result.emplace_back(std::move(newLayer)); + } + return result; +} +} // namespace na::zoned diff --git a/src/na/zoned/scheduler/ASAPScheduler.cpp b/src/na/zoned/scheduler/ASAPScheduler.cpp index dec4dba87..939378303 100644 --- a/src/na/zoned/scheduler/ASAPScheduler.cpp +++ b/src/na/zoned/scheduler/ASAPScheduler.cpp @@ -53,14 +53,14 @@ ASAPScheduler::ASAPScheduler(const Architecture& architecture, } } auto ASAPScheduler::schedule(const qc::QuantumComputation& qc) const - -> std::pair, + -> std::pair, std::vector> { if (qc.empty()) { // early exit if there are no operations to schedule - return std::pair{std::vector{}, + return std::pair{std::vector{}, std::vector{}}; } - std::vector singleQubitGateLayers(1); + std::vector singleQubitGateLayers(1); std::vector twoQubitGateLayers(0); // the following vector contains a mapping from qubits to the layer where // the next two-qubit gate can be scheduled for that qubit, i.e., the layer diff --git a/test/na/zoned/test_code_generator.cpp b/test/na/zoned/test_code_generator.cpp index 677b9c45c..02a93b2cb 100644 --- a/test/na/zoned/test_code_generator.cpp +++ b/test/na/zoned/test_code_generator.cpp @@ -14,17 +14,21 @@ #include "ir/operations/SymbolicOperation.hpp" #include "na/zoned/code_generator/CodeGenerator.hpp" -#include #include -#include #include -#include -#include -#include +#include #include #include namespace na::zoned { +template +auto makeSingleOpLayer(Args&&... args) { + SingleQubitGateLayer layer; + layer.emplace_back(std::make_unique(std::forward(args)...)); + std::vector layers; + layers.emplace_back(std::move(layer)); + return layers; +} constexpr std::string_view architectureJson = R"({ "name": "code_generator_architecture", "storage_zones": [{ @@ -62,46 +66,37 @@ TEST_F(CodeGeneratorGenerateTest, Empty) { const auto& slm = *architecture.storageZones.front(); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}}}, - std::vector>>{}) + .generate(std::vector{}, + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n"); } TEST_F(CodeGeneratorGenerateTest, GlobalRYGate) { const auto& slm = *architecture.storageZones.front(); - const auto ry = qc::StandardOperation(0, qc::RY, {0.1}); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {ry}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}}}, - std::vector>>{}) + .generate(makeSingleOpLayer(0U, qc::RY, + std::vector{0.1}), + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "@+ ry 0.10000 global\n"); } TEST_F(CodeGeneratorGenerateTest, GlobalYGate) { const auto& slm = *architecture.storageZones.front(); - const auto y = qc::StandardOperation(0, qc::Y); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {y}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}}}, - std::vector>>{}) + .generate(makeSingleOpLayer(0U, qc::Y), + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "@+ ry 3.14159 global\n"); @@ -110,222 +105,187 @@ TEST_F(CodeGeneratorGenerateTest, GlobalCompoundRYGate) { const auto& slm = *architecture.storageZones.front(); // Create a compound operation with a single RY gate qc::CompoundOperation cry; - cry.emplace_back(0, qc::RY, std::vector{0.1}); + cry.emplace_back(0U, qc::RY, std::vector{0.1}); + SingleQubitGateLayer layer; + layer.emplace_back(std::make_unique(std::move(cry))); + std::vector layers; + layers.emplace_back(std::move(layer)); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {cry}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}}}, - std::vector>>{}) + .generate(layers, + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "@+ ry 0.10000 global\n"); } TEST_F(CodeGeneratorGenerateTest, GlobalCompoundYGate) { const auto& slm = *architecture.storageZones.front(); + // Create a compound operation with a single Y gate qc::CompoundOperation cy; - cy.emplace_back(0, qc::Y); + cy.emplace_back(0U, qc::Y); + SingleQubitGateLayer layer; + layer.emplace_back(std::make_unique(std::move(cy))); + std::vector layers; + layers.emplace_back(std::move(layer)); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {cy}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}}}, - std::vector>>{}) + .generate(layers, + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "@+ ry 3.14159 global\n"); } TEST_F(CodeGeneratorGenerateTest, RZGate) { const auto& slm = *architecture.storageZones.front(); - const auto rz = qc::StandardOperation(0, qc::RZ, {0.1}); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {rz}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}}}, - std::vector>>{}) + .generate(makeSingleOpLayer(0U, qc::RZ, + std::vector{0.1}), + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "@+ rz 0.10000 atom0\n"); } TEST_F(CodeGeneratorGenerateTest, PGate) { const auto& slm = *architecture.storageZones.front(); - const auto p = qc::StandardOperation(0, qc::P, {0.1}); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {p}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}}}, - std::vector>>{}) + .generate(makeSingleOpLayer(0U, qc::P, + std::vector{0.1}), + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "@+ rz 0.10000 atom0\n"); } TEST_F(CodeGeneratorGenerateTest, ZGate) { const auto& slm = *architecture.storageZones.front(); - const auto z = qc::StandardOperation(0, qc::Z); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {z}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}}}, - std::vector>>{}) + .generate(makeSingleOpLayer(0U, qc::Z), + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "@+ rz 3.14159 atom0\n"); } TEST_F(CodeGeneratorGenerateTest, SGate) { const auto& slm = *architecture.storageZones.front(); - const auto s = qc::StandardOperation(0, qc::S); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {s}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}}}, - std::vector>>{}) + .generate(makeSingleOpLayer(0U, qc::S), + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "@+ rz 1.57080 atom0\n"); } TEST_F(CodeGeneratorGenerateTest, SdgGate) { const auto& slm = *architecture.storageZones.front(); - const auto sdg = qc::StandardOperation(0, qc::Sdg); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {sdg}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}}}, - std::vector>>{}) + .generate(makeSingleOpLayer(0U, qc::Sdg), + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "@+ rz -1.57080 atom0\n"); } TEST_F(CodeGeneratorGenerateTest, TGate) { const auto& slm = *architecture.storageZones.front(); - const auto t = qc::StandardOperation(0, qc::T); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {t}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}}}, - std::vector>>{}) + .generate(makeSingleOpLayer(0U, qc::T), + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "@+ rz 0.78540 atom0\n"); } TEST_F(CodeGeneratorGenerateTest, TdgGate) { const auto& slm = *architecture.storageZones.front(); - const auto tdg = qc::StandardOperation(0, qc::Tdg); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {tdg}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}}}, - std::vector>>{}) + .generate(makeSingleOpLayer(0U, qc::Tdg), + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "@+ rz -0.78540 atom0\n"); } TEST_F(CodeGeneratorGenerateTest, U3Gate) { const auto& slm = *architecture.storageZones.front(); - const auto u = qc::StandardOperation(0, qc::U, {0.1, 0.2, 0.3}); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {u}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}}}, - std::vector>>{}) + .generate(makeSingleOpLayer( + 0U, qc::U, std::vector{0.1, 0.2, 0.3}), + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "@+ u 0.10000 0.20000 0.30000 atom0\n"); } TEST_F(CodeGeneratorGenerateTest, U2Gate) { const auto& slm = *architecture.storageZones.front(); - const auto u2 = qc::StandardOperation(0, qc::U2, {0.1, 0.2}); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {u2}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}}}, - std::vector>>{}) + .generate(makeSingleOpLayer( + 0U, qc::U2, std::vector{0.1, 0.2}), + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "@+ u 1.57080 0.10000 0.20000 atom0\n"); } TEST_F(CodeGeneratorGenerateTest, RXGate) { const auto& slm = *architecture.storageZones.front(); - const auto rx = qc::StandardOperation(0, qc::RX, {0.1}); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {rx}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}}}, - std::vector>>{}) + .generate(makeSingleOpLayer(0U, qc::RX, + std::vector{0.1}), + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "@+ u 0.10000 -1.57080 1.57080 atom0\n"); } TEST_F(CodeGeneratorGenerateTest, RYGate) { const auto& slm = *architecture.storageZones.front(); - const auto ry = qc::StandardOperation(0, qc::RY, {0.1}); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {ry}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}, {slm, 0, 1}}}, - std::vector>>{}) + .generate(makeSingleOpLayer(0U, qc::RY, + std::vector{0.1}), + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}, {slm, 0, 1}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "atom (3.000, 0.000) atom1\n" @@ -333,17 +293,13 @@ TEST_F(CodeGeneratorGenerateTest, RYGate) { } TEST_F(CodeGeneratorGenerateTest, YGate) { const auto& slm = *architecture.storageZones.front(); - const auto ry = qc::StandardOperation(0, qc::Y); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {ry}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}, {slm, 0, 1}}}, - std::vector>>{}) + .generate(makeSingleOpLayer(0U, qc::Y), + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}, {slm, 0, 1}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "atom (3.000, 0.000) atom1\n" @@ -351,113 +307,87 @@ TEST_F(CodeGeneratorGenerateTest, YGate) { } TEST_F(CodeGeneratorGenerateTest, HGate) { const auto& slm = *architecture.storageZones.front(); - const auto h = qc::StandardOperation(0, qc::H); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {h}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}}}, - std::vector>>{}) + .generate(makeSingleOpLayer(0U, qc::H), + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "@+ u 1.57080 0.00000 3.14159 atom0\n"); } TEST_F(CodeGeneratorGenerateTest, XGate) { const auto& slm = *architecture.storageZones.front(); - const auto x = qc::StandardOperation(0, qc::X); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {x}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}}}, - std::vector>>{}) + .generate(makeSingleOpLayer(0U, qc::X), + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "@+ u 3.14159 0.00000 3.14159 atom0\n"); } TEST_F(CodeGeneratorGenerateTest, VGate) { const auto& slm = *architecture.storageZones.front(); - const auto v = qc::StandardOperation(0, qc::V); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {v}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}}}, - std::vector>>{}) + .generate(makeSingleOpLayer(0U, qc::V), + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "@+ u -1.57080 -1.57080 1.57080 atom0\n"); } TEST_F(CodeGeneratorGenerateTest, VdgGate) { const auto& slm = *architecture.storageZones.front(); - const auto vdg = qc::StandardOperation(0, qc::Vdg); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {vdg}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}}}, - std::vector>>{}) + .generate(makeSingleOpLayer(0U, qc::Vdg), + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "@+ u -1.57080 1.57080 -1.57080 atom0\n"); } TEST_F(CodeGeneratorGenerateTest, SXGate) { const auto& slm = *architecture.storageZones.front(); - const auto sx = qc::StandardOperation(0, qc::SX); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {sx}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}}}, - std::vector>>{}) + .generate(makeSingleOpLayer(0U, qc::SX), + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "@+ u 1.57080 -1.57080 1.57080 atom0\n"); } TEST_F(CodeGeneratorGenerateTest, SXdgGate) { const auto& slm = *architecture.storageZones.front(); - const auto sxdg = qc::StandardOperation(0, qc::SXdg); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{ - {sxdg}}, - std::vector, size_t, size_t>>>{ - {{slm, 0, 0}}}, - std::vector>>{}) + .generate(makeSingleOpLayer(0U, qc::SXdg), + std::vector, size_t, size_t>>>{ + {{slm, 0, 0}}}, + std::vector>>{}) .toString(), "atom (0.000, 0.000) atom0\n" "@+ u -1.57080 -1.57080 1.57080 atom0\n"); } TEST_F(CodeGeneratorGenerateTest, UnsupportedGate) { const auto& slm = *architecture.storageZones.front(); - const auto unsupported = qc::NonUnitaryOperation(0, 0); EXPECT_THROW( std::ignore = codeGenerator.generate( - std::vector>>{ - {unsupported}}, + makeSingleOpLayer(0U, 0U), std::vector, size_t, size_t>>>{ {{slm, 0, 0}}}, @@ -470,19 +400,17 @@ TEST_F(CodeGeneratorGenerateTest, TwoQubitGate) { architecture.entanglementZones.front()->front(); const auto& entanglementRight = architecture.entanglementZones.front()->back(); + const std::vector layers(2); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{{}, - {}}, - std::vector, size_t, size_t>>>{ - {{storage, 19, 0}, {storage, 19, 1}}, - {{entanglementLeft, 0, 0}, {entanglementRight, 0, 0}}, - {{storage, 19, 0}, {storage, 19, 1}}}, - std::vector>>{{{0U, 1U}}, - {{0U, 1U}}}) + .generate(layers, + std::vector, size_t, size_t>>>{ + {{storage, 19, 0}, {storage, 19, 1}}, + {{entanglementLeft, 0, 0}, {entanglementRight, 0, 0}}, + {{storage, 19, 0}, {storage, 19, 1}}}, + std::vector>>{ + {{0U, 1U}}, {{0U, 1U}}}) .toString(), R"(atom (0.000, 57.000) atom0 atom (3.000, 57.000) atom1 @@ -543,28 +471,26 @@ TEST_F(CodeGeneratorGenerateTest, Offset) { architecture.entanglementZones.front()->front(); const auto& entanglementRight = architecture.entanglementZones.front()->back(); + const std::vector layers(2); EXPECT_EQ( codeGenerator - .generate( - std::vector< - std::vector>>{{}, - {}}, - std::vector, size_t, size_t>>>{ - {{storage, 18, 0}, - {storage, 18, 1}, - {storage, 19, 0}, - {storage, 19, 1}}, - {{entanglementLeft, 0, 0}, - {entanglementRight, 0, 0}, - {entanglementLeft, 1, 0}, - {entanglementRight, 1, 0}}, - {{storage, 18, 0}, - {storage, 18, 1}, - {storage, 19, 0}, - {storage, 19, 1}}}, - std::vector>>{ - {{0U, 1U, 2U, 3U}}, {{0U, 1U, 2U, 3U}}}) + .generate(layers, + std::vector, size_t, size_t>>>{ + {{storage, 18, 0}, + {storage, 18, 1}, + {storage, 19, 0}, + {storage, 19, 1}}, + {{entanglementLeft, 0, 0}, + {entanglementRight, 0, 0}, + {entanglementLeft, 1, 0}, + {entanglementRight, 1, 0}}, + {{storage, 18, 0}, + {storage, 18, 1}, + {storage, 19, 0}, + {storage, 19, 1}}}, + std::vector>>{ + {{0U, 1U, 2U, 3U}}, {{0U, 1U, 2U, 3U}}}) .toString(), R"(atom (0.000, 54.000) atom0 atom (0.000, 57.000) atom2 @@ -649,34 +575,32 @@ TEST_F(CodeGeneratorGenerateTest, ColumnByColumn) { architecture.entanglementZones.front()->front(); const auto& entanglementRight = architecture.entanglementZones.front()->back(); + const std::vector layers(2); EXPECT_TRUE( codeGenerator - .generate( - std::vector< - std::vector>>{{}, - {}}, - std::vector, size_t, size_t>>>{ - {{storage, 17, 0}, - {storage, 17, 1}, - {storage, 18, 0}, - {storage, 18, 1}, - {storage, 19, 0}, - {storage, 19, 1}}, - {{entanglementLeft, 0, 0}, - {entanglementRight, 0, 0}, - {entanglementLeft, 1, 0}, - {entanglementRight, 1, 0}, - {entanglementLeft, 2, 0}, - {entanglementRight, 2, 0}}, - {{storage, 17, 0}, - {storage, 17, 1}, - {storage, 18, 0}, - {storage, 18, 1}, - {storage, 19, 0}, - {storage, 19, 1}}}, - std::vector>>{ - {{0U, 1U, 2U, 3U, 4U, 5U}}, {{0U, 1U, 2U, 3U, 4U, 5U}}}) + .generate(layers, + std::vector, size_t, size_t>>>{ + {{storage, 17, 0}, + {storage, 17, 1}, + {storage, 18, 0}, + {storage, 18, 1}, + {storage, 19, 0}, + {storage, 19, 1}}, + {{entanglementLeft, 0, 0}, + {entanglementRight, 0, 0}, + {entanglementLeft, 1, 0}, + {entanglementRight, 1, 0}, + {entanglementLeft, 2, 0}, + {entanglementRight, 2, 0}}, + {{storage, 17, 0}, + {storage, 17, 1}, + {storage, 18, 0}, + {storage, 18, 1}, + {storage, 19, 0}, + {storage, 19, 1}}}, + std::vector>>{ + {{0U, 1U, 2U, 3U, 4U, 5U}}, {{0U, 1U, 2U, 3U, 4U, 5U}}}) .validate() .first); } @@ -694,28 +618,26 @@ TEST_F(CodeGeneratorGenerateTest, RelaxedRouting) { architecture.entanglementZones.front()->front(); const auto& entanglementRight = architecture.entanglementZones.front()->back(); + const std::vector layers(2); EXPECT_TRUE( codeGenerator - .generate( - std::vector< - std::vector>>{{}, - {}}, - std::vector, size_t, size_t>>>{ - {{storage, 18, 0}, - {storage, 18, 1}, - {storage, 19, 0}, - {storage, 19, 1}}, - {{entanglementRight, 1, 0}, - {entanglementLeft, 1, 0}, - {entanglementRight, 0, 0}, - {entanglementLeft, 0, 0}}, - {{storage, 18, 0}, - {storage, 18, 1}, - {storage, 19, 0}, - {storage, 19, 1}}}, - std::vector>>{ - {{0U, 1U, 2U, 3U}}, {{0U, 1U, 2U, 3U}}}) + .generate(layers, + std::vector, size_t, size_t>>>{ + {{storage, 18, 0}, + {storage, 18, 1}, + {storage, 19, 0}, + {storage, 19, 1}}, + {{entanglementRight, 1, 0}, + {entanglementLeft, 1, 0}, + {entanglementRight, 0, 0}, + {entanglementLeft, 0, 0}}, + {{storage, 18, 0}, + {storage, 18, 1}, + {storage, 19, 0}, + {storage, 19, 1}}}, + std::vector>>{ + {{0U, 1U, 2U, 3U}}, {{0U, 1U, 2U, 3U}}}) .validate() .first); }