@thi.ng/adjacency 2.5.12 → 2.5.14

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/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2023-12-09T19:12:03Z
3
+ - **Last updated**: 2023-12-18T13:41:19Z
4
4
  - **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
5
5
 
6
6
  All notable changes to this project will be documented in this file.
package/api.js CHANGED
@@ -1 +0,0 @@
1
- export {};
package/bfs.js CHANGED
@@ -1,85 +1,55 @@
1
1
  import { BitField } from "@thi.ng/bitfield/bitfield";
2
2
  import { DCons } from "@thi.ng/dcons/dcons";
3
- /**
4
- * Breadth-First / shortest path search between `src` and `dest` in `graph`,
5
- * with optional `cost` function. By default all edges have an uniform cost,
6
- * i.e. the overall path cost is topological distance.
7
- *
8
- * @remarks
9
- * Also see {@link bfs} for ad hoc queries.
10
- *
11
- * Reference:
12
- * - https://en.wikipedia.org/wiki/Breadth-first_search
13
- * - https://algs4.cs.princeton.edu/40graphs/
14
- */
15
- export class BFS {
16
- graph;
17
- marked;
18
- edges;
19
- dist;
20
- constructor(graph, src, cost = () => 1) {
21
- this.graph = graph;
22
- const numV = graph.numVertices();
23
- this.edges = new Uint32Array(numV);
24
- this.dist = new Float32Array(numV);
25
- this.marked = new BitField(numV);
26
- this.search(src, cost);
27
- }
28
- search(id, cost) {
29
- const queue = new DCons();
30
- queue.prepend(id);
31
- const { dist, edges, graph, marked } = this;
32
- dist.fill(0xffffffff);
33
- dist[id] = 0;
34
- marked.setAt(id);
35
- while (queue.length) {
36
- const v = queue.drop();
37
- for (let n of graph.neighbors(v)) {
38
- const c = dist[v] + cost(v, n);
39
- if (c < dist[n] || !marked.at(n)) {
40
- edges[n] = v;
41
- dist[n] = c;
42
- marked.setAt(n);
43
- queue.push(n);
44
- }
45
- }
3
+ class BFS {
4
+ graph;
5
+ marked;
6
+ edges;
7
+ dist;
8
+ constructor(graph, src, cost = () => 1) {
9
+ this.graph = graph;
10
+ const numV = graph.numVertices();
11
+ this.edges = new Uint32Array(numV);
12
+ this.dist = new Float32Array(numV);
13
+ this.marked = new BitField(numV);
14
+ this.search(src, cost);
15
+ }
16
+ search(id, cost) {
17
+ const queue = new DCons();
18
+ queue.prepend(id);
19
+ const { dist, edges, graph, marked } = this;
20
+ dist.fill(4294967295);
21
+ dist[id] = 0;
22
+ marked.setAt(id);
23
+ while (queue.length) {
24
+ const v = queue.drop();
25
+ for (let n of graph.neighbors(v)) {
26
+ const c = dist[v] + cost(v, n);
27
+ if (c < dist[n] || !marked.at(n)) {
28
+ edges[n] = v;
29
+ dist[n] = c;
30
+ marked.setAt(n);
31
+ queue.push(n);
46
32
  }
33
+ }
47
34
  }
48
- hasPathTo(id) {
49
- return this.marked.at(id) !== 0;
50
- }
51
- pathTo(id) {
52
- if (!this.marked.at(id))
53
- return;
54
- const { dist, edges } = this;
55
- const path = new DCons();
56
- for (; dist[id] > 0; id = edges[id]) {
57
- path.prepend(id);
58
- }
59
- path.prepend(id);
60
- return path;
35
+ }
36
+ hasPathTo(id) {
37
+ return this.marked.at(id) !== 0;
38
+ }
39
+ pathTo(id) {
40
+ if (!this.marked.at(id))
41
+ return;
42
+ const { dist, edges } = this;
43
+ const path = new DCons();
44
+ for (; dist[id] > 0; id = edges[id]) {
45
+ path.prepend(id);
61
46
  }
47
+ path.prepend(id);
48
+ return path;
49
+ }
62
50
  }
63
- /**
64
- * One-off Breadth-First / shortest path search between `src` and `dest` in
65
- * `graph`, with optional `cost` function. If successful, returns path as
66
- * iterable or undefined if no path connects the given vertices.
67
- *
68
- * @remarks
69
- * For repeated queries starting from the same `src` vertex, it's much better &
70
- * faster to create an {@link BFS} instance to re-use internal state and use
71
- * {@link BFS.pathTo} to check/obtain paths.
72
- *
73
- * By default all edges have an uniform cost, i.e. the overall path cost is
74
- * topological distance.
75
- *
76
- * Reference:
77
- * - https://en.wikipedia.org/wiki/Breadth-first_search
78
- * - https://algs4.cs.princeton.edu/40graphs/
79
- *
80
- * @param graph -
81
- * @param src -
82
- * @param dest -
83
- * @param cost -
84
- */
85
- export const bfs = (graph, src, dest, cost) => new BFS(graph, src, cost).pathTo(dest);
51
+ const bfs = (graph, src, dest, cost) => new BFS(graph, src, cost).pathTo(dest);
52
+ export {
53
+ BFS,
54
+ bfs
55
+ };
package/binary.js CHANGED
@@ -1,111 +1,101 @@
1
1
  import { BitMatrix } from "@thi.ng/bitfield/bitmatrix";
2
2
  import { __into, __invert, __toDot } from "./utils.js";
3
- /**
4
- * Adjacency matrix representation for both directed and undirected graphs and
5
- * using a compact bit matrix to store edges. Each edge requires only 1 bit
6
- * in directed graphs or 2 bits in undirected graphs. E.g. this is allows
7
- * storing 16384 directed edges in just 2KB of memory (128 * 128 / 8 = 2048).
8
- */
9
- export class AdjacencyBitMatrix {
10
- mat;
11
- undirected;
12
- numE;
13
- constructor(n, edges, undirected = false) {
14
- this.mat = new BitMatrix(n);
15
- this.undirected = undirected;
16
- this.numE = 0;
17
- edges && __into(this, edges);
18
- }
19
- *edges() {
20
- const directed = !this.undirected;
21
- for (let i = this.mat.m; i-- > 0;) {
22
- for (let n of this.neighbors(i)) {
23
- if (directed || n > i) {
24
- yield [i, n];
25
- }
26
- }
27
- }
28
- }
29
- numEdges() {
30
- return this.numE;
31
- }
32
- numVertices() {
33
- return this.mat.m;
34
- }
35
- /**
36
- * Resizes matrix to new size given.
37
- *
38
- * @param n - new max vertices
39
- */
40
- resize(n) {
41
- this.mat.resize(n);
42
- return this;
43
- }
44
- addEdge(from, to) {
45
- if (!this.mat.setAt(from, to, true)) {
46
- this.numE++;
47
- this.undirected && this.mat.setAt(to, from, true);
48
- return true;
49
- }
50
- return false;
51
- }
52
- removeEdge(from, to) {
53
- if (this.mat.setAt(from, to, false)) {
54
- this.numE--;
55
- this.undirected && this.mat.setAt(to, from, false);
56
- return true;
57
- }
58
- return false;
59
- }
60
- hasEdge(from, to) {
61
- return this.mat.at(from, to) !== 0;
62
- }
63
- hasVertex(id) {
64
- return (this.mat.popCountRow(id) !== 0 || this.mat.popCountColumn(id) !== 0);
65
- }
66
- degree(id, type = "out") {
67
- let degree = 0;
68
- if (this.undirected || type !== "in")
69
- degree += this.mat.popCountRow(id);
70
- if (!this.undirected && type !== "out")
71
- degree += this.mat.popCountColumn(id);
72
- return degree;
73
- }
74
- neighbors(id) {
75
- return [...this.mat.row(id, true).positions()];
76
- }
77
- similarity(id, threshold = 0) {
78
- const mat = this.mat;
79
- const query = mat.row(id, true);
80
- const acc = [];
81
- for (let i = 0, m = mat.m; i < m; i++) {
82
- if (i === id)
83
- continue;
84
- const sim = query.similarity(mat.row(i, true));
85
- if (sim >= threshold)
86
- acc.push([i, sim]);
3
+ class AdjacencyBitMatrix {
4
+ mat;
5
+ undirected;
6
+ numE;
7
+ constructor(n, edges, undirected = false) {
8
+ this.mat = new BitMatrix(n);
9
+ this.undirected = undirected;
10
+ this.numE = 0;
11
+ edges && __into(this, edges);
12
+ }
13
+ *edges() {
14
+ const directed = !this.undirected;
15
+ for (let i = this.mat.m; i-- > 0; ) {
16
+ for (let n of this.neighbors(i)) {
17
+ if (directed || n > i) {
18
+ yield [i, n];
87
19
  }
88
- return acc.sort((a, b) => b[1] - a[1]);
20
+ }
89
21
  }
90
- invert() {
91
- return __invert(new AdjacencyBitMatrix(this.mat.n, undefined, this.undirected), this.edges());
22
+ }
23
+ numEdges() {
24
+ return this.numE;
25
+ }
26
+ numVertices() {
27
+ return this.mat.m;
28
+ }
29
+ /**
30
+ * Resizes matrix to new size given.
31
+ *
32
+ * @param n - new max vertices
33
+ */
34
+ resize(n) {
35
+ this.mat.resize(n);
36
+ return this;
37
+ }
38
+ addEdge(from, to) {
39
+ if (!this.mat.setAt(from, to, true)) {
40
+ this.numE++;
41
+ this.undirected && this.mat.setAt(to, from, true);
42
+ return true;
92
43
  }
93
- toString() {
94
- return this.mat.toString();
44
+ return false;
45
+ }
46
+ removeEdge(from, to) {
47
+ if (this.mat.setAt(from, to, false)) {
48
+ this.numE--;
49
+ this.undirected && this.mat.setAt(to, from, false);
50
+ return true;
95
51
  }
96
- toDot(ids) {
97
- return __toDot(this.edges(), this.undirected, ids);
52
+ return false;
53
+ }
54
+ hasEdge(from, to) {
55
+ return this.mat.at(from, to) !== 0;
56
+ }
57
+ hasVertex(id) {
58
+ return this.mat.popCountRow(id) !== 0 || this.mat.popCountColumn(id) !== 0;
59
+ }
60
+ degree(id, type = "out") {
61
+ let degree = 0;
62
+ if (this.undirected || type !== "in")
63
+ degree += this.mat.popCountRow(id);
64
+ if (!this.undirected && type !== "out")
65
+ degree += this.mat.popCountColumn(id);
66
+ return degree;
67
+ }
68
+ neighbors(id) {
69
+ return [...this.mat.row(id, true).positions()];
70
+ }
71
+ similarity(id, threshold = 0) {
72
+ const mat = this.mat;
73
+ const query = mat.row(id, true);
74
+ const acc = [];
75
+ for (let i = 0, m = mat.m; i < m; i++) {
76
+ if (i === id)
77
+ continue;
78
+ const sim = query.similarity(mat.row(i, true));
79
+ if (sim >= threshold)
80
+ acc.push([i, sim]);
98
81
  }
82
+ return acc.sort((a, b) => b[1] - a[1]);
83
+ }
84
+ invert() {
85
+ return __invert(
86
+ new AdjacencyBitMatrix(this.mat.n, void 0, this.undirected),
87
+ this.edges()
88
+ );
89
+ }
90
+ toString() {
91
+ return this.mat.toString();
92
+ }
93
+ toDot(ids) {
94
+ return __toDot(this.edges(), this.undirected, ids);
95
+ }
99
96
  }
100
- /**
101
- * Creates adjacency matrix backed by a
102
- * [`BitMatrix`](https://docs.thi.ng/umbrella/bitfield/classes/BitMatrix.html)
103
- * with capacity `n` (max vertices), optionally initialized with given edge
104
- * pairs. Each edge is `[src-node dest-node]`. If `undirected` is true (default:
105
- * false), creates symmetrical adjacencies.
106
- *
107
- * @param n - max vertices
108
- * @param edges - edge pairs
109
- * @param undirected -true, if undirected
110
- */
111
- export const defAdjBitMatrix = (n, edges, undirected) => new AdjacencyBitMatrix(n, edges, undirected);
97
+ const defAdjBitMatrix = (n, edges, undirected) => new AdjacencyBitMatrix(n, edges, undirected);
98
+ export {
99
+ AdjacencyBitMatrix,
100
+ defAdjBitMatrix
101
+ };
package/dfs.js CHANGED
@@ -1,50 +1,45 @@
1
1
  import { BitField } from "@thi.ng/bitfield/bitfield";
2
2
  import { DCons } from "@thi.ng/dcons/dcons";
3
- export class DFS {
4
- graph;
5
- marked;
6
- edges;
7
- src;
8
- constructor(graph, src) {
9
- this.graph = graph;
10
- this.src = src;
11
- const numV = graph.numVertices();
12
- this.edges = new Uint32Array(numV);
13
- this.marked = new BitField(numV);
14
- this.search(src);
3
+ class DFS {
4
+ graph;
5
+ marked;
6
+ edges;
7
+ src;
8
+ constructor(graph, src) {
9
+ this.graph = graph;
10
+ this.src = src;
11
+ const numV = graph.numVertices();
12
+ this.edges = new Uint32Array(numV);
13
+ this.marked = new BitField(numV);
14
+ this.search(src);
15
+ }
16
+ search(id) {
17
+ const { edges, marked } = this;
18
+ marked.setAt(id);
19
+ for (let n of this.graph.neighbors(id)) {
20
+ if (!marked.at(n)) {
21
+ edges[n] = id;
22
+ this.search(n);
23
+ }
15
24
  }
16
- search(id) {
17
- const { edges, marked } = this;
18
- marked.setAt(id);
19
- for (let n of this.graph.neighbors(id)) {
20
- if (!marked.at(n)) {
21
- edges[n] = id;
22
- this.search(n);
23
- }
24
- }
25
- }
26
- hasPathTo(id) {
27
- return this.marked.at(id) !== 0;
28
- }
29
- pathTo(id) {
30
- if (!this.marked.at(id))
31
- return;
32
- const { edges, src } = this;
33
- const path = new DCons();
34
- for (; id !== src; id = edges[id]) {
35
- path.prepend(id);
36
- }
37
- path.prepend(id);
38
- return path;
25
+ }
26
+ hasPathTo(id) {
27
+ return this.marked.at(id) !== 0;
28
+ }
29
+ pathTo(id) {
30
+ if (!this.marked.at(id))
31
+ return;
32
+ const { edges, src } = this;
33
+ const path = new DCons();
34
+ for (; id !== src; id = edges[id]) {
35
+ path.prepend(id);
39
36
  }
37
+ path.prepend(id);
38
+ return path;
39
+ }
40
40
  }
41
- /**
42
- * One-off Depth-First path search from vertex `src` to `dest` in given `graph`.
43
- * If successful, returns path as iterable or undefined if no path connects the
44
- * given vertices.
45
- *
46
- * @param graph -
47
- * @param src -
48
- * @param dest -
49
- */
50
- export const dfs = (graph, src, dest) => new DFS(graph, src).pathTo(dest);
41
+ const dfs = (graph, src, dest) => new DFS(graph, src).pathTo(dest);
42
+ export {
43
+ DFS,
44
+ dfs
45
+ };
package/disjoint-set.js CHANGED
@@ -1,101 +1,91 @@
1
1
  import { fillRange } from "@thi.ng/arrays/fill-range";
2
- /**
3
- * Typed array based Disjoint Set implementation with quick union and path
4
- * compression, after Sedgewick & Wayne.
5
- *
6
- * @remarks
7
- * - https://en.wikipedia.org/wiki/Disjoint-set_data_structure
8
- * - https://algs4.cs.princeton.edu/lectures/15UnionFind-2x2.pdf
9
- */
10
- export class DisjointSet {
11
- roots;
12
- ranks;
13
- count;
14
- /**
15
- * Creates new instance with `n` initial singular subsets.
16
- *
17
- * @param n - initial capacity, ID range [0..n)
18
- */
19
- constructor(n) {
20
- this.roots = fillRange(new Uint32Array(n));
21
- this.ranks = new Uint8Array(n);
22
- this.count = n;
2
+ class DisjointSet {
3
+ roots;
4
+ ranks;
5
+ count;
6
+ /**
7
+ * Creates new instance with `n` initial singular subsets.
8
+ *
9
+ * @param n - initial capacity, ID range [0..n)
10
+ */
11
+ constructor(n) {
12
+ this.roots = fillRange(new Uint32Array(n));
13
+ this.ranks = new Uint8Array(n);
14
+ this.count = n;
15
+ }
16
+ /**
17
+ * Returns canonical ID (tree root) for given `id`. Unless `id`
18
+ * already is unified with some other ID, this will always return
19
+ * `id` itself (since each node is initially its own root).
20
+ *
21
+ * @param id - node ID
22
+ */
23
+ canonical(id) {
24
+ const roots = this.roots;
25
+ while (id !== roots[id]) {
26
+ id = roots[id] = roots[roots[id]];
23
27
  }
24
- /**
25
- * Returns canonical ID (tree root) for given `id`. Unless `id`
26
- * already is unified with some other ID, this will always return
27
- * `id` itself (since each node is initially its own root).
28
- *
29
- * @param id - node ID
30
- */
31
- canonical(id) {
32
- const roots = this.roots;
33
- while (id !== roots[id]) {
34
- id = roots[id] = roots[roots[id]];
35
- }
36
- return id;
28
+ return id;
29
+ }
30
+ /**
31
+ * Connects combines the trees of the given two node IDs and returns
32
+ * the new resulting canonical tree root ID.
33
+ *
34
+ * @param a - node ID
35
+ * @param b - node ID
36
+ */
37
+ union(a, b) {
38
+ const rootA = this.canonical(a);
39
+ const rootB = this.canonical(b);
40
+ if (rootA === rootB) {
41
+ return rootA;
37
42
  }
38
- /**
39
- * Connects combines the trees of the given two node IDs and returns
40
- * the new resulting canonical tree root ID.
41
- *
42
- * @param a - node ID
43
- * @param b - node ID
44
- */
45
- union(a, b) {
46
- const rootA = this.canonical(a);
47
- const rootB = this.canonical(b);
48
- if (rootA === rootB) {
49
- return rootA;
50
- }
51
- this.count--;
52
- const ranks = this.ranks;
53
- const ra = ranks[rootA];
54
- const rb = ranks[rootB];
55
- if (ra < rb) {
56
- return (this.roots[rootA] = rootB);
57
- }
58
- ra === rb && ranks[rootA]++;
59
- return (this.roots[rootB] = rootA);
43
+ this.count--;
44
+ const ranks = this.ranks;
45
+ const ra = ranks[rootA];
46
+ const rb = ranks[rootB];
47
+ if (ra < rb) {
48
+ return this.roots[rootA] = rootB;
60
49
  }
61
- /**
62
- * Returns true, if the given two nodes belong to the same tree /
63
- * subset.
64
- *
65
- * @param a - node ID
66
- * @param b - node ID
67
- */
68
- unified(a, b) {
69
- return this.canonical(a) === this.canonical(b);
70
- }
71
- /**
72
- * Returns a `Map` of all subsets (connected components) with their
73
- * canonical tree root IDs as keys and arrays of node IDs as values.
74
- *
75
- * @remarks
76
- * If only the number of subsets is required, use the `count`
77
- * property of this class instance instead (O(1), updated with each
78
- * call to {@link DisjointSet.union}).
79
- */
80
- subsets() {
81
- const sets = new Map();
82
- const roots = this.roots;
83
- for (let i = roots.length; i-- > 0;) {
84
- const id = this.canonical(i);
85
- const s = sets.get(id);
86
- if (s) {
87
- s.push(i);
88
- }
89
- else {
90
- sets.set(id, [i]);
91
- }
92
- }
93
- return sets;
50
+ ra === rb && ranks[rootA]++;
51
+ return this.roots[rootB] = rootA;
52
+ }
53
+ /**
54
+ * Returns true, if the given two nodes belong to the same tree /
55
+ * subset.
56
+ *
57
+ * @param a - node ID
58
+ * @param b - node ID
59
+ */
60
+ unified(a, b) {
61
+ return this.canonical(a) === this.canonical(b);
62
+ }
63
+ /**
64
+ * Returns a `Map` of all subsets (connected components) with their
65
+ * canonical tree root IDs as keys and arrays of node IDs as values.
66
+ *
67
+ * @remarks
68
+ * If only the number of subsets is required, use the `count`
69
+ * property of this class instance instead (O(1), updated with each
70
+ * call to {@link DisjointSet.union}).
71
+ */
72
+ subsets() {
73
+ const sets = /* @__PURE__ */ new Map();
74
+ const roots = this.roots;
75
+ for (let i = roots.length; i-- > 0; ) {
76
+ const id = this.canonical(i);
77
+ const s = sets.get(id);
78
+ if (s) {
79
+ s.push(i);
80
+ } else {
81
+ sets.set(id, [i]);
82
+ }
94
83
  }
84
+ return sets;
85
+ }
95
86
  }
96
- /**
97
- * Creates a new {@link DisjointSet} with capacity `n`.
98
- *
99
- * @param n -
100
- */
101
- export const defDisjointSet = (n) => new DisjointSet(n);
87
+ const defDisjointSet = (n) => new DisjointSet(n);
88
+ export {
89
+ DisjointSet,
90
+ defDisjointSet
91
+ };