@graphty/graphty-element 2.6.2 → 3.0.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 (163) hide show
  1. package/dist/ai.js +3 -3
  2. package/dist/catalog.js +53 -54
  3. package/dist/chunks/{AiManager-4iQpsJW1.js → AiManager-Bhh0rR_p.js} +706 -655
  4. package/dist/chunks/GraphSession-DPoTaT3b.js +21175 -0
  5. package/dist/chunks/{GraphtyLogger-B_O67a6c.js → GraphtyLogger-BtcBJQPL.js} +1 -1
  6. package/dist/chunks/{VoiceInputAdapter-Cc6mHXTI.js → VoiceInputAdapter-CdLsJ_nG.js} +1 -1
  7. package/dist/chunks/{XRPivotCameraController-BLa89LXn.js → XRPivotCameraController-CiLJ9Gz6.js} +2 -2
  8. package/dist/chunks/algorithms-qij74zEN.js +6811 -0
  9. package/dist/chunks/{capability-check-Am2zliFj.js → capability-check-BbgTejS3.js} +1 -1
  10. package/dist/chunks/definePalette-BYt2Llxs.js +1333 -0
  11. package/dist/chunks/fields-5uVC1Pll.js +4999 -0
  12. package/dist/chunks/{format-detection-BHwrAVzW.js → format-detection-FKaDMshR.js} +1 -1
  13. package/dist/chunks/{index-BkBLbvui.js → index-Cc_6D9hV.js} +6034 -7127
  14. package/dist/chunks/interpolation-Dk206AhZ.js +105 -0
  15. package/dist/chunks/paletteRegistry-De3CGdst.js +357 -0
  16. package/dist/chunks/parse-SVp77JbE.js +669 -0
  17. package/dist/chunks/{pluginRegistry-Bs8bEkz9.js → pluginRegistry-Ddfl6Mv2.js} +27 -24
  18. package/dist/chunks/{registry-BdGvyZou.js → registry-CgMqldp4.js} +1 -1
  19. package/dist/chunks/{DataSource-BL2UzPff.js → sources-BbAJCDIH.js} +279 -108
  20. package/dist/commands.d.ts +128 -19
  21. package/dist/commands.js +49 -1
  22. package/dist/custom-elements.json +1 -1
  23. package/dist/extend.d.ts +10 -2
  24. package/dist/extend.js +64 -57
  25. package/dist/graphty-catalog.json +6 -3
  26. package/dist/graphty.bundle.js +267177 -240648
  27. package/dist/graphty.js +84 -78
  28. package/dist/index.d.ts +4 -0
  29. package/dist/logging.js +2 -2
  30. package/dist/schema.js +70 -71
  31. package/dist/session.d.ts +5 -6
  32. package/dist/session.js +40 -86
  33. package/dist/src/Edge.d.ts +31 -67
  34. package/dist/src/Graph.d.ts +335 -77
  35. package/dist/src/Node.d.ts +36 -3
  36. package/dist/src/NodeBehavior.d.ts +28 -0
  37. package/dist/src/Styles.d.ts +15 -4
  38. package/dist/src/acceleration/AccelerationController.d.ts +8 -0
  39. package/dist/src/acceleration/narrow.d.ts +9 -1
  40. package/dist/src/acceleration/types.d.ts +10 -0
  41. package/dist/src/ai/AiController.d.ts +12 -0
  42. package/dist/src/ai/AiManager.d.ts +7 -0
  43. package/dist/src/ai/commands/AlgorithmCommands.d.ts +1 -1
  44. package/dist/src/ai/commands/LayoutCommands.d.ts +1 -1
  45. package/dist/src/ai/commands/StyleCommands.d.ts +1 -1
  46. package/dist/src/ai/commands/types.d.ts +20 -1
  47. package/dist/src/algorithms/Algorithm.d.ts +21 -4
  48. package/dist/src/algorithms/BFSAlgorithm.d.ts +0 -9
  49. package/dist/src/algorithms/BellmanFordAlgorithm.d.ts +0 -14
  50. package/dist/src/algorithms/DFSAlgorithm.d.ts +1 -1
  51. package/dist/src/algorithms/FloydWarshallAlgorithm.d.ts +5 -0
  52. package/dist/src/algorithms/GirvanNewmanAlgorithm.d.ts +5 -0
  53. package/dist/src/algorithms/LeidenAlgorithm.d.ts +5 -0
  54. package/dist/src/algorithms/PageRankAlgorithm.d.ts +1 -1
  55. package/dist/src/algorithms/StronglyConnectedComponentsAlgorithm.d.ts +1 -1
  56. package/dist/src/algorithms/metrics/fields.d.ts +23 -1
  57. package/dist/src/algorithms/utils/graphUtils.d.ts +13 -1
  58. package/dist/src/catalog/paletteRegistry.d.ts +4 -4
  59. package/dist/src/catalog/registry.d.ts +3 -2
  60. package/dist/src/catalog/types.d.ts +18 -1
  61. package/dist/src/config/GraphStyle.d.ts +1 -1
  62. package/dist/src/config/StyleTemplate.d.ts +2 -2
  63. package/dist/src/config/xr-config-schema.d.ts +4 -4
  64. package/dist/src/data/CSVDataSource.d.ts +77 -22
  65. package/dist/src/data/ErrorAggregator.d.ts +5 -0
  66. package/dist/src/data/GEXFDataSource.d.ts +12 -61
  67. package/dist/src/data/GraphMLDataSource.d.ts +3 -44
  68. package/dist/src/data/GraphStore.d.ts +322 -15
  69. package/dist/src/data/JsonDataSource.d.ts +43 -1
  70. package/dist/src/data/graph-io-import.d.ts +89 -0
  71. package/dist/src/data/graph-io-records.d.ts +64 -0
  72. package/dist/src/data/lane.d.ts +23 -0
  73. package/dist/src/data/positions.d.ts +13 -0
  74. package/dist/src/data/seedPosition.d.ts +16 -0
  75. package/dist/src/errors/GraphtyError.d.ts +3 -1
  76. package/dist/src/errors/codes.d.ts +23 -0
  77. package/dist/src/events.d.ts +12 -0
  78. package/dist/src/graphty-element.d.ts +149 -54
  79. package/dist/src/input/types.d.ts +2 -0
  80. package/dist/src/layout/D3GraphLayoutEngine.d.ts +17 -3
  81. package/dist/src/layout/FixedLayoutEngine.d.ts +20 -4
  82. package/dist/src/layout/KamadaKawaiLayoutEngine.d.ts +6 -0
  83. package/dist/src/layout/LayoutEngine.d.ts +214 -116
  84. package/dist/src/layout/NGraphLayoutEngine.d.ts +10 -3
  85. package/dist/src/layout/SimulationLayoutEngine.d.ts +8 -3
  86. package/dist/src/managers/AlgorithmManager.d.ts +24 -5
  87. package/dist/src/managers/DataManager.d.ts +258 -181
  88. package/dist/src/managers/EventManager.d.ts +5 -2
  89. package/dist/src/managers/GraphContext.d.ts +7 -0
  90. package/dist/src/managers/InputManager.d.ts +11 -0
  91. package/dist/src/managers/LayoutManager.d.ts +129 -50
  92. package/dist/src/managers/RenderManager.d.ts +14 -1
  93. package/dist/src/managers/UpdateManager.d.ts +20 -0
  94. package/dist/src/screenshot/ScreenshotCapture.d.ts +1 -1
  95. package/dist/src/session/GraphSession.d.ts +83 -6
  96. package/dist/src/session/commands/algo.d.ts +169 -0
  97. package/dist/src/session/commands/config.d.ts +45 -0
  98. package/dist/src/session/commands/data.d.ts +178 -0
  99. package/dist/src/session/commands/doors.d.ts +93 -0
  100. package/dist/src/session/commands/index.d.ts +20 -0
  101. package/dist/src/session/commands/layout.d.ts +104 -0
  102. package/dist/src/session/commands/positions.d.ts +30 -0
  103. package/dist/src/session/commands/sets.d.ts +113 -0
  104. package/dist/src/session/commands/style.d.ts +92 -0
  105. package/dist/src/session/commands/view.d.ts +57 -0
  106. package/dist/src/session/commands/visibility.d.ts +41 -0
  107. package/dist/src/session/data.d.ts +131 -4
  108. package/dist/src/session/index.d.ts +1 -1
  109. package/dist/src/session/planning.d.ts +25 -8
  110. package/dist/src/session/project/Dispatcher.d.ts +905 -0
  111. package/dist/src/session/project/History.d.ts +382 -0
  112. package/dist/src/session/project/arrangement.d.ts +247 -0
  113. package/dist/src/session/project/derive.d.ts +132 -0
  114. package/dist/src/session/project/digest.d.ts +33 -0
  115. package/dist/src/session/project/draft.d.ts +194 -0
  116. package/dist/src/session/project/graphOps.d.ts +304 -0
  117. package/dist/src/session/project/ingest.d.ts +364 -0
  118. package/dist/src/session/project/state.d.ts +145 -0
  119. package/dist/src/session/project/strict.d.ts +68 -0
  120. package/dist/src/session/results/RunResult.d.ts +48 -0
  121. package/dist/src/session/results/statistics.d.ts +20 -0
  122. package/dist/src/session/runs/Run.d.ts +80 -4
  123. package/dist/src/session/runs/RunsApi.d.ts +23 -6
  124. package/dist/src/session/runs/types.d.ts +25 -6
  125. package/dist/src/session/scope/ElementMask.d.ts +14 -0
  126. package/dist/src/session/scope/ScopeApi.d.ts +3 -22
  127. package/dist/src/session/scope/spaces.d.ts +29 -0
  128. package/dist/src/session/sealed.d.ts +22 -0
  129. package/dist/src/session/selection/SelectionApi.d.ts +14 -4
  130. package/dist/src/session/sets/SetsApi.d.ts +13 -5
  131. package/dist/src/session/sets/store.d.ts +54 -53
  132. package/dist/src/session/sets/types.d.ts +5 -2
  133. package/dist/src/session/styles/Layer.d.ts +5 -0
  134. package/dist/src/session/styles/StylesApi.d.ts +68 -17
  135. package/dist/src/session/styles/autoApply.d.ts +64 -53
  136. package/dist/src/session/styles/index.d.ts +3 -3
  137. package/dist/src/session/styles/predicate.d.ts +7 -0
  138. package/dist/src/session/styles/repaint.d.ts +16 -1
  139. package/dist/src/session/styles/sources.d.ts +1 -1
  140. package/dist/src/session/types.d.ts +625 -54
  141. package/dist/src/session/visibility/VisibilityApi.d.ts +38 -18
  142. package/dist/src/session/visibility/filter.d.ts +10 -0
  143. package/dist/src/simple/defineAlgorithm.d.ts +28 -0
  144. package/dist/src/simple/defineLayout.d.ts +35 -0
  145. package/dist/src/simple/defineLogDestination.d.ts +31 -0
  146. package/dist/src/simple/definePalette.d.ts +26 -0
  147. package/dist/src/simple/definition.d.ts +106 -0
  148. package/dist/src/simple/options.d.ts +33 -0
  149. package/dist/src/simple/source.d.ts +49 -0
  150. package/dist/src/simple/types.d.ts +366 -0
  151. package/dist/src/simple/view.d.ts +107 -0
  152. package/dist/webgpu.js +2 -2
  153. package/package.json +10 -12
  154. package/dist/chunks/GraphSession-BhuHSXIo.js +0 -12819
  155. package/dist/chunks/GraphStyle-Cwr55SAE.js +0 -65
  156. package/dist/chunks/algorithms-BJ6DQMOe.js +0 -3777
  157. package/dist/chunks/detect-fyuVnlCT.js +0 -88
  158. package/dist/chunks/interpolation-DY-PNpqX.js +0 -43
  159. package/dist/chunks/optionsFromZod-CKMYSwTz.js +0 -3636
  160. package/dist/chunks/paletteRegistry-BCFSwJGK.js +0 -1196
  161. package/dist/chunks/parse-BMTqt4SS.js +0 -3658
  162. package/dist/src/data/csv-variant-detection.d.ts +0 -29
  163. package/dist/src/data/ingest.d.ts +0 -104
@@ -1,3777 +0,0 @@
1
- import { c as ut } from "./common-DWNKjpH_.js";
2
- import { E as pt, a as mt, N as gt } from "./NodeStyle-CtmA7dXi.js";
3
- import { J as ft, k as yt, a as E, K as R, h as S, M as ie, P as rt, f as C, A as v, Y as bt, N as wt, Q as vt, S as Nt, n as U, L as et, s as Ze, e as oe, m as Be, i as Ae, o as Qe } from "./optionsFromZod-CKMYSwTz.js";
4
- import { z as c } from "zod/v4";
5
- import { bellmanFord as kt, betweennessCentrality as St, breadthFirstSearch as Ot, bipartitePartition as xt, maximumBipartiteMatching as It, closenessCentrality as Ct, depthFirstSearch as Tt, eigenvectorCentrality as zt, ConvergenceError as Et, floydWarshall as Mt, girvanNewman as Dt, hits as Pt, katzCentrality as Rt, kCoreDecomposition as $t, labelPropagation as Ft, leiden as Gt, commonNeighborsPrediction as Ht, adamicAdarPrediction as Wt, louvain as Kt, Graph as _e, fordFulkerson as Lt, kargerMinCut as Ut, stoerWagner as jt, minSTCut as Vt, pageRank as qt, connectedComponents as Bt, primMST as tt, stronglyConnectedComponents as Yt } from "@graphty/algorithms";
6
- import { G as ct } from "./pluginRegistry-Bs8bEkz9.js";
7
- import { INVALID_INDEX as L } from "@graphty/graph-format";
8
- var Va = typeof globalThis < "u" ? globalThis : typeof window < "u" ? window : typeof global < "u" ? global : typeof self < "u" ? self : {};
9
- function qa(s) {
10
- return s && s.__esModule && Object.prototype.hasOwnProperty.call(s, "default") ? s.default : s;
11
- }
12
- const Zt = /^#?(?:[\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/i;
13
- function Xe(s) {
14
- return Number.isFinite(s) ? Math.max(0, Math.min(255, Math.round(s))) : 0;
15
- }
16
- function je(s) {
17
- return s.toString(16).padStart(2, "0");
18
- }
19
- function Jt(s) {
20
- if (!Zt.test(s))
21
- return null;
22
- const e = s.startsWith("#") ? s.slice(1) : s, t = e.length <= 4, n = (i) => {
23
- const o = t ? e[i].repeat(2) : e.slice(i * 2, i * 2 + 2);
24
- return parseInt(o, 16);
25
- }, a = e.length === 4 || e.length === 8;
26
- return {
27
- r: n(0),
28
- g: n(1),
29
- b: n(2),
30
- a: a ? n(3) / 255 : 1
31
- };
32
- }
33
- function Ba(s) {
34
- return typeof s != "object" || s === null || !("hex" in s) ? null : s;
35
- }
36
- function Xt(s) {
37
- if (s == null)
38
- return null;
39
- if (typeof s == "object")
40
- return at(s);
41
- const e = Jt(s);
42
- if (e !== null)
43
- return at(e);
44
- try {
45
- const t = ut(s);
46
- return t === void 0 ? null : Xt(t);
47
- } catch {
48
- return null;
49
- }
50
- }
51
- function at(s) {
52
- const e = Xe(s.r), t = Xe(s.g), n = Xe(s.b), a = Number.isFinite(s.a) ? Math.max(0, Math.min(1, s.a)) : 1, i = `#${je(e)}${je(t)}${je(n)}`;
53
- return { r: e, g: t, b: n, a, hex: a === 1 ? i : `${i}${je(Math.round(a * 255))}` };
54
- }
55
- const Qt = gt.options, At = mt.options, nt = pt.options, _t = "An outline has a colour and no width. Babylon draws it with a highlight LAYER, and the stroke's blur size belongs to that layer rather than to a mesh, so every outline on screen is drawn at one width whatever a style asks for -- which is why the node style declares no outline width to ask with. The outline's COLOUR is per style; a width per style would need one highlight layer per width, at a full-screen pass each.", Ve = 'A caption hangs from the cap at that end of the edge, so an end drawn with no arrow carries none: a tail caption needs `edge.arrowTail` set to something other than "none". The words are what switch a caption on, so a layer that writes only the caption\'s appearance and no words draws nothing, exactly as `node.labelStyle` draws nothing without `node.label`.', it = "A tooltip is drawn on hover and only on hover, so a graph at rest shows none: a layer that writes one changes nothing until a reader points at the node. The words are what switch it on, so a layer that writes only the tooltip's appearance and no words draws nothing, exactly as `node.labelStyle` draws nothing without `node.label`. Only a NODE has a tooltip; `edge.tooltip` was withdrawn in 2.0 because an edge cannot be hovered.", Ue = {
56
- "node.color": {
57
- channel: "node.color",
58
- target: "node",
59
- plainName: "Node Colour",
60
- accepts: "color",
61
- stylePath: "texture.color",
62
- renderable: !0
63
- },
64
- "node.size": {
65
- channel: "node.size",
66
- target: "node",
67
- plainName: "Node Size",
68
- accepts: "number",
69
- min: 0,
70
- stylePath: "shape.size",
71
- renderable: !0
72
- },
73
- "node.shape": {
74
- channel: "node.shape",
75
- target: "node",
76
- plainName: "Node Shape",
77
- accepts: "enum",
78
- values: Qt,
79
- stylePath: "shape.type",
80
- renderable: !0
81
- },
82
- "node.label": {
83
- channel: "node.label",
84
- target: "node",
85
- plainName: "Node Label",
86
- accepts: "text",
87
- stylePath: "label.text",
88
- renderable: !0
89
- },
90
- "node.labelStyle": {
91
- channel: "node.labelStyle",
92
- target: "node",
93
- plainName: "Node Label Style",
94
- accepts: "labelStyle",
95
- stylePath: "label",
96
- renderable: !0
97
- },
98
- "node.tooltip": {
99
- channel: "node.tooltip",
100
- target: "node",
101
- plainName: "Node Tooltip",
102
- accepts: "text",
103
- stylePath: "tooltip.text",
104
- renderable: !0,
105
- caveat: it
106
- },
107
- "node.tooltipStyle": {
108
- channel: "node.tooltipStyle",
109
- target: "node",
110
- plainName: "Node Tooltip Style",
111
- accepts: "labelStyle",
112
- stylePath: "tooltip",
113
- renderable: !0,
114
- caveat: it
115
- },
116
- "node.opacity": {
117
- channel: "node.opacity",
118
- target: "node",
119
- plainName: "Node Opacity",
120
- accepts: "number",
121
- min: 0,
122
- max: 1,
123
- stylePath: "texture.color.opacity",
124
- renderable: !0
125
- },
126
- "node.outline": {
127
- channel: "node.outline",
128
- target: "node",
129
- plainName: "Node Outline",
130
- accepts: "color",
131
- stylePath: "effect.outline.color",
132
- renderable: !0,
133
- caveat: _t
134
- },
135
- "node.glow": {
136
- channel: "node.glow",
137
- target: "node",
138
- plainName: "Node Glow",
139
- accepts: "color",
140
- stylePath: "effect.glow.color",
141
- renderable: !0
142
- },
143
- "node.glowStrength": {
144
- channel: "node.glowStrength",
145
- target: "node",
146
- plainName: "Node Glow Strength",
147
- accepts: "number",
148
- min: 0,
149
- stylePath: "effect.glow.strength",
150
- renderable: !0
151
- },
152
- "node.wireframe": {
153
- channel: "node.wireframe",
154
- target: "node",
155
- plainName: "Node Wireframe",
156
- accepts: "boolean",
157
- stylePath: "effect.wireframe",
158
- renderable: !0
159
- },
160
- "node.flat": {
161
- channel: "node.flat",
162
- target: "node",
163
- plainName: "Node Flat Shading",
164
- accepts: "boolean",
165
- stylePath: "effect.flatShaded",
166
- renderable: !0
167
- },
168
- "node.marker": {
169
- channel: "node.marker",
170
- target: "node",
171
- plainName: "Node Marker",
172
- accepts: "nothing",
173
- stylePath: "texture.icon",
174
- renderable: !1,
175
- caveat: "The element draws no marker. texture.icon is in the node schema and no renderer reads it, and the badge vocabulary belongs to a label rather than to a node."
176
- },
177
- "edge.color": {
178
- channel: "edge.color",
179
- target: "edge",
180
- plainName: "Edge Colour",
181
- accepts: "color",
182
- stylePath: "line.color",
183
- renderable: !0
184
- },
185
- "edge.width": {
186
- channel: "edge.width",
187
- target: "edge",
188
- plainName: "Edge Width",
189
- accepts: "number",
190
- min: 0,
191
- stylePath: "line.width",
192
- renderable: !0
193
- },
194
- "edge.opacity": {
195
- channel: "edge.opacity",
196
- target: "edge",
197
- plainName: "Edge Opacity",
198
- accepts: "number",
199
- min: 0,
200
- max: 1,
201
- stylePath: "line.opacity",
202
- renderable: !0
203
- },
204
- "edge.style": {
205
- channel: "edge.style",
206
- target: "edge",
207
- plainName: "Edge Line Pattern",
208
- accepts: "enum",
209
- values: At,
210
- stylePath: "line.type",
211
- renderable: !0
212
- },
213
- "edge.patternCount": {
214
- channel: "edge.patternCount",
215
- target: "edge",
216
- plainName: "Edge Pattern Count",
217
- accepts: "number",
218
- min: 2,
219
- stylePath: "line.patternCount",
220
- renderable: !0,
221
- caveat: "A cap on how many dots or dashes a patterned edge draws, and it applies to a patterned line only: a solid line has no elements to count, and zigzag and sinewave always tile the whole edge, so they ignore it. Left unset, the spacing rule decides, so a long edge or a thin one draws more of them."
222
- },
223
- "edge.curvature": {
224
- channel: "edge.curvature",
225
- target: "edge",
226
- plainName: "Edge Curve",
227
- accepts: "boolean",
228
- stylePath: "line.bezier",
229
- renderable: !0,
230
- caveat: "A switch, not an amount: the renderer offsets its control points by a fixed fraction of the edge's length, so how far an edge bows cannot be set."
231
- },
232
- "edge.arrowHead": {
233
- channel: "edge.arrowHead",
234
- target: "edge",
235
- plainName: "Arrow Head",
236
- accepts: "enum",
237
- values: nt,
238
- stylePath: "arrowHead.type",
239
- renderable: !0
240
- },
241
- "edge.arrowHeadSize": {
242
- channel: "edge.arrowHeadSize",
243
- target: "edge",
244
- plainName: "Arrow Head Size",
245
- accepts: "number",
246
- min: 0,
247
- stylePath: "arrowHead.size",
248
- renderable: !0
249
- },
250
- "edge.arrowHeadColor": {
251
- channel: "edge.arrowHeadColor",
252
- target: "edge",
253
- plainName: "Arrow Head Colour",
254
- accepts: "color",
255
- stylePath: "arrowHead.color",
256
- renderable: !0
257
- },
258
- "edge.arrowHeadOpacity": {
259
- channel: "edge.arrowHeadOpacity",
260
- target: "edge",
261
- plainName: "Arrow Head Opacity",
262
- accepts: "number",
263
- min: 0,
264
- max: 1,
265
- stylePath: "arrowHead.opacity",
266
- renderable: !0
267
- },
268
- "edge.arrowHeadText": {
269
- channel: "edge.arrowHeadText",
270
- target: "edge",
271
- plainName: "Arrow Head Caption",
272
- accepts: "text",
273
- stylePath: "arrowHead.text.text",
274
- renderable: !0,
275
- caveat: Ve
276
- },
277
- "edge.arrowHeadTextStyle": {
278
- channel: "edge.arrowHeadTextStyle",
279
- target: "edge",
280
- plainName: "Arrow Head Caption Style",
281
- accepts: "labelStyle",
282
- stylePath: "arrowHead.text",
283
- renderable: !0,
284
- caveat: Ve
285
- },
286
- "edge.arrowTail": {
287
- channel: "edge.arrowTail",
288
- target: "edge",
289
- plainName: "Arrow Tail",
290
- accepts: "enum",
291
- values: nt,
292
- stylePath: "arrowTail.type",
293
- renderable: !0
294
- },
295
- "edge.arrowTailSize": {
296
- channel: "edge.arrowTailSize",
297
- target: "edge",
298
- plainName: "Arrow Tail Size",
299
- accepts: "number",
300
- min: 0,
301
- stylePath: "arrowTail.size",
302
- renderable: !0
303
- },
304
- "edge.arrowTailColor": {
305
- channel: "edge.arrowTailColor",
306
- target: "edge",
307
- plainName: "Arrow Tail Colour",
308
- accepts: "color",
309
- stylePath: "arrowTail.color",
310
- renderable: !0
311
- },
312
- "edge.arrowTailOpacity": {
313
- channel: "edge.arrowTailOpacity",
314
- target: "edge",
315
- plainName: "Arrow Tail Opacity",
316
- accepts: "number",
317
- min: 0,
318
- max: 1,
319
- stylePath: "arrowTail.opacity",
320
- renderable: !0
321
- },
322
- "edge.arrowTailText": {
323
- channel: "edge.arrowTailText",
324
- target: "edge",
325
- plainName: "Arrow Tail Caption",
326
- accepts: "text",
327
- stylePath: "arrowTail.text.text",
328
- renderable: !0,
329
- caveat: Ve
330
- },
331
- "edge.arrowTailTextStyle": {
332
- channel: "edge.arrowTailTextStyle",
333
- target: "edge",
334
- plainName: "Arrow Tail Caption Style",
335
- accepts: "labelStyle",
336
- stylePath: "arrowTail.text",
337
- renderable: !0,
338
- caveat: Ve
339
- },
340
- "edge.animationSpeed": {
341
- channel: "edge.animationSpeed",
342
- target: "edge",
343
- plainName: "Edge Animation Speed",
344
- accepts: "number",
345
- min: 0,
346
- stylePath: "line.animationSpeed",
347
- renderable: !0
348
- },
349
- "edge.label": {
350
- channel: "edge.label",
351
- target: "edge",
352
- plainName: "Edge Label",
353
- accepts: "text",
354
- stylePath: "label.text",
355
- renderable: !0
356
- },
357
- "edge.labelStyle": {
358
- channel: "edge.labelStyle",
359
- target: "edge",
360
- plainName: "Edge Label Style",
361
- accepts: "labelStyle",
362
- stylePath: "label",
363
- renderable: !0
364
- }
365
- }, dt = Object.keys(Ue);
366
- dt.filter(
367
- (s) => Ue[s].accepts === "color"
368
- );
369
- function ea(s) {
370
- return typeof s == "string" && Object.hasOwn(Ue, s);
371
- }
372
- function Ya(s) {
373
- return ea(s) ? Ue[s] : void 0;
374
- }
375
- function Za(s) {
376
- return dt.map((e) => Ue[e]).filter(
377
- (e) => e.target === s
378
- );
379
- }
380
- const lt = Object.freeze([
381
- {
382
- id: "fitToGraph",
383
- plainName: "Fit to graph",
384
- description: "Frames every element, from an angle in 3D and straight on in 2D.",
385
- modes: ["2d", "3d"],
386
- options: []
387
- },
388
- {
389
- id: "topView",
390
- plainName: "From above",
391
- description: "Looks straight down at the graph. The natural view for a flat arrangement.",
392
- modes: ["2d", "3d"],
393
- options: []
394
- },
395
- {
396
- id: "sideView",
397
- plainName: "From the side",
398
- description: "Looks along the x axis, which shows how deep an arrangement is.",
399
- modes: ["3d"],
400
- options: []
401
- },
402
- {
403
- id: "frontView",
404
- plainName: "From the front",
405
- description: "Looks along the z axis, straight at the face of the arrangement.",
406
- modes: ["3d"],
407
- options: []
408
- },
409
- {
410
- id: "isometric",
411
- plainName: "Isometric",
412
- description: "The classic three-quarter view: 45 degrees around and about 35 degrees up.",
413
- modes: ["3d"],
414
- options: []
415
- }
416
- ]);
417
- function Ja(s) {
418
- return lt.find((e) => e.id === s) ?? ft(s)?.descriptor;
419
- }
420
- function Xa(s) {
421
- return [...lt, ...yt()].filter(
422
- (e) => e.modes.includes(s)
423
- );
424
- }
425
- function Qa(s) {
426
- return s;
427
- }
428
- function ta(s) {
429
- const e = {};
430
- for (const [t, n] of Object.entries(s))
431
- e[t] = n.schema;
432
- return c.object(e);
433
- }
434
- function aa(s, e) {
435
- return ta(s).parse(e);
436
- }
437
- function P(s, e) {
438
- return `${String(s)}:${String(e)}`;
439
- }
440
- function Ye(s, e, t, n) {
441
- const a = String(t);
442
- if (!n.some((i) => String(i) === a))
443
- throw new ct({
444
- code: "E_OPTION_RANGE",
445
- source: "run",
446
- message: `${s}: the ${e} option names node "${a}", which is not in the graph.`,
447
- details: { algorithm: s, option: e, value: t }
448
- });
449
- return a;
450
- }
451
- const na = {
452
- source: {
453
- schema: c.union([c.string(), c.number()]).nullable().default(null),
454
- meta: {
455
- label: "Source Node",
456
- description: "Starting node for shortest path (uses first node if not set)"
457
- }
458
- },
459
- target: {
460
- schema: c.union([c.string(), c.number()]).nullable().default(null),
461
- meta: {
462
- label: "Target Node",
463
- description: "Destination node for shortest path (uses last node if not set)"
464
- }
465
- }
466
- }, V = class V extends E {
467
- constructor() {
468
- super(...arguments), this.legacyOptions = null;
469
- }
470
- /**
471
- * Configure the algorithm with source and optional target nodes
472
- * @param options - Configuration options
473
- * @param options.source - The source node ID
474
- * @param options.target - The optional target node ID
475
- * @returns This algorithm instance for chaining
476
- * @deprecated Use constructor options instead. This method is kept for backward compatibility.
477
- */
478
- configure(e) {
479
- return this.legacyOptions = e, this;
480
- }
481
- /**
482
- * Find the cheapest route from the source to the target, negative weights included.
483
- *
484
- * Publishes the same path fields Dijkstra does, because the engine is a parameter of the
485
- * question and not a different question. What is different is what the method can tell you:
486
- * it detects a loop that costs less every time round, and it says so on the graph half of
487
- * the result, where a distance computed in the presence of one cannot be trusted.
488
- * @param context - What the element gave the run.
489
- * @returns The route, or null when there are no nodes to search.
490
- */
491
- async compute(e) {
492
- const t = this.input("undirected"), n = R(t);
493
- if (n.length === 0)
494
- return null;
495
- const a = this.legacyOptions?.source ?? this._schemaOptions.source ?? n[0], i = this.legacyOptions?.target ?? this._schemaOptions.target ?? n[n.length - 1], o = this.algorithmGraph("undirected");
496
- e.report({ phase: "Relaxing edges", total: null });
497
- const r = kt(o, a), d = this.reconstructPath(r.predecessors, a, i), h = /* @__PURE__ */ new Map();
498
- d.forEach((f, M) => h.set(f, M));
499
- for (const f of r.distances.values())
500
- ;
501
- const u = [];
502
- await S(e, "Marking the route", n, (f) => {
503
- const M = h.get(f), b = r.distances.get(f) ?? 1 / 0;
504
- u.push({ id: f, values: { onPath: M !== void 0, order: M, distance: b } });
505
- });
506
- const l = this.getPathEdges(d), p = [];
507
- await S(e, "Marking the route", ie(t), (f) => {
508
- const M = P(f.source, f.target), b = P(f.target, f.source);
509
- p.push({ id: f.id, values: { onPath: l.has(M) || l.has(b) } });
510
- });
511
- const m = [`Route from ${String(a)} to ${String(i)}.`];
512
- return r.hasNegativeCycle && m.push("A loop that costs less every time round was found, so no distance past it is meaningful."), {
513
- shape: "path",
514
- fields: [
515
- ...rt,
516
- { name: "distance", kind: "node", type: "number" },
517
- { name: "hasNegativeCycle", kind: "graph", type: "boolean" }
518
- ],
519
- nodes: u,
520
- edges: p,
521
- graph: {
522
- length: d.length,
523
- cost: d.length > 0 ? r.distances.get(i) ?? 0 : 0,
524
- hops: Math.max(d.length - 1, 0),
525
- hasNegativeCycle: r.hasNegativeCycle
526
- },
527
- caveats: C({
528
- method: "bellman-ford",
529
- direction: "undirected",
530
- weight: { attribute: "weight", meaning: "distance" },
531
- notes: m
532
- })
533
- };
534
- }
535
- /**
536
- * Reconstruct the shortest path from predecessors
537
- * @param predecessors - Map of node to its predecessor in the shortest path
538
- * @param source - The source node
539
- * @param target - The target node
540
- * @returns Array of node IDs representing the shortest path
541
- */
542
- reconstructPath(e, t, n) {
543
- const a = [];
544
- let i = n;
545
- for (; i !== null && (a.unshift(i), i !== t); )
546
- i = e.get(i) ?? null;
547
- return a.length === 0 || a[0] !== t ? [] : a;
548
- }
549
- /**
550
- * Get set of edge keys that are part of the path
551
- * @param path - Array of node IDs representing the path
552
- * @returns Set of edge keys in "srcId:dstId" format
553
- */
554
- getPathEdges(e) {
555
- const t = /* @__PURE__ */ new Set();
556
- for (let n = 0; n < e.length - 1; n++)
557
- t.add(P(e[n], e[n + 1]));
558
- return t;
559
- }
560
- };
561
- V.namespace = "graphty", V.type = "bellman-ford", V.scopeInput = "subgraph", V.zodOptionsSchema = na, V.optionsSchema = {
562
- source: {
563
- type: "nodeId",
564
- default: null,
565
- label: "Source Node",
566
- description: "Starting node for shortest path (uses first node if not set)",
567
- required: !1
568
- },
569
- target: {
570
- type: "nodeId",
571
- default: null,
572
- label: "Target Node",
573
- description: "Destination node for shortest path (uses last node if not set)",
574
- required: !1
575
- }
576
- };
577
- let ye = V;
578
- v.register(ye);
579
- const ia = 2048;
580
- function sa(s) {
581
- return {
582
- runId: s,
583
- signal: new AbortController().signal,
584
- report: () => {
585
- },
586
- yieldNow: () => new Promise((e) => {
587
- setTimeout(e, 0);
588
- })
589
- };
590
- }
591
- async function W(s, e, t, n) {
592
- const a = s.length;
593
- let i = performance.now();
594
- for (let o = 0; o < a; o++)
595
- e.signal.throwIfAborted(), n(s[o], o), (o + 1) % ia === 0 && o + 1 < a && (e.report({ phase: t, completed: o + 1, total: a }), performance.now() - i >= bt && (await e.yieldNow(), i = performance.now()));
596
- e.report({ phase: t, completed: a, total: a });
597
- }
598
- class j extends v {
599
- /** What the last run published, kept so a caller that has the algorithm can read the result. */
600
- #e;
601
- /**
602
- * What the last run published.
603
- * @returns The result, or undefined when nothing has run or the graph was empty.
604
- */
605
- get result() {
606
- return this.#e;
607
- }
608
- /**
609
- * Run the algorithm.
610
- *
611
- * This is the 1.10 entry point and it still returns nothing, because `AlgorithmManager` and
612
- * every caller behind `element.runAlgorithm` still expect that. It publishes a result all the
613
- * same: read {@link MetricAlgorithm.result}, or call {@link MetricAlgorithm.measureRun} with a
614
- * run context to get one back.
615
- * @returns A promise that settles once the result has been published.
616
- */
617
- async run() {
618
- await this.measureRun(sa(this.type));
619
- }
620
- /**
621
- * Measure the graph on behalf of a run, and hand the result back.
622
- *
623
- * The run machinery's entry point. A metric's own context is its run context plus the run id,
624
- * so this is the whole of the translation.
625
- * @param context - A signal, a progress channel and a yield.
626
- * @param runId - The id the result is published under.
627
- * @returns The result, or undefined when there was nothing to measure.
628
- * @throws Whatever the context's signal throws once the run has been cancelled.
629
- */
630
- publishResult(e, t) {
631
- return this.measureRun({ runId: t, ...e });
632
- }
633
- /**
634
- * Measure the graph, publish the result, and project the 1.10 view from it.
635
- * @param context - Where progress goes, where cancellation arrives, and which run id to
636
- * publish under.
637
- * @returns The result, or undefined when there was nothing to measure.
638
- * @throws Whatever the context's signal throws once the run has been cancelled, which is a
639
- * `DOMException` named `AbortError`.
640
- */
641
- async measureRun(e) {
642
- const t = this.input("declared"), n = R(t);
643
- if (n.length === 0)
644
- return;
645
- const a = Date.now(), i = await this.measure(e, n), o = wt(this.graph), r = vt(
646
- Nt(this, {
647
- ...o === void 0 ? {} : { labelOf: o },
648
- runId: e.runId,
649
- shape: "node-metric",
650
- fields: this.resultFields(),
651
- measured: { nodes: t.nodeCount, edges: t.edgeCount },
652
- // The only graph-level field a metric publishes itself. Everything else the shape
653
- // promises -- the range, the average, how many elements were measured, how many sit at
654
- // the bottom -- is computed from the column the measurement produced.
655
- graph: { normalization: i.normalization },
656
- nodes: i.nodes,
657
- caveats: i.caveats,
658
- durationMs: Date.now() - a
659
- })
660
- );
661
- return this.#e = r, r;
662
- }
663
- }
664
- const oa = U({
665
- plainName: "Bridging",
666
- technicalName: "betweenness"
667
- }), ce = class ce extends j {
668
- /**
669
- * The fields a betweenness result publishes.
670
- * @returns The uniform node-metric fields.
671
- */
672
- resultFields() {
673
- return oa;
674
- }
675
- /**
676
- * Score every node by the shortest paths that run through it.
677
- * @param context - Where progress goes and where cancellation arrives.
678
- * @param nodeIds - The nodes to measure.
679
- * @returns One raw path count per node, unscaled.
680
- */
681
- async measure(e, t) {
682
- const n = this.algorithmGraph("undirected");
683
- e.report({
684
- phase: "tracing shortest paths",
685
- completed: 0,
686
- total: t.length,
687
- message: "Brandes runs a breadth-first search from every node."
688
- });
689
- const a = St(n);
690
- e.signal.throwIfAborted();
691
- const i = [];
692
- return await W(t, e, "reading scores", (o) => {
693
- const r = a[String(o)];
694
- i.push({ id: o, values: r === void 0 ? {} : { value: r } });
695
- }), {
696
- nodes: i,
697
- // Raw counts. `options.normalized` is not set, so nothing rescales them.
698
- normalization: "none",
699
- caveats: {
700
- exact: !0,
701
- direction: "undirected",
702
- weight: null,
703
- precision: "f64",
704
- method: "brandes",
705
- notes: [
706
- "Every shortest path is counted exactly, over the graph read as undirected.",
707
- "The raw counts are halved, because an undirected shortest path is reached from both ends.",
708
- "Path lengths count edges; edge weights are not read."
709
- ]
710
- }
711
- };
712
- }
713
- };
714
- ce.namespace = "graphty", ce.type = "betweenness", ce.scopeInput = "subgraph";
715
- let be = ce;
716
- v.register(be);
717
- const ra = {
718
- source: {
719
- schema: c.union([c.string(), c.number()]).nullable().default(null),
720
- meta: {
721
- label: "Source Node",
722
- description: "Starting node for BFS traversal (uses first node if not set)"
723
- }
724
- },
725
- targetNode: {
726
- schema: c.union([c.string(), c.number()]).nullable().default(null),
727
- meta: {
728
- label: "Target Node",
729
- description: "Target node for early termination (optional - searches all nodes if not set)",
730
- advanced: !0
731
- }
732
- }
733
- }, q = class q extends E {
734
- constructor() {
735
- super(...arguments), this.legacyOptions = null;
736
- }
737
- /**
738
- * Configure the algorithm with source node
739
- * @param options - Configuration options
740
- * @param options.source - The source node to start BFS from
741
- * @returns This algorithm instance for chaining
742
- * @deprecated Use constructor options instead. This method is kept for backward compatibility.
743
- */
744
- configure(e) {
745
- return this.legacyOptions = e, this;
746
- }
747
- /**
748
- * Walk outwards from one node a level at a time.
749
- *
750
- * A breadth-first walk sorts the graph into layers, so the result is shaped as a layered
751
- * grouping: every reached node carries the level it sits on and the position it was reached
752
- * in, and the element derives how many nodes share each level and how many levels there are.
753
- * A node the walk never reached carries nothing at all -- it is not on level 0, and saying so
754
- * would put every unreachable node in the same layer as the source.
755
- * @param context - What the element gave the run.
756
- * @returns The layered result, or null when there is nothing to walk.
757
- */
758
- async compute(e) {
759
- const t = R(this.input("undirected"));
760
- if (t.length === 0)
761
- return null;
762
- const n = this._schemaOptions.targetNode ?? void 0, a = this.legacyOptions?.source ?? this._schemaOptions.source ?? t[0];
763
- if (n !== void 0)
764
- return this.legacyWalk(e, t, a, n);
765
- const { snapshot: i, run: o } = this.accelerated("breadthFirstSearch", "undirected"), r = this.nodeIndex(i, "source", a);
766
- e.report({ phase: "Walking outwards", total: null });
767
- const { value: d, precision: h } = await o((p, m) => p.breadthFirstSearch(m, r)), u = /* @__PURE__ */ new Map();
768
- for (let p = 0; p < d.visitedCount; p++)
769
- u.set(d.order[p], p);
770
- const l = [];
771
- return await S(e, "Recording levels", t, (p) => {
772
- const m = i.ids.indexOf(p), f = m === L ? L : d.depth[m];
773
- f !== L && l.push({ id: p, values: { level: f, order: u.get(m) } });
774
- }), {
775
- shape: "layered-grouping",
776
- fields: [...et, { name: "order", kind: "node", type: "integer" }],
777
- nodes: l,
778
- caveats: C({
779
- method: "bfs",
780
- direction: "undirected",
781
- weight: null,
782
- precision: h,
783
- notes: [`Walked outwards from ${String(a)}, which is level 0.`]
784
- })
785
- };
786
- }
787
- /**
788
- * Walk with an early stop at a target, on the CPU reference implementation.
789
- * @param context - What the element gave the run.
790
- * @param nodeIds - The nodes to publish for.
791
- * @param source - Where the walk starts.
792
- * @param targetNode - Where it stops.
793
- * @returns The layered result, or null when the source is not in the graph.
794
- */
795
- async legacyWalk(e, t, n, a) {
796
- const i = this.algorithmGraph("undirected");
797
- if (!i.hasNode(n))
798
- return null;
799
- const o = /* @__PURE__ */ new Map(), r = /* @__PURE__ */ new Map();
800
- let d = !1;
801
- e.report({ phase: "Walking outwards", total: null }), Ot(i, n, {
802
- targetNode: a,
803
- visitCallback: (l, p) => {
804
- o.set(l, p), r.set(l, r.size), l === a && (d = !0);
805
- }
806
- });
807
- const h = [];
808
- return await S(e, "Recording levels", t, (l) => {
809
- const p = o.get(l);
810
- p !== void 0 && h.push({ id: l, values: { level: p, order: r.get(l) } });
811
- }), {
812
- shape: "layered-grouping",
813
- fields: [
814
- ...et,
815
- { name: "order", kind: "node", type: "integer" },
816
- { name: "targetFound", kind: "graph", type: "boolean" }
817
- ],
818
- nodes: h,
819
- graph: { targetFound: d },
820
- caveats: C({
821
- method: "bfs",
822
- direction: "undirected",
823
- weight: null,
824
- notes: [
825
- `Walked outwards from ${String(n)}, which is level 0.`,
826
- `Computed on the CPU reference implementation: the walk stops early at ${String(a)}, and no accelerated implementation has an early stop.`
827
- ]
828
- })
829
- };
830
- }
831
- };
832
- q.namespace = "graphty", q.type = "bfs", q.scopeInput = "subgraph", q.zodOptionsSchema = ra, q.optionsSchema = {
833
- source: {
834
- type: "nodeId",
835
- default: null,
836
- label: "Source Node",
837
- description: "Starting node for BFS traversal (uses first node if not set)",
838
- required: !1
839
- },
840
- targetNode: {
841
- type: "nodeId",
842
- default: null,
843
- label: "Target Node",
844
- description: "Target node for early termination (optional - searches all nodes if not set)",
845
- required: !1,
846
- advanced: !0
847
- }
848
- };
849
- let we = q;
850
- v.register(we);
851
- const de = class de extends E {
852
- /**
853
- * Pair up the two sides of a two-sided graph so that as many nodes as possible get a partner.
854
- *
855
- * A matching is a set of edges, so the result is shaped as one: every edge says whether it is
856
- * in the pairing and the element counts how many are. The one number the answer is read for
857
- * is whether the graph has two sides at all -- a graph that does not cannot be paired up, and
858
- * the run says so rather than returning an empty pairing that looks like a bad result.
859
- * @param context - What the element gave the run.
860
- * @returns The edge set, or null when there are no edges to pair.
861
- */
862
- async compute(e) {
863
- const t = ie(this.input("undirected"));
864
- if (t.length === 0)
865
- return null;
866
- const n = this.algorithmGraph("undirected"), a = [
867
- ...Ze("edge", { name: "bipartite", type: "boolean" }),
868
- { name: "side", kind: "node", type: "string" },
869
- { name: "matched", kind: "node", type: "boolean" }
870
- ];
871
- e.report({ phase: "Checking for two sides", total: null });
872
- const i = xt(n);
873
- if (i === null) {
874
- const l = [];
875
- return await S(e, "Marking the pairing", t, (p) => {
876
- l.push({ id: p.id, values: { in: !1 } });
877
- }), {
878
- shape: "edge-set",
879
- fields: a,
880
- edges: l,
881
- graph: { bipartite: !1 },
882
- caveats: C({
883
- method: "bipartite-matching",
884
- direction: "undirected",
885
- weight: null,
886
- notes: ["The graph does not have two sides, so nothing could be paired up."]
887
- })
888
- };
889
- }
890
- e.report({ phase: "Pairing nodes", total: null });
891
- const o = It(n, {
892
- leftNodes: i.left,
893
- rightNodes: i.right
894
- }), r = /* @__PURE__ */ new Set();
895
- for (const [l, p] of o.matching)
896
- r.add(P(l, p)), r.add(P(p, l));
897
- const d = [];
898
- await S(e, "Marking the pairing", t, (l) => {
899
- d.push({ id: l.id, values: { in: r.has(P(l.source, l.target)) } });
900
- });
901
- const h = new Set(o.matching.values()), u = [];
902
- return await S(e, "Marking sides", Array.from(i.left), (l) => {
903
- u.push({ id: l, values: { side: "left", matched: o.matching.has(l) } });
904
- }), await S(e, "Marking sides", Array.from(i.right), (l) => {
905
- u.push({ id: l, values: { side: "right", matched: h.has(l) } });
906
- }), {
907
- shape: "edge-set",
908
- fields: a,
909
- nodes: u,
910
- edges: d,
911
- graph: { bipartite: !0 },
912
- caveats: C({
913
- method: "bipartite-matching",
914
- direction: "undirected",
915
- weight: null,
916
- notes: [`${String(o.size)} of the two sides' nodes found a partner.`]
917
- })
918
- };
919
- }
920
- };
921
- de.namespace = "graphty", de.type = "bipartite-matching", de.scopeInput = "subgraph";
922
- let ve = de;
923
- v.register(ve);
924
- const ca = U({
925
- plainName: "Reach",
926
- technicalName: "closeness"
927
- }), le = class le extends j {
928
- /**
929
- * The fields a closeness result publishes.
930
- * @returns The uniform node-metric fields.
931
- */
932
- resultFields() {
933
- return ca;
934
- }
935
- /**
936
- * Score every node by how short its paths to the rest of the graph are.
937
- * @param context - Where progress goes and where cancellation arrives.
938
- * @param nodeIds - The nodes to measure.
939
- * @returns One score per node, unscaled.
940
- */
941
- async measure(e, t) {
942
- const n = this.algorithmGraph("undirected");
943
- e.report({
944
- phase: "measuring distances",
945
- completed: 0,
946
- total: t.length,
947
- message: "A breadth-first search runs from every node."
948
- });
949
- const a = Ct(n);
950
- e.signal.throwIfAborted();
951
- const i = [];
952
- return await W(t, e, "reading scores", (o) => {
953
- const r = a[String(o)];
954
- i.push({ id: o, values: r === void 0 ? {} : { value: r } });
955
- }), {
956
- nodes: i,
957
- // The algorithm's own figure, published as it was computed.
958
- normalization: "none",
959
- caveats: {
960
- exact: !0,
961
- direction: "undirected",
962
- weight: null,
963
- precision: "f64",
964
- method: "closeness-bfs",
965
- notes: [
966
- "Distances are exact, measured over the graph read as undirected.",
967
- "Distance counts edges; edge weights are not read.",
968
- "A score is the reciprocal of the total distance to the nodes this one can reach, with no correction for how many that is, so a node in a small component scores as though it reached the whole graph."
969
- ]
970
- }
971
- };
972
- }
973
- };
974
- le.namespace = "graphty", le.type = "closeness", le.scopeInput = "subgraph";
975
- let Ne = le;
976
- v.register(Ne);
977
- const he = class he extends E {
978
- /**
979
- * Find the separate pieces of the graph.
980
- *
981
- * A piece is a group in the community shape's sense -- two nodes are in the same one when a
982
- * path joins them -- so this publishes the same `group` per node that every grouping method
983
- * does, and no modularity, which is not defined for a partition nothing optimised.
984
- * @param context - What the element gave the run.
985
- * @returns The community result, or null when there are no nodes to group.
986
- */
987
- async compute(e) {
988
- const t = R(this.input("undirected"));
989
- if (t.length === 0)
990
- return null;
991
- const { snapshot: n, run: a } = this.accelerated("connectedComponents", "undirected"), { ids: i } = n;
992
- e.report({ phase: "Finding pieces", total: null });
993
- const { value: o, precision: r } = await a((h, u) => h.connectedComponents(u)), d = [];
994
- return await S(e, "Grouping nodes", t, (h) => {
995
- const u = i.indexOf(h);
996
- d.push({ id: h, values: { group: o.labels[u] ?? 0 } });
997
- }), {
998
- shape: "community",
999
- fields: oe(!1),
1000
- nodes: d,
1001
- caveats: C({
1002
- method: "connected-components",
1003
- direction: "undirected",
1004
- weight: null,
1005
- precision: r,
1006
- notes: ["Strength: weak. An edge joins its two nodes whichever way it was declared."]
1007
- })
1008
- };
1009
- }
1010
- };
1011
- he.namespace = "graphty", he.type = "connected-components", he.scopeInput = "subgraph";
1012
- let ke = he;
1013
- v.register(ke);
1014
- const da = [
1015
- ...U({ plainName: "Connections", technicalName: "degree", type: "integer", unit: "links" }),
1016
- Be({
1017
- name: "inDegree",
1018
- plainName: "Incoming connections",
1019
- technicalName: "inDegree",
1020
- kind: "node",
1021
- type: "integer"
1022
- }),
1023
- Be({
1024
- name: "outDegree",
1025
- plainName: "Outgoing connections",
1026
- technicalName: "outDegree",
1027
- kind: "node",
1028
- type: "integer"
1029
- })
1030
- ], ue = class ue extends j {
1031
- /**
1032
- * The fields a degree result publishes.
1033
- * @returns The uniform node-metric fields, plus `inDegree` and `outDegree`.
1034
- */
1035
- resultFields() {
1036
- return da;
1037
- }
1038
- /**
1039
- * Count every node's edges.
1040
- * @param context - Where progress goes and where cancellation arrives.
1041
- * @param nodeIds - The nodes to measure.
1042
- * @returns One count per node, unscaled.
1043
- */
1044
- async measure(e, t) {
1045
- const n = this.algorithmGraph("directed"), a = [];
1046
- return await W(t, e, "counting connections", (i) => {
1047
- const o = n.inDegree(i), r = n.outDegree(i);
1048
- a.push({ id: i, values: { value: o + r, inDegree: o, outDegree: r } });
1049
- }), {
1050
- nodes: a,
1051
- // A count of edges, published as it was counted.
1052
- normalization: "none",
1053
- caveats: {
1054
- exact: !0,
1055
- direction: "directed",
1056
- weight: null,
1057
- precision: "f64",
1058
- method: "degree",
1059
- notes: [
1060
- "Counted over the graph as the records declared it, so a node's total is its incoming edges plus its outgoing ones."
1061
- ]
1062
- }
1063
- };
1064
- }
1065
- };
1066
- ue.namespace = "graphty", ue.type = "degree", ue.scopeInput = "subgraph";
1067
- let Se = ue;
1068
- v.register(Se);
1069
- const la = {
1070
- source: {
1071
- schema: c.union([c.string(), c.number()]).nullable().default(null),
1072
- meta: {
1073
- label: "Source Node",
1074
- description: "Starting node for DFS traversal (uses first node if not set)"
1075
- }
1076
- },
1077
- targetNode: {
1078
- schema: c.union([c.string(), c.number()]).nullable().default(null),
1079
- meta: {
1080
- label: "Target Node",
1081
- description: "Target node for early termination (optional - searches all nodes if not set)",
1082
- advanced: !0
1083
- }
1084
- },
1085
- recursive: {
1086
- schema: c.boolean().default(!1),
1087
- meta: {
1088
- label: "Recursive",
1089
- description: "Use recursive implementation instead of iterative (may cause stack overflow on large graphs)",
1090
- advanced: !0
1091
- }
1092
- },
1093
- preOrder: {
1094
- schema: c.boolean().default(!0),
1095
- meta: {
1096
- label: "Pre-Order",
1097
- description: "Visit nodes before their children (pre-order) vs after (post-order)",
1098
- advanced: !0
1099
- }
1100
- }
1101
- }, B = class B extends E {
1102
- constructor() {
1103
- super(...arguments), this.legacyOptions = null;
1104
- }
1105
- /**
1106
- * Configure the algorithm with source node
1107
- * @param options - Configuration options
1108
- * @param options.source - The source node to start DFS from
1109
- * @returns This algorithm instance for chaining
1110
- * @deprecated Use constructor options instead. This method is kept for backward compatibility.
1111
- */
1112
- configure(e) {
1113
- return this.legacyOptions = e, this;
1114
- }
1115
- /**
1116
- * Walk as deep as possible from one node before backtracking.
1117
- *
1118
- * What a depth-first walk produces is one number per node -- the position it was reached in
1119
- * -- so the result is a node metric, and the ranking and range of that column come from the
1120
- * element rather than from here. A node the walk never reached carries no position, and says
1121
- * so with `visited`, because "not reached" and "reached first" are not the same answer.
1122
- * @param context - What the element gave the run.
1123
- * @returns The exploration order, or null when there is nothing to walk.
1124
- */
1125
- async compute(e) {
1126
- const t = R(this.input("undirected"));
1127
- if (t.length === 0)
1128
- return null;
1129
- const n = this.legacyOptions?.source ?? this._schemaOptions.source ?? t[0], { targetNode: a, recursive: i, preOrder: o } = this._schemaOptions;
1130
- if (!t.includes(n))
1131
- return null;
1132
- const r = this.algorithmGraph("undirected");
1133
- e.report({ phase: "Walking deep", total: null });
1134
- const d = Tt(r, n, {
1135
- targetNode: a ?? void 0,
1136
- recursive: i,
1137
- preOrder: o
1138
- }), h = /* @__PURE__ */ new Map();
1139
- d.order.forEach((l, p) => h.set(l, p));
1140
- const u = [];
1141
- return await S(e, "Recording the order", t, (l) => {
1142
- const p = h.get(l);
1143
- u.push({ id: l, values: { value: p, visited: d.visited.has(l) } });
1144
- }), {
1145
- shape: "node-metric",
1146
- fields: [...Ae("node", "integer"), { name: "visited", kind: "node", type: "boolean" }],
1147
- nodes: u,
1148
- caveats: C({
1149
- method: "dfs",
1150
- direction: "undirected",
1151
- weight: null,
1152
- notes: [
1153
- `Walked from ${String(n)}, ${o ? "recording each node as it was reached" : "recording each node as it was left"}.`
1154
- ]
1155
- })
1156
- };
1157
- }
1158
- };
1159
- B.namespace = "graphty", B.type = "dfs", B.scopeInput = "subgraph", B.zodOptionsSchema = la, B.optionsSchema = {
1160
- source: {
1161
- type: "nodeId",
1162
- default: null,
1163
- label: "Source Node",
1164
- description: "Starting node for DFS traversal (uses first node if not set)",
1165
- required: !1
1166
- },
1167
- targetNode: {
1168
- type: "nodeId",
1169
- default: null,
1170
- label: "Target Node",
1171
- description: "Target node for early termination (optional - searches all nodes if not set)",
1172
- required: !1,
1173
- advanced: !0
1174
- },
1175
- recursive: {
1176
- type: "boolean",
1177
- default: !1,
1178
- label: "Recursive",
1179
- description: "Use recursive implementation instead of iterative (may cause stack overflow on large graphs)",
1180
- advanced: !0
1181
- },
1182
- preOrder: {
1183
- type: "boolean",
1184
- default: !0,
1185
- label: "Pre-Order",
1186
- description: "Visit nodes before their children (pre-order) vs after (post-order)",
1187
- advanced: !0
1188
- }
1189
- };
1190
- let Oe = B;
1191
- v.register(Oe);
1192
- const ha = {
1193
- source: {
1194
- schema: c.union([c.string(), c.number()]).nullable().default(null),
1195
- meta: {
1196
- label: "Source Node",
1197
- description: "Starting node for shortest path (uses first node if not set)"
1198
- }
1199
- },
1200
- target: {
1201
- schema: c.union([c.string(), c.number()]).nullable().default(null),
1202
- meta: {
1203
- label: "Target Node",
1204
- description: "Destination node for shortest path (uses last node if not set)"
1205
- }
1206
- },
1207
- bidirectional: {
1208
- schema: c.boolean().default(!0),
1209
- meta: {
1210
- label: "Bidirectional Search",
1211
- description: "Accepted and ignored: the shortest-path search relaxes outwards from the source in one direction, and the route it finds is the same one either way",
1212
- advanced: !0
1213
- }
1214
- }
1215
- }, K = class K extends E {
1216
- constructor() {
1217
- super(...arguments), this.legacyOptions = null;
1218
- }
1219
- /**
1220
- * Configure the algorithm with source and optional target nodes
1221
- * @param options - Configuration options
1222
- * @param options.source - The source node for shortest path computation
1223
- * @param options.target - The target node (optional)
1224
- * @returns This algorithm instance for chaining
1225
- * @deprecated Use constructor options instead. This method is kept for backward compatibility.
1226
- */
1227
- configure(e) {
1228
- return this.legacyOptions = e, this;
1229
- }
1230
- /**
1231
- * Find the cheapest route from the source to the target.
1232
- *
1233
- * Publishes the path shape's uniform fields: whether each node and edge is on the route,
1234
- * where each node sits along it counting the source as 0, and the route's size and cost. It
1235
- * also publishes how far every reachable node is from the source, which is what makes one
1236
- * run answer both "show me the way there" and "how far is everything".
1237
- * @param context - What the element gave the run.
1238
- * @returns The route, or null when there are no nodes to search.
1239
- */
1240
- async compute(e) {
1241
- const t = this.input("undirected"), n = R(t);
1242
- if (n.length === 0)
1243
- return null;
1244
- const { snapshot: a, edgeRemap: i, run: o } = this.accelerated("sssp", "undirected"), { ids: r } = a, d = this.legacyOptions?.source ?? this._schemaOptions.source ?? r.idOf(0), h = this.legacyOptions?.target ?? this._schemaOptions.target ?? r.idOf(a.nodeCount - 1), u = this.nodeIndex(a, "source", d), l = this.nodeIndex(a, "target", h);
1245
- e.report({ phase: "Searching for the route", total: null });
1246
- const { value: p, precision: m } = await o((N, z) => N.sssp(z, u)), f = p.pathTo(l), M = new Set(p.pathEdges(l)), b = /* @__PURE__ */ new Map();
1247
- f.forEach((N, z) => b.set(N, z));
1248
- const w = [];
1249
- await S(e, "Marking the route", n, (N) => {
1250
- const z = r.indexOf(N), y = z === L ? void 0 : b.get(z);
1251
- w.push({
1252
- id: N,
1253
- values: {
1254
- onPath: y !== void 0,
1255
- order: y,
1256
- distance: z === L ? 1 / 0 : p.dist[z]
1257
- }
1258
- });
1259
- });
1260
- const O = ie(t), { src: I, weights: x } = t.graph.edgeList(), T = /* @__PURE__ */ new Map();
1261
- for (const N of O) {
1262
- const z = i === null ? N.row : i[N.row] ?? L;
1263
- if (!M.has(z))
1264
- continue;
1265
- const y = t.graph.directed ? `${String(z)}>${String(I[N.row])}` : String(z), $ = x === null ? 1 : x[N.row], se = T.get(y);
1266
- (se === void 0 || $ < se.weight) && T.set(y, { row: N.row, weight: $ });
1267
- }
1268
- const F = new Set([...T.values()].map((N) => N.row)), D = [];
1269
- return await S(e, "Marking the route", O, (N) => {
1270
- D.push({ id: N.id, values: { onPath: F.has(N.row) } });
1271
- }), {
1272
- shape: "path",
1273
- fields: [...rt, { name: "distance", kind: "node", type: "number" }],
1274
- nodes: w,
1275
- edges: D,
1276
- graph: {
1277
- length: f.length,
1278
- cost: f.length === 0 ? 0 : p.dist[l],
1279
- hops: Math.max(f.length - 1, 0)
1280
- },
1281
- caveats: C({
1282
- method: "dijkstra",
1283
- direction: "undirected",
1284
- weight: { attribute: "weight", meaning: "distance" },
1285
- precision: m,
1286
- notes: f.length === 0 ? [`No route runs from ${String(d)} to ${String(h)}.`] : [`Route from ${String(d)} to ${String(h)}.`]
1287
- })
1288
- };
1289
- }
1290
- };
1291
- K.namespace = "graphty", K.type = "dijkstra", K.scopeInput = "subgraph", K.parallelEdges = "min", K.zodOptionsSchema = ha, K.optionsSchema = {
1292
- source: {
1293
- type: "nodeId",
1294
- default: null,
1295
- label: "Source Node",
1296
- description: "Starting node for shortest path (uses first node if not set)",
1297
- required: !1
1298
- },
1299
- target: {
1300
- type: "nodeId",
1301
- default: null,
1302
- label: "Target Node",
1303
- description: "Destination node for shortest path (uses last node if not set)",
1304
- required: !1
1305
- },
1306
- bidirectional: {
1307
- type: "boolean",
1308
- default: !0,
1309
- label: "Bidirectional Search",
1310
- description: "Accepted and ignored: the shortest-path search relaxes outwards from the source in one direction, and the route it finds is the same one either way",
1311
- advanced: !0
1312
- }
1313
- };
1314
- let xe = K;
1315
- v.register(xe);
1316
- const ua = {
1317
- maxIterations: {
1318
- schema: c.number().int().min(1).max(1e4).default(1e3),
1319
- meta: {
1320
- label: "Max Iterations",
1321
- description: "Maximum power iterations",
1322
- advanced: !0
1323
- }
1324
- },
1325
- tolerance: {
1326
- schema: c.number().min(1e-10).max(0.01).default(1e-6),
1327
- meta: {
1328
- label: "Tolerance",
1329
- description: "Convergence threshold",
1330
- advanced: !0
1331
- }
1332
- },
1333
- normalized: {
1334
- schema: c.boolean().default(!0),
1335
- meta: {
1336
- label: "Normalized",
1337
- description: "Whether to normalize the final scores",
1338
- advanced: !0
1339
- }
1340
- },
1341
- mode: {
1342
- schema: c.enum(["in", "out", "total"]).default("total"),
1343
- meta: {
1344
- label: "Direction Mode",
1345
- description: "Direction mode for directed graphs",
1346
- advanced: !0
1347
- }
1348
- },
1349
- endpoints: {
1350
- schema: c.boolean().default(!1),
1351
- meta: {
1352
- label: "Include Endpoints",
1353
- description: "Whether to include endpoints in path calculations",
1354
- advanced: !0
1355
- }
1356
- }
1357
- }, pa = U({
1358
- plainName: "Influence by association",
1359
- technicalName: "eigenvector score"
1360
- }), H = class H extends j {
1361
- /**
1362
- * The fields an eigenvector result publishes.
1363
- * @returns The uniform node-metric fields.
1364
- */
1365
- resultFields() {
1366
- return pa;
1367
- }
1368
- /**
1369
- * Score every node by the influence of its neighbours.
1370
- * @param context - Where progress goes and where cancellation arrives.
1371
- * @param nodeIds - The nodes to measure.
1372
- * @returns One score per node, scaled as the options asked for.
1373
- */
1374
- async measure(e, t) {
1375
- const { maxIterations: n, tolerance: a, normalized: i, mode: o, endpoints: r } = this.schemaOptions, d = this._schemaOptions.startVector ?? void 0, h = o !== "total" && this.input("declared").graph.directed, u = this.algorithmGraph(h ? "directed" : "undirected");
1376
- e.report({
1377
- phase: "iterating",
1378
- completed: 0,
1379
- total: t.length,
1380
- message: `Power iteration, up to ${String(n)} passes.`
1381
- });
1382
- let l;
1383
- try {
1384
- l = zt(u, {
1385
- normalized: i,
1386
- maxIterations: n,
1387
- tolerance: a,
1388
- mode: o,
1389
- endpoints: r,
1390
- startVector: d
1391
- });
1392
- } catch (m) {
1393
- if (!(m instanceof Et))
1394
- throw m;
1395
- const f = `${H.namespace}:${H.type}`;
1396
- throw new ct({
1397
- code: "E_NOT_CONVERGED",
1398
- message: `Eigenvector centrality did not converge in ${String(n)} iterations (tolerance ${String(a)}). Raise the maxIterations param (up to 10000), or loosen tolerance, and run it again.`,
1399
- source: "run",
1400
- target: { kind: "run", id: e.runId },
1401
- details: { algorithm: f, maxIterations: n, tolerance: a },
1402
- cause: m
1403
- });
1404
- }
1405
- e.signal.throwIfAborted();
1406
- const p = [];
1407
- return await W(t, e, "reading scores", (m) => {
1408
- const f = l[String(m)];
1409
- p.push({ id: m, values: f === void 0 ? {} : { value: f } });
1410
- }), {
1411
- nodes: p,
1412
- // With `normalized`, the algorithm rescales its eigenvector so the lowest score is 0
1413
- // and the highest is 1. Without it the raw unit-length eigenvector is published.
1414
- normalization: i ? "min-max" : "none",
1415
- caveats: {
1416
- exact: !0,
1417
- direction: h ? "directed" : "undirected",
1418
- weight: null,
1419
- precision: "f64",
1420
- method: "power-iteration",
1421
- // Measured, not assumed: the algorithm throws when it hits its iteration cap, and
1422
- // that becomes E_NOT_CONVERGED above, so a result exists only when it converged.
1423
- converged: !0,
1424
- notes: [
1425
- `Power iteration reached a tolerance of ${String(a)} within ${String(n)} passes.`,
1426
- ...h ? [
1427
- o === "in" ? "A node is scored by the nodes whose edges point at it." : "A node is scored by the nodes its edges point at."
1428
- ] : [],
1429
- "Edge weights are not read."
1430
- ]
1431
- }
1432
- };
1433
- }
1434
- };
1435
- H.namespace = "graphty", H.type = "eigenvector", H.scopeInput = "subgraph", H.zodOptionsSchema = ua, H.optionsSchema = {
1436
- maxIterations: {
1437
- type: "integer",
1438
- default: 1e3,
1439
- label: "Max Iterations",
1440
- description: "Maximum power iterations",
1441
- min: 1,
1442
- max: 1e4,
1443
- advanced: !0
1444
- },
1445
- tolerance: {
1446
- type: "number",
1447
- default: 1e-6,
1448
- label: "Tolerance",
1449
- description: "Convergence threshold",
1450
- min: 1e-10,
1451
- max: 0.01,
1452
- advanced: !0
1453
- },
1454
- normalized: {
1455
- type: "boolean",
1456
- default: !0,
1457
- label: "Normalized",
1458
- description: "Whether to normalize the final scores",
1459
- advanced: !0
1460
- },
1461
- mode: {
1462
- type: "select",
1463
- default: "total",
1464
- label: "Direction Mode",
1465
- description: "Direction mode for directed graphs",
1466
- options: [
1467
- { value: "total", label: "Total (both directions)" },
1468
- { value: "in", label: "In-degree (incoming edges)" },
1469
- { value: "out", label: "Out-degree (outgoing edges)" }
1470
- ],
1471
- advanced: !0
1472
- },
1473
- endpoints: {
1474
- type: "boolean",
1475
- default: !1,
1476
- label: "Include Endpoints",
1477
- description: "Whether to include endpoints in path calculations",
1478
- advanced: !0
1479
- }
1480
- // Note: startVector is a Map type - programmatic only, not in schema
1481
- };
1482
- let Ie = H;
1483
- v.register(Ie);
1484
- const pe = class pe extends E {
1485
- /**
1486
- * Measure the distance between every pair of nodes, and give each node the distance to the
1487
- * node furthest from it.
1488
- *
1489
- * Asked for every pair, the shortest-path question stops being one route: there is no route
1490
- * here to put an `onPath` on. What it produces instead is a measurement of every node -- its
1491
- * eccentricity, the distance to the furthest node it can reach -- so the result is shaped as
1492
- * a node metric, and the widest and narrowest of those distances, the graph's diameter and
1493
- * radius, ride along as facts about the whole graph.
1494
- *
1495
- * THE SHAPE IS WHAT MAKES IT PAINTABLE. A shape of "fact" declares no per-element field, so
1496
- * the element would have had nothing to colour by and a consumer asking to encode
1497
- * eccentricity on node colour would have been refused -- with no way to do it by hand
1498
- * either, which is the case that has to be fixed here rather than worked around outside.
1499
- * @param context - What the element gave the run.
1500
- * @returns The all-pairs measurement, or null when there are no nodes to measure.
1501
- */
1502
- async compute(e) {
1503
- const t = R(this.input("undirected"));
1504
- if (t.length === 0)
1505
- return null;
1506
- const n = this.algorithmGraph("undirected");
1507
- e.report({ phase: "Measuring every pair", total: null });
1508
- const a = Mt(n), i = /* @__PURE__ */ new Map();
1509
- await S(e, "Measuring reach", t, (h) => {
1510
- const u = a.distances.get(h);
1511
- if (u === void 0) {
1512
- i.set(h, 1 / 0);
1513
- return;
1514
- }
1515
- let l = 0;
1516
- for (const p of u.values())
1517
- isFinite(p) && p > l && (l = p);
1518
- i.set(h, l);
1519
- });
1520
- let o = 0, r = 1 / 0;
1521
- for (const h of i.values())
1522
- isFinite(h) && (o = Math.max(o, h), r = Math.min(r, h));
1523
- r === 1 / 0 && (r = 0);
1524
- const d = [];
1525
- return await S(e, "Publishing distances", t, (h) => {
1526
- const u = i.get(h) ?? 1 / 0;
1527
- isFinite(u) && d.push({ id: h, values: { value: u } });
1528
- }), {
1529
- shape: "node-metric",
1530
- fields: [
1531
- ...Ae("node"),
1532
- { name: "diameter", kind: "graph", type: "number" },
1533
- { name: "radius", kind: "graph", type: "number" },
1534
- { name: "hasNegativeCycle", kind: "graph", type: "boolean" }
1535
- ],
1536
- nodes: d,
1537
- graph: { diameter: o, radius: r, hasNegativeCycle: a.hasNegativeCycle },
1538
- caveats: C({
1539
- method: "floyd-warshall",
1540
- direction: "undirected",
1541
- weight: { attribute: "weight", meaning: "distance" },
1542
- notes: [
1543
- "Every pair was measured. Each node's value is its eccentricity: the distance to the furthest node it can reach."
1544
- ]
1545
- })
1546
- };
1547
- }
1548
- };
1549
- pe.namespace = "graphty", pe.type = "floyd-warshall", pe.scopeInput = "subgraph";
1550
- let Ce = pe;
1551
- v.register(Ce);
1552
- const ma = {
1553
- maxCommunities: {
1554
- schema: c.number().int().min(0).max(100).default(0),
1555
- meta: {
1556
- label: "Max Communities",
1557
- description: "Stop when this many communities reached (0 = find optimal)"
1558
- }
1559
- },
1560
- minCommunitySize: {
1561
- schema: c.number().int().min(1).max(100).default(1),
1562
- meta: {
1563
- label: "Min Community Size",
1564
- description: "Minimum nodes per community",
1565
- advanced: !0
1566
- }
1567
- },
1568
- maxIterations: {
1569
- schema: c.number().int().min(1).max(1e4).default(1e3),
1570
- meta: {
1571
- label: "Max Iterations",
1572
- description: "Maximum edge removal iterations before stopping",
1573
- advanced: !0
1574
- }
1575
- }
1576
- }, Y = class Y extends E {
1577
- /**
1578
- * Group the nodes into communities by cutting the edges the most routes run through.
1579
- *
1580
- * The method produces a dendrogram -- one partition per cut -- and the run publishes the cut
1581
- * that scored the highest modularity, with that score. A graph with no edges falls apart at
1582
- * the first step, and every node is then its own community.
1583
- * @param context - What the element gave the run.
1584
- * @returns The community result, or null when there are no nodes to group.
1585
- */
1586
- async compute(e) {
1587
- const t = R(this.input("undirected"));
1588
- if (t.length === 0)
1589
- return null;
1590
- const { maxCommunities: n, minCommunitySize: a, maxIterations: i } = this.schemaOptions, o = this.algorithmGraph("undirected");
1591
- e.report({ phase: "Cutting bridges", total: null });
1592
- const r = Dt(o, {
1593
- maxCommunities: n > 0 ? n : void 0,
1594
- minCommunitySize: a,
1595
- maxIterations: i
1596
- }), d = n > 0 ? r.filter((m) => m.communities.length <= n) : r, u = (d.length > 0 ? d : r.slice(0, 1)).reduce(
1597
- (m, f) => m === void 0 || f.modularity > m.modularity ? f : m,
1598
- void 0
1599
- ), l = /* @__PURE__ */ new Map();
1600
- if (u !== void 0)
1601
- for (let m = 0; m < u.communities.length; m++)
1602
- for (const f of u.communities[m])
1603
- l.set(f, m);
1604
- else
1605
- t.forEach((m, f) => l.set(m, f));
1606
- const p = [];
1607
- return await S(e, "Grouping nodes", t, (m) => {
1608
- p.push({ id: m, values: { group: l.get(m) ?? 0 } });
1609
- }), {
1610
- shape: "community",
1611
- fields: oe(!0),
1612
- nodes: p,
1613
- graph: { modularity: u?.modularity ?? 0 },
1614
- caveats: C({
1615
- method: "girvan-newman",
1616
- direction: "undirected",
1617
- weight: { attribute: "weight", meaning: "strength" },
1618
- notes: u === void 0 ? ["No edge could be cut, so every node is its own community."] : [`Kept the best of ${String(r.length)} successive cuts.`]
1619
- })
1620
- };
1621
- }
1622
- };
1623
- Y.namespace = "graphty", Y.type = "girvan-newman", Y.scopeInput = "subgraph", Y.zodOptionsSchema = ma, Y.optionsSchema = {
1624
- maxCommunities: {
1625
- type: "integer",
1626
- default: 0,
1627
- label: "Max Communities",
1628
- description: "Stop when this many communities reached (0 = find optimal)",
1629
- min: 0,
1630
- max: 100
1631
- },
1632
- minCommunitySize: {
1633
- type: "integer",
1634
- default: 1,
1635
- label: "Min Community Size",
1636
- description: "Minimum nodes per community",
1637
- min: 1,
1638
- max: 100,
1639
- advanced: !0
1640
- },
1641
- maxIterations: {
1642
- type: "integer",
1643
- default: 1e3,
1644
- label: "Max Iterations",
1645
- description: "Maximum edge removal iterations before stopping",
1646
- min: 1,
1647
- max: 1e4,
1648
- advanced: !0
1649
- }
1650
- };
1651
- let Te = Y;
1652
- v.register(Te);
1653
- const ga = {
1654
- maxIterations: {
1655
- schema: c.number().int().min(1).max(1e3).default(100),
1656
- meta: {
1657
- label: "Max Iterations",
1658
- description: "Maximum iterations for hub/authority computation",
1659
- advanced: !0
1660
- }
1661
- },
1662
- tolerance: {
1663
- schema: c.number().min(1e-10).max(0.01).default(1e-6),
1664
- meta: {
1665
- label: "Tolerance",
1666
- description: "Convergence threshold",
1667
- advanced: !0
1668
- }
1669
- },
1670
- normalized: {
1671
- schema: c.boolean().default(!0),
1672
- meta: {
1673
- label: "Normalized",
1674
- description: "Whether to normalize the final hub/authority scores",
1675
- advanced: !0
1676
- }
1677
- },
1678
- mode: {
1679
- schema: c.enum(["in", "out", "total"]).default("total"),
1680
- meta: {
1681
- label: "Direction Mode",
1682
- description: "Direction mode for directed graphs",
1683
- advanced: !0
1684
- }
1685
- },
1686
- endpoints: {
1687
- schema: c.boolean().default(!1),
1688
- meta: {
1689
- label: "Include Endpoints",
1690
- description: "Whether to include endpoints in path calculations",
1691
- advanced: !0
1692
- }
1693
- }
1694
- }, fa = [
1695
- ...U({ plainName: "Hub and authority", technicalName: "combined HITS score" }),
1696
- Be({ name: "hub", plainName: "Hub", technicalName: "hub score", kind: "node", type: "number" }),
1697
- Be({
1698
- name: "authority",
1699
- plainName: "Authority",
1700
- technicalName: "authority score",
1701
- kind: "node",
1702
- type: "number"
1703
- })
1704
- ], Z = class Z extends j {
1705
- /**
1706
- * The fields a HITS result publishes.
1707
- * @returns The uniform node-metric fields, plus `hub` and `authority`.
1708
- */
1709
- resultFields() {
1710
- return fa;
1711
- }
1712
- /**
1713
- * Score every node as a hub and as an authority.
1714
- * @param context - Where progress goes and where cancellation arrives.
1715
- * @param nodeIds - The nodes to measure.
1716
- * @returns The combined score per node, with the two halves beside it.
1717
- */
1718
- async measure(e, t) {
1719
- const { maxIterations: n, tolerance: a, normalized: i, mode: o, endpoints: r } = this.schemaOptions, d = this.algorithmGraph("directed");
1720
- e.report({
1721
- phase: "iterating",
1722
- completed: 0,
1723
- total: t.length,
1724
- message: `Hub and authority scores refine each other, up to ${String(n)} passes.`
1725
- });
1726
- const h = Pt(d, {
1727
- maxIterations: n,
1728
- tolerance: a,
1729
- normalized: i,
1730
- mode: o,
1731
- endpoints: r
1732
- });
1733
- e.signal.throwIfAborted();
1734
- const u = [];
1735
- return await W(t, e, "reading scores", (l) => {
1736
- const p = h.hubs[String(l)], m = h.authorities[String(l)];
1737
- if (p === void 0 || m === void 0) {
1738
- u.push({ id: l, values: {} });
1739
- return;
1740
- }
1741
- u.push({ id: l, values: { value: (p + m) / 2, hub: p, authority: m } });
1742
- }), {
1743
- nodes: u,
1744
- // The combined score is the average of two vectors the algorithm scaled, which leaves
1745
- // it scaled by neither of the two rules this field names. The notes say what was done.
1746
- normalization: "none",
1747
- caveats: {
1748
- exact: !0,
1749
- direction: "directed",
1750
- weight: null,
1751
- precision: "f64",
1752
- method: "hits",
1753
- // `converged` and `iterations` are deliberately absent: the implementation stops
1754
- // either at its tolerance or at its iteration cap and reports neither.
1755
- notes: [
1756
- "The published value is the average of this node's hub score and its authority score.",
1757
- i ? "The hub and authority vectors each have unit length, which is how the iteration leaves them." : "The hub and authority vectors were each divided by their own highest score.",
1758
- `Iteration stops at a tolerance of ${String(a)} or after ${String(n)} passes, whichever comes first.`,
1759
- "Whether it reached the tolerance is not reported by the implementation, so this run cannot say whether it converged.",
1760
- "Edge weights are not read."
1761
- ]
1762
- }
1763
- };
1764
- }
1765
- };
1766
- Z.namespace = "graphty", Z.type = "hits", Z.scopeInput = "subgraph", Z.zodOptionsSchema = ga, Z.optionsSchema = {
1767
- maxIterations: {
1768
- type: "integer",
1769
- default: 100,
1770
- label: "Max Iterations",
1771
- description: "Maximum iterations for hub/authority computation",
1772
- min: 1,
1773
- max: 1e3,
1774
- advanced: !0
1775
- },
1776
- tolerance: {
1777
- type: "number",
1778
- default: 1e-6,
1779
- label: "Tolerance",
1780
- description: "Convergence threshold",
1781
- min: 1e-10,
1782
- max: 0.01,
1783
- advanced: !0
1784
- },
1785
- normalized: {
1786
- type: "boolean",
1787
- default: !0,
1788
- label: "Normalized",
1789
- description: "Whether to normalize the final hub/authority scores",
1790
- advanced: !0
1791
- },
1792
- mode: {
1793
- type: "select",
1794
- default: "total",
1795
- label: "Direction Mode",
1796
- description: "Direction mode for directed graphs",
1797
- options: [
1798
- { value: "total", label: "Total (both directions)" },
1799
- { value: "in", label: "In-degree (incoming edges)" },
1800
- { value: "out", label: "Out-degree (outgoing edges)" }
1801
- ],
1802
- advanced: !0
1803
- },
1804
- endpoints: {
1805
- type: "boolean",
1806
- default: !1,
1807
- label: "Include Endpoints",
1808
- description: "Whether to include endpoints in path calculations",
1809
- advanced: !0
1810
- }
1811
- };
1812
- let ze = Z;
1813
- v.register(ze);
1814
- const ya = {
1815
- alpha: {
1816
- schema: c.number().min(0.01).max(0.5).default(0.1),
1817
- meta: {
1818
- label: "Alpha (Attenuation)",
1819
- description: "Attenuation factor - must be less than 1/λmax",
1820
- step: 0.01
1821
- }
1822
- },
1823
- beta: {
1824
- schema: c.number().min(0).max(10).default(1),
1825
- meta: {
1826
- label: "Beta (Base Weight)",
1827
- description: "Base centrality added to each node",
1828
- step: 0.1,
1829
- advanced: !0
1830
- }
1831
- },
1832
- maxIterations: {
1833
- schema: c.number().int().min(1).max(1e3).default(100),
1834
- meta: {
1835
- label: "Max Iterations",
1836
- description: "Maximum iterations",
1837
- advanced: !0
1838
- }
1839
- },
1840
- tolerance: {
1841
- schema: c.number().min(1e-10).max(0.01).default(1e-6),
1842
- meta: {
1843
- label: "Tolerance",
1844
- description: "Convergence threshold",
1845
- advanced: !0
1846
- }
1847
- },
1848
- normalized: {
1849
- schema: c.boolean().default(!0),
1850
- meta: {
1851
- label: "Normalized",
1852
- description: "Whether to normalize the final scores",
1853
- advanced: !0
1854
- }
1855
- },
1856
- mode: {
1857
- schema: c.enum(["in", "out", "total"]).default("total"),
1858
- meta: {
1859
- label: "Direction Mode",
1860
- description: "Direction mode for directed graphs",
1861
- advanced: !0
1862
- }
1863
- },
1864
- endpoints: {
1865
- schema: c.boolean().default(!1),
1866
- meta: {
1867
- label: "Include Endpoints",
1868
- description: "Whether to include endpoints in path calculations",
1869
- advanced: !0
1870
- }
1871
- }
1872
- }, ba = U({
1873
- plainName: "Influence at a distance",
1874
- technicalName: "Katz score"
1875
- }), J = class J extends j {
1876
- /**
1877
- * The fields a Katz result publishes.
1878
- * @returns The uniform node-metric fields.
1879
- */
1880
- resultFields() {
1881
- return ba;
1882
- }
1883
- /**
1884
- * Score every node by the paths that reach it.
1885
- * @param context - Where progress goes and where cancellation arrives.
1886
- * @param nodeIds - The nodes to measure.
1887
- * @returns One score per node, scaled as the options asked for.
1888
- */
1889
- async measure(e, t) {
1890
- const { alpha: n, beta: a, maxIterations: i, tolerance: o, normalized: r, mode: d, endpoints: h } = this.schemaOptions, u = this.algorithmGraph("undirected");
1891
- e.report({
1892
- phase: "iterating",
1893
- completed: 0,
1894
- total: t.length,
1895
- message: `Attenuated path sums, up to ${String(i)} passes.`
1896
- });
1897
- const l = Rt(u, {
1898
- normalized: r,
1899
- alpha: n,
1900
- beta: a,
1901
- maxIterations: i,
1902
- tolerance: o,
1903
- mode: d,
1904
- endpoints: h
1905
- });
1906
- e.signal.throwIfAborted();
1907
- const p = [];
1908
- return await W(t, e, "reading scores", (m) => {
1909
- const f = l[String(m)];
1910
- p.push({ id: m, values: f === void 0 ? {} : { value: f } });
1911
- }), {
1912
- nodes: p,
1913
- // With `normalized`, the algorithm rescales so the lowest score is 0 and the highest
1914
- // is 1. Without it the raw attenuated sums are published.
1915
- normalization: r ? "min-max" : "none",
1916
- caveats: {
1917
- exact: !0,
1918
- direction: "undirected",
1919
- weight: null,
1920
- precision: "f64",
1921
- method: "katz-iteration",
1922
- // `converged` and `iterations` are deliberately absent: the implementation stops
1923
- // either at its tolerance or at its iteration cap and reports neither.
1924
- notes: [
1925
- `Every node starts with a base influence of ${String(a)}, and a path of length k contributes ${String(n)} to the power k.`,
1926
- `Iteration stops at a tolerance of ${String(o)} or after ${String(i)} passes, whichever comes first.`,
1927
- "Whether it reached the tolerance is not reported by the implementation, so this run cannot say whether it converged.",
1928
- "Edge weights are not read."
1929
- ]
1930
- }
1931
- };
1932
- }
1933
- };
1934
- J.namespace = "graphty", J.type = "katz", J.scopeInput = "subgraph", J.zodOptionsSchema = ya, J.optionsSchema = {
1935
- alpha: {
1936
- type: "number",
1937
- default: 0.1,
1938
- label: "Alpha (Attenuation)",
1939
- description: "Attenuation factor - must be less than 1/λmax",
1940
- min: 0.01,
1941
- max: 0.5,
1942
- step: 0.01
1943
- },
1944
- beta: {
1945
- type: "number",
1946
- default: 1,
1947
- label: "Beta (Base Weight)",
1948
- description: "Base centrality added to each node",
1949
- min: 0,
1950
- max: 10,
1951
- step: 0.1,
1952
- advanced: !0
1953
- },
1954
- maxIterations: {
1955
- type: "integer",
1956
- default: 100,
1957
- label: "Max Iterations",
1958
- description: "Maximum iterations",
1959
- min: 1,
1960
- max: 1e3,
1961
- advanced: !0
1962
- },
1963
- tolerance: {
1964
- type: "number",
1965
- default: 1e-6,
1966
- label: "Tolerance",
1967
- description: "Convergence threshold",
1968
- min: 1e-10,
1969
- max: 0.01,
1970
- advanced: !0
1971
- },
1972
- normalized: {
1973
- type: "boolean",
1974
- default: !0,
1975
- label: "Normalized",
1976
- description: "Whether to normalize the final scores",
1977
- advanced: !0
1978
- },
1979
- mode: {
1980
- type: "select",
1981
- default: "total",
1982
- label: "Direction Mode",
1983
- description: "Direction mode for directed graphs",
1984
- options: [
1985
- { value: "total", label: "Total (both directions)" },
1986
- { value: "in", label: "In-degree (incoming edges)" },
1987
- { value: "out", label: "Out-degree (outgoing edges)" }
1988
- ],
1989
- advanced: !0
1990
- },
1991
- endpoints: {
1992
- type: "boolean",
1993
- default: !1,
1994
- label: "Include Endpoints",
1995
- description: "Whether to include endpoints in path calculations",
1996
- advanced: !0
1997
- }
1998
- };
1999
- let Ee = J;
2000
- v.register(Ee);
2001
- const wa = U({
2002
- plainName: "Core depth",
2003
- technicalName: "core number",
2004
- type: "integer"
2005
- }), me = class me extends j {
2006
- /**
2007
- * The fields a k-core result publishes.
2008
- * @returns The uniform node-metric fields.
2009
- */
2010
- resultFields() {
2011
- return wa;
2012
- }
2013
- /**
2014
- * Give every node its core number.
2015
- * @param context - Where progress goes and where cancellation arrives.
2016
- * @param nodeIds - The nodes to measure.
2017
- * @returns One core number per node.
2018
- */
2019
- async measure(e, t) {
2020
- const n = this.algorithmGraph("undirected");
2021
- e.report({ phase: "peeling cores", total: null });
2022
- const { coreness: a } = $t(n);
2023
- e.signal.throwIfAborted();
2024
- const i = [];
2025
- return await W(t, e, "reading core numbers", (o) => {
2026
- const r = a.get(String(o));
2027
- i.push({ id: o, values: r === void 0 ? {} : { value: r } });
2028
- }), {
2029
- nodes: i,
2030
- // A count of neighbours, published as it was counted.
2031
- normalization: "none",
2032
- caveats: {
2033
- exact: !0,
2034
- direction: "undirected",
2035
- weight: null,
2036
- precision: "f64",
2037
- method: "k-core",
2038
- notes: ["Counted over the graph read as undirected; edge weights are not read."]
2039
- }
2040
- };
2041
- }
2042
- };
2043
- me.namespace = "graphty", me.type = "k-core", me.scopeInput = "subgraph";
2044
- let Me = me;
2045
- v.register(Me);
2046
- const ge = class ge extends E {
2047
- /**
2048
- * Find the cheapest set of edges that still joins every node.
2049
- *
2050
- * A spanning tree is a set of edges, so the result is shaped as one: every edge says whether
2051
- * it is in the network, the element counts how many are, and the run publishes the one number
2052
- * the answer is actually read for -- what the network costs in total.
2053
- * @param context - What the element gave the run.
2054
- * @returns The edge set, or null when there are no edges to choose from.
2055
- */
2056
- async compute(e) {
2057
- const t = ie(this.input("undirected"));
2058
- if (t.length === 0)
2059
- return null;
2060
- const { edgeRemap: n, run: a } = this.accelerated("minimumSpanningTree", "undirected");
2061
- e.report({ phase: "Choosing edges", total: null });
2062
- const { value: i, precision: o } = await a((h, u) => h.minimumSpanningTree(u)), r = new Set(i.edges), d = [];
2063
- return await S(e, "Marking the network", t, (h) => {
2064
- const u = n === null ? h.row : n[h.row] ?? L;
2065
- d.push({ id: h.id, values: { in: r.has(u) } });
2066
- }), {
2067
- shape: "edge-set",
2068
- fields: Ze("edge", { name: "totalWeight", type: "number" }),
2069
- edges: d,
2070
- graph: { totalWeight: i.totalWeight },
2071
- caveats: C({
2072
- method: "kruskal",
2073
- direction: "undirected",
2074
- weight: { attribute: "weight", meaning: "distance" },
2075
- precision: o,
2076
- notes: [`The tree joins the graph with ${String(i.edges.length)} edges.`]
2077
- })
2078
- };
2079
- }
2080
- };
2081
- ge.namespace = "graphty", ge.type = "kruskal", ge.scopeInput = "subgraph";
2082
- let De = ge;
2083
- v.register(De);
2084
- const va = {
2085
- maxIterations: {
2086
- schema: c.number().int().min(1).max(500).default(100),
2087
- meta: {
2088
- label: "Max Iterations",
2089
- description: "Maximum label propagation rounds"
2090
- }
2091
- },
2092
- randomSeed: {
2093
- schema: c.number().int().min(0).max(2147483647).default(42),
2094
- meta: {
2095
- label: "Random Seed",
2096
- description: "Seed for reproducible tie-breaking",
2097
- advanced: !0
2098
- }
2099
- }
2100
- }, X = class X extends E {
2101
- /**
2102
- * Group the nodes into communities by letting labels spread between neighbours.
2103
- *
2104
- * Publishes a group per node and nothing the shape does not derive. Label propagation does
2105
- * not score its own partition, so it reports no modularity: the community shape makes that
2106
- * field optional for exactly this reason, and a number invented to fill it would be read as
2107
- * a measurement.
2108
- * @param context - What the element gave the run.
2109
- * @returns The community result, or null when there are no nodes to group.
2110
- */
2111
- async compute(e) {
2112
- const t = R(this.input("undirected"));
2113
- if (t.length === 0)
2114
- return null;
2115
- const { maxIterations: n, randomSeed: a } = this.schemaOptions, i = this.algorithmGraph("undirected");
2116
- e.report({ phase: "Spreading labels", total: null });
2117
- const o = Ft(i, {
2118
- maxIterations: n,
2119
- randomSeed: a
2120
- }), r = [];
2121
- return await S(e, "Grouping nodes", t, (d) => {
2122
- r.push({ id: d, values: { group: o.communities.get(String(d)) ?? 0 } });
2123
- }), {
2124
- shape: "community",
2125
- fields: oe(!1),
2126
- nodes: r,
2127
- caveats: C({
2128
- method: "label-propagation",
2129
- direction: "undirected",
2130
- weight: { attribute: "weight", meaning: "strength" },
2131
- converged: o.converged,
2132
- iterations: o.iterations,
2133
- seed: a,
2134
- notes: ["Label propagation does not score its own partition, so it reports no modularity."]
2135
- })
2136
- };
2137
- }
2138
- };
2139
- X.namespace = "graphty", X.type = "label-propagation", X.scopeInput = "subgraph", X.zodOptionsSchema = va, X.optionsSchema = {
2140
- maxIterations: {
2141
- type: "integer",
2142
- default: 100,
2143
- label: "Max Iterations",
2144
- description: "Maximum label propagation rounds",
2145
- min: 1,
2146
- max: 500
2147
- },
2148
- randomSeed: {
2149
- type: "integer",
2150
- default: 42,
2151
- label: "Random Seed",
2152
- description: "Seed for reproducible tie-breaking",
2153
- min: 0,
2154
- max: 2147483647,
2155
- advanced: !0
2156
- }
2157
- };
2158
- let Pe = X;
2159
- v.register(Pe);
2160
- const Na = {
2161
- resolution: {
2162
- schema: c.number().min(0.1).max(5).default(1),
2163
- meta: {
2164
- label: "Resolution",
2165
- description: "Controls community granularity",
2166
- step: 0.1
2167
- }
2168
- },
2169
- randomSeed: {
2170
- schema: c.number().int().min(0).max(2147483647).default(42),
2171
- meta: {
2172
- label: "Random Seed",
2173
- description: "Seed for reproducible results",
2174
- advanced: !0
2175
- }
2176
- },
2177
- maxIterations: {
2178
- schema: c.number().int().min(1).max(500).default(100),
2179
- meta: {
2180
- label: "Max Iterations",
2181
- description: "Maximum refinement iterations",
2182
- advanced: !0
2183
- }
2184
- },
2185
- threshold: {
2186
- schema: c.number().min(1e-10).max(0.01).default(1e-6),
2187
- meta: {
2188
- label: "Threshold",
2189
- description: "Convergence threshold",
2190
- advanced: !0
2191
- }
2192
- }
2193
- }, Q = class Q extends E {
2194
- /**
2195
- * Group the nodes into communities, refining each group before it is kept.
2196
- *
2197
- * Publishes the community shape's uniform fields: a group per node, and the modularity the
2198
- * method reported. How many passes it took qualifies those numbers rather than being one of
2199
- * them, so it travels in the caveats.
2200
- * @param context - What the element gave the run.
2201
- * @returns The community result, or null when there are no nodes to group.
2202
- */
2203
- async compute(e) {
2204
- const t = R(this.input("undirected"));
2205
- if (t.length === 0)
2206
- return null;
2207
- const { resolution: n, randomSeed: a, maxIterations: i, threshold: o } = this.schemaOptions, r = this.algorithmGraph("undirected");
2208
- e.report({ phase: "Refining communities", total: null });
2209
- const d = Gt(r, {
2210
- resolution: n,
2211
- randomSeed: a,
2212
- maxIterations: i,
2213
- threshold: o
2214
- }), h = [];
2215
- return await S(e, "Grouping nodes", t, (u) => {
2216
- h.push({ id: u, values: { group: d.communities.get(String(u)) ?? 0 } });
2217
- }), {
2218
- shape: "community",
2219
- fields: oe(!0),
2220
- nodes: h,
2221
- graph: { modularity: d.modularity },
2222
- caveats: C({
2223
- method: "leiden",
2224
- direction: "undirected",
2225
- weight: { attribute: "weight", meaning: "strength" },
2226
- iterations: d.iterations,
2227
- notes: [`Resolution ${String(n)}.`]
2228
- })
2229
- };
2230
- }
2231
- };
2232
- Q.namespace = "graphty", Q.type = "leiden", Q.scopeInput = "subgraph", Q.zodOptionsSchema = Na, Q.optionsSchema = {
2233
- resolution: {
2234
- type: "number",
2235
- default: 1,
2236
- label: "Resolution",
2237
- description: "Controls community granularity",
2238
- min: 0.1,
2239
- max: 5,
2240
- step: 0.1
2241
- },
2242
- randomSeed: {
2243
- type: "integer",
2244
- default: 42,
2245
- label: "Random Seed",
2246
- description: "Seed for reproducible results",
2247
- min: 0,
2248
- max: 2147483647,
2249
- advanced: !0
2250
- },
2251
- maxIterations: {
2252
- type: "integer",
2253
- default: 100,
2254
- label: "Max Iterations",
2255
- description: "Maximum refinement iterations",
2256
- min: 1,
2257
- max: 500,
2258
- advanced: !0
2259
- },
2260
- threshold: {
2261
- type: "number",
2262
- default: 1e-6,
2263
- label: "Threshold",
2264
- description: "Convergence threshold",
2265
- min: 1e-10,
2266
- max: 0.01,
2267
- advanced: !0
2268
- }
2269
- };
2270
- let Re = Q;
2271
- v.register(Re);
2272
- const ka = {
2273
- "adamic-adar": Wt,
2274
- "common-neighbors": Ht
2275
- }, Sa = {
2276
- method: {
2277
- schema: c.enum(["adamic-adar", "common-neighbors"]).default("adamic-adar"),
2278
- meta: {
2279
- label: "Method",
2280
- description: "Common neighbours counts the neighbours two nodes share. Adamic-Adar counts them too, but a shared neighbour with few connections counts for more than one with many."
2281
- }
2282
- },
2283
- topK: {
2284
- schema: c.number().int().min(1).max(1e3).default(10),
2285
- meta: {
2286
- label: "Pairs",
2287
- description: "How many of the best-scoring pairs to keep."
2288
- }
2289
- }
2290
- }, A = class A extends E {
2291
- /**
2292
- * Score every unconnected pair of the run's nodes and keep the best. A pair is published as a
2293
- * row of the result and never written into the graph.
2294
- * @param context - What the element gave the run.
2295
- * @returns The scored pairs, best first, or null when the graph has no nodes.
2296
- */
2297
- compute(e) {
2298
- if (this.input("undirected").nodeCount === 0)
2299
- return Promise.resolve(null);
2300
- const { method: t, topK: n } = this.schemaOptions, a = this.algorithmGraph("undirected");
2301
- e.report({ phase: "Scoring pairs", total: null });
2302
- const i = /* @__PURE__ */ new Set(), o = ka[t](a).filter(({ source: r, target: d }) => {
2303
- const h = [String(r), String(d)].sort().join("\0");
2304
- return i.has(h) ? !1 : (i.add(h), !0);
2305
- }).slice(0, n);
2306
- return e.signal.throwIfAborted(), Promise.resolve({
2307
- shape: "pair-list",
2308
- fields: [{ name: "pairs", kind: "graph", type: "table" }],
2309
- graph: { pairs: o },
2310
- caveats: C({
2311
- method: t,
2312
- direction: "undirected",
2313
- weight: null,
2314
- notes: ["Only pairs that are not already joined, and that share at least one neighbour, are scored."]
2315
- })
2316
- });
2317
- }
2318
- };
2319
- A.namespace = "graphty", A.type = "link-prediction", A.scopeInput = "subgraph", A.zodOptionsSchema = Sa, A.optionsSchema = {
2320
- method: {
2321
- type: "select",
2322
- default: "adamic-adar",
2323
- label: "Method",
2324
- description: "Common neighbours counts the neighbours two nodes share. Adamic-Adar counts them too, but a shared neighbour with few connections counts for more than one with many.",
2325
- options: [
2326
- { value: "adamic-adar", label: "Adamic-Adar" },
2327
- { value: "common-neighbors", label: "Common neighbours" }
2328
- ]
2329
- },
2330
- topK: {
2331
- type: "integer",
2332
- default: 10,
2333
- label: "Pairs",
2334
- description: "How many of the best-scoring pairs to keep.",
2335
- min: 1,
2336
- max: 1e3
2337
- }
2338
- };
2339
- let $e = A;
2340
- v.register($e);
2341
- const Oa = {
2342
- resolution: {
2343
- schema: c.number().min(0.1).max(5).default(1),
2344
- meta: {
2345
- label: "Resolution",
2346
- description: "Higher = more communities, lower = fewer larger communities",
2347
- step: 0.1
2348
- }
2349
- },
2350
- maxIterations: {
2351
- schema: c.number().int().min(1).max(500).default(100),
2352
- meta: {
2353
- label: "Max Iterations",
2354
- description: "Maximum optimization iterations",
2355
- advanced: !0
2356
- }
2357
- },
2358
- tolerance: {
2359
- schema: c.number().min(1e-10).max(0.01).default(1e-6),
2360
- meta: {
2361
- label: "Tolerance",
2362
- description: "Minimum modularity improvement to continue",
2363
- advanced: !0
2364
- }
2365
- },
2366
- useOptimized: {
2367
- schema: c.boolean().default(!0),
2368
- meta: {
2369
- label: "Use Optimized",
2370
- description: "Use optimized implementation for better performance on large graphs",
2371
- advanced: !0
2372
- }
2373
- }
2374
- }, _ = class _ extends E {
2375
- /**
2376
- * Group the nodes into communities by optimising modularity.
2377
- *
2378
- * Publishes the community shape's uniform fields: a group per node, and the modularity the
2379
- * method reported for its own partition. The group sizes, the group count and the size table
2380
- * are derived from the groups when the result is built, not counted again here.
2381
- * @param context - What the element gave the run.
2382
- * @returns The community result, or null when there are no nodes to group.
2383
- */
2384
- async compute(e) {
2385
- const t = R(this.input("undirected"));
2386
- if (t.length === 0)
2387
- return null;
2388
- const { resolution: n, maxIterations: a, tolerance: i, useOptimized: o } = this.schemaOptions, r = this.algorithmGraph("undirected");
2389
- e.report({ phase: "Optimizing modularity", total: null });
2390
- const d = Kt(r, {
2391
- resolution: n,
2392
- maxIterations: a,
2393
- tolerance: i,
2394
- useOptimized: o
2395
- }), h = /* @__PURE__ */ new Map();
2396
- for (let l = 0; l < d.communities.length; l++)
2397
- for (const p of d.communities[l])
2398
- h.set(p, l);
2399
- const u = [];
2400
- return await S(e, "Grouping nodes", t, (l) => {
2401
- u.push({ id: l, values: { group: h.get(l) ?? 0 } });
2402
- }), {
2403
- shape: "community",
2404
- fields: oe(!0),
2405
- nodes: u,
2406
- graph: { modularity: d.modularity },
2407
- caveats: C({
2408
- method: "louvain",
2409
- direction: "undirected",
2410
- weight: { attribute: "weight", meaning: "strength" },
2411
- notes: [`Resolution ${String(n)}.`]
2412
- })
2413
- };
2414
- }
2415
- };
2416
- _.namespace = "graphty", _.type = "louvain", _.scopeInput = "subgraph", _.zodOptionsSchema = Oa, _.optionsSchema = {
2417
- resolution: {
2418
- type: "number",
2419
- default: 1,
2420
- label: "Resolution",
2421
- description: "Higher = more communities, lower = fewer larger communities",
2422
- min: 0.1,
2423
- max: 5,
2424
- step: 0.1
2425
- },
2426
- maxIterations: {
2427
- type: "integer",
2428
- default: 100,
2429
- label: "Max Iterations",
2430
- description: "Maximum optimization iterations",
2431
- min: 1,
2432
- max: 500,
2433
- advanced: !0
2434
- },
2435
- tolerance: {
2436
- type: "number",
2437
- default: 1e-6,
2438
- label: "Tolerance",
2439
- description: "Minimum modularity improvement to continue",
2440
- min: 1e-10,
2441
- max: 0.01,
2442
- advanced: !0
2443
- },
2444
- useOptimized: {
2445
- type: "boolean",
2446
- default: !0,
2447
- label: "Use Optimized",
2448
- description: "Use optimized implementation for better performance on large graphs",
2449
- advanced: !0
2450
- }
2451
- };
2452
- let Fe = _;
2453
- v.register(Fe);
2454
- const xa = {
2455
- source: {
2456
- schema: c.union([c.string(), c.number()]).nullable().default(null),
2457
- meta: {
2458
- label: "Source Node",
2459
- description: "Source node for flow network (uses first node if not set)"
2460
- }
2461
- },
2462
- sink: {
2463
- schema: c.union([c.string(), c.number()]).nullable().default(null),
2464
- meta: {
2465
- label: "Sink Node",
2466
- description: "Sink node for flow network (uses last node if not set)"
2467
- }
2468
- }
2469
- }, ee = class ee extends E {
2470
- constructor() {
2471
- super(...arguments), this.legacyOptions = null;
2472
- }
2473
- /**
2474
- * Configure the algorithm with source and sink nodes
2475
- * @param options - Configuration options
2476
- * @param options.source - The source node for the flow network
2477
- * @param options.sink - The sink node for the flow network
2478
- * @returns This algorithm instance for chaining
2479
- * @deprecated Use constructor options instead. This method is kept for backward compatibility.
2480
- */
2481
- configure(e) {
2482
- return this.legacyOptions = e, this;
2483
- }
2484
- /**
2485
- * Find the most that can flow from the source to the sink.
2486
- *
2487
- * The answer is a number per edge -- how much runs through it -- so the result is shaped as
2488
- * an edge metric, and the ranking, the range and the rest come from that column. Two further
2489
- * numbers travel with each edge because they cannot be recovered from the flow alone: the
2490
- * capacity it was measured against, and the share of that capacity it used.
2491
- * @param context - What the element gave the run.
2492
- * @returns The flow per edge, or null when there is no network to measure.
2493
- */
2494
- async compute(e) {
2495
- const t = this.input("declared"), n = ie(t), a = R(t);
2496
- if (n.length === 0 || a.length === 0)
2497
- return null;
2498
- const i = this.legacyOptions?.source ?? this._schemaOptions.source, o = this.legacyOptions?.sink ?? this._schemaOptions.sink, r = i === null ? String(a[0]) : Ye("max-flow", "source", i, a), d = o === null ? String(a[a.length - 1]) : Ye("max-flow", "sink", o, a), h = new _e({ directed: !0 });
2499
- for (const b of a)
2500
- h.addNode(String(b));
2501
- const u = /* @__PURE__ */ new Map();
2502
- for (const b of n) {
2503
- const w = String(b.source), O = String(b.target), I = this.edgeRecord(b.id), x = I?.capacity ?? I?.value ?? 1, T = typeof x == "number" ? x : 1, F = P(w, O), D = (u.get(F) ?? 0) + T;
2504
- u.set(F, D), h.removeEdge(w, O), h.addEdge(w, O, D);
2505
- }
2506
- e.report({ phase: "Pushing flow", total: null });
2507
- const l = Lt(h, r, d), p = /* @__PURE__ */ new Map(), m = /* @__PURE__ */ new Map();
2508
- for (const [b, w] of l.flowGraph)
2509
- for (const [O, I] of w)
2510
- m.set(b, (m.get(b) ?? 0) + I), p.set(O, (p.get(O) ?? 0) + I);
2511
- const f = [];
2512
- await S(e, "Measuring edges", n, (b) => {
2513
- const w = String(b.source), O = String(b.target), I = l.flowGraph.get(w)?.get(O) ?? 0, x = u.get(P(w, O)) ?? 1, T = x > 0 ? Math.abs(I) / x : 0;
2514
- f.push({ id: b.id, values: { value: I, capacity: x, utilization: T } });
2515
- });
2516
- const M = [];
2517
- return await S(e, "Measuring nodes", a, (b) => {
2518
- const w = String(b), O = p.get(w) ?? 0, I = m.get(w) ?? 0, x = { netFlow: O - I };
2519
- w === r ? x.role = "source" : w === d && (x.role = "sink"), M.push({ id: b, values: x });
2520
- }), {
2521
- shape: "edge-metric",
2522
- fields: [
2523
- ...Ae("edge"),
2524
- { name: "capacity", kind: "edge", type: "number" },
2525
- { name: "utilization", kind: "edge", type: "number" },
2526
- { name: "netFlow", kind: "node", type: "number" },
2527
- { name: "role", kind: "node", type: "string" },
2528
- { name: "maxFlow", kind: "graph", type: "number" }
2529
- ],
2530
- nodes: M,
2531
- edges: f,
2532
- graph: { maxFlow: l.maxFlow },
2533
- caveats: C({
2534
- method: "ford-fulkerson",
2535
- direction: "directed",
2536
- weight: { attribute: "capacity", meaning: "strength" },
2537
- notes: [
2538
- `Flow from ${r} to ${d}.`,
2539
- ...i === null || o === null ? [
2540
- "The source or sink was chosen automatically (the first and last node); set the source and sink options to measure between the nodes you mean."
2541
- ] : [],
2542
- ...l.maxFlow === 0 ? [`There is no directed path from ${r} to ${d}, so no flow can run.`] : []
2543
- ]
2544
- })
2545
- };
2546
- }
2547
- };
2548
- ee.namespace = "graphty", ee.type = "max-flow", ee.scopeInput = "subgraph", ee.zodOptionsSchema = xa, ee.optionsSchema = {
2549
- source: {
2550
- type: "nodeId",
2551
- default: null,
2552
- label: "Source Node",
2553
- description: "Source node for flow network (uses first node if not set)",
2554
- required: !1
2555
- },
2556
- sink: {
2557
- type: "nodeId",
2558
- default: null,
2559
- label: "Sink Node",
2560
- description: "Sink node for flow network (uses last node if not set)",
2561
- required: !1
2562
- }
2563
- };
2564
- let Ge = ee;
2565
- v.register(Ge);
2566
- const Ia = {
2567
- source: {
2568
- schema: c.union([c.string(), c.number()]).nullable().default(null),
2569
- meta: {
2570
- label: "Source Node",
2571
- description: "Source node for s-t cut (uses global min cut if not set)"
2572
- }
2573
- },
2574
- sink: {
2575
- schema: c.union([c.string(), c.number()]).nullable().default(null),
2576
- meta: {
2577
- label: "Sink Node",
2578
- description: "Sink node for s-t cut (uses global min cut if not set)"
2579
- }
2580
- },
2581
- useGlobalMinCut: {
2582
- schema: c.boolean().default(!1),
2583
- meta: {
2584
- label: "Use Global Min Cut",
2585
- description: "Use global minimum cut algorithm instead of s-t cut"
2586
- }
2587
- },
2588
- useKarger: {
2589
- schema: c.boolean().default(!1),
2590
- meta: {
2591
- label: "Use Karger Algorithm",
2592
- description: "Use Karger's randomized algorithm instead of Stoer-Wagner for global min cut",
2593
- advanced: !0
2594
- }
2595
- },
2596
- kargerIterations: {
2597
- schema: c.number().int().min(1).max(1e4).default(100),
2598
- meta: {
2599
- label: "Karger Iterations",
2600
- description: "Number of iterations for Karger's algorithm (higher = better accuracy but slower)",
2601
- advanced: !0
2602
- }
2603
- }
2604
- }, te = class te extends E {
2605
- constructor() {
2606
- super(...arguments), this.legacyOptions = null;
2607
- }
2608
- /**
2609
- * Configure the algorithm with source, sink, and useGlobalMinCut options
2610
- * @param options - Configuration options
2611
- * @param options.source - The source node for s-t cut (optional)
2612
- * @param options.sink - The sink node for s-t cut (optional)
2613
- * @param options.useGlobalMinCut - Whether to use global min cut (optional)
2614
- * @returns This algorithm instance for chaining
2615
- * @deprecated Use constructor options instead. This method is kept for backward compatibility.
2616
- */
2617
- configure(e) {
2618
- return this.legacyOptions = e, this;
2619
- }
2620
- /**
2621
- * Find the cheapest set of edges to remove to split the graph in two.
2622
- *
2623
- * A cut is a set of edges, so the result is shaped as one: every edge says whether it is in
2624
- * the cut, the element counts how many are, and the run publishes what the cut costs, which
2625
- * is the number the answer is read for. Every node also carries which side of the split it
2626
- * ended up on, because a cut is only legible beside the two pieces it makes.
2627
- *
2628
- * Three methods can answer, and they answer different questions: Stoer-Wagner and Karger find
2629
- * the cheapest cut anywhere in the graph, while the max-flow route finds the cheapest cut
2630
- * between two named nodes. Karger is randomised and is the only one that may be wrong, so the
2631
- * caveats say which ran and whether it was exact.
2632
- * @param context - What the element gave the run.
2633
- * @returns The edge set, or null when there is nothing to cut.
2634
- */
2635
- async compute(e) {
2636
- const t = this.input("undirected"), n = ie(t), a = R(t);
2637
- if (n.length === 0 || a.length === 0)
2638
- return null;
2639
- const i = t.subgraph(), { ids: o } = i, { src: r, dst: d, weights: h } = i.edgeList(), u = /* @__PURE__ */ new Map(), l = new _e({ directed: !1 });
2640
- for (const y of a)
2641
- u.set(String(y), /* @__PURE__ */ new Map()), l.addNode(String(y));
2642
- for (let y = 0; y < i.edgeCount; y++) {
2643
- const $ = String(o.idOf(r[y])), se = String(o.idOf(d[y])), Je = h === null ? 1 : h[y];
2644
- u.get($)?.set(se, Je), u.get(se)?.set($, Je), l.addEdge($, se, Je);
2645
- }
2646
- const p = this.legacyOptions?.useGlobalMinCut ?? this._schemaOptions.useGlobalMinCut, m = this.legacyOptions?.source ?? this._schemaOptions.source, f = this.legacyOptions?.sink ?? this._schemaOptions.sink, { useKarger: M, kargerIterations: b } = this._schemaOptions;
2647
- let w, O, I, x, T, F;
2648
- if (e.report({ phase: "Finding the cut", total: null }), p || m === null && f === null)
2649
- M ? (T = "karger", { partition1: w, partition2: O, cutEdges: I, cutValue: x } = Ut(u, b)) : (T = "stoer-wagner", { partition1: w, partition2: O, cutEdges: I, cutValue: x } = jt(u));
2650
- else {
2651
- T = "min-st-cut";
2652
- const y = m === null ? String(a[0]) : Ye("min-cut", "source", m, a), $ = f === null ? String(a[a.length - 1]) : Ye("min-cut", "sink", f, a);
2653
- (m === null || f === null) && (F = `Only one end was set, so the other was chosen automatically (cut between ${y} and ${$}); set both source and sink to cut between the nodes you mean.`), { partition1: w, partition2: O, cutEdges: I, cutValue: x } = Vt(l, y, $);
2654
- }
2655
- const D = /* @__PURE__ */ new Map();
2656
- for (const y of I)
2657
- D.set(P(y.from, y.to), y.weight), D.set(P(y.to, y.from), y.weight);
2658
- const N = [];
2659
- await S(e, "Marking the cut", n, (y) => {
2660
- const $ = D.get(P(y.source, y.target));
2661
- N.push({ id: y.id, values: { in: $ !== void 0 } });
2662
- });
2663
- const z = [];
2664
- return await S(e, "Marking the sides", a, (y) => {
2665
- const $ = w.has(String(y));
2666
- z.push({ id: y, values: { side: $ ? "1" : "2" } });
2667
- }), {
2668
- shape: "edge-set",
2669
- fields: [
2670
- ...Ze("edge", { name: "cutValue", type: "number" }),
2671
- { name: "side", kind: "node", type: "string" }
2672
- ],
2673
- nodes: z,
2674
- edges: N,
2675
- graph: { cutValue: x },
2676
- caveats: C({
2677
- method: T,
2678
- direction: "undirected",
2679
- weight: { attribute: "weight", meaning: "strength" },
2680
- exact: T !== "karger",
2681
- iterations: T === "karger" ? b : void 0,
2682
- notes: [
2683
- T === "karger" ? "Karger's method is randomised: it finds the cheapest cut with high probability, not certainty." : `The cut separates ${String(w.size)} nodes from ${String(O.size)}.`,
2684
- ...F === void 0 ? [] : [F]
2685
- ]
2686
- })
2687
- };
2688
- }
2689
- };
2690
- te.namespace = "graphty", te.type = "min-cut", te.scopeInput = "subgraph", te.zodOptionsSchema = Ia, te.optionsSchema = {
2691
- source: {
2692
- type: "nodeId",
2693
- default: null,
2694
- label: "Source Node",
2695
- description: "Source node for s-t cut (uses global min cut if not set)",
2696
- required: !1
2697
- },
2698
- sink: {
2699
- type: "nodeId",
2700
- default: null,
2701
- label: "Sink Node",
2702
- description: "Sink node for s-t cut (uses global min cut if not set)",
2703
- required: !1
2704
- },
2705
- useGlobalMinCut: {
2706
- type: "boolean",
2707
- default: !1,
2708
- label: "Use Global Min Cut",
2709
- description: "Use global minimum cut algorithm instead of s-t cut",
2710
- required: !1
2711
- },
2712
- useKarger: {
2713
- type: "boolean",
2714
- default: !1,
2715
- label: "Use Karger Algorithm",
2716
- description: "Use Karger's randomized algorithm instead of Stoer-Wagner for global min cut",
2717
- advanced: !0
2718
- },
2719
- kargerIterations: {
2720
- type: "integer",
2721
- default: 100,
2722
- label: "Karger Iterations",
2723
- description: "Number of iterations for Karger's algorithm (higher = better accuracy but slower)",
2724
- min: 1,
2725
- max: 1e4,
2726
- advanced: !0
2727
- }
2728
- };
2729
- let He = te;
2730
- v.register(He);
2731
- const st = {
2732
- dampingFactor: {
2733
- schema: c.number().min(0).max(1).default(0.85),
2734
- meta: {
2735
- label: "Damping Factor",
2736
- description: "Probability of following a link (0.85 is standard for web graphs)",
2737
- step: 0.05
2738
- }
2739
- },
2740
- maxIterations: {
2741
- schema: c.number().int().min(1).max(1e3).default(100),
2742
- meta: {
2743
- label: "Max Iterations",
2744
- description: "Maximum power iterations before stopping",
2745
- advanced: !0
2746
- }
2747
- },
2748
- tolerance: {
2749
- schema: c.number().min(1e-10).max(0.1).default(1e-6),
2750
- meta: {
2751
- label: "Tolerance",
2752
- description: "Convergence threshold for early stopping",
2753
- advanced: !0
2754
- }
2755
- },
2756
- weight: {
2757
- schema: c.string().nullable().default(null),
2758
- meta: {
2759
- label: "Weight Attribute",
2760
- description: "Edge attribute name for weighted PageRank (empty = unweighted)",
2761
- advanced: !0
2762
- }
2763
- },
2764
- useDelta: {
2765
- schema: c.boolean().default(!0),
2766
- meta: {
2767
- label: "Use Delta Optimization",
2768
- description: "Use delta-based optimization for faster convergence on large graphs",
2769
- advanced: !0
2770
- }
2771
- }
2772
- }, Ca = U({
2773
- plainName: "Influence",
2774
- technicalName: "PageRank score"
2775
- }), Ta = 100;
2776
- function za(s) {
2777
- return s.hasPersonalization ? "a personalization vector was given, and only the reference implementation takes one" : s.hasInitialRanks ? "initial ranks were given, and only the reference implementation takes them" : s.directed ? null : "the graph is undirected, and rank flows along out-edges";
2778
- }
2779
- const ae = class ae extends j {
2780
- /**
2781
- * Creates a new PageRank algorithm instance
2782
- * @param g - The graph to run the algorithm on
2783
- * @param options - Optional configuration options
2784
- */
2785
- constructor(e, t) {
2786
- super(e, t), this.zodOptions = aa(st, t ?? {}), this.programmaticOptions = {
2787
- initialRanks: t?.initialRanks ?? null,
2788
- personalization: t?.personalization ?? null
2789
- };
2790
- }
2791
- /**
2792
- * The fields a PageRank result publishes.
2793
- * @returns The uniform node-metric fields.
2794
- */
2795
- resultFields() {
2796
- return Ca;
2797
- }
2798
- /**
2799
- * Rank every node by the influence flowing into it.
2800
- * @param context - Where progress goes and where cancellation arrives.
2801
- * @param nodeIds - The nodes to measure.
2802
- * @returns One raw rank per node, unscaled.
2803
- */
2804
- async measure(e, t) {
2805
- const { dampingFactor: n, maxIterations: a, tolerance: i, weight: o, useDelta: r } = this.zodOptions, d = this.programmaticOptions.initialRanks ?? void 0, h = this.programmaticOptions.personalization ?? void 0, u = za({
2806
- directed: this.input("declared").graph.directed,
2807
- hasInitialRanks: d !== void 0,
2808
- hasPersonalization: h !== void 0
2809
- });
2810
- if (u === null) {
2811
- const { snapshot: b, run: w } = this.accelerated("pageRank", "directed");
2812
- e.report({
2813
- phase: "iterating",
2814
- completed: 0,
2815
- total: t.length,
2816
- message: `Power iteration, up to ${String(a)} passes.`
2817
- });
2818
- const { value: O, precision: I } = await w(
2819
- (D, N) => D.pageRank(N, {
2820
- dampingFactor: n,
2821
- maxIterations: a,
2822
- tolerance: i,
2823
- // ONE weight column, so naming an attribute is the same request as asking for
2824
- // a weighted run: the snapshot carries the weight the element resolved.
2825
- weighted: o !== null
2826
- })
2827
- );
2828
- e.signal.throwIfAborted();
2829
- const { ids: x } = b, T = [];
2830
- await W(t, e, "reading ranks", (D) => {
2831
- const N = x.indexOf(D), z = N === L ? void 0 : O.scores[N];
2832
- T.push({ id: D, values: z === void 0 ? {} : { value: z } });
2833
- });
2834
- const F = [
2835
- `A reader follows a link with probability ${String(n)} and jumps to a random node otherwise.`,
2836
- "The ranks sum to 1 across the graph."
2837
- ];
2838
- return o === null && F.push("Edge weights are not read."), r && F.push(
2839
- "Power iteration ran: the delta optimisation is the CPU reference implementation's own and has no index-based counterpart, so it was not taken."
2840
- ), {
2841
- nodes: T,
2842
- normalization: "none",
2843
- caveats: {
2844
- exact: !0,
2845
- direction: "directed",
2846
- weight: o === null ? null : { attribute: o, meaning: "strength" },
2847
- precision: I,
2848
- method: "power-iteration",
2849
- converged: O.converged,
2850
- iterations: O.iterations,
2851
- notes: F
2852
- }
2853
- };
2854
- }
2855
- const l = this.algorithmGraph("directed"), p = r && l.nodeCount > Ta;
2856
- e.report({
2857
- phase: "iterating",
2858
- completed: 0,
2859
- total: t.length,
2860
- message: `Power iteration, up to ${String(a)} passes.`
2861
- });
2862
- const m = qt(l, {
2863
- dampingFactor: n,
2864
- maxIterations: a,
2865
- tolerance: i,
2866
- weight: o ?? void 0,
2867
- useDelta: r,
2868
- initialRanks: d,
2869
- personalization: h
2870
- });
2871
- e.signal.throwIfAborted();
2872
- const f = [];
2873
- await W(t, e, "reading ranks", (b) => {
2874
- const w = m.ranks[String(b)];
2875
- f.push({ id: b, values: w === void 0 ? {} : { value: w } });
2876
- });
2877
- const M = [
2878
- `A reader follows a link with probability ${String(n)} and jumps to a random node otherwise.`,
2879
- "The ranks sum to 1 across the graph.",
2880
- `Computed on the CPU reference implementation: ${u}.`
2881
- ];
2882
- return o === null && M.push("Edge weights are not read."), {
2883
- nodes: f,
2884
- // Raw ranks, published as they were computed.
2885
- normalization: "none",
2886
- caveats: {
2887
- exact: !0,
2888
- direction: "directed",
2889
- weight: o === null ? null : { attribute: o, meaning: "strength" },
2890
- precision: "f64",
2891
- method: p ? "delta-pagerank" : "power-iteration",
2892
- converged: m.converged,
2893
- iterations: m.iterations,
2894
- notes: M
2895
- }
2896
- };
2897
- }
2898
- };
2899
- ae.namespace = "graphty", ae.type = "pagerank", ae.scopeInput = "subgraph", ae.zodOptionsSchema = st, ae.optionsSchema = {
2900
- dampingFactor: {
2901
- type: "number",
2902
- default: 0.85,
2903
- label: "Damping Factor",
2904
- description: "Probability of following a link (0.85 is standard for web graphs)",
2905
- min: 0,
2906
- max: 1,
2907
- step: 0.05
2908
- },
2909
- maxIterations: {
2910
- type: "integer",
2911
- default: 100,
2912
- label: "Max Iterations",
2913
- description: "Maximum power iterations before stopping",
2914
- min: 1,
2915
- max: 1e3,
2916
- advanced: !0
2917
- },
2918
- tolerance: {
2919
- type: "number",
2920
- default: 1e-6,
2921
- label: "Tolerance",
2922
- description: "Convergence threshold for early stopping",
2923
- min: 1e-10,
2924
- max: 0.1,
2925
- advanced: !0
2926
- },
2927
- weight: {
2928
- type: "string",
2929
- default: null,
2930
- label: "Weight Attribute",
2931
- description: "Edge attribute name for weighted PageRank (empty = unweighted)",
2932
- advanced: !0
2933
- },
2934
- useDelta: {
2935
- type: "boolean",
2936
- default: !0,
2937
- label: "Use Delta Optimization",
2938
- description: "Use delta-based optimization for faster convergence on large graphs",
2939
- advanced: !0
2940
- }
2941
- // Note: initialRanks and personalization are Map types - programmatic only, not in schema
2942
- };
2943
- let We = ae;
2944
- v.register(We);
2945
- const Ea = {
2946
- startNode: {
2947
- schema: c.union([c.string(), c.number()]).nullable().default(null),
2948
- meta: {
2949
- label: "Start Node",
2950
- description: "Starting node for MST growth (uses first node if not set)"
2951
- }
2952
- }
2953
- };
2954
- function Ma(s, e) {
2955
- if (e !== void 0 && !s.hasNode(e))
2956
- throw new Error(`Start node ${String(e)} not found in graph`);
2957
- const t = Bt(s);
2958
- if (t.length <= 1)
2959
- return tt(s, e);
2960
- const n = [];
2961
- let a = 0;
2962
- for (const i of t) {
2963
- const o = new Set(i), r = new _e({ directed: !1 });
2964
- for (const h of i)
2965
- r.addNode(h);
2966
- for (const h of s.edges())
2967
- o.has(h.source) && r.addEdge(h.source, h.target, h.weight);
2968
- const d = tt(r, e !== void 0 && o.has(e) ? e : void 0);
2969
- n.push(...d.edges), a += d.totalWeight;
2970
- }
2971
- return { edges: n, totalWeight: a };
2972
- }
2973
- const ne = class ne extends E {
2974
- constructor() {
2975
- super(...arguments), this.legacyOptions = null;
2976
- }
2977
- /**
2978
- * Configure the algorithm with an optional start node
2979
- * @param options - Configuration options
2980
- * @param options.startNode - The optional start node ID
2981
- * @returns This algorithm instance for chaining
2982
- * @deprecated Use constructor options instead. This method is kept for backward compatibility.
2983
- */
2984
- configure(e) {
2985
- return this.legacyOptions = { startNode: e.startNode ?? null }, this;
2986
- }
2987
- /**
2988
- * Find the cheapest set of edges that still joins every node.
2989
- *
2990
- * A spanning tree is a set of edges, so the result is shaped as one: every edge says whether
2991
- * it is in the network, the element counts how many are, and the run publishes the one number
2992
- * the answer is actually read for -- what the network costs in total.
2993
- * @param context - What the element gave the run.
2994
- * @returns The edge set, or null when there are no edges to choose from.
2995
- */
2996
- async compute(e) {
2997
- const t = ie(this.input("undirected"));
2998
- if (t.length === 0)
2999
- return null;
3000
- const n = this.legacyOptions?.startNode ?? this._schemaOptions.startNode ?? void 0, a = this.algorithmGraph("undirected");
3001
- e.report({ phase: "Choosing edges", total: null });
3002
- const i = Ma(a, n), o = /* @__PURE__ */ new Set();
3003
- for (const d of i.edges)
3004
- o.add(P(d.source, d.target)), o.add(P(d.target, d.source));
3005
- const r = [];
3006
- return await S(e, "Marking the network", t, (d) => {
3007
- r.push({ id: d.id, values: { in: o.has(P(d.source, d.target)) } });
3008
- }), {
3009
- shape: "edge-set",
3010
- fields: Ze("edge", { name: "totalWeight", type: "number" }),
3011
- edges: r,
3012
- graph: { totalWeight: i.totalWeight },
3013
- caveats: C({
3014
- method: "prim",
3015
- direction: "undirected",
3016
- weight: { attribute: "weight", meaning: "distance" },
3017
- notes: [`The tree joins the graph with ${String(i.edges.length)} edges.`]
3018
- })
3019
- };
3020
- }
3021
- };
3022
- ne.namespace = "graphty", ne.type = "prim", ne.scopeInput = "subgraph", ne.zodOptionsSchema = Ea, ne.optionsSchema = {
3023
- startNode: {
3024
- type: "nodeId",
3025
- default: null,
3026
- label: "Start Node",
3027
- description: "Starting node for MST growth (uses first node if not set)",
3028
- required: !1
3029
- }
3030
- };
3031
- let Ke = ne;
3032
- v.register(Ke);
3033
- const fe = class fe extends E {
3034
- /**
3035
- * Find the pieces of the graph that a directed path can cross both ways.
3036
- *
3037
- * The same community shape as the weak reading, run at the other strength: two nodes share a
3038
- * group only when a directed path runs each way between them. The caveats say which strength
3039
- * ran, because on a directed graph the two answers differ and the numbers do not say so.
3040
- * @param context - What the element gave the run.
3041
- * @returns The community result, or null when there are no nodes to group.
3042
- */
3043
- async compute(e) {
3044
- const t = R(this.input("declared"));
3045
- if (t.length === 0)
3046
- return null;
3047
- const n = this.algorithmGraph("directed");
3048
- e.report({ phase: "Finding pieces", total: null });
3049
- const a = Yt(n), i = /* @__PURE__ */ new Map();
3050
- for (let r = 0; r < a.length; r++)
3051
- for (const d of a[r])
3052
- i.set(d, r);
3053
- const o = [];
3054
- return await S(e, "Grouping nodes", t, (r) => {
3055
- o.push({ id: r, values: { group: i.get(r) ?? 0 } });
3056
- }), {
3057
- shape: "community",
3058
- fields: oe(!1),
3059
- nodes: o,
3060
- caveats: C({
3061
- method: "strongly-connected-components",
3062
- direction: "directed",
3063
- weight: null,
3064
- notes: ["Strength: strong. Two nodes share a piece only when a directed path runs each way."]
3065
- })
3066
- };
3067
- }
3068
- };
3069
- fe.namespace = "graphty", fe.type = "scc", fe.scopeInput = "subgraph";
3070
- let Le = fe;
3071
- v.register(Le);
3072
- const Da = "results.$";
3073
- function g(s) {
3074
- return { ...s, path: `${Da}.${s.name}` };
3075
- }
3076
- function G(s, e) {
3077
- return [
3078
- g({
3079
- name: "value",
3080
- plainName: e.plainName,
3081
- technicalName: e.technicalName,
3082
- kind: s,
3083
- type: e.type ?? "number",
3084
- ...e.unit === void 0 ? {} : { unit: e.unit }
3085
- }),
3086
- g({ name: "rank", plainName: "Rank", technicalName: "rank", kind: s, type: "integer" }),
3087
- g({ name: "percentile", plainName: "Percentile", technicalName: "percentile", kind: s, type: "number" }),
3088
- g({ name: "min", plainName: "Lowest", technicalName: "min", kind: "graph", type: "number" }),
3089
- g({ name: "max", plainName: "Highest", technicalName: "max", kind: "graph", type: "number" }),
3090
- g({ name: "median", plainName: "Middle", technicalName: "median", kind: "graph", type: "number" }),
3091
- g({ name: "mean", plainName: "Average", technicalName: "mean", kind: "graph", type: "number" }),
3092
- g({ name: "measured", plainName: "Measured", technicalName: "measured", kind: "graph", type: "integer" }),
3093
- g({
3094
- name: "normalization",
3095
- plainName: "Normalization",
3096
- technicalName: "normalization",
3097
- kind: "graph",
3098
- type: "string"
3099
- }),
3100
- g({
3101
- name: "tiedAtMin",
3102
- plainName: "Tied at the lowest value",
3103
- technicalName: "tiedAtMin",
3104
- kind: "graph",
3105
- type: "integer"
3106
- })
3107
- ];
3108
- }
3109
- function re(s, e) {
3110
- const t = [
3111
- g({ name: "group", plainName: s, technicalName: "group", kind: "node", type: "integer" }),
3112
- g({
3113
- name: "groupSize",
3114
- plainName: `${s} size`,
3115
- technicalName: "groupSize",
3116
- kind: "node",
3117
- type: "integer"
3118
- }),
3119
- g({
3120
- name: "groupCount",
3121
- plainName: `${s} count`,
3122
- technicalName: "groupCount",
3123
- kind: "graph",
3124
- type: "integer"
3125
- }),
3126
- g({
3127
- name: "sizes",
3128
- plainName: `${s} sizes`,
3129
- technicalName: "sizes",
3130
- kind: "graph",
3131
- type: "table"
3132
- })
3133
- ];
3134
- return e && t.push(
3135
- g({
3136
- name: "modularity",
3137
- plainName: "Community strength",
3138
- technicalName: "modularity",
3139
- kind: "graph",
3140
- type: "number"
3141
- })
3142
- ), t;
3143
- }
3144
- function qe(s, e, t) {
3145
- return [
3146
- g({ name: "in", plainName: e, technicalName: "in", kind: s, type: "boolean" }),
3147
- g({ name: "count", plainName: "Count", technicalName: "count", kind: "graph", type: "integer" }),
3148
- g({ ...t, kind: "graph" })
3149
- ];
3150
- }
3151
- function k(s) {
3152
- return Qe(s.getZodOptionsSchema());
3153
- }
3154
- function ot(...s) {
3155
- const e = [], t = /* @__PURE__ */ new Set();
3156
- for (const n of s)
3157
- for (const a of n)
3158
- t.has(a.name) || (t.add(a.name), e.push(a));
3159
- return e;
3160
- }
3161
- const Pa = {
3162
- method: {
3163
- schema: c.enum(["dijkstra", "bellman-ford"]).nullable().default(null),
3164
- meta: {
3165
- label: "Method",
3166
- description: "Which engine computes the paths. Left unset, the element uses Dijkstra when every weight is zero or above and Bellman-Ford when any weight is negative.",
3167
- advanced: !0,
3168
- group: "engine"
3169
- }
3170
- }
3171
- }, Ra = {
3172
- strength: {
3173
- schema: c.enum(["weak", "strong"]).default("weak"),
3174
- meta: {
3175
- label: "Strength",
3176
- description: "Weak follows an edge in either direction. Strong needs a directed path each way, and only differs from weak on a directed graph."
3177
- }
3178
- }
3179
- }, $a = [
3180
- {
3181
- key: "degree",
3182
- plainName: "Connections",
3183
- technicalName: "Degree centrality",
3184
- description: "Counts how many edges each node has, incoming, outgoing and in total.",
3185
- category: "centrality",
3186
- shape: "node-metric",
3187
- fields: [
3188
- ...G("node", {
3189
- plainName: "Connections",
3190
- technicalName: "degree",
3191
- type: "integer",
3192
- unit: "links"
3193
- }),
3194
- g({
3195
- name: "inDegree",
3196
- plainName: "Incoming connections",
3197
- technicalName: "inDegree",
3198
- kind: "node",
3199
- type: "integer"
3200
- }),
3201
- g({
3202
- name: "outDegree",
3203
- plainName: "Outgoing connections",
3204
- technicalName: "outDegree",
3205
- kind: "node",
3206
- type: "integer"
3207
- })
3208
- ],
3209
- options: k(Se),
3210
- costClass: "instant",
3211
- complexity: "O(n + m)",
3212
- legacyKeys: [{ key: "degree" }]
3213
- },
3214
- {
3215
- key: "betweenness",
3216
- plainName: "Bridges",
3217
- technicalName: "Betweenness centrality",
3218
- description: "Scores a node by how many of the shortest paths between other nodes run through it, which is what finds the bridges.",
3219
- category: "centrality",
3220
- shape: "node-metric",
3221
- fields: G("node", { plainName: "Bridging", technicalName: "betweenness" }),
3222
- options: k(be),
3223
- costClass: "heavy",
3224
- complexity: "O(n * m)",
3225
- legacyKeys: [{ key: "betweenness" }]
3226
- },
3227
- {
3228
- key: "closeness",
3229
- plainName: "Reach",
3230
- technicalName: "Closeness centrality",
3231
- description: "Scores a node by how short its paths to the rest of the graph are, which is what finds the nodes that reach everything quickly.",
3232
- category: "centrality",
3233
- shape: "node-metric",
3234
- fields: G("node", { plainName: "Reach", technicalName: "closeness" }),
3235
- options: k(Ne),
3236
- costClass: "heavy",
3237
- complexity: "O(n * m)",
3238
- legacyKeys: [{ key: "closeness" }]
3239
- },
3240
- {
3241
- key: "pagerank",
3242
- plainName: "Influence",
3243
- technicalName: "PageRank",
3244
- description: "Ranks a node by the influence that flows into it from the nodes that point at it.",
3245
- category: "centrality",
3246
- shape: "node-metric",
3247
- fields: G("node", { plainName: "Influence", technicalName: "PageRank score" }),
3248
- options: k(We),
3249
- costClass: "iterative",
3250
- complexity: "O(k(n + m))",
3251
- legacyKeys: [{ key: "pagerank" }]
3252
- },
3253
- {
3254
- key: "eigenvector",
3255
- plainName: "Influence by association",
3256
- technicalName: "Eigenvector centrality",
3257
- description: "Scores a node by how influential its neighbours are, so a few important connections count for more than many unimportant ones.",
3258
- category: "centrality",
3259
- shape: "node-metric",
3260
- fields: G("node", { plainName: "Influence by association", technicalName: "eigenvector score" }),
3261
- options: k(Ie),
3262
- costClass: "iterative",
3263
- complexity: "O(k(n + m))",
3264
- legacyKeys: [{ key: "eigenvector" }]
3265
- },
3266
- {
3267
- key: "katz",
3268
- plainName: "Influence at a distance",
3269
- technicalName: "Katz centrality",
3270
- description: "Scores a node by every path that reaches it, with a longer path counting for less.",
3271
- category: "centrality",
3272
- shape: "node-metric",
3273
- fields: G("node", { plainName: "Influence at a distance", technicalName: "Katz score" }),
3274
- options: k(Ee),
3275
- costClass: "iterative",
3276
- complexity: "O(k(n + m))",
3277
- legacyKeys: [{ key: "katz" }]
3278
- },
3279
- {
3280
- key: "hits",
3281
- plainName: "Hubs and authorities",
3282
- technicalName: "HITS",
3283
- description: "Scores every node twice: as a hub that points at good sources, and as an authority that good hubs point at.",
3284
- category: "centrality",
3285
- shape: "node-metric",
3286
- fields: [
3287
- ...G("node", { plainName: "Hub and authority", technicalName: "combined HITS score" }),
3288
- g({ name: "hub", plainName: "Hub", technicalName: "hub score", kind: "node", type: "number" }),
3289
- g({
3290
- name: "authority",
3291
- plainName: "Authority",
3292
- technicalName: "authority score",
3293
- kind: "node",
3294
- type: "number"
3295
- })
3296
- ],
3297
- options: k(ze),
3298
- costClass: "iterative",
3299
- complexity: "O(k(n + m))",
3300
- legacyKeys: [{ key: "hits" }]
3301
- },
3302
- {
3303
- key: "louvain",
3304
- plainName: "Communities",
3305
- technicalName: "Louvain",
3306
- description: "Groups nodes into communities by repeatedly merging the groups whose merger tightens the partition the most.",
3307
- category: "community",
3308
- shape: "community",
3309
- fields: re("Community", !0),
3310
- options: k(Fe),
3311
- costClass: "iterative",
3312
- complexity: "O(k(n + m))",
3313
- legacyKeys: [{ key: "louvain" }]
3314
- },
3315
- {
3316
- key: "leiden",
3317
- plainName: "Communities, refined",
3318
- technicalName: "Leiden",
3319
- description: "Groups nodes into communities the way Louvain does, with a refinement pass that rules out groups held together by a single node.",
3320
- category: "community",
3321
- shape: "community",
3322
- fields: re("Community", !0),
3323
- options: k(Re),
3324
- costClass: "iterative",
3325
- complexity: "O(k(n + m))",
3326
- legacyKeys: [{ key: "leiden" }]
3327
- },
3328
- {
3329
- key: "label-propagation",
3330
- plainName: "Communities, fast",
3331
- technicalName: "Label propagation",
3332
- description: "Groups nodes into communities by letting every node repeatedly take whichever label most of its neighbours carry.",
3333
- category: "community",
3334
- shape: "community",
3335
- fields: re("Community", !1),
3336
- options: k(Pe),
3337
- costClass: "iterative",
3338
- complexity: "O(k(n + m))",
3339
- legacyKeys: [{ key: "label-propagation" }]
3340
- },
3341
- {
3342
- key: "girvan-newman",
3343
- plainName: "Communities by cutting bridges",
3344
- technicalName: "Girvan-Newman",
3345
- description: "Groups nodes into communities by repeatedly cutting the edge that the most shortest paths run through, until the graph falls apart.",
3346
- category: "community",
3347
- shape: "community",
3348
- fields: re("Community", !0),
3349
- options: k(Te),
3350
- costClass: "heavy",
3351
- complexity: "O(n * m^2)",
3352
- legacyKeys: [{ key: "girvan-newman" }]
3353
- },
3354
- {
3355
- key: "components",
3356
- plainName: "Separate pieces",
3357
- technicalName: "Connected components",
3358
- description: "Finds the separate pieces of the graph: two nodes belong to the same piece when a path joins them.",
3359
- category: "structure",
3360
- shape: "community",
3361
- fields: re("Piece", !1),
3362
- options: ot(
3363
- k(ke),
3364
- k(Le),
3365
- Qe(Ra)
3366
- ),
3367
- costClass: "instant",
3368
- complexity: "O(n + m)",
3369
- legacyKeys: [{ key: "connected-components" }, { key: "scc", params: { strength: "strong" } }]
3370
- },
3371
- {
3372
- key: "shortest-path",
3373
- plainName: "Shortest route",
3374
- technicalName: "Shortest path",
3375
- description: "Finds the cheapest route from one node to another, and how far every reachable node is from the source.",
3376
- category: "path",
3377
- shape: "path",
3378
- fields: [
3379
- g({
3380
- name: "onPath",
3381
- plainName: "On the route",
3382
- technicalName: "onPath",
3383
- kind: "node",
3384
- type: "boolean"
3385
- }),
3386
- g({
3387
- name: "order",
3388
- plainName: "Position on the route",
3389
- technicalName: "order",
3390
- kind: "node",
3391
- type: "integer"
3392
- }),
3393
- g({
3394
- name: "distance",
3395
- plainName: "Distance from the source",
3396
- technicalName: "distance",
3397
- kind: "node",
3398
- type: "number"
3399
- }),
3400
- g({
3401
- name: "onPath",
3402
- plainName: "On the route",
3403
- technicalName: "onPath",
3404
- kind: "edge",
3405
- type: "boolean"
3406
- }),
3407
- g({
3408
- name: "length",
3409
- plainName: "Nodes on the route",
3410
- technicalName: "length",
3411
- kind: "graph",
3412
- type: "integer"
3413
- }),
3414
- g({ name: "cost", plainName: "Total cost", technicalName: "cost", kind: "graph", type: "number" }),
3415
- g({
3416
- name: "hops",
3417
- plainName: "Edges on the route",
3418
- technicalName: "hops",
3419
- kind: "graph",
3420
- type: "integer"
3421
- }),
3422
- g({
3423
- name: "hasNegativeCycle",
3424
- plainName: "Has a loop that costs less every time round",
3425
- technicalName: "hasNegativeCycle",
3426
- kind: "graph",
3427
- type: "boolean"
3428
- })
3429
- ],
3430
- options: ot(
3431
- k(xe),
3432
- k(ye),
3433
- Qe(Pa)
3434
- ),
3435
- costClass: "instant",
3436
- complexity: "O((n + m) log n) with dijkstra, O(n * m) with bellman-ford",
3437
- legacyKeys: [
3438
- { key: "dijkstra", params: { method: "dijkstra" } },
3439
- { key: "bellman-ford", params: { method: "bellman-ford" } }
3440
- ]
3441
- },
3442
- {
3443
- /* ONE KEY, ONE RESULT SHAPE. The all-pairs sweep used to be a PARAMETER on shortest-path
3444
- -- `allPairs: true`, engine floyd-warshall -- which made one catalogue key claim two
3445
- different result shapes: a route for the single-source engines, and a measurement of
3446
- every node for this one. A run takes its shape from its descriptor once, at creation,
3447
- so the all-pairs run reported "path", found no `onPath` to paint from, and suggested
3448
- nothing; `styles.encode` refused a hand-written encoding for the same reason. Its own
3449
- key is what makes the shape true, and every consumer that reads the catalogue up front
3450
- right about what a run of it will publish. */
3451
- key: "all-pairs-distance",
3452
- plainName: "How far from everything else",
3453
- technicalName: "All-pairs shortest paths",
3454
- description: "Measures the distance between every pair of nodes, and gives each node the distance to the furthest node it can reach.",
3455
- category: "path",
3456
- shape: "node-metric",
3457
- fields: [
3458
- ...G("node", { plainName: "Distance to the furthest node", technicalName: "eccentricity" }),
3459
- g({
3460
- name: "diameter",
3461
- plainName: "Widest distance",
3462
- technicalName: "diameter",
3463
- kind: "graph",
3464
- type: "number"
3465
- }),
3466
- g({
3467
- name: "radius",
3468
- plainName: "Narrowest distance",
3469
- technicalName: "radius",
3470
- kind: "graph",
3471
- type: "number"
3472
- }),
3473
- g({
3474
- name: "hasNegativeCycle",
3475
- plainName: "Has a loop that costs less every time round",
3476
- technicalName: "hasNegativeCycle",
3477
- kind: "graph",
3478
- type: "boolean"
3479
- })
3480
- ],
3481
- options: k(Ce),
3482
- costClass: "cubic",
3483
- complexity: "O(n^3)",
3484
- legacyKeys: [{ key: "floyd-warshall" }]
3485
- },
3486
- {
3487
- key: "bfs",
3488
- plainName: "Steps away",
3489
- technicalName: "Breadth-first search",
3490
- description: "Walks outwards from one node a level at a time, recording how many steps away each node is.",
3491
- category: "path",
3492
- shape: "layered-grouping",
3493
- fields: [
3494
- g({ name: "level", plainName: "Steps away", technicalName: "level", kind: "node", type: "integer" }),
3495
- g({
3496
- name: "levelSize",
3497
- plainName: "Nodes this many steps away",
3498
- technicalName: "levelSize",
3499
- kind: "node",
3500
- type: "integer"
3501
- }),
3502
- g({ name: "order", plainName: "Visit order", technicalName: "order", kind: "node", type: "integer" }),
3503
- g({
3504
- name: "levelCount",
3505
- plainName: "Levels",
3506
- technicalName: "levelCount",
3507
- kind: "graph",
3508
- type: "integer"
3509
- }),
3510
- g({ name: "sizes", plainName: "Level sizes", technicalName: "sizes", kind: "graph", type: "table" }),
3511
- g({
3512
- name: "targetFound",
3513
- plainName: "Target reached",
3514
- technicalName: "targetFound",
3515
- kind: "graph",
3516
- type: "boolean"
3517
- })
3518
- ],
3519
- options: k(we),
3520
- costClass: "instant",
3521
- complexity: "O(n + m)",
3522
- legacyKeys: [{ key: "bfs" }]
3523
- },
3524
- {
3525
- key: "dfs",
3526
- plainName: "Exploration order",
3527
- technicalName: "Depth-first search",
3528
- description: "Walks as deep as it can from one node before backtracking, recording the order in which it reached each node.",
3529
- category: "path",
3530
- shape: "node-metric",
3531
- fields: [
3532
- ...G("node", {
3533
- plainName: "Exploration order",
3534
- technicalName: "discovery time",
3535
- type: "integer"
3536
- }),
3537
- g({ name: "visited", plainName: "Reached", technicalName: "visited", kind: "node", type: "boolean" })
3538
- ],
3539
- options: k(Oe),
3540
- costClass: "instant",
3541
- complexity: "O(n + m)",
3542
- legacyKeys: [{ key: "dfs" }]
3543
- },
3544
- {
3545
- key: "kruskal",
3546
- plainName: "Cheapest connecting network",
3547
- technicalName: "Kruskal minimum spanning tree",
3548
- description: "Finds the cheapest set of edges that still joins every node, by taking the cheapest edge that does not close a loop.",
3549
- category: "structure",
3550
- shape: "edge-set",
3551
- fields: qe("edge", "In the network", {
3552
- name: "totalWeight",
3553
- plainName: "Total cost",
3554
- technicalName: "totalWeight",
3555
- type: "number"
3556
- }),
3557
- options: k(De),
3558
- costClass: "instant",
3559
- complexity: "O(m log m)",
3560
- requires: { directed: !1 },
3561
- legacyKeys: [{ key: "kruskal" }]
3562
- },
3563
- {
3564
- key: "prim",
3565
- plainName: "Cheapest network from one node",
3566
- technicalName: "Prim minimum spanning tree",
3567
- description: "Finds the cheapest set of edges that still joins every node, by growing outwards from one starting node.",
3568
- category: "structure",
3569
- shape: "edge-set",
3570
- fields: qe("edge", "In the network", {
3571
- name: "totalWeight",
3572
- plainName: "Total cost",
3573
- technicalName: "totalWeight",
3574
- type: "number"
3575
- }),
3576
- options: k(Ke),
3577
- costClass: "instant",
3578
- complexity: "O(m log n)",
3579
- requires: { directed: !1 },
3580
- legacyKeys: [{ key: "prim" }]
3581
- },
3582
- {
3583
- key: "bipartite-matching",
3584
- plainName: "Best pairing",
3585
- technicalName: "Maximum bipartite matching",
3586
- description: "Pairs up the two sides of a two-sided graph so that as many nodes as possible end up with a partner.",
3587
- category: "structure",
3588
- shape: "edge-set",
3589
- fields: [
3590
- ...qe("edge", "In the pairing", {
3591
- name: "bipartite",
3592
- plainName: "Has two sides",
3593
- technicalName: "bipartite",
3594
- type: "boolean"
3595
- }),
3596
- g({ name: "side", plainName: "Side", technicalName: "side", kind: "node", type: "string" }),
3597
- g({ name: "matched", plainName: "Paired", technicalName: "matched", kind: "node", type: "boolean" })
3598
- ],
3599
- options: k(ve),
3600
- costClass: "heavy",
3601
- complexity: "O(n * m)",
3602
- requires: { directed: !1 },
3603
- legacyKeys: [{ key: "bipartite-matching" }]
3604
- },
3605
- {
3606
- key: "max-flow",
3607
- plainName: "Most that can flow",
3608
- technicalName: "Maximum flow",
3609
- description: "Finds the most that can flow from a source to a sink when every edge has a capacity.",
3610
- category: "flow",
3611
- shape: "edge-metric",
3612
- fields: [
3613
- ...G("edge", { plainName: "Flow", technicalName: "flow" }),
3614
- g({ name: "capacity", plainName: "Capacity", technicalName: "capacity", kind: "edge", type: "number" }),
3615
- g({
3616
- name: "utilization",
3617
- plainName: "Share of capacity used",
3618
- technicalName: "utilization",
3619
- kind: "edge",
3620
- type: "number"
3621
- }),
3622
- g({ name: "netFlow", plainName: "Net flow", technicalName: "netFlow", kind: "node", type: "number" }),
3623
- g({ name: "role", plainName: "Source or sink", technicalName: "role", kind: "node", type: "string" }),
3624
- g({
3625
- name: "maxFlow",
3626
- plainName: "Most that can flow",
3627
- technicalName: "maxFlow",
3628
- kind: "graph",
3629
- type: "number"
3630
- })
3631
- ],
3632
- options: k(Ge),
3633
- costClass: "heavy",
3634
- complexity: "O(n * m^2)",
3635
- legacyKeys: [{ key: "max-flow" }]
3636
- },
3637
- {
3638
- key: "min-cut",
3639
- plainName: "Weakest link",
3640
- technicalName: "Minimum cut",
3641
- description: "Finds the cheapest set of edges to remove to split the graph into two pieces.",
3642
- category: "flow",
3643
- shape: "edge-set",
3644
- fields: [
3645
- ...qe("edge", "In the cut", {
3646
- name: "cutValue",
3647
- plainName: "Cost of the cut",
3648
- technicalName: "cutValue",
3649
- type: "number"
3650
- }),
3651
- g({ name: "side", plainName: "Side", technicalName: "side", kind: "node", type: "string" })
3652
- ],
3653
- options: k(He),
3654
- costClass: "heavy",
3655
- complexity: "O(n * m^2) between two nodes, O(n * m + n^2 log n) across the whole graph, O(k * n^2) with Karger",
3656
- legacyKeys: [{ key: "min-cut" }]
3657
- },
3658
- {
3659
- key: "k-core",
3660
- plainName: "Core depth",
3661
- technicalName: "K-core decomposition",
3662
- description: "Peels the graph layer by layer and gives each node the depth of the most tightly knit core it belongs to: a node of depth k has at least k neighbours inside that core.",
3663
- category: "structure",
3664
- shape: "node-metric",
3665
- fields: G("node", { plainName: "Core depth", technicalName: "core number", type: "integer" }),
3666
- options: k(Me),
3667
- costClass: "instant",
3668
- complexity: "O(n + m)",
3669
- legacyKeys: [{ key: "k-core" }]
3670
- },
3671
- {
3672
- key: "link-prediction",
3673
- plainName: "Likely new links",
3674
- technicalName: "Link prediction",
3675
- description: "Scores the pairs of nodes that are not yet joined by the neighbours they share, and lists the pairs most likely to be joined next.",
3676
- category: "prediction",
3677
- shape: "pair-list",
3678
- fields: [
3679
- g({
3680
- name: "pairs",
3681
- plainName: "Likely new links",
3682
- technicalName: "pairs",
3683
- kind: "graph",
3684
- type: "table"
3685
- })
3686
- ],
3687
- options: k($e),
3688
- costClass: "heavy",
3689
- complexity: "O(n^2 * d)",
3690
- legacyKeys: [{ key: "link-prediction" }]
3691
- }
3692
- ], Fa = new Map(
3693
- [
3694
- ye,
3695
- be,
3696
- we,
3697
- ve,
3698
- Ne,
3699
- ke,
3700
- Se,
3701
- Oe,
3702
- xe,
3703
- Ie,
3704
- Ce,
3705
- Te,
3706
- ze,
3707
- Ee,
3708
- Me,
3709
- De,
3710
- Pe,
3711
- Re,
3712
- $e,
3713
- Fe,
3714
- Ge,
3715
- He,
3716
- We,
3717
- Ke,
3718
- Le
3719
- ].map((s) => [s.type, s])
3720
- ), ht = $a.map((s) => ({
3721
- ...s,
3722
- scopeInput: s.legacyKeys.every((e) => Fa.get(e.key)?.scopeInput === "subgraph") ? "subgraph" : "none"
3723
- }));
3724
- function Aa(s) {
3725
- return ht.find((e) => e.key === s);
3726
- }
3727
- function _a(s) {
3728
- for (const e of ht)
3729
- for (const t of e.legacyKeys)
3730
- if (t.key === s)
3731
- return { descriptor: e, params: t.params ?? {} };
3732
- }
3733
- export {
3734
- He as A,
3735
- ht as B,
3736
- lt as C,
3737
- Se as D,
3738
- Ie as E,
3739
- Ce as F,
3740
- Te as G,
3741
- ze as H,
3742
- Me as I,
3743
- $e as J,
3744
- Ee as K,
3745
- Fe as L,
3746
- Ge as M,
3747
- _t as N,
3748
- ta as O,
3749
- We as P,
3750
- Le as S,
3751
- dt as a,
3752
- Ue as b,
3753
- Aa as c,
3754
- _a as d,
3755
- Ja as e,
3756
- Xa as f,
3757
- Ya as g,
3758
- Za as h,
3759
- Va as i,
3760
- qa as j,
3761
- Qa as k,
3762
- ea as l,
3763
- Ba as m,
3764
- xe as n,
3765
- be as o,
3766
- Ne as p,
3767
- Re as q,
3768
- Pe as r,
3769
- ye as s,
3770
- Xt as t,
3771
- we as u,
3772
- Oe as v,
3773
- ke as w,
3774
- De as x,
3775
- Ke as y,
3776
- ve as z
3777
- };