graph

package module
v0.0.0-...-fcdb8db Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 21, 2023 License: MPL-2.0 Imports: 6 Imported by: 0

README

graph

A graph is a collection of "nodes" (a.k.a points or vertices) and "edges" (a.k.a lines or arcs) connecting some (possibly empty) subset of them. The edges of graphs may also be imbued with directedness. The study of graphs is known as graph theory.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func AddEdges

func AddEdges(addEdge ...AddEdge)

func ConnectNodes

func ConnectNodes(nodes ...*Node)

ConnectNodes creats an ordered, directed relationship between the given nodes. The first node has an edge to the second node, which has a relationship to the third node, etc.

a → b → c → ...
Example
package main

import (
	"fmt"

	"github.com/picatz/graph"
)

func main() {
	a := &graph.Node{Name: "a"}
	b := &graph.Node{Name: "b"}
	c := &graph.Node{Name: "c"}

	graph.ConnectNodes(a, b, c)

	fmt.Println(a.PathTo(c))
}
Output:

a → b → c
Example (Path_to)
package main

import (
	"fmt"

	"github.com/picatz/graph"
)

func main() {
	a := &graph.Node{Name: "a"}
	b := &graph.Node{Name: "b"}
	c := &graph.Node{Name: "c"}

	// a → b → c
	graph.ConnectNodes(a, b, c)

	path := a.PathTo(c)
	fmt.Println("a to c:", a.HasPath(c))
	fmt.Println("c to a:", c.HasPath(a))
	fmt.Println("nodes in path:", len(path))
	fmt.Println("0:", path[0].Name)
	fmt.Println("1:", path[1].Name)
	fmt.Println("2:", path[2].Name)
}
Output:

a to c: true
c to a: false
nodes in path: 3
0: a
1: b
2: c

func DeleteAttribute

func DeleteAttribute[T any](attrs Attributes, name string)

DeleteAttribute is a helper function to remove a named attribute.

func EncodeDOT

func EncodeDOT(w io.Writer, nodes Nodes) error

func EncodeJSON

func EncodeJSON(w io.Writer, nodes Nodes) error

func GetAttribute

func GetAttribute[T any](attrs Attributes, name string) (T, error)

GetAttributes is a helper function to return a named attribute of a specific type.

func MeshNodes

func MeshNodes(nodes ...*Node)

MeshNodes creats a fully meshed, bi-directional relationship between all of the given nodes.

    a
 ⤢  ↑  ⤡
b ←─┼─→ d
 ⤡  ↓  ⤢
    c
Example
package main

import (
	"fmt"

	"github.com/picatz/graph"
)

func main() {
	a := &graph.Node{Name: "a"}
	b := &graph.Node{Name: "b"}
	c := &graph.Node{Name: "c"}

	//      a
	//    ⤢   ⤡
	//   b  ↔  c
	graph.MeshNodes(a, b, c)

	fmt.Println(a.PathTo(c))
	fmt.Println(c.PathTo(a))
}
Output:

a → c
c → a
Example (Path_to)
package main

import (
	"fmt"

	"github.com/picatz/graph"
)

func main() {
	a := &graph.Node{Name: "a"}
	b := &graph.Node{Name: "b"}
	c := &graph.Node{Name: "c"}

	//      a
	//    ⤢   ⤡
	//   b  ↔  c
	graph.MeshNodes(a, b, c)

	fmt.Println("a to b:", a.HasPath(b))
	fmt.Println("b to a:", b.HasPath(a))
	fmt.Println("a to c:", a.HasPath(c))
	fmt.Println("c to a:", c.HasPath(a))
	fmt.Println("b to c:", b.HasPath(c))
	fmt.Println("c to b:", c.HasPath(b))
}
Output:

a to b: true
b to a: true
a to c: true
c to a: true
b to c: true
c to b: true

func SetAttribute

func SetAttribute[T any](attrs Attributes, name string, value T)

SetAttribute is a helper function to set a named attribute.

func UseAttribute

func UseAttribute[T any](attrs Attributes, name string, fn func(T)) error

UseAttribute is a helper function to use a named attribute of a specific type.

func WithAttributes

func WithAttributes(attrs Attributes) func(*Instance)

WithAttributes is a functional option that sets the attributes of the graph.

func WithNodes

func WithNodes(nodes Nodes) func(*Instance)

WithNodes is a functional option that sets the nodes of the graph.

Types

type AddEdge

type AddEdge struct {
	From      *Node
	Direction *EdgeDirection
	To        *Node
}

type Attributes

type Attributes map[string]any

Attributes are named values that can be associated with a node or subgraph.

type Clique

type Clique = NodeSet

Clique is a subset of nodes in a graph such that every two distinct nodes in the set are adjacent.

https://en.wikipedia.org/wiki/Clique_(graph_theory)

type Cliques

type Cliques []Clique

Cliques is a collection of clique node sets.

func FindCliques

func FindCliques(root *Node, minSize int) Cliques

FindCliques handles finding all "cliques" within a graph. A a clique is a subset of nodes in a graph such that every two distinct nodes in the clique are adjacent. That is, a clique of a graph "G" is an induced subgraph of "G" that is complete.

Example

       b
     ↙   ↖
   c       a
 ↙   ↘   ↗
e  →   d

Cliques: [1] {c, e, d}

References - https://en.wikipedia.org/wiki/Clique_(graph_theory) - https://en.wikipedia.org/wiki/Induced_subgraph - https://en.wikipedia.org/wiki/Complete_graph - https://mathworld.wolfram.com/Clique.html

func (Cliques) ContainsClique

func (cliques Cliques) ContainsClique(c Clique) bool

func (Cliques) ContainsNode

func (cliques Cliques) ContainsNode(n *Node) bool

type Edge

type Edge struct {
	Name      string
	Node      *Node
	Direction EdgeDirection
	Attributes
}

Edge is a relationship with a Node, which can be directed if the edge is an "in" or "out" (directed) or neither (undirected).

type EdgeDirection

type EdgeDirection int

EdgeDirection describes the "direction" of an edge relative to a node. A direction can be in one of five states:

  1. Unknown
  2. None
  3. In
  4. Out
  5. Both
const (
	Unknown EdgeDirection = 0 // [ ┄ ] Edge has an unknown direction.
	None    EdgeDirection = 1 // [ - ] Edge has no direction, is undirected.
	In      EdgeDirection = 2 // [ ← ] Edge has inward direction.
	Out     EdgeDirection = 3 // [ → ] Edge has outward direction.
	Both    EdgeDirection = 4 // [ ↔ ] Edge has both inward and outward direction.
)

func (EdgeDirection) AnyOf

func (d EdgeDirection) AnyOf(directions ...EdgeDirection) bool

AnyOf checks if the edge direction is any of the given directions.

func (EdgeDirection) Match

func (d EdgeDirection) Match(direction EdgeDirection) bool

Match checks if the edge direction matches the given edge direction, either exactly, or implicitly.

  1. Unknown ┄ : ┄
  2. None - : -, ↔
  3. In ← : ←, ↔
  4. Out → : →, ↔
  5. Both ↔ : ↔, ←, →

func (EdgeDirection) String

func (d EdgeDirection) String() string

String returns a human and command-line friendly representation of the edge direction.

type EdgeMap

type EdgeMap map[*Node]Nodes

EdgeMap is a map of nodes to a slice of nodes.

type Edges

type Edges []*Edge

Edges is a collection of Node relationships.

func (Edges) AdjacentNodes

func (edges Edges) AdjacentNodes() NodeSet

func (Edges) AdjacentTo

func (edges Edges) AdjacentTo(nodes ...*Node) bool

func (Edges) ButNotWith

func (edges Edges) ButNotWith(n *Node) Edges

func (Edges) Contains

func (edges Edges) Contains(n *Node) bool

func (Edges) In

func (es Edges) In() Edges

In returns the edges that are directed inwards (pointing to).

func (Edges) NodeSet

func (es Edges) NodeSet() NodeSet

func (Edges) Nodes

func (es Edges) Nodes() Nodes

func (Edges) Out

func (es Edges) Out() Edges

In returns the edges that are directed outwards (pointing from).

type Instance

type Instance struct {
	// Name is the name of the graph instance.
	Name string

	// Attributes is a map of key-value pairs that describe the graph instance.
	Attributes

	// Nodes is a slice of nodes that belong to the graph instance.
	Nodes
}

Instance describes a graph of zero or more nodes.

func New

func New(name string, opts ...func(*Instance)) *Instance

New returns a new instance of a graph.

func (*Instance) AddEdge

func (inst *Instance) AddEdge(from, to *Node)

AddEdge adds an edge to the graph from the source node to the target node.

func (*Instance) AddEdges

func (inst *Instance) AddEdges(em EdgeMap)

AddEdges adds a slice of edges to the graph.

func (*Instance) AddNode

func (inst *Instance) AddNode(node *Node)

AddNode adds a node to the graph.

func (*Instance) AddNodes

func (inst *Instance) AddNodes(nodes ...*Node)

AddNodes adds a slice of nodes to the graph.

func (*Instance) BFS

func (inst *Instance) BFS(fn func(*Node))

BFS performs a breadth-first-search of the graph.

https://en.wikipedia.org/wiki/Breadth-first_search

func (*Instance) DFS

func (inst *Instance) DFS(fn func(*Node))

DFS performs a depth-first-search of the graph.

https://en.wikipedia.org/wiki/Depth-first_search

func (*Instance) IsAcyclic

func (inst *Instance) IsAcyclic() bool

IsAcyclic returns true if the nodes in the graph contains no cycles.

https://mathworld.wolfram.com/AcyclicGraph.html

func (*Instance) IsBipartite

func (inst *Instance) IsBipartite() bool

IsBipartite returns true if the nodes in the graph is a Bipartite graph, also called a bigraph, where nodes can be decomposed into two disjoint sets such that no two nodes within the same set are adjacent.

https://mathworld.wolfram.com/BipartiteGraph.html

func (*Instance) IsMultipartite

func (inst *Instance) IsMultipartite(k int) bool

https://en.wikipedia.org/wiki/Multipartite_graph

func (*Instance) IsUnicyclic

func (inst *Instance) IsUnicyclic() bool

IsUnicyclic returns true if the graph contains only a single cycle.

https://mathworld.wolfram.com/UnicyclicGraph.html

func (*Instance) Visit

func (inst *Instance) Visit(fn func(*Node))

Visit walks the nodes of the graph.

It does not perform depth-first-search, but the given function can choose to implement that using the node's Visit method.

type Node

type Node struct {
	// The (unique) name (or label).
	Name string
	// Adjacency list of edges.
	Edges
	// Named attributes about the node.
	Attributes
}

Node is the base unit of which graphs are formed.

Example
package main

import (
	"fmt"

	"github.com/picatz/graph"
)

func main() {
	a := &graph.Node{Name: "a"}
	b := &graph.Node{Name: "b"}

	a.AddEdge(b)

	fmt.Println(a.Edges[0].Node.Name)
}
Output:

b

func NewNode

func NewNode(name string, attrs Attributes) *Node

NewNode returns a new node with the given name and attributes.

func (*Node) AddEdge

func (n *Node) AddEdge(e *Node)

AddEdge adds a directed relationship to a Node.

n → e

To control the direction used for the relationship, use the AddEdgeWithDirection method.

func (*Node) AddEdgeWithDirection

func (n *Node) AddEdgeWithDirection(e *Node, direction EdgeDirection)

AddEdgeWithDirection adds a potentially directed relationship to a Node. The direction is up to the caller of the function. A corresponding edge is automatically added added; that is, if an "out edge" is added, an "in edge" is added on the other side of the edge relationship. This allows for the relationships to be bi-directionally walked from any point in the graph.

func (n *Node) AddLink(e *Node)

AddLink adds a bi-directional relationship to a Node.

Note: while this is sometimes rendered with a single "↔" (Both),

    this method really defines two distinct edges using the
    In and Out direction.

n ↔ e : [ n → e, e → n ]

func (*Node) HasCycleContaining

func (n *Node) HasCycleContaining(node *Node) bool

HasCycleContaining checks if the Node is part of a cycle that contains the given node.

func (*Node) HasCycles

func (n *Node) HasCycles() bool

HasCycles checks if the Node is part of a cycle. A cycle of a graph is a subset of the edge set of a graph that forms a path such that the first node of the path corresponds to the last.

Example of Cycle

a → b → c → a

Example of Non-Cycle

a → b → c

https://mathworld.wolfram.com/GraphCycle.html https://en.wikipedia.org/wiki/Cycle_(graph_theory)

func (*Node) HasPath

func (n *Node) HasPath(end *Node) bool

HasPath checks if there is a Path to the given end Node.

root node       f           end node
┌────────     ↗             ┌───────
a → b → c → e           i → e
        ↓     ↘       ↗
        d       g → h

Path: a → b → c → e → g → h → i → e

func (*Node) PathTo

func (n *Node) PathTo(end *Node) Path

PathTo returns the Path to the given end Node, nil if no path was found.

func (*Node) PathToWithout

func (n *Node) PathToWithout(end, without *Node) bool

PathToWithout checks if there's a path to the given end node, without having to "go through" or "use" the other given node.

func (*Node) Visit

func (n *Node) Visit(fn func(*Node))

Visit walks the outward nodes using a depth-first-search.

   root node
   ┌────────         1. Start at root "a"
1  a           e 5   2. Go to edge node "b"
   ↑ ⤡ 3   4 ⤢ ↑     3. Go to edge node "c"
   |   c ↔ d   |     4. Go to edge node "d"
   ↓ ⤢       ⤡ ↓     5. Go to edge node "e"
2  b           f 6   6. Go to edge node "f"
Example
package main

import (
	"fmt"

	"github.com/picatz/graph"
)

func main() {
	a := &graph.Node{Name: "a"}
	b := &graph.Node{Name: "b"}
	c := &graph.Node{Name: "c"}

	graph.AddEdges(
		graph.AddEdge{From: a, To: b},
		graph.AddEdge{From: b, To: c},
		graph.AddEdge{From: c, To: a},
	)

	a.Visit(func(n *graph.Node) {
		fmt.Println(n.Name)
	})
}
Output:

a
b
c

func (*Node) VisitAll

func (n *Node) VisitAll(fn func(*Node))

VisitAll walks the the outwards and inwards nodes with a depth-first-search algorithm.

type NodeSet

type NodeSet map[*Node]struct{}

NodeSet is a collection of uniqe Node objects. Meant to be useful for algorithms that require collections of nodes that should not have repeated sequences.

Also particularly useful for recording visited nodes during graph traversal.

func NewNodeSet

func NewNodeSet(nodes ...*Node) NodeSet

NewNodeSet returns a new NodeSet that includes the given nodes.

func (NodeSet) Add

func (ns NodeSet) Add(node *Node)

Add adds the given node to the set.

func (NodeSet) Contains

func (n NodeSet) Contains(node *Node) bool

Contains returns true if the given node is in the set.

func (NodeSet) Emtpy

func (ns NodeSet) Emtpy() bool

Emtpy returns true if the set is empty, false otherwise.

func (NodeSet) IsAdjacentWith

func (ns NodeSet) IsAdjacentWith(other ...*Node) bool

IsAdjacentWith returns true if the given node is adjacent to all the other given nodes.

func (NodeSet) Nodes

func (ns NodeSet) Nodes() []*Node

Nodes returns a slice of nodes in the set.

func (NodeSet) SameAs

func (ns NodeSet) SameAs(other NodeSet) bool

SameAs returns true if the given node set contains the same nodes as the other node set.

func (NodeSet) String

func (n NodeSet) String() string

String returns a comma-separated list of node names in the set in alphabetical order.

type NodeSets

type NodeSets []NodeSet

NodeSets is a collection of NodeSet objects.

func (NodeSets) Contains

func (nodeSets NodeSets) Contains(n *Node) bool

Contains returns true if the given node is in the set.

func (NodeSets) Emtpy

func (nodeSets NodeSets) Emtpy() bool

Emtpy returns true if the set is empty, false otherwise.

func (NodeSets) GetSetNotAdjacentWith

func (nodeSets NodeSets) GetSetNotAdjacentWith(nodes ...*Node) (NodeSet, bool)

GetSetNotAdjacentWith returns the NodeSet that is not adjacent to the given nodes.

func (NodeSets) GetSetThatContains

func (nodeSets NodeSets) GetSetThatContains(n *Node) (NodeSet, bool)

GetSetThatContains returns the NodeSet that contains the given node.

type Nodes

type Nodes []*Node

Nodes is a collection of Node objects.

func DecodeDOT

func DecodeDOT(r io.Reader) (Nodes, error)

func DecodeJSON

func DecodeJSON(r io.Reader) (Nodes, error)

func NewNodes

func NewNodes(nodes ...*Node) Nodes

func (Nodes) AtIndex

func (nodes Nodes) AtIndex(i int) (*Node, error)

AtIndex returns the node at the given index.

func (Nodes) IndexOf

func (nodes Nodes) IndexOf(o *Node) int

IndexOf returns the index of the given node in the set.

func (Nodes) Names

func (nodes Nodes) Names() []string

func (Nodes) String

func (nodes Nodes) String() string

type Path

type Path Nodes

Path is an ordered set of Nodes that make a path from the start, the first element in the slice, to the end, the last element in the slice.

func FindBridges

func FindBridges(root *Node) []Path

FindBridges finds all "bridge" paths within a graph. An edge, part of a path, is a bridge if and only if it is not contained in any cycle. Therefore, a bridge cannot be a cycle chord.

A "bridge" is also known as an "isthmus", "cut-edge", or "cut arc".

       a ← d
     ↙   ↖
e → b  →  c     Bridges (3): e → b, f → b, d → a
    ↑
    f

a           e
↑ ⤡       ⤢ ↑
|   c → d   |   Bridges (1): c → d
↓ ⤢       ⤡ ↓
b           f

To find the bridges in a graph, we need to visit each node and determine if it contains an edge that, if removed, would disconnect the graph into two. This is, if the number of components increases.

A bridge, isthmus, cut-edge, or cut arc is an edge of a graph whose deletion increases the graph's number of connected components. Equivalently, an edge is a bridge if and only if it is not contained in any cycle.

References - https://en.wikipedia.org/wiki/Bridge_(graph_theory) - https://en.wikipedia.org/wiki/Strongly_connected_component - https://mathworld.wolfram.com/GraphBridge.html

func (Path) ContainsNode

func (path Path) ContainsNode(n *Node) bool

ContainsNode checks if the given node is part of the path.

func (Path) Identical

func (path Path) Identical(path2 Path) bool

Identical checks if the given path is the same.

Note: this currently uses the string representation, which might not always

be accurate if the nodes do not, or contain non-uniq names.

func (Path) String

func (path Path) String() string

String returns a human-readable string for the Path.

type Paths

type Paths []Path

Paths is a collection of Path nodes.

func (Paths) ContainsNode

func (paths Paths) ContainsNode(n *Node) bool

ContainsNode checks if the given node is contained in any one of the path node sets.

func (Paths) ContainsPath

func (paths Paths) ContainsPath(p Path) bool

ContainsPath checks if the given path is identical to any of one of the path node sets.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL