hungarian_algorithm_c 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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