mirror of
				https://github.com/gonum/gonum.git
				synced 2025-10-31 18:42:45 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			395 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			395 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright ©2015 The Gonum Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| package network
 | |
| 
 | |
| import (
 | |
| 	"math"
 | |
| 	"testing"
 | |
| 
 | |
| 	"gonum.org/v1/gonum/floats"
 | |
| 	"gonum.org/v1/gonum/graph/path"
 | |
| 	"gonum.org/v1/gonum/graph/simple"
 | |
| )
 | |
| 
 | |
| var undirectedCentralityTests = []struct {
 | |
| 	g []set
 | |
| 
 | |
| 	farness  map[int64]float64
 | |
| 	harmonic map[int64]float64
 | |
| 	residual map[int64]float64
 | |
| }{
 | |
| 	{
 | |
| 		g: []set{
 | |
| 			A: linksTo(B),
 | |
| 			B: linksTo(C),
 | |
| 			C: nil,
 | |
| 		},
 | |
| 
 | |
| 		farness: map[int64]float64{
 | |
| 			A: 1 + 2,
 | |
| 			B: 1 + 1,
 | |
| 			C: 2 + 1,
 | |
| 		},
 | |
| 		harmonic: map[int64]float64{
 | |
| 			A: 1 + 1.0/2.0,
 | |
| 			B: 1 + 1,
 | |
| 			C: 1.0/2.0 + 1,
 | |
| 		},
 | |
| 		residual: map[int64]float64{
 | |
| 			A: 1/math.Exp2(1) + 1/math.Exp2(2),
 | |
| 			B: 1/math.Exp2(1) + 1/math.Exp2(1),
 | |
| 			C: 1/math.Exp2(2) + 1/math.Exp2(1),
 | |
| 		},
 | |
| 	},
 | |
| 	{
 | |
| 		g: []set{
 | |
| 			A: linksTo(B),
 | |
| 			B: linksTo(C),
 | |
| 			C: linksTo(D),
 | |
| 			D: linksTo(E),
 | |
| 			E: nil,
 | |
| 		},
 | |
| 
 | |
| 		farness: map[int64]float64{
 | |
| 			A: 1 + 2 + 3 + 4,
 | |
| 			B: 1 + 1 + 2 + 3,
 | |
| 			C: 2 + 1 + 1 + 2,
 | |
| 			D: 3 + 2 + 1 + 1,
 | |
| 			E: 4 + 3 + 2 + 1,
 | |
| 		},
 | |
| 		harmonic: map[int64]float64{
 | |
| 			A: 1 + 1.0/2.0 + 1.0/3.0 + 1.0/4.0,
 | |
| 			B: 1 + 1 + 1.0/2.0 + 1.0/3.0,
 | |
| 			C: 1.0/2.0 + 1 + 1 + 1.0/2.0,
 | |
| 			D: 1.0/3.0 + 1.0/2.0 + 1 + 1,
 | |
| 			E: 1.0/4.0 + 1.0/3.0 + 1.0/2.0 + 1,
 | |
| 		},
 | |
| 		residual: map[int64]float64{
 | |
| 			A: 1/math.Exp2(1) + 1/math.Exp2(2) + 1/math.Exp2(3) + 1/math.Exp2(4),
 | |
| 			B: 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(2) + 1/math.Exp2(3),
 | |
| 			C: 1/math.Exp2(2) + 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(2),
 | |
| 			D: 1/math.Exp2(3) + 1/math.Exp2(2) + 1/math.Exp2(1) + 1/math.Exp2(1),
 | |
| 			E: 1/math.Exp2(4) + 1/math.Exp2(3) + 1/math.Exp2(2) + 1/math.Exp2(1),
 | |
| 		},
 | |
| 	},
 | |
| 	{
 | |
| 		g: []set{
 | |
| 			A: linksTo(C),
 | |
| 			B: linksTo(C),
 | |
| 			C: nil,
 | |
| 			D: linksTo(C),
 | |
| 			E: linksTo(C),
 | |
| 		},
 | |
| 
 | |
| 		farness: map[int64]float64{
 | |
| 			A: 2 + 2 + 1 + 2,
 | |
| 			B: 2 + 1 + 2 + 2,
 | |
| 			C: 1 + 1 + 1 + 1,
 | |
| 			D: 2 + 1 + 2 + 2,
 | |
| 			E: 2 + 2 + 1 + 2,
 | |
| 		},
 | |
| 		harmonic: map[int64]float64{
 | |
| 			A: 1.0/2.0 + 1.0/2.0 + 1 + 1.0/2.0,
 | |
| 			B: 1.0/2.0 + 1 + 1.0/2.0 + 1.0/2.0,
 | |
| 			C: 1 + 1 + 1 + 1,
 | |
| 			D: 1.0/2.0 + 1 + 1.0/2.0 + 1.0/2.0,
 | |
| 			E: 1.0/2.0 + 1.0/2.0 + 1 + 1.0/2.0,
 | |
| 		},
 | |
| 		residual: map[int64]float64{
 | |
| 			A: 1/math.Exp2(2) + 1/math.Exp2(2) + 1/math.Exp2(1) + 1/math.Exp2(2),
 | |
| 			B: 1/math.Exp2(2) + 1/math.Exp2(1) + 1/math.Exp2(2) + 1/math.Exp2(2),
 | |
| 			C: 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1),
 | |
| 			D: 1/math.Exp2(2) + 1/math.Exp2(1) + 1/math.Exp2(2) + 1/math.Exp2(2),
 | |
| 			E: 1/math.Exp2(2) + 1/math.Exp2(2) + 1/math.Exp2(1) + 1/math.Exp2(2),
 | |
| 		},
 | |
| 	},
 | |
| 	{
 | |
| 		g: []set{
 | |
| 			A: linksTo(B, C, D, E),
 | |
| 			B: linksTo(C, D, E),
 | |
| 			C: linksTo(D, E),
 | |
| 			D: linksTo(E),
 | |
| 			E: nil,
 | |
| 		},
 | |
| 
 | |
| 		farness: map[int64]float64{
 | |
| 			A: 1 + 1 + 1 + 1,
 | |
| 			B: 1 + 1 + 1 + 1,
 | |
| 			C: 1 + 1 + 1 + 1,
 | |
| 			D: 1 + 1 + 1 + 1,
 | |
| 			E: 1 + 1 + 1 + 1,
 | |
| 		},
 | |
| 		harmonic: map[int64]float64{
 | |
| 			A: 1 + 1 + 1 + 1,
 | |
| 			B: 1 + 1 + 1 + 1,
 | |
| 			C: 1 + 1 + 1 + 1,
 | |
| 			D: 1 + 1 + 1 + 1,
 | |
| 			E: 1 + 1 + 1 + 1,
 | |
| 		},
 | |
| 		residual: map[int64]float64{
 | |
| 			A: 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1),
 | |
| 			B: 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1),
 | |
| 			C: 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1),
 | |
| 			D: 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1),
 | |
| 			E: 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1),
 | |
| 		},
 | |
| 	},
 | |
| }
 | |
| 
 | |
| func TestDistanceCentralityUndirected(t *testing.T) {
 | |
| 	const tol = 1e-12
 | |
| 	prec := 1 - int(math.Log10(tol))
 | |
| 
 | |
| 	for i, test := range undirectedCentralityTests {
 | |
| 		g := simple.NewWeightedUndirectedGraph(0, math.Inf(1))
 | |
| 		for u, e := range test.g {
 | |
| 			// Add nodes that are not defined by an edge.
 | |
| 			if !g.Has(int64(u)) {
 | |
| 				g.AddNode(simple.Node(u))
 | |
| 			}
 | |
| 			for v := range e {
 | |
| 				g.SetWeightedEdge(simple.WeightedEdge{F: simple.Node(u), T: simple.Node(v), W: 1})
 | |
| 			}
 | |
| 		}
 | |
| 		p, ok := path.FloydWarshall(g)
 | |
| 		if !ok {
 | |
| 			t.Errorf("unexpected negative cycle in test %d", i)
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		var got map[int64]float64
 | |
| 
 | |
| 		got = Closeness(g, p)
 | |
| 		for n := range test.g {
 | |
| 			if !floats.EqualWithinAbsOrRel(got[int64(n)], 1/test.farness[int64(n)], tol, tol) {
 | |
| 				want := make(map[int64]float64)
 | |
| 				for n, v := range test.farness {
 | |
| 					want[n] = 1 / v
 | |
| 				}
 | |
| 				t.Errorf("unexpected closeness centrality for test %d:\ngot: %v\nwant:%v",
 | |
| 					i, orderedFloats(got, prec), orderedFloats(want, prec))
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		got = Farness(g, p)
 | |
| 		for n := range test.g {
 | |
| 			if !floats.EqualWithinAbsOrRel(got[int64(n)], test.farness[int64(n)], tol, tol) {
 | |
| 				t.Errorf("unexpected farness for test %d:\ngot: %v\nwant:%v",
 | |
| 					i, orderedFloats(got, prec), orderedFloats(test.farness, prec))
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		got = Harmonic(g, p)
 | |
| 		for n := range test.g {
 | |
| 			if !floats.EqualWithinAbsOrRel(got[int64(n)], test.harmonic[int64(n)], tol, tol) {
 | |
| 				t.Errorf("unexpected harmonic centrality for test %d:\ngot: %v\nwant:%v",
 | |
| 					i, orderedFloats(got, prec), orderedFloats(test.harmonic, prec))
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		got = Residual(g, p)
 | |
| 		for n := range test.g {
 | |
| 			if !floats.EqualWithinAbsOrRel(got[int64(n)], test.residual[int64(n)], tol, tol) {
 | |
| 				t.Errorf("unexpected residual closeness for test %d:\ngot: %v\nwant:%v",
 | |
| 					i, orderedFloats(got, prec), orderedFloats(test.residual, prec))
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| var directedCentralityTests = []struct {
 | |
| 	g []set
 | |
| 
 | |
| 	farness  map[int64]float64
 | |
| 	harmonic map[int64]float64
 | |
| 	residual map[int64]float64
 | |
| }{
 | |
| 	{
 | |
| 		g: []set{
 | |
| 			A: linksTo(B),
 | |
| 			B: linksTo(C),
 | |
| 			C: nil,
 | |
| 		},
 | |
| 
 | |
| 		farness: map[int64]float64{
 | |
| 			A: 0,
 | |
| 			B: 1,
 | |
| 			C: 2 + 1,
 | |
| 		},
 | |
| 		harmonic: map[int64]float64{
 | |
| 			A: 0,
 | |
| 			B: 1,
 | |
| 			C: 1.0/2.0 + 1,
 | |
| 		},
 | |
| 		residual: map[int64]float64{
 | |
| 			A: 0,
 | |
| 			B: 1 / math.Exp2(1),
 | |
| 			C: 1/math.Exp2(2) + 1/math.Exp2(1),
 | |
| 		},
 | |
| 	},
 | |
| 	{
 | |
| 		g: []set{
 | |
| 			A: linksTo(B),
 | |
| 			B: linksTo(C),
 | |
| 			C: linksTo(D),
 | |
| 			D: linksTo(E),
 | |
| 			E: nil,
 | |
| 		},
 | |
| 
 | |
| 		farness: map[int64]float64{
 | |
| 			A: 0,
 | |
| 			B: 1,
 | |
| 			C: 2 + 1,
 | |
| 			D: 3 + 2 + 1,
 | |
| 			E: 4 + 3 + 2 + 1,
 | |
| 		},
 | |
| 		harmonic: map[int64]float64{
 | |
| 			A: 0,
 | |
| 			B: 1,
 | |
| 			C: 1.0/2.0 + 1,
 | |
| 			D: 1.0/3.0 + 1.0/2.0 + 1,
 | |
| 			E: 1.0/4.0 + 1.0/3.0 + 1.0/2.0 + 1,
 | |
| 		},
 | |
| 		residual: map[int64]float64{
 | |
| 			A: 0,
 | |
| 			B: 1 / math.Exp2(1),
 | |
| 			C: 1/math.Exp2(2) + 1/math.Exp2(1),
 | |
| 			D: 1/math.Exp2(3) + 1/math.Exp2(2) + 1/math.Exp2(1),
 | |
| 			E: 1/math.Exp2(4) + 1/math.Exp2(3) + 1/math.Exp2(2) + 1/math.Exp2(1),
 | |
| 		},
 | |
| 	},
 | |
| 	{
 | |
| 		g: []set{
 | |
| 			A: linksTo(C),
 | |
| 			B: linksTo(C),
 | |
| 			C: nil,
 | |
| 			D: linksTo(C),
 | |
| 			E: linksTo(C),
 | |
| 		},
 | |
| 
 | |
| 		farness: map[int64]float64{
 | |
| 			A: 0,
 | |
| 			B: 0,
 | |
| 			C: 1 + 1 + 1 + 1,
 | |
| 			D: 0,
 | |
| 			E: 0,
 | |
| 		},
 | |
| 		harmonic: map[int64]float64{
 | |
| 			A: 0,
 | |
| 			B: 0,
 | |
| 			C: 1 + 1 + 1 + 1,
 | |
| 			D: 0,
 | |
| 			E: 0,
 | |
| 		},
 | |
| 		residual: map[int64]float64{
 | |
| 			A: 0,
 | |
| 			B: 0,
 | |
| 			C: 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1),
 | |
| 			D: 0,
 | |
| 			E: 0,
 | |
| 		},
 | |
| 	},
 | |
| 	{
 | |
| 		g: []set{
 | |
| 			A: linksTo(B, C, D, E),
 | |
| 			B: linksTo(C, D, E),
 | |
| 			C: linksTo(D, E),
 | |
| 			D: linksTo(E),
 | |
| 			E: nil,
 | |
| 		},
 | |
| 
 | |
| 		farness: map[int64]float64{
 | |
| 			A: 0,
 | |
| 			B: 1,
 | |
| 			C: 1 + 1,
 | |
| 			D: 1 + 1 + 1,
 | |
| 			E: 1 + 1 + 1 + 1,
 | |
| 		},
 | |
| 		harmonic: map[int64]float64{
 | |
| 			A: 0,
 | |
| 			B: 1,
 | |
| 			C: 1 + 1,
 | |
| 			D: 1 + 1 + 1,
 | |
| 			E: 1 + 1 + 1 + 1,
 | |
| 		},
 | |
| 		residual: map[int64]float64{
 | |
| 			A: 0,
 | |
| 			B: 1 / math.Exp2(1),
 | |
| 			C: 1/math.Exp2(1) + 1/math.Exp2(1),
 | |
| 			D: 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1),
 | |
| 			E: 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1),
 | |
| 		},
 | |
| 	},
 | |
| }
 | |
| 
 | |
| func TestDistanceCentralityDirected(t *testing.T) {
 | |
| 	const tol = 1e-12
 | |
| 	prec := 1 - int(math.Log10(tol))
 | |
| 
 | |
| 	for i, test := range directedCentralityTests {
 | |
| 		g := simple.NewWeightedDirectedGraph(0, math.Inf(1))
 | |
| 		for u, e := range test.g {
 | |
| 			// Add nodes that are not defined by an edge.
 | |
| 			if !g.Has(int64(u)) {
 | |
| 				g.AddNode(simple.Node(u))
 | |
| 			}
 | |
| 			for v := range e {
 | |
| 				g.SetWeightedEdge(simple.WeightedEdge{F: simple.Node(u), T: simple.Node(v), W: 1})
 | |
| 			}
 | |
| 		}
 | |
| 		p, ok := path.FloydWarshall(g)
 | |
| 		if !ok {
 | |
| 			t.Errorf("unexpected negative cycle in test %d", i)
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		var got map[int64]float64
 | |
| 
 | |
| 		got = Closeness(g, p)
 | |
| 		for n := range test.g {
 | |
| 			if !floats.EqualWithinAbsOrRel(got[int64(n)], 1/test.farness[int64(n)], tol, tol) {
 | |
| 				want := make(map[int64]float64)
 | |
| 				for n, v := range test.farness {
 | |
| 					want[int64(n)] = 1 / v
 | |
| 				}
 | |
| 				t.Errorf("unexpected closeness centrality for test %d:\ngot: %v\nwant:%v",
 | |
| 					i, orderedFloats(got, prec), orderedFloats(want, prec))
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		got = Farness(g, p)
 | |
| 		for n := range test.g {
 | |
| 			if !floats.EqualWithinAbsOrRel(got[int64(n)], test.farness[int64(n)], tol, tol) {
 | |
| 				t.Errorf("unexpected farness for test %d:\ngot: %v\nwant:%v",
 | |
| 					i, orderedFloats(got, prec), orderedFloats(test.farness, prec))
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		got = Harmonic(g, p)
 | |
| 		for n := range test.g {
 | |
| 			if !floats.EqualWithinAbsOrRel(got[int64(n)], test.harmonic[int64(n)], tol, tol) {
 | |
| 				t.Errorf("unexpected harmonic centrality for test %d:\ngot: %v\nwant:%v",
 | |
| 					i, orderedFloats(got, prec), orderedFloats(test.harmonic, prec))
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		got = Residual(g, p)
 | |
| 		for n := range test.g {
 | |
| 			if !floats.EqualWithinAbsOrRel(got[int64(n)], test.residual[int64(n)], tol, tol) {
 | |
| 				t.Errorf("unexpected residual closeness for test %d:\ngot: %v\nwant:%v",
 | |
| 					i, orderedFloats(got, prec), orderedFloats(test.residual, prec))
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | 
