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] }