RelationOpsBuddy.hpp

Go to the documentation of this file.
00001 #ifndef RELATION_OPS_BUDDY_HPP
00002 #define RELATION_OPS_BUDDY_HPP
00003 
00004 #include <algorithm>
00005 #include <iterator>
00006 #include <cassert>
00007 #include <utility>
00008 #include <map>
00009 #include <set>
00010 
00011 #ifndef NDEBUG
00012 #include <boost/type_traits/is_same.hpp>
00013 #include <boost/static_assert.hpp>
00014 #endif
00015 #include <boost/shared_ptr.hpp>
00016 
00017 #include <fdd.h>
00018 
00019 #include "wali/ref_ptr.hpp"
00020 #include "wali/KeyContainer.hpp"
00021 
00022 namespace wali {
00023   namespace relations {
00024 #if 0
00025     (Not used right now, but could be slightly helpful)
00026         
00027     /// Returns floor(log_2(n))
00028     //
00029     // E.g. floorLog2(11):
00030     //  iteration#   n (bin)     n (dec)   log [values at end of iteration]
00031     //     --         1011         11       -1
00032     //     1          0101         5        0
00033     //     2          0010         2        1
00034     //     3          0001         1        2
00035     //     4          0000         0        3
00036     //
00037     // log_2(11) ~= 3.46, so we're right (we return 3)
00038     //
00039     // E.g. floorLog2(4):
00040     //  iteration#   n (bin)     n (dec)   log [values at end of iteration]
00041     //     --         0100          4      -1
00042     //     1          0010          2      0
00043     //     2          0001          1      1
00044     //     3          0000          0      2
00045     //
00046     // log_2(4) = 2, so again we're right.
00047     inline int floorLog2(unsigned int n) {
00048       assert(n>0);
00049 
00050       int log = -1;
00051       while (n > 0) {
00052         n >>= 1;
00053         ++log;
00054       }
00055       return log;
00056     }
00057 #endif
00058 
00059     inline
00060     Quad<int, int, int, int>
00061     getFddNumbers(unsigned int largest)
00062     {
00063       // Maps from a relation largest to the base bdd number.
00064       static std::map<unsigned int, int> fddMap;
00065             
00066       if (fddMap.find(largest) == fddMap.end()) {
00067         // Make a new domain for each relation component that we'll ever need in composition
00068         int domains[4] = {
00069           largest + 1,
00070           largest + 1,
00071           largest + 1,
00072           largest + 1
00073         };
00074         int base = fdd_extdomain(domains, 4);
00075         fddMap[largest] = base;
00076       }
00077 
00078       int base = fddMap[largest];
00079 
00080       return Quad<int, int, int, int>(base, base+1, base+2, base+3);
00081     }
00082 
00083 
00084     /// Private structure
00085     //
00086     // Used for each component in a relation (e.g. domain & range).
00087     //
00088     // This used to hold more, but was kind of neutered. Still, I like the
00089     // way it makes accesses look, so I'll leave it.
00090     struct Component {
00091       int fdd_number;
00092 
00093       bool operator== (Component rhs) const {
00094         return fdd_number == rhs.fdd_number;
00095       }
00096 
00097       bool operator!= (Component rhs) const {
00098         return fdd_number != rhs.fdd_number;
00099       }
00100     };
00101 
00102 
00103     /// This class represents a particular domain (set) that a given relation is over.
00104     ///
00105     /// Relations on a domain A can only be composed, merged, etc. with
00106     /// other relations on the domain A.
00107     class Domain {
00108     public: // FIXME
00109       Component left, middle, right, extra;
00110 
00111     private: // REMOVE ONCE THE ABOVE IS FIXED
00112       unsigned int _largest;
00113 
00114       typedef void(*pairFreer)(bddPair*);
00115       boost::shared_ptr<bddPair> shift_LM_to_MR;
00116       boost::shared_ptr<bddPair> shift_R_to_M;
00117       boost::shared_ptr<bddPair> shift_LR_to_RE;
00118       boost::shared_ptr<bddPair> shift_E_to_M;
00119 
00120     public:
00121       Domain(unsigned int largest)
00122         : shift_LM_to_MR(bdd_newpair(), bdd_freepair)
00123         , shift_R_to_M(bdd_newpair(), bdd_freepair)
00124         , shift_LR_to_RE(bdd_newpair(), bdd_freepair)
00125         , shift_E_to_M(bdd_newpair(), bdd_freepair)
00126       {
00127         _largest = largest;
00128 
00129         if (largest > 0) {
00130           Quad<int, int, int, int> fdds = getFddNumbers(largest);
00131           left.fdd_number = fdds.first;
00132           middle.fdd_number = fdds.second;
00133           right.fdd_number = fdds.third;
00134           extra.fdd_number = fdds.fourth;
00135                     
00136           fdd_setpair(shift_LM_to_MR.get(), left.fdd_number, middle.fdd_number);
00137           fdd_setpair(shift_LM_to_MR.get(), middle.fdd_number, right.fdd_number);
00138                     
00139           fdd_setpair(shift_R_to_M.get(), right.fdd_number, middle.fdd_number);
00140                     
00141           fdd_setpair(shift_LR_to_RE.get(), left.fdd_number, right.fdd_number);
00142           fdd_setpair(shift_LR_to_RE.get(), right.fdd_number, extra.fdd_number);
00143                     
00144           fdd_setpair(shift_E_to_M.get(), extra.fdd_number, middle.fdd_number);
00145         }
00146       }
00147             
00148       bool operator!= (Domain const & rhs) const {
00149         return !(*this == rhs);
00150       }
00151 
00152       bool operator== (Domain const & rhs) const {
00153         if (_largest == rhs._largest) {
00154           assert (left == rhs.left
00155                   && middle == rhs.middle
00156                   && right == rhs.right
00157                   && extra == rhs.extra);
00158           return true;
00159         }
00160         else {
00161           return false;
00162         }
00163       }
00164 
00165       bddPair* shift_out_compose() const {
00166         return shift_LM_to_MR.get();
00167       }
00168 
00169       bddPair* shift_in_compose() const {
00170         return shift_R_to_M.get();
00171       }
00172 
00173       bddPair* shift_out_merge() const {
00174         return shift_LR_to_RE.get();
00175       }
00176 
00177       bddPair* shift_in_merge() const {
00178         return shift_E_to_M.get();
00179       }
00180 
00181       unsigned int largest() const {
00182         return _largest;
00183       }
00184 
00185     private:
00186       friend class BinaryRelation;
00187       friend class TernaryRelation;
00188     };
00189 
00190         
00191     /// Wraps a bdd in a nice friendly package
00192     class BinaryRelation {
00193     public: // FIXME
00194       Domain domain;
00195       bdd myBdd;
00196 
00197     public:
00198       BinaryRelation(unsigned int largest)
00199         : domain(largest)
00200         , myBdd(bddfalse)
00201       {}
00202 
00203       BinaryRelation()
00204         : domain(0)
00205         , myBdd(bddfalse)
00206       {}
00207 
00208       bool insert(unsigned int leftVal, unsigned int rightVal)
00209       {
00210         assert(leftVal <= domain.largest());
00211         assert(rightVal <= domain.largest());
00212 
00213         bdd left_is_leftVal = fdd_ithvar(domain.left.fdd_number, leftVal);
00214         bdd right_is_rightVal = fdd_ithvar(domain.middle.fdd_number, rightVal);
00215 
00216         bdd old = myBdd;
00217         myBdd = myBdd | (left_is_leftVal & right_is_rightVal);
00218         return myBdd != old;
00219       }
00220 
00221       bool insert(std::pair<unsigned int, unsigned int> pair)
00222       {
00223         return insert(pair.first, pair.second);
00224       }
00225 
00226       bool empty() const
00227       {
00228         return myBdd == bddfalse;
00229       }
00230 
00231       bdd getBdd() const
00232       {
00233         return myBdd;
00234       }
00235 
00236       bool operator== (BinaryRelation const & other) const {
00237         return (myBdd == other.myBdd && domain == other.domain);
00238       }
00239 
00240       friend void compose(BinaryRelation &, BinaryRelation const &, BinaryRelation const &);
00241       friend void intersect(BinaryRelation &, BinaryRelation const &, BinaryRelation const &);
00242       friend void union_(BinaryRelation &, BinaryRelation const &, BinaryRelation const &);
00243       friend void merge(BinaryRelation &, BinaryRelation const &, BinaryRelation const &, BinaryRelation const &);
00244     };
00245 
00246 
00247     /// Wraps a bdd in a nice friendly package
00248     class TernaryRelation {
00249     public: // FIXME
00250       Domain domain;
00251       bdd myBdd;
00252 
00253     public:
00254       TernaryRelation(unsigned int largest)
00255         : domain(largest)
00256         , myBdd(bddfalse)
00257       {}
00258 
00259       TernaryRelation()
00260         : domain(0)
00261         , myBdd(bddfalse)
00262       {}
00263 
00264 
00265       bool insert(unsigned int leftVal, unsigned int middleVal, unsigned int rightVal)
00266       {
00267         assert(leftVal <= domain.largest());
00268         assert(middleVal <= domain.largest());
00269         assert(rightVal <= domain.largest());
00270 
00271         bdd left_is_leftVal = fdd_ithvar(domain.left.fdd_number, leftVal);
00272         bdd middle_is_middleVal = fdd_ithvar(domain.middle.fdd_number, middleVal);
00273         bdd right_is_rightVal = fdd_ithvar(domain.right.fdd_number, rightVal);
00274 
00275         bdd old = myBdd;
00276         myBdd = myBdd | (left_is_leftVal & middle_is_middleVal & right_is_rightVal);
00277         return old != myBdd;
00278       }
00279 
00280       bool insert(Triple<unsigned, unsigned, unsigned> triple)
00281       {
00282         return insert(triple.first, triple.second, triple.third);
00283       }
00284 
00285       bool insert(Triple<unsigned long, unsigned long, unsigned long> triple)
00286       {
00287         return insert(triple.first, triple.second, triple.third);
00288       }
00289 
00290       bdd getBdd() const
00291       {
00292         return myBdd;
00293       }
00294     };
00295 
00296         
00297     /// This can be used in client code to hide the actual relation types
00298     template<typename State>
00299     struct RelationTypedefs
00300     {
00301       typedef wali::relations::BinaryRelation BinaryRelation;
00302       typedef wali::relations::TernaryRelation TernaryRelation;
00303     };
00304 
00305 
00306     inline
00307     void buddyInit()
00308     {
00309       if (!bdd_isrunning()) {
00310         const int million = 1000000;
00311         int rc = bdd_init( 50*million, 100000 );
00312         if( rc < 0 ) {
00313           std::cerr << "[ERROR] " << bdd_errstring(rc) << std::endl;
00314           assert( 0 );
00315           exit(10);
00316         }
00317         // Default is 50,000 (1 Mb),memory is cheap, so use 100,000
00318         bdd_setmaxincrease(100000);
00319       }
00320     }
00321 
00322 
00323     /// Composes two binary relations
00324     ///
00325     /// { (x,z) | (x,y) \in r1,  (y,z) \in r2}
00326     ///
00327     /// Parameters:
00328     ///   out_result: The relational composition of r1 and r2
00329     ///   r1:         relation 1
00330     ///   r2:         relation 2
00331     inline void
00332     compose(BinaryRelation & out_result,
00333             BinaryRelation const & r1,
00334             BinaryRelation const & r2)
00335     {
00336       if (r1.domain != r2.domain || out_result.domain != r1.domain) {
00337         std::cerr << "Error: compose (Buddy version): relations don't share a domain\n";
00338         exit(20);
00339       }
00340 
00341       bdd r1_bdd = r1.myBdd;
00342       bdd r2_shifted = bdd_replace(r2.myBdd, r2.domain.shift_out_compose());
00343       bdd composed = bdd_relprod(r1_bdd, r2_shifted, fdd_ithset(r1.domain.middle.fdd_number));
00344       out_result.myBdd = bdd_replace(composed, out_result.domain.shift_in_compose());
00345     }
00346 
00347         
00348     /// Projects out the symbol in the internal and call relation
00349     ///
00350     /// {(source, target) | (source, alpha, target) \in delta}
00351     ///
00352     /// Parameters:
00353     ///   out_result: The relation delta restricted to alpha
00354     ///   delta:      Internals or calls relation
00355     ///   alpha:      Alphabet symbol to select and project
00356     template<typename OutRelation, typename State, typename Symbol>
00357     void
00358     project_symbol_3(OutRelation & out_result,
00359                      std::set<Triple<State, Symbol, State> > const & delta,
00360                      Symbol alpha)
00361     {
00362       typedef typename std::set<Triple<State, Symbol, State> >::const_iterator Iterator;
00363 
00364       for(Iterator cur_trans = delta.begin(); cur_trans != delta.end(); ++cur_trans) {
00365         State source = cur_trans->first;
00366         Symbol symb = cur_trans->second;
00367         State target = cur_trans->third;
00368 
00369         if(symb == alpha) {
00370           out_result.insert(std::make_pair(source, target));
00371         }
00372       }
00373     }
00374 
00375 
00376     /// Performs the sort of merge required for NWA return edges
00377     ///
00378     /// {(q, q') | (q,q1) \in r_call, (q1,q2) \in r_exit, (q2,q1,q') \in delta}
00379     ///
00380     /// Parameters:
00381     ///   out_result: The relational composition of R1 and R2
00382     ///   r_exit:     The relation at the exit node
00383     ///   r_call:     The relation at the call node
00384     ///   delta_r:    The return transition relation with the alphabet
00385     ///               symbol projected out
00386     inline void
00387     merge(BinaryRelation & out_result,
00388           BinaryRelation const & r_exit,
00389           BinaryRelation const & r_call,
00390           TernaryRelation const & delta_r)
00391     {
00392       if (out_result.domain != r_exit.domain
00393           || r_exit.domain != r_call.domain
00394           || r_call.domain != delta_r.domain)
00395       {
00396         std::cerr << "Error: merge (Buddy version): relations don't share a domain\n";
00397         exit(20);
00398       }
00399 
00400       bdd r1_bdd = r_call.myBdd;
00401       bdd r2_bdd = bdd_replace(r_exit.myBdd, r_exit.domain.shift_out_compose());
00402       bdd r3_bdd = bdd_replace(delta_r.myBdd, r_call.domain.shift_out_merge());
00403 
00404       bdd middle_two = fdd_ithset(r_exit.domain.middle.fdd_number);
00405       //| fdd_ithset(r_exit.domain.right.fdd_number);
00406 
00407       bdd composed = bdd_appex(r1_bdd & r2_bdd,
00408                                r3_bdd,
00409                                bddop_and,
00410                                fdd_ithset(r_call.domain.middle.fdd_number));
00411 
00412       composed = bdd_exist(composed, fdd_ithset(r_exit.domain.right.fdd_number));
00413             
00414       out_result.myBdd = bdd_replace(composed, out_result.domain.shift_in_merge());
00415     }
00416 
00417 
00418     /// Projects out the symbol in the return relation
00419     ///
00420     /// {(source, pred, target) | (source, pred, alpha, target) \in delta}
00421     ///
00422     /// Parameters:
00423     ///   out_result: The ternary relation delta restricted to alpha
00424     ///   delta:      Return relation
00425     ///   alpha:      Alphabet symbol to select and project
00426     template<typename State, typename Symbol>
00427     void
00428     project_symbol_4(TernaryRelation & out_result,
00429                      std::set<Quad<State, State, Symbol, State> > const & delta,
00430                      Symbol alpha)
00431     {
00432       typedef typename std::set<Quad<State, State, Symbol, State> >::const_iterator Iterator;
00433 
00434       for(Iterator cur_trans = delta.begin(); cur_trans != delta.end(); ++cur_trans)
00435       {
00436         State source = cur_trans->first;
00437         State pred = cur_trans->second;
00438         Symbol symb = cur_trans->third;
00439         State target = cur_trans->fourth;
00440                 
00441         if(symb == alpha)
00442         {
00443           bool added = out_result.insert(Triple<State, State, State>(source, pred, target));
00444           assert(added);
00445         }
00446       }
00447     }
00448 
00449 
00450     template<typename State>
00451     State biggest(State s1, State s2, State s3)
00452     {
00453       return std::max(s1, std::max(s2, s3));
00454     }
00455 
00456     /// Constructs the transitive closure of an algorithm.
00457     ///
00458     /// TODO: Right now we break the abstraction and assume State is a
00459     /// (reasonably small) integer.
00460     ///
00461     /// { (p, q) | p R* q} or however you want to denote it
00462     ///
00463     /// Parameters:
00464     ///   out_result: The transitive closure of r
00465     ///   r:          The source relation
00466     template<typename Relation>
00467     void
00468     transitive_closure(Relation & out_result,
00469                        Relation const & r)
00470     {
00471       typedef typename Relation::const_iterator Iterator;
00472       typedef typename Relation::value_type::first_type State;
00473 
00474 #ifndef NDEBUG
00475       const bool domain_and_codomain_are_the_same =
00476         boost::is_same<typename Relation::value_type::first_type,
00477         typename Relation::value_type::second_type>::value;
00478       BOOST_STATIC_ASSERT(domain_and_codomain_are_the_same);
00479 #endif
00480 
00481       // Find the largest state
00482       State largest_state = State();
00483       for(Iterator edge = r.begin(); edge != r.end(); ++edge)
00484       {
00485         largest_state = biggest(largest_state, edge->first, edge->second);
00486       }
00487 
00488       largest_state = largest_state + 1;
00489 
00490       assert(largest_state < 4000); // reasonable based on my examples
00491       // I think
00492       
00493       // Set up the path matrix
00494       std::vector<std::deque<bool> > matrix(largest_state);
00495       for(size_t i = 0; i<matrix.size(); ++i)
00496       {
00497         matrix[i].resize(largest_state);
00498       }
00499 
00500       for(size_t source=0; source<largest_state; ++source)
00501       {
00502         matrix[source][source] = 1;
00503       }
00504        
00505       for(Iterator edge = r.begin(); edge != r.end(); ++edge)
00506       {
00507         matrix[edge->first][edge->second] = 1;
00508       }
00509 
00510       // Now perform Floyd-Warshall alg. From Wikipedia:
00511       //
00512       //  /* Assume a function edgeCost(i,j) which returns the cost of
00513       //     the edge from i to j (infinity if there is none).  Also
00514       //     assume that n is the number of vertices and
00515       //     edgeCost(i,i) = 0
00516       //   */
00517       //
00518       //  int path[][];
00519       //
00520       //  /* A 2-dimensional matrix. At each step in the algorithm,
00521       //     path[i][j] is the shortest path from i to j using
00522       //     intermediate vertices (1..k-1).  Each path[i][j] is
00523       //     initialized to edgeCost(i,j) or infinity if there is no
00524       //     edge between i and j.
00525       //   */
00526       //
00527       //   procedure FloydWarshall ()
00528       //      for k := 1 to n
00529       //         for i := 1 to n
00530       //            for j := 1 to n
00531       //               path[i][j] = min ( path[i][j], path[i][k]+path[k][j] );
00532 
00533       for(size_t k = 0; k < largest_state; ++k)
00534         for(size_t i = 0; i < largest_state; ++i)
00535           for(size_t j = 0; j < largest_state; ++j)
00536             matrix[i][j] = matrix[i][j] || (matrix[i][k] && matrix[k][j]);
00537 
00538 
00539       // Now go through and convert back to the multimap view
00540       for(size_t source=0; source<largest_state; ++source)
00541       {
00542         for(size_t target=0; target<largest_state; ++target)
00543         {
00544           if(matrix[source][target])
00545           {
00546             out_result.insert(std::make_pair(source,target));
00547           }
00548         }
00549       } // done with 
00550     }
00551 
00552 
00553     /// Returns the intersection of two binary relations on states
00554     ///
00555     /// Parameters:
00556     ///   out_result: The intersection of r1 and r2
00557     ///   r1:         One binary relation on states
00558     ///   r2:         Another binary relation on states
00559     inline void
00560     intersect(BinaryRelation & out_result,
00561               BinaryRelation const & r1,
00562               BinaryRelation const & r2)
00563     {
00564       if (r1.domain != r2.domain || out_result.domain != r1.domain) {
00565         std::cerr << "Error: intersect (Buddy version): relations don't share a domain\n";
00566         exit(20);
00567       }
00568 
00569       out_result.myBdd = r1.myBdd & r2.myBdd;
00570     }
00571 
00572 
00573     /// Returns the union of two binary relations on states
00574     ///
00575     /// Parameters:
00576     ///   out_result: The union of r1 and r2
00577     ///   r1:         One binary relation on states
00578     ///   r2:         Another binary relation on states
00579     inline void
00580     union_(BinaryRelation & out_result,
00581            BinaryRelation const & r1,
00582            BinaryRelation const & r2)
00583     {
00584       if (r1.domain != r2.domain || out_result.domain != r1.domain) {
00585         std::cerr << "Error: intersect (Buddy version): relations don't share a domain\n";
00586         exit(20);
00587       }
00588 
00589       out_result.myBdd = r1.myBdd | r2.myBdd;
00590     }
00591 
00592 
00593 
00594     template<typename T>
00595     class VectorSet {
00596       std::deque<T> items;
00597 
00598     public:
00599       typedef typename std::deque<T>::iterator iterator;
00600       typedef typename std::deque<T>::const_iterator const_iterator;
00601            
00602       void insert(T const & item)  {
00603         if (find(item) == end()) {
00604           items.push_back(item);
00605         }
00606       }
00607 
00608       iterator find(T const & item) { return std::find(items.begin(), items.end(), item); }
00609       const_iterator find(T const & item) const { return std::find(items.begin(), items.end(), item); }
00610 
00611       bool empty() const { return items.empty(); }
00612 
00613       iterator begin() { return items.begin(); }
00614       const_iterator begin() const { return items.begin(); }
00615 
00616       iterator end() { return items.end(); }
00617       const_iterator end() const { return items.end(); }
00618 
00619       void erase(iterator i) { items.erase(i); }
00620       void erase(const_iterator i) { items.erase(i); }
00621     };
00622         
00623   } // namespace relations
00624 } // namespace wali
00625 
00626 
00627 
00628 // Yo, Emacs!
00629 // Local Variables:
00630 //   c-file-style: "ellemtel"
00631 //   c-basic-offset: 2
00632 // End:
00633 
00634 #endif