// Copyright 2014 Sonia Keys // License MIT: http://opensource.org/licenses/MIT package graph import ( "bytes" "errors" "fmt" "math" "reflect" "text/template" "github.com/soniakeys/bits" ) // graph.go contains type definitions for all graph types and components. // Also, go generate directives for source transformations. // // For readability, the types are defined in a dependency order: // // NI // AdjacencyList // Directed // Undirected // Bipartite // Subgraph // DirectedSubgraph // UndirectedSubgraph // LI // Half // fromHalf // LabeledAdjacencyList // LabeledDirected // LabeledUndirected // LabeledBipartite // LabeledSubgraph // LabeledDirectedSubgraph // LabeledUndirectedSubgraph // Edge // LabeledEdge // LabeledPath // WeightFunc // WeightedEdgeList // TraverseOption //go:generate cp adj_cg.go adj_RO.go //go:generate gofmt -r "LabeledAdjacencyList -> AdjacencyList" -w adj_RO.go //go:generate gofmt -r "n.To -> n" -w adj_RO.go //go:generate gofmt -r "Half -> NI" -w adj_RO.go //go:generate gofmt -r "LabeledSubgraph -> Subgraph" -w adj_RO.go //go:generate cp dir_cg.go dir_RO.go //go:generate gofmt -r "LabeledDirected -> Directed" -w dir_RO.go //go:generate gofmt -r "LabeledDirectedSubgraph -> DirectedSubgraph" -w dir_RO.go //go:generate gofmt -r "LabeledAdjacencyList -> AdjacencyList" -w dir_RO.go //go:generate gofmt -r "labEulerian -> eulerian" -w dir_RO.go //go:generate gofmt -r "newLabEulerian -> newEulerian" -w dir_RO.go //go:generate gofmt -r "Half{n, -1} -> n" -w dir_RO.go //go:generate gofmt -r "n.To -> n" -w dir_RO.go //go:generate gofmt -r "Half -> NI" -w dir_RO.go //go:generate cp undir_cg.go undir_RO.go //go:generate gofmt -r "LabeledUndirected -> Undirected" -w undir_RO.go //go:generate gofmt -r "LabeledBipartite -> Bipartite" -w undir_RO.go //go:generate gofmt -r "LabeledUndirectedSubgraph -> UndirectedSubgraph" -w undir_RO.go //go:generate gofmt -r "LabeledAdjacencyList -> AdjacencyList" -w undir_RO.go //go:generate gofmt -r "newLabEulerian -> newEulerian" -w undir_RO.go //go:generate gofmt -r "Half{n, -1} -> n" -w undir_RO.go //go:generate gofmt -r "n.To -> n" -w undir_RO.go //go:generate gofmt -r "Half -> NI" -w undir_RO.go // An AdjacencyList represents a graph as a list of neighbors for each node. // The "node ID" of a node is simply it's slice index in the AdjacencyList. // For an AdjacencyList g, g[n] represents arcs going from node n to nodes // g[n]. // // Adjacency lists are inherently directed but can be used to represent // directed or undirected graphs. See types Directed and Undirected. type AdjacencyList [][]NI // Directed represents a directed graph. // // Directed methods generally rely on the graph being directed, specifically // that arcs do not have reciprocals. type Directed struct { AdjacencyList // embedded to include AdjacencyList methods } // Undirected represents an undirected graph. // // In an undirected graph, for each arc between distinct nodes there is also // a reciprocal arc, an arc in the opposite direction. Loops do not have // reciprocals. // // Undirected methods generally rely on the graph being undirected, // specifically that every arc between distinct nodes has a reciprocal. type Undirected struct { AdjacencyList // embedded to include AdjacencyList methods } // Bipartite represents a bipartite graph. // // In a bipartite graph, nodes are partitioned into two sets, or // "colors," such that every edge in the graph goes from one set to the // other. // // Member Color represents the partition with a bitmap of length the same // as the number of nodes in the graph. For convenience N0 stores the number // of zero bits in Color. // // To construct a Bipartite object, if you can easily or efficiently use // available information to construct the Color member, then you should do // this and construct a Bipartite object with a Go struct literal. // // If partition information is not readily available, see the constructor // Undirected.Bipartite. // // Alternatively, in some cases where the graph may have multiple connected // components, the lower level Undirected.BipartiteComponent can be used to // control color assignment by component. type Bipartite struct { Undirected Color bits.Bits N0 int } // Subgraph represents a subgraph mapped to a supergraph. // // The subgraph is the embedded AdjacencyList and so the Subgraph type inherits // all methods of Adjacency list. // // The embedded subgraph mapped relative to a specific supergraph, member // Super. A subgraph may have fewer nodes than its supergraph. // Each node of the subgraph must map to a distinct node of the supergraph. // // The mapping giving the supergraph node for a given subgraph node is // represented by member SuperNI, a slice parallel to the the subgraph. // // The mapping in the other direction, giving a subgraph NI for a given // supergraph NI, is represented with map SubNI. // // Multiple Subgraphs can be created relative to a single supergraph. // The Subgraph type represents a mapping to only a single supergraph however. // // See graph methods InduceList and InduceBits for construction of // node-induced subgraphs. // // Alternatively an empty subgraph can be constructed with InduceList(nil). // Arbitrary subgraphs can then be built up with methods AddNode and AddArc. type Subgraph struct { AdjacencyList // the subgraph Super *AdjacencyList // the supergraph SubNI map[NI]NI // subgraph NIs, indexed by supergraph NIs SuperNI []NI // supergraph NIs indexed by subgraph NIs } // DirectedSubgraph represents a subgraph mapped to a supergraph. // // See additional doc at Subgraph type. type DirectedSubgraph struct { Directed Super *Directed SubNI map[NI]NI SuperNI []NI } // UndirectedSubgraph represents a subgraph mapped to a supergraph. // // See additional doc at Subgraph type. type UndirectedSubgraph struct { Undirected Super *Undirected SubNI map[NI]NI SuperNI []NI } // LI is a label integer, used for associating labels with arcs. type LI int32 // Half is a half arc, representing a labeled arc and the "neighbor" node // that the arc leads to. // // Halfs can be composed to form a labeled adjacency list. type Half struct { To NI // node ID, usable as a slice index Label LI // half-arc ID for application data, often a weight } // fromHalf is a half arc, representing a labeled arc and the "neighbor" node // that the arc originates from. // // This used internally in a couple of places. It used to be exported but is // not currently needed anwhere in the API. type fromHalf struct { From NI Label LI } // A LabeledAdjacencyList represents a graph as a list of neighbors for each // node, connected by labeled arcs. // // Arc labels are not necessarily unique arc IDs. Different arcs can have // the same label. // // Arc labels are commonly used to assocate a weight with an arc. Arc labels // are general purpose however and can be used to associate arbitrary // information with an arc. // // Methods implementing weighted graph algorithms will commonly take a // weight function that turns a label int into a float64 weight. // // If only a small amount of information -- such as an integer weight or // a single printable character -- needs to be associated, it can sometimes // be possible to encode the information directly into the label int. For // more generality, some lookup scheme will be needed. // // In an undirected labeled graph, reciprocal arcs must have identical labels. // Note this does not preclude parallel arcs with different labels. type LabeledAdjacencyList [][]Half // LabeledDirected represents a directed labeled graph. // // This is the labeled version of Directed. See types LabeledAdjacencyList // and Directed. type LabeledDirected struct { LabeledAdjacencyList // embedded to include LabeledAdjacencyList methods } // LabeledUndirected represents an undirected labeled graph. // // This is the labeled version of Undirected. See types LabeledAdjacencyList // and Undirected. type LabeledUndirected struct { LabeledAdjacencyList // embedded to include LabeledAdjacencyList methods } // LabeledBipartite represents a bipartite graph. // // In a bipartite graph, nodes are partitioned into two sets, or // "colors," such that every edge in the graph goes from one set to the // other. // // Member Color represents the partition with a bitmap of length the same // as the number of nodes in the graph. For convenience N0 stores the number // of zero bits in Color. // // To construct a LabeledBipartite object, if you can easily or efficiently use // available information to construct the Color member, then you should do // this and construct a LabeledBipartite object with a Go struct literal. // // If partition information is not readily available, see the constructor // Undirected.LabeledBipartite. // // Alternatively, in some cases where the graph may have multiple connected // components, the lower level LabeledUndirected.BipartiteComponent can be used // to control color assignment by component. type LabeledBipartite struct { LabeledUndirected Color bits.Bits N0 int } // LabeledSubgraph represents a subgraph mapped to a supergraph. // // See additional doc at Subgraph type. type LabeledSubgraph struct { LabeledAdjacencyList Super *LabeledAdjacencyList SubNI map[NI]NI SuperNI []NI } // LabeledDirectedSubgraph represents a subgraph mapped to a supergraph. // // See additional doc at Subgraph type. type LabeledDirectedSubgraph struct { LabeledDirected Super *LabeledDirected SubNI map[NI]NI SuperNI []NI } // LabeledUndirectedSubgraph represents a subgraph mapped to a supergraph. // // See additional doc at Subgraph type. type LabeledUndirectedSubgraph struct { LabeledUndirected Super *LabeledUndirected SubNI map[NI]NI SuperNI []NI } // Edge is an undirected edge between nodes N1 and N2. type Edge struct{ N1, N2 NI } // LabeledEdge is an undirected edge with an associated label. type LabeledEdge struct { Edge LI } // LabeledPath is a start node and a path of half arcs leading from start. type LabeledPath struct { Start NI Path []Half } // Distance returns total path distance given WeightFunc w. func (p LabeledPath) Distance(w WeightFunc) float64 { d := 0. for _, h := range p.Path { d += w(h.Label) } return d } // WeightFunc returns a weight for a given label. // // WeightFunc is a parameter type for various search functions. The intent // is to return a weight corresponding to an arc label. The name "weight" // is an abstract term. An arc "weight" will typically have some application // specific meaning other than physical weight. type WeightFunc func(label LI) (weight float64) // WeightedEdgeList is a graph representation. // // It is a labeled edge list, with an associated weight function to return // a weight given an edge label. // // Also associated is the order, or number of nodes of the graph. // All nodes occurring in the edge list must be strictly less than Order. // // WeigtedEdgeList sorts by weight, obtained by calling the weight function. // If weight computation is expensive, consider supplying a cached or // memoized version. type WeightedEdgeList struct { Order int WeightFunc Edges []LabeledEdge } // DistanceMatrix constructs a distance matrix corresponding to the weighted // edges of l. // // An edge n1, n2 with WeightFunc return w is represented by both // d[n1][n2] == w and d[n2][n1] = w. In case of parallel edges, the lowest // weight is stored. The distance from any node to itself d[n][n] is 0, unless // the node has a loop with a negative weight. If g has no edge between n1 and // distinct n2, +Inf is stored for d[n1][n2] and d[n2][n1]. // // The returned DistanceMatrix is suitable for DistanceMatrix.FloydWarshall. func (l WeightedEdgeList) DistanceMatrix() (d DistanceMatrix) { d = newDM(l.Order) for _, e := range l.Edges { n1 := e.Edge.N1 n2 := e.Edge.N2 wt := l.WeightFunc(e.LI) // < to pick min of parallel arcs (also nicely ignores NaN) if wt < d[n1][n2] { d[n1][n2] = wt d[n2][n1] = wt } } return } // A DistanceMatrix is a square matrix representing some distance between // nodes of a graph. If the graph is directected, d[from][to] represents // some distance from node 'from' to node 'to'. Depending on context, the // distance may be an arc weight or path distance. A value of +Inf typically // means no arc or no path between the nodes. type DistanceMatrix [][]float64 // little helper function, makes a blank distance matrix for FloydWarshall. // could be exported? func newDM(n int) DistanceMatrix { inf := math.Inf(1) d := make(DistanceMatrix, n) for i := range d { di := make([]float64, n) for j := range di { di[j] = inf } di[i] = 0 d[i] = di } return d } // FloydWarshall finds all pairs shortest distances for a weighted graph // without negative cycles. // // It operates on a distance matrix representing arcs of a graph and // destructively replaces arc weights with shortest path distances. // // In receiver d, d[fr][to] will be the shortest distance from node // 'fr' to node 'to'. An element value of +Inf means no path exists. // Any diagonal element < 0 indicates a negative cycle exists. // // See DistanceMatrix constructor methods of LabeledAdjacencyList and // WeightedEdgeList for suitable inputs. func (d DistanceMatrix) FloydWarshall() { for k, dk := range d { for _, di := range d { dik := di[k] for j := range d { if d2 := dik + dk[j]; d2 < di[j] { di[j] = d2 } } } } } // PathMatrix is a return type for FloydWarshallPaths. // // It encodes all pairs shortest paths. type PathMatrix [][]NI // Path returns a shortest path from node start to end. // // Argument p is truncated, appended to, and returned as the result. // Thus the underlying allocation is reused if possible. // If there is no path from start to end, p is returned truncated to // zero length. // // If receiver m is not a valid populated PathMatrix as returned by // FloydWarshallPaths, behavior is undefined and a panic is likely. func (m PathMatrix) Path(start, end NI, p []NI) []NI { p = p[:0] for { p = append(p, start) if start == end { return p } start = m[start][end] if start < 0 { return p[:0] } } } // FloydWarshallPaths finds all pairs shortest paths for a weighted graph // without negative cycles. // // It operates on a distance matrix representing arcs of a graph and // destructively replaces arc weights with shortest path distances. // // In receiver d, d[fr][to] will be the shortest distance from node // 'fr' to node 'to'. An element value of +Inf means no path exists. // Any diagonal element < 0 indicates a negative cycle exists. // // The return value encodes the paths. See PathMatrix.Path. // // See DistanceMatrix constructor methods of LabeledAdjacencyList and // WeightedEdgeList for suitable inputs. // // See also similar method FloydWarshallFromLists which has a richer // return value. func (d DistanceMatrix) FloydWarshallPaths() PathMatrix { m := make(PathMatrix, len(d)) inf := math.Inf(1) for i, di := range d { mi := make([]NI, len(d)) for j, dij := range di { if dij == inf { mi[j] = -1 } else { mi[j] = NI(j) } } m[i] = mi } for k, dk := range d { for i, di := range d { mi := m[i] dik := di[k] for j := range d { if d2 := dik + dk[j]; d2 < di[j] { di[j] = d2 mi[j] = mi[k] } } } } return m } // FloydWarshallFromLists finds all pairs shortest paths for a weighted // graph without negative cycles. // // It operates on a distance matrix representing arcs of a graph and // destructively replaces arc weights with shortest path distances. // // In receiver d, d[fr][to] will be the shortest distance from node // 'fr' to node 'to'. An element value of +Inf means no path exists. // Any diagonal element < 0 indicates a negative cycle exists. // // The return value encodes the paths. The FromLists are fully populated // with Leaves and Len values. See for example FromList.PathTo for // extracting paths. Note though that for i'th FromList of the return // value, PathTo(j) will return the path from j's root, which will not // be i in the case that there is no path from i to j. You must check // the first node of the path to see if it is i. If not, there is no // path from i to j. See example. // // See DistanceMatrix constructor methods of LabeledAdjacencyList and // WeightedEdgeList for suitable inputs. // // See also similar method FloydWarshallPaths, which has a lighter // weight return value. func (d DistanceMatrix) FloydWarshallFromLists() []FromList { l := make([]FromList, len(d)) inf := math.Inf(1) for i, di := range d { li := NewFromList(len(d)) p := li.Paths for j, dij := range di { if i == j || dij == inf { p[j] = PathEnd{From: -1} } else { p[j] = PathEnd{From: NI(i)} } } l[i] = li } for k, dk := range d { pk := l[k].Paths for i, di := range d { dik := di[k] pi := l[i].Paths for j := range d { if d2 := dik + dk[j]; d2 < di[j] { di[j] = d2 pi[j] = pk[j] } } } } for _, li := range l { li.RecalcLeaves() li.RecalcLen() } return l } // AddEdge adds an edge to a subgraph. // // For argument e, e.N1 and e.N2 must be NIs in supergraph s.Super. As with // AddNode, AddEdge panics if e.N1 and e.N2 are not valid node indexes of // s.Super. // // Edge e must exist in s.Super. Further, the number of // parallel edges in the subgraph cannot exceed the number of corresponding // parallel edges in the supergraph. That is, each edge already added to the // subgraph counts against the edges available in the supergraph. If a matching // edge is not available, AddEdge returns an error. // // If a matching edge is available, subgraph nodes are added as needed, the // subgraph edge is added, and the method returns nil. func (s *UndirectedSubgraph) AddEdge(n1, n2 NI) error { // verify supergraph NIs first, but without adding subgraph nodes just yet. if int(n1) < 0 || int(n1) >= s.Super.Order() { panic(fmt.Sprint("AddEdge: NI ", n1, " not in supergraph")) } if int(n2) < 0 || int(n2) >= s.Super.Order() { panic(fmt.Sprint("AddEdge: NI ", n2, " not in supergraph")) } // count existing matching edges in subgraph n := 0 a := s.Undirected.AdjacencyList if b1, ok := s.SubNI[n1]; ok { if b2, ok := s.SubNI[n2]; ok { // both NIs already exist in subgraph, need to count edges for _, t := range a[b1] { if t == b2 { n++ } } if b1 != b2 { // verify reciprocal arcs exist r := 0 for _, t := range a[b2] { if t == b1 { r++ } } if r < n { n = r } } } } // verify matching edges are available in supergraph m := 0 for _, t := range (*s.Super).AdjacencyList[n1] { if t == n2 { if m == n { goto r // arc match after all existing arcs matched } m++ } } return errors.New("edge not available in supergraph") r: if n1 != n2 { // verify reciprocal arcs m = 0 for _, t := range (*s.Super).AdjacencyList[n2] { if t == n1 { if m == n { goto good } m++ } } return errors.New("edge not available in supergraph") } good: // matched enough edges. nodes can finally // be added as needed and then the edge can be added. b1 := s.AddNode(n1) b2 := s.AddNode(n2) s.Undirected.AddEdge(b1, b2) return nil // success } // AddEdge adds an edge to a subgraph. // // For argument e, e.N1 and e.N2 must be NIs in supergraph s.Super. As with // AddNode, AddEdge panics if e.N1 and e.N2 are not valid node indexes of // s.Super. // // Edge e must exist in s.Super with label l. Further, the number of // parallel edges in the subgraph cannot exceed the number of corresponding // parallel edges in the supergraph. That is, each edge already added to the // subgraph counts against the edges available in the supergraph. If a matching // edge is not available, AddEdge returns an error. // // If a matching edge is available, subgraph nodes are added as needed, the // subgraph edge is added, and the method returns nil. func (s *LabeledUndirectedSubgraph) AddEdge(e Edge, l LI) error { // verify supergraph NIs first, but without adding subgraph nodes just yet. if int(e.N1) < 0 || int(e.N1) >= s.Super.Order() { panic(fmt.Sprint("AddEdge: NI ", e.N1, " not in supergraph")) } if int(e.N2) < 0 || int(e.N2) >= s.Super.Order() { panic(fmt.Sprint("AddEdge: NI ", e.N2, " not in supergraph")) } // count existing matching edges in subgraph n := 0 a := s.LabeledUndirected.LabeledAdjacencyList if b1, ok := s.SubNI[e.N1]; ok { if b2, ok := s.SubNI[e.N2]; ok { // both NIs already exist in subgraph, need to count edges h := Half{b2, l} for _, t := range a[b1] { if t == h { n++ } } if b1 != b2 { // verify reciprocal arcs exist r := 0 h.To = b1 for _, t := range a[b2] { if t == h { r++ } } if r < n { n = r } } } } // verify matching edges are available in supergraph m := 0 h := Half{e.N2, l} for _, t := range (*s.Super).LabeledAdjacencyList[e.N1] { if t == h { if m == n { goto r // arc match after all existing arcs matched } m++ } } return errors.New("edge not available in supergraph") r: if e.N1 != e.N2 { // verify reciprocal arcs m = 0 h.To = e.N1 for _, t := range (*s.Super).LabeledAdjacencyList[e.N2] { if t == h { if m == n { goto good } m++ } } return errors.New("edge not available in supergraph") } good: // matched enough edges. nodes can finally // be added as needed and then the edge can be added. n1 := s.AddNode(e.N1) n2 := s.AddNode(e.N2) s.LabeledUndirected.AddEdge(Edge{n1, n2}, l) return nil // success } // utility function called from all of the InduceList methods. func mapList(l []NI) (sub map[NI]NI, sup []NI) { sub = map[NI]NI{} // one pass to collect unique NIs for _, p := range l { sub[NI(p)] = -1 } if len(sub) == len(l) { // NIs in l are unique sup = append([]NI{}, l...) // just copy them for b, p := range l { sub[p] = NI(b) // and fill in map } } else { // NIs in l not unique sup = make([]NI, 0, len(sub)) for _, p := range l { // preserve ordering of first occurrences in l if sub[p] < 0 { sub[p] = NI(len(sup)) sup = append(sup, p) } } } return } // utility function called from all of the InduceBits methods. func mapBits(t bits.Bits) (sub map[NI]NI, sup []NI) { sup = make([]NI, 0, t.OnesCount()) sub = make(map[NI]NI, cap(sup)) t.IterateOnes(func(n int) bool { sub[NI(n)] = NI(len(sup)) sup = append(sup, NI(n)) return true }) return } // OrderMap formats maps for testable examples. // // OrderMap provides simple, no-frills formatting of maps in sorted order, // convenient in some cases for output of testable examples. func OrderMap(m interface{}) string { // in particular exclude slices, which template would happily accept but // which would probably represent a coding mistake if reflect.TypeOf(m).Kind() != reflect.Map { panic("not a map") } t := template.Must(template.New("").Parse( `map[{{range $k, $v := .}}{{$k}}:{{$v}} {{end}}]`)) var b bytes.Buffer if err := t.Execute(&b, m); err != nil { panic(err) } return b.String() }