/* igraph library. Copyright (C) 2021 The igraph development team 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 . */ #include #include "test_utilities.h" #include void test_spanner(igraph_t *graph, igraph_vector_int_t *spanner, double stretch, igraph_vector_t *weights) { igraph_int_t no_of_nodes = igraph_vcount(graph); igraph_int_t no_of_edges = igraph_ecount(graph); igraph_t spanner_graph; igraph_vector_t spanner_weights; igraph_matrix_t res_spanner, res_graph; /* create the spanner graph with igraph_subgraph_from_edges() as recommended in the docs, then compare it with the original graph and validate the stretch factor */ if (weights) { igraph_cattribute_EAN_setv(graph, "weight", weights); } igraph_subgraph_from_edges(graph, &spanner_graph, igraph_ess_vector(spanner), 0); igraph_vector_init(&spanner_weights, igraph_vector_int_size(spanner)); if (weights){ igraph_cattribute_EANV(&spanner_graph, "weight", igraph_ess_all(IGRAPH_EDGEORDER_ID), &spanner_weights); } else { igraph_vector_fill(&spanner_weights, 1); } // compare number of nodes IGRAPH_ASSERT(igraph_vcount(&spanner_graph) == no_of_nodes); // compare number of edges. We expect the number of edges to decrease in // all cases but the trivial ones if (stretch > 1 && no_of_edges > 1) { IGRAPH_ASSERT(igraph_ecount(&spanner_graph) < no_of_edges); } // print the number of nodes, the number of original edges and the new edge // count. This is not validated (there is no expected output) but it helps // to gauge whether the algorithm is not simply keeping most of the edges printf( "%" IGRAPH_PRId ", %" IGRAPH_PRId " --> %" IGRAPH_PRId "\n", no_of_nodes, no_of_edges, igraph_ecount(&spanner_graph) ); // Validate the stretch factor igraph_matrix_init(&res_spanner, 0, 0); igraph_matrix_init(&res_graph, 0, 0); igraph_distances(graph, weights, &res_graph, igraph_vss_all(), igraph_vss_all(), IGRAPH_ALL); igraph_distances(&spanner_graph, &spanner_weights, &res_spanner, igraph_vss_all(), igraph_vss_all(), IGRAPH_ALL); for (igraph_int_t x = 0; x < no_of_nodes; x++) { for (igraph_int_t y = 0; y < no_of_nodes; y++) { if (x == y) { continue; } IGRAPH_ASSERT(MATRIX(res_spanner, x, y) <= MATRIX(res_graph, x, y) * stretch); } } igraph_matrix_destroy(&res_graph); igraph_matrix_destroy(&res_spanner); // Clean up igraph_vector_destroy(&spanner_weights); igraph_destroy(&spanner_graph); } int main(void) { igraph_t graph; igraph_vector_t weights; igraph_vector_int_t spanner; igraph_int_t no_of_edges; /* Initialize attribute handler; we will use edge attributes in test_spanner() */ igraph_set_attribute_table(&igraph_cattribute_table); /* Seed the RNG to make the test output predictable */ igraph_rng_seed(igraph_rng_default(), 42); /* Create the output vector -- this will be re-used several times */ igraph_vector_int_init(&spanner, 0); /* Trivial spanner with stretch of one */ printf("Complete graph with stretch of one\n"); igraph_full(&graph, 20, IGRAPH_UNDIRECTED, 0); igraph_spanner(&graph, &spanner, 1, NULL); test_spanner(&graph, &spanner, 1, NULL); igraph_destroy(&graph); /* Test spanner for random weighted complete graph */ printf("Weighted complete graph with random weights and stretch = 10\n"); igraph_full(&graph, 20, IGRAPH_UNDIRECTED, 0); no_of_edges = igraph_ecount(&graph); igraph_vector_init(&weights, no_of_edges); for (int i = 0; i < no_of_edges; i++) { double generated_number = igraph_rng_get_unif(igraph_rng_default(), 1, 100); VECTOR(weights)[i] = generated_number; } igraph_spanner(&graph, &spanner, 10, &weights); test_spanner(&graph, &spanner, 10, &weights); igraph_vector_destroy(&weights); igraph_destroy(&graph); /* Test spanner for unweighted complete graph */ printf("Unweighted complete graph with stretch = 5\n"); igraph_full(&graph, 20, IGRAPH_UNDIRECTED, 0); igraph_spanner(&graph, &spanner, 5, NULL); test_spanner(&graph, &spanner, 5, NULL); igraph_destroy(&graph); /* Random Erdos-Renyi graph */ printf("Random Erdos-Renyi graph\n"); igraph_erdos_renyi_game_gnp(&graph, 200, 0.25, IGRAPH_UNDIRECTED, IGRAPH_SIMPLE_SW, IGRAPH_EDGE_UNLABELED); no_of_edges = igraph_ecount(&graph); igraph_vector_init(&weights, no_of_edges); for (igraph_int_t i = 0; i < no_of_edges; i++) { double generated_number = igraph_rng_get_unif(igraph_rng_default(), 1, 100); VECTOR(weights)[i] = generated_number; } igraph_spanner(&graph, &spanner, 7, &weights); test_spanner(&graph, &spanner, 7, &weights); igraph_vector_destroy(&weights); igraph_destroy(&graph); /* Geometric random graph */ printf("Geometric random graph, unweighted\n"); igraph_grg_game(&graph, 100, 0.2, /* torus = */ 0, 0, 0); igraph_spanner(&graph, &spanner, 7, 0); test_spanner(&graph, &spanner, 7, 0); igraph_destroy(&graph); /* Singleton graph */ printf("Singleton graph\n"); igraph_empty(&graph, 1, IGRAPH_UNDIRECTED); igraph_spanner(&graph, &spanner, 2, 0); test_spanner(&graph, &spanner, 2, 0); igraph_destroy(&graph); /* Null graph */ printf("Null graph\n"); igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); igraph_spanner(&graph, &spanner, 2, 0); test_spanner(&graph, &spanner, 2, 0); igraph_destroy(&graph); VERIFY_FINALLY_STACK(); /* Error conditions */ igraph_set_error_handler(igraph_error_handler_ignore); igraph_erdos_renyi_game_gnp(&graph, 200, 0.9, IGRAPH_UNDIRECTED, IGRAPH_SIMPLE_SW, IGRAPH_EDGE_UNLABELED); no_of_edges = igraph_ecount(&graph); igraph_vector_init(&weights, no_of_edges); for (igraph_int_t i = 0; i < no_of_edges; i++) { double generated_number = igraph_rng_get_unif(igraph_rng_default(), 1, 100); VECTOR(weights)[i] = generated_number; } printf("Negative weight\n"); VECTOR(weights)[10] = -42; IGRAPH_ASSERT(igraph_spanner(&graph, &spanner, 7, &weights) == IGRAPH_EINVAL); VECTOR(weights)[10] = 42; printf("NaN weight\n"); VECTOR(weights)[10] = IGRAPH_NAN; IGRAPH_ASSERT(igraph_spanner(&graph, &spanner, 7, &weights) == IGRAPH_EINVAL); VECTOR(weights)[10] = 42; printf("Invalid spanning factor\n"); IGRAPH_ASSERT(igraph_spanner(&graph, &spanner, 0.5, &weights) == IGRAPH_EINVAL); printf("Invalid weight vector length\n"); igraph_vector_resize(&weights, no_of_edges - 1); IGRAPH_ASSERT(igraph_spanner(&graph, &spanner, 7, &weights) == IGRAPH_EINVAL); igraph_vector_destroy(&weights); igraph_destroy(&graph); /* Regression test for https://github.com/igraph/igraph/issues/2487 * Edge directions should be ignored in directed graphs. */ igraph_rng_seed(igraph_rng_default(), 42); igraph_small(&graph, 2, IGRAPH_DIRECTED, 0, 1, -1); igraph_spanner(&graph, &spanner, 1.72, NULL); igraph_destroy(&graph); igraph_vector_int_destroy(&spanner); return IGRAPH_SUCCESS; }