Biorithm  1.1
 All Classes Functions Variables Typedefs Friends
dag.h
00001 /**************************************************************************
00002  * Copyright (c) 2004-2011 T. M. Murali                                   *
00003  * Copyright (c) 2004 Greg Grothaus                                       *
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 _DAG_H
00029 #define _DAG_H
00030 
00031 #include<iostream>
00032 #include<map>
00033 #include<set>
00034 #include<vector>
00035 #include<iterator>
00036 #include<set>
00037 #include<string>
00038 #include<sstream>
00039 #include "setTemplates.C"
00040 #include <queue>
00041 
00042 using namespace std;
00043 
00044 // forward declaration.
00045 template< typename NodeType > class DAG;
00046 
00047 
00052 template< typename NodeInfo > class DAGNode
00053 {
00054   friend class DAG< DAGNode < NodeInfo > >;
00055 
00056 private:
00057   NodeInfo _nodeInfo;
00058 
00059 protected:
00060   set< DAGNode< NodeInfo > *> parents[3],ancestors[3];  //0-all, 1-is_a, 2-part_of      
00061   set< DAGNode< NodeInfo > *> children[3], descendants[3];      //0-all, 1-is_a, 2-part_of      
00062 
00063 public:
00065   DAGNode()
00066       : _nodeInfo(), parents(), ancestors(), children(), descendants()
00067     {}
00068   DAGNode(const NodeInfo &info)
00069       : _nodeInfo(info), parents(), ancestors(), children(), descendants()
00070     {}
00071 
00077   DAGNode(const DAGNode& rhs)
00078       : _nodeInfo(rhs._nodeInfo), parents(), ancestors(), children(), descendants()
00079     {}
00080 
00081 
00082   virtual ~DAGNode()
00083     {}
00084 
00085   virtual void addChild(DAGNode< NodeInfo > *child, unsigned int type = 0)
00086     {
00087 #ifdef DEBUG
00088 //        cout << "DAGNode::addChild(): node is " << _nodeInfo.getId()
00089 //             << ". Child is " << child->_nodeInfo.getId()
00090 //             << " Edge type is " << type
00091 //             << endl;
00092 #endif
00093       // check for loops.
00094       if (isParent(child, type))
00095         cerr << "DAG::addChild(): adding child " << child->_nodeInfo.getId()
00096              << " for node " << _nodeInfo.getId() << " but child is already a parent!"
00097              << endl;
00098       children[type].insert(child);
00099       // also insert child in type 0 (which stores all children).
00100       children[0].insert(child);
00101       
00102     }
00103   
00104   virtual void addParent(DAGNode< NodeInfo > *parent, unsigned int type = 0)
00105     {
00106 #ifdef DEBUG
00107 //       cout << "DAGNode::addParent(): node is " << _nodeInfo.getId()
00108 //            << ". Parent is " << parent->_nodeInfo.getId() << endl;
00109 #endif      
00110       // check for loops.
00111       if (isChild(parent, type))
00112         cerr << "DAG::addParent(): adding parent " << parent->_nodeInfo.getId()
00113              << " for node " << _nodeInfo.getId() << " but parent is already a child!"
00114              << endl;
00115       parents[type].insert(parent);
00116       // also insert parent in type 0 (which stores all parents).
00117       parents[0].insert(parent);
00118     }
00119   
00120   virtual set< DAGNode< NodeInfo > *> getParents(unsigned int type = 0)
00121     {
00122       return(parents[type]);
00123     }
00124 
00128   virtual unsigned int getParentEdgeType(DAGNode< NodeInfo > *parent) 
00129     {
00130       for (unsigned int i = 1; i < 3; i++)
00131         if (isParent(parent, i))
00132           return(i);
00133       // i do not know any type for this parent.
00134       cerr << "DAG::getParentEdgeType(): No edge type known for node " << _nodeInfo.getId()
00135            << " and parent " << parent->_nodeInfo.getId() << endl;
00136       return(0);
00137     }
00138   
00139 //  virtual
00140   set< DAGNode< NodeInfo > *> getChildren(unsigned int type = 0)
00141     {
00142       return(children[type]);
00143     }
00144   
00145   virtual set< DAGNode< NodeInfo > *> getAncestors(int type = 0)
00146     {
00147       
00148       //check if already computed/no parents
00149       if(ancestors[type].size()>0)
00150         return ancestors[type];
00151       ancestors[type]= parents[type];
00152       for(typename set< DAGNode< NodeInfo > *>::iterator i = parents[type].begin();
00153           i != parents[type].end(); i++)
00154         ancestors[type]= setunion((*i)->getAncestors(type), ancestors[type]);
00155   
00156       return ancestors[type];
00157     }
00158 
00159   virtual set< DAGNode< NodeInfo > *> getDescendants(int type = 0)
00160     {
00161       //check if already computed or no descendants.
00162       if(descendants[type].size()>0)
00163         return descendants[type];
00164       descendants[type] = children[type];
00165       for(typename set< DAGNode< NodeInfo > *>::iterator i = children[type].begin();
00166           i != children[type].end(); i++)
00167         descendants[type]= setunion((*i)->getDescendants(type), descendants[type]);
00168   
00169       return descendants[type];
00170     }
00171 
00172 
00174   virtual NodeInfo getNodeInfo() const
00175     {
00176       return(_nodeInfo);
00177     }
00178   
00179   
00181   virtual NodeInfo &getNodeInfo() 
00182     {
00183       return(_nodeInfo);
00184     }
00185   
00186   
00190   virtual bool isChild(DAGNode< NodeInfo > *node, unsigned int type = 0)
00191     {
00192       return(children[type].end() != children[type].find(node));
00193       
00194     }
00195   
00199   virtual bool isParent(DAGNode< NodeInfo > *node, unsigned int type = 0) 
00200     {
00201       return(parents[type].end() != parents[type].find(node));
00202       
00203     }
00204   
00205 };
00206 
00207   
00208 
00209 
00214 template< typename NodeType > class DAG
00215 {
00216 
00217 protected:
00218 
00219   vector< NodeType* > _nodes;
00220   // map telling me in which index of _nodes each node is stored.
00221   map< const NodeType*, unsigned int > _nodesToIndices;
00222 
00223 protected:
00224 
00225   // copy rhs into *this.
00226   void _copy(const DAG< NodeType > &rhs);
00227 
00228   // copy the nodes of rhs into *this.
00229   void _copyNodes(const DAG< NodeType > &rhs);
00230   
00231   // methods to get the index from a NodeType*.
00232   unsigned int _getNodeIndex(const NodeType *node) const
00233     {
00234       return(_nodesToIndices.find(node)->second);
00235     }
00236   
00237   
00238   
00239 public:
00242   DAG()
00243       : _nodes(), _nodesToIndices()
00244     {}
00245 
00248   DAG(const DAG< NodeType > &rhs)
00249     {
00250       _copy(rhs);
00251     }
00252   
00253   
00256   DAG(istream *in);
00257 
00259   virtual ~DAG()
00260     {
00261       clear();
00262     }
00263 
00264 
00268   const DAG< NodeType > &operator=(const DAG &rhs)
00269     {
00270       if (this != &rhs)
00271         {
00272           clear();
00273           _copy(rhs);
00274         }
00275       return(*this);
00276     }
00277   
00280   void clear()
00281     {
00282       for (unsigned int i = 0; i < _nodes.size(); i++)
00283         delete _nodes[i];
00284       _nodes.clear();
00285       _nodesToIndices.clear();
00286     }
00287   
00288 
00295   virtual void addNode(NodeType *node)
00296     {
00297 #ifdef DEBUG
00298 //       cout << "DAG::addNode(): node is " << node->_nodeInfo.getId()
00299 //            << "." << endl;
00300 #endif      
00301       _nodes.push_back(node);
00302       _nodesToIndices[node] = _nodes.size() - 1;
00303     }
00304   
00311 //   virtual void addNode(const NodeType)
00312 //     {
00313 //       _nodes.push_back(node);
00314 //     }
00315   
00316 
00335   void computeDifference(DAG< NodeType> &other, DAG< NodeType> &difference);
00336 
00337   
00351   virtual void computePower(unsigned int exponent, DAG< NodeType> &powerDAG);
00352   
00354   virtual void computeLeaves(vector< NodeType* > &leaves, unsigned int type = 0);
00355 
00357   virtual void computeRoots(vector< NodeType* > &roots, unsigned int type = 0);
00358   
00359   
00373   virtual void computeTopologicalSort(vector< NodeType* > &sortedFunctions,
00374                                       unsigned int type = 0);
00375   
00376 
00385   virtual void computeTransitiveClosure(DAG< NodeType> &closedDAG);
00386 
00399   virtual void computeTransitiveReduction(DAG< NodeType> &reducedDAG);
00400 
00401   
00402 
00406 //  virtual void read(string file);
00407 
00411 //  virtual void read(istream *in);
00412   
00431 //  virtual set<NodeType*> getAncestors(NodeType* obj, int type=0);
00432 
00443 //  virtual set<NodeType*> getParents(NodeType* obj, int type=0);
00444 
00458 //  virtual set<NodeType*> getDescendants(NodeType* obj, int type=0);
00459 
00470 //  virtual set<NodeType*> getChildren(NodeType* obj, int type=0);
00471   
00475 //   NodeType* getFunctionById(int id) const;
00476   
00483 //   NodeType* getFunctionById(string id) const
00484 //     {
00485 //       unsigned int intID;
00486 //       stringstream ss;
00487 //       ss << id;
00488 //       ss >> intID;
00489 //       return(getFunctionById(intID));
00490 //     }
00491   
00494   unsigned int getNumEdges() const;
00495   
00498   unsigned int getNumNodes() const;
00499   
00506   void print(string file) const;
00507   
00514   void print(ostream &ostr) const;
00515   
00516 
00517 };
00518 
00519 
00520 // template< typename NodeType >
00521 // set< NodeType *> DAG< NodeType >::getAncestors(NodeType * obj, int type)
00522 // {
00523 //   return(obj->getAncestors(type));
00524 // }
00525 
00526 // template< typename NodeType >
00527 // set< NodeType* > DAG< NodeType >::getDescendants(NodeType* obj, int type)
00528 // {
00529 //   return(obj->getDescendants(type));
00530 // }
00531 
00532 // template< typename NodeType >
00533 // set< NodeType* > DAG< NodeType >::getParents(NodeType* obj, int type)
00534 // {
00535 //   return obj->getParents(type);
00536 // }
00537 
00538 // template< typename NodeType >
00539 // set< NodeType* > DAG< NodeType >::getChildren(NodeType* obj, int type)
00540 // {
00541 //   return obj->getChildren(type);
00542 // }
00543 
00544 template< typename NodeType >
00545 void DAG< NodeType >::_copyNodes(const DAG< NodeType > &rhs)
00546 {
00547   // add nodes from rhs to *this.
00548   for (unsigned int i = 0; i < rhs._nodes.size(); i++)
00549     {
00550       addNode(new NodeType(*rhs._nodes[i]));
00551 //       _nodes.push_back(new NodeType(*rhs._nodes[i]));
00552 //       _nodesToIndices[_nodes.back()] =  i;
00553     }
00554 }
00555 
00556 template< typename NodeType >
00557 void DAG< NodeType >::_copy(const DAG< NodeType > &rhs)
00558 {
00559   _copyNodes(rhs);
00560   unsigned int parentIndex, edgeType;
00561   NodeType *node, *parent;
00562   typename set< NodeType *>::iterator pitr;
00563   // loop over all edges of rhs and add them to *this.
00564   for (unsigned int i = 0; i < rhs._nodes.size(); i++)
00565     {
00566       node = _nodes[i];
00567       set< NodeType *> parents = rhs._nodes[i]->getParents();
00568       for (pitr = parents.begin(); pitr != parents.end(); pitr++)
00569         {
00570           parentIndex = rhs._getNodeIndex(*pitr);
00571           parent = _nodes[parentIndex];
00572           edgeType = rhs._nodes[i]->getParentEdgeType(*pitr);
00573           // add edge to *this.
00574           node->addParent(parent, edgeType);
00575           parent->addChild(node, edgeType);
00576         }
00577     }
00578 }
00579 
00580 template< typename NodeType >
00581 unsigned int DAG< NodeType >::getNumEdges() const
00582 {
00583   // loop over the nodes.
00584   typename vector< NodeType* >::const_iterator nitr;
00585   unsigned int numEdges = 0;
00586   for (nitr = _nodes.begin(); nitr != _nodes.end(); nitr++)
00587     {
00588       set< NodeType *> parents = (*nitr)->getParents();
00589       numEdges += parents.size();
00590     }
00591   return(numEdges);
00592 }
00593 
00594 template< typename NodeType >
00595 unsigned int DAG< NodeType >::getNumNodes() const
00596 {
00597   return(_nodes.size());
00598 }
00599 
00600 template< typename NodeType >
00601 void DAG< NodeType >::print(string file) const
00602 {
00603   ofstream fstr(file.c_str());
00604   print(fstr);
00605   fstr.close();
00606 }
00607 
00608 template< typename NodeType >
00609 void DAG< NodeType >::print(ostream &ostr) const
00610 {
00611   // loop over the nodes.
00612   typename vector< NodeType* >::const_iterator nitr;
00613   typename set< NodeType *>::const_iterator sitr;
00614   for (nitr = _nodes.begin(); nitr != _nodes.end(); nitr++)
00615     {
00616       set< NodeType *> parents = (*nitr)->getParents();
00617 #ifdef DEBUG
00618 //       cout << "Node " << (*nitr)->_nodeInfo.getId() << " has "
00619 //            << parents.size() << " parents." << endl;
00620 #endif
00621       for (sitr = parents.begin(); sitr != parents.end(); sitr++)
00622         ostr << (*nitr)->_nodeInfo.getId()
00623              << "\t" << (*sitr)->_nodeInfo.getId()
00624              << "\t" << (*nitr)->getParentEdgeType(*sitr)
00625              << endl;
00626     }
00627   
00628 }
00629 
00630 template< typename NodeType >
00631 void DAG< NodeType >::computeDifference(DAG< NodeType> &other,
00632                                         DAG< NodeType> &difference)
00633 {
00634   // let us add all the nodes in *this to difference.
00636   difference._copyNodes(*this);
00637 //   for (unsigned int i = 0; i < _nodes.size(); i++)
00638 //     {
00639 //       difference._nodes.push_back(new NodeType(*_nodes[i]));
00640 //       difference._nodesToIndices[difference._nodes.back()] =  i;
00641 //     }
00642   
00643 
00644   typename set< NodeType * >::iterator pitr;
00645   NodeType *nodeInOther, *nodeInDifference, *parentInOther, *parentInDifference;
00646   unsigned int parentIndex, parentEdgeType;
00647   // loop over all edges in *this.
00648   for (unsigned int i = 0; i < _nodes.size(); i++)
00649     {
00650       nodeInOther = other._nodes[i];
00651       nodeInDifference = difference._nodes[i];
00652       set< NodeType *> parents = _nodes[i]->getParents();
00653       for (pitr = parents.begin(); pitr != parents.end(); pitr++)
00654         {
00655           parentIndex = _getNodeIndex(*pitr);
00656           parentInOther = other._nodes[parentIndex];
00657           parentEdgeType = _nodes[i]->getParentEdgeType(*pitr);
00658           if (nodeInOther->isParent(parentInOther))
00659             // this edge exists in other, so i ignore it.
00660             continue;
00661           // add edge between node and other to difference.
00662           
00663           // locate parent in difference.
00664           parentInDifference = difference._nodes[parentIndex];
00665           nodeInDifference->addParent(parentInDifference, parentEdgeType);
00666           parentInDifference->addChild(nodeInDifference, parentEdgeType);
00667         }
00668     }
00669   
00670 }
00671 
00672 template< typename NodeType >
00673 void DAG< NodeType >::computeLeaves(vector< NodeType* > &leaves, unsigned int type)
00674 {
00675   for (unsigned int i = 0; i < _nodes.size(); i++)
00676     {
00677       if (0 == _nodes[i]->getChildren(type).size())
00678         // _nodes[i] has no leaves.
00679         leaves.push_back(_nodes[i]);
00680     }
00681 }
00682 
00683 template< typename NodeType >
00684 void DAG< NodeType >::computePower(unsigned int exponent, DAG< NodeType> &powerDAG)
00685 {
00686   if (0 == exponent)
00687     return;
00688   if (1 == exponent)
00689     {
00690       powerDAG = *this;
00691       return;
00692     }
00693   DAG< NodeType > grandparentDAG;
00694   if (2 < exponent)
00695     // make a recursive call.
00696     {
00697       computePower(exponent - 1, grandparentDAG);
00698     }
00699   else
00700     grandparentDAG = *this;
00701 
00702   // invariant: at this stage, grandparentDAG = (*this)^{exponent - 1}.
00703   
00704   // now join *this and grandparentDAG to form powerDAG. 
00705 
00706   // first, add all nodes in *this to powerDAG.
00707   powerDAG._copyNodes(*this);
00708 //   for (unsigned int i = 0; i < _nodes.size(); i++)
00709 //     powerDAG._nodes.push_back(new NodeType(*_nodes[i]));
00710 
00711   // next, for each node in *this, find its parents in *this, find the
00712   // parents of the parents in grandparentDAG, and in powerDAG,
00713   // connect the node to each of its grandparents.
00714   NodeType *nodeInDAG, *parentInGrandparentDAG, *grandparentInPowerDAG;
00715   typename set< NodeType *>::iterator pitr, gitr;
00716   unsigned int grandparentIndex;
00717   
00718   for (unsigned int i = 0; i < _nodes.size(); i++)
00719     {
00720 //      nodeInDAG = _nodes[i];
00721       set< NodeType *> parents = _nodes[i]->getParents();
00722       for (pitr = parents.begin(); pitr != parents.end(); pitr++)
00723         {
00724           // find the parent in grandparentDAG.
00725           parentInGrandparentDAG = grandparentDAG._nodes[_getNodeIndex(*pitr)];
00726           // find the parents of parentInGrandparentDAG.
00727           set< NodeType *> grandparents = parentInGrandparentDAG->getParents();
00728           for (gitr = grandparents.begin(); gitr != grandparents.end(); gitr++)
00729             {
00730               // in powerDAG, find the counterpart of *gitr. i use the
00731               // fact that node indices are identical in powerDAG and
00732               // grandparentDAG.
00733               grandparentIndex = grandparentDAG._getNodeIndex(*gitr);
00734               grandparentInPowerDAG = powerDAG._nodes[grandparentIndex];
00735               powerDAG._nodes[i]->addParent(grandparentInPowerDAG);
00736               grandparentInPowerDAG->addChild(powerDAG._nodes[i]);
00737             }
00738         }
00739     }
00740 }
00741 
00742 
00743 template< typename NodeType >
00744 void DAG< NodeType >::computeRoots(vector< NodeType* > &roots, unsigned int type)
00745 {
00746   for (unsigned int i = 0; i < _nodes.size(); i++)
00747     {
00748       if (0 == _nodes[i]->getParents(type).size())
00749         // _nodes[i] has no parents.
00750         roots.push_back(_nodes[i]);
00751     }
00752 }
00753 
00754 template< typename NodeType >
00755 void DAG< NodeType >::computeTopologicalSort(vector< NodeType* > &sortedNodes,
00756                                              unsigned int type)
00757 {
00758   map< NodeType *, unsigned int > numVisitedParents;
00759   set< NodeType * > roots;
00760   deque< NodeType * > nodeQueue;
00761   for (unsigned int i = 0; i < _nodes.size(); i++)
00762     {
00763       // initialise
00764       numVisitedParents[_nodes[i]] = 0;
00765       if (0 == _nodes[i]->getParents(type).size())
00766         // _nodes[i] has no parents.
00767         roots.insert(_nodes[i]);
00768 //        nodeQueue.push_back(_nodes[i]);
00769     }
00770   NodeType *nextNode;
00771   // pop elements from the queue until it is empty.
00772   while (!nodeQueue.empty() || !roots.empty())
00773     {
00774       if (nodeQueue.empty())
00775         // insert a node from from roots and remove it from roots
00776         // so that it is not inserted. this way the nodes in each
00777         // component of the DAG appear consecutively in the sorted
00778         // order.
00779         {
00780           nodeQueue.push_back(*(roots.begin()));
00781           roots.erase(roots.begin());
00782         }
00783       
00784       nextNode = nodeQueue.front();
00785       nodeQueue.pop_front();
00786       sortedNodes.push_back(nextNode);
00787       // process children of nextNode.
00788       set< NodeType *> children = nextNode->getChildren(type);
00789       typename set< NodeType* >::const_iterator itr;
00790       
00791       for (itr = children.begin();
00792            itr != children.end(); itr++)
00793         {
00794           numVisitedParents[*itr]++;
00795           // push child into queue if all parents are visited.
00796           if (numVisitedParents[*itr] == (*itr)->getParents().size())
00797             nodeQueue.push_back(*itr);
00798         }
00799     }
00800 }
00801 
00802 template< typename NodeType >
00803 void DAG< NodeType >::computeTransitiveClosure(DAG< NodeType> &closedDAG)
00804 {
00805   cout << "DAG has " << getNumNodes() << " nodes and " << getNumEdges()
00806        << " edges." << endl;
00807   // i must operate on the nodes in closedDAG, so first make closedDAG
00808   // a copy of *this.
00809   closedDAG = *this;
00810   cout << "Copy of DAG has " << closedDAG.getNumNodes() << " nodes and "
00811        << closedDAG.getNumEdges() << " edges." << endl;
00812 //   // add each node in *this to closedDAG.
00813 //   for (unsigned int i = 0; i < _nodes.size(); i++)
00814 //     closedDAG.addNode(_nodes[i]);
00815 
00816   unsigned int numClosureEdges = 0;
00817   typename set< NodeType *>::iterator aitr;
00818   // loop over each node in *this.
00819   for (unsigned int i = 0; i < closedDAG._nodes.size(); i++)
00820     {
00821       cout << "DAG::computeTransitiveClosure(): Processing " << closedDAG._nodes[i]->_nodeInfo.getId() << endl;
00822       // find ancestors of each node.
00823       set< NodeType *> ancestors = closedDAG._nodes[i]->getAncestors();
00824       for (aitr = ancestors.begin(); aitr != ancestors.end(); aitr++)
00825         // add link between ancestor and node to closedDAG.
00826         {
00827           cout << "\tChild " << closedDAG._nodes[i]->_nodeInfo.getId()
00828                << ", parent " << (*aitr)->_nodeInfo.getId() << endl;
00829           closedDAG._nodes[i]->addParent(*aitr);
00830           (*aitr)->addChild(closedDAG._nodes[i]);
00831           numClosureEdges++;
00832         }
00833     }
00834   cout << "DAG< NodeType >::computeTransitiveClosure(): added "
00835        << numClosureEdges << " edges to the transitive closure." << endl;
00836   cout << "Transitive closure of DAG has " << closedDAG.getNumNodes() << " nodes and "
00837        << closedDAG.getNumEdges() << " edges." << endl;
00838 }
00839 
00840 
00841 
00842 template< typename NodeType >
00843 void DAG< NodeType >::computeTransitiveReduction(DAG< NodeType> &reducedDAG)
00844 {
00845   cout << "DAG has " << getNumNodes() << " nodes and "
00846        << getNumEdges() << " edges." << endl;
00847   DAG< NodeType > closedDAG, squareClosedDAG;
00848   // compute transitive closure of *this.
00849 //  computeTransitiveClosure(closedDAG);
00850   closedDAG = *this;
00851   cout << "Copy of DAG has "
00852        << closedDAG.getNumNodes() << " nodes and "
00853        << closedDAG.getNumEdges() << " edges." << endl;
00854   
00855   // compute square of transitive closure.
00856   closedDAG.computePower(2, squareClosedDAG);
00857   cout << "Square of transitive closure of DAG has "
00858        << squareClosedDAG.getNumNodes() << " nodes and "
00859        << squareClosedDAG.getNumEdges() << " edges." << endl;
00860   
00861   // reducedDAG consists of edges in transitive closure that are not in the square.
00862   closedDAG.computeDifference(squareClosedDAG, reducedDAG);
00863   cout << "Transitive reduction of DAG has "
00864        << reducedDAG.getNumNodes() << " nodes and "
00865        << reducedDAG.getNumEdges() << " edges." << endl;
00866 }
00867 
00868 
00869 #endif // _DAG_H 
00870 
 All Classes Functions Variables Typedefs Friends