/* 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 3 of the License, or (at your option) any later version, see . 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. */ /**************************************************************************************************************************** Very quick guide for Linux users (For more details including compilation on Windows see below.) 1) Install csdp or sdpa. For example on Ubuntu you can install csdp by typing: % sudo apt-get install coinor-csdp 2) Compile this file: % g++ ExactDensityBounder.cpp -lm -fopenmp -O3 -o edb 3) Tell the program which SDP solver you are using: e.g. if you are using csdp then: % ./edb -s csdp 4) Examples of usage: % ./edb 5 {123 124 345} % ./edb 6 {123 124 345 156} % ./edb 6 {123 124 134 234} {123 124 125 345 346} {123 124 345 156 256} {123 124 125 346 356 456} -o F_3 % ./edb 6 {123 124 134 234} 4:{123} -i -o K4G1 % ./edb 6 {123 124 134 234} 4:{123} -i -t + 1.1 4.2 4.3 -o ShortProof The 1st example reproves the upper bound of 2/9 of Frankl and Furedi's result (Theorem 2.2) using 5 vertex 3-graphs. The proof is written in "Temp.txt". (Note that supersaturation is used so the forbidden graph K_4^- = {123 124 134} is automatically added.) The 2nd example reproves the upper bound of 2/9 in Theorem 2.3 using 6 vertex 3-graphs. The proof is written in "Temp.txt". The 3rd example reproves the upper bound of 8/27 for the family F_3 in Theorem 3.4 using 6 vertex 3-graphs. The proof is written in "F_3.txt". The 4th example reproves the bound of 5/9 for the induced {K_4^(3),G_1} problem (Theorem 4.1 due to Razborov) using 6 vertex 3-graphs. The proof is written in "K4G1.txt". The 5th example proves the same result as the 4th but only makes use of the single type of order 1 together with two of the three types of order 4. This gives a shorter proof than the previous example, which is written in "ShortProof.txt". Provided that the SDP solver is successful the program will attempt to prove that (a suitable rational approximation of the SDP solver's output) is exact. In many cases it will fail (often because the result is not sharp) however in these cases the absolute value of the SDP output still provides a numerical upper bound for the Turan density that may be of interest. See the comments below for more detailed usage instructions. ****************************************************************************************************************************/ //Name : ExactDensityBounder. // //Author : Rahil Baber. // //Date : 6th July 2011. // //Description : An easier to use version of DensityBounder which can make approximate results exact. The program // determines an upper bound for Turan densities of 3-uniform hypergraphs. It creates an SDP problem to // determine the bound. The SDP problem is then solved by third party SDP solvers (such as csdp or sdpa). // The program then removes rounding errors and outputs the details into a ".txt" file that can be verified // by the program DensityChecker. This source code has not been written so that it is easy to understand // (instead see the source code for DensityBounder which is a bit more readable). // //Acknowledgements : This program is largely based on the method described by Alexander Razborov in his paper // "On 3-Hypergraphs with Forbidden 4-Vertex Configurations", SIAM Journal on Discrete Mathematics, 24 (3) // 946-963 (2010). Thanks to Emil Vaughan for pointing out that formulating the problem in the primal // setting is more efficient for smaller problems. // //Compilation instructions : This program has been successfully compiled under Visual C++ 2010, and gcc 4.4.1. The code makes // use of OpenMP to speed up the calculations when multiple processors are present, though the program will // still function correctly if OpenMP is not enabled/supported by the compiler. // // Visual C++ 2010 : The program should be compiled as a console application. It is recommended that the // "Active Solution Configuration" be set to "Release" so that the compiler can fully optimize the code. // OpenMP can be enabled via "Project>Properties>Configuration Properties>C/C++>Language>Open MP Support". // // gcc 4.4.1 : Simply navigate to the directory in which the source code resides and type at the terminal // "g++ ExactDensityBounder.cpp -lm -O3 -o edb" // this should create an optimized executable called "./edb". To enable OpenMP support instead type // "g++ ExactDensityBounder.cpp -lm -fopenmp -O3 -o edb". // //Basic usage instructions : The program formulates the problem of finding an upper bound of the Turan density of a family // of 3-graphs as an SDP (semidefinite program). The program cannot solve SDP problems itself but instead // calls an external third party SDP solver such as CSDP or SDPA. As such a stand-alone binary executable // of an SDP solver should be available somewhere on your system. Currently this program only produces the // SDP problem in the ".dat-s" format and can only handle solutions written in a format which is consistent // with that of CSDP's output or one of the SDPA family of solvers. // // The program should be run from a command-line interface as it expects arguments to be passed to // it from the command-line. The very first time the program is run it should be told what the default // command is for solving an SDP problem. This can be set using the "-s" switch. For example in windows we // might type "ExactDensityBounder.exe -s C:\sdpa.exe" at the command-line to indicate the executable // "sdpa.exe" located on the C drive should be run when the program wants to solve an SDP problem. In linux // we might type "./edb -s ./csdp" to indicate that the executable "csdp" located in the current working // directory should be run as the default solver. // // Once the default SDP command has been set, an upper bound for the turan density can be calculated by // typing first the order of the subgraphs H which we will be searching over, followed by the edge sets of // the graphs that are being forbidden. E.g. "./edb 6 {123 124 134 234}". Optionally a series of switches // can be passed after the edge lists to indicate a variety of extra data. E.g. the "-o" switch is used to // specify an output filename, the "-i" switch indicates that the graphs are forbidden induced subgraphs, // and the "-c" switch overrides the default SDP command. See the sections below for more detailed // information. // // The program analyses the solution to the SDP problem given by the SDP solvers and tries to guess what // the bound should be if there were no rounding errors. It then tries to rigorously prove this bound. If // it fails it should return the best upper bound that it could prove, however, this functionality has not // yet been fully implemented (see the "To do" list below). // //Examples of basic usage : // ExactDensityBounder.exe -s csdp.exe // ./edb 6 {123 124 134 234} -o K4 // ExactDensityBounder 5 {234} 4 : {123 124 134 234} -i -o "Induced Prob" // ./edb 5 {123 124 134} -c ./sdpa // //Usage : // // Displays (basic) usage. // // Test // Wipes and writes "" into it. Used to internally test the system() command. This is not intended for // users but included for completeness. // // -s // Use to set the default SDP command for solving SDP problems. See the "Arguments" section below. // // [-i] [-c ] [-o ] [-t <+|-> [. ...]] [-] //[-r] [-a ] [-b] // Use to calculate an upper bound for the Turan density of . See the "Arguments" section below. The switches can // be specified in any order after . // //Arguments : // <> : A mandatory field. // // [] : An optional field. // // : The name of the compiled program. For example "ExactDensityBounder.exe" on windows, "./edb" or "./a.out" // in linux. // // : The order of the subgraphs H. This should be an integer between 3 and 9. Increasing the order will result in // a better bound but the computation will be more intensive. Currently orders higher than 9 are not supported by this // program. // // : A list of 3-uniform hypergraphs (separated by spaces). Graphs with orders higher than 9 are not supported. // Each graph should either be of the form " : " or simply "". "" // should be a single digit and if it is omitted then it is taken to be the largest label of a vertex in the edge set. // The vertices are assumed to be labelled by integers starting from 1. The edge set should be enclosed in curly braces // and contain a list of 3 digit numbers (separated by spaces) representing the edges. See the "Examples" section above. // // [-i] : Indicates that we wish to consider only induced subgraphs. If "-i" is used then the program only forbids // from appearing as *induced* subgraphs. If "-i" is omitted then are forbidden from appearing as // subgraphs (as in the normal definition of Turan density). // // [-c] : Used to override the default command (that runs the SDP solver) with . See also the // "" and "-s" sections below. // // : The command used to run an SDP solver on the underlying semidefinite programming problem. For example // "./csdp" or "sdpa.exe". The program runs the SDP solver by running the command // " .dat-s .out". It always assumes the SDP solver completed successfully and that the // solution is stored in ".out". It can currently only read the ".out" file if it adheres to the same format as // csdp or sdpa's output files. See also the sections on "[-c]", "-s", and "". // // [-o] : Used to indicate as the name to be used for the files the program creates. If is not set // using "-o" then the default name "Temp" is used. See also the section on "". // // : A filename (without extension). It is used to name the temporary ".dat-s" input file to the SDP solver // and the ".out" output file. It is also used to name the ".txt" file which will hold the data which forms the proof. // The program DensityChecker can be used to verify the calculation from the ".txt" file. // // -s : Used to save as the default command that runs the SDP solver. The command is saved in the file // "ConfigEDB.cfg". See the "[-c]" and "" sections. // // [-t] : Sets which types to include or exclude. Run with "-t -" to see the "." of each of the // types. "-t -" is used to exclude specific types, "-t +" is used to include specific types. For example "-t + 4.1 0.1" // means include only the types "4.1" and "0.1". Note that starts from 1 not 0. If this switch is not used // the default behaviour is to include all types (which is equivalent to the "-t -" command). // // [-p] : Forces the program to work in the primal SDP setting (maximizing Tr(CX) s.t. X>=0 and Tr(A_i X)=a_i). If both // -p and -d are omitted then the application will choose the form which produces the smallest problem. // // [-d] : Forces the program to work in the dual SDP setting (minimizing sum(a_i y_i) s.t. sum(A_i y_i)-C>=0). If both -p // and -d are omitted then the application will choose the form which produces the smallest problem. // // [-r] : Causes the default rounding scheme to be ignored, and instead asks the user to choose the target density, the // sharp inequalities, and the 0-eigenvectors. // // [-a] : Overrides the accuracyProjectSolution variable (which describes how many decimal places the SDP solution should // be rounded too) with . should be a positive integer less than or // equal to 1000 (values less than 10 are recommended). // // [-b] : If this switch is omitted (and "-i" is omitted) then the program will automatically add graphs (which have // blow-ups that are supergraphs of a graph in ) to the forbidden graph list. This does not affect the Turan // density by a supersaturation argument, but can improve the bound the program is able to find. If "-b" (or "-i") is // specified then no additional graphs are added to the forbidden graph list. //*** Unfinished. *** //To do: //- A parameter file to allow the user to tweak all the tolerances without recompilation. //- Need to add the functionality that if making the Turan density exact fails it should output its best bound. // The work around of using "-r" with target density "1/1" should sometimes work. //- Currently does not take the extremal constructions into account (which may help in making a result exact). //- Need to neaten the code and add more comments. //It is unlikely to get finished. #ifdef _OPENMP #include #endif #include #include #include #include #include #include #include #include #include #include #include using namespace std; /***Start of the list of tolerance and threshold values which you may want to tweak.***/ //Used to allocate memory for CLargeInt objects. If an overflow error occurs it most likely //means that LI_MAX was set too low. The larger LI_MAX is, the slower the program will run //and the more memory it will require. //*** You may need to change this value.*** #define LI_MAX 1000 //Used by ProjectSolution(). //The higher the value the closer the solution will be to the SDP's output (but the longer the program will take to run). //Roughly speaking it will round the output to "accuracyProjectSolution" number of decimal places. //Should be set to a positive integer. Can be changed via the "-a" command-line switch. long accuracyProjectSolution = 6; //Used by CalculateSharpIneq() to decide which inequalities are sufficiently close to the target density that they //should be made sharp. //Should be set to a small non-negative value. double toleranceSharpIneq = 1e-6; //Used in MakeMatricesPositiveDefinite() to decide how many 0-eigenvectors there are in each matrix. //If the "Threshold for zero" value in the "_constraints.txt" file is above thresholdMaxConstraint then it is excluded as //a choice for the number of 0-eigenvectors. //Should be set to a small non-negative value. double thresholdMaxConstraint = 1e-5; //Used in MakeMatricesPositiveDefinite() to decide how many 0-eigenvectors there are in each matrix. //If the "max error in rationalized values" value in the "_constraints.txt" file is above thresholdMaxError then it is //excluded as a choice for the number of 0-eigenvectors. //Should be set to a small non-negative value. double thresholdMaxError = 1e-5; //Used in MakeMatricesPositiveDefinite() to decide how many 0-eigenvectors there are in each matrix. //If the "largest denominator in program's guess" value in the "_constraints.txt" file is above thresholdSimpleRational //then it is excluded as a choice for the number of 0-eigenvectors. //Should be set to a small positive value. long thresholdSimpleRational = 20; //Used in MakeMatricesPositiveDefinite() to rationalize the 0-eigenvectors. //The denominator of the rationalized eigenvector entry is limited to "accuracySimpleRational". //Should be set to a positive value. long accuracySimpleRational = 100; //Used by ProjectSolution() to identify zero entries when solving a linear system of constraints. //Should be a small non-negative value. double toleranceProjectSolution = 1e-6; //Used by FindZeroEigenData() to identify entries which should be considered zero. //Should be a small non-negative value. double toleranceEigenConstraint = 1e-10; //Used by CalculateSharpIneq() to decide what the target density we should be aiming for should be. //The denominator is limited to be less than or equal to TargetDensityAccuracy. //Should be set to a positive integer. It is unlikely that you will need to change this value. long accuracyTargetDensity = 100; /***End of the list of tolerances and thresholds.***/ //Set to false to manually choose the target density, sharp inequalities, and the 0-eigenvectors. //Can be set to false via the "-r" command-line switch. bool bDefaultRoundingScheme = true; const char* configName = "ConfigEDB.cfg"; //Holds the default SDP command. const char* defaultFilename = "Temp"; string FilenameSDP; string FilenameOut; string FilenameTxt; string SDPCommand; string RunCommand; //Factorial[n] = n!. long Factorial[10] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880}; //Iso[][] (together with Factorial[]) will be useful in generating isomorphic copies of //graphs. Iso[][] is an array (orderOfH! by orderOfH) of all permutations of //{0,1,...,orderOfH-1}. Iso[p][n] is the image of n under permutation p. Iso[][] is ordered //in such a way that {Iso[p][] : p<=r!} is the set of permutations in which only {0,1,..,r-1} //are permuted and {r,...,orderOfH-1} are left in place. InitIso() allocates memory for //Iso[][] and initializes it. Its memory is deallocated by Cleanup(). vector > Iso; //Exception classes which are thrown when overflow or division by zero occurs. class ErrorOverflow{}; class ErrorDivisionByZero{}; //Used to store and manipulate very large integers. Can store any integer of magnitude less //than 2^(8*LI_MAX). The integer's magnitude is stored in a static array to avoid slow //dynamic memory allocation. The amount of the array being used is also stored, to increase //the speed of operations on small integers. Functions which use this class may throw an //ErrorOverflow or ErrorDivisionByZero object. class CLargeInt { public: //The default constructor, sets the large integer to 0. CLargeInt(); //The following constructors allow the fundamental integral data types to be //automatically cast into CLargeInt objects. These constructors call Assign() and so may //throw an ErrorOverflow object. No object will be thrown if LI_MAX >= //sizeof(unsigned long), (sizeof(unsigned long) = 4 usually). CLargeInt(unsigned int input); CLargeInt(int input); CLargeInt(unsigned long input); CLargeInt(long input); //The copy constructor. CLargeInt(const CLargeInt& input); //The assignment operator. CLargeInt& operator=(const CLargeInt& input); //The unary operators. Used to evaluate +A or -A, when A is a CLargeInt object. const CLargeInt operator+() const; const CLargeInt operator-() const; //The basic arithmetic operators. The +,-,* operators may throw an ErrorOverflow object. //The integer division operators / and % are wrappers for the Divide() function and so //may throw an ErrorDivisionByZero object. friend const CLargeInt operator+(const CLargeInt& A, const CLargeInt& B); friend const CLargeInt operator-(const CLargeInt& A, const CLargeInt& B); friend const CLargeInt operator*(const CLargeInt& A, const CLargeInt& B); friend const CLargeInt operator/(const CLargeInt& A, const CLargeInt& B); friend const CLargeInt operator%(const CLargeInt& A, const CLargeInt& B); //The comparison operators. They are wrappers for the Compare() function. friend const bool operator==(const CLargeInt& A, const CLargeInt& B); friend const bool operator!=(const CLargeInt& A, const CLargeInt& B); friend const bool operator< (const CLargeInt& A, const CLargeInt& B); friend const bool operator<=(const CLargeInt& A, const CLargeInt& B); friend const bool operator> (const CLargeInt& A, const CLargeInt& B); friend const bool operator>=(const CLargeInt& A, const CLargeInt& B); //Used to convert the CLargeInt object into a long. If the value stored in the object is //larger than what can be stored in a long variable it throws an ErrorOverflow object. long ConvertToLong() const; private: //Used by constructors to convert the fundamental integral data types to CLargeInt //objects. It may throw an ErrorOverflow object but only if LI_MAX B. static long Compare(const CLargeInt& A, const CLargeInt& B); //Used by the integer division operators / and %. Define |input| to be the magnitude of //input, and sign(input) to be +1 if input>=0 and -1 if input<0. The function calculates //outputQ, and outputR as follows: //outputQ = sign(inputN)*sign(inputD)*(|inputN|/|inputD|), where '/' is integer division, //outputR = sign(inputN)*(|inputN|%|inputD|). //The choice of signs for outputQ and outputR were chosen so that inputN = inputD * //outputQ + outputR. If inputD = 0 an ErrorDivisionByZero class is thrown. The function //assumes outputQ resides in a different address to inputN, inputD, and outputR. It also //assumes outputR resides in a different address to inputN, inputD, and outputQ. static void Divide(const CLargeInt& inputN, const CLargeInt& inputD, CLargeInt& outputQ, CLargeInt& outputR); //Data[], Size, and bPositive hold the data that represents the large integer. Data[] and //Size hold the magnitude of the large integer, bPositive holds the sign. bPositive = //true if the integer is greater than or equal to 0, and false otherwise. The magnitude //of the integer is given by: //Data[Size-1]*256^(Size-1)+Data[Size-2]*256^(Size-2)+...+Data[1]*256+Data[0]. //Size holds the number of bytes the integer takes up in Data[]. To avoid leading zeros, //which can artificially make a small integer use up a lot of the Data[] array, we add //the following restriction. If the integer is 0 then Size=0, otherwise Data[Size-1] must //be non-zero. unsigned char Data[LI_MAX]; long Size; //Number of bytes. Size=0 <=> integer=0. Size!=0 => Data[Size-1]!=0. bool bPositive; //bPositive=true if integer>=0, false otherwise. }; //Allows us to use << operator to output the CLargeInt object L to the screen or a file. ostream& operator<<(ostream& out, const CLargeInt& L); //Used to store and manipulate fractions with very large numerators and denominators. It uses //two CLargeInt member variables, named N and D, which store the numerator and denominator of //the fraction respectively. Functions which use this class may throw an ErrorOverflow or //ErrorDivisionByZero object. class CLargeFraction { public: //The default constructor, sets the fraction to 0. CLargeFraction(); //The following constructors allow the fundamental integral data types to be //automatically cast into CLargeFraction objects. They may throw an ErrorOverflow object. //No object will be thrown if LI_MAX >= sizeof(unsigned long), (sizeof(unsigned long) = 4 //usually). CLargeFraction(unsigned int input); CLargeFraction(int input); CLargeFraction(unsigned long input); CLargeFraction(long input); //A constructor that allows CLargeInt objects to be automatically cast into //CLargeFraction objects. CLargeFraction(const CLargeInt& input); //The unary operators. Used to evaluate +A or -A, when A is a CLargeFraction object. //These functions may throw an ErrorDivisionByZero object. const CLargeFraction operator+() const; const CLargeFraction operator-() const; //The basic arithmetic operators. The +,-,*,/ operators may throw an ErrorOverflow or //ErrorDivisionByZero object. friend const CLargeFraction operator+(const CLargeFraction& A, const CLargeFraction& B); friend const CLargeFraction operator-(const CLargeFraction& A, const CLargeFraction& B); friend const CLargeFraction operator*(const CLargeFraction& A, const CLargeFraction& B); friend const CLargeFraction operator/(const CLargeFraction& A, const CLargeFraction& B); //The comparison operators. They are wrappers for the Compare() function. They may throw //an ErrorOverflow or ErrorDivisionByZero object. friend const bool operator==(const CLargeFraction& A, const CLargeFraction& B); friend const bool operator!=(const CLargeFraction& A, const CLargeFraction& B); friend const bool operator< (const CLargeFraction& A, const CLargeFraction& B); friend const bool operator<=(const CLargeFraction& A, const CLargeFraction& B); friend const bool operator> (const CLargeFraction& A, const CLargeFraction& B); friend const bool operator>=(const CLargeFraction& A, const CLargeFraction& B); //Removes any common factors between the numerator and the denominator and makes the //denominator positive. It may throw an ErrorDivisionByZero object. void Simplify(); CLargeInt N; //Numerator. CLargeInt D; //Denominator. private: //Used by the comparison operators to compare two fractions A, B. //Returns -1 if A < B, // 0 if A = B, // 1 if A > B. //It may throw an ErrorOverflow or ErrorDivisionByZero object. static long Compare(const CLargeFraction& A, const CLargeFraction& B); }; //Allows us to use << operator to output the CLargeFraction object F to the screen or a file. ostream& operator<<(ostream& out, const CLargeFraction& F); //The default constructor, sets the large integer to 0. CLargeInt::CLargeInt() { Size = 0; bPositive = true; return; } //Allows an unsigned int to be automatically cast into a CLargeInt object. It may throw an //ErrorOverflow object. CLargeInt::CLargeInt(unsigned int input) { Assign(input); return; } //Allows an int to be automatically cast into a CLargeInt object. It may throw an //ErrorOverflow object. CLargeInt::CLargeInt(int input) { unsigned long temp; if(input>=0) Assign(input); else { //Call Assign() on -input. //-input may be 2147483648 which could cause an overflow. To avoid this situation we //cast -(input+1) to an unsigned long then add one. input++; temp = -input; temp++; Assign(temp); bPositive = false; } return; } //Allows an unsigned long to be automatically cast into a CLargeInt object. It may throw an //ErrorOverflow object. CLargeInt::CLargeInt(unsigned long input) { Assign(input); return; } //Allows a long to be automatically cast into a CLargeInt object. It may throw an //ErrorOverflow object. CLargeInt::CLargeInt(long input) { unsigned long temp; if(input>=0) Assign(input); else { //Call Assign() on -input. //-input may be 2147483648 which could cause an overflow. To avoid this situation we //cast -(input+1) to an unsigned long then add one. input++; temp = -input; temp++; Assign(temp); bPositive = false; } return; } //The copy constructor. CLargeInt::CLargeInt(const CLargeInt& input) { long i; //Check if we are trying to copy the object to itself. if(this==&input) return; Size = input.Size; bPositive = input.bPositive; //We do not need to copy all of Data[]. for(i=0;i=B.Size) output.Size = A.Size; else output.Size = B.Size; //Do the addition byte by byte. carryOver = 0; for(i=0;i=LI_MAX) throw ErrorOverflow(); //Overflow occured. output.Data[output.Size] = carryOver; output.Size++; } return output; } else { //Find the difference between the magnitudes of A and B. //If A.Size==B.Size assume |A|>=|B|. if(A.Size>=B.Size) { output.bPositive = A.bPositive; output.Size = A.Size; //Do the subtraction of B from A, byte by byte. carryOver = 0; for(i=0;i=0) { output.Data[i] = sum; carryOver = 0; } else { output.Data[i] = 256+sum; carryOver = -1; } } if(carryOver==0) { //Remove leading zeros. while(true) { if(output.Size==0) { output.bPositive = true; return output; } if(output.Data[output.Size-1]!=0) return output; output.Size--; } } //A.Size==B.Size but |A|<|B|. } //A.Size |A|<|B| output.bPositive = B.bPositive; output.Size = B.Size; //Do the subtraction of A from B, byte by byte. carryOver = 0; for(i=0;i=0) { output.Data[i] = sum; carryOver = 0; } else { output.Data[i] = 256+sum; carryOver = -1; } } //Remove leading zeros. while(true) { if(output.Size==0) { output.bPositive = true; return output; } if(output.Data[output.Size-1]!=0) return output; output.Size--; } } } //The - arithmetic operator. Returns A-B. It may throw an ErrorOverflow object. const CLargeInt operator-(const CLargeInt& A, const CLargeInt& B) { return A+(-B); } //The * arithmetic operator. Returns A*B. It may throw an ErrorOverflow object. const CLargeInt operator*(const CLargeInt& A, const CLargeInt& B) { long i, j, k; long sum, carryOver; CLargeInt output; //Deal with A=0 or B=0 as a special case. if(A.Size==0) return A; if(B.Size==0) return B; //A!=0 and B!=0. //Calculate the sign of A*B. if(A.bPositive==B.bPositive) output.bPositive = true; else output.bPositive = false; //output will take up A.Size+B.Size-{1 or 0} bytes. Provisionally set the size. if(A.Size+B.Size-1>LI_MAX) throw ErrorOverflow(); //Overflow will occur. output.Size = A.Size+B.Size-1; //Initialize output to 0. for(i=0;i=LI_MAX) throw ErrorOverflow(); output.Data[output.Size] = carryOver; output.Size++; } } return output; } //The / arithmetic operator. Returns A/B where / is integer division (see Divide() for a more //precise definition of /). If B=0 an ErrorDivisionByZero object is thrown. const CLargeInt operator/(const CLargeInt& A, const CLargeInt& B) { CLargeInt output, Temp; CLargeInt::Divide(A,B,output,Temp); return output; } //The % arithmetic operator. Returns A%B (see Divide() for a more precise definition of %). //If B=0 an ErrorDivisionByZero object is thrown. const CLargeInt operator%(const CLargeInt& A, const CLargeInt& B) { CLargeInt output, Temp; CLargeInt::Divide(A,B,Temp,output); return output; } //Returns (A==B). const bool operator==(const CLargeInt& A, const CLargeInt& B) { return (CLargeInt::Compare(A,B)==0); } //Returns (A!=B). const bool operator!=(const CLargeInt& A, const CLargeInt& B) { return (CLargeInt::Compare(A,B)!=0); } //Returns (AB). const bool operator>(const CLargeInt& A, const CLargeInt& B) { return (CLargeInt::Compare(A,B)==1); } //Returns (A>=B). const bool operator>=(const CLargeInt& A, const CLargeInt& B) { return (CLargeInt::Compare(A,B)!=-1); } //Used to convert the CLargeInt object into a long. If the value stored in the object is //larger than what can be stored in a long variable it throws an ErrorOverflow object. long CLargeInt::ConvertToLong() const { long i; long max, min; long result; //Check if overflow occurs. if(Size>=sizeof(long)) { //max = 2^{8*sizeof(long)-1} - 1, //min = -2^{8*sizeof(long)-1}. //To avoid overflow, we define them as follows: max = (1L<<(8*sizeof(long)-2))-1; max += 1L<<(8*sizeof(long)-2); min = -max; min -= 1; if(*this=LI_MAX) throw ErrorOverflow(); //Remove the least significant byte of input and store it in Data[i]. Data[i] = input%256; input = input/256; if(input==0) { Size = i+1; return; } } return; } //Used by the comparison operators to compare two large integers A, B. //Returns -1 if A < B, // 0 if A = B, // 1 if A > B. long CLargeInt::Compare(const CLargeInt& A, const CLargeInt& B) { long i; if(A.bPositive!=B.bPositive) { if(A.bPositive==true) return 1; //A >= 0 > B. else return -1; //A < 0 <= B. } //A and B have the same sign. if(A.Size!=B.Size) { if((A.bPositive==true && A.Size>B.Size) || (A.bPositive==false && A.Size B. else return -1; //A < B. } //A.Size = B.Size and A.bPositive = B.bPositive. //Check the magnitude of the integers, beginning with the most significant byte. for(i=A.Size-1;i>=0;i--) { if(A.Data[i]>B.Data[i]) { if(A.bPositive==true) return 1; //A > B. else return -1; //A < B. } if(A.Data[i] B. } //A.Data[i] = B.Data[i]. Check the next most significant byte. } return 0; //A = B. } //Used by the integer division operators / and %. Define |input| to be the magnitude of //input, and sign(input) to be +1 if input>=0 and -1 if input<0. The function calculates //outputQ, and outputR as follows: //outputQ = sign(inputN)*sign(inputD)*(|inputN|/|inputD|), where '/' is integer division, //outputR = sign(inputN)*(|inputN|%|inputD|). //The choice of signs for outputQ and outputR were chosen so that inputN = inputD * //outputQ + outputR. If inputD = 0 an ErrorDivisionByZero class is thrown. The function //assumes outputQ resides in a different address to inputN, inputD, and outputR. It also //assumes outputR resides in a different address to inputN, inputD, and outputQ. void CLargeInt::Divide(const CLargeInt& inputN, const CLargeInt& inputD, CLargeInt& outputQ, CLargeInt& outputR) { long i, j; long sum, carryOver; bool bSubtractD; long bitD, bitN; long nBit, nByte; long rBit, rByte; long overflowByte; //Deal with inputN=0 and inputD=0 as a special case. if(inputD.Size==0) throw ErrorDivisionByZero(); if(inputN.Size==0) { outputQ = 0; outputR = 0; return; } //We will do the division via the long division method in binary. //Find the leading bit of inputD. j = inputD.Data[inputD.Size-1]; for(i=7;i>=0;i--) { if((j>>i)%2==1) break; } bitD = i; //Find the leading bit of inputN. j = inputN.Data[inputN.Size-1]; for(i=7;i>=0;i--) { if((j>>i)%2==1) break; } bitN = i; //Check if |inputD|>|inputN| obviously holds. if(inputD.Size>inputN.Size || (inputD.Size==inputN.Size && bitD>bitN)) { outputQ = 0; outputR = inputN; return; } //|inputN| is stored in at least as many bits as |inputD|. //|inputD| is stored in "(inputD.Size-1)*8+bitD+1" bits. Fill outputR with the //"(inputD.Size-1)*8+bitD+1" most significant bits of inputN. outputR.Size = inputD.Size; for(i=0;i>nBit)&1)<= |inputD|. If it is we set the bit in outputQ to 1, and //subtract |inputD| from |outputR|. if(overflowByte!=0) bSubtractD = true; //|outputR| takes up more bytes than |inputD|. else { for(i=outputR.Size-1;i>=0;i--) { if(outputR.Data[i]>inputD.Data[i]) { bSubtractD = true; break; } if(outputR.Data[i]=0) { outputR.Data[i] = sum; carryOver = 0; } else { outputR.Data[i] = 256+sum; carryOver = -1; } } //Set the bit of outputQ to 1. outputQ.Data[nByte] |= 1<>nBit)&1; for(i=0;i=0;i--) sout << char('0'+digit[i]); out << sout.str(); return out; } //The default constructor, sets the fraction to 0. CLargeFraction::CLargeFraction() { N = 0; D = 1; return; } //Allows an unsigned int to be automatically cast into a CLargeFraction object. It may throw //an ErrorOverflow object. CLargeFraction::CLargeFraction(unsigned int input) { N = input; D = 1; return; } //Allows an int to be automatically cast into a CLargeFraction object. It may throw an //ErrorOverflow object. CLargeFraction::CLargeFraction(int input) { N = input; D = 1; return; } //Allows an unsigned long to be automatically cast into a CLargeFraction object. It may throw //an ErrorOverflow object. CLargeFraction::CLargeFraction(unsigned long input) { N = input; D = 1; return; } //Allows a long to be automatically cast into a CLargeFraction object. It may throw an //ErrorOverflow object. CLargeFraction::CLargeFraction(long input) { N = input; D = 1; return; } //Allows a CLargeInt object to be automatically cast into a CLargeFraction object. CLargeFraction::CLargeFraction(const CLargeInt& input) { N = input; D = 1; return; } //The + unary operator. Used to evaluate +A, when A is a CLargeFraction object. It may throw //an ErrorDivisionByZero object. const CLargeFraction CLargeFraction::operator+() const { CLargeFraction output = *this; output.Simplify(); return output; } //The - unary operator. Used to evaluate -A, when A is a CLargeFraction object. It may throw //an ErrorDivisionByZero object. const CLargeFraction CLargeFraction::operator-() const { CLargeFraction output; output.N = -N; output.D = D; output.Simplify(); return output; } //The + arithmetic operator. Returns A+B. It may throw an ErrorOverflow or //ErrorDivisionByZero object. const CLargeFraction operator+(const CLargeFraction& A, const CLargeFraction& B) { CLargeFraction output; output.N = A.N*B.D+B.N*A.D; output.D = A.D*B.D; output.Simplify(); return output; } //The - arithmetic operator. Returns A-B. It may throw an ErrorOverflow or //ErrorDivisionByZero object. const CLargeFraction operator-(const CLargeFraction& A, const CLargeFraction& B) { return A+(-B); } //The * arithmetic operator. Returns A*B. It may throw an ErrorOverflow or //ErrorDivisionByZero object. const CLargeFraction operator*(const CLargeFraction& A, const CLargeFraction& B) { CLargeFraction output; output.N = A.N*B.N; output.D = A.D*B.D; output.Simplify(); return output; } //The / arithmetic operator. Returns A/B. It may throw an ErrorOverflow or //ErrorDivisionByZero object. const CLargeFraction operator/(const CLargeFraction& A, const CLargeFraction& B) { CLargeFraction output; if(B.D==0) throw ErrorDivisionByZero(); output.N = A.N*B.D; output.D = B.N*A.D; output.Simplify(); return output; } //Returns (A==B). It may throw an ErrorOverflow or ErrorDivisionByZero object. const bool operator==(const CLargeFraction& A, const CLargeFraction& B) { return (CLargeFraction::Compare(A,B)==0); } //Returns (A!=B). It may throw an ErrorOverflow or ErrorDivisionByZero object. const bool operator!=(const CLargeFraction& A, const CLargeFraction& B) { return (CLargeFraction::Compare(A,B)!=0); } //Returns (AB). It may throw an ErrorOverflow or ErrorDivisionByZero object. const bool operator>(const CLargeFraction& A, const CLargeFraction& B) { return (CLargeFraction::Compare(A,B)==1); } //Returns (A>=B). It may throw an ErrorOverflow or ErrorDivisionByZero object. const bool operator>=(const CLargeFraction& A, const CLargeFraction& B) { return (CLargeFraction::Compare(A,B)!=-1); } //Removes any common factors between the numerator and the denominator and makes the //denominator positive. It may throw an ErrorDivisionByZero object. void CLargeFraction::Simplify() { CLargeInt R1, R2, R3; if(D==0) throw ErrorDivisionByZero(); if(N==0) { D = 1; return; } //Apply the Euclidean algorithm to determine the highest common factor. R1 = N; R2 = D; while(true) { R3 = R1%R2; if(R3==0) break; //R2 is the highest common factor. R1 = R2; R2 = R3; } //Divide by the highest common factor. N = N/R2; D = D/R2; //Make the denominator positive. if(D<0) { N = -N; D = -D; } return; } //Used by the comparison operators to compare two fractions A, B. //Returns -1 if A < B, // 0 if A = B, // 1 if A > B. //It may throw an ErrorOverflow or ErrorDivisionByZero object. long CLargeFraction::Compare(const CLargeFraction& A, const CLargeFraction& B) { long result; CLargeInt L; if(A.D==0 || B.D==0) throw ErrorDivisionByZero(); L = A.N*B.D-A.D*B.N; if(L==0) return 0; //A = B. if(L>0) result = 1; //A > B, if both A.D>0 and B.D>0. else result = -1;//A < B, if both A.D>0 and B.D>0. //Take the signs of A.D and B.D into account. if(A.D<0) result = -result; if(B.D<0) result = -result; return result; } //Allows us to use << operator to output the CLargeFraction object F to the screen or a file. ostream& operator<<(ostream& out, const CLargeFraction& F) { stringstream sout; sout << F.N << "/" << F.D; out << sout.str(); return out; } //A simple class to store 3-graphs that have at most nine vertices. We take our vertex set to be //{0,1,...,nV-1}, where nV is the number of vertices in our graph. We can represent a flag as //(g, n) a CGraph g and an integer n, by taking the labelled vertices to be 0,1,...,n-1 and to //avoid confusion we label them 0,1,...,n-1 respectively (instead of 1,2,...,n). class CGraph { public: //Number of vertices. long nV; //The adjacency matrix. //M[i][j][k] = true if edge ijk is in the 3-graph, // false if edge ijk is not in the 3-graph. bool M[9][9][9]; }; //A structure that stores information about flags generated from a type. class CFlagInfo { public: //The graph of the type that the labelled vertices of the flags induce. Formally the type //is (Type, Type.nV). CGraph Type; //FlagList defines a list of flags (FlagList[], Type.nV) for which the labelled //vertices induce the graph Type, i.e. FlagList[n].M[i][j][k] should be identical to //Type.M[i][j][k] for all i,j,k < Type.nV. vector FlagList; //An element of the basis is a linear combination of flags (with coefficients 1 or -1). //How many flags the element is composed of is stored in Basis[r][i].size() (where //r=0 if the element is invariant, r=1 if it isn't, and 0 <= i < Basis[r].size() indexes //elements in the basis). The indices of where the flags, from the element, appear in //FlagList is stored in Basis[r][i][]. The coefficients of the flags can //be determined based on whether we are looking at an invariant element or not. If the //element is invariant we take all the coefficients to be 1. If it is not invariant we //take the coefficient of the first flag to be 1 and the coefficient of the second to be //-1. vector > Basis[2]; }; //CHashNode and CHashTable are used to define a very basic hash table. //CHashTable::HashFunction() needs to be defined for each type. //The Key class should provide an operator== function. //Used to create a linked list of key, value pairs in CHashTable. template class CHashNode { public: CHashNode* pNext; long hashedKey; Key key; Val val; }; //A hash table where collisions are resolved via a linked list for each "bucket". template class CHashTable { public: CHashTable():noOfEntries(0),sizeOfTable(0),Table(NULL),mask(0){} ~CHashTable(){Free();} //Free the memory allocated to Table[] and set sizeOfTable to 0. void Free(); //Adds an element to the table. void Add(const Key& key, const Val& val); //Gets a pointer to the value associated with the first matching key it can find. //Returns NULL if no matching key can found. Val* Get(const Key& key) const; //Used to map keys into buckets. static long HashFunction(const Key& key); //Displays the lengths of the lists. Useful for debugging purposes. //Long list lengths indicate a poor hash function or a small table size. void CollisionStats(); long noOfEntries; long sizeOfTable; CHashNode** Table; //An array of linked lists. private: //Allocates the size and memory for the table. void Allocate(long sizeOfTable); //Used to resize Table if it is getting too full. void DoubleSize(); //Used to do fast modding. long mask; }; //Free the memory allocated to Table[] and set sizeOfTable to 0. template void CHashTable::Free() { long i; CHashNode* pNode; if(Table!=NULL) { for(i=0;ipNext; delete Table[i]; Table[i] = pNode; } } delete[] Table; Table = NULL; } sizeOfTable = 0; noOfEntries = 0; mask = 0; return; } //Allocates the size and memory for the table. template void CHashTable::Allocate(long sizeOfTable) { long i; //Clear the table. Free(); if(sizeOfTable<=0) return; //Allocate the table. Table = new CHashNode*[sizeOfTable]; this->sizeOfTable = sizeOfTable; //Initialize the table. for(i=0;i void CHashTable::DoubleSize() { long i; long bucketIndex; CHashTable ht; CHashNode* pNode; long newSize; if(sizeOfTable==0) newSize = 2; else newSize = sizeOfTable<<1; ht.Allocate(newSize); ht.mask = (mask<<1) | 1; //Move the entries accross. for(i=0;ipNext; bucketIndex = (pNode->hashedKey)&ht.mask; pNode->pNext = ht.Table[bucketIndex]; ht.Table[bucketIndex] = pNode; ht.noOfEntries++; } //Clear the table. Free(); //Swap the tables. Table = ht.Table; sizeOfTable = ht.sizeOfTable; noOfEntries = ht.noOfEntries; mask = ht.mask; ht.Table = NULL; ht.sizeOfTable = 0; ht.noOfEntries = 0; ht.mask = 0; return; } //Adds an element to the table. template void CHashTable::Add(const Key& key, const Val& val) { long hashedKey; long bucketIndex; CHashNode* pNew; //Check if the table is 3/4 full and if so create a larger Table. if(noOfEntries/3+1>sizeOfTable/4) DoubleSize(); hashedKey = HashFunction(key); bucketIndex = hashedKey & mask; //Create a new node. pNew = new CHashNode; //Set the new node as the head of the list. pNew->pNext = Table[bucketIndex]; Table[bucketIndex] = pNew; noOfEntries++; //Fill in the rest of the node. pNew->hashedKey = hashedKey; pNew->key = key; pNew->val = val; return; } //Gets a pointer to the value associated with the first matching key it can find. //Returns NULL if no matching key can be found. template Val* CHashTable::Get(const Key& key) const { long hashedKey; CHashNode* pNode; if(sizeOfTable==0) return NULL; hashedKey = HashFunction(key); pNode = Table[hashedKey & mask]; while(pNode!=NULL) { //Check if pNode hashedKey matches. if(pNode->hashedKey==hashedKey) { //Check the keys match. if(pNode->key==key) return &(pNode->val); } pNode = pNode->pNext; } return NULL; } //Displays the lengths of the lists. Useful for debugging purposes. //Long list lengths indicate a poor hash function or a small table size. template void CHashTable::CollisionStats() { long i, j; long lengths[6]; CHashNode* pNode; cout << "Stats : " << endl; cout << noOfEntries << " / " << sizeOfTable << endl; for(i=0;i<6;i++) lengths[i] = 0; for(i=0;ipNext; } if(j<=4) lengths[j]++; else lengths[5]++; } for(i=0;i<=4;i++) cout << i << " : " << lengths[i] << endl; cout << "5+ : " << lengths[5] << endl; return; } //sw, and sz should not be zero. //Returns an integer in [0,2^31-1] long GetRandomInt(bool bSeed=false, unsigned long sw=123, unsigned long sz=345678) { static unsigned long w = 123; static unsigned long z = 3456789; if(bSeed==true) { w = sw; z = sz; } z = 36969*(z&65535)+(z>>16); w = 18000*(w&65535)+(w>>16); return ((z<<16)+w)&0x7FFFFFFF; } class KeyFlag { public: long typeOrder; CGraph flag; //Used when searching through the hash table. const bool operator==(const KeyFlag& in) const; //Used to hash the key. static long hashOrder[9]; static long hashM[9][9][9]; }; long KeyFlag::hashOrder[9]; long KeyFlag::hashM[9][9][9]; bool IsCopy(const CGraph& g1, const CGraph& g2); const bool KeyFlag::operator==(const KeyFlag& in) const { if(typeOrder!=in.typeOrder) return false; if(IsCopy(flag,in.flag)==false) return false; return true; } class ValFlag { public: //Indices. long iType; //Index of the type in FInfo[typeOrder][]. long iFlag; //Index of the flag in FlagList. }; //Used to map keys into buckets. template<> long CHashTable::HashFunction(const KeyFlag& key) { long i, j, k; long hashedKey; hashedKey = 0; hashedKey ^= key.hashOrder[key.typeOrder]; hashedKey ^= key.hashOrder[key.flag.nV]; for(i=0;i long CHashTable::HashFunction(const KeyProd& key) { long hashedKey = 0; hashedKey ^= 196723186*(key.nType); hashedKey ^= 26764*(key.iType&0xFFFF); hashedKey ^= 16223*(key.iType>>16); hashedKey ^= 9218*(key.iFlag1&0xFFFF); hashedKey ^= 8137*(key.iFlag1>>16); hashedKey ^= 24855*(key.iFlag2&0xFFFF); hashedKey ^= 29529*(key.iFlag2>>16); return hashedKey; } long orderOfH; vector H; vector ForbiddenGraphs; bool bForbidInduced; vector FInfo[8]; //[Max order of a type+1] bool bDisplayTypeNumber; bool bIncludeTypesByDefault; vector modifyTypeList[8]; //[Max order of a type+1] bool bForbidBlowups = false; long ForbiddenNonBlowups = 0; //Indicates which setting the SDP should take. // 1 = Primal. // 0 = Undecided. //-1 = Dual. long ForceForm; //A hash table to quickly find the position of flags in FInfo[][].FlagList[]. CHashTable htFlags; //A hash table for each member of H to quickly find the product of flags. vector > htProd; //Outputs the edge set of a CGraph object to the screen or a file. void DisplayEdges(ostream& out, const CGraph& g) { long i, j, k; bool bFirst; stringstream sout; sout << "{"; //Used to help us put the commas in the right place. bFirst = true; for(i=0;i= orderOfType because of the way we are permuting gIn. for(i=orderOfType;i& List) { long i, j, k; long b, n; long noOfEdgesToSet; bool e; CGraph& g = inputFlag; //Create a new graph which is inputFlag with an extra vertex v. g.nV++; //We need to determine which edges containing vertex v our new graph will contain. noOfEdgesToSet = (g.nV-1)*(g.nV-2)/2; //There are 2^noOfEdgesToSet possible choices for the set of edges containing v. We let b //encode which of the edges we include in g via the value of its bits. //Bits 0, 1, 2, 3, 4, 5, 6, 7, 8, ... of b tells us whether edges //edges 01v, 02v, 12v, 03v, 13v, 23v, 04v, 14v, 24v, ... respectively are in graph g. //Test all sets of edges containing v. for(b=0;b<(1<>n)%2==1) e = true; else e = false; //Update the adjacency matrix. g.M[i][j][k] = e; g.M[i][k][j] = e; g.M[j][i][k] = e; g.M[j][k][i] = e; g.M[k][i][j] = e; g.M[k][j][i] = e; n++; } //Check if the graph is admissible. for(i=0;i OldList; list NewList; list::const_iterator it; CGraph g; long graphIndex, total, foundSoFar; #ifdef _OPENMP long globalError; long loopError; vector > listArray; vector::const_iterator> listIndex; #endif cout << "Generating H..." << endl; vector().swap(H); //We take 'OldList', a list of graphs of order n-1, and add a vertex to each graph in the //list to generate 'NewList' a list of graphs of order n. We then make 'NewList' the new //'OldList' and repeat the procedure until we get the desired order of graphs. cout << "\rChecking for order 0 forbidden graphs." << flush; //Check if order 0 graphs are forbidden. for(i=0;iglobalError) globalError = loopError; if(globalError==0) { foundSoFar += listArray[i].size(); graphIndex++; cout << "\rGenerating order " << n << " graphs : "; cout << graphIndex << " of " << total << " graphs extended. "; cout << "Found " << foundSoFar << "." << flush; } //Move to the next graph in OldList. } } //Display any errors. if(globalError!=0) { cout << endl << endl; if(globalError==1) cout << "ERROR : An unknown error occurred."; if(globalError==2) cout << "ERROR : Allocating memory failed."; cout << endl << endl; return false; } //Append ListArray into NewList. for(i=0;i OldList; list NewList; list::const_iterator it; //Set the undefined parts of M[][][] to false. for(i=0;i<9;i++) for(j=0;j<9;j++) for(k=0;k<9;k++) { if(i==j || i==k || j==k || i>=FI.Type.nV || j>=FI.Type.nV || k>=FI.Type.nV) FI.Type.M[i][j][k] = false; } FI.FlagList.clear(); vector().swap(FI.FlagList); //Deal with the nonsensical case of creating a flag with FlagOrder < FI.Type.nV. if(FlagOrder minFlag; CGraph g; //Generate the orbit of each flag under the automorphism group of FI.Type, and store the //flag with the smallest index in minFlag[]. minFlag.resize(FI.FlagList.size()); //To start with we assume the unmodified flag has the smallest index out of the flags //that form its orbit. for(i=0;i(1,i)); //Find the other flags in the orbit. for(j=i+1;j(2,0)); FI.Basis[1].back()[0] = j; FI.Basis[1].back()[1] = i; } } return; } //Generate all the types of order less than or equal to orderOfH-2. void FillFlagInfo() { long i, j, k, p, n; bool bKeep, bFirst; long m[4]; CFlagInfo FI; //Set the type. FI.Type.nV = 0; for(i=0;i<9;i++) for(j=0;j<9;j++) for(k=0;k<9;k++) FI.Type.M[i][j][k] = false; cout << "\rFinding the types..." << flush; for(n=0;n<=orderOfH-2;n++) { GenerateFlags(FI,n); FInfo[n].resize(FI.FlagList.size()); for(i=0;i Temp; //Count how many types there are. k = 0; for(i=0;i0) k++; Temp.resize(k); k = 0; for(i=0;iData[][][]. bool CalculateFlagProducts() { long i, j, k, n, p; long h, hTotal; long orderOfType, orderOfFlag; KeyFlag key; ValFlag val, *pValT, *pValF1, *pValF2; KeyProd keyProd; long* pValProd; long mapping1[9], mapping2[9]; CGraph f1, f2; long loopError, globalError; long oldOrder, oldP; ///Used to speed up displaying of the progress. cout << "Calculating the flag products..." << endl; hTotal = H.size(); //Create the hash tables. { cout << "\rCreating hash tables..." << flush; //Set the random values used by the hash functions. for(i=0;i<9;i++) KeyFlag::hashOrder[i] = GetRandomInt(); for(i=0;i<9;i++) for(j=0;j<9;j++) for(k=0;k<9;k++) KeyFlag::hashM[i][j][k] = GetRandomInt(); //Add the flags to the htFlags hash table. for(n=0;n<=orderOfH-2;n++) for(i=0;imapping2[orderOfType]) continue; for(i=orderOfType;imapping1[i+1]) break; if(mapping2[i]>mapping2[i+1]) break; } //Check if we broke out the for loop early. if(i!=orderOfFlag-1) continue; if(orderOfType>oldOrder || p>oldP+10) { oldOrder = orderOfType; oldP = p; cout << "\rChecking order " << orderOfType << " of " << orderOfH-2 << " : "; cout << "Applying mapping " << p+1 << " of " << Factorial[orderOfH] << ". " << flush; } //Apply the mappings to each H to get two flags f1, f2. globalError = 0; //No error #pragma omp parallel for schedule(dynamic) default(shared) private(h,i,j,k,f1,f2,key,pValT,pValF1,pValF2,keyProd,pValProd,loopError) for(h=0;hiType; if(pValF1->iFlag<=pValF2->iFlag) { keyProd.iFlag1 = pValF1->iFlag; keyProd.iFlag2 = pValF2->iFlag; } else { keyProd.iFlag1 = pValF2->iFlag; keyProd.iFlag2 = pValF1->iFlag; } //Store the amount to increment by in i. i = Factorial[orderOfFlag-orderOfType]*Factorial[orderOfFlag-orderOfType]; if(pValF1->iFlag==pValF2->iFlag) i *= 2; pValProd = htProd[h].Get(keyProd); if(pValProd==NULL) htProd[h].Add(keyProd,i); //Add a new entry. else *pValProd += i; } } } } catch(bad_alloc&) { loopError = 2; //bad_alloc error. } catch(...) { loopError = 1; //Unknown error. } #pragma omp critical (CalcProd_ErrorHandling) { if(loopError>globalError) globalError = loopError; } } //Display any errors. if(globalError!=0) { cout << endl << endl; if(globalError==1) cout << "ERROR : An unknown error occurred."; if(globalError==2) cout << "ERROR : Allocating memory failed."; cout << endl << endl; return false; } } } cout << "\rDone. " << flush; cout << endl; cout << endl; return true; } //Creates a ".dat-s" file that can be used as input to a SDP solver. //Returns false if some sort of error was encountered, and true if successful. //This function formulates the optimization as a primal sdp problem. bool CreatePrimalSDPFile() { long i, j, m, n, t; long b, v; long r, c; long nVar, nBlocks, noOfH; long coeff; long product; ofstream FileOut; long* pVal; KeyProd key; //Used to buffer the output. long nBuf; const long maxBuf = 1000000; vector memBuffer; char* buffer; //Create the ".dat-s" file for an SDP solver. //Create a character array to store the output to the file. { //maxBuf = The amount the buffer is allowed to be filled before a write operation to the file. //memBuffer = A vector which allocates (and automatically deallocates) the memory for the buffer. //buffer = A pointer that allows us to use the memory as if it was a C-style array. //nBuf = The amount of the buffer that is filled. //Add some extra space to account for characters which may be //written during a write operation which exceeds the buffer size. memBuffer.resize(maxBuf+1000); buffer = &memBuffer[0]; nBuf = 0; } //Calculate the number of blocks and the number of variables we are optimizing over. //We have one constraint matrix for each H. noOfH = H.size(); nVar = noOfH; nBlocks = 0; for(n=0;n<=orderOfH-2;n++) for(t=0;t0) nBlocks++; //As well as the matrices which we are constraining to be positive semidefinite, we have //a diagonal matrix of slack variables and $\rho$ ($\rho$ represents the density). $\rho$ //will be positioned in the last entry of the diagonal matrix. nBlocks++; cout << "Opening the \".dat-s\" file..." << flush; FileOut.open(FilenameSDP.c_str(),ios::trunc); if(FileOut.good()==false) { cout << endl << endl; cout << "ERROR: Cannot create the file \"" << FilenameSDP << "\"." << endl; cout << endl; return false; } ForceForm = 1; cout << "\rCreating the (primal) SDP problem... " << endl; //Output the number of blocks and variables we will be optimizing over. nBuf += sprintf(buffer+nBuf,"%li\n%li\n",nVar,nBlocks); //Output the dimensions of the blocks. for(n=0;n<=orderOfH-2;n++) for(t=0;t0) { nBuf += sprintf(buffer+nBuf,"%li ",(long)FInfo[n][t].Basis[j].size()); if(nBuf>=maxBuf) { FileOut.write(buffer,nBuf); nBuf = 0; } } //Output the dimension of the block that contains the slack variables and $\rho$. //The block is diagonal and we encode this information by prefixing a minus sign. nBuf += sprintf(buffer+nBuf,"-%li\n",noOfH+1); //Set the constant variables "a" for(i=0;i=maxBuf) { FileOut.write(buffer,nBuf); nBuf = 0; } } //Set the matrix entries. Entries take the form: //"Matrix number" "Block number" row col value //Indexes start at 1 not 0. //Set the constant matrix C, so that the objective function we are trying to maximize is $-\rho$. nBuf += sprintf(buffer+nBuf,"0 %li %li %li -1\n",nBlocks,noOfH+1,noOfH+1); if(nBuf>=maxBuf) { FileOut.write(buffer,nBuf); nBuf = 0; } //Set the other constraint matrices. for(v=0;v=maxBuf) { FileOut.write(buffer,nBuf); nBuf = 0; } } b++; } } //Set the slack variable. nBuf += sprintf(buffer+nBuf,"%li %li %li %li %li\n",v+1,nBlocks,v+1,v+1,Factorial[orderOfH]); //Set the coefficient of $\rho$. nBuf += sprintf(buffer+nBuf,"%li %li %li %li %li\n",v+1,nBlocks,noOfH+1,noOfH+1,-Factorial[orderOfH]); if(nBuf>=maxBuf) { FileOut.write(buffer,nBuf); nBuf = 0; } } FileOut.write(buffer,nBuf); nBuf = 0; FileOut.close(); cout << "\r "; for(i=0;i memBuffer; char* buffer; //Create the ".dat-s" file for an SDP solver. //Create a character array to store the output to the file. { //maxBuf = The amount the buffer is allowed to be filled before a write operation to the file. //memBuffer = A vector which allocates (and automatically deallocates) the memory for the buffer. //buffer = A pointer that allows us to use the memory as if it was a C-style array. //nBuf = The amount of the buffer that is filled. //Add some extra space to account for characters which may be //written during a write operation which exceeds the buffer size. memBuffer.resize(maxBuf+1000); buffer = &memBuffer[0]; nBuf = 0; } //Calculate the number of blocks and the number of variables we are optimizing over. noOfH = H.size(); nVar = 0; nBlocks = 0; for(n=0;n<=orderOfH-2;n++) for(t=0;t0) { nBlocks++; //We only need to determine the upper right half of the positive semidefinite //matrices as they are symmetric. nVar += FInfo[n][t].Basis[j].size()*(FInfo[n][t].Basis[j].size()+1)/2; } //We have an extra variable $\rho$, that represents the upper bound of the Turan density, //which we are trying to minimize. nVar++; //As well as the matrices which we are constraining to be positive semidefinite, we have //a series of lower bounds on $\rho$ that must also be satisfied. We encode this in an //extra block. nBlocks++; cout << "Opening the \".dat-s\" file..." << flush; FileOut.open(FilenameSDP.c_str(),ios::trunc); if(FileOut.good()==false) { cout << endl << endl; cout << "ERROR: Cannot create the file \"" << FilenameSDP << "\"." << endl; cout << endl; return false; } ForceForm = -1; cout << "\rCreating the (dual) SDP problem... " << endl; //Output the number of blocks and variables we will be optimizing over. nBuf += sprintf(buffer+nBuf,"%li\n%li\n",nVar,nBlocks); //Output the dimensions of the blocks. for(n=0;n<=orderOfH-2;n++) for(t=0;t0) { nBuf += sprintf(buffer+nBuf,"%li ",(long)FInfo[n][t].Basis[j].size()); if(nBuf>=maxBuf) { FileOut.write(buffer,nBuf); nBuf = 0; } } //Output the dimension of the block that represents the inequalities. //The block is diagonal and we encode this information by prefixing a minus sign. nBuf += sprintf(buffer+nBuf,"-%li\n",noOfH); //Set the objective function to $\rho$. //We take the first variable to be $\rho$. nBuf += sprintf(buffer+nBuf,"1"); //Set the coefficients of all other variables to be 0. for(i=0;i=maxBuf) { FileOut.write(buffer,nBuf); nBuf = 0; } } nBuf += sprintf(buffer+nBuf,"\n"); //Set the matrix entries. Entries take the form: //"Matrix number" "Block number" row col value //Indexes start at 1 not 0. //For each variable we have a matrix that consists of blocks down its diagonal. We start //by defining such a matrix associated with 0 th variable which is defined as the //constant 1. The only constant terms are $d(H_i)$ which appear in the constraints of //$\rho$. We multiply the constraints by Factorial[orderOfH] to keep all the coefficients //integers. for(i=0;i=maxBuf) { FileOut.write(buffer,nBuf); nBuf = 0; } } //Set the matrix corresponding to variable 1, which we've defined to be $\rho$. for(i=0;i=maxBuf) { FileOut.write(buffer,nBuf); nBuf = 0; } } //Set the matrices for the other variables. b = 1; //Current block number. v = 2; //Current variable number. for(n=0;n<=orderOfH-2;n++) for(t=0;t=maxBuf) { FileOut.write(buffer,nBuf); nBuf = 0; } //Multiply basis element r with element c and store the result in product. for(k=0;k=maxBuf) { FileOut.write(buffer,nBuf); nBuf = 0; } } v++; } b++; } } FileOut.write(buffer,nBuf); nBuf = 0; FileOut.close(); cout << "\r "; for(i=0;i [-i] [-c ] [-o ]" << endl; cout << " -s " << endl; cout << endl; cout << "Examples :" << endl; cout << " ExactDensityBounder.exe -s csdp.exe" << endl; cout << " ./edb 6 {123 124 134, 234} -o K4" << endl; cout << " ExactDensityBounder 5 {234} 4 : {123 124 134 234} -i -o \"Induced Prob\"" << endl; cout << " ./edb 5 {123 124 134} -c ./sdpa" << endl; cout << endl; cout << "Run the program with no arguments for more details." << endl; cout << endl; return; } //A description of the usage of the program. void Usage() { cout << "Name :" << endl; cout << " ExactDensityBounder" << endl; cout << endl; cout << "Description :" << endl; cout << " Finds an upper bound of the Turan density for a family of 3-graphs." << endl; cout << endl; cout << "Usage :" << endl; cout << " [-i] [-c ] [-o ]" << endl; cout << " -s " << endl; cout << endl; cout << "Examples :" << endl; cout << " ExactDensityBounder.exe -s csdp.exe" << endl; cout << " ./edb 6 {123 124 134 234} -o K4" << endl; cout << " ExactDensityBounder 5 {234} 4 : {123 124 134 234} -i -o \"Induced Prob\"" << endl; cout << " ./edb 5 {123 124 134} -c ./sdpa" << endl; cout << endl; cout << "Arguments :" << endl; cout << " <> : A mandatory field." << endl; cout << endl; cout << " [] : An optional field." << endl; cout << endl; cout << " : The name of the compiled program. For example" << endl; cout << " \"ExactDensityBounder.exe\" on windows, \"./edb\" or \"./a.out\" in linux." << endl; cout << endl; cout << " : The order of the subgraphs H. This should be an integer between" << endl; cout << " 3 and 9. Increasing the order will result in a better bound but the" << endl; cout << " computation will be more intensive. Currently orders higher than 9 are not" << endl; cout << " supported by this program." << endl; cout << endl; cout << " : A list of 3-uniform hypergraphs (separated by spaces). Graphs" << endl; cout << " with orders higher than 9 are not supported. Each graph should either be" << endl; cout << " of the form \" : \" or simply \"\"." << endl; cout << " \"\" should be a single digit and if it is omitted then it is" << endl; cout << " taken to be the largest label of a vertex in the edge set. The vertices are" << endl; cout << " assumed to be labelled by integers starting from 1. The edge set should be" << endl; cout << " enclosed in curly braces and contain a list of 3 digit numbers (separated" << endl; cout << " by spaces) representing the edges. See the \"Examples\" section above." << endl; cout << endl; cout << " [-i] : Indicates that we wish to consider only induced subgraphs. If \"-i\"" << endl; cout << " is used then the program only forbids from appearing as *induced*" << endl; cout << " subgraphs. If \"-i\" is omitted then are forbidden from appearing as" << endl; cout << " subgraphs (as in the normal definition of Turan density)." << endl; cout << endl; cout << " [-c] : Used to override the default command (that runs the SDP solver) with" << endl; cout << " . See also the \"\" and \"-s\" sections below." << endl; cout << endl; cout << " : The command used to run an SDP solver on the underlying" << endl; cout << " semidefinite programming problem. For example \"./csdp\" or \"sdpa.exe\". The" << endl; cout << " program runs the SDP solver by running the command" << endl; cout << " \" .dat-s .out\". It always assumes the SDP" << endl; cout << " solver completed successfully and that the solution is stored in" << endl; cout << " \".out\". It can currently only read the \".out\" file if it adheres" << endl; cout << " to the same format as csdp or sdpa's output files. See also the sections on" << endl; cout << " \"[-c]\", \"-s\", and \"\"." << endl; cout << endl; cout << " [-o] : Used to indicate as the name to be used for the files the" << endl; cout << " program creates. If is not set using \"-o\" then the default name" << endl; cout << " \"Temp\" is used. See also the section on \"\"." << endl; cout << endl; cout << " : A filename (without extension). It is used to name the" << endl; cout << " temporary \".dat-s\" input file to the SDP solver and the \".out\" output file." << endl; cout << " It is also used to name the \".txt\" file which will hold the data which" << endl; cout << " forms the proof. The program DensityChecker can be used to verify the" << endl; cout << " calculation from the \".txt\" file." << endl; cout << endl; cout << " -s : Used to save as the default command that runs the SDP" << endl; cout << " solver. The command is saved in the file \"ConfigEDB.cfg\". See the \"[-c]\"" << endl; cout << " and \"\" sections." << endl; cout << endl; return; } //Outputs the full filename of the config file based on the path used to execute the program. string GetConfigName(const char* argv0) { long i; string Filename; Filename = argv0; for(i=Filename.size()-1;i>=0;i--) if(Filename[i]=='/' || Filename[i]=='\\') break; Filename.resize(i+1); Filename += configName; return Filename; } //Saves command[] in the config file. //Returns true if successful. bool SaveSDPCommand(const char* argv0, const char* command) { ofstream FileConfig; string Filename; Filename = GetConfigName(argv0); cout << endl; cout << "Opening \"" << configName << "\"."<< endl; FileConfig.open(Filename.c_str(),ios::trunc); if(FileConfig.good()==false) { cout << endl; cout << "ERROR : Could not open \"" << Filename << "\" to write the command." << endl << endl; return false; } cout << "Writing the command..." << endl; FileConfig << command << endl; if(FileConfig.good()==false) { cout << endl; cout << "ERROR : Writing to the file failed." << endl << endl; FileConfig.close(); return false; } cout << "Closing \"" << configName << "\"."<< endl << endl; FileConfig.close(); return true; } //Reads 'command' from the config file. //Returns true if successful. bool ReadSDPCommand(const char* argv0, string& command) { long i; ifstream FileConfig; string Filename; string blank; Filename = GetConfigName(argv0); //Create a blank string the same size as configName. blank.resize(strlen(configName)); for(i=0;i"); return false; } cout << endl; if(argc<=1) { Usage(); return false; } //argv[0] is the application name. //argv[1] should be a single digit integer or -s. bError = false; if(strlen(argv[1])==1) { if('3'<=argv[1][0] && argv[1][0]<='9') orderOfH = argv[1][0]-'0'; else bError = true; } else if(strlen(argv[1])==2) { if(argv[1][0]=='-' && (argv[1][1]=='s' || argv[1][1]=='S')) { cout << "Saving the default SDP command : "; if(argc<=2) { cout << endl; cout << endl; cout << "ERROR : Expected a second argument indicating the SDP command to save." << endl; ShortUsage(); return false; } cout << "\"" << argv[2] << "\"" << endl; if(argc>3) { cout << endl; cout << "ERROR : Unexpected extra arguments." << endl; ShortUsage(); return false; } //Save the command. if(SaveSDPCommand(argv[0],argv[2])==false) return false; //Even though we succeeded, return false to cause the program to terminate. return false; } else bError = true; } else bError = true; cout << "OpenMP (multiprocessing) : "; #ifdef _OPENMP cout << "Enabled (" << omp_get_num_procs() << " processors)." << endl; #else cout << "Disabled" << endl; #endif if(bError==true) { cout << endl; cout << "ERROR : Expected the first argument to be \"-s\" or an integer between 3 and 9." << endl; ShortUsage(); return false; } cout << "Order of the subgraphs H : " << orderOfH << endl; //Read in the graphs. { ForbiddenGraphs.clear(); cout << "Forbidden graphs : " << endl; aN = 1; cN = 0; bMidway = false; while(true) { //Move to the next character. cN++; while(argv[aN][cN]=='\0') { aN++; cN = 0; if(aN>=argc) break; } //Check if the arguments have ended. if(aN>=argc) { //Reached the end of the arguments. if(bMidway==true) { cout << endl; cout << "ERROR : Input unexpectedly ended midway through a definition of a graph." << endl; ShortUsage(); return false; } break; } //Check if we've reached the end of the graph list. if(cN==0 && bMidway==false && argv[aN][cN]=='-') break; if(bMidway==false) { //Expect to read a comma, space, integer, ':' or '{'. if(argv[aN][cN]==',' || argv[aN][cN]==' ') continue; else if('0'<=argv[aN][cN] && argv[aN][cN]<='9') { g.nV = argv[aN][cN]-'0'; for(i=0;i<9;i++) for(j=0;j<9;j++) for(k=0;k<9;k++) g.M[i][j][k] = false; bMidway = true; bReadColon = true; bReadEdgeSet = false; continue; } else if(argv[aN][cN]==':') { g.nV = -1; for(i=0;i<9;i++) for(j=0;j<9;j++) for(k=0;k<9;k++) g.M[i][j][k] = false; bMidway = true; bReadColon = false; bReadEdgeSet = false; continue; } else if(argv[aN][cN]=='{') { g.nV = -1; for(i=0;i<9;i++) for(j=0;j<9;j++) for(k=0;k<9;k++) g.M[i][j][k] = false; bMidway = true; bReadEdgeSet = true; continue; } else { bError = true; break; } } if(bMidway==true) { if(bReadEdgeSet==false) { //Read the separating colon or the starting edge set brace '{'. if(bReadColon==true) { //Read the separating colon. //Ignore spaces if(argv[aN][cN]==' ') continue; else if(argv[aN][cN]==':') { bReadColon = false; continue; } else { bError = true; break; } } else { //Read the starting brace. //Ignore spaces if(argv[aN][cN]==' ') continue; else if(argv[aN][cN]=='{') { bReadEdgeSet = true; continue; } else { bError = true; break; } } } if(bReadEdgeSet==true) { //Read the edges. //Expects to read an integer, space, comma, or a closing brace. if(argv[aN][cN]=='}') { bMidway = false; if(g.nV==-1) { //Find the largest index for(i=0;i<9;i++) for(j=i+1;j<9;j++) for(k=j+1;k<9;k++) { if(g.M[i][j][k]==false) continue; if(k>g.nV) g.nV = k; } g.nV++; } cout << g << endl; //Save the graph ForbiddenGraphs.push_back(g); continue; } else if('1'<=argv[aN][cN] && argv[aN][cN]<='9') { //Read the edge "ijk". i = argv[aN][cN]-'1'; cN++; if(argv[aN][cN]<'1' || '9'=argc) break; if(strlen(argv[aN])!=2 || argv[aN][0]!='-' ||(argv[aN][1]!='i' && argv[aN][1]!='I' && argv[aN][1]!='c' && argv[aN][1]!='C' && argv[aN][1]!='o' && argv[aN][1]!='O' && argv[aN][1]!='t' && argv[aN][1]!='T' && argv[aN][1]!='p' && argv[aN][1]!='P' && argv[aN][1]!='d' && argv[aN][1]!='D' && argv[aN][1]!='r' && argv[aN][1]!='R' && argv[aN][1]!='a' && argv[aN][1]!='A' && argv[aN][1]!='b' && argv[aN][1]!='B')) throw 0; if(argv[aN][1]=='i' || argv[aN][1]=='I') { if(bI==true) throw 0; else bI = true; bForbidInduced = true; aN++; continue; } if(argv[aN][1]=='c' || argv[aN][1]=='C') { if(bC==true) throw 0; else bC = true; aN++; if(aN>=argc) { cout << endl; cout << endl; cout << "ERROR : Expected another argument indicating the command to run the SDP solver." << endl; ShortUsage(); return false; } SDPCommand = argv[aN]; aN++; continue; } if(argv[aN][1]=='o' || argv[aN][1]=='O') { if(bO==true) throw 0; else bO = true; aN++; if(aN>=argc) { cout << endl; cout << endl; cout << "ERROR : Expected another argument indicating the filename." << endl; ShortUsage(); return false; } Filename = argv[aN]; aN++; continue; } //Set the type list. //expects arguments of the form: //+ 4.5 7.13 //- 0.1 //+ = Remove all types except the ones specified. //- = Include all types except the ones specified. //n.x = n is the order of the type (a single digit), x is the index (starting from 1). if(argv[aN][1]=='t' || argv[aN][1]=='T') { if(bT==true) throw 0; else bT = true; //Read the types to add/remove. bool bReadFirstArg, bReadSecondArg; bDisplayTypeNumber = true; //Clear the modifiedTypeList array. for(i=0;i<=9-2;i++) modifyTypeList[i].clear(); bReadFirstArg = false; while(true) { aN++; if(aN>=argc) { if(bReadFirstArg==false) { cout << endl << endl; cout << "ERROR : Expected another argument indicating the default treatment of types." << endl; ShortUsage(); return false; } else break; //Successfully read all the arguments. } if(bReadFirstArg==false) { if(strlen(argv[aN])!=1 || (argv[aN][0]!='+' && argv[aN][0]!='-')) { cout << endl << endl; cout << "ERROR : Expected to read \"+\" or \"-\"." << endl; ShortUsage(); return false; } if(argv[aN][0]=='+') bIncludeTypesByDefault = false; else bIncludeTypesByDefault = true; bReadFirstArg = true; bReadSecondArg = false; continue; } //Read in a type. //"order.index" { //Check if the next switch has been encountered. if(strlen(argv[aN])==2 && argv[aN][0]=='-') break; //Finished reading the types. if(strlen(argv[aN])<3) throw 0; //Read the order. n = argv[aN][0]-'0'; if(n<0 || orderOfH-2=argc) { cout << endl; cout << endl; cout << "ERROR : Expected another argument specifying the number of decimal places." << endl; ShortUsage(); return false; } //Read the number of decimal places which should be an integer between 1 and 1000 inclusive. { //Remove white spaces. cN = 0; while(true) { if(argv[aN][cN]==' ' || argv[aN][cN]=='\t' || argv[aN][cN]=='\r' || argv[aN][cN]=='\n') { cN++; continue; } break; } //Read the sign. if(argv[aN][cN]=='-') throw 0; if(argv[aN][cN]=='+') cN++; //Check that the first character is a digit. if(argv[aN][cN]-'0'<0 || 91000). n = 0; while(true) { if(0<=argv[aN][cN]-'0' && argv[aN][cN]-'0'<=9) { n = 10*n+argv[aN][cN]-'0'; if(n>1000) throw 0; cN++; continue; } break; } //Check n is not 0. if(n<=0) throw 0; //Check that the rest of the characters are white spaces. while(true) { if(argv[aN][cN]=='\0') break; if(argv[aN][cN]==' ' || argv[aN][cN]=='\t' || argv[aN][cN]=='\r' || argv[aN][cN]=='\n') { cN++; continue; } throw 0; } } accuracyProjectSolution = n; aN++; continue; } if(argv[aN][1]=='b' || argv[aN][1]=='B') { if(bB==true) throw 0; else bB = true; aN++; continue; } } //Set the defaults. { if(bI==false) bForbidInduced = false; if(bC==false) { if(ReadSDPCommand(argv[0],SDPCommand)==false) return false; } if(bO==false) Filename = defaultFilename; FilenameSDP = Filename+".dat-s"; FilenameOut = Filename+".out"; FilenameTxt = Filename+".txt"; if(bT==false) { //Do not remove any types. bDisplayTypeNumber = false; bIncludeTypesByDefault = true; //Clear the modifiedTypeList array. for(i=0;i<=9-2;i++) modifyTypeList[i].clear(); } if(bPD==false) ForceForm = 0; if(bB==false && bForbidInduced==false) bForbidBlowups = true; else bForbidBlowups = false; } //Display the Results. { cout << "Forbid only induced subgraphs : " << (bForbidInduced?"True":"False") << endl; cout << "SDP command : \"" << SDPCommand << "\"" << endl; cout << "Filename : \"" << Filename << "\"" << endl; if(bT==true) { cout << "Types specified : " << (bIncludeTypesByDefault?"All":"None"); for(n=0;n<=9-2;n++) { if(modifyTypeList[n].size()<=1) continue; //Order modifyTypeList[n]. for(i=0;imodifyTypeList[n][j]) { //Swap. k = modifyTypeList[n][i]; modifyTypeList[n][i] = modifyTypeList[n][j]; modifyTypeList[n][j] = k; } } bool bFirstType = false; for(n=0;n<=9-2;n++) { if(bFirstType==false && modifyTypeList[n].size()>0) { cout << " except"; bFirstType = true; } for(j=0;j" .dat-s file. if(SaveFormatTest(FilenameSDP.c_str(),"")==false) { ShortUsage(); return false; } //Check it was created successfully. if(ReadFormatTest(FilenameSDP.c_str(),data)==false) return false; if(data!="") { cout << endl; cout << "ERROR : Retrieving data from the file \"" << FilenameSDP.c_str() << "\" failed." << endl << endl; return false; } //Try the 'sh' style format argument format. { //"" "Test" "" toTest = string("\"")+argv0+"\" \"Test\" \""+FilenameSDP+"\""; //"" "" "" RunCommand = "\""+SDPCommand+"\" \""+FilenameSDP+"\" \""+FilenameOut+"\""; //Test the command interperter. ret = system(toTest.c_str()); //Check if the file was modified. if(ReadFormatTest(FilenameSDP.c_str(),data)==false) return false; if(data!="" && data!="") { cout << endl; cout << "ERROR : Retrieving data from the file \"" << FilenameSDP.c_str() << "\" failed." << endl << endl; return false; } if(data=="") { cout << "Format 1 succeeded." << endl; cout << endl << endl; return true; } } //'sh' style format failed. //Try the 'cmd.exe' style argument format. { cout << "Format 1 failed. Trying format 2..." << endl; //""" "Test" """ toTest = "\""+toTest+"\""; //""" "" """ RunCommand = "\""+RunCommand+"\""; //cmd.exe style format. //Test the command interperter. ret = system(toTest.c_str()); //Check if the file was modified. if(ReadFormatTest(FilenameSDP.c_str(),data)==false) return false; if(data!="" && data!="") { cout << endl; cout << "ERROR : Retrieving data from the file \"" << FilenameSDP.c_str() << "\" failed." << endl << endl; return false; } if(data=="") { cout << "Format 2 succeeded." << endl; cout << endl << endl; return true; } } RunCommand = ""; cout << "Format 2 failed." << endl; cout << endl; cout << "Could not get the command interpreter to run successfully." << endl; cout << "The SDP solver will not be run." << endl; cout << endl << endl; return true; } bool ChooseSDPFormat() { long j, n, t; long nVar; if(ForceForm==0) { //Determine which formulation is more efficient. //Calculate the number of constraint matrices in the dual formulation. nVar = 1; for(n=0;n<=orderOfH-2;n++) for(t=0;t0) nVar += FInfo[n][t].Basis[j].size()*(FInfo[n][t].Basis[j].size()+1)/2; if(H.size()" << endl << endl << endl; else { cout << RunCommand << endl << endl << endl; ret = system(RunCommand.c_str()); cout << endl; } cout << endl; cout << "Control returned to ExactDensityBounder." << endl; cout << endl; return; } template vector > Inverse(vector > Matrix) { long i, j, n; long dim; long maxIndex; T maxSoFar; T temp; vector > Inv; //Will hold the inverse of Matrix. dim = Matrix.size(); //Allocate Inv matrix. Inv.resize(dim); for(i=0;i=0) maxSoFar = Matrix[maxIndex][n]; else maxSoFar = -Matrix[maxIndex][n]; for(i=n+1;i void LinearConstraintSolver(vector > Prob, vector >& Soln, T tolerance) { long i, j, k; long nVar, nConstraints; long maxV, maxC; T maxSoFar; T temp; vector leadingVariable, leadingConstraint; vector >().swap(Soln); //The number of variables we are trying to find. nVar = Prob.size()-1; if(nVar<=0) { //No variables to find. vector >().swap(Soln); return; } Soln.resize(nVar); //The number of constraints. nConstraints = Prob[0].size(); if(nConstraints<=0) { //There are no constraints. for(i=0;i class SparseComponent { public: T coeff; long index; }; //Zero entries are (mostly) omitted from Soln to save space. //Soln[0] to Soln[Soln.size()-2] describe the kernel vectors. //Soln[Soln.size()-1] describes a solution. template void SparseLinearConstraintSolver(vector >& Prob, vector > >& Soln, T tolerance) { long i, j; long nVar, nConstraints; long maxV, maxC; T maxSoFar; T temp; vector leadingVariable, leadingConstraint; vector > >().swap(Soln); //The number of variables we are trying to find. nVar = Prob.size()-1; if(nVar<=0) { //No variables to find. vector > >().swap(Soln); return; } //The number of constraints. nConstraints = Prob[0].size(); if(nConstraints<=0) { //There are no constraints. Soln.resize(nVar+1); for(i=0;i >()); for(j=0;j()); Soln.back().back().index = j; Soln.back().back().coeff = -1; } else if(leadingConstraint[j]==-1) continue; else { Soln.back().push_back(SparseComponent()); Soln.back().back().index = j; Soln.back().back().coeff = Prob[i][leadingConstraint[j]]; } } } //Find a specific solution. Soln.push_back(vector >()); for(i=0;i()); Soln.back().back().index = i; Soln.back().back().coeff = Prob[nVar][leadingConstraint[i]]; } } return; } double ConvertFracToDouble(CLargeFraction F) { long i; double N, D; bool bPositive; long eN, eD; CLargeInt temp; if(F.N>=0) bPositive = true; else { bPositive = false; F.N = -F.N; } N = 0; eN = 0; while(F.N!=0) { temp = F.N%256; F.N = F.N/256; N += temp.ConvertToLong(); N /= 256; eN++; } if(bPositive==false) N = -N; if(F.D>=0) bPositive = true; else { bPositive = false; F.D = -F.D; } D = 0; eD = 0; while(F.D!=0) { temp = F.D%256; F.D = F.D/256; D += temp.ConvertToLong(); D /= 256; eD++; } if(bPositive==false) D = -D; //Floating value is N/D*256^(eN-eD). N = N/D; eN = eN-eD; if(eN>0) { for(i=0;i=1) { d /= 256; n++; Pow256 = Pow256*256; } while(true) { temp = (int)d; Result.N = Result.N+temp*Pow256; d -= temp; d *= 256; Pow256 = Pow256/256; n--; if(n==-1) break; } if(bPositive==false) Result.N = -Result.N; return Result; } class CTerm { public: vector > dMatrix; vector > fMatrix; //Flag information. long nType; long iType; vector > BasisFlag; vector > BasisCoeff; }; vector Soln; vector sharpIneq; CLargeFraction TargetTuranDensity; CLargeInt GreatestCommonDivisor(CLargeInt A, CLargeInt B) { CLargeInt& R1 = A; CLargeInt& R2 = B; CLargeInt R3; if(R1<0) R1 = -R1; if(R2<0) R2 = -R2; if(R1==0) return R2; if(R2==0) return R1; //Apply the Euclidean algorithm to determine the highest common factor. while(true) { R3 = R1%R2; if(R3==0) break; //R2 is the highest common factor. R1 = R2; R2 = R3; } return R2; } CLargeInt LowestCommonMultiple(CLargeInt A, CLargeInt B) { CLargeInt& R1 = A; CLargeInt& R2 = B; CLargeInt R3; CLargeInt Product; if(R1<0) R1 = -R1; if(R2<0) R2 = -R2; if(R1==0) return R2; if(R2==0) return R1; return R1*R2/GreatestCommonDivisor(R1,R2); } CLargeFraction NearestRational(double toMakeRational, long maxDenominator) { long i; double diff; double bestDiff; CLargeFraction F, Best; Best = 0; bestDiff = toMakeRational - ConvertFracToDouble(Best); if(bestDiff<0) bestDiff = -bestDiff; for(i=1;i<=maxDenominator;i++) { F = ConvertDoubleToFrac(toMakeRational,i); diff = toMakeRational-ConvertFracToDouble(F); if(diff<0) diff = -diff; if(diff Ineq; vector sortIndex; ofstream File; string filename; cout << "Finding the sharp inequalities..." << endl; Ineq.resize(H.size()); for(i=0;iIneq[sortIndex[maxIneq]]) maxIneq = i; //Swap h and maxIneq i = sortIndex[h]; sortIndex[h] = sortIndex[maxIneq]; sortIndex[maxIneq] = i; } TargetTuranDensity = NearestRational(Ineq[sortIndex[0]],accuracyTargetDensity); if(bDefaultRoundingScheme==false) { cout << setprecision(20); cout << "Density : " << Ineq[sortIndex[0]] << " ("<< TargetTuranDensity << ")" << endl; cout << "Numerator : "; cin >> i; cout << "Denominator : "; cin >> j; TargetTuranDensity.N = i; TargetTuranDensity.D = j; TargetTuranDensity.Simplify(); cout << endl; } else { cout << "Target density : " << TargetTuranDensity << endl; } double dTarget; dTarget = ConvertFracToDouble(TargetTuranDensity); for(n=0;n> n; } vector().swap(sharpIneq); sharpIneq.resize(n); for(i=0;i0) firstSign = 1; else firstSign = -1; } } LCM = firstSign*LCM; //Scale the basis. for(j=0;j Ineq; cout << "Calculating the upper bound..." << endl; ScaleBasis(); //Calculate the coefficient of each H. Ineq.resize(noOfH); doneSoFar = 0; globalError = 0; //No error. #pragma omp parallel for schedule(dynamic) default(shared) private(h,loopError) for(h=0;hglobalError) globalError = loopError; if(globalError==0) { doneSoFar++; cout << "\r" << doneSoFar << " of " << noOfH << " coefficients calculated." << flush; } } } //Display any errors. if(globalError!=0) { cout << endl << endl; if(globalError==4) cout << "ERROR : Division by zero."; if(globalError==3) cout << "ERROR : Numeric overflow occurred. Try increasing LI_MAX in the source code."; if(globalError==2) cout << "ERROR : Allocating memory failed."; if(globalError==1) cout << "ERROR : An unknown error occurred."; cout << endl << endl; return false; } cout << "\r "; for(i=0;i<2*NumberOfDigits(noOfH);i++) cout << " "; cout << "\rDone."<< endl; //Find the largest coefficient. Density = 0; for(h=0;h > Matrix, vector > >& vecSystem, vector& maxVal) { long i, j, k; long dim; long maxV, maxC; double maxSoFar; double tolerance = toleranceEigenConstraint; double temp; vector leadingVariable, leadingConstraint; //Works in much the same way as LinearConstraintSolver(), but the number of zero rows is varied. //The constants of the constraints are all zero and so have been omitted. vector().swap(maxVal); vector > >().swap(vecSystem); dim = Matrix.size(); if(dim<=0) { //No vectors to find. vecSystem.push_back(vector >()); return; } //For each constraint record its maximum value. maxVal.resize(dim,0); //Add the system when all rows are considered zero. vecSystem.push_back(vector >()); vecSystem.back().resize(dim); for(i=0;i >()); for(i=0;i()); for(j=0;j >& vectors) { long i, j, n; long nVec, dim; long indexOfMax; double maxSoFar, temp; //Store the number of components in a vector. dim = vectors.size(); //Store the number of vectors. nVec = vectors[0].size(); for(n=0;n noOfZeroVectors; vector > zeroVectors; vector > > ChangeOfBasis; vector > eigenvalues; //Holds the max constraint element not the eigenvalues. vector > > eigenvectors; vector > > > eSystem; noOfZeroVectors.resize(Soln.size()); cout << "Computing the eigenvector constraints..." << endl; { ofstream File; string filename; filename = FilenameOut; filename.resize(filename.size()-4); filename += "_constraints.txt"; File.open(filename.c_str(),ios::trunc); if(File.good()==false) { cout << endl; cout << "ERROR : Could not open \"" << filename << "\" for writing." << endl; return false; } File << "Matrix [matrix number] : ([program's guess at the number of 0-eigenvectors])" << endl; File << "[assumed number of 0-eigenvectors] : [Threshold for zero] : "; File << "[largest denominator in program's guess] : [max error in rationalized values]" << endl; File << endl << endl; eigenvalues.resize(Soln.size()); eigenvectors.resize(Soln.size()); eSystem.resize(Soln.size()); File << setprecision(10) << scientific; for(n=0;n maxError; vector D; CLargeFraction F; maxError.resize(eigenvalues[n].size()); D.resize(eigenvalues[n].size()); for(i=0;imaxError[i]) maxError[i] = error; } } } //Decide how many 0-eigenvectors there are. noOfZeroVectors[n] = 0; for(i=0;i> noOfZeroVectors[n]; } cout << endl; } cout << "Computing the eigenvectors..." << endl; { ofstream File; string filename; filename = FilenameOut; filename.resize(filename.size()-4); filename += "_eigenvectors.txt"; File.open(filename.c_str(),ios::trunc); if(File.good()==false) { cout << endl; cout << "ERROR : Could not open \"" << filename << "\" for writing." << endl; return false; } File << "Matrix [matrix number] :" << endl; File << "[0-eigenvector number] : [floating point entry] "; File << "([guessed rational] [error in guess]), ..." << endl; File << endl << endl; File << scientific; ChangeOfBasis.resize(Soln.size()); for(n=0;n::max(),'\n'); cin >> c; if(c=='y' || c=='Y') { bUseFile = false; break; } if(c=='n' || c=='N') { bUseFile = true; break; } } if(bUseFile==true) { cout << "Name of vector file : " << endl; cin.clear(); cin.ignore(numeric_limits::max(),'\n'); getline(cin,filename); FileIn.open(filename.c_str()); if(FileIn.good()==false) { cout << endl; cout << "ERROR : Could not open \"" << filename << "\" for reading." << endl; return false; } c = ReadChar(FileIn); for(n=0;n > LinearProb; vector > LinearSoln; vector > dChangeOfBasis; CTerm newTerm; for(n=0;n(LinearProb,LinearSoln,0); //Store the kernel in ChangeOfBasis. for(i=0;i >().swap(newTerm.fMatrix); vector >().swap(newTerm.dMatrix); vector >().swap(newTerm.BasisFlag); vector >().swap(newTerm.BasisCoeff); newTerm.dMatrix.resize(Soln[n].dMatrix.size()-noOfZeroVectors[n]); for(i=0;i coeff; coeff.resize(FInfo[newTerm.nType][newTerm.iType].FlagList.size(),0); for(j=0;j > vectorToMatrixMap; vector > unusedVarMap; vector > > SharpSoln; vector > ProjSoln; CLargeInt Accuracy; Accuracy = 1; for(i=0;i()); vectorToMatrixMap.back().resize(3); vectorToMatrixMap.back()[0] = n; vectorToMatrixMap.back()[1] = i; vectorToMatrixMap.back()[2] = j; } if(vectorToMatrixMap.size()==0) return true; //No matrix entries. cout << "Solving the sharp inequality problem..." << endl; //Find all solution vectors which cause the sharp inequalties to equal the TargetTuranDensity. { vector > SharpProb; SharpProb.resize(vectorToMatrixMap.size()+1); for(i=0;i > SharpProbShrink; vector > usedVarMap; bool isUsed; vector >().swap(unusedVarMap); for(var=0;var(SharpProb,SharpSoln,0); if(SharpSoln.size()<=0) { cout << endl; cout << "ERROR : The matrices could not satisfy the sharp inequalities." << endl; return false; } } cout << "Solving the projection problem..." << endl; //Project the dMatrix solution onto the SharpSoln. { vector > ProjProb; vector offset; vector > commonEntry; //[component index][list index] = kernel number. vector > commonEntryCoeff; //[component index][list index] = coefficient of kernel entry. offset.resize(vectorToMatrixMap.size()); for(var=0;var(ProjProb,ProjSoln,toleranceProjectSolution); } cout << "Converting the solution into matrices..." << endl; //Map the projected solution back into matrices. { //Convert ProjSoln into fractions. vector fProjSoln; fProjSoln.resize(ProjSoln.size()); for(i=0;i > M; cout << endl; cout << "Checking the matrices are positive semidefinite..." << endl; noOfTerms = Soln.size(); for(nTerm=1;nTerm<=noOfTerms;nTerm++) { if(Soln[nTerm-1].fMatrix.size()<=0) continue; cout << "\rChecking matrix " << nTerm << " of " << noOfTerms << " : "; cout << "Copying matrix. " << flush; M = Soln[nTerm-1].fMatrix; dim = M.size(); //Make M[][] symmetric. for(i=0;iglobalError) globalError = loopError; } } //Display any errors. if(globalError!=0) { cout << endl << endl; if(globalError==4) cout << "ERROR : Division by zero."; if(globalError==3) cout << "ERROR : Numeric overflow occurred. Try increasing LI_MAX in the source code."; if(globalError==2) cout << "ERROR : Allocating memory failed."; if(globalError==1) cout << "ERROR : An unknown error occurred."; cout << endl << endl; return false; } //Now we need to restore row n, and do the column operations. It is easy to show //that this will not effect the entries M[i][j] where i,j>=n+1. Furthermore row n //and column n will end up consisting entirely of zeros, with the exception of //M[n][n] which will be some positive value. Hence to save time we will skip //these operations. It is enough to check the submatrix consisting of the entries //M[i][j] where i,j>=n+1. The entire matrix is psd if and only if the submatrix //is psd. } cout << "\rChecking matrix " << nTerm << " of " << noOfTerms << " : "; cout << " "; for(i=0;i<2*NumberOfDigits(dim);i++) cout << " "; } cout << "\r "; for(i=0;i<2*NumberOfDigits(noOfTerms);i++) cout << " "; cout << "\rDone." << endl; cout << endl; return true; } void InitSoln() { long i, j, n, t, m; vector().swap(Soln); //Determine the blocks we are optimizing over. for(n=0;n<=orderOfH-2;n++) for(t=0;t0) { Soln.push_back(CTerm()); Soln.back().nType = n; Soln.back().iType = t; //Copy the basis. Soln.back().BasisFlag.resize(FInfo[n][t].Basis[m].size()); Soln.back().BasisCoeff.resize(FInfo[n][t].Basis[m].size()); for(i=0;i> d; while(true) { //Read "Matrix number" "Block number" row column value. File >> m >> n >> i >> j >> d; if(File.eof()==true) break; if(File.good()==false) { cout << endl; cout << "ERROR : Reading \"" << FilenameOut << "\" failed." << endl << endl; return false; } if(m!=2 || n>Soln.size()) continue; if(i>Soln[n-1].dMatrix.size() || j>Soln[n-1].dMatrix.size()) { cout << endl; cout << "ERROR : Reading \"" << FilenameOut << "\" failed." << endl << endl; return false; } Soln[n-1].dMatrix[i-1][j-1] = d; Soln[n-1].dMatrix[j-1][i-1] = d; } File.close(); return true; } bool ReadOutFile_DualCSDP() { long n, r, c; double d; ifstream File; InitSoln(); File.open(FilenameOut.c_str()); if(File.good()==false) { cout << endl; cout << "ERROR : Could not open \"" << FilenameOut << "\" for reading." << endl << endl; return false; } //Read in the density bound $\rho$ (and ignore it). File >> d; for(n=0;n> d; if(File.eof()==true || File.good()==false) { cout << endl; cout << "ERROR : Reading \"" << FilenameOut << "\" failed." << endl << endl; return false; } Soln[n].dMatrix[r][c] = d; Soln[n].dMatrix[c][r] = d; } File.close(); return true; } //Reads through "in" until it finds "search". //If successful it returns true. //If "search" cannot be found it returns false. bool SearchForHeader(istream& in, string search) { long i; char c; vector found; if(search.size()==0) return true; found.push_back(0); while(true) { //Check if there are any members of found which indicate they've completed. for(i=0;i> d; if(sstr.fail()==true) return false; return true; } bool ReadOutFile_PrimalSDPA() { long n, r, c; double d; ifstream File; InitSoln(); File.open(FilenameOut.c_str()); if(File.good()==false) { cout << endl; cout << "ERROR : Could not open \"" << FilenameOut << "\" for reading." << endl << endl; return false; } //Find "yMat =" if(SearchForHeader(File,"yMat =")==false) { cout << endl; cout << "ERROR : Reading \"" << FilenameOut << "\" failed." << endl << endl; return false; } //Read in the matrix entries. for(n=0;n0) m++; FileOut << m << endl; FileOut << endl; //Write out the Terms. m = 0; for(n=0;n FlagMap; //Ignore zero dimensional matrices. if(Term.fMatrix.size()<=0) continue; m++; FileOut << endl; FileOut << "--- Term " << m << " ---" << endl; FileOut << endl; FileOut << "Order of type : " << FI.Type.nV << endl; FileOut << "Order of flags : " << FI.FlagList[0].nV << endl; //Relabel flags to eliminate flags which do not appear in the basis. { FlagMap.resize(FI.FlagList.size(),-1); for(i=0;i0) { if(bFirst==false) FileOut << "+"; } else { FileOut << "-"; bCoeff = -bCoeff; } if(bCoeff!=1) FileOut << bCoeff; FileOut << "(F"; FileOut << FlagMap[Term.BasisFlag[i][j]] << ")"; bFirst = false; } } FileOut << endl; } FileOut << endl; FileOut << "Matrix :" << endl; for(i=0;i& Family, long order, const CGraph& S) { long i, j, k, n; long c[10]; CGraph g, gMin; KeyFlag key; ValFlag val; CHashTable htList; vector classFamily[10]; vector index; vector > classVertices; vector().swap(Family); key.typeOrder = 0; val.iFlag = 0; val.iType = 0; for(i=0;i<9;i++) for(j=0;j<9;j++) for(k=0;k<9;k++) KeyFlag::hashM[i][j][k] = GetRandomInt(); for(i=0;i<9;i++) KeyFlag::hashOrder[i] = GetRandomInt(); //Fill in the first few classFamily members. { for(i=0;i<9;i++) for(j=0;j<9;j++) for(k=0;k<9;k++) g.M[i][j][k] = false; g.nV = 0; classFamily[0].push_back(g); g.nV = 1; classFamily[1].push_back(g); g.nV = 2; classFamily[2].push_back(g); g.nV = 3; classFamily[3].push_back(g); if(NumberOfEdges(S)>0) { g.M[0][1][2] = true; g.M[0][2][1] = true; g.M[1][0][2] = true; g.M[1][2][0] = true; g.M[2][0][1] = true; g.M[2][1][0] = true; classFamily[3].push_back(g); } } if(order<=3) { Family = classFamily[order]; return; } n = order; //for(n=4;n<=order;n++) { htList.Free(); g.nV = n; for(i=0;i=g.nV || j>=g.nV || k>=g.nV) continue; if(S.M[c[i]][c[j]][c[k]]==true) g.M[i][j][k] = true; } FindMinIso(g,gMin,0); key.flag = gMin; if(htList.Get(key)==NULL) { htList.Add(key,val); Family.push_back(gMin); } } } return; } void FindForbiddenBlowups() { long i, j, k, p, q; CGraph g; list NewForbiddenGraphs; list TempBlowups; list::iterator it, jt, kt, endt; list >::const_iterator itF; ForbiddenNonBlowups = ForbiddenGraphs.size(); if(ForbiddenNonBlowups<=0) return; cout << "Extending the forbidden graph list..." << endl; //Copy ForbiddenGraphs vector into a list. NewForbiddenGraphs.clear(); for(i=0;inV;i++) for(j=i+1;jnV;j++) { //Check if i,j are a non-covering pair of vertices. for(k=0;knV;k++) { if(k==i || k==j) continue; if(it->M[i][j][k]==true) break; } if(k==it->nV) { //Found a non-covering pair of vertices. //Union vertex j into i. g = *it; for(p=0;pM[j][p][q]==true) { g.M[i][p][q] = true; g.M[p][i][q] = true; g.M[p][q][i] = true; } //Isolate vertex j. for(p=0;p >().swap(Iso); //Clear H. vector().swap(H); //Clear the forbidden graph list. vector().swap(ForbiddenGraphs); //Clear FInfo[] and the type list. for(i=0;i<8;i++) { vector().swap(FInfo[i]); vector().swap(modifyTypeList[i]); } //Clear the flag hash table. htFlags.Free(); //Clear the product hash tables. vector >().swap(htProd); //Clear Soln and sharpIneq. vector().swap(Soln); vector().swap(sharpIneq); cout << "\r \r" << flush; return; } int main(int argc, char* argv[]) { try { if(ProcessArguments(argc,argv)==false) return 0; if(FindSystemFormat(argv[0])==false) return 0; InitIso(); if(bForbidBlowups==true) FindForbiddenBlowups(); FillFlagInfo(); if(GenerateH()==false) return 0; if(CalculateFlagProducts()==false) return 0; if(ChooseSDPFormat()==false) return 0; RunSDPSolver(); if(ReadOutFile()==false) return 0; //To do: Rationalize the solution if it is not exact. if(CalculateSharpIneq()==false) return 0; if(MakeMatricesPositiveDefinite()==false) return 0; if(ProjectSolution()==false) return 0; //Check matrices are positive definite. if(IsPSD()==false) return 0; CLargeFraction Density; if(CalculateDensityBound(Density)==false) return 0; cout << endl; DisplayDensity(Density,10); if(TargetTuranDensity!=Density) { cout << endl << endl; cout << "ERROR : Failed to reach the target density." << endl << endl; //return 0; //Output best bound anyway. TargetTuranDensity = Density; } if(WriteProofFile()==false) return 0; } catch(ErrorOverflow&) { cout << endl << endl; cout << "ERROR : Numeric overflow occurred. Try increasing LI_MAX in the source code."; cout << endl << endl; return 0; } catch(ErrorDivisionByZero&) { cout << endl << endl; cout << "ERROR : Division by zero."; cout << endl << endl; return 0; } catch(bad_alloc&) { cout << endl << endl; cout << "ERROR : Allocating memory failed."; cout << endl << endl; return 0; } catch(exception& e) { cout << endl << endl; cout << "ERROR : An exception occurred." << endl; cout << e.what(); cout << endl << endl; return 0; } catch(...) { cout << endl << endl; cout << "ERROR : An unknown error occurred."; cout << endl << endl; return 0; } Cleanup(); cout << "--- Finished ---" << endl << endl; return 0; }