diff --git a/.gitignore b/.gitignore index 11756b408..25e80f7bf 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ openql.egg-info lab env .eggs/ +examples/cpp-standalone-example/testb_build # Temp files *.dot diff --git a/include/ql/com/topology.h b/include/ql/com/topology.h index d3d351ba0..dca6d0b94 100644 --- a/include/ql/com/topology.h +++ b/include/ql/com/topology.h @@ -195,6 +195,11 @@ class Topology { public: + /** + * Get the conectivity + */ + GridConnectivity get_connectivity(); + /** * Constructs the grid for the given number of qubits from the given JSON * object. Refer to dump_docs() for details. diff --git a/src/ql/com/options.cc b/src/ql/com/options.cc index aa65f89a9..f31db2fb8 100644 --- a/src/ql/com/options.cc +++ b/src/ql/com/options.cc @@ -399,6 +399,13 @@ Options make_ql_options() { true ); + options.add_bool( + "maprOEE", + "Applies the rOEE routing algorithm for multi-core architecures " + "with an all to all connection.", + false + ); + #if 0 // FIXME: removed, use pass options //========================================================================// // Default-inserted CC code generation pass behavior // diff --git a/src/ql/com/topology.cc b/src/ql/com/topology.cc index b7a96307d..1c7b90596 100644 --- a/src/ql/com/topology.cc +++ b/src/ql/com/topology.cc @@ -621,6 +621,14 @@ Topology::Neighbors Topology::get_neighbors(Qubit qubit) const { } } +/** + * Get the conectivity + */ +GridConnectivity Topology::get_connectivity(){ + return connectivity; +} + + /** * Returns whether the given qubit is a communication qubit of a core. */ @@ -706,6 +714,7 @@ utils::UInt Topology::get_min_hops(Qubit source, Qubit target) const { utils::UInt d = get_distance(source, target); utils::UInt cd = get_core_distance(source, target); QL_ASSERT(cd <= d); + if (cd == d) { return d+2; } else { diff --git a/src/ql/pass/map/qubits/map/detail/future.cc b/src/ql/pass/map/qubits/map/detail/future.cc index 26379aa51..040e85330 100644 --- a/src/ql/pass/map/qubits/map/detail/future.cc +++ b/src/ql/pass/map/qubits/map/detail/future.cc @@ -3,7 +3,7 @@ */ #include "future.h" - +#include #include "ql/utils/filesystem.h" namespace ql { @@ -46,6 +46,7 @@ void Future::set_kernel(const ir::compat::KernelRef &kernel, const utils::Ptrgates) { scheduled.set(gp) = false; // none were scheduled + remaining_gates.push_back(gp); } scheduled.set(scheduler->instruction[scheduler->s]) = false; // also the dummy nodes not scheduled.set(scheduler->instruction[scheduler->t]) = false; @@ -137,6 +138,15 @@ void Future::completed_gate(const ir::compat::GateRef &gate) { input_gatepp = std::next(input_gatepp); } else { scheduler->take_available(scheduler->node.at(gate), avlist, scheduled, rmgr::Direction::FORWARD); + + std::list::iterator it = remaining_gates.begin(); + for(auto &i : remaining_gates){ + if(i==gate){ + remaining_gates.erase(it); + break; + } + it++; + } } } diff --git a/src/ql/pass/map/qubits/map/detail/future.h b/src/ql/pass/map/qubits/map/detail/future.h index 3d84ac3fc..c1fbf82bd 100644 --- a/src/ql/pass/map/qubits/map/detail/future.h +++ b/src/ql/pass/map/qubits/map/detail/future.h @@ -15,6 +15,7 @@ #include "options.h" #include "past.h" #include "alter.h" +#include namespace ql { namespace pass { @@ -111,6 +112,11 @@ class Future { */ utils::UInt approx_gates_remaining; + /** + * List of total number of gates remaining. + */ + utils::List remaining_gates; + /** * Program-wide initialization function. */ diff --git a/src/ql/pass/map/qubits/map/detail/mapper.cc b/src/ql/pass/map/qubits/map/detail/mapper.cc index f9e2c4a61..4be1c5207 100644 --- a/src/ql/pass/map/qubits/map/detail/mapper.cc +++ b/src/ql/pass/map/qubits/map/detail/mapper.cc @@ -8,6 +8,8 @@ #include "ql/utils/filesystem.h" #include "ql/pass/ana/statistics/annotations.h" #include "ql/pass/map/qubits/place_mip/detail/algorithm.h" +#include +#include // uncomment next line to enable multi-line dumping // #define MULTI_LINE_LOG_DEBUG @@ -557,6 +559,7 @@ Bool Mapper::map_mappable_gates( // Find minimum number of hops between real counterparts. UInt d = platform->topology->get_min_hops(src, tgt); + UInt partitions = platform->topology->get_num_cores(); if (d == 1) { @@ -824,6 +827,241 @@ void Mapper::select_alter( } +void Mapper::rOEE( + List &gates, + List &remaining_gates, + Future &future, + Past &past, + Past &base_past + ){ + + UInt partitions = platform->topology->get_num_cores(); + UInt n_qubits = platform->topology->get_num_qubits(); + + std::vector part(n_qubits); + + std::vector C; + com::map::QubitMapping v2r; + + for (auto &gate : gates){ + + std::vector q1; + std::vector q2; + + auto &q = gate->operands; + + past.map_qubit(q[0]); + past.map_qubit(q[1]); + + bool val; + float inf = pow(2,16); + + // --------- Partition and weight matrix ----------- + + List remaining_gates_aux(remaining_gates); + std::list::iterator it = remaining_gates_aux.begin(); + float w_matrix[n_qubits][n_qubits] = {}; + float w_aux[n_qubits][n_qubits] = {}; + + // Create weight matrix + if(remaining_gates_aux.size() == 1){ + if(remaining_gates_aux.front() == gate) + remaining_gates_aux.pop_front(); + } else { + for(auto &i : remaining_gates_aux){ + if(i==gate){ + remaining_gates_aux.erase(it); + break; + } + it++; + } + } + + w_matrix[q[0]][q[1]] = 1*inf; + w_matrix[q[1]][q[0]] = 1*inf; + w_aux[q[0]][q[1]] = 1*pow(2,-1); + w_aux[q[1]][q[0]] = 1*pow(2,-1); + + for (int i = 0; i < n_qubits; i++){ + for (int j = 0; j < n_qubits; j++) + w_matrix[i][j] += w_aux[i][j]; + } + w_aux[q[0]][q[1]] = 0; + w_aux[q[1]][q[0]] = 0; + + Int n = 2; + for(const auto &gate :remaining_gates_aux){ + auto &p = gate->operands; + if(p.size() > 1){ + past.map_qubit(p[0]); + past.map_qubit(p[1]); + w_aux[p[0]][p[1]] = 1*pow(2,-n); + w_aux[p[1]][p[0]] = 1*pow(2,-n); + for (int i = 0; i < n_qubits; i++){ + for (int j = 0; j < n_qubits; j++) + w_matrix[i][j] += w_aux[i][j]; + } + w_aux[p[0]][p[1]] = 0; + w_aux[p[1]][p[0]] = 0; + n++; + } + } + // v2r[i]/partitions + past.export_mapping(v2r); + for(int i = 0; i < n_qubits; i++) + part[i] = platform->topology->get_core_index(v2r[i]); + + // --------- End partition and weight matrix ----------- + + // --------------------- ROEE -------------------------- + // If they are equal qubits are already in same partition + if (part[q[0]] != part[q[1]]){ + float g_max = 1; + // Step 7 + val = 1; + while(g_max > 0 && val == 1){ + + if(part[q[0]] == part[q[1]]){ + val = 0; + break; + } + // Step 1 + utils::UInt index = 0; + C.clear(); + + for(int i = 0; i < n_qubits; ++i) + C.push_back(i); + // Calculating W_(i,l) + float W[n_qubits][partitions] = {}; + for(int i = 0; i < n_qubits; i++){ + for(int j = 0; j < partitions; j++){ + for(int p = 0; p < n_qubits; p++){ + + // p is the qubit index in that core + if(part[p] == j){ + W[i][j] += w_matrix[i][p]; + } + } + } + } + + // Calculating D(i,l) = W(i,l)-W[i,col(i)] + float D[n_qubits][partitions] = {}; + for(int i = 0; i < n_qubits; i++){ + for(int j = 0; j < partitions; j++) + D[i][j] = W[i][j] - W[i][part[i]]; + } + + std::vector g(C.size(), -INFINITY); + std::vector a(C.size(), 0); + std::vector b(C.size(), 0); + + // Step 4 + while(C.size() > 1){ + // Step 2 + + // Find max g(i,l) + float aux= -1; + int a_aux = 0; + int b_aux = 0; + + for(auto &i : C){ + for(auto &j : C){ + if( i != j ){ + aux = D[i][part[j]] + D[j][part[i]] - 2*w_matrix[i][j]; + if(aux > g[index]){ + g[index] = aux; + a_aux = i; + b_aux = j; + } + } + } + } + + // Delete a, b from C + C.erase(std::remove(C.begin(),C.end(),a_aux),C.end()); + C.erase(std::remove(C.begin(),C.end(),b_aux),C.end()); + + // Step 3 + for(auto &i : C){ + for (int l = 0; l < partitions; l++){ + if(l == part[a_aux]){ + if(part[i] != part[a_aux] && part[i] != part[b_aux]) + D[i][l] = D[i][l] + w_matrix[i][b_aux] - w_matrix[i][a_aux]; + if(part[i] == part[b_aux]) + D[i][l] = D[i][l] + 2*w_matrix[i][b_aux] - 2*w_matrix[i][a_aux]; + } else if (l == part[b_aux]) { + if(part[i] != part[a_aux] && part[i] != part[b_aux]) + D[i][l] = D[i][l] + w_matrix[i][a_aux] - w_matrix[i][b_aux]; + if(part[i] == part[a_aux]) + D[i][l] = D[i][l] + 2*w_matrix[i][a_aux] - 2*w_matrix[i][b_aux]; + } else { + if(part[i] == part[a_aux]){ + D[i][l] = D[i][l] + w_matrix[i][a_aux] - w_matrix[i][b_aux]; + } else if(part[i] == part[b_aux]){ + D[i][l] = D[i][l] + w_matrix[i][b_aux] - w_matrix[i][a_aux]; + } + } + } + } + a[index] = a_aux; + b[index] = b_aux; + + index++; + } + // Step 5 + // Calculate g_max + float g_aux = 0; + float g_max_idx = -1; + + for(int i = 0; i < g.size(); i++ ){ + if(g[i]==-INFINITY) + break; + if(g_aux+g[i] > g_aux){ + g_max_idx = i; + g_max = g_aux+g[i]; + } + g_aux += g[i]; + } + + // Step 6 + // Exchange firstg m pairs + int tmp = 0; + + for(int i = 0; i < g_max_idx+1 && val == 1; i++ ){ + tmp = part[a[i]]; + part[a[i]] = part[b[i]]; + part[b[i]] = tmp; + + q1.push_back(a[i]); + q2.push_back(b[i]); + + if(part[q[0]] == part[q[1]]){ + val = 0; + } + + } + } + + // --------------------- End ROEE -------------------------- + + // --------------------- Mapping qubits ------------------- + if(val == 1){ + fprintf(stderr, "Valid partition not found\n"); + exit(EXIT_FAILURE); + } + + for(int i=0; i < q1.size(); i++){ + if (q1[i] != q2[i]) + past.add_swap(past.map_qubit(q1[i]), past.map_qubit(q2[i])); + } + } + + map_routed_gate(gate, past); + future.completed_gate(gate); + } +} + /** * Given the states of past and future, map all mappable gates and find the * non-mappable ones. For those, evaluate what to do next and do it. During @@ -847,24 +1085,33 @@ void Mapper::map_gates(Future &future, Past &past, Past &base_past) { // Handle all the gates one by one. map_mappable_gates returns false when no // gates remain. while (map_mappable_gates(future, past, gates, also_nn_two_qubit_gates)) { - - // All gates in the gates list are two-qubit quantum gates that cannot - // be mapped yet. Select which one(s) to (partially) route, according to - // one of the known strategies. The only requirement on the code below - // is that at least something is done that decreases the problem. - - // Generate all alternative routes. - List alters; - gen_alters(gates, alters, past); - - // Select the best one based on the configured strategy. - Alter alter; - select_alter(alters, alter, future, past, base_past, 0); - - // Commit to selected alternative. This adds all or just one swap - // (depending on configuration) to THIS past, and schedules them/it in. - commit_alter(alter, future, past); - + + // Check if rOEE mapping option is set and architecture constraints are + // satisfied + if(options->rOEE_routing_algorithm && + platform->topology->get_num_cores() > 1 && + platform->topology->get_connectivity() == GridConnectivity::FULL){ + + rOEE(gates, future.remaining_gates,future,past,base_past); + + } else { + // All gates in the gates list are two-qubit quantum gates that cannot + // be mapped yet. Select which one(s) to (partially) route, according to + // one of the known strategies. The only requirement on the code below + // is that at least something is done that decreases the problem. + + // Generate all alternative routes. + List alters; + gen_alters(gates, alters, past); + + // Select the best one based on the configured strategy. + Alter alter; + select_alter(alters, alter, future, past, base_past, 0); + + // Commit to selected alternative. This adds all or just one swap + // (depending on configuration) to THIS past, and schedules them/it in. + commit_alter(alter, future, past); + } // Print progress every once in a while if we're taking long. Real progress = 1.0; if (future.approx_gates_total) { diff --git a/src/ql/pass/map/qubits/map/detail/mapper.h b/src/ql/pass/map/qubits/map/detail/mapper.h index ab84df62a..825baf020 100644 --- a/src/ql/pass/map/qubits/map/detail/mapper.h +++ b/src/ql/pass/map/qubits/map/detail/mapper.h @@ -507,6 +507,13 @@ class Mapper { */ void map_kernel(const ir::compat::KernelRef &k); + void rOEE( + utils::List &gates, + utils::List &remaining_gates, + Future &future, + Past &past, + Past &base_past); + public: /** diff --git a/src/ql/pass/map/qubits/map/detail/options.h b/src/ql/pass/map/qubits/map/detail/options.h index 639b2f9dc..7bd5ccade 100644 --- a/src/ql/pass/map/qubits/map/detail/options.h +++ b/src/ql/pass/map/qubits/map/detail/options.h @@ -315,6 +315,11 @@ struct Options { */ utils::Bool write_dot_graphs = false; + /** + * Use the rOEE routing algorithm algorithm for multi-core architectures. + */ + utils::Bool rOEE_routing_algorithm = false; + }; /** diff --git a/src/ql/pass/map/qubits/map/map.cc b/src/ql/pass/map/qubits/map/map.cc index 1bdae6ddb..b69de126c 100644 --- a/src/ql/pass/map/qubits/map/map.cc +++ b/src/ql/pass/map/qubits/map/map.cc @@ -383,6 +383,17 @@ MapQubitsPass::MapQubitsPass( false ); + //========================================================================// + // Options for the routing in multi-core architectures // + //========================================================================// + + options.add_bool( + "rOEE_routing_algorithm", + "Applies the rOEE routing algorithm for multi-core architecures " + "with an all to all connection.", + false + ); + } /** @@ -493,6 +504,13 @@ pmgr::pass_types::NodeType MapQubitsPass::on_construct( parsed_options->max_move_penalty = utils::parse_uint(use_moves); } + auto map_rOEE = options["rOEE_routing_algorithm"].as_str(); + if (map_rOEE == "no") { + parsed_options->rOEE_routing_algorithm = false; + } else { + parsed_options->rOEE_routing_algorithm = true; + } + parsed_options->reverse_swap_if_better = options["reverse_swap_if_better"].as_bool(); parsed_options->commute_multi_qubit = options["commute_multi_qubit"].as_bool(); parsed_options->commute_single_qubit = options["commute_single_qubit"].as_bool(); diff --git a/src/ql/pmgr/manager.cc b/src/ql/pmgr/manager.cc index a64293e12..4345e6e28 100644 --- a/src/ql/pmgr/manager.cc +++ b/src/ql/pmgr/manager.cc @@ -437,6 +437,12 @@ static utils::Map convert_global_to_pass_options() { retval.set("reverse_swap_if_better") = mapreverseswap.as_str(); } + const auto &maprOEE = com::options::global["maprOEE"]; + if (maprOEE.is_set()) { + retval.set("rOEE_routing_algorithm") = maprOEE.as_str(); + } + + #if 0 // FIXME: removed, use pass options // Set options for CC backend. const auto &backend_cc_map_input_file = com::options::global["backend_cc_map_input_file"]; diff --git a/tests/golden/test_rOEE_last.qasm b/tests/golden/test_rOEE_last.qasm new file mode 100644 index 000000000..fa45399d2 --- /dev/null +++ b/tests/golden/test_rOEE_last.qasm @@ -0,0 +1,109 @@ +# Generated by OpenQL 0.10.5 for program test_rOEE +version 1.2 + +pragma @ql.name("test_rOEE") + + +.kernel_rOEE + { # start at cycle 0 + x q[0] + x q[1] + x q[4] + x q[5] + } + { # start at cycle 1 + cnot q[0], q[1] + cnot q[4], q[5] + } + skip 3 + { # start at cycle 5 + x q[8] + x q[9] + } + { # start at cycle 6 + cnot q[8], q[9] + cnot q[0], q[4] + cnot q[1], q[5] + cnot q[2], q[6] + cnot q[3], q[7] + } + skip 3 + { # start at cycle 10 + x q[12] + x q[13] + } + { # start at cycle 11 + cnot q[12], q[13] + cnot q[0], q[8] + cnot q[1], q[9] + cnot q[2], q[10] + cnot q[3], q[11] + } + skip 4 + { # start at cycle 16 + cnot q[0], q[12] + cnot q[1], q[13] + cnot q[2], q[14] + cnot q[3], q[15] + } + skip 4 + { # start at cycle 21 + cnot q[4], q[0] + cnot q[5], q[1] + cnot q[6], q[2] + cnot q[7], q[3] + } + skip 4 + { # start at cycle 26 + cnot q[4], q[8] + cnot q[5], q[9] + cnot q[6], q[10] + cnot q[7], q[11] + } + skip 4 + { # start at cycle 31 + cnot q[4], q[12] + cnot q[5], q[13] + cnot q[6], q[14] + cnot q[7], q[15] + cnot q[8], q[0] + cnot q[9], q[1] + cnot q[10], q[2] + cnot q[11], q[3] + } + skip 4 + { # start at cycle 36 + cnot q[8], q[4] + cnot q[9], q[5] + cnot q[10], q[6] + cnot q[11], q[7] + } + skip 4 + { # start at cycle 41 + cnot q[8], q[12] + cnot q[9], q[13] + cnot q[10], q[14] + cnot q[11], q[15] + } + skip 4 + { # start at cycle 46 + cnot q[12], q[0] + cnot q[13], q[1] + cnot q[14], q[2] + cnot q[15], q[3] + } + skip 4 + { # start at cycle 51 + cnot q[12], q[4] + cnot q[13], q[5] + cnot q[14], q[6] + cnot q[15], q[7] + } + skip 4 + { # start at cycle 56 + cnot q[12], q[8] + cnot q[13], q[9] + cnot q[14], q[10] + cnot q[15], q[11] + } + skip 4 diff --git a/tests/test_rOEE.py b/tests/test_rOEE.py new file mode 100644 index 000000000..435fb7e63 --- /dev/null +++ b/tests/test_rOEE.py @@ -0,0 +1,51 @@ + +from openql import openql as ql +import os +import unittest +from utils import file_compare + +curdir = os.path.dirname(os.path.realpath(__file__)) +output_dir = os.path.join(curdir, 'test_output') + +class Test_rOEE(unittest.TestCase): + + def setUp(self): + ql.initialize() + ql.set_option('output_dir', output_dir) + ql.set_option('log_level', 'LOG_NOTHING') + ql.set_option('use_default_gates', 'no') + ql.set_option('optimize', 'no') + ql.set_option('generate_code', 'no') + ql.set_option('print_dot_graphs', 'no') + + ql.set_option('scheduler', 'ALAP') + ql.set_option('maprOEE', 'yes') + + def test_rOEE(self): + config = os.path.join(curdir, "test_multi_core_4x4_full.json") + platform = ql.Platform("mc4x4full", config) + num_qubits = 16 + p = ql.Program('test_rOEE', platform, num_qubits) + k = ql.Kernel('kernel_rOEE', platform, num_qubits) + for i in range(4): + k.gate("x", [4*i]) + k.gate("x", [4*i+1]) + for i in range(4): + k.gate("cnot", [4*i,4*i+1]) + for i in range(4): + for j in range(4): + if i != j: + k.gate("cnot", [4*i,4*j]) + k.gate("cnot", [4*i+1,4*j+1]) + k.gate("cnot", [4*i+2,4*j+2]) + k.gate("cnot", [4*i+3,4*j+3]) + + p.add_kernel(k) + p.compile() + + gold_fn = curdir + '/golden/' + 'test_rOEE' +'_last.qasm' + qasm_fn = os.path.join(output_dir, 'test_rOEE'+'_last.qasm') + self.assertTrue( file_compare(qasm_fn, gold_fn) ) + +if __name__ == '__main__': + unittest.main()