// EdgeBetweennessWeighted returns the non-zero betweenness centrality for edges in // the weighted graph g. For an edge e the centrality C_B is computed as // // C_B(e) = \sum_{s ≠ t ∈ V} (\sigma_{st}(e) / \sigma_{st}), // // where \sigma_{st} and \sigma_{st}(e) are the number of shortest paths from s // to t, and the subset of those paths containing e, respectively. // // If g is undirected, edges are retained such that u.ID < v.ID where u and v are // the nodes of e. func EdgeBetweennessWeighted(g WeightedGraph, p path.AllShortest) map[[2]int]float64 { cb := make(map[[2]int]float64) _, isUndirected := g.(graph.Undirected) nodes := g.Nodes() for i, s := range nodes { for j, t := range nodes { if i == j { continue } d := p.Weight(s, t) if math.IsInf(d, 0) { continue } // If we have a unique path, don't do the // extra work needed to get all paths. path, _, unique := p.Between(s, t) if unique { for k, v := range path[1:] { // For undirected graphs we double count // passage though edges. This is consistent // with Brandes' algorithm's behaviour. uid := path[k].ID() vid := v.ID() if isUndirected && vid < uid { uid, vid = vid, uid } cb[[2]int{uid, vid}]++ } continue } // Otherwise iterate over all paths. paths, _ := p.AllBetween(s, t) stFrac := 1 / float64(len(paths)) for _, path := range paths { for k, v := range path[1:] { uid := path[k].ID() vid := v.ID() if isUndirected && vid < uid { uid, vid = vid, uid } cb[[2]int{uid, vid}] += stFrac } } } } return cb }
// BetweennessWeighted returns the non-zero betweenness centrality for nodes in the weighted // graph g used to construct the given shortest paths. // // 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 BetweennessWeighted(g WeightedGraph, p path.AllShortest) map[int]float64 { cb := make(map[int]float64) nodes := g.Nodes() for i, s := range nodes { for j, t := range nodes { if i == j { continue } d := p.Weight(s, t) if math.IsInf(d, 0) { continue } sID := s.ID() tID := t.ID() // If we have a unique path, don't do the // extra work needed to get all paths. path, _, unique := p.Between(s, t) if unique { for _, v := range path { if vID := v.ID(); vID == sID || vID == tID { continue } // For undirected graphs we double count // passage though nodes. This is consistent // with Brandes' algorithm's behaviour. cb[v.ID()]++ } continue } // Otherwise iterate over all paths. paths, _ := p.AllBetween(s, t) stFrac := 1 / float64(len(paths)) for _, path := range paths { for _, v := range path { if vID := v.ID(); vID == sID || vID == tID { continue } cb[v.ID()] += stFrac } } } } return cb }
// 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 }
// 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 TestDijkstraAllPaths(t *testing.T) { for _, test := range shortestPathTests { g := test.g() for _, e := range test.edges { g.SetEdge(e, e.Cost) } var ( pt path.AllShortest panicked bool ) func() { defer func() { panicked = recover() != nil }() pt = path.DijkstraAllPaths(g.(graph.Graph)) }() if panicked || test.negative { if !test.negative { t.Errorf("%q: unexpected panic", test.name) } if !panicked { t.Errorf("%q: expected panic for negative edge weight", test.name) } continue } // Check all random paths returned are OK. for i := 0; i < 10; i++ { p, weight, unique := pt.Between(test.query.From(), test.query.To()) if weight != test.weight { t.Errorf("%q: unexpected weight from Between: got:%f want:%f", test.name, weight, test.weight) } if weight := pt.Weight(test.query.From(), test.query.To()); weight != test.weight { t.Errorf("%q: unexpected weight from Weight: got:%f want:%f", test.name, weight, test.weight) } if unique != test.unique { t.Errorf("%q: unexpected number of paths: got: unique=%t want: unique=%t", test.name, unique, test.unique) } var got []int for _, n := range p { got = append(got, n.ID()) } ok := len(got) == 0 && len(test.want) == 0 for _, sp := range test.want { if reflect.DeepEqual(got, sp) { ok = true break } } if !ok { t.Errorf("%q: unexpected shortest path:\ngot: %v\nwant from:%v", test.name, p, test.want) } } np, weight, unique := pt.Between(test.none.From(), test.none.To()) if np != nil || !math.IsInf(weight, 1) || unique != false { t.Errorf("%q: unexpected path:\ngot: path=%v weight=%f unique=%t\nwant:path=<nil> weight=+Inf unique=false", test.name, np, weight, unique) } paths, weight := pt.AllBetween(test.query.From(), test.query.To()) if weight != test.weight { t.Errorf("%q: unexpected weight from Between: got:%f want:%f", test.name, weight, test.weight) } var got [][]int if len(paths) != 0 { got = make([][]int, len(paths)) } for i, p := range paths { for _, v := range p { got[i] = append(got[i], v.ID()) } } sort.Sort(internal.BySliceValues(got)) if !reflect.DeepEqual(got, test.want) { t.Errorf("testing %q: unexpected shortest paths:\ngot: %v\nwant:%v", test.name, got, test.want) } nps, weight := pt.AllBetween(test.none.From(), test.none.To()) if nps != nil || !math.IsInf(weight, 1) { t.Errorf("%q: unexpected path:\ngot: paths=%v weight=%f\nwant:path=<nil> weight=+Inf", test.name, nps, weight) } } }