Graph Algorithms¶
Grafeo includes a library of built-in graph algorithms for common analysis tasks, accessible via the db.algorithms() API.
Algorithm Categories¶
-
Path Finding
Shortest paths, all paths and path analysis.
-
Centrality
PageRank, betweenness, closeness and degree centrality.
-
Community Detection
Louvain, label propagation and connected components.
-
Link Prediction
Predict missing or future relationships.
-
Similarity
Node similarity and graph matching.
-
Graph Metrics
Density, diameter, clustering coefficient.
Quick Reference¶
| Algorithm | Category | Complexity | Use Case |
|---|---|---|---|
| Shortest Path | Path | O(V + E) | Navigation, routing |
| PageRank | Centrality | O(V + E) | Ranking, importance |
| Louvain | Community | O(V log V) | Clustering |
| Stochastic Block Partition | Community | O(V * B^2) | Bayesian community inference |
| Connected Components | Community | O(V + E) | Graph structure |
| Triangle Count | Metrics | O(E^1.5) | Clustering analysis |
| K-Truss | Structure | O(m^1.5) | Dense subgraph discovery |
| Subgraph Isomorphism | Structure | O(n^k) | Pattern matching, motif counting |
Using Algorithms¶
From Python¶
Algorithms are accessed through the db.algorithms() method:
import grafeo
db = grafeo.GrafeoDB()
# Get the algorithms interface
algs = db.algorithms()
# Run PageRank
scores = algs.pagerank()
for node_id, score in scores.items():
print(f"Node {node_id}: {score:.4f}")
# Find shortest path
path = algs.shortest_path(source=1, target=100)
print(f"Path length: {len(path)}")
# Connected components
components = algs.connected_components()
print(f"Found {len(components)} components")
Available Methods¶
The algorithms() object provides these methods:
Traversal:
bfs(start)- Breadth-first searchdfs(start)- Depth-first search
Shortest Paths:
dijkstra(source, target)- Dijkstra's algorithmshortest_path(source, target)- Generic shortest pathsssp(source, weight_attr)- Single-source shortest paths (weighted)all_pairs_shortest_path()- All-to-all shortest paths
Centrality:
pagerank()- PageRank algorithmbetweenness_centrality()- Betweenness centralitycloseness_centrality()- Closeness centralityeigenvector_centrality()- Eigenvector centralitydegree_centrality()- Degree centrality
Community Detection:
connected_components()- Find connected componentsstrongly_connected_components()- Find SCCslouvain()- Louvain community detectionlabel_propagation()- Label propagation
Structure:
triangles()- Triangle countingtriangles(parallel=True)- Parallel triangle counting (degree-ordered, merge intersection)transitivity()- Global clustering coefficientktruss(k=None)- K-truss decomposition (dense subgraph discovery)subgraph_isomorphism_count(pattern)- Count subgraph isomorphismsminimum_spanning_tree()- MST constructionmax_flow(source, sink)- Maximum flow
From Query Languages (GQL, Cypher, SQL/PGQ)¶
All algorithms are available via CALL statements in any supported query language:
-- Run PageRank with default parameters
CALL grafeo.pagerank()
-- Run PageRank with custom parameters
CALL grafeo.pagerank({damping: 0.85, max_iterations: 20})
-- Select specific columns with YIELD
CALL grafeo.pagerank() YIELD node_id, score
-- Alias output columns
CALL grafeo.pagerank() YIELD node_id AS id, score AS rank
-- Ignore edge direction: each pair of connected nodes counts once
CALL grafeo.pagerank({directed: false})
-- Run on a projection only (CREATE PROJECTION or create_projection())
CALL grafeo.pagerank({projection: 'people'})
-- List all available procedures
CALL grafeo.procedures()
A CALL reads the graph the session selects (USE GRAPH, or set_graph() in Python), unless its projection argument names a projection.
Works the same way across all three languages:
Available Procedures¶
| Procedure | Category | Output Columns |
|---|---|---|
grafeo.pagerank() | Centrality | node_id, score |
grafeo.betweenness_centrality() | Centrality | node_id, centrality |
grafeo.closeness_centrality() | Centrality | node_id, centrality |
grafeo.degree_centrality() | Centrality | node_id, in_degree, out_degree, total_degree |
grafeo.degree_centrality({normalized: true}) | Centrality | node_id, centrality |
grafeo.bfs(start) | Traversal | node_id, depth |
grafeo.dfs(start) | Traversal | node_id, depth, discovery, finish |
grafeo.dijkstra(source) | Shortest Path | node_id, distance |
grafeo.dijkstra(source, target) | Shortest Path | source, target, distance, path |
grafeo.bellman_ford(source) | Shortest Path | node_id, distance, has_negative_cycle |
grafeo.sssp(source, weight) | Shortest Path | node_id, distance |
grafeo.floyd_warshall() | Shortest Path | source, target, distance |
grafeo.connected_components() | Components | node_id, component_id |
grafeo.strongly_connected_components() | Components | node_id, component_id |
grafeo.topological_sort() | Components | node_id, order |
grafeo.louvain() | Community | node_id, community_id, modularity |
grafeo.label_propagation() | Community | node_id, community_id |
grafeo.clustering_coefficient() | Clustering | node_id, coefficient, triangle_count |
grafeo.kruskal() | MST | source, target, weight, total_weight |
grafeo.prim() | MST | source, target, weight, total_weight |
grafeo.max_flow(source, sink) | Flow | source, target, flow, max_flow |
grafeo.min_cost_max_flow(source, sink) | Flow | source, target, flow, cost, max_flow, total_cost |
grafeo.articulation_points() | Structure | node_id |
grafeo.bridges() | Structure | source, target |
grafeo.kcore() | Structure | node_id, core_number, max_core |
grafeo.kcore(k) | Structure | node_id, in_k_core |
grafeo.ktruss() | Structure | source, target, truss_number |
grafeo.total_triangles() | Clustering | total_triangles |
grafeo.stochastic_block_partition() | Community | node_id, block_id, description_length |
grafeo.subgraph_isomorphism() | Structure | count |
grafeo.bfs reports each reached node's distance from the start as depth. grafeo.dfs reports its depth in the DFS tree (0 for the start, which can be more than the distance), and its positions in the order DFS reaches the nodes (discovery, pre-order) and finishes them (finish, post-order).
Arguments are constants: literals, parameters (CALL grafeo.bfs($start)) and expressions of them. An argument that reads a row variable, or a value of the wrong type for its parameter, is an error, and so is a map key that names no parameter ({dampingg: 0.5}; projection is taken by every procedure) or a positional argument past the last parameter. The columns follow the arguments: dijkstra with a target gives the one path, kcore with a k the members of that core.
Determinism¶
The same graph, loaded in the same order, gives bit-identical results with the same Grafeo version, in any process, on any platform and with any number of threads:
- Results come in node-id order:
CALLrows (edge results by source, then target), the dicts and lists ofdb.algorithms(the nodes of each component too), and the lists the Rust functions return (flow edges by source, then target). Results whose order is the answer (paths, BFS and DFS order, topological order, spanning tree edges) keep their own order. - Ties are broken by node id. Louvain and label propagation visit nodes in node-id order, a node that gains equally from several communities joins the one with the smallest index, and communities are numbered 0, 1, 2, ... in order of their smallest node id.
- Floating-point sums run in a fixed order, and averages over many nodes (the clustering coefficient) use compensated summation, so the parallel and the sequential paths agree.
Loaded in a different order, the results that do not depend on node ids stay identical: core numbers, degrees, triangle counts, and connected components and articulation points as sets. PageRank agrees up to rounding, and Louvain and label propagation can find a different partition, since they visit nodes in node-id order.
Results independent of the load order: key=¶
In Python, the methods of db.algorithms that return a value per node, and articulation_points() and kcore(), take key=: a node property every node in scope holds, each with its own value (an id from outside the database). The result is then keyed by that value instead of the node id, in key order:
scores = db.algorithms.pagerank(directed=False, key="id") # {"src/main.py": 0.12, ...}
communities = db.algorithms.louvain(key="id")["communities"]
PageRank, Louvain and label propagation also run in key order: every sum, visit, tie and community number follows the keys instead of the node ids. Their results are then bit-identical for the same graph whatever order its nodes and edges were loaded in. With keys that sort like the node ids, the results equal those without a key. The other methods (degree, betweenness and closeness centrality, connected components, triangle counts, the clustering coefficients, k-core, articulation points) only change their keys: betweenness and closeness still sum in node-id order.
Every node in scope needs a non-null value of the key, and no two nodes can share one: a node without it, or two nodes whose values are equal (1 and 1.0 too), raise GrafeoError before the algorithm runs. Keys of different types are allowed; they order as ORDER BY orders them. The values must also work as Python dict keys: strings, numbers, booleans, bytes, dates, times and datetimes do. A list, a map, a vector or a duration raises GrafeoError, and so do two values Python treats as the same key, such as True and 1, or two times within the same microsecond (Python times hold microseconds). Ordering the nodes costs one read of the key per node and a sort, about 1.5 ms for 9,000 nodes.
When a release changes an algorithm's results, its CHANGELOG lists the change under "Result changes".
NetworkX Integration¶
For additional algorithms, use the NetworkX adapter:
# Convert to NetworkX graph
nx_adapter = db.as_networkx(directed=True)
nx_graph = nx_adapter.to_networkx()
# Use any NetworkX algorithm
import networkx as nx
centrality = nx.eigenvector_centrality(nx_graph)
Performance Considerations¶
- Graph Size - Large graphs may require more memory
- Iterations - More iterations = better accuracy, longer runtime
- Parallelism - Many algorithms support parallel execution
- Caching - Results can be cached for repeated queries