...]] [-]
//[-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 >().swap(Iso);
//Allocate memory for Iso[][].
Iso.resize(Factorial[orderOfH]);
for(i=0;ij>k).
for(i=0;i i > j > k >=0).
//Specifically ijk > rst if
// i>r,
//or i==r and j>s,
//or i==r and j==s and k>t.
//Our integer is a "gIn.nV choose 3" bit number where each bit is associated with a
//triple in our ordering. The largest triple is associated with the least significant
//bit, and the second largest triple with the second least significant bit, etc. The
//value of the bits in the integer are given by the associated triples, with a value
//of 1 if ijk is an edge in g and 0 if it isn't.
//We will not explicitly construct such integers, but by examining the edge set of
//gOut and gIn, under the permutation p, we will make a comparison of which
//associated integer would be smaller.
//Cycle through the triples from the smallest to the largest. Note that we can take
//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;h