gonum.org/v1/gonum@v0.14.0/graph/network/page_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 "fmt" 9 "math" 10 "sort" 11 "testing" 12 13 "gonum.org/v1/gonum/floats/scalar" 14 "gonum.org/v1/gonum/graph/simple" 15 ) 16 17 var pageRankTests = []struct { 18 g []set 19 damp float64 20 tol float64 21 22 wantTol float64 23 want map[int64]float64 24 }{ 25 { 26 // Example graph from http://en.wikipedia.org/wiki/File:PageRanks-Example.svg 16:17, 8 July 2009 27 g: []set{ 28 A: nil, 29 B: linksTo(C), 30 C: linksTo(B), 31 D: linksTo(A, B), 32 E: linksTo(D, B, F), 33 F: linksTo(B, E), 34 G: linksTo(B, E), 35 H: linksTo(B, E), 36 I: linksTo(B, E), 37 J: linksTo(E), 38 K: linksTo(E), 39 }, 40 damp: 0.85, 41 tol: 1e-8, 42 43 wantTol: 1e-8, 44 want: map[int64]float64{ 45 A: 0.03278149, 46 B: 0.38440095, 47 C: 0.34291029, 48 D: 0.03908709, 49 E: 0.08088569, 50 F: 0.03908709, 51 G: 0.01616948, 52 H: 0.01616948, 53 I: 0.01616948, 54 J: 0.01616948, 55 K: 0.01616948, 56 }, 57 }, 58 { 59 // Example graph from http://en.wikipedia.org/w/index.php?title=PageRank&oldid=659286279#Power_Method 60 // Expected result calculated with the given MATLAB code. 61 g: []set{ 62 A: linksTo(B, C), 63 B: linksTo(D), 64 C: linksTo(D, E), 65 D: linksTo(E), 66 E: linksTo(A), 67 }, 68 damp: 0.80, 69 tol: 1e-3, 70 71 wantTol: 1e-3, 72 want: map[int64]float64{ 73 A: 0.250, 74 B: 0.140, 75 C: 0.140, 76 D: 0.208, 77 E: 0.262, 78 }, 79 }, 80 } 81 82 func TestPageRank(t *testing.T) { 83 for i, test := range pageRankTests { 84 g := simple.NewDirectedGraph() 85 for u, e := range test.g { 86 // Add nodes that are not defined by an edge. 87 if g.Node(int64(u)) == nil { 88 g.AddNode(simple.Node(u)) 89 } 90 for v := range e { 91 g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)}) 92 } 93 } 94 got := pageRank(g, test.damp, test.tol) 95 prec := 1 - int(math.Log10(test.wantTol)) 96 for n := range test.g { 97 if !scalar.EqualWithinAbsOrRel(got[int64(n)], test.want[int64(n)], test.wantTol, test.wantTol) { 98 t.Errorf("unexpected PageRank result for test %d:\ngot: %v\nwant:%v", 99 i, orderedFloats(got, prec), orderedFloats(test.want, prec)) 100 break 101 } 102 } 103 } 104 } 105 106 func TestPageRankSparse(t *testing.T) { 107 for i, test := range pageRankTests { 108 g := simple.NewDirectedGraph() 109 for u, e := range test.g { 110 // Add nodes that are not defined by an edge. 111 if g.Node(int64(u)) == nil { 112 g.AddNode(simple.Node(u)) 113 } 114 for v := range e { 115 g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)}) 116 } 117 } 118 got := pageRankSparse(g, test.damp, test.tol) 119 prec := 1 - int(math.Log10(test.wantTol)) 120 for n := range test.g { 121 if !scalar.EqualWithinAbsOrRel(got[int64(n)], test.want[int64(n)], test.wantTol, test.wantTol) { 122 t.Errorf("unexpected PageRank result for test %d:\ngot: %v\nwant:%v", 123 i, orderedFloats(got, prec), orderedFloats(test.want, prec)) 124 break 125 } 126 } 127 } 128 } 129 130 var edgeWeightedPageRankTests = []struct { 131 g []set 132 self, absent float64 133 edges map[int]map[int64]float64 134 damp float64 135 tol float64 136 137 wantTol float64 138 want map[int64]float64 139 }{ 140 { 141 // This test case is created according to the result with the following python code 142 // on python 3.6.4 (using "networkx" of version 2.1) 143 // 144 // >>> import networkx as nx 145 // >>> D = nx.DiGraph() 146 // >>> D.add_weighted_edges_from([('A', 'B', 0.3), ('A','C', 1.2), ('B', 'A', 0.4), ('C', 'B', 0.3), ('D', 'A', 0.3), ('D', 'B', 2.1)]) 147 // >>> nx.pagerank(D, alpha=0.85, tol=1e-10) 148 // {'A': 0.3409109390701202, 'B': 0.3522682754411842, 'C': 0.2693207854886954, 'D': 0.037500000000000006} 149 150 g: []set{ 151 A: linksTo(B, C), 152 B: linksTo(A), 153 C: linksTo(B), 154 D: linksTo(A, B), 155 }, 156 edges: map[int]map[int64]float64{ 157 A: { 158 B: 0.3, 159 C: 1.2, 160 }, 161 B: { 162 A: 0.4, 163 }, 164 C: { 165 B: 0.3, 166 }, 167 D: { 168 A: 0.3, 169 B: 2.1, 170 }, 171 }, 172 damp: 0.85, 173 tol: 1e-10, 174 175 wantTol: 1e-8, 176 want: map[int64]float64{ 177 A: 0.3409120160955594, 178 B: 0.3522678129306601, 179 C: 0.2693201709737804, 180 D: 0.037500000000000006, 181 }, 182 }, 183 } 184 185 func TestEdgeWeightedPageRank(t *testing.T) { 186 for i, test := range edgeWeightedPageRankTests { 187 g := simple.NewWeightedDirectedGraph(test.self, test.absent) 188 for u, e := range test.g { 189 // Add nodes that are not defined by an edge. 190 if g.Node(int64(u)) == nil { 191 g.AddNode(simple.Node(u)) 192 } 193 ws, ok := test.edges[u] 194 if !ok { 195 t.Errorf("edges not found for %v", u) 196 } 197 198 for v := range e { 199 if w, ok := ws[v]; ok { 200 g.SetWeightedEdge(g.NewWeightedEdge(simple.Node(u), simple.Node(v), w)) 201 } 202 } 203 } 204 got := edgeWeightedPageRank(g, test.damp, test.tol) 205 prec := 1 - int(math.Log10(test.wantTol)) 206 for n := range test.g { 207 if !scalar.EqualWithinAbsOrRel(got[int64(n)], test.want[int64(n)], test.wantTol, test.wantTol) { 208 t.Errorf("unexpected PageRank result for test %d:\ngot: %v\nwant:%v", 209 i, orderedFloats(got, prec), orderedFloats(test.want, prec)) 210 break 211 } 212 } 213 } 214 } 215 216 func TestEdgeWeightedPageRankSparse(t *testing.T) { 217 for i, test := range edgeWeightedPageRankTests { 218 g := simple.NewWeightedDirectedGraph(test.self, test.absent) 219 for u, e := range test.g { 220 // Add nodes that are not defined by an edge. 221 if g.Node(int64(u)) == nil { 222 g.AddNode(simple.Node(u)) 223 } 224 ws, ok := test.edges[u] 225 if !ok { 226 t.Errorf("edges not found for %v", u) 227 } 228 229 for v := range e { 230 if w, ok := ws[v]; ok { 231 g.SetWeightedEdge(g.NewWeightedEdge(simple.Node(u), simple.Node(v), w)) 232 } 233 } 234 } 235 got := edgeWeightedPageRankSparse(g, test.damp, test.tol) 236 prec := 1 - int(math.Log10(test.wantTol)) 237 for n := range test.g { 238 if !scalar.EqualWithinAbsOrRel(got[int64(n)], test.want[int64(n)], test.wantTol, test.wantTol) { 239 t.Errorf("unexpected PageRank result for test %d:\ngot: %v\nwant:%v", 240 i, orderedFloats(got, prec), orderedFloats(test.want, prec)) 241 break 242 } 243 } 244 } 245 } 246 247 func orderedFloats(w map[int64]float64, prec int) []keyFloatVal { 248 o := make(orderedFloatsMap, 0, len(w)) 249 for k, v := range w { 250 o = append(o, keyFloatVal{prec: prec, key: k, val: v}) 251 } 252 sort.Sort(o) 253 return o 254 } 255 256 type keyFloatVal struct { 257 prec int 258 key int64 259 val float64 260 } 261 262 func (kv keyFloatVal) String() string { return fmt.Sprintf("%c:%.*f", kv.key+'A', kv.prec, kv.val) } 263 264 type orderedFloatsMap []keyFloatVal 265 266 func (o orderedFloatsMap) Len() int { return len(o) } 267 func (o orderedFloatsMap) Less(i, j int) bool { return o[i].key < o[j].key } 268 func (o orderedFloatsMap) Swap(i, j int) { o[i], o[j] = o[j], o[i] }