1 #include "prpack_solver.h"
2 #include "prpack_utils.h"
3 #include <cmath>
4 #include <cstdlib>
5 #include <cstring>
6 #include <algorithm>
7 using namespace prpack;
8 using namespace std;
9 
initialize()10 void prpack_solver::initialize() {
11     geg = NULL;
12     gsg = NULL;
13     sg = NULL;
14     sccg = NULL;
15 	owns_bg = true;
16 }
17 
prpack_solver(const prpack_csc * g)18 prpack_solver::prpack_solver(const prpack_csc* g) {
19     initialize();
20     TIME(read_time, bg = new prpack_base_graph(g));
21 }
22 
prpack_solver(const prpack_int64_csc * g)23 prpack_solver::prpack_solver(const prpack_int64_csc* g) {
24     initialize();
25     TIME(read_time, bg = new prpack_base_graph(g));
26 }
27 
prpack_solver(const prpack_csr * g)28 prpack_solver::prpack_solver(const prpack_csr* g) {
29     initialize();
30     TIME(read_time, bg = new prpack_base_graph(g));
31 }
32 
prpack_solver(const prpack_edge_list * g)33 prpack_solver::prpack_solver(const prpack_edge_list* g) {
34     initialize();
35     TIME(read_time, bg = new prpack_base_graph(g));
36 }
37 
prpack_solver(prpack_base_graph * g,bool owns_bg)38 prpack_solver::prpack_solver(prpack_base_graph* g, bool owns_bg) {
39     initialize();
40 	this->owns_bg = owns_bg;
41     TIME(read_time, bg = g);
42 }
43 
prpack_solver(const char * filename,const char * format,const bool weighted)44 prpack_solver::prpack_solver(const char* filename, const char* format, const bool weighted) {
45     initialize();
46     TIME(read_time, bg = new prpack_base_graph(filename, format, weighted));
47 }
48 
~prpack_solver()49 prpack_solver::~prpack_solver() {
50 	if (owns_bg) {
51 		delete bg;
52 	}
53     delete geg;
54     delete gsg;
55     delete sg;
56     delete sccg;
57 }
58 
get_num_vs()59 int prpack_solver::get_num_vs() {
60     return bg->num_vs;
61 }
62 
solve(const double alpha,const double tol,const char * method)63 prpack_result* prpack_solver::solve(const double alpha, const double tol, const char* method) {
64     return solve(alpha, tol, NULL, NULL, method);
65 }
66 
solve(const double alpha,const double tol,const double * u,const double * v,const char * method)67 prpack_result* prpack_solver::solve(
68         const double alpha,
69         const double tol,
70         const double* u,
71         const double* v,
72         const char* method) {
73     double preprocess_time = 0;
74     double compute_time = 0;
75     prpack_result* ret = NULL;
76     // decide which method to run
77     string m;
78     if (strcmp(method, "") != 0)
79         m = string(method);
80     else {
81         if (bg->num_vs < 128)
82             m = "ge";
83         else if (sccg != NULL)
84             m = "sccgs";
85         else if (sg != NULL)
86             m = "sg";
87         else
88             m = "sccgs";
89         if (u != v)
90             m += "_uv";
91     }
92     // run the appropriate method
93     if (m == "ge") {
94         if (geg == NULL) {
95             TIME(preprocess_time, geg = new prpack_preprocessed_ge_graph(bg));
96         }
97         TIME(compute_time, ret = solve_via_ge(
98                 alpha,
99                 tol,
100                 geg->num_vs,
101                 geg->matrix,
102                 u));
103     } else if (m == "ge_uv") {
104         if (geg == NULL) {
105             TIME(preprocess_time, geg = new prpack_preprocessed_ge_graph(bg));
106         }
107         TIME(compute_time, ret = solve_via_ge_uv(
108                 alpha,
109                 tol,
110                 geg->num_vs,
111                 geg->matrix,
112                 geg->d,
113                 u,
114                 v));
115     } else if (m == "gs") {
116         if (gsg == NULL) {
117             TIME(preprocess_time, gsg = new prpack_preprocessed_gs_graph(bg));
118         }
119         TIME(compute_time, ret = solve_via_gs(
120                 alpha,
121                 tol,
122                 gsg->num_vs,
123                 gsg->num_es,
124                 gsg->heads,
125                 gsg->tails,
126                 gsg->vals,
127                 gsg->ii,
128                 gsg->d,
129                 gsg->num_outlinks,
130                 u,
131                 v));
132     } else if (m == "gserr") {
133         if (gsg == NULL) {
134             TIME(preprocess_time, gsg = new prpack_preprocessed_gs_graph(bg));
135         }
136         TIME(compute_time, ret = solve_via_gs_err(
137                 alpha,
138                 tol,
139                 gsg->num_vs,
140                 gsg->num_es,
141                 gsg->heads,
142                 gsg->tails,
143                 gsg->ii,
144                 gsg->num_outlinks,
145                 u,
146                 v));
147     } else if (m == "sgs") {
148         if (sg == NULL) {
149             TIME(preprocess_time, sg = new prpack_preprocessed_schur_graph(bg));
150         }
151         TIME(compute_time, ret = solve_via_schur_gs(
152                 alpha,
153                 tol,
154                 sg->num_vs,
155                 sg->num_no_in_vs,
156                 sg->num_no_out_vs,
157                 sg->num_es,
158                 sg->heads,
159                 sg->tails,
160                 sg->vals,
161                 sg->ii,
162                 sg->d,
163                 sg->num_outlinks,
164                 u,
165                 sg->encoding,
166                 sg->decoding));
167     } else if (m == "sgs_uv") {
168         if (sg == NULL) {
169             TIME(preprocess_time, sg = new prpack_preprocessed_schur_graph(bg));
170         }
171         TIME(compute_time, ret = solve_via_schur_gs_uv(
172                 alpha,
173                 tol,
174                 sg->num_vs,
175                 sg->num_no_in_vs,
176                 sg->num_no_out_vs,
177                 sg->num_es,
178                 sg->heads,
179                 sg->tails,
180                 sg->vals,
181                 sg->ii,
182                 sg->d,
183                 sg->num_outlinks,
184                 u,
185                 v,
186                 sg->encoding,
187                 sg->decoding));
188     } else if (m == "sccgs") {
189         if (sccg == NULL) {
190             TIME(preprocess_time, sccg = new prpack_preprocessed_scc_graph(bg));
191         }
192         TIME(compute_time, ret = solve_via_scc_gs(
193                 alpha,
194                 tol,
195                 sccg->num_vs,
196                 sccg->num_es_inside,
197                 sccg->heads_inside,
198                 sccg->tails_inside,
199                 sccg->vals_inside,
200                 sccg->num_es_outside,
201                 sccg->heads_outside,
202                 sccg->tails_outside,
203                 sccg->vals_outside,
204                 sccg->ii,
205                 sccg->d,
206                 sccg->num_outlinks,
207                 u,
208                 sccg->num_comps,
209                 sccg->divisions,
210                 sccg->encoding,
211                 sccg->decoding));
212     } else if (m == "sccgs_uv") {
213         if (sccg == NULL) {
214             TIME(preprocess_time, sccg = new prpack_preprocessed_scc_graph(bg));
215         }
216         TIME(compute_time, ret = solve_via_scc_gs_uv(
217                 alpha,
218                 tol,
219                 sccg->num_vs,
220                 sccg->num_es_inside,
221                 sccg->heads_inside,
222                 sccg->tails_inside,
223                 sccg->vals_inside,
224                 sccg->num_es_outside,
225                 sccg->heads_outside,
226                 sccg->tails_outside,
227                 sccg->vals_outside,
228                 sccg->ii,
229                 sccg->d,
230                 sccg->num_outlinks,
231                 u,
232                 v,
233                 sccg->num_comps,
234                 sccg->divisions,
235                 sccg->encoding,
236                 sccg->decoding));
237     } else {
238         // TODO: throw exception
239     }
240     ret->method = m;
241     ret->read_time = read_time;
242     ret->preprocess_time = preprocess_time;
243     ret->compute_time = compute_time;
244     ret->num_vs = bg->num_vs;
245     ret->num_es = bg->num_es;
246     return ret;
247 }
248 
249 // VARIOUS SOLVING METHODS ////////////////////////////////////////////////////////////////////////
250 
solve_via_ge(const double alpha,const double tol,const int num_vs,const double * matrix,const double * uv)251 prpack_result* prpack_solver::solve_via_ge(
252         const double alpha,
253         const double tol,
254         const int num_vs,
255         const double* matrix,
256         const double* uv) {
257     prpack_result* ret = new prpack_result();
258     // initialize uv values
259     const double uv_const = 1.0/num_vs;
260     const int uv_exists = (uv) ? 1 : 0;
261     uv = (uv) ? uv : &uv_const;
262     // create matrix A
263     double* A = new double[num_vs*num_vs];
264     for (int i = 0; i < num_vs*num_vs; ++i)
265         A[i] = -alpha*matrix[i];
266     for (int i = 0; i < num_vs*num_vs; i += num_vs + 1)
267         ++A[i];
268     // create vector b
269     double* b = new double[num_vs];
270     for (int i = 0; i < num_vs; ++i)
271         b[i] = uv[uv_exists*i];
272     // solve and normalize
273     ge(num_vs, A, b);
274     normalize(num_vs, b);
275     // clean up and return
276     delete[] A;
277     ret->num_es_touched = -1;
278     ret->x = b;
279     return ret;
280 }
281 
solve_via_ge_uv(const double alpha,const double tol,const int num_vs,const double * matrix,const double * d,const double * u,const double * v)282 prpack_result* prpack_solver::solve_via_ge_uv(
283         const double alpha,
284         const double tol,
285         const int num_vs,
286         const double* matrix,
287         const double* d,
288         const double* u,
289         const double* v) {
290     prpack_result* ret = new prpack_result();
291     // initialize u and v values
292     const double u_const = 1.0/num_vs;
293     const double v_const = 1.0/num_vs;
294     const int u_exists = (u) ? 1 : 0;
295     const int v_exists = (v) ? 1 : 0;
296     u = (u) ? u : &u_const;
297     v = (v) ? v : &v_const;
298     // create matrix A
299     double* A = new double[num_vs*num_vs];
300     for (int i = 0; i < num_vs*num_vs; ++i)
301         A[i] = -alpha*matrix[i];
302     for (int i = 0, inum_vs = 0; i < num_vs; ++i, inum_vs += num_vs)
303         for (int j = 0; j < num_vs; ++j)
304             A[inum_vs + j] -= alpha*u[u_exists*i]*d[j];
305     for (int i = 0; i < num_vs*num_vs; i += num_vs + 1)
306         ++A[i];
307     // create vector b
308     double* b = new double[num_vs];
309     for (int i = 0; i < num_vs; ++i)
310         b[i] = (1 - alpha)*v[v_exists*i];
311     // solve
312     ge(num_vs, A, b);
313     // clean up and return
314     delete[] A;
315     ret->num_es_touched = -1;
316     ret->x = b;
317     return ret;
318 }
319 
320 // Vanilla Gauss-Seidel.
solve_via_gs(const double alpha,const double tol,const int num_vs,const int num_es,const int * heads,const int * tails,const double * vals,const double * ii,const double * d,const double * num_outlinks,const double * u,const double * v)321 prpack_result* prpack_solver::solve_via_gs(
322         const double alpha,
323         const double tol,
324         const int num_vs,
325         const int num_es,
326         const int* heads,
327         const int* tails,
328         const double* vals,
329         const double* ii,
330         const double* d,
331         const double* num_outlinks,
332         const double* u,
333         const double* v) {
334     prpack_result* ret = new prpack_result();
335     const bool weighted = vals != NULL;
336     // initialize u and v values
337     const double u_const = 1.0/num_vs;
338     const double v_const = 1.0/num_vs;
339     const int u_exists = (u) ? 1 : 0;
340     const int v_exists = (v) ? 1 : 0;
341     u = (u) ? u : &u_const;
342     v = (v) ? v : &v_const;
343     // initialize the eigenvector (and use personalization vector)
344     double* x = new double[num_vs];
345     for (int i = 0; i < num_vs; ++i)
346         x[i] = 0;
347     // initialize delta
348     double delta = 0;
349     // run Gauss-Seidel
350     ret->num_es_touched = 0;
351     double err = 1, c = 0;
352     do {
353         if (weighted) {
354             for (int i = 0; i < num_vs; ++i) {
355                 double new_val = 0;
356                 const int start_j = tails[i];
357                 const int end_j = (i + 1 != num_vs) ? tails[i + 1] : num_es;
358                 for (int j = start_j; j < end_j; ++j)
359                     // TODO: might want to use compensation summation for large: end_j - start_j
360                     new_val += x[heads[j]]*vals[j];
361                 new_val = alpha*new_val + (1 - alpha)*v[v_exists*i];
362                 delta -= alpha*x[i]*d[i];
363                 new_val += delta*u[u_exists*i];
364                 new_val /= 1 - alpha*(d[i]*u[u_exists*i] + (1 - d[i])*ii[i]);
365                 delta += alpha*new_val*d[i];
366                 COMPENSATED_SUM(err, x[i] - new_val, c);
367                 x[i] = new_val;
368             }
369         } else {
370             for (int i = 0; i < num_vs; ++i) {
371                 const double old_val = x[i]*num_outlinks[i];
372                 double new_val = 0;
373                 const int start_j = tails[i];
374                 const int end_j = (i + 1 != num_vs) ? tails[i + 1] : num_es;
375                 for (int j = start_j; j < end_j; ++j)
376                     // TODO: might want to use compensation summation for large: end_j - start_j
377                     new_val += x[heads[j]];
378                 new_val = alpha*new_val + (1 - alpha)*v[v_exists*i];
379                 if (num_outlinks[i] < 0) {
380                     delta -= alpha*old_val;
381                     new_val += delta*u[u_exists*i];
382                     new_val /= 1 - alpha*u[u_exists*i];
383                     delta += alpha*new_val;
384                 } else {
385                     new_val += delta*u[u_exists*i];
386                     new_val /= 1 - alpha*ii[i];
387                 }
388                 COMPENSATED_SUM(err, old_val - new_val, c);
389                 x[i] = new_val/num_outlinks[i];
390             }
391         }
392         // update iteration index
393         ret->num_es_touched += num_es;
394     } while (err >= tol);
395     // undo num_outlinks transformation
396     if (!weighted)
397         for (int i = 0; i < num_vs; ++i)
398             x[i] *= num_outlinks[i];
399     // return results
400     ret->x = x;
401     return ret;
402 }
403 
404 // Implement a gauss-seidel-like process with a strict error bound
405 // we return a solution with 1-norm error less than tol.
solve_via_gs_err(const double alpha,const double tol,const int num_vs,const int num_es,const int * heads,const int * tails,const double * ii,const double * num_outlinks,const double * u,const double * v)406 prpack_result* prpack_solver::solve_via_gs_err(
407         const double alpha,
408         const double tol,
409         const int num_vs,
410         const int num_es,
411         const int* heads,
412         const int* tails,
413         const double* ii,
414         const double* num_outlinks,
415         const double* u,
416         const double* v) {
417     prpack_result* ret = new prpack_result();
418     // initialize u and v values
419     const double u_const = 1.0/num_vs;
420     const double v_const = 1.0/num_vs;
421     const int u_exists = (u) ? 1 : 0;
422     const int v_exists = (v) ? 1 : 0;
423     u = (u) ? u : &u_const;
424     v = (v) ? v : &v_const;
425     // Note to Dave, we can't rescale v because we could be running this
426     // same routine from multiple threads.
427     // initialize the eigenvector (and use personalization vector)
428     double* x = new double[num_vs];
429     for (int i = 0; i < num_vs; ++i) {
430         x[i] = 0.;
431     }
432     // initialize delta
433     double delta = 0.;
434     // run Gauss-Seidel, note that we store x/deg[i] throughout this
435     // iteration.
436     int64_t maxedges = (int64_t)((double)num_es*std::min(
437                             log(tol)/log(alpha),
438                             (double)PRPACK_SOLVER_MAX_ITERS));
439     ret->num_es_touched = 0;
440     double err=1., c = 0.;
441     do {
442         // iterate through vertices
443         for (int i = 0; i < num_vs; ++i) {
444             double old_val = x[i]*num_outlinks[i]; // adjust back to the "true" value.
445             double new_val = 0.;
446             int start_j = tails[i], end_j = (i + 1 != num_vs) ? tails[i + 1] : num_es;
447             for (int j = start_j; j < end_j; ++j) {
448                 // TODO: might want to use compensation summation for large: end_j - start_j
449                 new_val += x[heads[j]];
450             }
451             new_val = alpha*new_val + alpha*ii[i]*old_val + (1.0-alpha)*v[v_exists*i];
452             new_val += delta*u[u_exists*i]; // add the dangling node adjustment
453             if (num_outlinks[i] < 0) {
454                 delta += alpha*(new_val - old_val);
455             }
456             // note that new_val > old_val, but the fabs is just for
457             COMPENSATED_SUM(err, -(new_val - old_val), c);
458             x[i] = new_val/num_outlinks[i];
459         }
460         // update iteration index
461         ret->num_es_touched += num_es;
462     } while (err >= tol && ret->num_es_touched < maxedges);
463     if (err >= tol) {
464         ret->converged = 0;
465     } else {
466         ret->converged = 1;
467     }
468     // undo num_outlinks transformation
469     for (int i = 0; i < num_vs; ++i)
470         x[i] *= num_outlinks[i];
471     // return results
472     ret->x = x;
473     return ret;
474 }
475 
476 // Gauss-Seidel using the Schur complement to separate dangling nodes.
solve_via_schur_gs(const double alpha,const double tol,const int num_vs,const int num_no_in_vs,const int num_no_out_vs,const int num_es,const int * heads,const int * tails,const double * vals,const double * ii,const double * d,const double * num_outlinks,const double * uv,const int * encoding,const int * decoding,const bool should_normalize)477 prpack_result* prpack_solver::solve_via_schur_gs(
478         const double alpha,
479         const double tol,
480         const int num_vs,
481         const int num_no_in_vs,
482         const int num_no_out_vs,
483         const int num_es,
484         const int* heads,
485         const int* tails,
486         const double* vals,
487         const double* ii,
488         const double* d,
489         const double* num_outlinks,
490         const double* uv,
491         const int* encoding,
492         const int* decoding,
493         const bool should_normalize) {
494     prpack_result* ret = new prpack_result();
495     const bool weighted = vals != NULL;
496     // initialize uv values
497     const double uv_const = 1.0/num_vs;
498     const int uv_exists = (uv) ? 1 : 0;
499     uv = (uv) ? prpack_utils::permute(num_vs, uv, encoding) : &uv_const;
500     // initialize the eigenvector (and use personalization vector)
501     double* x = new double[num_vs];
502     for (int i = 0; i < num_vs - num_no_out_vs; ++i)
503         x[i] = uv[uv_exists*i]/(1 - alpha*ii[i])/((weighted) ? 1 : num_outlinks[i]);
504     // run Gauss-Seidel for the top left part of (I - alpha*P)*x = uv
505     ret->num_es_touched = 0;
506     double err, c;
507     do {
508         // iterate through vertices
509         int num_es_touched = 0;
510         err = c = 0;
511         #pragma omp parallel for firstprivate(c) reduction(+:err, num_es_touched) schedule(dynamic, 64)
512         for (int i = num_no_in_vs; i < num_vs - num_no_out_vs; ++i) {
513             double new_val = 0;
514             const int start_j = tails[i];
515             const int end_j = (i + 1 != num_vs) ? tails[i + 1] : num_es;
516             if (weighted) {
517                 for (int j = start_j; j < end_j; ++j)
518                     // TODO: might want to use compensation summation for large: end_j - start_j
519                     new_val += x[heads[j]]*vals[j];
520                 COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]), c);
521                 new_val = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i]);
522                 x[i] = new_val;
523             } else {
524                 for (int j = start_j; j < end_j; ++j)
525                     // TODO: might want to use compensation summation for large: end_j - start_j
526                     new_val += x[heads[j]];
527                 COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]*num_outlinks[i]), c);
528                 new_val = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i]);
529                 x[i] = new_val/num_outlinks[i];
530             }
531             num_es_touched += end_j - start_j;
532         }
533         // update iteration index
534         ret->num_es_touched += num_es_touched;
535     } while (err/(1 - alpha) >= tol);
536     // solve for the dangling nodes
537     int num_es_touched = 0;
538     #pragma omp parallel for reduction(+:num_es_touched) schedule(dynamic, 64)
539     for (int i = num_vs - num_no_out_vs; i < num_vs; ++i) {
540         x[i] = 0;
541         const int start_j = tails[i];
542         const int end_j = (i + 1 != num_vs) ? tails[i + 1] : num_es;
543         for (int j = start_j; j < end_j; ++j)
544             x[i] += x[heads[j]]*((weighted) ? vals[j] : 1);
545         x[i] = (alpha*x[i] + uv[uv_exists*i])/(1 - alpha*ii[i]);
546         num_es_touched += end_j - start_j;
547     }
548     ret->num_es_touched += num_es_touched;
549     // undo num_outlinks transformation
550     if (!weighted)
551         for (int i = 0; i < num_vs - num_no_out_vs; ++i)
552             x[i] *= num_outlinks[i];
553     // normalize x to get the solution for: (I - alpha*P - alpha*u*d')*x = (1 - alpha)*v
554     if (should_normalize)
555         normalize(num_vs, x);
556     // return results
557     ret->x = prpack_utils::permute(num_vs, x, decoding);
558     delete[] x;
559     if (uv_exists)
560         delete[] uv;
561     return ret;
562 }
563 
solve_via_schur_gs_uv(const double alpha,const double tol,const int num_vs,const int num_no_in_vs,const int num_no_out_vs,const int num_es,const int * heads,const int * tails,const double * vals,const double * ii,const double * d,const double * num_outlinks,const double * u,const double * v,const int * encoding,const int * decoding)564 prpack_result* prpack_solver::solve_via_schur_gs_uv(
565         const double alpha,
566         const double tol,
567         const int num_vs,
568         const int num_no_in_vs,
569         const int num_no_out_vs,
570         const int num_es,
571         const int* heads,
572         const int* tails,
573         const double* vals,
574         const double* ii,
575         const double* d,
576         const double* num_outlinks,
577         const double* u,
578         const double* v,
579         const int* encoding,
580         const int* decoding) {
581     // solve uv = u
582     prpack_result* ret_u = solve_via_schur_gs(
583             alpha,
584             tol,
585             num_vs,
586             num_no_in_vs,
587             num_no_out_vs,
588             num_es,
589             heads,
590             tails,
591             vals,
592             ii,
593             d,
594             num_outlinks,
595             u,
596             encoding,
597             decoding,
598             false);
599     // solve uv = v
600     prpack_result* ret_v = solve_via_schur_gs(
601             alpha,
602             tol,
603             num_vs,
604             num_no_in_vs,
605             num_no_out_vs,
606             num_es,
607             heads,
608             tails,
609             vals,
610             ii,
611             d,
612             num_outlinks,
613             v,
614             encoding,
615             decoding,
616             false);
617     // combine the u and v cases
618     return combine_uv(num_vs, d, num_outlinks, encoding, alpha, ret_u, ret_v);
619 }
620 
621 /** Gauss-Seidel using strongly connected components.
622  * Notes:
623  *   If not weighted, then we store x[i] = "x[i]/outdegree" to
624  *   avoid additional arithmetic.  We don't do this for the weighted
625  *   case because the adjustment may not be constant.
626  */
solve_via_scc_gs(const double alpha,const double tol,const int num_vs,const int num_es_inside,const int * heads_inside,const int * tails_inside,const double * vals_inside,const int num_es_outside,const int * heads_outside,const int * tails_outside,const double * vals_outside,const double * ii,const double * d,const double * num_outlinks,const double * uv,const int num_comps,const int * divisions,const int * encoding,const int * decoding,const bool should_normalize)627 prpack_result* prpack_solver::solve_via_scc_gs(
628         const double alpha,
629         const double tol,
630         const int num_vs,
631         const int num_es_inside,
632         const int* heads_inside,
633         const int* tails_inside,
634         const double* vals_inside,
635         const int num_es_outside,
636         const int* heads_outside,
637         const int* tails_outside,
638         const double* vals_outside,
639         const double* ii,
640         const double* d,
641         const double* num_outlinks,
642         const double* uv,
643         const int num_comps,
644         const int* divisions,
645         const int* encoding,
646         const int* decoding,
647         const bool should_normalize) {
648     prpack_result* ret = new prpack_result();
649     const bool weighted = vals_inside != NULL;
650     // initialize uv values
651     const double uv_const = 1.0/num_vs;
652     const int uv_exists = (uv) ? 1 : 0;
653     uv = (uv) ? prpack_utils::permute(num_vs, uv, encoding) : &uv_const;
654     // CHECK initialize the solution with one iteration of GS from x=0.
655     double* x = new double[num_vs];
656     for (int i = 0; i < num_vs; ++i)
657         x[i] = uv[uv_exists*i]/(1 - alpha*ii[i])/((weighted) ? 1 : num_outlinks[i]);
658     // create x_outside
659     double* x_outside = new double[num_vs];
660     // run Gauss-Seidel for (I - alpha*P)*x = uv
661     ret->num_es_touched = 0;
662     for (int comp_i = 0; comp_i < num_comps; ++comp_i) {
663         const int start_comp = divisions[comp_i];
664         const int end_comp = (comp_i + 1 != num_comps) ? divisions[comp_i + 1] : num_vs;
665         const bool parallelize = end_comp - start_comp > 512;
666         // initialize relevant x_outside values
667         for (int i = start_comp; i < end_comp; ++i) {
668             x_outside[i] = 0;
669             const int start_j = tails_outside[i];
670             const int end_j = (i + 1 != num_vs) ? tails_outside[i + 1] : num_es_outside;
671             for (int j = start_j; j < end_j; ++j)
672                 x_outside[i] += x[heads_outside[j]]*((weighted) ? vals_outside[j] : 1.);
673             ret->num_es_touched += end_j - start_j;
674         }
675         double err, c;
676         do {
677             int num_es_touched = 0;
678             err = c = 0;
679             if (parallelize) {
680                 // iterate through vertices
681                 #pragma omp parallel for firstprivate(c) reduction(+:err, num_es_touched) schedule(dynamic, 64)
682                 for (int i = start_comp; i < end_comp; ++i) {
683                     double new_val = x_outside[i];
684                     const int start_j = tails_inside[i];
685                     const int end_j = (i + 1 != num_vs) ? tails_inside[i + 1] : num_es_inside;
686                     if (weighted) {
687                         for (int j = start_j; j < end_j; ++j) {
688                             // TODO: might want to use compensation summation for large: end_j - start_j
689                             new_val += x[heads_inside[j]]*vals_inside[j];
690                         }
691                         COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]), c);
692                         x[i] = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i]);
693                     } else {
694                         for (int j = start_j; j < end_j; ++j) {
695                             // TODO: might want to use compensation summation for large: end_j - start_j
696                             new_val += x[heads_inside[j]];
697                         }
698                         COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]*num_outlinks[i]), c);
699                         x[i] = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i])/num_outlinks[i];
700                     }
701                     num_es_touched += end_j - start_j;
702                 }
703             } else {
704                 for (int i = start_comp; i < end_comp; ++i) {
705                     double new_val = x_outside[i];
706                     const int start_j = tails_inside[i];
707                     const int end_j = (i + 1 != num_vs) ? tails_inside[i + 1] : num_es_inside;
708                     if (weighted) {
709                         for (int j = start_j; j < end_j; ++j) {
710                             // TODO: might want to use compensation summation for large: end_j - start_j
711                             new_val += x[heads_inside[j]]*vals_inside[j];
712                         }
713                         COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]), c);
714                         x[i] = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i]);
715                     } else {
716                         for (int j = start_j; j < end_j; ++j) {
717                             // TODO: might want to use compensation summation for large: end_j - start_j
718                             new_val += x[heads_inside[j]];
719                         }
720                         COMPENSATED_SUM(err, fabs(uv[uv_exists*i] + alpha*new_val - (1 - alpha*ii[i])*x[i]*num_outlinks[i]), c);
721                         x[i] = (alpha*new_val + uv[uv_exists*i])/(1 - alpha*ii[i])/num_outlinks[i];
722                     }
723                     num_es_touched += end_j - start_j;
724                 }
725             }
726             // update iteration index
727             ret->num_es_touched += num_es_touched;
728         } while (err/(1 - alpha) >= tol*(end_comp - start_comp)/num_vs);
729     }
730     // undo num_outlinks transformation
731     if (!weighted)
732         for (int i = 0; i < num_vs; ++i)
733             x[i] *= num_outlinks[i];
734     // normalize x to get the solution for: (I - alpha*P - alpha*u*d')*x = (1 - alpha)*v
735     if (should_normalize)
736         normalize(num_vs, x);
737     // return results
738     ret->x = prpack_utils::permute(num_vs, x, decoding);
739     delete[] x;
740     delete[] x_outside;
741     if (uv_exists)
742         delete[] uv;
743     return ret;
744 }
745 
solve_via_scc_gs_uv(const double alpha,const double tol,const int num_vs,const int num_es_inside,const int * heads_inside,const int * tails_inside,const double * vals_inside,const int num_es_outside,const int * heads_outside,const int * tails_outside,const double * vals_outside,const double * ii,const double * d,const double * num_outlinks,const double * u,const double * v,const int num_comps,const int * divisions,const int * encoding,const int * decoding)746 prpack_result* prpack_solver::solve_via_scc_gs_uv(
747         const double alpha,
748         const double tol,
749         const int num_vs,
750         const int num_es_inside,
751         const int* heads_inside,
752         const int* tails_inside,
753         const double* vals_inside,
754         const int num_es_outside,
755         const int* heads_outside,
756         const int* tails_outside,
757         const double* vals_outside,
758         const double* ii,
759         const double* d,
760         const double* num_outlinks,
761         const double* u,
762         const double* v,
763         const int num_comps,
764         const int* divisions,
765         const int* encoding,
766         const int* decoding) {
767     // solve uv = u
768     prpack_result* ret_u = solve_via_scc_gs(
769             alpha,
770             tol,
771             num_vs,
772             num_es_inside,
773             heads_inside,
774             tails_inside,
775             vals_inside,
776             num_es_outside,
777             heads_outside,
778             tails_outside,
779             vals_outside,
780             ii,
781             d,
782             num_outlinks,
783             u,
784             num_comps,
785             divisions,
786             encoding,
787             decoding,
788             false);
789     // solve uv = v
790     prpack_result* ret_v = solve_via_scc_gs(
791             alpha,
792             tol,
793             num_vs,
794             num_es_inside,
795             heads_inside,
796             tails_inside,
797             vals_inside,
798             num_es_outside,
799             heads_outside,
800             tails_outside,
801             vals_outside,
802             ii,
803             d,
804             num_outlinks,
805             v,
806             num_comps,
807             divisions,
808             encoding,
809             decoding,
810             false);
811     // combine u and v
812     return combine_uv(num_vs, d, num_outlinks, encoding, alpha, ret_u, ret_v);
813 }
814 
815 // VARIOUS HELPER METHODS /////////////////////////////////////////////////////////////////////////
816 
817 // Run Gaussian-Elimination (note: this changes A and returns the solution in b)
ge(const int sz,double * A,double * b)818 void prpack_solver::ge(const int sz, double* A, double* b) {
819     // put into triangular form
820     for (int i = 0, isz = 0; i < sz; ++i, isz += sz)
821         for (int k = 0, ksz = 0; k < i; ++k, ksz += sz)
822             if (A[isz + k] != 0) {
823                 const double coeff = A[isz + k]/A[ksz + k];
824                 A[isz + k] = 0;
825                 for (int j = k + 1; j < sz; ++j)
826                     A[isz + j] -= coeff*A[ksz + j];
827                 b[i] -= coeff*b[k];
828             }
829     // backwards substitution
830     for (int i = sz - 1, isz = (sz - 1)*sz; i >= 0; --i, isz -= sz) {
831         for (int j = i + 1; j < sz; ++j)
832             b[i] -= A[isz + j]*b[j];
833         b[i] /= A[isz + i];
834     }
835 }
836 
837 // Normalize a vector to sum to 1.
normalize(const int length,double * x)838 void prpack_solver::normalize(const int length, double* x) {
839     double norm = 0, c = 0;
840     for (int i = 0; i < length; ++i) {
841         COMPENSATED_SUM(norm, x[i], c);
842     }
843     norm = 1/norm;
844     for (int i = 0; i < length; ++i)
845         x[i] *= norm;
846 }
847 
848 // Combine u and v results.
combine_uv(const int num_vs,const double * d,const double * num_outlinks,const int * encoding,const double alpha,const prpack_result * ret_u,const prpack_result * ret_v)849 prpack_result* prpack_solver::combine_uv(
850         const int num_vs,
851         const double* d,
852         const double* num_outlinks,
853         const int* encoding,
854         const double alpha,
855         const prpack_result* ret_u,
856         const prpack_result* ret_v) {
857     prpack_result* ret = new prpack_result();
858     const bool weighted = d != NULL;
859     double delta_u = 0;
860     double delta_v = 0;
861     for (int i = 0; i < num_vs; ++i) {
862         if ((weighted) ? (d[encoding[i]] == 1) : (num_outlinks[encoding[i]] < 0)) {
863             delta_u += ret_u->x[i];
864             delta_v += ret_v->x[i];
865         }
866     }
867     const double s = ((1 - alpha)*alpha*delta_v)/(1 - alpha*delta_u);
868     const double t = 1 - alpha;
869     ret->x = new double[num_vs];
870     for (int i = 0; i < num_vs; ++i)
871         ret->x[i] = s*ret_u->x[i] + t*ret_v->x[i];
872     ret->num_es_touched = ret_u->num_es_touched + ret_v->num_es_touched;
873     // clean up and return
874     delete ret_u;
875     delete ret_v;
876     return ret;
877 }
878 
879