diff --git a/src/SOCadmissible.c b/src/SOCadmissible.c index 20e9e50..a1bf59c 100644 --- a/src/SOCadmissible.c +++ b/src/SOCadmissible.c @@ -1,4 +1,5 @@ -// SPDX-FileCopyrightText: 2023 Ämin Baumeler and Eleftherios-Ermis Tselentis +// SPDX-FileCopyrightText: 2023 Ämin Baumeler and +// Eleftherios-Ermis Tselentis // // SPDX-License-Identifier: GPL-3.0-or-later @@ -8,10 +9,10 @@ /*** * Read graphs from file. - * Input `filename': string - * Input `cnt': pointer to int - * Input `maxdim': pointer to int - * Output: pointer to graph data + * Input `filename': string + * Input `cnt': pointer to int + * Input `maxdim': pointer to int + * Output: pointer to graph data * * This function sets `cnt' to the total number of graphs read from `filename', * sets `maxdim' to the dimension (number of nodes) of the largest graph, @@ -20,93 +21,96 @@ * The ith graph is at position i*(maxdim*maxdim + 1). ***/ int* read_graphs(char* filename, int *cnt, int *maxdim) { - // Open file - FILE *fp; - fp = fopen(filename, "r"); - if(fp == NULL) - return NULL; - // Count number of lines, allocate space and initiate counter - char ch; - int n = 0; - *cnt = 0; // Total number of graphs - *maxdim = 0; // Maximum number of vertices - while(!feof(fp)) { - ch = fgetc(fp); - switch(ch) { - case '\n': - (*cnt)++; - n = 0; - break; - case '{': - case '}': - *maxdim = (n > *maxdim) ? n : *maxdim; - n = 0; - break; - case '0': - case '1': - n++; - break; - case ',': - case ' ': - case 0xffffffff: - break; - default: - return NULL; // Format error - } - } - rewind(fp); // Return to start of file + // Open file + FILE *fp; + fp = fopen(filename, "r"); + if (fp == NULL) + return NULL; + // Count number of lines, allocate space and initiate counter + char ch; + int n = 0; + *cnt = 0; // Total number of graphs + *maxdim = 0; // Maximum number of vertices + while (!feof(fp)) { + ch = fgetc(fp); + switch (ch) { + case '\n': + (*cnt)++; + n = 0; + break; + case '{': + case '}': + *maxdim = (n > *maxdim) ? n : *maxdim; + n = 0; + break; + case '0': + case '1': + n++; + break; + case ',': + case ' ': + case 0xffffffff: + break; + default: + return NULL; // Format error + } + } + rewind(fp); // Return to start of file - // Allocate space for graph data - const int gdatasize = *maxdim * *maxdim + 1; // Size allocation per graph - const int arraysize = (1+ *cnt) * gdatasize; // Total space to allocate - int *graphs = (int*)malloc(sizeof(int)*arraysize); // Allocation - if(!graphs) { - fprintf(stderr, "ERROR: Could not allocate enough memory to store the graphs.\n"); - return NULL; - } - // Parse file - int col = 0; // Column number - int row = 0; // Row number - int i = 0; // Current graph index - n = -1; // Graph dimension - int j = 0; // Running index - while ((ch = fgetc(fp)) != EOF) { - // New line encountered - if(ch == '\n') { - if(row != n) { - fprintf(stderr, "ERROR: File not properly formatted.\n"); - return NULL; - } - graphs[i*gdatasize] = n; - i++; - j = 0; - col = 0; - row = 0; - n = -1; - // Matrix entry encountered - } else if(ch == '0' || ch == '1') { - int x = ch - '0'; - graphs[i*gdatasize + 1 + j] = x; - col++; - j++; - // End-of-row or end-of-matrix encountered - } else if(ch == '}') { - if(n == -1) { - n = col; - } else if(col > 0 && n != col) { - fprintf(stderr, "ERROR: File not properly formatted.\n"); - return NULL; - } - // End of row - if(col != 0) { - row++; - col = 0; - } - } - } - // Close file - fclose(fp); - return graphs; + // Allocate space for graph data + // Size allocation per graph + const int gdatasize = *maxdim * *maxdim + 1; + // Total space to allocate + const int arraysize = (1+ *cnt) * gdatasize; + // Allocation + int *graphs = (int*)malloc(sizeof(int)*arraysize); + if (!graphs) { + fprintf(stderr, "ERROR: Could not allocate enough memory to store the graphs.\n"); + return NULL; + } + // Parse file + int col = 0; // Column number + int row = 0; // Row number + int i = 0; // Current graph index + n = -1; // Graph dimension + int j = 0; // Running index + while ((ch = fgetc(fp)) != EOF) { + // New line encountered + if (ch == '\n') { + if (row != n) { + fprintf(stderr, "ERROR: File not properly formatted.\n"); + return NULL; + } + graphs[i*gdatasize] = n; + i++; + j = 0; + col = 0; + row = 0; + n = -1; + // Matrix entry encountered + } else if (ch == '0' || ch == '1') { + int x = ch - '0'; + graphs[i*gdatasize + 1 + j] = x; + col++; + j++; + // End-of-row or end-of-matrix encountered + } else if (ch == '}') { + if (n == -1) { + n = col; + } else if (col > 0 && n != col) { + fprintf(stderr, "ERROR: File not properly formatted.\n"); + return NULL; + } + // End of row + if (col != 0) { + row++; + col = 0; + } + } + } + // Close file + fclose(fp); + return graphs; } /*** @@ -114,15 +118,15 @@ int* read_graphs(char* filename, int *cnt, int *maxdim) { * Returns 0 if we run out of interventions, and 1 otherwise. ***/ int nextintervention(int n, int *interventionslen, int *interventionidx) { - for(int i=0; i 0) { - x /= 10; - res++; - } - return res; + int res = 0; + while (x > 0) { + x /= 10; + res++; + } + return res; } /*** @@ -212,193 +225,214 @@ int ceillog10(int x) { * Return -1 on error. ***/ int test_graphs(int startidx, int len, int maxn, const int *graphs) { - const int gdatasize = maxn * maxn + 1; // Size allocation per graph - const int idatasize = (maxn+1)*(maxn+1)*2; // Max number of interventions per party - int range[maxn+1]; // Range of intervention - int interventions[maxn*idatasize]; // Hold all interventions per party {f_0(0), f_0(1), g_0(0), g_0(1), ... , f_1(0), f_1(1), ...} - int interventionslen[maxn]; // Number of interventions per party - int interventionidx[maxn]; // Current intervention executed - int parents[maxn*maxn]; // List all parents per party - int parentslen[maxn]; // Number of parents per party - int children[maxn*maxn]; // List all children per party - int childrenlen[maxn]; // Numer of children per party - const int padlen = ceillog10(len); - time_t t0 = time(NULL); - const time_t tstart = t0; - for(int graphidx=startidx; graphidx t0) { - const int count = graphidx - startidx + 1; - const float percentage = 100*(float)count/(float)len; - const int deltat = t1-tstart; - const float rate = (float)count/(float)deltat; - fprintf(stderr, "\r%6.2f%% %*d/%d (%*.2f graphs/s in %d seconds; current line %d)",percentage,padlen,count,len,padlen+3,rate,deltat,graphidx+1); - t0 = t1; - } - // Test graph with index `graphidx' - // `n' is the order of this graph - const int n = graphs[graphidx*gdatasize]; - // Get parents and children - // Adj matrix is such that (row,col) = 1 <=> edge from row to col - for(int i=0; i t0) { + const int count = graphidx - startidx + 1; + const float percentage = 100*(float)count/(float)len; + const int deltat = t1-tstart; + const float rate = (float)count/(float)deltat; + fprintf(stderr, "\r%6.2f%% %*d/%d (%*.2f graphs/s in %d seconds; current line %d)", \ + percentage, padlen, count, len, padlen+3, rate, deltat, graphidx+1); + t0 = t1; + } + // Test graph with index `graphidx' + // `n' is the order of this graph + const int n = graphs[graphidx*gdatasize]; + // Get parents and children + // Adj matrix is such that (row,col) = 1 <=> edge from row to col + for (int i = 0; i < n; i++) { + parentslen[i] = 0; + childrenlen[i] = 0; + } + int idx = 0; + for (int row = 0; row < n; row++) { + for (int col = 0; col < n; col++) { + if (graphs[graphidx*gdatasize+1+idx] == 1) { + // Edge from row to col + children[maxn*row + childrenlen[row]] = col; + childrenlen[row]++; + parents[maxn*col + parentslen[col]] = row; + parentslen[col]++; + } + idx++; + } + } + // Fill in interventions + for (int party = 0; party < n; party++) { + // This is a small trick to speed-up processing: + // If the party `party' has two or more children, + // ignore the `discard' intervention (-1) (tmp=0), + // else make use of the `discard' internvetion (tmp=1). + // Populate range of `party's interventions + // The range has childrenlen[party]+tmp entries + range[0] = -1; + const int tmp = (childrenlen[party] <= 1) ? 1 : 0; + for (int j = 0; j < childrenlen[party]; j++) { + range[j+tmp] = children[maxn*party+j]; + } + // Enumerate, store, and count all possible interventions + // Each intervention is a tuple (mu(0), mu(1)), x corresponds to the first + // entry, y to the second + int cnt = 0; + for (int x = 0; x < childrenlen[party]+tmp; x++) { + for (int y = 0; y < childrenlen[party]+tmp; y++) { + interventions[party*idatasize+2*cnt] = range[x]; + interventions[party*idatasize+2*cnt+1] = range[y]; + cnt++; + } + interventionslen[party] = cnt; + } + } + // Loop over intervetions and verify fixed-point expression + int fp[n]; // Array to store alpha(party) values + int afterfp[n]; // \omega(\mu(fp)) + int path[n+1]; // Current path + // Initialize intervention index to the first intervention + for (int i = 0; i < n; i++) interventionidx[i] = 0; + // Flag to see whether we are still verifying, or we verified all + // interventions + int running = 1; + while (running) { + // Reset fixed-point entries + for (int party = 0; party < n; party++) + fp[party] = -1; + // Fill out fixed-points that are trivially computed + for (int party = 0; party < n; party++) + alphapre(party, parents, parentslen, interventions, interventionidx, \ + maxn); + // Invoke recursive function + for (int party = 0; party < n; party++) { + path[0] = party; + const int val = alpha(path, 1, parents, parentslen, interventions, \ + interventionidx, fp, n, maxn); + fp[party] = val; + } - // Compute \omega(\mu(fp)) - for(int i=0; i= 3) - START = atoi(argv[2]); - if(argc >= 4) { - if(argv[3][0] == '+') - NUM = atoi(argv[3]+1); - else - NUM = atoi(argv[3])-START+1; - } - if(!(argc >= 2 && (argc < 3 || START > 0) && (argc < 4 || NUM > 0) && argc <= 4)) { - fprintf(stderr, "Usage: %s [ [ | +]]\n", argv[0]); - fprintf(stderr, " File name with adjacency matrices of simple directed graphs\n"); - fprintf(stderr, " Verify graphs starting from line `startline'\n"); - fprintf(stderr, " Verify graphs up to and including line `endline'\n"); - fprintf(stderr, " + Verify `count' number of graphs\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "[FILE FORMAT]\n"); - fprintf(stderr, " Each line in `filename' must contain the adjacency matrix of a simple directed graph in the format\n"); - fprintf(stderr, " {{a00,a01,...},{a10,a11,...},...} where aij=1 if and only if the graph has the edge i -> j\n"); - fprintf(stderr, " The file `filename' may contain graphs with different order (number of vertices)\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "This program verifies the admissibility of simple directed graphs.\n"); - return -1; - } + // Parse command-line arguments + int START = 0; + int NUM = 0; + if (argc >= 3) + START = atoi(argv[2]); + if (argc >= 4) { + if (argv[3][0] == '+') + NUM = atoi(argv[3]+1); + else + NUM = atoi(argv[3])-START+1; + } + if (!(argc >= 2 && (argc < 3 || START > 0) && (argc < 4 || NUM > 0) && \ + argc <= 4)) { + fprintf(stderr, "Usage: %s [ [ | +]]\n", argv[0]); + fprintf(stderr, " File name with adjacency matrices of simple directed graphs\n"); + fprintf(stderr, " Verify graphs starting from line `startline'\n"); + fprintf(stderr, " Verify graphs up to and including line `endline'\n"); + fprintf(stderr, " + Verify `count' number of graphs\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "[FILE FORMAT]\n"); + fprintf(stderr, " Each line in `filename' must contain the adjacency matrix of a simple directed graph in the format\n"); + fprintf(stderr, " {{a00,a01,...},{a10,a11,...},...} where aij=1 if and only if the graph has the edge i -> j\n"); + fprintf(stderr, " The file `filename' may contain graphs with different order (number of vertices)\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "This program verifies the admissibility of simple directed graphs.\n"); + return -1; + } - // Read Graphs from File - int graphcount = 0; - int maxdimension = -1; - const int *graphs = read_graphs(argv[1], &graphcount, &maxdimension); - if(graphs == NULL) { - fprintf(stderr, "ERROR: Could not read graphs from file `%s'.\n", argv[1]); - return -1; - } - const int startidx = START ? START-1 : 0; // Index of graph to start verifying - const int tot = NUM ? NUM : graphcount-startidx; // Number of graphs to be verified - if(startidx >= graphcount || tot <= 0 || tot+startidx-1 >= graphcount) { - fprintf(stderr, "ERROR: Lines to be verified are out of range\n"); - return -1; - } + // Read Graphs from File + int graphcount = 0; + int maxdimension = -1; + const int *graphs = read_graphs(argv[1], &graphcount, &maxdimension); + if (graphs == NULL) { + fprintf(stderr, "ERROR: Could not read graphs from file `%s'.\n", argv[1]); + return -1; + } + const int startidx = START ? START-1 : 0; // Index of graph to start verifying + const int tot = NUM ? NUM : graphcount-startidx; // Number of graphs to be verified + if (startidx >= graphcount || tot <= 0 || tot+startidx-1 >= graphcount) { + fprintf(stderr, "ERROR: Lines to be verified are out of range\n"); + return -1; + } - // Test `tot' graphs, starting from index `startidx' - printf("Verifying the admissibility of %d graphs in the file `%s' (line %d to line %d)\n", tot, argv[1], startidx+1, startidx+tot); - const int result = test_graphs(startidx, tot, maxdimension, graphs); - if (result == 0) - printf("These graphs are admissible\n"); - else if(result == -1) - fprintf(stderr, "ERROR: Something went wrong\n"); - else - printf("The function alpha does not represent the fixed point, or the graph on line %d is inadmissible\n", result); - return result; + // Test `tot' graphs, starting from index `startidx' + printf("Verifying the admissibility of %d graphs in the file `%s' (line %d to line %d)\n", \ + tot, argv[1], startidx+1, startidx+tot); + const int result = test_graphs(startidx, tot, maxdimension, graphs); + if (result == 0) + printf("These graphs are admissible\n"); + else if (result == -1) + fprintf(stderr, "ERROR: Something went wrong\n"); + else + printf("The function alpha does not represent the fixed point, or the graph on line %d is inadmissible\n", \ + result); + return result; } diff --git a/src/SOCgen.c b/src/SOCgen.c index eb9a1a9..9265369 100644 --- a/src/SOCgen.c +++ b/src/SOCgen.c @@ -1,4 +1,5 @@ -// SPDX-FileCopyrightText: 2023 Ämin Baumeler and Eleftherios-Ermis Tselentis +// SPDX-FileCopyrightText: 2023 Ämin Baumeler and +// Eleftherios-Ermis Tselentis // // SPDX-License-Identifier: GPL-3.0-or-later @@ -11,186 +12,201 @@ * Print the graph's adjacency matrix ***/ void dumpgraph(int n, const int *children, const int *childrenlen) { - int adj[n*n]; - for(int i=0; i G%dN%d", graphnr, v, graphnr, w); - if(childrenlen[v] >= 2) - printf(" [color=\"red\"]"); - printf(";"); - } - } - printf("}\n"); - return; + printf("strict digraph G%d {node[shape=point];", graphnr); + for (int v = 0; v < n; v++) { + for (int i = 0; i < childrenlen[v]; i++) { + const int w = children[v*n+i]; + printf("G%dN%d -> G%dN%d", graphnr, v, graphnr, w); + if (childrenlen[v] >= 2) + printf(" [color=\"red\"]"); + printf(";"); + } + } + printf("}\n"); + return; } /*** * Block size of our datastructure. ***/ int blocksize(int n) { - int res=(n+1)*n; - for(int i=0; i path[k]) { - smallest = path[k]; - smallestidx = k; - } - } - // Now, copy cycle to `cycle' starting from the smallest - int j=0; // This is the index on where to write on the array `cycle' - for(int i=smallestidx;; i++) { - if(j==*cyclelen) break; // Stop filling in, if we reached the end - if(i==pathlen-1) i = startidx; // If we reach the end of the path, jump to the start - cycle[j] = path[i]; // Write at position `j' the element from the cycle on the path at position `i`' - j++; // Increase index to write on `cycle' - } +void canonical_cycle(int n, int *cycle, int *cyclelen, const int *path, \ + int pathlen) { + int startidx; + int pathhascycle = 0; + const int last = path[pathlen-1]; // This is the last node + for (int k = 0; k < pathlen-1; k++) { + if (path[k] == last) { + // We have found the first occurance of node `last' on the path + // The cycle is the following part of path: path[startidx], + // path[startidx+1], ..., path[pathlen-2], and therefore has + // length pathlen-startidx-1 + startidx = k; + (*cyclelen) = pathlen-startidx-1; + pathhascycle = 1; + break; + } + } + if (!pathhascycle) { // Sanity check. This should NEVER happen! + fprintf(stderr, "ERROR: You provided a path to `canonical_cycle' that does NOT contain a cycle as its tail! I'm returning now.\n"); + return; + } + // Generate canonical representation + // First, find smallest node on cycle + int smallestidx = -1; + int smallest = n; + for (int k = startidx; k < pathlen-1; k++) { + if (smallest > path[k]) { + smallest = path[k]; + smallestidx = k; + } + } + // Now, copy cycle to `cycle' starting from the smallest + // This is the index on where to write on the array `cycle' + int j = 0; + for (int i = smallestidx;; i++) { + // Stop filling in, if we reached the end + if (j == *cyclelen) break; + // If we reach the end of the path, jump to the start + if (i == pathlen-1) i = startidx; + // Write at position `j' the element from the cycle on the path at + // position `i`' + cycle[j] = path[i]; + // Increase index to write on `cycle' + j++; + } } /*** * Return 1 if the cycle is not in `cycles', otherwise 0. ***/ -int cycleisnew(int n, const int *c, int clen, const int *cycles, const int *cyclescnt) { - const int cnt = cyclescnt[clen]; - const int bs = blocksize(n); - for(int i=0; i parentslen[i+1]) { - return 0; - } else if(parentslen[i] == parentslen[i+1]) { - if(childrenlen[i] > childrenlen[i+1]) { - return 0; - } else if(childrenlen[i] == childrenlen[i+1]) { - if(grandparentslen(i,parents,parentslen) > grandparentslen(i+1,parents,parentslen)) { - return 0; - } else if(grandparentslen(i,parents,parentslen) == grandparentslen(i+1,parents,parentslen)) { - if(grandchildrenlen(i,children,childrenlen) > grandchildrenlen(i+1,children,childrenlen)) { - return 0; - } - } - } - } - } - return 1; +int isdegreeordered(int n, const int *parents, const int *parentslen, \ + const int *children, const int *childrenlen) { + // Decreasingling order by in-degree, then out-degree, then ... + for (int i = 0; i < n-1; i++) { + if (parentslen[i] > parentslen[i+1]) { + return 0; + } else if (parentslen[i] == parentslen[i+1]) { + if (childrenlen[i] > childrenlen[i+1]) { + return 0; + } else if (childrenlen[i] == childrenlen[i+1]) { + if (grandparentslen(i, parents, parentslen) > \ + grandparentslen(i+1, parents, parentslen)) { + return 0; + } else if (grandparentslen(i, parents, parentslen) == \ + grandparentslen(i+1, parents, parentslen)) { + if (grandchildrenlen(i, children, childrenlen) > \ + grandchildrenlen(i+1, children, childrenlen)) { + return 0; + } + } + } + } + } + return 1; } /* Random nubers */ @@ -390,155 +444,175 @@ int isdegreeordered(int n, const int *parents, const int *parentslen, const int #endif u_int64_t rand_uint64(unsigned long long max) { - u_int64_t r = 0; - for (int i=LOOP_COUNT; i > 0; i--) { - r = r*(RAND_MAX + (u_int64_t)1) + rand(); - } - return r%max; + u_int64_t r = 0; + for (int i=LOOP_COUNT; i > 0; i--) { + r = r*(RAND_MAX + (u_int64_t)1) + rand(); + } + return r%max; } /* End of random numbers */ int main(int argc, char *argv[]) { - // Parse command-line arguments - int n = -1; - int NONDAGONLY = 0; - int NOSINK = 0; - int NOSOURCE = 0; - int RANDOM = 0; - int GRAPHVIZ = 0; - int UNKOPTION = 0; - for(int i=1; i [-r ] [--grpahviz] [FILTER ...]\n", argv[0]); - fprintf(stderr, " -n Generate SOCs with `order' connected nodes\n"); - fprintf(stderr, " -r Pick directed graphs at random, and exit after having found `num' SOCs\n"); - fprintf(stderr, " --graphviz Output SOCs in Graphviz format, arcs of common parents are highlighted\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "[FILTER] Consider only simple directed graphs ...\n"); - fprintf(stderr, " -c ... that are cyclic (i.e., not DAGs)\n"); - fprintf(stderr, " --no-sink ... without sink nodes (this logically implies -c)\n"); - fprintf(stderr, " --no-source ... without source nodes (also this logically implies -c)\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "This program prints the found SOCs as adjacency matrices to stdout, unless --grpahviz has been specified.\n"); - fprintf(stderr, "To exclude (some) of the isomorphic SOCs, it uses a degree-order filter.\n"); - return -1; - } - // EO Parse command-line arguments - // Setup datastructures - int parents[n*n]; // Each node i can have at most n-1 parents. These are listed at parents[n*i+k] for 0<=k64) { - fprintf(stderr, "Too many graphs to enumarate with an unsiged long long integer (number of node pairs = %d)\n", m); - return -1; - } - unsigned long long max = 1L << m; // Largest `graphnumber' - const int padlen = (int)((float)m/3.322)+1; // Convert log 2 to log 10 - int len = 0; - time_t t0 = time(NULL); - time_t t = time(NULL); - srand((unsigned) time(&t)); - // EO Initiate graph enumeration - fprintf(stderr, "Generating SOCs with %d nodes\n", n); - if(RANDOM) fprintf(stderr, " Picking graphs at random\n"); - if(NONDAGONLY) fprintf(stderr, " Filter: Omitting DAGs\n"); - if(NOSINK) fprintf(stderr, " Filter: Omitting graphs with sink nodes\n"); - if(NOSOURCE) fprintf(stderr, " Filter: Omitting graphs with source nodes\n"); - unsigned long long graphnumber = 0; // We will always omit the graph with `graphnumber' 0; it is unintersting anyway - unsigned long long graphschecked = 0; - for(;graphnumber < max && (!RANDOM || len < RANDOM);) { - const time_t now = time(NULL); - // Break if we exhaustively check all graphs or if we found enough SOCs at random - //if(graphnumber >= max || (RANDOM && len >= RANDOM)) - // break; - // Print status every second - if(now > t) { - t = now; - const int deltat = now - t0; - const float rate = (float)graphschecked/(float)deltat; - const float SOCrate = (float)len/(float)deltat; - const float percentage = (RANDOM==0) ? 100*(float)graphnumber/(float)max : 100*(float)len/(float)RANDOM; - const float ETC = (RANDOM==0) ? ((float)(max - graphschecked)/(rate))/3600 : ((float)(RANDOM - len)/(SOCrate))/3600; - fprintf(stderr, "\r%6.2f%% %*llu/%llu (%i SOCs found, %d seconds, %*.2f graphs/s, %*.2f SOCs/s, %*.2fh estimated time left)",percentage,padlen,graphnumber,max,len,deltat,padlen+3,rate,padlen+3,SOCrate,padlen,ETC); - fflush(stderr); - } - // Convert graph index `grpahnumber' to the lists parents, children, parentslen, childrenlen - graphnrtolists(n, graphnumber, parents, parentslen, children, childrenlen); - // Increase checked counter and prepare for next iteration - graphschecked++; - if(RANDOM) - graphnumber = rand_uint64(max); - else - graphnumber++; - // EO Increase checked counter and prepare for next iteration - // If enabled, compare against degree options - if(NOSINK || NOSOURCE) { - // Get some degree properties - int minindegree = n; - int minoutdegree = n; - for(int node=0; node parentslen[node]) - minindegree = parentslen[node]; - if(minoutdegree > childrenlen[node]) - minoutdegree = childrenlen[node]; - } - if(NOSINK && minoutdegree == 0) continue; - if(NOSOURCE && minindegree == 0) continue; - } - // Check whether this graph is canonical or not; continue if graph has not proper ordering of degress (there is an isomorphic graphs to this one that has been or will be checked) - if(!isdegreeordered(n, parents, parentslen, children, childrenlen)) continue; - // Ignore graphs that are not connected - if(!isconnected(n, parents, parentslen, children, childrenlen)) continue; - // Find cycles - const int num_cycles = find_cycles(cycles, cyclescnt, n, children, childrenlen); - // If enabled, ignore DAGs - if(NONDAGONLY && num_cycles > 0) continue; - // Test the Siblings-On-Cycles property - // A DAG is trivially a SOC - if(num_cycles > 0 && !gissoc(n, parents, parentslen, children, childrenlen, cycles, cyclescnt)) continue; - // We have found a SOC - len++; - if(GRAPHVIZ) - dumpgv(n, graphnumber, children, childrenlen); - else - dumpgraph(n, children, childrenlen); - } - const int deltat = time(NULL) - t0; - const float rate = (deltat == 0) ? graphschecked : (float)graphschecked/(float)deltat; - const float SOCrate = (deltat == 0) ? len : (float)len/(float)deltat; - fprintf(stderr, "\r%6.2f%% %*llu/%llu (%i SOCs found, %d seconds, %*.2f graphs/s, %*.2f SOCs/s, %*.2fh estimated time left)",100.00,padlen,graphnumber,max,len,deltat,padlen+3,rate,padlen+3,SOCrate,padlen,0.00); - fprintf(stderr, "\nFound %d SOCs in %d seconds\n", len,deltat); - // Free manually allocated memory - free(cycles); - return 0; + // Parse command-line arguments + int n = -1; + int NONDAGONLY = 0; + int NOSINK = 0; + int NOSOURCE = 0; + int RANDOM = 0; + int GRAPHVIZ = 0; + int UNKOPTION = 0; + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-n") == 0 && i+1 < argc) { + n = atoi(argv[i+1]); + i++; + } else if (strcmp(argv[i], "-r") == 0 && i+1 < argc) { + RANDOM = atoi(argv[i+1]); + i++; + } else if (strcmp(argv[i], "-c") == 0) { + NONDAGONLY = 1; + } else if (strcmp(argv[i], "--no-sink") == 0) { + NOSINK = 1; + } else if (strcmp(argv[i], "--no-source") == 0) { + NOSOURCE = 1; + } else if (strcmp(argv[i], "--graphviz") == 0) { + GRAPHVIZ = 1; + } else { + UNKOPTION = 1; + break; + } + } + if (UNKOPTION || n <= 1) { + fprintf(stderr, "Usage: %s -n [-r ] [--grpahviz] [FILTER ...]\n", argv[0]); + fprintf(stderr, " -n Generate SOCs with `order' connected nodes\n"); + fprintf(stderr, " -r Pick directed graphs at random, and exit after having found `num' SOCs\n"); + fprintf(stderr, " --graphviz Output SOCs in Graphviz format, arcs of common parents are highlighted\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "[FILTER] Consider only simple directed graphs ...\n"); + fprintf(stderr, " -c ... that are cyclic (i.e., not DAGs)\n"); + fprintf(stderr, " --no-sink ... without sink nodes (this logically implies -c)\n"); + fprintf(stderr, " --no-source ... without source nodes (also this logically implies -c)\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "This program prints the found SOCs as adjacency matrices to stdout, unless --grpahviz has been specified.\n"); + fprintf(stderr, "To exclude (some) of the isomorphic SOCs, it uses a degree-order filter.\n"); + return -1; + } + // EO Parse command-line arguments + // Setup datastructures + int parents[n*n]; // Each node i can have at most n-1 parents. These are listed at parents[n*i+k] for 0<=k 64) { + fprintf(stderr, "Too many graphs to enumarate with an unsiged long long integer (number of node pairs = %d)\n", \ + m); + return -1; + } + unsigned long long max = 1L << m; // Largest `graphnumber' + const int padlen = (int)((float)m/3.322)+1; // Convert log 2 to log 10 + int len = 0; + time_t t0 = time(NULL); + time_t t = time(NULL); + srand((unsigned) time(&t)); + // EO Initiate graph enumeration + fprintf(stderr, "Generating SOCs with %d nodes\n", n); + if (RANDOM) + fprintf(stderr, " Picking graphs at random\n"); + if (NONDAGONLY) + fprintf(stderr, " Filter: Omitting DAGs\n"); + if (NOSINK) + fprintf(stderr, " Filter: Omitting graphs with sink nodes\n"); + if (NOSOURCE) + fprintf(stderr, " Filter: Omitting graphs with source nodes\n"); + // We will always omit the graph with `graphnumber' 0; + // it is unintersting anyway + unsigned long long graphnumber = 0; + unsigned long long graphschecked = 0; + for (; graphnumber < max && (!RANDOM || len < RANDOM);) { + const time_t now = time(NULL); + // Print status every second + if (now > t) { + t = now; + const int deltat = now - t0; + const float rate = (float)graphschecked/(float)deltat; + const float SOCrate = (float)len/(float)deltat; + const float percentage = (RANDOM == 0) ? \ + 100*(float)graphnumber/(float)max : 100*(float)len/(float)RANDOM; + const float ETC = (RANDOM == 0) ? \ + ((float)(max - graphschecked)/(rate))/3600 : \ + ((float)(RANDOM - len)/(SOCrate))/3600; + fprintf(stderr, "\r%6.2f%% %*llu/%llu (%i SOCs found, %d seconds, %*.2f graphs/s, %*.2f SOCs/s, %*.2fh estimated time left)", \ + percentage, padlen, graphnumber, max, len, deltat, padlen+3, rate, \ + padlen+3, SOCrate, padlen, ETC); + fflush(stderr); + } + // Convert graph index `grpahnumber' to the lists parents, children, + // parentslen, childrenlen + graphnrtolists(n, graphnumber, parents, parentslen, children, childrenlen); + // Increase checked counter and prepare for next iteration + graphschecked++; + if (RANDOM) + graphnumber = rand_uint64(max); + else + graphnumber++; + // EO Increase checked counter and prepare for next iteration + // If enabled, compare against degree options + if (NOSINK || NOSOURCE) { + // Get some degree properties + int minindegree = n; + int minoutdegree = n; + for (int node = 0; node < n; node++) { + if (minindegree > parentslen[node]) + minindegree = parentslen[node]; + if (minoutdegree > childrenlen[node]) + minoutdegree = childrenlen[node]; + } + if (NOSINK && minoutdegree == 0) continue; + if (NOSOURCE && minindegree == 0) continue; + } + // Check whether this graph is canonical or not; continue if graph has not + // proper ordering of degress (there is an isomorphic graphs to this one + // that has been or will be checked) + if (!isdegreeordered(n, parents, parentslen, children, childrenlen)) + continue; + // Ignore graphs that are not connected + if (!isconnected(n, parents, parentslen, children, childrenlen)) + continue; + // Find cycles + const int num_cycles = find_cycles(cycles, cyclescnt, n, children, \ + childrenlen); + // If enabled, ignore DAGs + if (NONDAGONLY && num_cycles > 0) continue; + // Test the Siblings-On-Cycles property + // A DAG is trivially a SOC + if (num_cycles > 0 && !gissoc(n, parents, parentslen, children, \ + childrenlen, cycles, cyclescnt)) continue; + // We have found a SOC + len++; + if (GRAPHVIZ) + dumpgv(n, graphnumber, children, childrenlen); + else + dumpgraph(n, children, childrenlen); + } + const int deltat = time(NULL) - t0; + const float rate = (deltat == 0) ? graphschecked : \ + (float)graphschecked/(float)deltat; + const float SOCrate = (deltat == 0) ? len : (float)len/(float)deltat; + fprintf(stderr, "\r%6.2f%% %*llu/%llu (%i SOCs found, %d seconds, %*.2f graphs/s, %*.2f SOCs/s, %*.2fh estimated time left)", \ + 100.00, padlen, graphnumber, max, len, deltat, padlen+3, rate, padlen+3, \ + SOCrate, padlen, 0.00); + fprintf(stderr, "\nFound %d SOCs in %d seconds\n", len, deltat); + // Free manually allocated memory + free(cycles); + return 0; } diff --git a/src/SOCgraphviz.c b/src/SOCgraphviz.c index a34d808..94db46b 100644 --- a/src/SOCgraphviz.c +++ b/src/SOCgraphviz.c @@ -1,80 +1,80 @@ -// SPDX-FileCopyrightText: 2023 Ämin Baumeler and Eleftherios-Ermis Tselentis +// SPDX-FileCopyrightText: 2023 Ämin Baumeler and +// Eleftherios-Ermis Tselentis // // SPDX-License-Identifier: GPL-3.0-or-later #include int main(int argc, char *argv[]) { - // Parse command-line arguments - if(argc != 2) { - fprintf(stderr, "Usage: %s \n", argv[0]); - fprintf(stderr, " File name with adjacency matrices of simple directed graphs\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "[FILE FORMAT]\n"); - fprintf(stderr, " Each line in `filename' must contain the adjacency matrix of a simple directed graph in the format\n"); - fprintf(stderr, " {{a00,a01,...},{a10,a11,...},...} where aij=1 if and only if the graph has the edge i -> j\n"); - fprintf(stderr, " The file `filename' may contain graphs with different order (number of vertices)\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "This program translates to adjacency matrices into the Graphviz format, and prints them to stdout.\n"); - return -1; - } + // Parse command-line arguments + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + fprintf(stderr, " File name with adjacency matrices of simple directed graphs\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "[FILE FORMAT]\n"); + fprintf(stderr, " Each line in `filename' must contain the adjacency matrix of a simple directed graph in the format\n"); + fprintf(stderr, " {{a00,a01,...},{a10,a11,...},...} where aij=1 if and only if the graph has the edge i -> j\n"); + fprintf(stderr, " The file `filename' may contain graphs with different order (number of vertices)\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "This program translates to adjacency matrices into the Graphviz format, and prints them to stdout.\n"); + return -1; + } - // Open file - FILE *fp; - fp = fopen(argv[1], "r"); - if(fp == NULL){ - fprintf(stderr, "ERROR: Could not read graphs from file `%s'.\n", argv[1]); - return -1; - } - // Parse - int headprinted = 1; - int line = 1; - int n = -1; - int row = 0; - int col = 0; - char ch; - printf("strict digraph \"File_%s_line_%d\" {", argv[1], line); - while(!feof(fp)) { - ch = fgetc(fp); - switch(ch) { - case '{': - if(row==0 && col==0 && headprinted==0) { - headprinted = 1; - printf("strict digraph \"File_%s_line_%d\" {", argv[1], line); - } - break; - case '0': - case '1': - if(ch=='1') - printf("G%dN%d->G%dN%d;", line,row,line,col); - col++; - break; - case '}': - if(n==-1) - n = col; - if(col==n) { - row++; - col=0; - } - break; - case '\n': - printf("}\n"); - headprinted = 0; - line++; - row = 0; - col = 0; - n = -1; - break; - case ',': - case ' ': - case 0xffffffff: - break; - default: - fprintf(stderr, "File contains wrongly formatted graph\n"); - fclose(fp); - return -1; // Format error - } - } - fclose(fp); - return 0; + // Open file + FILE *fp; + fp = fopen(argv[1], "r"); + if (fp == NULL) { + fprintf(stderr, "ERROR: Could not read graphs from file `%s'.\n", argv[1]); + return -1; + } + // Parse + int headprinted = 1; + int line = 1; + int n = -1; + int row = 0; + int col = 0; + printf("strict digraph \"File_%s_line_%d\" {", argv[1], line); + while (!feof(fp)) { + char ch = fgetc(fp); + switch (ch) { + case '{': + if (row == 0 && col == 0 && headprinted == 0) { + headprinted = 1; + printf("strict digraph \"File_%s_line_%d\" {", argv[1], line); + } + break; + case '0': + case '1': + if (ch == '1') + printf("G%dN%d->G%dN%d;", line, row, line, col); + col++; + break; + case '}': + if (n == -1) + n = col; + if (col == n) { + row++; + col = 0; + } + break; + case '\n': + printf("}\n"); + headprinted = 0; + line++; + row = 0; + col = 0; + n = -1; + break; + case ',': + case ' ': + case 0xffffffff: + break; + default: + fprintf(stderr, "File contains wrongly formatted graph\n"); + fclose(fp); + return -1; // Format error + } + } + fclose(fp); + return 0; } diff --git a/tools/removeiso.py b/tools/removeiso.py index f9337dd..d461df4 100755 --- a/tools/removeiso.py +++ b/tools/removeiso.py @@ -1,6 +1,7 @@ #!/usr/bin/python3 """ -SPDX-FileCopyrightText: 2023 Ämin Baumeler and Eleftherios-Ermis Tselentis +SPDX-FileCopyrightText: 2023 Ämin Baumeler and +Eleftherios-Ermis Tselentis SPDX-License-Identifier: GPL-3.0-or-later """