From fe6463fd000be4adcb93f58c9660fcd497de235d Mon Sep 17 00:00:00 2001 From: Slobodan Jelic Date: Sat, 4 Jan 2025 06:10:19 +0100 Subject: [PATCH] second working, with arg parser, optimized, group creation is unoptimized --- .vscode/settings.json | 5 +- Makefile | 4 +- include/random_graph.h | 9 +- include/random_group_steiner.h | 4 +- include/random_groups.h | 2 +- random_instance.stp | 0 requirements.txt | 1 + src/main.c | 189 +++++++++++++++++++++++++++--- src/random_edge_weight.c | 19 +-- src/random_graph.c | 204 ++++++++++++++++++--------------- src/random_group_steiner.c | 92 +++++++++++---- src/random_groups.c | 80 +++++++++++-- src/random_vertex_weight.c | 13 ++- 13 files changed, 465 insertions(+), 157 deletions(-) delete mode 100644 random_instance.stp create mode 100644 requirements.txt diff --git a/.vscode/settings.json b/.vscode/settings.json index 9efaf49..53e6c48 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,9 @@ "rand_large.h": "c", "stdint.h": "c", "random_group_steiner.h": "c", - "random_vertex_weight.h": "c" + "random_vertex_weight.h": "c", + "getopt.h": "c", + "string.h": "c", + "stdlib.h": "c" } } \ No newline at end of file diff --git a/Makefile b/Makefile index 541e33a..7b26853 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # Compiler and Flags CC = gcc -CFLAGS = -Wall -Wextra -Iinclude -g -pg -O0 -# CFLAGS = -O3 -march=native -DNDEBUG -Wall -Wextra -Werror -Iinclude -s -flto -fno-math-errno +# CFLAGS = -Wall -Wextra -Iinclude -g -pg -O0 +CFLAGS = -O3 -march=native -DNDEBUG -Wall -Wextra -Werror -Iinclude -s -flto -fno-math-errno LDFLAGS = -lm -flto # Directories diff --git a/include/random_graph.h b/include/random_graph.h index 94104db..975b2ce 100644 --- a/include/random_graph.h +++ b/include/random_graph.h @@ -7,6 +7,9 @@ #include #include "rand_large.h" + + + // Edge structure typedef struct Edge { uint32_t src; @@ -15,9 +18,11 @@ typedef struct Edge { // Function prototypes int cmpInt(const void *a, const void *b); +uint64_t permuted(uint64_t pos, uint64_t a, uint64_t c, uint64_t n); +int8_t binarySearch(uint64_t *arr, uint64_t size, uint64_t key); uint64_t uv2index(uint32_t u, uint32_t v, uint32_t n); -uint32_t min(uint32_t u, uint32_t v); -uint32_t max(uint32_t u, uint32_t v); +uint64_t min(uint64_t u, uint64_t v); +uint64_t max(uint64_t u, uint64_t v); int8_t index2uv(uint64_t index, uint32_t n, uint32_t *u, uint32_t *v); void random_connected_graph( uint32_t n, uint64_t m, Edge *edges, uint16_t* seed); diff --git a/include/random_group_steiner.h b/include/random_group_steiner.h index 372631d..d573eb6 100644 --- a/include/random_group_steiner.h +++ b/include/random_group_steiner.h @@ -6,8 +6,8 @@ // Function to generate a random Group Steiner instance void generate_random_group_steiner_instance( - uint32_t n, uint32_t m, uint16_t k, const char *filename, + uint32_t n, uint64_t m, uint16_t k, const char* e, const char* v, const char* p, char* filename, float vertex_a, float vertex_b, const char *vertex_weight_type, - float edge_a, float edge_b, const char *edge_weight_family, const char *edge_weight_type, const char *instance_type, uint16_t* seed); + float edge_a, float edge_b, const char *edge_weight_family, const char *edge_weight_type, float group_penalty_a, float group_penalty_b, const char* group_penalty_type, uint32_t* group_size, const char* group_size_mode, char* group_mode, const char *instance_type, const char* instance_type_code, uint16_t* seed); #endif // RANDOM_GROUP_STEINER_H diff --git a/include/random_groups.h b/include/random_groups.h index cf1fb10..9c81792 100644 --- a/include/random_groups.h +++ b/include/random_groups.h @@ -13,6 +13,6 @@ typedef struct { // Function to generate random groups void shuffle(uint32_t *array, uint32_t size, uint16_t* seed); -void generate_random_groups(uint32_t n, uint16_t k, Group *groups, uint16_t* seed); +void generate_random_groups(uint32_t n, uint16_t k, Group *groups, uint32_t* group_size, const char* group_size_mode, char* groups_disjoint, uint16_t* seed); #endif // RANDOM_GROUPS_H diff --git a/random_instance.stp b/random_instance.stp deleted file mode 100644 index e69de29..0000000 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1411a4a --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +pandas \ No newline at end of file diff --git a/src/main.c b/src/main.c index 64a7584..76515b8 100644 --- a/src/main.c +++ b/src/main.c @@ -1,27 +1,186 @@ #include +#include +#include +#include +#include #include "random_group_steiner.h" +void print_usage(const char *prog_name) { + printf("Usage: %s [OPTIONS]\n", prog_name); + printf("\nOptions:\n"); + printf(" -n Number of vertices (default: 10)\n"); + printf(" -m Number of edges (default: 30)\n"); + printf(" -k Number of groups (default: 4)\n"); + printf(" -s Random seed (default: 12345)\n"); + printf(" -e If 'yes', non-zero edge weights are generated, else if 'no', no weights provided (default: yes)\n"); + printf(" -v If 'yes', non-zero node weights are generated, else if 'no', no weights provided (default: yes)\n"); + printf(" -p If 'yes', group penalites are generated, else if 'no', penalites are not generated (default: no)\n"); + printf(" -f Output filename (default: auto-generated)\n"); + printf(" --vertex-a Lower bound for vertex weights (default: 1.0)\n"); + printf(" --vertex-b Upper bound for vertex weights (default: 10.0)\n"); + printf(" --vertex-type Vertex weight type (default: int)\n"); + printf(" --edge-a Lower bound for edge weights (default: 1.0)\n"); + printf(" --edge-b Upper bound for edge weights (default: 10.0)\n"); + printf(" --edge-family Edge weight family (default: RANDOM)\n"); + printf(" --edge-type Edge weight type (default: int)\n"); + printf(" --group-penalty-a Lower bound for group penalties (default: 1.0)\n"); + printf(" --group-penalty-b Upper bound for group penalties (default: 10.0)\n"); + printf(" --group-penalty-type Group penalty type (default: int)\n"); + printf(" --group-size Group size (default: 10.0)\n"); + printf(" --group-size-mode Group size mode (default: auto)\n"); + printf(" --group-mode If 'disjoint' groups tend to be disjoint, if 'overlap', groups may overlap (default: overlap)\n"); + printf(" --instance-type Instance type description (default: Node Weighted Group Steiner Tree Instance)\n"); +} -int main() { - uint32_t n = 90000; // Number of vertices - uint64_t m = 1000000; // Number of edges - uint16_t k = 2000; // Number of groups - uint16_t seed = 12345; +int main(int argc, char *argv[]) { + uint32_t n = 10; // Number of vertices + uint64_t m = 30; // Number of edges + uint16_t k = 4; // Number of groups + uint16_t seed = 12345; // Random seed + const char *e = "yes"; + const char *v = "yes"; + const char *p = "no"; + uint32_t group_size = n; - - - const char *filename = "random_instance.stp"; + char filename[256] = ""; float vertex_a = 1.0, vertex_b = 10.0; - const char *vertex_weight_type = "float"; + const char *vertex_weight_type = "int"; float edge_a = 1.0, edge_b = 10.0; - const char *edge_weight_family = "random"; - const char *edge_weight_type = "float"; - const char *instance_type = "Node Weighted Group Steiner Tree Instance"; + const char *edge_weight_family = "RANDOM"; + const char *edge_weight_type = "int"; + float group_penalty_a = 1.0, group_penalty_b = 10.0; + const char *group_penalty_type = "int"; + char instance_type[256]; + char instance_type_code[256]; + const char *instance_type_per_size = "Group "; + const char *instance_type_code_per_size = "G"; + const char *group_size_mode = "auto"; + char group_mode[256] = "overlap"; + + + struct option long_options[] = { + {"vertex-a", required_argument, 0, 0}, + {"vertex-b", required_argument, 0, 0}, + {"vertex-type", required_argument, 0, 0}, + {"edge-a", required_argument, 0, 0}, + {"edge-b", required_argument, 0, 0}, + {"edge-family", required_argument, 0, 0}, + {"edge-type", required_argument, 0, 0}, + {"group-penalty-a", required_argument, 0, 0}, + {"group-penalty-b", required_argument, 0, 0}, + {"group-penalty-type", required_argument, 0, 0}, + {"group-size", required_argument, 0, 0}, + {"group-size-mode", required_argument, 0, 0}, + {"group-mode", required_argument, 0, 0}, + {0, 0, 0, 0} + }; + + int opt; + int long_index = 0; + + while ((opt = getopt_long(argc, argv, "n:m:k:s:e:v:p:f:", long_options, &long_index)) != -1) { + switch (opt) { + case 'n': + n = (uint32_t)atoi(optarg); + break; + case 'm': + m = (uint64_t)atoll(optarg); + break; + case 'k': + k = (uint16_t)atoi(optarg); + break; + case 's': + seed = (uint16_t)atoi(optarg); + break; + case 'e': + e = optarg; + break; + case 'v': + v = optarg; + break; + case 'p': + p = optarg; + break; + case 'f': + snprintf(filename, sizeof(filename), "%s", optarg); + break; + case 0: + if (strcmp(long_options[long_index].name, "vertex-a") == 0) { + vertex_a = atof(optarg); + } else if (strcmp(long_options[long_index].name, "vertex-b") == 0) { + vertex_b = atof(optarg); + } else if (strcmp(long_options[long_index].name, "vertex-type") == 0) { + vertex_weight_type = optarg; + } else if (strcmp(long_options[long_index].name, "edge-a") == 0) { + edge_a = atof(optarg); + } else if (strcmp(long_options[long_index].name, "edge-b") == 0) { + edge_b = atof(optarg); + } else if (strcmp(long_options[long_index].name, "edge-family") == 0) { + edge_weight_family = optarg; + } else if (strcmp(long_options[long_index].name, "edge-type") == 0) { + edge_weight_type = optarg; + } else if (strcmp(long_options[long_index].name, "group-penalty-a") == 0) { + group_penalty_a = atof(optarg); + } else if (strcmp(long_options[long_index].name, "group-penalty-b") == 0) { + group_penalty_b = atof(optarg); + } else if (strcmp(long_options[long_index].name, "group-penalty-type") == 0) { + group_penalty_type = optarg; + } else if (strcmp(long_options[long_index].name, "group-size") == 0) { + group_size = atoi(optarg); + } else if (strcmp(long_options[long_index].name, "group-size-mode") == 0) { + group_size_mode = optarg; + } else if (strcmp(long_options[long_index].name, "group-mode") == 0) { + snprintf(group_mode, sizeof(group_mode), "%s", optarg); + } + break; + default: + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + } + + if (group_size == 1) { + instance_type_per_size = ""; + instance_type_code_per_size = ""; + } else if (group_size <= 0 || group_size > n){ + fprintf(stderr, "Group size is: %d. MUST BE at least 1 and at most n=%d.\n", group_size, n); + exit(EXIT_FAILURE); + } + + if (strcmp(e,"yes") == 0 && strcmp(v,"yes")==0 && strcmp(p,"yes")==0){ + snprintf(instance_type, sizeof(instance_type), "Prize-Collecting %sSteiner Tree Instance with both edge and node weights", instance_type_per_size); + snprintf(instance_type_code, sizeof(instance_type_code), "PCNWEW%sST", instance_type_code_per_size); + }else if (strcmp(e,"yes") == 0 && strcmp(v,"yes")==0 && strcmp(p,"no")==0){ + snprintf(instance_type, sizeof(instance_type), "%sSteiner Tree Instance with both edge and node weights", instance_type_per_size); + snprintf(instance_type_code, sizeof(instance_type_code), "NWEW%sST", instance_type_code_per_size); + }else if (strcmp(e,"yes") == 0 && strcmp(v,"no")==0 && strcmp(p,"yes")==0){ + snprintf(instance_type, sizeof(instance_type), "Prize-Collecting %sSteiner Tree Instance", instance_type_per_size); + snprintf(instance_type_code, sizeof(instance_type_code), "PCEW%sST", instance_type_code_per_size); + }else if (strcmp(e,"yes") == 0 && strcmp(v,"no")==0 && strcmp(p,"no")==0){ + snprintf(instance_type, sizeof(instance_type), "%sSteiner Tree Instance", instance_type_per_size); + snprintf(instance_type_code, sizeof(instance_type_code), "EW%sST", instance_type_code_per_size); + }else if (strcmp(e,"no") == 0 && strcmp(v,"yes")==0 && strcmp(p,"no")==0){ + snprintf(instance_type, sizeof(instance_type), "Node Weighted %sSteiner Tree Instance", instance_type_per_size); + snprintf(instance_type_code, sizeof(instance_type_code), "NW%sST", instance_type_code_per_size); + }else if (strcmp(e,"no") == 0 && strcmp(v,"yes")==0 && strcmp(p,"yes")==0){ + snprintf(instance_type, sizeof(instance_type), "Prize-Collecting Node Weighted %sSteiner Tree Instance", instance_type_per_size); + snprintf(instance_type_code, sizeof(instance_type_code), "PCNW%sST", instance_type_code_per_size); + }else if (strcmp(e,"no") == 0 && strcmp(v,"no")==0 && strcmp(p,"yes")==0){ + snprintf(instance_type, sizeof(instance_type), "Prize-Collecting Unweighted %sSteiner Tree Instance", instance_type_per_size); + snprintf(instance_type_code, sizeof(instance_type_code), "PCUW%sST", instance_type_code_per_size); + }else{ + snprintf(instance_type, sizeof(instance_type), "Unweighted %sSteiner Tree Instance", instance_type_per_size); + snprintf(instance_type_code, sizeof(instance_type_code), "UW%sST", instance_type_code_per_size); + } + + + + generate_random_group_steiner_instance( - n, m, k, filename, + n, m, k, e, v, p, filename, vertex_a, vertex_b, vertex_weight_type, - edge_a, edge_b, edge_weight_family, edge_weight_type, instance_type, &seed); + edge_a, edge_b, edge_weight_family, edge_weight_type, group_penalty_a, group_penalty_b, group_penalty_type, &group_size, group_size_mode, group_mode, instance_type, instance_type_code, &seed); - printf("Random Group Steiner instance written to %s\n", filename); + printf("Random %s written to %s\n",instance_type, filename); return 0; } diff --git a/src/random_edge_weight.c b/src/random_edge_weight.c index 3f9ae89..61cf095 100644 --- a/src/random_edge_weight.c +++ b/src/random_edge_weight.c @@ -19,29 +19,34 @@ float generate_random_edge_weight(float *x, float *y, unsigned int N, unsigned i exit(EXIT_FAILURE); } - float weight = 1.0; + float weight = 0.0; - if (strcmp(family, "euclid") == 0) { + if (strcmp(family, "EUCLID") == 0) { // Euclidean distance float dx = x[s] - x[t]; float dy = y[s] - y[t]; weight += sqrt(dx * dx + dy * dy); - } else if (strcmp(family, "grid") == 0) { + } else if (strcmp(family, "GRID") == 0) { // Manhattan distance weight += fabs(x[s] - x[t]) + fabs(y[s] - y[t]); - } else if (strcmp(family, "random") == 0) { + } else if (strcmp(family, "RANDOM") == 0) { // Uniform random weight weight += a + ((float)rand_r((unsigned int* )seed) / RAND_MAX) * (b - a); } else { - fprintf(stderr, "Error: Invalid weight type '%s'. Use 'euclid', 'grid', or 'random'.\n", family); + fprintf(stderr, "Error: Invalid edge weight family '%s'. Use 'EUCLID', 'GRID', or 'RANDOM'.\n", family); exit(EXIT_FAILURE); } // Cast to integer if the type is "int" if (strcmp(type, "int") == 0) { weight = (float)((int)weight); - } else if (strcmp(type, "float") != 0) { - fprintf(stderr, "Error: Invalid type '%s'. Use 'int' or 'float'.\n", type); + if(weight < 1.0) weight = 1.0; + } + else if (strcmp(type, "float") == 0){ + if(weight < 1e-8) weight = 1e-8; + } + else{ + fprintf(stderr, "Error: Invalid edge weight type '%s'. Use 'int' or 'float'.\n", type); exit(EXIT_FAILURE); } diff --git a/src/random_graph.c b/src/random_graph.c index 0c97da5..d94986c 100644 --- a/src/random_graph.c +++ b/src/random_graph.c @@ -10,15 +10,15 @@ int cmpInt(const void *a, const void *b) { } -inline uint32_t min(uint32_t u, uint32_t v){ +inline uint64_t min(uint64_t u, uint64_t v){ return u < v ? u : v; } -inline uint32_t max(uint32_t u, uint32_t v){ +inline uint64_t max(uint64_t u, uint64_t v){ return u < v ? v : u; } -uint64_t uv2index(uint32_t u, uint32_t v, uint32_t n) { +inline uint64_t uv2index(uint32_t u, uint32_t v, uint32_t n) { if (u < n && v < n && u < v) { uint64_t uu = (uint64_t)(u); uint64_t vv = (uint64_t)(v); @@ -32,18 +32,18 @@ uint64_t uv2index(uint32_t u, uint32_t v, uint32_t n) { return (uint64_t)(-1); } } - fprintf(stderr, "Error: Invalid u or v values.\n"); + fprintf(stderr, "Error: Invalid u or v values. u = %u, v = %u, n=%u \n", u, v, n); return (uint64_t)(-1); } -int8_t index2uv(uint64_t index, uint32_t n, uint32_t *u, uint32_t *v) { - if (index < (n * (n - 1)) / 2) { - int64_t nn = (int64_t)n; - int64_t indexx = (int64_t)index; - int64_t dd = (2 * nn - 1) * (2 * nn - 1) - 8 * indexx; - // double uur = ((double)(2 * n - 1) - sqrt(dd)) / 2; - int64_t uu = ((double)(2 * n - 1) - sqrt(dd)) / 2; - int64_t vv = uu + index + 1 - uu * (2*n - uu - 1) / 2; +inline int8_t index2uv(uint64_t index, uint32_t n, uint32_t *u, uint32_t *v) { + int64_t nn = (int64_t)n; + int64_t indexx = (int64_t)index; + int64_t dd = (2 * nn - 1) * (2 * nn - 1) - 8 * indexx; + // double uur = ((double)(2 * n - 1) - sqrt(dd)) / 2; + int64_t uu = ((double)(2 * nn - 1) - sqrt(dd)) / 2; + int64_t vv = uu + index + 1 - uu * (2*nn - uu - 1) / 2; + if (indexx < (nn * (nn - 1)) / 2) { if (uu >= 0 && uu < n && vv >= uu + 1 && vv < n) { *u = (uint32_t)uu; *v = (uint32_t)vv; @@ -62,112 +62,134 @@ int8_t index2uv(uint64_t index, uint32_t n, uint32_t *u, uint32_t *v) { return -1; } -void random_connected_graph( - uint32_t n, uint64_t m, Edge *edges, uint16_t* seed) { +inline uint64_t permuted(uint64_t pos, uint64_t a, uint64_t c, uint64_t n) { + return (((uint64_t)a) * ((uint64_t)pos) + (uint64_t)c) % ((uint64_t)n); +} +// Custom binary search function +inline int8_t binarySearch(uint64_t *arr, uint64_t size, uint64_t key) { + int64_t low = 0, high = size - 1; + while (low <= high) { + int64_t mid = low + (high - low) / 2; + if (arr[mid] == key) + return 1; // Key found + if (arr[mid] < key) + low = mid + 1; + else + high = mid - 1; + } + return 0; // Key not found +} + +void random_connected_graph(uint32_t n, uint64_t m, Edge *edges, uint16_t *seed) { if (m > n * (n - 1) / 2) { fprintf(stderr, "Error: Number of edges exceeds the maximum possible for %u vertices.\n", n); exit(EXIT_FAILURE); } + uint64_t PRIME_PERM = 5000000029; uint64_t mmax = (uint64_t)n * ((uint64_t)n - 1) / 2; // Maximum possible number of edges - uint32_t *N = malloc(n * sizeof(uint32_t)); // Vertices array - uint64_t *M = malloc(mmax * sizeof(uint64_t)); // All edge indices - uint64_t *T = malloc(m * sizeof(uint64_t)); // Selected edges for the graph - if (!N || !M || !T) { - fprintf(stderr, "Error: Memory allocation of arrays N, M or T failed.\n"); - free(T); - free(M); - free(N); + uint64_t *T = malloc(m * sizeof(uint64_t)); // Array for selected edge indices + if (!T) { + fprintf(stderr, "Error: Memory allocation failed for array T.\n"); + exit(EXIT_FAILURE); + } + + // Select coprime multiplier `a` + + // uint32_t c = *seed % n; // Random offset + uint32_t c = 0; + // Step 1: Build a spanning tree + for (uint64_t v = 1; v < n; v++) { + uint64_t u = rand_r_large(seed) % v; + uint64_t permutedV = permuted(v, PRIME_PERM, c, n); + uint64_t permutedU = permuted(u, PRIME_PERM, c, n); + + if (min(permutedU,permutedV) == max(permutedU,permutedV)){ + free(T); + fprintf(stderr, "u and v are not mapped to different indices: u=%lu, v=%lu, min = %lu\n", u, v, min(permutedU,permutedV)); exit(EXIT_FAILURE); - } + } - // Initialize vertex and edge index arrays - for (uint32_t i = 0; i < n; i++) N[i] = i; - for (uint64_t i = 0; i < mmax; i++) M[i] = i; - - // Seed random number generator and shuffle vertices - for (uint32_t i = n - 1; i > 0; i--) { - uint32_t j = rand_r_large(seed) % (i + 1); - uint32_t temp = N[i]; - N[i] = N[j]; - N[j] = temp; - } - - // Build spanning tree by randomly selecting edges - for (uint32_t v = 1; v < n; v++) { - uint32_t u = rand_r_large(seed) % v; - uint64_t index = uv2index(min(N[u],N[v]), max(N[u],N[v]), n); + uint64_t index = uv2index(min(permutedU,permutedV), max(permutedU,permutedV), n); if (index == (uint64_t)(-1)) { free(T); - free(M); - free(N); exit(EXIT_FAILURE); } - if (index >= mmax){ - fprintf(stderr, "u = %u; v = %u", u, v); - fprintf(stderr, "index = %lu; mmax = %lu", index, mmax); - } - T[v - 1] = index; // Add edge index to T + T[v - 1] = index; // Add spanning tree edge index } - // Sort the initial edges of the spanning tree + // Sort the first n-1 elements for binary search qsort(T, n - 1, sizeof(uint64_t), cmpInt); - - // put all selected edges back to the array (last n-1 elements of M) - for (int64_t i = n-2; i >= 0; i--) { - if (T[i] > mmax){ - fprintf(stderr, "T[i] = %lu; mmax = %lu", T[i], mmax); - - } - uint32_t temp = M[mmax - 1 - n + 2 + i]; - M[mmax - 1 - n + 2 + i] = M[T[i]]; - M[T[i]] = temp; - } - - - // Randomly shuffle remaining edges in M[0,...,mmax - n] - for (int64_t i = mmax - n; i > 0; i--) { - uint32_t j = rand_r_large(seed) % (i + 1); - uint32_t temp = M[i]; - M[i] = M[j]; - M[j] = temp; - } - - // Select the first m - n + 1 additional edges - for (uint64_t i = 0; i < m - n + 1; i++) { - T[n - 1 + i] = M[i]; - } - - // Sort the entire T array - qsort(T, m, sizeof(uint64_t), cmpInt); - - // Convert indices to edges and create the edge array - - uint64_t check = m; - for (uint64_t i = 0; i < m; i++) { + uint64_t previous_index = mmax; // Initialize with an invalid index + for (uint64_t i = 0; i < n-1; i++) { uint32_t u, v; - if (index2uv(T[i], n, &u, &v) == -1) { free(T); - free(M); - free(N); exit(EXIT_FAILURE); } - if (T[i] == check){ - fprintf(stderr, "Error: Duplicated edge (%d - %d) found.\n", u, v); + + // Check for duplicate edges + if (T[i] == previous_index) { + fprintf(stderr, "ERROR: Duplicate edge (%u, %u) detected, T[i] = %lu, previous_index = %lu.\n", u, v, T[i], previous_index); + free(T); exit(EXIT_FAILURE); } - check = T[i]; + previous_index = T[i]; + + // edges[i].src = u; + // edges[i].dest = v; + } + // Step 2: Add additional edges + uint64_t additional_edges_added = 0; + // uint64_t current = 0; + + for (uint64_t i = 0; i < mmax && additional_edges_added < m - n + 1; i++) { + uint64_t permutedIndex = permuted(i, PRIME_PERM, c, mmax); + + // Check if the edge index is already in the spanning tree + if (binarySearch(T, n - 1, permutedIndex)) { + continue; // Edge already exists in the tree + } + + // Add the edge index to T after n-1 elements + T[n - 1 + additional_edges_added] = permutedIndex; + additional_edges_added++; + } + + // Verify enough edges were added + if (additional_edges_added != m - n + 1) { + fprintf(stderr, "Error: Could not generate the required number of edges.\n"); + free(T); + exit(EXIT_FAILURE); + } + + // c = *seed % mmax; + c = 0; + // Step 3: Sort the entire T array + qsort(T, m, sizeof(uint64_t), cmpInt); + + // Step 4: Convert edge indices to actual edges + previous_index = mmax; // Initialize with an invalid index + for (uint64_t i = 0; i < m; i++) { + uint32_t u, v; + if (index2uv(T[i], n, &u, &v) == -1) { + free(T); + exit(EXIT_FAILURE); + } + + // Check for duplicate edges + if (T[i] == previous_index) { + fprintf(stderr, "Error: Duplicate edge (%u, %u) detected, T[i] = %lu, previous_index = %lu.\n", u, v, T[i], previous_index); + free(T); + exit(EXIT_FAILURE); + } + previous_index = T[i]; edges[i].src = u; edges[i].dest = v; } - // Output edges - - // Cleanup + // Free dynamically allocated memory free(T); - free(M); - free(N); } diff --git a/src/random_group_steiner.c b/src/random_group_steiner.c index 23853c4..d32affb 100644 --- a/src/random_group_steiner.c +++ b/src/random_group_steiner.c @@ -8,22 +8,17 @@ #include "random_vertex_weight.h" void generate_random_group_steiner_instance( - uint32_t n, uint32_t m, uint16_t k, const char *filename, + uint32_t n, uint64_t m, uint16_t k, const char* e, const char* v, const char* p, char* filename, float vertex_a, float vertex_b, const char *vertex_weight_type, - float edge_a, float edge_b, const char *edge_weight_family, const char *edge_weight_type, const char *instance_type, uint16_t* seed) { + float edge_a, float edge_b, const char *edge_weight_family, const char *edge_weight_type, float group_penalty_a, float group_penalty_b, const char* group_penalty_type, uint32_t* group_size, const char* group_size_mode, char* group_mode, const char *instance_type, const char* instance_type_code, uint16_t* seed) { - // Open file for writing - FILE *file = fopen(filename, "w"); - if (!file) { - fprintf(stderr, "Error: Could not open file %s for writing.\n", filename); - exit(EXIT_FAILURE); - } + // Generate a random connected graph Edge *edges = (Edge *)malloc(m * sizeof(Edge)); if (!edges) { fprintf(stderr, "Error: Memory allocation failed for edges.\n"); - fclose(file); + exit(EXIT_FAILURE); } // uint64_t mmax = (uint64_t)(n * (n - 1) / 2); @@ -34,7 +29,6 @@ void generate_random_group_steiner_instance( if (!x) { fprintf(stderr, "Error: Memory allocation failed for array x.\n"); free(edges); - fclose(file); exit(EXIT_FAILURE); } @@ -43,7 +37,6 @@ void generate_random_group_steiner_instance( fprintf(stderr, "Error: Memory allocation failed for array y.\n"); free(x); free(edges); - fclose(file); exit(EXIT_FAILURE); } @@ -75,38 +68,93 @@ void generate_random_group_steiner_instance( } } // Generate random groups - generate_random_groups(n, k, groups, seed); + generate_random_groups(n, k, groups, group_size, group_size_mode, group_mode, seed); + + if (strcmp(filename,"") == 0) { + snprintf(filename, 256, "DWW_%s_%s_n=%u_m=%lu_k=%u_s=%u_vwt=%s_vwl=%.1f_vwu=%.1f_ewt=%s_gs=%u_gsm=%s_gm=%s.stp", + instance_type_code,edge_weight_family, n, m, k, (uint32_t)*seed, vertex_weight_type, vertex_a, vertex_b, edge_weight_type, *group_size, group_size_mode, group_mode); + + } + + // Open file for writing + FILE *file = fopen(filename, "w"); + if (!file) { + fprintf(stderr, "Error: Could not open file %s for writing.\n", filename); + exit(EXIT_FAILURE); + } // Write the .stp file header fprintf(file, "0 string 33D32945 STP Steiner Tree Problem File\n"); fprintf(file, "SECTION Comment\n"); - fprintf(file, "Name DWW_NWGST_%s_N=%u_M=%u_k=%u\n",edge_weight_family,n,m,k); + fprintf(file, "DWW_%s_%s_n=%u_m=%lu_k=%u_s=%u_vwt=%s_vwl=%.1f_vwu=%.1f_ewt=%s_gs=%u_gsm=%s_gm=%s\n", instance_type_code,edge_weight_family, n, m, k, (uint32_t)*seed, vertex_weight_type, vertex_a, vertex_b, edge_weight_type, *group_size, group_size_mode, group_mode); fprintf(file, "Remark '%s'\n",instance_type); fprintf(file, "END\n\n"); // Write the graph section fprintf(file, "SECTION Graph\n"); fprintf(file, "Nodes %u\n", n); - fprintf(file, "Edges %u\n", m); + fprintf(file, "Edges %lu\n", m); // Write vertex weights - for (unsigned int i = 0; i < n; i++) { - float vertex_weight = generate_random_vertex_weight(vertex_weight_type, vertex_a, vertex_b, seed); - fprintf(file, "N %.1f\n", vertex_weight); + if (strcmp(v,"yes") == 0 && strcmp(vertex_weight_type,"int") == 0) { + for (unsigned int i = 0; i < n; i++) { + float vertex_weight = generate_random_vertex_weight(vertex_weight_type, vertex_a, vertex_b, seed); + fprintf(file, "N %.1f\n", vertex_weight); + } + } else if (strcmp(v,"yes") == 0 && strcmp(vertex_weight_type,"float") == 0) { + for (unsigned int i = 0; i < n; i++) { + float vertex_weight = generate_random_vertex_weight(vertex_weight_type, vertex_a, vertex_b, seed); + fprintf(file, "N %.9f\n", vertex_weight); + } } + + if (strcmp(e, "yes") == 0 && strcmp(edge_weight_type,"int") == 0){ + for (unsigned int i = 0; i < m; i++) { + + float edge_weight = generate_random_edge_weight( + x, y, n, edges[i].src, edges[i].dest, edge_a, edge_b, edge_weight_family, edge_weight_type, seed); + fprintf(file, "E %d %d %.1f\n", edges[i].src, edges[i].dest, edge_weight); + } - // Write edges - for (unsigned int i = 0; i < m; i++) { - float edge_weight = generate_random_edge_weight( - x, y, n, edges[i].src, edges[i].dest, edge_a, edge_b, edge_weight_family, edge_weight_type, seed); - fprintf(file, "E %d %d %.2f\n", edges[i].src, edges[i].dest, edge_weight); + } else if (strcmp(e, "yes") == 0 && strcmp(edge_weight_type,"float") == 0) { + + for (unsigned int i = 0; i < m; i++) { + + float edge_weight = generate_random_edge_weight( + x, y, n, edges[i].src, edges[i].dest, edge_a, edge_b, edge_weight_family, edge_weight_type, seed); + fprintf(file, "E %d %d %.9f\n", edges[i].src, edges[i].dest, edge_weight); + } + + } else if (strcmp(e, "no") == 0) { + + for (unsigned int i = 0; i < m; i++) { + + fprintf(file, "E %d %d\n", edges[i].src, edges[i].dest); + } } + fprintf(file, "END\n\n"); // Write the terminals section fprintf(file, "SECTION Terminals\n"); fprintf(file, "Terminals %u\n", k); + // Write group penalties + if (strcmp(p,"yes") == 0 && strcmp(group_penalty_type,"int") == 0) { + for (unsigned int i = 0; i < k; i++) { + float group_penalty = generate_random_vertex_weight(group_penalty_type, group_penalty_a, group_penalty_b, seed); + fprintf(file, "P %.1f\n", group_penalty); + } + } else if (strcmp(p,"yes") == 0 && strcmp(group_penalty_type,"float") == 0) { + for (unsigned int i = 0; i < k; i++) { + float group_penalty = generate_random_vertex_weight(group_penalty_type, group_penalty_a, group_penalty_b, seed); + fprintf(file, "P %.9f\n", group_penalty); + } + } + + + + for (unsigned int i = 0; i < k; i++) { fprintf(file, "T"); for (unsigned int j = 0; j < groups[i].size; j++) { diff --git a/src/random_groups.c b/src/random_groups.c index 4f25a30..cac828d 100644 --- a/src/random_groups.c +++ b/src/random_groups.c @@ -1,5 +1,17 @@ #include "random_groups.h" #include "random_graph.h" +#include + + + +int cmpGroups(const void *a, const void *b) { + uint32_t int_a = *(uint32_t *)a; + uint32_t int_b = *(uint32_t *)b; + + // Return the difference (int_a - int_b) + return (int) ((int_a > int_b) - (int_a < int_b)); // Returns -1, 0, or 1 +} + // Helper function to shuffle an array void shuffle(uint32_t *array, uint32_t size, uint16_t* seed) { @@ -12,7 +24,7 @@ void shuffle(uint32_t *array, uint32_t size, uint16_t* seed) { } -void generate_random_groups(uint32_t n, uint16_t k, Group *groups, uint16_t* seed) { +void generate_random_groups(uint32_t n, uint16_t k, Group *groups, uint32_t* group_size, const char* group_size_mode, char* group_mode, uint16_t* seed) { if (k > n) { fprintf(stderr, "Error: Number of groups (k) cannot exceed the number of vertices (n).\n"); exit(EXIT_FAILURE); @@ -61,18 +73,68 @@ void generate_random_groups(uint32_t n, uint16_t k, Group *groups, uint16_t* see groups[i].group[groups[i].size++] = N[offset + i]; } - uint32_t total = (n - offset) / k - 1; - for (uint32_t i = 0; i < k; i++) { - - shuffle(I,npool,seed); - for (uint32_t j = 0; j < total; j++) { - groups[i].group[groups[i].size++] = N[offset + k + I[j]]; + + + uint32_t total; + + if(strcmp(group_size_mode, "auto") == 0){ + total = (n - offset) / k - 1; + *group_size = total + 1; + + }else if (strcmp(group_size_mode, "manual") == 0){ + + if (*group_size > n - offset - k ) { + fprintf(stderr, "Warning: Group size is shrinked to %d.\n", n - offset - k); + *group_size = n - offset - k; } + total = *group_size - 1; + + + if(strcmp(group_mode, "disjoint") == 0){ + if ( *group_size > (n - offset) / k - 1){ + snprintf(group_mode, 256, "overlap"); + fprintf(stderr, "Warning: Groups cannot be disjoint, because group size is too large: %d. It has to be at most %d. Switched to overlapping instance.\n", *group_size, (n - offset) / k - 1); + + } + } + + + }else{ + fprintf(stderr, "Error: Group size mode %s is not supported.\n", group_size_mode); + free(I); + free(N); + exit(EXIT_FAILURE); } - // Sort each group + + if (strcmp(group_mode, "overlap") == 0){ + for (uint32_t i = 0; i < k; i++) { + + shuffle(I,npool,seed); + for (uint32_t j = 0; j < total; j++) { + groups[i].group[groups[i].size++] = N[offset + k + I[j]]; + } + } + } + else if (strcmp(group_mode, "disjoint") == 0){ + // shuffle(I,npool,seed); + for (uint32_t i = 0; i < k; i++) { + for (uint32_t j = 0; j < total; j++) { + groups[i].group[groups[i].size++] = N[offset + k + i*total + I[j]]; + } + } + } + else{ + fprintf(stderr, "Error: Parameter 'group_mode' can be either 'overlap' or 'disjoint', but %s provided.\n", group_mode); + free(I); + free(N); + exit(EXIT_FAILURE); + + } + + //Sort each group for (uint32_t i = 0; i < k; i++) { - qsort(groups[i].group, groups[i].size, sizeof(uint32_t), cmpInt); + qsort(groups[i].group, groups[i].size, sizeof(uint32_t), cmpGroups); } // Clean up diff --git a/src/random_vertex_weight.c b/src/random_vertex_weight.c index b1ca6ba..ccf1617 100644 --- a/src/random_vertex_weight.c +++ b/src/random_vertex_weight.c @@ -6,15 +6,18 @@ float generate_random_vertex_weight(const char *weight_type, float a, float b, u fprintf(stderr, "Error: Lower bound 'a' cannot be greater than upper bound 'b'.\n"); exit(EXIT_FAILURE); } - float random_value = 1 + a + ((float)rand_r((unsigned int* )seed) / RAND_MAX) * (b - a); + float random_value = a + ((float)rand_r((unsigned int* )seed) / RAND_MAX) * (b - a); // Check the weight type and cast appropriately - if (strcmp(weight_type, "integer") == 0) { - return (float)((int)random_value); + if (strcmp(weight_type, "int") == 0) { + if(random_value < 1.0) random_value = 1.0; + random_value = (float)((int)random_value); } else if (strcmp(weight_type, "float") == 0) { - return random_value; + if(random_value < 1e-8) random_value = 1e-8; } else { - fprintf(stderr, "Error: Invalid weight type '%s'. Use 'integer' or 'float'.\n", weight_type); + fprintf(stderr, "Error: Invalid vertex weight type '%s'. Use 'int' or 'float'.\n", weight_type); exit(EXIT_FAILURE); } + + return random_value; }