// Copyright 2014 Sonia Keys // License MIT: http://opensource.org/licenses/MIT package graph import ( "errors" "fmt" "github.com/soniakeys/bits" ) // dir_RO.go is code generated from dir_cg.go by directives in graph.go. // Editing dir_cg.go is okay. It is the code generation source. // DO NOT EDIT dir_RO.go. // The RO means read only and it is upper case RO to slow you down a bit // in case you start to edit the file. // Balanced returns true if for every node in g, in-degree equals out-degree. // // There are equivalent labeled and unlabeled versions of this method. func (g Directed) Balanced() bool { for n, in := range g.InDegree() { if in != len(g.AdjacencyList[n]) { return false } } return true } // Copy makes a deep copy of g. // Copy also computes the arc size ma, the number of arcs. // // There are equivalent labeled and unlabeled versions of this method. func (g Directed) Copy() (c Directed, ma int) { l, s := g.AdjacencyList.Copy() return Directed{l}, s } // Cyclic determines if g contains a cycle, a non-empty path from a node // back to itself. // // Cyclic returns true if g contains at least one cycle. It also returns // an example of an arc involved in a cycle. // Cyclic returns false if g is acyclic. // // Also see Topological, which detects cycles. // // There are equivalent labeled and unlabeled versions of this method. func (g Directed) Cyclic() (cyclic bool, fr NI, to NI) { a := g.AdjacencyList fr, to = -1, -1 temp := bits.New(len(a)) perm := bits.New(len(a)) var df func(int) df = func(n int) { switch { case temp.Bit(n) == 1: cyclic = true return case perm.Bit(n) == 1: return } temp.SetBit(n, 1) for _, nb := range a[n] { df(int(nb)) if cyclic { if fr < 0 { fr, to = NI(n), nb } return } } temp.SetBit(n, 0) perm.SetBit(n, 1) } for n := range a { if perm.Bit(n) == 1 { continue } if df(n); cyclic { // short circuit as soon as a cycle is found break } } return } // DegreeCentralization returns out-degree centralization. // // Out-degree of a node is one measure of node centrality and is directly // available from the adjacency list representation. This allows degree // centralization for the graph to be very efficiently computed. // // The value returned is from 0 to 1 inclusive for simple directed graphs of // two or more nodes. As a special case, 0 is returned for graphs of 0 or 1 // nodes. The value returned can be > 1 for graphs with loops or parallel // edges. // // In-degree centralization can be computed as DegreeCentralization of the // transpose. // // There are equivalent labeled and unlabeled versions of this method. func (g Directed) DegreeCentralization() float64 { a := g.AdjacencyList if len(a) <= 1 { return 0 } var max, sum int for _, to := range a { if len(to) > max { max = len(to) } sum += len(to) } l1 := len(a) - 1 return float64(len(a)*max-sum) / float64(l1*l1) } // Dominators computes the immediate dominator for each node reachable from // start. // // The slice returned as Dominators.Immediate will have the length of // g.AdjacencyList. Nodes without a path to end will have a value of -1. // // See also the method Doms. Internally Dominators must construct the // transpose of g and also compute a postordering of a spanning tree of the // subgraph reachable from start. If you happen to have either of these // computed anyway, it can be more efficient to call Doms directly. func (g Directed) Dominators(start NI) Dominators { a := g.AdjacencyList l := len(a) // ExampleDoms shows traditional depth-first postorder, but it works to // generate a reverse preorder. Also breadth-first works instead of // depth-first and may allow Doms to run a little faster by presenting // a shallower tree. post := make([]NI, l) a.BreadthFirst(start, func(n NI) { l-- post[l] = n }) tr, _ := g.Transpose() return g.Doms(tr, post[l:]) } // Doms computes either immediate dominators or postdominators. // // The slice returned as Dominators.Immediate will have the length of // g.AdjacencyList. Nodes without a path to end will have a value of -1. // // But see also the simpler methods Dominators and PostDominators. // // Doms requires argument tr to be the transpose graph of receiver g, // and requres argument post to be a post ordering of receiver g. More // specifically a post ordering of a spanning tree of the subgraph reachable // from some start node in g. The start node will always be the last node in // this postordering so it does not need to passed as a separate argument. // // Doms can be used to construct either dominators or postdominators. // To construct dominators on a graph f, generate a postordering p on f // and call f.Doms(f.Transpose(), p). To construct postdominators, generate // the transpose t first, then a postordering p on t (not f), and call // t.Doms(f, p). // // Caution: The argument tr is retained in the returned Dominators object // and is used by the method Dominators.Frontier. It is not deep-copied // so it is invalid to call Doms, modify the tr graph, and then call Frontier. func (g Directed) Doms(tr Directed, post []NI) Dominators { a := g.AdjacencyList dom := make([]NI, len(a)) pi := make([]int, len(a)) for i, n := range post { pi[n] = i } intersect := func(b1, b2 NI) NI { for b1 != b2 { for pi[b1] < pi[b2] { b1 = dom[b1] } for pi[b2] < pi[b1] { b2 = dom[b2] } } return b1 } for n := range dom { dom[n] = -1 } start := post[len(post)-1] dom[start] = start for changed := false; ; changed = false { for i := len(post) - 2; i >= 0; i-- { b := post[i] var im NI fr := tr.AdjacencyList[b] var j int var fp NI for j, fp = range fr { if dom[fp] >= 0 { im = fp break } } for _, p := range fr[j:] { if dom[p] >= 0 { im = intersect(im, p) } } if dom[b] != im { dom[b] = im changed = true } } if !changed { return Dominators{dom, tr} } } } // PostDominators computes the immediate postdominator for each node that can // reach node end. // // The slice returned as Dominators.Immediate will have the length of // g.AdjacencyList. Nodes without a path to end will have a value of -1. // // See also the method Doms. Internally Dominators must construct the // transpose of g and also compute a postordering of a spanning tree of the // subgraph of the transpose reachable from end. If you happen to have either // of these computed anyway, it can be more efficient to call Doms directly. // // See the method Doms anyway for the caution note. PostDominators calls // Doms internally, passing receiver g as Doms argument tr. The caution means // that it is invalid to call PostDominators, modify the graph g, then call // Frontier. func (g Directed) PostDominators(end NI) Dominators { tr, _ := g.Transpose() a := tr.AdjacencyList l := len(a) post := make([]NI, l) a.BreadthFirst(end, func(n NI) { l-- post[l] = n }) return tr.Doms(g, post[l:]) } // called from Dominators.Frontier via interface func (from Directed) domFrontiers(d Dominators) DominanceFrontiers { im := d.Immediate f := make(DominanceFrontiers, len(im)) for i := range f { if im[i] >= 0 { f[i] = map[NI]struct{}{} } } for b, fr := range from.AdjacencyList { if len(fr) < 2 { continue } imb := im[b] for _, p := range fr { for runner := p; runner != imb; runner = im[runner] { f[runner][NI(b)] = struct{}{} } } } return f } // Eulerian scans a directed graph to determine if it is Eulerian. // // If the graph represents an Eulerian cycle, it returns -1, -1, nil. // // If the graph does not represent an Eulerian cycle but does represent an // Eulerian path, it returns the start and end nodes of the path, and nil. // // Otherwise it returns an error indicating a reason the graph is non-Eulerian. // Also in this case it returns a relevant node in either start or end. // // See also method EulerianStart, which short-circuits when it finds a start // node whereas this method completely validates a graph as Eulerian. // // There are equivalent labeled and unlabeled versions of this method. func (g Directed) Eulerian() (start, end NI, err error) { ind := g.InDegree() start = -1 end = -1 for n, to := range g.AdjacencyList { switch { case len(to) > ind[n]: if start >= 0 { return NI(n), -1, errors.New("multiple start candidates") } if len(to) > ind[n]+1 { return NI(n), -1, errors.New("excessive out-degree") } start = NI(n) case ind[n] > len(to): if end >= 0 { return -1, NI(n), errors.New("multiple end candidates") } if ind[n] > len(to)+1 { return -1, NI(n), errors.New("excessive in-degree") } end = NI(n) } } return start, end, nil } // EulerianCycle finds an Eulerian cycle in a directed multigraph. // // * If g has no nodes, result is nil, nil. // // * If g is Eulerian, result is an Eulerian cycle with err = nil. // The first element of the result represents only a start node. // The remaining elements represent the half arcs of the cycle. // // * Otherwise, result is nil, with a non-nil error giving a reason the graph // is not Eulerian. // // Internally, EulerianCycle copies the entire graph g. // See EulerianCycleD for a more space efficient version. // // There are nearly equivalent labeled and unlabeled versions of this method. // In the labeled version the first element of of the func (g Directed) EulerianCycle() ([]NI, error) { c, m := g.Copy() return c.EulerianCycleD(m) } // EulerianCycleD finds an Eulerian cycle in a directed multigraph. // // EulerianCycleD is destructive on its receiver g. See EulerianCycle for // a non-destructive version. // // Argument ma must be the correct arc size, or number of arcs in g. // // * If g has no nodes, result is nil, nil. // // * If g is Eulerian, result is an Eulerian cycle with err = nil. // The first element of the result represents only a start node. // The remaining elements represent the half arcs of the cycle. // // * Otherwise, result is nil, with a non-nil error giving a reason the graph // is not Eulerian. // // There are equivalent labeled and unlabeled versions of this method. func (g Directed) EulerianCycleD(ma int) ([]NI, error) { // algorithm adapted from "Sketch of Eulerian Circuit Algorithm" by // Carl Lee, accessed at http://www.ms.uky.edu/~lee/ma515fa10/euler.pdf. if g.Order() == 0 { return nil, nil } e := newEulerian(g.AdjacencyList, ma) e.p[0] = 0 for e.s >= 0 { v := e.top() // v is node that starts cycle e.push() // if Eulerian, we'll always come back to starting node if e.top() != v { return nil, errors.New("not Eulerian") } e.keep() } if !e.uv.AllZeros() { return nil, errors.New("not strongly connected") } return e.p, nil } // EulerianPath finds an Eulerian path in a directed multigraph. // // * If g has no nodes, result is nil, nil. // // * If g has an Eulerian path, result is an Eulerian path with err = nil. // The first element of the result represents only a start node. // The remaining elements represent the half arcs of the path. // // * Otherwise, result is nil, with a non-nil error giving a reason the graph // is not Eulerian. // // Internally, EulerianPath copies the entire graph g. // See EulerianPathD for a more space efficient version. // // There are equivalent labeled and unlabeled versions of this method. func (g Directed) EulerianPath() ([]NI, error) { c, m := g.Copy() start, err := c.EulerianStart() if err != nil { return nil, err } if start < 0 { start = 0 } return c.EulerianPathD(m, start) } // EulerianPathD finds an Eulerian path in a directed multigraph. // // EulerianPathD is destructive on its receiver g. See EulerianPath for // a non-destructive version. // // Argument ma must be the correct arc size, or number of arcs in g. // Argument start must be a valid start node for the path. // // * If g has no nodes, result is nil, nil. // // * If g has an Eulerian path starting at start, result is an Eulerian path // with err = nil. // The first element of the result represents only a start node. // The remaining elements represent the half arcs of the path. // // * Otherwise, result is nil, with a non-nil error giving a reason the graph // is not Eulerian. // // There are equivalent labeled and unlabeled versions of this method. func (g Directed) EulerianPathD(ma int, start NI) ([]NI, error) { if g.Order() == 0 { return nil, nil } e := newEulerian(g.AdjacencyList, ma) e.p[0] = start // unlike EulerianCycle, the first path doesn't have to be a cycle. e.push() e.keep() for e.s >= 0 { start = e.top() e.push() // paths after the first must be cycles though // (as long as there are nodes on the stack) if e.top() != start { return nil, errors.New("no Eulerian path") } e.keep() } if !e.uv.AllZeros() { return nil, errors.New("no Eulerian path") } return e.p, nil } // EulerianStart finds a candidate start node for an Eulerian path. // // A candidate start node in the directed case has out-degree one greater then // in-degree. EulerianStart scans the graph returning immediately with the // node (and err == nil) when it finds such a candidate. // // EulerianStart also returns immediately with an error if it finds the graph // cannot contain an Eulerian path. In this case it also returns a relevant // node. // // If the scan completes without finding a candidate start node, the graph // represents an Eulerian cycle. In this case it returns -1, nil, and any // node can be chosen as a start node for an eulerian path. // // See also method Eulerian, which completely validates a graph as Eulerian // whereas this method short-curcuits when it finds a start node. // // There are equivalent labeled and unlabeled versions of this method. func (g Directed) EulerianStart() (start NI, err error) { ind := g.InDegree() end := -1 for n, to := range g.AdjacencyList { switch { case len(to) > ind[n]: if len(to) == ind[n]+1 { return NI(n), nil // candidate start } return -1, errors.New("excessive out-degree") case ind[n] > len(to): if end >= 0 { return NI(n), errors.New("multiple end candidates") } if ind[n] > len(to)+1 { return NI(n), errors.New("excessive in-degree") } end = n } } return -1, nil // cycle } type eulerian struct { g AdjacencyList // working copy of graph, it gets consumed m int // number of arcs in g, updated as g is consumed uv bits.Bits // unvisited // low end of p is stack of unfinished nodes // high end is finished path p []NI // stack + path s int // stack pointer } func newEulerian(g AdjacencyList, m int) *eulerian { e := &eulerian{ g: g, m: m, uv: bits.New(len(g)), p: make([]NI, m+1), } e.uv.SetAll() return e } // starting with the node on top of the stack, move nodes with no arcs. func (e *eulerian) keep() { for e.s >= 0 { n := e.top() if len(e.g[n]) > 0 { break } e.p[e.m] = n e.s-- e.m-- } } func (e *eulerian) top() NI { return e.p[e.s] } // MaximalNonBranchingPaths finds all paths in a directed graph that are // "maximal" and "non-branching". // // A non-branching path is one where path nodes other than the first and last // have exactly one arc leading to the node and one arc leading from the node, // thus there is no possibility to branch away to a different path. // // A maximal non-branching path cannot be extended to a longer non-branching // path by including another node at either end. // // In the case of a cyclic non-branching path, the first and last nodes // of the path will be the same node, indicating an isolated cycle. // // The method calls the emit argument for each path or isolated cycle in g, // as long as emit returns true. If emit returns false, // MaximalNonBranchingPaths returns immediately. // // There are equivalent labeled and unlabeled versions of this method. func (g Directed) MaximalNonBranchingPaths(emit func([]NI) bool) { a := g.AdjacencyList ind := g.InDegree() uv := bits.New(g.Order()) uv.SetAll() for v, vTo := range a { if !(ind[v] == 1 && len(vTo) == 1) { for _, w := range vTo { n := []NI{NI(v), w} uv.SetBit(v, 0) uv.SetBit(int(w), 0) wTo := a[w] for ind[w] == 1 && len(wTo) == 1 { u := wTo[0] n = append(n, u) uv.SetBit(int(u), 0) w = u wTo = a[w] } if !emit(n) { // n is a path return } } } } // use uv.From rather than uv.Iterate. // Iterate doesn't work here because we're modifying uv for b := uv.OneFrom(0); b >= 0; b = uv.OneFrom(b + 1) { v := NI(b) n := []NI{v} for w := v; ; { w = a[w][0] uv.SetBit(int(w), 0) n = append(n, w) if w == v { break } } if !emit(n) { // n is an isolated cycle return } } } // InDegree computes the in-degree of each node in g // // There are equivalent labeled and unlabeled versions of this method. func (g Directed) InDegree() []int { ind := make([]int, g.Order()) for _, nbs := range g.AdjacencyList { for _, nb := range nbs { ind[nb]++ } } return ind } // AddNode maps a node in a supergraph to a subgraph node. // // Argument p must be an NI in supergraph s.Super. AddNode panics if // p is not a valid node index of s.Super. // // AddNode is idempotent in that it does not add a new node to the subgraph if // a subgraph node already exists mapped to supergraph node p. // // The mapped subgraph NI is returned. func (s *DirectedSubgraph) AddNode(p NI) (b NI) { if int(p) < 0 || int(p) >= s.Super.Order() { panic(fmt.Sprint("AddNode: NI ", p, " not in supergraph")) } if b, ok := s.SubNI[p]; ok { return b } a := s.Directed.AdjacencyList b = NI(len(a)) s.Directed.AdjacencyList = append(a, nil) s.SuperNI = append(s.SuperNI, p) s.SubNI[p] = b return } // AddArc adds an arc to a subgraph. // // Arguments fr, to must be NIs in supergraph s.Super. As with AddNode, // AddArc panics if fr and to are not valid node indexes of s.Super. // // The arc specfied by fr, to must exist in s.Super. Further, the number of // parallel arcs in the subgraph cannot exceed the number of corresponding // parallel arcs in the supergraph. That is, each arc already added to the // subgraph counts against the arcs available in the supergraph. If a matching // arc is not available, AddArc returns an error. // // If a matching arc is available, subgraph nodes are added as needed, the // subgraph arc is added, and the method returns nil. func (s *DirectedSubgraph) AddArc(fr NI, to NI) error { // verify supergraph NIs first, but without adding subgraph nodes just yet. if int(fr) < 0 || int(fr) >= s.Super.Order() { panic(fmt.Sprint("AddArc: NI ", fr, " not in supergraph")) } if int(to) < 0 || int(to) >= s.Super.Order() { panic(fmt.Sprint("AddArc: NI ", to, " not in supergraph")) } // count existing matching arcs in subgraph n := 0 a := s.Directed.AdjacencyList if bf, ok := s.SubNI[fr]; ok { if bt, ok := s.SubNI[to]; ok { // both NIs already exist in subgraph, need to count arcs bTo := to bTo = bt for _, t := range a[bf] { if t == bTo { n++ } } } } // verify matching arcs are available in supergraph for _, t := range (*s.Super).AdjacencyList[fr] { if t == to { if n > 0 { n-- // match existing arc continue } // no more existing arcs need to be matched. nodes can finally // be added as needed and then the arc can be added. bf := s.AddNode(fr) to = s.AddNode(to) s.Directed.AdjacencyList[bf] = append(s.Directed.AdjacencyList[bf], to) return nil // success } } return errors.New("arc not available in supergraph") } // InduceList constructs a node-induced subgraph. // // The subgraph is induced on receiver graph g. Argument l must be a list of // NIs in receiver graph g. Receiver g becomes the supergraph of the induced // subgraph. // // Duplicate NIs are allowed in list l. The duplicates are effectively removed // and only a single corresponding node is created in the subgraph. Subgraph // NIs are mapped in the order of list l, execpt for ignoring duplicates. // NIs in l that are not in g will panic. // // Returned is the constructed Subgraph object containing the induced subgraph // and the mappings to the supergraph. func (g *Directed) InduceList(l []NI) *DirectedSubgraph { sub, sup := mapList(l) return &DirectedSubgraph{ Super: g, SubNI: sub, SuperNI: sup, Directed: Directed{ g.AdjacencyList.induceArcs(sub, sup), }} } // InduceBits constructs a node-induced subgraph. // // The subgraph is induced on receiver graph g. Argument t must be a bitmap // representing NIs in receiver graph g. Receiver g becomes the supergraph // of the induced subgraph. NIs in t that are not in g will panic. // // Returned is the constructed Subgraph object containing the induced subgraph // and the mappings to the supergraph. func (g *Directed) InduceBits(t bits.Bits) *DirectedSubgraph { sub, sup := mapBits(t) return &DirectedSubgraph{ Super: g, SubNI: sub, SuperNI: sup, Directed: Directed{ g.AdjacencyList.induceArcs(sub, sup), }} } // IsTree identifies trees in directed graphs. // // Return value isTree is true if the subgraph reachable from root is a tree. // Further, return value allTree is true if the entire graph g is reachable // from root. // // There are equivalent labeled and unlabeled versions of this method. func (g Directed) IsTree(root NI) (isTree, allTree bool) { a := g.AdjacencyList v := bits.New(len(a)) v.SetAll() var df func(NI) bool df = func(n NI) bool { if v.Bit(int(n)) == 0 { return false } v.SetBit(int(n), 0) for _, to := range a[n] { if !df(to) { return false } } return true } isTree = df(root) return isTree, isTree && v.AllZeros() } // PageRank computes a significance score for each node of a graph. // // The algorithm is credited to Google founders Brin and Lawrence. // // Argument d is a damping factor. Reportedly a value of .85 works well. // Argument n is a number of iterations. Reportedly values of 20 to 50 // work well. // // Returned is the PageRank score for each node of g. // // There are equivalent labeled and unlabeled versions of this method. func (g Directed) PageRank(d float64, n int) []float64 { // Following "PageRank Explained" by Ian Rogers, accessed at // http://www.cs.princeton.edu/~chazelle/courses/BIB/pagerank.htm a := g.AdjacencyList p0 := make([]float64, len(a)) p1 := make([]float64, len(a)) for i := range p0 { p0[i] = 1 } d1 := 1 - d for ; n > 0; n-- { for i := range p1 { p1[i] = d1 } for fr, to := range a { f := d / float64(len(to)) for _, to := range to { p1[to] += p0[fr] * f } } p0, p1 = p1, p0 } return p0 } // StronglyConnectedComponents identifies strongly connected components in // a directed graph. // // The method calls the emit function for each component identified. The // argument to emit is the node list of a component. The emit function must // return true for the method to continue identifying components. If emit // returns false, the method returns immediately. // // Note well: The backing slice for the node list passed to emit is reused // across emit calls. If you need to retain the node list you must copy it. // // The components emitted represent a partition of the nodes in g. // So for example, if the first component emitted has the same length as g // then it will be the only component and it means the entire graph g is // strongly connected. // // See also Condensation which returns a condensation graph in addition // to the strongly connected components. // // There are equivalent labeled and unlabeled versions of this method. // // The algorithm here is by David Pearce. See also alt.SCCPathBased and // alt.SCCTarjan. func (g Directed) StronglyConnectedComponents(emit func([]NI) bool) { // See Algorithm 3 PEA FIND SCC2(V,E) in "An Improved Algorithm for // Finding the Strongly Connected Components of a Directed Graph" // by David J. Pearce. a := g.AdjacencyList rindex := make([]int, len(a)) var S, scc []NI index := 1 c := len(a) - 1 var visit func(NI) bool visit = func(v NI) bool { root := true rindex[v] = index index++ for _, w := range a[v] { if rindex[w] == 0 { if !visit(w) { return false } } if rindex[w] < rindex[v] { rindex[v] = rindex[w] root = false } } if !root { S = append(S, v) return true } scc = scc[:0] index-- for last := len(S) - 1; last >= 0; last-- { w := S[last] if rindex[v] > rindex[w] { break } S = S[:last] rindex[w] = c scc = append(scc, w) index-- } rindex[v] = c c-- return emit(append(scc, v)) } for v := range a { if rindex[v] == 0 && !visit(NI(v)) { break } } } // Condensation returns strongly connected components and their // condensation graph. // // A condensation represents a directed acyclic graph. // Components are ordered in a reverse topological ordering. // // See also StronglyConnectedComponents, which returns the components only. // // There are equivalent labeled and unlabeled versions of this method. func (g Directed) Condensation() (scc [][]NI, cd AdjacencyList) { a := g.AdjacencyList b := make([]NI, len(a)) // backing slice for scc g.StronglyConnectedComponents(func(c []NI) bool { n := copy(b, c) scc = append(scc, b[:n]) b = b[n:] return true }) cd = make(AdjacencyList, len(scc)) // return value cond := make([]NI, len(a)) // mapping from g node to cd node for cn, c := range scc { for _, n := range c { cond[n] = NI(cn) // map g node to cd node } var tos []NI // list of 'to' nodes m := bits.New(len(cd)) // tos map m.SetBit(cn, 1) for _, n := range c { for _, to := range a[n] { if ct := cond[to]; m.Bit(int(ct)) == 0 { m.SetBit(int(ct), 1) tos = append(tos, ct) } } } cd[cn] = tos } return } // Topological computes a topological ordering of a directed acyclic graph. // // For an acyclic graph, return value ordering is a permutation of node numbers // in topologically sorted order and cycle will be nil. If the graph is found // to be cyclic, ordering will be nil and cycle will be the path of a found // cycle. // // There are equivalent labeled and unlabeled versions of this method. func (g Directed) Topological() (ordering, cycle []NI) { i := -1 return g.dfTopo(func() NI { i++ if i < g.Order() { return NI(i) } return -1 }) } func (g Directed) dfTopo(f func() NI) (ordering, cycle []NI) { a := g.AdjacencyList ordering = make([]NI, len(a)) i := len(ordering) temp := bits.New(len(a)) perm := bits.New(len(a)) var cycleFound bool var cycleStart NI var df func(NI) df = func(n NI) { switch { case temp.Bit(int(n)) == 1: cycleFound = true cycleStart = n return case perm.Bit(int(n)) == 1: return } temp.SetBit(int(n), 1) for _, nb := range a[n] { df(nb) if cycleFound { if cycleStart >= 0 { // a little hack: orderng won't be needed so repurpose the // slice as cycle. this is read out in reverse order // as the recursion unwinds. x := len(ordering) - 1 - len(cycle) ordering[x] = n cycle = ordering[x:] if n == cycleStart { cycleStart = -1 } } return } } temp.SetBit(int(n), 0) perm.SetBit(int(n), 1) i-- ordering[i] = n } for { n := f() if n < 0 { return ordering[i:], nil } if perm.Bit(int(n)) == 1 { continue } df(n) if cycleFound { return nil, cycle } } } // TopologicalKahn computes a topological ordering of a directed acyclic graph. // // For an acyclic graph, return value ordering is a permutation of node numbers // in topologically sorted order and cycle will be nil. If the graph is found // to be cyclic, ordering will be nil and cycle will be the path of a found // cycle. // // This function is based on the algorithm by Arthur Kahn and requires the // transpose of g be passed as the argument. // // There are equivalent labeled and unlabeled versions of this method. func (g Directed) TopologicalKahn(tr Directed) (ordering, cycle []NI) { // code follows Wikipedia pseudocode. var L, S []NI // rem for "remaining edges," this function makes a local copy of the // in-degrees and consumes that instead of consuming an input. rem := make([]int, g.Order()) for n, fr := range tr.AdjacencyList { if len(fr) == 0 { // accumulate "set of all nodes with no incoming edges" S = append(S, NI(n)) } else { // initialize rem from in-degree rem[n] = len(fr) } } for len(S) > 0 { last := len(S) - 1 // "remove a node n from S" n := S[last] S = S[:last] L = append(L, n) // "add n to tail of L" for _, m := range g.AdjacencyList[n] { // WP pseudo code reads "for each node m..." but it means for each // node m *remaining in the graph.* We consume rem rather than // the graph, so "remaining in the graph" for us means rem[m] > 0. if rem[m] > 0 { rem[m]-- // "remove edge from the graph" if rem[m] == 0 { // if "m has no other incoming edges" S = append(S, m) // "insert m into S" } } } } // "If graph has edges," for us means a value in rem is > 0. for c, in := range rem { if in > 0 { // recover cyclic nodes for _, nb := range g.AdjacencyList[c] { if rem[nb] > 0 { cycle = append(cycle, NI(c)) break } } } } if len(cycle) > 0 { return nil, cycle } return L, nil } // TopologicalSubgraph computes a topological ordering of a subgraph of a // directed acyclic graph. // // The subgraph considered is that reachable from the specified node list. // // For an acyclic subgraph, return value ordering is a permutation of reachable // node numbers in topologically sorted order and cycle will be nil. If the // subgraph is found to be cyclic, ordering will be nil and cycle will be // the path of a found cycle. // // There are equivalent labeled and unlabeled versions of this method. func (g Directed) TopologicalSubgraph(nodes []NI) (ordering, cycle []NI) { i := -1 return g.dfTopo(func() NI { i++ if i < len(nodes) { return nodes[i] } return -1 }) } // TransitiveClosure returns the transitive closure of directed graph g. // // The algorithm is Warren's, which works most naturally with an adjacency // matrix representation. The returned transitive closure is left in this // adjacency matrix representation. For a graph g of order n, matrix tc // is returned as a length n slice of length n bits.Bits values, where // tc[from].Bit(to) == 1 represents an arc of the transitive closure. func (g Directed) TransitiveClosure() []bits.Bits { // construct adjacency matrix a := g.AdjacencyList t := make([]bits.Bits, len(a)) for n := range t { tn := bits.New(len(a)) for _, to := range a[n] { tn.SetBit(int(to), 1) } t[n] = tn } // above diagonal for i := 1; i < len(a); i++ { ti := t[i] for k := 0; k < i; k++ { if ti.Bit(k) == 1 { ti.Or(ti, t[k]) } } } // below diagonal for i, ti := range t[:len(a)-1] { for k := i + 1; k < len(a); k++ { if ti.Bit(k) == 1 { ti.Or(ti, t[k]) } } } return t }