diff --git a/src/duostra/duostra.cpp b/src/duostra/duostra.cpp index 9af610b6..49dd8fc6 100644 --- a/src/duostra/duostra.cpp +++ b/src/duostra/duostra.cpp @@ -64,7 +64,59 @@ bool Duostra::map(bool use_device_as_placement) { if (!use_device_as_placement) { spdlog::info("Calculating Initial Placement..."); auto placer = get_placer(_config.placer_type); - assign = placer->place_and_assign(_device); + + if (_config.placer_type == PlacerType::qmdla) { + // Calculate number of 2q gates for each qubit + std::vector> qubit_interactions; + std::vector interaction_counts(topo->get_num_qubits(), 0); + + for (const auto& gate : _logical_circuit->get_gates()) { + auto qubits = gate->get_qubits(); + if (qubits.size() == 2) { + interaction_counts[qubits[0]]++; + interaction_counts[qubits[1]]++; + } + } + + // Create pairs of (qubit_id, interaction_count) and sort by count + for (QubitIdType i = 0; i < topo->get_num_qubits(); i++) { + qubit_interactions.emplace_back(i, interaction_counts[i]); + } + + // Sort in descending order of interaction count + std::sort(qubit_interactions.begin(), qubit_interactions.end(), + [](const auto& a, const auto& b) { return a.second > b.second; }); + + // Extract just the qubit IDs in priority order + std::vector qubit_priority; + for (const auto& pair : qubit_interactions) { + qubit_priority.push_back(pair.first); + } + + // Calculate QPI (Qubit Pair Interaction) matrix with proper weights + std::vector> qpi(topo->get_num_qubits(), + std::vector(topo->get_num_qubits(), 0)); + + // Get total number of gates for weight calculation + size_t total_gates = _logical_circuit->get_gates().size(); + + // Calculate QPI with weights (m-i) for each gate + size_t gate_index = 0; + for (const auto& gate : _logical_circuit->get_gates()) { + auto qubits = gate->get_qubits(); + if (qubits.size() == 2) { + // Weight of current gate is (total_gates - gate_index) + size_t weight = total_gates - gate_index; + qpi[qubits[0]][qubits[1]] += weight; + qpi[qubits[1]][qubits[0]] += weight; + } + gate_index++; + } + + assign = placer->place_and_assign(_device, qubit_priority, qpi); + } else { + assign = placer->place_and_assign(_device); + } } // scheduler spdlog::info("Creating Scheduler..."); @@ -172,3 +224,4 @@ void Duostra::build_circuit_by_result() { } } // namespace qsyn::duostra + diff --git a/src/duostra/placer.cpp b/src/duostra/placer.cpp index da6afe6f..2912717b 100644 --- a/src/duostra/placer.cpp +++ b/src/duostra/placer.cpp @@ -46,8 +46,11 @@ std::unique_ptr get_placer(PlacerType type) { * * @param device */ -std::vector BasePlacer::place_and_assign(Device& device) { - auto assign = _place(device); +std::vector BasePlacer::place_and_assign( + Device& device, + const std::vector& qubit_priority, + const std::vector>& qpi) { + auto assign = _place(device, qubit_priority, qpi); device.place(assign); return assign; } @@ -60,7 +63,10 @@ std::vector BasePlacer::place_and_assign(Device& device) { * @param device * @return vector */ -std::vector RandomPlacer::_place(Device& device) const { +std::vector RandomPlacer::_place( + Device& device, + const std::vector& /*unused*/, + const std::vector>& /*unused*/) const { std::vector assign; for (size_t i = 0; i < device.get_num_qubits(); ++i) assign.emplace_back(i); @@ -77,11 +83,13 @@ std::vector RandomPlacer::_place(Device& device) const { * @param device * @return vector */ -std::vector StaticPlacer::_place(Device& device) const { +std::vector StaticPlacer::_place( + Device& device, + const std::vector& /*unused*/, + const std::vector>& /*unused*/) const { std::vector assign; for (size_t i = 0; i < device.get_num_qubits(); ++i) assign.emplace_back(i); - return assign; } @@ -93,7 +101,10 @@ std::vector StaticPlacer::_place(Device& device) const { * @param device * @return vector */ -std::vector DFSPlacer::_place(Device& device) const { +std::vector DFSPlacer::_place( + Device& device, + const std::vector& /*unused*/, + const std::vector>& /*unused*/) const { std::vector assign; std::vector qubit_mark(device.get_num_qubits(), false); _dfs_device(0, device, assign, qubit_mark); @@ -146,12 +157,179 @@ void DFSPlacer::_dfs_device(QubitIdType current, Device& device, std::vector */ -std::vector QMDLAPlacer::_place(Device& device) const { - std::vector assign; - // TODO +std::vector QMDLAPlacer::_place( + Device& device, + const std::vector& qubit_priority_input, + const std::vector>& qpi) const { - return assign; -} + // Initialize mappings with invalid values + std::vector M1(device.get_num_qubits(), -1); // logical -> physical + std::vector M2(device.get_num_qubits(), -1); // physical -> logical + + // Keep track of all possible mappings + std::vector> Maps; + Maps.push_back(M1); // Start with empty mapping + + // Calculate Physical Coupling Score (PCS) + std::vector pcs(device.get_num_qubits(), 0); + for (size_t i = 0; i < device.get_num_qubits(); ++i) { + const auto& q = device.get_physical_qubit(i); + size_t score = 0; + score += q.get_adjacencies().size(); // 1-hop neighbors + for (auto adj : q.get_adjacencies()) { + const auto& adj_q = device.get_physical_qubit(adj); + for (auto second_adj : adj_q.get_adjacencies()) { + if (second_adj != i && + std::find(q.get_adjacencies().begin(), + q.get_adjacencies().end(), + second_adj) == q.get_adjacencies().end()) { + score++; // 2-hop neighbors + } + } + } + pcs[i] = score; + } + + // Make a copy of priority list that we can modify + std::vector qubit_priority = qubit_priority_input; + // For each qubit in priority order + while (!qubit_priority.empty()) { + std::vector> new_Maps; + + for (auto& M : Maps) { // For each current mapping possibility + // Get highest priority unmapped qubit + QubitIdType curr_qubit = qubit_priority[0]; + + // Get logical neighbors of current qubit + std::vector curr_neighbors; + for (size_t i = 0; i < qpi[curr_qubit].size(); i++) { + if (qpi[curr_qubit][i] > 0) { + curr_neighbors.push_back(i); + } + } + + // Check if current qubit has any mapped neighbors + bool has_mapped_neighbor = false; + for (auto neighbor : curr_neighbors) { + if (M[neighbor] != -1) { + has_mapped_neighbor = true; + break; + } + } + + if (!has_mapped_neighbor) { + // Case 1: No mapped neighbors - use PCS + QubitIdType best_physical = -1; + size_t best_pcs = 0; + + // Find unassigned physical qubit with highest PCS + for (size_t p = 0; p < device.get_num_qubits(); p++) { + bool is_used = false; + for (size_t i = 0; i < device.get_num_qubits(); i++) { + if (M[i] == (QubitIdType)p) { + is_used = true; + break; + } + } + if (!is_used && pcs[p] > best_pcs) { + best_pcs = pcs[p]; + best_physical = p; + } + } + + if (best_physical != -1) { + auto M_new = M; + M_new[curr_qubit] = best_physical; + new_Maps.push_back(M_new); + } + + } else { + // Case 2: Has mapped neighbors - use QBN + std::vector> candidates; + + // For each unmapped physical qubit + for (size_t p = 0; p < device.get_num_qubits(); p++) { + bool is_used = false; + for (size_t i = 0; i < device.get_num_qubits(); i++) { + if (M[i] == (QubitIdType)p) { + is_used = true; + break; + } + } + if (is_used) continue; + + // Calculate QBN score + size_t qbn_score = 0; + const auto& phys_q = device.get_physical_qubit(p); + + for (auto neighbor : curr_neighbors) { + if (M[neighbor] != -1) { // if neighbor is mapped + if (std::find(phys_q.get_adjacencies().begin(), + phys_q.get_adjacencies().end(), + M[neighbor]) != phys_q.get_adjacencies().end()) { + qbn_score += qpi[curr_qubit][neighbor]; + } + } + } + + if (qbn_score > 0) { + candidates.emplace_back(p, qbn_score); + } + } + + // Sort candidates by QBN score + std::sort(candidates.begin(), candidates.end(), + [](const auto& a, const auto& b) { return a.second > b.second; }); + + if (!candidates.empty()) { + // Get all candidates with the highest score + size_t best_score = candidates[0].second; + for (const auto& [p, score] : candidates) { + if (score == best_score) { + auto M_new = M; + M_new[curr_qubit] = p; + new_Maps.push_back(M_new); + } else { + break; + } + } + } + } + } + + // Update Maps with new mappings + if (!new_Maps.empty()) { + Maps = new_Maps; + } + + // Remove current qubit from priority list + qubit_priority.erase(qubit_priority.begin()); + } + + // Choose the first valid mapping + for (const auto& M : Maps) { + bool is_valid = true; + std::vector used(device.get_num_qubits(), false); + + // Check if mapping is complete and valid + for (size_t i = 0; i < M.size(); i++) { + if (M[i] == -1 || M[i] >= device.get_num_qubits() || used[M[i]]) { + is_valid = false; + break; + } + used[M[i]] = true; + } + + if (is_valid) { + return M; + } + } + + // Fallback to identity mapping if no valid mapping found + std::vector fallback(device.get_num_qubits()); + std::iota(fallback.begin(), fallback.end(), 0); + return fallback; +} } // namespace qsyn::duostra diff --git a/src/duostra/placer.hpp b/src/duostra/placer.hpp index d0ae7bbe..f2d9015c 100644 --- a/src/duostra/placer.hpp +++ b/src/duostra/placer.hpp @@ -27,10 +27,16 @@ class BasePlacer { BasePlacer() {} virtual ~BasePlacer() = default; - std::vector place_and_assign(Device& device); + std::vector place_and_assign( + Device& device, + const std::vector& qubit_priority = std::vector(), + const std::vector>& qpi = std::vector>()); protected: - virtual std::vector _place(Device&) const = 0; + virtual std::vector _place( + Device& device, + const std::vector& qubit_priority, + const std::vector>& qpi) const = 0; }; class RandomPlacer : public BasePlacer { @@ -38,7 +44,10 @@ class RandomPlacer : public BasePlacer { using Device = BasePlacer::Device; protected: - std::vector _place(Device& /*unused*/) const override; + std::vector _place( + Device& device, + const std::vector& /*unused*/, + const std::vector>& /*unused*/) const override; }; class StaticPlacer : public BasePlacer { @@ -46,7 +55,10 @@ class StaticPlacer : public BasePlacer { using Device = BasePlacer::Device; protected: - std::vector _place(Device& /*unused*/) const override; + std::vector _place( + Device& device, + const std::vector& /*unused*/, + const std::vector>& /*unused*/) const override; }; class DFSPlacer : public BasePlacer { @@ -54,7 +66,10 @@ class DFSPlacer : public BasePlacer { using Device = BasePlacer::Device; protected: - std::vector _place(Device& /*unused*/) const override; + std::vector _place( + Device& device, + const std::vector& /*unused*/, + const std::vector>& /*unused*/) const override; private: void _dfs_device(QubitIdType current, Device& device, std::vector& assign, std::vector& qubit_marks) const; @@ -67,7 +82,10 @@ class QMDLAPlacer : public BasePlacer { using Device = BasePlacer::Device; protected: - std::vector _place(Device& /*unused*/) const override; + std::vector _place( + Device& device, + const std::vector& qubit_priority, + const std::vector>& qpi) const override; };