TdZdd  1.1
A top-down/breadth-first decision diagram manipulation framework
DdStructure.hpp
1 /*
2  * TdZdd: a Top-down/Breadth-first Decision Diagram Manipulation Framework
3  * by Hiroaki Iwashita <iwashita@erato.ist.hokudai.ac.jp>
4  * Copyright (c) 2014 ERATO MINATO Project
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24 
25 #pragma once
26 
27 #include <algorithm>
28 #include <cassert>
29 #include <climits>
30 #include <ostream>
31 #include <set>
32 #include <stdexcept>
33 #include <vector>
34 
35 #include "DdEval.hpp"
36 #include "DdSpec.hpp"
37 #include "dd/DdBuilder.hpp"
38 #include "dd/DdReducer.hpp"
39 #include "dd/Node.hpp"
40 #include "dd/NodeTable.hpp"
41 #include "eval/Cardinality.hpp"
42 #include "op/Lookahead.hpp"
43 #include "op/Unreduction.hpp"
44 #include "util/demangle.hpp"
45 #include "util/MessageHandler.hpp"
46 #include "util/MyHashTable.hpp"
47 #include "util/MyVector.hpp"
48 
49 namespace tdzdd {
50 
55 template<int ARITY>
56 class DdStructure: public DdSpec<DdStructure<ARITY>,NodeId,ARITY> {
57  NodeTableHandler<ARITY> diagram;
58  NodeId root_;
59  bool useMP;
60 
61 public:
66  root_(0), useMP(false) {
67  }
68 
69 // /*
70 // * Imports the ZDD that can be broken.
71 // * @param o the ZDD.
72 // */
73 // void moveFrom(DdStructure& o) {
74 // diagram.moveAssign(o.diagram);
75 // }
76 
82  DdStructure(int n, bool useMP = false) :
83  diagram(n + 1), root_(1), useMP(useMP) {
84  assert(n >= 0);
85  NodeTableEntity<ARITY>& table = diagram.privateEntity();
86  NodeId f(1);
87 
88  for (int i = 1; i <= n; ++i) {
89  table.initRow(i, 1);
90  for (int b = 0; b < ARITY; ++b) {
91  table[i][0].branch[b] = f;
92  }
93  f = NodeId(i, 0);
94  }
95 
96  root_ = f;
97  }
98 
104  template<typename SPEC>
105  DdStructure(DdSpecBase<SPEC,ARITY> const& spec, bool useMP = false) :
106  useMP(useMP) {
107 #ifdef _OPENMP
108  if (useMP) constructMP_(spec.entity());
109  else
110 #endif
111  construct_(spec.entity());
112  }
113 
114 private:
115  template<typename SPEC>
116  void construct_(SPEC const& spec) {
117  MessageHandler mh;
118  mh.begin(typenameof(spec));
119  DdBuilder<SPEC> zc(spec, diagram);
120  int n = zc.initialize(root_);
121 
122  if (n > 0) {
123  mh.setSteps(n);
124  for (int i = n; i > 0; --i) {
125  zc.construct(i);
126  mh.step();
127  }
128  }
129  else {
130  mh << " ...";
131  }
132 
133  mh.end(size());
134  }
135 
136  template<typename SPEC>
137  void constructMP_(SPEC const& spec) {
138  MessageHandler mh;
139  mh.begin(typenameof(spec));
140  DdBuilderMP<SPEC> zc(spec, diagram);
141  int n = zc.initialize(root_);
142 
143  if (n > 0) {
144 #ifdef _OPENMP
145  mh << " " << omp_get_max_threads() << "x";
146 #endif
147  mh.setSteps(n);
148  for (int i = n; i > 0; --i) {
149  zc.construct(i);
150  mh.step();
151  }
152  }
153  else {
154  mh << " ...";
155  }
156 
157  mh.end(size());
158  }
159 
160 public:
165  template<typename SPEC>
166  void zddSubset(DdSpecBase<SPEC,ARITY> const& spec) {
167 #ifdef _OPENMP
168  if (useMP) zddSubsetMP_(spec.entity());
169  else
170 #endif
171  zddSubset_(spec.entity());
172  }
173 
174 private:
175  template<typename SPEC>
176  void zddSubset_(SPEC const& spec) {
177  MessageHandler mh;
178  mh.begin(typenameof(spec));
179  NodeTableHandler<ARITY> tmpTable;
180  ZddSubsetter<SPEC> zs(diagram, spec, tmpTable);
181  int n = zs.initialize(root_);
182 
183  if (n > 0) {
184  mh.setSteps(n);
185  for (int i = n; i > 0; --i) {
186  zs.subset(i);
187  diagram.derefLevel(i);
188  mh.step();
189  }
190  }
191  else {
192  mh << " ...";
193  }
194 
195  diagram = tmpTable;
196  mh.end(size());
197  }
198 
199  template<typename SPEC>
200  void zddSubsetMP_(SPEC const& spec) {
201  MessageHandler mh;
202  mh.begin(typenameof(spec));
203  NodeTableHandler<ARITY> tmpTable;
204  ZddSubsetterMP<SPEC> zs(diagram, spec, tmpTable);
205  int n = zs.initialize(root_);
206 
207  if (n > 0) {
208 #ifdef _OPENMP
209  mh << " " << omp_get_max_threads() << "x";
210 #endif
211  mh.setSteps(n);
212  for (int i = n; i > 0; --i) {
213  zs.subset(i);
214  diagram.derefLevel(i);
215  mh.step();
216  }
217  }
218  else {
219  mh << " ...";
220  }
221 
222  diagram = tmpTable;
223  mh.end(size());
224  }
225 
226 public:
232  bool useMultiProcessors(bool flag = true) {
233  bool old = useMP;
234  useMP = flag;
235  return old;
236  }
237 
242  NodeId& root() {
243  return root_;
244  }
245 
250  NodeId root() const {
251  return root_;
252  }
253 
260  NodeId child(NodeId f, int b) const {
261  return diagram->child(f, b);
262  }
263 
268  NodeTableHandler<ARITY>& getDiagram() {
269  return diagram;
270  }
271 
276  NodeTableHandler<ARITY> const& getDiagram() const {
277  return diagram;
278  }
279 
284  int topLevel() const {
285  return root_.row();
286  }
287 
292  size_t size() const {
293  return diagram->size();
294  }
295 
300  bool empty() const {
301  return root_ == 0;
302  }
303 
308  bool operator==(DdStructure const& o) const {
309  int n = root_.row();
310  if (n != o.root_.row()) return false;
311  if (n == 0) return root_ == o.root_;
312  if (root_ == o.root_ && &*diagram == &*o.diagram) return true;
313  if (size() > o.size()) return o.operator==(*this);
314 
316  DataTable<NodeId> equiv(n + 1);
317  {
318  size_t om = (*o.diagram)[0].size();
319  equiv[0].resize(om);
320  for (size_t j = 0; j < om; ++j) {
321  equiv[0][j] = j;
322  }
323  }
324 
325  for (int i = 1; i <= n; ++i) {
326  size_t m = (*diagram)[i].size();
327  uniq.initialize(m * 2);
328 
329  for (size_t j = 0; j < m; ++j) {
330  uniq[(*diagram)[i][j]] = j;
331  }
332 
333  size_t om = (*o.diagram)[i].size();
334  equiv[i].resize(om);
335 
336  for (size_t j = 0; j < om; ++j) {
337  InitializedNode<ARITY> node;
338 
339  for (int b = 0; b < ARITY; ++b) {
340  NodeId f = (*o.diagram)[i][j].branch[b];
341  node.branch[b] = equiv[f.row()][f.col()];
342  }
343 
344  size_t* p = uniq.getValue(node);
345  equiv[i][j] = NodeId(i, (p != 0) ? *p : m);
346  }
347  }
348 
349  return root_ == equiv[o.root_.row()][o.root_.col()];
350  }
351 
356  bool operator!=(DdStructure const& o) const {
357  return !operator==(o);
358  }
359 
364  void qddReduce() {
365  reduce<false,false>();
366  }
367 
372  void bddReduce() {
373  reduce<true,false>();
374  }
375 
380  void zddReduce() {
381  reduce<false,true>();
382  }
383 
389  template<bool BDD, bool ZDD>
390  void reduce() {
391  MessageHandler mh;
392  mh.begin("reduction");
393  int n = root_.row();
394 
395 #ifdef _OPENMP
396  if (useMP) mh << " " << omp_get_max_threads() << "x";
397 #endif
398 
399  DdReducer<ARITY,BDD,ZDD> zr(diagram, useMP);
400  zr.setRoot(root_);
401 
402  mh.setSteps(n);
403  for (int i = 1; i <= n; ++i) {
404  zr.reduce(i, useMP);
405  mh.step();
406  }
407 
408  mh.end(size());
409  }
410 
411 public:
416  DdStructure bdd2zdd(int numVars) const {
417  return DdStructure(
418  ZddLookahead<BddUnreduction<DdStructure> >(
419  BddUnreduction<DdStructure>(*this, numVars)), useMP);
420  }
421 
426  DdStructure zdd2bdd(int numVars) const {
427  return DdStructure(
428  BddLookahead<ZddUnreduction<DdStructure> >(
429  ZddUnreduction<DdStructure>(*this, numVars)), useMP);
430  }
431 
437  std::string bddCardinality(int numVars) const {
439  }
440 
445  std::string zddCardinality() const {
447  }
448 
454  template<typename S, typename T, typename R>
455  R evaluate(DdEval<S,T,R> const& evaluator) const {
456  S eval(evaluator.entity()); // copied
457 #ifdef _OPENMP
458  bool useMP = this->useMP && eval.isThreadSafe();
459 #endif
460  bool msg = eval.showMessages();
461  int n = root_.row();
462 
463  MessageHandler mh;
464  if (msg) {
465  mh.begin(typenameof(eval));
466 #ifdef _OPENMP
467  if (useMP) mh << " " << omp_get_max_threads() << "x";
468 #endif
469  mh.setSteps(n);
470  }
471 
472 #ifdef _OPENMP
473  int threads = useMP ? omp_get_max_threads() : 0;
474  MyVector<S> evals(threads, eval);
475 #endif
476  eval.initialize(n);
477 #ifdef _OPENMP
478  if (useMP)
479 #pragma omp parallel
480  {
481  int k = omp_get_thread_num();
482  evals[k].initialize(n);
483  }
484 #endif
485 
486  DataTable<T> work(diagram->numRows());
487  {
488  size_t const m = (*diagram)[0].size();
489  assert(m >= 2);
490  work[0].resize(m);
491  for (size_t j = 0; j < m; ++j) {
492  eval.evalTerminal(work[0][j], j);
493  }
494  }
495 
496  for (int i = 1; i <= n; ++i) {
497  MyVector<Node<ARITY> > const& node = (*diagram)[i];
498  size_t const m = node.size();
499  work[i].resize(m);
500 
501 #ifdef _OPENMP
502  if (useMP)
503 #pragma omp parallel
504  {
505  int k = omp_get_thread_num();
506 
507 #pragma omp for schedule(static)
508  for (intmax_t j = 0; j < intmax_t(m); ++j) {
509  DdValues<T,ARITY> values;
510  for (int b = 0; b < ARITY; ++b) {
511  NodeId f = node[j].branch[b];
512  values.setReference(b, work[f.row()][f.col()]);
513  values.setLevel(b, f.row());
514  }
515  evals[k].evalNode(work[i][j], i, values);
516  }
517  }
518  else
519 #endif
520  for (size_t j = 0; j < m; ++j) {
521  DdValues<T,ARITY> values;
522  for (int b = 0; b < ARITY; ++b) {
523  NodeId f = node[j].branch[b];
524  values.setReference(b, work[f.row()][f.col()]);
525  values.setLevel(b, f.row());
526  }
527  eval.evalNode(work[i][j], i, values);
528  }
529 
530  MyVector<int> const& levels = diagram->lowerLevels(i);
531  for (int const* t = levels.begin(); t != levels.end(); ++t) {
532  work[*t].clear();
533  eval.destructLevel(*t);
534  }
535 #ifdef _OPENMP
536  if (useMP)
537 #pragma omp parallel
538  {
539  int k = omp_get_thread_num();
540  for (int const* t = levels.begin(); t != levels.end(); ++t) {
541  evals[k].destructLevel(*t);
542  }
543  }
544 #endif
545  if (msg) mh.step();
546  }
547 
548  R retval = eval.getValue(work[root_.row()][root_.col()]);
549  if (msg) mh.end();
550  return retval;
551  }
552 
557  struct Selection {
558  NodeId node;
559  bool val;
560 
561  Selection() :
562  val(false) {
563  }
564 
565  Selection(NodeId node, bool val) :
566  node(node), val(val) {
567  }
568 
569  bool operator==(Selection const& o) const {
570  return node == o.node && val == o.val;
571  }
572  };
573 
574  DdStructure const& dd;
575  int cursor;
576  std::vector<Selection> path;
577  std::set<int> itemset;
578 
579  public:
580  const_iterator(DdStructure const& dd, bool begin) :
581  dd(dd), cursor(begin ? -1 : -2), path(), itemset() {
582  if (begin) next(dd.root_);
583  }
584 
585  const_iterator& operator++() {
586  next(NodeId(0, 0));
587  return *this;
588  }
589 
590  std::set<int> const& operator*() const {
591  return itemset;
592  }
593 
594  std::set<int> const* operator->() const {
595  return &itemset;
596  }
597 
598  bool operator==(const_iterator const& o) const {
599  return cursor == o.cursor && path == o.path;
600  }
601 
602  bool operator!=(const_iterator const& o) const {
603  return !operator==(o);
604  }
605 
606  private:
607  void next(NodeId f) {
608  itemset.clear();
609 
610  for (;;) {
611  while (f > 1) { /* down */
612  Node<ARITY> const& s = (*dd.diagram)[f.row()][f.col()];
613 
614  if (s.branch[0] != 0) {
615  cursor = path.size();
616  path.push_back(Selection(f, false));
617  f = s.branch[0];
618  }
619  else {
620  path.push_back(Selection(f, true));
621  f = s.branch[1];
622  }
623  }
624 
625  if (f == 1) break; /* found */
626 
627  for (; cursor >= 0; --cursor) { /* up */
628  Selection& sel = path[cursor];
629  Node<ARITY> const& ss =
630  (*dd.diagram)[sel.node.row()][sel.node.col()];
631  if (sel.val == false && ss.branch[1] != 0) {
632  f = sel.node;
633  sel.val = true;
634  path.resize(cursor + 1);
635  f = dd.diagram->child(f, 1);
636  break;
637  }
638  }
639 
640  if (cursor < 0) { /* end() state */
641  cursor = -2;
642  path.clear();
643  return;
644  }
645  }
646 
647  for (size_t i = 0; i < path.size(); ++i) {
648  if (path[i].val) {
649  itemset.insert(path[i].node.row());
650  }
651  }
652  }
653  };
654 
662  return const_iterator(*this, true);
663  }
664 
670  const_iterator end() const {
671  return const_iterator(*this, false);
672  }
673 
677  int getRoot(NodeId& f) const {
678  f = root_;
679  return (f == 1) ? -1 : f.row();
680  }
681 
685  int getChild(NodeId& f, int level, int value) const {
686  assert(level > 0 && level == f.row());
687  assert(0 <= value && value < ARITY);
688  f = child(f, value);
689  return (f.row() > 0) ? f.row() : -f.col();
690  }
691 
695  size_t hashCode(NodeId const& f) const {
696  return f.hash();
697  }
698 
704  void dumpSapporo(std::ostream& os) const {
705  int const n = diagram->numRows() - 1;
706  size_t const l = size();
707 
708  os << "_i " << n << "\n";
709  os << "_o 1\n";
710  os << "_n " << l << "\n";
711 
712  DataTable<size_t> nodeId(diagram->numRows());
713  size_t k = 0;
714 
715  for (int i = 1; i <= n; ++i) {
716  size_t const m = (*diagram)[i].size();
717  Node<ARITY> const* p = (*diagram)[i].data();
718  nodeId[i].resize(m);
719 
720  for (size_t j = 0; j < m; ++j, ++p) {
721  k += 2;
722  nodeId[i][j] = k;
723  os << k << " " << i;
724 
725  for (int c = 0; c <= 1; ++c) {
726  NodeId fc = p->branch[c];
727  if (fc == 0) {
728  os << " F";
729  }
730  else if (fc == 1) {
731  os << " T";
732  }
733  else {
734  os << " " << nodeId[fc.row()][fc.col()];
735  }
736  }
737 
738  os << "\n";
739  }
740 
741  MyVector<int> const& levels = diagram->lowerLevels(i);
742  for (int const* t = levels.begin(); t != levels.end(); ++t) {
743  nodeId[*t].clear();
744  }
745  }
746 
747  os << nodeId[root_.row()][root_.col()] << "\n";
748  assert(k == l * 2);
749  }
750 };
751 
752 } // namespace tdzdd
NodeId child(NodeId f, int b) const
Gets a child node.
Closed hash map implementation.
NodeTableHandler< ARITY > & getDiagram()
Gets the diagram.
DdStructure(int n, bool useMP=false)
Universal ZDD constructor.
Definition: DdStructure.hpp:82
void zddReduce()
ZDD reduction.
void bddReduce()
BDD reduction.
DdStructure()
Default constructor.
Definition: DdStructure.hpp:65
Ordered n-ary decision diagram structure.
Definition: DdStructure.hpp:56
int initialize(NodeId &root)
Initializes the builder.
Definition: DdBuilder.hpp:998
NodeId root() const
Gets the root node.
Basic breadth-first DD builder.
Definition: DdBuilder.hpp:175
bool empty() const
Checks if DD is a 0-terminal only.
ZDD evaluator that counts the number of elements.
void reduce()
BDD/ZDD reduction.
NodeId & root()
Gets the root node.
Breadth-first ZDD subset builder.
Definition: DdBuilder.hpp:678
std::string bddCardinality(int numVars) const
Counts the number of minterms of the function represented by this BDD.
void subset(int i)
Builds one level.
Definition: DdBuilder.hpp:771
DdStructure bdd2zdd(int numVars) const
Transforms a BDD into a ZDD.
void zddSubset(DdSpecBase< SPEC, ARITY > const &spec)
ZDD subsetting.
int getChild(NodeId &f, int level, int value) const
Implements DdSpec.
Base class of DD specs.
Definition: DdSpec.hpp:68
Multi-threaded breadth-first ZDD subset builder.
Definition: DdBuilder.hpp:958
NodeTableHandler< ARITY > const & getDiagram() const
Gets the diagram.
void subset(int i)
Builds one level.
Definition: DdBuilder.hpp:1048
BDD evaluator that counts the number of elements.
int initialize(NodeId &root)
Initializes the builder.
Definition: DdBuilder.hpp:471
Collection of child node values/levels for DdEval::evalNode function interface.
Definition: DdEval.hpp:39
std::string zddCardinality() const
Counts the number of sets in the family of sets represented by this ZDD.
Base class of DD evaluators.
Definition: DdEval.hpp:102
V * getValue(K const &key) const
Get the value that is already registered.
size_t size() const
Gets the number of nonterminal nodes.
DdStructure(DdSpecBase< SPEC, ARITY > const &spec, bool useMP=false)
DD construction.
const_iterator begin() const
Returns an iterator to the first instance, which is viewed as a collection of item numbers...
const_iterator end() const
Returns an iterator to the element following the last instance.
R evaluate(DdEval< S, T, R > const &evaluator) const
Evaluates the DD from the bottom to the top.
int initialize(NodeId &root)
Initializes the builder.
Definition: DdBuilder.hpp:231
void qddReduce()
QDD reduction.
bool useMultiProcessors(bool flag=true)
Enables or disables multiple processor algorithms.
int topLevel() const
Gets the level of the root node.
bool operator!=(DdStructure const &o) const
Checks structural inequivalence with another DD.
Abstract class of DD specifications using scalar states.
Definition: DdSpec.hpp:257
int getRoot(NodeId &f) const
Implements DdSpec.
DdStructure zdd2bdd(int numVars) const
Transforms a ZDD into a BDD.
bool operator==(DdStructure const &o) const
Checks structural equivalence with another DD.
int initialize(NodeId &root)
Initializes the builder.
Definition: DdBuilder.hpp:721
void dumpSapporo(std::ostream &os) const
Dumps the node table in Sapporo ZDD format.
void construct(int i)
Builds one level.
Definition: DdBuilder.hpp:494
size_t hashCode(NodeId const &f) const
Implements DdSpec.
void construct(int i)
Builds one level.
Definition: DdBuilder.hpp:258
Multi-threaded breadth-first DD builder.
Definition: DdBuilder.hpp:395
Iterator on a set of integer vectors represented by a DD.