@graphty/algorithms 1.5.0 → 1.7.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.
package/README.md CHANGED
@@ -5,6 +5,7 @@
5
5
  [![npm version](https://img.shields.io/npm/v/@graphty/algorithms.svg)](https://www.npmjs.com/package/@graphty/algorithms)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
  [![Documentation](https://img.shields.io/badge/docs-vitepress-blue)](https://graphty.app/docs/algorithms/)
8
+ [![Storybook](https://img.shields.io/badge/storybook-interactive%20demos-ff4785)](https://graphty.app/storybook/algorithms/)
8
9
  [![Examples](https://img.shields.io/badge/demo-github%20pages-blue)](https://graphty.app/algorithms/)
9
10
 
10
11
  A comprehensive TypeScript graph algorithms library with 98 algorithms optimized for browser environments and visualization applications.
@@ -1028,6 +1029,18 @@ const result = grsbm(graph, {
1028
1029
 
1029
1030
  ## Interactive Examples
1030
1031
 
1032
+ ### Storybook (Recommended)
1033
+
1034
+ Explore algorithms with interactive Storybook demos: **[View Storybook →](https://graphty.app/storybook/algorithms/)**
1035
+
1036
+ Storybook provides:
1037
+ - **Animated visualizations** showing how each algorithm works step-by-step
1038
+ - **Interactive controls** to modify parameters and graph types
1039
+ - **Deterministic output** with seeded randomness for reproducibility
1040
+ - **Visual testing** with Chromatic for regression detection
1041
+
1042
+ ### Legacy HTML Examples
1043
+
1031
1044
  Try out all algorithms with interactive visualizations: **[Live Demo →](https://graphty.app/algorithms/)**
1032
1045
 
1033
1046
  The library includes comprehensive examples demonstrating each algorithm. You can:
@@ -6370,7 +6370,7 @@ function girvanNewman(graph, options = {}) {
6370
6370
  });
6371
6371
  while (Array.from(workingGraph.edges()).length > 0 && iterations < maxIterations) {
6372
6372
  iterations++;
6373
- const edgeBetweenness = calculateEdgeBetweenness(workingGraph);
6373
+ const { betweenness: edgeBetweenness, edgeEndpoints } = calculateEdgeBetweenness(workingGraph);
6374
6374
  if (edgeBetweenness.size === 0) {
6375
6375
  break;
6376
6376
  }
@@ -6378,9 +6378,9 @@ function girvanNewman(graph, options = {}) {
6378
6378
  const edgesToRemove = [];
6379
6379
  for (const [edgeKey, centrality] of edgeBetweenness) {
6380
6380
  if (Math.abs(centrality - maxBetweenness) < 1e-10) {
6381
- const [source, target] = edgeKey.split("|");
6382
- if (source && target) {
6383
- edgesToRemove.push({ source, target });
6381
+ const endpoints = edgeEndpoints.get(edgeKey);
6382
+ if (endpoints) {
6383
+ edgesToRemove.push(endpoints);
6384
6384
  }
6385
6385
  }
6386
6386
  }
@@ -6405,9 +6405,11 @@ function girvanNewman(graph, options = {}) {
6405
6405
  }
6406
6406
  function calculateEdgeBetweenness(graph) {
6407
6407
  const edgeBetweenness = /* @__PURE__ */ new Map();
6408
+ const edgeEndpoints = /* @__PURE__ */ new Map();
6408
6409
  for (const edge of graph.edges()) {
6409
6410
  const edgeKey = getEdgeKey(edge.source, edge.target);
6410
6411
  edgeBetweenness.set(edgeKey, 0);
6412
+ edgeEndpoints.set(edgeKey, { source: edge.source, target: edge.target });
6411
6413
  }
6412
6414
  for (const sourceNode of graph.nodes()) {
6413
6415
  const source = sourceNode.id;
@@ -6458,7 +6460,7 @@ function calculateEdgeBetweenness(graph) {
6458
6460
  edgeBetweenness.set(edgeKey, betweenness / 2);
6459
6461
  }
6460
6462
  }
6461
- return edgeBetweenness;
6463
+ return { betweenness: edgeBetweenness, edgeEndpoints };
6462
6464
  }
6463
6465
  function findAllShortestPaths(graph, source) {
6464
6466
  const distances = /* @__PURE__ */ new Map();
@@ -9022,7 +9024,7 @@ function buildTransitionMatrix(graph, nodeIds, selfLoops) {
9022
9024
  nodeIds.forEach((id, index) => nodeToIndex.set(id, index));
9023
9025
  for (let i = 0; i < n; i++) {
9024
9026
  const nodeId = nodeIds[i];
9025
- if (!nodeId) {
9027
+ if (nodeId === void 0) {
9026
9028
  continue;
9027
9029
  }
9028
9030
  const neighbors = graph.neighbors(nodeId);
@@ -9221,37 +9223,45 @@ function extractClusters(matrix, nodeIds) {
9221
9223
  attractors.add(nodeId);
9222
9224
  }
9223
9225
  }
9224
- let clusterIndex = 0;
9226
+ const attractorToCommunity = /* @__PURE__ */ new Map();
9225
9227
  for (let j = 0; j < n; j++) {
9226
- const clusterNodes = [];
9228
+ let maxVal = 0;
9229
+ let attractorRow = -1;
9227
9230
  for (let i = 0; i < n; i++) {
9228
9231
  const matrixRow = matrix[i];
9229
9232
  if (!matrixRow) {
9230
9233
  continue;
9231
9234
  }
9232
9235
  const val = matrixRow[j];
9233
- if (val !== void 0 && val > 0 && !nodeToCluster.has(i)) {
9234
- clusterNodes.push(i);
9235
- nodeToCluster.set(i, clusterIndex);
9236
+ if (val !== void 0 && val > maxVal) {
9237
+ maxVal = val;
9238
+ attractorRow = i;
9236
9239
  }
9237
9240
  }
9238
- if (clusterNodes.length > 0) {
9239
- communities.push(
9240
- clusterNodes.map((idx) => {
9241
- const nodeId = nodeIds[idx];
9242
- return nodeId;
9243
- }).filter((node) => node !== void 0)
9244
- );
9245
- clusterIndex++;
9241
+ if (attractorRow >= 0) {
9242
+ let community = attractorToCommunity.get(attractorRow);
9243
+ if (!community) {
9244
+ community = [];
9245
+ attractorToCommunity.set(attractorRow, community);
9246
+ }
9247
+ community.push(j);
9248
+ nodeToCluster.set(j, attractorRow);
9246
9249
  }
9247
9250
  }
9248
- for (let i = 0; i < n; i++) {
9249
- if (!nodeToCluster.has(i)) {
9250
- const nodeId = nodeIds[i];
9251
+ for (const [, memberIndices] of attractorToCommunity) {
9252
+ communities.push(
9253
+ memberIndices.map((idx) => {
9254
+ const nodeId = nodeIds[idx];
9255
+ return nodeId;
9256
+ }).filter((node) => node !== void 0)
9257
+ );
9258
+ }
9259
+ for (let j = 0; j < n; j++) {
9260
+ if (!nodeToCluster.has(j)) {
9261
+ const nodeId = nodeIds[j];
9251
9262
  if (nodeId !== void 0) {
9252
9263
  communities.push([nodeId]);
9253
9264
  }
9254
- nodeToCluster.set(i, clusterIndex++);
9255
9265
  }
9256
9266
  }
9257
9267
  return { communities, attractors };