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
27namespace bpp
28{
30std::string prettyTypeName (const std::type_info& type_info);
31
33template<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
39class Node_DF;
40template<typename T> class Value;
41class Context;
42
43
46enum class DotOptions
47{
48 None = 0,
49 DetailedNodeInfo = 1 << 0,
50 FollowUpwardLinks = 1 << 1,
51 ShowDependencyIndex = 1 << 2
52};
53
56
58void writeGraphToDot (std::ostream& os, const std::vector<const Node_DF*>& nodes, DotOptions opt = DotOptions::None);
59
61void writeGraphToDot (const std::string& filename, const std::vector<const Node_DF*>& nodes,
63
65template<typename T>
66std::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
78using NodeRef = std::shared_ptr<Node_DF>;
79
81using NodeRefVec = std::vector<NodeRef>;
82
84template<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
151class Node_DF : public std::enable_shared_from_this<Node_DF>
152{
153public:
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;
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
246protected:
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
273protected:
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
308private:
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
320template<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
329bool isTransitivelyDependentOn (const Node_DF& searchedDependency, const Node_DF& node);
330
332NodeRef recreateWithSubstitution (Context& c, const NodeRef& node,
333 const std::unordered_map<const Node_DF*, NodeRef>& substitutions);
334
335
351template<typename T> class Value : public Node_DF
352{
353public:
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
413protected:
416 T& accessValueMutable() noexcept { return value_; }
417
418private:
420};
421
424template<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
433void checkDependencyVectorSize (const std::type_info& contextNodeType, const NodeRefVec& deps,
434 std::size_t expectedSize);
435
437void checkDependencyVectorMinSize (const std::type_info& contextNodeType, const NodeRefVec& deps,
438 std::size_t expectedMinSize);
439
441void checkDependenciesNotNull (const std::type_info& contextNodeType, const NodeRefVec& deps);
442
443void checkNthDependencyNotNull (const std::type_info& contextNodeType, const NodeRefVec& deps, std::size_t index);
444
446template<typename T>
447void 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
456template<typename T1, typename T2>
457void 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
472template<typename T>
473void 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
483template<typename T>
484void checkNthDependencyIsValue (const std::type_info& contextNodeType, const NodeRefVec& deps,
485 std::size_t index)
486{
487 checkNthDependencyIs<Value<T>>(contextNodeType, deps, index);
488}
489
491template<typename T>
492void 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
527{
528public:
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
580private:
590 {
592
593 CachedNodeRef(NodeRef && r) : ref(std::move(r)) {}
594
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
611template<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
617template<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
r = 1 for each component.
r = 0 for each component.
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
const std::vector< Node_DF * > & dependentNodes() const noexcept
Definition: DataFlow.h:175
NodeRefVec dependencyNodes_
Definition: DataFlow.h:312
virtual ~Node_DF()
Definition: DataFlow.cpp:142
const NodeRefVec & dependencies() const noexcept
Definition: DataFlow.h:183
virtual std::size_t hashAdditionalArguments() const
Return the hash of node-specific configuration.
Definition: DataFlow.cpp:171
virtual void compute()=0
Computation implementation.
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
const NodeRef & dependency(std::size_t i) const noexcept
Definition: DataFlow.h:185
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
Node_DF & operator=(const Node_DF &)=delete
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
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
std::size_t nbDependencies() const noexcept
Number of dependencies (ie nodes we depend on)
Definition: DataFlow.h:181
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
ValueRef< T > deriveAsValue(Context &c, const Node_DF &node)
Definition: DataFlow.h:389
Value(NodeRefVec &&deps, Args &&... args)
Definition: DataFlow.h:362
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
const T & accessValueConst() const noexcept
Raw value access (const).
Definition: DataFlow.h:385
Defines the basic types of data flow nodes.
const T & accessValueConstCast(const Node_DF &node)
Definition: DataFlow.h:424
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
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
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
std::shared_ptr< T > convertRef(const std::shared_ptr< U > &from)
Convert a node ref with runtime type check.
Definition: DataFlow.h:320
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
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
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