Skip to main content

Fast-Sugiyama

codecov Rust Lint & Test Python Lint & Test Package version

This project adds python bindings for the rust-sugiyama crate, to produce directed graph layouts similar to the GraphViz dot program.

It's an implementation of Sugiyama's algorithm for displaying a layered, or hierarchical, directed graph like so:

Graph Example

Why?

We already have nx.nx_pydot.pydot_layout(gn, prog="dot"), grandalf.layouts.SugiyamaLayout, netgraph.get_sugiyama_layout, and rustworkx.visualization.graphviz_draw... why pile on?

All of the above either depend on either the dot program from graphviz, or the pure-python Sugiyama implementation from grandalf.

The dot program is probably the best solution to this problem that currently exists, and you should use the dot program if you are working in an environment that allows you to install graphviz and your python application only needs to render very small graphs and/or you don't need to produce them quickly.

The grandalf-based solutions are pure-python which is awesome for portability, but is slow for medium/large graphs (because python). In addition, that project hasn't had maintenance in many years, including incorporating the 2020 Erratum from Brandes et., al on which the implementation appears to be based. This implementation suffers from some occasions layouts where two nodes can share position, which either reveals a bug referenced in the erratum, or in the original implementation. Anyway, the grandalf project has an excellent framework for this problem, and has a deep bench of potential mentors and prior contributors, so if you need a pure-python implementation you should support them.

This project is about having zero 3rd-party dependencies, reasonable multi-platform support (arm & x86 wheels are available for linux, windows, and mac), and speed. In service of all three of these goals, this library is a thin wrapper around the excellent rust crate rust-sugiyama, which is likely the clearest-eyed implementation of the complex Sugiyama algorithm that's available for free on the internet.

With these shoulders to stand on, this project aims to offer very good dot-like layouts, very quickly, and with no 3rd-party runtime dependencies. This is a great fit for folks who can't (or don't want to) install graphviz, or for web-applications that want smaller images, or for anyone needing a large directed graph layout to run fast.

Quick Start

import networkx as nx
from fast_sugiyama import from_edges

g = nx.gn_graph(42, seed=132, create_using=nx.DiGraph)
pos = from_edges(g.edges()).to_dict()
nx.draw_networkx(g, pos=pos, with_labels=False, node_size=150)
Quick Start Output

The above example is for a networkx graph, but the fast-sugiyama package can be used with any graph library. The return type from the to_dict() method works equally well for the rustworkx Pos2DMapping type as the networkx pos arg. It's a {node_id: (x, y)} coordinate mapping you can take where you please.

You might also wonder why we don't emulate the networkx layout api conventions exactly (e.g., immediately return exactly one layout), and we'll talk about that a little later.

Install

This package is pip installable.

pip install fast-sugiyama

As with all python endeavours, you should probably be using a virtual environment for your project via conda \ mamba or venv (preferrably via uv).

uv pip install fast-sugiyama

Either of the above two install commands should work well with either ARM or x86 systems running Linux, Mac, or Windows. If you have another system and want to run this package and you can't install/build/run from the sdist or the repo please open a detailed issue and let me know what happened. Maybe I can help.

Usage

Purely for simplicity, this section will use networkx to facilitate usage demonstration and discussion. Again, this project has no dependencies, and can be used to support high-quality layout visuals for any graph library that uses a node -> coord mapping.

The from_edges function returns a list of layouts, one for each weakly connected component of the input graph. Each layout is a tuple containing the positions, width, height, and edges returned by the solver. This makes it easy to deal with multi-graphs, and let's us write helper functions to support various ways of displaying separate graphs in one layout.

This section will use a multi-graph from the test suite that includes 40 component graphs, each with a range of sizes. In these examples, the full graph g is made of 40 components, 435 nodes, and 395 edges. There is also a sub-set of these components called sg that includes just 12 of the 40 components.

dot-like layouts

This package has a helper function for users who would like to get a layout (or a multi-graph layout) similar to the dot program -- specifically, similar to the layout produced by nx.pydot.pydot_layout. This layout aligns hierarchies to the top of the figure, lines them up side by side horizontally, and packs them efficiently such that nodes in neighboring graph components are separated by the same spacing interval as nodes within each component. The following plot compares the layout results for a 12 component multi-graph sg.

Pydot Compare

Note that the bounding boxes of the layouts are allowed to overlap in the pydot layout, so this library supports that as well.

rect-like / compact layouts

Lining up components horizontally quickly becomes unwieldy with the pydot layout though. Here's all 40 components of our multi-graph g.

Difficult Pydot Layout

Users can install an optional dependency rectangle-packer to produce an improved layout that compactly packs the components.

pos = from_edges(g.edges()).rect_pack_layouts(max_width=2500).to_dict()
Rect Pack Layout

One can even utilize the 'horizontal compact' idea from the dot-like layouts to tighten things up even further.

pos = from_edges(g.edges()).compact_layout(max_width=2500).to_dict()
# or equivalently
pos = (
    from_edges(g.edges())
    .rect_pack_layouts(max_width=2500)
    .sort_horizontal()
    .compact_layouts_horizontal()
)
Compact Layout

DIY layouts

The helper layouts above simply chain a couple layout 'primitive' operations. For example, the .dot_layout() is equivalent to .align_layouts_vertical_top().compact_layouts_horizontal() You can chain together the built in primitives, or adapt their code into your own custom functions to obtain any arrangement.

pos = (
    from_edges(g.edges())
    [:6] # chaining even works after slices
    .align_layouts_vertical()
    .align_layouts_horizontal_center()
    .to_dict()
)
Sliced, centered, vertically stacked

If you're feeling wild and your graph is not simple and acyclic, you can even capture the dummy vertices and their edges from the Sugiyama solver and render those.

Here's another graph from the test suite that is directed but is not a DAG. Let's say these edges are in a variable called edges.

g = nx.DiGraph()
g.add_edges_from(edges)
pos = from_edges(edges).to_dict()

fig, ax = plt.subplots()
nx.draw_networkx(g, pos=pos, ax=ax)
Hero with no dummies

But there's an issue with the edge from (15, 1) -- the algorithm created a gap for the edge to go around node 8, not behind it. We can render that edge path easily by using the edges returned by the solver in our layout.

layouts = from_edges(edges)
pos = layouts.to_dict()
nodes = list({n for edge in edges for n in edge})

g = nx.DiGraph()
for *_, edgelist in layouts: # the solver returns the edges with the dummy's included
    if edgelist is not None:
        g.add_edges_from(edgelist)

fig, ax = plt.subplots()

nx.draw_networkx_nodes(g, pos, ax=ax, nodelist=nodes)
nx.draw_networkx_labels(g, pos, ax=ax, labels={n: n for n in nodes})

_ = nx.draw_networkx_edges(
    g,
    pos,
    [e for e in g.edges() if e[1] not in nodes],
    arrows=False,
    ax=ax,
)
_ = nx.draw_networkx_edges(
    g,
    pos,
    [e for e in g.edges() if e[1] in nodes],
    node_size=[300 if n in nodes else 0 for n in g.nodes()],
    ax=ax,
)
Hero with dummies

Benchmarks

Speed is not the purpose of this package, but it is a welcome benefit of using rust for all the heavy lifting. There are vanishingly few non-pathological reasons to produce a hierarchical graph plot with 5,000+ nodes, but for reasonable graph sizes, say, under 1000, this package is ~100-400x faster than using pydot. NOTE: pydot will typically yield a nicer looking layout, and if you can install it in your environment and wait for pydot to do its serialization/deserialization, you should.

for n in [100, 500, 1000, 5000, 10000]:
    g = nx.gn_graph(n, seed=42)

    %timeit _ = from_edges(g.edges()).dot_layout().to_dict()
    %timeit _ = nx.nx_pydot.pydot_layout(g, prog='dot')
786 μs ± 6.88 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each) <-- ours @ n==100 (410x faster)
325 ms ± 9.01 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
10.9 ms ± 112 μs per loop (mean ± std. dev. of 7 runs, 100 loops each) <-- ours @ n==500 (140x faster)
1.57 s ± 27.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
35.8 ms ± 48.2 μs per loop (mean ± std. dev. of 7 runs, 10 loops each) <-- ours @ n==1000 (89x faster)
3.2 s ± 8.41 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
855 ms ± 5.84 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) <-- ours @ n==5000 (21x faster)
18.7 s ± 234 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
3.1 s ± 23.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) <-- ours @ n==10000 (16x faster)
49.6 s ± 1.07 s per loop (mean ± std. dev. of 7 runs, 1 loop each)

And if we plot this up...

Single Graph Benchmark

Rust Sugiyama

This project contains a fork of the excellent rust-sugiyama crate, and includes minor api changes to support layouts via the python bindings. This fork is not intended to ever be published to crates.io. Instead anything useful we do here will be offered to the upstream crate for consideration.

Differences from the upstream project currently include:

  • Compute layout width and height in layout units rather than node count
  • Optionally validate the layout to prevent/detect overlapping nodes & invalid layouts
  • Return dummy vertices and edges if they're part of the layout
  • Remove dev-dependencies that point to personal github repos and replace with alternatives

Big thank you to the developers of the rust-sugiyama crate for doing the heavy lifting!

Metadata

Release files for fast-sugiyama 0.5.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fast-sugiyama 0.5.3
File Size Uploaded Provenance
fast_sugiyama-0.5.3.tar.gz 798.5 kB With provenance Details

Built distributions (wheels)

Table of built distributions (wheels) for fast-sugiyama 0.5.3
File
fast_sugiyama-0.5.3-cp311-abi3-win_arm64.whl CPython 3.11 abi3 Windows ARM64 Details
fast_sugiyama-0.5.3-cp311-abi3-win_amd64.whl CPython 3.11 abi3 Windows x86-64 Details
fast_sugiyama-0.5.3-cp311-abi3-manylinux_2_34_x86_64.whl CPython 3.11 abi3 Linux glibc 2.34+ x86-64 Details
fast_sugiyama-0.5.3-cp311-abi3-manylinux_2_34_aarch64.whl CPython 3.11 abi3 Linux glibc 2.34+ ARM64 Details
fast_sugiyama-0.5.3-cp311-abi3-macosx_11_0_arm64.whl CPython 3.11 abi3 macOS 11.0+ ARM64 Details
fast_sugiyama-0.5.3-cp311-abi3-macosx_10_12_x86_64.whl CPython 3.11 abi3 macOS 10.12+ x86-64 Details

Total release size: 2.5 MB

Release files / fast_sugiyama-0.5.3.tar.gz

Download URL fast_sugiyama-0.5.3.tar.gz
Size 798.5 kB
Tags Source
SHA-256 checksum
How to use checksums
073cba7184418ac2df1dba2f8835ffc10a687db1f130bfa0a1228ab4e6827c4f
BLAKE2b-256 checksum
How to use checksums
8db34d23c2a3ac51da42de40e1a59c1a801963274665a98c24503e18ba5e28e4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 10, 2026.

Transparency log

Release files / fast_sugiyama-0.5.3-cp311-abi3-win_arm64.whl

Download URL fast_sugiyama-0.5.3-cp311-abi3-win_arm64.whl
Size 219.2 kB
Tags CPython 3.11 Windows ARM64 abi3
SHA-256 checksum
How to use checksums
ef4500892c400adb6471f33b31bec7fb107877d3bb60b178c2f73b125b2c0449
BLAKE2b-256 checksum
How to use checksums
1f644dc1b2a0f2185552f3be186683ca23e1d69a7fb0e730092ca163d071a5cf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 10, 2026.

Transparency log

Release files / fast_sugiyama-0.5.3-cp311-abi3-win_amd64.whl

Download URL fast_sugiyama-0.5.3-cp311-abi3-win_amd64.whl
Size 231.4 kB
Tags CPython 3.11 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
a222fecca64a9db6b60dcf02fb9b3b8d69b9e9e7f592f70e830965899d612d78
BLAKE2b-256 checksum
How to use checksums
4ab998c6cad41de1023055c5d4bae7191ef258b192a36a1d3497bc9a12777abb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 10, 2026.

Transparency log

Release files / fast_sugiyama-0.5.3-cp311-abi3-manylinux_2_34_x86_64.whl

Download URL fast_sugiyama-0.5.3-cp311-abi3-manylinux_2_34_x86_64.whl
Size 328.1 kB
Tags CPython 3.11 Linux glibc 2.34+ x86-64 abi3
SHA-256 checksum
How to use checksums
79ae9abd9a22398c87b161785c0f3174dfff25bf3d3464b45881a559860c6560
BLAKE2b-256 checksum
How to use checksums
0d11b4c3dfc25ffa9518c9148be9d1f7f1edb92c83fae46c4900249d3798c9cb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 10, 2026.

Transparency log

Release files / fast_sugiyama-0.5.3-cp311-abi3-manylinux_2_34_aarch64.whl

Download URL fast_sugiyama-0.5.3-cp311-abi3-manylinux_2_34_aarch64.whl
Size 316.4 kB
Tags CPython 3.11 Linux glibc 2.34+ ARM64 abi3
SHA-256 checksum
How to use checksums
10b8112a05650feb84b82fbd46a2e5553986a3f5c122df52b2811d1a7b140143
BLAKE2b-256 checksum
How to use checksums
f2d4a93c2249e62eb01c8059b4dcd59fa0c2594cb0f642b55b16dbdf83147afa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 10, 2026.

Transparency log

Release files / fast_sugiyama-0.5.3-cp311-abi3-macosx_11_0_arm64.whl

Download URL fast_sugiyama-0.5.3-cp311-abi3-macosx_11_0_arm64.whl
Size 282.7 kB
Tags CPython 3.11 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
a93e94bbf815ec7255a1e2515e65796f32bcd1fdff9ec232b006e0c1c50e970d
BLAKE2b-256 checksum
How to use checksums
339d55ac5653b95961b92a1a2aa78871a0a65956b1fdb1f6019a8bf020a8fcef
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 10, 2026.

Transparency log

Release files / fast_sugiyama-0.5.3-cp311-abi3-macosx_10_12_x86_64.whl

Download URL fast_sugiyama-0.5.3-cp311-abi3-macosx_10_12_x86_64.whl
Size 310.1 kB
Tags CPython 3.11 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
bb08aae01fc62fd6a7ad7a67387d085bd4016c538c05d5a80b6264cb89e43890
BLAKE2b-256 checksum
How to use checksums
9fe119bf4e9600edebd1b9d62a4e8413327a2e11f08040369e14ce5db05cefa2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 10, 2026.

Transparency log

Security for fast-sugiyama 0.5.3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 7 files in this release have been uploaded with provenance metadata.

Select from 7 files

Attestation for fast_sugiyama-0.5.3.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 10, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page