Add graph references
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user