1/*
2 * Based on: Jonker, R., & Volgenant, A. (1987). <i>A shortest augmenting path
3 * algorithm for dense and sparse linear assignment problems</i>. Computing,
4 * 38(4), 325-340.
5 */
6#include "cache.h"
7#include "linear-assignment.h"
8
9#define COST(column, row) cost[(column) + column_count * (row)]
10
11/*
12 * The parameter `cost` is the cost matrix: the cost to assign column j to row
13 * i is `cost[j + column_count * i].
14 */
15void compute_assignment(int column_count, int row_count, int *cost,
16 int *column2row, int *row2column)
17{
18 int *v, *d;
19 int *free_row, free_count = 0, saved_free_count, *pred, *col;
20 int i, j, phase;
21
22 memset(column2row, -1, sizeof(int) * column_count);
23 memset(row2column, -1, sizeof(int) * row_count);
24 ALLOC_ARRAY(v, column_count);
25
26 /* column reduction */
27 for (j = column_count - 1; j >= 0; j--) {
28 int i1 = 0;
29
30 for (i = 1; i < row_count; i++)
31 if (COST(j, i1) > COST(j, i))
32 i1 = i;
33 v[j] = COST(j, i1);
34 if (row2column[i1] == -1) {
35 /* row i1 unassigned */
36 row2column[i1] = j;
37 column2row[j] = i1;
38 } else {
39 if (row2column[i1] >= 0)
40 row2column[i1] = -2 - row2column[i1];
41 column2row[j] = -1;
42 }
43 }
44
45 /* reduction transfer */
46 ALLOC_ARRAY(free_row, row_count);
47 for (i = 0; i < row_count; i++) {
48 int j1 = row2column[i];
49 if (j1 == -1)
50 free_row[free_count++] = i;
51 else if (j1 < -1)
52 row2column[i] = -2 - j1;
53 else {
54 int min = COST(!j1, i) - v[!j1];
55 for (j = 1; j < column_count; j++)
56 if (j != j1 && min > COST(j, i) - v[j])
57 min = COST(j, i) - v[j];
58 v[j1] -= min;
59 }
60 }
61
62 if (free_count ==
63 (column_count < row_count ? row_count - column_count : 0)) {
64 free(v);
65 free(free_row);
66 return;
67 }
68
69 /* augmenting row reduction */
70 for (phase = 0; phase < 2; phase++) {
71 int k = 0;
72
73 saved_free_count = free_count;
74 free_count = 0;
75 while (k < saved_free_count) {
76 int u1, u2;
77 int j1 = 0, j2, i0;
78
79 i = free_row[k++];
80 u1 = COST(j1, i) - v[j1];
81 j2 = -1;
82 u2 = INT_MAX;
83 for (j = 1; j < column_count; j++) {
84 int c = COST(j, i) - v[j];
85 if (u2 > c) {
86 if (u1 < c) {
87 u2 = c;
88 j2 = j;
89 } else {
90 u2 = u1;
91 u1 = c;
92 j2 = j1;
93 j1 = j;
94 }
95 }
96 }
97 if (j2 < 0) {
98 j2 = j1;
99 u2 = u1;
100 }
101
102 i0 = column2row[j1];
103 if (u1 < u2)
104 v[j1] -= u2 - u1;
105 else if (i0 >= 0) {
106 j1 = j2;
107 i0 = column2row[j1];
108 }
109
110 if (i0 >= 0) {
111 if (u1 < u2)
112 free_row[--k] = i0;
113 else
114 free_row[free_count++] = i0;
115 }
116 row2column[i] = j1;
117 column2row[j1] = i;
118 }
119 }
120
121 /* augmentation */
122 saved_free_count = free_count;
123 ALLOC_ARRAY(d, column_count);
124 ALLOC_ARRAY(pred, column_count);
125 ALLOC_ARRAY(col, column_count);
126 for (free_count = 0; free_count < saved_free_count; free_count++) {
127 int i1 = free_row[free_count], low = 0, up = 0, last, k;
128 int min, c, u1;
129
130 for (j = 0; j < column_count; j++) {
131 d[j] = COST(j, i1) - v[j];
132 pred[j] = i1;
133 col[j] = j;
134 }
135
136 j = -1;
137 do {
138 last = low;
139 min = d[col[up++]];
140 for (k = up; k < column_count; k++) {
141 j = col[k];
142 c = d[j];
143 if (c <= min) {
144 if (c < min) {
145 up = low;
146 min = c;
147 }
148 col[k] = col[up];
149 col[up++] = j;
150 }
151 }
152 for (k = low; k < up; k++)
153 if (column2row[col[k]] == -1)
154 goto update;
155
156 /* scan a row */
157 do {
158 int j1 = col[low++];
159
160 i = column2row[j1];
161 u1 = COST(j1, i) - v[j1] - min;
162 for (k = up; k < column_count; k++) {
163 j = col[k];
164 c = COST(j, i) - v[j] - u1;
165 if (c < d[j]) {
166 d[j] = c;
167 pred[j] = i;
168 if (c == min) {
169 if (column2row[j] == -1)
170 goto update;
171 col[k] = col[up];
172 col[up++] = j;
173 }
174 }
175 }
176 } while (low != up);
177 } while (low == up);
178
179update:
180 /* updating of the column pieces */
181 for (k = 0; k < last; k++) {
182 int j1 = col[k];
183 v[j1] += d[j1] - min;
184 }
185
186 /* augmentation */
187 do {
188 if (j < 0)
189 BUG("negative j: %d", j);
190 i = pred[j];
191 column2row[j] = i;
192 SWAP(j, row2column[i]);
193 } while (i1 != i);
194 }
195
196 free(col);
197 free(pred);
198 free(d);
199 free(v);
200 free(free_row);
201}