edmonds-blossom-esm 1.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.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 mattkrick
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # EdmondsBlossom
2
+ Edmond's maximum weighted matching algorithm (Blossom algorithm) in O(n^3)
3
+
4
+ ##Installation
5
+ `npm install edmonds-blossom --save`
6
+
7
+ ##How to use
8
+ ```
9
+ var blossom = require('./edmonds-blossom');
10
+ var data = [
11
+ [0, 1, 6],
12
+ [0, 2, 10],
13
+ [1, 2, 5]
14
+ ];
15
+ var results = blossom(data);
16
+ //results: [2,-1,0];
17
+ ```
18
+ The results are read as follows: index 0 is matchced up with index 2. Index 1 is not used. Index 2 is matched up with index 0 (redundant information).
19
+ ###What's the most basic example of a problem that this solves?
20
+ Let's assume I own a store & only sell items in groups of two.
21
+
22
+ - If I sell a PENCIL and an ERASER, I earn $3
23
+ - If I sell a PENCIL and a MARBLE , I earn $2
24
+ - If I sell a RULER and an ERASER, I earn $2
25
+
26
+ Each customer is willing to buy up to 1 item of each & I want to maximize profit. Therefore, selling #1 AND #2 is illegal because that would be 2 pencils. #1 AND #3 would is also illegal due to the erasers. So, legal moves are: only #1, only #2, only #3, or #2 AND #3. Since the last option has a profit of $4, I should do that.
27
+
28
+
package/app/blossom.js ADDED
@@ -0,0 +1,608 @@
1
+ /*Converted to JS from Python by Matt Krick. Original: http://jorisvr.nl/maximummatching.html*/
2
+ /*Fixed: all implicit global variables now properly declared with var (strict mode compatible)*/
3
+
4
+ module.exports = function (edges, maxCardinality) {
5
+ if (edges.length === 0) {
6
+ return edges;
7
+ }
8
+ var edmonds = new Edmonds(edges, maxCardinality);
9
+ return edmonds.maxWeightMatching();
10
+ };
11
+
12
+ var Edmonds = function (edges, maxCardinality) {
13
+ this.edges = edges;
14
+ this.maxCardinality = maxCardinality;
15
+ this.nEdge = edges.length;
16
+ this.init();
17
+ };
18
+
19
+ Edmonds.prototype.maxWeightMatching = function () {
20
+ for (var t = 0; t < this.nVertex; t++) {
21
+ this.label = filledArray(2 * this.nVertex, 0);
22
+ this.bestEdge = filledArray(2 * this.nVertex, -1);
23
+ this.blossomBestEdges = initArrArr(2 * this.nVertex);
24
+ this.allowEdge = filledArray(this.nEdge, false);
25
+ this.queue = [];
26
+ for (var v = 0; v < this.nVertex; v++) {
27
+ if (this.mate[v] === -1 && this.label[this.inBlossom[v]] === 0) {
28
+ this.assignLabel(v, 1, -1);
29
+ }
30
+ }
31
+ var augmented = false;
32
+ while (true) {
33
+ while (this.queue.length > 0 && !augmented) {
34
+ v = this.queue.pop();
35
+ for (var ii = 0; ii < this.neighbend[v].length; ii++) {
36
+ var p = this.neighbend[v][ii];
37
+ var k = ~~(p / 2);
38
+ var w = this.endpoint[p];
39
+ if (this.inBlossom[v] === this.inBlossom[w]) continue;
40
+ var kSlack;
41
+ if (!this.allowEdge[k]) {
42
+ kSlack = this.slack(k);
43
+ if (kSlack <= 0) {
44
+ this.allowEdge[k] = true;
45
+ }
46
+ }
47
+ if (this.allowEdge[k]) {
48
+ if (this.label[this.inBlossom[w]] === 0) {
49
+ this.assignLabel(w, 2, p ^ 1);
50
+ } else if (this.label[this.inBlossom[w]] === 1) {
51
+ var base = this.scanBlossom(v, w);
52
+ if (base >= 0) {
53
+ this.addBlossom(base, k);
54
+ } else {
55
+ this.augmentMatching(k);
56
+ augmented = true;
57
+ break;
58
+ }
59
+ } else if (this.label[w] === 0) {
60
+ this.label[w] = 2;
61
+ this.labelEnd[w] = p ^ 1;
62
+ }
63
+ } else if (this.label[this.inBlossom[w]] === 1) {
64
+ var b = this.inBlossom[v];
65
+ if (this.bestEdge[b] === -1 || kSlack < this.slack(this.bestEdge[b])) {
66
+ this.bestEdge[b] = k;
67
+ }
68
+ } else if (this.label[w] === 0) {
69
+ if (this.bestEdge[w] === -1 || kSlack < this.slack(this.bestEdge[w])) {
70
+ this.bestEdge[w] = k;
71
+ }
72
+ }
73
+ }
74
+ }
75
+ if (augmented) break;
76
+ var deltaType = -1;
77
+ var delta = 0;
78
+ var deltaEdge = -1;
79
+ var deltaBlossom = -1;
80
+ if (!this.maxCardinality) {
81
+ deltaType = 1;
82
+ delta = getMin(this.dualVar, 0, this.nVertex - 1);
83
+ }
84
+ for (v = 0; v < this.nVertex; v++) {
85
+ if (this.label[this.inBlossom[v]] === 0 && this.bestEdge[v] !== -1) {
86
+ var d = this.slack(this.bestEdge[v]);
87
+ if (deltaType === -1 || d < delta) {
88
+ delta = d;
89
+ deltaType = 2;
90
+ deltaEdge = this.bestEdge[v];
91
+ }
92
+ }
93
+ }
94
+ for (var b = 0; b < 2 * this.nVertex; b++) {
95
+ if (this.blossomParent[b] === -1 && this.label[b] === 1 && this.bestEdge[b] !== -1) {
96
+ kSlack = this.slack(this.bestEdge[b]);
97
+ d = kSlack / 2;
98
+ if (deltaType === -1 || d < delta) {
99
+ delta = d;
100
+ deltaType = 3;
101
+ deltaEdge = this.bestEdge[b];
102
+ }
103
+ }
104
+ }
105
+ for (b = this.nVertex; b < this.nVertex * 2; b++) {
106
+ if (this.blossomBase[b] >= 0 && this.blossomParent[b] === -1 && this.label[b] === 2 && (deltaType === -1 || this.dualVar[b] < delta)) {
107
+ delta = this.dualVar[b];
108
+ deltaType = 4;
109
+ deltaBlossom = b;
110
+ }
111
+ }
112
+ if (deltaType === -1) {
113
+ deltaType = 1;
114
+ delta = Math.max(0, getMin(this.dualVar, 0, this.nVertex - 1));
115
+ }
116
+ for (v = 0; v < this.nVertex; v++) {
117
+ var curLabel = this.label[this.inBlossom[v]];
118
+ if (curLabel === 1) {
119
+ this.dualVar[v] -= delta;
120
+ } else if (curLabel === 2) {
121
+ this.dualVar[v] += delta;
122
+ }
123
+ }
124
+ for (b = this.nVertex; b < this.nVertex * 2; b++) {
125
+ if (this.blossomBase[b] >= 0 && this.blossomParent[b] === -1) {
126
+ if (this.label[b] === 1) {
127
+ this.dualVar[b] += delta;
128
+ } else if (this.label[b] === 2) {
129
+ this.dualVar[b] -= delta;
130
+ }
131
+ }
132
+ }
133
+ if (deltaType === 1) {
134
+ break;
135
+ } else if (deltaType === 2) {
136
+ this.allowEdge[deltaEdge] = true;
137
+ var i = this.edges[deltaEdge][0];
138
+ var j = this.edges[deltaEdge][1];
139
+ var wt = this.edges[deltaEdge][2];
140
+ if (this.label[this.inBlossom[i]] === 0) {
141
+ i = i ^ j;
142
+ j = j ^ i;
143
+ i = i ^ j;
144
+ }
145
+ this.queue.push(i);
146
+ } else if (deltaType === 3) {
147
+ this.allowEdge[deltaEdge] = true;
148
+ i = this.edges[deltaEdge][0];
149
+ j = this.edges[deltaEdge][1];
150
+ wt = this.edges[deltaEdge][2];
151
+ this.queue.push(i);
152
+ } else if (deltaType === 4) {
153
+ this.expandBlossom(deltaBlossom, false);
154
+ }
155
+ }
156
+ if (!augmented) break;
157
+ for (b = this.nVertex; b < this.nVertex * 2; b++) {
158
+ if (this.blossomParent[b] === -1 && this.blossomBase[b] >= 0 && this.label[b] === 1 && this.dualVar[b] === 0) {
159
+ this.expandBlossom(b, true);
160
+ }
161
+ }
162
+ }
163
+ for (v = 0; v < this.nVertex; v++) {
164
+ if (this.mate[v] >= 0) {
165
+ this.mate[v] = this.endpoint[this.mate[v]];
166
+ }
167
+ }
168
+ for (v = 0; v < this.nVertex; v++) {
169
+ }
170
+ return this.mate;
171
+ };
172
+
173
+ Edmonds.prototype.slack = function (k) {
174
+ var i = this.edges[k][0];
175
+ var j = this.edges[k][1];
176
+ var wt = this.edges[k][2];
177
+ return this.dualVar[i] + this.dualVar[j] - 2 * wt;
178
+ };
179
+
180
+ Edmonds.prototype.blossomLeaves = function (b) {
181
+ if (b < this.nVertex) {
182
+ return [b];
183
+ }
184
+ var leaves = [];
185
+ var childList = this.blossomChilds[b];
186
+ for (var t = 0; t < childList.length; t++) {
187
+ if (childList[t] <= this.nVertex) {
188
+ leaves.push(childList[t]);
189
+ } else {
190
+ var leafList = this.blossomLeaves(childList[t]);
191
+ for (var v = 0; v < leafList.length; v++) {
192
+ leaves.push(leafList[v]);
193
+ }
194
+ }
195
+ }
196
+ return leaves;
197
+ };
198
+
199
+ Edmonds.prototype.assignLabel = function (w, t, p) {
200
+ var b = this.inBlossom[w];
201
+ this.label[w] = this.label[b] = t;
202
+ this.labelEnd[w] = this.labelEnd[b] = p;
203
+ this.bestEdge[w] = this.bestEdge[b] = -1;
204
+ if (t === 1) {
205
+ this.queue.push.apply(this.queue, this.blossomLeaves(b));
206
+ } else if (t === 2) {
207
+ var base = this.blossomBase[b];
208
+ this.assignLabel(this.endpoint[this.mate[base]], 1, this.mate[base] ^ 1);
209
+ }
210
+ };
211
+
212
+ Edmonds.prototype.scanBlossom = function (v, w) {
213
+ var path = [];
214
+ var base = -1;
215
+ while (v !== -1 || w !== -1) {
216
+ var b = this.inBlossom[v];
217
+ if ((this.label[b] & 4)) {
218
+ base = this.blossomBase[b];
219
+ break;
220
+ }
221
+ path.push(b);
222
+ this.label[b] = 5;
223
+ if (this.labelEnd[b] === -1) {
224
+ v = -1;
225
+ } else {
226
+ v = this.endpoint[this.labelEnd[b]];
227
+ b = this.inBlossom[v];
228
+ v = this.endpoint[this.labelEnd[b]];
229
+ }
230
+ if (w !== -1) {
231
+ v = v ^ w;
232
+ w = w ^ v;
233
+ v = v ^ w;
234
+ }
235
+ }
236
+ for (var ii = 0; ii < path.length; ii++) {
237
+ var bb = path[ii];
238
+ this.label[bb] = 1;
239
+ }
240
+ return base;
241
+ };
242
+
243
+ Edmonds.prototype.addBlossom = function (base, k) {
244
+ var v = this.edges[k][0];
245
+ var w = this.edges[k][1];
246
+ var wt = this.edges[k][2];
247
+ var bb = this.inBlossom[base];
248
+ var bv = this.inBlossom[v];
249
+ var bw = this.inBlossom[w];
250
+ var b = this.unusedBlossoms.pop();
251
+ this.blossomBase[b] = base;
252
+ this.blossomParent[b] = -1;
253
+ this.blossomParent[bb] = b;
254
+ var path = this.blossomChilds[b] = [];
255
+ var endPs = this.blossomEndPs[b] = [];
256
+ while (bv !== bb) {
257
+ this.blossomParent[bv] = b;
258
+ path.push(bv);
259
+ endPs.push(this.labelEnd[bv]);
260
+ v = this.endpoint[this.labelEnd[bv]];
261
+ bv = this.inBlossom[v];
262
+ }
263
+ path.push(bb);
264
+ path.reverse();
265
+ endPs.reverse();
266
+ endPs.push((2 * k));
267
+ while (bw !== bb) {
268
+ this.blossomParent[bw] = b;
269
+ path.push(bw);
270
+ endPs.push(this.labelEnd[bw] ^ 1);
271
+ w = this.endpoint[this.labelEnd[bw]];
272
+ bw = this.inBlossom[w];
273
+ }
274
+ this.label[b] = 1;
275
+ this.labelEnd[b] = this.labelEnd[bb];
276
+ this.dualVar[b] = 0;
277
+ var leaves = this.blossomLeaves(b);
278
+ for (var ii = 0; ii < leaves.length; ii++) {
279
+ v = leaves[ii];
280
+ if (this.label[this.inBlossom[v]] === 2) {
281
+ this.queue.push(v);
282
+ }
283
+ this.inBlossom[v] = b;
284
+ }
285
+ var bestEdgeTo = filledArray(2 * this.nVertex, -1);
286
+ for (ii = 0; ii < path.length; ii++) {
287
+ bv = path[ii];
288
+ var nbLists;
289
+ if (this.blossomBestEdges[bv].length === 0) {
290
+ nbLists = [];
291
+ leaves = this.blossomLeaves(bv);
292
+ for (var x = 0; x < leaves.length; x++) {
293
+ v = leaves[x];
294
+ nbLists[x] = [];
295
+ for (var y = 0; y < this.neighbend[v].length; y++) {
296
+ var pp = this.neighbend[v][y];
297
+ nbLists[x].push(~~(pp / 2));
298
+ }
299
+ }
300
+ } else {
301
+ nbLists = [this.blossomBestEdges[bv]];
302
+ }
303
+ for (x = 0; x < nbLists.length; x++) {
304
+ var nbList = nbLists[x];
305
+ for (y = 0; y < nbList.length; y++) {
306
+ k = nbList[y];
307
+ var i = this.edges[k][0];
308
+ var j = this.edges[k][1];
309
+ wt = this.edges[k][2];
310
+ if (this.inBlossom[j] === b) {
311
+ i = i ^ j;
312
+ j = j ^ i;
313
+ i = i ^ j;
314
+ }
315
+ var bj = this.inBlossom[j];
316
+ if (bj !== b && this.label[bj] === 1 && (bestEdgeTo[bj] === -1 || this.slack(k) < this.slack(bestEdgeTo[bj]))) {
317
+ bestEdgeTo[bj] = k;
318
+ }
319
+ }
320
+ }
321
+ this.blossomBestEdges[bv] = [];
322
+ this.bestEdge[bv] = -1;
323
+ }
324
+ var be = [];
325
+ for (ii = 0; ii < bestEdgeTo.length; ii++) {
326
+ k = bestEdgeTo[ii];
327
+ if (k !== -1) {
328
+ be.push(k);
329
+ }
330
+ }
331
+ this.blossomBestEdges[b] = be;
332
+ this.bestEdge[b] = -1;
333
+ for (ii = 0; ii < this.blossomBestEdges[b].length; ii++) {
334
+ k = this.blossomBestEdges[b][ii];
335
+ if (this.bestEdge[b] === -1 || this.slack(k) < this.slack(this.bestEdge[b])) {
336
+ this.bestEdge[b] = k;
337
+ }
338
+ }
339
+ };
340
+
341
+ Edmonds.prototype.expandBlossom = function (b, endStage) {
342
+ for (var ii = 0; ii < this.blossomChilds[b].length; ii++) {
343
+ var s = this.blossomChilds[b][ii];
344
+ this.blossomParent[s] = -1;
345
+ if (s < this.nVertex) {
346
+ this.inBlossom[s] = s;
347
+ } else if (endStage && this.dualVar[s] === 0) {
348
+ this.expandBlossom(s, endStage);
349
+ } else {
350
+ var leaves = this.blossomLeaves(s);
351
+ for (var jj = 0; jj < leaves.length; jj++) {
352
+ var v = leaves[jj];
353
+ this.inBlossom[v] = s;
354
+ }
355
+ }
356
+ }
357
+ if (!endStage && this.label[b] === 2) {
358
+ var entryChild = this.inBlossom[this.endpoint[this.labelEnd[b] ^ 1]];
359
+ var j = this.blossomChilds[b].indexOf(entryChild);
360
+ var jStep, endpTrick;
361
+ if ((j & 1)) {
362
+ j -= this.blossomChilds[b].length;
363
+ jStep = 1;
364
+ endpTrick = 0;
365
+ } else {
366
+ jStep = -1;
367
+ endpTrick = 1;
368
+ }
369
+ var p = this.labelEnd[b];
370
+ while (j !== 0) {
371
+ this.label[this.endpoint[p ^ 1]] = 0;
372
+ this.label[this.endpoint[pIndex(this.blossomEndPs[b], j - endpTrick) ^ endpTrick ^ 1]] = 0;
373
+ this.assignLabel(this.endpoint[p ^ 1], 2, p);
374
+ this.allowEdge[~~(pIndex(this.blossomEndPs[b], j - endpTrick) / 2)] = true;
375
+ j += jStep;
376
+ p = pIndex(this.blossomEndPs[b], j - endpTrick) ^ endpTrick;
377
+ this.allowEdge[~~(p / 2)] = true;
378
+ j += jStep;
379
+ }
380
+ var bv = pIndex(this.blossomChilds[b], j);
381
+ this.label[this.endpoint[p ^ 1]] = this.label[bv] = 2;
382
+ this.labelEnd[this.endpoint[p ^ 1]] = this.labelEnd[bv] = p;
383
+ this.bestEdge[bv] = -1;
384
+ j += jStep;
385
+ while (pIndex(this.blossomChilds[b], j) !== entryChild) {
386
+ bv = pIndex(this.blossomChilds[b], j);
387
+ if (this.label[bv] === 1) {
388
+ j += jStep;
389
+ continue;
390
+ }
391
+ leaves = this.blossomLeaves(bv);
392
+ var leafV = -1;
393
+ for (ii = 0; ii < leaves.length; ii++) {
394
+ v = leaves[ii];
395
+ if (this.label[v] !== 0) { leafV = v; break; }
396
+ }
397
+ if (leafV !== -1) {
398
+ v = leafV;
399
+ this.label[v] = 0;
400
+ this.label[this.endpoint[this.mate[this.blossomBase[bv]]]] = 0;
401
+ this.assignLabel(v, 2, this.labelEnd[v]);
402
+ }
403
+ j += jStep;
404
+ }
405
+ }
406
+ this.label[b] = this.labelEnd[b] = -1;
407
+ this.blossomEndPs[b] = this.blossomChilds[b] = [];
408
+ this.blossomBase[b] = -1;
409
+ this.blossomBestEdges[b] = [];
410
+ this.bestEdge[b] = -1;
411
+ this.unusedBlossoms.push(b);
412
+ };
413
+
414
+ Edmonds.prototype.augmentBlossom = function (b, v) {
415
+ var i, j;
416
+ var t = v;
417
+ while (this.blossomParent[t] !== b) {
418
+ t = this.blossomParent[t];
419
+ }
420
+ if (t > this.nVertex) {
421
+ this.augmentBlossom(t, v);
422
+ }
423
+ i = j = this.blossomChilds[b].indexOf(t);
424
+ var jStep, endpTrick;
425
+ if ((i & 1)) {
426
+ j -= this.blossomChilds[b].length;
427
+ jStep = 1;
428
+ endpTrick = 0;
429
+ } else {
430
+ jStep = -1;
431
+ endpTrick = 1;
432
+ }
433
+ while (j !== 0) {
434
+ j += jStep;
435
+ t = pIndex(this.blossomChilds[b], j);
436
+ var p = pIndex(this.blossomEndPs[b], j - endpTrick) ^ endpTrick;
437
+ if (t >= this.nVertex) {
438
+ this.augmentBlossom(t, this.endpoint[p]);
439
+ }
440
+ j += jStep;
441
+ t = pIndex(this.blossomChilds[b], j);
442
+ if (t >= this.nVertex) {
443
+ this.augmentBlossom(t, this.endpoint[p ^ 1]);
444
+ }
445
+ this.mate[this.endpoint[p]] = p ^ 1;
446
+ this.mate[this.endpoint[p ^ 1]] = p;
447
+ }
448
+ this.blossomChilds[b] = this.blossomChilds[b].slice(i).concat(this.blossomChilds[b].slice(0, i));
449
+ this.blossomEndPs[b] = this.blossomEndPs[b].slice(i).concat(this.blossomEndPs[b].slice(0, i));
450
+ this.blossomBase[b] = this.blossomBase[this.blossomChilds[b][0]];
451
+ };
452
+
453
+ Edmonds.prototype.augmentMatching = function (k) {
454
+ var v = this.edges[k][0];
455
+ var w = this.edges[k][1];
456
+ for (var ii = 0; ii < 2; ii++) {
457
+ var s, p;
458
+ if (ii === 0) {
459
+ s = v;
460
+ p = 2 * k + 1;
461
+ } else {
462
+ s = w;
463
+ p = 2 * k;
464
+ }
465
+ while (true) {
466
+ var bs = this.inBlossom[s];
467
+ if (bs >= this.nVertex) {
468
+ this.augmentBlossom(bs, s);
469
+ }
470
+ this.mate[s] = p;
471
+ if (this.labelEnd[bs] === -1) break;
472
+ var t = this.endpoint[this.labelEnd[bs]];
473
+ var bt = this.inBlossom[t];
474
+ s = this.endpoint[this.labelEnd[bt]];
475
+ var jj = this.endpoint[this.labelEnd[bt] ^ 1];
476
+ if (bt >= this.nVertex) {
477
+ this.augmentBlossom(bt, jj);
478
+ }
479
+ this.mate[jj] = this.labelEnd[bt];
480
+ p = this.labelEnd[bt] ^ 1;
481
+ }
482
+ }
483
+ };
484
+
485
+ Edmonds.prototype.init = function () {
486
+ this.nVertexInit();
487
+ this.maxWeightInit();
488
+ this.endpointInit();
489
+ this.neighbendInit();
490
+ this.mate = filledArray(this.nVertex, -1);
491
+ this.label = filledArray(2 * this.nVertex, 0);
492
+ this.labelEnd = filledArray(2 * this.nVertex, -1);
493
+ this.inBlossomInit();
494
+ this.blossomParent = filledArray(2 * this.nVertex, -1);
495
+ this.blossomChilds = initArrArr(2 * this.nVertex);
496
+ this.blossomBaseInit();
497
+ this.blossomEndPs = initArrArr(2 * this.nVertex);
498
+ this.bestEdge = filledArray(2 * this.nVertex, -1);
499
+ this.blossomBestEdges = initArrArr(2 * this.nVertex);
500
+ this.unusedBlossomsInit();
501
+ this.dualVarInit();
502
+ this.allowEdge = filledArray(this.nEdge, false);
503
+ this.queue = [];
504
+ };
505
+
506
+ Edmonds.prototype.blossomBaseInit = function () {
507
+ var base = [];
508
+ for (var i = 0; i < this.nVertex; i++) {
509
+ base[i] = i;
510
+ }
511
+ var negs = filledArray(this.nVertex, -1);
512
+ this.blossomBase = base.concat(negs);
513
+ };
514
+
515
+ Edmonds.prototype.dualVarInit = function () {
516
+ var mw = filledArray(this.nVertex, this.maxWeight);
517
+ var zeros = filledArray(this.nVertex, 0);
518
+ this.dualVar = mw.concat(zeros);
519
+ };
520
+
521
+ Edmonds.prototype.unusedBlossomsInit = function () {
522
+ var i, unusedBlossoms = [];
523
+ for (i = this.nVertex; i < 2 * this.nVertex; i++) {
524
+ unusedBlossoms.push(i);
525
+ }
526
+ this.unusedBlossoms = unusedBlossoms;
527
+ };
528
+
529
+ Edmonds.prototype.inBlossomInit = function () {
530
+ var i, inBlossom = [];
531
+ for (i = 0; i < this.nVertex; i++) {
532
+ inBlossom[i] = i;
533
+ }
534
+ this.inBlossom = inBlossom;
535
+ };
536
+
537
+ Edmonds.prototype.neighbendInit = function () {
538
+ var k, i, j;
539
+ var neighbend = initArrArr(this.nVertex);
540
+ for (k = 0; k < this.nEdge; k++) {
541
+ i = this.edges[k][0];
542
+ j = this.edges[k][1];
543
+ neighbend[i].push(2 * k + 1);
544
+ neighbend[j].push(2 * k);
545
+ }
546
+ this.neighbend = neighbend;
547
+ };
548
+
549
+ Edmonds.prototype.endpointInit = function () {
550
+ var p;
551
+ var endpoint = [];
552
+ for (p = 0; p < 2 * this.nEdge; p++) {
553
+ endpoint[p] = this.edges[~~(p / 2)][p % 2];
554
+ }
555
+ this.endpoint = endpoint;
556
+ };
557
+
558
+ Edmonds.prototype.nVertexInit = function () {
559
+ var nVertex = 0;
560
+ for (var k = 0; k < this.nEdge; k++) {
561
+ var i = this.edges[k][0];
562
+ var j = this.edges[k][1];
563
+ if (i >= nVertex) nVertex = i + 1;
564
+ if (j >= nVertex) nVertex = j + 1;
565
+ }
566
+ this.nVertex = nVertex;
567
+ };
568
+
569
+ Edmonds.prototype.maxWeightInit = function () {
570
+ var maxWeight = 0;
571
+ for (var k = 0; k < this.nEdge; k++) {
572
+ var weight = this.edges[k][2];
573
+ if (weight > maxWeight) {
574
+ maxWeight = weight;
575
+ }
576
+ }
577
+ this.maxWeight = maxWeight;
578
+ };
579
+
580
+ function filledArray(len, fill) {
581
+ var i, newArray = [];
582
+ for (i = 0; i < len; i++) {
583
+ newArray[i] = fill;
584
+ }
585
+ return newArray;
586
+ }
587
+
588
+ function initArrArr(len) {
589
+ var arr = [];
590
+ for (var i = 0; i < len; i++) {
591
+ arr[i] = [];
592
+ }
593
+ return arr;
594
+ }
595
+
596
+ function getMin(arr, start, end) {
597
+ var min = Infinity;
598
+ for (var i = start; i <= end; i++) {
599
+ if (arr[i] < min) {
600
+ min = arr[i];
601
+ }
602
+ }
603
+ return min;
604
+ }
605
+
606
+ function pIndex(arr, idx) {
607
+ return idx < 0 ? arr[arr.length + idx] : arr[idx];
608
+ }
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "edmonds-blossom-esm",
3
+ "version": "1.0.1",
4
+ "description": "Edmond's weighted maximum matching algorithm (Blossom algorithm) - fixed for strict mode and ES modules compatibility",
5
+ "main": "app/blossom.js",
6
+ "keywords": [
7
+ "graph",
8
+ "edmonds",
9
+ "maximum matching",
10
+ "blossom",
11
+ "swiss",
12
+ "tournament"
13
+ ],
14
+ "author": "Guillaume Fournier",
15
+ "license": "MIT",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/Kobiyami/EdmondsBlossom.git"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/Kobiyami/EdmondsBlossom/issues"
22
+ },
23
+ "homepage": "https://github.com/Kobiyami/EdmondsBlossom#readme",
24
+ "scripts": {
25
+ "test": "jasmine-node spec"
26
+ },
27
+ "devDependencies": {
28
+ "jasmine-node": "^1.16.2"
29
+ }
30
+ }
@@ -0,0 +1,165 @@
1
+ describe("Blossom", function () {
2
+ var blossom = require('../app/blossom.js');
3
+ it('should return an empty array if no array is given', function () {
4
+ var data = [];
5
+ var correctAnswer = [];
6
+ var result = blossom(data);
7
+ expect(result).toEqual(correctAnswer);
8
+ });
9
+ it('should handle a single edge', function () {
10
+ var data = [[0, 1, 1]];
11
+ var correctAnswer = [1, 0];
12
+ var result = blossom(data);
13
+ expect(result).toEqual(correctAnswer);
14
+ });
15
+ it('should handle 2 edges', function () {
16
+ var data = [[1, 2, 10], [2, 3, 11]];
17
+ var correctAnswer = [-1, -1, 3, 2];
18
+ var result = blossom(data);
19
+ expect(result).toEqual(correctAnswer);
20
+ });
21
+ it('should handle 3 edges', function () {
22
+ var data = [[1, 2, 5], [2, 3, 11], [3, 4, 5]];
23
+ var correctAnswer = [-1, -1, 3, 2, -1];
24
+ var result = blossom(data);
25
+ expect(result).toEqual(correctAnswer);
26
+ });
27
+ it('should handle maximum cardinality', function () {
28
+ var data = [[1, 2, 5], [2, 3, 11], [3, 4, 5]];
29
+ var correctAnswer = [-1, 2, 1, 4, 3];
30
+ var result = blossom(data, true);
31
+ expect(result).toEqual(correctAnswer);
32
+ });
33
+ it('should handle floating point weights', function () {
34
+ var data = [[1, 2, Math.PI], [2, 3, Math.exp(1)], [1, 3, 3.0], [1, 4, Math.sqrt(2.0)]];
35
+ var correctAnswer = [-1, 4, 3, 2, 1];
36
+ var result = blossom(data);
37
+ expect(result).toEqual(correctAnswer);
38
+ });
39
+ it('should handle negative weights', function () {
40
+ var data = [[1, 2, 2], [1, 3, -2], [2, 3, 1], [2, 4, -1], [3, 4, -6]];
41
+ var correctAnswer = [-1, 2, 1, -1, -1];
42
+ var result = blossom(data);
43
+ expect(result).toEqual(correctAnswer);
44
+ });
45
+ it('should handle negative weights with max cardinality', function () {
46
+ var data = [[1, 2, 2], [1, 3, -2], [2, 3, 1], [2, 4, -1], [3, 4, -6]];
47
+ var correctAnswer = [-1, 3, 4, 1, 2];
48
+ var result = blossom(data, true);
49
+ expect(result).toEqual(correctAnswer);
50
+ });
51
+ it('should create S-blossom and use it for augmentation (1)', function () {
52
+ var data = [[1, 2, 8], [1, 3, 9], [2, 3, 10], [3, 4, 7]];
53
+ var correctAnswer = [-1, 2, 1, 4, 3];
54
+
55
+ var result = blossom(data);
56
+ expect(result).toEqual(correctAnswer);
57
+ });
58
+ it('should create S-blossom and use it for augmentation (2)', function () {
59
+ var data = [[1, 2, 8], [1, 3, 9], [2, 3, 10], [3, 4, 7], [1, 6, 5], [4, 5, 6]];
60
+ var correctAnswer = [-1, 6, 3, 2, 5, 4, 1];
61
+ var result = blossom(data);
62
+ expect(result).toEqual(correctAnswer);
63
+ });
64
+ it('should create S-blossom, relabel as T-blossom, use for augmentation (1)', function () {
65
+ var data = [[1, 2, 9], [1, 3, 8], [2, 3, 10], [1, 4, 5], [4, 5, 4], [1, 6, 3]];
66
+ var correctAnswer = [-1, 6, 3, 2, 5, 4, 1];
67
+ var result = blossom(data);
68
+ expect(result).toEqual(correctAnswer);
69
+ });
70
+ it('should create S-blossom, relabel as T-blossom, use for augmentation (2)', function () {
71
+ var data = [[1, 2, 9], [1, 3, 8], [2, 3, 10], [1, 4, 5], [4, 5, 3], [1, 6, 4]];
72
+ var correctAnswer = [-1, 6, 3, 2, 5, 4, 1];
73
+ var result = blossom(data);
74
+ expect(result).toEqual(correctAnswer);
75
+ });
76
+ it('should create S-blossom, relabel as T-blossom, use for augmentation (3)', function () {
77
+ var data = [[1, 2, 9], [1, 3, 8], [2, 3, 10], [1, 4, 5], [4, 5, 3], [3, 6, 4]];
78
+ var correctAnswer = [-1, 2, 1, 6, 5, 4, 3];
79
+ var result = blossom(data);
80
+ expect(result).toEqual(correctAnswer);
81
+ });
82
+ it('should create nested S-blossom, use for augmentation', function () {
83
+ var data = [[1, 2, 9], [1, 3, 9], [2, 3, 10], [2, 4, 8], [3, 5, 8], [4, 5, 10], [5, 6, 6]];
84
+ var correctAnswer = [-1, 3, 4, 1, 2, 6, 5];
85
+ var result = blossom(data);
86
+ expect(result).toEqual(correctAnswer);
87
+ });
88
+ it('should create S-blossom, relabel as S, include in nested S-blossom', function () {
89
+ var data = [[1, 2, 10], [1, 7, 10], [2, 3, 12], [3, 4, 20], [3, 5, 20], [4, 5, 25], [5, 6, 10], [6, 7, 10], [7, 8, 8]];
90
+ var correctAnswer = [-1, 2, 1, 4, 3, 6, 5, 8, 7];
91
+ var result = blossom(data);
92
+ expect(result).toEqual(correctAnswer);
93
+ });
94
+ it('should create nested S-blossom, augment, expand recursively', function () {
95
+ var data = [[1, 2, 8], [1, 3, 8], [2, 3, 10], [2, 4, 12], [3, 5, 12], [4, 5, 14], [4, 6, 12], [5, 7, 12], [6, 7, 14], [7, 8, 12]];
96
+ var correctAnswer = [-1, 2, 1, 5, 6, 3, 4, 8, 7];
97
+ var result = blossom(data);
98
+ expect(result).toEqual(correctAnswer);
99
+ });
100
+ it('should create S-blossom, relabel as T, expand', function () {
101
+ var data = [[1, 2, 23], [1, 5, 22], [1, 6, 15], [2, 3, 25], [3, 4, 22], [4, 5, 25], [4, 8, 14], [5, 7, 13]];
102
+ var correctAnswer = [-1, 6, 3, 2, 8, 7, 1, 5, 4];
103
+ var result = blossom(data);
104
+ expect(result).toEqual(correctAnswer);
105
+ });
106
+ it('should create nested S-blossom, relabel as T, expand', function () {
107
+ var data = [[1, 2, 19], [1, 3, 20], [1, 8, 8], [2, 3, 25], [2, 4, 18], [3, 5, 18], [4, 5, 13], [4, 7, 7], [5, 6, 7]];
108
+ var correctAnswer = [-1, 8, 3, 2, 7, 6, 5, 4, 1];
109
+ var result = blossom(data);
110
+ expect(result).toEqual(correctAnswer);
111
+ });
112
+ it('should create blossom, relabel as T in more than one way, expand, augment', function () {
113
+ var data = [ [1,2,45], [1,5,45], [2,3,50], [3,4,45], [4,5,50], [1,6,30], [3,9,35], [4,8,35], [5,7,26], [9,10,5] ];
114
+ var correctAnswer = [ -1, 6, 3, 2, 8, 7, 1, 5, 4, 10, 9 ];
115
+ var result = blossom(data);
116
+ expect(result).toEqual(correctAnswer);
117
+ });
118
+ it('should create blossom, relabel as T in more than one way, expand, augment (2)', function () {
119
+ var data = [ [1,2,45], [1,5,45], [2,3,50], [3,4,45], [4,5,50], [1,6,30], [3,9,35], [4,8,26], [5,7,40], [9,10,5] ];
120
+ var correctAnswer = [ -1, 6, 3, 2, 8, 7, 1, 5, 4, 10, 9 ];
121
+ var result = blossom(data);
122
+ expect(result).toEqual(correctAnswer);
123
+ });
124
+ it('should create blossom, relabel as T, expand such that a new least-slack S-to-free edge is produced, augment', function () {
125
+ var data = [ [1,2,45], [1,5,45], [2,3,50], [3,4,45], [4,5,50], [1,6,30], [3,9,35], [4,8,28], [5,7,26], [9,10,5] ];
126
+ var correctAnswer = [ -1, 6, 3, 2, 8, 7, 1, 5, 4, 10, 9 ];
127
+ var result = blossom(data);
128
+ expect(result).toEqual(correctAnswer);
129
+ });
130
+ it('should create nested blossom, relabel as T in more than one way, expand outer blossom such that inner blossom ends up on an augmenting path', function () {
131
+ var data = [ [1,2,45], [1,7,45], [2,3,50], [3,4,45], [4,5,95], [4,6,94], [5,6,94], [6,7,50], [1,8,30], [3,11,35], [5,9,36], [7,10,26], [11,12,5] ];
132
+ var correctAnswer = [ -1, 8, 3, 2, 6, 9, 4, 10, 1, 5, 7, 12, 11 ];
133
+ var result = blossom(data);
134
+ expect(result).toEqual(correctAnswer);
135
+ });
136
+ it('create nested S-blossom, relabel as S, expand recursively', function () {
137
+ var data = [ [1,2,40], [1,3,40], [2,3,60], [2,4,55], [3,5,55], [4,5,50], [1,8,15], [5,7,30], [7,6,10], [8,10,10], [4,9,30] ];
138
+ var correctAnswer = [ -1, 2, 1, 5, 9, 3, 7, 6, 10, 4, 8 ];
139
+ var result = blossom(data);
140
+ expect(result).toEqual(correctAnswer);
141
+ });
142
+ it('should test array structure of nbLists', function () {
143
+ var data = [[1, 2, 5], [1, 3, 6], [1, 4, 2], [2, 3, 4], [2, 4, 1], [3, 4, 3]];
144
+ var correctAnswer = [-1, 2, 1 ,4 ,3];
145
+ var result = blossom(data);
146
+ expect(result).toEqual(correctAnswer);
147
+ });
148
+ it('should test array structure of nbLists with floats', function () {
149
+ var data = [[1, 2, 47.2612], [1, 3, 46.9176], [2, 3, 49.3305], [1, 4, 44.7978], [2, 4, 49.1123], [2, 5, 51.1539], [4, 5, 50.5430], [2, 6, 48.2873], [3, 6, 47.7470], [4, 6, 46.8674], [5, 6, 48.8397]];
150
+ var correctAnswer = [-1, 3, 6, 1, 5, 4, 2];
151
+ var result = blossom(data);
152
+ expect(result).toEqual(correctAnswer);
153
+ });
154
+ it('should handle a simple zero-index love triangle', function() {
155
+ var data = [
156
+ [0, 1, 6],
157
+ [0, 2, 10],
158
+ [1, 2, 5]
159
+ ];
160
+ var correctAnswer = [2,-1,0];
161
+ var result = blossom(data);
162
+ expect(result).toEqual(correctAnswer);
163
+ })
164
+
165
+ });
@@ -0,0 +1,9 @@
1
+ {
2
+ "spec_dir": "spec",
3
+ "spec_files": [
4
+ "**/*[sS]pec.js"
5
+ ],
6
+ "helpers": [
7
+ "helpers/**/*.js"
8
+ ]
9
+ }