1 //------------------------------------------------------------------------------
2 // SuiteSparse/GraphBLAS/Demo/Source/dpagerank: pagerank using a real semiring
3 //------------------------------------------------------------------------------
4
5 // SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2021, All Rights Reserved.
6 // SPDX-License-Identifier: Apache-2.0
7
8 //------------------------------------------------------------------------------
9
10 // A is a square unsymmetric binary matrix of size n-by-n, where A(i,j) is the
11 // edge (i,j). Self-edges are OK. A can be of any built-in type.
12
13 // On output, P is pointer to an array of PageRank structs. P[0] is the
14 // highest ranked page, with pagerank P[0].pagerank and the page corresponds to
15 // node number P[0].page in the graph. P[1] is the next page, and so on, to
16 // the lowest-ranked page P[n-1].page with rank P[n-1].pagerank.
17
18 // See dpagerank.m for the equivalent computation in MATLAB (except the random
19 // number generator differs).
20
21 #include "GraphBLAS.h"
22
23 //------------------------------------------------------------------------------
24 // helper macros
25 //------------------------------------------------------------------------------
26
27 // free all workspace
28 #define FREEWORK \
29 { \
30 GrB_Matrix_free (&C) ; \
31 GrB_Vector_free (&r) ; \
32 if (I != NULL) free (I) ; \
33 if (X != NULL) free (X) ; \
34 GrB_UnaryOp_free (&op_scale) ; \
35 GrB_UnaryOp_free (&op_div) ; \
36 }
37
38 // error handler: free output P and all workspace (used by CHECK and OK macros)
39 #define FREE_ALL \
40 { \
41 if (P != NULL) free (P) ; \
42 FREEWORK ; \
43 }
44
45 #undef GB_PUBLIC
46 #define GB_LIBRARY
47 #include "graphblas_demos.h"
48
49 //------------------------------------------------------------------------------
50 // scalar operators
51 //------------------------------------------------------------------------------
52
53 // NOTE: these operators use global values. dpagerank can be done in parallel,
54 // internally, but only one instance of dpagerank can be used.
55 double c, s ;
fscale(double * z,const double * x)56 void fscale (double *z, const double *x) { (*z) = c * (*x) ; }
fdiv(double * z,const double * x)57 void fdiv (double *z, const double *x) { (*z) = (*x) / s ; }
58
59 //------------------------------------------------------------------------------
60 // comparison function for qsort
61 //------------------------------------------------------------------------------
62
compar(const void * x,const void * y)63 int compar (const void *x, const void *y)
64 {
65 PageRank *a = (PageRank *) x ;
66 PageRank *b = (PageRank *) y ;
67
68 // sort by pagerank in descending order
69 if (a->pagerank > b->pagerank)
70 {
71 return (-1) ;
72 }
73 else if (a->pagerank == b->pagerank)
74 {
75 return (0) ;
76 }
77 else
78 {
79 return (1) ;
80 }
81 }
82
83 //------------------------------------------------------------------------------
84 // dpagerank: compute the PageRank of all nodes in a graph
85 //------------------------------------------------------------------------------
86
87 GB_PUBLIC
dpagerank(PageRank ** Phandle,GrB_Matrix A)88 GrB_Info dpagerank // GrB_SUCCESS or error condition
89 (
90 PageRank **Phandle, // output: pointer to array of PageRank structs
91 GrB_Matrix A // input graph, not modified
92 )
93 {
94
95 //--------------------------------------------------------------------------
96 // initializations
97 //--------------------------------------------------------------------------
98
99 GrB_Info info ;
100 double *X = NULL ;
101 GrB_Index n, *I = NULL ;
102 PageRank *P = NULL ;
103 GrB_Vector r = NULL ;
104 GrB_UnaryOp op_scale = NULL, op_div = NULL ;
105 GrB_Matrix C = NULL ;
106 (*Phandle) = NULL ;
107
108 // n = size (A,1) ; // number of nodes
109 OK (GrB_Matrix_nrows (&n, A)) ;
110
111 c = 0.85 ; // probability of walking to random neighbor
112
113 // Note the random number generate used here differs from MATLAB, so this
114 // function will not compute exactly the same thing as dpagerank.m.
115
116 // r = rand (1,n) ; // random initial pageranks
117 // simple_rand_seed ((uint64_t) n) ;
118 srand ((int) n) ;
119 OK (GrB_Vector_new (&r, GrB_FP64, n)) ;
120 for (int64_t i = 0 ; i < n ; i++)
121 {
122 // get a random double value in the range 0 to 1
123 // this is too low quality:
124 // double x = simple_rand_x ( ) ;
125 // this is not portable:
126 double x = ((double) rand ( )) / (double) RAND_MAX ;
127 OK (GrB_Vector_setElement_FP64 (r, x, i)) ;
128 }
129
130 // skip this (see dpagerank.m and compare with ipagerank.m):
131 // r = r / sum (r) ; // normalize so sum(r) == 1
132
133 double a = (1-c) / n ; // to jump to any random node in entire graph
134
135 OK (drowscale (&C, A)) ; // C = scale A by out-degree
136
137 // create unary operators
138 OK (GrB_UnaryOp_new (&op_scale,
139 (GxB_unary_function) fscale, GrB_FP64, GrB_FP64)) ;
140 OK (GrB_UnaryOp_new (&op_div ,
141 (GxB_unary_function) fdiv, GrB_FP64, GrB_FP64)) ;
142
143 //--------------------------------------------------------------------------
144 // iterate to compute the pagerank of each node
145 //--------------------------------------------------------------------------
146
147 for (int i = 0 ; i < 20 ; i++)
148 {
149 // r = ((c*r) * C) + (a * sum (r)) ;
150
151 // s = a * sum (r) ;
152 OK (GrB_Vector_reduce_FP64 (&s, NULL, GrB_PLUS_MONOID_FP64, r, NULL)) ;
153 s *= a ;
154
155 // r = c * r
156 OK (GrB_Vector_apply (r, NULL, NULL, op_scale, r, NULL)) ;
157
158 // r = r * C
159 OK (GrB_vxm (r, NULL, NULL, GxB_PLUS_TIMES_FP64, r, C, NULL)) ;
160
161 // r = r + s
162 OK (GrB_Vector_assign_FP64 (r, NULL, GrB_PLUS_FP64, s,
163 GrB_ALL, n, NULL)) ;
164 }
165
166 //--------------------------------------------------------------------------
167 // scale the result
168 //--------------------------------------------------------------------------
169
170 // s = sum (r)
171 OK (GrB_Vector_reduce_FP64 (&s, NULL, GrB_PLUS_MONOID_FP64, r, NULL)) ;
172
173 // r = r / s
174 OK (GrB_Vector_apply (r, NULL, NULL, op_div, r, NULL)) ;
175
176 //--------------------------------------------------------------------------
177 // sort the nodes by pagerank
178 //--------------------------------------------------------------------------
179
180 // [r,irank] = sort (r, 'descend') ;
181
182 // [I,X] = find (r) ;
183 X = (double *) malloc (n * sizeof (double)) ;
184 I = (GrB_Index *) malloc (n * sizeof (GrB_Index)) ;
185 CHECK (I != NULL && X != NULL, GrB_OUT_OF_MEMORY) ;
186 GrB_Index nvals = n ;
187 OK (GrB_Vector_extractTuples_FP64 (I, X, &nvals, r)) ;
188
189 // this will always be true since r is dense, but double-check anyway:
190 CHECK (nvals == n, GrB_INVALID_VALUE) ;
191
192 // r no longer needed
193 GrB_Vector_free (&r) ;
194
195 // P = struct (X,I)
196 P = (PageRank *) malloc (n * sizeof (PageRank)) ;
197 CHECK (P != NULL, GrB_OUT_OF_MEMORY) ;
198 for (int64_t k = 0 ; k < nvals ; k++)
199 {
200 // The kth ranked page is P[k].page (with k=0 being the highest rank),
201 // and its pagerank is P[k].pagerank.
202 P [k].pagerank = X [k] ;
203 // I [k] == k will be true for SuiteSparse:GraphBLAS but in general I
204 // can be returned in any order, so use I [k] instead of k, for other
205 // GraphBLAS implementations.
206 P [k].page = I [k] ;
207 }
208
209 // free workspace
210 FREEWORK ;
211
212 // qsort (P) in descending order
213 qsort (P, n, sizeof (PageRank), compar) ;
214
215 //--------------------------------------------------------------------------
216 // return result
217 //--------------------------------------------------------------------------
218
219 (*Phandle) = P ;
220 return (GrB_SUCCESS) ;
221 }
222
223