rustworkx: a graph library for Python where the graph library is Rust
A high performance Python graph library implemented in Rust.
At a glance
- What is it?
- rustworkx is a high-performance general-purpose graph library for Python written in Rust, built with PyO3 and maturin so that the Python API and the Rust crate share one implementation. It is Apache-2.0 licensed, the current release is 0.18.1 from 2026-07-29, and the minimum supported Rust is 1.85.
- Who is it for?
- rustworkx fits a Python project doing graph work at a scale where pure Python is the bottleneck, particularly the quantum computing work the project came out of, and it fits a team that wants the same algorithms available from Rust and Python. It does not fit a project with a light graph workload, where networkx is simpler and has no build step.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One implementation, two languages, and the PyO3 boundary
rustworkx is a high-performance, general-purpose graph library for Python, written in Rust, and the interesting architectural decision is that the Python package and the Rust crate are not two implementations of the same thing.
The `Cargo.toml` shows a workspace whose members are `rustworkx-core` and `rustworkx-core/fuzz`, with the root package itself built as a `cdylib`, which is the artefact type Python extension modules use. The binding layer is `pyo3` at 0.29.2 with the `abi3-py310` feature, and the `pyproject.toml` confirms the same from the Python side with `bindings = "pyo3"` and `features = ["pyo3/extension-module"]` under the maturin configuration.
The abi3 feature is worth noting. It means the extension is compiled against a stable ABI from Python 3.10 onwards, so one binary works across the supported Python versions rather than one per version. That is what makes the wheel matrix in the documentation practical.
The core crate is the interesting part for a Rust user. `rustworkx-core` is a separate workspace member that the Python extension depends on with an exact version match, `=0.18.0`, and the fuzz target is a member of the workspace too. So the algorithms live in Rust, the Python layer is a thin binding over them, and the same algorithms are reachable from Rust directly. For the quantum computing work the project came out of, that matters: a simulation step that runs millions of times should not be paying for Python object overhead, and the same algorithm should be callable from a Rust extension without being reimplemented.
The dependency list shows the shape of the implementation. `petgraph` at 0.8 is the underlying generic graph library, `indexmap` and `foldhash` are the ordered and hashed map types, `rayon` is the parallelism, `fixedbitset` handles bitsets, `ndarray` and `ndarray-stats` handle the numerical side, and `quick-xml` and `pest` handle the formats the library parses.
The Python API is the same library, with node indices instead of node objects
The usage example in the readme is the shortest way to understand the API, because the one thing that changes from a typical Python graph library is how you refer to nodes.
import rustworkx
# Rustworkx's undirected graph type.
graph = rustworkx.PyGraph()
# Each time add node is called, it returns a new node index
a = graph.add_node("A")
b = graph.add_node("B")
c = graph.add_node("C")
# add_edges_from takes tuples of node indices and weights,
# and returns edge indices
graph.add_edges_from([(a, b, 1.5), (a, c, 5.0), (b, c, 2.5)])
# Returns the path A -> B -> C
rustworkx.dijkstra_shortest_paths(graph, a, c, weight_fn=float)Nodes are referred to by integer index rather than by the object you put in. `add_node` returns a new node index, and `add_edges_from` takes tuples of node indices and weights and returns edge indices. The node payload, the string `"A"`, is stored alongside the index and retrieved by index.
That is a design decision with real consequences. It means the graph structure is compact and index-based, which is what makes the Rust implementation fast and what makes the algorithms that work on the structure itself, traversals, path finding, colouring, cycle detection, work on plain integers. It also means your own code has to carry the index-to-payload mapping, and it means a node payload cannot be compared by value in the way it could in a library that hands you back node objects.
The function style is also worth noting. `rustworkx.dijkstra_shortest_paths` is a top-level function taking the graph, rather than a method on the graph. The readme makes the point that all graph classes and top-level functions are accessible with a single import, and the general pattern is that the algorithm takes the graph as its first argument, which is what allows the same algorithm to work across the several graph types the library provides.
Installing is usually a wheel, and the fallback needs Rust
rustworkx is published on PyPI with precompiled binaries for x86_64, i686, ppc64le and s390x and aarch64 Linux systems, x86_64 on macOS, and 32 and 64-bit Windows, and the install is:
pip install rustworkxThat covers most of what a Python team runs on. The list is more interesting than it looks, because the s390x and ppc64le entries mean the project is built for the architectures that show up in enterprise and scientific computing deployments, which is where a quantum computing stack is often found.
Where there is no prebuilt binary, you build from source, and the requirements are Rust 1.85 or newer with cargo, which normally comes with Rust, and rustup is suggested as the cross-platform installer. The source package is published on PyPI as well, so the same `pip install rustworkx` command works, it just compiles.
The PEP 517 detail is the part that catches people out. To build from source you need pip 19.0.0 or newer, which supports PEP-517, or you need to have installed `maturin>=1.9` manually. If you get an error about maturin not being found, the fix is to upgrade pip with `pip install -U pip` or install maturin directly with `pip install -U maturin>=1.9`.
That is maturin doing the work, and the build backend in `pyproject.toml` is `maturin` with `requires = ["maturin>=1.9,<2.0"]`. The `locked = true` setting in the maturin configuration means the build uses the `Cargo.lock` exactly, which is what keeps a source build reproducible.
The conda route exists too. Community-supported binaries are published to conda-forge, described as unofficial but helpful for the conda ecosystem including mamba, micromamba and pixi, installed with `conda install -c conda-forge rustworkx`.
Drawing is optional, and it pulls in matplotlib or graphviz
The `rustworkx.visualization` module is the only part of the library with optional dependencies, and the two extras cover the two drawing backends.
The matplotlib-based drawer function `rustworkx.visualization.mpl_draw` requires the matplotlib library, installed with `pip install matplotlib` or as `pip install 'rustworkx[mpl]'`.
The graphviz-based drawer function `rustworkx.visualization.graphviz_drawer` first needs graphviz itself installed, following the instructions on the graphviz download page, and then the pillow Python library, installed with `pip install pillow` or as `pip install 'rustworkx[graphviz]'`.
So the graphviz extra needs a system package and a Python package, while the matplotlib extra needs only a Python package. That difference matters in a container, where installing graphviz is an extra layer.
If you want everything, `pip install 'rustworkx[all]'` installs all the optional Python dependencies at once. The extras in the manifest are `mpl` for `matplotlib>=3.0`, `graphviz` for `pillow>=5.4`, and `all` for both, and there is a comment in the manifest noting the intention to use self-referential extras like `rustworkx[mpl]` and `rustworkx[graphviz]` once that is better supported, with a typo in the graphviz name that suggests the author was still working on it.
The core library itself has exactly one runtime dependency, `numpy>=1.16.0,<3`, which is a striking number for a graph library and is the direct consequence of the index-based design.
The project structure, and the two research citations
The top-level listing shows a project with an unusually broad set of supporting infrastructure.
There is `src/` and `rustworkx/` for the Python package, `rustworkx-core/` for the Rust core, `tests/`, `docs/`, `tools/`, and `releasenotes/` for the release notes. The build configuration is spread across `Cargo.toml` with a `Cargo.lock`, `pyproject.toml` with a `uv.lock`, `setup.py` which is a four-line shim calling setuptools' setup, `MANIFEST.in`, `constraints.txt`, `noxfile.py` and `tox.ini`, plus `rustfmt.toml` for the Rust formatting and `.cargo/` for the Cargo configuration.
Having both `noxfile.py` and `tox.ini` is a deliberate belt and braces approach, and the dependency group in `pyproject.toml` shows the test tooling is pinned hard, with `nox==2025.10.16` and `uv==2025.11.15` in the `testinfra` group. The `constraints.txt` file is the older convention for pinning build dependencies and its presence alongside `uv.lock` suggests the project has migrated rather than replaced.
Two research artefacts are worth pointing out because they say something about how the project is treated. There is a JOSS DOI badge pointing at a Journal of Open Source Software paper, and an arXiv badge. There is a `CITATION.bib` file, and the readme asks that if you use rustworkx in your research you cite the paper as per the included BibTeX file. And there is a `.zenodo.json` with a Zenodo DOI, which is the mechanism that gives the project a citable archive snapshot for each release.
So rustworkx is a research-grade project, not just an internal library of the Qiskit stack. The topic tags carry the same signal: graph, graph-theory, dag, python, rust.
Where rustworkx is the wrong choice
The first case is a light graph workload. If your graph has a few hundred nodes and you run the algorithm once, the Rust implementation is not the bottleneck and the extra machinery is. networkx is pure Python, has no build step, has a far larger ecosystem of contributed algorithms, and for most research and scripting use it is the better tool.
The second case is a team that cannot ship a compiled extension. A native module means your wheel, your platform, your Python version and your architecture all have to line up, and if they do not, someone has to have a Rust toolchain available at install time. That is a legitimate operational cost, and the documentation is candid about it, but it is a real constraint for an organisation that standardises on pure Python packages.
The third case is a project that needs a stable API. The release line is 0.18.x, and the Rust side moves with the Python side. The 0.x version number is the usual signal that the API is not frozen, and the readme's own building-from-source section notes a caveat about running Python from the repo root, which is the kind of detail that shifts as the build changes.
The fourth is a team that wants the algorithms in Python with Python semantics. Because nodes are indices and payloads are stored alongside them, code written against rustworkx is not portable to networkx without a translation layer, and vice versa. The algorithms are the same, the interface is not, and the interface is usually what your code is written against.
One more practical point. The optional drawing dependencies are only optional in name for the graphviz route, which needs a system package as well as pillow, so a container that wants graph drawing needs an extra layer and a system install step. If drawing is a core part of your workflow rather than a debugging aid, that is a cost to account for before you commit to the library.
Editorial conclusion
rustworkx fits a Python project doing graph work at a scale where pure Python is the bottleneck, particularly the quantum computing work the project came out of, and it fits a team that wants the same algorithms available from Rust and Python. It does not fit a project with a light graph workload, where networkx is simpler and has no build step. Before you adopt it, check the platform list, because prebuilt wheels exist for x86_64, i686, ppc64le and s390x and aarch64 Linux, x86_64 macOS and 32 and 64-bit Windows, and anything else means a source build with Rust 1.85 or newer and maturin.
Frequently asked questions
How do I install rustworkx?
With `pip install rustworkx`, which installs a precompiled version for x86_64, i686, ppc64le and s390x and aarch64 Linux, x86_64 macOS and 32 and 64-bit Windows. On any other platform you need Rust 1.85 or newer and maturin, and the same pip command builds from the source package.
Why do rustworkx graphs use node indices instead of node objects?
The Python API mirrors the Rust implementation, where the structure works on integer indices and the payload is stored alongside. `add_node` returns a new node index and `add_edges_from` takes tuples of node indices and weights, which keeps the graph compact and lets the same algorithms run from Rust and Python.
What are the optional dependencies of rustworkx?
The `rustworkx.visualization` module needs extras. The matplotlib drawer `rustworkx.visualization.mpl_draw` requires matplotlib, installed as `pip install 'rustworkx[mpl]'`, and the graphviz drawer `rustworkx.visualization.graphviz_drawer` needs graphviz installed on the system plus pillow, installed as `pip install 'rustworkx[graphviz]'`. `pip install 'rustworkx[all]'` gets both.
Is rustworkx still maintained and how is it cited?
The most recent release is 0.18.1 from 2026-07-29 and the last push was on 2026-10-13. It is published with a Journal of Open Source Software paper, an arXiv entry, a CITATION.bib file and a Zenodo DOI, and the readme asks that researchers cite the paper.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/qiskit-rustworkx)Was this useful?