From 1a9e1bb232223e467991b6ba4950bd2d825068ab Mon Sep 17 00:00:00 2001 From: hustcc Date: Thu, 7 Sep 2023 14:53:18 +0800 Subject: [PATCH 1/3] feat: add floydWarshall algo --- __tests__/unit/floydWarshall.spec.ts | 131 +++++++++++++++++++++++++++ package.json | 3 +- packages/graph/src/floydWarshall.ts | 70 ++++++++++++++ packages/graph/src/index.ts | 3 +- 4 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 __tests__/unit/floydWarshall.spec.ts create mode 100644 packages/graph/src/floydWarshall.ts diff --git a/__tests__/unit/floydWarshall.spec.ts b/__tests__/unit/floydWarshall.spec.ts new file mode 100644 index 0000000..8a336a2 --- /dev/null +++ b/__tests__/unit/floydWarshall.spec.ts @@ -0,0 +1,131 @@ +import { Graph } from "@antv/graphlib"; +import { floydWarshall } from "../../packages/graph/src"; + +const graph = new Graph({ + nodes: [ + { + id: 'A', + data: { + }, + }, + { + id: 'B', + data: { + } + }, + { + id: 'C', + data: { + } + }, + { + id: 'D', + data: { + } + }, + { + id: 'E', + data: { + } + }, + { + id: 'F', + data: { + } + }, + { + id: 'G', + data: { + } + }, + { + id: 'H', + data: { + } + }, + ], + edges: [ + { + id: 'e1', + source: 'A', + target: 'B', + data: {}, + }, + { + id: 'e2', + source: 'B', + target: 'C', + data: {}, + }, + { + id: 'e3', + source: 'C', + target: 'G', + data: {}, + }, + { + id: 'e4', + source: 'A', + target: 'D', + data: {}, + }, + { + id: 'e5', + source: 'A', + target: 'E', + data: {}, + }, + { + id: 'e6', + source: 'E', + target: 'F', + data: {}, + }, + { + id: 'e7', + source: 'F', + target: 'D', + data: {}, + }, + ], +}); + +describe('Adjacency Matrix by Algorithm', () => { + it('get graph shortestpath matrix', () => { + const matrix = floydWarshall(graph); + expect(Object.keys(matrix).length).toBe(8); + const node0 = matrix[0]; + expect(node0.length).toBe(8); + expect(node0[0]).toBe(0); + expect(node0[1]).toBe(1); + expect(node0[2]).toBe(2); + expect(node0[3]).toBe(1); + expect(node0[4]).toBe(1); + expect(node0[5]).toBe(2); + expect(node0[6]).toBe(3); + expect(node0[7]).toBe(Infinity); + expect(matrix[1][7]).toBe(Infinity); + expect(matrix[2][7]).toBe(Infinity); + expect(matrix[3][7]).toBe(Infinity); + }); + + it('directed', () => { + // directed + const matrix = floydWarshall(graph, true); + expect(Object.keys(matrix).length).toBe(8); + const node0 = matrix[0]; + expect(node0.length).toBe(8); + expect(node0[0]).toBe(0); + expect(node0[1]).toBe(1); + expect(node0[2]).toBe(2); + expect(node0[3]).toBe(1); + expect(node0[4]).toBe(1); + expect(node0[5]).toBe(2); + expect(node0[6]).toBe(3); + expect(node0[7]).toBe(Infinity); + const node8 = matrix[6]; + expect(node8.length).toBe(8); + expect(node8[1]).toBe(Infinity); + expect(node8[6]).toBe(0); + }); +}); diff --git a/package.json b/package.json index 36c2fa5..be4e667 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ }, "devDependencies": { "@antv/graphlib": "^2.0.0", + "@antv/util": "^2.0.13", "@changesets/cli": "^2.26.1", "@types/jest": "latest", "benchmark": "^2.1.4", @@ -74,4 +75,4 @@ "access": "public", "registry": "https://registry.npmjs.org" } -} \ No newline at end of file +} diff --git a/packages/graph/src/floydWarshall.ts b/packages/graph/src/floydWarshall.ts new file mode 100644 index 0000000..b5cd1ea --- /dev/null +++ b/packages/graph/src/floydWarshall.ts @@ -0,0 +1,70 @@ +import { Graph, Matrix } from "./types"; + +function getAdjMatrix(graph: Graph, directed: boolean) { + const nodes = graph.getAllNodes(); + const matrix: Matrix[] = []; + // map node with index in data.nodes + const nodeMap: { + [key: string]: number; + } = {}; + + if (!nodes) { + throw new Error("invalid nodes data!"); + } + + if (nodes) { + nodes.forEach((node, i) => { + nodeMap[node.id] = i; + const row: number[] = []; + matrix.push(row); + }); + } + + const edges = graph.getAllEdges(); + if (edges) { + edges.forEach((edge) => { + const { source, target } = edge; + const sIndex = nodeMap[source as string]; + const tIndex = nodeMap[target as string]; + if ((!sIndex && sIndex !== 0) || (!tIndex && tIndex !== 0)) return; + matrix[sIndex][tIndex] = 1; + if (!directed) { + matrix[tIndex][sIndex] = 1; + } + }); + } + return matrix; +} + +export function floydWarshall( + graph: Graph, + directed = false, +) { + const adjacentMatrix = getAdjMatrix(graph, directed); + + const dist: Matrix[] = []; + const size = adjacentMatrix.length; + for (let i = 0; i < size; i += 1) { + dist[i] = []; + for (let j = 0; j < size; j += 1) { + if (i === j) { + dist[i][j] = 0; + } else if (adjacentMatrix[i][j] === 0 || !adjacentMatrix[i][j]) { + dist[i][j] = Infinity; + } else { + dist[i][j] = adjacentMatrix[i][j]; + } + } + } + // floyd + for (let k = 0; k < size; k += 1) { + for (let i = 0; i < size; i += 1) { + for (let j = 0; j < size; j += 1) { + if (dist[i][j] > dist[i][k] + dist[k][j]) { + dist[i][j] = dist[i][k] + dist[k][j]; + } + } + } + } + return dist; +} \ No newline at end of file diff --git a/packages/graph/src/index.ts b/packages/graph/src/index.ts index bcbd01f..6a22580 100644 --- a/packages/graph/src/index.ts +++ b/packages/graph/src/index.ts @@ -2,4 +2,5 @@ export * from "./pageRank"; export * from "./findPath"; export * from "./louvain"; export * from "./iLouvain"; -export * from "./k-core"; \ No newline at end of file +export * from "./k-core"; +export * from "./floydWarshall"; From 0fa44352fb93fef2eecffbd8d1c61c956d25a7bc Mon Sep 17 00:00:00 2001 From: hustcc Date: Thu, 7 Sep 2023 14:56:49 +0800 Subject: [PATCH 2/3] chore: remove devDependencies --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index be4e667..c6cac2c 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,6 @@ }, "devDependencies": { "@antv/graphlib": "^2.0.0", - "@antv/util": "^2.0.13", "@changesets/cli": "^2.26.1", "@types/jest": "latest", "benchmark": "^2.1.4", From 4612d9900f06c1f0b28575a72e7721c3bf1b69b4 Mon Sep 17 00:00:00 2001 From: hustcc Date: Thu, 7 Sep 2023 15:10:17 +0800 Subject: [PATCH 3/3] chore: use Map instead of object --- packages/graph/src/floydWarshall.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/graph/src/floydWarshall.ts b/packages/graph/src/floydWarshall.ts index b5cd1ea..7beb047 100644 --- a/packages/graph/src/floydWarshall.ts +++ b/packages/graph/src/floydWarshall.ts @@ -4,9 +4,7 @@ function getAdjMatrix(graph: Graph, directed: boolean) { const nodes = graph.getAllNodes(); const matrix: Matrix[] = []; // map node with index in data.nodes - const nodeMap: { - [key: string]: number; - } = {}; + const nodeMap = new Map(); if (!nodes) { throw new Error("invalid nodes data!"); @@ -14,7 +12,7 @@ function getAdjMatrix(graph: Graph, directed: boolean) { if (nodes) { nodes.forEach((node, i) => { - nodeMap[node.id] = i; + nodeMap.set(node.id, i); const row: number[] = []; matrix.push(row); }); @@ -24,8 +22,8 @@ function getAdjMatrix(graph: Graph, directed: boolean) { if (edges) { edges.forEach((edge) => { const { source, target } = edge; - const sIndex = nodeMap[source as string]; - const tIndex = nodeMap[target as string]; + const sIndex = nodeMap.get(source); + const tIndex = nodeMap.get(target); if ((!sIndex && sIndex !== 0) || (!tIndex && tIndex !== 0)) return; matrix[sIndex][tIndex] = 1; if (!directed) {