Skip to content

Commit

Permalink
[FEATURE]: implementing graph dsa
Browse files Browse the repository at this point in the history
  • Loading branch information
helabenkhalfallah committed Nov 28, 2024
1 parent c407cfe commit 77ac213
Show file tree
Hide file tree
Showing 37 changed files with 925 additions and 3,612 deletions.
296 changes: 235 additions & 61 deletions benchmark-graph.ts
Original file line number Diff line number Diff line change
@@ -1,83 +1,257 @@
import { AdjacencyList, AdjacencyMatrix, EdgeList } from './src/index.ts';
import Graph from 'graphology';
import graphologyLouvain from 'graphology-communities-louvain';
import {
degreeCentrality,
inDegreeCentrality,

Check failure on line 5 in benchmark-graph.ts

View workflow job for this annotation

GitHub Actions / build

'inDegreeCentrality' is defined but never used
outDegreeCentrality,

Check failure on line 6 in benchmark-graph.ts

View workflow job for this annotation

GitHub Actions / build

'outDegreeCentrality' is defined but never used
} from 'graphology-metrics/centrality/degree.js';
import { density } from 'graphology-metrics/graph/density.js';

console.log('---------------- AdjacencyList (number) ------------------');
import {
AdjacencyList,
AdjacencyMatrix,
BipartiteGraph,
CyclicGraph,
DAG,
DirectedGraph,
EdgeList,
Louvain,
UndirectedGraph,
WeightedGraph,
} from './src/index.ts';

// First Example: Using Simple Strings as Vertices and Numbers as Edge Data
const adjacencyList = new AdjacencyList<string, number>(); // Specify types for vertices (string) and edge data (number)
console.log('---------------- AdjacencyList ------------------');
const adjacencyList = new AdjacencyList<string, number>();
adjacencyList.addEdge('A', 'B', 10);
adjacencyList.addEdge('A', 'C', 15);
adjacencyList.printGraph();

// Adding vertices
adjacencyList.addVertex('A');
adjacencyList.addVertex('B');
adjacencyList.addVertex('C');
console.log('---------------- AdjacencyMatrix ------------------');
const adjacencyMatrix = new AdjacencyMatrix<string, number>();
adjacencyMatrix.addEdge('A', 'B', 10);
adjacencyMatrix.addEdge('B', 'C', 5);
adjacencyMatrix.printGraph();

// Adding edges with optional edge data (e.g., weights for edges)
adjacencyList.addEdge('A', 'B', 10); // Edge A -> B with weight 10
adjacencyList.addEdge('A', 'C', 15); // Edge A -> C with weight 15
adjacencyList.addEdge('B', 'C', 5); // Edge B -> C with weight 5
console.log('---------------- EdgeList ------------------');
const edgeList = new EdgeList<string, number>();
edgeList.addEdge('A', 'B', 10);
edgeList.addEdge('B', 'C', 5);
edgeList.printGraph();

// Print the adjacencyList to see the current state
adjacencyList.printGraph();
// Output: A → B (Edge Data: 10), C (Edge Data: 15) | B → C (Edge Data: 5) | C →
console.log('---------------- DirectedGraph ------------------');
const directedGraph = new DirectedGraph<string, number>();
directedGraph.addEdge('A', 'B', 10);
directedGraph.addEdge('B', 'C', 5);
directedGraph.printGraph();

// Removing an edge: A -> C
adjacencyList.removeEdge('A', 'C');
adjacencyList.printGraph();
// Output: A → B (Edge Data: 10) | B → C (Edge Data: 5) | C →
console.log('---------------- UndirectedGraph ------------------');
const undirectedGraph = new UndirectedGraph<string, number>();
undirectedGraph.addEdge('A', 'B', 10);
undirectedGraph.addEdge('B', 'C', 5);
undirectedGraph.printGraph();

// Removing vertex C (this also removes its associated edges)
adjacencyList.removeVertex('C');
adjacencyList.printGraph();
// Output: A → B (Edge Data: 10) | B →
console.log('---------------- DAG ------------------');
const dag = new DAG<string, number>();
dag.addEdge('A', 'B', 10);
dag.addEdge('B', 'C', 5);
try {
dag.addEdge('C', 'A', 15); // This should throw an error
} catch (e) {
console.error(e.message);
}
dag.printGraph();

// Second Example: Using Custom Objects as Vertices
console.log('---------------- AdjacencyList (Object) ------------------');
console.log('---------------- CyclicGraph ------------------');
const cyclicGraph = new CyclicGraph<string, number>();
cyclicGraph.addEdge('A', 'B', 10);
cyclicGraph.addEdge('B', 'C', 5);
cyclicGraph.addEdge('C', 'A', 15);
cyclicGraph.printGraph();

// Define a custom object type for vertices
interface MyObject {
id: number;
name: string;
console.log('---------------- BipartiteGraph ------------------');
const bipartiteGraph = new BipartiteGraph<string>();
bipartiteGraph.addVertexToSetA('A');
bipartiteGraph.addVertexToSetB('B');
bipartiteGraph.addEdge('A', 'B');
try {
bipartiteGraph.addEdge('A', 'A'); // Should throw an error
} catch (e) {
console.error(e.message);
}
bipartiteGraph.printGraph();

// Create custom object vertices
const obj1: MyObject = { id: 1, name: 'Node 1' };
const obj2: MyObject = { id: 2, name: 'Node 2' };
const obj3: MyObject = { id: 3, name: 'Node 3' };
console.log('---------------- WeightedGraph ------------------');
const weightedGraph = new WeightedGraph<string>();
weightedGraph.addEdge('A', 'B', 10);
weightedGraph.addEdge('B', 'C', 15);
weightedGraph.printGraph();

// Example usage with custom objects as vertices
const adjacencyListObject = new AdjacencyList<MyObject, number>(); // Using MyObject as vertices and number as edge data
console.log('================== Advanced Graph Benchmark ==================');

// Adding edges between custom objects with edge data
adjacencyListObject.addEdge(obj1, obj2, 10); // Edge between obj1 and obj2 with weight 10
adjacencyListObject.addEdge(obj2, obj3, 5); // Edge between obj2 and obj3 with weight 5
//
// 1. AdjacencyList: Organizational Hierarchy
//
console.log('---------------- AdjacencyList: Organizational Hierarchy ------------------');
const orgChart = new AdjacencyList<string, string>();
orgChart.addEdge('CEO', 'VP1', 'Leads');
orgChart.addEdge('CEO', 'VP2', 'Leads');
orgChart.addEdge('VP1', 'Manager1', 'Manages');
orgChart.addEdge('VP2', 'Manager2', 'Manages');
orgChart.printGraph();
// Example: CEO → VP1 (Edge Data: Leads), VP2 (Edge Data: Leads)

// Print the adjacencyList to see the custom object vertices and their relationships
adjacencyListObject.printGraph();
// Output:
// {"id":1,"name":"Node 1"} → {"id":2,"name":"Node 2"}
// {"id":2,"name":"Node 2"} → {"id":3,"name":"Node 3"}
//
// 2. AdjacencyMatrix: Efficient Graph Traversal (Warshall’s Algorithm for Path Finding)
//
console.log('---------------- AdjacencyMatrix: Path Finding ------------------');
const warshallMatrix = new AdjacencyMatrix<string, null>();
warshallMatrix.addEdge('A', 'B');
warshallMatrix.addEdge('B', 'C');
warshallMatrix.addEdge('C', 'D');
// Compute transitive closure
const vertices = warshallMatrix.getVertices();
vertices.forEach((k) => {
vertices.forEach((i) => {
vertices.forEach((j) => {
if (warshallMatrix.getEdgeData(i, k) && warshallMatrix.getEdgeData(k, j)) {
warshallMatrix.addEdge(i, j);
}
});
});
});
warshallMatrix.printGraph();

console.log('---------------- AdjacencyMatrix ------------------');
//
// 3. EdgeList: Airline Route Optimization
//
console.log('---------------- EdgeList: Airline Route Optimization ------------------');
const routes = new EdgeList<string, number>();
routes.addEdge('A', 'B', 500); // Cost in dollars
routes.addEdge('B', 'C', 300);
routes.addEdge('C', 'A', 700);
// Finding the cheapest route
const cheapest = routes.getEdgeData('A', 'B');
console.log(`Cheapest route from A to B: $${cheapest}`);
// Example: A → B (Edge Data: 500)

const adjacencyMatrix = new AdjacencyMatrix<MyObject, number>(); // Using MyObject as vertices and number as edge data
//
// 4. DirectedGraph: Citation Network
//
console.log('---------------- DirectedGraph: Citation Network ------------------');
const citations = new DirectedGraph<string, null>();
citations.addEdge('PaperA', 'PaperB');
citations.addEdge('PaperB', 'PaperC');
// Find papers that cite "PaperA"
console.log('Cited by PaperA:', citations.getNeighbors('PaperA'));

adjacencyMatrix.addEdge(obj1, obj2, 10); // Edge between obj1 and obj2 with weight 10
adjacencyMatrix.addEdge(obj2, obj3, 5); // Edge between obj2 and obj3 with weight 5
//
// 5. UndirectedGraph: Collaboration Network
//
console.log('---------------- UndirectedGraph: Collaboration Network ------------------');
const collaborations = new UndirectedGraph<string, null>();
collaborations.addEdge('Alice', 'Bob');
collaborations.addEdge('Alice', 'Charlie');
collaborations.addEdge('Bob', 'David');
// Find all collaborators of "Alice"
console.log('Collaborators of Alice:', collaborations.getNeighbors('Alice'));

adjacencyMatrix.printGraph();
// Output:
// {"id":1,"name":"Node 1"} → {"id":2,"name":"Node 2"} (Edge Data: 10)
// {"id":2,"name":"Node 2"} → {"id":3,"name":"Node 3"} (Edge Data: 5)
//
// 6. DAG: Build System Dependency Resolution
//
console.log('---------------- DAG: Build System Dependency Resolution ------------------');
const buildSystem = new DAG<string, null>();
buildSystem.addEdge('A', 'B'); // B depends on A
buildSystem.addEdge('B', 'C'); // C depends on B
buildSystem.addEdge('A', 'C'); // Direct dependency for optimization
console.log('Build Order:');
buildSystem.getVertices().forEach((vertex) => {
console.log(vertex, '->', buildSystem.getNeighbors(vertex));
});

console.log('---------------- EdgeList ------------------');
//
// 7. CyclicGraph: Transaction Network with Loops
//
console.log('---------------- CyclicGraph: Transaction Network ------------------');
const transactions = new CyclicGraph<string, number>();
transactions.addEdge('AccountA', 'AccountB', 100); // $100 transfer
transactions.addEdge('AccountB', 'AccountC', 50);
transactions.addEdge('AccountC', 'AccountA', 150); // Circular flow
transactions.printGraph();

const edgeList = new EdgeList<MyObject, number>(); // Using MyObject as vertices and number as edge data
//
// 8. BipartiteGraph: Content Recommendation
//
console.log('---------------- BipartiteGraph: Content Recommendation ------------------');
const recommendations = new BipartiteGraph<string>();
recommendations.addVertexToSetA('User1');
recommendations.addVertexToSetA('User2');
recommendations.addVertexToSetB('MovieA');
recommendations.addVertexToSetB('MovieB');
recommendations.addEdge('User1', 'MovieA');
recommendations.addEdge('User2', 'MovieB');
// Find all movies recommended for User1
console.log('Movies for User1:', recommendations.getNeighbors('User1'));

// Adding edges between custom objects with edge data (e.g., weight)
edgeList.addEdge(obj1, obj2, 10); // Edge between obj1 and obj2 with weight 10
edgeList.addEdge(obj2, obj3, 5); // Edge between obj2 and obj3 with weight 5
//
// 9. WeightedGraph: Shortest Path for Delivery Routes
//
console.log('---------------- WeightedGraph: Delivery Routes ------------------');
const deliveryRoutes = new WeightedGraph<string>();
deliveryRoutes.addEdge('Warehouse', 'Store1', 10); // Distance in miles
deliveryRoutes.addEdge('Store1', 'Store2', 20);
deliveryRoutes.addEdge('Warehouse', 'Store2', 25);
// Find the shortest route
const distance = deliveryRoutes.getEdgeData('Warehouse', 'Store1');
console.log(`Shortest route from Warehouse to Store1: ${distance} miles`);

// Print the graph to see the custom object vertices and their relationships
edgeList.printGraph();
// Output:
// {"id":1,"name":"Node 1"} → {"id":2,"name":"Node 2"} (Edge Data: 10)
// {"id":2,"name":"Node 2"} → {"id":3,"name":"Node 3"} (Edge Data: 5)
console.log('---------------- Graph Algorithms (Graphology) ------------------');

const GRAPH_OPTIONS = {
allowSelfLoops: true,
multi: false,
};

const projectGraph = new Graph.UndirectedGraph(GRAPH_OPTIONS);

// Add nodes
const nodes = ['A', 'B', 'C', 'D'];
nodes.forEach((node) => projectGraph.addNode(node));

// Add edges with weights
const edges = [
['A', 'B', 1],
['B', 'C', 2],
['C', 'D', 1],
['A', 'D', 2],
];

edges.forEach(([source, target, weight], index) => {
projectGraph.addEdgeWithKey(`${source}-${target}-${index}`, source, target, { weight });
});

// Perform Louvain community detection
const louvainResults = graphologyLouvain.detailed(projectGraph, {
attributes: { weight: 'weight' },
});

// Output the results
console.log({
modularity: louvainResults.modularity,
communities: louvainResults.communities,
density: density(projectGraph),
degreeCentrality: degreeCentrality(projectGraph),
});

console.log('---------------- Graph Algorithms (DSA Toolkit) ------------------');

const dsaGraph = new UndirectedGraph<string, number>();
dsaGraph.addEdge('A', 'B', 1);
dsaGraph.addEdge('B', 'C', 2);
dsaGraph.addEdge('C', 'D', 1);
dsaGraph.addEdge('D', 'A', 2);

const louvain = new Louvain(dsaGraph);
const result = louvain.run();

console.log('Modularity:', result.modularity);
console.log('Communities:', Array.from(result.communities.entries()));
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"graphology": "=0.25.4",
"graphology-communities-louvain": "=2.0.1",
"graphology-metrics": "=2.3.1"
},
"devDependencies": {
"@eslint/js": "=9.11.1",
"@trivago/prettier-plugin-sort-imports": "=4.3.0",
Expand All @@ -37,6 +42,7 @@
"eslint": "=9.0.0",
"eslint-config-prettier": "=9.1.0",
"eslint-plugin-prettier": "=5.2.1",
"graphology-types": "=0.24.8",
"husky": "=9.1.6",
"nodemon": "=2.0.19",
"rimraf": "=6.0.1",
Expand Down
Loading

0 comments on commit 77ac213

Please sign in to comment.