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 }