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