Quetzal-CoaTL
The Coalescence Template Library
Loading...
Searching...
No Matches
munkres.hpp
1/*
2 * Copyright (c) 2007 John Weaver
3 * Copyright (c) 2015 Miroslav Krajicek
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20/*
21Modified by Arnaud Becheler, 2016, removing cpp include.
22*/
23
24#ifndef _MUNKRES_H_
25#define _MUNKRES_H_
26
27#include "matrix.hpp"
28
29#include <cmath>
30#include <iostream>
31#include <limits>
32#include <list>
33#include <utility>
34
36{
37namespace fuzzy_transfer_distance
38{
39template <typename Data> class Munkres
40{
41 static constexpr int NORMAL = 0;
42 static constexpr int STAR = 1;
43 static constexpr int PRIME = 2;
44
45 public:
46 /*
47 *
48 * Linear assignment problem solution
49 * [modifies matrix in-place.]
50 * matrix(row,col): row major format assumed.
51 *
52 * Assignments are remaining 0 values
53 * (extra 0 values are replaced with -1)
54 *
55 */
56 void solve(Matrix<Data> &m)
57 {
58 const size_t rows = m.rows(), columns = m.columns(), size = std::max(rows, columns);
59
60#ifdef DEBUG
61 std::cout << "Munkres input: " << m << std::endl;
62#endif
63
64 // Copy input matrix
65 this->matrix = m;
66
67 if (rows != columns)
68 {
69 // If the input matrix isn't square, make it square
70 // and fill the empty values with the largest value present
71 // in the matrix.
72 matrix.resize(size, size, matrix.max());
73 }
74
75 // STAR == 1 == starred, PRIME == 2 == primed
76 mask_matrix.resize(size, size);
77
78 row_mask = new bool[size];
79 col_mask = new bool[size];
80 for (size_t i = 0; i < size; i++)
81 {
82 row_mask[i] = false;
83 }
84
85 for (size_t i = 0; i < size; i++)
86 {
87 col_mask[i] = false;
88 }
89
90 // Prepare the matrix values...
91
92 // If there were any infinities, replace them with a value greater
93 // than the maximum value in the matrix.
94 replace_infinites(matrix);
95
96 minimize_along_direction(matrix, rows >= columns);
97 minimize_along_direction(matrix, rows < columns);
98
99 // Follow the steps
100 int step = 1;
101 while (step)
102 {
103 switch (step)
104 {
105 case 1:
106 step = step1();
107 // step is always 2
108 break;
109 case 2:
110 step = step2();
111 // step is always either 0 or 3
112 break;
113 case 3:
114 step = step3();
115 // step in [3, 4, 5]
116 break;
117 case 4:
118 step = step4();
119 // step is always 2
120 break;
121 case 5:
122 step = step5();
123 // step is always 3
124 break;
125 }
126 }
127
128 // Store results
129 for (size_t row = 0; row < size; row++)
130 {
131 for (size_t col = 0; col < size; col++)
132 {
133 if (mask_matrix(row, col) == STAR)
134 {
135 matrix(row, col) = 0;
136 }
137 else
138 {
139 matrix(row, col) = -1;
140 }
141 }
142 }
143
144#ifdef DEBUG
145 std::cout << "Munkres output: " << matrix << std::endl;
146#endif
147 // Remove the excess rows or columns that we added to fit the
148 // input to a square matrix.
149 matrix.resize(rows, columns);
150
151 m = matrix;
152
153 delete[] row_mask;
154 delete[] col_mask;
155 }
156
157 static void replace_infinites(Matrix<Data> &matrix)
158 {
159 const size_t rows = matrix.rows(), columns = matrix.columns();
160 assert(rows > 0 && columns > 0);
161 double max = matrix(0, 0);
162 constexpr auto infinity = std::numeric_limits<double>::infinity();
163
164 // Find the greatest value in the matrix that isn't infinity.
165 for (size_t row = 0; row < rows; row++)
166 {
167 for (size_t col = 0; col < columns; col++)
168 {
169 if (matrix(row, col) != infinity)
170 {
171 if (max == infinity)
172 {
173 max = matrix(row, col);
174 }
175 else
176 {
177 max = std::max<double>(max, matrix(row, col));
178 }
179 }
180 }
181 }
182
183 // a value higher than the maximum value present in the matrix.
184 if (max == infinity)
185 {
186 // This case only occurs when all values are infinite.
187 max = 0;
188 }
189 else
190 {
191 max++;
192 }
193
194 for (size_t row = 0; row < rows; row++)
195 {
196 for (size_t col = 0; col < columns; col++)
197 {
198 if (matrix(row, col) == infinity)
199 {
200 matrix(row, col) = max;
201 }
202 }
203 }
204 }
205
206 static void minimize_along_direction(Matrix<Data> &matrix, const bool over_columns)
207 {
208 const size_t outer_size = over_columns ? matrix.columns() : matrix.rows(),
209 inner_size = over_columns ? matrix.rows() : matrix.columns();
210
211 // Look for a minimum value to subtract from all values along
212 // the "outer" direction.
213 for (size_t i = 0; i < outer_size; i++)
214 {
215 double min = over_columns ? matrix(0, i) : matrix(i, 0);
216
217 // As long as the current minimum is greater than zero,
218 // keep looking for the minimum.
219 // Start at one because we already have the 0th value in min.
220 for (size_t j = 1; j < inner_size && min > 0; j++)
221 {
222 min = std::min<double>(min, over_columns ? matrix(j, i) : matrix(i, j));
223 }
224
225 if (min > 0)
226 {
227 for (size_t j = 0; j < inner_size; j++)
228 {
229 if (over_columns)
230 {
231 matrix(j, i) -= min;
232 }
233 else
234 {
235 matrix(i, j) -= min;
236 }
237 }
238 }
239 }
240 }
241
242 private:
243 inline bool find_uncovered_in_matrix(const double item, size_t &row, size_t &col) const
244 {
245 const size_t rows = matrix.rows(), columns = matrix.columns();
246
247 for (row = 0; row < rows; row++)
248 {
249 if (!row_mask[row])
250 {
251 for (col = 0; col < columns; col++)
252 {
253 if (!col_mask[col])
254 {
255 if (matrix(row, col) == item)
256 {
257 return true;
258 }
259 }
260 }
261 }
262 }
263
264 return false;
265 }
266
267 bool pair_in_list(const std::pair<size_t, size_t> &needle, const std::list<std::pair<size_t, size_t>> &haystack)
268 {
269 for (std::list<std::pair<size_t, size_t>>::const_iterator i = haystack.begin(); i != haystack.end(); i++)
270 {
271 if (needle == *i)
272 {
273 return true;
274 }
275 }
276
277 return false;
278 }
279
280 int step1()
281 {
282 const size_t rows = matrix.rows(), columns = matrix.columns();
283
284 for (size_t row = 0; row < rows; row++)
285 {
286 for (size_t col = 0; col < columns; col++)
287 {
288 if (0 == matrix(row, col))
289 {
290 for (size_t nrow = 0; nrow < row; nrow++)
291 if (STAR == mask_matrix(nrow, col))
292 goto next_column;
293
294 mask_matrix(row, col) = STAR;
295 goto next_row;
296 }
297 next_column:;
298 }
299 next_row:;
300 }
301
302 return 2;
303 }
304
305 int step2()
306 {
307 const size_t rows = matrix.rows(), columns = matrix.columns();
308 size_t covercount = 0;
309
310 for (size_t row = 0; row < rows; row++)
311 for (size_t col = 0; col < columns; col++)
312 if (STAR == mask_matrix(row, col))
313 {
314 col_mask[col] = true;
315 covercount++;
316 }
317
318 if (covercount >= matrix.minsize())
319 {
320#ifdef DEBUG
321 std::cout << "Final cover count: " << covercount << std::endl;
322#endif
323 return 0;
324 }
325
326#ifdef DEBUG
327 std::cout << "Munkres matrix has " << covercount << " of " << matrix.minsize()
328 << " Columns covered:" << std::endl;
329 std::cout << matrix << std::endl;
330#endif
331
332 return 3;
333 }
334
335 int step3()
336 {
337 /*
338 Main Zero Search
339
340 1. Find an uncovered Z in the distance matrix and prime it. If no such zero exists, go to Step 5
341 2. If No Z* exists in the row of the Z', go to Step 4.
342 3. If a Z* exists, cover this row and uncover the column of the Z*. Return to Step 3.1 to find a new Z
343 */
344 if (find_uncovered_in_matrix(0, saverow, savecol))
345 {
346 mask_matrix(saverow, savecol) = PRIME; // prime it.
347 }
348 else
349 {
350 return 5;
351 }
352
353 for (size_t ncol = 0; ncol < matrix.columns(); ncol++)
354 {
355 if (mask_matrix(saverow, ncol) == STAR)
356 {
357 row_mask[saverow] = true; // cover this row and
358 col_mask[ncol] = false; // uncover the column containing the starred zero
359 return 3; // repeat
360 }
361 }
362
363 return 4; // no starred zero in the row containing this primed zero
364 }
365
366 int step4()
367 {
368 const size_t rows = matrix.rows(), columns = matrix.columns();
369
370 // seq contains pairs of row/column values where we have found
371 // either a star or a prime that is part of the ``alternating sequence``.
372 std::list<std::pair<size_t, size_t>> seq;
373 // use saverow, savecol from step 3.
374 std::pair<size_t, size_t> z0(saverow, savecol);
375 seq.insert(seq.end(), z0);
376
377 // We have to find these two pairs:
378 std::pair<size_t, size_t> z1(-1, -1);
379 std::pair<size_t, size_t> z2n(-1, -1);
380
381 size_t row, col = savecol;
382 /*
383 Increment Set of Starred Zeros
384
385 1. Construct the ``alternating sequence'' of primed and starred zeros:
386
387 Z0 : Unpaired Z' from Step 4.2
388 Z1 : The Z* in the column of Z0
389 Z[2N] : The Z' in the row of Z[2N-1], if such a zero exists
390 Z[2N+1] : The Z* in the column of Z[2N]
391
392 The sequence eventually terminates with an unpaired Z' = Z[2N] for some N.
393 */
394 bool madepair;
395 do
396 {
397 madepair = false;
398 for (row = 0; row < rows; row++)
399 {
400 if (mask_matrix(row, col) == STAR)
401 {
402 z1.first = row;
403 z1.second = col;
404 if (pair_in_list(z1, seq))
405 {
406 continue;
407 }
408
409 madepair = true;
410 seq.insert(seq.end(), z1);
411 break;
412 }
413 }
414
415 if (!madepair)
416 break;
417
418 madepair = false;
419
420 for (col = 0; col < columns; col++)
421 {
422 if (mask_matrix(row, col) == PRIME)
423 {
424 z2n.first = row;
425 z2n.second = col;
426 if (pair_in_list(z2n, seq))
427 {
428 continue;
429 }
430 madepair = true;
431 seq.insert(seq.end(), z2n);
432 break;
433 }
434 }
435 } while (madepair);
436
437 for (std::list<std::pair<size_t, size_t>>::iterator i = seq.begin(); i != seq.end(); i++)
438 {
439 // 2. Unstar each starred zero of the sequence.
440 if (mask_matrix(i->first, i->second) == STAR)
441 mask_matrix(i->first, i->second) = NORMAL;
442
443 // 3. Star each primed zero of the sequence,
444 // thus increasing the number of starred zeros by one.
445 if (mask_matrix(i->first, i->second) == PRIME)
446 mask_matrix(i->first, i->second) = STAR;
447 }
448
449 // 4. Erase all primes, uncover all columns and rows,
450 for (size_t row = 0; row < mask_matrix.rows(); row++)
451 {
452 for (size_t col = 0; col < mask_matrix.columns(); col++)
453 {
454 if (mask_matrix(row, col) == PRIME)
455 {
456 mask_matrix(row, col) = NORMAL;
457 }
458 }
459 }
460
461 for (size_t i = 0; i < rows; i++)
462 {
463 row_mask[i] = false;
464 }
465
466 for (size_t i = 0; i < columns; i++)
467 {
468 col_mask[i] = false;
469 }
470
471 // and return to Step 2.
472 return 2;
473 }
474
475 int step5()
476 {
477 const size_t rows = matrix.rows(), columns = matrix.columns();
478 /*
479 New Zero Manufactures
480
481 1. Let h be the smallest uncovered entry in the (modified) distance matrix.
482 2. Add h to all covered rows.
483 3. Subtract h from all uncovered columns
484 4. Return to Step 3, without altering stars, primes, or covers.
485 */
486 double h = std::numeric_limits<double>::max();
487 for (size_t row = 0; row < rows; row++)
488 {
489 if (!row_mask[row])
490 {
491 for (size_t col = 0; col < columns; col++)
492 {
493 if (!col_mask[col])
494 {
495 if (h > matrix(row, col) && matrix(row, col) != 0)
496 {
497 h = matrix(row, col);
498 }
499 }
500 }
501 }
502 }
503
504 for (size_t row = 0; row < rows; row++)
505 {
506 if (row_mask[row])
507 {
508 for (size_t col = 0; col < columns; col++)
509 {
510 matrix(row, col) += h;
511 }
512 }
513 }
514
515 for (size_t col = 0; col < columns; col++)
516 {
517 if (!col_mask[col])
518 {
519 for (size_t row = 0; row < rows; row++)
520 {
521 matrix(row, col) -= h;
522 }
523 }
524 }
525
526 return 3;
527 }
528
529 Matrix<int> mask_matrix;
530 Matrix<Data> matrix;
531 bool *row_mask;
532 bool *col_mask;
533 size_t saverow = 0, savecol = 0;
534};
535} // end namespace fuzzy_transfer_distance
536} // namespace quetzal::polymorphism
537
538#endif
Generic components for polymorphism analysis.
Definition polymorphism.hpp:16