UNPKG

@crstrskp/graph

Version:

High-performance TypeScript graph algorithms library optimized for trading bots and arbitrage detection

345 lines 11.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GraphImpl = void 0; const Edge_1 = require("./Edge"); const Vertex_1 = require("./Vertex"); const Path_1 = require("./Path"); const PriorityQueue_1 = require("./PriorityQueue"); class GraphImpl { constructor() { this.edges = []; this.vertices = []; this.vertexMap = new Map(); this.attributes = {}; } static generateId() { return this.idCounter++; } bellmanFord(src) { var vDists = new Map(); this.vertices.forEach((v) => { vDists.set(v, Number.MAX_VALUE); }); vDists.set(src, 0); for (var i = 0; i < this.vertices.length; i++) { for (var j = 0; j < this.edges.length; j++) { var start = this.edges[j].start; var end = this.edges[j].end; var cost = this.edges[j].getCost(); var startCost = vDists.get(start); var newCost = startCost + cost; if (newCost < vDists.get(end)) { vDists.set(end, newCost); end.prev = this.edges[j]; this.edges[j].prev = start; } } } return vDists; } bellmanFord_shortestPath(src, dest) { var path = new Path_1.Path(); path.addStep(dest); var bmf = this.bellmanFord(src); // this.bmf_print(bmf); var step = dest; while (step != undefined) { step = step.getPrev(); path.addStep(step); } path.reverse(); path.next(); // delete me - removes the first "dest" node, however this shouldn't be done like this! return path; } bmf_print(bmf) { var output = "Node\tDistance from source\n"; bmf.forEach((value, key) => { output += key.label + "\t\t" + value + "\n"; }); // Debug output - uncomment if needed for debugging // console.log(output); } bmf_negativeCycles() { var cycles = []; var vDists = this.bellmanFord(this.vertices[0]); for (var j = 0; j < this.edges.length; j++) { var start = this.edges[j].start; var end = this.edges[j].end; var cost = this.edges[j].getCost(); var c = vDists.get(start) + cost; if (c < vDists.get(end)) { // Found a negative cycle - construct the full cycle path var cycle = this.extractNegativeCycle(end); if (cycle && cycle.steps.length > 0) { cycles.push(cycle); } } } return cycles; } extractNegativeCycle(startVertex) { var cycle = new Path_1.Path(); var current = startVertex; // Walk backward V steps to ensure we're in the cycle for (var i = 0; i < this.vertices.length; i++) { if (!current) break; current = current.getPrev(); } if (!current || !(current instanceof Vertex_1.Vertex)) return null; // Now extract the actual cycle by following predecessors until we loop back var cycleStart = current; var pathSteps = []; var visited = new Set(); do { if (current instanceof Vertex_1.Vertex) { if (visited.has(current.label)) { // Found the start of the cycle, break break; } visited.add(current.label); pathSteps.push(current); if (current.prev) { pathSteps.push(current.prev); current = current.prev.start; } else { break; } } else { break; } } while (current && pathSteps.length < this.vertices.length * 3); // Add the cycle back to the path in correct order for (var i = pathSteps.length - 1; i >= 0; i--) { cycle.addStep(pathSteps[i]); } return cycle.steps.length > 0 ? cycle : null; } dijkstra_shortestPath(src, dest) { var path = new Path_1.Path(); var vDists = new Map(); var visited = new Set(); var pq = new PriorityQueue_1.PriorityQueue(); this.vertices.forEach((v) => { vDists.set(v.label, Number.MAX_VALUE); }); vDists.set(src.label, 0); pq.enqueue(src, 0); while (!pq.isEmpty()) { var currentVertex = pq.dequeue(); if (visited.has(currentVertex.label)) continue; visited.add(currentVertex.label); if (currentVertex === dest) break; var incidentEdges = this.getIncidentStartEdges(currentVertex); incidentEdges.forEach((edge) => { var neighbor = edge.end; var currentCost = vDists.get(currentVertex.label); var newCost = currentCost + edge.getCost(); if (newCost < vDists.get(neighbor.label)) { vDists.set(neighbor.label, newCost); neighbor.prev = edge; edge.prev = currentVertex; pq.enqueue(neighbor, newCost); } }); } if (dest.prev == undefined) { return path; } // console.log(vDists); // build path var step = dest; path.addStep(step); while (step != undefined) { step = step.getPrev(); if (step instanceof Vertex_1.Vertex) path.addStep(step); if (step instanceof Edge_1.Edge) path.addStep(step); } path.reverse(); // console.log(path.toString()); return path; } setAttribute(key, value) { this.attributes[key] = value; } getAttribute(key) { return this.attributes[key]; } getVertexByLabel(label) { return this.vertexMap.get(label); } getVertexById(id) { return this.vertices.find(vertex => vertex.getId() === id); } getEdgeById(id) { return this.edges.find(edge => edge.getId() === id); } getAllVertices() { var vertices = []; for (var i = 0; i < this.vertices.length; i++) { if (this.vertices[i] != null && this.vertices[i] != undefined) { vertices.push(this.vertices[i]); } } this.vertices = vertices; return this.vertices; } getAllEdges() { var edges = []; for (var i = 0; i < this.edges.length; i++) { var e = this.edges[i]; if (e != null && e != undefined) { edges.push(e); } } this.edges = edges; return this.edges; } getIncidentEdges(v) { var incidentEdges = []; this.edges.forEach((edge) => { if (edge.end === v || edge.start === v) if (incidentEdges.includes(edge) == false) incidentEdges.push(edge); }); // console.log("incident edges for vertex {",v, "}:\n",incidentEdges); return incidentEdges; } getIncidentStartEdges(v) { var incidentEdges = []; this.edges.forEach((edge) => { if (edge.start === v) if (incidentEdges.includes(edge) == false) incidentEdges.push(edge); }); return incidentEdges; } getIncidentEndEdges(v) { var incidentEdges = []; this.edges.forEach((edge) => { if (edge.end === v) if (incidentEdges.includes(edge) == false) incidentEdges.push(edge); }); return incidentEdges; } getOpposite(v, e) { if (e.start === v) return e.end; else if (e.end === v) return e.start; else return undefined; } getVertices(e) { var vertices = []; vertices.push(e.start); vertices.push(e.end); return vertices; } getAdjacentVertices(v) { var edges = this.getIncidentEdges(v); var neighbors = []; edges.forEach((e) => { var opp = this.getOpposite(v, e); if (opp != undefined && !neighbors.includes(opp)) neighbors.push(opp); }); return neighbors; } areAdjacent(v, w) { var adj = false; for (var i = 0; i < this.edges.length; i++) { var e = this.edges[i]; if (e.start === v && e.end === w) adj = true; else if (e.start === w && e.end === v) adj = true; else adj = false; } return adj; } insertVertex(o) { if (this.isOfTypeVertex(o)) { this.vertices.push(o); this.vertexMap.set(o.label, o); return o; } else { var v = new Vertex_1.Vertex(o); this.vertices.push(v); this.vertexMap.set(v.label, v); return v; } } isOfTypeVertex(input) { return input instanceof Vertex_1.Vertex; } insertEdge(v, w, o) { var e = new Edge_1.Edge(v, w); e.id = GraphImpl.generateId(); if (Number.isFinite(o)) { e.setCost(o); } else { e.setAttribute("payload", o); } this.edges.push(e); return e; } removeVertex(v) { var edges = this.getIncidentEdges(v); for (var i = 0; i < edges.length; i++) { this.removeEdge(edges[i]); } var i = this.vertices.indexOf(v); var removedElement = this.vertices.splice(i, 1); this.vertexMap.delete(v.label); } removeEdge(e) { for (var i = 0; i < this.edges.length; i++) { if (this.edges[i] === e) delete this.edges[i]; } this.getAllEdges(); // updates the this.edges array } /** * * @param edges list of edges to be sorted * @returns a new list of sorted edges */ sortEdgesASC(edges) { var sortedArray = []; edges.forEach((e) => sortedArray.push(e)); return sortedArray.sort((e1, e2) => { if (e1.cost > e2.cost) return 1; if (e1.cost < e2.cost) return -1; return 0; }); } /** * * @param edges list of edges to be sorted * @returns a new list of sorted edges */ sortEdgesDESC(edges) { var sortedArray = []; edges.forEach((e) => sortedArray.push(e)); return sortedArray.sort((e1, e2) => { if (e1.cost < e2.cost) return 1; if (e1.cost > e2.cost) return -1; return 0; }); } } exports.GraphImpl = GraphImpl; GraphImpl.idCounter = 0; //# sourceMappingURL=GraphImpl.js.map