libcrn  3.9.5
A document image processing library
•All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CRNBipartite.cpp
Go to the documentation of this file.
1 /* Copyright 2015 INSA-Lyon, Université Paris Descartes
2  *
3  * This file is part of libcrn.
4  *
5  * libcrn is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * libcrn 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 Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with libcrn. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * file: CRNBipartite.cpp
19  * \author Yann LEYDIER
20  */
21 
22 #include <CRNAI/CRNBipartite.h>
24 #include <CRNException.h>
25 #include <CRNStringUTF8.h>
26 #include <limits>
27 #include <algorithm>
28 #include <CRNi18n.h>
29 
30 using namespace crn;
31 using namespace crn::literals;
32 
34 {
35  kuhn_munkres(const std::vector<std::vector<double>> &dm):
36  c(dm),
37  n(dm.size()),
38  row_covered(dm.size(), false),
39  col_covered(dm.size(), false),
40  path(dm.size() * 2, std::vector<size_t>(dm.size() * 2, 0)),
41  marked(dm.size(), std::vector<uint8_t>(dm.size(), 0))
42  { }
44  c(dm),
45  n(dm.GetRows()),
46  row_covered(dm.GetRows(), false),
47  col_covered(dm.GetRows(), false),
48  path(dm.GetRows() * 2, std::vector<size_t>(dm.GetRows() * 2, 0)),
49  marked(dm.GetRows(), std::vector<uint8_t>(dm.GetRows(), 0))
50  { }
52  const size_t n;
53  std::vector<bool> row_covered, col_covered;
54  size_t z0_r = 0, z0_c = 0;
55  std::vector<std::vector<size_t>> path;
56  std::vector<std::vector<uint8_t>> marked;
57 };
58 
59 static inline void clear_covers(kuhn_munkres &km)
60 {
61  //Clear all covered matrix cells
62  std::fill(km.row_covered.begin(), km.row_covered.end(), false);
63  std::fill(km.col_covered.begin(), km.col_covered.end(), false);
64 }
65 
66 static inline std::pair<int, int> find_a_zero(const kuhn_munkres &km)
67 {
68  // Find the first uncovered element with value 0
69  for (int i = 0; i < int(km.n); ++i)
70  for (int j = 0; j < int(km.n); ++j)
71  {
72  if ((km.c[i][j] == 0) && !km.row_covered[i] && !km.col_covered[j])
73  return std::make_pair(i, j);
74  }
75  return std::make_pair(-1, -1);
76 }
77 
78 static inline int find_star_in_row(const kuhn_munkres &km, size_t row)
79 {
80  // Find the first starred element in the specified row. Returns the column index, or -1 if no starred element was found.
81  for (int j = 0; j < int(km.n); ++j)
82  if (km.marked[row][j] == 1)
83  return j;
84  return -1;
85 }
86 
87 static inline int find_star_in_col(const kuhn_munkres &km, size_t col)
88 {
89  // Find the first starred element in the specified row. Returns the row index, or -1 if no starred element was found.
90  for (int i = 0; i < int(km.n); ++i)
91  if (km.marked[i][col] == 1)
92  return i;
93  return -1;
94 }
95 
96 static inline int find_prime_in_row(const kuhn_munkres &km, size_t row)
97 {
98  // Find the first prime element in the specified row. Returns the column index, or -1 if no starred element was found.
99  for (int j = 0; j < int(km.n); ++j)
100  if (km.marked[row][j] == 2)
101  return j;
102  return -1;
103 }
104 
105 static inline void convert_path(kuhn_munkres &km, size_t count)
106 {
107  for (size_t i = 0; i <= count; ++i)
108  if (km.marked[km.path[i][0]][km.path[i][1]] == 1)
109  km.marked[km.path[i][0]][km.path[i][1]] = 0;
110  else
111  km.marked[km.path[i][0]][km.path[i][1]] = 1;
112 }
113 
114 void inline erase_primes(kuhn_munkres &km)
115 {
116  // Erase all prime markings
117  for (int i = 0; i < int(km.n); ++i)
118  for (int j = 0; j < int(km.n); ++j)
119  if (km.marked[i][j] == 2)
120  km.marked[i][j] = 0;
121 }
122 
123 static inline double find_smallest(const kuhn_munkres &km)
124 {
125  // Find the smallest uncovered value in the matrix.
126  auto minval = std::numeric_limits<double>::max();
127  for (size_t i = 0; i < km.n; ++i)
128  for (size_t j = 0; j < km.n; ++j)
129  if (!km.row_covered[i] && !km.col_covered[j])
130  if (minval > km.c[i][j])
131  minval = km.c[i][j];
132  return minval;
133 }
134 
135 static inline int step1(kuhn_munkres &km)
136 {
137  // For each row of the matrix, find the smallest element and subtract it from every element in its row. Go to Step 2.
138  for (size_t i = 0; i < km.n; ++i)
139  {
140  // Find the minimum value for this row and subtract that minimum from every element in the row.
141  auto minval = *std::min_element(km.c[i], km.c[i] + km.c.GetCols());
142  for (auto v = size_t(0); v < km.c.GetCols(); ++v)
143  km.c[i][v] -= minval;
144  }
145  return 2;
146 }
147 
148 static inline int step2(kuhn_munkres &km)
149 {
150  // Find a zero (Z) in the resulting matrix. If there is no starred zero in its row or column, star Z. Repeat for each element in the matrix. Go to Step 3.
151  for (size_t i = 0; i < km.n; ++i)
152  for (size_t j = 0; j < km.n; ++j)
153  if ((km.c[i][j] == 0) && !km.row_covered[i] && !km.col_covered[j])
154  {
155  km.marked[i][j] = 1;
156  km.row_covered[i] = true;
157  km.col_covered[j] = true;
158  }
159 
160  clear_covers(km);
161  return 3;
162 }
163 
164 static inline int step3(kuhn_munkres &km)
165 {
166  // Cover each column containing a starred zero. If K columns are covered, the starred zeros describe a complete set of unique assignments. In this case, Go to DONE, otherwise, Go to Step 4.
167  auto count = size_t(0);
168  for (size_t i = 0; i < km.n; ++i)
169  for (size_t j = 0; j < km.n; ++j)
170  if (km.marked[i][j] == 1)
171  {
172  km.col_covered[j] = true;
173  count += 1;
174  }
175  if (count >= km.n)
176  return 0;
177  else
178  return 4;
179 }
180 
181 static inline int step4(kuhn_munkres &km)
182 {
183  // Find a noncovered zero and prime it. If there is no starred zero in the row containing this primed zero, Go to Step 5. Otherwise, cover this row and uncover the column containing the starred zero. Continue in this manner until there are no uncovered zeros left. Save the smallest uncovered value and Go to Step 6.
184  while (true)
185  {
186  auto rc = find_a_zero(km);
187  if (rc.first < 0)
188  return 6;
189  km.marked[rc.first][rc.second] = 2;
190  auto star_col = find_star_in_row(km, rc.first);
191  if (star_col >= 0)
192  {
193  rc.second = star_col;
194  km.row_covered[rc.first] = true;
195  km.col_covered[rc.second] = false;
196  }
197  else
198  {
199  km.z0_r = rc.first;
200  km.z0_c = rc.second;
201  return 5;
202  }
203  }
204 }
205 
206 static inline int step5(kuhn_munkres &km)
207 {
208  // Construct a series of alternating primed and starred zeros as follows. Let Z0 represent the uncovered primed zero found in Step 4. Let Z1 denote the starred zero in the column of Z0 (if any). Let Z2 denote the primed zero in the row of Z1 (there will always be one). Continue until the series terminates at a primed zero that has no starred zero in its column. Unstar each starred zero of the series, star each primed zero of the series, erase all primes and uncover every line in the matrix. Return to Step 3
209  auto count = size_t(0);
210  km.path[count][0] = km.z0_r;
211  km.path[count][1] = km.z0_c;
212  while (true)
213  {
214  auto row = find_star_in_col(km, km.path[count][1]);
215  if (row >= 0)
216  {
217  count += 1;
218  km.path[count][0] = row;
219  km.path[count][1] = km.path[count-1][1];
220  }
221  else
222  break;
223 
224  auto col = find_prime_in_row(km, km.path[count][0]);
225  count += 1;
226  km.path[count][0] = km.path[count-1][0];
227  km.path[count][1] = col;
228  }
229  convert_path(km, count);
230  clear_covers(km);
231  erase_primes(km);
232  return 3;
233 }
234 
235 static inline int step6(kuhn_munkres &km)
236 {
237  // Add the value found in Step 4 to every element of each covered row, and subtract it from every element of each uncovered column. Return to Step 4 without altering any stars, primes, or covered lines.
238  auto minval = find_smallest(km);
239  for (size_t i = 0; i < km.n; ++i)
240  for (size_t j = 0; j < km.n; ++j)
241  {
242  if (km.row_covered[i])
243  km.c[i][j] += minval;
244  if (!km.col_covered[j])
245  km.c[i][j] -= minval;
246  }
247  return 4;
248 }
249 
250 template<typename T> std::tuple<double, std::vector<std::pair<size_t, size_t>>> hung(const T &distmat)
251 {
252  auto km = kuhn_munkres(distmat);
253  auto step = 1;
254  while (step)
255  {
256  switch (step)
257  {
258  case 1:
259  step = step1(km);
260  break;
261  case 2:
262  step = step2(km);
263  break;
264  case 3:
265  step = step3(km);
266  break;
267  case 4:
268  step = step4(km);
269  break;
270  case 5:
271  step = step5(km);
272  break;
273  case 6:
274  step = step6(km);
275  break;
276  }
277  }
278  auto pairs = std::vector<std::pair<size_t, size_t>>{};
279  auto cost = 0.0;
280  // Look for the starred columns
281  for (size_t i = 0; i < km.n; ++i)
282  for (size_t j = 0; j < km.n; ++j)
283  if (km.marked[i][j] == 1)
284  {
285  pairs.emplace_back(i, j);
286  cost += distmat[i][j];
287  }
288 
289  return std::make_tuple(cost, std::move(pairs));
290 }
291 
300 std::tuple<double, std::vector<std::pair<size_t, size_t>>> crn::Hungarian(const std::vector<std::vector<double>> &distmat)
301 {
302  if (distmat.empty())
303  throw ExceptionInvalidArgument(u8"Hugarian(): "_s + _("empty distance matrix."));
304  for (const auto &row : distmat)
305  if (row.size() != distmat.size())
306  throw ExceptionInvalidArgument(u8"Hugarian(): "_s + _("the distance matrix is not square."));
307  return hung(distmat);
308 }
309 
318 std::tuple<double, std::vector<std::pair<size_t, size_t>>> crn::Hungarian(const SquareMatrixDouble &distmat)
319 {
320  return hung(distmat);
321 }
322 
323 
std::vector< std::vector< size_t > > path
std::vector< bool > row_covered
size_t GetCols() const noexcept
Returns the number of columns.
Definition: CRNMatrix.h:163
kuhn_munkres(const std::vector< std::vector< double >> &dm)
#define _(String)
Definition: CRNi18n.h:51
const size_t n
crn::SquareMatrixDouble c
std::vector< bool > col_covered
std::tuple< double, std::vector< std::pair< size_t, size_t > > > hung(const T &distmat)
#define false
Definition: ConvertUTF.cpp:56
std::vector< std::vector< uint8_t > > marked
void erase_primes(kuhn_munkres &km)
std::tuple< double, std::vector< std::pair< size_t, size_t > > > Hungarian(const std::vector< std::vector< double >> &distmat)
Square double matrix class.
kuhn_munkres(const crn::SquareMatrixDouble &dm)
Invalid argument error (e.g.: nullptr pointer)
Definition: CRNException.h:107