Awesome Go

gograph

CategoryScience and Data Analysis
SubcategoryScience and Data Analysis
Stars135

A golang generic graph library that provides mathematical graph-theory and algorithms

About gograph

This large README is shown as a plain-text preview. Read the full README at the source

<p align="center">
  <img alt="gograph, a generic graph library for Go" src="https://github.com/user-attachments/assets/b5728572-9c17-47e8-aa32-28aeeedf1e25" width="420" title="gograph"/>
</p>

<p align="center">
  <b>Dependency graphs and graph algorithms for Go, with no dependencies outside the standard library.</b>
</p>

<p align="center">
  <a href="https://github.com/hmdsefi/gograph/actions/workflows/build.yml"><img alt="build" src="https://github.com/hmdsefi/gograph/actions/workflows/build.yml/badge.svg"></a>
  <a href="https://github.com/hmdsefi/gograph/actions/workflows/build.yml?query=branch%3Amaster"><img alt="coverage" src="https://img.shields.io/github/issues/detail/title/hmdsefi/gograph/162?label=coverage&color=brightgreen"></a>
  <a href="https://pkg.go.dev/github.com/hmdsefi/gograph"><img alt="Go Reference" src="https://pkg.go.dev/badge/github.com/hmdsefi/gograph.svg"></a>
  <a href="https://github.com/hmdsefi/gograph/releases"><img alt="release" src="https://img.shields.io/github/v/release/hmdsefi/gograph"></a>
  <a href="go.mod"><img alt="Go version" src="https://img.shields.io/github/go-mod/go-version/hmdsefi/gograph"></a>
  <a href="LICENSE"><img alt="license" src="https://img.shields.io/github/license/hmdsefi/gograph"></a>
  <a href="https://github.com/avelino/awesome-go#science-and-data-analysis"><img alt="Mentioned in Awesome Go" src="https://awesome.re/mentioned-badge.svg"></a>
  <a href="https://coderabbit.ai"><img alt="CodeRabbit Pull Request Reviews" src="https://img.shields.io/coderabbit/prs/github/hmdsefi/gograph?utm_source=oss&utm_medium=github&utm_campaign=hmdsefi%2Fgograph&labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit+Reviews"></a>
  <a href="https://github.com/sponsors/hmdsefi"><img alt="Sponsor" src="https://img.shields.io/badge/sponsor-hmdsefi-ea4aaa?logo=githubsponsors"></a>
  <a href="https://github.com/hmdsefi/gograph"><img alt="GitHub stars" src="https://img.shields.io/github/stars/hmdsefi/gograph?style=social"></a>
</p>

<p align="center">
  <a href="https://pkg.go.dev/github.com/hmdsefi/gograph"><b>Documentation</b></a> ·
  <a href="https://gograph.dev"><b>Interactive demos</b></a> ·
  <a href="examples"><b>Examples</b></a> ·
  <a href="CHANGELOG.md"><b>Changelog</b></a> ·
  <a href="https://github.com/hmdsefi/gograph/issues/136"><b>Roadmap</b></a>
</p>

<p align="center">
  <a href="https://gograph.dev/algorithms/dijkstra/new-york">
    <img alt="Dijkstra finding the quickest drive from Manhattan to Brooklyn on a New York street map, on gograph.dev" src=".github/images/dijkstra-new-york.gif" width="860">
  </a>
  <br>
  <sub><code>path.Dijkstra</code> finding the quickest drive from Canal St in Manhattan to Court St in Brooklyn on <a href="https://gograph.dev">gograph.dev</a>, where every algorithm runs step by step on example graphs.</sub>
</p>

gograph is a generic graph library for Go (Golang) with first-class support for dependency
graphs. Acyclic graphs refuse edges that would create a cycle, and `TopologySort`
gives you an order to run things in. It also covers traversal, shortest paths,
strongly connected components and graph partitioning.

- **Generic:** vertex labels can be any comparable type, such as strings, integers or your own structs.
- **Graph data structures:** directed, undirected, acyclic (DAG) and weighted graphs, with weights on vertices and edges.
- **Dependency graphs:** `Acyclic()` graphs reject cycles, `TopologySort` returns a valid order, and the `dag` package finds what depends on what and what can run in parallel.
- **Traversal:** BFS (breadth-first search), DFS (depth-first search), topological sort, closest-first and random-walk iterators.
- **Shortest paths:** Dijkstra, Bellman-Ford, Floyd-Warshall, multi-source Dijkstra, k-center and transitive reduction.
- **Connectivity:** strongly connected components with Tarjan, Kosaraju and Gabow, and condensation into a DAG.
- **Partitioning:** maximal cliques (Bron-Kerbosch), Girvan-Newman communities and randomized k-cut.
- **Diagrams:** `encoding/mermaid` writes a graph as a Mermaid flowchart that GitHub renders in Markdown, and `encoding/dot` writes it in the Graphviz DOT language for larger graphs.
- **Deterministic:** a graph built the same way gives the same topological order, components and cliques on every run.

Imported by [20+ public Go modules](https://pkg.go.dev/github.com/hmdsefi/gograph?tab=importedby),
including the Cilium project's [ariane](https://github.com/cilium/ariane),
BoostSecurity's [smokedmeat](https://github.com/boostsecurityio/smokedmeat),
[simplecontainer](https://github.com/simplecontainer/smr) and
[faultline](https://github.com/hmdsefi/faultline), deterministic simulation testing for Go by the
same author.

<h3 align="center">⭐ If gograph is useful to you, a star on GitHub helps other Go developers find it.</h3>

## Table of contents

* [Installation](#installation)
* [Quick start](#quick-start)
* [Use cases](#use-cases)
* [Graphs](#graphs)
    * [Directed](#directed)
    * [Acyclic](#acyclic)
    * [Undirected](#undirected)
    * [Weighted](#weighted)
* [Traversal](#traversal)
* [Algorithms](#algorithms)
* [Diagrams](#diagrams)
* [Determinism](#determinism)
* [Performance](#performance)
* [Examples](#examples)
* [Stability](#stability)
* [Roadmap](#roadmap)
* [Contributing](#contributing)
* [Sponsoring](#sponsoring)
* [License](#license)

## Installation

```shell
go get github.com/hmdsefi/gograph
```

gograph requires Go 1.26 or later and uses only the standard library.

## Quick start

```go
package main

import (
	"errors"
	"fmt"

	"github.com/hmdsefi/gograph"
)

func main() {
	// An edge A -> B means A has to happen before B.
	g := gograph.New[string](gograph.Acyclic())

	checkout := g.AddVertexByLabel("checkout")
	build := g.AddVertexByLabel("build")
	test := g.AddVertexByLabel("test")
	release := g.AddVertexByLabel("release")

	_, _ = g.AddEdge(checkout, build)
	_, _ = g.AddEdge(build, test)
	_, _ = g.AddEdge(test, release)

	// Acyclic graphs reject any edge that would create a cycle.
	_, err := g.AddEdge(release, checkout)
	fmt.Println(errors.Is(err, gograph.ErrDAGCycle)) // true

	order, _ := gograph.TopologySort(g)
	for _, v := range order {
		fmt.Println(v.Label()) // checkout, build, test, release
	}
}
```

## Use cases

- **Build and CI pipelines:** order the steps with `TopologySort`, run independent
  steps together with `dag.Levels` or `dag.Tracker`, and find the chain of steps that
  sets the total time with `dag.CriticalPath`.
- **Task schedulers and workflow engines:** `dag.Tracker` hands out tasks as their
  dependencies finish.
- **Incremental builds and spreadsheets:** `dag.Affected` lists everything a change
  reaches, so you recompute only that.
- **Module and package dependencies:** find requirement cycles with
  `connectivity.Tarjan` and collapse them with `connectivity.Condense`. The
  [gomodgraph](examples/gomodgraph) example does this for `go mod graph` output.
- **Routing and placement:** shortest routes with `path.Dijkstra`, the nearest depot
  for every address with `path.DijkstraMultiSource`, and where to put k facilities
  with `path.KCenter`.
- **Networks and communities:** loops in service calls with strongly connected
  components, and groups in a social graph with `partition.MaximalCliques` and
  `partition.GirvanNewman`.

## Graphs

`gograph.New[T]` creates a graph. `T` is the vertex label type and must be
comparable, so slices, maps and functions can't be labels. Options choose the kind
of graph:

- `gograph.Directed()` creates a directed graph. Without it, graphs are undirected.
- `gograph.Acyclic()` creates a directed graph that rejects edges that would create a cycle.
- `gograph.Weighted()` marks the graph as weighted. `BellmanFord` and `FloydWarshall` require it.

Every graph implements the `Graph[T]` interface. See the
[package documentation](https://pkg.go.dev/github.com/hmdsefi/gograph#Graph) for the full list
of methods.

`AddEdge` creates missing vertices, so `gograph.NewVertex` is enough for quick
examples. To keep a reference to a vertex, use `AddVertexByLabel`, which adds the
vertex and returns it.

The diagrams in this section are the output of [`encoding/mermaid`](#diagrams) for
each example graph.

### Directed

```mermaid
flowchart LR
    n0["1"]
    n1["2"]
    n2["3"]
    n3["4"]
    n4["5"]
    n5["6"]
    n0 --> n1
    n0 --> n2
    n1 --> n3
    n2 --> n3
    n3 --> n4
    n4 --> n5
```

```go
g := gograph.New[int](gograph.Directed())

_, _ = g.AddEdge(gograph.NewVertex(1), gograph.NewVertex(2))
_, _ = g.AddEdge(gograph.NewVertex(1), gograph.NewVertex(3))
_, _ = g.AddEdge(gograph.NewVertex(2), gograph.NewVertex(4))
_, _ = g.AddEdge(gograph.NewVertex(3), gograph.NewVertex(4))
_, _ = g.AddEdge(gograph.NewVertex(4), gograph.NewVertex(5))
_, _ = g.AddEdge(gograph.NewVertex(5), gograph.NewVertex(6))
```

### Acyclic

```mermaid
flowchart LR
    n0["1"]
    n1["2"]
    n2["3"]
    n0 --> n1
    n1 --> n2
```

```go
g := gograph.New[int](gograph.Acyclic())

_, _ = g.AddEdge(gograph.NewVertex(1), gograph.NewVertex(2))
_, _ = g.AddEdge(gograph.NewVertex(2), gograph.NewVertex(3))

_, err := g.AddEdge(gograph.NewVertex(3), gograph.NewVertex(1))
fmt.Println(err) // edges would create cycle
```

### Undirected

```mermaid
flowchart LR
    n0["A"]
    n1["B"]
    n2["C"]
    n3["D"]
    n0 --- n1
    n0 --- n3
    n1 --- n2
    n1 --- n3
```

```go
// Graphs are undirected by default.
g := gograph.New[string]()

a := g.AddVertexByLabel("A")
b := g.AddVertexByLabel("B")
c := g.AddVertexByLabel("C")
d := g.AddVertexByLabel("D")

_, _ = g.AddEdge(a, b)
_, _ = g.AddEdge(a, d)
_, _ = g.AddEdge(b, c)
_, _ = g.AddEdge(b, d)

// Every undirected edge can be followed both ways.
fmt.Println(g.ContainsEdge(a, b), g.ContainsEdge(b, a)) // true true
```

### Weighted

```mermaid
flowchart LR
    n0["A"]
    n1["B"]
    n2["C"]
    n3["D"]
    n0 ---|"4"| n1
    n0 ---|"3"| n3
    n1 ---|"3"| n2
    n1 ---|"1"| n3
    n2 ---|"2"| n3
```

```go
g := gograph.New[string](gograph.Weighted())

a := g.AddVertexByLabel("A")
b := g.AddVertexByLabel("B")
c := g.AddVertexByLabel("C")
d := g.AddVertexByLabel("D")

_, _ = g.AddEdge(a, b, gograph.WithEdgeWeight(4))
_, _ = g.AddEdge(a, d, gograph.WithEdgeWeight(3))
_, _ = g.AddEdge(b, c, gograph.WithEdgeWeight(3))
_, _ = g.AddEdge(b, d, gograph.WithEdgeWeight(1))
_, _ = g.AddEdge(c, d, gograph.WithEdgeWeight(2))

dist := path.Dijkstra(g, "A")
fmt.Println(dist["C"]) // 5
```

Vertices can have weights too:

```mermaid
flowchart LR
    n0["A, weight 3"]
    n1["B, weight 2"]
    n2["C, weight 4"]
    n0 --> n1
    n1 --> n2
```

```go
g := gograph.New[string](gograph.Directed(), gograph.Weighted())

a := g.AddVertexByLabel("A", gograph.WithVertexWeight(3))
b := g.AddVertexByLabel("B", gograph.WithVertexWeight(2))
c := g.AddVertexByLabel("C", gograph.WithVertexWeight(4))

_, _ = g.AddEdge(a, b)
_, _ = g.AddEdge(b, c)

fmt.Println(a.Weight(), b.Weight(), c.Weight()) // 3 2 4
```

## Traversal

The `traverse` package provides iterators that all implement the same interface:

```go
type Iterator[T comparable] interface {
	HasNext() bool
	Next() *gograph.Vertex[T]
	Iterate(func(v *gograph.Vertex[T]) error) error
	Reset()
}
```

```go
g := gograph.New[string](gograph.Directed())

_, _ = g.AddEdge(gograph.NewVertex("A"), gograph.NewVertex("B"))
_, _ = g.AddEdge(gograph.NewVertex("A"), gograph.NewVertex("C"))
_, _ = g.AddEdge(gograph.NewVertex("B"), gograph.NewVertex("D"))

it, err := traverse.NewBreadthFirstIterator(g, "A")
if err != nil {
	fmt.Println(err)
	return
}

for it.HasNext() {
	fmt.Println(it.Next().Label()) // A, B, C, D
}
```

| Iterator | Order | Demo |
|---|---|---|
| [`NewBreadthFirstIterator`](traverse#bfs) | Nearest vertices first, level by level | [watch](https://gograph.dev/algorithms/bfs/office-network) |
| [`NewDepthFirstIterator`](traverse#dfs) | Each branch as deep as it goes before the next | [watch](https://gograph.dev/algorithms/dfs/office-network) |
| [`NewTopologicalIterator`](traverse#topological-sort) | Topological order, one vertex at a time | [watch](https://gograph.dev/algorithms/topological-sort/build-pipeline) |
| [`NewClosestFirstIterator`](traverse#closest-first) | By shortest weighted distance from the start | [watch](https://gograph.dev/algorithms/closest-first/school-run) |
| [`NewRandomWalkIterator`](traverse#random-walk) | A random walk that picks each edge in proportion to its weight in a weighted graph | [watch](https://gograph.dev/algorithms/random-walk/service-calls) |

## Algorithms

V is the number of vertices and E the number of edges. Every link in the Demo column
runs the algorithm step by step on example graphs at [gograph.dev](https://gograph.dev).

**Ordering and subgraphs** (`gograph` package)

| Function | Answers | Time | Demo |
|---|---|---|---|
| `TopologySort` | An order where every edge points forward (Kahn's algorithm) | O(V+E) | [watch](https://gograph.dev/algorithms/topological-sort/build-pipeline) |
| `StableTopologySort` | The same, picking the smallest ready vertex by your compare function | O((V+E) log V) | [watch](https://gograph.dev/algorithms/stable-topological-sort/build-pipeline) |
| `InducedSubgraph` | A chosen set of vertices and the edges between them | O(k+D) for k labels with D outgoing edges | |

**Dependencies** (`dag` package)

| Function | Answers | Time | Demo |
|---|---|---|---|
| `Descendants` | What depends on a vertex | O(V+E) | [watch](https://gograph.dev/algorithms/descendants/build-pipeline) |
| `Ancestors` | What a vertex depends on | O(V+E) | [watch](https://gograph.dev/algorithms/ancestors/build-pipeline) |
| `Affected` | Everything a change to some vertices reaches | O(V+E) | [watch](https://gograph.dev/algorithms/affected/build-pipeline) |
| `Levels` | Which vertices can run at the same time | O(V+E) | |
| `NewTracker` | Which vertices are ready as others finish | O(V+E) in total | |
| `CriticalPath` | The most expensive chain of dependent vertices | O(V+E) | |

**Shortest paths** (`path` package)

| Function | Answers | Time | Demo |
|---|---|---|---|
| [`Dijkstra`](path/dijkstra.md) | Distances from one vertex, no negative weights | O((V+E) log V) | [watch](https://gograph.dev/algorithms/dijkstra/new-york) |
| [`BellmanFord`](path/bellman-ford.md) | Distances from one vertex, negative weights allowed | O(V·E) | [watch](https://gograph.dev/algorithms/bellman-ford/school-run) |
| [`FloydWarshall`](path/floyd-warshall.md) | Distances between every pair of vertices | O(V³) | [watch](https://gograph.dev/algorithms/floyd-warshall/fx-majors) |
| `DijkstraMultiSource` | Each vertex's nearest source, in one search | O((V+E) log V) | |
| `KCenter` | k centers that keep every vertex close to one, within twice the best radius on undirected graphs | O(k (V+E) log V) | |
| [`TransitiveReduction`](path/transitive-reduction.md) | The fewest edges that keep every path | O(V·(V+E)) | [watch](https://gograph.dev/algorithms/transitive-reduction/build-pipeline) |

**Connectivity** (`connectivity` package, [notes](connectivity#gograph---connectivity))

| Function | Answers | Time | Demo |
|---|---|---|---|
| `Tarjan` | Strongly connected components | O(V+E) | [watch](https://gograph.dev/algorithms/tarjan/service-calls) |
| `Kosaraju` | Strongly connected components | O(V+E) | [watch](https://gograph.dev/algorithms/kosaraju/service-calls) |
| `Gabow` | Strongly connected components | O(V+E) | [watch](https://gograph.dev/algorithms/gabow/service-calls) |
| [`Condense`](connectivity#condensation) | The DAG of components, one vertex per component | O(V+E) | |

**Partitioning** (`partition` package)

| Function | Answers | Time | Demo |
|---|---|---|---|
| [`MaximalCliques`](partition/bron_kerbosch.md) | Every group where all vertices connect to each other (Bron-Kerbosch) | O(V·3^(V/3)) worst case, including the output | [watch](https://gograph.dev/algorithms/maximal-cliques/friend-groups) |
| [`GirvanNewman`](partition/girvan-newman.md) | Communities, by removing the most central edges | O(E·V·(V+E)) | [watch](https://gograph.dev/algorithms/girvan-newman/friend-groups) |
| [`RandomizedKCut`](partition/k-cut.md) | A split into k groups with few edges between them | O(V·E) per run | [watch](https://gograph.dev/algorithms/randomized-k-cut/friend-groups) |

## Diagrams

`encoding/mermaid` writes a graph as a Mermaid flowchart, which GitHub and GitLab render
in Markdown. Options set the direction, the vertex and edge text, and classes for
highlighting:

```go
g := gograph.New[string](gograph.Directed())

_, _ = g.AddEdge(gograph.NewVertex("checkout"), gograph.NewVertex("build"))
_, _ = g.AddEdge(g.GetVertexByID("build"), gograph.NewVertex("test"))

// highlight the vertices that failed
failed := map[string]bool{"test": true}

err := mermaid.Write(os.Stdout, g,
	mermaid.WithVertexClass(func(v *gograph.Vertex[string]) string {
		if failed[v.Label()] {
			return "failed"
		}
		return ""
	}),
	mermaid.WithClassDef[string]("failed", "fill:#f96"),
)
```

```mermaid
flowchart TD
    n0["build"]
    n1["checkout"]
    n2["test"]
    n0 --> n2
    n1 --> n0
    classDef failed fill:#f96
    class n2 failed
```

For graphs too large for a Mermaid diagram, `encoding/dot` writes the Graphviz DOT
language, which `dot -Tsvg` turns into an image.

## Determinism

When several orders are valid, `TopologySort` follows the order the vertices and edges
were added, so the same graph gives the same result on every run. To choose the order
yourself, `StableTopologySort` takes a compare function such as `cmp.Compare` and always
picks the smallest vertex that is ready.

`GetAllVertices` returns vertices in the order they were added, and `AllEdges` and
`EdgesOf` follow the same order. `Tarjan`, `Kosaraju`, `Gabow`, `MaximalCliques`,
`GirvanNewman` and `TransitiveReduction` give the same result on every run for a graph
built the same way.

## Performance

Measured with `go test -run '^$' -bench . -benchmem ./...` on an Apple M4 Max with Go 1.27:

| Benchmark | Graph | Time per run |
|---|---|---|
| `TopologySort` | Binary tree, 100,000 vertices | 7.0 ms |
| `StableTopologySort` | Binary tree, 100,000 vertices | 15.9 ms |
| `dag.CriticalPath` | Binary tree, 100,000 vertices | 10.8 ms |
| `path.DijkstraMultiSource` | Path of 2,000 vertices, 32 sources | 0.19 ms |
| `path.KCenter` | Path of 2,000 vertices, k = 32 | 0.31 ms |
| `InducedSubgraph` | 256 vertices out of 4,000 | 0.05 ms |

Each algorithm page on [gograph.dev](https://gograph.dev) also has a performance card
that times the algorithm on that page's example graphs.

## Examples

- [gomodgraph](examples/gomodgraph): loads `go mod graph` output and finds requirement
  cycles, the modules that depend on a module, why a module is needed, what changed
  between two versions of `go.mod`, and a diagram of a module with its direct
  dependencies and dependents.

## Stability

gograph follows [semantic versioning](https://semver.org) and is used by other
projects, so changes keep existing code working: exported interfaces keep their
method sets, exported functions keep their signatures, and sentinel errors stay
comparable with `==`. The rules are in
[CONTRIBUTING.md](CONTRIBUTING.md#backward-compatibility), and every release is listed
in the [changelog](CHANGELOG.md).

## Roadmap

Planned work, in the order it's likely to land, is tracked in
[#136](https://github.com/hmdsefi/gograph/issues/136). Issues labeled
[good first issue](https://github.com/hmdsefi/gograph/labels/good%20first%20issue)
are a good place to start.

## Contributing

Contributions are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) before
opening a pull request. The README examples are also Go examples in
[`example_test.go`](example_test.go), so `go test ./...` checks that they still compile
and print what they claim.

To report a security problem, follow [SECURITY.md](SECURITY.md).

## Sponsoring

Frequently Asked Questions

What is gograph?

gograph is a Science and Data Analysis library for the Go programming language. A golang generic graph library that provides mathematical graph-theory and algorithms

How do I install gograph?

Install gograph with the Go module system using `go get hmdsefi/gograph`. Check the repository for the current installation instructions.

What category does gograph belong to?

gograph is listed under Science and Data Analysis, specifically Science and Data Analysis.

← Back to Science and Data Analysis