diff --git a/graph/graph.cpp b/graph/graph.cpp index 91fae50cb2c684b411b2c669c11861d8469152a9..a3ed67b350289013b1289e02237582a60f8b4688 100644 --- a/graph/graph.cpp +++ b/graph/graph.cpp @@ -35,9 +35,96 @@ //----------------------------------------------------------------------------- // The first command line argument is the graph ID. -// Currently, this program can generate only the following graphs: -// fn_k: "Fence Graph" with Nodes V = {1, ..., 2n} -// and Edges E = { (i, (i+j-1 mod n)+n+1) | 1 <= i <= n and 0 <= j < k } +// The graph ID consists of a letter (lowercase or uppercase are equivalent) +// and one or two parameters (natural numbers). +// If there are two parameters, they are separated with "_". +// For instance, M1000_10 is a "Multipath" graph that consists of 10 paths +// of length 1000. + +// The second and following command line parameters are the output files. +// Currently, the extension of the output files must be .P or .tsv. +// For .P, Prolog facts are generated, +// for .tsv, Tab-separated values. + +// This program can generate the following graphs: +// ----------------------------------------------- + +// Bn: Binary tree of height n +// 2^n - 1 nodes, 2^n - 2 edges. +// N = {1, ..., 2^n - 1} +// E = {(i, 2*i) | i = 1, ..., 2^{n-1} - 1} u +// {(i, 2*i + 1) | i = 1, ..., 2^{n-1} - 1} + +// Cn: Cycle with n nodes +// n nodes, n edges. +// N = {1, ..., n} +// E = {(i, i+1) | i = 1, ..., n-1} u {(n, 1)} + +// Dn_k: Diamond (k copies) +// Dn: Diamond (k = 1, i.e. one copy) +// Each diamond consists of a bottom node, e.g. 1 for the first diamond, +// from there edges to n nodes (e.g. 2, ..., n+1), +// and from each of these nodes edges to a top node (e.g. n+2). +// k * (n+2) nodes, k * 2*n edges +// N = {1, ..., k * (n+2)} +// E = { (i*(n+2) + 1, i*(n+2) + 1 + j) | i = 0,...,k; j = 1,...,n} u +// { (i*(n+2) + j, i*(n+2) + n + 2) | i = 0,...,k; j = 1,...,n} u + +// Kn: Complete graph with n nodes. +// n nodes, n^2 edges. +// N = {1, ..., n} +// E = {(i, j) | i = 1, ..., n; j = 1, ..., n } + +// Mn_k: Multipath: k paths of length m. +// n*k nodes, (n-1)*k edges. +// V = {1, ..., n*k} +// E = {(i, i+k) | i = 1, ..., (n-1)*k} + +// Pn: Path of length n +// n nodes, n-1 edges. +// V = {1, ..., n} +// E = {(i, i+1) | i = 1, ..., n-1} + +// Sn_k: Shortcut graph (cycle of length n with shortcuts) +// It is required that n is divisible by k+1. +// Each node has out degree k+1. +// E.g. from node 1, there is one edge to node 2 (as in standard cycle), +// then one edge to node 2 + (n/(k+1)), +// then one edge to node 2 + 2 * (n/(k+1)), +// and so on until node 2 + k * (n/(k+1)). +// The next value in the sequence, 2 + (k+1) * (n/(k+1)) modulo n is 2. +// For instance, if k=1, there is one shortcut edge spanning half the +// cycle, e.g. node 1 has connections to node 2 and node 2 + n/2. +// For n=10 and k=2, the edges are (1,2), (1,7), (2,3), (2,8), ... +// The graph has n nodes, n * (k+1) edges. +// N = {1, ..., n} +// E = {(i, i+1) | i = 1, ..., n-1} u {(n, 1)} u // Standard Cycle +// {(i, 1 + ((i + (n*j)/(k+1)) mod n) | i = 1,...,n; j=1,...,k } + +// Tn: Total order graph (maximum acyclic graph) + +// Un_k: Random Graph, Uniform Distribution of Node Degrees +// n nodes, k edges + +// Vn: InVerted binary tree of height n (looks like V if edges point down) +// 2^n - 1 nodes, 2^n - 2 edges. +// N = {1, ..., 2^n - 1} +// E = {(2*i, i) | i = 1, ..., 2^{n-1} - 1} u +// {(2*i + 1, i) | i = 1, ..., 2^{n-1} - 1} + + +// Wn_k: Single level of edges, mesh, 2n nodes, degree k): +// V = {1, ..., 2n} +// E = { (i, (i+j-1 mod n)+n+1) | 1 <= i <= n and 0 <= j < k } + +// Xn_k: X-graph (n edges to central node, k from there) + +// Yn_k: Y-graph (n nodes point to central node, then path of length k) +// n+k nodes: V= {1,...,n+k} +// n+k-1 edges: +// E = {(i, n+1)} | i = 1,...,n} u {(n+i, n+i+1) | i = 1,...,k-1} + +// The program code for graph g is in the procedure gen_graph_g. //============================================================================= // Buffer Size for Graph Parameter: @@ -1044,21 +1131,78 @@ int next_prime(int p) { return p; } +//----------------------------------------------------------------------------- +// Random number generator (to make random graphs deterministic and portable): +//----------------------------------------------------------------------------- + +// The code is from +// https://de.wikipedia.org/wiki/KISS_(Zufallszahlengenerator) +// The same generator is the main random number generator mentioned in +// http://www0.cs.ucl.ac.uk/staff/d.jones/GoodPracticeRNG.pdf + +static uint32_t x = 123456789; // seed can be arbitrary, +static uint32_t y = 362436000; // as long as y != 0 and +static uint32_t z = 521288629; // z,c are not both 0 +static uint32_t c = 7654321; + +uint32_t rand_KISS() { + uint64_t t; + + // Linear congruential generator: + x = 69069 * x + 12345; + + // Xorshift: + y ^= y << 13; + y ^= y >> 17; + y ^= y << 5; + + // Multiply-with-carry: + t = (uint64_t)698769069 * z + c; + c = t >> 32; + z = (uint32_t) t; + + return x + y + z; +} + //----------------------------------------------------------------------------- // Compute Random Number from 0 to n (inclusive): //----------------------------------------------------------------------------- int rand_until(int n) { - //return rand() % (n+1); - int r = rand(); + //int r = rand(); + uint32_t r = rand_KISS(); + int result = r % (n+1); + // This is the simple version that can be used when n is much + // smaller than the maximal random number that is generated. + // Theoretically, the numbers are not completely evenly + // distributed, because the last set of n+1 numbers before + // the maximal random number is usually not complete + // (unless the number of results that the random number generates + // is dividable by n+1). + // However, if n is not more than 4 million (e.g. we generate + // not more than 4M edges), the error from a uniform distribution + // is less than one promille. + + //int result = (int) (((double) r) * (n+1) / (0xFFFFFFFF + 1.0)); + // This version avoids the slight problem, but uses double. + // The divisor is the maximal result of rand_KISS + 1: + // 4294967296.0 + // When double is converted to int, no rounding is done, + // but the floor function is used. + // Therefore, the result can never be n+1 or larger. + + if(result < 0 || result > n) { + std::cerr << "Error in random number generator!\n"; + exit(24); + } std::cout << "rand_until(" << n << ") = " << result << "\t[rand = " << r << "]\n"; return result; } //----------------------------------------------------------------------------- -// Random Suffling of Array: +// Random Shuffling of Array: //----------------------------------------------------------------------------- void rand_shuffle(int arr[], int len) { @@ -1070,7 +1214,7 @@ void rand_shuffle(int arr[], int len) { } std::cout << "\n"; - std::cout << "Result of Shuffle (Array Length: " << len << "\n"; + std::cout << "Result of Shuffle (Array Length: " << len << ")\n"; for(int i = 0; i < len; i++) std::cout << "\t arr[" << i << "] = " << arr[i] << "\n"; std::cout << "\n"; @@ -1274,7 +1418,7 @@ void gen_graph_s(int n, int k, output_t out, gsize_t gsize) { // Check divisibility condition: if(n % (k+1) != 0) { std::cout << "S[n,k] requires that n is divisible by k+1.\n"; - exit(24); + exit(25); } // Set number of nodes: @@ -1290,7 +1434,8 @@ void gen_graph_s(int n, int k, output_t out, gsize_t gsize) { for(int j = 1; j <= k; j++) { int skip = (n*j)/(k+1); for(int i = 1; i <= n; i++) { - out->write_edge(i, 1 + (i-1 + skip) % n); + //out->write_edge(i, 1 + (i-1 + skip) % n); + out->write_edge(i, 1 + (i + skip) % n); } } @@ -1305,14 +1450,22 @@ void gen_graph_s(int n, int k, output_t out, gsize_t gsize) { gsize->set_tc_inst(r); // SG Size: - if(k == 1 && n % 4 == 2) { - gsize->set_sg_size(m * m / 2); - gsize->set_sg_inst(2 * (m * m + m)); - } - else { - gsize->set_sg_size(m * m); - gsize->set_sg_inst(m * (k + 1) + m * m * (k+1) * (k+1)); - } + //if(k == 1 && n % 4 == 2) { + // gsize->set_sg_size(m * m / 2); + // gsize->set_sg_inst(2 * (m * m + m)); + //} + //else { + // gsize->set_sg_size(m * m); + // gsize->set_sg_inst(m * (k + 1) + m * m * (k+1) * (k+1)); + //} + gsize->set_sg_size(m * (k+1)); + //gsize->set_sg_inst(m*(k+1) + (k+1) * (m * (k+1)) * (k+1)); + //The following is the same, slightly shorter: + gsize->set_sg_inst(m * (k+1) * (1 + (k+1) * (k+1))); + if(k > 0 && n > 1) + gsize->set_sg_iter(2); + else + gsize->set_sg_iter(1); } //----------------------------------------------------------------------------- @@ -1627,7 +1780,7 @@ int main(int argc, str_t argv[]) // The program should be called with the graph and the output files: if(argc < 3) { std::cout << "Usage: ./graph GraphID OutputFile1 ...\n"; - exit(25); + exit(26); } // Get graph parameters, first code: @@ -1636,7 +1789,7 @@ int main(int argc, str_t argv[]) char graph_code = *p++; if(graph_code == '\0') { std::cout << "Impossible empty Graph ID.\n"; - exit(26); + exit(27); } // First parameter: @@ -1647,7 +1800,7 @@ int main(int argc, str_t argv[]) par_chars[i] = 0; if(i == 0) { std::cout << "First parameter in graph ID missing.\n"; - exit(27); + exit(28); } int par1 = str_int(par_chars); if(*p == 'k') { @@ -1682,7 +1835,7 @@ int main(int argc, str_t argv[]) par_chars[i] = 0; if(i == 0) { std::cout << "Second parameter in graph ID missing.\n"; - exit(28); + exit(29); } par2 = str_int(par_chars); if(*p == 'k') { @@ -1712,7 +1865,7 @@ int main(int argc, str_t argv[]) // Check that we have successfully parsed the entire graph ID: if(*p != '\0') { std::cout << "Unexpected characters at the end of graph ID.\n"; - exit(29); + exit(30); } // Open output file(s): @@ -1795,11 +1948,11 @@ int main(int argc, str_t argv[]) // Acyclic random graph std::cout << "Please use the other graph generator " << "for this graph.\n"; - exit(30); + exit(31); default: std::cout << "Unknown graph type '" << graph_code << "'.\n"; - exit(31); + exit(32); } // Output number of edges written: @@ -1832,6 +1985,7 @@ int main(int argc, str_t argv[]) sg.export_gsize(&gsize); // Print computed size data: + std::cout << "\n"; std::cout << "Size Data Computed From Graph:\n"; std::cout << "=============================\n"; gsize.print();