1 // -*- c++ -*-
2 //*****************************************************************************
3 /** @file CCuddDDFacade.h
4  *
5  * @author Alexander Dreyer
6  * @date 2010-08-25
7  *
8  * This file defines a facade for decision diagrams
9  *
10  * @par Copyright:
11  *   (c) by The PolyBoRi Team
12  *
13 **/
14 //*****************************************************************************
15 
16 #ifndef polybori_diagram_CCuddDDFacade_h
17 #define polybori_diagram_CCuddDDFacade_h
18 
19 // include basic definitions
20 #include <polybori/pbori_defs.h>
21 
22 #include <polybori/cudd/cudd.h>
23 #include <polybori/cudd/prefix.h>
24 #include "CApplyNodeFacade.h"
25 #include "CNodeCounter.h"
26 
27 #include <polybori/routines/pbori_routines_cuddext.h>
28 #include <polybori/common/CExtrusivePtr.h>
29 
30 // Getting iterator type for navigating through Cudd's ZDDs structure
31 #include <polybori/iterators/CCuddNavigator.h>
32 
33 // Getting iterator type for retrieving first term from Cudd's ZDDs
34 #include <polybori/iterators/CCuddFirstIter.h>
35 
36 // Getting iterator type for retrieving last term from Cudd's ZDDs
37 #include <polybori/iterators/CCuddLastIter.h>
38 
39 // Getting output iterator functionality
40 #include <polybori/iterators/PBoRiOutIter.h>
41 
42 // Getting error coe functionality
43 #include <polybori/except/PBoRiGenericError.h>
44 
45 // test input idx_type
46 #include <polybori/common/CCheckedIdx.h>
47 
48 #include <polybori/routines/pbori_algo.h>
49 #include <polybori/common/tags.h>
50 #include <polybori/routines/pbori_routines_hash.h>
51 
52 #include <boost/preprocessor/cat.hpp>
53 #include <boost/preprocessor/seq/for_each.hpp>
54 #include <boost/preprocessor/facilities/expand.hpp>
55 #include <boost/preprocessor/stringize.hpp>
56 #include <stdexcept>
57 #include <algorithm>
58 #include <numeric>
59 
60 BEGIN_NAMESPACE_PBORI
61 
62 
63 /// Releasing raw pointers to decision diagrams here
64 template <class DataType>
65 inline void
extrusive_ptr_release(const DataType & data,DdNode * ptr)66 extrusive_ptr_release(const DataType& data, DdNode* ptr) {
67    if (ptr != NULL) {
68      PBORI_PREFIX(Cudd_RecursiveDerefZdd)(data.getManager(), ptr);
69    }
70 }
71 
72 /// Incrememting reference counts to raw pointers to decision diagrams
73 template <class DataType>
74 inline void
extrusive_ptr_add_ref(const DataType &,DdNode * ptr)75 extrusive_ptr_add_ref(const DataType&, DdNode* ptr) {
76   if (ptr) PBORI_PREFIX(Cudd_Ref)(ptr);
77 }
78 
79 /** @class CCuddDDFacade
80  * @brief This template class defines a facade for decision diagrams.
81  *
82  **/
83 
84 #define PBORI_NAME_Product product
85 #define PBORI_NAME_UnateProduct unateProduct
86 #define PBORI_NAME_WeakDiv weakDivide
87 #define PBORI_NAME_Divide divide
88 #define PBORI_NAME_WeakDivF weakDivF
89 #define PBORI_NAME_DivideF divideF
90 #define PBORI_NAME_Union unite
91 #define PBORI_NAME_Intersect intersect
92 #define PBORI_NAME_Diff diff
93 #define PBORI_NAME_Subset1 subset1
94 #define PBORI_NAME_Subset0 subset0
95 #define PBORI_NAME_Change change
96 
97 #define PB_ZDD_APPLY(count, data, funcname) \
98   diagram_type BOOST_PP_CAT(PBORI_NAME_, funcname)(data rhs) const {    \
99     return apply(BOOST_PP_CAT(PBORI_PREFIX(Cudd_zdd), funcname),        \
100                               rhs); }
101 
102 template <class RingType, class DiagramType>
103 class CCuddDDFacade:
104   public CApplyNodeFacade<DiagramType, DdNode*>,
105   public CAuxTypes {
106 
107   /// Type of *this
108   typedef CCuddDDFacade self;
109 public:
110 
111   /// Type for output streams
112   typedef CTypes::ostream_type ostream_type;
113 
114   /// Type for diagrams
115   typedef RingType ring_type;
116 
117   /// Type for diagrams
118   typedef DiagramType diagram_type;
119 
120   /// Type for C-style struct
121   typedef DdNode node_type;
122 
123   /// Pointer type for nodes
124   typedef node_type* node_ptr;
125 
126   /// Type this is inherited from the following
127   typedef CApplyNodeFacade<diagram_type, node_ptr> base;
128 
129   /// Iterator type for retrieving first term from Cudd's ZDDs
130   typedef CCuddFirstIter first_iterator;
131 
132   /// Iterator type for retrieving last term from Cudd's ZDDs
133   typedef CCuddLastIter last_iterator;
134 
135   /// Iterator type for navigation throught Cudd's ZDDs structure
136   typedef CCuddNavigator navigator;
137 
138   /// This type has an easy equality check
139   typedef valid_tag easy_equality_property;
140 
141   /// Raw context
142   typedef typename ring_type::mgr_type mgr_type;
143 
144   /// Cudd's index type
145   typedef int cudd_idx_type;
146 
147   /// Cudd's index type
148   typedef CCheckedIdx checked_idx_type;
149 
150   /// Construct diagram from ring and node
CCuddDDFacade(const ring_type & ring,node_ptr node)151   CCuddDDFacade(const ring_type& ring, node_ptr node):
152     p_node(ring, node) {
153     checkAssumption(node != NULL);
154   }
155 
156   /// Construct from Manager and navigator
CCuddDDFacade(const ring_type & ring,const navigator & navi)157   CCuddDDFacade(const ring_type& ring, const navigator& navi):
158     p_node(ring, navi.getNode()) {
159     checkAssumption(navi.isValid());
160   }
161   /// Construct new node from manager, index, and navigators
CCuddDDFacade(const ring_type & ring,idx_type idx,navigator thenNavi,navigator elseNavi)162   CCuddDDFacade(const ring_type& ring,
163                idx_type idx, navigator thenNavi, navigator elseNavi):
164     p_node(ring, getNewNode(ring, idx, thenNavi, elseNavi)) { }
165 
166   /// Construct new node from manager, index, and common navigator for then and
167   /// else-branches
CCuddDDFacade(const ring_type & ring,idx_type idx,navigator navi)168   CCuddDDFacade(const ring_type& ring,
169                idx_type idx, navigator navi):
170     p_node(ring, getNewNode(ring, idx, navi, navi)) { }
171 
172   /// Construct new node
CCuddDDFacade(idx_type idx,const self & thenDD,const self & elseDD)173   CCuddDDFacade(idx_type idx, const self& thenDD, const self& elseDD):
174     p_node(thenDD.ring(), getNewNode(idx, thenDD, elseDD)) { }
175 
176   /// Default constructor
CCuddDDFacade()177   private: CCuddDDFacade(): p_node()  {}  ///  @todo NULL?
178     public:
179   /// Copy constructor
CCuddDDFacade(const self & from)180   CCuddDDFacade(const self &from): p_node(from.p_node) {}
181 
182   /// Destructor
~CCuddDDFacade()183   ~CCuddDDFacade() {}
184 
185   /// Assignment operator
186   diagram_type& operator=(const diagram_type& rhs) {
187     p_node = rhs.p_node;
188     return static_cast<diagram_type&>(*this);
189   }
190 
191   /// @note Preprocessor generated members
192   /// @code
193   BOOST_PP_SEQ_FOR_EACH(PB_ZDD_APPLY, const diagram_type&,
194     (Product)(UnateProduct)(WeakDiv)(Divide)(WeakDivF)(DivideF)
195     (Union)(Intersect)(Diff))
196 
197   BOOST_PP_SEQ_FOR_EACH(PB_ZDD_APPLY, int, (Subset1)(Subset0)(Change))
198   /** @endcode */
199 
200   /// Performs the inclusion test for ZDDs
implies(const self & rhs)201   bool implies(const self& rhs) const {
202     return PBORI_PREFIX(Cudd_zddDiffConst)(getManager(), getNode(), rhs.getNode()) ==
203       PBORI_PREFIX(Cudd_ReadZero)(getManager());
204   }
205 
206 
207   /// @name Functions for print useful information
208   //@{
printIntern(std::ostream & os)209   std::ostream& printIntern(std::ostream& os) const {
210     os << getNode() <<": ";
211 
212     if (isZero())
213       os << "empty";
214     else
215       os << nNodes() << " nodes " <<  count() << "terms";
216 
217     return os;
218 
219   }
PrintMinterm()220   void PrintMinterm() const  {
221     checkAssumption(apply(PBORI_PREFIX(Cudd_zddPrintMinterm)) == 1);
222   }
223   //@}
224 
225   /// Determine the number of terms
count()226   size_type count() const { return dd_long_count<size_type>(navigation());  }
227 
228   /// Appriximate the number of terms
countDouble()229   double countDouble() const { return dd_long_count<double>(navigation()); }
230 
231   /// Get index of curent node
rootIndex()232   size_type rootIndex() const { return PBORI_PREFIX(Cudd_NodeReadIndex)(getNode()); }
233 
234   /// Number of nodes in the current decision diagram
nNodes()235   size_type nNodes() const { return CNodeCounter<navigator>()(navigation()); }
236 
237   /// Number of references pointing here
refCount()238   size_type refCount() const {
239     PBORI_ASSERT(getNode() != NULL);
240     return PBORI_PREFIX(Cudd_Regular)(getNode())->ref;
241   }
242 
243   /// Test whether diagram represents the empty set
isZero()244   bool isZero() const { return getNode() == PBORI_PREFIX(Cudd_ReadZero)(getManager()); }
245 
246   /// Test whether diagram represents the empty set
isOne()247   bool isOne() const { return getNode() == DD_ONE(getManager()); }
248 
249   /// Get reference to ring
ring()250   const ring_type& ring() const { return p_node.data(); }
251 
252   /// Get raw node structure
getNode()253   node_ptr getNode() const { return p_node.get(); }
254 
255   /// Get raw decision diagram manager
getManager()256   mgr_type* getManager() const { return p_node.data().getManager(); }
257 
258 protected:
259 
260   /// Apply C-style function to diagram
261   using base::apply;
262 
263 
264   template <class ResultType>
memApply(ResultType (* func)(mgr_type *,node_ptr))265   ResultType memApply(ResultType (*func)(mgr_type *, node_ptr)) const {
266     ResultType result = apply(func);
267     checkAssumption(result != (ResultType) CUDD_OUT_OF_MEM);
268     return result;
269   }
270 
271   /// Check whether previous decision diagram operation for validity
checkAssumption(bool isValid)272   void checkAssumption(bool isValid) const {
273     if PBORI_UNLIKELY(!isValid)
274       throw std::runtime_error(error_text(getManager()));
275   }
276 
277     /// temporarily (needs to be more generic) (similar fct in pbori_algo.h)
278   template<class ManagerType, class ReverseIterator, class MultReverseIterator>
279   diagram_type
cudd_generate_multiples(const ManagerType & mgr,ReverseIterator start,ReverseIterator finish,MultReverseIterator multStart,MultReverseIterator multFinish)280   cudd_generate_multiples(const ManagerType& mgr,
281                           ReverseIterator start, ReverseIterator finish,
282                           MultReverseIterator multStart,
283                           MultReverseIterator multFinish) const {
284 
285     // signed case
286     while ((multStart != multFinish) && (*multStart > CTypes::max_idx))
287       ++multStart;
288     // unsigned case
289     while ((multStart != multFinish) && (*multStart < 0))
290       --multFinish;
291 
292     DdNode* prev( (getManager())->one );
293 
294     DdNode* zeroNode( (getManager())->zero );
295 
296     PBORI_PREFIX(Cudd_Ref)(prev);
297     while(start != finish) {
298 
299       while((multStart != multFinish) && (*start < *multStart)) {
300 
301         DdNode* result = PBORI_PREFIX(cuddUniqueInterZdd)( getManager(), *multStart,
302                                              prev, prev );
303 
304         PBORI_PREFIX(Cudd_Ref)(result);
305         PBORI_PREFIX(Cudd_RecursiveDerefZdd)(getManager(), prev);
306 
307         prev = result;
308         ++multStart;
309 
310       };
311 
312       DdNode* result = PBORI_PREFIX(cuddUniqueInterZdd)( getManager(), *start,
313                                            prev, zeroNode );
314 
315       PBORI_PREFIX(Cudd_Ref)(result);
316       PBORI_PREFIX(Cudd_RecursiveDerefZdd)(getManager(), prev);
317 
318       prev = result;
319 
320 
321       if((multStart != multFinish) && (*start == *multStart))
322         ++multStart;
323 
324 
325       ++start;
326     }
327 
328     while(multStart != multFinish) {
329 
330       DdNode* result = PBORI_PREFIX(cuddUniqueInterZdd)( getManager(), *multStart,
331                                            prev, prev );
332 
333       PBORI_PREFIX(Cudd_Ref)(result);
334       PBORI_PREFIX(Cudd_RecursiveDerefZdd)(getManager(), prev);
335 
336       prev = result;
337       ++multStart;
338 
339     };
340 
341     PBORI_PREFIX(Cudd_Deref)(prev);
342 
343 
344     return diagram_type(mgr, prev);
345   }
346 
347     /// temporarily (needs to be more generic) (similar fct in pbori_algo.h)
348   template<class ManagerType, class ReverseIterator>
349   diagram_type
cudd_generate_divisors(const ManagerType & mgr,ReverseIterator start,ReverseIterator finish)350   cudd_generate_divisors(const ManagerType& mgr,
351                          ReverseIterator start, ReverseIterator finish) const {
352 
353 
354     DdNode* prev= (getManager())->one;
355 
356     PBORI_PREFIX(Cudd_Ref)(prev);
357     while(start != finish) {
358 
359       DdNode* result = PBORI_PREFIX(cuddUniqueInterZdd)( getManager(),
360                                            *start, prev, prev);
361 
362       PBORI_PREFIX(Cudd_Ref)(result);
363       PBORI_PREFIX(Cudd_RecursiveDerefZdd)(getManager(), prev);
364 
365       prev = result;
366       ++start;
367     }
368 
369     PBORI_PREFIX(Cudd_Deref)(prev);
370     ///@todo Next line needs generalization
371       return diagram_type(mgr, prev);
372 
373 }
374 public:
375 
376   /// Union Xor
Xor(const diagram_type & rhs)377   diagram_type Xor(const diagram_type& rhs) const {
378     if (rhs.isZero())
379       return *this;
380 
381     //    return apply(pboriPBORI_PREFIX(Cudd_zddUnionXor), rhs);
382     base::checkSameManager(rhs);
383     return diagram_type(ring(), pboriCudd_zddUnionXor(getManager(), getNode(), rhs.getNode()) );
384   }
385 
386   /// Division with first term of right-hand side
divideFirst(const diagram_type & rhs)387   diagram_type divideFirst(const diagram_type& rhs) const {
388 
389     diagram_type result(*this);
390     PBoRiOutIter<diagram_type, idx_type, subset1_assign<diagram_type> >  outiter(result);
391     std::copy(rhs.firstBegin(), rhs.firstEnd(), outiter);
392 
393     return result;
394   }
395 
396 
397   /// Get number of nodes in decision diagram
print(ostream_type & os)398   ostream_type& print(ostream_type& os) const {
399 
400     printIntern(os) << std::endl;;
401     PrintMinterm();
402 
403     return os;
404   }
405 
406 
407   /// Get numbers of used variables
408   //  size_type nSupport() const { return apply(PBORI_PREFIX(PBORI_PREFIX(Cudd_SupportSize));) }
409 
410   /// Start of first term
firstBegin()411   first_iterator firstBegin() const {
412     return first_iterator(navigation());
413   }
414 
415   /// Finish of first term
firstEnd()416   first_iterator firstEnd() const {
417     return first_iterator();
418   }
419 
420   /// Start of first term
lastBegin()421   last_iterator lastBegin() const {
422     return last_iterator(getNode());
423   }
424 
425   /// Finish of first term
lastEnd()426   last_iterator lastEnd() const {
427     return last_iterator();
428   }
429 
430   /// Get decison diagram representing the multiples of the first term
firstMultiples(const std::vector<idx_type> & input_multipliers)431   diagram_type firstMultiples(const std::vector<idx_type>& input_multipliers) const {
432 
433     std::set<idx_type> multipliers(input_multipliers.begin(), input_multipliers.end());
434     std::vector<idx_type> indices( std::distance(firstBegin(), firstEnd()) );
435 
436     std::copy( firstBegin(), firstEnd(), indices.begin() );
437 
438     return cudd_generate_multiples( ring(),
439                                     indices.rbegin(), indices.rend(),
440                                     multipliers.rbegin(),
441                                     multipliers.rend() );
442   }
443 
444   /// Get decison diagram representing the divisors of the first term
firstDivisors()445   diagram_type firstDivisors() const {
446 
447     std::vector<idx_type> indices( std::distance(firstBegin(), firstEnd()) );
448 
449     std::copy( firstBegin(), firstEnd(), indices.begin() );
450 
451     return cudd_generate_divisors(ring(), indices.rbegin(), indices.rend());
452   }
453 
454   /// Navigate through ZDD by incrementThen(), incrementElse(), and terminated()
navigation()455   navigator navigation() const {
456     return navigator(getNode());
457   }
458 
459   /// Checks whether the the diagram corresponds to {} or {{}} (polynomial 0 or 1)
isConstant()460   bool_type isConstant() const {
461     return (getNode()) && PBORI_PREFIX(Cudd_IsConstant)(getNode());
462   }
463 
464 
465 
466 private:
467 
468   /// Save variant for generating fast ite operation (when idx < root index)
469   static node_ptr
getNewNode(const ring_type & ring,checked_idx_type idx,navigator thenNavi,navigator elseNavi)470   getNewNode(const ring_type& ring, checked_idx_type idx,
471              navigator thenNavi, navigator elseNavi) {
472 
473     if ((idx >= *thenNavi) || (idx >= *elseNavi))
474       throw PBoRiGenericError<CTypes::invalid_ite>();
475 
476     return PBORI_PREFIX(cuddZddGetNode)(ring.getManager(), idx,
477                           thenNavi.getNode(), elseNavi.getNode());
478   }
479 
480   /// Convenience version for diagrams
481   static node_ptr
getNewNode(idx_type idx,const self & thenDD,const self & elseDD)482   getNewNode(idx_type idx, const self& thenDD, const self& elseDD) {
483     thenDD.checkSameManager(elseDD);
484     return getNewNode(thenDD.ring(),
485                       idx, thenDD.navigation(), elseDD.navigation());
486   }
487 
488 private:
489   /// Smart pointer, which uses the ring for deallocating the node
490   CExtrusivePtr<ring_type, node_type> p_node;
491 };
492 
493 
494 #undef PB_ZDD_APPLY
495 
496 END_NAMESPACE_PBORI
497 
498 #endif
499