Skip to content

Commit

Permalink
QMDLA (executable)
Browse files Browse the repository at this point in the history
  • Loading branch information
xNTsai committed Feb 9, 2025
1 parent fcb3d2b commit e35f891
Show file tree
Hide file tree
Showing 3 changed files with 267 additions and 18 deletions.
55 changes: 54 additions & 1 deletion src/duostra/duostra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::pair<QubitIdType, size_t>> qubit_interactions;
std::vector<size_t> 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<QubitIdType> 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<std::vector<size_t>> qpi(topo->get_num_qubits(),
std::vector<size_t>(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...");
Expand Down Expand Up @@ -172,3 +224,4 @@ void Duostra::build_circuit_by_result() {
}

} // namespace qsyn::duostra

200 changes: 189 additions & 11 deletions src/duostra/placer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,11 @@ std::unique_ptr<BasePlacer> get_placer(PlacerType type) {
*
* @param device
*/
std::vector<QubitIdType> BasePlacer::place_and_assign(Device& device) {
auto assign = _place(device);
std::vector<QubitIdType> BasePlacer::place_and_assign(
Device& device,
const std::vector<QubitIdType>& qubit_priority,
const std::vector<std::vector<size_t>>& qpi) {
auto assign = _place(device, qubit_priority, qpi);
device.place(assign);
return assign;
}
Expand All @@ -60,7 +63,10 @@ std::vector<QubitIdType> BasePlacer::place_and_assign(Device& device) {
* @param device
* @return vector<size_t>
*/
std::vector<QubitIdType> RandomPlacer::_place(Device& device) const {
std::vector<QubitIdType> RandomPlacer::_place(
Device& device,
const std::vector<QubitIdType>& /*unused*/,
const std::vector<std::vector<size_t>>& /*unused*/) const {
std::vector<QubitIdType> assign;
for (size_t i = 0; i < device.get_num_qubits(); ++i)
assign.emplace_back(i);
Expand All @@ -77,11 +83,13 @@ std::vector<QubitIdType> RandomPlacer::_place(Device& device) const {
* @param device
* @return vector<size_t>
*/
std::vector<QubitIdType> StaticPlacer::_place(Device& device) const {
std::vector<QubitIdType> StaticPlacer::_place(
Device& device,
const std::vector<QubitIdType>& /*unused*/,
const std::vector<std::vector<size_t>>& /*unused*/) const {
std::vector<QubitIdType> assign;
for (size_t i = 0; i < device.get_num_qubits(); ++i)
assign.emplace_back(i);

return assign;
}

Expand All @@ -93,7 +101,10 @@ std::vector<QubitIdType> StaticPlacer::_place(Device& device) const {
* @param device
* @return vector<size_t>
*/
std::vector<QubitIdType> DFSPlacer::_place(Device& device) const {
std::vector<QubitIdType> DFSPlacer::_place(
Device& device,
const std::vector<QubitIdType>& /*unused*/,
const std::vector<std::vector<size_t>>& /*unused*/) const {
std::vector<QubitIdType> assign;
std::vector<bool> qubit_mark(device.get_num_qubits(), false);
_dfs_device(0, device, assign, qubit_mark);
Expand Down Expand Up @@ -146,12 +157,179 @@ void DFSPlacer::_dfs_device(QubitIdType current, Device& device, std::vector<Qub
* @param device
* @return vector<size_t>
*/
std::vector<QubitIdType> QMDLAPlacer::_place(Device& device) const {
std::vector<QubitIdType> assign;
// TODO
std::vector<QubitIdType> QMDLAPlacer::_place(
Device& device,
const std::vector<QubitIdType>& qubit_priority_input,
const std::vector<std::vector<size_t>>& qpi) const {

return assign;
}
// Initialize mappings with invalid values
std::vector<QubitIdType> M1(device.get_num_qubits(), -1); // logical -> physical
std::vector<QubitIdType> M2(device.get_num_qubits(), -1); // physical -> logical

// Keep track of all possible mappings
std::vector<std::vector<QubitIdType>> Maps;
Maps.push_back(M1); // Start with empty mapping

// Calculate Physical Coupling Score (PCS)
std::vector<size_t> 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<QubitIdType> qubit_priority = qubit_priority_input;

// For each qubit in priority order
while (!qubit_priority.empty()) {
std::vector<std::vector<QubitIdType>> 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<QubitIdType> 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<std::pair<QubitIdType, size_t>> 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<bool> 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<QubitIdType> fallback(device.get_num_qubits());
std::iota(fallback.begin(), fallback.end(), 0);
return fallback;
}

} // namespace qsyn::duostra
30 changes: 24 additions & 6 deletions src/duostra/placer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,34 +27,49 @@ class BasePlacer {
BasePlacer() {}
virtual ~BasePlacer() = default;

std::vector<QubitIdType> place_and_assign(Device& device);
std::vector<QubitIdType> place_and_assign(
Device& device,
const std::vector<QubitIdType>& qubit_priority = std::vector<QubitIdType>(),
const std::vector<std::vector<size_t>>& qpi = std::vector<std::vector<size_t>>());

protected:
virtual std::vector<QubitIdType> _place(Device&) const = 0;
virtual std::vector<QubitIdType> _place(
Device& device,
const std::vector<QubitIdType>& qubit_priority,
const std::vector<std::vector<size_t>>& qpi) const = 0;
};

class RandomPlacer : public BasePlacer {
public:
using Device = BasePlacer::Device;

protected:
std::vector<QubitIdType> _place(Device& /*unused*/) const override;
std::vector<QubitIdType> _place(
Device& device,
const std::vector<QubitIdType>& /*unused*/,
const std::vector<std::vector<size_t>>& /*unused*/) const override;
};

class StaticPlacer : public BasePlacer {
public:
using Device = BasePlacer::Device;

protected:
std::vector<QubitIdType> _place(Device& /*unused*/) const override;
std::vector<QubitIdType> _place(
Device& device,
const std::vector<QubitIdType>& /*unused*/,
const std::vector<std::vector<size_t>>& /*unused*/) const override;
};

class DFSPlacer : public BasePlacer {
public:
using Device = BasePlacer::Device;

protected:
std::vector<QubitIdType> _place(Device& /*unused*/) const override;
std::vector<QubitIdType> _place(
Device& device,
const std::vector<QubitIdType>& /*unused*/,
const std::vector<std::vector<size_t>>& /*unused*/) const override;

private:
void _dfs_device(QubitIdType current, Device& device, std::vector<QubitIdType>& assign, std::vector<bool>& qubit_marks) const;
Expand All @@ -67,7 +82,10 @@ class QMDLAPlacer : public BasePlacer {
using Device = BasePlacer::Device;

protected:
std::vector<QubitIdType> _place(Device& /*unused*/) const override;
std::vector<QubitIdType> _place(
Device& device,
const std::vector<QubitIdType>& qubit_priority,
const std::vector<std::vector<size_t>>& qpi) const override;
};


Expand Down

0 comments on commit e35f891

Please sign in to comment.