Biorithm  1.1
 All Classes Functions Variables Typedefs Friends
treedex.h
00001 /**************************************************************************
00002  * Copyright (c) 2002-2011 T. M. Murali                                   *
00003  * Copyright (c) 2007-2011 Naveed Massjouni                               *
00004  *                                                                        *
00005  * This file is part of Biorithm.                                         *
00006  *                                                                        *
00007  * Biorithm is free software: you can redistribute it and/or modify       *
00008  * it under the terms of the GNU General Public License as published by   *
00009  * the Free Software Foundation, either version 3 of the License, or      *
00010  * (at your option) any later version.                                    *
00011  *                                                                        *
00012  * Biorithm is distributed in the hope that it will be useful,            *
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of         *
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
00015  * GNU General Public License for more details.                           *
00016  *                                                                        *
00017  * You should have received a copy of the GNU General Public License      *
00018  * along with Biorithm.  If not, see <http://www.gnu.org/licenses/>.      *
00019  *                                                                        *
00020  **************************************************************************/
00021 
00028 #ifndef TREEDECOMPOSITION_H
00029 #define TREEDECOMPOSITION_H
00030 
00031 #include <iostream>
00032 #include <string>
00033 #include <list>
00034 #include <vector>
00035 #include <set>
00036 #include <sstream>
00037 #include <stack>
00038 #include "graph.h"
00039 #include "treewidth.h"
00040 using namespace  std;
00041 
00042 typedef MyNode Node;
00043 typedef MyEdge Edge;
00044 typedef MyNode::EdgeIterator EdgeIterator;
00045 typedef MyGraph Graph;
00046 typedef MyGraph::NodeIterator NodeIterator;
00047 typedef string NodeId;
00048 typedef string BagId;
00049 
00058 class TreeDecomposition {
00059 
00060 private:
00061   map< BagId, set<NodeId> > bagMap; // maps bags to set of nodes they contain
00062   map< BagId, set<NodeId> > blackNodesMap; 
00063   Graph _tree; // underlying graph representing the tree structure
00064   const Graph _origGraph; // original graph to be decomposed
00065   vector<BagId> bagOrder; // the order the bags are added
00066   vector<NodeId> nodeOrder; // the order the nodes are processed
00067   int bagCount;
00068   int nodeCount;
00069 
00070   vector<NodeId> getNodes(Graph graph) {
00071     vector<NodeId> nodes;
00072     NodeIterator nodesItr = graph.nodes();
00073     while (nodesItr.hasNext()) {
00074       Node node = nodesItr.next();
00075       nodes.push_back(node.getId());
00076     }
00077     return nodes;
00078   }
00079 
00080   vector<Edge> getEdges(Graph& graph) {
00081     vector<Edge> edges;
00082     Graph::MyEdgeIterator edgeItr = graph.edges();
00083     while (edgeItr.hasNext()) {
00084       Edge edge = edgeItr.next();
00085       edges.push_back(edge);
00086     }
00087     return edges;
00088   }
00089 
00090   vector<NodeId> getNeighbors(Graph& graph, NodeId nodeId) {
00091     vector<NodeId> neighbors;
00092     EdgeIterator edgeItr = graph.incidentEdges(nodeId);
00093     while (edgeItr.hasNext()) {
00094       const Edge& edge = *edgeItr.next();
00095       Node* nodePtr = edge.opposite(nodeId);
00096       neighbors.push_back(nodePtr->getId());
00097     }
00098     return neighbors;
00099   }
00100 
00101   set<NodeId> getNeighborSet(Graph& graph, NodeId nodeId) {
00102     cout << endl << "getNeighborSet called: ";
00103     set<NodeId> result;
00104     vector<NodeId> neighbors = getNeighbors(graph, nodeId);
00105     for (int i = 0; i < neighbors.size(); i++) {
00106       result.insert(neighbors[i]);
00107     }
00108     cout << result << endl;
00109     return result;
00110   }
00111 
00112   // Connect the neighbors of a node to each other.
00113   void connectNeighbors(Graph& graph, NodeId nodeId) {
00114     vector<NodeId> neighbors = getNeighbors(graph, nodeId);
00115     for (int i = 0; i < neighbors.size(); i++) {
00116       for (int j = i+1; j < neighbors.size(); j++) {
00117         graph.addEdge(neighbors[i], neighbors[j]);
00118       }
00119     }
00120   }
00121 
00122   // Determine if graph is a tree.
00123   bool isTree(Graph& graph) {
00124     if (graph.numNodes() != graph.numEdges() + 1)
00125       return false;
00126     Treewidth treewidth(graph);
00127     int prevSize = 0;
00128     while (treewidth.numNodes() != prevSize) {
00129       prevSize = treewidth.numNodes();
00130       treewidth.runRule(Treewidth::TWIG_RULE);
00131     }
00132     return treewidth.numNodes() == 1;
00133   }
00134 
00135   // Determine if graph is a clique.
00136   bool isClique(Graph& graph) {
00137     unsigned int numNodes = graph.numNodes();
00138     unsigned int numEdges = graph.numEdges();
00139     if (numNodes*(numNodes - 1)/2 == numEdges)
00140       return true;
00141     return(false);
00142   }
00143 
00144   // Find a bag in bagOrder with the minimum index
00145   // that contains every node in neighborSet.
00146   BagId findBagWithNeighbors(set<NodeId> neighborSet, int minIndex) {
00147     BagId bagId;
00148     set<BagId> bagSet;
00149     vector<BagId> intersection(neighborSet.size());
00150     vector<BagId>::iterator endOfIntersection;
00151     for (int i = minIndex; i < bagOrder.size(); i++) {
00152       bagId = bagOrder[i];
00153       set<BagId> bagSet = bagMap[bagId];
00154       endOfIntersection = set_intersection(
00155         neighborSet.begin(), neighborSet.end(),
00156         bagSet.begin(), bagSet.end(), intersection.begin());
00157       int intersectionSize = endOfIntersection - intersection.begin();
00158       if (intersectionSize == neighborSet.size())
00159         return bagId;
00160     }
00161     return "";
00162   }
00163 
00165   BagId addBag(const set<NodeId>& nodeSet) {
00166       ostringstream oss;
00167       oss << "bag" << (1000000 + bagCount);
00168       BagId bagId = oss.str();
00169       _tree.addNode(bagId);
00170       bagMap[bagId] = nodeSet;
00171       bagCount++;
00172       updateBlackNodes(bagId, nodeSet);
00173       return bagId;
00174   }
00175 
00179   void updateBlackNodes(BagId bagId, const set<NodeId>& nodeSet) {
00180     Graph induced;
00181     _origGraph.computeInducedSubgraph(nodeSet, induced);
00182 
00183     NodeIterator itr = induced.nodes();
00184     while (itr.hasNext()) {
00185       Node node = itr.next();
00186       if (node.degree() == 0)
00187         blackNodesMap[bagId].insert(node.getId());
00188     }
00189   }
00190 
00191 public:
00192   TreeDecomposition()
00193     : bagCount(0), nodeCount(0) {}
00194 
00195   TreeDecomposition(Graph graph)
00196     : bagCount(0), nodeCount(0), _origGraph(graph) {}
00197 
00198   void printBags() {
00199     for (map< BagId, set<NodeId> >::iterator itr = bagMap.begin();
00200          itr != bagMap.end(); itr++) {
00201       //cout << itr->first << "|" << itr->second << endl;
00202       BagId bagId = itr->first;
00203       cout << bagId << "|";
00204       set<NodeId> nodes = itr->second;
00205       for (set<NodeId>::iterator nodeItr = nodes.begin();
00206            nodeItr != nodes.end(); nodeItr++) {
00207         NodeId nodeId = *nodeItr;
00208         cout << nodeId;
00209         set<NodeId> blackNodes = blackNodesMap[bagId];
00210         if (blackNodes.find(nodeId) != blackNodes.end())
00211           cout << "*"; // is black node, so mark with *
00212         cout << ' ';
00213       }
00214       cout << endl;
00215     }
00216   }
00217 
00218   int getMaxBagSize() {
00219     int max = 0;
00220     map< BagId, set<NodeId> >::iterator itr = bagMap.begin();
00221     while (itr != bagMap.end()) {
00222       if (itr->second.size() > max)
00223         max = itr->second.size();
00224       itr++;
00225     }
00226     return max;
00227   }
00228 
00230   BagId getMaxBagId() {
00231     BagId maxBagId = "undefined";
00232     int max = 0;
00233     map< BagId, set<NodeId> >::iterator itr = bagMap.begin();
00234     while (itr != bagMap.end()) {
00235       if (itr->second.size() > max) {
00236         max = itr->second.size();
00237         maxBagId = itr->first;
00238       }
00239       itr++;
00240     }
00241     return maxBagId;
00242   }
00243 
00244   set<NodeId> getBlackNodesForBagId(BagId bagId) {
00245     return blackNodesMap[bagId];
00246   }
00247 
00255   class TreeWidthNodeSelector
00256   {
00257   protected:
00258     // the graph the node selector will operate on. it is a reference
00259     // since the TreeDecomposition class will also operate on the same instance.
00260     MyGraph &_graph;
00261   public:
00262     TreeWidthNodeSelector(MyGraph & g)
00263         : _graph(g)
00264       {}
00265 
00266     virtual ~TreeWidthNodeSelector()
00267       {}
00268 
00270     virtual void initialise() = 0;
00271 
00273     virtual MyNodeId getNextNode() = 0;
00274     
00278     virtual void update(MyNodeId deletedNodeId, set< MyNodeId > &neighbours)=0;
00279     
00280   };
00281 
00287   class TreeWidthNodeSelectorMinDegree : public TreeWidthNodeSelector
00288   {
00289   protected:
00290     // the current minimum degree.
00291     unsigned int _minDegree;
00292     
00293     // store nodes in order of degree. for each possible degree (the
00294     // index of the vector, store a list of nodes with that degree.
00295     vector< list< MyNodeId > > _degreeLists;
00296     // for each node, store a pointer to the list where it is stored.
00297     // this map will be useful for efficiently moving nodes from one
00298     // list to another.
00299     map< MyNodeId, list< MyNodeId >::iterator > _nodePositions;
00300   public:
00301     TreeWidthNodeSelectorMinDegree(MyGraph & g)
00302         : TreeWidthNodeSelector(g), _minDegree(0),
00303           _degreeLists(_graph.numNodes() + 1),
00304           _nodePositions()
00305       {}
00306 
00307     virtual ~TreeWidthNodeSelectorMinDegree()
00308       {}
00309 
00311     //virtual void initialise();
00312     virtual void initialise() {
00313       _graph._createDegreeLists(_minDegree, _degreeLists, _nodePositions);
00314     }
00315     
00317     virtual MyNodeId getNextNode() {
00318       static unsigned int numNodes = 0;
00319       // remove the node with smallest degree. get the element at the
00320       // front of the right list and also pop it.
00321       string nodeId = _degreeLists[_minDegree].front();
00322       _degreeLists[_minDegree].pop_front();
00323       cout << ++numNodes << " Returning node " << nodeId << " with degree "
00324            << _minDegree << ". ";
00325       return(nodeId);
00326     }
00327 
00328 
00333     virtual void  update(MyNodeId deletedNodeId, set<NodeId> &neighbours) {
00334       // decrement the degree of all its neighbours.
00335       _graph._decrementDegrees(_degreeLists, _nodePositions, neighbours);
00336       // update minDegree. it either decreases by 1, stays the same,
00337       // or increases.
00338       if ((0 != _minDegree) && (0 != _degreeLists[_minDegree - 1].size()))
00339         _minDegree--;
00340       else if (0 != _degreeLists[_minDegree].size()) {} // do nothing.
00341       else while ((_minDegree <= _graph.numNodes()) &&
00342                   (0 == _degreeLists[++_minDegree].size()));
00343     }
00344 
00345   };
00346 
00347   struct MyNodeFillInRatioPQNode
00348   {
00349     MyNT _fillInRatio;
00350     unsigned int _fillIn;
00351     MyNodeId _id;
00352   };
00353 
00354   // function object for comparing two node PQ nodes. i want nodes with
00355   // smaller weight at top, so the comparison is not < but >
00356   // (priority_queue::top() returns the max element.)
00357   struct compareFillInValues : public
00358     binary_function<
00359       const MyNodeFillInRatioPQNode&, const MyNodeFillInRatioPQNode&, bool> 
00360   {
00361     bool operator()(
00362         const MyNodeFillInRatioPQNode& __x,
00363         const MyNodeFillInRatioPQNode& __y) const
00364     {
00365       return(__x._fillIn > __y._fillIn);
00366     }
00367   };
00368 
00376   class TreeWidthNodeSelectorMinFillIn : public TreeWidthNodeSelector
00377   {
00378   protected:
00379     typedef priority_queue< MyNodeFillInRatioPQNode,
00380                             vector< MyNodeFillInRatioPQNode >,
00381                             compareFillInValues >
00382     MyFillInPQ;
00383     MyFillInPQ _npq;
00384 
00385     vector<NodeId> getNeighbors(Graph& graph, NodeId nodeId) {
00386       vector<NodeId> neighbors;
00387       EdgeIterator edgeItr = graph.incidentEdges(nodeId);
00388       while (edgeItr.hasNext()) {
00389         const Edge& edge = *edgeItr.next();
00390         Node* nodePtr = edge.opposite(nodeId);
00391         neighbors.push_back(nodePtr->getId());
00392       }
00393       return neighbors;
00394     }
00395 
00396     // Compute the number of pairs of neighbors of a node that are not connected
00397     unsigned int computeFillIn(Graph& graph, NodeId nodeId) {
00398       vector<NodeId> neighbors = getNeighbors(graph, nodeId);
00399             unsigned int fillIn = 0;
00400       for (int i = 0; i < neighbors.size(); i++) {
00401         for (int j = i+1; j < neighbors.size(); j++) {
00402                       if (!graph.isEdge(neighbors[i], neighbors[j]))
00403                         fillIn++;
00404                         }
00405       }
00406             return(fillIn);
00407     }
00408 
00409   public:
00410     TreeWidthNodeSelectorMinFillIn(MyGraph & g)
00411         : TreeWidthNodeSelector(g), _npq() {}
00412 
00413     virtual ~TreeWidthNodeSelectorMinFillIn() {}
00414 
00416     virtual void initialise() {
00417       MyGraph::NodeIterator nitr = _graph.nodes();
00418       MyNodeFillInRatioPQNode npqNode;
00419       MyNT quotient;
00420       unsigned int degree;
00421       while (nitr.hasNext())
00422         {
00423           MyNode &node = nitr.next();
00424           npqNode._id = node.getId();
00425           npqNode._fillIn = computeFillIn(_graph, npqNode._id);
00426           degree = node.degree();
00427           quotient = degree*(degree - 1)/2.0;
00428           if (0 == quotient)
00429             npqNode._fillInRatio = 0;
00430           else
00431             npqNode._fillInRatio = npqNode._fillIn/quotient;
00432           _npq.push(npqNode);
00433         }
00434     }
00435     
00437     virtual MyNodeId getNextNode() {
00438       static unsigned int numNodes = 0;
00439       MyNodeFillInRatioPQNode pqNode;
00440       do
00441         {
00442           pqNode = _npq.top();
00443           _npq.pop();
00444         }
00445       // the pq may have incorrect and old info for nodes i have already
00446       // processed, so i keep popping nodes until i find a node in _graph.
00447       while (!_graph.isNode(pqNode._id));
00448       cout << ++numNodes << " Returning node " << pqNode._id
00449            << " with fill in " << pqNode._fillIn
00450            << " and fill in ratio " << pqNode._fillInRatio << ". ";
00451 
00452       return(pqNode._id);
00453     }
00454 
00459     virtual void  update(MyNodeId deletedNodeId, set<NodeId> &neighbours) {
00460       set<NodeId>::const_iterator nitr;
00461       MyNodeFillInRatioPQNode npqNode;
00462       unsigned int degree;
00463       MyNT quotient;
00464       for (nitr = neighbours.begin(); nitr != neighbours.end(); nitr++)
00465         {
00466           npqNode._id = *nitr;
00467           npqNode._fillIn = computeFillIn(_graph, npqNode._id);
00468           degree = _graph._getNode(npqNode._id).degree();
00469           quotient = degree*(degree - 1)/2.0;
00470           if (0 == quotient)
00471             npqNode._fillInRatio = 0;
00472           else
00473             npqNode._fillInRatio = npqNode._fillIn/quotient;
00474           _npq.push(npqNode);
00475         }
00476     }
00477 
00478   };
00479 
00480   // function object for comparing two edge PQ nodes for computing
00481   // minimum spanning trees. i want nodes with smaller weight at top, so
00482   // the comparison is not < but > (priority_queue::top() returns the
00483   // max element.) 
00484   struct compareFillInRatioValues
00485     : public binary_function< const MyNodeFillInRatioPQNode&,
00486                               const MyNodeFillInRatioPQNode&, bool> 
00487   {
00488     bool operator()(const MyNodeFillInRatioPQNode& __x,
00489                     const MyNodeFillInRatioPQNode& __y) const
00490     {
00491       return(__x._fillInRatio > __y._fillInRatio);
00492     }
00493   };
00494 
00501   class TreeWidthNodeSelectorMinFillInRatio
00502     : public TreeWidthNodeSelectorMinFillIn
00503   {
00504   protected:
00505     // the only change between TreeWidthNodeSelectorMinFillInRatio and
00506     // TreeWidthNodeSelectorMinFillIn is the compare function in the pq.
00507     typedef priority_queue<
00508       MyNodeFillInRatioPQNode,
00509       vector< MyNodeFillInRatioPQNode >,
00510       compareFillInRatioValues > MyFillInRatioPQ;
00511 
00512     MyFillInRatioPQ _npq;
00513   public:
00514     TreeWidthNodeSelectorMinFillInRatio(MyGraph & g)
00515         : TreeWidthNodeSelectorMinFillIn(g), _npq()
00516       {}
00517 
00518     virtual ~TreeWidthNodeSelectorMinFillInRatio()
00519       {}
00520   };
00521 
00522   // making pq tree efficient (avoid the "if (!graph.isNode(nodeId))"
00523   // check). store MyNodeFillInPQNodes in a two sets, one sorted by _id
00524   // and the other by _fillIn. pop from the _fillIn map. pop same _id
00525   // from the _id map. create MyNodeFillInPQNodes for all the
00526   // neighbours. for each neighbour, find the entry in the _id map,
00527   // update the entry, find the entry in the _fillIn map, delete the
00528   // entry, and insert the new entry.
00529 
00530   // Recursively find tree decomposition.
00531   void decompose() {
00532     Graph graph = _origGraph;
00533 
00534     unsigned int maxBagSize  = 0;
00535     MyNodeId id, nodeId;
00536     
00537     //TreeWidthNodeSelectorMinFillInRatio twns(graph);
00538     //TreeWidthNodeSelectorMinFillIn twns(graph);
00539     TreeWidthNodeSelectorMinDegree twns(graph);
00540     twns.initialise();
00541     
00542     while (!isTree(graph) && !isClique(graph)) {
00543       nodeId = twns.getNextNode();
00544       nodeOrder.push_back(nodeId);
00545 
00546 #ifdef DEBUG
00547       // we may have already processed this node.
00548       if (!graph.isNode(nodeId)) {
00549         cerr << "Node id " << nodeId
00550           << " is not a graph node but the node selector returned it."
00551           << endl;
00552         continue;
00553       }
00554 #endif      
00555       set<NodeId> bagNodes = getNeighborSet(graph, nodeId);
00556       connectNeighbors(graph, nodeId);
00557       graph.deleteNode(nodeId);
00558       
00559 //     cout << ++nodeCount << " Deleted node " << nodeId << " with "
00560 //          << bagNodes.size() << " neighbors" << endl;
00561 
00562       twns.update(nodeId, bagNodes);
00563 
00564       bagNodes.insert(nodeId);
00565       BagId newBagId = addBag(bagNodes);
00566       cout << "adding bag: " << bagNodes << endl;
00567       bagOrder.push_back(newBagId);
00568       if (bagNodes.size() > maxBagSize)
00569         maxBagSize = bagNodes.size();
00570       cout << " Graph size is (" << graph.numNodes()
00571            << ", " << graph.numEdges() << "). Treewidth is "
00572            << maxBagSize << "." << endl;
00573     }
00574 
00575     cout << "Base Case (tree/clique): " << endl;
00576     //graph.printEdges(cout);
00577     set<NodeId> nodeSet;
00578     graph.getNodeSet(nodeSet);
00579     BagId newBagId = addBag(nodeSet);
00580     bagOrder.push_back(newBagId);
00581 
00582     // Note that bagOrder should have 1 more element than nodeOrder.
00583     // Process the bags in the reverse order that they were added.
00584     // For each bag b_i, there is an associated node n_i,
00585     // except for the last bag that was added.
00586     // For a given bag b_i, consider only bags b_j: j > i.
00587     // Add an edge from a bag b1 in b_i to a bag b2 in b_j
00588     // s.t. (b1 - n1) is a subset of b2.
00589     cout << "Going to add edges" << endl;
00590     for (int i = nodeOrder.size()-1; i >= 0; i--) {
00591       cout << ".";
00592       set<NodeId> naybears = bagMap[bagOrder[i]];
00593       naybears.erase(nodeOrder[i]);
00594       BagId bagToConnect = findBagWithNeighbors(naybears, i+1);
00595       _tree.addEdge(bagOrder[i], bagToConnect);
00596     }
00597     cout << endl;
00598 
00599   }
00600 
00601   void computeInducedSubtree(const NodeId nodeId, TreeDecomposition& subtree) {
00602     set<NodeId> bag;
00603     bag.insert(nodeId);
00604     set<BagId> bagsWithNode;
00605 
00606     NodeIterator bagItr = _tree.nodes();
00607     while (bagItr.hasNext()) {
00608       BagId bagId = bagItr.next().getId();
00609       set<NodeId> nodeSet = bagMap[bagId];
00610       if (nodeSet.find(nodeId) != nodeSet.end()) {
00611         cout << "found bag: " << bagId << endl;
00612         bagsWithNode.insert(bagId);
00613         subtree.bagMap[bagId] = bag;
00614       }
00615     }
00616 
00617     _tree.computeInducedSubgraph(bagsWithNode, subtree._tree);
00618   }
00619 
00620 };
00621 
00622 #endif
 All Classes Functions Variables Typedefs Friends