mbsoft31/graph-algorithms
PHP 8.2+ graph algorithms built on nexus-scholar/graph-core. Includes PageRank and degree centrality, Dijkstra and A* shortest paths, BFS/DFS traversal, Tarjan SCC, topological sort with cycle detection, and minimum spanning tree utilities via typed APIs.
composer require nexus-scholar/graph-algorithms nexus-scholar/graph-core
nexus-scholar/graph-core:
use NexusScholar\GraphCore\Graph;
$graph = new Graph();
$graph->addNode('node1');
$graph->addNode('node2');
$graph->addEdge('node1', 'node2', ['weight' => 5]);
use Mbsoft\Graph\Algorithms\Centrality\PageRank;
$pagerank = new PageRank();
$scores = $pagerank->compute($graph);
// Load a citation graph (e.g., from a database or CSV)
$citationGraph = buildCitationGraphFromData();
// Compute author influence using PageRank
$pagerank = new PageRank(dampingFactor: 0.85);
$influenceScores = $pagerank->compute($citationGraph);
// Find shortest path between two papers (e.g., for "citation lineage")
$dijkstra = new Dijkstra();
$path = $dijkstra->find($citationGraph, 'paperA', 'paperB');
Graph Preparation:
nexus-scholar/graph-core to build your graph (nodes/edges with attributes).$indexMap = new \Mbsoft\Graph\Algorithms\IndexMap($graph);
$algorithmGraph = new \Mbsoft\Graph\Algorithms\AlgorithmGraph($graph, $indexMap);
Algorithm Execution:
$pagerank = new PageRank(maxIterations: 50);
$scores = $pagerank->compute($graph);
$astar = new AStar(
heuristicCallback: fn ($from, $to) => haversineDistance($from, $to),
);
$path = $astar->find($graph, 'start', 'end');
$components = (new StronglyConnected())->findComponents($graph);
Result Handling:
PathResult, MstResult) for structured data.if ($path instanceof PathResult) {
$nodes = $path->nodes;
$cost = $path->cost;
}
Service Container Binding:
$this->app->bind(MultiPathFinder::class, function ($app) {
return new MultiPathFinder(
new AStar(heuristicCallback: $app['heuristic.callback']),
new Dijkstra()
);
});
Job Queues for Heavy Computations:
use Mbsoft\Graph\Algorithms\Centrality\PageRank;
use Illuminate\Bus\Queueable;
class ComputeInfluence implements ShouldQueue
{
use Queueable;
public function handle() {
$pagerank = new PageRank();
$scores = $pagerank->compute($this->graph);
$this->storeScores($scores);
}
}
API Responses:
return response()->json([
'path' => $path->nodes,
'cost' => $path->cost,
'nodes_visited' => count($path->nodes),
]);
Custom Weights for Pathfinding:
$dijkstra = new Dijkstra(
weightExtractor: fn ($attrs) => $attrs['custom_weight'] ?? 1.0
);
Heuristic Functions for A*:
$heuristic = fn ($from, $to) => sqrt(
($from['x'] - $to['x']) ** 2 + ($from['y'] - $to['y']) ** 2
);
$astar = new AStar($heuristic);
Post-Processing Results:
$components = (new StronglyConnected())->findComponents($graph);
$largestComponent = collect($components)->max(fn ($c) => count($c));
Negative Weights:
$edges = $graph->getEdges();
foreach ($edges as $edge) {
if ($edge['weight'] < 0) {
throw new \InvalidArgumentException("Negative weights not allowed for Dijkstra.");
}
}
Disconnected Graphs:
null or empty results.$mst = (new MinimumSpanningTree())->compute($graph);
if ($mst === null) {
return response()->json(['error' => 'Graph is disconnected'], 400);
}
Performance with Large Graphs:
AlgorithmGraph proxy improves speed but requires O(n) memory for indexing.IndexMap and AlgorithmGraph if running repeated operations:
$indexMap = new IndexMap($graph);
$algorithmGraph = new AlgorithmGraph($graph, $indexMap);
// Reuse $algorithmGraph for multiple algorithm runs
String Node IDs:
IndexMap or pre-process data.Floating-Point Precision:
tolerance and maxIterations:
$pagerank = new PageRank(tolerance: 1e-8, maxIterations: 200);
Log Intermediate States:
$pagerank = new PageRank();
$pagerank->setLogger($this->logger); // If supported
$scores = $pagerank->compute($graph);
Visualize Graphs:
Use graph-core's export methods to generate DOT files for debugging:
$graph->toDot()->saveToFile('graph.dot');
Test with Fixtures: The package includes Pest fixtures. Reuse them for local testing:
use Mbsoft\Graph\Algorithms\Tests\Fixtures\SampleGraph;
$graph = SampleGraph::create();
Custom Algorithms:
Implement interfaces like CentralityAlgorithmInterface to add new algorithms:
class MyCentrality implements CentralityAlgorithmInterface {
public function compute(GraphInterface $graph): array {
// Custom logic
}
}
Graph Adapters:
Create adapters to bridge graph-core with other graph libraries (e.g., jenssegers/agent):
class LaravelGraphAdapter implements GraphInterface {
// Delegate to Laravel's graph storage
}
Event Hooks:
Extend algorithms by overriding methods (e.g., add logging in AStar::find()).
Default Parameters:
0.85 by default (standard for web graphs).0.95 for higher "teleportation" probability).Edge Attributes:
null or 1.0 for unweighted edges:
$graph->addEdge('a', 'b', ['weight' => null]); // Treated as unweighted
Cycle Detection:
try {
$order = (new TopologicalSort())->sort($graph);
} catch (CycleDetectedException $e) {
$order = null;
}
AlgorithmGraph:
Convert the graph to AlgorithmGraph once and reuse it:
$indexMap = new IndexMap($graph);
$algorithmGraph = new AlgorithmGraph($graph, $indexMap);
$pager
How can I help you explore Laravel packages today?