@crstrskp/graph
Version:
High-performance TypeScript graph algorithms library optimized for trading bots and arbitrage detection
345 lines • 11.2 kB
JavaScript
"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