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..c6cac2c 100644 --- a/package.json +++ b/package.json @@ -74,4 +74,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..7beb047 --- /dev/null +++ b/packages/graph/src/floydWarshall.ts @@ -0,0 +1,68 @@ +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 = new Map(); + + if (!nodes) { + throw new Error("invalid nodes data!"); + } + + if (nodes) { + nodes.forEach((node, i) => { + nodeMap.set(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.get(source); + const tIndex = nodeMap.get(target); + 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";