Molassembler  3.0.0
Molecule graph and conformer library
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
BoundedNodeTrie.h
Go to the documentation of this file.
1 
8 #ifndef INCLUDE_MOLASSEMBLER_TEMPLE_BOUNDED_NODE_TRIE_H
9 #define INCLUDE_MOLASSEMBLER_TEMPLE_BOUNDED_NODE_TRIE_H
10 
11 #include <memory>
12 #include <cassert>
13 
15 
16 #include "boost/dynamic_bitset.hpp"
17 
18 namespace Scine {
19 namespace Molassembler {
20 namespace Temple {
21 
31 template<typename ChoiceIndex>
33  static_assert(std::is_integral<ChoiceIndex>::value, "ChoiceIndex must be an integral type");
34 
35 public:
39  using ChoiceList = std::vector<ChoiceIndex>;
44  using ChoosingFunction = std::function<ChoiceIndex(const std::vector<ChoiceIndex>&, const boost::dynamic_bitset<>&)>;
46 
49  BoundedNodeTrie() = default;
57  setBounds(std::move(bounds));
58  }
60 
63 
75  bool insert(const ChoiceList& values) {
76  // Ensure the bounds are set
77  if(bounds_.empty()) {
78  throw std::logic_error("No bounds are set!");
79  }
80 
81  // Make sure we have a root to insert to
82  if(!root_) {
84  }
85 
86  InsertResult result = root_->insert(values, bounds_, 0);
87 
88  if(result.insertedSomething) {
89  ++size_;
90  }
91 
92  return result.insertedSomething;
93  }
94 
110  if(bounds_.empty()) {
111  throw std::logic_error("No bounds are set!");
112  }
113 
114  if(!root_) {
115  establishRoot_();
116  }
117 
118  if(size_ == capacity_) {
119  throw std::logic_error("Trie is at capacity!");
120  }
121 
122  ChoiceList values;
123  values.reserve(bounds_.size());
124 
125  InsertResult result = root_->generate(chooseFunction, values, bounds_, 0);
126 
127  if(!result.insertedSomething) {
128  throw std::logic_error("Trie has failed to generate a new entry");
129  }
130 
131  ++size_;
132 
133  return values;
134  }
135 
141  bounds_ = std::move(bounds);
142 
143  // Make sure there is at least a single bound
144  assert(!bounds_.empty());
145  // For there to be a decision, there need to be at least two options
146  assert(
148  bounds_,
149  [](const ChoiceIndex value) -> bool {
150  return value > 1;
151  }
152  )
153  );
154 
156  clear();
157  }
158 
163  void clear() {
164  if(root_) {
165  root_.reset();
166  }
167 
168  size_ = 0;
169  }
171 
174 
187  bool contains(const ChoiceList& values) const {
188  if(!root_) {
189  return false;
190  }
191 
192  return root_->contains(values, 0);
193  }
194 
196  const ChoiceList& bounds() const {
197  return bounds_;
198  }
199 
204  unsigned size() const {
205  if(root_) {
206  assert(root_->size() == size_);
207  }
208 
209  return size_;
210  }
211 
216  unsigned capacity() const {
217  return capacity_;
218  }
220 
221 private:
224  struct InsertResult {
225  bool subtreeIsFull;
226  bool insertedSomething;
227  };
228 
230  struct AbstractNode {
231  virtual ~AbstractNode() = default;
232 
233  virtual bool contains(const ChoiceList& values, unsigned depth) const = 0;
234 
235  virtual InsertResult insert(
236  const ChoiceList& values,
237  const ChoiceList& bounds,
238  unsigned depth
239  ) = 0;
240 
241  virtual InsertResult generate(
242  const ChoosingFunction& choosingFunction,
243  ChoiceList& values,
244  const ChoiceList& bounds,
245  unsigned depth
246  ) = 0;
247 
248  virtual unsigned size() const = 0;
249  };
250 
252  using NodePtr = std::unique_ptr<AbstractNode>;
253 
255  class Node final : public AbstractNode {
256  public:
257  Node(const ChoiceIndex U) : children(U), fullChildren(U) {
258  assert(U > 1);
259  }
260 
261  bool contains(const ChoiceList& values, const unsigned depth) const final {
262  const ChoiceIndex& branch = values.at(depth);
263  const NodePtr& childPtr = children.at(branch);
264 
265  if(!childPtr) {
266  return false;
267  }
268 
269  return childPtr->contains(values, depth + 1);
270  }
271 
272  InsertResult insert(const ChoiceList& values, const ChoiceList& bounds, const unsigned depth) final {
273  const ChoiceIndex& branchChoice = values.at(depth);
274  NodePtr& childPtr = children.at(branchChoice);
275 
276  InsertResult subtreeResult;
277 
278  bool insertedSomething = false;
279  if(!childPtr) {
280  if(depth + 1 == bounds.size() - 1) {
281  childPtr = std::make_unique<Leaf>(bounds.at(depth + 1));
282  } else {
283  childPtr = std::make_unique<Node>(bounds.at(depth + 1));
284  }
285 
286  insertedSomething = true;
287  }
288 
289  subtreeResult = childPtr->insert(values, bounds, depth + 1);
290  subtreeResult.insertedSomething |= insertedSomething;
291 
292  if(subtreeResult.subtreeIsFull) {
293  fullChildren.set(branchChoice);
294  }
295 
296  subtreeResult.subtreeIsFull = fullChildren.all();
297 
298  return subtreeResult;
299  }
300 
301  InsertResult generate(
302  const ChoosingFunction& choosingFunction,
303  ChoiceList& values,
304  const ChoiceList& bounds,
305  const unsigned depth
306  ) final {
307  // Prepare parameters for the choosing function
308  const unsigned N = children.size();
309  boost::dynamic_bitset<> existingChildren(N);
310  for(unsigned i = 0; i < N; ++i) {
311  if(children[i]) {
312  existingChildren.set(i);
313  }
314  }
315 
316  std::vector<ChoiceIndex> viableIndices;
317  viableIndices.reserve(N);
318  for(unsigned i = 0; i < N; ++i) {
319  if(!fullChildren.test(i)) {
320  viableIndices.push_back(i);
321  }
322  }
323 
324  const ChoiceIndex choice = choosingFunction(viableIndices, existingChildren);
325  assert(choice < N);
326  assert(Temple::makeContainsPredicate(viableIndices)(choice));
327 
328  values.push_back(choice);
329  NodePtr& childPtr = children.at(choice);
330 
331  bool insertedSomething = false;
332  if(!childPtr) {
333  insertedSomething = true;
334  if(depth + 1 == bounds.size() - 1) {
335  childPtr = std::make_unique<Leaf>(bounds.at(depth + 1));
336  } else {
337  childPtr = std::make_unique<Node>(bounds.at(depth + 1));
338  }
339  }
340 
341  InsertResult subtreeResult = childPtr->generate(choosingFunction, values, bounds, depth + 1);
342 
343  subtreeResult.insertedSomething |= insertedSomething;
344 
345  if(subtreeResult.subtreeIsFull) {
346  fullChildren.set(choice);
347  }
348 
349  subtreeResult.subtreeIsFull = fullChildren.all();
350 
351  return subtreeResult;
352  }
353 
354  unsigned size() const final {
355  unsigned count = 0;
356 
357  for(const NodePtr& nodePtr : children) {
358  if(nodePtr) {
359  count += nodePtr->size();
360  }
361  }
362 
363  return count;
364  }
365 
366  private:
367  std::vector<NodePtr> children;
368  boost::dynamic_bitset<> fullChildren;
369  };
370 
372  class Leaf final : public AbstractNode {
373  public:
374  Leaf(const ChoiceIndex U) : children(U) {
375  assert(U > 1);
376  }
377 
378  bool contains(const ChoiceList& values, const unsigned depth) const final {
379  const ChoiceIndex& choice = values.at(depth);
380  return children.test(choice);
381  }
382 
383  InsertResult insert(const ChoiceList& values, const ChoiceList& /* bounds */, const unsigned depth) final {
384  const ChoiceIndex& choice = values.at(depth);
385  assert(choice < children.size());
386 
387  InsertResult result;
388  result.insertedSomething = !children.test(choice);
389  result.subtreeIsFull = children.all();
390 
391  children.set(choice);
392 
393  return result;
394  }
395 
396  InsertResult generate(
397  const ChoosingFunction& choosingFunction,
398  ChoiceList& values,
399  const ChoiceList& /* bounds */,
400  const unsigned /* depth */
401  ) final {
402  std::vector<ChoiceIndex> viableIndices;
403  const ChoiceIndex N = children.size();
404  viableIndices.reserve(N);
405  for(ChoiceIndex i = 0; i < N; ++i) {
406  if(!children.test(i)) {
407  viableIndices.push_back(i);
408  }
409  }
410 
411  const ChoiceIndex choice = choosingFunction(viableIndices, children);
412  assert(choice < N);
413  assert(Temple::makeContainsPredicate(viableIndices)(choice));
414 
415  InsertResult result;
416  result.insertedSomething = !children.test(choice);
417 
418  values.push_back(choice);
419  children.set(choice);
420 
421  result.subtreeIsFull = children.all();
422 
423  return result;
424  }
425 
426  unsigned size() const final {
427  return children.count();
428  }
429 
430  private:
431  boost::dynamic_bitset<> children;
432  };
434 
438  void establishRoot_() {
439  if(bounds_.size() == 1) {
440  root_ = std::make_unique<Leaf>(bounds_.front());
441  } else {
442  root_ = std::make_unique<Node>(bounds_.front());
443  }
444  }
445 
447  capacity_ = Temple::accumulate(
448  bounds_,
449  1U,
450  std::multiplies<>()
451  );
452  }
454 
457  ChoiceList bounds_;
458  NodePtr root_;
459  unsigned size_ = 0;
460  unsigned capacity_ = 0;
462 };
463 
464 } // namespace Temple
465 } // namespace Molassembler
466 } // namespace Scine
467 
468 #endif
unsigned capacity() const
Returns the total number of value lists this trie might contain.
Definition: BoundedNodeTrie.h:216
std::unique_ptr< AbstractNode > NodePtr
Owning pointer to base class.
Definition: BoundedNodeTrie.h:252
const ChoiceList & bounds() const
Accessor for underlying bounds.
Definition: BoundedNodeTrie.h:196
BoundedNodeTrie()=default
Construct an empty trie, setting the upper exclusive bound on values at each level.
Leaf node at bottom of the tree.
Definition: BoundedNodeTrie.h:372
void clear()
Removes all inserted lists from the trie.
Definition: BoundedNodeTrie.h:163
BoundedNodeTrie(ChoiceList bounds)
Construct an empty trie, setting the upper exclusive bound on values at each level.
Definition: BoundedNodeTrie.h:56
Data structure to store chains of discrete choices with an finite range of choices at each position...
Definition: BoundedNodeTrie.h:32
T accumulate(const Container &container, T init, BinaryFunction &&reductionFunction)
Accumulate shorthand.
Definition: Functional.h:216
unsigned size() const
Returns the number of value lists this trie contains.
Definition: BoundedNodeTrie.h:204
std::function< std::uint8_t(const std::vector< std::uint8_t > &, const boost::dynamic_bitset<> &)> ChoosingFunction
Function signature needed to choose which child to descend to at a level in the tree.
Definition: BoundedNodeTrie.h:44
void setBounds(ChoiceList bounds)
Changes the bounds of the trie. Clears the trie.
Definition: BoundedNodeTrie.h:140
void establishRoot_()
Generate a root node.
Definition: BoundedNodeTrie.h:438
bool contains(const ChoiceList &values) const
Checks whether a value list is present in the trie.
Definition: BoundedNodeTrie.h:187
auto makeContainsPredicate(const Container &container)
Creates a predicate that uses std::find for the container&#39;s value type.
Definition: Functional.h:398
Abstract base class for nodes to allow differentiation.
Definition: BoundedNodeTrie.h:230
bool insert(const ChoiceList &values)
Inserts a value list into the prefix trie if not already present.
Definition: BoundedNodeTrie.h:75
Functional-style container-related algorithms.
ChoiceList generateNewEntry(const ChoosingFunction &chooseFunction)
Create a new entry by choosing branch at each level of the trie.
Definition: BoundedNodeTrie.h:109
std::vector< std::uint8_t > ChoiceList
Type used to represent choices made at each level of the tree.
Definition: BoundedNodeTrie.h:39
void establishCapacity_()
Generate a root node.
Definition: BoundedNodeTrie.h:446
bool all_of(const Container &container, UnaryPredicate &&predicate=UnaryPredicate{})
all_of shorthand
Definition: Functional.h:234
Most common type of nodes have other AbstractNodes as children.
Definition: BoundedNodeTrie.h:255