// PostDominatores returns all dominators for all nodes in g. It does not // prune for strict post-dominators, immediate dominators etc. // // A dominates B if and only if the only path through B travels through A. func Dominators(start graph.Node, g graph.Graph) map[int]set.Nodes { allNodes := make(set.Nodes) nlist := g.Nodes() dominators := make(map[int]set.Nodes, len(nlist)) for _, node := range nlist { allNodes.Add(node) } var to func(graph.Node) []graph.Node switch g := g.(type) { case graph.Directed: to = g.To default: to = g.From } for _, node := range nlist { dominators[node.ID()] = make(set.Nodes) if node.ID() == start.ID() { dominators[node.ID()].Add(start) } else { dominators[node.ID()].Copy(allNodes) } } for somethingChanged := true; somethingChanged; { somethingChanged = false for _, node := range nlist { if node.ID() == start.ID() { continue } preds := to(node) if len(preds) == 0 { continue } tmp := make(set.Nodes).Copy(dominators[preds[0].ID()]) for _, pred := range preds[1:] { tmp.Intersect(tmp, dominators[pred.ID()]) } dom := make(set.Nodes) dom.Add(node) dom.Union(dom, tmp) if !set.Equal(dom, dominators[node.ID()]) { dominators[node.ID()] = dom somethingChanged = true } } } return dominators }
// brandes is the common code for Betweenness and EdgeBetweenness. It corresponds // to algorithm 1 in http://algo.uni-konstanz.de/publications/b-vspbc-08.pdf with // the accumulation loop provided by the accumulate closure. func brandes(g graph.Graph, accumulate func(s graph.Node, stack internal.NodeStack, p map[int][]graph.Node, delta, sigma map[int]float64)) { var ( nodes = g.Nodes() stack internal.NodeStack p = make(map[int][]graph.Node, len(nodes)) sigma = make(map[int]float64, len(nodes)) d = make(map[int]int, len(nodes)) delta = make(map[int]float64, len(nodes)) queue internal.NodeQueue ) for _, s := range nodes { stack = stack[:0] for _, w := range nodes { p[w.ID()] = p[w.ID()][:0] } for _, t := range nodes { sigma[t.ID()] = 0 d[t.ID()] = -1 } sigma[s.ID()] = 1 d[s.ID()] = 0 queue.Enqueue(s) for queue.Len() != 0 { v := queue.Dequeue() stack.Push(v) for _, w := range g.From(v) { // w found for the first time? if d[w.ID()] < 0 { queue.Enqueue(w) d[w.ID()] = d[v.ID()] + 1 } // shortest path to w via v? if d[w.ID()] == d[v.ID()]+1 { sigma[w.ID()] += sigma[v.ID()] p[w.ID()] = append(p[w.ID()], v) } } } for _, v := range nodes { delta[v.ID()] = 0 } // S returns vertices in order of non-increasing distance from s accumulate(s, stack, p, delta, sigma) } }
// FloydWarshall returns a shortest-path tree for the graph g or false indicating // that a negative cycle exists in the graph. If the graph does not implement // graph.Weighter, UniformCost is used. // // The time complexity of FloydWarshall is O(|V|^3). func FloydWarshall(g graph.Graph) (paths AllShortest, ok bool) { var weight Weighting if wg, ok := g.(graph.Weighter); ok { weight = wg.Weight } else { weight = UniformCost(g) } nodes := g.Nodes() paths = newAllShortest(nodes, true) for i, u := range nodes { paths.dist.Set(i, i, 0) for _, v := range g.From(u) { j := paths.indexOf[v.ID()] w, ok := weight(u, v) if !ok { panic("floyd-warshall: unexpected invalid weight") } paths.set(i, j, w, j) } } for k := range nodes { for i := range nodes { for j := range nodes { ij := paths.dist.At(i, j) joint := paths.dist.At(i, k) + paths.dist.At(k, j) if ij > joint { paths.set(i, j, joint, paths.at(i, k)...) } else if ij-joint == 0 { paths.add(i, j, paths.at(i, k)...) } } } } ok = true for i := range nodes { if paths.dist.At(i, i) < 0 { ok = false break } } return paths, ok }
// PostDominatores returns all post-dominators for all nodes in g. It does not // prune for strict post-dominators, immediate post-dominators etc. // // A post-dominates B if and only if all paths from B travel through A. func PostDominators(end graph.Node, g graph.Graph) map[int]set.Nodes { allNodes := make(set.Nodes) nlist := g.Nodes() dominators := make(map[int]set.Nodes, len(nlist)) for _, node := range nlist { allNodes.Add(node) } for _, node := range nlist { dominators[node.ID()] = make(set.Nodes) if node.ID() == end.ID() { dominators[node.ID()].Add(end) } else { dominators[node.ID()].Copy(allNodes) } } for somethingChanged := true; somethingChanged; { somethingChanged = false for _, node := range nlist { if node.ID() == end.ID() { continue } succs := g.From(node) if len(succs) == 0 { continue } tmp := make(set.Nodes).Copy(dominators[succs[0].ID()]) for _, succ := range succs[1:] { tmp.Intersect(tmp, dominators[succ.ID()]) } dom := make(set.Nodes) dom.Add(node) dom.Union(dom, tmp) if !set.Equal(dom, dominators[node.ID()]) { dominators[node.ID()] = dom somethingChanged = true } } } return dominators }
// DijkstraFrom returns a shortest-path tree for a shortest path from u to all nodes in // the graph g. If the graph does not implement graph.Weighter, UniformCost is used. // DijkstraFrom will panic if g has a u-reachable negative edge weight. // // The time complexity of DijkstrFrom is O(|E|+|V|.log|V|). func DijkstraFrom(u graph.Node, g graph.Graph) Shortest { if !g.Has(u) { return Shortest{from: u} } var weight Weighting if wg, ok := g.(graph.Weighter); ok { weight = wg.Weight } else { weight = UniformCost(g) } nodes := g.Nodes() path := newShortestFrom(u, nodes) // Dijkstra's algorithm here is implemented essentially as // described in Function B.2 in figure 6 of UTCS Technical // Report TR-07-54. // // http://www.cs.utexas.edu/ftp/techreports/tr07-54.pdf Q := priorityQueue{{node: u, dist: 0}} for Q.Len() != 0 { mid := heap.Pop(&Q).(distanceNode) k := path.indexOf[mid.node.ID()] if mid.dist < path.dist[k] { path.dist[k] = mid.dist } for _, v := range g.From(mid.node) { j := path.indexOf[v.ID()] w, ok := weight(mid.node, v) if !ok { panic("dijkstra: unexpected invalid weight") } if w < 0 { panic("dijkstra: negative edge weight") } joint := path.dist[k] + w if joint < path.dist[j] { heap.Push(&Q, distanceNode{node: v, dist: joint}) path.set(j, joint, k) } } } return path }
// Farness returns the farness for nodes in the graph g used to construct // the given shortest paths. // // F(v) = \sum_u d(u,v) // // For directed graphs the incoming paths are used. Infinite distances are // not considered. func Farness(g graph.Graph, p path.AllShortest) map[int]float64 { nodes := g.Nodes() f := make(map[int]float64, len(nodes)) for _, u := range nodes { var sum float64 for _, v := range nodes { // The ordering here is not relevant for // undirected graphs, but we make sure we // are counting incoming paths. d := p.Weight(v, u) if math.IsInf(d, 0) { continue } sum += d } f[u.ID()] = sum } return f }
// FloydWarshall returns a shortest-path tree for the graph g or false indicating // that a negative cycle exists in the graph. If the graph does not implement // graph.Weighter, graph.UniformCost is used. // // The time complexity of FloydWarshall is O(|V|^3). func FloydWarshall(g graph.Graph) (paths AllShortest, ok bool) { var weight graph.WeightFunc if g, ok := g.(graph.Weighter); ok { weight = g.Weight } else { weight = graph.UniformCost } nodes := g.Nodes() paths = newAllShortest(nodes, true) for i, u := range nodes { paths.dist.Set(i, i, 0) for _, v := range g.From(u) { j := paths.indexOf[v.ID()] paths.set(i, j, weight(g.Edge(u, v)), j) } } for k := range nodes { for i := range nodes { for j := range nodes { ij := paths.dist.At(i, j) joint := paths.dist.At(i, k) + paths.dist.At(k, j) if ij > joint { paths.set(i, j, joint, paths.at(i, k)...) } else if ij-joint == 0 { paths.add(i, j, paths.at(i, k)...) } } } } ok = true for i := range nodes { if paths.dist.At(i, i) < 0 { ok = false break } } return paths, ok }
// Harmonic returns the harmonic centrality for nodes in the graph g used to // construct the given shortest paths. // // H(v)= \sum_{u ≠ v} 1 / d(u,v) // // For directed graphs the incoming paths are used. Infinite distances are // not considered. func Harmonic(g graph.Graph, p path.AllShortest) map[int]float64 { nodes := g.Nodes() h := make(map[int]float64, len(nodes)) for i, u := range nodes { var sum float64 for j, v := range nodes { // The ordering here is not relevant for // undirected graphs, but we make sure we // are counting incoming paths. d := p.Weight(v, u) if math.IsInf(d, 0) { continue } if i != j { sum += 1 / d } } h[u.ID()] = sum } return h }
func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool) error { nodes := g.Nodes() sort.Sort(ordered.ByID(nodes)) p.buf.WriteString(p.prefix) if needsIndent { for i := 0; i < p.depth; i++ { p.buf.WriteString(p.indent) } } _, isDirected := g.(graph.Directed) if isSubgraph { p.buf.WriteString("sub") } else if isDirected { p.buf.WriteString("di") } p.buf.WriteString("graph") if name == "" { if g, ok := g.(Graph); ok { name = g.DOTID() } } if name != "" { p.buf.WriteByte(' ') p.buf.WriteString(name) } p.openBlock(" {") if a, ok := g.(Attributers); ok { p.writeAttributeComplex(a) } if s, ok := g.(Structurer); ok { for _, g := range s.Structure() { _, subIsDirected := g.(graph.Directed) if subIsDirected != isDirected { return errors.New("dot: mismatched graph type") } p.buf.WriteByte('\n') p.print(g, g.DOTID(), true, true) } } havePrintedNodeHeader := false for _, n := range nodes { if s, ok := n.(Subgrapher); ok { // If the node is not linked to any other node // the graph needs to be written now. if len(g.From(n)) == 0 { g := s.Subgraph() _, subIsDirected := g.(graph.Directed) if subIsDirected != isDirected { return errors.New("dot: mismatched graph type") } if !havePrintedNodeHeader { p.newline() p.buf.WriteString("// Node definitions.") havePrintedNodeHeader = true } p.newline() p.print(g, graphID(g, n), false, true) } continue } if !havePrintedNodeHeader { p.newline() p.buf.WriteString("// Node definitions.") havePrintedNodeHeader = true } p.newline() p.writeNode(n) if a, ok := n.(Attributer); ok { p.writeAttributeList(a) } p.buf.WriteByte(';') } havePrintedEdgeHeader := false for _, n := range nodes { to := g.From(n) sort.Sort(ordered.ByID(to)) for _, t := range to { if isDirected { if p.visited[edge{inGraph: name, from: n.ID(), to: t.ID()}] { continue } p.visited[edge{inGraph: name, from: n.ID(), to: t.ID()}] = true } else { if p.visited[edge{inGraph: name, from: n.ID(), to: t.ID()}] { continue } p.visited[edge{inGraph: name, from: n.ID(), to: t.ID()}] = true p.visited[edge{inGraph: name, from: t.ID(), to: n.ID()}] = true } if !havePrintedEdgeHeader { p.buf.WriteByte('\n') p.buf.WriteString(strings.TrimRight(p.prefix, " \t\xa0")) // Trim whitespace suffix. p.newline() p.buf.WriteString("// Edge definitions.") havePrintedEdgeHeader = true } p.newline() if s, ok := n.(Subgrapher); ok { g := s.Subgraph() _, subIsDirected := g.(graph.Directed) if subIsDirected != isDirected { return errors.New("dot: mismatched graph type") } p.print(g, graphID(g, n), false, true) } else { p.writeNode(n) } e, edgeIsPorter := g.Edge(n, t).(Porter) if edgeIsPorter { p.writePorts(e.FromPort()) } if isDirected { p.buf.WriteString(" -> ") } else { p.buf.WriteString(" -- ") } if s, ok := t.(Subgrapher); ok { g := s.Subgraph() _, subIsDirected := g.(graph.Directed) if subIsDirected != isDirected { return errors.New("dot: mismatched graph type") } p.print(g, graphID(g, t), false, true) } else { p.writeNode(t) } if edgeIsPorter { p.writePorts(e.ToPort()) } if a, ok := g.Edge(n, t).(Attributer); ok { p.writeAttributeList(a) } p.buf.WriteByte(';') } } p.closeBlock("}") return nil }
// NewDStarLite returns a new DStarLite planner for the path from s to t in g using the // heuristic h. The world model, m, is used to store shortest path information during path // planning. The world model must be an empty graph when NewDStarLite is called. // // If h is nil, the DStarLite will use the g.HeuristicCost method if g implements // path.HeuristicCoster, falling back to path.NullHeuristic otherwise. If the graph does not // implement graph.Weighter, path.UniformCost is used. NewDStarLite will panic if g has // a negative edge weight. func NewDStarLite(s, t graph.Node, g graph.Graph, h path.Heuristic, m WorldModel) *DStarLite { /* procedure Initialize() {02”} U = ∅; {03”} k_m = 0; {04”} for all s ∈ S rhs(s) = g(s) = ∞; {05”} rhs(s_goal) = 0; {06”} U.Insert(s_goal, [h(s_start, s_goal); 0]); */ d := &DStarLite{ s: newDStarLiteNode(s), t: newDStarLiteNode(t), // badKey is overwritten below. model: m, heuristic: h, } d.t.rhs = 0 /* procedure Main() {29”} s_last = s_start; {30”} Initialize(); */ d.last = d.s if wg, ok := g.(graph.Weighter); ok { d.weight = wg.Weight } else { d.weight = path.UniformCost(g) } if d.heuristic == nil { if g, ok := g.(path.HeuristicCoster); ok { d.heuristic = g.HeuristicCost } else { d.heuristic = path.NullHeuristic } } d.queue.insert(d.t, key{d.heuristic(s, t), 0}) for _, n := range g.Nodes() { switch n.ID() { case d.s.ID(): d.model.AddNode(d.s) case d.t.ID(): d.model.AddNode(d.t) default: d.model.AddNode(newDStarLiteNode(n)) } } for _, u := range d.model.Nodes() { for _, v := range g.From(u) { w := edgeWeight(d.weight, u, v) if w < 0 { panic("D* Lite: negative edge weight") } d.model.SetEdge(concrete.Edge{F: u, T: d.model.Node(v.ID()), W: w}) } } /* procedure Main() {31”} ComputeShortestPath(); */ d.findShortestPath() return d }
// DijkstraAllPaths returns a shortest-path tree for shortest paths in the graph g. // If the graph does not implement graph.Weighter, UniformCost is used. // DijkstraAllPaths will panic if g has a negative edge weight. // // The time complexity of DijkstrAllPaths is O(|V|.|E|+|V|^2.log|V|). func DijkstraAllPaths(g graph.Graph) (paths AllShortest) { paths = newAllShortest(g.Nodes(), false) dijkstraAllPaths(g, paths) return paths }
// Betweenness returns the non-zero betweenness centrality for nodes in the unweighted graph g. // // C_B(v) = \sum_{s ≠ v ≠ t ∈ V} (\sigma_{st}(v) / \sigma_{st}) // // where \sigma_{st} and \sigma_{st}(v) are the number of shortest paths from s to t, // and the subset of those paths containing v respectively. func Betweenness(g graph.Graph) map[int]float64 { // Brandes' algorithm for finding betweenness centrality for nodes in // and unweighted graph: // // http://www.inf.uni-konstanz.de/algo/publications/b-fabc-01.pdf // TODO(kortschak): Consider using the parallel algorithm when // GOMAXPROCS != 1. // // http://htor.inf.ethz.ch/publications/img/edmonds-hoefler-lumsdaine-bc.pdf // Also note special case for sparse networks: // http://wwwold.iit.cnr.it/staff/marco.pellegrini/papiri/asonam-final.pdf var ( cb = make(map[int]float64) nodes = g.Nodes() stack internal.NodeStack p = make(map[int][]graph.Node, len(nodes)) sigma = make(map[int]float64, len(nodes)) d = make(map[int]int, len(nodes)) delta = make(map[int]float64, len(nodes)) queue internal.NodeQueue ) for _, s := range nodes { stack = stack[:0] for _, w := range nodes { p[w.ID()] = p[w.ID()][:0] } for _, t := range nodes { sigma[t.ID()] = 0 d[t.ID()] = -1 } sigma[s.ID()] = 1 d[s.ID()] = 0 queue.Enqueue(s) for queue.Len() != 0 { v := queue.Dequeue() stack.Push(v) for _, w := range g.From(v) { // w found for the first time? if d[w.ID()] < 0 { queue.Enqueue(w) d[w.ID()] = d[v.ID()] + 1 } // shortest path to w via v? if d[w.ID()] == d[v.ID()]+1 { sigma[w.ID()] += sigma[v.ID()] p[w.ID()] = append(p[w.ID()], v) } } } for _, v := range nodes { delta[v.ID()] = 0 } // S returns vertices in order of non-increasing distance from s for stack.Len() != 0 { w := stack.Pop() for _, v := range p[w.ID()] { delta[v.ID()] += sigma[v.ID()] / sigma[w.ID()] * (1 + delta[w.ID()]) } if w.ID() != s.ID() { if d := delta[w.ID()]; d != 0 { cb[w.ID()] += d } } } } return cb }
// BellmanFordFrom returns a shortest-path tree for a shortest path from u to all nodes in // the graph g, or false indicating that a negative cycle exists in the graph. If the graph // does not implement graph.Weighter, UniformCost is used. // // The time complexity of BellmanFordFrom is O(|V|.|E|). func BellmanFordFrom(u graph.Node, g graph.Graph) (path Shortest, ok bool) { if !g.Has(u) { return Shortest{from: u}, true } var weight Weighting if wg, ok := g.(graph.Weighter); ok { weight = wg.Weight } else { weight = UniformCost(g) } nodes := g.Nodes() path = newShortestFrom(u, nodes) path.dist[path.indexOf[u.ID()]] = 0 // TODO(kortschak): Consider adding further optimisations // from http://arxiv.org/abs/1111.5414. for i := 1; i < len(nodes); i++ { changed := false for j, u := range nodes { for _, v := range g.From(u) { k := path.indexOf[v.ID()] w, ok := weight(u, v) if !ok { panic("bellman-ford: unexpected invalid weight") } joint := path.dist[j] + w if joint < path.dist[k] { path.set(k, joint, j) changed = true } } } if !changed { break } } for j, u := range nodes { for _, v := range g.From(u) { k := path.indexOf[v.ID()] w, ok := weight(u, v) if !ok { panic("bellman-ford: unexpected invalid weight") } if path.dist[j]+w < path.dist[k] { return path, false } } } return path, true }