Skip to content

Commit 6f84ace

Browse files
✨ feat: Settle on API.
1 parent e0ad468 commit 6f84ace

23 files changed

+189
-42
lines changed

README.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
[@aureooms/js-maximum-matching](https://aureooms.github.io/js-maximum-matching)
1+
:cherry_blossom: [@aureooms/js-maximum-matching](https://aureooms.github.io/js-maximum-matching)
22
==
33

4-
Graphs and networks code bricks for JavaScript.
5-
Parents are
6-
[@aureooms/js-algorithms](https://github.com/aureooms/js-algorithms)
7-
and
8-
[@aureooms/js-data-structures](https://github.com/aureooms/js-data-structures).
4+
Maximum matching algorithms for JavaScript.
5+
Parent is [@aureooms/js-gn](https://github.com/aureooms/js-gn).
6+
See [docs](https://aureooms.github.io/js-maximum-matching/index.html).
7+
8+
```js
9+
import maximumMatching, {iter} from '@aureooms/js-maximum-matching';
10+
const edges = [[1, 2, 10], [2, 3, 11]] ;
11+
const matching = maximumMatching(edges) ; // [-1, -1, 3, 2]
12+
iter(matching); // [ [2, 3] ]
13+
```
914

1015
[![License](https://img.shields.io/github/license/aureooms/js-maximum-matching.svg)](https://raw.githubusercontent.com/aureooms/js-maximum-matching/master/LICENSE)
1116
[![Version](https://img.shields.io/npm/v/@aureooms/js-maximum-matching.svg)](https://www.npmjs.org/package/@aureooms/js-maximum-matching)

src/cardinality/approx/bipartite.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import general from './general';
2+
3+
export default general;

src/cardinality/approx/general.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import general from '../opt/general';
2+
3+
const generalApprox = (edges, _eps) => general(edges);
4+
export default generalApprox;

src/cardinality/approx/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import bipartite from './bipartite';
2+
import general from './general';
3+
4+
export default general;
5+
6+
export {bipartite, general};

src/cardinality/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import approx from './approx';
2+
import opt from './opt';
3+
4+
export default opt;
5+
6+
export {approx, opt};

src/cardinality/opt/bipartite.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import general from './general';
2+
3+
export default general;

src/cardinality/opt/general.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import blossomNoChecks from '../../core/blossomNoChecks';
2+
3+
const general = (edges) => blossomNoChecks(edges, true);
4+
5+
export default general;

src/cardinality/opt/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import bipartite from './bipartite';
2+
import general from './general';
3+
4+
export default general;
5+
6+
export {bipartite, general};

src/exact/blossom.js renamed to src/core/blossom.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,14 @@ export default function blossom(CHECK_OPTIMUM, CHECK_DELTA) {
3232
// Check optimality of solution before returning; only works on integer weights.
3333
if (CHECK_OPTIMUM === undefined) CHECK_OPTIMUM = true;
3434

35-
const maxWeightMatching = function (edges, maxcardinality) {
35+
const maxWeightMatching = function (edges, maxcardinality = false) {
3636
let i;
3737
let j;
3838
let k;
3939
let p;
4040
let w;
4141
let length;
4242

43-
if (maxcardinality === undefined) maxcardinality = false;
44-
4543
/**
4644
*
4745
* Compute a maximum-weighted matching in the general undirected

src/core/blossomNoChecks.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import blossom from './blossom';
2+
3+
const blossomNoChecks = blossom(false, false);
4+
5+
export default blossomNoChecks;

src/core/index.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import blossom from './blossom';
2+
import blossomNoChecks from './blossomNoChecks';
3+
4+
/* eslint import/no-anonymous-default-export: [2, {"allowObject": true}] */
5+
export default {
6+
blossom,
7+
blossomNoChecks
8+
};
9+
10+
export {blossom, blossomNoChecks};

src/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import cardinality from './cardinality';
2+
import core from './core';
3+
import weight from './weight';
4+
import iter from './iter';
5+
6+
export default weight;
7+
8+
export {cardinality, core, weight, iter};

src/iter.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default function* iter(matching) {
2+
let i = 0;
3+
for (const j of matching) {
4+
// This takes care of j === -1
5+
if (i < j) yield [i, j];
6+
++i;
7+
}
8+
}

src/weight/approx/bipartite.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import general from './general';
2+
3+
export default general;

src/weight/approx/general.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import general from '../opt/general';
2+
3+
const generalApprox = (edges, _eps) => general(edges);
4+
export default generalApprox;

src/weight/approx/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import bipartite from './bipartite';
2+
import general from './general';
3+
4+
export default general;
5+
6+
export {bipartite, general};

src/weight/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import approx from './approx';
2+
import opt from './opt';
3+
4+
export default opt;
5+
6+
export {approx, opt};

src/weight/opt/bipartite.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import general from './general';
2+
3+
export default general;

src/weight/opt/general.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import blossomNoChecks from '../../core/blossomNoChecks';
2+
3+
const general = (edges) => blossomNoChecks(edges);
4+
5+
export default general;

src/weight/opt/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import bipartite from './bipartite';
2+
import general from './general';
3+
4+
export default general;
5+
6+
export {bipartite, general};

test/src/cardinality.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import test from 'ava';
2+
import {enumerate} from '@aureooms/js-itertools';
3+
4+
import maximumCardinalityMatching from '../../src/cardinality';
5+
import blossom from '../../src/core/blossom';
6+
7+
const macro = (t, algorithm, edges, expected) => {
8+
const input = edges.map((edge) => edge.slice()); // Deepcopy
9+
const result = algorithm(edges);
10+
t.deepEqual(expected, result);
11+
t.deepEqual(input, edges);
12+
};
13+
14+
macro.title = (title, algorithm, edges, expected) =>
15+
title ||
16+
`${algorithm.name}(${JSON.stringify(edges)}) = ${JSON.stringify(expected)}`;
17+
18+
const tests = {
19+
test14_maxcard: {
20+
edges: [
21+
[1, 2, 5],
22+
[2, 3, 11],
23+
[3, 4, 5]
24+
],
25+
expected: [-1, 2, 1, 4, 3]
26+
},
27+
28+
test16_negative_maxCardinality: {
29+
edges: [
30+
[1, 2, 2],
31+
[1, 3, -2],
32+
[2, 3, 1],
33+
[2, 4, -1],
34+
[3, 4, -6]
35+
],
36+
expected: [-1, 3, 4, 1, 2]
37+
}
38+
};
39+
40+
const btt = blossom(true, true);
41+
const bdflt = blossom();
42+
43+
const algorithms = [
44+
maximumCardinalityMatching,
45+
(edges) => btt(edges, true),
46+
(edges) => bdflt(edges, true)
47+
];
48+
49+
for (const [i, algorithm] of enumerate(algorithms))
50+
for (const [key, {edges, expected}] of Object.entries(tests))
51+
test(`${key} ${i}`, macro, algorithm, edges, expected);

test/src/readme.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import test from 'ava';
2+
3+
import maximumMatching, {iter} from '../../src';
4+
5+
test('README', (t) => {
6+
const edges = [
7+
[1, 2, 10],
8+
[2, 3, 11]
9+
];
10+
const matching = maximumMatching(edges); // [-1, -1, 3, 2]
11+
t.deepEqual([-1, -1, 3, 2], matching);
12+
t.deepEqual([[2, 3]], [...iter(matching)]);
13+
});

test/src/exact/blossom.js renamed to test/src/weight.js

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
import test from 'ava';
22
import {enumerate} from '@aureooms/js-itertools';
33

4-
import blossom from '../../../src/exact/blossom';
4+
import maximumMatching from '../../src';
5+
import blossom from '../../src/core/blossom';
56

6-
const macro = (t, algorithm, edges, maxCardinality, expected) => {
7+
const macro = (t, algorithm, edges, expected) => {
78
const input = edges.map((edge) => edge.slice()); // Deepcopy
8-
const result = algorithm(edges, maxCardinality);
9+
const result = algorithm(edges);
910
t.deepEqual(expected, result);
1011
t.deepEqual(input, edges);
1112
};
1213

13-
macro.title = (title, algorithm, edges, maxCardinality, expected) =>
14+
macro.title = (title, algorithm, edges, expected) =>
1415
title ||
15-
`${algorithm.name}(${JSON.stringify(
16-
edges
17-
)}, ${maxCardinality}) = ${JSON.stringify(expected)}`;
16+
`${algorithm.name}(${JSON.stringify(edges)}) = ${JSON.stringify(expected)}`;
1817

1918
const tests = {
2019
// Empty input graph
@@ -40,17 +39,6 @@ const tests = {
4039
expected: [-1, -1, 3, 2, -1]
4140
},
4241

43-
// Maximum cardinality
44-
test14_maxcard: {
45-
edges: [
46-
[1, 2, 5],
47-
[2, 3, 11],
48-
[3, 4, 5]
49-
],
50-
maxCardinality: true,
51-
expected: [-1, 2, 1, 4, 3]
52-
},
53-
5442
// Floating point weigths
5543
test15_float: {
5644
edges: [
@@ -71,20 +59,8 @@ const tests = {
7159
[2, 4, -1],
7260
[3, 4, -6]
7361
],
74-
maxCardinality: false,
7562
expected: [-1, 2, 1, -1, -1]
7663
},
77-
test16_negative_maxCardinality: {
78-
edges: [
79-
[1, 2, 2],
80-
[1, 3, -2],
81-
[2, 3, 1],
82-
[2, 4, -1],
83-
[3, 4, -6]
84-
],
85-
maxCardinality: true,
86-
expected: [-1, 3, 4, 1, 2]
87-
},
8864

8965
// Create S-blossom and use it for augmentation
9066
test20_sblossom_1: {
@@ -311,8 +287,15 @@ const tests = {
311287
}
312288
};
313289

314-
const algorithms = [blossom(true, true), blossom(false, false), blossom()];
290+
const btt = blossom(true, true);
291+
const bdflt = blossom();
292+
293+
const algorithms = [
294+
maximumMatching,
295+
(edges) => btt(edges),
296+
(edges) => bdflt(edges)
297+
];
315298

316299
for (const [i, algorithm] of enumerate(algorithms))
317-
for (const [key, {edges, maxCardinality, expected}] of Object.entries(tests))
318-
test(`${key} ${i}`, macro, algorithm, edges, maxCardinality, expected);
300+
for (const [key, {edges, expected}] of Object.entries(tests))
301+
test(`${key} ${i}`, macro, algorithm, edges, expected);

0 commit comments

Comments
 (0)