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