mirror of
https://github.com/gonum/gonum.git
synced 2025-10-06 07:37:03 +08:00

Ruleguard run with ruleguard -c=0 -rules ruleguard.rules.go ./... from https://github.com/dgryski/semgrep-go with the following rules inactivated to reduce noise: - unconvert for floating point values - oddcomparisons - floateq
395 lines
9.1 KiB
Go
395 lines
9.1 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/scalar"
|
|
"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.Node(int64(u)) == nil {
|
|
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 !scalar.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 !scalar.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 !scalar.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 !scalar.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.Node(int64(u)) == nil {
|
|
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 !scalar.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 !scalar.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 !scalar.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 !scalar.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
|
|
}
|
|
}
|
|
}
|
|
}
|