hungarian_algorithm_c 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3131f481cd59f14347ab0881138cebeee3e426df
4
+ data.tar.gz: 8d5f3506ffe58e10ccb2bb5b550aaec1d3296a18
5
+ SHA512:
6
+ metadata.gz: 3f41e80045ae12207881e794037d078e890e60bae9045c363cd86c3716f148318204ed06bdd67b2f0ef0630ede36cf83cc3a048a7edcb7cf1a83dbd8b0f5a957
7
+ data.tar.gz: ea61a05071cab151bb5e867e535b6127351889f610671800e8c800996ad432fbb20151e2a86333a5bb9179457f33c47c2a46133b29b6815db03ad4010f9892cc
@@ -0,0 +1,9 @@
1
+ tmp/
2
+ .DS_Store
3
+ *.gem
4
+ *.bundle
5
+ Gemfile.lock
6
+ lib/hungarian_algorithm_c.so
7
+ ext/hungarian_algorithm_c/libhungarian/hungarian.o
8
+ ext/hungarian_algorithm_c/libhungarian/hungarian_test
9
+ ext/hungarian_algorithm_c/libhungarian/libhungarian.a
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.2.7
5
+ - 2.3.1
6
+ - 2.4.1
7
+
8
+ script:
9
+ - bundle exec rake test
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,49 @@
1
+ # HungarianAlgorithmC
2
+
3
+ A Ruby gem that computes the [Hungarian Algorithm](https://en.wikipedia.org/wiki/Hungarian_algorithm) in C.
4
+
5
+ ## Installation
6
+
7
+ Add the following to your [Gemfile](http://tosbourn.com/what-is-the-gemfile/):
8
+
9
+ ```ruby
10
+ # Gemfile
11
+ gem 'hungarian_algorithm_c', github: 'deliveroo/hungarian_algorithm_c'
12
+ ```
13
+
14
+ Run `bundle install`.
15
+
16
+ ## Usage
17
+
18
+ Find the best pairings for elements with costs like so:
19
+
20
+ ```ruby
21
+ costs = [
22
+ [4, 3],
23
+ [3, 0]
24
+ ]
25
+
26
+ HungarianAlgorithmC.find_pairings(costs)
27
+
28
+ #=> [[0, 0], [1, 1]]
29
+ ```
30
+
31
+ The output will be an array of arrays; each sub-array will have the first element as the row index and the second element as the column index for the `costs` matrix. The indices together will select the lowest-cost combination.
32
+
33
+ Another example:
34
+
35
+ ```ruby
36
+ costs = [
37
+ [2, 3, 3],
38
+ [3, 3, 2],
39
+ [3, 2, 3]
40
+ ]
41
+
42
+ HungarianAlgorithmC.find_pairings(costs)
43
+
44
+ #=> [[0, 0], [1, 2], [2, 1]]
45
+ ```
46
+
47
+ ## Acknowledgements
48
+
49
+ The C code uses the implementation by [Cyrill Stachniss](ext/hungarian_algorithm_c/libhungarian).
@@ -0,0 +1,8 @@
1
+ require 'rake/extensiontask'
2
+ require 'rake/testtask'
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ Rake::ExtensionTask.new('hungarian_algorithm_c')
7
+
8
+ task test: [:compile, :spec]
@@ -0,0 +1,3 @@
1
+ require 'mkmf'
2
+ extension_name = 'hungarian_algorithm_c'
3
+ create_makefile(extension_name)
@@ -0,0 +1,69 @@
1
+ #include "ruby.h"
2
+ #include "libhungarian/hungarian.c"
3
+ #include "libhungarian/hungarian_test.c"
4
+
5
+ // Define namespace / module name
6
+ VALUE HungarianAlgorithmC = Qnil;
7
+
8
+ // function declarations
9
+ void Init_hungarian_algorithm_c();
10
+ static VALUE indices_array(hungarian_problem_t* p, int row_size);
11
+ VALUE pairs(VALUE self, VALUE flattened_array_ruby, VALUE row_size_val);
12
+
13
+ // function to initialize module
14
+ void Init_hungarian_algorithm_c() {
15
+ HungarianAlgorithmC = rb_define_module("HungarianAlgorithmC");
16
+ rb_define_singleton_method(HungarianAlgorithmC, "pairs", pairs, 2);
17
+ }
18
+
19
+ // function to extract assignment indices from a hungarian_problem_t struct to a '2D' Ruby array
20
+ static VALUE indices_array(hungarian_problem_t* p, int row_size) {
21
+ int** values = p->assignment;
22
+ VALUE array = rb_ary_new2(row_size);
23
+
24
+ int i;
25
+ for (i = 0; i < row_size; i++) {
26
+ VALUE row = rb_ary_new2(2);
27
+
28
+ int j;
29
+ for (j = 0; j < row_size; j++) {
30
+ int value = values[i][j];
31
+ if (value == 1) {
32
+ rb_ary_push(row, INT2NUM(i));
33
+ rb_ary_push(row, INT2NUM(j));
34
+ }
35
+ }
36
+
37
+ rb_ary_push(array, row);
38
+ }
39
+
40
+ return array;
41
+ }
42
+
43
+ // function to find the optimal assignments from a flattend Ruby array
44
+ // array must be flatted from an array representation of a rectangular matrix
45
+ // i.e. the number of rows should equal the number of columns
46
+ VALUE pairs(VALUE self, VALUE flattened_array_ruby, VALUE row_size_val) {
47
+ VALUE output;
48
+ hungarian_problem_t p;
49
+ int** matrix;
50
+ int row_size = NUM2INT(row_size_val);
51
+ int array_size = row_size * row_size;
52
+ int array_c[array_size];
53
+
54
+ int index;
55
+ for (index = 0; index < array_size; index++) {
56
+ double element = 100 * NUM2DBL(rb_ary_entry(flattened_array_ruby, index));
57
+ int rounded_element = element;
58
+ array_c[index] = rounded_element;
59
+ }
60
+
61
+ matrix = array_to_matrix(array_c, row_size, row_size);
62
+
63
+ hungarian_init(&p, matrix, row_size, row_size, 0);
64
+ hungarian_solve(&p);
65
+ output = indices_array(&p, row_size);
66
+ hungarian_free(&p);
67
+
68
+ return output;
69
+ }
@@ -0,0 +1,20 @@
1
+ CC = gcc
2
+ AR = ar
3
+
4
+ CFLAGS = -O3 -Wall -I.
5
+ LDFLAGS = -L. -lhungarian
6
+
7
+ all: libhungarian.a hungarian_test
8
+
9
+ hungarian_test: hungarian_test.c $(HUNGARIANLIB)
10
+ $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
11
+
12
+
13
+ libhungarian.a: hungarian.o
14
+ $(AR) cr $@ hungarian.o
15
+
16
+ %.o: %.c %.h
17
+ $(CC) $(CFLAGS) -c $<
18
+
19
+ clean:
20
+ rm -f *.o *.a hungarian_test
@@ -0,0 +1,11 @@
1
+ # C Implementation of the Hungarian Method
2
+
3
+ [Download libhungarian-v0.1.3.tgz (21.09.2015)](http://www2.informatik.uni-freiburg.de/~stachnis/misc/libhungarian-v0.1.3.tgz)
4
+
5
+ From [homepage](http://www2.informatik.uni-freiburg.de/~stachnis/misc.html):
6
+
7
+ > C-implementation of the Hungarian Method: finding the optimal assignment (assigning a set of jobs to a set of machines) in O(n^3), where n=max{#jobs, #machines}. The implementation is a sligntly enhanced version of the implementation provided by the Stanford GraphBase. See also: Stanford GraphBase, Hungarian Method by Brian Gerkey.
8
+
9
+ From [implementation file](hungarian.c):
10
+
11
+ > This file may be freely copied and distributed!
@@ -0,0 +1,419 @@
1
+ /********************************************************************
2
+ ********************************************************************
3
+ **
4
+ ** libhungarian by Cyrill Stachniss, 2004
5
+ **
6
+ **
7
+ ** Solving the Minimum Assignment Problem using the
8
+ ** Hungarian Method.
9
+ **
10
+ ** ** This file may be freely copied and distributed! **
11
+ **
12
+ ** Parts of the used code was originally provided by the
13
+ ** "Stanford GraphGase", but I made changes to this code.
14
+ ** As asked by the copyright node of the "Stanford GraphGase",
15
+ ** I hereby proclaim that this file are *NOT* part of the
16
+ ** "Stanford GraphGase" distrubition!
17
+ **
18
+ ** This file is distributed in the hope that it will be useful,
19
+ ** but WITHOUT ANY WARRANTY; without even the implied
20
+ ** warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
21
+ ** PURPOSE.
22
+ **
23
+ ********************************************************************
24
+ ********************************************************************/
25
+
26
+
27
+ #include <stdio.h>
28
+ #include <stdlib.h>
29
+ #include "hungarian.h"
30
+
31
+ #define INF (0x7FFFFFFF)
32
+ #define verbose (0)
33
+
34
+ #define hungarian_test_alloc(X) do {if ((void *)(X) == NULL) fprintf(stderr, "Out of memory in %s, (%s, line %d).\n", __FUNCTION__, __FILE__, __LINE__); } while (0)
35
+
36
+
37
+ void hungarian_print_matrix(int** C, int rows, int cols) {
38
+ int i,j;
39
+ fprintf(stderr , "\n");
40
+ for(i=0; i<rows; i++) {
41
+ fprintf(stderr, " [");
42
+ for(j=0; j<cols; j++) {
43
+ fprintf(stderr, "%5d ",C[i][j]);
44
+ }
45
+ fprintf(stderr, "]\n");
46
+ }
47
+ fprintf(stderr, "\n");
48
+ }
49
+
50
+ void hungarian_print_assignment(hungarian_problem_t* p) {
51
+ hungarian_print_matrix(p->assignment, p->num_rows, p->num_cols) ;
52
+ }
53
+
54
+ void hungarian_print_costmatrix(hungarian_problem_t* p) {
55
+ hungarian_print_matrix(p->cost, p->num_rows, p->num_cols) ;
56
+ }
57
+
58
+ void hungarian_print_status(hungarian_problem_t* p) {
59
+
60
+ fprintf(stderr,"cost:\n");
61
+ hungarian_print_costmatrix(p);
62
+
63
+ fprintf(stderr,"assignment:\n");
64
+ hungarian_print_assignment(p);
65
+
66
+ }
67
+
68
+ int hungarian_imax(int a, int b) {
69
+ return (a<b)?b:a;
70
+ }
71
+
72
+ int hungarian_init(hungarian_problem_t* p, int** cost_matrix, int rows, int cols, int mode) {
73
+
74
+ int i,j, org_cols, org_rows;
75
+ int max_cost;
76
+ max_cost = 0;
77
+
78
+ org_cols = cols;
79
+ org_rows = rows;
80
+
81
+ // is the number of cols not equal to number of rows ?
82
+ // if yes, expand with 0-cols / 0-cols
83
+ rows = hungarian_imax(cols, rows);
84
+ cols = rows;
85
+
86
+ p->num_rows = rows;
87
+ p->num_cols = cols;
88
+
89
+ p->cost = (int**)calloc(rows,sizeof(int*));
90
+ hungarian_test_alloc(p->cost);
91
+ p->assignment = (int**)calloc(rows,sizeof(int*));
92
+ hungarian_test_alloc(p->assignment);
93
+
94
+ for(i=0; i<p->num_rows; i++) {
95
+ p->cost[i] = (int*)calloc(cols,sizeof(int));
96
+ hungarian_test_alloc(p->cost[i]);
97
+ p->assignment[i] = (int*)calloc(cols,sizeof(int));
98
+ hungarian_test_alloc(p->assignment[i]);
99
+ for(j=0; j<p->num_cols; j++) {
100
+ p->cost[i][j] = (i < org_rows && j < org_cols) ? cost_matrix[i][j] : 0;
101
+ p->assignment[i][j] = 0;
102
+
103
+ if (max_cost < p->cost[i][j])
104
+ max_cost = p->cost[i][j];
105
+ }
106
+ }
107
+
108
+
109
+ if (mode == HUNGARIAN_MODE_MAXIMIZE_UTIL) {
110
+ for(i=0; i<p->num_rows; i++) {
111
+ for(j=0; j<p->num_cols; j++) {
112
+ p->cost[i][j] = max_cost - p->cost[i][j];
113
+ }
114
+ }
115
+ }
116
+ else if (mode == HUNGARIAN_MODE_MINIMIZE_COST) {
117
+ // nothing to do
118
+ }
119
+ else
120
+ fprintf(stderr,"%s: unknown mode. Mode was set to HUNGARIAN_MODE_MINIMIZE_COST !\n", __FUNCTION__);
121
+
122
+ return rows;
123
+ }
124
+
125
+
126
+
127
+
128
+ void hungarian_free(hungarian_problem_t* p) {
129
+ int i;
130
+ for(i=0; i<p->num_rows; i++) {
131
+ free(p->cost[i]);
132
+ free(p->assignment[i]);
133
+ }
134
+ free(p->cost);
135
+ free(p->assignment);
136
+ p->cost = NULL;
137
+ p->assignment = NULL;
138
+ }
139
+
140
+
141
+
142
+ void hungarian_solve(hungarian_problem_t* p)
143
+ {
144
+ int i, j, m, n, k, l, s, t, q, unmatched, cost;
145
+ int* col_mate;
146
+ int* row_mate;
147
+ int* parent_row;
148
+ int* unchosen_row;
149
+ int* row_dec;
150
+ int* col_inc;
151
+ int* slack;
152
+ int* slack_row;
153
+
154
+ cost=0;
155
+ m =p->num_rows;
156
+ n =p->num_cols;
157
+
158
+ col_mate = (int*)calloc(p->num_rows,sizeof(int));
159
+ hungarian_test_alloc(col_mate);
160
+ unchosen_row = (int*)calloc(p->num_rows,sizeof(int));
161
+ hungarian_test_alloc(unchosen_row);
162
+ row_dec = (int*)calloc(p->num_rows,sizeof(int));
163
+ hungarian_test_alloc(row_dec);
164
+ slack_row = (int*)calloc(p->num_rows,sizeof(int));
165
+ hungarian_test_alloc(slack_row);
166
+
167
+ row_mate = (int*)calloc(p->num_cols,sizeof(int));
168
+ hungarian_test_alloc(row_mate);
169
+ parent_row = (int*)calloc(p->num_cols,sizeof(int));
170
+ hungarian_test_alloc(parent_row);
171
+ col_inc = (int*)calloc(p->num_cols,sizeof(int));
172
+ hungarian_test_alloc(col_inc);
173
+ slack = (int*)calloc(p->num_cols,sizeof(int));
174
+ hungarian_test_alloc(slack);
175
+
176
+ for (i=0;i<p->num_rows;i++) {
177
+ col_mate[i]=0;
178
+ unchosen_row[i]=0;
179
+ row_dec[i]=0;
180
+ slack_row[i]=0;
181
+ }
182
+ for (j=0;j<p->num_cols;j++) {
183
+ row_mate[j]=0;
184
+ parent_row[j] = 0;
185
+ col_inc[j]=0;
186
+ slack[j]=0;
187
+ }
188
+
189
+ for (i=0;i<p->num_rows;++i)
190
+ for (j=0;j<p->num_cols;++j)
191
+ p->assignment[i][j]=HUNGARIAN_NOT_ASSIGNED;
192
+
193
+ // Begin subtract column minima in order to start with lots of zeroes 12
194
+ if (verbose)
195
+ fprintf(stderr, "Using heuristic\n");
196
+ for (l=0;l<n;l++)
197
+ {
198
+ s=p->cost[0][l];
199
+ for (k=1;k<m;k++)
200
+ if (p->cost[k][l]<s)
201
+ s=p->cost[k][l];
202
+ cost+=s;
203
+ if (s!=0)
204
+ for (k=0;k<m;k++)
205
+ p->cost[k][l]-=s;
206
+ }
207
+ // End subtract column minima in order to start with lots of zeroes 12
208
+
209
+ // Begin initial state 16
210
+ t=0;
211
+ for (l=0;l<n;l++)
212
+ {
213
+ row_mate[l]= -1;
214
+ parent_row[l]= -1;
215
+ col_inc[l]=0;
216
+ slack[l]=INF;
217
+ }
218
+ for (k=0;k<m;k++)
219
+ {
220
+ s=p->cost[k][0];
221
+ for (l=1;l<n;l++)
222
+ if (p->cost[k][l]<s)
223
+ s=p->cost[k][l];
224
+ row_dec[k]=s;
225
+ for (l=0;l<n;l++)
226
+ if (s==p->cost[k][l] && row_mate[l]<0)
227
+ {
228
+ col_mate[k]=l;
229
+ row_mate[l]=k;
230
+ if (verbose)
231
+ fprintf(stderr, "matching col %d==row %d\n",l,k);
232
+ goto row_done;
233
+ }
234
+ col_mate[k]= -1;
235
+ if (verbose)
236
+ fprintf(stderr, "node %d: unmatched row %d\n",t,k);
237
+ unchosen_row[t++]=k;
238
+ row_done:
239
+ ;
240
+ }
241
+ // End initial state 16
242
+
243
+ // Begin Hungarian algorithm 18
244
+ if (t==0)
245
+ goto done;
246
+ unmatched=t;
247
+ while (1)
248
+ {
249
+ if (verbose)
250
+ fprintf(stderr, "Matched %d rows.\n",m-t);
251
+ q=0;
252
+ while (1)
253
+ {
254
+ while (q<t)
255
+ {
256
+ // Begin explore node q of the forest 19
257
+ {
258
+ k=unchosen_row[q];
259
+ s=row_dec[k];
260
+ for (l=0;l<n;l++)
261
+ if (slack[l])
262
+ {
263
+ int del;
264
+ del=p->cost[k][l]-s+col_inc[l];
265
+ if (del<slack[l])
266
+ {
267
+ if (del==0)
268
+ {
269
+ if (row_mate[l]<0)
270
+ goto breakthru;
271
+ slack[l]=0;
272
+ parent_row[l]=k;
273
+ if (verbose)
274
+ fprintf(stderr, "node %d: row %d==col %d--row %d\n",
275
+ t,row_mate[l],l,k);
276
+ unchosen_row[t++]=row_mate[l];
277
+ }
278
+ else
279
+ {
280
+ slack[l]=del;
281
+ slack_row[l]=k;
282
+ }
283
+ }
284
+ }
285
+ }
286
+ // End explore node q of the forest 19
287
+ q++;
288
+ }
289
+
290
+ // Begin introduce a new zero into the matrix 21
291
+ s=INF;
292
+ for (l=0;l<n;l++)
293
+ if (slack[l] && slack[l]<s)
294
+ s=slack[l];
295
+ for (q=0;q<t;q++)
296
+ row_dec[unchosen_row[q]]+=s;
297
+ for (l=0;l<n;l++)
298
+ if (slack[l])
299
+ {
300
+ slack[l]-=s;
301
+ if (slack[l]==0)
302
+ {
303
+ // Begin look at a new zero 22
304
+ k=slack_row[l];
305
+ if (verbose)
306
+ fprintf(stderr,
307
+ "Decreasing uncovered elements by %d produces zero at [%d,%d]\n",
308
+ s,k,l);
309
+ if (row_mate[l]<0)
310
+ {
311
+ for (j=l+1;j<n;j++)
312
+ if (slack[j]==0)
313
+ col_inc[j]+=s;
314
+ goto breakthru;
315
+ }
316
+ else
317
+ {
318
+ parent_row[l]=k;
319
+ if (verbose)
320
+ fprintf(stderr, "node %d: row %d==col %d--row %d\n",t,row_mate[l],l,k);
321
+ unchosen_row[t++]=row_mate[l];
322
+ }
323
+ // End look at a new zero 22
324
+ }
325
+ }
326
+ else
327
+ col_inc[l]+=s;
328
+ // End introduce a new zero into the matrix 21
329
+ }
330
+ breakthru:
331
+ // Begin update the matching 20
332
+ if (verbose)
333
+ fprintf(stderr, "Breakthrough at node %d of %d!\n",q,t);
334
+ while (1)
335
+ {
336
+ j=col_mate[k];
337
+ col_mate[k]=l;
338
+ row_mate[l]=k;
339
+ if (verbose)
340
+ fprintf(stderr, "rematching col %d==row %d\n",l,k);
341
+ if (j<0)
342
+ break;
343
+ k=parent_row[j];
344
+ l=j;
345
+ }
346
+ // End update the matching 20
347
+ if (--unmatched==0)
348
+ goto done;
349
+ // Begin get ready for another stage 17
350
+ t=0;
351
+ for (l=0;l<n;l++)
352
+ {
353
+ parent_row[l]= -1;
354
+ slack[l]=INF;
355
+ }
356
+ for (k=0;k<m;k++)
357
+ if (col_mate[k]<0)
358
+ {
359
+ if (verbose)
360
+ fprintf(stderr, "node %d: unmatched row %d\n",t,k);
361
+ unchosen_row[t++]=k;
362
+ }
363
+ // End get ready for another stage 17
364
+ }
365
+ done:
366
+
367
+ // Begin doublecheck the solution 23
368
+ for (k=0;k<m;k++)
369
+ for (l=0;l<n;l++)
370
+ if (p->cost[k][l]<row_dec[k]-col_inc[l])
371
+ exit(0);
372
+ for (k=0;k<m;k++)
373
+ {
374
+ l=col_mate[k];
375
+ if (l<0 || p->cost[k][l]!=row_dec[k]-col_inc[l])
376
+ exit(0);
377
+ }
378
+ k=0;
379
+ for (l=0;l<n;l++)
380
+ if (col_inc[l])
381
+ k++;
382
+ if (k>m)
383
+ exit(0);
384
+ // End doublecheck the solution 23
385
+ // End Hungarian algorithm 18
386
+
387
+ for (i=0;i<m;++i)
388
+ {
389
+ p->assignment[i][col_mate[i]]=HUNGARIAN_ASSIGNED;
390
+ /*TRACE("%d - %d\n", i, col_mate[i]);*/
391
+ }
392
+ for (k=0;k<m;++k)
393
+ {
394
+ for (l=0;l<n;++l)
395
+ {
396
+ /*TRACE("%d ",p->cost[k][l]-row_dec[k]+col_inc[l]);*/
397
+ p->cost[k][l]=p->cost[k][l]-row_dec[k]+col_inc[l];
398
+ }
399
+ /*TRACE("\n");*/
400
+ }
401
+ for (i=0;i<m;i++)
402
+ cost+=row_dec[i];
403
+ for (i=0;i<n;i++)
404
+ cost-=col_inc[i];
405
+ if (verbose)
406
+ fprintf(stderr, "Cost is %d\n",cost);
407
+
408
+
409
+ free(slack);
410
+ free(col_inc);
411
+ free(parent_row);
412
+ free(row_mate);
413
+ free(slack_row);
414
+ free(row_dec);
415
+ free(unchosen_row);
416
+ free(col_mate);
417
+ }
418
+
419
+
@@ -0,0 +1,74 @@
1
+ /********************************************************************
2
+ ********************************************************************
3
+ **
4
+ ** libhungarian by Cyrill Stachniss, 2004
5
+ **
6
+ **
7
+ ** Solving the Minimum Assignment Problem using the
8
+ ** Hungarian Method.
9
+ **
10
+ ** ** This file may be freely copied and distributed! **
11
+ **
12
+ ** Parts of the used code was originally provided by the
13
+ ** "Stanford GraphGase", but I made changes to this code.
14
+ ** As asked by the copyright node of the "Stanford GraphGase",
15
+ ** I hereby proclaim that this file are *NOT* part of the
16
+ ** "Stanford GraphGase" distrubition!
17
+ **
18
+ ** This file is distributed in the hope that it will be useful,
19
+ ** but WITHOUT ANY WARRANTY; without even the implied
20
+ ** warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
21
+ ** PURPOSE.
22
+ **
23
+ ********************************************************************
24
+ ********************************************************************/
25
+
26
+ #ifndef HUNGARIAN_H
27
+ #define HUNGARIAN_H
28
+
29
+ #ifdef __cplusplus
30
+ extern "C" {
31
+ #endif
32
+
33
+ #define HUNGARIAN_NOT_ASSIGNED 0
34
+ #define HUNGARIAN_ASSIGNED 1
35
+
36
+ #define HUNGARIAN_MODE_MINIMIZE_COST 0
37
+ #define HUNGARIAN_MODE_MAXIMIZE_UTIL 1
38
+
39
+ typedef struct {
40
+ int num_rows;
41
+ int num_cols;
42
+ int** cost;
43
+ int** assignment;
44
+ } hungarian_problem_t;
45
+
46
+ /** This method initialize the hungarian_problem structure and init
47
+ * the cost matrices (missing lines or columns are filled with 0).
48
+ * It returns the size of the quadratic(!) assignment matrix. **/
49
+ int hungarian_init(hungarian_problem_t* p,
50
+ int** cost_matrix,
51
+ int rows,
52
+ int cols,
53
+ int mode);
54
+
55
+ /** Free the memory allocated by init. **/
56
+ void hungarian_free(hungarian_problem_t* p);
57
+
58
+ /** This method computes the optimal assignment. **/
59
+ void hungarian_solve(hungarian_problem_t* p);
60
+
61
+ /** Print the computed optimal assignment. **/
62
+ void hungarian_print_assignment(hungarian_problem_t* p);
63
+
64
+ /** Print the cost matrix. **/
65
+ void hungarian_print_costmatrix(hungarian_problem_t* p);
66
+
67
+ /** Print cost matrix and assignment matrix. **/
68
+ void hungarian_print_status(hungarian_problem_t* p);
69
+
70
+ #ifdef __cplusplus
71
+ }
72
+ #endif
73
+
74
+ #endif
@@ -0,0 +1,83 @@
1
+ /********************************************************************
2
+ ********************************************************************
3
+ **
4
+ ** libhungarian by Cyrill Stachniss, 2004
5
+ **
6
+ **
7
+ ** Solving the Minimum Assignment Problem using the
8
+ ** Hungarian Method.
9
+ **
10
+ ** ** This file may be freely copied and distributed! **
11
+ **
12
+ ** Parts of the used code was originally provided by the
13
+ ** "Stanford GraphGase", but I made changes to this code.
14
+ ** As asked by the copyright node of the "Stanford GraphGase",
15
+ ** I hereby proclaim that this file are *NOT* part of the
16
+ ** "Stanford GraphGase" distrubition!
17
+ **
18
+ ** This file is distributed in the hope that it will be useful,
19
+ ** but WITHOUT ANY WARRANTY; without even the implied
20
+ ** warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
21
+ ** PURPOSE.
22
+ **
23
+ ********************************************************************
24
+ ********************************************************************/
25
+
26
+
27
+ #include <stdio.h>
28
+ #include <stdlib.h>
29
+ #include "hungarian.h"
30
+
31
+ int** array_to_matrix(int* m, int rows, int cols) {
32
+ int i,j;
33
+ int** r;
34
+ r = (int**)calloc(rows,sizeof(int*));
35
+ for(i=0;i<rows;i++)
36
+ {
37
+ r[i] = (int*)calloc(cols,sizeof(int));
38
+ for(j=0;j<cols;j++)
39
+ r[i][j] = m[i*cols+j];
40
+ }
41
+ return r;
42
+ }
43
+
44
+
45
+ int main() {
46
+
47
+ hungarian_problem_t p;
48
+
49
+ /* an example cost matrix */
50
+ int r[4*3] = { 100, 1, 1,
51
+ 100, 2, 2,
52
+ 1, 0, 0,
53
+ 0, 2, 0 };
54
+ int** m = array_to_matrix(r,4,3);
55
+
56
+ /* initialize the gungarian_problem using the cost matrix*/
57
+ int matrix_size = hungarian_init(&p, m , 4,3, HUNGARIAN_MODE_MINIMIZE_COST) ;
58
+
59
+ fprintf(stderr, "assignement matrix has a now a size %d rows and %d columns.\n\n", matrix_size,matrix_size);
60
+
61
+ /* some output */
62
+ fprintf(stderr, "cost-matrix:");
63
+ hungarian_print_costmatrix(&p);
64
+
65
+ /* solve the assignement problem */
66
+ hungarian_solve(&p);
67
+
68
+ /* some output */
69
+ fprintf(stderr, "assignment:");
70
+ hungarian_print_assignment(&p);
71
+
72
+ /* free used memory */
73
+ hungarian_free(&p);
74
+
75
+ int idx;
76
+ for (idx=0; idx < 4; idx+=1) {
77
+ free(m[idx]);
78
+ }
79
+ free(m);
80
+
81
+ return 0;
82
+ }
83
+
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+
3
+ require_relative './lib/hungarian_algorithm_c/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "hungarian_algorithm_c"
7
+ spec.version = HungarianAlgorithmC::VERSION
8
+ spec.authors = ["Syed Humza Shah"]
9
+ spec.email = ["humza.shah@deliveroo.co.uk"]
10
+
11
+ spec.summary = 'Evaluates the Hungarian algorithm in C'
12
+ spec.description = 'A Ruby gem that evaluates the Hungarian algorithm in C'
13
+ spec.homepage = 'https://github.com/deliveroo/hungarian_algorithm_c'
14
+ spec.license = "MIT"
15
+
16
+ spec.require_paths = ["lib"]
17
+
18
+ all_files = `git ls-files -z`.split("\x0")
19
+ test_file_regex = %r{^(test|spec|features)/}
20
+ spec.files = all_files.reject { |f| f.match(test_file_regex) }
21
+ spec.test_files = all_files.select { |f| f.match(test_file_regex) }
22
+
23
+ spec.has_rdoc = false
24
+ spec.extensions = %w[ext/hungarian_algorithm_c/extconf.rb]
25
+
26
+ spec.required_ruby_version = '>= 2.0.0'
27
+
28
+ spec.add_development_dependency 'bundler'
29
+ spec.add_development_dependency 'rspec'
30
+ spec.add_development_dependency 'rake-compiler'
31
+ end
@@ -0,0 +1,22 @@
1
+ require 'hungarian_algorithm_c.so'
2
+
3
+ module HungarianAlgorithmC
4
+ class << self
5
+ def find_pairings(array)
6
+ validate!(array)
7
+ pairs(array.flatten, array.size)
8
+ end
9
+
10
+ def validate!(array)
11
+ return if rectangular?(array)
12
+ raise ArgumentError.new('array must be rectangular')
13
+ end
14
+
15
+ def rectangular?(array)
16
+ row_size = array.size
17
+ array.all? { |column| row_size == column.size }
18
+ end
19
+
20
+ private :pairs, :validate!, :rectangular?
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module HungarianAlgorithmC
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,64 @@
1
+ require_relative './spec_helper'
2
+ require_relative '../lib/hungarian_algorithm_c'
3
+
4
+ RSpec.describe HungarianAlgorithmC do
5
+ describe '.find_pairings' do
6
+ subject { described_class.find_pairings(matrix_with_costs) }
7
+
8
+ context 'unbalanced array / non-square matrix' do
9
+ let(:matrix_with_costs) { [1, 2] }
10
+
11
+ it 'raises ArgumentError' do
12
+ expect { subject }.to raise_error(ArgumentError)
13
+ end
14
+ end
15
+
16
+ context '2x2 array' do
17
+ let(:matrix_with_costs) { [
18
+ [4, 3],
19
+ [3, 0]
20
+ ] }
21
+
22
+ it 'should output minimum cost pairs' do
23
+ should match_array([
24
+ [1, 1],
25
+ [0, 0]
26
+ ])
27
+ end
28
+ end
29
+
30
+ context '3x3 array' do
31
+ let(:matrix_with_costs) { [
32
+ [2, 3, 3],
33
+ [3, 3, 2],
34
+ [3, 2, 3]
35
+ ] }
36
+
37
+ it 'should output minimum cost pairs' do
38
+ should match_array([
39
+ [0, 0],
40
+ [1, 2],
41
+ [2, 1]
42
+ ])
43
+ end
44
+ end
45
+
46
+ context '4x4 array' do
47
+ let(:matrix_with_costs) { [
48
+ [2, 3, 1, 3],
49
+ [10, 2, 90, 6],
50
+ [10, 3, 34, 4],
51
+ [11, 13, 15, 17]
52
+ ] }
53
+
54
+ it 'should output minimum cost pairs' do
55
+ should match_array([
56
+ [0, 2],
57
+ [1, 1],
58
+ [2, 3],
59
+ [3, 0]
60
+ ])
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,5 @@
1
+ RSpec.configure do |config|
2
+ config.color = true
3
+ config.tty = true
4
+ config.formatter = :documentation
5
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hungarian_algorithm_c
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Syed Humza Shah
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-05-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake-compiler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: A Ruby gem that evaluates the Hungarian algorithm in C
56
+ email:
57
+ - humza.shah@deliveroo.co.uk
58
+ executables: []
59
+ extensions:
60
+ - ext/hungarian_algorithm_c/extconf.rb
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - ".travis.yml"
65
+ - Gemfile
66
+ - README.md
67
+ - Rakefile
68
+ - ext/hungarian_algorithm_c/extconf.rb
69
+ - ext/hungarian_algorithm_c/hungarian_algorithm_c.c
70
+ - ext/hungarian_algorithm_c/libhungarian/Makefile
71
+ - ext/hungarian_algorithm_c/libhungarian/README.md
72
+ - ext/hungarian_algorithm_c/libhungarian/hungarian.c
73
+ - ext/hungarian_algorithm_c/libhungarian/hungarian.h
74
+ - ext/hungarian_algorithm_c/libhungarian/hungarian_test.c
75
+ - hungarian_algorithm_c.gemspec
76
+ - lib/hungarian_algorithm_c.rb
77
+ - lib/hungarian_algorithm_c/version.rb
78
+ - spec/hungarian_algorithm_c_spec.rb
79
+ - spec/spec_helper.rb
80
+ homepage: https://github.com/deliveroo/hungarian_algorithm_c
81
+ licenses:
82
+ - MIT
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 2.0.0
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 2.4.5.1
101
+ signing_key:
102
+ specification_version: 4
103
+ summary: Evaluates the Hungarian algorithm in C
104
+ test_files:
105
+ - spec/hungarian_algorithm_c_spec.rb
106
+ - spec/spec_helper.rb
107
+ has_rdoc: false