diff options
-rw-r--r-- | planning/planning.cpp | 233 |
1 files changed, 159 insertions, 74 deletions
diff --git a/planning/planning.cpp b/planning/planning.cpp index 3a81b36..590d6b1 100644 --- a/planning/planning.cpp +++ b/planning/planning.cpp @@ -2,10 +2,10 @@ // About three times as fast as the old DP+heuristics-based solution // (<2ms for planning TG), and can deal with less regular cost metrics. // -// Given D distro switches and N access switches, complexity is approx. O(dn³) -// (runs n iterations, each iteration is O(VE), V is O(n), E is O(dn))). +// Given D distro switches and N access switches, complexity is approx. O(n²(log d+log n)) +// (runs n iterations, each iteration is O(Vlog E), V is O(n), E is O(dn))). // -// g++ -std=gnu++11 -Wall -g -O2 -DOUTPUT_FILES=1 -o planning planning.cpp && ./planning -3 -6 11 22 -26 35 +// g++ -std=gnu++11 -Wall -g -O3 -fopenmp -DOUTPUT_FILES=1 -o planning planning.cpp && ./planning -3 -6 11 22 -26 35 #include <stdio.h> #include <math.h> @@ -19,9 +19,11 @@ #include <netinet/in.h> #include <arpa/inet.h> +#include <atomic> #include <vector> #include <map> #include <string> +#include <queue> #define NUM_DISTRO 6 #define NUM_ROWS 41 @@ -119,6 +121,11 @@ struct Graph { vector<Edge> edges; vector<Node*> all_nodes; }; +struct CompareByCost { + bool operator() (const Node *a, const Node *b) const { + return a->cost_from_source > b->cost_from_source; + } +}; const unsigned horiz_cost[SWITCHES_PER_ROW] = { 216, 72, 72, 216 // Gap costs are added separately. @@ -144,14 +151,14 @@ class Planner { string *log_buf; unsigned find_distance(Switch from_where, int distro); - unsigned find_slack(Inventory inventory, unsigned distance); unsigned find_cost(Switch from_where, int distro); Inventory find_inventory(Switch from_where, int distro); + unsigned find_slack(Inventory inventory, unsigned distance); void logprintf(const char *str, ...); void init_switches(); void construct_graph(const vector<Switch> &switches, Graph *g); void find_mincost_maxflow(Graph *g); - void print_switch(const Graph &g, int i); + void print_switch(const Graph &g, int i, int distro); public: Planner() : log_buf(NULL) {} @@ -164,13 +171,22 @@ unsigned Planner::find_distance(Switch from_where, int distro) assert(distro != -1); const unsigned dp = std::abs(distro_placements[distro]); - // 3.6m from row to row (2.4m gap + 1.2m boards). - unsigned base_cost = 36 * abs(int(from_where.row) - int(dp)) + - horiz_cost[from_where.num]; + unsigned base_cost = horiz_cost[from_where.num]; if ((distro_placements[distro] >= 0) == (from_where.num >= 2)) { - // 5.0m horizontal gap. + int bridge_row = distro_placements[NUM_DISTRO - 1]; + + // Go to the bridge... + base_cost += 36 * abs(int(from_where.row) - bridge_row); + + // Cross it (5.0m horizontal gap)... base_cost += 50; + + // ...and away from the bridge again. + base_cost += 36 * abs(int(dp) - bridge_row); + } else { + // 3.6m from row to row (2.4m gap + 1.2m boards). + base_cost += 36 * abs(int(from_where.row) - int(dp)); } for (const VerticalGap& gap : vertical_gaps) { @@ -226,6 +242,12 @@ Inventory Planner::find_inventory(Switch from_where, int distro) inv.vert_chasm_crossings = 1; } + // So is the gap over the scene. + if ((abs(distro_placements[distro]) <= 13) == (from_where.row >= 14) && + from_where.num >= 2 && distro_placements[distro] < 0) { + inv.vert_chasm_crossings = 1; + } + return inv; } @@ -247,12 +269,15 @@ unsigned Planner::find_cost(Switch from_where, int distro) // cost = ((distance + 90) / 100) * 100; #endif +#if 0 // We really, really do not want to cross the gap on the north side. + // (now handled in bridge cost) if (from_where.row <= 30) { cost += _INF * inv.horiz_gap_crossings; } else { cost += HORIZ_GAP_COST * inv.horiz_gap_crossings; } +#endif // Also, the gap between Game and Sector 8 is unsurmountable. cost += _INF * inv.vert_chasm_crossings; @@ -393,25 +418,31 @@ void Planner::construct_graph(const vector<Switch> &switches, Graph *g) } g->all_nodes.push_back(&g->source_node); - strcpy(g->source_node.name, "source"); - g->all_nodes.push_back(&g->sink_node); - strcpy(g->sink_node.name, "sink"); for (unsigned i = 0; i < NUM_DISTRO; ++i) { g->all_nodes.push_back(&g->distro_nodes[i]); - sprintf(g->distro_nodes[i].name, "distro%d", i); } for (unsigned i = 0; i < switches.size(); ++i) { g->all_nodes.push_back(&g->switch_nodes[i]); + } + +#if 0 + strcpy(g->source_node.name, "source"); + strcpy(g->sink_node.name, "sink"); + for (unsigned i = 0; i < NUM_DISTRO; ++i) { + sprintf(g->distro_nodes[i].name, "distro%d", i); + } + for (unsigned i = 0; i < switches.size(); ++i) { sprintf(g->switch_nodes[i].name, "switch%d", i); } +#endif } void Planner::find_mincost_maxflow(Graph *g) { - // We use the successive shortest path algorithm, using a primitive Dijkstra - // (not heap-based, so O(VE)) for search. + // We use the successive shortest path algorithm, using a simple + // heap-based Dijkstra (O(Vlog E)) for search. int num_paths = 0; for ( ;; ) { // Reset Dijkstra state. @@ -422,42 +453,44 @@ void Planner::find_mincost_maxflow(Graph *g) } g->source_node.cost_from_source = 0; - for ( ;; ) { - Node *cheapest_unseen_node = NULL; - for (Node *n : g->all_nodes) { - if (n->seen || n->cost_from_source >= _INF) { - continue; - } - if (cheapest_unseen_node == NULL || - n->cost_from_source < cheapest_unseen_node->cost_from_source) { - cheapest_unseen_node = n; - } - } - if (cheapest_unseen_node == NULL) { - // Oops, no usable path. - goto end; + priority_queue<Node *, vector<Node *>, CompareByCost> q; + q.push(&g->source_node); + + while (!q.empty()) { + Node *u = q.top(); + q.pop(); + if (u->seen) { + continue; } - if (cheapest_unseen_node == &g->sink_node) { + u->seen = true; + if (u == &g->sink_node) { // Yay, we found a path to the sink. break; } - cheapest_unseen_node->seen = true; // Relax outgoing edges from this node. - for (Edge *e : cheapest_unseen_node->edges) { + for (Edge *e : u->edges) { + Node *v = e->to; + if (v->seen) { + continue; + } if (e->flow + 1 > e->capacity || e->reverse->flow - 1 > e->reverse->capacity) { // Not feasible. continue; } - if (e->to->cost_from_source <= cheapest_unseen_node->cost_from_source + e->cost) { + if (v->cost_from_source <= u->cost_from_source + e->cost) { // Already seen through a better path. continue; } - e->to->seen = false; - e->to->prev_edge = e; - e->to->cost_from_source = cheapest_unseen_node->cost_from_source + e->cost; + v->prev_edge = e; + v->cost_from_source = u->cost_from_source + e->cost; + q.push(v); } } + if (q.empty()) { + // Oops, no usable path. + goto end; + } // Increase flow along the path, moving backwards towards the source. Node *n = &g->sink_node; @@ -478,26 +511,30 @@ end: logprintf("Augmented using %d paths.\n", num_paths); } -// Figure out which distro this switch was connected to. -int find_distro(const Graph &g, int switch_index) +// Figure out which distro each switch was connected to. +map<int, int> find_switch_distro_map(const Graph &g) { - for (unsigned j = 0; j < NUM_DISTRO; ++j) { - for (Edge *e : g.distro_nodes[j].edges) { - if (e->to == &g.switch_nodes[switch_index] && e->flow > 0) { - return j; + map<int, int> ret; + for (unsigned distro_num = 0; distro_num < NUM_DISTRO; ++distro_num) { + for (Edge *e : g.distro_nodes[distro_num].edges) { + if (e->flow <= 0) { + continue; + } + if (e->to >= &g.switch_nodes[0] && e->to < &g.switch_nodes[g.switch_nodes.size()]) { + int switch_index = (e->to - &g.switch_nodes[0]); + ret.insert(make_pair(switch_index, distro_num)); } } } - return -1; + return ret; } -void Planner::print_switch(const Graph &g, int i) +void Planner::print_switch(const Graph &g, int i, int distro) { if (i == -1) { logprintf("%16s", ""); return; } - int distro = find_distro(g, i); if (distro == -1) { logprintf("[%u;22m- ", distro + 32); #if TRUNCATE_METRIC @@ -523,8 +560,6 @@ int Planner::do_work(int distro_placements[NUM_DISTRO]) { memcpy(this->distro_placements, distro_placements, sizeof(distro_placements[0]) * NUM_DISTRO); - num_ports_used.clear(); - Inventory total_inv; unsigned total_cost = 0, total_slack = 0; @@ -535,6 +570,27 @@ int Planner::do_work(int distro_placements[NUM_DISTRO]) Graph g; construct_graph(switches, &g); find_mincost_maxflow(&g); + map<int, int> switches_to_distros = find_switch_distro_map(g); + + for (unsigned i = 0; i < switches.size(); ++i) { + const auto distro_it = switches_to_distros.find(i); + if (distro_it == switches_to_distros.end()) { + total_cost += _INF; + continue; + } + int distro = distro_it->second; + total_cost += find_cost(switches[i], distro); + int this_distance = find_distance(switches[i], distro); + Inventory this_inv = find_inventory(switches[i], distro); + total_slack += find_slack(this_inv, this_distance); + total_inv += this_inv; + } + +#if !OUTPUT_FILES + if (log_buf == NULL) { + return total_cost; + } +#endif for (unsigned row = 1; row <= NUM_ROWS; ++row) { // Figure out distro markers. @@ -564,7 +620,12 @@ int Planner::do_work(int distro_placements[NUM_DISTRO]) logprintf("[31;22m%2u (%2u-%2u) ", row, row * 2 - 1, row * 2 + 0); for (unsigned num = 0; num < SWITCHES_PER_ROW; ++num) { - print_switch(g, switch_indexes[num]); + const auto distro_it = switches_to_distros.find(switch_indexes[num]); + if (distro_it == switches_to_distros.end()) { + print_switch(g, switch_indexes[num], -1); + } else { + print_switch(g, switch_indexes[num], distro_it->second); + } if (num == 1) { logprintf("%s %s", distro_marker_left, distro_marker_right); @@ -583,29 +644,17 @@ int Planner::do_work(int distro_placements[NUM_DISTRO]) } logprintf("[%u;22m\n", 37); - for (unsigned i = 0; i < switches.size(); ++i) { - int distro = find_distro(g, i); - if (distro == -1) { - total_cost += _INF; - continue; - } - int this_distance = find_distance(switches[i], distro); - Inventory this_inv = find_inventory(switches[i], distro); - total_cost += find_cost(switches[i], distro); - total_slack += find_slack(this_inv, this_distance); - total_inv += this_inv; - } - #if OUTPUT_FILES FILE *patchlist = fopen("patchlist.txt", "w"); FILE *switchlist = fopen("switches.txt", "w"); in_addr_t subnet_address = inet_addr(FIRST_SUBNET_ADDRESS); + num_ports_used.clear(); for (unsigned i = 0; i < switches.size(); ++i) { - int distro = find_distro(g, i); - if (distro == -1) { + const auto distro_it = switches_to_distros.find(i); + if (distro_it == switches_to_distros.end()) { continue; } - + int distro = distro_it->second; int port_num = num_ports_used[distro]++; fprintf(patchlist, "e%u-%u %s %s %s %s %s\n", switches[i].row * 2 - 1, switches[i].num + 1, @@ -649,29 +698,36 @@ int Planner::do_work(int distro_placements[NUM_DISTRO]) for (int i = 0; i < NUM_DISTRO; ++i) { Edge *e = g.source_node.edges[i]; - logprintf("Remaining ports on distro %d: %d\n", i + 1, e->capacity - e->flow); + logprintf("Remaining ports on distro %d: %d\n", i, e->capacity - e->flow); } return total_cost; } -void plan_recursively(int distro_placements[NUM_DISTRO], int distro_num, int min_placement, int max_placement, int *best_cost) +void plan_recursively(int distro_placements[NUM_DISTRO], int distro_num, int min_placement, int max_placement, atomic<int> *best_cost) { if (distro_num == NUM_DISTRO) { - string log; Planner p; - log.clear(); - p.set_log_buf(&log); - int cost = p.do_work(distro_placements); - if (cost >= *best_cost) { +try_again: + int old_best_cost = best_cost->load(); + if (cost >= old_best_cost) { return; } - *best_cost = cost; + if (!best_cost->compare_exchange_weak(old_best_cost, cost)) { + // Someone else changed the value in the meantime. + goto try_again; + } for (unsigned i = 0; i < NUM_DISTRO; ++i) { printf("%d ", distro_placements[i]); } printf("= %d\n", cost); + + // Do it once more, but this time with logging enabled. + string log; + p.set_log_buf(&log); + p.do_work(distro_placements); printf("%s\n", log.c_str()); + return; } @@ -699,9 +755,38 @@ int main(int argc, char **argv) printf("%s\n", log.c_str()); return 0; #else - int best_cost = _INF * 1000; + atomic<int> best_cost(_INF * 1000); distro_placements[0] = -3; // obvious - plan_recursively(distro_placements, 1, 6, NUM_ROWS, &best_cost); + + constexpr int min_placement = 6; + constexpr int max_placement = NUM_ROWS; + + // Boring single-threaded version + // plan_recursively(distro_placements, 1, min_placement, max_placement, &best_cost); + + #pragma omp parallel for schedule(dynamic,1) collapse(2) + for (int i = min_placement; i <= max_placement; ++i) { + for (int j = min_placement; j <= max_placement; ++j) { + if (j <= i) continue; + + int new_distro_placements[NUM_DISTRO]; + memcpy(new_distro_placements, distro_placements, sizeof(distro_placements)); + + new_distro_placements[1] = i; + + new_distro_placements[2] = j; + plan_recursively(new_distro_placements, 3, j + 1, max_placement, &best_cost); + new_distro_placements[2] = -j; + plan_recursively(new_distro_placements, 3, j + 1, max_placement, &best_cost); + + new_distro_placements[1] = -i; + new_distro_placements[2] = j; + plan_recursively(new_distro_placements, 3, j + 1, max_placement, &best_cost); + new_distro_placements[2] = -j; + plan_recursively(new_distro_placements, 3, j + 1, max_placement, &best_cost); + } + } + return 0; #endif } |