Documentation
¶
Overview ¶
Package graph implements general-purpose graph algorithms.
This package does not implement any specific graph data structures: bring your own implementation!
Many of the algorithms and definitions are thanks to CLRS "Introduction to Algorithms", 3rd edition.
Example (SolarSystem) ¶
package main import ( "fmt" stdslices "slices" "strings" "github.com/tawesoft/golib/v2/ds/graph" "github.com/tawesoft/golib/v2/fun/slices" "github.com/tawesoft/golib/v2/iter" "github.com/tawesoft/golib/v2/must" ) type Distance int type Body struct { Name string Orbits map[graph.VertexIndex]Distance } // Universe is a multigraph. type Universe struct { Bodies map[graph.VertexIndex]Body } // Vertexes implements the graph.Iterator interface. func (u Universe) Vertexes() func() (graph.VertexIndex, bool) { return iter.Keys(iter.FromMap(u.Bodies)) } // Edges implements the graph.Iterator interface. func (u Universe) Edges(source graph.VertexIndex) func() (target graph.VertexIndex, count int, ok bool) { vertex := u.Bodies[source] it := iter.Keys(iter.FromMap(vertex.Orbits)) return func() (_ graph.VertexIndex, _ int, _ bool) { target, ok := it() if !ok { return } return target, 1, true } } // AddVertex implements the graph.Builder interface func (u *Universe) AddVertex() graph.VertexIndex { index := graph.VertexIndex(len(u.Bodies)) if u.Bodies == nil { u.Bodies = make(map[graph.VertexIndex]Body) } u.Bodies[index] = Body{} return index } // AddEdge implements the graph.Builder interface func (u *Universe) AddEdge(from graph.VertexIndex, to graph.VertexIndex) { u.Bodies[from].Orbits[to] = Distance(0) } func (u *Universe) AddBody(name string) graph.VertexIndex { index := u.AddVertex() u.Bodies[index] = Body{ Name: name, Orbits: make(map[graph.VertexIndex]Distance), } return index } func (u *Universe) AddOrbit(center graph.VertexIndex, body graph.VertexIndex, distance Distance) { u.AddEdge(center, body) u.Bodies[center].Orbits[body] = distance } func main() { // define a custom multigraph of orbit distances and solar irradiation var d Universe // Alpha Centuri is trinary star system with a binary star center alphaCentauri := d.AddBody("Alpha Centauri") // Alpha Centauri AB barycenter proximaCentauri := d.AddBody("Proxima Centauri") // Alpha Centauri C sun := d.AddBody("Sol") mercury := d.AddBody("Mercury") venus := d.AddBody("Venus") earth := d.AddBody("The Earth") mars := d.AddBody("Mars") jupiter := d.AddBody("Jupiter") saturn := d.AddBody("Saturn") uranus := d.AddBody("Uranus") neptune := d.AddBody("Neptune") moon := d.AddBody("The Moon") demos := d.AddBody("Demos") phobos := d.AddBody("Phobos") // Average orbit distance (kilometres) // (figures from NASA) d.AddOrbit(alphaCentauri, proximaCentauri, 1_937_292_425_565) d.AddOrbit(sun, mercury, 57_000_000) d.AddOrbit(sun, venus, 108_000_000) d.AddOrbit(sun, earth, 149_000_000) d.AddOrbit(sun, mars, 228_000_000) d.AddOrbit(sun, jupiter, 780_000_000) d.AddOrbit(sun, saturn, 1_437_000_000) d.AddOrbit(sun, uranus, 2_871_000_000) d.AddOrbit(sun, neptune, 4_530_000_000) d.AddOrbit(earth, moon, 384_400) d.AddOrbit(mars, demos, 23_460) d.AddOrbit(mars, phobos, 6_000) // construct an adjacency matrix for efficient adjacency lookup orbitMatrix := graph.NewAdjacencyMatrix(nil) orbitMatrix.Calculate(d) // construct degree matrices for efficient in-/out- degree lookup indegreeMatrix := graph.NewDegreeMatrix() outdegreeMatrix := graph.NewDegreeMatrix() indegreeMatrix.Calculate(d.Vertexes, orbitMatrix.Indegree) outdegreeMatrix.Calculate(d.Vertexes, orbitMatrix.Outdegree) // efficiently returns true iff a orbits b orbits := func(a graph.VertexIndex, b graph.VertexIndex) bool { return orbitMatrix.Get(b, a) > 0 } // efficiently returns the number of satellites orbiting a numSatellites := func(a graph.VertexIndex) int { return outdegreeMatrix.Get(a) } // produces target vertexes from orbit edges satellites := func(source graph.VertexIndex) func() (graph.VertexIndex, bool) { edges := d.Edges(source) return func() (graph.VertexIndex, bool) { for { targetIdx, _, ok := edges() return targetIdx, ok } } } must.Truef(orbits(earth, sun), "expected: earth orbits the sun") must.Truef(orbits(moon, earth), "expected: moon orbits the earth") must.Falsef(orbits(sun, earth), "expected: sun does not orbit the earth") // efficiently find all graph roots roots := iter.ToSlice(graph.Roots(orbitMatrix.Vertexes, indegreeMatrix.Get)) vertexName := func(x graph.VertexIndex) string { vertex := d.Bodies[x] return vertex.Name } sorted := func(xs []string) []string { stdslices.Sort(xs) return xs } fmt.Printf("The graph contains data for %d solar systems: %s.\n", len(roots), strings.Join(sorted(slices.Map(vertexName, roots)), " and ")) fmt.Printf("The sun is orbited by %d satellites.\n", numSatellites(sun)) fmt.Printf("Mars is orbited by %d satellites: %s.\n", numSatellites(mars), strings.Join(sorted(slices.Map(vertexName, iter.ToSlice(satellites(mars)))), " and ")) }
Output: The graph contains data for 2 solar systems: Alpha Centauri and Sol. The sun is orbited by 8 satellites. Mars is orbited by 2 satellites: Demos and Phobos.
Index ¶
- type AdjacencyMatrix
- func (m AdjacencyMatrix) Calculate(g Iterator)
- func (m AdjacencyMatrix) Clear()
- func (m AdjacencyMatrix) CountEdges() int
- func (m AdjacencyMatrix) Degree(source VertexIndex) int
- func (m AdjacencyMatrix) Edges(source VertexIndex) func() (VertexIndex, int, bool)
- func (m AdjacencyMatrix) Get(source, target VertexIndex) int
- func (m AdjacencyMatrix) Indegree(target VertexIndex) int
- func (m AdjacencyMatrix) Matrix() matrix.Interface[int]
- func (m AdjacencyMatrix) Outdegree(source VertexIndex) int
- func (m AdjacencyMatrix) Resize(width int)
- func (m AdjacencyMatrix) Set(source, target VertexIndex, count int)
- func (m AdjacencyMatrix) Vertexes() func() (VertexIndex, bool)
- func (m AdjacencyMatrix) Width() int
- type DegreeMatrix
- func (m DegreeMatrix) Calculate(g func() VertexIterator, degree func(index VertexIndex) int)
- func (m DegreeMatrix) Clear()
- func (m DegreeMatrix) CountEdges() int
- func (m DegreeMatrix) Get(source VertexIndex) int
- func (m DegreeMatrix) Matrix() matrix.Interface[int]
- func (m DegreeMatrix) Resize(width int)
- func (m DegreeMatrix) Set(source VertexIndex, count int)
- func (m DegreeMatrix) Width() int
- type EdgeIndex
- type EdgeIterator
- type FilterEdges
- type FilterVertexes
- type Iterator
- type Number
- type VertexIndex
- type VertexIterator
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AdjacencyMatrix ¶
type AdjacencyMatrix struct {
// contains filtered or unexported fields
}
AdjacencyMatrix represents the number of directed edges from a source vertex (along the x-axis) to a target vertex (along the y-axis), including self-loops, if any.
Values are indexed by VertexIndex with source vertexes in the x-axis and target vertexes in the y-axis.
As a representation of a graph itself, AdjacencyMatrix implements the graph Iterator interface.
func NewAdjacencyMatrix ¶
func NewAdjacencyMatrix(m matrix.Constructor[int]) AdjacencyMatrix
NewAdjacencyMatrix returns an AdjacencyMatrix backed by the specified matrix implementation. If nil, defaults to matrix.NewBit.
A matrix implementation with a wider data type is needed to implement an AdjacencyMatrix for a multigraph e.g. matrix.NewGrid.
func (AdjacencyMatrix) Calculate ¶
func (m AdjacencyMatrix) Calculate(g Iterator)
Calculate computes the adjacency matrix for a finite graph g. The created adjacency matrix is itself a graph implementing [Interface] containing only the vertexes of g with at least one inward or outward edge.
Each vertex index in the adjacency matrix corresponds to a matching index in graph g. Once an adjacency matrix has been constructed, it is not affected by future changes to graph g.
func (AdjacencyMatrix) Clear ¶
func (m AdjacencyMatrix) Clear()
func (AdjacencyMatrix) CountEdges ¶
func (m AdjacencyMatrix) CountEdges() int
CountEdges returns the total number of edges in the adjacency matrix.
func (AdjacencyMatrix) Degree ¶
func (m AdjacencyMatrix) Degree(source VertexIndex) int
Degree returns the number of edges either to or from a specific vertex, including self-loops which are counted (by definition) as two edges.
This is computed with O(n) complexity; construct a DegreeMatrix for constant time.
func (AdjacencyMatrix) Edges ¶
func (m AdjacencyMatrix) Edges(source VertexIndex) func() (VertexIndex, int, bool)
Edges implements the graph Iterator Edges method.
func (AdjacencyMatrix) Get ¶
func (m AdjacencyMatrix) Get(source, target VertexIndex) int
Get returns the number of directed edges from a source vertex to a target vertex (including self-loops, if any).
func (AdjacencyMatrix) Indegree ¶
func (m AdjacencyMatrix) Indegree(target VertexIndex) int
Indegree returns the number of directed edges from any vertex to the specific target vertex (including self-loops from the target to itself).
This is computed with O(n) complexity; construct a DegreeMatrix for constant time.
func (AdjacencyMatrix) Matrix ¶
func (m AdjacencyMatrix) Matrix() matrix.Interface[int]
Matrix returns a pointer to the underlying matrix.Interface (of type int).
func (AdjacencyMatrix) Outdegree ¶
func (m AdjacencyMatrix) Outdegree(source VertexIndex) int
Outdegree returns the number of directed edges from a specific source vertex to any other vertex (including self-loops from the source to itself).
This is computed with O(n) complexity; construct a DegreeMatrix for constant time.
func (AdjacencyMatrix) Resize ¶
func (m AdjacencyMatrix) Resize(width int)
Resize updates the adjacency matrix, if necessary, so that it has at least capacity for width elements in each dimension. It reuses underlying memory from the existing matrix where possible. Note that this will clear the matrix.
func (AdjacencyMatrix) Set ¶
func (m AdjacencyMatrix) Set(source, target VertexIndex, count int)
Set stores the number of directed edges from a source vertex to a target vertex (including self-loops, if any).
func (AdjacencyMatrix) Vertexes ¶
func (m AdjacencyMatrix) Vertexes() func() (VertexIndex, bool)
Vertexes implements the graph Iterator Vertexes method. Every vertex will have at least one edge (in either direction).
func (AdjacencyMatrix) Width ¶
func (m AdjacencyMatrix) Width() int
Width returns the width of the adjacency matrix. Note that if VertexIndexes are sparsely distributed, width may be greater the number of vertexes produced by iteration.
type DegreeMatrix ¶
type DegreeMatrix struct {
// contains filtered or unexported fields
}
DegreeMatrix represents the number of edges on a vertex (to or from any other vertex in aggregate). This may be the in-, out-, or undirected-, degree.
Once built, a degree matrix can be queried in constant time.
Values are indexed by VertexIndex. A DegreeMatrix is a diagonal-matrix; values off the diagonal are zero.
func NewDegreeMatrix ¶
func NewDegreeMatrix() DegreeMatrix
NewDegreeMatrix returns a new degree matrix of undefined size.
func (DegreeMatrix) Calculate ¶
func (m DegreeMatrix) Calculate(g func() VertexIterator, degree func(index VertexIndex) int)
Calculate computes the degree matrix from any vertex iterator and any degree function.
For example, an in-, out-, or undirected degree matrix may be constructed from an AdjacencyMatrix using its Vertexes method and its indegree, outdegree, and degree methods respectively.
Each vertex index in the degree matrix corresponds to the matching index in the input graph g. Once a degree matrix has been constructed, it is not affected by future changes to g.
func (DegreeMatrix) Clear ¶
func (m DegreeMatrix) Clear()
func (DegreeMatrix) CountEdges ¶
func (m DegreeMatrix) CountEdges() int
CountEdges returns the total number of edges in the degree matrix.
func (DegreeMatrix) Get ¶
func (m DegreeMatrix) Get(source VertexIndex) int
Get returns the (in-, out-, or undirected) degree of the given vertex.
func (DegreeMatrix) Matrix ¶
func (m DegreeMatrix) Matrix() matrix.Interface[int]
Matrix returns a pointer to the underlying matrix.Interface (of type int).
func (DegreeMatrix) Resize ¶
func (m DegreeMatrix) Resize(width int)
Resize updates the degree matrix, if necessary, so that it has at least capacity for width elements in each dimension. It reuses underlying memory from the existing matrix where possible. Note that this will clear the matrix.
func (DegreeMatrix) Set ¶
func (m DegreeMatrix) Set(source VertexIndex, count int)
Set stores the (in-, out-, or undirected) degree of the given vertex.
func (DegreeMatrix) Width ¶
func (m DegreeMatrix) Width() int
Width returns the width of the degree matrix. Note that if VertexIndexes are sparsely distributed, width may be greater the number of vertexes produced by iteration.
type EdgeIndex ¶
type EdgeIndex int
EdgeIndex optionally uniquely identifies a directed edge from a source vertex to a target vertex, for a unique (source, target) vertex pair in a multigraph. Edge indexes do not have to be consecutive or sorted, may be sparse, and do not have to start at zero, but it may be more efficient where that is the case.
An EdgeIndex is only particularly useful in a multigraph. In normal graphs, each edge from a source can always be uniquely identified by the target. In this case, EdgeIndex is equal to the VertexIndex of the target.
In some graph representations, unique information about each edge is lost. By convention, use -1 to represent a missing index.
Note that while edges are always directed, an undirected graph can be implemented efficiently by representing an undirected edge as a directed edge from a lower-indexed vertex to a higher-indexed vertex, or vice-versa.
type EdgeIterator ¶
type EdgeIterator = func() (target VertexIndex, count int, ok bool)
type FilterEdges ¶
type FilterEdges struct { Parent Iterator Filter func(source VertexIndex, target VertexIndex) bool }
FilterEdges implements the graph Iterator interface and represents a subgraph of a parent graph of only edges that satisfy the given filter function.
The Iterator implemented by FilterEdges is only a view of the parent graph and is computed on-the-fly as the parent changes.
func (FilterEdges) Edges ¶
func (f FilterEdges) Edges(source VertexIndex) func() (VertexIndex, int, bool)
func (FilterEdges) Vertexes ¶
func (f FilterEdges) Vertexes() func() (VertexIndex, bool)
type FilterVertexes ¶
type FilterVertexes struct { Parent Iterator Filter func(vertex VertexIndex) bool }
FilterVertexes implements the graph Iterator interface and represents a subgraph of a parent graph of only vertexes that satisfy the given filter function.
The Iterator implemented by FilterVertexes is only a view of the parent graph and is computed on-the-fly as the parent changes.
func (FilterVertexes) Edges ¶
func (f FilterVertexes) Edges(source VertexIndex) func() (VertexIndex, int, bool)
func (FilterVertexes) Vertexes ¶
func (f FilterVertexes) Vertexes() func() (VertexIndex, bool)
type Iterator ¶
type Iterator interface { Vertexes() VertexIterator Edges(source VertexIndex) EdgeIterator }
Iterator is the basic interface that a graph data type must implement to be able to use many of the algorithms in this package.
The mapping between this general-purpose graph interface and a given graph implementation is achieved through iterator-style functions that generate a VertexIndex. The Edges iterator function generates a count of the number of edges between the two vertexes. If the graph is not a multigraph, this count will always be one. Each iterator-style function returns false as the last argument iff they have completed.
A graph can have many equivalent implementations: a data structure, perhaps an adjacency list or an adjacency matrix; or an algorithm, for example an algorithm that generates an infinite line of vertexes with a directed edge from each vertex to its successor.
Graphs may contain cycles, loops, be disjoint, be multigraphs, be infinite, etc. without restriction except where noted. These properties can be tested for by algorithms implemented in this package.
type Number ¶
type Number interface { constraints.Integer | constraints.Float }
Number represents any integer or float type.
type VertexIndex ¶
type VertexIndex int
VertexIndex is a non-negative index that uniquely identifies each vertex in a graph. Vertex indexes do not have to be consecutive or sorted, may be sparse, and do not have to start at zero, but it may be more efficient where that is the case.
type VertexIterator ¶
type VertexIterator = func() (vertex VertexIndex, ok bool)
func Leaves ¶
func Leaves( vertexes func() VertexIterator, indegree func(VertexIndex) int, outdegree func(index VertexIndex) int, ) VertexIterator
Leaves returns an iterator that generates indexes of vertexes that have exactly one parent and exactly zero children in a directed graph.
A degree function can be made using an AdjacencyMatrix or DegreeMatrix.
func Roots ¶
func Roots( vertexes func() VertexIterator, indegree func(VertexIndex) int, ) VertexIterator
Roots returns an iterator that generates only the indexes of vertexes that have no parents i.e. root vertexes in a directed graph.
A degree function can be made using an AdjacencyMatrix or DegreeMatrix.