Add Fastor library
This commit is contained in:
191
noarch/include/Fastor/expressions/linalg_ops/binary_cross_op.h
Normal file
191
noarch/include/Fastor/expressions/linalg_ops/binary_cross_op.h
Normal file
@@ -0,0 +1,191 @@
|
||||
#ifndef BINARY_CROSS_OP_H
|
||||
#define BINARY_CROSS_OP_H
|
||||
|
||||
#include "Fastor/meta/meta.h"
|
||||
#include "Fastor/simd_vector/SIMDVector.h"
|
||||
#include "Fastor/backend/tensor_cross.h"
|
||||
#include "Fastor/tensor/AbstractTensor.h"
|
||||
#include "Fastor/tensor/TensorTraits.h"
|
||||
#include "Fastor/tensor/Aliasing.h"
|
||||
#include "Fastor/expressions/expression_traits.h"
|
||||
|
||||
namespace Fastor {
|
||||
|
||||
// classic vector cross product
|
||||
template<typename T, size_t I, enable_if_t_<I==3,bool> = false>
|
||||
FASTOR_INLINE Tensor<T,I> cross(const Tensor<T,I> &a, const Tensor<T,I> &b) {
|
||||
Tensor<T,I> out;
|
||||
_vector_crossproduct<T,I>(a.data(),b.data(),out.data());
|
||||
return out;
|
||||
}
|
||||
template<typename T, size_t I, enable_if_t_<I==2,bool> = false>
|
||||
FASTOR_INLINE Tensor<T,I+1> cross(const Tensor<T,I> &a, const Tensor<T,I> &b) {
|
||||
Tensor<T,I+1> out;
|
||||
_vector_crossproduct<T,I>(a.data(),b.data(),out.data());
|
||||
return out;
|
||||
}
|
||||
|
||||
// Tensor cross product of two 2nd order tensors
|
||||
template<typename T, size_t I, size_t J, typename std::enable_if<I==3 && J==3,bool>::type=0>
|
||||
FASTOR_INLINE Tensor<T,I,J> cross(const Tensor<T,I,J> &b, const Tensor<T,I,J> &a) {
|
||||
Tensor<T,I,J> out;
|
||||
_crossproduct<T,I,I,J>(a.data(),b.data(),out.data());
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename T, size_t I, size_t J, typename std::enable_if<I==2 && J==2,bool>::type=0>
|
||||
FASTOR_INLINE Tensor<T,I+1,J+1> cross(const Tensor<T,I,J> &b, const Tensor<T,I,J> &a) {
|
||||
Tensor<T,I+1,J+1> out;
|
||||
_crossproduct<T,I,I,J>(a.data(),b.data(),out.data());
|
||||
return out;
|
||||
}
|
||||
|
||||
template<int Plane, typename T, size_t I, size_t J, typename std::enable_if<I==2 && J==2 && Plane==FASTOR_PlaneStrain,bool>::type=0>
|
||||
FASTOR_INLINE Tensor<T,I,J> cross(const Tensor<T,I,J> &b, const Tensor<T,I,J> &a) {
|
||||
// Plane strain case
|
||||
Tensor<T,I,J> out;
|
||||
Tensor<T,I+1,J+1> a3, b3;
|
||||
a3(0,0) = a(0,0);
|
||||
a3(0,1) = a(0,1);
|
||||
a3(1,0) = a(1,0);
|
||||
a3(1,1) = a(1,1);
|
||||
a3(2,2) = 1;
|
||||
b3(0,0) = b(0,0);
|
||||
b3(0,1) = b(0,1);
|
||||
b3(1,0) = b(1,0);
|
||||
b3(1,1) = b(1,1);
|
||||
b3(2,2) = 1;
|
||||
_crossproduct<T,FASTOR_PlaneStrain>(a.data(),b.data(),out.data());
|
||||
return out;
|
||||
}
|
||||
|
||||
// Tensor cross product of a vector with 2nd order tensor
|
||||
template<typename T, size_t I, size_t J, typename std::enable_if<I==3 && J==3,bool>::type=0>
|
||||
FASTOR_INLINE Tensor<T,I,J> cross(const Tensor<T,I> &b, const Tensor<T,I,J> &a) {
|
||||
Tensor<T,I,J> out;
|
||||
_crossproduct<T,I,1,J>(a.data(),b.data(),out.data());
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename T, size_t I, size_t J, typename std::enable_if<I==2 && J==2,bool>::type=0>
|
||||
FASTOR_INLINE Tensor<T,I+1,J+1> cross(const Tensor<T,I> &b, const Tensor<T,I,J> &a) {
|
||||
Tensor<T,I+1,J+1> out;
|
||||
_crossproduct<T,I,1,J>(a.data(),b.data(),out.data());
|
||||
return out;
|
||||
}
|
||||
|
||||
// Tensor cross product of a 2nd order tensor with a vector
|
||||
template<typename T, size_t I, size_t J, typename std::enable_if<I==3 && J==3,bool>::type=0>
|
||||
FASTOR_INLINE Tensor<T,I,J> cross(const Tensor<T,I,J> &b, const Tensor<T,J> &a) {
|
||||
Tensor<T,I,J> out;
|
||||
_crossproduct<T,I,J,1>(a.data(),b.data(),out.data());
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename T, size_t I, size_t J, typename std::enable_if<I==2 && J==2,bool>::type=0>
|
||||
FASTOR_INLINE Tensor<T,I+1,J+1> cross(const Tensor<T,I,J> &b, const Tensor<T,J> &a) {
|
||||
Tensor<T,I+1,J+1> out;
|
||||
_crossproduct<T,I,J,1>(a.data(),b.data(),out.data());
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// Tensor cross product of a 3rd order tensors
|
||||
template<typename T, size_t I, size_t J, size_t K, size_t L, size_t M, size_t N,
|
||||
typename std::enable_if<I==3 && J==3 && K==3 && L==3 && M==3 && N==3,bool>::type=0>
|
||||
FASTOR_INLINE Tensor<T,I,3,3,N> cross(const Tensor<T,I,J,K> &A, const Tensor<T,L,M,N> &B) {
|
||||
Tensor<T,I,3,3,N> C;
|
||||
_crossproduct<T,I,J,K,L,M,N>(A.data(),B.data(),C.data());
|
||||
return C;
|
||||
}
|
||||
|
||||
|
||||
// Tensor cross product of a 4th order tensors
|
||||
template<typename T, size_t I, size_t J, size_t K, size_t L, size_t M, size_t N, size_t O, size_t P,
|
||||
typename std::enable_if<I==3 && J==3 && K==3 && L==3 && M==3 && N==3 && O==3 && P==3,bool>::type=0>
|
||||
FASTOR_INLINE Tensor<T,I,J,3,3,O,P> cross(const Tensor<T,I,J,K,L> &A, const Tensor<T,M,N,O,P> &B) {
|
||||
Tensor<T,I,J,3,3,O,P> C;
|
||||
_crossproduct<T,I,J,K,L,M,N,O,P>(A.data(),B.data(),C.data());
|
||||
return C;
|
||||
}
|
||||
|
||||
|
||||
// Tensor cross product of a 4th order tensor with 2nd order tensor
|
||||
template<typename T, size_t I, size_t J, size_t K, size_t L, size_t M, size_t N,
|
||||
typename std::enable_if<I==3 && J==3 && K==3 && L==3 && M==3 && N==3,bool>::type=0>
|
||||
FASTOR_INLINE Tensor<T,I,J,K,L> cross(const Tensor<T,I,J,K,L> &A, const Tensor<T,M,N> &B) {
|
||||
Tensor<T,I,J,K,L> C;
|
||||
_crossproduct42<T,I,J,K,L,M,N>(A.data(),B.data(),C.data());
|
||||
return C;
|
||||
}
|
||||
|
||||
|
||||
// Tensor cross product of a 2nd order tensor with 4th order tensor
|
||||
template<typename T, size_t I, size_t J, size_t K, size_t L, size_t M, size_t N,
|
||||
typename std::enable_if<I==3 && J==3 && K==3 && L==3 && M==3 && N==3,bool>::type=0>
|
||||
FASTOR_INLINE Tensor<T,I,J,K,L> cross(const Tensor<T,M,N> &A, const Tensor<T,I,J,K,L> &B) {
|
||||
Tensor<T,I,J,K,L> C;
|
||||
_crossproduct24<T,I,J,K,L,M,N>(B.data(),A.data(),C.data());
|
||||
return C;
|
||||
}
|
||||
|
||||
|
||||
// Tensor cross product of a 3rd order tensor with 2nd order tensor
|
||||
template<typename T, size_t I, size_t J, size_t K, size_t L, size_t M,
|
||||
typename std::enable_if<I==3 && J==3 && K==3 && L==3 && M==3,bool>::type=0>
|
||||
FASTOR_INLINE Tensor<T,I,J,K> cross(const Tensor<T,I,J,K> &A, const Tensor<T,L,M> &B) {
|
||||
Tensor<T,I,J,K> C;
|
||||
_crossproduct32<T,I,J,K,L,M>(A.data(),B.data(),C.data());
|
||||
return C;
|
||||
}
|
||||
|
||||
|
||||
// Tensor cross product of a 2nd order tensor with 3rd order tensor
|
||||
template<typename T, size_t I, size_t J, size_t K, size_t L, size_t M,
|
||||
typename std::enable_if<I==3 && J==3 && K==3 && L==3 && M==3,bool>::type=0>
|
||||
FASTOR_INLINE Tensor<T,I,J,K> cross(const Tensor<T,L,M> &A, const Tensor<T,I,J,K> &B) {
|
||||
Tensor<T,I,J,K> C;
|
||||
_crossproduct23<T,I,J,K,L,M>(B.data(),A.data(),C.data());
|
||||
return C;
|
||||
}
|
||||
|
||||
|
||||
#if FASTOR_CXX_VERSION >= 2014
|
||||
// Tensor cross product for generic expressions
|
||||
template<typename Derived0, size_t DIM0, typename Derived1, size_t DIM1,
|
||||
enable_if_t_<is_tensor_v<Derived0> && !is_tensor_v<Derived1>,bool> = 0 >
|
||||
FASTOR_INLINE
|
||||
auto
|
||||
cross(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b) {
|
||||
using rhs_type = typename Derived1::result_type;
|
||||
const rhs_type tmp_b(b);
|
||||
return cross(a.self(),tmp_b);
|
||||
}
|
||||
|
||||
template<typename Derived0, size_t DIM0, typename Derived1, size_t DIM1,
|
||||
enable_if_t_<!is_tensor_v<Derived0> && is_tensor_v<Derived1>,bool> = 0 >
|
||||
FASTOR_INLINE
|
||||
auto
|
||||
cross(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b) {
|
||||
using lhs_type = typename Derived0::result_type;
|
||||
const lhs_type tmp_a(a);
|
||||
return cross(tmp_a,b.self());
|
||||
}
|
||||
|
||||
template<typename Derived0, size_t DIM0, typename Derived1, size_t DIM1,
|
||||
enable_if_t_<!is_tensor_v<Derived0> && !is_tensor_v<Derived1>,bool> = 0 >
|
||||
FASTOR_INLINE
|
||||
auto
|
||||
cross(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b) {
|
||||
using lhs_type = typename Derived0::result_type;
|
||||
using rhs_type = typename Derived1::result_type;
|
||||
const lhs_type tmp_a(a);
|
||||
const rhs_type tmp_b(b);
|
||||
return cross(tmp_a,tmp_b);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // end of namespace Fastor
|
||||
|
||||
|
||||
#endif // BINARY_CROSS_OP_H
|
||||
627
noarch/include/Fastor/expressions/linalg_ops/binary_matmul_op.h
Normal file
627
noarch/include/Fastor/expressions/linalg_ops/binary_matmul_op.h
Normal file
@@ -0,0 +1,627 @@
|
||||
#ifndef BINARY_MATMUL_OP_H
|
||||
#define BINARY_MATMUL_OP_H
|
||||
|
||||
#include "Fastor/meta/meta.h"
|
||||
#include "Fastor/simd_vector/SIMDVector.h"
|
||||
#include "Fastor/backend/inner.h"
|
||||
#include "Fastor/backend/matmul/matmul.h"
|
||||
#include "Fastor/tensor/AbstractTensor.h"
|
||||
#include "Fastor/tensor/Aliasing.h"
|
||||
#include "Fastor/tensor/TensorTraits.h"
|
||||
#include "Fastor/expressions/expression_traits.h"
|
||||
|
||||
|
||||
namespace Fastor {
|
||||
|
||||
template<typename TLhs, typename TRhs, size_t DIM0>
|
||||
struct BinaryMatMulOp: public AbstractTensor<BinaryMatMulOp<TLhs, TRhs, DIM0>,DIM0> {
|
||||
using lhs_expr_type = expression_t<TLhs>;
|
||||
using rhs_expr_type = expression_t<TRhs>;
|
||||
using lhs_type = typename TLhs::result_type;
|
||||
using rhs_type = typename TRhs::result_type;
|
||||
static constexpr FASTOR_INDEX lhs_rank = lhs_type::dimension_t::value;
|
||||
static constexpr FASTOR_INDEX rhs_rank = rhs_type::dimension_t::value;
|
||||
static constexpr FASTOR_INDEX M = get_tensor_dimension_v<0,lhs_type>;
|
||||
static constexpr FASTOR_INDEX K = get_tensor_dimension_v<1,lhs_type>;
|
||||
static constexpr FASTOR_INDEX N = get_tensor_dimension_v<1,rhs_type>;
|
||||
static constexpr FASTOR_INDEX K_other = get_tensor_dimension_v<0,rhs_type>;
|
||||
static constexpr FASTOR_INDEX flop_count = M*N*K;
|
||||
static constexpr FASTOR_INDEX Dimension = DIM0;
|
||||
static constexpr FASTOR_INDEX rank() {return DIM0;}
|
||||
using scalar_type = typename scalar_type_finder<BinaryMatMulOp<TLhs, TRhs, DIM0>>::type;
|
||||
// does not matter which one as matmul bypasses this
|
||||
using simd_vector_type = typename TLhs::simd_vector_type;
|
||||
using simd_abi_type = typename simd_vector_type::abi_type;
|
||||
using result_type = conditional_t_<lhs_rank == 1, // vector-matrix
|
||||
Tensor<scalar_type,N> ,
|
||||
conditional_t_<rhs_rank == 1, // matrix-vector
|
||||
Tensor<scalar_type,M>,
|
||||
Tensor<scalar_type,M,N> // matrix-matrix
|
||||
>
|
||||
>;
|
||||
|
||||
FASTOR_INLINE BinaryMatMulOp(lhs_expr_type inlhs, rhs_expr_type inrhs) : _lhs(inlhs), _rhs(inrhs) {
|
||||
static_assert(lhs_rank <=2 && rhs_rank <=2, "EXPRESSIONS FOR MATRIX MULTIPLICATION HAVE TO BE 2-DIMENSIONAL");
|
||||
static_assert(K == K_other, "INVALID MATMUL OPERANDS. COLUMNS(A)!=ROWS(B)");
|
||||
}
|
||||
|
||||
constexpr FASTOR_INLINE FASTOR_INDEX size() const {return M*N;}
|
||||
constexpr FASTOR_INLINE FASTOR_INDEX dimension(FASTOR_INDEX i) const {return i==0 ? M : N;}
|
||||
|
||||
constexpr FASTOR_INLINE lhs_expr_type lhs() const {return _lhs;}
|
||||
constexpr FASTOR_INLINE rhs_expr_type rhs() const {return _rhs;}
|
||||
|
||||
private:
|
||||
lhs_expr_type _lhs;
|
||||
rhs_expr_type _rhs;
|
||||
};
|
||||
|
||||
template<typename TLhs, typename TRhs, size_t DIM0, size_t DIM1,
|
||||
enable_if_t_<!is_arithmetic_v_<TLhs> &&!is_arithmetic_v_<TRhs>,bool> = 0 >
|
||||
constexpr FASTOR_INLINE BinaryMatMulOp<TLhs, TRhs, meta_min<DIM0,DIM1>::value>
|
||||
operator %(const AbstractTensor<TLhs,DIM0> &lhs, const AbstractTensor<TRhs,DIM1> &rhs) {
|
||||
return BinaryMatMulOp<TLhs, TRhs, meta_min<DIM0,DIM1>::value>(lhs.self(), rhs.self());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// helper dispatcher functions
|
||||
namespace internal {
|
||||
// till streaming gemm is implemented
|
||||
template<typename T, size_t M, size_t K, size_t N>
|
||||
FASTOR_INLINE
|
||||
void _gemm(const T alpha, const T * FASTOR_RESTRICT a, const T * FASTOR_RESTRICT b, const T beta, T * FASTOR_RESTRICT c) {
|
||||
|
||||
FASTOR_ARCH_ALIGN T tmp[M*N];
|
||||
if (beta == 0) {
|
||||
// non-streaming
|
||||
_matmul<T,M,K,N>(a,b,tmp);
|
||||
for (size_t i = 0; i<M*N; ++i)
|
||||
c[i] = alpha * tmp[i];
|
||||
}
|
||||
else {
|
||||
// streaming
|
||||
_matmul<T,M,K,N>(a,b,tmp);
|
||||
for (size_t i = 0; i<M*N; ++i)
|
||||
c[i] = alpha * tmp[i] + beta*c[i];
|
||||
}
|
||||
}
|
||||
template<typename T, size_t M, size_t K, size_t N>
|
||||
FASTOR_INLINE
|
||||
void _gemm_mul(const T * FASTOR_RESTRICT a, const T * FASTOR_RESTRICT b, T * FASTOR_RESTRICT c) {
|
||||
FASTOR_ARCH_ALIGN T tmp[M*N];
|
||||
_matmul<T,M,K,N>(a,b,tmp);
|
||||
for (size_t i = 0; i<M*N; ++i)
|
||||
c[i] *= tmp[i];
|
||||
}
|
||||
template<typename T, size_t M, size_t K, size_t N>
|
||||
FASTOR_INLINE
|
||||
void _gemm_div(const T * FASTOR_RESTRICT a, const T * FASTOR_RESTRICT b, T * FASTOR_RESTRICT c) {
|
||||
FASTOR_ARCH_ALIGN T tmp[M*N];
|
||||
_matmul<T,M,K,N>(a,b,tmp);
|
||||
for (size_t i = 0; i<M*N; ++i)
|
||||
c[i] /= tmp[i];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// matmul - matvec overloads
|
||||
template<typename T, size_t I, size_t J, size_t K>
|
||||
FASTOR_INLINE void matmul_dispatcher(const Tensor<T,I,J> &a, const Tensor<T,J,K> &b, Tensor<T,I,K> &out) {
|
||||
FASTOR_IF_CONSTEXPR(J==1) {
|
||||
_dyadic<T,I,K>(a.data(),b.data(),out.data());
|
||||
}
|
||||
else FASTOR_IF_CONSTEXPR(I==1 && J!=1 && K==1) {
|
||||
out.data()[0] = _inner<T,J>(a.data(),b.data());
|
||||
}
|
||||
else {
|
||||
_matmul<T,I,J,K>(a.data(),b.data(),out.data());
|
||||
}
|
||||
}
|
||||
template<typename T, size_t I, size_t J>
|
||||
FASTOR_INLINE void matmul_dispatcher(const Tensor<T,I,J> &a, const Tensor<T,J> &b, Tensor<T,I> &out) {
|
||||
_matmul<T,I,J,1>(a.data(),b.data(),out.data());
|
||||
}
|
||||
template<typename T, size_t J, size_t K>
|
||||
FASTOR_INLINE void matmul_dispatcher(const Tensor<T,J> &a, const Tensor<T,J,K> &b, Tensor<T,K> &out) {
|
||||
_matmul<T,1,J,K>(a.data(),b.data(),out.data());
|
||||
}
|
||||
|
||||
template<typename T, size_t I, size_t J, size_t K>
|
||||
FASTOR_INLINE void matmul_dispatcher(const T alpha, const Tensor<T,I,J> &a, const Tensor<T,J,K> &b, const T beta, Tensor<T,I,K> &out) {
|
||||
_gemm<T,I,J,K>(alpha,a.data(),b.data(),beta,out.data());
|
||||
}
|
||||
template<typename T, size_t I, size_t J>
|
||||
FASTOR_INLINE void matmul_dispatcher(const T alpha, const Tensor<T,I,J> &a, const Tensor<T,J> &b, const T beta, Tensor<T,I> &out) {
|
||||
_gemm<T,I,J,1>(alpha,a.data(),b.data(),beta,out.data());
|
||||
}
|
||||
template<typename T, size_t J, size_t K>
|
||||
FASTOR_INLINE void matmul_dispatcher(const T alpha, const Tensor<T,J> &a, const Tensor<T,J,K> &b, const T beta, Tensor<T,K> &out) {
|
||||
_gemm<T,1,J,K>(alpha,a.data(),b.data(),beta,out.data());
|
||||
}
|
||||
|
||||
template<typename T, size_t I, size_t J, size_t K>
|
||||
FASTOR_INLINE void matmul_dispatcher_mul(const Tensor<T,I,J> &a, const Tensor<T,J,K> &b, Tensor<T,I,K> &out) {
|
||||
_gemm_mul<T,I,J,K>(a.data(),b.data(),out.data());
|
||||
}
|
||||
template<typename T, size_t I, size_t J>
|
||||
FASTOR_INLINE void matmul_dispatcher_mul(const Tensor<T,I,J> &a, const Tensor<T,J> &b, Tensor<T,I> &out) {
|
||||
_gemm_mul<T,I,J,1>(a.data(),b.data(),out.data());
|
||||
}
|
||||
template<typename T, size_t J, size_t K>
|
||||
FASTOR_INLINE void matmul_dispatcher_mul(const Tensor<T,J> &a, const Tensor<T,J,K> &b, Tensor<T,K> &out) {
|
||||
_gemm_mul<T,1,J,K>(a.data(),b.data(),out.data());
|
||||
}
|
||||
|
||||
template<typename T, size_t I, size_t J, size_t K>
|
||||
FASTOR_INLINE void matmul_dispatcher_div(const Tensor<T,I,J> &a, const Tensor<T,J,K> &b, Tensor<T,I,K> &out) {
|
||||
_gemm_div<T,I,J,K>(a.data(),b.data(),out.data());
|
||||
}
|
||||
template<typename T, size_t I, size_t J>
|
||||
FASTOR_INLINE void matmul_dispatcher_div(const Tensor<T,I,J> &a, const Tensor<T,J> &b, Tensor<T,I> &out) {
|
||||
_gemm_div<T,I,J,1>(a.data(),b.data(),out.data());
|
||||
}
|
||||
template<typename T, size_t J, size_t K>
|
||||
FASTOR_INLINE void matmul_dispatcher_div(const Tensor<T,J> &a, const Tensor<T,J,K> &b, Tensor<T,K> &out) {
|
||||
_gemm_div<T,1,J,K>(a.data(),b.data(),out.data());
|
||||
}
|
||||
|
||||
} // internal
|
||||
|
||||
|
||||
// For tensors
|
||||
template<typename T, size_t I, size_t J, size_t K>
|
||||
FASTOR_INLINE Tensor<T,I,K> matmul(const Tensor<T,I,J> &a, const Tensor<T,J,K> &b) {
|
||||
Tensor<T,I,K> out;
|
||||
FASTOR_IF_CONSTEXPR(J==1) {
|
||||
_dyadic<T,I,K>(a.data(),b.data(),out.data());
|
||||
}
|
||||
else FASTOR_IF_CONSTEXPR(I==1 && J!=1 && K==1) {
|
||||
out.data()[0] = _inner<T,J>(a.data(),b.data());
|
||||
}
|
||||
else {
|
||||
_matmul<T,I,J,K>(a.data(),b.data(),out.data());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template<typename T, size_t I, size_t J>
|
||||
FASTOR_INLINE Tensor<T,I> matmul(const Tensor<T,I,J> &a, const Tensor<T,J> &b) {
|
||||
Tensor<T,I> out;
|
||||
_matmul<T,I,J,1>(a.data(),b.data(),out.data());
|
||||
return out;
|
||||
}
|
||||
template<typename T, size_t J, size_t K>
|
||||
FASTOR_INLINE Tensor<T,K> matmul(const Tensor<T,J> &a, const Tensor<T,J,K> &b) {
|
||||
Tensor<T,K> out;
|
||||
_matmul<T,1,J,K>(a.data(),b.data(),out.data());
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// Generic matmul function for AbstractTensor types are provided here
|
||||
template<typename Derived0, size_t DIM0, typename Derived1, size_t DIM1,
|
||||
enable_if_t_<is_less_equal_v_<DIM0,2> && is_less_equal_v_<DIM1,2>
|
||||
&& !is_tensor_v<Derived0> && !is_tensor_v<Derived1>,bool> = 0 >
|
||||
FASTOR_INLINE
|
||||
conditional_t_<Derived0::result_type::dimension_t::value == 1,
|
||||
Tensor<typename Derived0::scalar_type,
|
||||
get_tensor_dimension_v<1,typename Derived1::result_type> >,
|
||||
conditional_t_<Derived1::result_type::dimension_t::value == 1,
|
||||
Tensor<typename Derived0::scalar_type,
|
||||
get_tensor_dimension_v<0,typename Derived0::result_type> >,
|
||||
Tensor<typename Derived0::scalar_type,
|
||||
get_tensor_dimension_v<0,typename Derived0::result_type> ,
|
||||
get_tensor_dimension_v<1,typename Derived1::result_type> >
|
||||
>
|
||||
>
|
||||
matmul(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b) {
|
||||
using lhs_type = typename Derived0::result_type;
|
||||
using rhs_type = typename Derived1::result_type;
|
||||
const lhs_type tmp_a(a);
|
||||
const rhs_type tmp_b(b);
|
||||
return matmul(tmp_a,tmp_b);
|
||||
}
|
||||
|
||||
template<typename Derived0, size_t DIM0, typename Derived1, size_t DIM1,
|
||||
enable_if_t_<is_less_equal_v_<DIM0,2> && is_less_equal_v_<DIM1,2>
|
||||
&& !is_tensor_v<Derived0> && is_tensor_v<Derived1>,bool> = 0 >
|
||||
FASTOR_INLINE
|
||||
conditional_t_<Derived0::result_type::dimension_t::value == 1,
|
||||
Tensor<typename Derived0::scalar_type,
|
||||
get_tensor_dimension_v<1,typename Derived1::result_type> >,
|
||||
conditional_t_<Derived1::result_type::dimension_t::value == 1,
|
||||
Tensor<typename Derived0::scalar_type,
|
||||
get_tensor_dimension_v<0,typename Derived0::result_type> >,
|
||||
Tensor<typename Derived0::scalar_type,
|
||||
get_tensor_dimension_v<0,typename Derived0::result_type> ,
|
||||
get_tensor_dimension_v<1,typename Derived1::result_type> >
|
||||
>
|
||||
>
|
||||
matmul(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b) {
|
||||
using lhs_type = typename Derived0::result_type;
|
||||
const lhs_type tmp_a(a);
|
||||
return matmul(tmp_a,b.self());
|
||||
}
|
||||
|
||||
template<typename Derived0, size_t DIM0, typename Derived1, size_t DIM1,
|
||||
enable_if_t_<is_less_equal_v_<DIM0,2> && is_less_equal_v_<DIM1,2>
|
||||
&& is_tensor_v<Derived0> && !is_tensor_v<Derived1>,bool> = 0 >
|
||||
FASTOR_INLINE
|
||||
conditional_t_<Derived0::result_type::dimension_t::value == 1,
|
||||
Tensor<typename Derived0::scalar_type,
|
||||
get_tensor_dimension_v<1,typename Derived1::result_type> >,
|
||||
conditional_t_<Derived1::result_type::dimension_t::value == 1,
|
||||
Tensor<typename Derived0::scalar_type,
|
||||
get_tensor_dimension_v<0,typename Derived0::result_type> >,
|
||||
Tensor<typename Derived0::scalar_type,
|
||||
get_tensor_dimension_v<0,typename Derived0::result_type> ,
|
||||
get_tensor_dimension_v<1,typename Derived1::result_type> >
|
||||
>
|
||||
>
|
||||
matmul(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b) {
|
||||
using rhs_type = typename Derived1::result_type;
|
||||
const rhs_type tmp_b(b);
|
||||
return matmul(a.self(),tmp_b);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// triangular matmul functions
|
||||
template<typename LhsType = UpLoType::General, typename RhsType = UpLoType::General, typename T, size_t M, size_t K, size_t N>
|
||||
Tensor<T,M,N> tmatmul(const Tensor<T,M,K> &a, const Tensor<T,K,N> &b) {
|
||||
Tensor<T,M,N> out;
|
||||
_tmatmul<T,M,K,N,LhsType,RhsType>(a.data(),b.data(),out.data());
|
||||
return out;
|
||||
}
|
||||
template<typename LhsType = UpLoType::General, typename RhsType = UpLoType::General, typename T, size_t I, size_t J>
|
||||
FASTOR_INLINE Tensor<T,I> tmatmul(const Tensor<T,I,J> &a, const Tensor<T,J> &b) {
|
||||
Tensor<T,I> out;
|
||||
_tmatmul<T,I,J,1,LhsType,RhsType>(a.data(),b.data(),out.data());
|
||||
return out;
|
||||
}
|
||||
template<typename LhsType = UpLoType::General, typename RhsType = UpLoType::General, typename T, size_t J, size_t K>
|
||||
FASTOR_INLINE Tensor<T,K> tmatmul(const Tensor<T,J> &a, const Tensor<T,J,K> &b) {
|
||||
Tensor<T,K> out;
|
||||
_tmatmul<T,1,J,K,LhsType,RhsType>(a.data(),b.data(),out.data());
|
||||
return out;
|
||||
}
|
||||
|
||||
#if FASTOR_CXX_VERSION >= 2014
|
||||
// tmatmul for generic expressions
|
||||
template<typename LhsType = UpLoType::General, typename RhsType = UpLoType::General,
|
||||
typename Derived0, size_t DIM0, typename Derived1, size_t DIM1,
|
||||
enable_if_t_<is_less_equal_v_<DIM0,2> && is_less_equal_v_<DIM1,2>
|
||||
&& !is_tensor_v<Derived0> && !is_tensor_v<Derived1>,bool> = 0 >
|
||||
FASTOR_INLINE
|
||||
decltype(auto)
|
||||
tmatmul(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b) {
|
||||
using lhs_type = typename Derived0::result_type;
|
||||
using rhs_type = typename Derived1::result_type;
|
||||
const lhs_type tmp_a(a);
|
||||
const rhs_type tmp_b(b);
|
||||
return tmatmul<LhsType,RhsType>(tmp_a,tmp_b);
|
||||
}
|
||||
|
||||
template<typename LhsType = UpLoType::General, typename RhsType = UpLoType::General,
|
||||
typename Derived0, size_t DIM0, typename Derived1, size_t DIM1,
|
||||
enable_if_t_<is_less_equal_v_<DIM0,2> && is_less_equal_v_<DIM1,2>
|
||||
&& !is_tensor_v<Derived0> && is_tensor_v<Derived1>,bool> = 0 >
|
||||
FASTOR_INLINE
|
||||
decltype(auto)
|
||||
tmatmul(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b) {
|
||||
using lhs_type = typename Derived0::result_type;
|
||||
const lhs_type tmp_a(a);
|
||||
return tmatmul<LhsType,RhsType>(tmp_a,b.self());
|
||||
}
|
||||
|
||||
template<typename LhsType = UpLoType::General, typename RhsType = UpLoType::General,
|
||||
typename Derived0, size_t DIM0, typename Derived1, size_t DIM1,
|
||||
enable_if_t_<is_less_equal_v_<DIM0,2> && is_less_equal_v_<DIM1,2>
|
||||
&& is_tensor_v<Derived0> && !is_tensor_v<Derived1>,bool> = 0 >
|
||||
FASTOR_INLINE
|
||||
decltype(auto)
|
||||
tmatmul(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b) {
|
||||
using rhs_type = typename Derived1::result_type;
|
||||
const rhs_type tmp_b(b);
|
||||
return tmatmul<LhsType,RhsType>(a.self(),tmp_b);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// assignments
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs, size_t OtherDIM,
|
||||
typename std::enable_if<is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_expr_type>> &&
|
||||
is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_expr_type>>, bool >::type = false>
|
||||
FASTOR_INLINE void assign(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<TLhs, TRhs, OtherDIM> &src) {
|
||||
// dst = matmul(src.lhs().self(),src.rhs().self()); // this makes a copy for dst, compiler emits a memcpy
|
||||
internal::matmul_dispatcher(src.lhs().self(),src.rhs().self(),dst.self());
|
||||
}
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs, size_t OtherDIM,
|
||||
typename std::enable_if<!is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_expr_type>> &&
|
||||
is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_expr_type>>, bool >::type = false>
|
||||
FASTOR_INLINE void assign(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<TLhs, TRhs, OtherDIM> &src) {
|
||||
using lhs_t = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_type;
|
||||
lhs_t a(src.lhs().self());
|
||||
// dst = matmul(a,src.rhs().self()); // this makes a copy for dst, compiler emits a memcpy
|
||||
internal::matmul_dispatcher(a,src.rhs().self(),dst.self());
|
||||
}
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs, size_t OtherDIM,
|
||||
typename std::enable_if<is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_expr_type>> &&
|
||||
!is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_expr_type>>, bool >::type = false>
|
||||
FASTOR_INLINE void assign(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<TLhs, TRhs, OtherDIM> &src) {
|
||||
using rhs_t = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_type;
|
||||
rhs_t b(src.rhs().self());
|
||||
// dst = matmul(src.lhs().self(),b); // this makes a copy for dst, compiler emits a memcpy
|
||||
internal::matmul_dispatcher(src.lhs().self(),b,dst.self());
|
||||
}
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs, size_t OtherDIM,
|
||||
typename std::enable_if<!is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_expr_type>> &&
|
||||
!is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_expr_type>>, bool >::type = false>
|
||||
FASTOR_INLINE void assign(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<TLhs, TRhs, OtherDIM> &src) {
|
||||
using lhs_t = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_type;
|
||||
using rhs_t = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_type;
|
||||
lhs_t a(src.lhs().self());
|
||||
rhs_t b(src.rhs().self());
|
||||
// dst = matmul(a,b); // this makes a copy for dst, compiler emits a memcpy
|
||||
internal::matmul_dispatcher(a,b,dst.self());
|
||||
}
|
||||
|
||||
|
||||
|
||||
// assignments add
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs, size_t OtherDIM,
|
||||
typename std::enable_if<is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_expr_type>> &&
|
||||
is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_expr_type>>, bool >::type = false>
|
||||
FASTOR_INLINE void assign_add(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<TLhs, TRhs, OtherDIM> &src) {
|
||||
using T = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::scalar_type;
|
||||
internal::matmul_dispatcher((T)1,src.lhs().self(),src.rhs().self(),(T)1,dst.self());
|
||||
}
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs, size_t OtherDIM,
|
||||
typename std::enable_if<!is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_expr_type>> &&
|
||||
is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_expr_type>>, bool >::type = false>
|
||||
FASTOR_INLINE void assign_add(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<TLhs, TRhs, OtherDIM> &src) {
|
||||
using T = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::scalar_type;
|
||||
using lhs_t = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_type;
|
||||
lhs_t a(src.lhs().self());
|
||||
internal::matmul_dispatcher((T)1,a,src.rhs().self(),(T)1,dst.self());
|
||||
}
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs, size_t OtherDIM,
|
||||
typename std::enable_if<is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_expr_type>> &&
|
||||
!is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_expr_type>>, bool >::type = false>
|
||||
FASTOR_INLINE void assign_add(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<TLhs, TRhs, OtherDIM> &src) {
|
||||
using T = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::scalar_type;
|
||||
using rhs_t = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_type;
|
||||
rhs_t b(src.rhs().self());
|
||||
internal::matmul_dispatcher((T)1,src.lhs().self(),b,(T)1,dst.self());
|
||||
}
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs, size_t OtherDIM,
|
||||
typename std::enable_if<!is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_expr_type>> &&
|
||||
!is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_expr_type>>, bool >::type = false>
|
||||
FASTOR_INLINE void assign_add(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<TLhs, TRhs, OtherDIM> &src) {
|
||||
using T = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::scalar_type;
|
||||
using lhs_t = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_type;
|
||||
using rhs_t = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_type;
|
||||
lhs_t a(src.lhs().self());
|
||||
rhs_t b(src.rhs().self());
|
||||
internal::matmul_dispatcher((T)1,a,b,(T)1,dst.self());
|
||||
}
|
||||
|
||||
|
||||
// assignments sub
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs, size_t OtherDIM,
|
||||
typename std::enable_if<is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_expr_type>> &&
|
||||
is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_expr_type>>, bool >::type = false>
|
||||
FASTOR_INLINE void assign_sub(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<TLhs, TRhs, OtherDIM> &src) {
|
||||
using T = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::scalar_type;
|
||||
internal::matmul_dispatcher((T)-1,src.lhs().self(),src.rhs().self(),(T)1,dst.self());
|
||||
}
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs, size_t OtherDIM,
|
||||
typename std::enable_if<!is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_expr_type>> &&
|
||||
is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_expr_type>>, bool >::type = false>
|
||||
FASTOR_INLINE void assign_sub(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<TLhs, TRhs, OtherDIM> &src) {
|
||||
using T = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::scalar_type;
|
||||
using lhs_t = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_type;
|
||||
lhs_t a(src.lhs().self());
|
||||
internal::matmul_dispatcher((T)-1,a,src.rhs().self(),(T)1,dst.self());
|
||||
}
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs, size_t OtherDIM,
|
||||
typename std::enable_if<is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_expr_type>> &&
|
||||
!is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_expr_type>>, bool >::type = false>
|
||||
FASTOR_INLINE void assign_sub(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<TLhs, TRhs, OtherDIM> &src) {
|
||||
using T = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::scalar_type;
|
||||
using rhs_t = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_type;
|
||||
rhs_t b(src.rhs().self());
|
||||
internal::matmul_dispatcher((T)-1,src.lhs().self(),b,(T)1,dst.self());
|
||||
}
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs, size_t OtherDIM,
|
||||
typename std::enable_if<!is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_expr_type>> &&
|
||||
!is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_expr_type>>, bool >::type = false>
|
||||
FASTOR_INLINE void assign_sub(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<TLhs, TRhs, OtherDIM> &src) {
|
||||
using T = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::scalar_type;
|
||||
using lhs_t = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_type;
|
||||
using rhs_t = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_type;
|
||||
lhs_t a(src.lhs().self());
|
||||
rhs_t b(src.rhs().self());
|
||||
internal::matmul_dispatcher((T)-1,a,b,(T)1,dst.self());
|
||||
}
|
||||
|
||||
|
||||
// assignments mul
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs, size_t OtherDIM,
|
||||
typename std::enable_if<is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_expr_type>> &&
|
||||
is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_expr_type>>, bool >::type = false>
|
||||
FASTOR_INLINE void assign_mul(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<TLhs, TRhs, OtherDIM> &src) {
|
||||
internal::matmul_dispatcher_mul(src.lhs().self(),src.rhs().self(),dst.self());
|
||||
}
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs, size_t OtherDIM,
|
||||
typename std::enable_if<!is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_expr_type>> &&
|
||||
is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_expr_type>>, bool >::type = false>
|
||||
FASTOR_INLINE void assign_mul(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<TLhs, TRhs, OtherDIM> &src) {
|
||||
using lhs_t = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_type;
|
||||
lhs_t a(src.lhs().self());
|
||||
internal::matmul_dispatcher_mul(a,src.rhs().self(),dst.self());
|
||||
}
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs, size_t OtherDIM,
|
||||
typename std::enable_if<is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_expr_type>> &&
|
||||
!is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_expr_type>>, bool >::type = false>
|
||||
FASTOR_INLINE void assign_mul(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<TLhs, TRhs, OtherDIM> &src) {
|
||||
using rhs_t = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_type;
|
||||
rhs_t b(src.rhs().self());
|
||||
internal::matmul_dispatcher_mul(src.lhs().self(),b,dst.self());
|
||||
}
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs, size_t OtherDIM,
|
||||
typename std::enable_if<!is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_expr_type>> &&
|
||||
!is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_expr_type>>, bool >::type = false>
|
||||
FASTOR_INLINE void assign_mul(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<TLhs, TRhs, OtherDIM> &src) {
|
||||
using lhs_t = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_type;
|
||||
using rhs_t = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_type;
|
||||
lhs_t a(src.lhs().self());
|
||||
rhs_t b(src.rhs().self());
|
||||
internal::matmul_dispatcher_mul(a,b,dst.self());
|
||||
}
|
||||
|
||||
|
||||
// assignments div
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs, size_t OtherDIM,
|
||||
typename std::enable_if<is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_expr_type>> &&
|
||||
is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_expr_type>>, bool >::type = false>
|
||||
FASTOR_INLINE void assign_div(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<TLhs, TRhs, OtherDIM> &src) {
|
||||
internal::matmul_dispatcher_div(src.lhs().self(),src.rhs().self(),dst.self());
|
||||
}
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs, size_t OtherDIM,
|
||||
typename std::enable_if<!is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_expr_type>> &&
|
||||
is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_expr_type>>, bool >::type = false>
|
||||
FASTOR_INLINE void assign_div(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<TLhs, TRhs, OtherDIM> &src) {
|
||||
using lhs_t = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_type;
|
||||
lhs_t a(src.lhs().self());
|
||||
internal::matmul_dispatcher_div(a,src.rhs().self(),dst.self());
|
||||
}
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs, size_t OtherDIM,
|
||||
typename std::enable_if<is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_expr_type>> &&
|
||||
!is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_expr_type>>, bool >::type = false>
|
||||
FASTOR_INLINE void assign_div(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<TLhs, TRhs, OtherDIM> &src) {
|
||||
using rhs_t = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_type;
|
||||
rhs_t b(src.rhs().self());
|
||||
internal::matmul_dispatcher_div(src.lhs().self(),b,dst.self());
|
||||
}
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs, size_t OtherDIM,
|
||||
typename std::enable_if<!is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_expr_type>> &&
|
||||
!is_tensor_v<remove_all_t<typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_expr_type>>, bool >::type = false>
|
||||
FASTOR_INLINE void assign_div(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<TLhs, TRhs, OtherDIM> &src) {
|
||||
using lhs_t = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::lhs_type;
|
||||
using rhs_t = typename BinaryMatMulOp<TLhs, TRhs, OtherDIM>::rhs_type;
|
||||
lhs_t a(src.lhs().self());
|
||||
rhs_t b(src.rhs().self());
|
||||
internal::matmul_dispatcher_div(a,b,dst.self());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// recursive greedy-like
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs0, typename TRhs1, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<BinaryMatMulOp<TLhs, TRhs0, OtherDIM>, TRhs1, OtherDIM> &src) {
|
||||
FASTOR_IF_CONSTEXPR(BinaryMatMulOp<TLhs, TRhs0, OtherDIM>::flop_count > BinaryMatMulOp<TRhs0, TRhs1, OtherDIM>::flop_count)
|
||||
{
|
||||
using result_t = typename BinaryMatMulOp<TRhs0, TRhs1, OtherDIM>::result_type;
|
||||
result_t tmp;
|
||||
assign(tmp, BinaryMatMulOp<TRhs0, TRhs1, OtherDIM>(src.lhs().rhs().self(),src.rhs().self()));
|
||||
assign(dst, BinaryMatMulOp<TLhs, result_t, OtherDIM>(src.lhs().lhs().self(),tmp));
|
||||
}
|
||||
else
|
||||
{
|
||||
using result_t = typename BinaryMatMulOp<TLhs, TRhs0, OtherDIM>::result_type;
|
||||
result_t tmp;
|
||||
assign(tmp, BinaryMatMulOp<TLhs, TRhs0, OtherDIM>(src.lhs().lhs().self(),src.lhs().rhs().self()));
|
||||
assign(dst, BinaryMatMulOp<result_t, TRhs1, OtherDIM>(tmp,src.rhs().self()));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs0, typename TRhs1, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_add(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<BinaryMatMulOp<TLhs, TRhs0, OtherDIM>, TRhs1, OtherDIM> &src) {
|
||||
FASTOR_IF_CONSTEXPR(BinaryMatMulOp<TLhs, TRhs0, OtherDIM>::flop_count > BinaryMatMulOp<TRhs0, TRhs1, OtherDIM>::flop_count)
|
||||
{
|
||||
using result_t = typename BinaryMatMulOp<TRhs0, TRhs1, OtherDIM>::result_type;
|
||||
result_t tmp;
|
||||
assign(tmp, BinaryMatMulOp<TRhs0, TRhs1, OtherDIM>(src.lhs().rhs().self(),src.rhs().self()));
|
||||
assign_add(dst, BinaryMatMulOp<TLhs, result_t, OtherDIM>(src.lhs().lhs().self(),tmp));
|
||||
}
|
||||
else
|
||||
{
|
||||
using result_t = typename BinaryMatMulOp<TLhs, TRhs0, OtherDIM>::result_type;
|
||||
result_t tmp;
|
||||
assign(tmp, BinaryMatMulOp<TLhs, TRhs0, OtherDIM>(src.lhs().lhs().self(),src.lhs().rhs().self()));
|
||||
assign_add(dst, BinaryMatMulOp<result_t, TRhs1, OtherDIM>(tmp,src.rhs().self()));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs0, typename TRhs1, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_sub(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<BinaryMatMulOp<TLhs, TRhs0, OtherDIM>, TRhs1, OtherDIM> &src) {
|
||||
FASTOR_IF_CONSTEXPR(BinaryMatMulOp<TLhs, TRhs0, OtherDIM>::flop_count > BinaryMatMulOp<TRhs0, TRhs1, OtherDIM>::flop_count)
|
||||
{
|
||||
using result_t = typename BinaryMatMulOp<TRhs0, TRhs1, OtherDIM>::result_type;
|
||||
result_t tmp;
|
||||
assign(tmp, BinaryMatMulOp<TRhs0, TRhs1, OtherDIM>(src.lhs().rhs().self(),src.rhs().self()));
|
||||
assign_sub(dst, BinaryMatMulOp<TLhs, result_t, OtherDIM>(src.lhs().lhs().self(),tmp));
|
||||
}
|
||||
else
|
||||
{
|
||||
using result_t = typename BinaryMatMulOp<TLhs, TRhs0, OtherDIM>::result_type;
|
||||
result_t tmp;
|
||||
assign(tmp, BinaryMatMulOp<TLhs, TRhs0, OtherDIM>(src.lhs().lhs().self(),src.lhs().rhs().self()));
|
||||
assign_sub(dst, BinaryMatMulOp<result_t, TRhs1, OtherDIM>(tmp,src.rhs().self()));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs0, typename TRhs1, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_mul(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<BinaryMatMulOp<TLhs, TRhs0, OtherDIM>, TRhs1, OtherDIM> &src) {
|
||||
FASTOR_IF_CONSTEXPR(BinaryMatMulOp<TLhs, TRhs0, OtherDIM>::flop_count > BinaryMatMulOp<TRhs0, TRhs1, OtherDIM>::flop_count)
|
||||
{
|
||||
using result_t = typename BinaryMatMulOp<TRhs0, TRhs1, OtherDIM>::result_type;
|
||||
result_t tmp;
|
||||
assign(tmp, BinaryMatMulOp<TRhs0, TRhs1, OtherDIM>(src.lhs().rhs().self(),src.rhs().self()));
|
||||
assign_mul(dst, BinaryMatMulOp<TLhs, result_t, OtherDIM>(src.lhs().lhs().self(),tmp));
|
||||
}
|
||||
else
|
||||
{
|
||||
using result_t = typename BinaryMatMulOp<TLhs, TRhs0, OtherDIM>::result_type;
|
||||
result_t tmp;
|
||||
assign(tmp, BinaryMatMulOp<TLhs, TRhs0, OtherDIM>(src.lhs().lhs().self(),src.lhs().rhs().self()));
|
||||
assign_mul(dst, BinaryMatMulOp<result_t, TRhs1, OtherDIM>(tmp,src.rhs().self()));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename TLhs, typename TRhs0, typename TRhs1, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_div(AbstractTensor<Derived,DIM> &dst, const BinaryMatMulOp<BinaryMatMulOp<TLhs, TRhs0, OtherDIM>, TRhs1, OtherDIM> &src) {
|
||||
FASTOR_IF_CONSTEXPR(BinaryMatMulOp<TLhs, TRhs0, OtherDIM>::flop_count > BinaryMatMulOp<TRhs0, TRhs1, OtherDIM>::flop_count)
|
||||
{
|
||||
using result_t = typename BinaryMatMulOp<TRhs0, TRhs1, OtherDIM>::result_type;
|
||||
result_t tmp;
|
||||
assign(tmp, BinaryMatMulOp<TRhs0, TRhs1, OtherDIM>(src.lhs().rhs().self(),src.rhs().self()));
|
||||
assign_div(dst, BinaryMatMulOp<TLhs, result_t, OtherDIM>(src.lhs().lhs().self(),tmp));
|
||||
}
|
||||
else
|
||||
{
|
||||
using result_t = typename BinaryMatMulOp<TLhs, TRhs0, OtherDIM>::result_type;
|
||||
result_t tmp;
|
||||
assign(tmp, BinaryMatMulOp<TLhs, TRhs0, OtherDIM>(src.lhs().lhs().self(),src.lhs().rhs().self()));
|
||||
assign_div(dst, BinaryMatMulOp<result_t, TRhs1, OtherDIM>(tmp,src.rhs().self()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // end of namespace Fastor
|
||||
|
||||
|
||||
#endif // BINARY_MATMUL_OP_H
|
||||
197
noarch/include/Fastor/expressions/linalg_ops/binary_solve_op.h
Normal file
197
noarch/include/Fastor/expressions/linalg_ops/binary_solve_op.h
Normal file
@@ -0,0 +1,197 @@
|
||||
#ifndef BINARY_SOLVE_OP_H
|
||||
#define BINARY_SOLVE_OP_H
|
||||
|
||||
#include "Fastor/meta/meta.h"
|
||||
#include "Fastor/simd_vector/SIMDVector.h"
|
||||
#include "Fastor/tensor/AbstractTensor.h"
|
||||
#include "Fastor/tensor/TensorTraits.h"
|
||||
#include "Fastor/tensor/Aliasing.h"
|
||||
#include "Fastor/expressions/linalg_ops/linalg_computation_types.h"
|
||||
#include "Fastor/expressions/linalg_ops/binary_matmul_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_inv_op.h"
|
||||
#include "Fastor/expressions/expression_traits.h"
|
||||
#include "Fastor/expressions/linalg_ops/linalg_computation_types.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_piv_op.h"
|
||||
|
||||
|
||||
namespace Fastor {
|
||||
|
||||
// Solving using LU decomposition is in the LU module [unary_lu_op]
|
||||
// For tensors
|
||||
// Solve - no pivot
|
||||
template<SolveCompType SType = SolveCompType::SimpleInv, typename T, size_t I,
|
||||
enable_if_t_< SType == SolveCompType::SimpleInv, bool> = false>
|
||||
FASTOR_INLINE Tensor<T,I> solve(const Tensor<T,I,I> &A, const Tensor<T,I> &b) {
|
||||
return matmul(inverse<InvCompType::SimpleInv>(A),b);
|
||||
}
|
||||
// Solve - pivot
|
||||
template<SolveCompType SType = SolveCompType::SimpleInv, typename T, size_t I,
|
||||
enable_if_t_< SType == SolveCompType::SimpleInvPiv, bool> = false>
|
||||
FASTOR_INLINE Tensor<T,I> solve(const Tensor<T,I,I> &A, const Tensor<T,I> &b) {
|
||||
// We need to post multiply - swap columns using row permutation vector
|
||||
Tensor<size_t,I> p;
|
||||
pivot_inplace(A,p);
|
||||
auto tmp(apply_pivot(A,p));
|
||||
Tensor<T,I,I> invA = inverse<InvCompType::SimpleInv>(tmp);
|
||||
return matmul(reconstruct_colwise(invA,p),b);
|
||||
|
||||
// // matrix version
|
||||
// Tensor<T,I,I> p;
|
||||
// pivot_inplace(A,p);
|
||||
// auto tmp(apply_pivot(A,p));
|
||||
// Tensor<T,I,I> invA = inverse<InvCompType::SimpleInv>(tmp);
|
||||
// return matmul(matmul(invA,p),b);
|
||||
}
|
||||
|
||||
// Multiple right hand sides
|
||||
template<SolveCompType SType = SolveCompType::SimpleInv, typename T, size_t I, size_t J,
|
||||
enable_if_t_< SType == SolveCompType::SimpleInv, bool> = false>
|
||||
FASTOR_INLINE Tensor<T,I,J> solve(const Tensor<T,I,I> &A, const Tensor<T,I,J> &B) {
|
||||
return matmul(inverse(A),B);
|
||||
}
|
||||
|
||||
// Solve - pivot
|
||||
template<SolveCompType SType = SolveCompType::SimpleInv, typename T, size_t I, size_t J,
|
||||
enable_if_t_< SType == SolveCompType::SimpleInvPiv, bool> = false>
|
||||
FASTOR_INLINE Tensor<T,I,J> solve(const Tensor<T,I,I> &A, const Tensor<T,I,J> &B) {
|
||||
Tensor<size_t,I> p;
|
||||
pivot_inplace(A,p);
|
||||
auto tmp(apply_pivot(A,p));
|
||||
Tensor<T,I,I> invA = inverse<InvCompType::SimpleInv>(tmp);
|
||||
return matmul(reconstruct(invA,p),B);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// For expressions
|
||||
template<SolveCompType SType = SolveCompType::SimpleInv,
|
||||
typename TLhs, typename TRhs, size_t DIM0, size_t DIM1,
|
||||
enable_if_t_< is_tensor_v<TLhs> && is_tensor_v<TRhs> && DIM0==2,bool> = false>
|
||||
FASTOR_INLINE
|
||||
conditional_t_<TRhs::result_type::dimension_t::value == 1,
|
||||
Tensor<
|
||||
typename TLhs::scalar_type,
|
||||
get_tensor_dimension_v<0,typename TLhs::result_type>
|
||||
>,
|
||||
Tensor<
|
||||
typename TLhs::scalar_type,
|
||||
get_tensor_dimension_v<0,typename TLhs::result_type>,
|
||||
get_tensor_dimension_v<1,typename TRhs::result_type>
|
||||
>
|
||||
>
|
||||
solve(const AbstractTensor<TLhs,DIM0> &lhs, const AbstractTensor<TRhs,DIM1> &rhs) {
|
||||
|
||||
using lhs_type = typename TLhs::result_type;
|
||||
using rhs_type = typename TRhs::result_type;
|
||||
constexpr FASTOR_INDEX M = get_tensor_dimension_v<0,lhs_type>;
|
||||
constexpr FASTOR_INDEX N = get_tensor_dimension_v<1,lhs_type>;
|
||||
constexpr FASTOR_INDEX M_other = get_tensor_dimension_v<0,rhs_type>;
|
||||
|
||||
static_assert(M==N, "LHS EXPRESSION FOR SOLVE HAS TO BE A SQUARE MATRIX");
|
||||
static_assert(M == M_other, "INVALID SOLVE OPERANDS. IN Ax=b ROWS(A)!=ROWS(b)");
|
||||
|
||||
return solve<SType>(lhs.self(),rhs.self());
|
||||
}
|
||||
|
||||
|
||||
template<SolveCompType SType = SolveCompType::SimpleInv,
|
||||
typename TLhs, typename TRhs, size_t DIM0, size_t DIM1,
|
||||
enable_if_t_< !is_tensor_v<TLhs> && is_tensor_v<TRhs> && DIM0==2,bool> = false>
|
||||
FASTOR_INLINE
|
||||
conditional_t_<TRhs::result_type::dimension_t::value == 1,
|
||||
Tensor<
|
||||
typename TLhs::scalar_type,
|
||||
get_tensor_dimension_v<0,typename TLhs::result_type>
|
||||
>,
|
||||
Tensor<
|
||||
typename TLhs::scalar_type,
|
||||
get_tensor_dimension_v<0,typename TLhs::result_type>,
|
||||
get_tensor_dimension_v<1,typename TRhs::result_type>
|
||||
>
|
||||
>
|
||||
solve(const AbstractTensor<TLhs,DIM0> &lhs, const AbstractTensor<TRhs,DIM1> &rhs) {
|
||||
|
||||
using lhs_type = typename TLhs::result_type;
|
||||
using rhs_type = typename TRhs::result_type;
|
||||
constexpr FASTOR_INDEX M = get_tensor_dimension_v<0,lhs_type>;
|
||||
constexpr FASTOR_INDEX N = get_tensor_dimension_v<1,lhs_type>;
|
||||
constexpr FASTOR_INDEX M_other = get_tensor_dimension_v<0,rhs_type>;
|
||||
|
||||
static_assert(M==N, "LHS EXPRESSION FOR SOLVE HAS TO BE A SQUARE MATRIX");
|
||||
static_assert(M == M_other, "INVALID SOLVE OPERANDS. IN Ax=b ROWS(A)!=ROWS(b)");
|
||||
|
||||
const lhs_type A(lhs.self());
|
||||
|
||||
return solve<SType>(A,rhs.self());
|
||||
}
|
||||
|
||||
|
||||
template<SolveCompType SType = SolveCompType::SimpleInv,
|
||||
typename TLhs, typename TRhs, size_t DIM0, size_t DIM1,
|
||||
enable_if_t_< is_tensor_v<TLhs> && !is_tensor_v<TRhs> && DIM0==2,bool> = false>
|
||||
FASTOR_INLINE
|
||||
conditional_t_<TRhs::result_type::dimension_t::value == 1,
|
||||
Tensor<
|
||||
typename TLhs::scalar_type,
|
||||
get_tensor_dimension_v<0,typename TLhs::result_type>
|
||||
>,
|
||||
Tensor<
|
||||
typename TLhs::scalar_type,
|
||||
get_tensor_dimension_v<0,typename TLhs::result_type>,
|
||||
get_tensor_dimension_v<1,typename TRhs::result_type>
|
||||
>
|
||||
>
|
||||
solve(const AbstractTensor<TLhs,DIM0> &lhs, const AbstractTensor<TRhs,DIM1> &rhs) {
|
||||
|
||||
using lhs_type = typename TLhs::result_type;
|
||||
using rhs_type = typename TRhs::result_type;
|
||||
constexpr FASTOR_INDEX M = get_tensor_dimension_v<0,lhs_type>;
|
||||
constexpr FASTOR_INDEX N = get_tensor_dimension_v<1,lhs_type>;
|
||||
constexpr FASTOR_INDEX M_other = get_tensor_dimension_v<0,rhs_type>;
|
||||
|
||||
static_assert(M==N, "LHS EXPRESSION FOR SOLVE HAS TO BE A SQUARE MATRIX");
|
||||
static_assert(M == M_other, "INVALID SOLVE OPERANDS. IN Ax=b ROWS(A)!=ROWS(b)");
|
||||
|
||||
const rhs_type b(rhs.self());
|
||||
|
||||
return solve<SType>(lhs.self(),b);
|
||||
}
|
||||
|
||||
|
||||
template<SolveCompType SType = SolveCompType::SimpleInv,
|
||||
typename TLhs, typename TRhs, size_t DIM0, size_t DIM1,
|
||||
enable_if_t_< !is_tensor_v<TLhs> && !is_tensor_v<TRhs> && DIM0==2,bool> = false>
|
||||
FASTOR_INLINE
|
||||
conditional_t_<TRhs::result_type::dimension_t::value == 1,
|
||||
Tensor<
|
||||
typename TLhs::scalar_type,
|
||||
get_tensor_dimension_v<0,typename TLhs::result_type>
|
||||
>,
|
||||
Tensor<
|
||||
typename TLhs::scalar_type,
|
||||
get_tensor_dimension_v<0,typename TLhs::result_type>,
|
||||
get_tensor_dimension_v<1,typename TRhs::result_type>
|
||||
>
|
||||
>
|
||||
solve(const AbstractTensor<TLhs,DIM0> &lhs, const AbstractTensor<TRhs,DIM1> &rhs) {
|
||||
|
||||
using lhs_type = typename TLhs::result_type;
|
||||
using rhs_type = typename TRhs::result_type;
|
||||
constexpr FASTOR_INDEX M = get_tensor_dimension_v<0,lhs_type>;
|
||||
constexpr FASTOR_INDEX N = get_tensor_dimension_v<1,lhs_type>;
|
||||
constexpr FASTOR_INDEX M_other = get_tensor_dimension_v<0,rhs_type>;
|
||||
|
||||
static_assert(M==N, "LHS EXPRESSION FOR SOLVE HAS TO BE A SQUARE MATRIX");
|
||||
static_assert(M == M_other, "INVALID SOLVE OPERANDS. IN Ax=b ROWS(A)!=ROWS(b)");
|
||||
|
||||
const lhs_type A(lhs.self());
|
||||
const rhs_type b(rhs.self());
|
||||
|
||||
return solve<SType>(A,b);
|
||||
}
|
||||
|
||||
|
||||
} // end of namespace Fastor
|
||||
|
||||
|
||||
#endif // BINARY_SOLVE_OP_H
|
||||
@@ -0,0 +1,66 @@
|
||||
#ifndef LINALG_COMPUTATION_TYPES_H
|
||||
#define LINALG_COMPUTATION_TYPES_H
|
||||
|
||||
namespace Fastor {
|
||||
|
||||
// Pivot types
|
||||
enum class PivType : int
|
||||
{
|
||||
V = 0, /* The permutation vector */
|
||||
M, /* The permutatoin matrix */
|
||||
};
|
||||
|
||||
|
||||
// QR factorisation computation types
|
||||
enum class QRCompType : int
|
||||
{
|
||||
MGSR = 0, /* Modified Gram-Schmidt Row-wise */
|
||||
MGSRPiv, /* Modified Gram-Schmidt Row-wise with pivot */
|
||||
HHR /* House Holder Reflections */
|
||||
};
|
||||
|
||||
// LU factorisation computation types
|
||||
enum class LUCompType : int
|
||||
{
|
||||
BlockLU = 0, /* Block LU decomposition wiht no pivot */
|
||||
BlockLUPiv, /* Block LU decomposition wiht pivot */
|
||||
SimpleLU, /* Simple LU decomposition wiht no pivot */
|
||||
SimpleLUPiv, /* Simple LU decomposition wiht pivot */
|
||||
};
|
||||
|
||||
// Determinant computation type
|
||||
enum class DetCompType : int
|
||||
{
|
||||
Simple = 0, /* Using simple hand-optimised calculations */
|
||||
LU, /* Using LU factorisation */
|
||||
QR, /* Using QR factorisation */
|
||||
};
|
||||
|
||||
// Inverse computation type
|
||||
enum class InvCompType : int
|
||||
{
|
||||
SimpleInv = 0,/* Using simple hand-optimised calculations */
|
||||
SimpleInvPiv, /* Simple with pivot */
|
||||
BlockLU, /* Using block LU factorisation */
|
||||
BlockLUPiv, /* Using block LU factorisation with pivot */
|
||||
SimpleLU, /* Using simple LU factorisation */
|
||||
SimpleLUPiv, /* Using simple LU factorisation with pivot */
|
||||
};
|
||||
|
||||
// Solve computation type
|
||||
enum class SolveCompType : int
|
||||
{
|
||||
SimpleInv = 0, /* Using optimised inversion */
|
||||
SimpleInvPiv, /* Using optimised inversion with pivot */
|
||||
BlockLU, /* Using block LU factorisation */
|
||||
BlockLUPiv, /* Using block LU factorisation with pivot */
|
||||
SimpleLU, /* Using simple LU factorisation */
|
||||
SimpleLUPiv, /* Using simple LU factorisation with pivot */
|
||||
QR, /* Using QR factorisation */
|
||||
Chol, /* Using Cholesky factorisation */
|
||||
};
|
||||
|
||||
|
||||
} // end of namespace Fastor
|
||||
|
||||
#endif // LINALG_COMPUTATION_TYPES_H
|
||||
24
noarch/include/Fastor/expressions/linalg_ops/linalg_ops.h
Normal file
24
noarch/include/Fastor/expressions/linalg_ops/linalg_ops.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef LINALG_OPS_H
|
||||
#define LINALG_OPS_H
|
||||
|
||||
#include "Fastor/expressions/linalg_ops/linalg_computation_types.h"
|
||||
|
||||
#include "Fastor/expressions/linalg_ops/binary_matmul_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_piv_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_lu_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/binary_solve_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_trans_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_ctrans_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_adj_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_cof_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_inv_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_trace_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_norm_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_qr_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_det_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_svd_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/binary_cross_op.h"
|
||||
|
||||
#include "Fastor/expressions/linalg_ops/linalg_traits.h"
|
||||
|
||||
#endif // LINALG_OPS_H
|
||||
273
noarch/include/Fastor/expressions/linalg_ops/linalg_traits.h
Normal file
273
noarch/include/Fastor/expressions/linalg_ops/linalg_traits.h
Normal file
@@ -0,0 +1,273 @@
|
||||
#ifndef LINALG_TRAITS_H
|
||||
#define LINALG_TRAITS_H
|
||||
|
||||
|
||||
#include "Fastor/expressions/linalg_ops/binary_matmul_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_trans_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_ctrans_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_adj_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_cof_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_inv_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/binary_solve_op.h"
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
namespace Fastor {
|
||||
|
||||
|
||||
// Is a binary matmul expression
|
||||
//----------------------------------------------------------------------------------------------------------//
|
||||
template<typename Derived>
|
||||
struct is_binary_matmul_op {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
template<typename T, size_t ... Rest0, size_t ... Rest1, size_t DIM>
|
||||
struct is_binary_matmul_op<BinaryMatMulOp<Tensor<T,Rest0...>,Tensor<T,Rest1...>,DIM>> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
template<typename Derived0, typename Derived1, size_t DIM>
|
||||
struct is_binary_matmul_op<BinaryMatMulOp<Derived0,Derived1,DIM>> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
template<typename Derived>
|
||||
struct has_binary_matmul_op {
|
||||
static constexpr bool value = is_binary_matmul_op<Derived>::value ? true : false;
|
||||
};
|
||||
template<typename T, size_t ... Rest0, size_t ... Rest1, size_t DIM>
|
||||
struct has_binary_matmul_op<BinaryMatMulOp<Tensor<T,Rest0...>,Tensor<T,Rest1...>,DIM>> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
template<typename Derived0, typename Derived1, size_t DIM>
|
||||
struct has_binary_matmul_op<BinaryMatMulOp<Derived0,Derived1,DIM>> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
template<template<typename,size_t> class UnaryExpr, typename Expr, size_t DIM>
|
||||
struct has_binary_matmul_op<UnaryExpr<Expr,DIM>> {
|
||||
static constexpr bool value = has_binary_matmul_op<Expr>::value;
|
||||
};
|
||||
template<template<class,class,size_t> class BinaryExpr, typename TLhs, typename TRhs, size_t DIMS>
|
||||
struct has_binary_matmul_op<BinaryExpr<TLhs,TRhs,DIMS>> {
|
||||
static constexpr bool value = has_binary_matmul_op<TRhs>::value || has_binary_matmul_op<TLhs>::value;
|
||||
};
|
||||
|
||||
// helper
|
||||
template<typename Derived>
|
||||
static constexpr bool is_binary_matmul_op_v = is_binary_matmul_op<Derived>::value;
|
||||
template<typename Derived>
|
||||
static constexpr bool has_binary_matmul_op_v = has_binary_matmul_op<Derived>::value;
|
||||
//----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
|
||||
// Is unary trans expression
|
||||
//----------------------------------------------------------------------------------------------------------//
|
||||
template<typename Derived>
|
||||
struct is_unary_trans_op {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
template<typename Derived, size_t DIM>
|
||||
struct is_unary_trans_op<UnaryTransOp<Derived,DIM>> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
template<typename Derived>
|
||||
struct has_unary_trans_op {
|
||||
static constexpr bool value = is_unary_trans_op<Derived>::value ? true : false;
|
||||
};
|
||||
template<typename Derived, size_t DIM>
|
||||
struct has_unary_trans_op<UnaryTransOp<Derived,DIM>> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
template<template<typename,size_t> class UnaryExpr, typename Expr, size_t DIM>
|
||||
struct has_unary_trans_op<UnaryExpr<Expr,DIM>> {
|
||||
static constexpr bool value = has_unary_trans_op<Expr>::value;
|
||||
};
|
||||
template<template<class,class,size_t> class BinaryExpr, typename TLhs, typename TRhs, size_t DIMS>
|
||||
struct has_unary_trans_op<BinaryExpr<TLhs,TRhs,DIMS>> {
|
||||
static constexpr bool value = has_unary_trans_op<TRhs>::value || has_unary_trans_op<TLhs>::value;
|
||||
};
|
||||
|
||||
// helper
|
||||
template<typename Derived>
|
||||
static constexpr bool is_unary_trans_op_v = is_unary_trans_op<Derived>::value;
|
||||
template<typename Derived>
|
||||
static constexpr bool has_unary_trans_op_v = has_unary_trans_op<Derived>::value;
|
||||
//----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
|
||||
// Is unary ctrans expression
|
||||
//----------------------------------------------------------------------------------------------------------//
|
||||
template<typename Derived>
|
||||
struct is_unary_ctrans_op {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
template<typename Derived, size_t DIM>
|
||||
struct is_unary_ctrans_op<UnaryCTransOp<Derived,DIM>> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
template<typename Derived>
|
||||
struct has_unary_ctrans_op {
|
||||
static constexpr bool value = is_unary_ctrans_op<Derived>::value ? true : false;
|
||||
};
|
||||
template<typename Derived, size_t DIM>
|
||||
struct has_unary_ctrans_op<UnaryCTransOp<Derived,DIM>> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
template<template<typename,size_t> class UnaryExpr, typename Expr, size_t DIM>
|
||||
struct has_unary_ctrans_op<UnaryExpr<Expr,DIM>> {
|
||||
static constexpr bool value = has_unary_ctrans_op<Expr>::value;
|
||||
};
|
||||
template<template<class,class,size_t> class BinaryExpr, typename TLhs, typename TRhs, size_t DIMS>
|
||||
struct has_unary_ctrans_op<BinaryExpr<TLhs,TRhs,DIMS>> {
|
||||
static constexpr bool value = has_unary_ctrans_op<TRhs>::value || has_unary_ctrans_op<TLhs>::value;
|
||||
};
|
||||
|
||||
// helper
|
||||
template<typename Derived>
|
||||
static constexpr bool is_unary_ctrans_op_v = is_unary_ctrans_op<Derived>::value;
|
||||
template<typename Derived>
|
||||
static constexpr bool has_unary_ctrans_op_v = has_unary_ctrans_op<Derived>::value;
|
||||
//----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
|
||||
// Is unary adj expression
|
||||
//----------------------------------------------------------------------------------------------------------//
|
||||
template<typename Derived>
|
||||
struct is_unary_adj_op {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
template<typename Derived, size_t DIM>
|
||||
struct is_unary_adj_op<UnaryAdjOp<Derived,DIM>> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
template<typename Derived>
|
||||
struct has_unary_adj_op {
|
||||
static constexpr bool value = is_unary_adj_op<Derived>::value ? true : false;
|
||||
};
|
||||
template<typename Derived, size_t DIM>
|
||||
struct has_unary_adj_op<UnaryAdjOp<Derived,DIM>> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
template<template<typename,size_t> class UnaryExpr, typename Expr, size_t DIM>
|
||||
struct has_unary_adj_op<UnaryExpr<Expr,DIM>> {
|
||||
static constexpr bool value = has_unary_adj_op<Expr>::value;
|
||||
};
|
||||
template<template<class,class,size_t> class BinaryExpr, typename TLhs, typename TRhs, size_t DIMS>
|
||||
struct has_unary_adj_op<BinaryExpr<TLhs,TRhs,DIMS>> {
|
||||
static constexpr bool value = has_unary_adj_op<TRhs>::value || has_unary_adj_op<TLhs>::value;
|
||||
};
|
||||
|
||||
// helper
|
||||
template<typename Derived>
|
||||
static constexpr bool is_unary_adj_op_v = is_unary_adj_op<Derived>::value;
|
||||
template<typename Derived>
|
||||
static constexpr bool has_unary_adj_op_v = has_unary_adj_op<Derived>::value;
|
||||
//----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
// Is unary adj expression
|
||||
//----------------------------------------------------------------------------------------------------------//
|
||||
template<typename Derived>
|
||||
struct is_unary_cof_op {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
template<typename Derived, size_t DIM>
|
||||
struct is_unary_cof_op<UnaryCofOp<Derived,DIM>> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
template<typename Derived>
|
||||
struct has_unary_cof_op {
|
||||
static constexpr bool value = is_unary_cof_op<Derived>::value ? true : false;
|
||||
};
|
||||
template<typename Derived, size_t DIM>
|
||||
struct has_unary_cof_op<UnaryCofOp<Derived,DIM>> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
template<template<typename,size_t> class UnaryExpr, typename Expr, size_t DIM>
|
||||
struct has_unary_cof_op<UnaryExpr<Expr,DIM>> {
|
||||
static constexpr bool value = has_unary_cof_op<Expr>::value;
|
||||
};
|
||||
template<template<class,class,size_t> class BinaryExpr, typename TLhs, typename TRhs, size_t DIMS>
|
||||
struct has_unary_cof_op<BinaryExpr<TLhs,TRhs,DIMS>> {
|
||||
static constexpr bool value = has_unary_cof_op<TRhs>::value || has_unary_cof_op<TLhs>::value;
|
||||
};
|
||||
|
||||
// helper
|
||||
template<typename Derived>
|
||||
static constexpr bool is_unary_cof_op_v = is_unary_cof_op<Derived>::value;
|
||||
template<typename Derived>
|
||||
static constexpr bool has_unary_cof_op_v = has_unary_cof_op<Derived>::value;
|
||||
//----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
|
||||
// Is unary inv expression
|
||||
//----------------------------------------------------------------------------------------------------------//
|
||||
template<typename Derived>
|
||||
struct is_unary_inv_op {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
template<typename Derived, size_t DIM>
|
||||
struct is_unary_inv_op<UnaryInvOp<Derived,DIM>> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
template<typename Derived>
|
||||
struct has_unary_inv_op {
|
||||
static constexpr bool value = is_unary_inv_op<Derived>::value ? true : false;
|
||||
};
|
||||
template<typename Derived, size_t DIM>
|
||||
struct has_unary_inv_op<UnaryInvOp<Derived,DIM>> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
template<template<typename,size_t> class UnaryExpr, typename Expr, size_t DIM>
|
||||
struct has_unary_inv_op<UnaryExpr<Expr,DIM>> {
|
||||
static constexpr bool value = has_unary_inv_op<Expr>::value;
|
||||
};
|
||||
template<template<class,class,size_t> class BinaryExpr, typename TLhs, typename TRhs, size_t DIMS>
|
||||
struct has_unary_inv_op<BinaryExpr<TLhs,TRhs,DIMS>> {
|
||||
static constexpr bool value = has_unary_inv_op<TRhs>::value || has_unary_inv_op<TLhs>::value;
|
||||
};
|
||||
|
||||
// helper
|
||||
template<typename Derived>
|
||||
static constexpr bool is_unary_inv_op_v = is_unary_inv_op<Derived>::value;
|
||||
template<typename Derived>
|
||||
static constexpr bool has_unary_inv_op_v = has_unary_inv_op<Derived>::value;
|
||||
//----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
|
||||
// Is a linear algebra expression
|
||||
//----------------------------------------------------------------------------------------------------------//
|
||||
template<typename Derived>
|
||||
struct has_linalg_op {
|
||||
static constexpr bool value = has_binary_matmul_op<Derived>::value || has_unary_trans_op<Derived>::value ||
|
||||
has_unary_ctrans_op<Derived>::value || has_unary_adj_op<Derived>::value ||
|
||||
has_unary_cof_op<Derived>::value || has_unary_inv_op<Derived>::value;
|
||||
};
|
||||
|
||||
// helper
|
||||
template<typename Derived>
|
||||
static constexpr bool has_linalg_op_v = has_linalg_op<Derived>::value;
|
||||
//----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
|
||||
// Requires immediate evaluation
|
||||
//----------------------------------------------------------------------------------------------------------//
|
||||
template<typename Derived>
|
||||
struct requires_evaluation {
|
||||
static constexpr bool value = has_linalg_op<Derived>::value;
|
||||
};
|
||||
|
||||
// helper
|
||||
template<typename Derived>
|
||||
static constexpr bool requires_evaluation_v = requires_evaluation<Derived>::value;
|
||||
//----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
|
||||
|
||||
} // end of namespace Fastor
|
||||
|
||||
|
||||
#endif // LINALG_TRAITS_H
|
||||
160
noarch/include/Fastor/expressions/linalg_ops/unary_adj_op.h
Normal file
160
noarch/include/Fastor/expressions/linalg_ops/unary_adj_op.h
Normal file
@@ -0,0 +1,160 @@
|
||||
#ifndef UNARY_ADJ_OP_H
|
||||
#define UNARY_ADJ_OP_H
|
||||
|
||||
#include "Fastor/meta/meta.h"
|
||||
#include "Fastor/backend/adjoint.h"
|
||||
#include "Fastor/simd_vector/SIMDVector.h"
|
||||
#include "Fastor/tensor/AbstractTensor.h"
|
||||
#include "Fastor/tensor/Aliasing.h"
|
||||
#include "Fastor/tensor/Tensor.h"
|
||||
#include "Fastor/tensor/Ranges.h"
|
||||
#include "Fastor/tensor/TensorTraits.h"
|
||||
#include "Fastor/expressions/expression_traits.h"
|
||||
|
||||
|
||||
namespace Fastor {
|
||||
|
||||
template<typename Expr, size_t DIM0>
|
||||
struct UnaryAdjOp: public AbstractTensor<UnaryAdjOp<Expr, DIM0>,DIM0> {
|
||||
using expr_type = expression_t<Expr>;
|
||||
using result_type = typename Expr::result_type;
|
||||
static constexpr FASTOR_INDEX M = get_tensor_dimension_v<0,result_type>;
|
||||
static constexpr FASTOR_INDEX N = get_tensor_dimension_v<1,result_type>;
|
||||
static constexpr FASTOR_INDEX Dimension = DIM0;
|
||||
static constexpr FASTOR_INDEX rank() {return DIM0;}
|
||||
using scalar_type = typename scalar_type_finder<UnaryAdjOp<Expr, DIM0>>::type;
|
||||
using simd_vector_type = typename Expr::simd_vector_type;
|
||||
using simd_abi_type = typename simd_vector_type::abi_type;
|
||||
|
||||
FASTOR_INLINE UnaryAdjOp(expr_type inexpr) : _expr(inexpr) {
|
||||
static_assert(M==N, "MATRIX MUST BE SQUARE");
|
||||
}
|
||||
|
||||
FASTOR_INLINE FASTOR_INDEX size() const {return M*N;}
|
||||
FASTOR_INLINE FASTOR_INDEX dimension(FASTOR_INDEX ) const {return M;}
|
||||
|
||||
constexpr FASTOR_INLINE expr_type expr() const {return _expr;}
|
||||
|
||||
private:
|
||||
expr_type _expr;
|
||||
};
|
||||
|
||||
template<typename Expr, size_t DIM0>
|
||||
FASTOR_INLINE UnaryAdjOp<Expr, DIM0>
|
||||
adj(const AbstractTensor<Expr,DIM0> &src) {
|
||||
return UnaryAdjOp<Expr, DIM0>(src.self());
|
||||
}
|
||||
|
||||
|
||||
namespace internal {
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,0> && is_less_equal_v_<M,4>,bool> = false>
|
||||
FASTOR_INLINE void adjoint_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
_adjoint<T,M>(in.data(),out.data());
|
||||
}
|
||||
} // internal
|
||||
|
||||
|
||||
// For tensors
|
||||
template<typename T, size_t I>
|
||||
FASTOR_INLINE Tensor<T,I,I> adjoint(const Tensor<T,I,I> &a) {
|
||||
Tensor<T,I,I> out;
|
||||
_adjoint<T,I>(a.data(),out.data());
|
||||
return out;
|
||||
}
|
||||
|
||||
// For high order tensors
|
||||
template<typename T, size_t ... Rest, typename std::enable_if<sizeof...(Rest)>=3,bool>::type=0>
|
||||
FASTOR_INLINE Tensor<T,Rest...>
|
||||
adjoint(const Tensor<T,Rest...> &a) {
|
||||
|
||||
constexpr size_t remaining_product = last_matrix_extracter<Tensor<T,Rest...>,
|
||||
typename std_ext::make_index_sequence<sizeof...(Rest)-2>::type>::remaining_product;
|
||||
|
||||
constexpr size_t I = get_value<sizeof...(Rest)-1,Rest...>::value;
|
||||
constexpr size_t J = get_value<sizeof...(Rest),Rest...>::value;
|
||||
static_assert(I==J,"THE LAST TWO DIMENSIONS OF TENSOR MUST BE THE SAME");
|
||||
|
||||
Tensor<T,Rest...> out;
|
||||
T *a_data = a.data();
|
||||
T *out_data = out.data();
|
||||
|
||||
for (size_t i=0; i<remaining_product; ++i) {
|
||||
_adjoint<T,J,J>(a_data+i*J*J,out_data+i*J*J);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// Adjoint for generic expressions is provided here
|
||||
template<typename Derived, size_t DIM>
|
||||
FASTOR_INLINE
|
||||
typename Derived::result_type
|
||||
adjoint(const AbstractTensor<Derived,DIM> &src) {
|
||||
// If we are here Derived is already an expression
|
||||
using result_type = typename Derived::result_type;
|
||||
const result_type tmp(src.self());
|
||||
return adjoint(tmp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// assignments
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign(AbstractTensor<Derived,DIM> &dst, const UnaryAdjOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
internal::adjoint_dispatcher(tmp,dst.self());
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_add(AbstractTensor<Derived,DIM> &dst, const UnaryAdjOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
// no copies if expr is a tensor
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
// one copy for the inverse
|
||||
result_type tmp_inv;
|
||||
internal::adjoint_dispatcher(tmp,tmp_inv);
|
||||
trivial_assign_add(dst.self(),tmp_inv);
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_sub(AbstractTensor<Derived,DIM> &dst, const UnaryAdjOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
// no copies if expr is a tensor
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
// one copy for the inverse
|
||||
result_type tmp_inv;
|
||||
internal::adjoint_dispatcher(tmp,tmp_inv);
|
||||
trivial_assign_sub(dst.self(),tmp_inv);
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_mul(AbstractTensor<Derived,DIM> &dst, const UnaryAdjOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
// no copies if expr is a tensor
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
// one copy for the inverse
|
||||
result_type tmp_inv;
|
||||
internal::adjoint_dispatcher(tmp,tmp_inv);
|
||||
trivial_assign_mul(dst.self(),tmp_inv);
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_div(AbstractTensor<Derived,DIM> &dst, const UnaryAdjOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
// no copies if expr is a tensor
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
// one copy for the inverse
|
||||
result_type tmp_inv;
|
||||
internal::adjoint_dispatcher(tmp,tmp_inv);
|
||||
trivial_assign_div(dst.self(),tmp_inv);
|
||||
}
|
||||
|
||||
|
||||
} // end of namespace Fastor
|
||||
|
||||
|
||||
#endif // UNARY_ADJ_OP_H
|
||||
160
noarch/include/Fastor/expressions/linalg_ops/unary_cof_op.h
Normal file
160
noarch/include/Fastor/expressions/linalg_ops/unary_cof_op.h
Normal file
@@ -0,0 +1,160 @@
|
||||
#ifndef UNARY_COF_OP_H
|
||||
#define UNARY_COF_OP_H
|
||||
|
||||
#include "Fastor/meta/meta.h"
|
||||
#include "Fastor/backend/cofactor.h"
|
||||
#include "Fastor/simd_vector/SIMDVector.h"
|
||||
#include "Fastor/tensor/AbstractTensor.h"
|
||||
#include "Fastor/tensor/Aliasing.h"
|
||||
#include "Fastor/tensor/Tensor.h"
|
||||
#include "Fastor/tensor/Ranges.h"
|
||||
#include "Fastor/tensor/TensorTraits.h"
|
||||
#include "Fastor/expressions/expression_traits.h"
|
||||
|
||||
|
||||
namespace Fastor {
|
||||
|
||||
template<typename Expr, size_t DIM0>
|
||||
struct UnaryCofOp: public AbstractTensor<UnaryCofOp<Expr, DIM0>,DIM0> {
|
||||
using expr_type = expression_t<Expr>;
|
||||
using result_type = typename Expr::result_type;
|
||||
static constexpr FASTOR_INDEX M = get_tensor_dimension_v<0,result_type>;
|
||||
static constexpr FASTOR_INDEX N = get_tensor_dimension_v<1,result_type>;
|
||||
static constexpr FASTOR_INDEX Dimension = DIM0;
|
||||
static constexpr FASTOR_INDEX rank() {return DIM0;}
|
||||
using scalar_type = typename scalar_type_finder<UnaryCofOp<Expr, DIM0>>::type;
|
||||
using simd_vector_type = typename Expr::simd_vector_type;
|
||||
using simd_abi_type = typename simd_vector_type::abi_type;
|
||||
|
||||
FASTOR_INLINE UnaryCofOp(expr_type inexpr) : _expr(inexpr) {
|
||||
static_assert(M==N, "MATRIX MUST BE SQUARE");
|
||||
}
|
||||
|
||||
FASTOR_INLINE FASTOR_INDEX size() const {return M*N;}
|
||||
FASTOR_INLINE FASTOR_INDEX dimension(FASTOR_INDEX ) const {return M;}
|
||||
|
||||
constexpr FASTOR_INLINE expr_type expr() const {return _expr;}
|
||||
|
||||
private:
|
||||
expr_type _expr;
|
||||
};
|
||||
|
||||
template<typename Expr, size_t DIM0>
|
||||
FASTOR_INLINE UnaryCofOp<Expr, DIM0>
|
||||
cof(const AbstractTensor<Expr,DIM0> &src) {
|
||||
return UnaryCofOp<Expr, DIM0>(src.self());
|
||||
}
|
||||
|
||||
|
||||
namespace internal {
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,0> && is_less_equal_v_<M,4>,bool> = false>
|
||||
FASTOR_INLINE void cofactor_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
_cofactor<T,M>(in.data(),out.data());
|
||||
}
|
||||
} // internal
|
||||
|
||||
|
||||
// For tensors
|
||||
template<typename T, size_t I>
|
||||
FASTOR_INLINE Tensor<T,I,I> cofactor(const Tensor<T,I,I> &a) {
|
||||
Tensor<T,I,I> out;
|
||||
_cofactor<T,I>(a.data(),out.data());
|
||||
return out;
|
||||
}
|
||||
|
||||
// For high order tensors
|
||||
template<typename T, size_t ... Rest, typename std::enable_if<sizeof...(Rest)>=3,bool>::type=0>
|
||||
FASTOR_INLINE Tensor<T,Rest...>
|
||||
cofactor(const Tensor<T,Rest...> &a) {
|
||||
|
||||
constexpr size_t remaining_product = last_matrix_extracter<Tensor<T,Rest...>,
|
||||
typename std_ext::make_index_sequence<sizeof...(Rest)-2>::type>::remaining_product;
|
||||
|
||||
constexpr size_t I = get_value<sizeof...(Rest)-1,Rest...>::value;
|
||||
constexpr size_t J = get_value<sizeof...(Rest),Rest...>::value;
|
||||
static_assert(I==J,"THE LAST TWO DIMENSIONS OF TENSOR MUST BE THE SAME");
|
||||
|
||||
Tensor<T,Rest...> out;
|
||||
T *a_data = a.data();
|
||||
T *out_data = out.data();
|
||||
|
||||
for (size_t i=0; i<remaining_product; ++i) {
|
||||
_cofactor<T,J,J>(a_data+i*J*J,out_data+i*J*J);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// Cofactor for generic expressions is provided here
|
||||
template<typename Derived, size_t DIM>
|
||||
FASTOR_INLINE
|
||||
typename Derived::result_type
|
||||
cofactor(const AbstractTensor<Derived,DIM> &src) {
|
||||
// If we are here Derived is already an expression
|
||||
using result_type = typename Derived::result_type;
|
||||
const result_type tmp(src.self());
|
||||
return cofactor(tmp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// assignments
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign(AbstractTensor<Derived,DIM> &dst, const UnaryCofOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
internal::cofactor_dispatcher(tmp,dst.self());
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_add(AbstractTensor<Derived,DIM> &dst, const UnaryCofOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
// no copies if expr is a tensor
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
// one copy for the inverse
|
||||
result_type tmp_inv;
|
||||
internal::cofactor_dispatcher(tmp,tmp_inv);
|
||||
trivial_assign_add(dst.self(),tmp_inv);
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_sub(AbstractTensor<Derived,DIM> &dst, const UnaryCofOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
// no copies if expr is a tensor
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
// one copy for the inverse
|
||||
result_type tmp_inv;
|
||||
internal::cofactor_dispatcher(tmp,tmp_inv);
|
||||
trivial_assign_sub(dst.self(),tmp_inv);
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_mul(AbstractTensor<Derived,DIM> &dst, const UnaryCofOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
// no copies if expr is a tensor
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
// one copy for the inverse
|
||||
result_type tmp_inv;
|
||||
internal::cofactor_dispatcher(tmp,tmp_inv);
|
||||
trivial_assign_mul(dst.self(),tmp_inv);
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_div(AbstractTensor<Derived,DIM> &dst, const UnaryCofOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
// no copies if expr is a tensor
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
// one copy for the inverse
|
||||
result_type tmp_inv;
|
||||
internal::cofactor_dispatcher(tmp,tmp_inv);
|
||||
trivial_assign_div(dst.self(),tmp_inv);
|
||||
}
|
||||
|
||||
|
||||
} // end of namespace Fastor
|
||||
|
||||
|
||||
#endif // UNARY_COF_OP_H
|
||||
178
noarch/include/Fastor/expressions/linalg_ops/unary_ctrans_op.h
Normal file
178
noarch/include/Fastor/expressions/linalg_ops/unary_ctrans_op.h
Normal file
@@ -0,0 +1,178 @@
|
||||
#ifndef UNARY_CTRANS_OP_H
|
||||
#define UNARY_CTRANS_OP_H
|
||||
|
||||
#include "Fastor/meta/meta.h"
|
||||
#include "Fastor/simd_vector/SIMDVector.h"
|
||||
#include "Fastor/tensor/AbstractTensor.h"
|
||||
#include "Fastor/tensor/Aliasing.h"
|
||||
#include "Fastor/tensor/Tensor.h"
|
||||
#include "Fastor/tensor/Ranges.h"
|
||||
#include "Fastor/tensor/TensorTraits.h"
|
||||
#include "Fastor/expressions/expression_traits.h"
|
||||
|
||||
|
||||
namespace Fastor {
|
||||
|
||||
template<typename Expr, size_t DIM0>
|
||||
struct UnaryCTransOp: public AbstractTensor<UnaryCTransOp<Expr, DIM0>,DIM0> {
|
||||
using expr_type = expression_t<Expr>;
|
||||
static constexpr FASTOR_INDEX M = get_tensor_dimension_v<0,typename Expr::result_type>;
|
||||
static constexpr FASTOR_INDEX N = get_tensor_dimension_v<1,typename Expr::result_type>;
|
||||
static constexpr FASTOR_INDEX Dimension = DIM0;
|
||||
static constexpr FASTOR_INDEX rank() {return DIM0;}
|
||||
using scalar_type = typename scalar_type_finder<UnaryCTransOp<Expr, DIM0>>::type;
|
||||
using simd_vector_type = typename Expr::simd_vector_type;
|
||||
using simd_abi_type = typename simd_vector_type::abi_type;
|
||||
using result_type = Tensor<scalar_type,N,M>;
|
||||
|
||||
FASTOR_INLINE UnaryCTransOp(expr_type inexpr) : _expr(inexpr) {
|
||||
}
|
||||
|
||||
FASTOR_INLINE FASTOR_INDEX size() const {return M*N;}
|
||||
FASTOR_INLINE FASTOR_INDEX dimension(FASTOR_INDEX i) const {return i == 0 ? N : M;}
|
||||
|
||||
constexpr FASTOR_INLINE expr_type expr() const {return _expr;}
|
||||
|
||||
private:
|
||||
expr_type _expr;
|
||||
};
|
||||
|
||||
/* Tensor conjugate transpose returning a tensor expression */
|
||||
template<typename Expr, size_t DIM0>
|
||||
FASTOR_INLINE UnaryCTransOp<Expr, DIM0>
|
||||
ctrans(const AbstractTensor<Expr,DIM0> &src) {
|
||||
return UnaryCTransOp<Expr, DIM0>(src.self());
|
||||
}
|
||||
|
||||
/* Backend implementation */
|
||||
template<typename T, size_t M, size_t N>
|
||||
FASTOR_INLINE void _ctranspose(const T * FASTOR_RESTRICT a, T * FASTOR_RESTRICT out) {
|
||||
for (size_t j=0; j<N; ++j)
|
||||
for (size_t i=0; i< M; ++i)
|
||||
out[j*M+i] = conj(a[i*N+j]);
|
||||
}
|
||||
|
||||
/* Tensor conjugate transpose immediately returning a tensor */
|
||||
template<typename T, size_t I, size_t J>
|
||||
FASTOR_INLINE Tensor<T,J,I> ctranspose(const Tensor<T,I,J> &a) {
|
||||
Tensor<T,J,I> out;
|
||||
_ctranspose<T,I,J>(a.data(),out.data());
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Tensor conjugate transpose for higher order tensors immediately returning a tensor */
|
||||
template<typename T, size_t ... Rest, typename std::enable_if<sizeof...(Rest)>=3,bool>::type=0>
|
||||
FASTOR_INLINE Tensor<T,Rest...>
|
||||
ctranspose(const Tensor<T,Rest...> &a) {
|
||||
|
||||
constexpr size_t remaining_product = last_matrix_extracter<Tensor<T,Rest...>,
|
||||
typename std_ext::make_index_sequence<sizeof...(Rest)-2>::type>::remaining_product;
|
||||
|
||||
constexpr size_t I = get_value<sizeof...(Rest)-1,Rest...>::value;
|
||||
constexpr size_t J = get_value<sizeof...(Rest),Rest...>::value;
|
||||
static_assert(I==J,"THE LAST TWO DIMENSIONS OF TENSOR MUST BE THE SAME");
|
||||
|
||||
Tensor<T,Rest...> out;
|
||||
T *a_data = a.data();
|
||||
T *out_data = out.data();
|
||||
|
||||
for (size_t i=0; i<remaining_product; ++i) {
|
||||
_ctranspose<T,J,J>(a_data+i*J*J,out_data+i*J*J);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// Conjagate transpose for generic expressions
|
||||
template<typename Expr, size_t DIM0>
|
||||
FASTOR_INLINE
|
||||
Tensor<
|
||||
typename scalar_type_finder<Expr>::type,
|
||||
get_tensor_dimension_v<1,typename Expr::result_type>,
|
||||
get_tensor_dimension_v<0,typename Expr::result_type>>
|
||||
ctranspose(const AbstractTensor<Expr,DIM0> &src) {
|
||||
// If we are here Expr is already an expression
|
||||
using result_type = typename Expr::result_type;
|
||||
const result_type tmp(src.self());
|
||||
return ctranspose(tmp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// assignments
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign(AbstractTensor<Derived,DIM> &dst, const UnaryCTransOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
using T = typename UnaryCTransOp<Expr, OtherDIM>::scalar_type;
|
||||
static constexpr size_t M = UnaryCTransOp<Expr, OtherDIM>::M;
|
||||
static constexpr size_t N = UnaryCTransOp<Expr, OtherDIM>::N;
|
||||
_ctranspose<T,M,N>(tmp.data(),dst.self().data());
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_add(AbstractTensor<Derived,DIM> &dst, const UnaryCTransOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
// no copies if expr is a tensor
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
// one copy for the inverse
|
||||
using T = typename UnaryCTransOp<Expr, OtherDIM>::scalar_type;
|
||||
using result_t = typename UnaryCTransOp<Expr, OtherDIM>::result_type;
|
||||
result_t tmp_trans;
|
||||
static constexpr size_t M = UnaryCTransOp<Expr, OtherDIM>::M;
|
||||
static constexpr size_t N = UnaryCTransOp<Expr, OtherDIM>::N;
|
||||
_ctranspose<T,M,N>(tmp.data(),tmp_trans.data());
|
||||
trivial_assign_add(dst.self(),tmp_trans);
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_sub(AbstractTensor<Derived,DIM> &dst, const UnaryCTransOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
// no copies if expr is a tensor
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
// one copy for the inverse
|
||||
using T = typename UnaryCTransOp<Expr, OtherDIM>::scalar_type;
|
||||
using result_t = typename UnaryCTransOp<Expr, OtherDIM>::result_type;
|
||||
result_t tmp_trans;
|
||||
static constexpr size_t M = UnaryCTransOp<Expr, OtherDIM>::M;
|
||||
static constexpr size_t N = UnaryCTransOp<Expr, OtherDIM>::N;
|
||||
_ctranspose<T,M,N>(tmp.data(),tmp_trans.data());
|
||||
trivial_assign_sub(dst.self(),tmp_trans);
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_mul(AbstractTensor<Derived,DIM> &dst, const UnaryCTransOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
// no copies if expr is a tensor
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
// one copy for the inverse
|
||||
using T = typename UnaryCTransOp<Expr, OtherDIM>::scalar_type;
|
||||
using result_t = typename UnaryCTransOp<Expr, OtherDIM>::result_type;
|
||||
result_t tmp_trans;
|
||||
static constexpr size_t M = UnaryCTransOp<Expr, OtherDIM>::M;
|
||||
static constexpr size_t N = UnaryCTransOp<Expr, OtherDIM>::N;
|
||||
_ctranspose<T,M,N>(tmp.data(),tmp_trans.data());
|
||||
trivial_assign_mul(dst.self(),tmp_trans);
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_div(AbstractTensor<Derived,DIM> &dst, const UnaryCTransOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
// no copies if expr is a tensor
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
// one copy for the inverse
|
||||
using T = typename UnaryCTransOp<Expr, OtherDIM>::scalar_type;
|
||||
using result_t = typename UnaryCTransOp<Expr, OtherDIM>::result_type;
|
||||
result_t tmp_trans;
|
||||
static constexpr size_t M = UnaryCTransOp<Expr, OtherDIM>::M;
|
||||
static constexpr size_t N = UnaryCTransOp<Expr, OtherDIM>::N;
|
||||
_ctranspose<T,M,N>(tmp.data(),tmp_trans.data());
|
||||
trivial_assign_div(dst.self(),tmp_trans);
|
||||
}
|
||||
|
||||
|
||||
} // end of namespace Fastor
|
||||
|
||||
|
||||
#endif // UNARY_CTRANS_OP_H
|
||||
86
noarch/include/Fastor/expressions/linalg_ops/unary_det_op.h
Normal file
86
noarch/include/Fastor/expressions/linalg_ops/unary_det_op.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#ifndef UNARY_DET_OP_H
|
||||
#define UNARY_DET_OP_H
|
||||
|
||||
#include "Fastor/meta/meta.h"
|
||||
#include "Fastor/simd_vector/SIMDVector.h"
|
||||
#include "Fastor/backend/determinant.h"
|
||||
#include "Fastor/tensor/AbstractTensor.h"
|
||||
#include "Fastor/tensor/TensorTraits.h"
|
||||
#include "Fastor/expressions/expression_traits.h"
|
||||
#include "Fastor/expressions/linalg_ops/linalg_computation_types.h"
|
||||
#include "Fastor/expressions/linalg_ops/linalg_traits.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace Fastor {
|
||||
|
||||
// Computing determinant using QR/LU decompositions are in those respective modules
|
||||
// For tensors
|
||||
template<DetCompType DetType = DetCompType::Simple, typename T, size_t M,
|
||||
enable_if_t_<is_less_equal_v_<M,4UL> && DetType==DetCompType::Simple,bool> = false>
|
||||
FASTOR_INLINE T determinant(const Tensor<T,M,M> &a) {
|
||||
return _det<T,M,M>(static_cast<const T *>(a.data()));
|
||||
}
|
||||
template<DetCompType DetType = DetCompType::Simple, typename T, size_t M,
|
||||
enable_if_t_<is_greater_v_<M,4UL> && DetType == DetCompType::Simple,bool> = false>
|
||||
FASTOR_INLINE T determinant(const Tensor<T,M,M> &a) {
|
||||
// Dispatch to LU
|
||||
return determinant<DetCompType::LU>(a);
|
||||
}
|
||||
|
||||
// For high order tensors
|
||||
template<DetCompType DetType = DetCompType::Simple,
|
||||
typename T, size_t ... Rest, enable_if_t_<sizeof...(Rest)>=3 && DetType == DetCompType::Simple,bool> = false>
|
||||
FASTOR_INLINE
|
||||
typename last_matrix_extracter<Tensor<T,Rest...>, typename std_ext::make_index_sequence<sizeof...(Rest)-2>::type>::type
|
||||
determinant(const Tensor<T,Rest...> &a) {
|
||||
|
||||
using OutTensor = typename last_matrix_extracter<Tensor<T,Rest...>,
|
||||
typename std_ext::make_index_sequence<sizeof...(Rest)-2>::type>::type;
|
||||
constexpr size_t remaining_product = last_matrix_extracter<Tensor<T,Rest...>,
|
||||
typename std_ext::make_index_sequence<sizeof...(Rest)-2>::type>::remaining_product;
|
||||
|
||||
constexpr size_t I = get_value<sizeof...(Rest)-1,Rest...>::value;
|
||||
constexpr size_t J = get_value<sizeof...(Rest),Rest...>::value;
|
||||
static_assert(I==J,"THE LAST TWO DIMENSIONS OF TENSOR MUST BE THE SAME");
|
||||
|
||||
OutTensor out;
|
||||
T *a_data = a.data();
|
||||
T *out_data = out.data();
|
||||
|
||||
for (size_t i=0; i<remaining_product; ++i) {
|
||||
out_data[i] = _det<T,J,J>(static_cast<const T *>(a_data+i*J*J));
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// Find determinant of a tensor expression - dispatches to determinant for tensors
|
||||
template<DetCompType DetType = DetCompType::Simple, typename Derived, size_t DIMS>
|
||||
FASTOR_INLINE typename Derived::scalar_type determinant(const AbstractTensor<Derived,DIMS> &_src) {
|
||||
const Derived &src = _src.self();
|
||||
using result_type = typename Derived::result_type;
|
||||
const result_type out = evaluate(src);
|
||||
return determinant<DetType>(out);
|
||||
}
|
||||
|
||||
|
||||
template<DetCompType DetType = DetCompType::Simple, typename Derived, size_t DIMS>
|
||||
FASTOR_INLINE typename Derived::scalar_type det(const AbstractTensor<Derived,DIMS> &_src) {
|
||||
return determinant<DetType>(_src.self());
|
||||
}
|
||||
|
||||
template<DetCompType DetType = DetCompType::Simple, typename Derived, size_t DIMS>
|
||||
FASTOR_INLINE typename Derived::scalar_type absdet(const AbstractTensor<Derived,DIMS> &_src) {
|
||||
return std::abs(determinant<DetType>(_src.self()));
|
||||
}
|
||||
|
||||
template<DetCompType DetType = DetCompType::Simple, typename Derived, size_t DIMS>
|
||||
FASTOR_INLINE typename Derived::scalar_type logdet(const AbstractTensor<Derived,DIMS> &_src) {
|
||||
return std::log(std::abs(determinant<DetType>(_src.self())));
|
||||
}
|
||||
|
||||
|
||||
} // end of namespace Fastor
|
||||
|
||||
#endif // UNARY_DET_OP_H
|
||||
731
noarch/include/Fastor/expressions/linalg_ops/unary_inv_op.h
Normal file
731
noarch/include/Fastor/expressions/linalg_ops/unary_inv_op.h
Normal file
@@ -0,0 +1,731 @@
|
||||
#ifndef UNARY_INV_OP_H
|
||||
#define UNARY_INV_OP_H
|
||||
|
||||
#include "Fastor/meta/meta.h"
|
||||
#include "Fastor/backend/inverse.h"
|
||||
#include "Fastor/backend/lut_inverse.h"
|
||||
#include "Fastor/simd_vector/SIMDVector.h"
|
||||
#include "Fastor/tensor/AbstractTensor.h"
|
||||
#include "Fastor/tensor/Aliasing.h"
|
||||
#include "Fastor/tensor/Tensor.h"
|
||||
#include "Fastor/tensor/Ranges.h"
|
||||
#include "Fastor/tensor/TensorTraits.h"
|
||||
#include "Fastor/expressions/expression_traits.h"
|
||||
#include "Fastor/expressions/linalg_ops/linalg_computation_types.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_piv_op.h"
|
||||
|
||||
|
||||
namespace Fastor {
|
||||
|
||||
template<typename Expr, size_t DIM0>
|
||||
struct UnaryInvOp: public AbstractTensor<UnaryInvOp<Expr, DIM0>,DIM0> {
|
||||
using expr_type = expression_t<Expr>;
|
||||
using result_type = typename Expr::result_type;
|
||||
static constexpr FASTOR_INDEX M = get_tensor_dimension_v<0,result_type>;
|
||||
static constexpr FASTOR_INDEX N = get_tensor_dimension_v<1,result_type>;
|
||||
static constexpr FASTOR_INDEX Dimension = DIM0;
|
||||
static constexpr FASTOR_INDEX rank() {return DIM0;}
|
||||
using scalar_type = typename scalar_type_finder<UnaryInvOp<Expr, DIM0>>::type;
|
||||
using simd_vector_type = typename Expr::simd_vector_type;
|
||||
using simd_abi_type = typename simd_vector_type::abi_type;
|
||||
|
||||
FASTOR_INLINE UnaryInvOp(expr_type inexpr) : _expr(inexpr) {
|
||||
static_assert(M==N, "MATRIX MUST BE SQUARE");
|
||||
}
|
||||
|
||||
constexpr FASTOR_INLINE FASTOR_INDEX size() const {return M*N;}
|
||||
constexpr FASTOR_INLINE FASTOR_INDEX dimension(FASTOR_INDEX ) const {return M;}
|
||||
|
||||
constexpr FASTOR_INLINE expr_type expr() const {return _expr;}
|
||||
|
||||
private:
|
||||
expr_type _expr;
|
||||
};
|
||||
|
||||
template<typename Expr, size_t DIM0>
|
||||
FASTOR_INLINE UnaryInvOp<Expr, DIM0>
|
||||
inv(const AbstractTensor<Expr,DIM0> &src) {
|
||||
return UnaryInvOp<Expr, DIM0>(src.self());
|
||||
}
|
||||
|
||||
|
||||
// Upper triangular block matrix inversion
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
namespace internal {
|
||||
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,0> && is_less_equal_v_<M,4>,bool> = false>
|
||||
FASTOR_INLINE void ut_inverse_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
_inverse<T,M>(in.data(),out.data());
|
||||
}
|
||||
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,4> && is_less_equal_v_<M,8>,bool> = false>
|
||||
FASTOR_INLINE void ut_inverse_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
|
||||
constexpr size_t N = 4UL; // start size
|
||||
Tensor<T,N ,N > a = in(fseq<0,N>(),fseq<0,N>());
|
||||
Tensor<T,N ,M-N> b = in(fseq<0,N>(),fseq<N,M>());
|
||||
// Tensor<T,M-N, N> c = in(fseq<N,M>(),fseq<0,N>());
|
||||
Tensor<T,M-N,M-N> d = in(fseq<N,M>(),fseq<N,M>());
|
||||
|
||||
Tensor<T,N,N> inv_a;
|
||||
ut_inverse_dispatcher(a, inv_a);
|
||||
|
||||
Tensor<T,M-N,M-N> inv_d;
|
||||
ut_inverse_dispatcher(d, inv_d);
|
||||
|
||||
Tensor<T,N,M-N> b_invd = matmul(b, inv_d);
|
||||
|
||||
out(fseq<0,N>(),fseq<0,N>()) = inv_a;
|
||||
out(fseq<0,N>(),fseq<N,M>()) = -matmul(inv_a, b_invd);
|
||||
out(fseq<N,M>(),fseq<0,N>()) = 0;
|
||||
out(fseq<N,M>(),fseq<N,M>()) = inv_d;
|
||||
}
|
||||
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,8> && is_less_equal_v_<M,16>,bool> = false>
|
||||
FASTOR_INLINE void ut_inverse_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
|
||||
constexpr size_t N = 8UL; // start size
|
||||
Tensor<T,N ,N > a = in(fseq<0,N>(),fseq<0,N>());
|
||||
Tensor<T,N ,M-N> b = in(fseq<0,N>(),fseq<N,M>());
|
||||
// Tensor<T,M-N, N> c = in(fseq<N,M>(),fseq<0,N>());
|
||||
Tensor<T,M-N,M-N> d = in(fseq<N,M>(),fseq<N,M>());
|
||||
|
||||
Tensor<T,N,N> inv_a;
|
||||
ut_inverse_dispatcher(a, inv_a);
|
||||
|
||||
Tensor<T,M-N,M-N> inv_d;
|
||||
ut_inverse_dispatcher(d, inv_d);
|
||||
|
||||
Tensor<T,N,M-N> b_invd = matmul(b, inv_d);
|
||||
|
||||
out(fseq<0,N>(),fseq<0,N>()) = inv_a;
|
||||
out(fseq<0,N>(),fseq<N,M>()) = -matmul(inv_a, b_invd);
|
||||
out(fseq<N,M>(),fseq<0,N>()) = 0;
|
||||
out(fseq<N,M>(),fseq<N,M>()) = inv_d;
|
||||
}
|
||||
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,16> && is_less_equal_v_<M,32>,bool> = false>
|
||||
FASTOR_INLINE void ut_inverse_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
|
||||
// constexpr size_t N = 16UL; // start size
|
||||
constexpr size_t N = (M / 8UL * 8UL) / 2UL; // start size
|
||||
Tensor<T,N ,N > a = in(fseq<0,N>(),fseq<0,N>());
|
||||
Tensor<T,N ,M-N> b = in(fseq<0,N>(),fseq<N,M>());
|
||||
// Tensor<T,M-N, N> c = in(fseq<N,M>(),fseq<0,N>());
|
||||
Tensor<T,M-N,M-N> d = in(fseq<N,M>(),fseq<N,M>());
|
||||
|
||||
Tensor<T,N,N> inv_a;
|
||||
ut_inverse_dispatcher(a, inv_a);
|
||||
|
||||
Tensor<T,M-N,M-N> inv_d;
|
||||
ut_inverse_dispatcher(d, inv_d);
|
||||
|
||||
Tensor<T,N,M-N> b_invd = matmul(b, inv_d);
|
||||
|
||||
out(fseq<0,N>(),fseq<0,N>()) = inv_a;
|
||||
out(fseq<0,N>(),fseq<N,M>()) = -matmul(inv_a, b_invd);
|
||||
out(fseq<N,M>(),fseq<0,N>()) = 0;
|
||||
out(fseq<N,M>(),fseq<N,M>()) = inv_d;
|
||||
}
|
||||
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,32> && is_less_equal_v_<M,64>,bool> = false>
|
||||
FASTOR_INLINE void ut_inverse_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
|
||||
// constexpr size_t N = 32UL; // start size
|
||||
constexpr size_t N = (M / 16UL * 16UL) / 2UL; // start size
|
||||
Tensor<T,N ,N > a = in(fseq<0,N>(),fseq<0,N>());
|
||||
Tensor<T,N ,M-N> b = in(fseq<0,N>(),fseq<N,M>());
|
||||
// Tensor<T,M-N, N> c = in(fseq<N,M>(),fseq<0,N>());
|
||||
Tensor<T,M-N,M-N> d = in(fseq<N,M>(),fseq<N,M>());
|
||||
|
||||
Tensor<T,N,N> inv_a;
|
||||
ut_inverse_dispatcher(a, inv_a);
|
||||
|
||||
Tensor<T,M-N,M-N> inv_d;
|
||||
ut_inverse_dispatcher(d, inv_d);
|
||||
|
||||
Tensor<T,N,M-N> b_invd = tmatmul<UpLoType::General,UpLoType::Upper>(b, inv_d);
|
||||
|
||||
out(fseq<0,N>(),fseq<0,N>()) = inv_a;
|
||||
out(fseq<0,N>(),fseq<N,M>()) = -tmatmul<UpLoType::Upper,UpLoType::General>(inv_a, b_invd);
|
||||
out(fseq<N,M>(),fseq<0,N>()) = 0;
|
||||
out(fseq<N,M>(),fseq<N,M>()) = inv_d;
|
||||
}
|
||||
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,64> && is_less_equal_v_<M,128>,bool> = false>
|
||||
FASTOR_INLINE void ut_inverse_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
|
||||
// constexpr size_t N = 64UL; // start size
|
||||
constexpr size_t N = (M / 32UL * 32UL) / 2UL; // start size
|
||||
Tensor<T,N ,N > a = in(fseq<0,N>(),fseq<0,N>());
|
||||
Tensor<T,N ,M-N> b = in(fseq<0,N>(),fseq<N,M>());
|
||||
// Tensor<T,M-N, N> c = in(fseq<N,M>(),fseq<0,N>());
|
||||
Tensor<T,M-N,M-N> d = in(fseq<N,M>(),fseq<N,M>());
|
||||
|
||||
Tensor<T,N,N> inv_a;
|
||||
ut_inverse_dispatcher(a, inv_a);
|
||||
|
||||
Tensor<T,M-N,M-N> inv_d;
|
||||
ut_inverse_dispatcher(d, inv_d);
|
||||
|
||||
Tensor<T,N,M-N> b_invd = tmatmul<UpLoType::General,UpLoType::Upper>(b, inv_d);
|
||||
|
||||
out(fseq<0,N>(),fseq<0,N>()) = inv_a;
|
||||
out(fseq<0,N>(),fseq<N,M>()) = -tmatmul<UpLoType::Upper,UpLoType::General>(inv_a, b_invd);
|
||||
out(fseq<N,M>(),fseq<0,N>()) = 0;
|
||||
out(fseq<N,M>(),fseq<N,M>()) = inv_d;
|
||||
}
|
||||
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,128> && is_less_equal_v_<M,256>,bool> = false>
|
||||
FASTOR_INLINE void ut_inverse_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
|
||||
// constexpr size_t N = 128UL; // start size
|
||||
constexpr size_t N = (M / 64UL * 64UL) / 2UL; // start size
|
||||
Tensor<T,N ,N > a = in(fseq<0,N>(),fseq<0,N>());
|
||||
Tensor<T,N ,M-N> b = in(fseq<0,N>(),fseq<N,M>());
|
||||
// Tensor<T,M-N, N> c = in(fseq<N,M>(),fseq<0,N>());
|
||||
Tensor<T,M-N,M-N> d = in(fseq<N,M>(),fseq<N,M>());
|
||||
|
||||
Tensor<T,N,N> inv_a;
|
||||
ut_inverse_dispatcher(a, inv_a);
|
||||
|
||||
Tensor<T,M-N,M-N> inv_d;
|
||||
ut_inverse_dispatcher(d, inv_d);
|
||||
|
||||
Tensor<T,N,M-N> b_invd = tmatmul<UpLoType::General,UpLoType::Upper>(b, inv_d);
|
||||
|
||||
out(fseq<0,N>(),fseq<0,N>()) = inv_a;
|
||||
out(fseq<0,N>(),fseq<N,M>()) = -tmatmul<UpLoType::Upper,UpLoType::General>(inv_a, b_invd);
|
||||
out(fseq<N,M>(),fseq<0,N>()) = 0;
|
||||
out(fseq<N,M>(),fseq<N,M>()) = inv_d;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
} // internal
|
||||
|
||||
|
||||
|
||||
|
||||
// Lower uni-triangular block matrix inversion
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
namespace internal {
|
||||
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,0> && is_less_equal_v_<M,4>,bool> = false>
|
||||
FASTOR_INLINE void lut_inverse_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
// This restricts the recursive block to lower uni-triangualar matrices
|
||||
_lowunitri_inverse<T,M>(in.data(),out.data());
|
||||
}
|
||||
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,4> && is_less_equal_v_<M,8>,bool> = false>
|
||||
FASTOR_INLINE void lut_inverse_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
|
||||
constexpr size_t N = 4UL; // start size
|
||||
Tensor<T,N ,N > a = in(fseq<0,N>(),fseq<0,N>());
|
||||
// Tensor<T,N ,M-N> b = in(fseq<0,N>(),fseq<N,M>());
|
||||
Tensor<T,M-N, N> c = in(fseq<N,M>(),fseq<0,N>());
|
||||
Tensor<T,M-N,M-N> d = in(fseq<N,M>(),fseq<N,M>());
|
||||
|
||||
Tensor<T,N,N> inv_a;
|
||||
lut_inverse_dispatcher(a, inv_a);
|
||||
|
||||
Tensor<T,M-N,N> c_inva = matmul(c, inv_a);
|
||||
|
||||
Tensor<T,M-N,M-N> inv_d;
|
||||
lut_inverse_dispatcher(d, inv_d);
|
||||
|
||||
out(fseq<0,N>(),fseq<0,N>()) = inv_a;
|
||||
out(fseq<0,N>(),fseq<N,M>()) = 0;
|
||||
out(fseq<N,M>(),fseq<0,N>()) = -matmul(inv_d, c_inva);
|
||||
out(fseq<N,M>(),fseq<N,M>()) = inv_d;
|
||||
}
|
||||
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,8> && is_less_equal_v_<M,16>,bool> = false>
|
||||
FASTOR_INLINE void lut_inverse_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
|
||||
constexpr size_t N = 8UL; // start size
|
||||
Tensor<T,N ,N > a = in(fseq<0,N>(),fseq<0,N>());
|
||||
// Tensor<T,N ,M-N> b = in(fseq<0,N>(),fseq<N,M>());
|
||||
Tensor<T,M-N, N> c = in(fseq<N,M>(),fseq<0,N>());
|
||||
Tensor<T,M-N,M-N> d = in(fseq<N,M>(),fseq<N,M>());
|
||||
|
||||
Tensor<T,N,N> inv_a;
|
||||
lut_inverse_dispatcher(a, inv_a);
|
||||
|
||||
Tensor<T,M-N,N> c_inva = matmul(c, inv_a);
|
||||
|
||||
Tensor<T,M-N,M-N> inv_d;
|
||||
lut_inverse_dispatcher(d, inv_d);
|
||||
|
||||
out(fseq<0,N>(),fseq<0,N>()) = inv_a;
|
||||
out(fseq<0,N>(),fseq<N,M>()) = 0;
|
||||
out(fseq<N,M>(),fseq<0,N>()) = -matmul(inv_d, c_inva);
|
||||
out(fseq<N,M>(),fseq<N,M>()) = inv_d;
|
||||
}
|
||||
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,16> && is_less_equal_v_<M,32>,bool> = false>
|
||||
FASTOR_INLINE void lut_inverse_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
|
||||
// constexpr size_t N = 16UL; // start size
|
||||
constexpr size_t N = (M / 8UL * 8UL) / 2UL; // start size
|
||||
Tensor<T,N ,N > a = in(fseq<0,N>(),fseq<0,N>());
|
||||
// Tensor<T,N ,M-N> b = in(fseq<0,N>(),fseq<N,M>());
|
||||
Tensor<T,M-N, N> c = in(fseq<N,M>(),fseq<0,N>());
|
||||
Tensor<T,M-N,M-N> d = in(fseq<N,M>(),fseq<N,M>());
|
||||
|
||||
Tensor<T,N,N> inv_a;
|
||||
lut_inverse_dispatcher(a, inv_a);
|
||||
|
||||
Tensor<T,M-N,N> c_inva = matmul(c, inv_a);
|
||||
|
||||
Tensor<T,M-N,M-N> inv_d;
|
||||
lut_inverse_dispatcher(d, inv_d);
|
||||
|
||||
out(fseq<0,N>(),fseq<0,N>()) = inv_a;
|
||||
out(fseq<0,N>(),fseq<N,M>()) = 0;
|
||||
out(fseq<N,M>(),fseq<0,N>()) = -matmul(inv_d, c_inva);
|
||||
out(fseq<N,M>(),fseq<N,M>()) = inv_d;
|
||||
}
|
||||
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,32> && is_less_equal_v_<M,64>,bool> = false>
|
||||
FASTOR_INLINE void lut_inverse_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
|
||||
// constexpr size_t N = 32UL; // start size
|
||||
constexpr size_t N = (M / 16UL * 16UL) / 2UL; // start size
|
||||
Tensor<T,N ,N > a = in(fseq<0,N>(),fseq<0,N>());
|
||||
// Tensor<T,N ,M-N> b = in(fseq<0,N>(),fseq<N,M>());
|
||||
Tensor<T,M-N, N> c = in(fseq<N,M>(),fseq<0,N>());
|
||||
Tensor<T,M-N,M-N> d = in(fseq<N,M>(),fseq<N,M>());
|
||||
|
||||
Tensor<T,N,N> inv_a;
|
||||
lut_inverse_dispatcher(a, inv_a);
|
||||
|
||||
Tensor<T,M-N,N> c_inva = tmatmul<UpLoType::General,UpLoType::Lower>(c, inv_a);
|
||||
|
||||
Tensor<T,M-N,M-N> inv_d;
|
||||
lut_inverse_dispatcher(d, inv_d);
|
||||
|
||||
out(fseq<0,N>(),fseq<0,N>()) = inv_a;
|
||||
out(fseq<0,N>(),fseq<N,M>()) = 0;
|
||||
out(fseq<N,M>(),fseq<0,N>()) = -tmatmul<UpLoType::Lower,UpLoType::General>(inv_d, c_inva);
|
||||
out(fseq<N,M>(),fseq<N,M>()) = inv_d;
|
||||
}
|
||||
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,64> && is_less_equal_v_<M,128>,bool> = false>
|
||||
FASTOR_INLINE void lut_inverse_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
|
||||
// constexpr size_t N = 64UL; // start size
|
||||
constexpr size_t N = (M / 32UL * 32UL) / 2UL; // start size
|
||||
Tensor<T,N ,N > a = in(fseq<0,N>(),fseq<0,N>());
|
||||
// Tensor<T,N ,M-N> b = in(fseq<0,N>(),fseq<N,M>());
|
||||
Tensor<T,M-N, N> c = in(fseq<N,M>(),fseq<0,N>());
|
||||
Tensor<T,M-N,M-N> d = in(fseq<N,M>(),fseq<N,M>());
|
||||
|
||||
Tensor<T,N,N> inv_a;
|
||||
lut_inverse_dispatcher(a, inv_a);
|
||||
|
||||
Tensor<T,M-N,N> c_inva = tmatmul<UpLoType::General,UpLoType::Lower>(c, inv_a);
|
||||
|
||||
Tensor<T,M-N,M-N> inv_d;
|
||||
lut_inverse_dispatcher(d, inv_d);
|
||||
|
||||
out(fseq<0,N>(),fseq<0,N>()) = inv_a;
|
||||
out(fseq<0,N>(),fseq<N,M>()) = 0;
|
||||
out(fseq<N,M>(),fseq<0,N>()) = -tmatmul<UpLoType::Lower,UpLoType::General>(inv_d, c_inva);
|
||||
out(fseq<N,M>(),fseq<N,M>()) = inv_d;
|
||||
}
|
||||
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,128> && is_less_equal_v_<M,256>,bool> = false>
|
||||
FASTOR_INLINE void lut_inverse_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
|
||||
// constexpr size_t N = 128UL; // start size
|
||||
constexpr size_t N = (M / 64UL * 64UL) / 2UL; // start size
|
||||
Tensor<T,N ,N > a = in(fseq<0,N>(),fseq<0,N>());
|
||||
// Tensor<T,N ,M-N> b = in(fseq<0,N>(),fseq<N,M>());
|
||||
Tensor<T,M-N, N> c = in(fseq<N,M>(),fseq<0,N>());
|
||||
Tensor<T,M-N,M-N> d = in(fseq<N,M>(),fseq<N,M>());
|
||||
|
||||
Tensor<T,N,N> inv_a;
|
||||
lut_inverse_dispatcher(a, inv_a);
|
||||
|
||||
Tensor<T,M-N,N> c_inva = tmatmul<UpLoType::General,UpLoType::Lower>(c, inv_a);
|
||||
|
||||
Tensor<T,M-N,M-N> inv_d;
|
||||
lut_inverse_dispatcher(d, inv_d);
|
||||
|
||||
out(fseq<0,N>(),fseq<0,N>()) = inv_a;
|
||||
out(fseq<0,N>(),fseq<N,M>()) = 0;
|
||||
out(fseq<N,M>(),fseq<0,N>()) = -tmatmul<UpLoType::Lower,UpLoType::General>(inv_d, c_inva);
|
||||
out(fseq<N,M>(),fseq<N,M>()) = inv_d;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
} // internal
|
||||
|
||||
|
||||
|
||||
|
||||
// General recursive block matrix inversion
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
namespace internal {
|
||||
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,0> && is_less_equal_v_<M,4>,bool> = false>
|
||||
FASTOR_INLINE void inverse_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
_inverse<T,M>(in.data(),out.data());
|
||||
}
|
||||
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,4> && is_less_equal_v_<M,8>,bool> = false>
|
||||
FASTOR_INLINE void inverse_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
|
||||
constexpr size_t N = 4UL; // start size
|
||||
Tensor<T,N ,N > a = in(fseq<0,N>(),fseq<0,N>());
|
||||
Tensor<T,N ,M-N> b = in(fseq<0,N>(),fseq<N,M>());
|
||||
Tensor<T,M-N, N> c = in(fseq<N,M>(),fseq<0,N>());
|
||||
Tensor<T,M-N,M-N> d = in(fseq<N,M>(),fseq<N,M>());
|
||||
|
||||
Tensor<T,N,N> inv_a = inverse(a);
|
||||
|
||||
Tensor<T,M-N,N> c_inva = matmul(c, inv_a);
|
||||
|
||||
Tensor<T,M-N,M-N> block_bb = inverse(static_cast<Tensor<T,M-N,M-N>>(d - matmul(c_inva, b)));
|
||||
|
||||
Tensor<T,N,M-N> inva_b = matmul(inv_a, b);
|
||||
Tensor<T,M-N,N> bb_c_inva = matmul(block_bb, c_inva);
|
||||
|
||||
Tensor<T,N ,N > block_aa = inv_a + matmul(inva_b, bb_c_inva);
|
||||
Tensor<T,N ,M-N> block_ab = -matmul(inva_b, block_bb);
|
||||
Tensor<T,M-N,N > block_ba = -bb_c_inva;
|
||||
|
||||
out(fseq<0,N>(),fseq<0,N>()) = block_aa;
|
||||
out(fseq<0,N>(),fseq<N,M>()) = block_ab;
|
||||
out(fseq<N,M>(),fseq<0,N>()) = block_ba;
|
||||
out(fseq<N,M>(),fseq<N,M>()) = block_bb;
|
||||
}
|
||||
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,8> && is_less_equal_v_<M,16>,bool> = false>
|
||||
FASTOR_INLINE void inverse_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
|
||||
constexpr size_t N = 8UL; // start size
|
||||
Tensor<T,N ,N > a = in(fseq<0,N>(),fseq<0,N>());
|
||||
Tensor<T,N ,M-N> b = in(fseq<0,N>(),fseq<N,M>());
|
||||
Tensor<T,M-N, N> c = in(fseq<N,M>(),fseq<0,N>());
|
||||
Tensor<T,M-N,M-N> d = in(fseq<N,M>(),fseq<N,M>());
|
||||
|
||||
Tensor<T,N,N> inv_a;
|
||||
inverse_dispatcher(a, inv_a);
|
||||
|
||||
Tensor<T,M-N,N> c_inva = matmul(c, inv_a);
|
||||
|
||||
Tensor<T,M-N,M-N> block_bb;
|
||||
inverse_dispatcher(static_cast<Tensor<T,M-N,M-N>>(d - matmul(c_inva, b)),block_bb);
|
||||
|
||||
Tensor<T,N,M-N> inva_b = matmul(inv_a, b);
|
||||
Tensor<T,M-N,N> bb_c_inva = matmul(block_bb, c_inva);
|
||||
|
||||
Tensor<T,N ,N > block_aa = inv_a + matmul(inva_b, bb_c_inva);
|
||||
Tensor<T,N ,M-N> block_ab = -matmul(inva_b, block_bb);
|
||||
Tensor<T,M-N,N > block_ba = -bb_c_inva;
|
||||
|
||||
out(fseq<0,N>(),fseq<0,N>()) = block_aa;
|
||||
out(fseq<0,N>(),fseq<N,M>()) = block_ab;
|
||||
out(fseq<N,M>(),fseq<0,N>()) = block_ba;
|
||||
out(fseq<N,M>(),fseq<N,M>()) = block_bb;
|
||||
}
|
||||
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,16> && is_less_equal_v_<M,32>,bool> = false>
|
||||
FASTOR_INLINE void inverse_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
|
||||
// constexpr size_t N = 16UL; // start size
|
||||
constexpr size_t N = (M / 8UL * 8UL) / 2UL; // start size
|
||||
Tensor<T,N ,N > a = in(fseq<0,N>(),fseq<0,N>());
|
||||
Tensor<T,N ,M-N> b = in(fseq<0,N>(),fseq<N,M>());
|
||||
Tensor<T,M-N, N> c = in(fseq<N,M>(),fseq<0,N>());
|
||||
Tensor<T,M-N,M-N> d = in(fseq<N,M>(),fseq<N,M>());
|
||||
|
||||
Tensor<T,N,N> inv_a;
|
||||
inverse_dispatcher(a, inv_a);
|
||||
|
||||
Tensor<T,M-N,N> c_inva = matmul(c, inv_a);
|
||||
|
||||
Tensor<T,M-N,M-N> block_bb;
|
||||
inverse_dispatcher(static_cast<Tensor<T,M-N,M-N>>(d - matmul(c_inva, b)),block_bb);
|
||||
|
||||
Tensor<T,N,M-N> inva_b = matmul(inv_a, b);
|
||||
Tensor<T,M-N,N> bb_c_inva = matmul(block_bb, c_inva);
|
||||
|
||||
Tensor<T,N ,N > block_aa = inv_a + matmul(inva_b, bb_c_inva);
|
||||
Tensor<T,N ,M-N> block_ab = -matmul(inva_b, block_bb);
|
||||
Tensor<T,M-N,N > block_ba = -bb_c_inva;
|
||||
|
||||
out(fseq<0,N>(),fseq<0,N>()) = block_aa;
|
||||
out(fseq<0,N>(),fseq<N,M>()) = block_ab;
|
||||
out(fseq<N,M>(),fseq<0,N>()) = block_ba;
|
||||
out(fseq<N,M>(),fseq<N,M>()) = block_bb;
|
||||
}
|
||||
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,32> && is_less_equal_v_<M,64>,bool> = false>
|
||||
FASTOR_INLINE void inverse_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
|
||||
// constexpr size_t N = 32UL; // start size
|
||||
constexpr size_t N = (M / 16UL * 16UL) / 2UL; // start size
|
||||
Tensor<T,N ,N > a = in(fseq<0,N>(),fseq<0,N>());
|
||||
Tensor<T,N ,M-N> b = in(fseq<0,N>(),fseq<N,M>());
|
||||
Tensor<T,M-N, N> c = in(fseq<N,M>(),fseq<0,N>());
|
||||
Tensor<T,M-N,M-N> d = in(fseq<N,M>(),fseq<N,M>());
|
||||
|
||||
Tensor<T,N,N> inv_a;
|
||||
inverse_dispatcher(a, inv_a);
|
||||
|
||||
Tensor<T,M-N,N> c_inva = matmul(c, inv_a);
|
||||
|
||||
Tensor<T,M-N,M-N> block_bb;
|
||||
inverse_dispatcher(static_cast<Tensor<T,M-N,M-N>>(d - matmul(c_inva, b)),block_bb);
|
||||
|
||||
Tensor<T,N,M-N> inva_b = matmul(inv_a, b);
|
||||
Tensor<T,M-N,N> bb_c_inva = matmul(block_bb, c_inva);
|
||||
|
||||
Tensor<T,N ,N > block_aa = inv_a + matmul(inva_b, bb_c_inva);
|
||||
Tensor<T,N ,M-N> block_ab = -matmul(inva_b, block_bb);
|
||||
Tensor<T,M-N,N > block_ba = -bb_c_inva;
|
||||
|
||||
out(fseq<0,N>(),fseq<0,N>()) = block_aa;
|
||||
out(fseq<0,N>(),fseq<N,M>()) = block_ab;
|
||||
out(fseq<N,M>(),fseq<0,N>()) = block_ba;
|
||||
out(fseq<N,M>(),fseq<N,M>()) = block_bb;
|
||||
}
|
||||
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,64> && is_less_equal_v_<M,128>,bool> = false>
|
||||
FASTOR_INLINE void inverse_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
|
||||
// constexpr size_t N = 64UL; // start size
|
||||
constexpr size_t N = (M / 32UL * 32UL) / 2UL; // start size
|
||||
Tensor<T,N ,N > a = in(fseq<0,N>(),fseq<0,N>());
|
||||
Tensor<T,N ,M-N> b = in(fseq<0,N>(),fseq<N,M>());
|
||||
Tensor<T,M-N, N> c = in(fseq<N,M>(),fseq<0,N>());
|
||||
Tensor<T,M-N,M-N> d = in(fseq<N,M>(),fseq<N,M>());
|
||||
|
||||
Tensor<T,N,N> inv_a;
|
||||
inverse_dispatcher(a, inv_a);
|
||||
|
||||
Tensor<T,M-N,N> c_inva = matmul(c, inv_a);
|
||||
|
||||
Tensor<T,M-N,M-N> block_bb;
|
||||
inverse_dispatcher(static_cast<Tensor<T,M-N,M-N>>(d - matmul(c_inva, b)),block_bb);
|
||||
|
||||
Tensor<T,N,M-N> inva_b = matmul(inv_a, b);
|
||||
Tensor<T,M-N,N> bb_c_inva = matmul(block_bb, c_inva);
|
||||
|
||||
Tensor<T,N ,N > block_aa = inv_a + matmul(inva_b, bb_c_inva);
|
||||
Tensor<T,N ,M-N> block_ab = -matmul(inva_b, block_bb);
|
||||
Tensor<T,M-N,N > block_ba = -bb_c_inva;
|
||||
|
||||
out(fseq<0,N>(),fseq<0,N>()) = block_aa;
|
||||
out(fseq<0,N>(),fseq<N,M>()) = block_ab;
|
||||
out(fseq<N,M>(),fseq<0,N>()) = block_ba;
|
||||
out(fseq<N,M>(),fseq<N,M>()) = block_bb;
|
||||
}
|
||||
|
||||
template<typename T, size_t M, enable_if_t_<is_greater_v_<M,128> && is_less_equal_v_<M,256>,bool> = false>
|
||||
FASTOR_INLINE void inverse_dispatcher(const Tensor<T,M,M> &in, Tensor<T,M,M>& out) {
|
||||
|
||||
// constexpr size_t N = 128UL; // start size
|
||||
constexpr size_t N = (M / 64UL * 64UL) / 2UL; // start size
|
||||
Tensor<T,N ,N > a = in(fseq<0,N>(),fseq<0,N>());
|
||||
Tensor<T,N ,M-N> b = in(fseq<0,N>(),fseq<N,M>());
|
||||
Tensor<T,M-N, N> c = in(fseq<N,M>(),fseq<0,N>());
|
||||
Tensor<T,M-N,M-N> d = in(fseq<N,M>(),fseq<N,M>());
|
||||
|
||||
Tensor<T,N,N> inv_a;
|
||||
inverse_dispatcher(a, inv_a);
|
||||
|
||||
Tensor<T,M-N,N> c_inva = matmul(c, inv_a);
|
||||
|
||||
Tensor<T,M-N,M-N> block_bb;
|
||||
inverse_dispatcher(static_cast<Tensor<T,M-N,M-N>>(d - matmul(c_inva, b)),block_bb);
|
||||
|
||||
Tensor<T,N,M-N> inva_b = matmul(inv_a, b);
|
||||
Tensor<T,M-N,N> bb_c_inva = matmul(block_bb, c_inva);
|
||||
|
||||
Tensor<T,N ,N > block_aa = inv_a + matmul(inva_b, bb_c_inva);
|
||||
Tensor<T,N ,M-N> block_ab = -matmul(inva_b, block_bb);
|
||||
Tensor<T,M-N,N > block_ba = -bb_c_inva;
|
||||
|
||||
out(fseq<0,N>(),fseq<0,N>()) = block_aa;
|
||||
out(fseq<0,N>(),fseq<N,M>()) = block_ab;
|
||||
out(fseq<N,M>(),fseq<0,N>()) = block_ba;
|
||||
out(fseq<N,M>(),fseq<N,M>()) = block_bb;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
} // internal
|
||||
|
||||
|
||||
|
||||
// Inversion using LU decomposition is in the LU module [unary_lu_op]
|
||||
// For tensors
|
||||
// SimpleInv - no pivot
|
||||
template<InvCompType InvType = InvCompType::SimpleInv,
|
||||
typename T, size_t M, enable_if_t_<is_less_equal_v_<M,4UL> && InvType == InvCompType::SimpleInv,bool> = false>
|
||||
FASTOR_INLINE Tensor<T,M,M> inverse(const Tensor<T,M,M> &a) {
|
||||
Tensor<T,M,M> out;
|
||||
_inverse<T,M>(a.data(),out.data());
|
||||
return out;
|
||||
}
|
||||
template<InvCompType InvType = InvCompType::SimpleInv,
|
||||
typename T, size_t M, enable_if_t_<is_greater_v_<M,4UL> && InvType == InvCompType::SimpleInv,bool> = false>
|
||||
FASTOR_INLINE Tensor<T,M,M> inverse(const Tensor<T,M,M> &in) {
|
||||
Tensor<T,M,M> out;
|
||||
internal::inverse_dispatcher(in,out);
|
||||
return out;
|
||||
}
|
||||
// SimpleInv pivot
|
||||
template<InvCompType InvType = InvCompType::SimpleInv,
|
||||
typename T, size_t M, enable_if_t_<InvType == InvCompType::SimpleInvPiv,bool> = false>
|
||||
FASTOR_INLINE Tensor<T,M,M> inverse(const Tensor<T,M,M> &in) {
|
||||
Tensor<T,M,M> out;
|
||||
// We need to post multiply - swap columns using row permutation vector
|
||||
Tensor<size_t,M> P;
|
||||
pivot_inplace(in,P);
|
||||
auto A(apply_pivot(in,P));
|
||||
internal::inverse_dispatcher(A,out);
|
||||
return reconstruct_colwise(out,P);
|
||||
|
||||
// // matrix version
|
||||
// Tensor<T,M,M> P;
|
||||
// pivot_inplace(in,P);
|
||||
// auto A(apply_pivot(in,P));
|
||||
// internal::inverse_dispatcher(A,out);
|
||||
// return matmul(out,P);
|
||||
}
|
||||
|
||||
// For high order tensors
|
||||
template<InvCompType InvType = InvCompType::SimpleInv,
|
||||
typename T, size_t ... Rest, enable_if_t_<sizeof...(Rest)>=3 && InvType == InvCompType::SimpleInv,bool> = false >
|
||||
FASTOR_INLINE Tensor<T,Rest...>
|
||||
inverse(const Tensor<T,Rest...> &a) {
|
||||
|
||||
constexpr size_t remaining_product = last_matrix_extracter<Tensor<T,Rest...>,
|
||||
typename std_ext::make_index_sequence<sizeof...(Rest)-2>::type>::remaining_product;
|
||||
|
||||
constexpr size_t I = get_value<sizeof...(Rest)-1,Rest...>::value;
|
||||
constexpr size_t J = get_value<sizeof...(Rest),Rest...>::value;
|
||||
static_assert(I==J,"THE LAST TWO DIMENSIONS OF TENSOR MUST BE THE SAME");
|
||||
|
||||
Tensor<T,Rest...> out;
|
||||
T *a_data = a.data();
|
||||
T *out_data = out.data();
|
||||
|
||||
for (size_t i=0; i<remaining_product; ++i) {
|
||||
T det = _det<T,J,J>(static_cast<const T *>(a_data+i*J*J));
|
||||
_inverse<T,J>(a_data+i*J*J,out_data+i*J*J);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// Inverse for generic expressions is provided here
|
||||
template<InvCompType InvType = InvCompType::SimpleInv,
|
||||
typename Derived, size_t DIM>
|
||||
FASTOR_INLINE
|
||||
typename Derived::result_type
|
||||
inverse(const AbstractTensor<Derived,DIM> &src) {
|
||||
// If we are here Derived is already an expression
|
||||
using result_type = typename Derived::result_type;
|
||||
const result_type tmp(src.self());
|
||||
return inverse<InvType>(tmp);
|
||||
}
|
||||
|
||||
|
||||
// Inverse of lower uni-triangular matrices
|
||||
template<InvCompType InvType = InvCompType::SimpleInv, typename ULType = UpLoType::General,
|
||||
typename T, size_t M, enable_if_t_<InvType == InvCompType::SimpleInv && is_same_v_<ULType,UpLoType::UniLower>,bool> = false>
|
||||
FASTOR_INLINE Tensor<T,M,M> tinverse(const Tensor<T,M,M> &in) {
|
||||
Tensor<T,M,M> out;
|
||||
internal::lut_inverse_dispatcher(in,out);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Inverse of lower uni-triangular matrices
|
||||
template<InvCompType InvType = InvCompType::SimpleInv, typename ULType = UpLoType::General,
|
||||
typename T, size_t M, enable_if_t_<InvType == InvCompType::SimpleInv && is_same_v_<ULType,UpLoType::Upper>,bool> = false>
|
||||
FASTOR_INLINE Tensor<T,M,M> tinverse(const Tensor<T,M,M> &in) {
|
||||
Tensor<T,M,M> out;
|
||||
internal::ut_inverse_dispatcher(in,out);
|
||||
return out;
|
||||
}
|
||||
// Inverse for generic expressions of lower uni-triangular matrices is provided here
|
||||
template<InvCompType InvType = InvCompType::SimpleInv, typename ULType = UpLoType::General,
|
||||
typename Derived, size_t DIM>
|
||||
FASTOR_INLINE
|
||||
typename Derived::result_type
|
||||
tinverse(const AbstractTensor<Derived,DIM> &src) {
|
||||
// If we are here Derived is already an expression
|
||||
using result_type = typename Derived::result_type;
|
||||
const result_type tmp(src.self());
|
||||
return tinverse<InvType,ULType>(tmp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// assignments
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign(AbstractTensor<Derived,DIM> &dst, const UnaryInvOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
internal::inverse_dispatcher(tmp,dst.self());
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_add(AbstractTensor<Derived,DIM> &dst, const UnaryInvOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
// no copies if expr is a tensor
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
// one copy for the inverse
|
||||
result_type tmp_inv;
|
||||
internal::inverse_dispatcher(tmp,tmp_inv);
|
||||
trivial_assign_add(dst.self(),tmp_inv);
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_sub(AbstractTensor<Derived,DIM> &dst, const UnaryInvOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
// no copies if expr is a tensor
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
// one copy for the inverse
|
||||
result_type tmp_inv;
|
||||
internal::inverse_dispatcher(tmp,tmp_inv);
|
||||
trivial_assign_sub(dst.self(),tmp_inv);
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_mul(AbstractTensor<Derived,DIM> &dst, const UnaryInvOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
// no copies if expr is a tensor
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
// one copy for the inverse
|
||||
result_type tmp_inv;
|
||||
internal::inverse_dispatcher(tmp,tmp_inv);
|
||||
trivial_assign_mul(dst.self(),tmp_inv);
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_div(AbstractTensor<Derived,DIM> &dst, const UnaryInvOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
// no copies if expr is a tensor
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
// one copy for the inverse
|
||||
result_type tmp_inv;
|
||||
internal::inverse_dispatcher(tmp,tmp_inv);
|
||||
trivial_assign_div(dst.self(),tmp_inv);
|
||||
}
|
||||
|
||||
|
||||
} // end of namespace Fastor
|
||||
|
||||
|
||||
#endif // UNARY_INV_OP_H
|
||||
970
noarch/include/Fastor/expressions/linalg_ops/unary_lu_op.h
Normal file
970
noarch/include/Fastor/expressions/linalg_ops/unary_lu_op.h
Normal file
@@ -0,0 +1,970 @@
|
||||
#ifndef UNARY_LU_OP_H
|
||||
#define UNARY_LU_OP_H
|
||||
|
||||
#include "Fastor/meta/meta.h"
|
||||
#include "Fastor/backend/inner.h"
|
||||
#include "Fastor/backend/lufact.h"
|
||||
#include "Fastor/simd_vector/SIMDVector.h"
|
||||
#include "Fastor/tensor/AbstractTensor.h"
|
||||
#include "Fastor/tensor/Aliasing.h"
|
||||
#include "Fastor/tensor/Tensor.h"
|
||||
#include "Fastor/tensor/Ranges.h"
|
||||
#include "Fastor/tensor/TensorTraits.h"
|
||||
#include "Fastor/expressions/expression_traits.h"
|
||||
#include "Fastor/expressions/linalg_ops/linalg_computation_types.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_piv_op.h"
|
||||
|
||||
|
||||
namespace Fastor {
|
||||
|
||||
namespace internal {
|
||||
|
||||
/* Compile time recursive loop with inner for forward substitution of b/B given the lower unitriangular matrix L.
|
||||
The following meta functions implements L * y = b for single or multiple right sides
|
||||
*/
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
template <size_t from, size_t to>
|
||||
struct forward_subs_impl {
|
||||
|
||||
template<typename T, size_t M>
|
||||
static FASTOR_INLINE void do_single_rhs(const Tensor<T,M,M> &L, const Tensor<T,M> &b, Tensor<T,M> &y) {
|
||||
y(from) = b(from) - _inner<T,from>(&L.data()[from*M],y.data());
|
||||
forward_subs_impl<from+1,to>::do_single_rhs(L, b, y);
|
||||
}
|
||||
template<typename T, size_t M>
|
||||
static FASTOR_INLINE void do_single_rhs_pivot(const Tensor<T,M,M> &L, const Tensor<T,M> &b, const Tensor<size_t,M> &p, Tensor<T,M> &y) {
|
||||
y(from) = b(p(from)) - _inner<T,from>(&L.data()[from*M],y.data());
|
||||
forward_subs_impl<from+1,to>::do_single_rhs_pivot(L, b, p, y);
|
||||
}
|
||||
|
||||
template<typename T, size_t M, size_t N>
|
||||
static FASTOR_INLINE void do_multi_rhs(const size_t j, const Tensor<T,M,M> &L, const Tensor<T,M,N> &B, Tensor<T,M> &y, Tensor<T,M,N> &X) {
|
||||
y(from) = B(from,j) - _inner<T,from>(&L.data()[from*M],y.data());
|
||||
X(from,j) = y(from);
|
||||
forward_subs_impl<from+1,to>::do_multi_rhs(j, L, B, y, X);
|
||||
}
|
||||
|
||||
template<typename T, size_t M, size_t N>
|
||||
static FASTOR_INLINE void do_multi_rhs_pivot(const size_t j, const Tensor<T,M,M> &L, const Tensor<T,M,N> &B,
|
||||
const Tensor<size_t,M> &p, Tensor<T,M> &y, Tensor<T,M,N> &X) {
|
||||
y(from) = B(p(from),j) - _inner<T,from>(&L.data()[from*M],y.data());
|
||||
X(from,j) = y(from);
|
||||
forward_subs_impl<from+1,to>::do_multi_rhs_pivot(j, L, B, p, y, X);
|
||||
}
|
||||
};
|
||||
template <size_t from>
|
||||
struct forward_subs_impl<from,from> {
|
||||
|
||||
template<typename T, size_t M>
|
||||
static FASTOR_INLINE void do_single_rhs(const Tensor<T,M,M> &L, const Tensor<T,M> &b, Tensor<T,M> &y) {
|
||||
y(from) = b(from) - _inner<T,from>(&L.data()[from*M],y.data());
|
||||
}
|
||||
template<typename T, size_t M>
|
||||
static FASTOR_INLINE void do_single_rhs_pivot(const Tensor<T,M,M> &L, const Tensor<T,M> &b, const Tensor<size_t,M> &p, Tensor<T,M> &y) {
|
||||
y(from) = b(p(from)) - _inner<T,from>(&L.data()[from*M],y.data());
|
||||
}
|
||||
|
||||
template<typename T, size_t M, size_t N>
|
||||
static FASTOR_INLINE void do_multi_rhs(const size_t j, const Tensor<T,M,M> &L, const Tensor<T,M,N> &B, Tensor<T,M> &y, Tensor<T,M,N> &X) {
|
||||
y(from) = B(from,j) - _inner<T,from>(&L.data()[from*M],y.data());
|
||||
X(from,j) = y(from);
|
||||
}
|
||||
template<typename T, size_t M, size_t N>
|
||||
static FASTOR_INLINE void do_multi_rhs_pivot(const size_t j, const Tensor<T,M,M> &L, const Tensor<T,M,N> &B,
|
||||
const Tensor<size_t,M> &p, Tensor<T,M> &y, Tensor<T,M,N> &X) {
|
||||
y(from) = B(p(from),j) - _inner<T,from>(&L.data()[from*M],y.data());
|
||||
X(from,j) = y(from);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, size_t M>
|
||||
FASTOR_INLINE Tensor<T,M> forward_subs(const Tensor<T,M,M> &L, const Tensor<T,M> &b) {
|
||||
|
||||
Tensor<T,M> y(0);
|
||||
|
||||
forward_subs_impl<0,M-1>::do_single_rhs(L, b, y);
|
||||
#if 0
|
||||
// The run-time loop version
|
||||
// Solve for L * y = b
|
||||
for (size_t i=0; i< M; ++i) {
|
||||
T value = 0;
|
||||
for (size_t k=0; k<i; ++k) {
|
||||
value += L(i,k)*y(k);
|
||||
}
|
||||
y(i) = b(i) - value;
|
||||
}
|
||||
#endif
|
||||
return y;
|
||||
}
|
||||
template<typename T, size_t M, size_t N>
|
||||
FASTOR_INLINE Tensor<T,M,N> forward_subs(const Tensor<T,M,M> &L, const Tensor<T,M,N> &B) {
|
||||
|
||||
// We keep a separate output tensor X from y [y is columns of X]
|
||||
// to avoid strided access in X for the inner product
|
||||
Tensor<T,M,N> X;
|
||||
|
||||
for (size_t j=0; j < N; ++j) {
|
||||
Tensor<T,M> y(0);
|
||||
forward_subs_impl<0,M-1>::do_multi_rhs(j, L, B, y, X);
|
||||
}
|
||||
#if 0
|
||||
// The run-time loop version - X needs to be zeroed out for this version
|
||||
for (size_t j=0; j < N; ++j) {
|
||||
Tensor<T,M> y(0);
|
||||
// Solve for L * y = b
|
||||
for (size_t i=0; i< M; ++i) {
|
||||
T value = 0;
|
||||
for (size_t k=0; k<i; ++k) {
|
||||
value += L(i,k)*y(k);
|
||||
}
|
||||
y(i) = B(i,j) - value;
|
||||
X(i,j) = y(i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return X;
|
||||
}
|
||||
template<typename T, size_t M>
|
||||
FASTOR_INLINE Tensor<T,M> forward_subs(const Tensor<T,M,M> &L, const Tensor<size_t,M> &p, const Tensor<T,M> &b) {
|
||||
|
||||
Tensor<T,M> y(0);
|
||||
|
||||
forward_subs_impl<0,M-1>::do_single_rhs_pivot(L, b, p, y);
|
||||
#if 0
|
||||
// The run-time loop version
|
||||
// Solve for L * y = b
|
||||
for (size_t i=0; i< M; ++i) {
|
||||
T value = 0;
|
||||
for (size_t k=0; k<i; ++k) {
|
||||
value += L(i,k)*y(k);
|
||||
}
|
||||
y(i) = b(p(i)) - value;
|
||||
}
|
||||
#endif
|
||||
return y;
|
||||
}
|
||||
template<typename T, size_t M, size_t N>
|
||||
FASTOR_INLINE Tensor<T,M,N> forward_subs(const Tensor<T,M,M> &L, const Tensor<size_t,M> &p, const Tensor<T,M,N> &B) {
|
||||
|
||||
// We keep a separate output tensor X from y [y is columns of X]
|
||||
// to avoid strided access in X for the inner product
|
||||
Tensor<T,M,N> X;
|
||||
|
||||
for (size_t j=0; j < N; ++j) {
|
||||
Tensor<T,M> y(0);
|
||||
forward_subs_impl<0,M-1>::do_multi_rhs_pivot(j, L, B, p, y, X);
|
||||
}
|
||||
#if 0
|
||||
// The run-time loop version - X needs to be zeroed out for this version
|
||||
for (size_t j=0; j < N; ++j) {
|
||||
Tensor<T,M> y(0);
|
||||
// Solve for L * y = b
|
||||
for (size_t i=0; i< M; ++i) {
|
||||
T value = 0;
|
||||
for (size_t k=0; k<i; ++k) {
|
||||
value += L(i,k)*y(k);
|
||||
}
|
||||
y(i) = B(p(i),j) - value;
|
||||
X(i,j) = y(i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return X;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
|
||||
|
||||
|
||||
/* Compile time recursive loop with inner for backward substitution of y/Y given the upper triangular matrix U.
|
||||
The following meta functions implements U * x = y for single or multiple right sides
|
||||
*/
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
template <int from, int to>
|
||||
struct backward_subs_impl {
|
||||
|
||||
template<typename T, size_t M>
|
||||
static FASTOR_INLINE void do_single_rhs(const Tensor<T,M,M> &U, const Tensor<T,M> &y, Tensor<T,M> &x) {
|
||||
constexpr int idx = (int)M - from;
|
||||
const T value = _inner<T,idx>(&U.data()[from*M+from],&x.data()[from]);
|
||||
x(from) = ( y(from) - value) / U(from, from);
|
||||
backward_subs_impl<from-1,to>::do_single_rhs(U, y, x);
|
||||
}
|
||||
|
||||
template<typename T, size_t M, size_t N>
|
||||
static FASTOR_INLINE void do_multi_rhs(const size_t j, const Tensor<T,M,M> &U, const Tensor<T,M,N> &Y, Tensor<T,M> &x, Tensor<T,M,N> &X) {
|
||||
constexpr int idx = (int)M - from;
|
||||
const T value = _inner<T,idx>(&U.data()[from*M+from],&x.data()[from]);
|
||||
x(from) = ( Y(from,j) - value ) / U(from, from);
|
||||
X(from,j) = x(from);
|
||||
backward_subs_impl<from-1,to>::do_multi_rhs(j, U, Y, x, X);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct backward_subs_impl<0,0> {
|
||||
|
||||
template<typename T, size_t M>
|
||||
static FASTOR_INLINE void do_single_rhs(const Tensor<T,M,M> &U, const Tensor<T,M> &y, Tensor<T,M> &x) {
|
||||
constexpr int idx = (int)M;
|
||||
const T value = _inner<T,idx>(&U.data()[0*M+0],&x.data()[0]);
|
||||
x(0) = ( y(0) - value) / U(0, 0);
|
||||
}
|
||||
|
||||
template<typename T, size_t M, size_t N>
|
||||
static FASTOR_INLINE void do_multi_rhs(const size_t j, const Tensor<T,M,M> &U, const Tensor<T,M,N> &Y, Tensor<T,M> &x, Tensor<T,M,N> &X) {
|
||||
constexpr int idx = (int)M;
|
||||
const T value = _inner<T,idx>(&U.data()[0*M+0],&x.data()[0]);
|
||||
x(0) = ( Y(0,j) - value ) / U(0, 0);
|
||||
X(0,j) = x(0);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, size_t M>
|
||||
FASTOR_INLINE Tensor<T,M> backward_subs(const Tensor<T,M,M> &U, const Tensor<T,M> &y) {
|
||||
|
||||
// We keep a separate output tensor X from x [x is columns of X]
|
||||
// to avoid strided access in X for the inner product
|
||||
Tensor<T,M> x(0);
|
||||
backward_subs_impl<int(M)-1,0>::do_single_rhs(U, y, x);
|
||||
#if 0
|
||||
// The run-time loop version
|
||||
// Solve for of U * x = y
|
||||
for (int i= int(M) - 1; i>=0; --i) {
|
||||
T value = 0;
|
||||
for (int k=i; k<int(M); ++k) {
|
||||
value += U(i,k)*x(k);
|
||||
}
|
||||
x(i) = (y(i) - value) / U(i, i);
|
||||
}
|
||||
#endif
|
||||
return x;
|
||||
}
|
||||
|
||||
template<typename T, size_t M, size_t N>
|
||||
FASTOR_INLINE Tensor<T,M,N> backward_subs(const Tensor<T,M,M> &U, const Tensor<T,M,N> &Y) {
|
||||
|
||||
// We keep a separate output tensor X from x [x is columns of X]
|
||||
// to avoid strided access in X for the inner product
|
||||
Tensor<T,M,N> X;
|
||||
|
||||
for (size_t j=0; j < N; ++j) {
|
||||
Tensor<T,M> x(0);
|
||||
backward_subs_impl<int(M)-1,0>::do_multi_rhs(j, U, Y, x, X);
|
||||
}
|
||||
#if 0
|
||||
// The run-time loop version - X needs to be zeroed out for this version
|
||||
Tensor<T,M,N> X(0);
|
||||
for (size_t j=0; j < 1; ++j) {
|
||||
// Solve for of U * x = y
|
||||
for (int i= int(M) - 1; i>=0; --i) {
|
||||
T value = 0;
|
||||
for (int k=i; k<int(M); ++k) {
|
||||
value += U(i,k)*X(k,j);
|
||||
}
|
||||
X(i,j) = (Y(i,j) - value) / U(i, i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return X;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
|
||||
|
||||
/* Simple LU factorisation without pivoting */
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
template <typename T, size_t M, enable_if_t_<is_greater_v_<M,0> && is_less_equal_v_<M,8>,bool> = false>
|
||||
FASTOR_INLINE void lu_simple_dispatcher(const Tensor<T,M,M>& A, Tensor<T,M,M>& L, Tensor<T,M,M>& U) {
|
||||
_lufact<T,M>(A.data(),L.data(),U.data());
|
||||
}
|
||||
|
||||
template <typename T, size_t M, enable_if_t_<is_greater_v_<M,8>,bool> = false>
|
||||
FASTOR_INLINE void lu_simple_dispatcher(const Tensor<T,M,M>& A1, Tensor<T,M,M>& L, Tensor<T,M,M>& U) {
|
||||
L.fill(0);
|
||||
U.fill(0);
|
||||
for (size_t j = 0; j < M; ++j) {
|
||||
L(j, j) = 1;
|
||||
for (size_t i = 0; i <= j; ++i) {
|
||||
T value = A1(i, j);
|
||||
for (size_t k = 0; k < i; ++k) {
|
||||
value -= L(i, k) * U(k, j);
|
||||
}
|
||||
U(i, j) = value;
|
||||
}
|
||||
for (size_t i = j; i < M; ++i) {
|
||||
T value = A1(i, j);
|
||||
for (size_t k = 0; k < j; ++k) {
|
||||
value -= L(i, k) * U(k, j);
|
||||
}
|
||||
value /= U(j, j);
|
||||
L(i, j) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
|
||||
// Recursive non-modifying LU using matmul/outer product
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
/* This in essence implements the following loop but with static views
|
||||
as dynamic views cannot be dispatched to matmul or a tensor cannot be
|
||||
constructed to be sent to matmul
|
||||
|
||||
for(size_t j=0; j<M-1; ++j) {
|
||||
L(seq(j+1,M),j) /= L(j,j);
|
||||
L(seq(j+1,M),seq(j+1,M)) -= L(seq(j+1,M),j) % L(j,seq(j+1,M));
|
||||
}
|
||||
|
||||
This is a non-modifying version that fills L and U directly and avoids
|
||||
the need for extracting L and U later
|
||||
The pre-requisite is that L should be Identity and U be a copy of A before
|
||||
the recursive routine starts
|
||||
|
||||
*/
|
||||
template<size_t from, size_t to>
|
||||
struct recursive_lu_impl {
|
||||
template<typename T, size_t M, size_t N>
|
||||
static FASTOR_INLINE void Do(Tensor<T,M,N> &L, Tensor<T,M,N> &U) {
|
||||
L(fseq<from+1,M>(),fix<from>) = U(fseq<from+1,M>(),fix<from>) / U.data()[from*N+from];
|
||||
U(fseq<from+1,M>(),fix<from>) = 0;
|
||||
U(fseq<from+1,M>(),fseq<from+1,M>()) -= matmul(L(fseq<from+1,M>(),fix<from>), U(fix<from>,fseq<from+1,M>()));
|
||||
recursive_lu_impl<from+1,to>::Do(L, U);
|
||||
}
|
||||
};
|
||||
template<size_t from>
|
||||
struct recursive_lu_impl<from,from> {
|
||||
template<typename T, size_t M, size_t N>
|
||||
static FASTOR_INLINE void Do(Tensor<T,M,N> &L, Tensor<T,M,N> &U) {
|
||||
L(fseq<from+1,M>(),fix<from>) = U(fseq<from+1,M>(),fix<from>) / U.data()[from*N+from];
|
||||
U(fseq<from+1,M>(),fix<from>) = 0;
|
||||
U(fseq<from+1,M>(),fseq<from+1,M>()) -= matmul(L(fseq<from+1,M>(),fix<from>), U(fix<from>,fseq<from+1,M>()));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, size_t M, enable_if_t_<is_equal_v_<M,1>,bool> = false>
|
||||
FASTOR_INLINE void recursive_lu_dispatcher(const Tensor<T,M,M>& A, Tensor<T,M,M>& L, Tensor<T,M,M>& U) {
|
||||
L(0,0) = 1; U(0,0) = A(0,0);
|
||||
}
|
||||
template <typename T, size_t M, enable_if_t_<is_greater_v_<M,1>,bool> = false>
|
||||
FASTOR_INLINE void recursive_lu_dispatcher(const Tensor<T,M,M>& A, Tensor<T,M,M>& L, Tensor<T,M,M>& U) {
|
||||
// Requires M >=2
|
||||
U = A;
|
||||
L.eye2();
|
||||
recursive_lu_impl<0,M-2>::Do(L, U);
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
// Recursive in-place LU using matmul/outer product
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
/* This in essence implements the following loop but with static views
|
||||
as dynamic views cannot be dispatched to matmul or a tensor cannot be
|
||||
constructed to be sent to matmul
|
||||
|
||||
for(size_t j=0; j<M-1; ++j) {
|
||||
L(seq(j+1,M),j) /= L(j,j);
|
||||
L(seq(j+1,M),seq(j+1,M)) -= L(seq(j+1,M),j) % L(j,seq(j+1,M));
|
||||
}
|
||||
|
||||
This is a self-modifying [in-place] version - if instead of a copy of
|
||||
(LU in this case) we pass the matrix itself it will decompose it in to
|
||||
an LU. Extracting L and U after this recursive decomposition is done
|
||||
almost beats the purpose performance wise
|
||||
|
||||
*/
|
||||
template<size_t from, size_t to>
|
||||
struct recursive_lu_impl_inplace {
|
||||
template<typename T, size_t M, size_t N>
|
||||
static FASTOR_INLINE void Do(Tensor<T,M,N> &LU) {
|
||||
LU(fseq<from+1,M>(),fix<from>) /= LU.data()[from*N+from];
|
||||
LU(fseq<from+1,M>(),fseq<from+1,M>()) -= matmul(LU(fseq<from+1,M>(),fix<from>), LU(fix<from>,fseq<from+1,M>()));
|
||||
recursive_lu_impl_inplace<from+1,to>::Do(LU);
|
||||
}
|
||||
};
|
||||
template<size_t from>
|
||||
struct recursive_lu_impl_inplace<from,from> {
|
||||
template<typename T, size_t M, size_t N>
|
||||
static FASTOR_INLINE void Do(Tensor<T,M,N> &LU) {
|
||||
LU(fseq<from+1,M>(),fix<from>) /= LU.data()[from*N+from];
|
||||
LU(fseq<from+1,M>(),fseq<from+1,M>()) -= matmul(LU(fseq<from+1,M>(),fix<from>), LU(fix<from>,fseq<from+1,M>()));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename T, size_t M, enable_if_t_<is_equal_v_<M,1>,bool> = false>
|
||||
FASTOR_INLINE void recursive_inplace_lu_dispatcher(Tensor<T,M,M>& A) {
|
||||
return;
|
||||
}
|
||||
template <typename T, size_t M, enable_if_t_<is_greater_v_<M,1>,bool> = false>
|
||||
FASTOR_INLINE void recursive_inplace_lu_dispatcher(Tensor<T,M,M>& A) {
|
||||
// Requires M >=2
|
||||
recursive_lu_impl_inplace<0,M-2>::Do(A);
|
||||
}
|
||||
|
||||
template <typename T, size_t M, enable_if_t_<is_equal_v_<M,1>,bool> = false>
|
||||
FASTOR_INLINE void recursive_inplace_lu_dispatcher(const Tensor<T,M,M>& A, Tensor<T,M,M>& LU) {
|
||||
LU(0,0) = A(0,0);
|
||||
}
|
||||
template <typename T, size_t M, enable_if_t_<is_greater_v_<M,1>,bool> = false>
|
||||
FASTOR_INLINE void recursive_inplace_lu_dispatcher(const Tensor<T,M,M>& A, Tensor<T,M,M>& LU) {
|
||||
// Requires M >=2
|
||||
LU =A;
|
||||
recursive_lu_impl_inplace<0,M-2>::Do(LU);
|
||||
}
|
||||
|
||||
template <typename T, size_t M, enable_if_t_<is_equal_v_<M,1>,bool> = false>
|
||||
FASTOR_INLINE void recursive_inplace_lu_dispatcher(const Tensor<T,M,M>& A, Tensor<T,M,M>& L, Tensor<T,M,M>& U) {
|
||||
L(0,0) = 1; U(0,0) = A(0,0);
|
||||
}
|
||||
template <typename T, size_t M, enable_if_t_<is_greater_v_<M,1>,bool> = false>
|
||||
FASTOR_INLINE void recursive_inplace_lu_dispatcher(const Tensor<T,M,M>& A, Tensor<T,M,M>& L, Tensor<T,M,M>& U) {
|
||||
// Requires M >=2
|
||||
Tensor<T,M,M> LU(A);
|
||||
recursive_lu_impl_inplace<0,M-2>::Do(LU);
|
||||
|
||||
// Extracting L and U after this recursive decomposition is done
|
||||
// almost beats the purpose performance wise
|
||||
for (size_t i=0; i<M; ++i) {
|
||||
L(i,i) = 1;
|
||||
}
|
||||
for (size_t i=0; i<M; ++i) {
|
||||
for (size_t j=0; j<i; ++j) {
|
||||
L(i,j) = LU(i,j);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i=0; i<M; ++i) {
|
||||
for (size_t j=i; j<M; ++j) {
|
||||
U(i,j) = LU(i,j);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
|
||||
/* Block LU factorisation without pivoting */
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
template <typename T, size_t M, enable_if_t_<is_greater_v_<M,0> && is_less_equal_v_<M,8>,bool> = false>
|
||||
FASTOR_INLINE void lu_block_dispatcher(const Tensor<T,M,M>& A, Tensor<T,M,M>& L, Tensor<T,M,M>& U) {
|
||||
_lufact<T,M>(A.data(),L.data(),U.data());
|
||||
}
|
||||
|
||||
template <typename T, size_t M, enable_if_t_<is_greater_v_<M,8> && is_less_equal_v_<M,32>,bool> = false>
|
||||
FASTOR_INLINE void lu_block_dispatcher(const Tensor<T,M,M>& A, Tensor<T,M,M>& L, Tensor<T,M,M>& U) {
|
||||
recursive_lu_dispatcher(A, L, U);
|
||||
}
|
||||
|
||||
template <typename T, size_t M, enable_if_t_<is_greater_v_<M,32> && is_less_equal_v_<M,64>,bool> = false>
|
||||
FASTOR_INLINE void lu_block_dispatcher(const Tensor<T,M,M>& A, Tensor<T,M,M>& L, Tensor<T,M,M>& U) {
|
||||
|
||||
// We will compute LU decomposition block-wise assuming that A11 and A22 are invertible
|
||||
//
|
||||
// [A11 A12] [L11 0] [U11 U12]
|
||||
// [A21 A22] [L21 L22] [0 U22]
|
||||
//
|
||||
// This results in
|
||||
//
|
||||
// A11 = L11 * U11
|
||||
// A12 = L11 * U12
|
||||
// A21 = L21 * U11
|
||||
// A22 = L21 * U12 - L22 * U22
|
||||
//
|
||||
// Hence we need to do LU factorisation once for A11 and once for A22
|
||||
|
||||
// This is to avoid odd sizes for instance for size 35 we would
|
||||
// want to do 35 = 16 + 19 rather than 35 = 32 + 3 if the start size was 32
|
||||
constexpr size_t N = (M / 8UL * 8UL) / 2UL; // start size
|
||||
Tensor<T,N ,N > A11 = A(fseq<0,N>(),fseq<0,N>());
|
||||
Tensor<T,N ,M-N> A12 = A(fseq<0,N>(),fseq<N,M>());
|
||||
Tensor<T,M-N, N> A21 = A(fseq<N,M>(),fseq<0,N>());
|
||||
Tensor<T,M-N,M-N> A22 = A(fseq<N,M>(),fseq<N,M>());
|
||||
|
||||
Tensor<T,N,N> L11(0), U11(0);
|
||||
lu_block_dispatcher(A11, L11, U11);
|
||||
|
||||
// Solve for U12 = {L11}^(-1)*A12
|
||||
Tensor<T,N ,M-N> U12 = tmatmul<UpLoType::Lower,UpLoType::General>(tinverse<InvCompType::SimpleInv, UpLoType::UniLower>(L11),A12);
|
||||
// Solve for L21 = A21*{U11}^(-1)
|
||||
Tensor<T,M-N, N> L21 = tmatmul<UpLoType::General,UpLoType::Upper>(A21,tinverse<InvCompType::SimpleInv, UpLoType::Upper>(U11));
|
||||
|
||||
Tensor<T,M-N,M-N> S = A22 - matmul(L21,U12);
|
||||
|
||||
Tensor<T,M-N,M-N> L22(0), U22(0);
|
||||
lu_block_dispatcher(S, L22, U22);
|
||||
|
||||
L(fseq<0,N>(),fseq<0,N>()) = L11;
|
||||
// L(fseq<0,N>(),fseq<N,M>()) = 0;
|
||||
L(fseq<N,M>(),fseq<0,N>()) = L21;
|
||||
L(fseq<N,M>(),fseq<N,M>()) = L22;
|
||||
|
||||
U(fseq<0,N>(),fseq<0,N>()) = U11;
|
||||
U(fseq<0,N>(),fseq<N,M>()) = U12;
|
||||
// U(fseq<N,M>(),fseq<0,N>()) = 0;
|
||||
U(fseq<N,M>(),fseq<N,M>()) = U22;
|
||||
}
|
||||
|
||||
// Conditional dispatch
|
||||
namespace useless {
|
||||
template <typename T, size_t M, enable_if_t_<is_greater_v_<M,0> && is_less_equal_v_<M,64>,bool> = false>
|
||||
FASTOR_INLINE void lu_block_simple_dispatcher(const Tensor<T,M,M>& A, Tensor<T,M,M>& L, Tensor<T,M,M>& U) {
|
||||
lu_block_dispatcher(A, L, U);
|
||||
return;
|
||||
}
|
||||
template <typename T, size_t M, enable_if_t_<is_greater_v_<M,64>,bool> = false>
|
||||
FASTOR_INLINE void lu_block_simple_dispatcher(const Tensor<T,M,M>& A, Tensor<T,M,M>& L, Tensor<T,M,M>& U) {
|
||||
// lu_simple_dispatcher(A, L, U);
|
||||
recursive_lu_dispatcher(A, L, U);
|
||||
return;
|
||||
}
|
||||
} // useless
|
||||
|
||||
/* For sizes greater than 64 we tile differently to avoid too many recursions
|
||||
*/
|
||||
template <typename T, size_t M, enable_if_t_<is_greater_v_<M,64>,bool> = false>
|
||||
FASTOR_INLINE void lu_block_dispatcher(const Tensor<T,M,M>& A, Tensor<T,M,M>& L, Tensor<T,M,M>& U) {
|
||||
|
||||
// We will compute LU decomposition block-wise assuming that A11 and A22 are invertible
|
||||
//
|
||||
// [A11 A12] [L11 0] [U11 U12]
|
||||
// [A21 A22] [L21 L22] [0 U22]
|
||||
//
|
||||
// This results in
|
||||
//
|
||||
// A11 = L11 * U11
|
||||
// A12 = L11 * U12
|
||||
// A21 = L21 * U11
|
||||
// A22 = L21 * U12 - L22 * U22
|
||||
//
|
||||
// Hence we need to do LU factorisation once for A11 and once for A22
|
||||
|
||||
// This is to avoid odd sizes for instance for size 65 we would
|
||||
// want to do 65 = 32 + 33 rather than 65 = 64 + 1 if the start size was 64
|
||||
constexpr size_t N = (M / 16UL * 16UL) / 2UL; // start size
|
||||
Tensor<T,N ,N > A11 = A(fseq<0,N>(),fseq<0,N>());
|
||||
Tensor<T,N ,M-N> A12 = A(fseq<0,N>(),fseq<N,M>());
|
||||
Tensor<T,M-N, N> A21 = A(fseq<N,M>(),fseq<0,N>());
|
||||
Tensor<T,M-N,M-N> A22 = A(fseq<N,M>(),fseq<N,M>());
|
||||
|
||||
Tensor<T,N,N> L11(0), U11(0);
|
||||
useless::lu_block_simple_dispatcher(A11, L11, U11);
|
||||
|
||||
// Solve for U12 = {L11}^(-1)*A12
|
||||
Tensor<T,N ,M-N> U12 = tmatmul<UpLoType::Lower,UpLoType::General>(tinverse<InvCompType::SimpleInv, UpLoType::UniLower>(L11),A12);
|
||||
// Ideally use forward_subs but its iterative nature makes it less efficient than tmatmul
|
||||
// Tensor<T,N ,M-N> U12 = forward_subs(L11,A12);
|
||||
// Solve for L21 = A21*{U11}^(-1)
|
||||
Tensor<T,M-N, N> L21 = tmatmul<UpLoType::General,UpLoType::Upper>(A21,tinverse<InvCompType::SimpleInv, UpLoType::Upper>(U11));
|
||||
// Not quite performant as we can't avoid the matmul here
|
||||
// Tensor<T,N ,N > I; I.eye2();
|
||||
// Tensor<T,M-N, N> L21 = matmul(A21, backward_subs(U11, I));
|
||||
|
||||
Tensor<T,M-N,M-N> S = A22 - matmul(L21,U12);
|
||||
|
||||
Tensor<T,M-N,M-N> L22(0), U22(0);
|
||||
useless::lu_block_simple_dispatcher(S, L22, U22);
|
||||
|
||||
L(fseq<0,N>(),fseq<0,N>()) = L11;
|
||||
// L(fseq<0,N>(),fseq<N,M>()) = 0;
|
||||
L(fseq<N,M>(),fseq<0,N>()) = L21;
|
||||
L(fseq<N,M>(),fseq<N,M>()) = L22;
|
||||
|
||||
U(fseq<0,N>(),fseq<0,N>()) = U11;
|
||||
U(fseq<0,N>(),fseq<N,M>()) = U12;
|
||||
// U(fseq<N,M>(),fseq<0,N>()) = 0;
|
||||
U(fseq<N,M>(),fseq<N,M>()) = U22;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
} // internal
|
||||
|
||||
|
||||
/* Block LU factorisation overloads */
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
// BlockLU - no pivot
|
||||
template<LUCompType LUType = LUCompType::BlockLU, typename Expr, size_t DIM0, typename T, size_t M,
|
||||
enable_if_t_<is_tensor_v<Expr> && LUType == LUCompType::BlockLU,bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
lu(const AbstractTensor<Expr,DIM0> &src, Tensor<T,M,M>& L, Tensor<T,M,M>& U) {
|
||||
L.fill(0);
|
||||
U.fill(0);
|
||||
internal::lu_block_dispatcher(src.self(),L,U);
|
||||
}
|
||||
template<LUCompType LUType = LUCompType::BlockLU, typename Expr, size_t DIM0, typename T, size_t M,
|
||||
enable_if_t_<!is_tensor_v<Expr> && LUType == LUCompType::BlockLU,bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
lu(const AbstractTensor<Expr,DIM0> &src, Tensor<T,M,M>& L, Tensor<T,M,M>& U) {
|
||||
L.fill(0);
|
||||
U.fill(0);
|
||||
typename Expr::result_type tmp(src.self());
|
||||
internal::lu_block_dispatcher(tmp,L,U);
|
||||
}
|
||||
|
||||
// BlockLU - vector pivot
|
||||
template<LUCompType LUType = LUCompType::BlockLU, typename Expr, size_t DIM0, typename T, size_t M,
|
||||
enable_if_t_<is_tensor_v<Expr> && LUType == LUCompType::BlockLUPiv,bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
lu(const AbstractTensor<Expr,DIM0> &src, Tensor<T,M,M>& L, Tensor<T,M,M>& U, Tensor<size_t,M>& P) {
|
||||
L.fill(0);
|
||||
U.fill(0);
|
||||
pivot_inplace(src.self(),P);
|
||||
auto A(apply_pivot(src.self(),P));
|
||||
internal::lu_block_dispatcher(A,L,U);
|
||||
}
|
||||
template<LUCompType LUType = LUCompType::BlockLU, typename Expr, size_t DIM0, typename T, size_t M,
|
||||
enable_if_t_<!is_tensor_v<Expr> && LUType == LUCompType::BlockLUPiv,bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
lu(const AbstractTensor<Expr,DIM0> &src, Tensor<T,M,M>& L, Tensor<T,M,M>& U, Tensor<size_t,M>& P) {
|
||||
L.fill(0);
|
||||
U.fill(0);
|
||||
typename Expr::result_type A(src.self());
|
||||
pivot_inplace(A,P);
|
||||
// Modify A as A is a temporary anyway
|
||||
apply_pivot_inplace(A,P);
|
||||
internal::lu_block_dispatcher(A,L,U);
|
||||
}
|
||||
|
||||
// BlockLU - matrix pivot
|
||||
template<LUCompType LUType = LUCompType::BlockLU, typename Expr, size_t DIM0, typename T, size_t M,
|
||||
enable_if_t_<is_tensor_v<Expr> && LUType == LUCompType::BlockLUPiv,bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
lu(const AbstractTensor<Expr,DIM0> &src, Tensor<T,M,M>& L, Tensor<T,M,M>& U, Tensor<T,M,M>& P) {
|
||||
L.fill(0);
|
||||
U.fill(0);
|
||||
pivot_inplace(src.self(),P);
|
||||
auto A(apply_pivot(src.self(),P));
|
||||
internal::lu_block_dispatcher(A,L,U);
|
||||
}
|
||||
template<LUCompType LUType = LUCompType::BlockLU, typename Expr, size_t DIM0, typename T, size_t M,
|
||||
enable_if_t_<!is_tensor_v<Expr> && LUType == LUCompType::BlockLUPiv,bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
lu(const AbstractTensor<Expr,DIM0> &src, Tensor<T,M,M>& L, Tensor<T,M,M>& U, Tensor<T,M,M>& P) {
|
||||
L.fill(0);
|
||||
U.fill(0);
|
||||
typename Expr::result_type A(src.self());
|
||||
pivot_inplace(A,P);
|
||||
// Modify A as A is a temporary anyway
|
||||
apply_pivot_inplace(A,P);
|
||||
internal::lu_block_dispatcher(A,L,U);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
|
||||
|
||||
/* Simple LU factorisation overloads */
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
// SimpleLU - no pivot
|
||||
template<LUCompType LUType = LUCompType::BlockLU, typename Expr, size_t DIM0, typename T, size_t M,
|
||||
enable_if_t_<is_tensor_v<Expr> && LUType == LUCompType::SimpleLU,bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
lu(const AbstractTensor<Expr,DIM0> &src, Tensor<T,M,M>& L, Tensor<T,M,M>& U) {
|
||||
internal::lu_simple_dispatcher(src.self(),L,U);
|
||||
}
|
||||
template<LUCompType LUType = LUCompType::BlockLU, typename Expr, size_t DIM0, typename T, size_t M,
|
||||
enable_if_t_<!is_tensor_v<Expr> && LUType == LUCompType::SimpleLU,bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
lu(const AbstractTensor<Expr,DIM0> &src, Tensor<T,M,M>& L, Tensor<T,M,M>& U) {
|
||||
typename Expr::result_type tmp(src.self());
|
||||
internal::lu_simple_dispatcher(tmp,L,U);
|
||||
}
|
||||
|
||||
// SimpleLU - vector pivot
|
||||
template<LUCompType LUType = LUCompType::BlockLU, typename Expr, size_t DIM0, typename T, size_t M,
|
||||
enable_if_t_<is_tensor_v<Expr> && LUType == LUCompType::SimpleLUPiv,bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
lu(const AbstractTensor<Expr,DIM0> &src, Tensor<T,M,M>& L, Tensor<T,M,M>& U, Tensor<size_t,M>& P) {
|
||||
pivot_inplace(src.self(),P);
|
||||
auto A(apply_pivot(src.self(),P));
|
||||
internal::lu_simple_dispatcher(A,L,U);
|
||||
}
|
||||
template<LUCompType LUType = LUCompType::BlockLU, typename Expr, size_t DIM0, typename T, size_t M,
|
||||
enable_if_t_<!is_tensor_v<Expr> && LUType == LUCompType::SimpleLUPiv,bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
lu(const AbstractTensor<Expr,DIM0> &src, Tensor<T,M,M>& L, Tensor<T,M,M>& U, Tensor<size_t,M>& P) {
|
||||
typename Expr::result_type A(src.self());
|
||||
pivot_inplace(A,P);
|
||||
// Modify A as A is a temporary anyway
|
||||
apply_pivot_inplace(A,P);
|
||||
internal::lu_simple_dispatcher(A,L,U);
|
||||
}
|
||||
|
||||
// SimpleLU - matrix pivot
|
||||
template<LUCompType LUType = LUCompType::BlockLU, typename Expr, size_t DIM0, typename T, size_t M,
|
||||
enable_if_t_<is_tensor_v<Expr> && LUType == LUCompType::SimpleLUPiv,bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
lu(const AbstractTensor<Expr,DIM0> &src, Tensor<T,M,M>& L, Tensor<T,M,M>& U, Tensor<T,M,M>& P) {
|
||||
pivot_inplace(src.self(),P);
|
||||
auto A(apply_pivot(src.self(),P));
|
||||
internal::lu_simple_dispatcher(A,L,U);
|
||||
}
|
||||
template<LUCompType LUType = LUCompType::BlockLU, typename Expr, size_t DIM0, typename T, size_t M,
|
||||
enable_if_t_<!is_tensor_v<Expr> && LUType == LUCompType::SimpleLUPiv,bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
lu(const AbstractTensor<Expr,DIM0> &src, Tensor<T,M,M>& L, Tensor<T,M,M>& U, Tensor<T,M,M>& P) {
|
||||
typename Expr::result_type A(src.self());
|
||||
pivot_inplace(A,P);
|
||||
// Modify A as A is a temporary anyway
|
||||
apply_pivot_inplace(A,P);
|
||||
internal::lu_simple_dispatcher(A,L,U);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Inversion using LU
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
namespace internal {
|
||||
|
||||
template<typename T, size_t M>
|
||||
FASTOR_INLINE Tensor<T,M,M> get_lu_inverse(const Tensor<T,M,M> &L, const Tensor<T,M,M> &U) {
|
||||
// We will solve for multiple RHS [B = I]
|
||||
// Loop over columns of B = I
|
||||
Tensor<T,M,M> I; I.eye2();
|
||||
Tensor<T,M,M> Y = forward_subs(L, I);
|
||||
Tensor<T,M,M> X = backward_subs(U, Y);
|
||||
return X;
|
||||
}
|
||||
|
||||
template<typename T, size_t M>
|
||||
FASTOR_INLINE Tensor<T,M,M> get_lu_inverse(Tensor<T,M,M> &L, const Tensor<T,M,M> &U, const Tensor<size_t,M> &p) {
|
||||
// We will solve for multiple RHS [B = I]
|
||||
// Loop over columns of B = I
|
||||
Tensor<T,M,M> I; I.eye2();
|
||||
Tensor<T,M,M> Y = forward_subs(L, p, I);
|
||||
Tensor<T,M,M> X = backward_subs(U, Y);
|
||||
return X;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
} // internal
|
||||
|
||||
|
||||
// SimpleLU
|
||||
template<InvCompType InvType = InvCompType::SimpleInv,
|
||||
typename T, size_t M, enable_if_t_<InvType == InvCompType::SimpleLU,bool> = false>
|
||||
FASTOR_INLINE Tensor<T,M,M> inverse(const Tensor<T,M,M> &A) {
|
||||
Tensor<T,M,M> L, U;
|
||||
lu<LUCompType::SimpleLU>(A, L, U);
|
||||
return internal::get_lu_inverse(L, U);
|
||||
}
|
||||
|
||||
// BlockLU
|
||||
template<InvCompType InvType = InvCompType::SimpleInv,
|
||||
typename T, size_t M, enable_if_t_<InvType == InvCompType::BlockLU,bool> = false>
|
||||
FASTOR_INLINE Tensor<T,M,M> inverse(const Tensor<T,M,M> &A) {
|
||||
Tensor<T,M,M> L, U;
|
||||
lu<LUCompType::BlockLU>(A, L, U);
|
||||
return internal::get_lu_inverse(L, U);
|
||||
}
|
||||
|
||||
// SimpleLUPiv
|
||||
template<InvCompType InvType = InvCompType::SimpleInv,
|
||||
typename T, size_t M, enable_if_t_<InvType == InvCompType::SimpleLUPiv,bool> = false>
|
||||
FASTOR_INLINE Tensor<T,M,M> inverse(const Tensor<T,M,M> &A) {
|
||||
Tensor<T,M,M> L, U;
|
||||
Tensor<size_t,M> p;
|
||||
lu<LUCompType::SimpleLUPiv>(A, L, U, p);
|
||||
return internal::get_lu_inverse(L, U, p);
|
||||
}
|
||||
|
||||
// BlockLUPiv
|
||||
template<InvCompType InvType = InvCompType::SimpleInv,
|
||||
typename T, size_t M, enable_if_t_<InvType == InvCompType::BlockLUPiv,bool> = false>
|
||||
FASTOR_INLINE Tensor<T,M,M> inverse(const Tensor<T,M,M> &A) {
|
||||
Tensor<T,M,M> L, U;
|
||||
Tensor<size_t,M> p;
|
||||
lu<LUCompType::BlockLUPiv>(A, L, U, p);
|
||||
return internal::get_lu_inverse(L, U, p);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Solving linear system of equations using LU
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
namespace internal {
|
||||
|
||||
template<typename T, size_t M>
|
||||
FASTOR_INLINE Tensor<T,M> get_lu_solve(const Tensor<T,M,M> &L, const Tensor<T,M,M> &U, const Tensor<T,M> &b) {
|
||||
Tensor<T,M> y = forward_subs(L, b);
|
||||
Tensor<T,M> x = backward_subs(U, y);
|
||||
return x;
|
||||
}
|
||||
|
||||
template<typename T, size_t M>
|
||||
FASTOR_INLINE Tensor<T,M> get_lu_solve(Tensor<T,M,M> &L, const Tensor<T,M,M> &U, const Tensor<size_t,M> &p, const Tensor<T,M> &b) {
|
||||
Tensor<T,M> y = forward_subs(L, p, b);
|
||||
Tensor<T,M> x = backward_subs(U, y);
|
||||
return x;
|
||||
}
|
||||
|
||||
// Multiple RHS
|
||||
template<typename T, size_t M, size_t N>
|
||||
FASTOR_INLINE Tensor<T,M,N> get_lu_solve(const Tensor<T,M,M> &L, const Tensor<T,M,M> &U, const Tensor<T,M,N> &B) {
|
||||
Tensor<T,M,N> Y = forward_subs(L, B);
|
||||
Tensor<T,M,N> X = backward_subs(U, Y);
|
||||
return X;
|
||||
}
|
||||
|
||||
template<typename T, size_t M, size_t N>
|
||||
FASTOR_INLINE Tensor<T,M,N> get_lu_solve(const Tensor<T,M,M> &L, const Tensor<T,M,M> &U, const Tensor<size_t,M> &p, const Tensor<T,M,N> &B) {
|
||||
Tensor<T,M,N> Y = forward_subs(L, p, B);
|
||||
Tensor<T,M,N> X = backward_subs(U, Y);
|
||||
return X;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
} // internal
|
||||
|
||||
|
||||
// Single RHS
|
||||
// SimpleLU - no pivot
|
||||
template<SolveCompType SType = SolveCompType::SimpleInv, typename T, size_t M,
|
||||
enable_if_t_< SType == SolveCompType::SimpleLU, bool> = false>
|
||||
FASTOR_INLINE Tensor<T,M> solve(const Tensor<T,M,M> &A, const Tensor<T,M> &b) {
|
||||
Tensor<T,M,M> L, U;
|
||||
lu<LUCompType::SimpleLU>(A, L, U);
|
||||
return internal::get_lu_solve(L, U, b);
|
||||
}
|
||||
|
||||
// SimpleLU - pivot
|
||||
template<SolveCompType SType = SolveCompType::SimpleInv, typename T, size_t M,
|
||||
enable_if_t_< SType == SolveCompType::SimpleLUPiv, bool> = false>
|
||||
FASTOR_INLINE Tensor<T,M> solve(const Tensor<T,M,M> &A, const Tensor<T,M> &b) {
|
||||
Tensor<T,M,M> L, U;
|
||||
Tensor<size_t,M> p;
|
||||
lu<LUCompType::SimpleLUPiv>(A, L, U, p);
|
||||
return internal::get_lu_solve(L, U, p, b);
|
||||
}
|
||||
|
||||
// BlockLU - no pivot
|
||||
template<SolveCompType SType = SolveCompType::SimpleInv, typename T, size_t M,
|
||||
enable_if_t_< SType == SolveCompType::BlockLU, bool> = false>
|
||||
FASTOR_INLINE Tensor<T,M> solve(const Tensor<T,M,M> &A, const Tensor<T,M> &b) {
|
||||
Tensor<T,M,M> L, U;
|
||||
lu<LUCompType::BlockLU>(A, L, U);
|
||||
return internal::get_lu_solve(L, U, b);
|
||||
}
|
||||
|
||||
// BlockLU - pivot
|
||||
template<SolveCompType SType = SolveCompType::SimpleInv, typename T, size_t M,
|
||||
enable_if_t_< SType == SolveCompType::BlockLUPiv, bool> = false>
|
||||
FASTOR_INLINE Tensor<T,M> solve(const Tensor<T,M,M> &A, const Tensor<T,M> &b) {
|
||||
Tensor<T,M,M> L, U;
|
||||
Tensor<size_t,M> p;
|
||||
lu<LUCompType::BlockLUPiv>(A, L, U, p);
|
||||
return internal::get_lu_solve(L, U, p, b);
|
||||
}
|
||||
|
||||
// Multiple RHS
|
||||
// SimpleLU - no pivot
|
||||
template<SolveCompType SType = SolveCompType::SimpleInv, typename T, size_t M, size_t N,
|
||||
enable_if_t_< SType == SolveCompType::SimpleLU, bool> = false>
|
||||
FASTOR_INLINE Tensor<T,M,N> solve(const Tensor<T,M,M> &A, const Tensor<T,M,N> &B) {
|
||||
Tensor<T,M,M> L, U;
|
||||
lu<LUCompType::SimpleLU>(A, L, U);
|
||||
return internal::get_lu_solve(L, U, B);
|
||||
}
|
||||
|
||||
// SimpleLU - pivot
|
||||
template<SolveCompType SType = SolveCompType::SimpleInv, typename T, size_t M, size_t N,
|
||||
enable_if_t_< SType == SolveCompType::SimpleLUPiv, bool> = false>
|
||||
FASTOR_INLINE Tensor<T,M,N> solve(const Tensor<T,M,M> &A, const Tensor<T,M,N> &B) {
|
||||
Tensor<T,M,M> L, U;
|
||||
Tensor<size_t,M> p;
|
||||
lu<LUCompType::SimpleLUPiv>(A, L, U, p);
|
||||
return internal::get_lu_solve(L, U, p, B);
|
||||
}
|
||||
|
||||
// SimpleLU - no pivot
|
||||
template<SolveCompType SType = SolveCompType::SimpleInv, typename T, size_t M, size_t N,
|
||||
enable_if_t_< SType == SolveCompType::BlockLU, bool> = false>
|
||||
FASTOR_INLINE Tensor<T,M,N> solve(const Tensor<T,M,M> &A, const Tensor<T,M,N> &B) {
|
||||
Tensor<T,M,M> L, U;
|
||||
lu<LUCompType::BlockLU>(A, L, U);
|
||||
return internal::get_lu_solve(L, U, B);
|
||||
}
|
||||
|
||||
// SimpleLU - pivot
|
||||
template<SolveCompType SType = SolveCompType::SimpleInv, typename T, size_t M, size_t N,
|
||||
enable_if_t_< SType == SolveCompType::BlockLUPiv, bool> = false>
|
||||
FASTOR_INLINE Tensor<T,M,N> solve(const Tensor<T,M,M> &A, const Tensor<T,M,N> &B) {
|
||||
Tensor<T,M,M> L, U;
|
||||
Tensor<size_t,M> p;
|
||||
lu<LUCompType::BlockLUPiv>(A, L, U, p);
|
||||
return internal::get_lu_solve(L, U, p, B);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Computing determinant using LU
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
template<DetCompType DetType = DetCompType::Simple, typename T, size_t M,
|
||||
enable_if_t_<DetType == DetCompType::LU,bool> = false>
|
||||
FASTOR_INLINE T determinant(const Tensor<T,M,M> &A) {
|
||||
int nswaps = internal::count_swaps(A) % 2UL == 0 ? 1 : -1;
|
||||
Tensor<T,M,M> L, U;
|
||||
Tensor<size_t,M> p;
|
||||
lu<LUCompType::BlockLUPiv>(A, L, U, p);
|
||||
return product(diag(U)) * nswaps;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
|
||||
} // end of namespace Fastor
|
||||
|
||||
|
||||
#endif // UNARY_LU_OP_H
|
||||
106
noarch/include/Fastor/expressions/linalg_ops/unary_norm_op.h
Normal file
106
noarch/include/Fastor/expressions/linalg_ops/unary_norm_op.h
Normal file
@@ -0,0 +1,106 @@
|
||||
#ifndef UNARY_NORM_OP_H
|
||||
#define UNARY_NORM_OP_H
|
||||
|
||||
#include "Fastor/meta/meta.h"
|
||||
#include "Fastor/simd_vector/SIMDVector.h"
|
||||
#include "Fastor/backend/norm.h"
|
||||
#include "Fastor/tensor/AbstractTensor.h"
|
||||
#include "Fastor/tensor/TensorTraits.h"
|
||||
#include "Fastor/expressions/expression_traits.h"
|
||||
#include "Fastor/expressions/linalg_ops/linalg_traits.h"
|
||||
|
||||
namespace Fastor {
|
||||
|
||||
// For tensors
|
||||
template<typename T, enable_if_t_<is_arithmetic_v_<T>,bool> = false>
|
||||
FASTOR_INLINE T norm(const T &a) {
|
||||
return std::abs(a);
|
||||
}
|
||||
template<typename T, size_t ... Rest>
|
||||
FASTOR_INLINE T norm(const Tensor<T,Rest...> &a) {
|
||||
if (sizeof...(Rest) == 0)
|
||||
return *a.data();
|
||||
return _norm<T,pack_prod<Rest...>::value>(a.data());
|
||||
}
|
||||
|
||||
// For generic expressions
|
||||
template<class Derived, size_t DIMS, enable_if_t_<!requires_evaluation_v<Derived>,bool> = false>
|
||||
FASTOR_INLINE typename Derived::scalar_type norm(const AbstractTensor<Derived,DIMS> &_src) {
|
||||
|
||||
const Derived &src = _src.self();
|
||||
using T = typename Derived::scalar_type;
|
||||
using V = choose_best_simd_vector_t<T>;
|
||||
T _scal=0;
|
||||
#ifdef FASTOR_AVX512_IMPL
|
||||
V omm0, omm1, omm2, omm3, omm4, omm5, omm6, omm7;
|
||||
#else
|
||||
V omm0, omm1, omm2, omm3;
|
||||
#endif
|
||||
FASTOR_INDEX i = 0;
|
||||
|
||||
// With AVX utilises all the 16 registers but hurts the performance
|
||||
// due to spill if eval has created temporary registers so only
|
||||
// activated for AVX512
|
||||
#ifdef FASTOR_AVX512_IMPL
|
||||
for (; i < ROUND_DOWN(src.size(),8*V::Size); i+=8*V::Size) {
|
||||
const auto smm0 = src.template eval<T>(i);
|
||||
const auto smm1 = src.template eval<T>(i+V::Size);
|
||||
const auto smm2 = src.template eval<T>(i+2*V::Size);
|
||||
const auto smm3 = src.template eval<T>(i+3*V::Size);
|
||||
const auto smm4 = src.template eval<T>(i+4*V::Size);
|
||||
const auto smm5 = src.template eval<T>(i+5*V::Size);
|
||||
const auto smm6 = src.template eval<T>(i+6*V::Size);
|
||||
const auto smm7 = src.template eval<T>(i+7*V::Size);
|
||||
omm0 = fmadd(smm0,smm0,omm0);
|
||||
omm1 = fmadd(smm1,smm1,omm1);
|
||||
omm2 = fmadd(smm2,smm2,omm2);
|
||||
omm3 = fmadd(smm3,smm3,omm3);
|
||||
omm4 = fmadd(smm4,smm4,omm4);
|
||||
omm5 = fmadd(smm5,smm5,omm5);
|
||||
omm6 = fmadd(smm6,smm6,omm6);
|
||||
omm7 = fmadd(smm7,smm7,omm7);
|
||||
}
|
||||
#endif
|
||||
for (; i < ROUND_DOWN(src.size(),4*V::Size); i+=4*V::Size) {
|
||||
const auto smm0 = src.template eval<T>(i);
|
||||
const auto smm1 = src.template eval<T>(i+V::Size);
|
||||
const auto smm2 = src.template eval<T>(i+2*V::Size);
|
||||
const auto smm3 = src.template eval<T>(i+3*V::Size);
|
||||
omm0 = fmadd(smm0,smm0,omm0);
|
||||
omm1 = fmadd(smm1,smm1,omm1);
|
||||
omm2 = fmadd(smm2,smm2,omm2);
|
||||
omm3 = fmadd(smm3,smm3,omm3);
|
||||
}
|
||||
for (; i < ROUND_DOWN(src.size(),2*V::Size); i+=2*V::Size) {
|
||||
const auto smm0 = src.template eval<T>(i);
|
||||
const auto smm1 = src.template eval<T>(i+V::Size);
|
||||
omm0 = fmadd(smm0,smm0,omm0);
|
||||
omm1 = fmadd(smm1,smm1,omm1);
|
||||
}
|
||||
for (; i < ROUND_DOWN(src.size(),V::Size); i+=V::Size) {
|
||||
const auto smm0 = src.template eval<T>(i);
|
||||
omm0 = fmadd(smm0,smm0,omm0);
|
||||
}
|
||||
for (; i < src.size(); ++i) {
|
||||
const auto smm0 = src.template eval_s<T>(i);
|
||||
_scal += smm0*smm0;
|
||||
}
|
||||
#ifdef FASTOR_AVX512_IMPL
|
||||
return sqrts( (omm0 + omm1 + omm2 + omm3 + omm4 + omm5 + omm6 + omm7).sum() + _scal);
|
||||
#else
|
||||
return sqrts( (omm0 + omm1 + omm2 + omm3).sum() + _scal);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
template<class Derived, size_t DIMS, enable_if_t_<requires_evaluation_v<Derived>,bool> = false>
|
||||
FASTOR_INLINE typename Derived::scalar_type norm(const AbstractTensor<Derived,DIMS> &_src) {
|
||||
const Derived &src = _src.self();
|
||||
using result_type = typename Derived::result_type;
|
||||
const result_type out(src);
|
||||
return norm(out);
|
||||
}
|
||||
|
||||
} // end of namespace Fastor
|
||||
|
||||
#endif // UNARY_NORM_OP_H
|
||||
409
noarch/include/Fastor/expressions/linalg_ops/unary_piv_op.h
Normal file
409
noarch/include/Fastor/expressions/linalg_ops/unary_piv_op.h
Normal file
@@ -0,0 +1,409 @@
|
||||
#ifndef UNARY_PIV_OP_H
|
||||
#define UNARY_PIV_OP_H
|
||||
|
||||
#include "Fastor/meta/meta.h"
|
||||
#include "Fastor/simd_vector/SIMDVector.h"
|
||||
#include "Fastor/tensor/AbstractTensor.h"
|
||||
#include "Fastor/tensor/Tensor.h"
|
||||
#include "Fastor/tensor/TensorTraits.h"
|
||||
#include "Fastor/expressions/expression_traits.h"
|
||||
#include "Fastor/expressions/linalg_ops/linalg_traits.h"
|
||||
#include "Fastor/expressions/linalg_ops/linalg_computation_types.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
namespace Fastor {
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename T, size_t M, size_t N>
|
||||
FASTOR_INLINE size_t count_swaps(const Tensor<T,M,N>& A) {
|
||||
size_t count = 0;
|
||||
for (size_t j = 0; j < M; ++j) {
|
||||
size_t max_index = j;
|
||||
for (size_t i = j; i < M; ++i) {
|
||||
// std::abs is necessary only for complex valued numbers
|
||||
if (std::abs(A(i, j)) > std::abs(A(max_index, j)))
|
||||
max_index = i;
|
||||
}
|
||||
if (j != max_index)
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
template<PivType PType = PivType::V, typename T, size_t M, size_t N,
|
||||
enable_if_t_<PType == PivType::M, bool> = false>
|
||||
FASTOR_INLINE Tensor<T,M,N> pivot(const Tensor<T,M,N>& A) {
|
||||
Tensor<size_t,M> perm;
|
||||
perm.iota();
|
||||
for (size_t j = 0; j < M; ++j) {
|
||||
size_t max_index = j;
|
||||
for (size_t i = j; i < M; ++i) {
|
||||
// std::abs is necessary only for complex valued numbers
|
||||
if (std::abs(A(i, j)) > std::abs(A(max_index, j)))
|
||||
max_index = i;
|
||||
}
|
||||
if (j != max_index)
|
||||
std::swap(perm(j), perm(max_index));
|
||||
}
|
||||
Tensor<T,M,N> P(0);
|
||||
for (size_t i = 0; i < M; ++i)
|
||||
P(i, perm(i)) = 1;
|
||||
return P;
|
||||
}
|
||||
|
||||
template<PivType PType = PivType::V, typename T, size_t M, size_t N,
|
||||
enable_if_t_<PType == PivType::V, bool> = false>
|
||||
FASTOR_INLINE Tensor<size_t,M> pivot(const Tensor<T,M,N>& A) {
|
||||
Tensor<size_t,M> perm;
|
||||
perm.iota();
|
||||
for (size_t j = 0; j < M; ++j) {
|
||||
size_t max_index = j;
|
||||
for (size_t i = j; i < M; ++i) {
|
||||
// std::abs is necessary only for complex valued numbers
|
||||
if (std::abs(A(i, j)) > std::abs(A(max_index, j)))
|
||||
max_index = i;
|
||||
}
|
||||
if (j != max_index)
|
||||
std::swap(perm(j), perm(max_index));
|
||||
}
|
||||
return perm;
|
||||
}
|
||||
|
||||
|
||||
// For generic expressions
|
||||
template<PivType PType = PivType::V, typename Derived, size_t DIM,
|
||||
enable_if_t_<PType == PivType::M && requires_evaluation_v<Derived>, bool> = false>
|
||||
FASTOR_INLINE
|
||||
Tensor<typename Derived::scalar_type,
|
||||
get_tensor_dimension_v<0,typename Derived::result_type>,
|
||||
get_tensor_dimension_v<1,typename Derived::result_type>
|
||||
>
|
||||
pivot(const AbstractTensor<Derived,DIM>& src) {
|
||||
typename Derived::result_type tmp(src.self());
|
||||
return pivot<PType>(tmp);
|
||||
}
|
||||
template<PivType PType = PivType::M, typename Derived, size_t DIM,
|
||||
enable_if_t_<PType == PivType::M && !requires_evaluation_v<Derived>, bool> = false>
|
||||
FASTOR_INLINE
|
||||
Tensor<typename Derived::scalar_type,
|
||||
get_tensor_dimension_v<0,typename Derived::result_type>,
|
||||
get_tensor_dimension_v<1,typename Derived::result_type>
|
||||
>
|
||||
pivot(const AbstractTensor<Derived,DIM>& src) {
|
||||
|
||||
static_assert(DIM==2, "TENSOR MUST BE SQUARE FOR PIVOT COMPUTATION");
|
||||
|
||||
using T = typename Derived::scalar_type;
|
||||
using result_type = typename Derived::result_type;
|
||||
constexpr FASTOR_INDEX M = get_tensor_dimension_v<0,result_type>;
|
||||
constexpr FASTOR_INDEX N = get_tensor_dimension_v<1,result_type>;
|
||||
|
||||
const Derived &A = src.self();
|
||||
|
||||
Tensor<size_t,M> perm;
|
||||
perm.iota();
|
||||
for (size_t j = 0; j < M; ++j) {
|
||||
size_t max_index = j;
|
||||
for (size_t i = j; i < M; ++i) {
|
||||
// std::abs is necessary only for complex valued numbers
|
||||
if (std::abs(A.template eval_s<T>(i, j)) > std::abs(A.template eval_s<T>(max_index, j)))
|
||||
max_index = i;
|
||||
}
|
||||
if (j != max_index)
|
||||
std::swap(perm(j), perm(max_index));
|
||||
}
|
||||
Tensor<T,M,N> P(0);
|
||||
for (size_t i = 0; i < M; ++i)
|
||||
P(i, perm(i)) = 1;
|
||||
return P;
|
||||
}
|
||||
|
||||
template<PivType PType = PivType::V, typename Derived, size_t DIM,
|
||||
enable_if_t_<PType == PivType::V && requires_evaluation_v<Derived>, bool> = false>
|
||||
FASTOR_INLINE
|
||||
Tensor<size_t,
|
||||
get_tensor_dimension_v<0,typename Derived::result_type>
|
||||
>
|
||||
pivot(const AbstractTensor<Derived,DIM>& src) {
|
||||
typename Derived::result_type tmp(src.self());
|
||||
return pivot<PType>(tmp);
|
||||
}
|
||||
template<PivType PType = PivType::M, typename Derived, size_t DIM,
|
||||
enable_if_t_<PType == PivType::V && !requires_evaluation_v<Derived>, bool> = false>
|
||||
FASTOR_INLINE
|
||||
Tensor<size_t,
|
||||
get_tensor_dimension_v<0,typename Derived::result_type>
|
||||
>
|
||||
pivot(const AbstractTensor<Derived,DIM>& src) {
|
||||
|
||||
static_assert(DIM==2, "TENSOR MUST BE SQUARE FOR PIVOT COMPUTATION");
|
||||
|
||||
using T = typename Derived::scalar_type;
|
||||
using result_type = typename Derived::result_type;
|
||||
constexpr FASTOR_INDEX M = get_tensor_dimension_v<0,result_type>;
|
||||
constexpr FASTOR_INDEX N = get_tensor_dimension_v<1,result_type>;
|
||||
|
||||
const Derived &A = src.self();
|
||||
|
||||
Tensor<size_t,M> perm;
|
||||
perm.iota();
|
||||
for (size_t j = 0; j < M; ++j) {
|
||||
size_t max_index = j;
|
||||
for (size_t i = j; i < M; ++i) {
|
||||
// std::abs is necessary only for complex valued numbers
|
||||
if (std::abs(A.template eval_s<T>(i, j)) > std::abs(A.template eval_s<T>(max_index, j)))
|
||||
max_index = i;
|
||||
}
|
||||
if (j != max_index)
|
||||
std::swap(perm(j), perm(max_index));
|
||||
}
|
||||
return perm;
|
||||
}
|
||||
|
||||
/* In place versions - Given an evaluated pivot tensor populates it
|
||||
*/
|
||||
template<typename T, size_t M, size_t N>
|
||||
FASTOR_INLINE void pivot_inplace(const Tensor<T,M,N>& A, Tensor<size_t,M>& perm) {
|
||||
perm.iota();
|
||||
for (size_t j = 0; j < M; ++j) {
|
||||
size_t max_index = j;
|
||||
for (size_t i = j; i < M; ++i) {
|
||||
// std::abs is necessary only for complex valued numbers
|
||||
if (std::abs(A(i, j)) > std::abs(A(max_index, j)))
|
||||
max_index = i;
|
||||
}
|
||||
if (j != max_index)
|
||||
std::swap(perm(j), perm(max_index));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, size_t M, size_t N>
|
||||
FASTOR_INLINE void pivot_inplace(const Tensor<T,M,N>& A, Tensor<T,M,N> &P) {
|
||||
Tensor<size_t,M> perm;
|
||||
perm.iota();
|
||||
for (size_t j = 0; j < M; ++j) {
|
||||
size_t max_index = j;
|
||||
for (size_t i = j; i < M; ++i) {
|
||||
// std::abs is necessary only for complex valued numbers
|
||||
if (std::abs(A(i, j)) > std::abs(A(max_index, j)))
|
||||
max_index = i;
|
||||
}
|
||||
if (j != max_index)
|
||||
std::swap(perm(j), perm(max_index));
|
||||
}
|
||||
P.fill(0);
|
||||
for (size_t i = 0; i < M; ++i)
|
||||
P(i, perm(i)) = 1;
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, size_t M,
|
||||
enable_if_t_<requires_evaluation_v<Derived>, bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
pivot_inplace(const AbstractTensor<Derived,DIM>& src, Tensor<size_t,M> &perm) {
|
||||
typename Derived::result_type tmp(src.self());
|
||||
pivot_inplace(tmp,perm);
|
||||
}
|
||||
template<typename Derived, size_t DIM, size_t M,
|
||||
enable_if_t_<!requires_evaluation_v<Derived>, bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
pivot_inplace(const AbstractTensor<Derived,DIM>& src, Tensor<size_t,M> &perm) {
|
||||
|
||||
static_assert(DIM==2, "TENSOR MUST BE 2D FOR PIVOT COMPUTATION");
|
||||
|
||||
using T = typename Derived::scalar_type;
|
||||
|
||||
const Derived &A = src.self();
|
||||
|
||||
perm.iota();
|
||||
for (size_t j = 0; j < M; ++j) {
|
||||
size_t max_index = j;
|
||||
for (size_t i = j; i < M; ++i) {
|
||||
// std::abs is necessary only for complex valued numbers
|
||||
if (std::abs(A.template eval_s<T>(i, j)) > std::abs(A.template eval_s<T>(max_index, j)))
|
||||
max_index = i;
|
||||
}
|
||||
if (j != max_index)
|
||||
std::swap(perm(j), perm(max_index));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename T, size_t M, size_t N,
|
||||
enable_if_t_<requires_evaluation_v<Derived>, bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
pivot_inplace(const AbstractTensor<Derived,DIM>& src, Tensor<T,M,N> &P) {
|
||||
typename Derived::result_type tmp(src.self());
|
||||
pivot_inplace(tmp,P);
|
||||
}
|
||||
template<typename Derived, size_t DIM, typename T, size_t M, size_t N,
|
||||
enable_if_t_<!requires_evaluation_v<Derived>, bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
pivot_inplace(const AbstractTensor<Derived,DIM>& src, Tensor<T,M,N> &P) {
|
||||
|
||||
static_assert(DIM==2, "TENSOR MUST BE 2D FOR PIVOT COMPUTATION");
|
||||
|
||||
const Derived &A = src.self();
|
||||
|
||||
Tensor<size_t,M> perm;
|
||||
perm.iota();
|
||||
for (size_t j = 0; j < M; ++j) {
|
||||
size_t max_index = j;
|
||||
for (size_t i = j; i < M; ++i) {
|
||||
// std::abs is necessary only for complex valued numbers
|
||||
if (std::abs(A.template eval_s<T>(i, j)) > std::abs(A.template eval_s<T>(max_index, j)))
|
||||
max_index = i;
|
||||
}
|
||||
if (j != max_index)
|
||||
std::swap(perm(j), perm(max_index));
|
||||
}
|
||||
for (size_t i = 0; i < M; ++i)
|
||||
P(i, perm(i)) = 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Apply a pivot on a tensor/matrix
|
||||
Applying pivot can only work on evaluated tensors and not expression
|
||||
as non-evaluated expression do not have storage/data
|
||||
*/
|
||||
template <typename T, size_t M, size_t N>
|
||||
FASTOR_INLINE Tensor<T,M,N> apply_pivot(const Tensor<T,M,N>& A, const Tensor<size_t,M>& P) {
|
||||
Tensor<T,M,N> copyA(A);
|
||||
for (size_t i=0; i< M; ++i) {
|
||||
if (P(i) != i) {
|
||||
std::copy_n(&A.data()[P(i)*N],N,©A.data()[i*N]);
|
||||
}
|
||||
}
|
||||
return copyA;
|
||||
}
|
||||
template <typename T, size_t M, size_t N>
|
||||
FASTOR_INLINE Tensor<T,M,N> apply_pivot(const Tensor<T,M,N>& A, const Tensor<T,M,N>& P) {
|
||||
// The output tensor is just matmul(P,A), but we are going to avoid
|
||||
// the call to matmul
|
||||
Tensor<T,M,N> copyA(A);
|
||||
for (size_t i=0; i< M; ++i) {
|
||||
auto it = std::find(&P.data()[i*N],&P.data()[i*N+N],T(1));
|
||||
size_t p = std::distance(&P.data()[i*N],it);
|
||||
// Swap row p and i
|
||||
if (p != i) {
|
||||
std::copy_n(&A.data()[p*N],N,©A.data()[i*N]);
|
||||
}
|
||||
}
|
||||
return copyA;
|
||||
}
|
||||
|
||||
template <typename T, size_t M, size_t N>
|
||||
FASTOR_INLINE void apply_pivot_inplace(Tensor<T,M,N>& A, const Tensor<size_t,M>& P) {
|
||||
Tensor<T,M,N> copyA(A);
|
||||
for (size_t i=0; i< M; ++i) {
|
||||
if (P(i) != i) {
|
||||
std::copy_n(©A.data()[P(i)*N],N,&A.data()[i*N]);
|
||||
}
|
||||
}
|
||||
}
|
||||
template <typename T, size_t M, size_t N>
|
||||
FASTOR_INLINE void apply_pivot_inplace(Tensor<T,M,N>& A, const Tensor<T,M,N>& P) {
|
||||
// The output tensor is just matmul(P,A), but we are going to avoid
|
||||
// the call to matmul
|
||||
Tensor<T,M,N> copyA(A);
|
||||
for (size_t i=0; i< M; ++i) {
|
||||
auto it = std::find(&P.data()[i*N],&P.data()[i*N+N],T(1));
|
||||
size_t p = std::distance(&P.data()[i*N],it);
|
||||
// Swap row p and i
|
||||
if (p != i) {
|
||||
std::copy_n(©A.data()[p*N],N,&A.data()[i*N]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Reconstructing the matrix back from a pivoted factorisation
|
||||
Reconstructs from LU/QR/QL etc
|
||||
*/
|
||||
template <typename T, size_t M, size_t N>
|
||||
FASTOR_INLINE Tensor<T,M,N> reconstruct(const Tensor<T,M,N>& L, Tensor<T,M,N>& U) {
|
||||
return matmul(L,U);
|
||||
}
|
||||
|
||||
/* Reconstructing the matrix back from a pivoted factorisation
|
||||
Reconstructs from an PLU decomposition given P, L, U where P is
|
||||
integral permutation vector
|
||||
A = {P}^(-1)*L*U
|
||||
*/
|
||||
template <typename T, size_t M, size_t N>
|
||||
FASTOR_INLINE Tensor<T,M,N> reconstruct(const Tensor<T,M,N>& L, Tensor<T,M,N>& U, const Tensor<size_t,M>& P) {
|
||||
Tensor<T,M,N> A = matmul(L,U);
|
||||
Tensor<T,M,N> copyA(A);
|
||||
for (size_t i=0; i< M; ++i) {
|
||||
if (P(i) != i) {
|
||||
std::copy_n(©A.data()[i*N],N,&A.data()[P(i)*N]);
|
||||
}
|
||||
}
|
||||
return A;
|
||||
}
|
||||
|
||||
/* Reconstructing the matrix back from a pivoted factorisation
|
||||
Reconstructs from an PLU decomposition given P, L, U
|
||||
A = {P}^(-1)*L*U
|
||||
*/
|
||||
template <typename T, size_t M, size_t N>
|
||||
FASTOR_INLINE Tensor<T,M,N> reconstruct(const Tensor<T,M,N>& L, Tensor<T,M,N>& U, const Tensor<T,M,N>& P) {
|
||||
// To avoid computing the inverse of P in {P}^(-1)*L*U
|
||||
Tensor<T,M,N> A = matmul(L,U);
|
||||
Tensor<T,M,N> copyA(A);
|
||||
for (size_t i=0; i< M; ++i) {
|
||||
auto it = std::find(&P.data()[i*N],&P.data()[i*N+N],T(1));
|
||||
size_t p = std::distance(&P.data()[i*N],it);
|
||||
if (p != i) {
|
||||
std::copy_n(©A.data()[i*N],N,&A.data()[p*N]);
|
||||
}
|
||||
}
|
||||
return A;
|
||||
}
|
||||
|
||||
|
||||
/* Reconstructing the matrix back from a pivot only
|
||||
*/
|
||||
template <typename T, size_t M, size_t N>
|
||||
FASTOR_INLINE Tensor<T,M,N> reconstruct(const Tensor<T,M,N>& A, const Tensor<size_t,M>& P) {
|
||||
Tensor<T,M,N> copyA(A);
|
||||
for (size_t i=0; i< M; ++i) {
|
||||
if (P(i) != i) {
|
||||
std::copy_n(&A.data()[i*N],N,©A.data()[P(i)*N]);
|
||||
}
|
||||
}
|
||||
return copyA;
|
||||
}
|
||||
|
||||
/* Reconstructing the matrix back from a pivot only - column-wise or rather post multiplication
|
||||
used for computing inverse
|
||||
*/
|
||||
template <typename T, size_t M, size_t N>
|
||||
FASTOR_INLINE Tensor<T,M,N> reconstruct_colwise(const Tensor<T,M,N>& A, const Tensor<size_t,M>& P) {
|
||||
Tensor<T,M,N> copyA(A);
|
||||
for (size_t i=0; i< M; ++i) {
|
||||
if (P(i) != i) {
|
||||
copyA(all,P(i)) = A(all,i);
|
||||
}
|
||||
}
|
||||
return copyA;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // end of namespace Fastor
|
||||
|
||||
|
||||
#endif // UNARY_PIV_OP_H
|
||||
198
noarch/include/Fastor/expressions/linalg_ops/unary_qr_op.h
Normal file
198
noarch/include/Fastor/expressions/linalg_ops/unary_qr_op.h
Normal file
@@ -0,0 +1,198 @@
|
||||
#ifndef UNARY_QR_OP_H
|
||||
#define UNARY_QR_OP_H
|
||||
|
||||
#include "Fastor/meta/meta.h"
|
||||
#include "Fastor/simd_vector/SIMDVector.h"
|
||||
#include "Fastor/tensor/AbstractTensor.h"
|
||||
#include "Fastor/tensor/Aliasing.h"
|
||||
#include "Fastor/tensor/Tensor.h"
|
||||
#include "Fastor/tensor/Ranges.h"
|
||||
#include "Fastor/tensor/TensorTraits.h"
|
||||
#include "Fastor/expressions/expression_traits.h"
|
||||
#include "Fastor/expressions/linalg_ops/linalg_computation_types.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_piv_op.h"
|
||||
|
||||
|
||||
namespace Fastor {
|
||||
|
||||
namespace internal {
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
/* Modified Gram-Schmidt Row-wise [MGSR] QR factorisation
|
||||
that provides numerical stabilitiy.
|
||||
This simple implementation provided for now is just for
|
||||
convenience and not tuned for performance. Given that Tensor
|
||||
types have fixed dimensions the compiler typically does a good
|
||||
job
|
||||
|
||||
See:
|
||||
Thomas Jakobs et. al. "Performance and energy consumption of the SIMD
|
||||
Gram–Schmidt process for vector orthogonalization"
|
||||
for implementation details including AVX/AVX512 implementation. A straight-
|
||||
forward explicit vectorisation of this does not yield good performance for
|
||||
row-major tensors, as step 1-2 need gather instructions and step 3-4 require
|
||||
dynamic masking to gain performance
|
||||
*/
|
||||
template<typename T, size_t M, size_t N>
|
||||
FASTOR_INLINE void qr_mgsr_dispatcher(const Tensor<T,M,N> &A0, Tensor<T,M,N>& Q, Tensor<T,M,N>& R) {
|
||||
|
||||
// Copy incoming tensor as we will modify A
|
||||
Tensor<T,M,N> A(A0);
|
||||
// Zero out
|
||||
R.fill(0);
|
||||
|
||||
for (size_t i=0; i< N; ++i) {
|
||||
// step 1
|
||||
T R_ii = 0;
|
||||
for (size_t k=0; k< M; ++k) {
|
||||
R_ii += A(k,i)*A(k,i);
|
||||
}
|
||||
R_ii = sqrts(R_ii);
|
||||
R(i,i) = R_ii;
|
||||
|
||||
// step 2
|
||||
for (size_t k=0; k< M; ++k) {
|
||||
Q(k,i) = A(k,i) / R_ii;
|
||||
}
|
||||
|
||||
// step 3
|
||||
for (size_t k=0; k< M; ++k) {
|
||||
for (size_t j=i+1; j<N; ++j) {
|
||||
R(i,j) += Q(k,i) * A(k,j);
|
||||
}
|
||||
}
|
||||
|
||||
// step 4
|
||||
for (size_t k=0; k< M; ++k) {
|
||||
for (size_t j=i+1; j<N; ++j) {
|
||||
A(k,j) -= Q(k,i) * R(i,j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
} // internal
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
// QR MGSR - no pivot
|
||||
template<QRCompType QRType = QRCompType::MGSR, typename Expr, size_t DIM0, typename T, size_t M, size_t N,
|
||||
enable_if_t_<is_tensor_v<Expr> && QRType == QRCompType::MGSR,bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
qr(const AbstractTensor<Expr,DIM0> &src, Tensor<T,M,N> &Q, Tensor<T,N,M> &R) {
|
||||
internal::qr_mgsr_dispatcher(src.self(),Q,R);
|
||||
}
|
||||
|
||||
template<QRCompType QRType = QRCompType::MGSR, typename Expr, size_t DIM0, typename T, size_t M, size_t N,
|
||||
enable_if_t_<!is_tensor_v<Expr> && QRType == QRCompType::MGSR,bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
qr(const AbstractTensor<Expr,DIM0> &src, Tensor<T,M,N> &Q, Tensor<T,N,M> &R) {
|
||||
Tensor<T,M,N> A(src.self());
|
||||
internal::qr_mgsr_dispatcher(A,Q,R);
|
||||
}
|
||||
|
||||
// QR MGSR - vector pivot
|
||||
template<QRCompType QRType = QRCompType::MGSR, typename Expr, size_t DIM0, typename T, size_t M, size_t N,
|
||||
enable_if_t_<is_tensor_v<Expr> && QRType == QRCompType::MGSRPiv,bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
qr(const AbstractTensor<Expr,DIM0> &src, Tensor<T,M,N> &Q, Tensor<T,N,M> &R, Tensor<size_t,M> &P) {
|
||||
pivot_inplace(src.self(),P);
|
||||
auto A(apply_pivot(src.self(),P));
|
||||
internal::qr_mgsr_dispatcher(A,Q,R);
|
||||
}
|
||||
|
||||
template<QRCompType QRType = QRCompType::MGSR, typename Expr, size_t DIM0, typename T, size_t M, size_t N,
|
||||
enable_if_t_<!is_tensor_v<Expr> && QRType == QRCompType::MGSRPiv,bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
qr(const AbstractTensor<Expr,DIM0> &src, Tensor<T,M,N> &Q, Tensor<T,N,M> &R, Tensor<size_t,M> &P) {
|
||||
typename Expr::result_type A(src.self());
|
||||
pivot_inplace(A,P);
|
||||
// Modify A as A is a temporary anyway
|
||||
apply_pivot_inplace(A,P);
|
||||
internal::qr_mgsr_dispatcher(A,Q,R);
|
||||
}
|
||||
|
||||
// QR MGSR - matrix pivot
|
||||
template<QRCompType QRType = QRCompType::MGSR, typename Expr, size_t DIM0, typename T, size_t M, size_t N,
|
||||
enable_if_t_<is_tensor_v<Expr> && QRType == QRCompType::MGSRPiv,bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
qr(const AbstractTensor<Expr,DIM0> &src, Tensor<T,M,N> &Q, Tensor<T,N,M> &R, Tensor<T,M,N> &P) {
|
||||
pivot_inplace(src.self(),P);
|
||||
auto A(apply_pivot(src.self(),P));
|
||||
internal::qr_mgsr_dispatcher(A,Q,R);
|
||||
}
|
||||
|
||||
template<QRCompType QRType = QRCompType::MGSR, typename Expr, size_t DIM0, typename T, size_t M, size_t N,
|
||||
enable_if_t_<!is_tensor_v<Expr> && QRType == QRCompType::MGSRPiv,bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
qr(const AbstractTensor<Expr,DIM0> &src, Tensor<T,M,N> &Q, Tensor<T,N,M> &R, Tensor<T,M,N> &P) {
|
||||
typename Expr::result_type A(src.self());
|
||||
pivot_inplace(A,P);
|
||||
// Modify A as A is a temporary anyway
|
||||
apply_pivot_inplace(A,P);
|
||||
internal::qr_mgsr_dispatcher(A,Q,R);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
// QR householder reflections etc
|
||||
template<QRCompType QRType = QRCompType::MGSR, typename Expr, size_t DIM0, typename T, size_t M, size_t N,
|
||||
enable_if_t_<QRType != QRCompType::MGSR,bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
qr(const AbstractTensor<Expr,DIM0> &src, Tensor<T,M,N> &Q, Tensor<T,N,M> &R) {
|
||||
static_assert(QRType==QRCompType::MGSR, "QR FACTORISATION USING HOUSEHOLDER REFLECTIONS IS NOT IMPLEMENETED YET");
|
||||
}
|
||||
template<QRCompType QRType = QRCompType::MGSR, typename Expr, size_t DIM0, typename T, size_t M, size_t N,
|
||||
enable_if_t_<QRType != QRCompType::MGSRPiv,bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
qr(const AbstractTensor<Expr,DIM0> &src, Tensor<T,M,N> &Q, Tensor<T,N,M> &R, Tensor<size_t,M> &P) {
|
||||
static_assert(QRType==QRCompType::MGSR, "QR FACTORISATION USING HOUSEHOLDER REFLECTIONS IS NOT IMPLEMENETED YET");
|
||||
}
|
||||
template<QRCompType QRType = QRCompType::MGSR, typename Expr, size_t DIM0, typename T, size_t M, size_t N,
|
||||
enable_if_t_<QRType != QRCompType::MGSRPiv,bool> = false>
|
||||
FASTOR_INLINE
|
||||
void
|
||||
qr(const AbstractTensor<Expr,DIM0> &src, Tensor<T,M,N> &Q, Tensor<T,N,M> &R, Tensor<T,M,N> &P) {
|
||||
static_assert(QRType==QRCompType::MGSR, "QR FACTORISATION USING HOUSEHOLDER REFLECTIONS IS NOT IMPLEMENETED YET");
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Computing determinant using QR
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
template<DetCompType DetType = DetCompType::Simple, typename T, size_t M,
|
||||
enable_if_t_<DetType == DetCompType::QR,bool> = false>
|
||||
FASTOR_INLINE T determinant(const Tensor<T,M,M> &a) {
|
||||
Tensor<T,M,M> Q, R;
|
||||
qr(a, Q, R);
|
||||
return product(diag(R));
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
|
||||
|
||||
} // end of namespace Fastor
|
||||
|
||||
|
||||
#endif // UNARY_QR_OP_H
|
||||
163
noarch/include/Fastor/expressions/linalg_ops/unary_svd_op.h
Normal file
163
noarch/include/Fastor/expressions/linalg_ops/unary_svd_op.h
Normal file
@@ -0,0 +1,163 @@
|
||||
#ifndef UNARY_SVD_OP_H
|
||||
#define UNARY_SVD_OP_H
|
||||
|
||||
#include "Fastor/meta/meta.h"
|
||||
#include "Fastor/backend/inner.h"
|
||||
#include "Fastor/backend/lufact.h"
|
||||
#include "Fastor/simd_vector/SIMDVector.h"
|
||||
#include "Fastor/tensor/AbstractTensor.h"
|
||||
#include "Fastor/tensor/Aliasing.h"
|
||||
#include "Fastor/tensor/Tensor.h"
|
||||
#include "Fastor/tensor/TensorTraits.h"
|
||||
#include "Fastor/expressions/expression_traits.h"
|
||||
#include "Fastor/expressions/linalg_ops/linalg_computation_types.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_det_op.h"
|
||||
#include "Fastor/expressions/linalg_ops/unary_trans_op.h"
|
||||
|
||||
|
||||
namespace Fastor {
|
||||
|
||||
// SVD
|
||||
template<typename T, size_t M, enable_if_t_<M==2, bool> = false >
|
||||
FASTOR_INLINE void svd(const Tensor<T,M,M> &A, Tensor<T,M,M> &U, Tensor<T,M,M> &S, Tensor<T,M,M> &V) {
|
||||
|
||||
constexpr T Epsilon_v = std::numeric_limits<T>::epsilon();
|
||||
|
||||
const T f00 = A(0, 0);
|
||||
const T f01 = A(0, 1);
|
||||
const T f10 = A(1, 0);
|
||||
const T f11 = A(1, 1);
|
||||
|
||||
// If matrix is diagonal, SVD is trivial
|
||||
if (std::abs(f01 - f10) < Epsilon_v && std::abs(f01) < Epsilon_v)
|
||||
{
|
||||
// Compute U
|
||||
U(0,0) = f00 < 0 ? -1. : 1.;
|
||||
U(0,1) = 0.;
|
||||
U(1,0) = 0.;
|
||||
U(1,1) = f11 < 0. ? -1. : 1.;
|
||||
|
||||
// Compute S
|
||||
S(0,0) = std::abs(f00);
|
||||
S(0,1) = 0;
|
||||
S(1,0) = 0;
|
||||
S(1,1) = std::abs(f11);
|
||||
|
||||
// Compute V
|
||||
V.eye2();
|
||||
}
|
||||
// Otherwise, we need to compute A^T*A
|
||||
else
|
||||
{
|
||||
T j = f00 * f00 + f01 * f01;
|
||||
T k = f10 * f10 + f11 * f11;
|
||||
T v_c = f00 * f10 + f01 * f11;
|
||||
// Check to see if A^T*A is diagonal
|
||||
if (std::abs(v_c) < Epsilon_v)
|
||||
{
|
||||
// Compute S
|
||||
T s1 = std::sqrt(j);
|
||||
T s2 = std::abs(j - k) < Epsilon_v ? s1 : std::sqrt(k);
|
||||
S(0,0) = s1;
|
||||
S(0,1) = 0;
|
||||
S(1,0) = 0;
|
||||
S(1,1) = s2;
|
||||
|
||||
// Compute U
|
||||
U.eye2();
|
||||
|
||||
// Compute V
|
||||
V(0,0) = f00 / s1;
|
||||
V(0,1) = f10 / s2;
|
||||
V(1,0) = f01 / s1;
|
||||
V(1,1) = f11 / s2;
|
||||
}
|
||||
// Otherwise, solve quadratic equation for eigenvalues
|
||||
else
|
||||
{
|
||||
T jmk = j - k;
|
||||
T jpk = j + k;
|
||||
T root = std::sqrt(jmk * jmk + 4. * v_c * v_c);
|
||||
T eig1 = (jpk + root) * 0.5;
|
||||
T eig2 = (jpk - root) * 0.5;
|
||||
|
||||
// Compute S
|
||||
T s1 = std::sqrt(eig1);
|
||||
T s2 = std::abs(root) < Epsilon_v ? s1 : ( eig2 > 0 ? std::sqrt(eig2) : Epsilon_v);
|
||||
S(0,0) = s1;
|
||||
S(0,1) = 0;
|
||||
S(1,0) = 0;
|
||||
S(1,1) = s2;
|
||||
|
||||
// Compute U - use eigenvectors of A^T*A as U
|
||||
T v_s = eig1 - j;
|
||||
T len = std::max(std::sqrt(v_s * v_s + v_c * v_c), Epsilon_v);
|
||||
v_c /= len;
|
||||
v_s /= len;
|
||||
|
||||
U(0,0) = v_c;
|
||||
U(0,1) = -v_s;
|
||||
U(1,0) = v_s;
|
||||
U(1,1) = v_c;
|
||||
|
||||
// Compute V - as A * U / s
|
||||
const T cc = (f00 * v_c + f10 * v_s) / s1;
|
||||
const T cs = (f01 * v_c + f11 * v_s) / s1;
|
||||
if (std::abs(s2) > Epsilon_v)
|
||||
{
|
||||
V(0,0) = cc;
|
||||
V(0,1) = (f10* v_c - f00 * v_s) / s2;
|
||||
V(1,0) = cs;
|
||||
V(1,1) = (f11 * v_c - f01 * v_s) / s2;
|
||||
}
|
||||
else
|
||||
{
|
||||
V(0,0) = cc;
|
||||
V(0,1) = cs;
|
||||
V(1,0) = cs;
|
||||
V(1,1) = -cc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Signed SVD
|
||||
template<typename T, size_t M>
|
||||
FASTOR_INLINE void ssvd(const Tensor<T,M,M> &A, Tensor<T,M,M> &U, Tensor<T,M,M> &S, Tensor<T,M,M> &V) {
|
||||
|
||||
// Same as above but avoiding the L matrix
|
||||
svd(A, U, S, V);
|
||||
|
||||
// See where to pull the reflection out of
|
||||
const T detU = determinant(U);
|
||||
const T detV = determinant(V);
|
||||
|
||||
if (detU >= 0 && detV >= 0)
|
||||
{
|
||||
// No reflection svd == svd_rv, return
|
||||
return;
|
||||
}
|
||||
|
||||
Tensor<T, M, M> L = matmul(U, transpose(V));
|
||||
const T lastColumn = determinant(L);
|
||||
|
||||
if (detU < 0 && detV > 0)
|
||||
{
|
||||
U(all, M - 1) *= lastColumn;
|
||||
}
|
||||
else if (detU > 0 && detV < 0)
|
||||
{
|
||||
V(all, M - 1) *= lastColumn;
|
||||
}
|
||||
|
||||
// Push the reflection to the diagonal
|
||||
S(M - 1, M - 1) *= lastColumn;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------------//
|
||||
|
||||
} // end of namespace Fastor
|
||||
|
||||
|
||||
#endif // UNARY_SVD_OP_H
|
||||
@@ -0,0 +1,75 @@
|
||||
#ifndef UNARY_TRACE_OP_H
|
||||
#define UNARY_TRACE_OP_H
|
||||
|
||||
#include "Fastor/meta/meta.h"
|
||||
#include "Fastor/simd_vector/SIMDVector.h"
|
||||
#include "Fastor/tensor/AbstractTensor.h"
|
||||
#include "Fastor/tensor/TensorTraits.h"
|
||||
#include "Fastor/expressions/expression_traits.h"
|
||||
#include "Fastor/expressions/linalg_ops/linalg_traits.h"
|
||||
|
||||
namespace Fastor {
|
||||
|
||||
// For tensors
|
||||
template<typename T, size_t I>
|
||||
FASTOR_INLINE T trace(const Tensor<T,I,I> &a) {
|
||||
return _trace<T,I,I>(static_cast<const T *>(a.data()));
|
||||
}
|
||||
|
||||
// For high order tensors
|
||||
template<typename T, size_t ... Rest, typename std::enable_if<sizeof...(Rest)>=3,bool>::type=0>
|
||||
FASTOR_INLINE
|
||||
typename last_matrix_extracter<Tensor<T,Rest...>, typename std_ext::make_index_sequence<sizeof...(Rest)-2>::type>::type
|
||||
trace(const Tensor<T,Rest...> &a) {
|
||||
|
||||
using OutTensor = typename last_matrix_extracter<Tensor<T,Rest...>,
|
||||
typename std_ext::make_index_sequence<sizeof...(Rest)-2>::type>::type;
|
||||
constexpr size_t remaining_product = last_matrix_extracter<Tensor<T,Rest...>,
|
||||
typename std_ext::make_index_sequence<sizeof...(Rest)-2>::type>::remaining_product;
|
||||
|
||||
constexpr size_t I = get_value<sizeof...(Rest)-1,Rest...>::value;
|
||||
constexpr size_t J = get_value<sizeof...(Rest),Rest...>::value;
|
||||
static_assert(I==J,"THE LAST TWO DIMENSIONS OF TENSOR MUST BE THE SAME");
|
||||
|
||||
OutTensor out;
|
||||
T *a_data = a.data();
|
||||
T *out_data = out.data();
|
||||
|
||||
for (size_t i=0; i<remaining_product; ++i) {
|
||||
out_data[i] = _trace<T,J,J>(static_cast<const T *>(a_data+i*J*J));
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// Trace for generic expressions
|
||||
template<class Derived, size_t DIMS, enable_if_t_<!requires_evaluation_v<Derived>,bool> = false>
|
||||
FASTOR_INLINE typename Derived::scalar_type trace(const AbstractTensor<Derived,DIMS> &_src) {
|
||||
|
||||
const Derived& src = _src.self();
|
||||
using T = typename Derived::scalar_type;
|
||||
using tensor_type = typename Derived::result_type;
|
||||
constexpr size_t M = get_tensor_dimensions<tensor_type>::dims[0];
|
||||
constexpr size_t N = get_tensor_dimensions<tensor_type>::dims[1];
|
||||
static_assert(DIMS==2,"TENSOR EXPRESSION SHOULD BE UNIFORM (SQUARE)");
|
||||
static_assert(M==N,"TENSOR EXPRESSION SHOULD BE TWO DIMENSIONAL");
|
||||
|
||||
FASTOR_INDEX i;
|
||||
T _scal=0;
|
||||
for (i = 0; i < M; ++i) {
|
||||
_scal += src.template eval_s<T>(i*(N+1));
|
||||
}
|
||||
return _scal;
|
||||
}
|
||||
|
||||
template<class Derived, size_t DIMS, enable_if_t_<requires_evaluation_v<Derived>,bool> = false>
|
||||
FASTOR_INLINE typename Derived::scalar_type trace(const AbstractTensor<Derived,DIMS> &_src) {
|
||||
|
||||
using result_type = typename Derived::result_type;
|
||||
const result_type out(_src.self());
|
||||
return trace(out);
|
||||
}
|
||||
|
||||
} // end of namespace Fastor
|
||||
|
||||
#endif // UNARY_TRACE_OP_H
|
||||
173
noarch/include/Fastor/expressions/linalg_ops/unary_trans_op.h
Normal file
173
noarch/include/Fastor/expressions/linalg_ops/unary_trans_op.h
Normal file
@@ -0,0 +1,173 @@
|
||||
#ifndef UNARY_TRANS_OP_H
|
||||
#define UNARY_TRANS_OP_H
|
||||
|
||||
#include "Fastor/meta/meta.h"
|
||||
#include "Fastor/backend/transpose/transpose.h"
|
||||
#include "Fastor/simd_vector/SIMDVector.h"
|
||||
#include "Fastor/tensor/AbstractTensor.h"
|
||||
#include "Fastor/tensor/Aliasing.h"
|
||||
#include "Fastor/tensor/Tensor.h"
|
||||
#include "Fastor/tensor/Ranges.h"
|
||||
#include "Fastor/tensor/TensorTraits.h"
|
||||
#include "Fastor/expressions/expression_traits.h"
|
||||
|
||||
|
||||
namespace Fastor {
|
||||
|
||||
template<typename Expr, size_t DIM0>
|
||||
struct UnaryTransOp: public AbstractTensor<UnaryTransOp<Expr, DIM0>,DIM0> {
|
||||
using expr_type = expression_t<Expr>;
|
||||
static constexpr FASTOR_INDEX M = get_tensor_dimension_v<0,typename Expr::result_type>;
|
||||
static constexpr FASTOR_INDEX N = get_tensor_dimension_v<1,typename Expr::result_type>;
|
||||
static constexpr FASTOR_INDEX Dimension = DIM0;
|
||||
static constexpr FASTOR_INDEX rank() {return DIM0;}
|
||||
using scalar_type = typename scalar_type_finder<UnaryTransOp<Expr, DIM0>>::type;
|
||||
using simd_vector_type = typename Expr::simd_vector_type;
|
||||
using simd_abi_type = typename simd_vector_type::abi_type;
|
||||
using result_type = Tensor<scalar_type,N,M>;
|
||||
|
||||
FASTOR_INLINE UnaryTransOp(expr_type inexpr) : _expr(inexpr) {
|
||||
}
|
||||
|
||||
FASTOR_INLINE FASTOR_INDEX size() const {return M*N;}
|
||||
FASTOR_INLINE FASTOR_INDEX dimension(FASTOR_INDEX i) const {return i == 0 ? N : M;}
|
||||
|
||||
constexpr FASTOR_INLINE expr_type expr() const {return _expr;}
|
||||
|
||||
private:
|
||||
expr_type _expr;
|
||||
};
|
||||
|
||||
|
||||
/* Tensor transpose returning a tensor expression */
|
||||
template<typename Expr, size_t DIM0>
|
||||
FASTOR_INLINE UnaryTransOp<Expr, DIM0>
|
||||
trans(const AbstractTensor<Expr,DIM0> &src) {
|
||||
return UnaryTransOp<Expr, DIM0>(src.self());
|
||||
}
|
||||
|
||||
|
||||
/* Tensor transpose immediately returning a tensor */
|
||||
template<typename T, size_t I, size_t J>
|
||||
FASTOR_INLINE Tensor<T,J,I> transpose(const Tensor<T,I,J> &a) {
|
||||
Tensor<T,J,I> out;
|
||||
_transpose<T,I,J>(a.data(),out.data());
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Tensor transpose for higher order tensors immediately returning a tensor */
|
||||
template<typename T, size_t ... Rest, typename std::enable_if<sizeof...(Rest)>=3,bool>::type=0>
|
||||
FASTOR_INLINE Tensor<T,Rest...>
|
||||
transpose(const Tensor<T,Rest...> &a) {
|
||||
|
||||
constexpr size_t remaining_product = last_matrix_extracter<Tensor<T,Rest...>,
|
||||
typename std_ext::make_index_sequence<sizeof...(Rest)-2>::type>::remaining_product;
|
||||
|
||||
constexpr size_t I = get_value<sizeof...(Rest)-1,Rest...>::value;
|
||||
constexpr size_t J = get_value<sizeof...(Rest),Rest...>::value;
|
||||
static_assert(I==J,"THE LAST TWO DIMENSIONS OF TENSOR MUST BE THE SAME");
|
||||
|
||||
Tensor<T,Rest...> out;
|
||||
T *a_data = a.data();
|
||||
T *out_data = out.data();
|
||||
|
||||
for (size_t i=0; i<remaining_product; ++i) {
|
||||
_transpose<T,J,J>(a_data+i*J*J,out_data+i*J*J);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// Transpose for generic expressions
|
||||
template<typename Expr, size_t DIM0>
|
||||
FASTOR_INLINE
|
||||
Tensor<
|
||||
typename scalar_type_finder<Expr>::type,
|
||||
get_tensor_dimension_v<1,typename Expr::result_type>,
|
||||
get_tensor_dimension_v<0,typename Expr::result_type>>
|
||||
transpose(const AbstractTensor<Expr,DIM0> &src) {
|
||||
// If we are here Expr is already an expression
|
||||
using result_type = typename Expr::result_type;
|
||||
const result_type tmp(src.self());
|
||||
return transpose(tmp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// assignments
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign(AbstractTensor<Derived,DIM> &dst, const UnaryTransOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
using T = typename UnaryTransOp<Expr, OtherDIM>::scalar_type;
|
||||
static constexpr size_t M = UnaryTransOp<Expr, OtherDIM>::M;
|
||||
static constexpr size_t N = UnaryTransOp<Expr, OtherDIM>::N;
|
||||
_transpose<T,M,N>(tmp.data(),dst.self().data());
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_add(AbstractTensor<Derived,DIM> &dst, const UnaryTransOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
// no copies if expr is a tensor
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
// one copy for the inverse
|
||||
using T = typename UnaryTransOp<Expr, OtherDIM>::scalar_type;
|
||||
using result_t = typename UnaryTransOp<Expr, OtherDIM>::result_type;
|
||||
result_t tmp_trans;
|
||||
static constexpr size_t M = UnaryTransOp<Expr, OtherDIM>::M;
|
||||
static constexpr size_t N = UnaryTransOp<Expr, OtherDIM>::N;
|
||||
_transpose<T,M,N>(tmp.data(),tmp_trans.data());
|
||||
trivial_assign_add(dst.self(),tmp_trans);
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_sub(AbstractTensor<Derived,DIM> &dst, const UnaryTransOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
// no copies if expr is a tensor
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
// one copy for the inverse
|
||||
using T = typename UnaryTransOp<Expr, OtherDIM>::scalar_type;
|
||||
using result_t = typename UnaryTransOp<Expr, OtherDIM>::result_type;
|
||||
result_t tmp_trans;
|
||||
static constexpr size_t M = UnaryTransOp<Expr, OtherDIM>::M;
|
||||
static constexpr size_t N = UnaryTransOp<Expr, OtherDIM>::N;
|
||||
_transpose<T,M,N>(tmp.data(),tmp_trans.data());
|
||||
trivial_assign_sub(dst.self(),tmp_trans);
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_mul(AbstractTensor<Derived,DIM> &dst, const UnaryTransOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
// no copies if expr is a tensor
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
// one copy for the inverse
|
||||
using T = typename UnaryTransOp<Expr, OtherDIM>::scalar_type;
|
||||
using result_t = typename UnaryTransOp<Expr, OtherDIM>::result_type;
|
||||
result_t tmp_trans;
|
||||
static constexpr size_t M = UnaryTransOp<Expr, OtherDIM>::M;
|
||||
static constexpr size_t N = UnaryTransOp<Expr, OtherDIM>::N;
|
||||
_transpose<T,M,N>(tmp.data(),tmp_trans.data());
|
||||
trivial_assign_mul(dst.self(),tmp_trans);
|
||||
}
|
||||
|
||||
template<typename Derived, size_t DIM, typename Expr, size_t OtherDIM>
|
||||
FASTOR_INLINE void assign_div(AbstractTensor<Derived,DIM> &dst, const UnaryTransOp<Expr, OtherDIM> &src) {
|
||||
using result_type = typename Expr::result_type;
|
||||
// no copies if expr is a tensor
|
||||
const result_type& tmp = evaluate(src.expr().self());
|
||||
// one copy for the inverse
|
||||
using T = typename UnaryTransOp<Expr, OtherDIM>::scalar_type;
|
||||
using result_t = typename UnaryTransOp<Expr, OtherDIM>::result_type;
|
||||
result_t tmp_trans;
|
||||
static constexpr size_t M = UnaryTransOp<Expr, OtherDIM>::M;
|
||||
static constexpr size_t N = UnaryTransOp<Expr, OtherDIM>::N;
|
||||
_transpose<T,M,N>(tmp.data(),tmp_trans.data());
|
||||
trivial_assign_div(dst.self(),tmp_trans);
|
||||
}
|
||||
|
||||
|
||||
} // end of namespace Fastor
|
||||
|
||||
|
||||
#endif // UNARY_TRANS_OP_H
|
||||
Reference in New Issue
Block a user