GraphMini is a high-performance graph pattern-matching system for subgraph enumeration on arbitrary patterns. It now provides both:
- a Python API for interactive use in Python scripts and Jupyter notebooks
- the original CLI for preprocessing, code generation, and benchmarking
The Python API is the easiest entry point for most users, so this README starts there.
- Python Quick Start
- Python API Overview
- Example: Run on a Preprocessed Graph
- Example: Build a Graph from NumPy CSR Arrays
- Example: Reuse a Compiled Plan
- CLI Workflow
- Requirements
- Tested Graph Data
- Citation
python3 -m venv venv
source venv/bin/activate
pip install numpy cmake ninja clang-formatFrom the repository root:
python scripts/install_python.pyThis is a source-tree install. The script:
- configures CMake for the active Python interpreter
- builds
pygraphminiand the generated plan-module target - writes a
.pthfile into the environment soimport pygraphminiresolves to this repository's build output
To remove that installation later:
python scripts/uninstall_python.pyYou can override the interpreter or build directory if needed:
python scripts/install_python.py --python venv/bin/python --build-dir buildOn Unix-like systems, the shell wrappers still work:
./scripts/install_python.sh
./scripts/uninstall_python.shThe extension module is named pygraphmini.
pygraphmini.GraphGraph.from_preprocessed(graph_dir, reorder_by_degree=False)Graph.from_csr(indptr, indices, offsets=None, triangles=None, reorder_by_degree=False)
pygraphmini.CompiledPlan- returned by
pygraphmini.compile_plan(...) - executes a compiled shared module in-process
- returned by
pygraphmini.RunResult- structured result object returned by
CompiledPlan.run(...)
- structured result object returned by
graph_dir- type:
str - path to a preprocessed GraphMini graph directory
- the directory is expected to contain GraphMini's binary graph files such as
meta.txt,indptr_u64.bin,offset_u64.bin, triangle counts, and the compiledindicesfile matching the current vertex-id width
- type:
reorder_by_degree- type:
bool - default:
False - when
True, GraphMini reloads the graph and remaps vertex ids so higher-degree data-graph vertices receive smaller ids
- type:
indptr- type: 1-D NumPy array of
np.uint64 - shape:
num_vertices + 1 - CSR row-pointer array
indptr[0]must be0indptr[-1]must equallen(indices)- must be non-decreasing
- type: 1-D NumPy array of
indices- type: 1-D NumPy integer array compatible with GraphMini vertex ids
- current build expects 32-bit vertex ids in practice
- stores all adjacency lists concatenated together in CSR order
- every value must be in
[0, num_vertices) - adjacency lists must be sorted
- adjacency lists must not contain duplicates
- self loops are rejected
- the graph must be undirected, so if
u -> vappears thenv -> umust also appear
offsets- type: optional 1-D NumPy array of
np.uint64 - shape:
num_vertices - if omitted, GraphMini computes it
offsets[v]is the split position inside vertexv's adjacency list where neighbors stop being< v- equivalently:
indices[indptr[v] : indptr[v] + offsets[v]]are the neighbors smaller thanvindices[indptr[v] + offsets[v] : indptr[v + 1]]are the neighbors greater than or equal tov
- this is used by GraphMini's bounded-neighbor and canonicality logic
- type: optional 1-D NumPy array of
triangles- type: optional 1-D NumPy array of
np.uint64 - shape:
num_vertices - if provided, it must follow GraphMini's raw preprocessing convention:
- each entry is the per-vertex triangle count without canonicality constraints
- the total sum over all vertices must therefore be divisible by
6for an undirected graph
- if omitted, GraphMini estimates the graph-wide triangle statistic used by scheduling:
- it selects the top-100 highest-degree vertices
- computes their triangle counts without canonicality constraints
- uses the sample average to estimate the graph-wide triangle count
- for the
graphpischeduler, it also uses those sampled vertices' degrees to derive the average-degree term in the cost model
- this estimate can be larger than the true global average, but it is intentionally biased toward the high-degree region that dominates runtime
- type: optional 1-D NumPy array of
reorder_by_degree- type:
bool - default:
False - applies the same degree-based graph reordering as the preprocessed-graph path
- type:
compile_plan(graph, query_adjmat, query_type, pruning_type="eager", parallel_type="nested_rt", scheduler="graphpi")
graph- type:
pygraphmini.Graph - the loaded graph object to compile against
- type:
query_adjmat- type:
str - flattened adjacency matrix for the query graph
- length must be a perfect square
- for a query with
kvertices, the string length must bek * k - example:
- triangle:
"011101110" - 4-clique:
"0111101111011110"
- triangle:
- type:
query_type- type:
str - required
- allowed values:
vertexedgeedge_iep
- type:
pruning_type- type:
str - default:
eager - Python API currently supports:
noneeagercostmodel
- type:
parallel_type- type:
str - default:
nested_rt - allowed values:
openmptbb_topnestednested_rt
- type:
scheduler- type:
str - default:
graphpi - allowed values:
graphpigraphminigraphzero
- type:
graph- type:
pygraphmini.Graph - graph to execute the compiled plan on
- in normal usage this should be the same graph that was used during compilation
- type:
num_threads- type:
int - default:
0 - if
<= 0, GraphMini uses the machine's hardware concurrency - otherwise it uses the requested positive thread count
- type:
compile_plan(...) requires:
graphquery_adjmatquery_type
The remaining options are optional:
query_type:vertex,edge,edge_ieppruning_type: defaults toeager; Python API supportsnone,eager,costmodelparallel_type: defaults tonested_rtscheduler: defaults tographpi
GraphMini preserves its current performance model:
- schedule the query
- generate specialized C++ code
- compile that code into a shared module
- load the module back into the current Python process
Compiled plan modules are cached under build/python_plan_cache, so repeated use of the same generated code avoids recompiling.
Preprocess the graph once:
./build/bin/prep --path_to_graph=./dataset/wikiThen use it from Python:
import pygraphmini as gm
graph = gm.Graph.from_preprocessed(
"./dataset/GraphMini/wiki",
reorder_by_degree=True,
)
plan = gm.compile_plan(
graph=graph,
query_adjmat="011101110",
query_type="vertex",
)
result = plan.run(graph, num_threads=32)
print("pattern_size:", plan.pattern_size)
print("module_path:", plan.module_path)
print("result:", result.result)
print("execution_time_seconds:", result.execution_time_seconds)
print("throughput:", result.throughput)print(graph.num_vertices)
print(graph.num_edges)
print(graph.num_triangles)Graph.from_csr(...) is intended for notebook use and in-memory workflows.
Requirements:
indptrmust be a 1-Dnp.uint64arrayindicesmust be a 1-D integer array compatible with GraphMini vertex ids- adjacency lists must be sorted
- the graph must be undirected, so if
u -> vappears thenv -> umust also appear - self loops and duplicate neighbors are rejected
offsetsis optional; if omitted, GraphMini computes ittrianglesis optional- if
trianglesis provided, it must follow GraphMini's raw preprocessing convention: per-vertex counts without canonicality constraints, so the total sum is divisible by 6
If triangles is omitted, GraphMini estimates the triangle statistic needed by the scheduler:
- select the top-100 highest-degree vertices
- compute their triangle counts without canonicality constraints
- use that sample average to estimate the graph-wide triangle count for schedule generation
- if the scheduler is
graphpi, use those same sampled vertices' degrees as the average-degree signal in its cost model
This estimate can be larger than the true graph-wide average, but the high-degree vertices dominate the expensive parts of the search, so their statistics are used to drive scheduling.
Example:
import numpy as np
import pygraphmini as gm
indptr = np.array([0, 2, 4, 6], dtype=np.uint64)
indices = np.array([1, 2, 0, 2, 0, 1], dtype=np.uint32)
graph = gm.Graph.from_csr(
indptr=indptr,
indices=indices,
offsets=None,
triangles=None,
reorder_by_degree=False,
)
plan = gm.compile_plan(
graph=graph,
query_adjmat="011101110",
query_type="vertex",
)
result = plan.run(graph, num_threads=1)
print(result.result)If you want to compile once and run multiple times:
import pygraphmini as gm
graph = gm.Graph.from_preprocessed("./dataset/GraphMini/wiki")
plan = gm.CompiledPlan(
graph=graph,
query_adjmat="011101110",
query_type="vertex",
)
for threads in (1, 8, 32):
result = plan.run(graph, num_threads=threads)
print(threads, result.execution_time_seconds, result.result)plan.run(...) returns a RunResult object with:
resultexecution_time_secondsthroughputnum_threadsvertex_allocatedminigraph_allocatedthread_min_time_secondsthread_mean_time_secondsthread_max_time_secondsthread_time_std_seconds
The CLI remains available for preprocessing and benchmark-style runs.
mkdir -p build
cd build
cmake ..
cmake --build . -jbash dataset/download.sh
bash dataset/prep.shTo preprocess a graph manually:
./build/bin/prep --path_to_graph=./dataset/wiki./build/bin/run \
--graph_name=wiki \
--path_to_graph=./dataset/GraphMini/wiki \
--query_name=P1 \
--query_adjmat=0111101111011110 \
--query_type=vertex \
--pruning_type=costmodel \
--parallel_type=nested_rt \
--scheduler=graphmini \
--num_threads=32 \
--graph_reordering=truePrimary CLI options:
--graph_name: graph nickname--path_to_graph: path to the preprocessed graph directory--query_name: query nickname--query_adjmat: flattened adjacency matrix string--query_type:vertex,edge,edge_iep--pruning_type:none,static,eager,online,costmodel--parallel_type:openmp,tbb_top,nested,nested_rt--scheduler:graphpi,graphzero,graphmini--num_threads: execution thread count--graph_reordering: enable or disable degree-based graph reordering
- 128GB of free RAM to preprocess Friendster correctly
- 180GB of free disk space to store preprocessed graphs
Current status:
- Ubuntu 22.04: supported
- Ubuntu 24.04: supported
- WSL on Windows: known to work
- macOS: portability changes have been added, but this remains untested
- native Windows: portability changes have been added, but this remains untested
Platform outlook:
- Linux support is already in place.
- macOS should be closer now because the project already had Apple-specific CMake handling, and the Python installer is no longer Linux-only.
- native Windows is more plausible now because shared-module loading, process helpers, and graph memory mapping no longer assume POSIX-only APIs.
Remaining caveats:
- macOS and Windows builds are still untested
- the dataset helper scripts are shell scripts aimed at Unix-like environments
- the build and runtime paths are still validated only on Linux in this repository
- CMake >= 3.20
- GCC >= 7
- Python >= 3.10
numpyfor the Python CSR APIclang-formatoptional
- Wiki
- YouTube
- Patents
- LiveJournal
- Orkut
- Friendster
If GraphMini is helpful in your work, please consider citing the paper:
@inproceedings{Liu_2023,
title={GraphMini: Accelerating Graph Pattern Matching Using Auxiliary Graphs},
url={http://dx.doi.org/10.1109/PACT58117.2023.00026},
DOI={10.1109/pact58117.2023.00026},
booktitle={2023 32nd International Conference on Parallel Architectures and Compilation Techniques (PACT)},
publisher={IEEE},
author={Liu, Juelin and Polisetty, Sandeep and Guan, Hui and Serafini, Marco},
year={2023},
month=oct,
pages={211--224}
}