gonum.org/v1/gonum@v0.14.0/graph/network/distance_test.go (about)

     1  // Copyright ©2015 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package network
     6  
     7  import (
     8  	"math"
     9  	"testing"
    10  
    11  	"gonum.org/v1/gonum/floats/scalar"
    12  	"gonum.org/v1/gonum/graph/path"
    13  	"gonum.org/v1/gonum/graph/simple"
    14  )
    15  
    16  var undirectedCentralityTests = []struct {
    17  	g []set
    18  
    19  	farness  map[int64]float64
    20  	harmonic map[int64]float64
    21  	residual map[int64]float64
    22  }{
    23  	{
    24  		g: []set{
    25  			A: linksTo(B),
    26  			B: linksTo(C),
    27  			C: nil,
    28  		},
    29  
    30  		farness: map[int64]float64{
    31  			A: 1 + 2,
    32  			B: 1 + 1,
    33  			C: 2 + 1,
    34  		},
    35  		harmonic: map[int64]float64{
    36  			A: 1 + 1.0/2.0,
    37  			B: 1 + 1,
    38  			C: 1.0/2.0 + 1,
    39  		},
    40  		residual: map[int64]float64{
    41  			A: 1/math.Exp2(1) + 1/math.Exp2(2),
    42  			B: 1/math.Exp2(1) + 1/math.Exp2(1),
    43  			C: 1/math.Exp2(2) + 1/math.Exp2(1),
    44  		},
    45  	},
    46  	{
    47  		g: []set{
    48  			A: linksTo(B),
    49  			B: linksTo(C),
    50  			C: linksTo(D),
    51  			D: linksTo(E),
    52  			E: nil,
    53  		},
    54  
    55  		farness: map[int64]float64{
    56  			A: 1 + 2 + 3 + 4,
    57  			B: 1 + 1 + 2 + 3,
    58  			C: 2 + 1 + 1 + 2,
    59  			D: 3 + 2 + 1 + 1,
    60  			E: 4 + 3 + 2 + 1,
    61  		},
    62  		harmonic: map[int64]float64{
    63  			A: 1 + 1.0/2.0 + 1.0/3.0 + 1.0/4.0,
    64  			B: 1 + 1 + 1.0/2.0 + 1.0/3.0,
    65  			C: 1.0/2.0 + 1 + 1 + 1.0/2.0,
    66  			D: 1.0/3.0 + 1.0/2.0 + 1 + 1,
    67  			E: 1.0/4.0 + 1.0/3.0 + 1.0/2.0 + 1,
    68  		},
    69  		residual: map[int64]float64{
    70  			A: 1/math.Exp2(1) + 1/math.Exp2(2) + 1/math.Exp2(3) + 1/math.Exp2(4),
    71  			B: 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(2) + 1/math.Exp2(3),
    72  			C: 1/math.Exp2(2) + 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(2),
    73  			D: 1/math.Exp2(3) + 1/math.Exp2(2) + 1/math.Exp2(1) + 1/math.Exp2(1),
    74  			E: 1/math.Exp2(4) + 1/math.Exp2(3) + 1/math.Exp2(2) + 1/math.Exp2(1),
    75  		},
    76  	},
    77  	{
    78  		g: []set{
    79  			A: linksTo(C),
    80  			B: linksTo(C),
    81  			C: nil,
    82  			D: linksTo(C),
    83  			E: linksTo(C),
    84  		},
    85  
    86  		farness: map[int64]float64{
    87  			A: 2 + 2 + 1 + 2,
    88  			B: 2 + 1 + 2 + 2,
    89  			C: 1 + 1 + 1 + 1,
    90  			D: 2 + 1 + 2 + 2,
    91  			E: 2 + 2 + 1 + 2,
    92  		},
    93  		harmonic: map[int64]float64{
    94  			A: 1.0/2.0 + 1.0/2.0 + 1 + 1.0/2.0,
    95  			B: 1.0/2.0 + 1 + 1.0/2.0 + 1.0/2.0,
    96  			C: 1 + 1 + 1 + 1,
    97  			D: 1.0/2.0 + 1 + 1.0/2.0 + 1.0/2.0,
    98  			E: 1.0/2.0 + 1.0/2.0 + 1 + 1.0/2.0,
    99  		},
   100  		residual: map[int64]float64{
   101  			A: 1/math.Exp2(2) + 1/math.Exp2(2) + 1/math.Exp2(1) + 1/math.Exp2(2),
   102  			B: 1/math.Exp2(2) + 1/math.Exp2(1) + 1/math.Exp2(2) + 1/math.Exp2(2),
   103  			C: 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1),
   104  			D: 1/math.Exp2(2) + 1/math.Exp2(1) + 1/math.Exp2(2) + 1/math.Exp2(2),
   105  			E: 1/math.Exp2(2) + 1/math.Exp2(2) + 1/math.Exp2(1) + 1/math.Exp2(2),
   106  		},
   107  	},
   108  	{
   109  		g: []set{
   110  			A: linksTo(B, C, D, E),
   111  			B: linksTo(C, D, E),
   112  			C: linksTo(D, E),
   113  			D: linksTo(E),
   114  			E: nil,
   115  		},
   116  
   117  		farness: map[int64]float64{
   118  			A: 1 + 1 + 1 + 1,
   119  			B: 1 + 1 + 1 + 1,
   120  			C: 1 + 1 + 1 + 1,
   121  			D: 1 + 1 + 1 + 1,
   122  			E: 1 + 1 + 1 + 1,
   123  		},
   124  		harmonic: map[int64]float64{
   125  			A: 1 + 1 + 1 + 1,
   126  			B: 1 + 1 + 1 + 1,
   127  			C: 1 + 1 + 1 + 1,
   128  			D: 1 + 1 + 1 + 1,
   129  			E: 1 + 1 + 1 + 1,
   130  		},
   131  		residual: map[int64]float64{
   132  			A: 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1),
   133  			B: 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1),
   134  			C: 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1),
   135  			D: 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1),
   136  			E: 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1),
   137  		},
   138  	},
   139  }
   140  
   141  func TestDistanceCentralityUndirected(t *testing.T) {
   142  	const tol = 1e-12
   143  	prec := 1 - int(math.Log10(tol))
   144  
   145  	for i, test := range undirectedCentralityTests {
   146  		g := simple.NewWeightedUndirectedGraph(0, math.Inf(1))
   147  		for u, e := range test.g {
   148  			// Add nodes that are not defined by an edge.
   149  			if g.Node(int64(u)) == nil {
   150  				g.AddNode(simple.Node(u))
   151  			}
   152  			for v := range e {
   153  				g.SetWeightedEdge(simple.WeightedEdge{F: simple.Node(u), T: simple.Node(v), W: 1})
   154  			}
   155  		}
   156  		p, ok := path.FloydWarshall(g)
   157  		if !ok {
   158  			t.Errorf("unexpected negative cycle in test %d", i)
   159  			continue
   160  		}
   161  
   162  		var got map[int64]float64
   163  
   164  		got = Closeness(g, p)
   165  		for n := range test.g {
   166  			if !scalar.EqualWithinAbsOrRel(got[int64(n)], 1/test.farness[int64(n)], tol, tol) {
   167  				want := make(map[int64]float64)
   168  				for n, v := range test.farness {
   169  					want[n] = 1 / v
   170  				}
   171  				t.Errorf("unexpected closeness centrality for test %d:\ngot: %v\nwant:%v",
   172  					i, orderedFloats(got, prec), orderedFloats(want, prec))
   173  				break
   174  			}
   175  		}
   176  
   177  		got = Farness(g, p)
   178  		for n := range test.g {
   179  			if !scalar.EqualWithinAbsOrRel(got[int64(n)], test.farness[int64(n)], tol, tol) {
   180  				t.Errorf("unexpected farness for test %d:\ngot: %v\nwant:%v",
   181  					i, orderedFloats(got, prec), orderedFloats(test.farness, prec))
   182  				break
   183  			}
   184  		}
   185  
   186  		got = Harmonic(g, p)
   187  		for n := range test.g {
   188  			if !scalar.EqualWithinAbsOrRel(got[int64(n)], test.harmonic[int64(n)], tol, tol) {
   189  				t.Errorf("unexpected harmonic centrality for test %d:\ngot: %v\nwant:%v",
   190  					i, orderedFloats(got, prec), orderedFloats(test.harmonic, prec))
   191  				break
   192  			}
   193  		}
   194  
   195  		got = Residual(g, p)
   196  		for n := range test.g {
   197  			if !scalar.EqualWithinAbsOrRel(got[int64(n)], test.residual[int64(n)], tol, tol) {
   198  				t.Errorf("unexpected residual closeness for test %d:\ngot: %v\nwant:%v",
   199  					i, orderedFloats(got, prec), orderedFloats(test.residual, prec))
   200  				break
   201  			}
   202  		}
   203  	}
   204  }
   205  
   206  var directedCentralityTests = []struct {
   207  	g []set
   208  
   209  	farness  map[int64]float64
   210  	harmonic map[int64]float64
   211  	residual map[int64]float64
   212  }{
   213  	{
   214  		g: []set{
   215  			A: linksTo(B),
   216  			B: linksTo(C),
   217  			C: nil,
   218  		},
   219  
   220  		farness: map[int64]float64{
   221  			A: 0,
   222  			B: 1,
   223  			C: 2 + 1,
   224  		},
   225  		harmonic: map[int64]float64{
   226  			A: 0,
   227  			B: 1,
   228  			C: 1.0/2.0 + 1,
   229  		},
   230  		residual: map[int64]float64{
   231  			A: 0,
   232  			B: 1 / math.Exp2(1),
   233  			C: 1/math.Exp2(2) + 1/math.Exp2(1),
   234  		},
   235  	},
   236  	{
   237  		g: []set{
   238  			A: linksTo(B),
   239  			B: linksTo(C),
   240  			C: linksTo(D),
   241  			D: linksTo(E),
   242  			E: nil,
   243  		},
   244  
   245  		farness: map[int64]float64{
   246  			A: 0,
   247  			B: 1,
   248  			C: 2 + 1,
   249  			D: 3 + 2 + 1,
   250  			E: 4 + 3 + 2 + 1,
   251  		},
   252  		harmonic: map[int64]float64{
   253  			A: 0,
   254  			B: 1,
   255  			C: 1.0/2.0 + 1,
   256  			D: 1.0/3.0 + 1.0/2.0 + 1,
   257  			E: 1.0/4.0 + 1.0/3.0 + 1.0/2.0 + 1,
   258  		},
   259  		residual: map[int64]float64{
   260  			A: 0,
   261  			B: 1 / math.Exp2(1),
   262  			C: 1/math.Exp2(2) + 1/math.Exp2(1),
   263  			D: 1/math.Exp2(3) + 1/math.Exp2(2) + 1/math.Exp2(1),
   264  			E: 1/math.Exp2(4) + 1/math.Exp2(3) + 1/math.Exp2(2) + 1/math.Exp2(1),
   265  		},
   266  	},
   267  	{
   268  		g: []set{
   269  			A: linksTo(C),
   270  			B: linksTo(C),
   271  			C: nil,
   272  			D: linksTo(C),
   273  			E: linksTo(C),
   274  		},
   275  
   276  		farness: map[int64]float64{
   277  			A: 0,
   278  			B: 0,
   279  			C: 1 + 1 + 1 + 1,
   280  			D: 0,
   281  			E: 0,
   282  		},
   283  		harmonic: map[int64]float64{
   284  			A: 0,
   285  			B: 0,
   286  			C: 1 + 1 + 1 + 1,
   287  			D: 0,
   288  			E: 0,
   289  		},
   290  		residual: map[int64]float64{
   291  			A: 0,
   292  			B: 0,
   293  			C: 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1),
   294  			D: 0,
   295  			E: 0,
   296  		},
   297  	},
   298  	{
   299  		g: []set{
   300  			A: linksTo(B, C, D, E),
   301  			B: linksTo(C, D, E),
   302  			C: linksTo(D, E),
   303  			D: linksTo(E),
   304  			E: nil,
   305  		},
   306  
   307  		farness: map[int64]float64{
   308  			A: 0,
   309  			B: 1,
   310  			C: 1 + 1,
   311  			D: 1 + 1 + 1,
   312  			E: 1 + 1 + 1 + 1,
   313  		},
   314  		harmonic: map[int64]float64{
   315  			A: 0,
   316  			B: 1,
   317  			C: 1 + 1,
   318  			D: 1 + 1 + 1,
   319  			E: 1 + 1 + 1 + 1,
   320  		},
   321  		residual: map[int64]float64{
   322  			A: 0,
   323  			B: 1 / math.Exp2(1),
   324  			C: 1/math.Exp2(1) + 1/math.Exp2(1),
   325  			D: 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1),
   326  			E: 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1) + 1/math.Exp2(1),
   327  		},
   328  	},
   329  }
   330  
   331  func TestDistanceCentralityDirected(t *testing.T) {
   332  	const tol = 1e-12
   333  	prec := 1 - int(math.Log10(tol))
   334  
   335  	for i, test := range directedCentralityTests {
   336  		g := simple.NewWeightedDirectedGraph(0, math.Inf(1))
   337  		for u, e := range test.g {
   338  			// Add nodes that are not defined by an edge.
   339  			if g.Node(int64(u)) == nil {
   340  				g.AddNode(simple.Node(u))
   341  			}
   342  			for v := range e {
   343  				g.SetWeightedEdge(simple.WeightedEdge{F: simple.Node(u), T: simple.Node(v), W: 1})
   344  			}
   345  		}
   346  		p, ok := path.FloydWarshall(g)
   347  		if !ok {
   348  			t.Errorf("unexpected negative cycle in test %d", i)
   349  			continue
   350  		}
   351  
   352  		var got map[int64]float64
   353  
   354  		got = Closeness(g, p)
   355  		for n := range test.g {
   356  			if !scalar.EqualWithinAbsOrRel(got[int64(n)], 1/test.farness[int64(n)], tol, tol) {
   357  				want := make(map[int64]float64)
   358  				for n, v := range test.farness {
   359  					want[n] = 1 / v
   360  				}
   361  				t.Errorf("unexpected closeness centrality for test %d:\ngot: %v\nwant:%v",
   362  					i, orderedFloats(got, prec), orderedFloats(want, prec))
   363  				break
   364  			}
   365  		}
   366  
   367  		got = Farness(g, p)
   368  		for n := range test.g {
   369  			if !scalar.EqualWithinAbsOrRel(got[int64(n)], test.farness[int64(n)], tol, tol) {
   370  				t.Errorf("unexpected farness for test %d:\ngot: %v\nwant:%v",
   371  					i, orderedFloats(got, prec), orderedFloats(test.farness, prec))
   372  				break
   373  			}
   374  		}
   375  
   376  		got = Harmonic(g, p)
   377  		for n := range test.g {
   378  			if !scalar.EqualWithinAbsOrRel(got[int64(n)], test.harmonic[int64(n)], tol, tol) {
   379  				t.Errorf("unexpected harmonic centrality for test %d:\ngot: %v\nwant:%v",
   380  					i, orderedFloats(got, prec), orderedFloats(test.harmonic, prec))
   381  				break
   382  			}
   383  		}
   384  
   385  		got = Residual(g, p)
   386  		for n := range test.g {
   387  			if !scalar.EqualWithinAbsOrRel(got[int64(n)], test.residual[int64(n)], tol, tol) {
   388  				t.Errorf("unexpected residual closeness for test %d:\ngot: %v\nwant:%v",
   389  					i, orderedFloats(got, prec), orderedFloats(test.residual, prec))
   390  				break
   391  			}
   392  		}
   393  	}
   394  }