Adiar  2.1.0
An External Memory Decision Diagram Library
builder.h
1 #ifndef ADIAR_BUILDER_H
2 #define ADIAR_BUILDER_H
3 
4 #include <adiar/bdd/bdd_policy.h>
5 #include <adiar/exception.h>
6 #include <adiar/zdd/zdd_policy.h>
7 
8 #include <adiar/internal/assert.h>
9 #include <adiar/internal/data_types/node.h>
10 #include <adiar/internal/data_types/uid.h>
11 #include <adiar/internal/io/node_file.h>
12 #include <adiar/internal/io/node_writer.h>
13 #include <adiar/internal/memory.h>
14 
25 
26 namespace adiar
27 {
32 
39  struct builder_shared
40  {};
42 
43  template <typename dd_policy>
44  class builder;
45 
52  template <typename Policy>
54  {
55  public:
56  friend class builder<Policy>;
57 
58  private:
62  // TODO: rename to 'ptr ptr' when using complement edges
63  /*const*/ internal::node::uid_type uid;
64 
68  /*const*/ shared_ptr<const builder_shared> builder_ref;
69 
74  /*const*/ shared_ptr<bool> unreferenced = make_shared<bool>(true);
75 
76  public:
80  builder_ptr() = default;
81 
85  builder_ptr(const builder_ptr& bp) = default;
86 
90  builder_ptr(builder_ptr&& bp) = default;
91 
92  public:
97  operator=(const builder_ptr& bp) = default;
98 
102  builder_ptr&
103  operator=(builder_ptr&& bp) = default;
104 
105  private:
115  builder_ptr(const internal::node::uid_type& p, const shared_ptr<const builder_shared>& sp)
116  : uid(p)
117  , builder_ref(sp)
118  {}
119  };
120 
127 
134 
144  template <typename Policy>
145  class builder
146  {
147  private:
148  // See 'attach_if_needed()' for default values of most values below.
149 
153  using node_type = typename Policy::node_type;
154 
159 
163  internal::node_writer nw;
164 
168  bool created_terminal;
169 
173  bool terminal_val;
174 
178  typename Policy::label_type current_label;
179 
183  typename Policy::id_type current_id;
184 
188  size_t unref_nodes;
189 
193  shared_ptr<builder_shared> builder_ref;
194 
195  public:
199  builder() noexcept
200  {}
201 
205  ~builder() noexcept
206  {}
207 
208  public:
235  add_node(typename Policy::label_type label,
236  const builder_ptr<Policy>& low,
237  const builder_ptr<Policy>& high)
238  {
239  attach_if_needed();
240 
241  // Check validity of input
242  if (low.builder_ref != builder_ref || high.builder_ref != builder_ref) {
243  throw invalid_argument("Cannot use pointers from a different builder");
244  }
245  if (label > Policy::max_label) { throw invalid_argument("Nodes must have a valid label"); }
246  if (label > current_label) { throw invalid_argument("Nodes must be added bottom-up"); }
247  if (low.uid.is_node() && low.uid.label() <= label) {
248  throw invalid_argument("Low child must point to a node with higher label");
249  }
250  if (high.uid.is_node() && high.uid.label() <= label) {
251  throw invalid_argument("High child must point to a node with higher label");
252  }
253 
254  // Update label and ID if necessary
255  if (label < current_label) {
256  current_label = label;
257  current_id = Policy::max_id;
258  }
259 
260  // Create potential node
261  const node_type n(label, current_id, low.uid, high.uid);
262 
263  // Check whether this node is 'redundant'
264  const typename node_type::uid_type res_uid = Policy::reduction_rule(n);
265 
266  if (res_uid.is_terminal()) {
267  created_terminal = true;
268  terminal_val = res_uid.value();
269  }
270 
271  if (res_uid == low.uid) { return low; }
272  if (res_uid == high.uid) { return high; }
273 
274  // Push node to file
275  nw.push(n);
276  unref_nodes++;
277  current_id--;
278 
279  // Update count of unreferenced nodes
280  bool& low_unref = *low.unreferenced;
281  if (low_unref && !low.uid.is_terminal()) {
282  low_unref = false;
283  unref_nodes--;
284  }
285 
286  bool& high_unref = *high.unreferenced;
287  if (high_unref && !high.uid.is_terminal()) {
288  high_unref = false;
289  unref_nodes--;
290  }
291 
292  return make_ptr(res_uid);
293  }
294 
313  add_node(const typename Policy::label_type label,
314  const bool low,
315  const builder_ptr<Policy>& high)
316  {
317  attach_if_needed();
318 
319  builder_ptr<Policy> low_ptr = make_ptr(typename node_type::pointer_type(low));
320  return add_node(label, low_ptr, high);
321  }
322 
341  add_node(const typename Policy::label_type label,
342  const builder_ptr<Policy>& low,
343  const bool high)
344  {
345  attach_if_needed();
346 
347  builder_ptr<Policy> high_ptr = make_ptr(typename node_type::pointer_type(high));
348  return add_node(label, low, high_ptr);
349  }
350 
368  add_node(const typename Policy::label_type label, const bool low, const bool high)
369  {
370  attach_if_needed();
371 
372  builder_ptr<Policy> low_ptr = make_ptr(typename node_type::pointer_type(low));
373  builder_ptr<Policy> high_ptr = make_ptr(typename node_type::pointer_type(high));
374  return add_node(label, low_ptr, high_ptr);
375  }
376 
387  add_node(bool terminal_value)
388  {
389  attach_if_needed();
390 
391  created_terminal = true;
392  terminal_val = terminal_value;
393 
394  return make_ptr(typename node_type::pointer_type(terminal_value));
395  }
396 
413  typename Policy::dd_type
415  {
416  attach_if_needed();
417 
418  if (!nw.has_pushed()) {
419  if (created_terminal) {
420  nw.push(node_type(terminal_val));
421  } else {
422  throw domain_error("There must be at least one node or terminal in the decision diagram");
423  }
424  }
425  nw.detach();
426 
427  if (unref_nodes > 1) { throw domain_error("Decision diagram has more than one root"); }
428 
429  const typename Policy::dd_type res(nf);
430  detach();
431  return res;
432  }
433 
441  void
442  clear() noexcept
443  {
444  detach();
445  }
446 
447  private:
451  inline void
452  attach_if_needed() noexcept
453  {
454  if (!nf) {
455  adiar_assert(!nw.attached(),
456  "`nw`'s attachment should be consistent with existence of `nf`");
457 
458  // Initialise file
459  nf = internal::make_shared_levelized_file<node_type>();
460  nw.attach(nf);
461 
462  // Initialise state variables
463  current_label = Policy::max_label;
464  current_id = Policy::max_id;
465  created_terminal = false;
466  terminal_val = false;
467  unref_nodes = 0;
468 
469  // Initialise shared builder reference.
470  builder_ref = make_shared<builder_shared>();
471  }
472  }
473 
477  inline void
478  detach() noexcept
479  {
480  nw.detach();
481  nf.reset();
482  }
483 
487  builder_ptr<Policy>
488  make_ptr(const typename node_type::pointer_type& p) noexcept
489  {
490  return builder_ptr<Policy>(p, builder_ref);
491  }
492  };
493 
500 
507 
510 }
511 
512 #endif // ADIAR_BUILDER_H
The pointer type that builders use to identify the nodes they have constructed in a decision diagram.
Definition: builder.h:54
builder_ptr()=default
Default construction for a pointer to null.
builder_ptr & operator=(builder_ptr &&bp)=default
Move construction.
builder_ptr & operator=(const builder_ptr &bp)=default
Copy construction.
builder_ptr(builder_ptr &&bp)=default
Move construction.
builder_ptr(const builder_ptr &bp)=default
Copy construction.
A builder for decision diagrams.
Definition: builder.h:146
builder_ptr< Policy > add_node(bool terminal_value)
Add a terminal node with a given value.
Definition: builder.h:387
builder_ptr< Policy > add_node(const typename Policy::label_type label, const builder_ptr< Policy > &low, const bool high)
Add an internal node with a given label and its two children.
Definition: builder.h:341
builder() noexcept
Constructor.
Definition: builder.h:199
void clear() noexcept
Clear builder of all its current content, discarding all nodes and invalidating any pointers to them.
Definition: builder.h:442
~builder() noexcept
Destructor.
Definition: builder.h:205
builder_ptr< Policy > add_node(typename Policy::label_type label, const builder_ptr< Policy > &low, const builder_ptr< Policy > &high)
Add an internal node with a given label and its two children.
Definition: builder.h:235
builder_ptr< Policy > add_node(const typename Policy::label_type label, const bool low, const builder_ptr< Policy > &high)
Add an internal node with a given label and its two children.
Definition: builder.h:313
Policy::dd_type build()
Builds the decision diagram with the added nodes. This also clears the builder.
Definition: builder.h:414
builder_ptr< Policy > add_node(const typename Policy::label_type label, const bool low, const bool high)
Add an internal node with a given label and its two children.
Definition: builder.h:368
Core types.
Definition: adiar.h:40
std::domain_error domain_error
Inputs for which an operation is undefined.
Definition: exception.h:38
std::invalid_argument invalid_argument
Inputs that are invalid.
Definition: exception.h:33
std::shared_ptr< T > shared_ptr
Shared ownership of an object on the heap. This smart pointer essentially provides a (thread-safe) re...
Definition: memory.h:52