#include #include #include using namespace std; //If bSaveToTxtFile is true it will save the output of the program to the file "Output.txt". bool bSaveToTxtFile = false; //CGraph stores data about a tripartite graph (with at most 3 vertices per vertex class, see //Lemma 4.9). // //Member variables //---------------- // //M[][] is the adjacency matrix of the graph. // An entry of 1 means the edge is present, 0 indicates it is missing. // //hasVertex[] tells us which vertices are part of the graph. // This allows us to represent smaller class sizes. // An entry of 1 means the vertex is part of the graph, 0 indicates it is missing. // //idEdge is a 27 bit integer representation of M[][]. // We use EdgeMatrix[][] to convert between M[][] and idEdge. // //idVertex is a 9 bit integer representation of hasVertex[]. // The least significant bit should correspond to hasVertex[0]. // //classSize[] keeps track of the size of the vertex classes (for speed). // E.g. classSize[0] = hasVertex[0]+hasVertex[1]+hasVertex[2]. // //Member functions //---------------- // //void AssignMatrixFromID(long id) id should be a 27 bit integer. The function sets idEdge // to id, then uses EdgeMatrix[][] to construct the // corresponding adjacency matrix M[][]. // //void AssignVerticesFromID(long id) id should be a 9 bit integer. The function sets idVertex // to id and then constructs the corresponding hasVertex[] // array. It also fills in classSize[]. // //Notes //----- // //The vertex classes are: //Class 0 - vertices 0, 1, 2, //Class 1 - vertices 3, 4, 5, //Class 2 - vertices 6, 7, 8. // //Observe that vertex v lies in vertex class v/3 (where '/' is the integer division //operator). This provides an efficient way of testing if two vertices lie in the same vertex //class, which we will need to do often. // //If hasVertex[v] is zero it means vertex v should not be considered part of the tripartite //graph. This is to allow CGraph to be flexible enough to represent tripartite graphs of //smaller order. The adjacency matrix M[][] is of fixed size but since vertex v is not part //of the graph, the entries in row v and column v are of no importance. Some functions assume //(for speed purposes) that if the vertex is missing then its corresponding column and row //entries should all be zero. // //To speed things up, functions that take CGraph objects as input do little (if any) checking //that the objects are well-formed, e.g. entries of M[][] are all 0 or 1, M[][] is symmetric, //that idEdge correctly represents M[][], or vertices that are not part of the graph have no //neighbours. It is the responsibility of the process calling such functions to ensure that //the CGraph objects are well-formed. // //We keep a record of the variables idEdge and idVertex as they provide a natural ordering of //CGraph objects. This helps make the process of testing if two tripartite graphs are //strongly-isomorphic to each other more efficient. class CGraph { public: //Construct M[][] using EdgeMatrix[][] and id (a 27 bit integer), idEdge is set to id. void AssignMatrixFromID(long id); //Sets idVertex to id (a 9 bit integer), then constructs hasVertex[] and classSize[]. void AssignVerticesFromID(long id); //The adjacency matrix of the graph. //0 = Edge missing. //1 = Edge present. long M[9][9]; //Used to indicate which vertices are part of the graph. //0 = Vertex has been removed. //1 = Vertex is part of the graph. long hasVertex[9]; //A 27 bit integer representing M[][]. long idEdge; //A 9 bit integer representing hasVertex[]. long idVertex; //The size of the vertex classes. long classSize[3]; }; //EdgeMatrix[][] is used to bijectively map the 27 possible edges of the tripartite graphs to //27 bit integers. The entries indicate which bit (0 to 26) in CGraph::idEdge is the //corresponding entry in the adjacency matrix CGraph::M[][]. Entries of -1 indicate that such //an edge lies within a vertex class, so should always be zero in CGraph::M[][], and //consequently does not get mapped to any of the 27 bits in CGraph::idEdge. long EdgeMatrix[9][9] = { {-1, -1, -1, 18, 19, 20, 0, 1, 2}, {-1, -1, -1, 21, 22, 23, 3, 4, 5}, {-1, -1, -1, 24, 25, 26, 6, 7, 8}, {18, 21, 24, -1, -1, -1, 9, 10, 11}, {19, 22, 25, -1, -1, -1, 12, 13, 14}, {20, 23, 26, -1, -1, -1, 15, 16, 17}, { 0, 3, 6, 9, 12, 15, -1, -1, -1}, { 1, 4, 7, 10, 13, 16, -1, -1, -1}, { 2, 5, 8, 11, 14, 17, -1, -1, -1}}; //The 1296 permutations of the the 9 vertices that leaves the tripartite graph //strongly-isomorphic to its original form. long StrongIsoPermute[1296][9]; //Store the possible extremal vertex minimal graphs in Store[][0]. Also keep a copy of all //graphs strongly-isomorphic to Store[i][0] in Store[i][1 to 1295]. Use Found to track how //much of Store[][] has been filled in. CGraph Store[200][1296]; long Found; //Graphs that are strongly-isomorphic to F7 and F9 (see Figure 8). CGraph F7, F9; //Displays the adjacency matrix of graph g. To help distinguish the vertex classes it uses //the symbol '.' to represent edges that lie within a class. void DisplayGraph(CGraph& g, ostream& os = cout); //Display all the possible extremal vertex minimal graphs in Store[][0]. //Ignores graphs with idEdge = -1. void DisplayStore(ostream& os = cout); //Declared in CGraph. Construct M[][] using EdgeMatrix[][] and id (a 27 bit integer), idEdge //is set to id. //void CGraph::AssignMatrixFromID(long id); //Declared in CGraph. Sets idVertex to id (a 9 bit integer), then constructs hasVertex[] //and classSize[]. //void CGraph::AssignVerticesFromID(long id); //Initializes the StrongIsoPermute[][] array and constructs the graphs F7 and F9. void SetUp(); //Returns true if it can show the graph is not extremal or not vertex minimal (using Lemmas //4.8, 4.10, and 4.11). bool bBadClassSize(CGraph& g); //Returns true if it can show the graph is not extremal or not vertex minimal (using //Corollary 4.4). bool bCanMoveEdgeWeights(CGraph& g); //Returns true if g contains no triangles. We are not interested in such a graph by Theorem //2.2. bool bHasNoTriangles(CGraph& g); //Returns true if it can show the graph is not extremal or not vertex minimal (using Lemma //4.12). This function assumes Store[][] is filled with a complete list of possible extremal //vertex minimal tripartite graphs. bool bCanReplaceBy8(CGraph& g); //Creates a graph strongly-isomorphic to gIn by using the vertex mapping given in //StrongIsoPermute[p][]. The result is stored in gOut (which should be a different object //to gIn). void StrongIsoGraph(CGraph& gIn, long p, CGraph& gOut); //Displays the adjacency matrix of graph g. To help distinguish the vertex classes it uses //the symbol '.' to represent edges that lie within a class. void DisplayGraph(CGraph& g, ostream& os) { long i,j; for(i=0;i<9;i++) { if(g.hasVertex[i]==0) continue; //Ignore vertices that are not part of the graph. for(j=0;j<9;j++) { if(g.hasVertex[j]==0) continue; //Ignore vertices that are not part of the graph. //Display whether ij is an edge. if(i/3==j/3) os << " ."; //The edge lies within a vertex class. else { if(g.M[i][j]==0) os << " 0"; //The edge is missing. else os << " 1"; //The edge is present. } } os << endl; } os << endl; return; } //Display all the possible extremal vertex minimal graphs in Store[][0]. //Ignores graphs with idEdge = -1. void DisplayStore(ostream& os) { long i,order; os << "Adjacency matrices of possible extremal vertex minimal tripartite" << endl; os << "graphs (edges which would lie within a vertex class are indicated" << endl; os << "by the entry \".\"):" << endl; os << endl; for(order=0;order<=9;order++) //Display the smallest order graphs first. for(i=0;i>EdgeMatrix[i][j])&1; //Assign the the correct bit of id. return; } //Sets idVertex to id (a 9 bit integer), then constructs hasVertex[] and classSize[]. void CGraph::AssignVerticesFromID(long id) { long i; idVertex = id; for(i=0;i<9;i++) hasVertex[i] = (id>>i)&1; //Assign the "i"th bit of id. for(i=0;i<3;i++) classSize[i] = hasVertex[3*i]+hasVertex[3*i+1]+hasVertex[3*i+2]; return; } //Initializes the StrongIsoPermute[][] array and constructs the graphs F7 and F9. void SetUp() { long i,n; long idEdge,idVertex; long P[4]; //Fill in the StrongIsoPermute[][] array. { //Permute[][] stores the 6 perumtations of {0,1,2}. long Permute[6][3] = {{0,1,2}, {0,2,1}, {1,0,2}, {1,2,0}, {2,0,1}, {2,1,0}}; //Permute class 0 by Permute[P[0]][]. //Permute class 1 by Permute[P[1]][]. //Permute class 2 by Permute[P[2]][]. //Permute classes by Permute[P[3]][]. n = 0; for(P[0]=0;P[0]<6;P[0]++) for(P[1]=0;P[1]<6;P[1]++) for(P[2]=0;P[2]<6;P[2]++) for(P[3]=0;P[3]<6;P[3]++) { //Calculate the new label for vertex i. //i is the "i%3" vertex in class "i/3", i.e. i = 3*(i/3)+(i%3). //Class "i/3" gets mapped to class Permute[P[3]][i/3]. //The vertices in class "i/3" are permuted by Permute[P[i/3]][]. for(i=0;i<9;i++) StrongIsoPermute[n][i] = 3*Permute[P[3]][i/3]+Permute[P[i/3]][i%3]; n++; } } //Construct the graph F7 (see Figure 8). { //Vertices 7,6,5,4,3,1,0 are part of F7. //Vertices 8 and 2 are not part of F7. idVertex = 251; //Binary 011111011. //An array of the 9 edges in F7. long Edge[9][2] = {{0,4}, {0,5}, {0,7}, {1,3}, {1,4}, {1,6}, {3,7}, {4,6}, {5,6}}; //Construct idEdge for F7 using Edge[][] and EdgeMatrix[][]. idEdge = 0; for(i=0;i<9;i++) idEdge |= 1<1 && g.classSize[1]>1 && g.classSize[2]>1) { idVertex[n] = i; n++; } } //Initialize the number of graphs found that are possibly extremal and vertex minimal. Found = 0; //Generate all possible tripartite graphs by cycling through all possible 27 bit //integers representing CGraph::idEdge, and all possible CGraph::idVertex values stored //in idVertex[]. for(idEdge=0;idEdge<(1<<27);idEdge++) { g.AssignMatrixFromID(idEdge); //Work out which vertices have neighbours, and store the result in notIsolatedID. //This will aid us in creating a well-formed CGraph object. Much like //CGraph::idVertex, if vertex i has a neighbour then bit i is 1 otherwise it is 0. //Bit 0 is the least significant bit. notIsolatedID = 0; for(i=0;i<9;i++) for(j=0;j<9;j++) notIsolatedID |= g.M[i][j]<=sizeof(Store)/sizeof(Store[0])) { //We should never run out of memory. cout << endl << endl; cout << "Error: Too many graphs, increase size of Store[][]." << endl; cout << endl; return 0; } //Start filling in Store[Found][] with all the graphs strongly-isomorphic to g. for(p=0;p<1296;p++) { StrongIsoGraph(g,p,Store[Found][p]); //If for any reason we wish to discard g and all its isomorphisms, we will //break out of this loop and deal with removing the graphs from Store[][]. //Note that if Store[x][0] and Store[y][0] are strongly-isomorphic to each //other. Then the arrays Store[x][] and Store[y][] are just permutations of //each other. To avoid such duplication we'll only keep Store[Found][] if the //idEdge and idVertex of Store[Found][0] is the smallest of all the //isomorphisms in Store[Found][]. if( Store[Found][0].idEdge > Store[Found][p].idEdge || (Store[Found][0].idEdge == Store[Found][p].idEdge && Store[Found][0].idVertex > Store[Found][p].idVertex)) break; //Get rid of graphs whose class size can be reduced, or is too small (i.e. //those satisfying Lemmas 4.8, 4.10, and 4.11). if(bBadClassSize(Store[Found][p])==true) break; //Get rid of graphs where we can reduce the density of triangles by moving //edge weights (see Corollary 4.4). if(bCanMoveEdgeWeights(Store[Found][p])==true) break; //Get rid of graphs without a triangle (see Theorem 2.2). if(bHasNoTriangles(Store[Found][p])==true) break; //Get rid of graphs strongly-isomorphic to F7 (see Lemma 4.13). if(Store[Found][p].idEdge ==F7.idEdge && Store[Found][p].idVertex==F7.idVertex) break; //Get rid of graphs strongly-isomorphic to F9 (see Lemma 4.15). if(Store[Found][p].idEdge ==F9.idEdge && Store[Found][p].idVertex==F9.idVertex) break; } //If p!=1296 we broke out of the loop earlier than expected because // (i) one of graphs was not extremal or not vertex minimal, //or (ii) Store[Found][0] was not the graph with the smallest IDs. //In either case we wish to discard everything in Store[Found][] which we can //easily do by not updating Found. if(p==1296) { //We didn't break out of the loop early. We wish to retain the graphs in //Store[Found][] which we do by increasing the value of Found. Found++; } } } //We have now tested every possible tripartite graph (with class sizes at most three). //Store[][] holds a list of possible extremal vertex minimal graphs, which we will reduce //further by repeated applications of Lemma 4.12, as implemented by the function //bCanReplaceBy8(). If Store[i][j] satisfies bCanReplaceBy8() then Store[i][j] and all //graphs strongly-isomorphic to it (i.e. the graphs in the Store[i][] array) can be //removed. For speed and convenience we will indicate that they have been removed by //setting Store[i][0].idEdge to -1. do { bStoreChanged = false; //Go through each possible extremal vertex minimal graph. for(i=0;i