Files
Abdelrahman Said a11edf0c53 Add graph references
2026-06-28 13:49:01 +01:00

365 lines
14 KiB
C

/*
igraph library.
Copyright (C) 2025 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"
igraph_error_t percolate_bond(igraph_t *graph, igraph_vector_int_t *edge_indices, igraph_bool_t printing) {
igraph_vector_int_t giant_size, vertex_count;
igraph_int_t ecount = igraph_ecount(graph);
igraph_int_t number_percolated;
if (edge_indices != NULL) {
number_percolated = igraph_vector_int_size(edge_indices);
} else {
number_percolated = ecount;
}
IGRAPH_VECTOR_INT_INIT_FINALLY(&giant_size, 0);
IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_count, 0);
IGRAPH_CHECK(igraph_bond_percolation(graph, &giant_size, &vertex_count, edge_indices));
if (printing) {
print_vector_int(&giant_size);
print_vector_int(&vertex_count);
}
if (number_percolated == ecount) {
// It's only guaranteed to have the same component ecount if all edges are included.
igraph_vector_int_t component_sizes;
IGRAPH_VECTOR_INT_INIT_FINALLY(&component_sizes, 0);
IGRAPH_CHECK(igraph_connected_components(graph, NULL, &component_sizes, NULL, IGRAPH_WEAK));
IGRAPH_ASSERT(igraph_vector_int_size(&giant_size) == ecount);
IGRAPH_ASSERT(igraph_vector_int_size(&vertex_count) == ecount);
if (ecount > 1) {
IGRAPH_ASSERT(igraph_vector_int_max(&giant_size) == igraph_vector_int_max(&component_sizes));
}
igraph_vector_int_destroy(&component_sizes);
IGRAPH_FINALLY_CLEAN(1);
}
igraph_int_t prev = 0;
for (igraph_int_t i = 0; i < number_percolated; i++) {
IGRAPH_ASSERT(VECTOR(giant_size)[i] > 0); // Sizes cannot be negative.
IGRAPH_ASSERT(VECTOR(giant_size)[i] >= prev); // Size of largest component must be nondecreasing.
IGRAPH_ASSERT(VECTOR(giant_size)[i] <= i + 2); // Largest component cannot be bigger than a tree with the same number of edges.
prev = VECTOR(giant_size)[i];
}
prev = 0;
for (igraph_int_t i = 0; i < number_percolated; i++) {
IGRAPH_ASSERT(VECTOR(vertex_count)[i] > 0); // Sizes cannot be negative.
IGRAPH_ASSERT(VECTOR(vertex_count)[i] >= prev); // Size of largest component must be nondecreasing.
IGRAPH_ASSERT(VECTOR(vertex_count)[i] <= 2*i + 2); // Largest component cannot be bigger than a tree with the same number of edges.
prev = VECTOR(vertex_count)[i];
}
igraph_vector_int_destroy(&giant_size);
igraph_vector_int_destroy(&vertex_count);
IGRAPH_FINALLY_CLEAN(2);
return IGRAPH_SUCCESS;
}
void test_bond(void) {
// Test with normal graph and provided edge list
igraph_t k_3, c_4, karate, random, null_graph, singleton;
igraph_empty(&null_graph, 0, false);
igraph_empty(&singleton, 1, false);
igraph_full(&k_3, 3, false, false);
igraph_small(&c_4, 4, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 3, 3, 0, -1);
igraph_famous(&karate, "Zachary");
igraph_erdos_renyi_game_gnp(&random, 100, 0.01, IGRAPH_UNDIRECTED, IGRAPH_MULTI_SW, IGRAPH_EDGE_UNLABELED);
printf("# Bond percolation test suite\n");
printf("Null graph, no provided edge order.\n");
percolate_bond(&null_graph, NULL, true);
printf("Singleton graph, no provided edge order.\n");
percolate_bond(&singleton, NULL, true);
printf("K_3 graph percolation curve, no provided edge sequence.\n");
percolate_bond(&k_3, NULL, true); // sequence is random, but since it should be consistent it doesn't matter
igraph_vector_int_t edge_ids;
igraph_vector_int_init_int(&edge_ids, 4, 0, 2, 1, 3);
printf("C_4 graph with edge sequence ( 0 2 1 3 ).\n");
percolate_bond(&c_4, &edge_ids, true);
igraph_vector_int_destroy(&edge_ids);
printf("Zachary karate graph, no edge list given.\n");
// Karate graph, no edge list given
percolate_bond(&karate, NULL, false);
// Generated disconnected graph, 100 vertices, p=0.01
igraph_vector_int_t storage_order;
igraph_vector_int_init_range(&storage_order, 0, igraph_ecount(&karate));
printf("Zachary karate graph, edges in storage order.\n");
percolate_bond(&karate, &storage_order, true);
igraph_vector_int_destroy(&storage_order);
percolate_bond(&random, NULL, false); // sanity check
printf("Error on duplicates\n");
igraph_vector_int_t duplicates;
igraph_vector_int_init_int(&duplicates, 5, 0,1,2,3,3);
CHECK_ERROR(percolate_bond(&c_4, &duplicates, false), IGRAPH_EINVAL);
igraph_vector_int_destroy(&duplicates);
printf("Null outputs\n");
igraph_bond_percolation(&karate, NULL, NULL, NULL);
igraph_destroy(&singleton);
igraph_destroy(&null_graph);
igraph_destroy(&k_3);
igraph_destroy(&c_4);
igraph_destroy(&karate);
igraph_destroy(&random);
VERIFY_FINALLY_STACK();
}
igraph_error_t percolate_site(igraph_t *graph, igraph_vector_int_t *vert_indices, igraph_bool_t printing) {
igraph_vector_int_t outputs, edge_counts;
igraph_int_t vcount = igraph_vcount(graph);
igraph_int_t number_percolated;
if (vert_indices != NULL) {
number_percolated = igraph_vector_int_size(vert_indices);
}
else {
number_percolated = vcount;
}
IGRAPH_VECTOR_INT_INIT_FINALLY(&outputs, 0);
IGRAPH_VECTOR_INT_INIT_FINALLY(&edge_counts, 0);
IGRAPH_CHECK(igraph_site_percolation(graph, &outputs, &edge_counts, vert_indices));
if (printing) {
print_vector_int(&outputs);
print_vector_int(&edge_counts);
}
igraph_vector_int_t component_sizes;
IGRAPH_VECTOR_INT_INIT_FINALLY(&component_sizes, 0);
IGRAPH_CHECK(igraph_connected_components(graph, NULL, &component_sizes, NULL, IGRAPH_WEAK));
IGRAPH_ASSERT(igraph_vector_int_size(&outputs) == number_percolated);
if (number_percolated > 1 && number_percolated == vcount) {
// it is only guaranteed to have the same component vcount if all vertices are included
IGRAPH_ASSERT(igraph_vector_int_max(&outputs) == igraph_vector_int_max(&component_sizes));
}
igraph_vector_int_destroy(&component_sizes);
IGRAPH_FINALLY_CLEAN(1);
igraph_int_t prev = 0;
for (igraph_int_t i = 0; i < number_percolated; i++) {
IGRAPH_ASSERT(VECTOR(outputs)[i] <= vcount); //vcount cannot be greater than number of vertices
IGRAPH_ASSERT(VECTOR(outputs)[i] > 0); // Sizes cannot be negative.
IGRAPH_ASSERT(VECTOR(outputs)[i] >= prev); // Size of largest component must be nondecreasing.
IGRAPH_ASSERT(VECTOR(outputs)[i] <= i + 1); // Largest component cannot be bigger than a tree with the same number of vertices.
prev = VECTOR(outputs)[i];
}
prev = 0;
for (igraph_int_t i = 0; i < number_percolated; i++) {
IGRAPH_ASSERT(VECTOR(edge_counts)[i] >= 0); // Sizes cannot be negative.
IGRAPH_ASSERT(VECTOR(edge_counts)[i] >= prev); // Size of largest component must be nondecreasing.
prev = VECTOR(edge_counts)[i];
}
igraph_vector_int_destroy(&edge_counts);
IGRAPH_FINALLY_CLEAN(1);
igraph_vector_int_destroy(&outputs);
IGRAPH_FINALLY_CLEAN(1);
return IGRAPH_SUCCESS;
}
void test_site(void) {
// Test with normal graph and provided edge list
igraph_t k_5, c_4, karate, random, null_graph, singleton;
igraph_empty(&null_graph, 0, false);
igraph_empty(&singleton, 1, false);
igraph_full(&k_5, 5, false, false);
igraph_small(&c_4, 4, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 3, 3, 0, -1);
igraph_famous(&karate, "Zachary");
igraph_erdos_renyi_game_gnp(&random, 100, 0.01, IGRAPH_UNDIRECTED, IGRAPH_MULTI_SW, IGRAPH_EDGE_UNLABELED);
printf("# Site percolation test suite\n");
printf("Null graph, no provided vertex order.\n");
percolate_site(&null_graph, NULL, true);
printf("Singleton graph, no provided vertex order.\n");
percolate_site(&singleton, NULL, true);
printf("K_5 graph percolation curve, no provided vertex sequence.\n");
percolate_site(&k_5, NULL, true); // sequence is random, but since it should be consistent it doesn't matter
igraph_vector_int_t vertex_ids;
igraph_vector_int_init_int(&vertex_ids, 4, 0, 2, 1, 3);
printf("C_4 graph with vertex sequence ( 0 2 1 3 ).\n");
percolate_site(&c_4, &vertex_ids, true);
igraph_vector_int_destroy(&vertex_ids);
printf("Zachary karate graph, no vertex list given.\n");
// Karate graph, no vertex list given
percolate_site(&karate, NULL, false);
// Generated disconnected graph, 100 vertices, p=0.01
igraph_vector_int_t storage_order;
igraph_vector_int_init_range(&storage_order, 0, igraph_vcount(&karate));
printf("Zachary karate graph, vertices in storage order.\n");
percolate_site(&karate, &storage_order, true);
igraph_vector_int_destroy(&storage_order);
percolate_site(&random, NULL, false);
igraph_vector_int_t bad_vert_list_repeat, bad_vert_list_too_big, vert_list_missing_vertex;
igraph_vector_int_init_int(&bad_vert_list_too_big, 6, 0, 1, 2, 3, 4, 5);
igraph_vector_int_init_int(&vert_list_missing_vertex, 3, 0, 1, 2);
igraph_vector_int_init_int(&bad_vert_list_repeat, 5, 0, 0, 0, 0, 0);
// should error due to vertex being too big
printf("K_5 with too big vertex index\n");
CHECK_ERROR(percolate_site(&k_5, &bad_vert_list_too_big, false), IGRAPH_EINVVID);
// missing vertices are allowed
printf("K_5 with missing vertices\n");
percolate_site(&k_5, &vert_list_missing_vertex, true);
// should error due to repeated vertices
printf("K_5 with repeated vertices\n");
CHECK_ERROR(percolate_site(&k_5, &bad_vert_list_repeat, false), IGRAPH_EINVAL);
printf("Null outputs\n");
igraph_bond_percolation(&karate, NULL, NULL, NULL);
igraph_destroy(&singleton);
igraph_destroy(&null_graph);
igraph_destroy(&k_5);
igraph_destroy(&c_4);
igraph_destroy(&karate);
igraph_destroy(&random);
igraph_vector_int_destroy(&vert_list_missing_vertex);
igraph_vector_int_destroy(&bad_vert_list_repeat);
igraph_vector_int_destroy(&bad_vert_list_too_big);
VERIFY_FINALLY_STACK();
}
igraph_error_t percolate_edgelist(igraph_vector_int_t *edges, igraph_bool_t printing) {
igraph_vector_int_t giant_size, vertex_count;
igraph_t graph;
igraph_int_t ecount;
IGRAPH_VECTOR_INT_INIT_FINALLY(&giant_size, 0);
IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_count, 0);
IGRAPH_CHECK(igraph_edgelist_percolation(edges, &giant_size, &vertex_count));
if (printing) {
print_vector_int(&giant_size);
print_vector_int(&vertex_count);
}
igraph_vector_int_t component_sizes;
IGRAPH_VECTOR_INT_INIT_FINALLY(&component_sizes, 0);
IGRAPH_CHECK(igraph_create(&graph, edges, 0, false));
ecount = igraph_ecount(&graph);
IGRAPH_CHECK(igraph_connected_components(&graph,NULL, &component_sizes,NULL,IGRAPH_WEAK));
igraph_destroy(&graph);
IGRAPH_ASSERT(igraph_vector_int_size(&giant_size) == ecount);
if (ecount > 1) {
IGRAPH_ASSERT(igraph_vector_int_max(&giant_size) == igraph_vector_int_max(&component_sizes));
}
igraph_int_t prev = 0;
for (igraph_int_t i = 0; i < ecount; i++) {
// Sizes cannot be negative.
IGRAPH_ASSERT(VECTOR(giant_size)[i] > 0);
// Size of largest component must be nondecreasing.
IGRAPH_ASSERT(VECTOR(giant_size)[i] >= prev);
// Largest component cannot be bigger than a tree with the same number of edges.
IGRAPH_ASSERT(VECTOR(giant_size)[i] <= i + 2);
prev = VECTOR(giant_size)[i];
}
prev = 0;
for (igraph_int_t i = 0; i < ecount; i++) {
// Sizes cannot be negative.
IGRAPH_ASSERT(VECTOR(vertex_count)[i] > 0);
// Size of largest component must be nondecreasing.
IGRAPH_ASSERT(VECTOR(vertex_count)[i] >= prev);
// Largest component cannot be bigger than a tree with the same number of edges.
IGRAPH_ASSERT(VECTOR(vertex_count)[i] <= 2*i + 2);
prev = VECTOR(vertex_count)[i];
}
igraph_vector_int_destroy(&giant_size);
igraph_vector_int_destroy(&component_sizes);
igraph_vector_int_destroy(&vertex_count);
IGRAPH_FINALLY_CLEAN(3);
return IGRAPH_SUCCESS;
}
void test_edgelist_percolation(void) {
// Edge list percolation is already called from bond percolation,
// so this mostly tests for expected errors that cannot occur from generated edge lists.
igraph_vector_int_t odd, negative, loopy;
igraph_vector_int_init_int(&odd, 5, 0, 1, 1, 2, 1);
igraph_vector_int_init_int(&negative, 6, -1, 1, 0, 0, 1, -1);
igraph_vector_int_init_int(&loopy, 8, 0, 0, 0, 1, 1, 2, 2, 0);
printf("# Edge list percolation\n");
printf("Percolation with ( 0 1 1 2 1 ), odd number of entries\n");
CHECK_ERROR(percolate_edgelist(&odd, false), IGRAPH_EINVAL);
printf("Percolation with ( -1 1 0 0 1 -1 ), negative numbers\n");
CHECK_ERROR(percolate_edgelist(&negative, false), IGRAPH_EINVVID);
printf("Percolation with ( 0 0 0 1 1 2 2 0 ), k_3 with loop\n");
percolate_edgelist(&loopy, true);
printf("Null outputs\n");
igraph_edgelist_percolation(&loopy, NULL, NULL);
igraph_vector_int_destroy(&odd);
igraph_vector_int_destroy(&negative);
igraph_vector_int_destroy(&loopy);
VERIFY_FINALLY_STACK();
}
int main(void) {
igraph_rng_seed(igraph_rng_default(), 30062025);
test_bond();
test_site();
test_edgelist_percolation();
return 0;
}