bmssp 0.17.0 → 0.18.0

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/baseCase.mjs +94 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bmssp",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Javascript package implementation of the bmssp algorithm.",
5
5
  "main": "index.mjs",
6
6
  "keywords": [
@@ -0,0 +1,94 @@
1
+ import { MinHeap } from "./heap.mjs";
2
+
3
+ /**
4
+ * BaseCase(B, S) — Algorithm 2 of "Breaking the Sorting Barrier for Directed
5
+ * Single-Source Shortest Paths". The level-0 case of the BMSSP recursion: a
6
+ * mini Dijkstra from the single complete source x in S, bounded above by B,
7
+ * that stops after settling k + 1 vertices.
8
+ *
9
+ * Preconditions (guaranteed by the caller, Algorithm 3):
10
+ * - S is a singleton {x} and x is complete (dHat holds its true distance).
11
+ * - Every incomplete vertex v with d(v) < B has a shortest path through x.
12
+ *
13
+ * Distance estimates are read from and written into dHat in place — exactly
14
+ * like the paper's global d̂[·] labels, so improvements made here are visible
15
+ * to the levels above.
16
+ *
17
+ * Two outcomes:
18
+ * - Full success (fewer than k + 1 vertices exist under B): every vertex
19
+ * with d(v) < B is settled and returned, with bound === B.
20
+ * - Partial (the k + 1 cap was hit): bound = the largest settled distance
21
+ * B' <= B, and only the strictly-closer vertices (d̂ < B') are returned.
22
+ * Every returned vertex is complete either way.
23
+ *
24
+ * @param {number} B - Strict upper bound on the distances to settle (Infinity is allowed)
25
+ * @param {Set<*>|Iterable<*>} S - Singleton set holding the complete source x
26
+ * @param {Map<*, number>} dHat - Global distance estimates d̂[·], updated in place
27
+ * @param {Map<*, Array<[*, number]>>} adjacency - nodeId -> outgoing [to, weight] edges
28
+ * @param {number} k - Settle cap parameter, >= 1 (floored); the paper's ⌊log^(1/3) n⌋
29
+ * @returns {{ bound: number, vertices: Set<*> }} The boundary B' <= B and the
30
+ * set U of vertices settled below it
31
+ * @throws {Error} If k is not a number >= 1, S is not a singleton, or the
32
+ * source has no finite distance estimate
33
+ */
34
+ function baseCase(B, S, dHat, adjacency, k) {
35
+ if (typeof k !== "number" || Number.isNaN(k) || k < 1) {
36
+ throw new Error("k must be a number >= 1");
37
+ }
38
+ const cap = Math.floor(k);
39
+ const sources = [...S];
40
+ if (sources.length !== 1) {
41
+ throw new Error("S must contain exactly one source node");
42
+ }
43
+ const [x] = sources;
44
+ const sourceDistance = dHat.get(x);
45
+ if (typeof sourceDistance !== "number" || !Number.isFinite(sourceDistance)) {
46
+ throw new Error("the source must have a finite distance estimate");
47
+ }
48
+
49
+ // U0 in the paper: the vertices settled by this call, seeded with x
50
+ const settled = new Set([x]);
51
+ const heap = new MinHeap();
52
+ heap.insert(x, sourceDistance);
53
+
54
+ while (!heap.isEmpty() && settled.size < cap + 1) {
55
+ const { key: u, value: du } = heap.extractMin();
56
+ settled.add(u);
57
+ for (const [v, weight] of adjacency.get(u) ?? []) {
58
+ const candidate = du + weight;
59
+ // Paper relaxation: d̂[u] + w(u,v) <= d̂[v], and below the bound B
60
+ if (candidate <= (dHat.get(v) ?? Infinity) && candidate < B) {
61
+ dHat.set(v, candidate);
62
+ // With non-negative weights a vertex settled in this call cannot
63
+ // strictly improve, so an equal-sum relaxation (which the <= allows,
64
+ // e.g. via a zero-weight cycle) must not re-enter the heap.
65
+ if (settled.has(v)) continue;
66
+ if (heap.has(v)) {
67
+ heap.decreaseKey(v, candidate);
68
+ } else {
69
+ heap.insert(v, candidate);
70
+ }
71
+ }
72
+ }
73
+ }
74
+
75
+ if (settled.size <= cap) {
76
+ // Exhausted before the cap: everything under B is settled (B' = B)
77
+ return { bound: B, vertices: settled };
78
+ }
79
+
80
+ // Hit the k + 1 cap: report B' = the largest settled distance and return
81
+ // only the vertices strictly below it
82
+ let boundary = -Infinity;
83
+ for (const v of settled) {
84
+ const distance = dHat.get(v);
85
+ if (distance > boundary) boundary = distance;
86
+ }
87
+ const vertices = new Set();
88
+ for (const v of settled) {
89
+ if (dHat.get(v) < boundary) vertices.add(v);
90
+ }
91
+ return { bound: boundary, vertices };
92
+ }
93
+
94
+ export { baseCase };