#include "coloquinte/rough_legalizers.hxx" #include "coloquinte/optimization_subproblems.hxx" #include #include #include #include #include namespace coloquinte{ namespace gp{ using std::cerr; using std::endl; namespace{ const capacity_t default_density_mul = 256; } void region_distribution::just_uniquify(std::vector & cell_references){ if(cell_references.size() >= 1){ index_t j=0; cell_ref prev_cell = cell_references[0]; for(auto it = cell_references.begin()+1; it != cell_references.end(); ++it){ if(it->index_in_list_ == prev_cell.index_in_list_){ prev_cell.allocated_capacity_ += it->allocated_capacity_; } else{ cell_references[j] = prev_cell; ++j; prev_cell = *it; } } cell_references[j]=prev_cell; cell_references.resize(j+1); } } void region_distribution::sort_uniquify(std::vector & cell_references){ std::sort(cell_references.begin(), cell_references.end(), [](cell_ref a, cell_ref b){ return a.index_in_list_ < b.index_in_list_; }); just_uniquify(cell_references); } void region_distribution::region::uniquify_references(){ sort_uniquify(cell_references_); } void region_distribution::fractions_minimization(){ for(region & R : placement_regions_){ R.uniquify_references(); } // Find cycles of cut cells, then find a spanning tree to reallocate the cells // TODO } void region_distribution::region::selfcheck() const{ capacity_t total_allocated = 0; for(cell_ref const c : cell_references_){ total_allocated += c.allocated_capacity_; assert(c.allocated_capacity_ > 0); } assert(total_allocated <= capacity_); } void region_distribution::selfcheck() const{ for(region const & R : placement_regions_){ R.selfcheck(); } std::vector capacities(cell_list_.size(), 0); for(region const & R : placement_regions_){ for(cell_ref const C : R.cell_references_){ capacities[C.index_in_list_] += C.allocated_capacity_; } } for(index_t i=0; i < cell_list_.size(); ++i){ assert(capacities[i] == cell_list_[i].demand_ * cell_density_mul); } } region_distribution::region::region(capacity_t cap, point pos, std::vector cells) : capacity_(cap), pos_(pos), cell_references_(cells){} box region_distribution::get_box(index_t x, index_t y, index_t x_cnt, index_t y_cnt) const{ auto ret = box( placement_area_.x_min + ( ((std::int64_t) (placement_area_.x_max - placement_area_.x_min)) * x ) / x_cnt, placement_area_.x_min + ( ((std::int64_t) (placement_area_.x_max - placement_area_.x_min)) * (x+1) ) / x_cnt, placement_area_.y_min + ( ((std::int64_t) (placement_area_.y_max - placement_area_.y_min)) * y ) / y_cnt, placement_area_.y_min + ( ((std::int64_t) (placement_area_.y_max - placement_area_.y_min)) * (y+1) ) / y_cnt ); assert(not ret.empty()); return ret; } std::vector region_distribution::prepare_regions(index_t x_cnt, index_t y_cnt) const{ assert(placement_area_.x_max > placement_area_.x_min); assert(placement_area_.y_max > placement_area_.y_min); // Uses a sweepline algorithm to initialize all regions' capacities at a time, taking macros and density maps into account // The events in the priority queue: basically a density_limit object, but the y_min may be different from the original one struct event{ box box_; capacity_t multiplicator_; bool operator<(event const o) const{ return box_.y_min > o.box_.y_min // Priority queue = highest first or (box_.y_min == o.box_.y_min and multiplicator_ > o.multiplicator_); // Smallest density first, just because } event(box surface, capacity_t den) : box_(surface), multiplicator_(den) {} event(density_limit D, capacity_t density_mul) : box_(D.box_) { multiplicator_ = static_cast(std::ceil(D.density_ * static_cast(density_mul))); } }; struct line_y{ int_t y_min, y_max; capacity_t multiplicator_; line_y(int_t mn, int_t mx, capacity_t cap) : y_min(mn), y_max(mx), multiplicator_(cap) {} }; // The regions' capacities std::vector region_caps(x_cnt * y_cnt, 0); // Find the limits of the regions std::vector x_reg_lims(x_cnt+1), y_reg_lims(y_cnt+1); for(index_t i=0; i<=x_cnt; ++i){ x_reg_lims[i] = placement_area_.x_min + ( ((std::int64_t) (placement_area_.x_max - placement_area_.x_min)) * i ) / x_cnt; } for(index_t i=0; i<=y_cnt; ++i){ y_reg_lims[i] = placement_area_.y_min + ( ((std::int64_t) (placement_area_.y_max - placement_area_.y_min)) * i ) / y_cnt; } //std::vector > added; auto add_region = [&](box bx, capacity_t d){ /* // Failed attempt at calculating the coordinates directly point dims = placement_area_.dimensions(); auto mins = point(placement_area_.x_min, placement_area_.y_min); index_t x_mn = (static_cast(bx.x_min - mins.x + 1) * x_cnt) / dims.x, x_mx = (static_cast(bx.x_max - mins.x - 1) * x_cnt) / dims.x + 1, y_mn = (static_cast(bx.y_min - mins.y + 1) * y_cnt) / dims.y, y_mx = (static_cast(bx.y_max - mins.y - 1) * y_cnt) / dims.y + 1; */ /* // Additional debugging for(index_t k=0; k= placement_area_.x_min); assert(bx.y_min >= placement_area_.y_min); assert(bx.x_max <= placement_area_.x_max); assert(bx.y_max <= placement_area_.y_max); index_t x_mn = std::upper_bound(x_reg_lims.begin(), x_reg_lims.end(), bx.x_min) - x_reg_lims.begin() -1, y_mn = std::upper_bound(y_reg_lims.begin(), y_reg_lims.end(), bx.y_min) - y_reg_lims.begin() -1, x_mx = std::lower_bound(x_reg_lims.begin(), x_reg_lims.end(), bx.x_max) - x_reg_lims.begin(), y_mx = std::lower_bound(y_reg_lims.begin(), y_reg_lims.end(), bx.y_max) - y_reg_lims.begin(); for(index_t x=x_mn; x cur_box = get_box(x, y, x_cnt, y_cnt); assert(bx.intersects(cur_box)); box inter = bx.intersection(cur_box); point dims = inter.dimensions(); region_caps[y*x_cnt + x] += d * static_cast(dims.x) * static_cast(dims.y); } } }; // All rectangles and new rectangles are pushed there std::priority_queue events; for(density_limit D : density_map_){ // Keep only the useful parts of the rectangles to simplify the algorithm if(D.box_.intersects(placement_area_)){ density_limit pushed; pushed.density_ = D.density_; pushed.box_ = D.box_.intersection(placement_area_); assert(not pushed.box_.empty()); // always true with this definition of intersects events.push(event(pushed, full_density_mul)); } } // The initial sweepline, with begin and end of the line std::map active_obstacles; line_y placement_begin (placement_area_.y_min, placement_area_.y_max, full_density_mul), placement_end (placement_area_.y_min, placement_area_.y_max, 0); active_obstacles.insert(std::pair(placement_area_.x_min, placement_begin)); // Full density placement area as initial object active_obstacles.insert(std::pair(placement_area_.x_max, placement_end)); // Main loop: sweep the line on y (the line is horizontal, and moves toward bigger y) while(not events.empty()){ event D = events.top(); int_t x_b=D.box_.x_min, x_e=D.box_.x_max; int_t y_b=D.box_.y_min, y_e=D.box_.y_max; events.pop(); assert(x_b >= placement_area_.x_min); assert(y_b >= placement_area_.y_min); assert(x_e <= placement_area_.x_max); assert(y_e <= placement_area_.y_max); // For each delimitation between the bounds of the new rectangle // If the new delimitation has higher density or this delimitation ends on y there // Remove it, push a new event if it ends on y after this rectangle // Else: // Keep it, push a new event if it ends on y before this rectangle // Find covered or partially covered rectangles on the line (rectangles on the line don't overlap) line_y const new_elt(y_b, y_e, D.multiplicator_); std::vector new_delimitations; // First element on the line whose x is after our x_min (i.e. may have an intersection), and while there is an intersection auto first_it = active_obstacles.upper_bound(x_b); assert(first_it != active_obstacles.begin()); assert(std::prev(first_it)->first <= x_b); bool currently_in = false; for(auto it = std::prev(first_it); it != active_obstacles.end() and it->first < x_e;){ auto next_it = std::next(it); assert(next_it != active_obstacles.end()); assert(it->second.y_min <= y_b); assert(it->second.y_max >= y_b); assert(it->first < x_e); assert(next_it->first > x_b); assert(next_it-> first > it->first); int_t x_c_min = std::max(x_b, it->first), x_c_max = std::min(x_e, next_it->first); assert(x_c_min < x_c_max); // Add the area from it->second.y_min to D.box_.y_min if(y_b > it->second.y_min) add_region(box(it->first, next_it->first, it->second.y_min, y_b), it->second.multiplicator_); it->second.y_min = y_b; // Now the part before has been used auto part_b = *it, part_e = *next_it; if(part_b.second.multiplicator_ > D.multiplicator_ or part_b.second.y_max == y_b){ // The new event is visible now // In case parts of the line become visible again later if(part_b.second.y_max > y_e){ // Push back a new event to account for the comeback events.push(event(box(x_c_min, x_c_max, y_e, part_b.second.y_max), part_b.second.multiplicator_)); } // Depending whether this part of the line is or is not fully covered if(part_b.first >= x_b){ // If covered at the beginning active_obstacles.erase(it); } if(part_e.first > x_e){ // If not covered at the end auto inserted = active_obstacles.insert(std::pair(x_e, part_b.second)); assert(inserted.second); } // The events becomes visible on the line if(not currently_in){ auto inserted = active_obstacles.insert(std::pair(x_c_min, new_elt)); assert(inserted.second); } currently_in = true; } else{ // The new event is not visible yet currently_in = false; if(part_b.second.y_max < y_e){ // Push back a new event events.push(event(box(x_c_min, x_c_max, part_b.second.y_max, y_e), D.multiplicator_)); } } it = next_it; } } for(auto it=active_obstacles.begin(); std::next(it) != active_obstacles.end(); ++it){ assert(it->second.y_max == placement_area_.y_max); add_region(box(it->first, std::next(it)->first, it->second.y_min, it->second.y_max), it->second.multiplicator_); } std::vector ret(x_cnt*y_cnt); for(index_t y=0; y bx = get_box(x, y, x_cnt, y_cnt); ret[y*x_cnt + x] = region( region_caps[y*x_cnt + x], point(0.5f * bx.x_min + 0.5f * bx.x_max, 0.5f * bx.y_min + 0.5f * bx.y_max), std::vector() ); } } return ret; } void region_distribution::x_bipartition(){ std::vector old_placement_regions = prepare_regions(2*x_regions_cnt(), y_regions_cnt()); placement_regions_.swap(old_placement_regions); index_t old_x_regions_cnt = x_regions_cnt(); index_t old_y_regions_cnt = y_regions_cnt(); x_regions_cnt_ *= 2; for(index_t x=0; x < old_x_regions_cnt; ++x){ for(index_t y=0; y < old_y_regions_cnt; ++y){ index_t i = y * old_x_regions_cnt + x; old_placement_regions[i].distribute_cells(get_region(2*x, y), get_region(2*x+1, y)); } } } void region_distribution::y_bipartition(){ std::vector old_placement_regions = prepare_regions(x_regions_cnt(), 2*y_regions_cnt()); placement_regions_.swap(old_placement_regions); index_t old_x_regions_cnt = x_regions_cnt(); index_t old_y_regions_cnt = y_regions_cnt(); y_regions_cnt_ *= 2; for(index_t x=0; x < old_x_regions_cnt; ++x){ for(index_t y=0; y < old_y_regions_cnt; ++y){ index_t i = y * old_x_regions_cnt + x; old_placement_regions[i].distribute_cells(get_region(x, 2*y), get_region(x, 2*y+1)); } } } // The big awful function that handles optimal cell distribution between two regions; not meant to be called externally void region_distribution::region::distribute_new_cells(region & region_a, region & region_b, std::vector basic_cells){ struct cost_diff_cell : cell_ref{ float_t marginal_cost_; bool operator<(cost_diff_cell const o) const{ return marginal_cost_ < o.marginal_cost_; } cost_diff_cell(cell_ref cell, float_t cost) : cell_ref(cell), marginal_cost_(cost) {} }; std::vector cells; for(cell_ref const c : basic_cells){ cells.push_back(cost_diff_cell(c, region_a.distance(c) - region_b.distance(c))); } // Cells trending toward a first std::sort(cells.begin(), cells.end());//, [](cell_ref const c1, cell_ref const c2) -> bool{ return c1.marginal_cost_ < c2.marginal_cost_; }); index_t preference_limit=0, // First cell that would rather go to b (or cells.size()) a_capacity_limit=0, // After the last cell that region_a can take entirely (or 0) b_capacity_limit=cells.size(); // Last cell (but first in the vector) that region_b can take entirely (or cells.size()) capacity_t remaining_capacity_a = region_a.capacity_, remaining_capacity_b = region_b.capacity_; for(;preference_limit < cells.size() && cells[preference_limit].marginal_cost_ <= 0.0; ++preference_limit) ; { // Block capacity_t remaining_cap_a = region_a.capacity_; index_t i=0; while(i= 0){ a_capacity_limit = i+1; remaining_capacity_a = remaining_cap_a; } ++i; } } // Block { // Block capacity_t remaining_cap_b = region_b.capacity_; index_t i=cells.size(); while(i>0){ --i; remaining_cap_b -= cells[i].allocated_capacity_; if(remaining_cap_b >= 0){ b_capacity_limit = i; remaining_capacity_b = remaining_cap_b; } } } // Block std::vector & cells_a_side = region_a.cell_references_; std::vector & cells_b_side = region_b.cell_references_; cells_a_side.clear(); cells_b_side.clear(); if(preference_limit >= b_capacity_limit and preference_limit <= a_capacity_limit){ cells_a_side.insert(cells_a_side.end(), cells.begin(), cells.begin() + preference_limit); cells_b_side.insert(cells_b_side.end(), cells.begin() + preference_limit, cells.end()); } else{ index_t cut_position; capacity_t allocated_to_a_part; // Where do we cut? if(preference_limit < b_capacity_limit){ // Pack on b cut_position = b_capacity_limit-1; // Exists since preference_limit >= 0 allocated_to_a_part = cells[cut_position].allocated_capacity_ - remaining_capacity_b; } else{ // Pack on a // if(preference_limit > a_capacity_limit) cut_position = a_capacity_limit; // Necessarily a correct position since preference_limits <= cells.size() allocated_to_a_part = remaining_capacity_a; } cells_a_side.reserve( cut_position +1); cells_b_side.reserve(cells.size()-cut_position +1); cells_a_side.insert(cells_a_side.end(), cells.begin(), cells.begin() + cut_position); cell_ref cell_cut_a = cells[cut_position], cell_cut_b = cells[cut_position]; cell_cut_a.allocated_capacity_ = allocated_to_a_part; cell_cut_b.allocated_capacity_ -= allocated_to_a_part; if(cell_cut_a.allocated_capacity_ > 0){ cells_a_side.push_back(cell_cut_a); } if(cell_cut_b.allocated_capacity_ > 0){ cells_b_side.push_back(cell_cut_b); } cells_b_side.insert(cells_b_side.end(), cells.begin() + cut_position+1, cells.end()); } } void region_distribution::region::distribute_cells(region & a, region & b) const{ distribute_new_cells(a, b, cell_references_); assert(a.allocated_capacity() + b.allocated_capacity() == allocated_capacity()); assert(a.capacity() + b.capacity() == capacity()); } void region_distribution::region::redistribute_cells(region & Ra, region & Rb){ if(Ra.capacity() > 0 and Rb.capacity() > 0){ std::vector cells; cells.reserve(Ra.cell_references_.size()+Rb.cell_references_.size()); cells.insert(cells.end(), Ra.cell_references_.begin(), Ra.cell_references_.end()); cells.insert(cells.end(), Rb.cell_references_.begin(), Rb.cell_references_.end()); distribute_new_cells(Ra, Rb, cells); } } void region_distribution::region::distribute_new_cells(std::vector > regions, std::vector all_cells){ std::vector caps; for(region_distribution::region & R : regions){ caps.push_back(R.capacity_); R.cell_references_.clear(); } sort_uniquify(all_cells); std::sort(all_cells.begin(), all_cells.end(), [](cell_ref const a, cell_ref const b){ return a.allocated_capacity_ > b.allocated_capacity_ or (a.allocated_capacity_ == b.allocated_capacity_ and a.pos_.x+a.pos_.y < b.pos_.x+b.pos_.y); }); std::vector > costs(regions.size()); std::vector demands; for(auto const C : all_cells){ for(index_t r=0; r > res = transport_generic(caps, demands, costs); assert(res.size() == regions.size()); for(index_t i=0; i 0){ cell_ref C = all_cells[j]; C.allocated_capacity_ = res[i][j]; regions[i].get().cell_references_.push_back(C); } } } } void region_distribution::region::redistribute_cells(std::vector > regions){ if(regions.size() > 1){ std::vector all_cells; for(region_distribution::region & R : regions){ all_cells.insert(all_cells.end(), R.cell_references_.begin(), R.cell_references_.end()); } distribute_new_cells(regions, all_cells); } } void region_distribution::region::distribute_cells(std::vector > regions) const{ distribute_new_cells(regions, cell_references_); } void region_distribution::multipartition(index_t x_width, index_t y_width){ assert(x_width > 0 and y_width > 0); std::vector old_placement_regions = prepare_regions(x_width*x_regions_cnt(), y_width*y_regions_cnt()); placement_regions_.swap(old_placement_regions); index_t old_x_regions_cnt = x_regions_cnt(); index_t old_y_regions_cnt = y_regions_cnt(); x_regions_cnt_ *= x_width; y_regions_cnt_ *= y_width; for(index_t x=0; x < old_x_regions_cnt; ++x){ for(index_t y=0; y < old_y_regions_cnt; ++y){ index_t i = y * old_x_regions_cnt + x; std::vector > destination_regions; capacity_t tot_cap = 0; // Take the new regions for(index_t l_x=0; l_x(cur_reg)); tot_cap += cur_reg.capacity(); } } // Distribute the cells old_placement_regions[i].distribute_cells(destination_regions); assert(tot_cap == old_placement_regions[i].capacity()); } } } void region_distribution::redo_bipartitions(){ // This function performs optimization between neighbouring regions in various directions // The most important feature is diagonal optimization, since it is not done during partitioning redo_diagonal_bipartitions(); redo_adjacent_bipartitions(); } void region_distribution::redo_diagonal_bipartitions(){ // Take four cells at a time and optimize them auto const optimize_quad_diag = [&](index_t x, index_t y){ region::redistribute_cells(get_region(x, y), get_region(x+1, y+1)); region::redistribute_cells(get_region(x+1, y), get_region(x, y+1)); }; // x is the fast index: the innermost loop operates on it auto const optimize_diag_on_y = [&](index_t y){ for(index_t x=0; x+1 < x_regions_cnt(); x+=2){ if(x+2 < x_regions_cnt()){ // x odd optimize_quad_diag(x+1, y); } // x even optimize_quad_diag(x, y); } }; // OpenMP doesn't allow y+1 < y_regions_cnt(), but anyway y_regions_cnt() >= 1 #pragma omp parallel for for(index_t y=0; y < y_regions_cnt()-1; y+=2){ // y even optimize_diag_on_y(y); } #pragma omp parallel for for(index_t y=1; y < y_regions_cnt()-1; y+=2){ // y odd optimize_diag_on_y(y); } } void region_distribution::redo_adjacent_bipartitions(){ auto const optimize_H = [&](index_t x, index_t y){ region::redistribute_cells(get_region(x, y), get_region(x+1, y)); }; auto const optimize_V = [&](index_t x, index_t y){ region::redistribute_cells(get_region(x, y), get_region(x, y+1)); }; // x bipartitions #pragma omp parallel for for(index_t y=0; y < y_regions_cnt(); ++y){ for(index_t x=0; x+1 < x_regions_cnt(); x+=2){ if(x+2 < x_regions_cnt()){ // x odd optimize_H(x+1, y); } // x even optimize_H(x, y); } } // y bipartitions #pragma omp parallel for for(index_t x=0; x < x_regions_cnt(); ++x){ for(index_t y=0; y+1 < y_regions_cnt(); y+=2){ if(y+2 < y_regions_cnt()){ // y odd optimize_V(x, y+1); } // y even optimize_V(x, y); } } } void region_distribution::redo_multipartitions(index_t x_width, index_t y_width){ if(x_width < 2 and y_width < 2) throw std::runtime_error("Multipartitioning requires an optimization window of 2 or more\n"); auto const reoptimize_group = [&](index_t x, index_t y){ std::vector > to_opt; for(index_t l_x=x; l_x < std::min(x+x_width, x_regions_cnt()); ++l_x){ for(index_t l_y=y; l_y < std::min(y+y_width, y_regions_cnt()); ++l_y){ to_opt.push_back(std::reference_wrapper(get_region(l_x, l_y))); } } region::redistribute_cells(to_opt); }; auto const optimize_on_y = [&](index_t y){ for(index_t x=0; x < x_regions_cnt(); x+=x_width){ if(x+x_width < x_regions_cnt()){ reoptimize_group(x+x_width/2, y); } reoptimize_group(x, y); } }; for(index_t y=0; y < y_regions_cnt(); y+=y_width){ if(y+y_width < y_regions_cnt()){ optimize_on_y(y+y_width/2); } optimize_on_y(y); } } inline void region_distribution::region::distribute_new_cells(std::vector > regions, std::vector cells, std::function)> coord){ // Gather all cells and the useful regions std::vector > all_regions; for(region & reg_ref : regions){ if(reg_ref.capacity() > 0){ cells.insert(cells.end(), reg_ref.cell_references_.begin(), reg_ref.cell_references_.end()); reg_ref.cell_references_.clear(); all_regions.push_back(std::reference_wrapper(reg_ref)); } else{ assert(reg_ref.cell_references_.empty()); } } // Sort the regions by coordinate std::sort(all_regions.begin(), all_regions.end(), [&](std::reference_wrapper const a, std::reference_wrapper const b){ return coord(a.get().pos_) < coord(b.get().pos_); }); // And the cells std::sort(cells.begin(), cells.end(), [&](cell_ref const a, cell_ref const b){ return coord(a.pos_) < coord(b.pos_); }); just_uniquify(cells); std::vector sources, sinks; for(cell_ref const c : cells){ sources.push_back(t1D_elt(static_cast(coord(c.pos_)), c.allocated_capacity_)); } for(region & reg_ref : all_regions){ sinks.push_back(t1D_elt(static_cast(coord(reg_ref.pos_)), reg_ref.capacity())); } std::vector const positions = transport_1D(sources, sinks); std::vector prev_cap(1, 0); for(t1D_elt e: sinks){ assert(e.second > 0); prev_cap.push_back(prev_cap.back() + e.second); } for(index_t i=0; i= 0); assert(sources[i].second > 0); if(i>0) assert(sources[i-1].second + positions[i-1] <= positions[i]); } for(index_t i=0, r=0; i 0){ while(prev_cap[r+1] <= cur_pos){ // After the end of region r ++r; } cell_ref new_c = c; capacity_t used_cap = std::min(prev_cap[r+1] - cur_pos, cur_cap); new_c.allocated_capacity_ = used_cap; assert(used_cap >= 0); if(used_cap > 0){ all_regions[r].get().cell_references_.push_back(new_c); } cur_pos += used_cap; cur_cap -= used_cap; } } } inline void region_distribution::region::redistribute_cells(std::vector > & regions, std::function)> coord){ std::vector cells; for(region & reg_ref : regions){ if(reg_ref.capacity() > 0){ cells.insert(cells.end(), reg_ref.cell_references_.begin(), reg_ref.cell_references_.end()); reg_ref.cell_references_.clear(); } else{ assert(reg_ref.cell_references_.empty()); } } distribute_new_cells(regions, cells, coord); } void region_distribution::redo_line_partitions(){ // Optimize a single line or column #pragma omp parallel for for(index_t y=0; y > regs; for(index_t x=0; x(get_region(x, y))); } region::redistribute_cells(regs, [](point p){ return p.x; }); } #pragma omp parallel for for(index_t x=0; x > regs; for(index_t y=0; y(get_region(x, y))); } region::redistribute_cells(regs, [](point p){ return p.y; }); } } void region_distribution::x_resize(index_t sz){ assert(sz > 0); std::vector old_placement_regions = prepare_regions(sz, y_regions_cnt()); placement_regions_.swap(old_placement_regions); index_t old_x_regions_cnt = x_regions_cnt(); x_regions_cnt_ = sz; #pragma omp parallel for for(index_t y=0; y cells; for(index_t x=0; x const & cur = old_placement_regions[y*old_x_regions_cnt + x].cell_references_; cells.insert(cells.end(), cur.begin(), cur.end()); } std::vector > regs; for(index_t x=0; x(get_region(x, y))); } region::distribute_new_cells(regs, cells, [](point p){ return p.x; }); } } void region_distribution::y_resize(index_t sz){ assert(sz > 0); std::vector old_placement_regions = prepare_regions(x_regions_cnt(), sz); placement_regions_.swap(old_placement_regions); index_t old_y_regions_cnt = y_regions_cnt(); y_regions_cnt_ = sz; #pragma omp parallel for for(index_t x=0; x cells; for(index_t y=0; y const & cur = old_placement_regions[y*x_regions_cnt() + x].cell_references_; cells.insert(cells.end(), cur.begin(), cur.end()); } std::vector > regs; for(index_t y=0; y(get_region(x, y))); } region::distribute_new_cells(regs, cells, [](point p){ return p.y; }); } } void region_distribution::redo_diag_partitions(index_t len){ if(x_regions_cnt() <= 1 or y_regions_cnt() <= 1) return; auto const reoptimize_rdiag = [&](index_t x_b, index_t y_b){ index_t max_delta = std::min(x_regions_cnt()-x_b, y_regions_cnt()-y_b); for(index_t delta=0; delta+len/2 > to_opt; for(index_t m=0; m < std::min(max_delta-delta, len); ++m){ to_opt.push_back(std::reference_wrapper(get_region(x_b+delta+m, y_b+delta+m))); } region::redistribute_cells(to_opt); } }; auto const reoptimize_ldiag = [&](index_t x_b, index_t y_b){ index_t max_delta = std::min(x_regions_cnt()-x_b, y_b+1); for(index_t delta=0; delta+len/2 > to_opt; for(index_t m=0; m < std::min(max_delta-delta, len); ++m){ to_opt.push_back(std::reference_wrapper(get_region(x_b+delta+m, y_b-delta-m))); } region::redistribute_cells(to_opt); } }; for(index_t y=y_regions_cnt()-2; y >= 1; --y) reoptimize_rdiag(0, y); for(index_t x=0; x+1 placement_area, netlist const & circuit, placement_t const & pl, std::vector const & density_map, bool full_density ): x_regions_cnt_(1), y_regions_cnt_(1), placement_area_(placement_area), density_map_(density_map), full_density_mul(default_density_mul) { capacity_t tot_area = 0; for(index_t i=0; i >(pl.positions_[i]) + 0.5f * static_cast >(c.size), i)); tot_area += c.area; } else{ // Create an obstacle corresponding to the macro auto pos = pl.positions_[i]; auto end = pos + c.size; density_limit macro; macro.box_ = box(pos.x, end.x, pos.y, end.y); macro.density_ = 0.0f; density_map_.push_back(macro); } } placement_regions_ = prepare_regions(1, 1); if(full_density){ cell_density_mul = default_density_mul; } else{ cell_density_mul = placement_regions_[0].capacity() / tot_area; if(cell_density_mul < full_density_mul){ throw std::runtime_error("The density required by the obstacles is above 1"); } } for(index_t i=0; i placement_area, netlist const & circuit, placement_t const & pl, std::vector const & density_map){ return region_distribution(placement_area, circuit, pl, density_map, true); } region_distribution region_distribution::uniform_density_distribution(box placement_area, netlist const & circuit, placement_t const & pl, std::vector const & density_map){ return region_distribution(placement_area, circuit, pl, density_map, false); } void region_distribution::update(netlist const & circuit, placement_t const & pl){ for(movable_cell & c : cell_list_){ index_t i = c.index_in_placement_; c.pos_ = static_cast >(pl.positions_[i]) + 0.5f * static_cast >(circuit.get_cell(i).size); } for(region & R : placement_regions_){ for(cell_ref & c : R.cell_references_){ c.pos_ = cell_list_[c.index_in_list_].pos_; } } } std::vector region_distribution::export_positions() const{ std::vector > weighted_pos(cell_list_.size(), point(0.0, 0.0)); for(region const & R : placement_regions_){ for(cell_ref C : R.cell_references_){ weighted_pos[C.index_in_list_] = weighted_pos[C.index_in_list_] + static_cast(C.allocated_capacity_) * R.pos_; } } std::vector ret; for(index_t i=0; i(1.0) / static_cast(C.demand_ * cell_density_mul) ) * weighted_pos[i]; ret.push_back(C); } return ret; } struct OSRP_task{ float_t size; float_t goal_pos; float_t weight; index_t orig; OSRP_task(float_t p, float_t s, float_t w, index_t i) : size(s), goal_pos(p), weight(w), orig(i) {} OSRP_task(){} }; std::vector get_optimal_quadratic_pos(std::vector cells, float_t pos_begin, float_t pos_end){ if(cells.empty()){ return std::vector(); } struct OSRP_cluster{ index_t begin, end; float_t pos, size; float_t weight; OSRP_cluster(index_t b, index_t e, float_t p, float_t s, float_t w) : begin(b), end(e), pos(p), size(s), weight(w) {} void merge(OSRP_cluster const o){ begin = o.begin; pos = (weight * pos + o.weight * o.pos) / (weight + o.weight); size += o.size; weight += o.weight; } }; std::sort(cells.begin(), cells.end(), [](OSRP_task a, OSRP_task b){ return a.goal_pos < b.goal_pos; }); // Modify the goal pos to get absolute goal positions between pos_begin and pos_end - sum_sizes float_t sum_sizes = 0.0; for(auto & c : cells){ c.goal_pos -= sum_sizes; sum_sizes += c.size; } float_t abs_begin = pos_begin + 0.5 * cells[0].size; // First cell must be far enough from the beginning float_t abs_end = pos_end - sum_sizes + 0.5 * cells[0].size; // Last cell must be far enough from the end for(auto & c : cells){ c.goal_pos = std::max(c.goal_pos, abs_begin); c.goal_pos = std::min(c.goal_pos, abs_end); } std::vector clusters; for(index_t i=0; i= to_add.pos)){ to_add.merge(clusters.back()); clusters.pop_back(); } clusters.push_back(to_add); } std::vector ret(cells.size(), 0.0); // For every cell, recover its true position from the absolute position of a cluster float_t sum_prev_sizes = 0.0; for(OSRP_cluster cur : clusters){ for(index_t i=cur.begin; i <= cur.end; ++i){ ret[cells[i].orig] = cur.pos + sum_prev_sizes; sum_prev_sizes += cells[i].size; } } return ret; } std::vector region_distribution::export_spread_positions_quadratic() const{ std::vector > weighted_pos(cell_list_.size(), point(0.0, 0.0)); for(index_t y=0; y(R.capacity()); box surface = static_cast >(get_box(x, y, x_regions_cnt(), y_regions_cnt())); std::vector x_cells(n), y_cells(n); for(index_t i=0; i pt = R.cell_references_[i].pos_; float_t cap = static_cast(R.cell_references_[i].allocated_capacity_); x_cells[i] = OSRP_task(pt.x, cap/total_capacity * (surface.x_max - surface.x_min), 1.0, i); y_cells[i] = OSRP_task(pt.y, cap/total_capacity * (surface.y_max - surface.y_min), 1.0, i); } std::vector x_ret = get_optimal_quadratic_pos(x_cells, surface.x_min, surface.x_max); std::vector y_ret = get_optimal_quadratic_pos(y_cells, surface.y_min, surface.y_max); for(index_t i=0; i(R.cell_references_[i].allocated_capacity_) * point(x_ret[i], y_ret[i]); } } } std::vector ret; for(index_t i=0; i 0); C.pos_ = ( static_cast(1.0) / static_cast(C.demand_) ) * weighted_pos[i]; assert(std::isfinite(C.pos_.x) and std::isfinite(C.pos_.y)); ret.push_back(C); } return ret; } std::vector region_distribution::export_spread_positions_linear() const{ std::vector > weighted_pos(cell_list_.size(), point(0.0, 0.0)); for(index_t y=0; y(R.capacity()); box surface = static_cast >(get_box(x, y, x_regions_cnt(), y_regions_cnt())); assert(surface.x_max > surface.x_min and surface.y_max > surface.y_min); std::vector > x_cells, y_cells; for(auto const C : R.cell_references_){ float_t cap = static_cast(C.allocated_capacity_); float_t x_cap_prop = cap/total_capacity * (surface.x_max - surface.x_min), y_cap_prop = cap/total_capacity * (surface.y_max - surface.y_min); x_cells.push_back(legalizable_task(x_cap_prop, C.pos_.x, C.index_in_list_)); y_cells.push_back(legalizable_task(y_cap_prop, C.pos_.y, C.index_in_list_)); } OSRP_leg x_leg(surface.x_min, surface.x_max), y_leg(surface.y_min, surface.y_max); std::sort(x_cells.begin(), x_cells.end()); for(legalizable_task & C : x_cells) C.target_pos -= 0.5 * C.width; for(legalizable_task & C : x_cells) x_leg.push(C); auto x_pl = x_leg.get_placement(); for(index_t i=0; i(x_cells[i].width * total_capacity / (surface.x_max - surface.x_min)); } std::sort(y_cells.begin(), y_cells.end()); for(legalizable_task & C : y_cells) C.target_pos -= 0.5 * C.width; for(legalizable_task & C : y_cells) y_leg.push(C); auto y_pl = y_leg.get_placement(); for(index_t i=0; i(y_cells[i].width * total_capacity / (surface.y_max - surface.y_min)); } } } std::vector ret; for(index_t i=0; i 0); C.pos_ = ( 1.0f / static_cast(C.demand_ * cell_density_mul) ) * weighted_pos[i]; assert(std::isfinite(C.pos_.x) and std::isfinite(C.pos_.y)); ret.push_back(C); } return ret; } float_t region_distribution::region::cost() const{ float_t res = 0.0; for(cell_ref const C : cell_references_){ res += (std::abs(C.pos_.x-pos_.x) + std::abs(C.pos_.y-pos_.y)) * static_cast(C.allocated_capacity_); } return res; } float_t region_distribution::cost() const{ float_t res = 0.0; capacity_t tot_cap = 0; for(region const & R : placement_regions_){ res += R.cost(); tot_cap += R.allocated_capacity(); } // Average over the cells' areas return res / static_cast(tot_cap); } } // Namespace gp } // Namespace coloquinte