#include "coloquinte/topologies.hxx" #include "coloquinte/circuit_helper.hxx" #include "coloquinte/union_find.hxx" #include #include #include #include #include #include namespace coloquinte{ using edge_t = std::pair; namespace{ struct minmax_t{ int_t min, max; minmax_t(int_t mn, int_t mx) : min(mn), max(mx) {} minmax_t() {} void merge(minmax_t const o){ min = std::min(o.max, min); max = std::max(o.min, max); } void merge(int_t const p){ min = std::min(p, min); max = std::max(p, max); } }; } namespace steiner_lookup{ template int_t Hconnectivity::get_wirelength(std::array, pin_cnt> const sorted_points) const{ std::array minmaxs; for(index_t i=0; i> 4; minmaxs[b_con].merge(sorted_points.front() .y); minmaxs[e_con].merge(sorted_points.back() .y); for(std::uint8_t const E : connexions){ minmaxs[(E >> 4)].merge(minmaxs[(E & 15u)]); } int_t cost = sorted_points.back().x - sorted_points.front().x + sorted_points[b_con+1].x - sorted_points[e_con+1].x; for(std::uint8_t const E : connexions){ cost += std::abs((float)(sorted_points[(E >> 4) +1].x - sorted_points[(E & 15u) +1].x)); } for(index_t i=0; i std::array Hconnectivity::get_x_topology(std::array, pin_cnt> const sorted_points) const{ std::array ret; std::uint8_t b_con = extremes & 15u, e_con = extremes >> 4; ret[0] = edge_t(0, b_con+1); ret[1] = edge_t(pin_cnt-1, e_con+1); for(index_t i=0; i> 4) +1); } return ret; } } // End namespace steiner_lookup namespace { template int_t get_wirelength_from_sorted(std::vector > const & pins, std::array, array_size> const & lookups){ std::array, n> points; std::copy_n(pins.begin(), n, points.begin()); int_t cost = std::numeric_limits::max(); for(auto const L : lookups){ cost = std::min(cost, L.get_wirelength(points)); } return cost; } std::int64_t get_wirelength_from_topo(std::vector > const & points, std::vector > Htopo){ std::vector minmaxs(points.size()); for(index_t i=0; i{ index_t index; indexed_pt(point pt, index_t pos) : point(pt), index(pos) {} indexed_pt(){} }; template std::vector > get_topology_from_sorted(std::vector > const & pins, std::array, array_size> const & lookups){ std::array, n> points; std::copy_n(pins.begin(), n, points.begin()); // Find the horizontal topology with the smallest cost int_t cost = std::numeric_limits::max(); index_t ind = std::numeric_limits::max(); for(index_t i=0; i::max()); auto ret = lookups[ind].get_x_topology(points); return std::vector >(ret.begin(), ret.end()); } std::vector get_vertical_topology(std::vector > pins, std::vector const & Htopo){ index_t const null_ind = std::numeric_limits::max(); std::vector ipoints(pins.size()); for(index_t i=0; i min_y_pin(pins.size()); for(index_t i=0; i max_y_pin = min_y_pin; std::vector nxt_y_pin(pins.size(), null_ind); std::vector ret; for(auto const E : Htopo){ // Assuming a correctly ordered horizontal topology where the first node of the edge is never visited again index_t f=E.first, s=E.second; index_t first_yf=min_y_pin[f], first_ys=min_y_pin[s]; // Push the edges from the first and insert one of its elements in the second's linked structure if(max_y_pin[f] < min_y_pin[s] or max_y_pin[s] < min_y_pin[f]){ for(index_t yf=first_yf; nxt_y_pin[yf] != null_ind; yf = nxt_y_pin[yf]){ ret.push_back(edge_t(yf, nxt_y_pin[yf])); } if(max_y_pin[f] < min_y_pin[s]){ nxt_y_pin[max_y_pin[f]] = min_y_pin[s]; min_y_pin[s] = max_y_pin[f]; } else if(max_y_pin[s] < min_y_pin[f]){ nxt_y_pin[max_y_pin[s]] = min_y_pin[f]; max_y_pin[s] = min_y_pin[f]; nxt_y_pin[min_y_pin[f]] = null_ind; } else{ abort(); } } else{ // Need to chose a pin with two connexions because there will be no L route // One pin from the second is in the middle of the first if(max_y_pin[f] > max_y_pin[s]){ index_t middle_pin = max_y_pin[s]; index_t yf=first_yf; // Make the first connexions for(; nxt_y_pin[yf] < middle_pin; yf = nxt_y_pin[yf]){ ret.push_back(edge_t(yf, nxt_y_pin[yf])); } // Make the two connexions with the new pin ret.push_back(edge_t(yf, middle_pin)); yf = nxt_y_pin[yf]; ret.push_back(edge_t(yf, middle_pin)); // Finish the connexions for(; nxt_y_pin[yf] != null_ind; yf = nxt_y_pin[yf]){ ret.push_back(edge_t(yf, nxt_y_pin[yf])); } } // One pin from the first is in the middle of the second else{ for(index_t yf=first_yf; nxt_y_pin[yf] != null_ind; yf = nxt_y_pin[yf]){ ret.push_back(edge_t(yf, nxt_y_pin[yf])); } index_t middle_pin = max_y_pin[f]; // Find the place where we can insert this pin index_t ys=first_ys; for(; nxt_y_pin[ys] < middle_pin; ys = nxt_y_pin[ys]); nxt_y_pin[middle_pin] = nxt_y_pin[ys]; nxt_y_pin[ys] = middle_pin; } } } // The last visited gives the remaining connexions to push for(index_t yf=min_y_pin[Htopo.back().second]; nxt_y_pin[yf] != null_ind; yf = nxt_y_pin[yf]){ ret.push_back(edge_t(yf, nxt_y_pin[yf])); } // Back to the original ordering for(auto & E : ret){ E.first = ipoints[E.first].index; E.second = ipoints[E.second].index; } return ret; } inline void northeast_octant_neighbours(std::vector > pins, std::vector > & edges){ std::vector point_list; for(index_t i=0; i > active_upper_octant([](indexed_pt const a, indexed_pt const b)->bool{return a.x > b.x;}), active_lower_octant([](indexed_pt const a, indexed_pt const b)->bool{return a.y > b.y;}); for(indexed_pt const current : point_list){ { // North to north-east region auto first_it = active_upper_octant.lower_bound(current); // Largest x with x <= current.x auto it = first_it; for(; it != active_upper_octant.end() && it->x - it->y >= current.x - current.y; ++it){ edges.push_back(std::pair(current.index, it->index)); } if(first_it != active_upper_octant.end()){ active_upper_octant.erase(first_it, it); } active_upper_octant.insert(it, current); // Hint to insert the element since it is the correct position } // End region { // North-east to east region auto first_it = active_lower_octant.lower_bound(current); // Largest y with y <= current.y auto it = first_it; for(; it != active_lower_octant.end() && it->y - it->x >= current.y - current.x; ++it){ edges.push_back(std::pair(current.index, it->index)); } if(first_it != active_lower_octant.end()){ active_lower_octant.erase(first_it, it); } active_lower_octant.insert(it, current); // Hint to insert the element since it is the correct position } // End region } } // Gets the nearest octant neighbour for each point in the south-east quadrant inline void southeast_octant_neighbours(std::vector > pins, std::vector > & edges){ for(auto & pin : pins){ pin.y = - pin.y; } northeast_octant_neighbours(pins, edges); } std::vector > get_small_horizontal_topology_from_sorted(std::vector > const & pins){ assert(pins.size() <= 10); switch(pins.size()){ case 2: return std::vector(1, edge_t(0, 1)); case 3: return std::vector{{0, 1}, {1, 2}}; case 4: return get_topology_from_sorted<4, 2>(pins, steiner_lookup::topologies_4); case 5: return get_topology_from_sorted<5, 6>(pins, steiner_lookup::topologies_5); case 6: return get_topology_from_sorted<6, 23>(pins, steiner_lookup::topologies_6); case 7: return get_topology_from_sorted<7, 111>(pins, steiner_lookup::topologies_7); case 8: return get_topology_from_sorted<8, 642>(pins, steiner_lookup::topologies_8); case 9: return get_topology_from_sorted<9, 4334>(pins, steiner_lookup::topologies_9); case 10: return get_topology_from_sorted<10, 33510>(pins, steiner_lookup::topologies_10); default: // Only 1 and 0 left (11 and more are protected by an assertion) return std::vector(); } } // Get an ordering of the edges that is compatible with the processing functions std::vector get_tree_topo_sort(std::vector const & topo){ std::vector sorted_topo; std::vector > neighbours(topo.size()+1); for(edge_t const E : topo){ neighbours[E.first].push_back(E.second); neighbours[E.second].push_back(E.first); } std::vector to_visit; std::vector nbr_unvisited(topo.size()+1); for(index_t i=0; i<=topo.size(); ++i){ nbr_unvisited[i] = neighbours[i].size(); assert(topo.size() == 0 or nbr_unvisited[i] >= 1); if(nbr_unvisited[i] == 1) to_visit.push_back(i); } std::vector visited(topo.size()+1, 0); while(not to_visit.empty()){ index_t f = to_visit.back(); assert(visited[f] == 0); visited[f] = 1; to_visit.pop_back(); for(index_t s : neighbours[f]){ --nbr_unvisited[s]; if(visited[s] == 0){ // It is not a node we already visited sorted_topo.push_back(edge_t(f, s)); } if(nbr_unvisited[s] == 1){ to_visit.push_back(s); } } } assert(sorted_topo.size() == topo.size()); return sorted_topo; } std::vector get_big_horizontal_topology_from_sorted(std::vector > const & pins, index_t exactitude_limit){ auto spanning = get_MST_topology(pins); // TODO: perform local optimizations on the topology using exact Steiner tree algorithms // Remove horizontal suboptimalities i.e. when the connexions to the left and right are unbalanced // Reuse existing code by translation to vertical topology auto first_Htopo = get_tree_topo_sort(spanning); auto Vtopo = get_vertical_topology(pins, first_Htopo); Vtopo = get_tree_topo_sort(Vtopo); std::vector > inverted_coords = pins; for(point & pt : inverted_coords){ std::swap(pt.x, pt.y); } auto Htopo = get_vertical_topology(inverted_coords, Vtopo); // Sort the tree so that it is usable when building an RSMT return get_tree_topo_sort(Htopo); } } // End anonymous namespace std::vector get_RSMT_horizontal_topology(std::vector > const & pins, index_t exactitude_limit){ if(pins.size() <= 1) return std::vector(); else if(pins.size() == 2) return std::vector(1, edge_t(0, 1)); else if(pins.size() == 3){ std::vector ipoints(pins.size()); for(index_t i=0; i{{xpoints[0].index, xpoints[1].index}, {xpoints[1].index, xpoints[2].index}}; } else{ std::vector horizontal_topology; // Sort the pins by x coordinate std::vector ipoints(pins.size()); for(index_t i=0; i > sorted_pins(pins.size()); for(index_t i=0; i > get_MST_topology(std::vector > const & pins){ std::vector edges; if(pins.size() <= 2){ if(pins.size() == 2){ edges.push_back(edge_t(0, 1)); } if(pins.size() == 3){ auto D = [](point a, point b){ return (int_t)(std::abs((float)(a.x - b.x)) + std::abs((float)(a.y - b.y))); }; auto dists = std::array({{D(pins[1], pins[2]), D(pins[1], pins[2]), D(pins[0], pins[1])}}); index_t mx = std::max_element(dists.begin(), dists.end()) - dists.begin(); for(index_t i=0; i<3; ++i){ if(i != mx) edges.push_back(edge_t((i+1) % 3, (i+2) % 3)); } } return edges; } northeast_octant_neighbours(pins, edges); southeast_octant_neighbours(pins, edges); std::vector returned_edges; auto edge_length = [&](edge_t E){ point p1 = pins[E.first], p2 = pins[E.second]; return std::abs((float)(p1.x - p2.x)) + std::abs((float)(p1.y - p2.y)); }; // Perform Kruskal to get the tree std::sort(edges.begin(), edges.end(), [&](edge_t a, edge_t b){ return edge_length(a) < edge_length(b); }); union_find merger(pins.size()); for(index_t i=0; i > const & pins){ auto edges = get_MST_topology(pins); std::int64_t sum = 0; for(auto E : edges){ sum += std::abs((float)(pins[E.first].x - pins[E.second].x)); sum += std::abs((float)(pins[E.first].y - pins[E.second].y)); } return sum; } std::int64_t RSMT_length(std::vector > const & pins, index_t exactitude_limit){ assert(exactitude_limit <= 10 and exactitude_limit >= 3); if(pins.size() <= 3){ if(pins.size() == 2){ return std::abs((float)(pins[0].x - pins[1].x)) + std::abs((float)(pins[0].y - pins[1].y)); } else if(pins.size() == 3){ auto minmaxX = std::minmax_element(pins.begin(), pins.end(), [](point a, point b){ return a.x < b.x; }), minmaxY = std::minmax_element(pins.begin(), pins.end(), [](point a, point b){ return a.y < b.y; }); return (minmaxX.second->x - minmaxX.first->x) + (minmaxY.second->y - minmaxY.first->y); } else{ return 0; } } else{ std::vector > points = pins; std::sort(points.begin(), points.end(), [](point a , point b){return a.x < b.x; }); if(points.size() <= exactitude_limit){ switch(points.size()){ case 4: return get_wirelength_from_sorted<4, 2>(points, steiner_lookup::topologies_4); case 5: return get_wirelength_from_sorted<5, 6>(points, steiner_lookup::topologies_5); case 6: return get_wirelength_from_sorted<6, 23>(points, steiner_lookup::topologies_6); case 7: return get_wirelength_from_sorted<7, 111>(points, steiner_lookup::topologies_7); case 8: return get_wirelength_from_sorted<8, 642>(points, steiner_lookup::topologies_8); case 9: return get_wirelength_from_sorted<9, 4334>(points, steiner_lookup::topologies_9); case 10: return get_wirelength_from_sorted<10, 33510>(points, steiner_lookup::topologies_10); default: abort(); } } else{ // Need to create the full topology, then calculate the length back //return MST_length(points); auto horizontal_topology = get_big_horizontal_topology_from_sorted(points, exactitude_limit); return get_wirelength_from_topo(points, horizontal_topology); } } } point > > get_RSMT_topology(std::vector > const & pins, index_t exactitude_limit){ assert(exactitude_limit <= 10 and exactitude_limit >= 3); // For 3 pin and fewer, the topology is very simple if(pins.size() <= 2){ if(pins.size() == 2){ auto ret = std::vector(1, edge_t(0, 1)); return point >(ret, ret); } else{ return point >(); } } else if(pins.size() == 3){ std::vector ipoints(pins.size()); for(index_t i=0; i >{{{xpoints[0].index, xpoints[1].index}, {xpoints[1].index, xpoints[2].index}}, {{ypoints[0].index, ypoints[1].index}, {ypoints[1].index, ypoints[2].index}}}; } else{ std::vector horizontal_topology = get_RSMT_horizontal_topology(pins, exactitude_limit); return point >(horizontal_topology, get_vertical_topology(pins, horizontal_topology)); } } } // Namespace coloquinte