diff --git a/lib_graphd/inc/Graph.h b/lib_graphd/inc/Graph.h index c259e99..0637314 100644 --- a/lib_graphd/inc/Graph.h +++ b/lib_graphd/inc/Graph.h @@ -127,8 +127,10 @@ namespace Graph { int add_vertex(); bool remove_edge(int u, int v); void remove_vertex(int u); - + int edge_subdivision(int u, int v, int w); int contract_edge(int u, int v); + int fuse_vertices(int u, int v, bool contract_edge); + void resize_graph(int n); void resize_adj_vec(int n); void eliminate_vertex(int v, list *forward_neighbors, bool remove); void initialize_params(); diff --git a/lib_graphd/inc/GraphUtil.h b/lib_graphd/inc/GraphUtil.h index 2dc8107..4e3780c 100644 --- a/lib_graphd/inc/GraphUtil.h +++ b/lib_graphd/inc/GraphUtil.h @@ -68,13 +68,15 @@ namespace Graph { int vertex_separator(Graph *g, list *V, vector *> *members); + //ConstructSeparator from metis + void metis_ConstructSeparator(VertexWeightedGraph *g, list *top, list *bottom); + //runs a BFS from start using //only vertices with allowed[v] = true. //you need to delete the memory in the returned bool array. //num_reached is the number of vertices which are reachable. This does not include //disallowed vertices, and does include the start. bool *bfs(Graph *g, int start, bool *allowed, int *num_reached); - bool *bfs_old(Graph *g, int start, bool *allowed, int *num_reached); int *bfs_dist(Graph *g, int start, bool *allowed, int *num_reached); int *bfs_dist(Graph *g, int start, bool *allowed, int *num_reached, int *ecc); diff --git a/lib_graphd/src/Graph.cpp b/lib_graphd/src/Graph.cpp index b9fdf7d..b3f6c30 100644 --- a/lib_graphd/src/Graph.cpp +++ b/lib_graphd/src/Graph.cpp @@ -197,7 +197,7 @@ namespace Graph { int Graph::get_num_edges_in_subgraph(list *vertices){ vertices->sort(); - int highest_index = vertices->back(); + //int highest_index = vertices->back(); vector v(this->num_nodes,false); list::iterator ii; for(ii = vertices->begin(); ii != vertices->end(); ++ii){ @@ -413,7 +413,67 @@ namespace Graph { num_nodes--; } // remove_vertex + int Graph::edge_subdivision(int u, int v, int w) { + if (nodes[u].label == -1 || nodes[v].label == -1){ + fatal_error( + "%s: Cannot remove edge (%d, %d) as one of its vertices is undefined!\n", + __FUNCTION__, u, v); + } + list::iterator it; + + // check that v is a neighbour of u + bool foundv = false; + for (it = nodes[u].nbrs.begin(); it != nodes[u].nbrs.end(); ++it){ + if(*it == v) + foundv = true; + } + if(foundv == false){ + return false; + } + + // if w provided, check that it is in bounds + if(w > capacity){ + nodes.resize(2*capacity); + capacity*=2; + } + + nodes[w].nbrs.clear(); + if(!nodes[w].nbrs.empty()){ + FERROR("%s: node is not empty", __FUNCTION__); + throw GraphException("node is not empty\n"); + } + else{ + nodes[w].label=w; + degree[w] = 2; + nodes[w].nbrs.push_back(u); + nodes[w].nbrs.push_back(v); + + // remove u from v's nbrs list and vice versa + for(it = nodes[u].nbrs.begin() ; it != nodes[u].nbrs.end();it++){ + if(*it==v) { + *it=w; + break; + } + } + for(it = nodes[v].nbrs.begin() ; it != nodes[v].nbrs.end();it++){ + if(*it==u) { + *it=w; + break; + } + } + } + num_edges++; + next_label++; + num_nodes++; + return w; + } + + // contract_edge and fuse_vertices, differ only in whether or not e=(u,v) has to be an edge int Graph::contract_edge(int u, int v){ + return fuse_vertices(u,v,true); + } + + int Graph::fuse_vertices(int u, int v, bool contract_edge=false){ int i; if(simple != true){ fatal_error("%s: called on a non-simple graph!\n", __FUNCTION__); @@ -426,24 +486,26 @@ namespace Graph { vector neighbors(capacity); fill(neighbors.begin(), neighbors.end(), false); list::iterator it; + + if (v == u)//maybe don't need this + return false; + bool foundv = false; - it = nodes[u].nbrs.begin(); - while(it != nodes[u].nbrs.end()){ + for (it = nodes[u].nbrs.begin(); it != nodes[u].nbrs.end(); ++it){ neighbors[*it] = true; if(*it == v){ foundv = true; } - - ++it; } - if(foundv == false){ - return false; + + if(contract_edge){ // for fuse_vertices alone, don't need to be neighbours. for contract_edge you do + if(foundv == false){ + return false; + } } - it = nodes[v].nbrs.begin(); - while(it != nodes[v].nbrs.end()){ + for (it = nodes[v].nbrs.begin(); it != nodes[v].nbrs.end(); ++it){ neighbors[*it] = true; - ++it; } remove_vertex(u); remove_vertex(v); @@ -461,9 +523,21 @@ namespace Graph { } } } - return u; - } // contract_edge + } // fuse_vertices + + void Graph::resize_graph(int size){ + if(size < capacity){ + fatal_error( + "%s: resize_graph() called with new size %d less than capacity %d\n", + __FUNCTION__, size, capacity); + } + else{ + nodes.resize(size,Node()); + degree.resize(size,0); + this->set_capacity(size); + } + } void Graph::set_simple(bool si){ simple = si; diff --git a/lib_graphd/src/GraphUtil.cpp b/lib_graphd/src/GraphUtil.cpp index 1168e63..ca895b3 100644 --- a/lib_graphd/src/GraphUtil.cpp +++ b/lib_graphd/src/GraphUtil.cpp @@ -633,6 +633,265 @@ namespace Graph { return k; } + /** + * Calls CreateSeparator from metis + * top and bottom cannot have any overlap coming in, going out they will share the elements of the separator + * they bring in the top and bottom lists, and take out the top and bottom bags + */ + void GraphUtil::metis_ConstructSeparator(VertexWeightedGraph *g, list *top, list *bottom) + { +#if !HAS_METIS + fatal_error("Called METIS CreateSeparator without HAS_METIS.\n"); + return; +#else + + GraphUtil util; + util.populate_CRS(g); /// construct xadj, adjncy + + graph_t *graph; + ctrl_t *ctrl; + + list temp; + + int topsize = top->size(); + int bottomsize = bottom->size(); + int *originalid = new int[bottomsize+topsize]; + + list::iterator ii, jj, kk; + int id=0; + + for(ii = bottom->begin(); ii != bottom->end(); ++ii){ + originalid[id] = *ii; + id++; + } + for(ii = top->begin(); ii != top->end(); ++ii){ + originalid[id] = *ii; + id++; + } + idx_t nvtxs = topsize + bottomsize; + + int sizexa = topsize + bottomsize+1; + idx_t *xadj = new idx_t[sizexa]; + int sizead = (sizexa-1)*(sizexa-2); + idx_t *adjncy = new idx_t[sizead]; + + id=0; // position of ii iterator in originalid[], and position in xadj[] + int jd=0; // position of jj iterator in originalid[] + int j = 0; // position in adjncy + + bool include = false; + + // construct xadj, edges come from completion of each list, plus edges between lists + for(ii = bottom->begin(); ii != bottom->end(); ++ii) + { + xadj[id] = j; + + jd=0; + for(jj = bottom->begin(); jj!= bottom->end(); ++jj) + { + if(*jj != *ii) + { + temp.push_back(jd); + j++; + } + jd++; + } + for(jj = top->begin(); jj != top->end(); ++jj) + { + include=false; + for(kk = g->nodes[*jj].nbrs.begin(); kk!= g->nodes[*jj].nbrs.end() && !include; ++kk) + { + if(*kk == *ii) + { + include = true; + } + } + if(include) + { + temp.push_back(jd); + j++; + } + jd++; + } + temp.sort();// ?unnecessary? + + // adjncy[] needs to be filled from position xadj[id] to j inclusive + jj = temp.begin(); + for(int k=xadj[id]; k < j; k++){ + adjncy[k]=*jj; + jj++; + } + temp.clear(); + id++; + } + for(ii = top->begin(); ii != top->end(); ++ii) + { + xadj[id] = j; + + jd=bottomsize; // adjncy already contains bottom list's elements + for(jj = top->begin(); jj!= top->end(); jj++) + { + if(*jj != *ii) + { + temp.push_back(jd); + j++; + } + jd++; + } + jd = 0; + + for(jj = bottom->begin(); jj != bottom->end(); ++jj) + { + include=false; + for(kk = g->nodes[*jj].nbrs.begin(); kk!= g->nodes[*jj].nbrs.end() && !include; ++kk) + { + if(*kk == *ii) + { + include = true; + } + } + if(include) + { + temp.push_back(jd); + j++; + } + jd++; + } + temp.sort(); + + jj = temp.begin(); + for(int k=xadj[id]; k < j; k++){ + adjncy[k] = *jj; + jj++; + } + temp.clear(); + id++; + } + xadj[id]=j; + + //copy to an array of correct size + idx_t *adjncysmall = new idx_t[j]; + for(id = 0; idclear(); + bottom->clear(); + + //initialization using METIS functions + ctrl = SetupCtrl(METIS_OP_OMETIS, NULL, 1, 3, NULL, NULL); + graph = SetupGraph(ctrl,nvtxs,1,xadj,adjncy,NULL,NULL,NULL); + // allocate workspace memory // + AllocateWorkSpace(ctrl, graph); + SetupGraph_tvwgt(graph); + + //Call to METIS function that provides a separator + MlevelNodeBisectionMultiple(ctrl, graph); + + /// where = 0 left set, =1 right, =2 separator + idx_t *where; + where = graph->where; + + // are there 3 sets? is left = bottom or right = bottom + int bot_i=0; + bool keepgoing = true; + bool bottom0; + bool leftexists = false; + bool rightexists = false; + bool separatorexists = false; + + while((bot_i < bottomsize) && keepgoing) + { + if(where[bot_i]==0) + { + bottom0 = true; + leftexists = true; + keepgoing=false; + } + else if(where[bot_i]==1) + { + bottom0=false; + rightexists = true; + keepgoing=false; + } + else if(where[bot_i]==2) + { + separatorexists = true; + } + bot_i++; + } + + while( !(separatorexists && leftexists && rightexists) && (bot_i < bottomsize + topsize) ) + { + if(where[bot_i]==2) + { + separatorexists=true; + } + else if(where[bot_i]==0) + { + leftexists=true; + } + else if(where[bot_i]==1) + { + rightexists=true; + } + bot_i++; + } + + // if don't have all 3 sets, then return everything in a bottom list, with top list empty. + if(separatorexists && leftexists && rightexists) + { + // get original position in larger graph g from originalid[s] + for(int s = 0; spush_back(originalid[s]); + } + else + { + top->push_back(originalid[s]); + } + } + else if(where[s] == 1) + { + if(bottom0) + { + top->push_back(originalid[s]); + } + else + { + bottom->push_back(originalid[s]); + } + } + else if(where[s] == 2) + { + top->push_back(originalid[s]); + bottom->push_back(originalid[s]); + } + } + } + else + { + for(int s = 0; spush_back(originalid[s]); + } + } + + FreeWorkSpace(ctrl); + FreeGraph(&graph); + util.free_CRS(g); + delete [] xadj; + delete [] adjncy; + return; +#endif + } + + + //runs a BFS from start using only vertices with allowed[v] = true. //Returns an array of integers giving distance from the source (0 for source). //Unreachable vertices have value GD_INFINITY. @@ -677,7 +936,7 @@ namespace Graph { j = S.front(); S.pop_front(); left_in_level--; - if(dists[j] == GD_INFINITY){ + if(dists[j] == GD_INFINITY || dists[j] == -1){ // We have now visited j dists[j] = curr_level; num_found++; @@ -690,13 +949,15 @@ namespace Graph { // We haven't seen *ii before and it is an "acceptable" vertex, // so it is a candidate to be in the path - add it to the Stack S.push_back(*ii); // must be push_back since we pop_front and need FIFO. + dists[*ii]=-1; next_level++; } } } } - *ecc = curr_level - 1; + //*ecc = curr_level - 1; + *ecc = curr_level; *num_reached = num_found; // We emptied the stack. return dists; diff --git a/lib_graphd/src/Makefile b/lib_graphd/src/Makefile index 3f42b36..1339dd6 100644 --- a/lib_graphd/src/Makefile +++ b/lib_graphd/src/Makefile @@ -3,7 +3,7 @@ include ../../make.inc METIS_PROG_DIR=$(METIS_SRC_DIR)/programs ifeq ($(HAS_METIS),1) -CFILES = $(METIS_PROG_DIR)/smbfactor.c +CFILES = $(METIS_PROG_DIR)/smbfactor.c endif LIBNAME = libgraphd.a @@ -24,7 +24,7 @@ OBJ = $(SRC:.cpp=.o) POBJ = #compile smbfactor only if using metis. ifeq ($(HAS_METIS),1) -OBJ = $(SRC:.cpp=.o) smbfactor.o +OBJ = $(SRC:.cpp=.o) smbfactor.o endif ifeq ($(HAS_PARMETIS),1) diff --git a/lib_treed/inc/TDTree.h b/lib_treed/inc/TDTree.h index a6d0a3b..c639a96 100644 --- a/lib_treed/inc/TDTree.h +++ b/lib_treed/inc/TDTree.h @@ -148,6 +148,10 @@ class TDTree // Sorts all the members of the bags of T. void sort_bags(); + // Constructs a tree dcomposition using Dourisboure-Gavoille's BFS-Layering algorithm + // doesn't require an elimination ordering + void construct_bfs(int root); + // Constructs a tree decomposition using Bodlaender-Koster's "Algorithm 2" // with the provided elimination order void construct_BK(vector *elim_order); diff --git a/lib_treed/src/TDTree.cpp b/lib_treed/src/TDTree.cpp index a49383c..c8ff6ff 100644 --- a/lib_treed/src/TDTree.cpp +++ b/lib_treed/src/TDTree.cpp @@ -794,6 +794,690 @@ int TDTree::replace_by_join_tree(int old_node, int max_id) } + +/** Constructs a tree decomposition of this->G using + * Dourisboure & Gavoille's bfs algorithm + * The graph G is assumed to have a single connected + * component since tree decompositions of disconnected + * components can be joined together in an arbitrary way. + */ +void TDTree::construct_bfs(int root=1) +{ + // First make sure this->G has one connected component + if(this->G->get_num_components()!=1) + { + fatal_error("%s: Tree decomposition routines operate only on connected graphs." + " This graph has %d connected components\n",__FUNCTION__, + this->G->get_num_components()); + } + // Make sure root > 0 + if(root < 1) + { + fatal_error("%s: must have root > 0",__FUNCTION__); + } + + // We change the graph G, so we should use a copy. Note that this is potentially + // expensive for large graphs + // H will be modified to give the tree structure + // G modified to make label = tree node index + Graph::VertexWeightedGraph H=*(this->G); + + Graph::GraphUtil util; + bool *allowed; + allowed = new bool[H.get_capacity()]; + std::fill(allowed, allowed+H.get_capacity(),true); + int temp=0; + int num_levs=0; + + int *levels = util.bfs_dist(&H,root-1,allowed,&temp,&num_levs); + delete [] allowed; + temp = 0; + num_levs +=1; + print_message(1, "Number of levels in BFS_DIST sorting = %d\n", num_levs); + vector< list < int > > levelbags(num_levs); // each level has a list of points + for(int j = 0; j < H.get_capacity(); j++) + { + levelbags[levels[j]].push_back(j); + } + + list treenodelist; + list newbag; + list::iterator it, level_it, nbr_it, nbr_it2; + bool inbag = false; + Graph::Node *n, *n2; + list nbrs, nbrs2; + int bagsize = 0; + + // split lists containing all pts on single level, into multiple sets per level + // call these containers "prebags", make one TreeNode per prebag + // and add the TreeNode to the TreeNode list (which we later use to make the TreeNode vector) + for(int level = num_levs-1; level >=0;level--) + { + while(!levelbags[level].empty()) + { + //set up a new bag, with an initial element + temp = *levelbags[level].begin(); + levelbags[level].pop_front(); + newbag.clear(); + newbag.push_back(temp); + bagsize=1; + + if(!levelbags[level].empty()) + { + for(it = newbag.begin();it!= newbag.end();it++) + { + n = H.get_node(*it); + nbrs = n->get_nbrs(); + + for(level_it = levelbags[level].begin();level_it!=levelbags[level].end();) + { + // it points to an element of newbag + // level_it points to the level list, i.e. candidates for newbag + for(nbr_it =nbrs.begin();(nbr_it != nbrs.end()) && (!inbag);nbr_it++)// stop searching if match is found + { + if (*nbr_it == *level_it) + { + inbag = true; + } + // also add nodes that connect through a node on the next level + else if (level < num_levs-1 ) + { + n2 = H.get_node(*level_it); + nbrs2 = n2->get_nbrs(); + for(nbr_it2 = nbrs2.begin(); (nbr_it2 != nbrs2.end() ) && (!inbag);nbr_it2++ ) + { + if (*nbr_it == *nbr_it2 && (levels[*nbr_it] > (level) ) ) + //if (*nbr_it == *nbr_it2 && (H.get_node(*nbr_it)->get_label() > (level) ) ) + { + inbag = true; + } + } + } + } + + // move *level_it from level bag, to newbag + if(inbag) + { + newbag.push_back(*level_it); + level_it = levelbags[level].erase(level_it); + bagsize++; + } + else + { + ++level_it; + } + inbag = false; + } + } + } + + //collapse bag contents in H, to get tree + if( bagsize > 1 ) + { + it=newbag.begin(); + nbr_it=newbag.begin(); + nbr_it++; + for(;nbr_it!=newbag.end();nbr_it++) + { + //id of bag will be lowest vertex label in prebag + if(*nbr_it < *it) + { + temp = H.fuse_vertices(*nbr_it,*it, false); + } + else if(*it <= *nbr_it) + { + temp = H.fuse_vertices(*it,*nbr_it, false); + } + } + } + + // make nodes here, assign bag, and id fields + // using "prebag" here to refer to list of points in tree node at intermediate stage of D&G bfs algorithm + TDTreeNode *next_tree_node; + next_tree_node = new TDTreeNode; + next_tree_node->id = temp; //id of bag is min vertex label in prebag + next_tree_node->bag = newbag; //copy bag over + + //also modify G's labels so each node.label = prebag id + for(it=newbag.begin();it!=newbag.end();it++) + { + this->G->get_node(*it)->set_label(*it+1);// undo fuse_vertices label changes + //this->G->get_node(*it)->set_label(temp); + } + + treenodelist.push_back(next_tree_node); + bagsize =0; + } + }//end loop to build "pre"-bags + + //build TDTree (i.e. add parent, child lists from H, build bags from prebags) + //Node has "bag" list and "all_ind_set_values" list + //we will place a prebag in each list, the bag list will contain the prebag that we formed in the intermediate stage above + //the all_ind_set_values list will contain the prebag from the parent node + //in the bfs paper, these lists are joined to create the bag + //here we try to form a separator, and split a single D&G node into 2 nodes, so it will be useful to have the two lists in one node + //but stored separately for now + int num_prebags = treenodelist.size(); + this->num_tree_nodes = num_prebags; + + /// each node in original tree has index 3*n and a reserved node at +1 and -1 index in case of splitting + this->tree_nodes.resize(3*num_prebags,NULL); + // resize H to fit new nodes we will add to it + H.resize_graph(3*this->G->get_capacity()); + + // id2node_index maps id -> index in tree_node + 1 ( why +1? because if(0)=if(false) ) + // traversing tree from root to leafs, same as if(node previously reached) + // will need to use this to access neighbours' information. e.g. get neighbor from adj list, then get neighbor's index from id2node_index + // then can use index to look up neighbors bag + int sizeH = H.get_capacity(); + int *id2node_index; + id2node_index = new int[sizeH]; + std::fill(id2node_index, id2node_index + sizeH, 0); + + // root node + this->root_node = treenodelist.back()->id; + this->rooted = true; + this->tree_nodes[0] = treenodelist.back(); + //use all_ind_set_values to store 2nd list on each node + this->tree_nodes[0]->all_ind_set_values=this->tree_nodes[0]->bag; + this->width = this->tree_nodes[0]->bag.size(); + treenodelist.pop_back(); + this->tree_nodes[0]->adj = H.get_node(this->root_node)->get_nbrs(); // all nbrs are children + + id2node_index[this->root_node]=1;//may need to do +1 on indices so that root node isn't = 0 + + // # of bags in final tree will be > num_prebags, due to splitting + int split = 0; + int parent_index; + int current_index; + for(int prebagcounter = 1; prebagcounter < num_prebags; prebagcounter++) + { + current_index = 3*prebagcounter; + this->tree_nodes[current_index] = treenodelist.back();// add node, reverse order + id2node_index[this->tree_nodes[current_index]->id]=current_index+1; + treenodelist.pop_back();// delete from list + + //fill adjacency list, parent at front, children at back + n2 = H.get_node(this->tree_nodes[current_index]->id); + nbrs2 = n2->get_nbrs(); + + // make the adj list of the node + for( list::iterator temp_it = nbrs2.begin();temp_it!=nbrs2.end();temp_it++) + { + if(id2node_index[*temp_it]) //parent + { + this->tree_nodes[current_index]->adj.push_front(*temp_it); + //copy bag for children to have access to original + this->tree_nodes[current_index]->all_ind_set_values=this->tree_nodes[current_index]->bag; + } + else // children + { + this->tree_nodes[current_index]->adj.push_back(*temp_it); + } + } + + // should i move this to directly before a new node is created???, also + // create node at +1 index, later need to delete unused nodes? + this->tree_nodes[current_index+1] = new TDTreeNode; + this->tree_nodes[current_index+1]->bag = this->tree_nodes[current_index]->bag; + this->tree_nodes[current_index+1]->id = this->tree_nodes[current_index]->id + this->G->get_capacity(); + this->tree_nodes[current_index+2] = new TDTreeNode; + this->tree_nodes[current_index+2]->bag = this->tree_nodes[current_index]->bag; /// not sure if this is right + this->tree_nodes[current_index+2]->id = this->tree_nodes[current_index]->id + 2*this->G->get_capacity(); // may not be needed, hopefully not used + + parent_index = id2node_index[this->tree_nodes[current_index]->adj.front()]-1; // reverse lookup of nodes in original intermediate tree that have been dealt with + list parentbaglist = this->tree_nodes[parent_index]->all_ind_set_values; + list templist; //subset of parent bag list = intersection of parent prebag and adj list + + //temporary bool to switch between old splitting and new splitting + // eventually want to either delete 2nd separator, or chain them so that both are used + //bool separator = false; + int separator_switch = 0; // 0 = bfs only, no separator, 1 = metis separator, 2 = parent separator, 3 = parent then metis/// should have a switch to turn both off, to just use bfs + if(separator_switch==0) + { + templist = this->tree_nodes[current_index+1]->bag; + // construct bag + this->tree_nodes[current_index]->bag = templist; + // update tree's width + if( this->width < this->tree_nodes[current_index]->bag.size() ) + this->width = this->tree_nodes[current_index]->bag.size(); + } + else if(separator_switch==1) + { + templist = this->tree_nodes[current_index+1]->bag; + // parentbaglist = prebag from parent node, templist = prebag from current node + // in bfs algorithm the union of these prebags gives the bag for the current node + // we call a separator, resulting in 2 possible outcomes + // a) no separator formed, then top list or bottom list come back empty. don't split. non-empty list = bag + // (when does this happend? only for very small lists? in my tests it only happened for lists of size 3) + // b) separator formed, top, bottom returned. split into 2 nodes, + // top goes into the top node as its bag, bottom into the bottom node as its bag. + // metis_ConstructSeparator(VertexWeightedGraph, top list, bottom list) + // if no separator is constructed by METIS, this returns union of toplist and bottomlist, in bottomlist. toplist is returned empty + // input lists are no longer available after calling this function (copies are still in bag, and all_ind_set_values) +std::cout << "0, ConstructSeparator 1\n"; + util.metis_ConstructSeparator(G,&parentbaglist,&templist); + + if(parentbaglist.empty()) // i.e. ConstructSeparator did not return 2 lists, no splittting, create a single node + { +std::cout << "constructsep did nothing\n"; + // construct bag + this->tree_nodes[current_index]->bag = templist; + + // update tree's width + if( this->width < this->tree_nodes[current_index]->bag.size() ) + this->width = this->tree_nodes[current_index]->bag.size(); + } + else // create 2 nodes with bags already created in parentbaglist, templist... + { + int w = this->tree_nodes[current_index-1]->id; + //int w = this->tree_nodes[parent_index+1]->id; + list::iterator it; + + // original tree: parent-\-current, new tree: parent-\-(newnode id=w)-\-current + H.edge_subdivision(this->tree_nodes[current_index]->adj.front(),this->tree_nodes[current_index]->id, w);// order = parent, child, new node + + //fix the adj lists: new node, then parent, then current + //new node; current node's parent becomes parent of new node. current node becomes it's only child + this->tree_nodes[current_index-1]->adj.push_back(this->tree_nodes[current_index]->id); + this->tree_nodes[current_index-1]->adj.push_front(this->tree_nodes[current_index]->adj.front()); + + for(it = this->tree_nodes[parent_index]->adj.begin(); it != this->tree_nodes[ parent_index ]->adj.end(); it++) + //old parent's adj list, replace child with new node + { + if(*it == this->tree_nodes[current_index]->id) + { + *it = w; + break; + } + } + + //current node has a new parent + this->tree_nodes[current_index]->adj.pop_front(); + this->tree_nodes[current_index]->adj.push_front(w); + + // construct bag + // D&G's bfs alg created the bag L^k \union L^{k+1} + // old separator construction created 2 bags: P^k and P^k \union L^{k+1} + // the L^j are stored in tree_nodes[]->bag + // the P^j are given by templist + // (not parentbaglist which is a larger set of all elements in parent bag, not just the neighbouring elements) + // to create the nodes, apparently templist bag is already done, and just need a single line inserting templist into bag + // old code: + // this->tree_nodes[current_index]->bag.insert(this->tree_nodes[current_index]->bag.begin(),templist.begin(),templist.end()); + // in this new construction we want to creat 2 bags: toplist, bottomlist + // instead of inserting anything in bottom bag, need to replace the bag with bottomlist + // but also need to do this for top bag + + // recall: util.metis_ConstructSeparator(G,&parentbaglist,&templist); i.e. toplist = parentbaglist and bottomlist = templist + this->tree_nodes[current_index]->bag=templist; + this->tree_nodes[current_index-1]->bag=parentbaglist; + + + if(this->width < this->tree_nodes[current_index]->bag.size() ) + this->width = this->tree_nodes[current_index]->bag.size(); + + if(this->width < this->tree_nodes[current_index-1]->bag.size() ) + this->width = this->tree_nodes[current_index-1]->bag.size(); + + split++; + } + } + else if(separator_switch==2) + { + // this separator is based on taking a subset of parent bag, and is actually 2 steps in 1 + // separator step: bfs algorithm creates a bag from union of current and parent prebags + // but only direct parents \subset parent bag share edges to current bag + // direct parents are a separator -> create 2 bags, top bag has parent prebag, bottom has current prebag union direct parents + // but if multiple children of same parent do this, will end up with multiple copies of node with bag = parent prebag + // so we should merge all those identical nodes into a single node + // so what this code does is attach the new node to the parent (i.e. build it at parent_index+1, instead of current_index-1) + // that means we have to search for existence of this + // construct templist = subset of parentbaglist that has a neighbor in (pre)bag at current node + bool keepsearching = true; + for(it = parentbaglist.begin(); it!=parentbaglist.end();it++) + { + n2 = G->get_node(*it); /// go to original graph, get nbrs + nbrs2 = n2->get_nbrs(); + for(nbr_it2 = nbrs2.begin() ; keepsearching && nbr_it2 != nbrs2.end() ; nbr_it2++) + { + for ( nbr_it = this->tree_nodes[current_index]->bag.begin(); keepsearching && nbr_it != this->tree_nodes[current_index]->bag.end() ; nbr_it++) + { + if(*nbr_it2 == *nbr_it) + { + keepsearching = false; + templist.push_back(*it); + } + } + } + keepsearching = true;// reset to true + } + + if(templist.size() < parentbaglist.size()) // create two nodes + //if(false) + { + //recall that: parent_index = id2node_index[this->tree_nodes[current_index]->adj.front()]-1; + // w is id of potential node paired to parent + int w = this->tree_nodes[parent_index+1]->id; + list::iterator it; + // modify H graph, maybe + + // add node if it hasn't already been added + // if it already exists, change edge from parent to w + if(this->tree_nodes[parent_index+1]->adj.empty()) + { + H.edge_subdivision(this->tree_nodes[current_index]->adj.front(),this->tree_nodes[current_index]->id, w);// order = parent, child, new node + + //fix the adj lists + //new node + this->tree_nodes[parent_index+1]->adj.push_back(this->tree_nodes[current_index]->id); + this->tree_nodes[parent_index+1]->adj.push_front(this->tree_nodes[current_index]->adj.front()); + + for(it = this->tree_nodes[parent_index]->adj.begin(); it != this->tree_nodes[parent_index]->adj.end(); it++) + //old parent's adj list, replace child with new node + { + if(*it == this->tree_nodes[current_index]->id) + { + *it = w; + break; + } + } + } + else + { + // this block hasn't been tested + H.remove_edge(this->tree_nodes[current_index]->id,this->tree_nodes[current_index]->adj.front()); + H.add_edge(this->tree_nodes[current_index]->id,w); + + //new node, already connected to parent + this->tree_nodes[parent_index+1]->adj.push_back(this->tree_nodes[current_index]->id); + //old parent, already connected to new node, just delete child from adj list + for( it = this->tree_nodes[parent_index]->adj.begin(); it != this->tree_nodes[parent_index]->adj.end(); it++) + { + if(*it == this->tree_nodes[current_index]->id) + { + it = this->tree_nodes[parent_index]->adj.erase(it); + break; + } + } + } + + // construct bag + this->tree_nodes[current_index]->bag.insert(this->tree_nodes[current_index]->bag.begin(),templist.begin(),templist.end()); + + if(this->width < this->tree_nodes[current_index]->bag.size() ) + this->width = this->tree_nodes[current_index]->bag.size(); + + //child adj list + this->tree_nodes[current_index]->adj.pop_front(); + this->tree_nodes[current_index]->adj.push_front(w); + split++; + } + + else // creat a single node + { + // construct bag + this->tree_nodes[current_index]->bag.insert(this->tree_nodes[current_index]->bag.begin(),this->tree_nodes[parent_index]->all_ind_set_values.begin(),this->tree_nodes[parent_index]->all_ind_set_values.end()); + + if( this->width < this->tree_nodes[current_index]->bag.size() ) + this->width = this->tree_nodes[current_index]->bag.size(); + } + } + else if(separator_switch==3) + { + // combine both of the above separators. create parent separator first, then call metis + // put new bag from parent separator in parent_index+1, new bag from metis separator in current_index-1 + + bool keepsearching = true; + for(it = parentbaglist.begin(); it!=parentbaglist.end();it++) + { + n2 = G->get_node(*it); /// go to original graph, get nbrs + nbrs2 = n2->get_nbrs(); + for(nbr_it2 = nbrs2.begin() ; keepsearching && nbr_it2 != nbrs2.end() ; nbr_it2++) + { + for ( nbr_it = this->tree_nodes[current_index]->bag.begin(); keepsearching && nbr_it != this->tree_nodes[current_index]->bag.end() ; nbr_it++) + { + if(*nbr_it2 == *nbr_it) + { + keepsearching = false; + templist.push_back(*it); + } + } + } + keepsearching = true;// reset to true + } + + if(templist.size() < parentbaglist.size()) // create two nodes plus whatever metis dictates + { + // w is id of potential node paired to parent + int w = this->tree_nodes[parent_index+1]->id;/// this node already exists, but should we in fact only be creating it here?... if yes, then we need to test if it exists because multiple nodes could have the same parent, and the same parent+1 + list::iterator it; + // modify H graph, maybe + + // add node if it hasn't already been added + // if it already exists, change edge from parent to w + if(this->tree_nodes[parent_index+1]->adj.empty()) + { + H.edge_subdivision(this->tree_nodes[current_index]->adj.front(),this->tree_nodes[current_index]->id, w);// order = parent, child, new node + + //fix the adj lists + //new node + this->tree_nodes[parent_index+1]->adj.push_back(this->tree_nodes[current_index]->id); + this->tree_nodes[parent_index+1]->adj.push_front(this->tree_nodes[current_index]->adj.front()); + + for(it = this->tree_nodes[parent_index]->adj.begin(); it != this->tree_nodes[parent_index]->adj.end(); it++ ) + { + //old parent's adj list, replace child with new node + if(*it == this->tree_nodes[current_index]->id) + { + *it = w; + break; + } + } + split++; + } + else + { + // this block hasnt been tested +std::cout << "flag 2\n"; + H.remove_edge(this->tree_nodes[current_index]->id,this->tree_nodes[current_index]->adj.front()); + H.add_edge(this->tree_nodes[current_index]->id,w); + + //new node, already connected to parent + this->tree_nodes[parent_index+1]->adj.push_back(this->tree_nodes[current_index]->id); + //old parent, already connected to new node, just delete child from adj list + for( it = this->tree_nodes[parent_index]->adj.begin(); it != this->tree_nodes[parent_index]->adj.end(); it++) + { + if(*it == this->tree_nodes[current_index]->id) + { + it = this->tree_nodes[parent_index]->adj.erase(it); + break; + } + } + } + + //child adj list + this->tree_nodes[current_index]->adj.pop_front(); + this->tree_nodes[current_index]->adj.push_front(w); + + // pass the templist and bag lists to metis_ConstructSeparator + list bottomlist = this->tree_nodes[current_index]->bag; // possibly this is not needed. just use tree_nodes[index]->bag +std::cout << "2, ConstructSeparator 1: both separators, potentially\n"; + util.metis_ConstructSeparator(G,&templist,&bottomlist); + + if(templist.empty()) // i.e. ConstructSeparator did not return 2 lists, no splittting, create a single node + { +std::cout << "constructsep did nothing\n"; + this->tree_nodes[current_index]->bag = bottomlist; //if we don't use bottomlist, can we avoid this line? + + if( this->width < this->tree_nodes[current_index]->bag.size() ) + this->width = this->tree_nodes[current_index]->bag.size(); + } + else // create 2 nodes with 2 lists from ConstructSeparator + { + int w2 = this->tree_nodes[current_index-1]->id; + list::iterator it; + + // original tree: parent-\-subparent-\-current, new tree: parent-\-subparent-\-(newnode id=w)-\-current + H.edge_subdivision(this->tree_nodes[current_index]->adj.front(),this->tree_nodes[current_index]->id, w2);// order = parent, child, new node + + //fix the adj lists: new node, then (sub)parent, then current + //new node; current node's parent becomes parent of new node. current node becomes it's only child + this->tree_nodes[current_index-1]->adj.push_back(this->tree_nodes[current_index]->id); + this->tree_nodes[current_index-1]->adj.push_front(this->tree_nodes[current_index]->adj.front()); + + for(it = this->tree_nodes[parent_index+1]->adj.begin(); it != this->tree_nodes[ parent_index+1 ]->adj.end(); it++) + //old parent's adj list, replace child with new node + { + if(*it == this->tree_nodes[current_index]->id) + { + *it = w2; + break; + } + } + + //current node has a new parent + this->tree_nodes[current_index]->adj.pop_front(); + this->tree_nodes[current_index]->adj.push_front(w2); + + // recall: util.metis_ConstructSeparator(G,&parentbaglist,&templist); i.e. toplist = parentbaglist and bottomlist = templist + // actually, in this block of code toplist = templist, bottomlist=bottomlist + this->tree_nodes[current_index]->bag=bottomlist; + this->tree_nodes[current_index-1]->bag=templist; + + //no need to test parent_index+1 since it has smaller width than parent_index + if(this->width < this->tree_nodes[current_index]->bag.size() ) + this->width = this->tree_nodes[current_index]->bag.size(); + + if(this->width < this->tree_nodes[current_index-1]->bag.size() ) + this->width = this->tree_nodes[current_index-1]->bag.size(); + + split++; + } + } + else // creat a single node plus whatever comes out of metis + { + // construct bag /// all of this code is reused from above. maybe move this out into a separate function: metis_sep_node_construct()... + list bottomlist = this->tree_nodes[current_index]->bag; // possibly this is not needed. just use tree_nodes[index]->bag + list toplist = this->tree_nodes[parent_index]->all_ind_set_values; // possibly this is not needed. just use tree_nodes[parent_index]->all_ind_set_values +std::cout << "2, ConstructSeparator 2: at most, METIS separator\n"; + util.metis_ConstructSeparator(G,&toplist,&bottomlist); + + if(toplist.empty()) // i.e. ConstructSeparator did not return 2 lists, no splittting, create a single node + { +std::cout << "constructsep did nothing\n"; + this->tree_nodes[current_index]->bag = bottomlist; //if we don't use bottomlist, can we avoid this line? + + if( this->width < this->tree_nodes[current_index]->bag.size() ) + this->width = this->tree_nodes[current_index]->bag.size(); + } + else // create 2 nodes with 2 lists from ConstructSeparator + { + int w2 = this->tree_nodes[current_index-1]->id; + list::iterator it; + + // original tree: parent-\-subparent-\-current, new tree: parent-\-subparent-\-(newnode id=w)-\-current + H.edge_subdivision(this->tree_nodes[current_index]->adj.front(),this->tree_nodes[current_index]->id, w2);// order = parent, child, new node + + //fix the adj lists: new node, then (sub)parent, then current + //new node; current node's parent becomes parent of new node. current node becomes it's only child + this->tree_nodes[current_index-1]->adj.push_back(this->tree_nodes[current_index]->id); + this->tree_nodes[current_index-1]->adj.push_front(this->tree_nodes[current_index]->adj.front()); + + // parent_index + 1 not used here, only splitting is from metis separator + for(it = this->tree_nodes[parent_index]->adj.begin(); it != this->tree_nodes[ parent_index ]->adj.end(); it++ ) + //old parent's adj list, replace child with new node + { + if(*it == this->tree_nodes[current_index]->id) + { + *it = w2; + break; + } + } + + //current node has a new parent + this->tree_nodes[current_index]->adj.pop_front(); + this->tree_nodes[current_index]->adj.push_front(w2); + + // recall: util.metis_ConstructSeparator(G,&parentbaglist,&templist); i.e. toplist = parentbaglist and bottomlist = templist + this->tree_nodes[current_index]->bag=bottomlist; + this->tree_nodes[current_index-1]->bag=toplist; + + //no need to test parent_index+1 since it has smaller width than parent_index + if(this->width < this->tree_nodes[current_index]->bag.size() ) + this->width = this->tree_nodes[current_index]->bag.size(); + + if(this->width < this->tree_nodes[current_index-1]->bag.size() ) + this->width = this->tree_nodes[current_index-1]->bag.size(); + + split++; + } + } + } + } + + ////// DAG: do we want to remap tree's id values, and G's label values so they go from 1 to |T| + ////// instead of taking values of lowest index in each bag? + ////// when we take union of bags, that feature will no longer hold + ////// answer: yes, do this + + //the correct solution might be to resize the vector and eliminate both NULL and unused nodes + //old2new_tree_index maps the tree index in the larger vector into the index in the smaller vector + //label2index maps the label (which is currently an element of the graph) into the old index + //int *old2new_tree_index; + //old2new_tree_index = new int[sizeH]; + //memset(old2new_tree_index,-1,sizeH*sizeof(int)); + int *label2index; + label2index = new int[sizeH]; + //memset(label2index,-1,sizeH*sizeof(int)); + std::fill(label2index, label2index+sizeH, -1); + int nodecount=0; + + // 2 possibilities: new vector of smaller size with no NULL nodes + // or leave NULLs, and just fix adj lists + + for(int k = 0; k < 3*num_prebags; k++) + { + if(this->tree_nodes[k]!=NULL) + { + if(this->tree_nodes[k]->adj.empty()) + { + this->tree_nodes[k]=NULL; + } + else + { + //old2new_tree_index[k] = nodecount; + label2index[this->tree_nodes[k]->id]=k; + //nodecount++; + } + } + } + for(int k = 0; k < 3*num_prebags; k++) + { + if(this->tree_nodes[k]!=NULL) + { + for(it = this->tree_nodes[k]->adj.begin();it!=this->tree_nodes[k]->adj.end();it++) + { + *it = label2index[*it]; + } + } + } + /*for(int k = 0; k < 3*num_prebags; k++) + { + std::cout << "k= " << k << " old2new_tree_index[k] = " << old2new_tree_index[k] << " id of tree node if it exists = "; +if(old2new_tree_index[k] != -1) +std::cout << this->tree_nodes[k]->id << std::endl; +else std::cout << std::endl; + }*/ + delete [] id2node_index; + //delete [] old2new_tree_index; + delete [] label2index; + return; +} + + + + /** * Constructs a tree decomposition of the graph in the current TDTree using * Bodlaender-Koster's algorithm 2 from Treewidth Computations I, Upper Bounds.