401 lines
14 KiB
C
401 lines
14 KiB
C
/*
|
|
igraph library.
|
|
Copyright (C) 2006-2021 The igraph development team <igraph@igraph.org>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <igraph.h>
|
|
#include <math.h>
|
|
#include <float.h>
|
|
|
|
#include "test_utilities.h"
|
|
|
|
int is_almost_one(igraph_real_t x) {
|
|
/* 2^5 = 32 is 5 binary digits of tolerance */
|
|
if (fabs(x - 1) > 32*DBL_EPSILON) {
|
|
printf("Expected value to be 1, but actually got %15g.", x);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int main(void) {
|
|
|
|
igraph_t g;
|
|
igraph_vector_t res, reset, weights;
|
|
igraph_arpack_options_t arpack_options;
|
|
igraph_real_t value;
|
|
igraph_error_t err;
|
|
|
|
/* The ARPACK method uses a random perturbation to the in-degrees
|
|
to set the starting vector for ARPACK. */
|
|
igraph_rng_seed(igraph_rng_default(), 137);
|
|
|
|
igraph_arpack_options_init(&arpack_options);
|
|
|
|
/* Test graphs taken from http://www.iprcom.com/papers/pagerank/ */
|
|
|
|
printf("\nTest graph 1\n");
|
|
igraph_small(&g, 0, IGRAPH_DIRECTED,
|
|
0,1, 1,2, 2,0, 3,2, 0,2,
|
|
-1);
|
|
|
|
igraph_vector_init(&res, 0);
|
|
igraph_pagerank(&g, 0, &res, &value, 0.85, 0,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_ARPACK, &arpack_options);
|
|
printf("ARPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_pagerank(&g, 0, &res, &value, 0.85, 0,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_PRPACK, 0);
|
|
printf("PRPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_vector_destroy(&res);
|
|
|
|
igraph_destroy(&g);
|
|
|
|
printf("\nTest graph 2\n");
|
|
igraph_small(&g, 0, IGRAPH_DIRECTED,
|
|
0,1, 0,2, 0,3, 1,0, 2,0, 3,0,
|
|
3,4, 3,5, 3,6, 3,7, 4,0, 5,0,
|
|
6,0, 7,0,
|
|
-1);
|
|
|
|
igraph_vector_init(&res, 0);
|
|
igraph_pagerank(&g, 0, &res, &value, 0.85, 0,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_ARPACK, &arpack_options);
|
|
printf("ARPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_pagerank(&g, 0, &res, &value, 0.85, 0,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_PRPACK, 0);
|
|
printf("PRPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_vector_destroy(&res);
|
|
igraph_destroy(&g);
|
|
|
|
/* Undirected star graph */
|
|
|
|
printf("\nUndirected star\n");
|
|
|
|
igraph_star(&g, 11, IGRAPH_STAR_UNDIRECTED, 0);
|
|
igraph_vector_init(&res, 0);
|
|
igraph_pagerank(&g, 0, &res, &value, 0.85, 0,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_ARPACK, &arpack_options);
|
|
printf("ARPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_pagerank(&g, 0, &res, &value, 0.85, 0,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_PRPACK, 0);
|
|
printf("PRPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
/* Check twice more for consistency, this time without explicitly
|
|
* supplied ARPACK options */
|
|
igraph_pagerank(&g, 0, &res, &value, 0.85, 0,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_ARPACK, 0);
|
|
printf("ARPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_pagerank(&g, 0, &res, &value, 0.85, 0,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_PRPACK, 0);
|
|
printf("PRPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_pagerank(&g, 0, &res, &value, 0.85, 0,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_ARPACK, 0);
|
|
printf("ARPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_pagerank(&g, 0, &res, &value, 0.85, 0,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_PRPACK, 0);
|
|
printf("PRPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
/* Check personalized PageRank */
|
|
|
|
printf("\nPersonalized PageRank\n");
|
|
|
|
igraph_personalized_pagerank_vs(&g, 0, &res, &value,
|
|
igraph_vss_1(1), 0.5, 0,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_ARPACK, &arpack_options);
|
|
printf("ARPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_personalized_pagerank_vs(&g, 0, &res, &value,
|
|
igraph_vss_1(1), 0.5, 0,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_PRPACK, 0);
|
|
printf("PRPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
|
|
/* Errors */
|
|
|
|
igraph_set_error_handler(igraph_error_handler_ignore);
|
|
igraph_vector_init(&reset, 2);
|
|
err = igraph_personalized_pagerank(&g, 0, &res, 0, &reset, 0.85, 0,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_ARPACK,
|
|
&arpack_options);
|
|
IGRAPH_ASSERT(err == IGRAPH_EINVAL);
|
|
|
|
err = igraph_personalized_pagerank(&g, 0, &res, 0, &reset, 0.85, 0,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_PRPACK, 0);
|
|
IGRAPH_ASSERT(err == IGRAPH_EINVAL);
|
|
|
|
igraph_vector_resize(&reset, 10);
|
|
igraph_vector_fill(&reset, 0);
|
|
err = igraph_personalized_pagerank(&g, 0,
|
|
&res, 0,
|
|
&reset, 0.85, 0, igraph_vss_all(), IGRAPH_PAGERANK_ALGO_ARPACK, &arpack_options);
|
|
IGRAPH_ASSERT(err == IGRAPH_EINVAL);
|
|
|
|
err = igraph_personalized_pagerank(&g, 0,
|
|
&res, 0,
|
|
&reset, 0.85, 0, igraph_vss_all(), IGRAPH_PAGERANK_ALGO_PRPACK, 0);
|
|
IGRAPH_ASSERT(err == IGRAPH_EINVAL);
|
|
|
|
igraph_vector_destroy(&reset);
|
|
igraph_destroy(&g);
|
|
igraph_set_error_handler(igraph_error_handler_abort);
|
|
|
|
|
|
/* Special cases: check for empty graph */
|
|
/* The ARPACK method has a special code path for this case */
|
|
|
|
printf("\nEdgeless graph\n");
|
|
|
|
igraph_empty(&g, 10, IGRAPH_UNDIRECTED);
|
|
igraph_pagerank(&g, 0, &res, &value, 0.85, 1,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_ARPACK, &arpack_options);
|
|
printf("ARPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_pagerank(&g, 0, &res, &value, 0.85, 1,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_PRPACK, 0);
|
|
printf("PRPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_destroy(&g);
|
|
|
|
/* Special cases: check for empty graph, personalized */
|
|
/* The ARPACK method has a special code path for this case */
|
|
|
|
printf("\nEdgeless graph, personalized PageRank\n");
|
|
|
|
igraph_empty(&g, 4, IGRAPH_UNDIRECTED);
|
|
igraph_vector_init_range(&reset, 1, 5);
|
|
|
|
igraph_personalized_pagerank(&g, 0, &res, &value, &reset, 0.85, 1,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_ARPACK, &arpack_options);
|
|
printf("ARPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_personalized_pagerank(&g, 0, &res, &value, &reset, 0.85, 1,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_PRPACK, 0);
|
|
printf("PRPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
/* We also test the personalized case on a non-empty disconnected case,
|
|
* which does not use the special code path for the ARPACK version.
|
|
* The result must be the same for ARPACK and PRPACK. */
|
|
|
|
printf("\nOne edge, two isolated vertices, personalized\n");
|
|
igraph_add_edge(&g, 0, 1);
|
|
|
|
igraph_personalized_pagerank(&g, 0, &res, &value, &reset, 0.85, 1,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_ARPACK, &arpack_options);
|
|
printf("ARPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_personalized_pagerank(&g, 0, &res, &value, &reset, 0.85, 1,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_PRPACK, 0);
|
|
printf("PRPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_vector_destroy(&reset);
|
|
igraph_destroy(&g);
|
|
|
|
/* Special cases: check for full graph, zero weights */
|
|
/* The ARPACK method has a special code path for this case */
|
|
|
|
printf("\nComplete graph, zero weights\n");
|
|
|
|
igraph_full(&g, 10, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS);
|
|
igraph_vector_init(&weights, 45);
|
|
igraph_vector_fill(&weights, 0);
|
|
igraph_pagerank(&g, &weights, &res, &value, 0.85, 1,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_ARPACK, &arpack_options);
|
|
printf("ARPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_pagerank(&g, &weights, &res, &value, 0.85, 1,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_PRPACK, 0);
|
|
printf("PRPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_vector_destroy(&weights);
|
|
igraph_destroy(&g);
|
|
|
|
/* Another test case for PageRank (bug #792352) */
|
|
|
|
printf("\nTest graph 3\n");
|
|
|
|
igraph_small(&g, 9, IGRAPH_DIRECTED, 0, 5, 1, 5, 2, 0, 3, 1, 5, 4, 5, 7, 6, 0, 8, 0, 8, 1, -1);
|
|
igraph_vector_init(&weights, 9);
|
|
VECTOR(weights)[0] = 4;
|
|
VECTOR(weights)[1] = 5;
|
|
VECTOR(weights)[2] = 5;
|
|
VECTOR(weights)[3] = 4;
|
|
VECTOR(weights)[4] = 4;
|
|
VECTOR(weights)[5] = 4;
|
|
VECTOR(weights)[6] = 3;
|
|
VECTOR(weights)[7] = 4;
|
|
VECTOR(weights)[8] = 4;
|
|
igraph_pagerank(&g, &weights, &res, &value, 0.85, 1,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_ARPACK, &arpack_options);
|
|
printf("ARPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_pagerank(&g, &weights, &res, &value, 0.85, 1,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_PRPACK, 0);
|
|
printf("PRPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_vector_destroy(&weights);
|
|
igraph_destroy(&g);
|
|
|
|
/* Multigraph */
|
|
|
|
printf("\nSmall undirected multigraph (unweighted)\n");
|
|
|
|
igraph_small(&g, 0, IGRAPH_UNDIRECTED,
|
|
0,1, 1,2, 1,2,
|
|
-1);
|
|
|
|
igraph_pagerank(&g, NULL, &res, &value, 0.85, 1,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_ARPACK, &arpack_options);
|
|
printf("ARPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_pagerank(&g, NULL, &res, &value, 0.85, 1,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_PRPACK, 0);
|
|
printf("PRPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
printf("\nSmall undirected multigraph (unit weights)\n");
|
|
|
|
igraph_vector_init(&weights, 3);
|
|
igraph_vector_fill(&weights, 1.0);
|
|
|
|
igraph_pagerank(&g, &weights, &res, &value, 0.85, 1,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_ARPACK, &arpack_options);
|
|
printf("ARPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_pagerank(&g, &weights, &res, &value, 0.85, 1,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_PRPACK, 0);
|
|
printf("PRPACK: "); print_vector(&res);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_vector_destroy(&weights);
|
|
igraph_destroy(&g);
|
|
|
|
igraph_vector_destroy(&res);
|
|
|
|
/* Graph with more than 127 vertices. PRPACK uses a different method above this size. */
|
|
|
|
{
|
|
igraph_vector_int_t edges_to_delete;
|
|
igraph_vector_t res_arpack, res_prpack;
|
|
igraph_vector_t weights;
|
|
igraph_int_t i, n;
|
|
|
|
printf("\nLarge test graph, unweighted\n");
|
|
|
|
/* 243 vertices, 729 edges */
|
|
igraph_de_bruijn(&g, 3, 5);
|
|
|
|
/* We delete some edges to break the symmetry of the graph.
|
|
* Otherwise all vertices would have the same PageRank. */
|
|
igraph_vector_int_init_range(&edges_to_delete, 0, 38);
|
|
igraph_delete_edges(&g, igraph_ess_vector(&edges_to_delete));
|
|
igraph_vector_int_destroy(&edges_to_delete);
|
|
|
|
/* Note: This test graph is not connected and has self-loops. */
|
|
|
|
igraph_vector_init(&res_arpack, 0);
|
|
igraph_vector_init(&res_prpack, 0);
|
|
|
|
igraph_pagerank(&g, NULL, &res_arpack, &value, 0.85, 1,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_ARPACK, &arpack_options);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_pagerank(&g, NULL, &res_prpack, &value, 0.85, 1,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_PRPACK, NULL);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
n = igraph_vector_size(&res_arpack);
|
|
for (i=0; i < n; ++i) {
|
|
igraph_real_t ar = VECTOR(res_arpack)[i];
|
|
igraph_real_t pr = VECTOR(res_prpack)[i];
|
|
if (fabs(ar - pr) > 1e-12) {
|
|
printf("Unexpected difference between ARPACK and PRPACK results for vertex %" IGRAPH_PRId ":\n"
|
|
"ARPACK: %g\n"
|
|
"PRPACK: %g\n"
|
|
"Difference: %g\n",
|
|
i, ar, pr, fabs(ar - pr));
|
|
}
|
|
}
|
|
|
|
printf("\nLarge test graph, weighted\n");
|
|
|
|
igraph_vector_init_range(&weights, igraph_ecount(&g) + 1, 2*igraph_ecount(&g) + 1);
|
|
|
|
igraph_pagerank(&g, &weights, &res_arpack, &value, 0.85, 1,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_ARPACK, &arpack_options);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
igraph_pagerank(&g, &weights, &res_prpack, &value, 0.85, 1,
|
|
igraph_vss_all(), IGRAPH_PAGERANK_ALGO_PRPACK, NULL);
|
|
IGRAPH_ASSERT(is_almost_one(value));
|
|
|
|
n = igraph_vector_size(&res_arpack);
|
|
for (i=0; i < n; ++i) {
|
|
igraph_real_t ar = VECTOR(res_arpack)[i];
|
|
igraph_real_t pr = VECTOR(res_prpack)[i];
|
|
if (fabs(ar - pr) > 1e-12) {
|
|
printf("Unexpected difference between ARPACK and PRPACK results for vertex %" IGRAPH_PRId ":\n"
|
|
"ARPACK: %g\n"
|
|
"PRPACK: %g\n"
|
|
"Difference: %g\n",
|
|
i, ar, pr, fabs(ar - pr));
|
|
}
|
|
}
|
|
|
|
igraph_vector_destroy(&weights);
|
|
|
|
igraph_vector_destroy(&res_arpack);
|
|
igraph_vector_destroy(&res_prpack);
|
|
|
|
igraph_destroy(&g);
|
|
}
|
|
|
|
VERIFY_FINALLY_STACK();
|
|
|
|
return 0;
|
|
}
|