• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

paulmthompson / WhiskerToolbox / 18117112849

30 Sep 2025 02:44AM UTC coverage: 70.161% (+0.03%) from 70.132%
18117112849

push

github

paulmthompson
hungarian algorithm is actually used

60 of 77 new or added lines in 2 files covered. (77.92%)

352 existing lines in 12 files now uncovered.

45125 of 64316 relevant lines covered (70.16%)

1116.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

31.03
/src/StateEstimation/Assignment/hungarian.cpp
1
/* This is an implementation of the Hungarian algorithm in C++
2
 * The Hungarian algorithm, also know as Munkres or Kuhn-Munkres
3
 * algorithm is usefull for solving the assignment problem.
4
 *
5
 * Assignment problem: Let C be an n x n matrix 
6
 * representing the costs of each of n workers to perform any of n jobs.
7
 * The assignment problem is to assign jobs to workers so as to 
8
 * minimize the total cost. Since each worker can perform only one job and 
9
 * each job can be assigned to only one worker the assignments constitute 
10
 * an independent set of the matrix C.
11
 * 
12
 * It is a port heavily based on http://csclab.murraystate.edu/~bob.pilgrim/445/munkres.html
13
 * 
14
 * This version is written by Fernando B. Giannasi */
15

16
 #include <algorithm>
17
 #include <cmath>
18
 #include <iostream>
19
 #include <iterator>
20
 #include <limits>
21
 #include <list>
22
 #include <stdexcept>
23
 #include <string>
24
 #include <type_traits>
25
 #include <vector>
26
 
27
 
28
 namespace Munkres {
29
     
30
 /* Utility function to print Matrix */
31
 template<template <typename, typename...> class Container,
32
                    typename T,
33
                    typename... Args>
34
 //disable for string, which is std::basic_string<char>, a container itself
35
 typename std::enable_if<!std::is_convertible<Container<T, Args...>, std::string>::value &&
36
                         !std::is_constructible<Container<T, Args...>, std::string>::value,
37
                             std::ostream&>::type
38
 operator<<(std::ostream& os, const Container<T, Args...>& con)
39
 {
40
     os << " ";
41
     for (auto& elem: con)
42
         os << elem << " ";
43
 
44
     os << "\n";
45
     return os;
46
 }
47
 
48
 /* Handle negative elements if present. If allowed = true, add abs(minval) to 
49
  * every element to create one zero. Else throw an exception */
50
 template<typename T>
51
 void handle_negatives(std::vector<std::vector<T>>& matrix, 
×
52
                       bool allowed = true)
53
 {
54
     T minval = std::numeric_limits<T>::max();
×
55
     
56
     for (auto& elem: matrix)
×
57
         for (auto& num: elem)
×
58
             minval = std::min(minval, num);
×
59
         
60
     if (minval < 0) {
×
61
         if (!allowed) { //throw
×
62
             throw std::runtime_error("Only non-negative values allowed");
×
63
         }
64
         else { // add abs(minval) to every element to create one zero
65
             minval = abs(minval);
×
66
             
67
             for (auto& elem: matrix)
×
68
                 for (auto& num: elem)
×
69
                     num += minval;
×
70
         }
71
     }
72
 }
×
73
 
74
 /* Ensure that the matrix is square by the addition of dummy rows/columns if necessary */
75
 template<typename T>
76
 void pad_matrix(std::vector<std::vector<T>>& matrix)
360✔
77
 {
78
     std::size_t i_size = matrix.size();
360✔
79
     std::size_t j_size = matrix[0].size();
360✔
80
     
81
     if (i_size > j_size) {
360✔
82
         for (auto& vec: matrix)
×
83
             vec.resize(i_size, std::numeric_limits<T>::max());
×
84
     }
85
     else if (i_size < j_size) {
360✔
86
         while (matrix.size() < j_size)
×
87
             matrix.push_back(std::vector<T>(j_size, std::numeric_limits<T>::max()));
×
88
     }
89
 }
360✔
90
 
91
 /* For each row of the matrix, find the smallest element and subtract it from every 
92
  * element in its row.  
93
  * For each col of the matrix, find the smallest element and subtract it from every 
94
  * element in its col. Go to Step 2. */
95
 template<typename T>
96
 void step1(std::vector<std::vector<T>>& matrix, 
360✔
97
            int& step)
98
 {
99
     // process rows
100
     for (auto& row: matrix) {
1,080✔
101
         auto smallest = *std::min_element(begin(row), end(row));
1,440✔
102
         if (smallest > 0)        
720✔
103
             for (auto& n: row)
2,160✔
104
                 n -= smallest;
1,440✔
105
     }
106
     
107
     // process cols
108
     int sz = matrix.size(); // square matrix is granted
360✔
109
     for (int j=0; j<sz; ++j) {
1,080✔
110
         T minval = std::numeric_limits<T>::max();
720✔
111
         for (int i=0; i<sz; ++i) {
2,160✔
112
             minval = std::min(minval, matrix[i][j]);
1,440✔
113
         }
114
         
115
         if (minval > 0) {
720✔
116
             for (int i=0; i<sz; ++i) {
×
117
                 matrix[i][j] -= minval;
×
118
             }
119
         }
120
     }
121
    
122
     step = 2;
360✔
123
 }
360✔
124
 
125
 /* helper to clear the temporary vectors */
126
 inline void clear_covers(std::vector<int>& cover) 
720✔
127
 {
128
     for (auto& n: cover) n = 0;
2,160✔
129
 }
720✔
130
 
131
 /* Find a zero (Z) in the resulting matrix.  If there is no starred zero in its row or 
132
  * column, star Z. Repeat for each element in the matrix. Go to Step 3.  In this step, 
133
  * we introduce the mask matrix M, which in the same dimensions as the cost matrix and 
134
  * is used to star and prime zeros of the cost matrix.  If M(i,j)=1 then C(i,j) is a 
135
  * starred zero,  If M(i,j)=2 then C(i,j) is a primed zero.  We also define two vectors 
136
  * RowCover and ColCover that are used to "cover" the rows and columns of the cost matrix.
137
  * In the nested loop (over indices i and j) we check to see if C(i,j) is a zero value 
138
  * and if its column or row is not already covered.  If not then we star this zero 
139
  * (i.e. set M(i,j)=1) and cover its row and column (i.e. set R_cov(i)=1 and C_cov(j)=1).
140
  * Before we go on to Step 3, we uncover all rows and columns so that we can use the 
141
  * cover vectors to help us count the number of starred zeros. */
142
 template<typename T>
143
 void step2(const std::vector<std::vector<T>>& matrix, 
360✔
144
            std::vector<std::vector<int>>& M, 
145
            std::vector<int>& RowCover,
146
            std::vector<int>& ColCover, 
147
            int& step)
148
 {
149
     int sz = matrix.size();
360✔
150
     
151
     for (int r=0; r<sz; ++r) 
1,080✔
152
         for (int c=0; c<sz; ++c) 
2,160✔
153
             if (matrix[r][c] == 0)
1,440✔
154
                 if (RowCover[r] == 0 && ColCover[c] == 0) {
720✔
155
                     M[r][c] = 1;
720✔
156
                     RowCover[r] = 1;
720✔
157
                     ColCover[c] = 1;
720✔
158
                 }
159
             
160
     clear_covers(RowCover); // reset vectors for posterior using
360✔
161
     clear_covers(ColCover);
360✔
162
     
163
     step = 3;
360✔
164
 }
360✔
165
 
166
 
167
 /* Cover each column containing a starred zero.  If K columns are covered, the starred 
168
  * zeros describe a complete set of unique assignments.  In this case, Go to DONE, 
169
  * otherwise, Go to Step 4. Once we have searched the entire cost matrix, we count the 
170
  * number of independent zeros found.  If we have found (and starred) K independent zeros 
171
  * then we are done.  If not we procede to Step 4.*/
172
 void step3(const std::vector<std::vector<int>>& M, 
360✔
173
            std::vector<int>& ColCover,
174
            int& step)
175
 {
176
     int sz = M.size();
360✔
177
     int colcount = 0;
360✔
178
     
179
     for (int r=0; r<sz; ++r)
1,080✔
180
         for (int c=0; c<sz; ++c)
2,160✔
181
             if (M[r][c] == 1)
1,440✔
182
                 ColCover[c] = 1;
720✔
183
             
184
     for (auto& n: ColCover)
1,080✔
185
         if (n == 1)
720✔
186
             colcount++;
720✔
187
     
188
     if (colcount >= sz) {
360✔
189
         step = 7; // solution found
360✔
190
     }
191
     else {
192
         step = 4;
×
193
     }
194
 }
360✔
195
 
196
 // Following functions to support step 4
197
 template<typename T>
198
 void find_a_zero(int& row, 
×
199
                  int& col,
200
                  const std::vector<std::vector<T>>& matrix,
201
                  const std::vector<int>& RowCover,
202
                  const std::vector<int>& ColCover)
203
 {
204
     int r = 0;
×
205
     int c = 0;
×
206
     int sz = matrix.size();
×
207
     bool done = false;
×
208
     row = -1;
×
209
     col = -1;
×
210
     
211
     while (!done) {
×
212
         c = 0;
×
213
         while (true) {
214
             if (matrix[r][c] == 0 && RowCover[r] == 0 && ColCover[c] == 0) {
×
215
                 row = r;
×
216
                 col = c;
×
217
                 done = true;
×
218
             }
219
             c += 1;
×
220
             if (c >= sz || done)
×
221
                 break;
222
         }
223
         r += 1;
×
224
         if (r >= sz)
×
225
             done = true;
×
226
     }
227
 }
×
228
 
229
 bool star_in_row(int row, 
×
230
                  const std::vector<std::vector<int>>& M)
231
 {
232
     bool tmp = false;
×
233
     for (unsigned c = 0; c < M.size(); c++)
×
234
         if (M[row][c] == 1)
×
235
             tmp = true;
×
236
     
237
     return tmp;
×
238
 }
239
 
240
 
241
 void find_star_in_row(int row,
×
242
                       int& col, 
243
                       const std::vector<std::vector<int>>& M)
244
 {
245
     col = -1;
×
246
     for (unsigned c = 0; c < M.size(); c++)
×
247
         if (M[row][c] == 1)
×
248
             col = c;
×
249
 }
×
250
 
251
 
252
 /* Find a noncovered zero and prime it.  If there is no starred zero in the row containing
253
  * this primed zero, Go to Step 5.  Otherwise, cover this row and uncover the column 
254
  * containing the starred zero. Continue in this manner until there are no uncovered zeros
255
  * left. Save the smallest uncovered value and Go to Step 6. */
256
 template<typename T>
257
 void step4(const std::vector<std::vector<T>>& matrix, 
×
258
            std::vector<std::vector<int>>& M, 
259
            std::vector<int>& RowCover,
260
            std::vector<int>& ColCover,
261
            int& path_row_0,
262
            int& path_col_0,
263
            int& step)
264
 {
265
     int row = -1;
×
266
     int col = -1;
×
267
     bool done = false;
×
268
 
269
     while (!done){
×
270
         find_a_zero(row, col, matrix, RowCover, ColCover);
×
271
         
272
         if (row == -1){
×
273
             done = true;
×
274
             step = 6;
×
275
         }
276
         else {
277
             M[row][col] = 2;
×
278
             if (star_in_row(row, M)) {
×
279
                 find_star_in_row(row, col, M);
×
280
                 RowCover[row] = 1;
×
281
                 ColCover[col] = 0;
×
282
             }
283
             else {
284
                 done = true;
×
285
                 step = 5;
×
286
                 path_row_0 = row;
×
287
                 path_col_0 = col;
×
288
             }
289
         }
290
     }
291
 }
×
292
 
293
 // Following functions to support step 5
294
 void find_star_in_col(int c, 
×
295
                       int& r,
296
                       const std::vector<std::vector<int>>& M)
297
 {
298
     r = -1;
×
299
     for (unsigned i = 0; i < M.size(); i++)
×
300
         if (M[i][c] == 1)
×
301
             r = i;
×
302
 }
×
303
 
304
 void find_prime_in_row(int r, 
×
305
                        int& c, 
306
                        const std::vector<std::vector<int>>& M)
307
 {
308
     for (unsigned j = 0; j < M.size(); j++)
×
309
         if (M[r][j] == 2)
×
310
             c = j;
×
311
 }
×
312
 
313
 void augment_path(std::vector<std::vector<int>>& path, 
×
314
                   int path_count, 
315
                   std::vector<std::vector<int>>& M)
316
 {
317
     for (int p = 0; p < path_count; p++)
×
318
         if (M[path[p][0]][path[p][1]] == 1)
×
319
             M[path[p][0]][path[p][1]] = 0;
×
320
         else
321
             M[path[p][0]][path[p][1]] = 1;
×
322
 }
×
323
 
324
 void erase_primes(std::vector<std::vector<int>>& M)
×
325
 {
326
     for (auto& row: M)
×
327
         for (auto& val: row)
×
328
             if (val == 2)
×
329
                 val = 0;
×
330
 }
×
331
 
332
 
333
 /* Construct a series of alternating primed and starred zeros as follows.  
334
  * Let Z0 represent the uncovered primed zero found in Step 4.  Let Z1 denote the 
335
  * starred zero in the column of Z0 (if any). Let Z2 denote the primed zero in the 
336
  * row of Z1 (there will always be one).  Continue until the series terminates at a 
337
  * primed zero that has no starred zero in its column.  Unstar each starred zero of 
338
  * the series, star each primed zero of the series, erase all primes and uncover every 
339
  * line in the matrix.  Return to Step 3.  You may notice that Step 5 seems vaguely 
340
  * familiar.  It is a verbal description of the augmenting path algorithm (for solving
341
  * the maximal matching problem). */
342
 void step5(std::vector<std::vector<int>>& path, 
×
343
            int path_row_0, 
344
            int path_col_0, 
345
            std::vector<std::vector<int>>& M, 
346
            std::vector<int>& RowCover,
347
            std::vector<int>& ColCover,
348
            int& step)
349
 {
350
     int r = -1;
×
351
     int c = -1;
×
352
     int path_count = 1;
×
353
     
354
     path[path_count - 1][0] = path_row_0;
×
355
     path[path_count - 1][1] = path_col_0;
×
356
     
357
     bool done = false;
×
358
     while (!done) {
×
359
         find_star_in_col(path[path_count - 1][1], r, M);
×
360
         if (r > -1) {
×
361
             path_count += 1;
×
362
             // Bounds check to prevent buffer overflow
363
             if (path_count > static_cast<int>(path.size())) {
×
364
                 throw std::runtime_error("Hungarian algorithm: path length exceeded maximum size");
×
365
             }
366
             path[path_count - 1][0] = r;
×
367
             path[path_count - 1][1] = path[path_count - 2][1];
×
368
         }
369
         else {done = true;}
×
370
         
371
         if (!done) {
×
372
             find_prime_in_row(path[path_count - 1][0], c, M);
×
373
             path_count += 1;
×
374
             // Bounds check to prevent buffer overflow
375
             if (path_count > static_cast<int>(path.size())) {
×
376
                 throw std::runtime_error("Hungarian algorithm: path length exceeded maximum size");
×
377
             }
378
             path[path_count - 1][0] = path[path_count - 2][0];
×
379
             path[path_count - 1][1] = c;
×
380
         }
381
     }
382
     
383
     augment_path(path, path_count, M);
×
384
     clear_covers(RowCover);
×
385
     clear_covers(ColCover);
×
386
     erase_primes(M);
×
387
     
388
     step = 3;
×
389
 }
×
390
 
391
 // methods to support step 6
392
 template<typename T>
393
 void find_smallest(T& minval, 
×
394
                    const std::vector<std::vector<T>>& matrix, 
395
                    const std::vector<int>& RowCover,
396
                    const std::vector<int>& ColCover)
397
 {
398
     for (unsigned r = 0; r < matrix.size(); r++)
×
399
         for (unsigned c = 0; c < matrix.size(); c++)
×
400
             if (RowCover[r] == 0 && ColCover[c] == 0)
×
401
                 if (minval > matrix[r][c])
×
402
                     minval = matrix[r][c];
×
403
 }
×
404
 
405
 /* Add the value found in Step 4 to every element of each covered row, and subtract it 
406
  * from every element of each uncovered column.  Return to Step 4 without altering any
407
  * stars, primes, or covered lines. Notice that this step uses the smallest uncovered 
408
  * value in the cost matrix to modify the matrix.  Even though this step refers to the
409
  * value being found in Step 4 it is more convenient to wait until you reach Step 6 
410
  * before searching for this value.  It may seem that since the values in the cost 
411
  * matrix are being altered, we would lose sight of the original problem.  
412
  * However, we are only changing certain values that have already been tested and 
413
  * found not to be elements of the minimal assignment.  Also we are only changing the 
414
  * values by an amount equal to the smallest value in the cost matrix, so we will not
415
  * jump over the optimal (i.e. minimal assignment) with this change. */
416
 template<typename T>
417
 void step6(std::vector<std::vector<T>>& matrix, 
×
418
            const std::vector<int>& RowCover,
419
            const std::vector<int>& ColCover,
420
            int& step)
421
 {
422
     T minval = std::numeric_limits<T>::max();
×
423
     find_smallest(minval, matrix, RowCover, ColCover);
×
424
     
425
     int sz = matrix.size();
×
426
     for (int r = 0; r < sz; r++)
×
427
         for (int c = 0; c < sz; c++) {
×
428
             if (RowCover[r] == 1)
×
429
                 matrix[r][c] += minval;
×
430
             if (ColCover[c] == 0)
×
431
                 matrix[r][c] -= minval;
×
432
     }
433
     
434
     step = 4;
×
435
 }
×
436
 
437
 /* Calculates the optimal cost from mask matrix */
438
 template<template <typename, typename...> class Container,
439
          typename T,
440
          typename... Args>
441
 T output_solution(const Container<Container<T,Args...>>& original,
360✔
442
                   const std::vector<std::vector<int>>& M)
443
 {
444
     T res = 0;
360✔
445
     
446
     for (unsigned j=0; j<original.begin()->size(); ++j)
1,080✔
447
         for (unsigned i=0; i<original.size(); ++i)
2,160✔
448
             if (M[i][j]) {
1,440✔
449
                 auto it1 = original.begin();
720✔
450
                 std::advance(it1, i);
451
                 auto it2 = it1->begin();
720✔
452
                 std::advance(it2, j);
453
                 res += *it2;
720✔
454
                 continue;                
720✔
455
             }
720✔
456
             
457
     return res;
360✔
458
 }
459
 
460
 
461
 /* Main function of the algorithm */
462
 template<template <typename, typename...> class Container,
463
          typename T,
464
          typename... Args>
465
 typename std::enable_if<std::is_integral<T>::value, T>::type // Work only on integral types
466
 hungarian(const Container<Container<T,Args...>>& original,
×
467
           bool allow_negatives = true)
468
 {  
469
     /* Initialize data structures */
470
     
471
     // Work on a vector copy to preserve original matrix
472
     // Didn't passed by value cause needed to access both
473
     std::vector<std::vector<T>> matrix (original.size(), 
×
474
                                         std::vector<T>(original.begin()->size()));
×
475
     
476
     auto it = original.begin();
×
477
     for (auto& vec: matrix) {         
×
478
         std::copy(it->begin(), it->end(), vec.begin());
×
479
         it = std::next(it);
×
480
     }
481
     
482
     // handle negative values -> pass true if allowed or false otherwise
483
     // if it is an unsigned type just skip this step
484
     if (!std::is_unsigned<T>::value) {
485
         handle_negatives(matrix, allow_negatives);
×
486
     }
487
     
488
     
489
     // make square matrix
490
     pad_matrix(matrix);
×
491
     std::size_t sz = matrix.size();
×
492
     
493
     /* The masked matrix M.  If M(i,j)=1 then C(i,j) is a starred zero,  
494
      * If M(i,j)=2 then C(i,j) is a primed zero. */
495
     std::vector<std::vector<int>> M (sz, std::vector<int>(sz, 0));
×
496
     
497
     /* We also define two vectors RowCover and ColCover that are used to "cover" 
498
      *the rows and columns of the cost matrix C*/
499
     std::vector<int> RowCover (sz, 0);
×
500
     std::vector<int> ColCover (sz, 0);
×
501
     
502
     int path_row_0, path_col_0; //temporary to hold the smallest uncovered value
×
503
     
504
     // Array for the augmenting path algorithm
505
     // Path can potentially alternate between starred and primed zeros up to 2*sz length
506
     std::vector<std::vector<int>> path (2*sz, std::vector<int>(2, 0));
×
507
     
508
     /* Now Work The Steps */
509
     bool done = false;
×
510
     int step = 1;
×
511
     while (!done) {
×
512
         switch (step) {
×
513
             case 1:
×
514
                 step1(matrix, step);
×
515
                 break;
×
516
             case 2:
×
517
                 step2(matrix, M, RowCover, ColCover, step);
×
518
                 break;
×
519
             case 3:
×
520
                 step3(M, ColCover, step);
×
521
                 break;
×
522
             case 4:
×
523
                 step4(matrix, M, RowCover, ColCover, path_row_0, path_col_0, step);
×
524
                 break;
×
525
             case 5:
×
526
                 step5(path, path_row_0, path_col_0, M, RowCover, ColCover, step);
×
527
                 break;
×
528
             case 6:
×
529
                 step6(matrix, RowCover, ColCover, step);
×
530
                 break;
×
531
             case 7:
×
532
                 for (auto& vec: M) {vec.resize(original.begin()->size());}
×
533
                 M.resize(original.size());
×
534
                 done = true;
×
535
                 break;
×
536
             default:
×
537
                 done = true;
×
538
                 break;
×
539
         }
540
     }
541
     
542
     //Printing part (optional)
543
     //std::cout << "Cost Matrix: \n" << original << std::endl 
544
     //          << "Optimal assignment: \n" << M;
545
     
546
     return output_solution(original, M);
×
547
 }
×
548

549
 template<template <typename, typename...> class Container,
550
          typename T,
551
          typename... Args>
552
 typename std::enable_if<std::is_integral<T>::value, T>::type
553
 hungarian_with_assignment(const Container<Container<T,Args...>>& original, 
360✔
554
                          std::vector<std::vector<int>>& assignment_matrix,
555
                          bool allow_negatives)
556
 {
557
     Container<Container<T,Args...>> matrix (original);
360✔
558
     
559
     /* If the problem is an odd sized matrix then we adjust to an even size by padding with zeroes  */
560
     pad_matrix(matrix);
360✔
561
     std::size_t sz = matrix.size();
360✔
562
     
563
     /* The masked matrix M.  If M(i,j)=1 then C(i,j) is a starred zero,  
564
      * If M(i,j)=2 then C(i,j) is a primed zero. */
565
     std::vector<std::vector<int>> M (sz, std::vector<int>(sz, 0));
1,800✔
566
     
567
     /* We also define two vectors RowCover and ColCover that are used to "cover" 
568
      *the rows and columns of the cost matrix C*/
569
     std::vector<int> RowCover (sz, 0);
1,080✔
570
     std::vector<int> ColCover (sz, 0);
1,080✔
571
     
572
     int path_row_0, path_col_0; //temporary to hold the smallest uncovered value
360✔
573
     
574
     // Array for the augmenting path algorithm
575
     // Path can potentially alternate between starred and primed zeros up to 2*sz length
576
     std::vector<std::vector<int>> path (2*sz, std::vector<int>(2, 0));
1,800✔
577
     
578
     /* Now Work The Steps */
579
     bool done = false;
360✔
580
     int step = 1;
360✔
581
     while (!done) {
1,800✔
582
         switch (step) {
1,440✔
583
             case 1:
360✔
584
                 step1(matrix, step);
360✔
585
                 break;
360✔
586
             case 2:
360✔
587
                 step2(matrix, M, RowCover, ColCover, step);
360✔
588
                 break;
360✔
589
             case 3:
360✔
590
                 step3(M, ColCover, step);
360✔
591
                 break;
360✔
NEW
592
             case 4:
×
NEW
593
                 step4(matrix, M, RowCover, ColCover, path_row_0, path_col_0, step);
×
NEW
594
                 break;
×
NEW
595
             case 5:
×
NEW
596
                 step5(path, path_row_0, path_col_0, M, RowCover, ColCover, step);
×
NEW
597
                 break;
×
NEW
598
             case 6:
×
NEW
599
                 step6(matrix, RowCover, ColCover, step);
×
NEW
600
                 break;
×
601
             case 7:
360✔
602
                 for (auto& vec: M) {vec.resize(original.begin()->size());}
1,080✔
603
                 M.resize(original.size());
360✔
604
                 done = true;
360✔
605
                 break;
360✔
NEW
606
             default:
×
NEW
607
                 done = true;
×
NEW
608
                 break;
×
609
         }
610
     }
611
     
612
     // Copy the assignment matrix to output
613
     assignment_matrix = M;
360✔
614
     
615
     return output_solution(original, M);
720✔
616
 }
360✔
617

618

619

620
} // end of namespace munkres
621

622
// Explicit instantiation for the types we need
623
template int Munkres::hungarian<std::vector, int>(
624
    const std::vector<std::vector<int>>& original, bool allow_negatives);
625

626
template int Munkres::hungarian_with_assignment<std::vector, int>(
627
    const std::vector<std::vector<int>>& original, 
628
    std::vector<std::vector<int>>& assignment_matrix,
629
    bool allow_negatives); 
630
 /*
631
 int main() //example of usage
632
 {
633
     using namespace Munkres;
634
     using namespace std;
635
     
636
     // work on multiple containers of the STL
637
     list<list<int>> matrix {{85,  12,  36,  83,  50,  96,  12,  1 },
638
                             {84,  35,  16,  17,  40,  94,  16,  52},
639
                             {14,  16,  8 ,  53,  14,  12,  70,  50},
640
                             {73,  83,  19,  44,  83,  66,  71,  18},
641
                             {36,  45,  29,  4 ,  61,  15,  70,  47},
642
                             {7 ,  14,  11,  69,  57,  32,  37,  81},
643
                             {9 ,  65,  38,  74,  87,  51,  86,  52},
644
                             {52,  40,  56,  10,  42,  2 ,  26,  36},
645
                             {85,  86,  36,  90,  49,  89,  41,  74},
646
                             {40,  67,  2 ,  70,  18,  5 ,  94,  43},
647
                             {85,  12,  36,  83,  50,  96,  12,  1 },
648
                             {84,  35,  16,  17,  40,  94,  16,  52},
649
                             {14,  16,  8 ,  53,  14,  12,  70,  50},
650
                             {73,  83,  19,  44,  83,  66,  71,  18},
651
                             {36,  45,  29,  4 ,  61,  15,  70,  47},
652
                             {7 ,  14,  11,  69,  57,  32,  37,  81},
653
                             {9 ,  65,  38,  74,  87,  51,  86,  52},
654
                             {52,  40,  56,  10,  42,  2 ,  26,  36},
655
                             {85,  86,  36,  90,  49,  89,  41,  74},
656
                             {40,  67,  2 ,  70,  18,  5 ,  94,  43}};
657
                                      
658
     auto res = hungarian(matrix);
659
     std::cout << "Optimal cost: " << res << std::endl;
660
     std::cout << "----------------- \n\n";
661
     
662
     vector<vector<vector<int>>> tests;
663
     
664
     tests.push_back({{25,40,35},
665
                      {40,60,35},
666
                      {20,40,25}});
667
     
668
     tests.push_back({{64,18,75},
669
                      {97,60,24},
670
                      {87,63,15}});
671
     
672
     tests.push_back({{80,40,50,46}, 
673
                      {40,70,20,25},
674
                      {30,10,20,30},
675
                      {35,20,25,30}});
676
     
677
     tests.push_back({{10,19,8,15},
678
                      {10,18,7,17},
679
                      {13,16,9,14},
680
                      {12,19,8,18},
681
                      {14,17,10,19}});
682
     
683
     for (auto& m: tests) {
684
         auto r = hungarian(m);
685
         std::cout << "Optimal cost: " << r << std::endl;
686
         std::cout << "----------------- \n\n";
687
     }
688
     
689
     return 0;
690
 }
691
     */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc