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
23namespace bpp
24{
25// Return a reference to the object for component-wise operations
26namespace numeric
27{
28template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value>::type>
29T& cwise (T& t)
30{
31 return t; // Do nothing for basic types
32}
33
35{
36 return t; // Do nothing
37}
38
39inline const ExtendedFloat& cwise (const ExtendedFloat& t)
40{
41 return t; // Do nothing
42}
43
44template<typename Derived> auto cwise (const Eigen::MatrixBase<Derived>& m) -> decltype (m.array ())
45{
46 return m.array (); // Use Array API in Eigen
47}
48
49template<typename Derived> auto cwise (Eigen::MatrixBase<Derived>& m) -> decltype (m.array ())
50{
51 return m.array (); // Use Array API in Eigen
52}
53
54inline auto cwise (const Eigen::RowVectorXi& m) -> decltype (m.template cast<double>().array ())
55{
56 return m.template cast<double>().array ();
57}
58
59template<int R, int C>
61{
62 return ExtendedFloatArray<R, C>(m.float_part().array(), m.exponent_part());
63}
64
65
66template<int R, int C>
68{
70}
71}
72
73/******************************************************************************
74 * Data flow nodes for those numerical functions.
75 *
76 */
77
78template<typename Result, typename From> class CWiseFill;
79template<typename Result, typename From> class CWiseMatching;
80template<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
89template<typename R, typename T> class CWiseFill : public Value<R>
90{
91public:
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
133 {
134 return Self::create (c, std::move (deps), targetDimension_);
135 }
136
137private:
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
181typedef Eigen::Matrix<size_t, -1, 1> PatternType;
182
183template<typename R> class CWisePattern : public Value<R>
184{
186 {
187 const R& m_arg_;
189
190public:
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
213public:
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
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
257 {
258 return Self::create (c, std::move (deps), targetDimension_);
259 }
260
261private:
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
290typedef Eigen::Matrix<size_t, -1, 2> MatchingType;
291
292template<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
299public:
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
338public:
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
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 {
374 return ConstantOne<R>::create (c, targetDimension_);
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
388 {
389 return Self::create (c, std::move (deps), targetDimension_);
390 }
391
392private:
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
417template<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
423public:
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
460public:
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
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 {
493 return ConstantOne<R>::create (c, targetDimension_);
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
506 {
507 return Self::create (c, std::move (deps), targetDimension_);
508 }
509
510private:
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
527extern template class CWiseFill<RowLik, double>;
528extern template class CWiseFill<VectorLik, double>;
529extern template class CWiseFill<MatrixLik, VectorLik>;
530extern template class CWiseFill<MatrixLik, RowLik>;
531
532extern template class CWisePattern<RowLik>;
533extern template class CWisePattern<MatrixLik>;
534
535extern template class CWiseMatching<RowLik, ReductionOf<RowLik>>;
538extern template class CWiseMatching<RowLik, ReductionOf<double>>;
539
542extern 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 & compute(Eigen::Index row, Eigen::Index col, typename std::enable_if< std::is_same< T2, RowLik >::value, T * >::type *=0) const
const R::Scalar & operator()(Eigen::Index row, Eigen::Index col) 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
static ValueRef< R > create(Context &c, NodeRefVec &&deps, const Dimension< R > &dim)
Build a new CWiseCompound node.
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.
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).
Dimension< R > targetDimension_
void compute() override
Computation implementation.
std::string debugInfo() const override
Node debug info (default = ""): user defined detailed info for DF graph debug.
std::enable_if< std::is_same< U, RowLik >::value >::type compute()
Computation implementation.
std::enable_if< std::is_same< U, VectorLik >::value >::type compute()
Computation implementation.
static ValueRef< R > create(Context &c, NodeRefVec &&deps, const Dimension< R > &dim)
Build a new CWiseFill node.
Definition: DataFlowCWise.h:95
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)
std::enable_if< std::is_arithmetic< U >::value, void >::type compute()
Computation implementation.
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
matching_functor(const std::vector< const T * > &arg, const MatchingType &matching)
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, 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
NodeRef derive(Context &c, const Node_DF &node) final
Returns a node computing d(this_node_expression)/d(node_expression).
static ValueRef< R > create(Context &c, NodeRefVec &&deps, const Dimension< R > &dim)
Build a new CWiseMatching node.
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.
void compute() override
Computation implementation.
const R::Scalar & operator()(Eigen::Index col) const
ExtendedFloat::ExtType exponent_part(typename std::enable_if< std::is_base_of< ExtendedFloatEigenBase< R2 >, R2 >::value >::type *=0) const
const R::Scalar & operator()(Eigen::Index row, Eigen::Index col) const
pattern_functor(const R &arg, const PatternType &pattern)
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.
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.
static ValueRef< R > create(Context &c, NodeRefVec &&deps, const Dimension< R > &dim)
Build a new CWisePattern node.
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 MatType & float_part() const
virtual const ExtType & exponent_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
Abstract Node storing a value of type T.
Definition: DataFlow.h:352
R & accessValueMutable() noexcept
Definition: DataFlow.h:416
const R & accessValueConst() const noexcept
Raw value access (const).
Definition: DataFlow.h:385
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