bpp-phyl3  3.0.0
DataFlowCWise.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_DATAFLOWCWISE_H
6 #define BPP_PHYL_LIKELIHOOD_DATAFLOW_DATAFLOWCWISE_H
7 
11 #include <Eigen/Core>
12 #include <algorithm>
13 #include <cassert>
14 #include <iostream>
15 #include <list>
16 #include <string>
17 #include <tuple>
18 #include <type_traits>
19 
20 #include "DataFlowNumeric.h"
21 #include "Definitions.h"
22 
23 namespace bpp
24 {
25 // Return a reference to the object for component-wise operations
26 namespace numeric
27 {
28 template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value>::type>
29 T& cwise (T& t)
30 {
31  return t; // Do nothing for basic types
32 }
33 
35 {
36  return t; // Do nothing
37 }
38 
39 inline const ExtendedFloat& cwise (const ExtendedFloat& t)
40 {
41  return t; // Do nothing
42 }
43 
44 template<typename Derived> auto cwise (const Eigen::MatrixBase<Derived>& m) -> decltype (m.array ())
45 {
46  return m.array (); // Use Array API in Eigen
47 }
48 
49 template<typename Derived> auto cwise (Eigen::MatrixBase<Derived>& m) -> decltype (m.array ())
50 {
51  return m.array (); // Use Array API in Eigen
52 }
53 
54 inline auto cwise (const Eigen::RowVectorXi& m) -> decltype (m.template cast<double>().array ())
55 {
56  return m.template cast<double>().array ();
57 }
58 
59 template<int R, int C>
61 {
62  return ExtendedFloatArray<R, C>(m.float_part().array(), m.exponent_part());
63 }
64 
65 
66 template<int R, int C>
68 {
70 }
71 }
72 
73 /******************************************************************************
74  * Data flow nodes for those numerical functions.
75  *
76  */
77 
78 template<typename Result, typename From> class CWiseFill;
79 template<typename Result, typename From> class CWiseMatching;
80 template<typename Result, typename From> class CWiseCompound;
81 
82 /*************************************************************************
83  * @brief build a Value to a Matrix or rowVector filled with
84  * values of references.
85  *
86  * Node construction should be done with the create static method.
87  */
88 
89 template<typename R, typename T> class CWiseFill : public Value<R>
90 {
91 public:
92  using Self = CWiseFill;
93 
95  static ValueRef<R> create (Context& c, NodeRefVec&& deps, const Dimension<R>& dim)
96  {
97  // Check dependencies
98  checkDependenciesNotNull (typeid (Self), deps);
99  checkDependencyVectorSize (typeid (Self), deps, 1);
100  checkNthDependencyIsValue<T>(typeid (Self), deps, 0);
101 
102  return cachedAs<Value<R>>(c, std::make_shared<Self>(std::move (deps), dim));
103  }
104 
105  CWiseFill (NodeRefVec&& deps, const Dimension<R>& dim)
106  : Value<R>(std::move (deps)), targetDimension_ (dim)
107  {
108  this->accessValueMutable().resize(dim.rows, dim.cols);
109  }
110 
111  std::string debugInfo () const override
112  {
113  using namespace numeric;
114  return debug (this->accessValueConst ()) + " targetDim=" + to_string (targetDimension_);
115  }
116 
117  // CWiseFill additional arguments = ().
118  bool compareAdditionalArguments (const Node_DF& other) const final
119  {
120  return dynamic_cast<const Self*>(&other) != nullptr;
121  }
122 
123  NodeRef derive (Context& c, const Node_DF& node) final
124  {
125  if (&node == this)
126  {
128  }
129  return Self::create (c, {this->dependency(0)->derive (c, node)}, targetDimension_);
130  }
131 
132  NodeRef recreate (Context& c, NodeRefVec&& deps) final
133  {
134  return Self::create (c, std::move (deps), targetDimension_);
135  }
136 
137 private:
138  void compute() override { compute<T>();}
139 
140  template<class U = T>
141  typename std::enable_if<std::is_arithmetic<U>::value, void>::type
143  {
144  using namespace numeric;
145  auto& result = this->accessValueMutable ();
146  const auto& x0 = accessValueConstCast<T>(*this->dependency (0));
147  result = convert(x0, targetDimension_);
148  }
149 
150  template<class U = T>
151  typename std::enable_if<std::is_same<U, RowLik>::value>::type
153  {
154  using namespace numeric;
155  auto& result = this->accessValueMutable ();
156  const auto& x0 = accessValueConstCast<T>(*this->dependency (0));
157  result.colwise() = x0.transpose();
158  }
159 
160  template<class U = T>
161  typename std::enable_if<std::is_same<U, VectorLik>::value>::type
163  {
164  using namespace numeric;
165  auto& result = this->accessValueMutable ();
166  const auto& x0 = accessValueConstCast<T>(*this->dependency (0));
167  result.colwise() = x0;
168  }
169 
171 };
172 
173 
174 /*************************************************************************
175  * @brief build a Value to a Eigen T which columns are accessible
176  * through a pattern of positions.
177  *
178  * Node construction should be done with the create static method.
179  */
180 
181 typedef Eigen::Matrix<size_t, -1, 1> PatternType;
182 
183 template<typename R> class CWisePattern : public Value<R>
184 {
186  {
187  const R& m_arg_;
189 
190 public:
191  pattern_functor(const R& arg, const PatternType& pattern) :
192  m_arg_(arg), pattern_(pattern) {}
193 
194 
195  const typename R::Scalar& operator()(Eigen::Index row, Eigen::Index col) const
196  {
197  return m_arg_(row, Eigen::Index(pattern_[col]));
198  }
199 
200  const typename R::Scalar& operator()(Eigen::Index col) const
201  {
202  return m_arg_(Eigen::Index(pattern_[col]));
203  }
204 
205  // Specific for ExtendedFloatEigen
206  template<typename R2 = R>
207  ExtendedFloat::ExtType exponent_part(typename std::enable_if< std::is_base_of<ExtendedFloatEigenBase<R2>, R2>::value>::type* = 0) const
208  {
209  return m_arg_.exponent_part();
210  }
211  };
212 
213 public:
215 
217  static ValueRef<R> create (Context& c, NodeRefVec&& deps, const Dimension<R>& dim)
218  {
219  // Check dependencies
220  checkDependenciesNotNull (typeid (Self), deps);
221  checkDependencyVectorSize (typeid (Self), deps, 2);
222  checkNthDependencyIsValue<R>(typeid (Self), deps, 0);
223  checkNthDependencyIsValue<PatternType>(typeid(Self), deps, 1);
224  // Remove 0s from deps
226  return convertRef<Value<R>>(deps[0]);
227  else
228  return cachedAs<Value<R>>(c, std::make_shared<Self>(std::move (deps), dim));
229  }
230 
231  CWisePattern (NodeRefVec&& deps, const Dimension<R>& dim)
232  : Value<R>(deps), targetDimension_ (dim)
233  {}
234 
235  std::string debugInfo () const override
236  {
237  using namespace numeric;
238  return debug (this->accessValueConst ()) + " targetDim=" + to_string (targetDimension_);
239  }
240 
241  // CWisePattern additional arguments = ().
242  bool compareAdditionalArguments (const Node_DF& other) const final
243  {
244  return dynamic_cast<const Self*>(&other) != nullptr;
245  }
246 
247  NodeRef derive (Context& c, const Node_DF& node) final
248  {
249  if (&node == this)
250  {
252  }
253  return Self::create (c, {this->dependency(0)->derive (c, node), this->dependency(1)}, targetDimension_);
254  }
255 
256  NodeRef recreate (Context& c, NodeRefVec&& deps) final
257  {
258  return Self::create (c, std::move (deps), targetDimension_);
259  }
260 
261 private:
262  void compute() override
263  {
264  const auto& arg = accessValueConstCast<R>(*this->dependency(0));
265  const auto& pattern = accessValueConstCast<PatternType>(*this->dependency(1));
266  this->accessValueMutable() = R::NullaryExpr(targetDimension_.rows, targetDimension_.cols, pattern_functor(arg, pattern));
267  }
268 
270 };
271 
272 
273 /*************************************************************************
274  * @brief build a Value to a Matrix R which columns and rows are
275  * accessible through a vector of T objects and a function of
276  * matching positions from T objects to R object.
277  *
278  * This class is originally made for partitions of likelihoods.
279  *
280  * Node construction should be done with the create static method.
281  *
282  */
283 
284 
285 /*
286  * Matrix of matching positions : site X (index of T in the vector of Ts, position for corresponding T)
287  *
288  */
289 
290 typedef Eigen::Matrix<size_t, -1, 2> MatchingType;
291 
292 template<typename R, typename T> class CWiseMatching<R, ReductionOf<T>> : public Value<R>
293 {
294  class matching_functor
295  {
296  const std::vector<const T*>& m_arg_;
298 
299 public:
300  matching_functor(const std::vector<const T*>& arg, const MatchingType& matching) :
301  m_arg_(arg), matching_(matching) {}
302 
303  const typename R::Scalar& operator()(Eigen::Index row, Eigen::Index col) const
304  {
305  return compute<T>(row, col);
306  }
307 
308  template<typename T2 = T>
309  const typename R::Scalar& compute(Eigen::Index row, Eigen::Index col,
310  typename std::enable_if< !std::is_same<T2, typename R::Scalar>::value, T*>::type* = 0) const
311  {
312  return (*m_arg_[matching_(col, 0)])(row, Eigen::Index(matching_(col, 1)));
313  }
314 
315 
316  // Specific case of Eigen::RowVector made from several elements
317  template<typename T2 = T>
318  const typename R::Scalar& compute(Eigen::Index row, Eigen::Index col,
319  typename std::enable_if< std::is_same<T2, typename R::Scalar>::value, T*>::type* = 0) const
320  {
321  return *m_arg_[matching_(col, 0)];
322  }
323 
324  // Specific for ExtendedFloat
325  template<typename R2 = R>
326  ExtendedFloat::ExtType exponent_part(typename std::enable_if< std::is_base_of<ExtendedFloatEigenBase<R2>, R2>::value>::type* = 0) const
327  {
328  std::vector<ExtendedFloat::ExtType> vexp(m_arg_.size());
329  std::transform(m_arg_.begin(), m_arg_.end(), vexp.begin(), [](const T* t){return t->exponent_part();});
330 
331  if (!std::equal(vexp.begin() + 1, vexp.end(), vexp.begin()) )
332  throw Exception("DataFlowCwise::CWiseMatching not possible on ExtendedFloatEigen data with different exponents. Ask developers.");
333 
334  return vexp[0];
335  }
336  };
337 
338 public:
340 
342  static ValueRef<R> create (Context& c, NodeRefVec&& deps, const Dimension<R>& dim)
343  {
344  // Check dependencies
345  checkDependenciesNotNull (typeid (Self), deps);
346  checkDependencyRangeIsValue<T>(typeid (Self), deps, 0, deps.size () - 1);
347  checkNthDependencyIsValue<MatchingType>(typeid(Self), deps, deps.size() - 1);
348 
349  // Remove 0s from deps
350 
351  return cachedAs<Value<R>>(c, std::make_shared<Self>(std::move (deps), dim));
352  }
353 
354  CWiseMatching (NodeRefVec&& deps, const Dimension<R>& dim)
355  : Value<R>(std::move(deps)), targetDimension_ (dim)
356  {}
357 
358  std::string debugInfo () const override
359  {
360  using namespace numeric;
361  return debug (this->accessValueConst ()) + " targetDim=" + to_string (targetDimension_);
362  }
363 
364  // CWisePattern additional arguments = ().
365  bool compareAdditionalArguments (const Node_DF& other) const final
366  {
367  return dynamic_cast<const Self*>(&other) != nullptr;
368  }
369 
370  NodeRef derive (Context& c, const Node_DF& node) final
371  {
372  if (&node == this)
373  {
375  }
376  const auto n = this->nbDependencies ();
377  NodeRefVec derivedDeps (n);
378  for (std::size_t i = 0; i < n - 1; ++i)
379  {
380  derivedDeps[i] = this->dependency (i)->derive (c, node);
381  }
382  derivedDeps[n - 1] = this->dependency(n - 1);
383 
384  return Self::create (c, std::move (derivedDeps), targetDimension_);
385  }
386 
387  NodeRef recreate (Context& c, NodeRefVec&& deps) final
388  {
389  return Self::create (c, std::move (deps), targetDimension_);
390  }
391 
392 private:
393  void compute() override
394  {
395  const auto n = this->nbDependencies ();
396  std::vector<const T*> vR(n - 1);
397  for (std::size_t i = 0; i < n - 1; ++i)
398  {
399  vR[i] = &accessValueConstCast<T>(*this->dependency(i));
400  }
401 
402  const auto& matching = accessValueConstCast<MatchingType>(*this->dependency(n - 1));
403 
404  this->accessValueMutable() = R::NullaryExpr(targetDimension_.rows, targetDimension_.cols, matching_functor(vR, matching));
405  }
406 
408 };
409 
410 /*************************************************************************
411  * @brief build a Value to a Eigen R from a compound of lignes or columns.
412  *
413  * Node construction should be done with the create static method.
414  *
415  */
416 
417 template<typename R, typename T> class CWiseCompound<R, ReductionOf<T>> : public Value<R>
418 {
419  class compound_functor
420  {
421  const std::vector<const T*>& m_arg_;
422 
423 public:
424  compound_functor(const std::vector<const T*>& arg) :
425  m_arg_(arg) {}
426 
427  const typename R::Scalar& operator()(Eigen::Index row, Eigen::Index col) const
428  {
429  return compute<T>(row, col);
430  }
431 
432  template<typename T2 = T>
433  const typename R::Scalar& compute(Eigen::Index row, Eigen::Index col,
434  typename std::enable_if< std::is_same<T2, RowLik>::value, T*>::type* = 0) const
435  {
436  return (*m_arg_[size_t(row)])(col);
437  }
438 
439  template<typename T2 = T>
440  const typename R::Scalar& compute(Eigen::Index row, Eigen::Index col,
441  typename std::enable_if< std::is_same<T2, VectorLik>::value, T*>::type* = 0) const
442  {
443  return (*m_arg_[size_t(col)])(row);
444  }
445 
446  // Specific for ExtendedFloat
447  template<typename R2 = R>
448  ExtendedFloat::ExtType exponent_part(typename std::enable_if< std::is_base_of<ExtendedFloatEigenBase<R2>, R2>::value>::type* = 0) const
449  {
450  std::vector<ExtendedFloat::ExtType> vexp(m_arg_.size());
451  std::transform(m_arg_.begin(), m_arg_.end(), vexp.begin(), [](const T* t){return t->exponent_part();});
452 
453  if (!std::equal(vexp.begin() + 1, vexp.end(), vexp.begin()) )
454  throw Exception("DataFlowCwise::CWiseCompound not possible on ExtendedFloatEigen data with different exponents. Ask developers.");
455 
456  return vexp[0];
457  }
458  };
459 
460 public:
462 
464  static ValueRef<R> create (Context& c, NodeRefVec&& deps, const Dimension<R>& dim)
465  {
466  // Check dependencies
467  checkDependenciesNotNull (typeid (Self), deps);
468  checkDependencyRangeIsValue<T>(typeid (Self), deps, 0, deps.size ());
469 
470  return cachedAs<Value<R>>(c, std::make_shared<Self>(std::move (deps), dim));
471  }
472 
473  CWiseCompound (NodeRefVec&& deps, const Dimension<R>& dim)
474  : Value<R>(std::move(deps)), targetDimension_ (dim)
475  {}
476 
477  std::string debugInfo () const override
478  {
479  using namespace numeric;
480  return debug (this->accessValueConst ()) + " targetDim=" + to_string (targetDimension_);
481  }
482 
483  // CWiseCompound additional arguments = ().
484  bool compareAdditionalArguments (const Node_DF& other) const final
485  {
486  return dynamic_cast<const Self*>(&other) != nullptr;
487  }
488 
489  NodeRef derive (Context& c, const Node_DF& node) final
490  {
491  if (&node == this)
492  {
494  }
495  const auto n = this->nbDependencies ();
496  NodeRefVec derivedDeps (n);
497  for (std::size_t i = 0; i < n; ++i)
498  {
499  derivedDeps[i] = this->dependency (i)->derive (c, node);
500  }
501 
502  return Self::create (c, std::move (derivedDeps), targetDimension_);
503  }
504 
505  NodeRef recreate (Context& c, NodeRefVec&& deps) final
506  {
507  return Self::create (c, std::move (deps), targetDimension_);
508  }
509 
510 private:
511  void compute() override
512  {
513  const auto n = this->nbDependencies ();
514  std::vector<const T*> vR(n);
515  for (std::size_t i = 0; i < n; ++i)
516  {
517  vR[i] = &accessValueConstCast<T>(*this->dependency(i));
518  }
519 
520  this->accessValueMutable() = R::NullaryExpr(targetDimension_.rows, targetDimension_.cols, compound_functor(vR));
521  }
522 
524 };
525 
526 // Precompiled instantiations
527 extern template class CWiseFill<RowLik, double>;
528 extern template class CWiseFill<VectorLik, double>;
529 extern template class CWiseFill<MatrixLik, VectorLik>;
530 extern template class CWiseFill<MatrixLik, RowLik>;
531 
532 extern template class CWisePattern<RowLik>;
533 extern template class CWisePattern<MatrixLik>;
534 
535 extern template class CWiseMatching<RowLik, ReductionOf<RowLik>>;
537 extern template class CWiseMatching<MatrixLik, ReductionOf<RowLik>>;
538 extern template class CWiseMatching<RowLik, ReductionOf<double>>;
539 
540 extern template class CWiseCompound<MatrixLik, ReductionOf<RowLik>>;
542 extern template class CWiseCompound<RowLik, ReductionOf<double>>;
543 } // namespace bpp
544 #endif // BPP_PHYL_LIKELIHOOD_DATAFLOW_DATAFLOWCWISE_H
const R::Scalar & compute(Eigen::Index row, Eigen::Index col, typename std::enable_if< std::is_same< T2, VectorLik >::value, T * >::type *=0) const
const R::Scalar & operator()(Eigen::Index row, Eigen::Index col) const
const R::Scalar & compute(Eigen::Index row, Eigen::Index col, typename std::enable_if< std::is_same< T2, RowLik >::value, T * >::type *=0) const
compound_functor(const std::vector< const T * > &arg)
ExtendedFloat::ExtType exponent_part(typename std::enable_if< std::is_base_of< ExtendedFloatEigenBase< R2 >, R2 >::value >::type *=0) const
std::string debugInfo() const override
Node debug info (default = ""): user defined detailed info for DF graph debug.
NodeRef recreate(Context &c, NodeRefVec &&deps) final
Recreate the node with different dependencies.
static ValueRef< R > create(Context &c, NodeRefVec &&deps, const Dimension< R > &dim)
Build a new CWiseCompound node.
bool compareAdditionalArguments(const Node_DF &other) const final
Compare node-specific configuration to another.
void compute() override
Computation implementation.
CWiseCompound(NodeRefVec &&deps, const Dimension< R > &dim)
NodeRef derive(Context &c, const Node_DF &node) final
Returns a node computing d(this_node_expression)/d(node_expression).
std::enable_if< std::is_same< U, VectorLik >::value >::type compute()
Computation implementation.
Dimension< R > targetDimension_
static ValueRef< R > create(Context &c, NodeRefVec &&deps, const Dimension< R > &dim)
Build a new CWiseFill node.
Definition: DataFlowCWise.h:95
void compute() override
Computation implementation.
std::enable_if< std::is_same< U, RowLik >::value >::type compute()
Computation implementation.
std::string debugInfo() const override
Node debug info (default = ""): user defined detailed info for DF graph debug.
std::enable_if< std::is_arithmetic< U >::value, void >::type compute()
Computation implementation.
NodeRef derive(Context &c, const Node_DF &node) final
Returns a node computing d(this_node_expression)/d(node_expression).
NodeRef recreate(Context &c, NodeRefVec &&deps) final
Recreate the node with different dependencies.
bool compareAdditionalArguments(const Node_DF &other) const final
Compare node-specific configuration to another.
CWiseFill(NodeRefVec &&deps, const Dimension< R > &dim)
matching_functor(const std::vector< const T * > &arg, const MatchingType &matching)
const R::Scalar & compute(Eigen::Index row, Eigen::Index col, typename std::enable_if< !std::is_same< T2, typename R::Scalar >::value, T * >::type *=0) const
ExtendedFloat::ExtType exponent_part(typename std::enable_if< std::is_base_of< ExtendedFloatEigenBase< R2 >, R2 >::value >::type *=0) const
const R::Scalar & compute(Eigen::Index row, Eigen::Index col, typename std::enable_if< std::is_same< T2, typename R::Scalar >::value, T * >::type *=0) const
const R::Scalar & operator()(Eigen::Index row, Eigen::Index col) const
NodeRef derive(Context &c, const Node_DF &node) final
Returns a node computing d(this_node_expression)/d(node_expression).
NodeRef recreate(Context &c, NodeRefVec &&deps) final
Recreate the node with different dependencies.
CWiseMatching(NodeRefVec &&deps, const Dimension< R > &dim)
bool compareAdditionalArguments(const Node_DF &other) const final
Compare node-specific configuration to another.
std::string debugInfo() const override
Node debug info (default = ""): user defined detailed info for DF graph debug.
static ValueRef< R > create(Context &c, NodeRefVec &&deps, const Dimension< R > &dim)
Build a new CWiseMatching node.
void compute() override
Computation implementation.
const R::Scalar & operator()(Eigen::Index row, Eigen::Index col) const
ExtendedFloat::ExtType exponent_part(typename std::enable_if< std::is_base_of< ExtendedFloatEigenBase< R2 >, R2 >::value >::type *=0) const
pattern_functor(const R &arg, const PatternType &pattern)
const R::Scalar & operator()(Eigen::Index col) const
CWisePattern(NodeRefVec &&deps, const Dimension< R > &dim)
NodeRef recreate(Context &c, NodeRefVec &&deps) final
Recreate the node with different dependencies.
std::string debugInfo() const override
Node debug info (default = ""): user defined detailed info for DF graph debug.
static ValueRef< R > create(Context &c, NodeRefVec &&deps, const Dimension< R > &dim)
Build a new CWisePattern node.
bool compareAdditionalArguments(const Node_DF &other) const final
Compare node-specific configuration to another.
NodeRef derive(Context &c, const Node_DF &node) final
Returns a node computing d(this_node_expression)/d(node_expression).
void compute() override
Computation implementation.
Dimension< R > targetDimension_
static std::shared_ptr< Self > create(Context &c, const Dimension< T > &dim)
Build a new ConstantOne node of the given dimension.
Context for dataflow node construction.
Definition: DataFlow.h:527
virtual const ExtType & exponent_part() const
virtual const MatType & float_part() const
Base dataflow Node class.
Definition: DataFlow.h:152
const NodeRef & dependency(std::size_t i) const noexcept
Definition: DataFlow.h:185
virtual bool hasNumericalProperty(NumericalProperty prop) const
Test if the node has the given numerical property.
Definition: DataFlow.cpp:168
std::size_t nbDependencies() const noexcept
Number of dependencies (ie nodes we depend on)
Definition: DataFlow.h:181
Abstract Node storing a value of type T.
Definition: DataFlow.h:352
const R & accessValueConst() const noexcept
Raw value access (const).
Definition: DataFlow.h:385
R & accessValueMutable() noexcept
Definition: DataFlow.h:416
T & cwise(T &t)
Definition: DataFlowCWise.h:29
std::string debug(const T &t, typename std::enable_if< std::is_arithmetic< T >::value >::type *=0)
Defines the basic types of data flow nodes.
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
std::string to_string(const NoDimension &)
std::vector< NodeRef > NodeRefVec
Alias for a dependency vector (of NodeRef).
Definition: DataFlow.h:81
double convert(const bpp::ExtendedFloat &ef)
void checkDependencyVectorSize(const std::type_info &contextNodeType, const NodeRefVec &deps, std::size_t expectedSize)
Definition: DataFlow.cpp:83
Eigen::Matrix< size_t, -1, 1 > PatternType
Eigen::Matrix< size_t, -1, 2 > MatchingType
std::shared_ptr< Node_DF > NodeRef
Definition: DataFlow.h:78