bpp-phyl3  3.0.0
DataFlow.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: The Bio++ Development Group
2 //
3 // SPDX-License-Identifier: CECILL-2.1
4 
5 #ifndef BPP_PHYL_LIKELIHOOD_DATAFLOW_DATAFLOW_H
6 #define BPP_PHYL_LIKELIHOOD_DATAFLOW_DATAFLOW_H
7 
8 #include <Bpp/Exceptions.h>
9 #include <cassert>
10 #include <cstddef>
11 #include <iostream>
12 #include <memory>
13 #include <sstream>
14 #include <string>
15 #include <typeinfo>
16 #include <unordered_map> // For recreateWithSubstitution
17 #include <unordered_set>
18 #include <utility>
19 #include <vector>
20 
21 
27 namespace bpp
28 {
30 std::string prettyTypeName (const std::type_info& type_info);
31 
33 template<typename T> void combineHash (std::size_t& seed, const T& t)
34 {
35  std::hash<T> hasher;
36  seed ^= hasher (t) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
37 }
38 
39 class Node_DF;
40 template<typename T> class Value;
41 class Context;
42 
43 
46 enum class DotOptions
47 {
48  None = 0,
49  DetailedNodeInfo = 1 << 0,
50  FollowUpwardLinks = 1 << 1,
51  ShowDependencyIndex = 1 << 2
52 };
53 
56 
58 void writeGraphToDot (std::ostream& os, const std::vector<const Node_DF*>& nodes, DotOptions opt = DotOptions::None);
59 
61 void writeGraphToDot (const std::string& filename, const std::vector<const Node_DF*>& nodes,
63 
65 template<typename T>
66 std::string to_string_with_precision(const T a_value, const int n = 6)
67 {
68  std::ostringstream out;
69  out.precision(n);
70  out << std::fixed << a_value;
71  return out.str();
72 }
73 
77 
78 using NodeRef = std::shared_ptr<Node_DF>;
79 
81 using NodeRefVec = std::vector<NodeRef>;
82 
84 template<typename T> using ValueRef = std::shared_ptr<Value<T>>;
85 
88 {
89  Constant, // Is a constant value
90  ConstantZero, // Is a zero value for its type
91  ConstantOne, // Is a one value for its type
92  ConstantIdentity, // Is identity (for matrices mostly ; double(1.) is considered identity)
93 };
94 
97 [[noreturn]] void failureComputeWasCalled (const std::type_info& nodeType);
98 [[noreturn]] void failureNodeConversion (const std::type_info& handleType, const Node_DF& node);
99 [[noreturn]] void failureDependencyNumberMismatch (const std::type_info& contextNodeType,
100  std::size_t expectedSize, std::size_t givenSize);
101 [[noreturn]] void failureEmptyDependency (const std::type_info& contextNodeType, std::size_t depIndex);
102 [[noreturn]] void failureDependencyTypeMismatch (const std::type_info& contextNodeType,
103  std::size_t depIndex,
104  const std::type_info& expectedType,
105  const std::type_info& givenNodeType);
107 
151 class Node_DF : public std::enable_shared_from_this<Node_DF>
152 {
153 public:
154  Node_DF () = default;
155  Node_DF (const Node_DF&) = delete;
156  Node_DF (Node_DF&&) = delete;
157  Node_DF& operator=(const Node_DF&) = delete;
158  Node_DF& operator=(Node_DF&&) = delete;
159  virtual ~Node_DF (); // Deregisters from dependencies
160 
161  // Sets dependencies + register
162  Node_DF (const NodeRefVec& dependenciesArg);
163  Node_DF (NodeRefVec&& dependenciesArg);
164 
165  // Accessors
166  bool isValid () const noexcept { return isValid_; }
167 
172  std::size_t nbDependentNodes () const noexcept { return dependentNodes_.size (); }
173 
174 
175  const std::vector<Node_DF*>& dependentNodes () const noexcept { return dependentNodes_; }
176 
181  std::size_t nbDependencies () const noexcept { return dependencyNodes_.size (); }
182 
183  const NodeRefVec& dependencies () const noexcept { return dependencyNodes_; }
184 
185  const NodeRef& dependency (std::size_t i) const noexcept { return dependencyNodes_[i]; }
186 
188  virtual std::string description () const;
189 
190  virtual std::string color () const
191  {
192  return "white";
193  }
194 
195  virtual std::string shape () const
196  {
197  return "box";
198  }
199 
201  virtual std::string debugInfo () const;
202 
209  virtual bool hasNumericalProperty (NumericalProperty prop) const;
210 
218  virtual bool compareAdditionalArguments (const Node_DF& other) const;
225  virtual std::size_t hashAdditionalArguments () const;
226 
235  virtual NodeRef derive (Context& c, const Node_DF& node);
236 
238  virtual NodeRef recreate (Context& c, NodeRefVec&& deps);
239 
244  void computeRecursively ();
245 
246 protected:
261  virtual void compute () = 0;
262 
263 
268  void invalidateRecursively () noexcept;
269 
270  void makeInvalid () noexcept { isValid_ = false; }
271  void makeValid () noexcept { isValid_ = true; }
272 
273 protected:
274  // void setDependencies_(NodeRefVec && dependenciesArg)
275  // {
276  // if (dependencyNodes_.size()!=0)
277  // throw bpp::Exception("Node::setDependencies_ possible only for Nodes without dependencies.");
278 
279  // dependencyNodes_=std::move(dependenciesArg);
280 
281  // for (auto & n : dependencyNodes_)
282  // n->registerNode (this);
283 
284  // invalidateRecursively ();
285  // makeInvalid ();
286  // }
287 
288  void resetDependencies_(NodeRefVec&& dependenciesArg)
289  {
290  for (auto& n : dependencyNodes_)
291  {
292  if (n)
293  n->unregisterNode (this);
294  }
295 
296  dependencyNodes_ = dependenciesArg;
297 
298  for (auto& n : dependencyNodes_)
299  {
300  if (n)
301  n->registerNode (this);
302  }
303 
305  makeInvalid ();
306  }
307 
308 private:
309  void registerNode (Node_DF* n);
310  void unregisterNode (const Node_DF* n);
311 
312  NodeRefVec dependencyNodes_{}; // Nodes that we depend on.
313  std::vector<Node_DF*> dependentNodes_{}; // Nodes that depend on us.
314  bool isValid_{false};
315 
316  friend class Context;
317 };
318 
320 template<typename T, typename U> std::shared_ptr<T> convertRef (const std::shared_ptr<U>& from)
321 {
322  auto p = std::dynamic_pointer_cast<T>(from);
323  if (!p)
324  failureNodeConversion (typeid (T), *from);
325  return p;
326 }
327 
329 bool isTransitivelyDependentOn (const Node_DF& searchedDependency, const Node_DF& node);
330 
332 NodeRef recreateWithSubstitution (Context& c, const NodeRef& node,
333  const std::unordered_map<const Node_DF*, NodeRef>& substitutions);
334 
335 
351 template<typename T> class Value : public Node_DF
352 {
353 public:
354  // Init deps
355  template<typename ... Args>
356  Value(const NodeRefVec& deps, Args&&... args) :
357  Node_DF(deps),
358  value_(std::forward<Args>(args)...)
359  {}
360 
361  template<typename ... Args>
362  Value(NodeRefVec&& deps, Args&&... args) :
363  Node_DF(std::move(deps)),
364  value_(std::forward<Args>(args)...)
365  {}
366 
374  const T& targetValue()
375  {
376  this->computeRecursively();
377  return accessValueConst();
378  }
379 
385  const T& accessValueConst() const noexcept { return value_; }
386 
390  {
391  return convertRef<Value<T>>(this->derive(c, node));
392  }
393 
405  template<typename Callable> void modify(Callable&& modifier, bool makeValid)
406  {
407  this->invalidateRecursively ();
408  std::forward<Callable>(modifier) (this->accessValueMutable ());
409  if (makeValid)
410  this->makeValid();
411  }
412 
413 protected:
416  T& accessValueMutable() noexcept { return value_; }
417 
418 private:
420 };
421 
424 template<typename T> const T& accessValueConstCast(const Node_DF& node)
425 {
426  // assert (dynamic_cast<const Value<T> *> (&node) != nullptr); // Check type in debug mode
427  return static_cast<const Value<T>&>(node).accessValueConst (); // Fast cast access
428 }
429 
433 void checkDependencyVectorSize (const std::type_info& contextNodeType, const NodeRefVec& deps,
434  std::size_t expectedSize);
435 
437 void checkDependencyVectorMinSize (const std::type_info& contextNodeType, const NodeRefVec& deps,
438  std::size_t expectedMinSize);
439 
441 void checkDependenciesNotNull (const std::type_info& contextNodeType, const NodeRefVec& deps);
442 
443 void checkNthDependencyNotNull (const std::type_info& contextNodeType, const NodeRefVec& deps, std::size_t index);
444 
446 template<typename T>
447 void checkNthDependencyIs (const std::type_info& contextNodeType, const NodeRefVec& deps,
448  std::size_t index)
449 {
450  const auto& dep = *deps[index];
451  if (dynamic_cast<const T*>(&dep) == nullptr)
452  failureDependencyTypeMismatch (contextNodeType, index, typeid (T), typeid (dep));
453 }
454 
456 template<typename T1, typename T2>
457 void checkNthDependencyIs (const std::type_info& contextNodeType, const NodeRefVec& deps,
458  std::size_t index)
459 {
460  const auto& dep = *deps[index];
461  if (dynamic_cast<const T1*>(&dep) == nullptr &&
462  (dynamic_cast<const T2*>(&dep) == nullptr))
463 
464  throw Exception (prettyTypeName (contextNodeType) + ": expected class derived from " +
465  prettyTypeName (typeid(T1))
466  + " or " + prettyTypeName (typeid(T2))
467  + " as " + std::to_string (index)
468  + "-th dependency, got " + prettyTypeName (typeid (dep)));
469 }
470 
472 template<typename T>
473 void checkDependencyRangeIs (const std::type_info& contextNodeType, const NodeRefVec& deps,
474  std::size_t start, std::size_t end)
475 {
476  for (std::size_t i = start; i < end; ++i)
477  {
478  checkNthDependencyIs<T>(contextNodeType, deps, i);
479  }
480 }
481 
483 template<typename T>
484 void checkNthDependencyIsValue (const std::type_info& contextNodeType, const NodeRefVec& deps,
485  std::size_t index)
486 {
487  checkNthDependencyIs<Value<T>>(contextNodeType, deps, index);
488 }
489 
491 template<typename T>
492 void checkDependencyRangeIsValue (const std::type_info& contextNodeType, const NodeRefVec& deps,
493  std::size_t start, std::size_t end)
494 {
495  for (std::size_t i = start; i < end; ++i)
496  {
497  checkNthDependencyIsValue<T>(contextNodeType, deps, i);
498  }
499 }
501 
526 class Context
527 {
528 public:
529  Context ();
530 
539  NodeRef cached(NodeRef&& newNode);
540 
541  NodeRef cached(NodeRef& newNode);
542 
543  size_t size() const
544  {
545  return nodeCache_.size();
546  }
547 
551  void clear();
552 
562  bool erase(const NodeRef& r);
563 
564  /*
565  * @brief Get a size_t 0 NodeRef
566  *
567  */
569  {
570  return zero_;
571  }
572 
578  std::vector<const Node_DF*> getAllNodes() const;
579 
580 private:
590  {
592 
593  CachedNodeRef(NodeRef && r) : ref(std::move(r)) {}
594 
595  CachedNodeRef(NodeRef & r) : ref(r) {}
596 
597  bool operator==(const CachedNodeRef& other) const;
598  };
599 
601  {
602  std::size_t operator()(const CachedNodeRef& ref) const;
603  };
604 
605  std::unordered_set<CachedNodeRef, CachedNodeRefHash> nodeCache_;
606 
608 };
609 
611 template<typename T> std::shared_ptr<T> cachedAs (Context& c, std::shared_ptr<T>&& newNode)
612 {
613  // We can use the faster static_cast due to Context::cached(): node types are conserved.
614  return std::static_pointer_cast<T>(c.cached (std::move(newNode)));
615 }
616 
617 template<typename T> std::shared_ptr<T> cachedAs (Context& c, std::shared_ptr<T>& newNode)
618 {
619  // We can use the faster static_cast due to Context::cached(): node types are conserved.
620  return std::static_pointer_cast<T>(c.cached(newNode));
621 }
622 } // namespace bpp
623 #endif // BPP_PHYL_LIKELIHOOD_DATAFLOW_DATAFLOW_H
Context for dataflow node construction.
Definition: DataFlow.h:527
void clear()
Clear the context.
Definition: DataFlow.cpp:340
std::unordered_set< CachedNodeRef, CachedNodeRefHash > nodeCache_
Definition: DataFlow.h:605
NodeRef getZero()
Definition: DataFlow.h:568
NodeRef cached(NodeRef &&newNode)
For a newly created node, return its equivalent from the cache. If not already present in the cache,...
Definition: DataFlow.cpp:315
NodeRef zero_
Definition: DataFlow.h:607
bool erase(const NodeRef &r)
Remove an element from the map, only if it has no dependent nodes, and likewise down the graph.
Definition: DataFlow.cpp:363
std::vector< const Node_DF * > getAllNodes() const
Returns the vector of all pointers to all nodes.
Definition: DataFlow.cpp:352
size_t size() const
Definition: DataFlow.h:543
Base dataflow Node class.
Definition: DataFlow.h:152
Node_DF()=default
Node_DF(const Node_DF &)=delete
virtual std::string description() const
Node pretty name (default = type name).
Definition: DataFlow.cpp:151
NodeRefVec dependencyNodes_
Definition: DataFlow.h:312
virtual ~Node_DF()
Definition: DataFlow.cpp:142
virtual std::size_t hashAdditionalArguments() const
Return the hash of node-specific configuration.
Definition: DataFlow.cpp:171
virtual void compute()=0
Computation implementation.
const NodeRef & dependency(std::size_t i) const noexcept
Definition: DataFlow.h:185
virtual std::string debugInfo() const
Node debug info (default = ""): user defined detailed info for DF graph debug.
Definition: DataFlow.cpp:166
virtual NodeRef derive(Context &c, const Node_DF &node)
Returns a node computing d(this_node_expression)/d(node_expression).
Definition: DataFlow.cpp:173
void resetDependencies_(NodeRefVec &&dependenciesArg)
Definition: DataFlow.h:288
bool isValid_
Definition: DataFlow.h:314
virtual bool hasNumericalProperty(NumericalProperty prop) const
Test if the node has the given numerical property.
Definition: DataFlow.cpp:168
virtual NodeRef recreate(Context &c, NodeRefVec &&deps)
Recreate the node with different dependencies.
Definition: DataFlow.cpp:178
bool isValid() const noexcept
Definition: DataFlow.h:166
void registerNode(Node_DF *n)
Definition: DataFlow.cpp:240
std::size_t nbDependentNodes() const noexcept
Number of dependent nodes (ie nodes that depend on this)
Definition: DataFlow.h:172
const std::vector< Node_DF * > & dependentNodes() const noexcept
Definition: DataFlow.h:175
virtual bool compareAdditionalArguments(const Node_DF &other) const
Compare node-specific configuration to another.
Definition: DataFlow.cpp:170
void unregisterNode(const Node_DF *n)
Definition: DataFlow.cpp:245
Node_DF & operator=(Node_DF &&)=delete
Node_DF(Node_DF &&)=delete
void makeValid() noexcept
Definition: DataFlow.h:271
void computeRecursively()
Compute this node value, recomputing dependencies (transitively) as needed.
Definition: DataFlow.cpp:183
void invalidateRecursively() noexcept
Invalidate (transitively) dependent nodes from this one.
Definition: DataFlow.cpp:219
virtual std::string shape() const
Definition: DataFlow.h:195
std::vector< Node_DF * > dependentNodes_
Definition: DataFlow.h:313
virtual std::string color() const
Definition: DataFlow.h:190
Node_DF & operator=(const Node_DF &)=delete
std::size_t nbDependencies() const noexcept
Number of dependencies (ie nodes we depend on)
Definition: DataFlow.h:181
const NodeRefVec & dependencies() const noexcept
Definition: DataFlow.h:183
void makeInvalid() noexcept
Definition: DataFlow.h:270
Abstract Node storing a value of type T.
Definition: DataFlow.h:352
Value(const NodeRefVec &deps, Args &&... args)
Definition: DataFlow.h:356
Value(NodeRefVec &&deps, Args &&... args)
Definition: DataFlow.h:362
ValueRef< T > deriveAsValue(Context &c, const Node_DF &node)
Definition: DataFlow.h:389
const T & accessValueConst() const noexcept
Raw value access (const).
Definition: DataFlow.h:385
const T & targetValue()
Access value, recompute if needed.
Definition: DataFlow.h:374
void modify(Callable &&modifier, bool makeValid)
General case for modification of the T object.
Definition: DataFlow.h:405
T & accessValueMutable() noexcept
Definition: DataFlow.h:416
Defines the basic types of data flow nodes.
std::shared_ptr< T > cachedAs(Context &c, std::shared_ptr< T > &&newNode)
Helper: Same as Context::cached but with a shared_ptr<T> node.
Definition: DataFlow.h:611
std::shared_ptr< Value< T > > ValueRef
Shared pointer alias for Value<T>.
Definition: DataFlow.h:84
void checkDependenciesNotNull(const std::type_info &contextNodeType, const NodeRefVec &deps)
Checks that all dependencies are not null, throws if not.
Definition: DataFlow.cpp:103
DotOptions operator|(DotOptions a, DotOptions b)
Definition: DataFlow.cpp:437
void checkNthDependencyIs(const std::type_info &contextNodeType, const NodeRefVec &deps, std::size_t index)
Checks that deps[index] is a T node, throws if not.
Definition: DataFlow.h:447
std::string to_string(const NoDimension &)
void checkNthDependencyNotNull(const std::type_info &contextNodeType, const NodeRefVec &deps, std::size_t index)
Definition: DataFlow.cpp:114
DotOptions
Definition: DataFlow.h:47
std::string to_string_with_precision(const T a_value, const int n=6)
Output with given precision.
Definition: DataFlow.h:66
const T & accessValueConstCast(const Node_DF &node)
Definition: DataFlow.h:424
std::vector< NodeRef > NodeRefVec
Alias for a dependency vector (of NodeRef).
Definition: DataFlow.h:81
std::string prettyTypeName(const std::type_info &ti)
Debug: return a readable name for a C++ type descriptor (from typeid operator).
Definition: DataFlow.cpp:43
void checkDependencyRangeIsValue(const std::type_info &contextNodeType, const NodeRefVec &deps, std::size_t start, std::size_t end)
Check that deps[start, end[ contains Value<T> nodes, throws if not.
Definition: DataFlow.h:492
std::shared_ptr< T > convertRef(const std::shared_ptr< U > &from)
Convert a node ref with runtime type check.
Definition: DataFlow.h:320
NumericalProperty
Numerical properties for DF Node.
Definition: DataFlow.h:88
void failureDependencyNumberMismatch(const std::type_info &contextNodeType, std::size_t expectedSize, std::size_t givenSize)
Definition: DataFlow.cpp:61
NodeRef recreateWithSubstitution(Context &c, const NodeRef &node, const std::unordered_map< const Node_DF *, NodeRef > &substitutions)
Recreate node by transitively replacing dependencies according to substitutions mapping.
Definition: DataFlow.cpp:272
void checkDependencyVectorSize(const std::type_info &contextNodeType, const NodeRefVec &deps, std::size_t expectedSize)
Definition: DataFlow.cpp:83
void failureDependencyTypeMismatch(const std::type_info &contextNodeType, std::size_t depIndex, const std::type_info &expectedType, const std::type_info &givenNodeType)
Definition: DataFlow.cpp:74
bool isTransitivelyDependentOn(const Node_DF &searchedDependency, const Node_DF &node)
Check if searchedDependency if a transitive dependency of node.
Definition: DataFlow.cpp:254
void checkDependencyVectorMinSize(const std::type_info &contextNodeType, const NodeRefVec &deps, std::size_t expectedMinSize)
Checks the minimum size of a dependency vector, throws if mismatch.
Definition: DataFlow.cpp:93
void writeGraphToDot(std::ostream &os, const std::vector< const Node_DF * > &nodes, DotOptions opt)
Write dataflow graph starting at nodes to output stream.
Definition: DataFlow.cpp:564
void failureNodeConversion(const std::type_info &handleType, const Node_DF &node)
Definition: DataFlow.cpp:56
void failureEmptyDependency(const std::type_info &contextNodeType, std::size_t depIndex)
Definition: DataFlow.cpp:68
void combineHash(std::size_t &seed, const T &t)
Combine hashable value to a hash, from Boost library.
Definition: DataFlow.h:33
void failureComputeWasCalled(const std::type_info &nodeType)
Definition: DataFlow.cpp:51
std::shared_ptr< Node_DF > NodeRef
Definition: DataFlow.h:78
void checkNthDependencyIsValue(const std::type_info &contextNodeType, const NodeRefVec &deps, std::size_t index)
Checks that deps[index] is a Value<T> node, throws if not.
Definition: DataFlow.h:484
void checkDependencyRangeIs(const std::type_info &contextNodeType, const NodeRefVec &deps, std::size_t start, std::size_t end)
Check that deps[start, end[ contains T, throws if not.
Definition: DataFlow.h:473
bool operator&(DotOptions a, DotOptions b)
Definition: DataFlow.cpp:442
std::size_t operator()(const CachedNodeRef &ref) const
Definition: DataFlow.cpp:420
NodeRef is hashable and comparable as a pointer. CachedNodeRef is hashable and comparable,...
Definition: DataFlow.h:590
bool operator==(const CachedNodeRef &other) const
Definition: DataFlow.cpp:412
CachedNodeRef(NodeRef &&r)
Definition: DataFlow.h:593