@thi.ng/adjacency 2.5.12 → 2.5.13

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/floyd-warshall.js CHANGED
@@ -1,106 +1,84 @@
1
1
  import { outOfBounds } from "@thi.ng/errors/out-of-bounds";
2
- /**
3
- * Implementation of the Floyd-Warshall algorithm for finding _all_ shortest
4
- * paths in a directed graph, optionally with positive or negative edge weights.
5
- * A single execution of the algorithm will find the lengths (summed weights) of
6
- * shortest paths between all pairs of vertices.
7
- *
8
- * @remarks
9
- * The default cost function is topological distance (i.e. every edge has a
10
- * length/cost of 1).
11
- *
12
- * Paths & accumulated distances can be queried via {@link FloydWarshall.path}
13
- * and {@link FloydWarshall.distance}.
14
- *
15
- * This algorithm is quite memory hungry and requires `|V| * |V| * 8 bytes`,
16
- * i.e. ~8MB for a graph with 1000 nodes. If possible, use {@link BFS} to
17
- * perform individual shortest-path queries (rather than this global approach).
18
- *
19
- * Reference:
20
- * - https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm
21
- */
22
- export class FloydWarshall {
23
- dist;
24
- next;
25
- numV;
26
- /**
27
- * Instantiates and pre-computes all shortest paths in given `graph`. See
28
- * class comments for details.
29
- *
30
- * @param graph
31
- * @param cost
32
- */
33
- constructor(graph, cost = () => 1) {
34
- const numV = (this.numV = graph.numVertices());
35
- const dist = (this.dist = new Float32Array(numV * numV).fill(Infinity));
36
- const next = (this.next = new Int32Array(numV * numV).fill(-1));
37
- for (let [u, v] of graph.edges()) {
38
- const idx = u * numV + v;
39
- dist[idx] = cost(u, v);
40
- next[idx] = v;
41
- }
42
- for (let v = 0; v < numV; v++) {
43
- const idx = v * numV + v;
44
- dist[idx] = 0;
45
- next[idx] = v;
46
- }
47
- for (let k = 0; k < numV; k++) {
48
- for (let i = 0; i < numV; i++) {
49
- const idxIK = i * numV + k;
50
- for (let j = 0; j < numV; j++) {
51
- const idxIJ = i * numV + j;
52
- const idxKJ = k * numV + j;
53
- const minD = dist[idxIK] + dist[idxKJ];
54
- if (dist[idxIJ] > minD) {
55
- dist[idxIJ] = minD;
56
- next[idxIJ] = next[idxIK];
57
- }
58
- }
59
- }
60
- }
2
+ class FloydWarshall {
3
+ dist;
4
+ next;
5
+ numV;
6
+ /**
7
+ * Instantiates and pre-computes all shortest paths in given `graph`. See
8
+ * class comments for details.
9
+ *
10
+ * @param graph
11
+ * @param cost
12
+ */
13
+ constructor(graph, cost = () => 1) {
14
+ const numV = this.numV = graph.numVertices();
15
+ const dist = this.dist = new Float32Array(numV * numV).fill(Infinity);
16
+ const next = this.next = new Int32Array(numV * numV).fill(-1);
17
+ for (let [u, v] of graph.edges()) {
18
+ const idx = u * numV + v;
19
+ dist[idx] = cost(u, v);
20
+ next[idx] = v;
61
21
  }
62
- /**
63
- * Returns shortest distance between vertices `a` and `b`, or `undefined` if
64
- * no connecting path exists. Throws an error if either `a` or `b` are out
65
- * of bounds.
66
- *
67
- * @param a
68
- * @param b
69
- */
70
- distance(a, b) {
71
- this.ensurePair(a, b);
72
- return this.dist[a * this.numV + b];
22
+ for (let v = 0; v < numV; v++) {
23
+ const idx = v * numV + v;
24
+ dist[idx] = 0;
25
+ next[idx] = v;
73
26
  }
74
- /**
75
- * Returns iterator of vertex IDs of path between `a` and `b` (if any).
76
- * Throws an error if either `a` or `b` are out of bounds.
77
- *
78
- * @param a
79
- * @param b
80
- */
81
- *path(a, b) {
82
- this.ensurePair(a, b);
83
- const { next, numV } = this;
84
- if (next[a * numV + b] === -1)
85
- return;
86
- yield a;
87
- while (a !== b) {
88
- a = next[a * numV + b];
89
- yield a;
27
+ for (let k = 0; k < numV; k++) {
28
+ for (let i = 0; i < numV; i++) {
29
+ const idxIK = i * numV + k;
30
+ for (let j = 0; j < numV; j++) {
31
+ const idxIJ = i * numV + j;
32
+ const idxKJ = k * numV + j;
33
+ const minD = dist[idxIK] + dist[idxKJ];
34
+ if (dist[idxIJ] > minD) {
35
+ dist[idxIJ] = minD;
36
+ next[idxIJ] = next[idxIK];
37
+ }
90
38
  }
39
+ }
91
40
  }
92
- ensureIndex(id) {
93
- !(id >= 0 && id < this.numV) && outOfBounds(id);
94
- }
95
- ensurePair(a, b) {
96
- this.ensureIndex(a);
97
- this.ensureIndex(b);
41
+ }
42
+ /**
43
+ * Returns shortest distance between vertices `a` and `b`, or `undefined` if
44
+ * no connecting path exists. Throws an error if either `a` or `b` are out
45
+ * of bounds.
46
+ *
47
+ * @param a
48
+ * @param b
49
+ */
50
+ distance(a, b) {
51
+ this.ensurePair(a, b);
52
+ return this.dist[a * this.numV + b];
53
+ }
54
+ /**
55
+ * Returns iterator of vertex IDs of path between `a` and `b` (if any).
56
+ * Throws an error if either `a` or `b` are out of bounds.
57
+ *
58
+ * @param a
59
+ * @param b
60
+ */
61
+ *path(a, b) {
62
+ this.ensurePair(a, b);
63
+ const { next, numV } = this;
64
+ if (next[a * numV + b] === -1)
65
+ return;
66
+ yield a;
67
+ while (a !== b) {
68
+ a = next[a * numV + b];
69
+ yield a;
98
70
  }
71
+ }
72
+ ensureIndex(id) {
73
+ !(id >= 0 && id < this.numV) && outOfBounds(id);
74
+ }
75
+ ensurePair(a, b) {
76
+ this.ensureIndex(a);
77
+ this.ensureIndex(b);
78
+ }
99
79
  }
100
- /**
101
- * Factory function for {@link FloydWarshall}.
102
- *
103
- * @param graph
104
- * @param cost
105
- */
106
- export const floydWarshall = (graph, cost) => new FloydWarshall(graph, cost);
80
+ const floydWarshall = (graph, cost) => new FloydWarshall(graph, cost);
81
+ export {
82
+ FloydWarshall,
83
+ floydWarshall
84
+ };
package/list.js CHANGED
@@ -1,138 +1,141 @@
1
1
  import { __into, __invert, __toDot } from "./utils.js";
2
- export class AdjacencyList {
3
- adjacency = [];
4
- indegree = [];
5
- numE = 0;
6
- numV = 0;
7
- constructor(edges) {
8
- edges && __into(this, edges);
9
- }
10
- numEdges() {
11
- return this.numE;
12
- }
13
- numVertices() {
14
- return this.numV;
15
- }
16
- *vertices() {
17
- const { adjacency } = this;
18
- for (let i = 0, n = adjacency.length; i < n; i++) {
19
- if (adjacency[i])
20
- yield i;
21
- }
22
- }
23
- *edges() {
24
- const { adjacency } = this;
25
- for (let i = 0, n = adjacency.length; i < n; i++) {
26
- const vertex = adjacency[i];
27
- if (!vertex)
28
- continue;
29
- for (let j of vertex)
30
- yield [i, j];
31
- }
32
- }
33
- addVertex(id) {
34
- this.ensureVertexData(id);
35
- }
36
- removeVertex(id) {
37
- const { adjacency, indegree } = this;
38
- const vertex = adjacency[id];
39
- if (!vertex)
40
- return false;
41
- // remove outgoing
42
- while (vertex.length) {
43
- indegree[vertex.pop()]--;
44
- this.numE--;
45
- }
46
- delete adjacency[id];
47
- // remove incoming
48
- for (let i = 0, n = adjacency.length; i < n && indegree[id] > 0; i++) {
49
- const vertex = adjacency[i];
50
- if (!vertex)
51
- continue;
52
- while (vertex.includes(id))
53
- this.removeEdge(i, id);
54
- }
55
- this.numV--;
2
+ class AdjacencyList {
3
+ adjacency = [];
4
+ indegree = [];
5
+ numE = 0;
6
+ numV = 0;
7
+ constructor(edges) {
8
+ edges && __into(this, edges);
9
+ }
10
+ numEdges() {
11
+ return this.numE;
12
+ }
13
+ numVertices() {
14
+ return this.numV;
15
+ }
16
+ *vertices() {
17
+ const { adjacency } = this;
18
+ for (let i = 0, n = adjacency.length; i < n; i++) {
19
+ if (adjacency[i])
20
+ yield i;
21
+ }
22
+ }
23
+ *edges() {
24
+ const { adjacency } = this;
25
+ for (let i = 0, n = adjacency.length; i < n; i++) {
26
+ const vertex = adjacency[i];
27
+ if (!vertex)
28
+ continue;
29
+ for (let j of vertex)
30
+ yield [i, j];
31
+ }
32
+ }
33
+ addVertex(id) {
34
+ this.ensureVertexData(id);
35
+ }
36
+ removeVertex(id) {
37
+ const { adjacency, indegree } = this;
38
+ const vertex = adjacency[id];
39
+ if (!vertex)
40
+ return false;
41
+ while (vertex.length) {
42
+ indegree[vertex.pop()]--;
43
+ this.numE--;
44
+ }
45
+ delete adjacency[id];
46
+ for (let i = 0, n = adjacency.length; i < n && indegree[id] > 0; i++) {
47
+ const vertex2 = adjacency[i];
48
+ if (!vertex2)
49
+ continue;
50
+ while (vertex2.includes(id))
51
+ this.removeEdge(i, id);
52
+ }
53
+ this.numV--;
54
+ return true;
55
+ }
56
+ hasVertex(id) {
57
+ return !!this.adjacency[id];
58
+ }
59
+ addEdge(from, to) {
60
+ const vertex = this.ensureVertexData(from);
61
+ this.ensureVertexData(to);
62
+ vertex.push(to);
63
+ this.indegree[to]++;
64
+ this.numE++;
65
+ return true;
66
+ }
67
+ removeEdge(from, to) {
68
+ const vertex = this.adjacency[from];
69
+ if (vertex) {
70
+ const dest = vertex.indexOf(to);
71
+ if (dest >= 0) {
72
+ vertex.splice(dest, 1);
73
+ this.numE--;
74
+ this.indegree[to]--;
56
75
  return true;
57
- }
58
- hasVertex(id) {
59
- return !!this.adjacency[id];
60
- }
61
- addEdge(from, to) {
62
- const vertex = this.ensureVertexData(from);
63
- this.ensureVertexData(to);
64
- vertex.push(to);
65
- this.indegree[to]++;
66
- this.numE++;
67
- return true;
68
- }
69
- removeEdge(from, to) {
70
- const vertex = this.adjacency[from];
71
- if (vertex) {
72
- const dest = vertex.indexOf(to);
73
- if (dest >= 0) {
74
- vertex.splice(dest, 1);
75
- this.numE--;
76
- this.indegree[to]--;
77
- return true;
78
- }
79
- }
80
- return false;
81
- }
82
- hasEdge(from, to) {
83
- const vertex = this.adjacency[from];
84
- return vertex ? vertex.includes(to) : false;
85
- }
86
- degree(id, type = "out") {
87
- let degree = 0;
88
- const vertex = this.adjacency[id];
89
- if (vertex) {
90
- if (type !== "in")
91
- degree += vertex.length;
92
- if (type !== "out")
93
- degree += this.indegree[id];
94
- }
95
- return degree;
96
- }
97
- neighbors(id) {
98
- return [...(this.adjacency[id] || [])];
99
- }
100
- invert() {
101
- return __invert(new AdjacencyList(), this.edges());
102
- }
103
- toDot(ids) {
104
- return __toDot(this.edges(), false, ids);
105
- }
106
- toString() {
107
- const { adjacency } = this;
108
- const res = [];
109
- for (let i = 0, n = adjacency.length; i < n; i++) {
110
- if (adjacency[i]) {
111
- res.push(`${i}: [${[...adjacency[i]]
112
- .sort((a, b) => a - b)
113
- .join(", ")}]`);
114
- }
115
- }
116
- return res.join("\n");
117
- }
118
- ensureVertexData(id) {
119
- const vertex = this.adjacency[id];
120
- if (vertex)
121
- return vertex;
122
- this.numV++;
123
- this.indegree[id] = 0;
124
- return (this.adjacency[id] = []);
125
- }
76
+ }
77
+ }
78
+ return false;
79
+ }
80
+ hasEdge(from, to) {
81
+ const vertex = this.adjacency[from];
82
+ return vertex ? vertex.includes(to) : false;
83
+ }
84
+ degree(id, type = "out") {
85
+ let degree = 0;
86
+ const vertex = this.adjacency[id];
87
+ if (vertex) {
88
+ if (type !== "in")
89
+ degree += vertex.length;
90
+ if (type !== "out")
91
+ degree += this.indegree[id];
92
+ }
93
+ return degree;
94
+ }
95
+ neighbors(id) {
96
+ return [...this.adjacency[id] || []];
97
+ }
98
+ invert() {
99
+ return __invert(new AdjacencyList(), this.edges());
100
+ }
101
+ toDot(ids) {
102
+ return __toDot(this.edges(), false, ids);
103
+ }
104
+ toString() {
105
+ const { adjacency } = this;
106
+ const res = [];
107
+ for (let i = 0, n = adjacency.length; i < n; i++) {
108
+ if (adjacency[i]) {
109
+ res.push(
110
+ `${i}: [${[...adjacency[i]].sort((a, b) => a - b).join(", ")}]`
111
+ );
112
+ }
113
+ }
114
+ return res.join("\n");
115
+ }
116
+ ensureVertexData(id) {
117
+ const vertex = this.adjacency[id];
118
+ if (vertex)
119
+ return vertex;
120
+ this.numV++;
121
+ this.indegree[id] = 0;
122
+ return this.adjacency[id] = [];
123
+ }
126
124
  }
127
- export const defAdjList = (edges) => new AdjacencyList(edges);
128
- export const adjListFromAdjacency = (src) => {
129
- const res = new AdjacencyList();
130
- for (let i = 0, n = src.length; i < n; i++) {
131
- const v = src[i];
132
- if (!v)
133
- continue;
134
- for (let w of v)
135
- res.addEdge(i, w);
136
- }
137
- return res;
125
+ const defAdjList = (edges) => new AdjacencyList(edges);
126
+ const adjListFromAdjacency = (src) => {
127
+ const res = new AdjacencyList();
128
+ for (let i = 0, n = src.length; i < n; i++) {
129
+ const v = src[i];
130
+ if (!v)
131
+ continue;
132
+ for (let w of v)
133
+ res.addEdge(i, w);
134
+ }
135
+ return res;
136
+ };
137
+ export {
138
+ AdjacencyList,
139
+ adjListFromAdjacency,
140
+ defAdjList
138
141
  };
package/mst.js CHANGED
@@ -1,64 +1,17 @@
1
1
  import { sortByCachedKey } from "@thi.ng/arrays/sort-cached";
2
2
  import { DisjointSet } from "./disjoint-set.js";
3
- /**
4
- * Computes the Minimum Spanning Tree from given weighted `edges`, using
5
- * Kruskal's algorithm (O(E log V)).
6
- *
7
- * @remarks
8
- * Edges can be of any type, but requires unsigned integer vertex IDs. The
9
- * latter can be extracted via the user supplied `verts` function. The edge
10
- * weights are extracted via the `cost` function.
11
- *
12
- * The `maxID` arg should equal or greater than the largest vertex ID referenced
13
- * by the given edges.
14
- *
15
- * The function returns a new array of the original edges, satisfying the MST
16
- * criteria. The result edges will be in ascending order, based on the supplied
17
- * cost function. The cost function is called once for each edge and return
18
- * values will be cached prior to sorting (see
19
- * [`sortByCachedKey()`](https://docs.thi.ng/umbrella/arrays/functions/sortByCachedKey.html)
20
- * for details).
21
- *
22
- * References:
23
- * - https://en.wikipedia.org/wiki/Kruskal%27s_algorithm
24
- *
25
- * @example
26
- * ```ts
27
- * // 2D vectors
28
- * verts = [[0,0], [0,1], [1,1], [1,2], [4,2]]
29
- *
30
- * // connections (vertex ID pairs)
31
- * edges = [[0,1], [0,4], [1,2], [1,3], [2,3], [2,4]]
32
- *
33
- * mst(
34
- * edges,
35
- * // max vertex ID
36
- * 4,
37
- * // cost function (cartesian distance, from @thi.ng/vectors pkg)
38
- * ([a,b]) => distSq(verts[a], verts[b]),
39
- * // edge vertex IDs
40
- * (e) => e
41
- * )
42
- *
43
- * // [ [ 0, 1 ], [ 1, 2 ], [ 2, 3 ], [ 2, 4 ] ]
44
- * ```
45
- *
46
- * @param edges - edge pairs
47
- * @param maxID - max vertex ID (+1)
48
- * @param cost - cost function
49
- * @param verts - vertices / graph nodes
50
- *
51
- * @typeParam T - edge type
52
- */
53
- export const mst = (edges, maxID, cost, verts) => {
54
- const graph = new DisjointSet(maxID + 1);
55
- const res = [];
56
- for (let e of sortByCachedKey(edges, cost)) {
57
- const v = verts(e);
58
- if (!graph.unified(v[0], v[1])) {
59
- graph.union(v[0], v[1]);
60
- res.push(e);
61
- }
3
+ const mst = (edges, maxID, cost, verts) => {
4
+ const graph = new DisjointSet(maxID + 1);
5
+ const res = [];
6
+ for (let e of sortByCachedKey(edges, cost)) {
7
+ const v = verts(e);
8
+ if (!graph.unified(v[0], v[1])) {
9
+ graph.union(v[0], v[1]);
10
+ res.push(e);
62
11
  }
63
- return res;
12
+ }
13
+ return res;
14
+ };
15
+ export {
16
+ mst
64
17
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/adjacency",
3
- "version": "2.5.12",
3
+ "version": "2.5.13",
4
4
  "description": "Sparse & bitwise adjacency matrices, lists and selected traversal algorithms for directed & undirected graphs",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -27,7 +27,9 @@
27
27
  ],
28
28
  "license": "Apache-2.0",
29
29
  "scripts": {
30
- "build": "yarn clean && tsc --declaration",
30
+ "build": "yarn build:esbuild && yarn build:decl",
31
+ "build:decl": "tsc --declaration --emitDeclarationOnly",
32
+ "build:esbuild": "esbuild --format=esm --platform=neutral --target=es2022 --tsconfig=tsconfig.json --outdir=. src/**/*.ts",
31
33
  "clean": "rimraf --glob '*.js' '*.d.ts' '*.map' doc",
32
34
  "doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",
33
35
  "doc:ae": "mkdir -p .ae/doc .ae/temp && api-extractor run --local --verbose",
@@ -36,16 +38,17 @@
36
38
  "test": "bun test"
37
39
  },
38
40
  "dependencies": {
39
- "@thi.ng/api": "^8.9.11",
40
- "@thi.ng/arrays": "^2.7.7",
41
- "@thi.ng/bitfield": "^2.3.9",
42
- "@thi.ng/dcons": "^3.2.76",
43
- "@thi.ng/errors": "^2.4.5",
44
- "@thi.ng/sparse": "^0.3.81"
41
+ "@thi.ng/api": "^8.9.12",
42
+ "@thi.ng/arrays": "^2.7.8",
43
+ "@thi.ng/bitfield": "^2.3.10",
44
+ "@thi.ng/dcons": "^3.2.77",
45
+ "@thi.ng/errors": "^2.4.6",
46
+ "@thi.ng/sparse": "^0.3.82"
45
47
  },
46
48
  "devDependencies": {
47
49
  "@microsoft/api-extractor": "^7.38.3",
48
- "@thi.ng/vectors": "^7.8.8",
50
+ "@thi.ng/vectors": "^7.8.9",
51
+ "esbuild": "^0.19.8",
49
52
  "rimraf": "^5.0.5",
50
53
  "tools": "^0.0.1",
51
54
  "typedoc": "^0.25.4",
@@ -123,5 +126,5 @@
123
126
  ],
124
127
  "year": 2018
125
128
  },
126
- "gitHead": "25f2ac8ff795a432a930119661b364d4d93b59a0\n"
129
+ "gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
127
130
  }