diff --git a/CHANGELOG.md b/CHANGELOG.md index d4f47d924c..36d73692b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ This project adheres to [Semantic Versioning], with the exception that minor rel ## [Unreleased] +### Added + +- ✨ Add support for bridge gates for the neutral atom hybrid mapper ([#1293]) ([**@lsschmid**]) + ## [3.3.2] - 2025-11-04 ### Added @@ -227,6 +231,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool +[#1293]: https://github.com/munich-quantum-toolkit/core/pull/1293 [#1287]: https://github.com/munich-quantum-toolkit/core/pull/1287 [#1283]: https://github.com/munich-quantum-toolkit/core/pull/1283 [#1279]: https://github.com/munich-quantum-toolkit/core/pull/1279 @@ -351,6 +356,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool [**@lavanya-m-k**]: https://github.com/lavanya-m-k [**@taminob**]: https://github.com/taminob [**@jannikpflieger**]: https://github.com/jannikpflieger +[**@lsschmid**]: https://github.com/lsschmid diff --git a/include/mqt-core/ir/QuantumComputation.hpp b/include/mqt-core/ir/QuantumComputation.hpp index 3833beac7e..c7980ffc11 100644 --- a/include/mqt-core/ir/QuantumComputation.hpp +++ b/include/mqt-core/ir/QuantumComputation.hpp @@ -328,6 +328,8 @@ class QuantumComputation { */ void measureAll(bool addBits = true); + void bridge(const Targets& targets); + void reset(Qubit target); void reset(const Targets& targets); diff --git a/include/mqt-core/ir/operations/OpType.inc b/include/mqt-core/ir/operations/OpType.inc index b06ed03e4f..a4099ca02b 100644 --- a/include/mqt-core/ir/operations/OpType.inc +++ b/include/mqt-core/ir/operations/OpType.inc @@ -65,11 +65,12 @@ HANDLE_OP_TYPE(36, MultiAFalse, 0, "multi_a_false") // Neutral atom shuttling operations HANDLE_OP_TYPE(37, Move, 0, "move") +HANDLE_OP_TYPE(42, Bridge, 0, "bridge") HANDLE_OP_TYPE(38, AodActivate, 0, "aod_activate") HANDLE_OP_TYPE(39, AodDeactivate, 0, "aod_deactivate") HANDLE_OP_TYPE(40, AodMove, 0, "aod_move") -LAST_OP_TYPE(42) +LAST_OP_TYPE(43) #undef OpTypeInv diff --git a/src/ir/QuantumComputation.cpp b/src/ir/QuantumComputation.cpp index 52cf4ba512..f46d236ceb 100644 --- a/src/ir/QuantumComputation.cpp +++ b/src/ir/QuantumComputation.cpp @@ -1566,6 +1566,11 @@ void QuantumComputation::measureAll(const bool addBits) { } } +void QuantumComputation::bridge(const Targets& targets) { + checkQubitRange(targets); + emplace_back(targets, Bridge); +} + void QuantumComputation::reset(const Qubit target) { checkQubitRange(target); emplace_back(std::vector{target}, Reset); diff --git a/src/ir/operations/AodOperation.cpp b/src/ir/operations/AodOperation.cpp index 29ce2e2ff5..190e373607 100644 --- a/src/ir/operations/AodOperation.cpp +++ b/src/ir/operations/AodOperation.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -42,6 +41,7 @@ std::string SingleOperation::toQASMString() const { ss << static_cast(dir) << ", " << start << ", " << end << "; "; return ss.str(); } + std::vector AodOperation::convertToDimension(const std::vector& dirs) { std::vector dirsEnum(dirs.size()); @@ -102,6 +102,7 @@ std::vector AodOperation::getEnds(const Dimension dir) const { } return ends; } + std::vector AodOperation::getStarts(const Dimension dir) const { std::vector starts; for (const auto& op : operations) { @@ -111,6 +112,7 @@ std::vector AodOperation::getStarts(const Dimension dir) const { } return starts; } + qc::fp AodOperation::getMaxDistance(const Dimension dir) const { const auto distances = getDistances(dir); if (distances.empty()) { @@ -118,6 +120,7 @@ qc::fp AodOperation::getMaxDistance(const Dimension dir) const { } return *std::ranges::max_element(distances); } + std::vector AodOperation::getDistances(const Dimension dir) const { std::vector params; for (const auto& op : operations) { @@ -127,26 +130,35 @@ std::vector AodOperation::getDistances(const Dimension dir) const { } return params; } + void AodOperation::dumpOpenQASM( std::ostream& of, const qc::QubitIndexToRegisterMap& qubitMap, [[maybe_unused]] const qc::BitIndexToRegisterMap& bitMap, const size_t indent, bool /*openQASM3*/) const { of << std::setprecision(std::numeric_limits::digits10); - of << std::string(indent * OUTPUT_INDENT_SIZE, ' '); - of << name; - // write AOD operations - of << " ("; + of << std::string(indent * OUTPUT_INDENT_SIZE, ' ') << name << " ("; + + // Write AOD operations with separator logic + bool first = true; for (const auto& op : operations) { - of << op.toQASMString(); + if (!first) { + of << "; "; + } + first = false; + of << static_cast(op.dir) << ", " << op.start << ", " + << op.end; } - // remove last semicolon - of.seekp(-1, std::ios_base::end); of << ")"; - // write qubit start + + // Write qubits with separator logic + bool firstQubit = true; for (const auto& qubit : targets) { - of << " " << qubitMap.at(qubit).second << ","; + if (!firstQubit) { + of << ","; + } + firstQubit = false; + of << " " << qubitMap.at(qubit).second; } - of.seekp(-1, std::ios_base::end); of << ";\n"; } @@ -161,5 +173,4 @@ void AodOperation::invert() { type = qc::OpType::AodActivate; } } - } // namespace na diff --git a/src/ir/operations/StandardOperation.cpp b/src/ir/operations/StandardOperation.cpp index 965bedb01a..5e33af72a0 100644 --- a/src/ir/operations/StandardOperation.cpp +++ b/src/ir/operations/StandardOperation.cpp @@ -457,6 +457,9 @@ void StandardOperation::dumpGateType( case iSWAPdg: op << "iswapdg"; break; + case Bridge: + op << "bridge"; + break; case Move: op << "move"; break; diff --git a/test/ir/test_operation.cpp b/test/ir/test_operation.cpp index fb46850d38..add3397256 100644 --- a/test/ir/test_operation.cpp +++ b/test/ir/test_operation.cpp @@ -257,6 +257,34 @@ TEST(StandardOperation, Move) { const qc::StandardOperation moveOp({0, 1}, qc::OpType::Move); EXPECT_EQ(moveOp.getTargets().size(), 2); EXPECT_EQ(moveOp.getNqubits(), 2); + + // QASM dump verification + std::stringstream ss; + qc::QuantumRegister qreg(0, 2, "q"); + qc::QubitIndexToRegisterMap qubitToReg{}; + qubitToReg.try_emplace(0, qreg, qreg.toString(0)); + qubitToReg.try_emplace(1, qreg, qreg.toString(1)); + moveOp.dumpOpenQASM(ss, qubitToReg, {}, 0, false); + EXPECT_EQ(ss.str(), "move q[0], q[1];\n"); +} + +TEST(StandardOperation, Bridge) { + const qc::StandardOperation bridgeOp({0, 1, 2}, qc::OpType::Bridge); + EXPECT_EQ(bridgeOp.getTargets().size(), 3); + EXPECT_EQ(bridgeOp.getNqubits(), 3); + + // QASM dump verification + std::stringstream ss; + qc::QuantumRegister qreg(0, 3, "q"); + qc::QubitIndexToRegisterMap qubitToReg{}; + qubitToReg.try_emplace(0, qreg, qreg.toString(0)); + qubitToReg.try_emplace(1, qreg, qreg.toString(1)); + qubitToReg.try_emplace(2, qreg, qreg.toString(2)); + bridgeOp.dumpOpenQASM(ss, qubitToReg, {}, 0, false); + EXPECT_EQ(ss.str(), "bridge q[0], q[1], q[2];\n"); + + qc::QuantumComputation qc(3); + EXPECT_NO_THROW(qc.bridge({0, 1, 2})); } TEST(AodOperation, Activate) { @@ -309,7 +337,7 @@ TEST(AodOperation, Qasm) { qubitToReg.try_emplace(1, qreg, qreg.toString(1)); move.dumpOpenQASM(ss, qubitToReg, {}, 0, false); - EXPECT_EQ(ss.str(), "aod_move (0, 0, 1; 1, 1, 3;) q[0], q[1];\n"); + EXPECT_EQ(ss.str(), "aod_move (0, 0, 1; 1, 1, 3) q[0], q[1];\n"); } TEST(AodOperation, Constructors) {