204 lines
7.7 KiB
C
204 lines
7.7 KiB
C
/*
|
|
igraph library.
|
|
Copyright (C) 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 "test_utilities.h"
|
|
#include <stdlib.h>
|
|
|
|
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;
|
|
}
|