#ifndef ABSTRACT_TENSOR_FUNCTIONS_H #define ABSTRACT_TENSOR_FUNCTIONS_H #include "Fastor/tensor/Tensor.h" #include "Fastor/tensor/TensorTraits.h" #include namespace Fastor { /* The implementation of the evaluate function that evaluates any expression in to a tensor */ //----------------------------------------------------------------------------------------------------------// template FASTOR_INLINE const Tensor& evaluate(const Tensor &src) { return src; } template FASTOR_INLINE typename Derived::result_type evaluate(const AbstractTensor &src) { typename Derived::result_type out(src); return out; } //----------------------------------------------------------------------------------------------------------// /* IO for tensor expressions */ //----------------------------------------------------------------------------------------------------------// template inline std::ostream& operator<<(std::ostream &os, const AbstractTensor &src) { using result_type = typename Expr::result_type; result_type tmp(src); print(tmp); return os; } template inline void print(const AbstractTensor &src) { using result_type = typename Expr::result_type; result_type tmp(src); print(tmp); } //----------------------------------------------------------------------------------------------------------// /* These are the set of functions that work on any expression that evaluate immediately */ /* Add all the elements of the tensor in a flattened sense */ template,bool> = false> FASTOR_INLINE typename Derived::scalar_type sum(const AbstractTensor &_src) { const Derived &src = _src.self(); using result_type = typename Derived::result_type; const result_type out(src); return out.sum(); } template,bool> = false> FASTOR_INLINE typename Derived::scalar_type sum(const AbstractTensor &_src) { const Derived &src = _src.self(); using T = typename Derived::scalar_type; using V = typename Derived::simd_vector_type; FASTOR_INDEX i; T _scal=0; V _vec(_scal); for (i = 0; i < ROUND_DOWN(src.size(),V::Size); i+=V::Size) { _vec += src.template eval(i); } for (; i < src.size(); ++i) { _scal += src.template eval_s(i); } return _vec.sum() + _scal; } /* Multiply all the elements of the tensor in a flattened sense */ template,bool> = false> FASTOR_INLINE typename Derived::scalar_type product(const AbstractTensor &_src) { const Derived &src = _src.self(); using result_type = typename Derived::result_type; const result_type out(src); return out.product(); } template,bool> = false> FASTOR_INLINE typename Derived::scalar_type product(const AbstractTensor &_src) { const Derived &src = _src.self(); using T = typename Derived::scalar_type; using V = typename Derived::simd_vector_type; FASTOR_INDEX i; T _scal=1; V _vec(_scal); for (i = 0; i < ROUND_DOWN(src.size(),V::Size); i+=V::Size) { _vec *= src.template eval(i); } for (; i < src.size(); ++i) { _scal *= src.template eval_s(i); } return _vec.product() * _scal; } /* Get minimum element of a tensor */ template,bool> = false> FASTOR_INLINE typename Derived::scalar_type min(const AbstractTensor &_src) { const Derived &src = _src.self(); using result_type = typename Derived::result_type; const result_type out(src); return min(out); } template,bool> = false> FASTOR_INLINE typename Derived::scalar_type min(const AbstractTensor &_src) { const Derived &src = _src.self(); using T = typename Derived::scalar_type; using V = typename Derived::simd_vector_type; FASTOR_INDEX i; T _scal=std::numeric_limits::max(); V _vec(_scal); for (i = 0; i < ROUND_DOWN(src.size(),V::Size); i+=V::Size) { _vec = min(src.template eval(i),_vec); } for (; i < src.size(); ++i) { _scal = std::min(src.template eval_s(i),_scal); } return std::min(_vec.minimum(), _scal); } /* Get maximum element of a tensor */ template,bool> = false> FASTOR_INLINE typename Derived::scalar_type max(const AbstractTensor &_src) { const Derived &src = _src.self(); using result_type = typename Derived::result_type; const result_type out(src); return max(out); } template,bool> = false> FASTOR_INLINE typename Derived::scalar_type max(const AbstractTensor &_src) { const Derived &src = _src.self(); using T = typename Derived::scalar_type; using V = typename Derived::simd_vector_type; FASTOR_INDEX i; T _scal=std::numeric_limits::min(); V _vec(_scal); for (i = 0; i < ROUND_DOWN(src.size(),V::Size); i+=V::Size) { _vec = max(src.template eval(i),_vec); } for (; i < src.size(); ++i) { _scal = std::max(src.template eval_s(i),_scal); } return std::max(_vec.maximum(), _scal); } /* Get the lower triangular matrix from a 2D expression */ template,bool> = false> FASTOR_INLINE typename Derived::scalar_type tril(const AbstractTensor &_src, int k = 0) { static_assert(DIMS==2,"TENSOR HAS TO BE 2D FOR TRIL"); const Derived &src = _src.self(); using result_type = typename Derived::result_type; const result_type out(src); return tril(out,k); } template,bool> = false> FASTOR_INLINE typename Derived::result_type tril(const AbstractTensor &_src, int k = 0) { static_assert(DIMS==2,"TENSOR HAS TO BE 2D FOR TRIL"); const Derived &src = _src.self(); using T = typename Derived::scalar_type; typename Derived::result_type out(0); int M = int(src.dimension(0)); int N = int(src.dimension(1)); for (int i = 0; i < M; ++i) { int jcount = k + i < N ? k + i : N - 1; for (int j = 0; j <= jcount; ++j) { out(i,j) = src.template eval_s(i,j); } } return out; } /* Get the upper triangular matrix from a 2D expression */ template,bool> = false> FASTOR_INLINE typename Derived::scalar_type triu(const AbstractTensor &_src, int k = 0) { static_assert(DIMS==2,"TENSOR HAS TO BE 2D FOR TRIU"); const Derived &src = _src.self(); using result_type = typename Derived::result_type; const result_type out(src); return triu(out,k); } template,bool> = false> FASTOR_INLINE typename Derived::result_type triu(const AbstractTensor &_src, int k = 0) { static_assert(DIMS==2,"TENSOR HAS TO BE 2D FOR TRIU"); const Derived &src = _src.self(); using T = typename Derived::scalar_type; typename Derived::result_type out(0); int M = int(src.dimension(0)); int N = int(src.dimension(1)); for (int i = 0; i < M; ++i) { int jcount = k + i < 0 ? 0 : k + i; for (int j = jcount; j < N; ++j) { out(i,j) = src.template eval_s(i,j); } } return out; } //----------------------------------------------------------------------------------------------------------// // Boolean functions //----------------------------------------------------------------------------------------------------------// //----------------------------------------------------------------------------------------------------------// template && requires_evaluation_v,bool> = false> FASTOR_INLINE bool all_of(const AbstractTensor &_src) { using result_type = typename Derived::result_type; const result_type tmp(_src.self()); return all_of(tmp); } template && !requires_evaluation_v,bool> = false> FASTOR_INLINE bool all_of(const AbstractTensor &_src) { const Derived &src = _src.self(); bool val = true; for (FASTOR_INDEX i = 0; i < src.size(); ++i) { if (src.template eval_s(i) == false) { val = false; break; } } return val; } template && requires_evaluation_v,bool> = false> FASTOR_INLINE bool any_of(const AbstractTensor &_src) { using result_type = typename Derived::result_type; const result_type tmp(_src.self()); return any_of(tmp); } template && !requires_evaluation_v,bool> = false> FASTOR_INLINE bool any_of(const AbstractTensor &_src) { const Derived &src = _src.self(); bool val = false; for (FASTOR_INDEX i = 0; i < src.size(); ++i) { if (src.template eval_s(i) == true) { val = true; break; } } return val; } template && requires_evaluation_v,bool> = false> FASTOR_INLINE bool none_of(const AbstractTensor &_src) { using result_type = typename Derived::result_type; const result_type tmp(_src.self()); return none_of(tmp); } template && !requires_evaluation_v,bool> = false> FASTOR_INLINE bool none_of(const AbstractTensor &_src) { const Derived &src = _src.self(); bool val = false; for (FASTOR_INDEX i = 0; i < src.size(); ++i) { if (src.template eval_s(i) == true) { val = true; break; } } return val; } /* Is a second order tensor expression a uniform A tensor expression is uniform if it spans equally in all dimensions, i.e. generalisation of square matrix to N-dimensions */ template constexpr FASTOR_INLINE bool isuniform(const AbstractTensor &_src) { return is_tensor_uniform_v; } /* Is a second order tensor expression a square matrix */ template = false> constexpr FASTOR_INLINE bool issquare(const AbstractTensor &_src) { return is_tensor_uniform_v; } /* Is a second order tensor expression orthogonal */ template = false> FASTOR_INLINE bool isorthogonal(const AbstractTensor &_src, const double Tol=PRECI_TOL) { typename Derived::result_type tmp1(_src.self()); typename Derived::result_type tmp2(matmul(transpose(tmp1),tmp1)); typename Derived::result_type I; I.eye2(); return isequal(tmp2,I,Tol); } /* Is a tensor expression symmetric - for higher order tensor two axes can defining a plane provided to determine if a tensor expression is symmertric in that plane */ template,bool> = false> FASTOR_INLINE bool issymmetric(const AbstractTensor &_src, const double Tol=PRECI_TOL) { if (!issquare(_src.self())) return false; return all_of( abs(evaluate(trans(_src.self()) - _src.self())) < Tol); } template,bool> = false> FASTOR_INLINE bool issymmetric(const AbstractTensor &_src, const double Tol=PRECI_TOL) { if (!issquare(_src.self())) return false; // Avoid copies const Derived& src = _src.self(); using T = typename Derived::scalar_type; using result_type = typename Derived::result_type; constexpr size_t M = get_tensor_dimension_v<0,result_type>; constexpr size_t N = get_tensor_dimension_v<1,result_type>; bool _issym = true; for (size_t i=0; i(i*N+j) - src.template eval_s(j*N+i) ) > Tol ) { _issym = false; break; } } } return _issym; } /* Is a second order tensor expression deviatoric - a 2D tensor expression is deviatoric if it is trace free */ template = false> constexpr FASTOR_INLINE bool isdeviatoric(const AbstractTensor &_src, const double Tol=PRECI_TOL) { return std::abs( trace(_src.self()) ) < Tol ? true : false; } /* Is a second order tensor expression volumetric - a 2D tensor expression is volumetric if 1/3*[A:I]*I = A */ template = false> constexpr FASTOR_INLINE bool isvolumetric(const AbstractTensor &_src, const double Tol=PRECI_TOL) { typename Derived::result_type I; I.eye2(); typename Derived::result_type tmp = trace(_src.self()) * I; return all_of( abs(tmp - _src.self()) < Tol); } /* A second order tensor expression belongs to the special linear group if it's determinant is +1 */ template = false> FASTOR_INLINE bool doesbelongtoSL3(const AbstractTensor &_src, const double Tol=PRECI_TOL) { // Expression must be square if ( !issquare(_src.self()) ) return false; return std::abs(determinant(_src.self()) - 1) < Tol ? true : false; } /* A second order tensor/expression belongs to the special orthogonal group if it is orthogonal and it's determinant is +1 */ template = false> FASTOR_INLINE bool doesbelongtoSO3(const AbstractTensor &_src, const double Tol=PRECI_TOL) { // Expression must be square and orthogonal return issquare(_src.self()) && isorthogonal(_src.self()) ? true : false; } /* Are two tensor expressions approximately equal */ template && !requires_evaluation_v,bool> = false> FASTOR_INLINE bool isequal( const AbstractTensor &_src0, const AbstractTensor &_src1, const double Tol=PRECI_TOL) { if ( DIMS0 != DIMS1) return false; if ( _src0.self().size() != _src1.self().size()) return false; return all_of( abs(_src0.self() - _src1.self()) < Tol); } template || requires_evaluation_v,bool> = false> FASTOR_INLINE bool isequal( const AbstractTensor &_src0, const AbstractTensor &_src1, const double Tol=PRECI_TOL) { if ( DIMS0 != DIMS1) return false; if ( _src0.self().size() != _src1.self().size()) return false; return all_of( abs(evaluate(_src0.self() - _src1.self())) < Tol); } //----------------------------------------------------------------------------------------------------------// //----------------------------------------------------------------------------------------------------------// } // end of namespace Fastor #endif // #ifndef TENSOR_FUNCTIONS_H