| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include <cmath> |
| #include <vector> |
| #include <numeric> |
| #include <algorithm> |
|
|
| #define RECTANGULAR_LSAP_INFEASIBLE -1 |
| #define RECTANGULAR_LSAP_INVALID -2 |
|
|
| template <typename T> std::vector<intptr_t> argsort_iter(const std::vector<T> &v) |
| { |
| std::vector<intptr_t> index(v.size()); |
| std::iota(index.begin(), index.end(), 0); |
| std::sort(index.begin(), index.end(), [&v](intptr_t i, intptr_t j) |
| {return v[i] < v[j];}); |
| return index; |
| } |
|
|
| static intptr_t |
| augmenting_path(intptr_t nc, double *cost, std::vector<double>& u, |
| std::vector<double>& v, std::vector<intptr_t>& path, |
| std::vector<intptr_t>& row4col, |
| std::vector<double>& shortestPathCosts, intptr_t i, |
| std::vector<bool>& SR, std::vector<bool>& SC, |
| std::vector<intptr_t>& remaining, double* p_minVal) |
| { |
| double minVal = 0; |
|
|
| |
| |
| intptr_t num_remaining = nc; |
| for (intptr_t it = 0; it < nc; it++) { |
| |
| |
| remaining[it] = nc - it - 1; |
| } |
|
|
| std::fill(SR.begin(), SR.end(), false); |
| std::fill(SC.begin(), SC.end(), false); |
| std::fill(shortestPathCosts.begin(), shortestPathCosts.end(), INFINITY); |
|
|
| |
| intptr_t sink = -1; |
| while (sink == -1) { |
|
|
| intptr_t index = -1; |
| double lowest = INFINITY; |
| SR[i] = true; |
|
|
| for (intptr_t it = 0; it < num_remaining; it++) { |
| intptr_t j = remaining[it]; |
|
|
| double r = minVal + cost[i * nc + j] - u[i] - v[j]; |
| if (r < shortestPathCosts[j]) { |
| path[j] = i; |
| shortestPathCosts[j] = r; |
| } |
|
|
| |
| |
| |
| if (shortestPathCosts[j] < lowest || |
| (shortestPathCosts[j] == lowest && row4col[j] == -1)) { |
| lowest = shortestPathCosts[j]; |
| index = it; |
| } |
| } |
|
|
| minVal = lowest; |
| if (minVal == INFINITY) { |
| return -1; |
| } |
|
|
| intptr_t j = remaining[index]; |
| if (row4col[j] == -1) { |
| sink = j; |
| } else { |
| i = row4col[j]; |
| } |
|
|
| SC[j] = true; |
| remaining[index] = remaining[--num_remaining]; |
| } |
|
|
| *p_minVal = minVal; |
| return sink; |
| } |
|
|
| static int solve(intptr_t nr, intptr_t nc, double* cost, bool maximize, |
| int64_t* a, int64_t* b) { |
| |
| if (nr == 0 || nc == 0) { |
| return 0; |
| } |
|
|
| |
| bool transpose = nc < nr; |
|
|
| |
| std::vector<double> temp; |
| if (transpose || maximize) { |
| temp.resize(nr * nc); |
|
|
| if (transpose) { |
| for (intptr_t i = 0; i < nr; i++) { |
| for (intptr_t j = 0; j < nc; j++) { |
| temp[j * nr + i] = cost[i * nc + j]; |
| } |
| } |
|
|
| std::swap(nr, nc); |
| } |
| else { |
| std::copy(cost, cost + nr * nc, temp.begin()); |
| } |
|
|
| |
| if (maximize) { |
| for (intptr_t i = 0; i < nr * nc; i++) { |
| temp[i] = -temp[i]; |
| } |
| } |
|
|
| cost = temp.data(); |
| } |
|
|
| |
| for (intptr_t i = 0; i < nr * nc; i++) { |
| if (cost[i] != cost[i] || cost[i] == -INFINITY) { |
| return RECTANGULAR_LSAP_INVALID; |
| } |
| } |
|
|
| |
| std::vector<double> u(nr, 0); |
| std::vector<double> v(nc, 0); |
| std::vector<double> shortestPathCosts(nc); |
| std::vector<intptr_t> path(nc, -1); |
| std::vector<intptr_t> col4row(nr, -1); |
| std::vector<intptr_t> row4col(nc, -1); |
| std::vector<bool> SR(nr); |
| std::vector<bool> SC(nc); |
| std::vector<intptr_t> remaining(nc); |
|
|
| |
| for (intptr_t curRow = 0; curRow < nr; curRow++) { |
|
|
| double minVal; |
| intptr_t sink = augmenting_path(nc, cost, u, v, path, row4col, |
| shortestPathCosts, curRow, SR, SC, |
| remaining, &minVal); |
| if (sink < 0) { |
| return RECTANGULAR_LSAP_INFEASIBLE; |
| } |
|
|
| |
| u[curRow] += minVal; |
| for (intptr_t i = 0; i < nr; i++) { |
| if (SR[i] && i != curRow) { |
| u[i] += minVal - shortestPathCosts[col4row[i]]; |
| } |
| } |
|
|
| for (intptr_t j = 0; j < nc; j++) { |
| if (SC[j]) { |
| v[j] -= minVal - shortestPathCosts[j]; |
| } |
| } |
|
|
| |
| intptr_t j = sink; |
| while (1) { |
| intptr_t i = path[j]; |
| row4col[j] = i; |
| std::swap(col4row[i], j); |
| if (i == curRow) { |
| break; |
| } |
| } |
| } |
|
|
| if (transpose) { |
| intptr_t i = 0; |
| for (auto v: argsort_iter(col4row)) { |
| a[i] = col4row[v]; |
| b[i] = v; |
| i++; |
| } |
| } |
| else { |
| for (intptr_t i = 0; i < nr; i++) { |
| a[i] = i; |
| b[i] = col4row[i]; |
| } |
| } |
|
|
| return 0; |
| } |
|
|
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
|
|
| int |
| solve_rectangular_linear_sum_assignment(intptr_t nr, intptr_t nc, |
| double* input_cost, bool maximize, |
| int64_t* a, int64_t* b) |
| { |
| return solve(nr, nc, input_cost, maximize, a, b); |
| } |
|
|
| #ifdef __cplusplus |
| } |
| #endif |
|
|