# GraKeL: graph kernels that plug straight into scikit-learn

> A Python library that implements 16 established graph kernels and 2 frameworks on top of them, so a graph can be turned into a feature vector and fed to a scikit-learn classifier or regressor. It ships Cython and C++ extensions for the hot loops, and the same kernels that come from a dozen separate papers live behind one API.

**ysig/GraKeL** — A scikit-learn compatible library for graph kernels

- Repository: https://github.com/ysig/GraKeL
- Website: https://ysig.github.io/GraKeL/
- Stars: 647 · Forks: 103
- Language: Jupyter Notebook
- License: NOASSERTION
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/ysig-grakel

## One API for kernels that normally arrive one paper at a time

Graph kernels are the standard way to apply machine learning to graph structured data, and the annoying part is that the literature delivers them one paper at a time, each with its own assumptions about labels, its own normalisation, its own way of handling disconnected graphs. GraKeL collects them into one library with one interface.

The readme states the count plainly: 16 kernels and 2 frameworks, with the frameworks implemented on top of the kernels. The homepage documentation links to a JMLR paper, volume 21, paper 18-370, which is where the library's design and benchmarks are described, and the repository's primary language is listed as Jupyter Notebook, which is a hint that the examples are notebooks rather than a package of finished applications.

The intended user is a researcher or practitioner who already knows the model they want to fit, an SVM say, and who does not want the comparison between two graphs to be the hard part of the project. GraKeL exposes the kernels as scikit-learn compatible transformers, so the kernel sits in a pipeline alongside the classifier and the hyperparameters are handled by the same search machinery you would use for any other model.

The range of kernels is wide enough to matter. There are the vertex histogram and edge histogram kernels, the shortest path kernel from Borgwardt and Kriegel, the graphlet sampling kernel from Shervashidze and colleagues, the random walk kernel from Vishwanathan and colleagues, the linear time neighborhood hash kernel from Hido and Kashima, the Weisfeiler-Lehman framework, the neighborhood subgraph pairwise distance kernel from Costa and De Grave, the Lovasz-theta and SVM-theta global kernels from Johansson and colleagues, the ordered decompositional DAG kernel from Da San Martino and colleagues, the GraphHopper kernel for graphs with continuous attributes, the propagation kernel from Neumann and colleagues, the pyramid match kernel from Nikolentzos and colleagues, the subgraph matching kernel from Kriege and Mutzel, the multiscale Laplacian kernel from Kondor and Pan, and the Weisfeiler-Lehman optimal assignment kernel from Kriege and colleagues. The two frameworks are the core framework and the Weisfeiler-Lehman optimal assignment kernel framework.

## The three families of kernel, and what each one costs

The kernels fall into families that differ in both mechanism and price, and choosing between them is mostly a question about what your graph contains.

Substructure counting kernels are the cheapest and the bluntest. The vertex histogram kernel counts how many vertices carry each label, the edge histogram kernel counts edges by label pair, and the graphlet sampling kernel counts small subgraphs of a given size. They answer the question of whether two graphs contain the same local pieces, which is useful when labels are informative and the structure is shallow.

The shortest path kernel walks every pair of vertices and aggregates the labels along the path, which makes it sensitive to the global shape of the graph and not only its local pieces. It is exact, and exactness is the problem: the cost grows with the number of vertex pairs, and the readme's example directory includes an erdos_renyi.py script and a separate shortest_path.py, which is a reasonable way to see the difference between a cheap kernel and an expensive one on a synthetic graph.

The global kernels, Lovasz-theta and SVM-theta, take a different route. They embed the graph into a geometric space and compare the embedding, so they capture global structure without enumerating all paths. That is what makes them interesting for large graphs, and they are also the ones with the optional dependency. The Lovasz-theta kernel needs cvxopt, which the pyproject.toml lists under an optional dependency group named lovasz.

The propagation kernel and the Weisfeiler-Lehman framework sit in between. Weisfeiler-Lehman works by iteratively refining vertex labels and counting the resulting colour histograms, which is linear in the number of iterations and the graph size, and the Weisfeiler-Lehman optimal assignment variant is built to be a valid kernel under the assignment formulation rather than a set match count.

The subgraph matching kernel from Kriege and Mutzel and the neighborhood subgraph pairwise distance kernel are the expensive end. Subgraph matching is essentially counting all common subgraphs between two graphs, which is the sort of problem that stops being tractable quickly.

## The Cython and C++ layer under the Python API

The setup.py in the repository shows that the pure Python surface sits on compiled code, and this is more than an optimisation detail because some of the kernels have combinatorial inner loops.

There is a Cython extension named grakel.kernels._c_functions, built from a functions.pyx file plus two C++ sources, ArashPartov.cpp and sm_core.cpp, with headers under an include directory. The extension is compiled as C++ with optimisation flags that depend on the platform, /O2 and /w on Windows and -O3 and -w on Linux and Darwin.

There is a second extension for graph isomorphism. The sources include a vendored copy of the bliss library, version 0.50, under grakel/kernels/_isomorphism/bliss-0.50/, with the core files graph.cc, heap.cc, orbit.cc, partition.cc and uintseqhash.cc, and the extension is named grakel.kernels._isomorphism.intpybliss with version macros for 0 and 50beta. bliss is the algorithm for canonical labelling of graphs, and canonical forms are how you answer whether two graphs are isomorphic quickly enough to be useful inside a kernel.

The build requirements in pyproject.toml are setuptools, cython and numpy, with numpy pinned at 1.19.0 or later, and the pyproject.toml lists Programming Language :: C as a classifier, which is a formal admission that this is not a pure Python package. The uv.lock file in the repository root points to a workflow where the exact dependency set is pinned.

That means installation is not always a simple wheel download. The pyproject.toml declares version 0.1.12 and the package name GraKeL, but the source tree needs a C++ toolchain to build from source, and the .conda_build.sh and meta.yaml files show that a conda recipe is maintained alongside the setuptools build.

## Installing GraKeL and the dependency choices that matter

The install is one command, and the optional extras are what you need to look at before you run it.

```bash
$ pip install grakel
```

The readme lists the requirements: Python at 3.10 or later and below 3.15, NumPy at 1.19.0 or later, SciPy at 1.12.0 or later, scikit-learn at 0.24.0 or later, and then two optional entries, cvxopt at 1.2.0 or later and networkx at 3.0 or later.

The pyproject.toml makes the same split explicit. The required dependencies are:

```toml
dependencies = [
    "numpy>=1.19.0",
    "scipy>=1.12.0",
    "scikit-learn>=0.24.0",
    "joblib",
]
```

And the extras are named groups rather than a single catch all:

```toml
[project.optional-dependencies]
lovasz = ["cvxopt>=1.2.0"]
networkx = ["networkx>=3.0"]
pandas = ["pandas"]
```

There is a dev group and a test group that add pytest, pytest-cov, torch-geometric, pandas and networkx, and a wheel group with pytest and pytest-cov. The presence of torch-geometric in the test extras is a good clue about what the project compares itself against in its own test suite.

The requirements.txt at the root of the repository lists a slightly different set, with cython at 0.29.21 or later in place of the extras. So if you are installing from a source tree rather than from PyPI, the build needs cython. The readme's development section starts with installing uv, and the uv.lock at the root of the repository pins the environment for contributors.

For the first real use, the readme points at two directories rather than at a single script: the examples directory, which contains files such as shortest_path.py, weisfeiler_lehman_subtree.py, nx_to_grakel.py, node_attributed_dataset.py, optimizing_hyperparameters.py, plot_pipeline_example.py, document_retrieval_example.py and erdos_renyi.py, and the tutorials directory. The nx_to_grakel.py name is the one to start with if your graphs come from networkx, since it describes converting them into the format the kernels expect.

## Fitting it inside a scikit-learn pipeline

The compatibility claim is the reason the library is packaged the way it is, and it is worth being concrete about what it means.

A graph kernel computes a similarity between two graphs, and machine learning models expect a fixed length numeric vector per sample. The conversion is a feature map, and the library's job is to provide the feature map for each kernel so the result can be handed to an SVM or a logistic regression or a ridge regressor without you writing the transformation yourself. The plot_pipeline_example.py file in the examples directory is named for exactly this usage, and optimizing_hyperparameters.py is named for the case where the kernel has parameters of its own that need searching alongside the classifier's.

The consequence is that the graph side of your model is a transformer in a pipeline. That means the same cross validation, the same parameter search and the same model comparison code apply to a graph classification problem as to a tabular one. It also means the failure modes are the familiar ones: leakage if you fit the kernel outside the cross validation loop, and a search space that grows when the kernel has its own parameters.

The framework layer adds something on top. The core framework, from the degeneracy framework work of Nikolentzos and colleagues, treats graph similarity as a sequence of local substructure matching steps rather than as one kernel, and the Weisfeiler-Lehman optimal assignment framework does the same for that kernel family. These are the pieces to look at if the plain kernels do not give you the behaviour you need on your data, because they let you compose rather than choose.

The examples directory also contains document_retrieval_example.py, which suggests the kernels are applied outside graph classification, and node_attributed_dataset.py, which is about the case where vertices carry features rather than discrete labels.

## Where the exact kernels stop being practical

There are clear limits, and they are limits of the method rather than of the implementation.

The first is cost. The shortest path kernel is exact, and an exact kernel over all vertex pairs does not scale the way a linear time method does. On the synthetic graphs in the erdos_renyi.py example, you can see how the cost moves with graph size and density, and that behaviour is a property of the kernel definition, so reimplementing it faster does not remove it. The Weisfeiler-Lehman family is the usual answer to this, and the neighborhood hash kernel from Hido and Kashima was explicitly built for linear time.

The second is the dependency on labels being present. Kernels such as the vertex histogram and the Weisfeiler-Lehman framework are label driven. If your graph has continuous node attributes rather than discrete labels, the kernels that count label frequencies have nothing to count, and the ones designed for that case, GraphHopper and the multiscale Laplacian kernel, are the ones to look at. The readme does not document how each kernel handles a graph with no labels at all, which is worth checking before you commit.

The third is installation friction. A package that compiles Cython and C++ extensions is not a pip install away on every platform, and the conda recipe exists precisely because of that. If you need a pinned, reproducible environment in CI, build the wheel once and pin it, which is what the wheel extra group in the pyproject.toml is for.

The fourth is that a graph kernel gives you a similarity, not an explanation. When a classification goes wrong you get a feature vector, not a reason, and the standard model diagnostics apply rather than anything graph specific.

The fifth is the version range. requires-python is >=3.10,<3.15, and the classifiers list 3.10 through 3.14, so the library has been kept current across Python releases, but the upper bound means a project on a future Python has to wait.

## Against writing your own kernel, and against message passing networks

Two comparisons decide whether GraKeL earns its place.

The first is against writing the kernel yourself. The naive approach is to hash node labels into a fixed size vector and concatenate, which is simple and gives up the structural information that makes graph kernels interesting. Doing it properly means implementing the path counting or the subgraph matching correctly, handling normalisation so that the kernel value is comparable across graphs, and handling disconnected graphs, and then getting the feature map right so a linear model can use the similarity. Each of those is a paper. The library packages the published versions with their citations attached, which is the argument for using it rather than reimplementing the shortest path kernel from a PDF.

The second is against graph neural networks, and here the comparison is less about accuracy than about cost and trust. A message passing network learns its own representation from data, which means it needs labelled examples in quantity and a training setup, and its output is hard to inspect. A graph kernel has a fixed, published definition of similarity, so it works in settings with few labels and its behaviour can be reasoned about. The counterargument is the reverse: kernels are hand designed for a specific notion of similarity, and if that notion is wrong for your data no amount of tuning helps, while a learned representation can adapt.

The pyproject.toml putting torch-geometric in the test extras is a fair signal that the project itself treats modern graph neural network tooling as the comparison point rather than as the threat. If you already have a working node embedding pipeline and a classification problem with enough labelled data, GraKeL is the more expensive and less flexible route. If you have few labels, or you want a similarity measure you can defend in a paper, the library gives you 16 published options and one API.

## Conclusion

Adopt GraKeL if you are doing graph classification or regression in Python and want well known kernels with a single API rather than a hand written feature extraction, since the scikit-learn compatibility means the classifier side is unchanged. Do not adopt it if you are working with large graphs under a fixed compute budget, because the shortest path and subgraph matching kernels are exact and their cost grows with the graph size, and the repo's own examples include an Erdős-Rényi benchmark for that reason. Verify first that your graph objects carry the node and edge labels the chosen kernel needs, and read the LICENSE file, since GitHub cannot classify this repository's licence while the pyproject.toml declares BSD-3-Clause.

## FAQ

### How do I install GraKeL?

Run pip install grakel. The library needs Python 3.10 or later and below 3.15, NumPy 1.19.0 or later, SciPy 1.12.0 or later and scikit-learn 0.24.0 or later, and the Lovasz-theta kernel additionally needs cvxopt, which is in the lovasz optional dependency group.

### Which graph kernels does GraKeL implement?

The readme lists 16 kernels and 2 frameworks, including vertex and edge histogram, shortest path, graphlet sampling, random walk, neighborhood hash, Weisfeiler-Lehman, neighborhood subgraph pairwise distance, Lovasz-theta, SVM-theta, ordered decompositional DAG, GraphHopper, propagation, pyramid match, subgraph matching and multiscale Laplacian, plus the core framework and the Weisfeiler-Lehman optimal assignment framework.

### Is GraKeL compatible with scikit-learn?

Yes. The readme states that the library is compatible with the scikit-learn pipeline, allowing integration inside machine learning algorithms, and the examples directory contains a plot_pipeline_example.py and an optimizing_hyperparameters.py for that usage.

### Does GraKeL need a compiler to install?

The setup.py builds a Cython extension named grakel.kernels._c_functions from a pyx file and two C++ sources, plus a second extension that vendors the bliss 0.50 isomorphism library, so building from a source tree needs a C++ toolchain. The pyproject.toml also requires cython 3.2.0 or later at build time.

### How current is GraKeL and what licence applies?

The pyproject.toml declares version 0.1.12 and license BSD-3-Clause, and the README badge reads BSD-3-Clause, while GitHub cannot classify this repository's licence, so the LICENSE file is the place to check. The repository is not archived, the last push was on 2026-10-20, and there are no GitHub releases.

## Sources

- [Issues](https://github.com/ysig/GraKeL/issues)
- [Project website](https://ysig.github.io/GraKeL/)
- [README](https://github.com/ysig/GraKeL/blob/master/README.md)
- [ysig/GraKeL on GitHub](https://github.com/ysig/GraKeL)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ysig-grakel
