Add Fastor library

This commit is contained in:
Bassem Girgis
2025-03-22 01:17:52 -05:00
parent 5546e086f6
commit 4dd5939693
132 changed files with 55086 additions and 0 deletions

View File

@@ -0,0 +1,233 @@
#ifndef ABSTRACT_CONTRACTION_H
#define ABSTRACT_CONTRACTION_H
#include "Fastor/tensor/Tensor.h"
#include "Fastor/tensor/TensorTraits.h"
#include "Fastor/tensor_algebra/indicial.h"
#include "Fastor/meta/opmin_meta.h"
#include "Fastor/expressions/expression_traits.h"
#include "Fastor/expressions/linalg_ops/linalg_traits.h"
#include "Fastor/tensor_algebra/contraction.h"
#include "Fastor/tensor_algebra/network_contraction.h"
#include "Fastor/tensor_algebra/network_contraction_no_opmin.h"
namespace Fastor {
#if FASTOR_CXX_VERSION >= 2014
// The following set of functions implement by-pair as well as
// network contraction/einsum for infinite number of expressions
// the expressions are always evaluated so no aliasing occurs
// and the contractions are forwarded to their tensor counterparts
// which perform operation minimiation and are optimised for performance
// Single expression - contraction
//-------------------------------------------------------------------------------------------------
template<class Index_I, typename Derived0, size_t DIM0,
enable_if_t_<!is_tensor_v<Derived0>,bool> = false>
FASTOR_INLINE
decltype(auto)
contraction(const AbstractTensor<Derived0,DIM0> &a)
{
typename Derived0::result_type res_a(a);
return extractor_contract_1<Index_I>::contract_impl(res_a);
}
//-------------------------------------------------------------------------------------------------
// Single expression - einsum
//-------------------------------------------------------------------------------------------------
template<class Index_I, typename Derived0, size_t DIM0,
enable_if_t_<!is_tensor_v<Derived0>,bool> = false>
FASTOR_INLINE
decltype(auto)
einsum(const AbstractTensor<Derived0,DIM0> &a)
{
typename Derived0::result_type res_a(a);
return extractor_contract_1<Index_I>::contract_impl(res_a);
}
//-------------------------------------------------------------------------------------------------
// By pair expressions - contraction
//-------------------------------------------------------------------------------------------------
template<class Index_I, class Index_J, typename Derived0, typename Derived1, size_t DIM0, size_t DIM1,
enable_if_t_<!is_tensor_v<Derived0> && !is_tensor_v<Derived1>,bool> = false>
FASTOR_INLINE
decltype(auto)
contraction(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b)
{
typename Derived0::result_type res_a(a);
typename Derived1::result_type res_b(b);
return extractor_contract_2<Index_I,Index_J>::contract_impl(res_a,res_b);
}
template<class Index_I, class Index_J, typename Derived0, typename Derived1, size_t DIM0, size_t DIM1,
enable_if_t_<is_tensor_v<Derived0> && !is_tensor_v<Derived1>,bool> = false>
FASTOR_INLINE
decltype(auto)
contraction(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b)
{
typename Derived1::result_type res_b(b);
return extractor_contract_2<Index_I,Index_J>::contract_impl(a,res_b);
}
template<class Index_I, class Index_J, typename Derived0, typename Derived1, size_t DIM0, size_t DIM1,
enable_if_t_<!is_tensor_v<Derived0> && is_tensor_v<Derived1>,bool> = false>
FASTOR_INLINE
decltype(auto)
contraction(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b)
{
typename Derived0::result_type res_a(a);
return extractor_contract_2<Index_I,Index_J>::contract_impl(res_a,b);
}
//-------------------------------------------------------------------------------------------------
// By pair expressions - einsum
//-------------------------------------------------------------------------------------------------
template<class Index_I, class Index_J, typename Derived0, typename Derived1, size_t DIM0, size_t DIM1,
enable_if_t_<!is_tensor_v<Derived0> && !is_tensor_v<Derived1>,bool> = false>
FASTOR_INLINE
decltype(auto)
einsum(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b)
{
typename Derived0::result_type res_a(a);
typename Derived1::result_type res_b(b);
return einsum<Index_I,Index_J>(res_a,res_b);
}
template<class Index_I, class Index_J, typename Derived0, typename Derived1, size_t DIM0, size_t DIM1,
enable_if_t_<is_tensor_v<Derived0> && !is_tensor_v<Derived1>,bool> = false>
FASTOR_INLINE
decltype(auto)
einsum(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b)
{
typename Derived1::result_type res_b(b);
return einsum<Index_I,Index_J>(a,res_b);
}
template<class Index_I, class Index_J, typename Derived0, typename Derived1, size_t DIM0, size_t DIM1,
enable_if_t_<!is_tensor_v<Derived0> && is_tensor_v<Derived1>,bool> = false>
FASTOR_INLINE
decltype(auto)
einsum(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b)
{
typename Derived0::result_type res_a(a);
return einsum<Index_I,Index_J>(res_a,b);
}
//-------------------------------------------------------------------------------------------------
// network contraction for expressions
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
namespace internal {
// helper functions to evaluate expression in to intermediate tensors
// We evaluate all intermediate tensors and pack them in to a single tuple
FASTOR_INLINE
std::tuple<>
contraction_chain_evaluate()
{
return std::tuple<>{};
}
// Note that if the expression is a tensor the evaluation is free
// as evaluate returns the tensor itself
template<typename Derived0, size_t DIM0>
FASTOR_INLINE
decltype(auto)
contraction_chain_evaluate(const AbstractTensor<Derived0,DIM0>& a)
{
return evaluate(a.self());
}
template<typename AbstractTensorType0, typename ... AbstractTensorTypes>
FASTOR_INLINE
decltype(auto)
contraction_chain_evaluate(const AbstractTensorType0& a, const AbstractTensorTypes& ... rest)
{
return std::tuple_cat(std::make_tuple(evaluate(a)),contraction_chain_evaluate(rest...));
}
// helper functor to unpack the tuple and forward the pack of tensor
// to network contraction for operation minimisation
template<class Index_I, class Index_J, class ... Index_Ks>
struct unpack_contraction_tuple {
template<typename Tuple, size_t ... I>
static auto apply(Tuple t, std_ext::index_sequence<I ...>)
{
return contraction<Index_I,Index_J,Index_Ks...>(std::get<I>(t) ...);
}
template<typename Tuple>
static auto apply(Tuple t)
{
constexpr auto size = std::tuple_size<Tuple>::value;
return apply(t, std_ext::make_index_sequence<size>{});
}
};
// helper functor to unpack the tuple and forward the pack of tensor
// to network einsum for operation minimisation
template<class Index_I, class Index_J, class ... Index_Ks>
struct unpack_einsum_tuple {
template<typename Tuple, size_t ... I>
static auto apply(Tuple t, std_ext::index_sequence<I ...>)
{
return einsum<Index_I,Index_J,Index_Ks...>(std::get<I>(t) ...);
}
template<typename Tuple>
static auto apply(Tuple t)
{
constexpr auto size = std::tuple_size<Tuple>::value;
return apply(t, std_ext::make_index_sequence<size>{});
}
};
template<class Index_I, class Index_J, class ... Index_Ks>
struct unpack_einsum_helper_tuple {
template<typename Tuple, size_t ... I>
static constexpr auto apply(Tuple t, std_ext::index_sequence<I ...>)
{
return einsum_helper<Index_I,Index_J,Index_Ks...,decltype(std::get<I>(t)) ...>{};
}
template<typename Tuple>
static constexpr auto apply(Tuple t)
{
constexpr auto size = std::tuple_size<Tuple>::value;
return apply(t, std_ext::make_index_sequence<size>{});
}
};
} // internal
//-------------------------------------------------------------------------------------------------
// network contraction
template<class Index_I, class Index_J, class ... Index_Ks,
typename AbstractTensorType0, typename AbstractTensorType1, typename ... AbstractTensorTypes>
FASTOR_INLINE
auto
contraction(const AbstractTensorType0& a, const AbstractTensorType1& b, const AbstractTensorTypes& ... rest)
{
return internal::unpack_contraction_tuple<Index_I,Index_J,Index_Ks...>::apply(internal::contraction_chain_evaluate(a,b,rest...));
}
// network einsum
template<class Index_I, class Index_J, class ... Index_Ks,
typename AbstractTensorType0, typename AbstractTensorType1, typename ... AbstractTensorTypes>
FASTOR_INLINE
auto
einsum(const AbstractTensorType0& a, const AbstractTensorType1& b, const AbstractTensorTypes& ... rest)
{
// network einsum is not defined yet
return internal::unpack_einsum_tuple<Index_I,Index_J,Index_Ks...>::apply(internal::contraction_chain_evaluate(a,b,rest...));
// but it dispatches to network contraction anyway and contraction uses by-pair einsum in turn
// return internal::unpack_contraction_tuple<Index_I,Index_J,Index_Ks...>::apply(internal::contraction_chain_evaluate(a,b,rest...));
}
#endif // CXX 2014
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
} // end of namespace Fastor
#endif // ABSTRACT_CONTRACTION_H

View File

@@ -0,0 +1,747 @@
#ifndef CONTRACTION_H
#define CONTRACTION_H
#include "Fastor/backend/dyadic.h"
#include "Fastor/tensor/Tensor.h"
#include "Fastor/tensor_algebra/indicial.h"
namespace Fastor {
//using namespace details;
//namespace details {
// Define this if fastest (complete meta-engine based) tensor contraction is required.
// Note that this blows up memory consumption and compilation time exponentially
//#define CONTRACT_OPT 2
// Define this if faster (partial meta-engine based) tensor contraction is required.
// Note that this blows up memory consumption and compilation time but not as much
//#define CONTRACT_OPT 1
template<class Idx0, class Idx1, class Tens0, class Tens1, size_t ... Args>
struct RecursiveCartesian;
template<typename T, size_t ...Idx0, size_t ...Idx1, size_t ...Rest0, size_t ...Rest1, size_t First, size_t ... Lasts>
struct RecursiveCartesian<Index<Idx0...>, Index<Idx1...>, Tensor<T,Rest0...>, Tensor<T,Rest1...>, First, Lasts...> {
static constexpr int out_dim = no_of_unique<Idx0...,Idx1...>::value;
static
FASTOR_INLINE
void Do(const T *a_data, const T *b_data, T *out_data, std::array<int,out_dim> &as, std::array<int,out_dim> &idx) {
for (size_t i=0; i<First; ++i) {
idx[sizeof...(Lasts)] = i;
RecursiveCartesian<Index<Idx0...>, Index<Idx1...>,
Tensor<T,Rest0...>, Tensor<T,Rest1...>,Lasts...>::Do(a_data, b_data, out_data, as, idx);
}
}
};
template<typename T, size_t Last, size_t ...Idx0, size_t ...Idx1, size_t ...Rest0, size_t ...Rest1>
struct RecursiveCartesian<Index<Idx0...>, Index<Idx1...>, Tensor<T,Rest0...>, Tensor<T,Rest1...>,Last>
{
using _contraction_impl = contraction_impl<Index<Idx0...,Idx1...>, Tensor<T,Rest0...,Rest1...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)+sizeof...(Rest1)>::type>;
using resulting_tensor = typename _contraction_impl::type;
using resulting_index = typename _contraction_impl::indices;
static constexpr bool _is_reduction = resulting_tensor::dimension_t::value == 0;
static constexpr int a_dim = sizeof...(Rest0);
static constexpr int b_dim = sizeof...(Rest1);
static constexpr int out_dim = no_of_unique<Idx0...,Idx1...>::value;
static constexpr auto& idx_a = IndexTensors<
Index<Idx0..., Idx1...>,
Tensor<T,Rest0...,Rest1...>,
Index<Idx0...>,Tensor<T,Rest0...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)>::type>::indices;
static constexpr auto& idx_b = IndexTensors<
Index<Idx0..., Idx1...>,
Tensor<T,Rest0...,Rest1...>,
Index<Idx1...>,Tensor<T,Rest1...>,
typename std_ext::make_index_sequence<sizeof...(Rest1)>::type>::indices;
static constexpr auto& idx_out = IndexTensors<
Index<Idx0..., Idx1...>,
Tensor<T,Rest0...,Rest1...>,
resulting_index,resulting_tensor,
typename std_ext::make_index_sequence<resulting_tensor::Dimension>::type>::indices;
using nloops = loop_setter<
Index<Idx0...,Idx1...>,
Tensor<T,Rest0...,Rest1...>,
typename std_ext::make_index_sequence<out_dim>::type>;
static constexpr auto& maxes_out = nloops::dims;
static constexpr int total = nloops::value;
static constexpr std::array<size_t,sizeof...(Rest0)> products_a = nprods<Index<Rest0...>,
typename std_ext::make_index_sequence<a_dim>::type>::values;
static constexpr std::array<size_t,sizeof...(Rest1)> products_b = nprods<Index<Rest1...>,
typename std_ext::make_index_sequence<b_dim>::type>::values;
#ifndef FASTOR_DONT_VECTORISE
using vectorisability = is_vectorisable<Index<Idx0...>,Index<Idx1...>,Tensor<T,Rest1...>>;
static constexpr int stride = vectorisability::stride;
using V = typename vectorisability::type;
#else
static constexpr int stride = 1;
using V = SIMDVector<T,simd_abi::scalar>;
#endif
static
FASTOR_INLINE
void Do(const T *a_data, const T *b_data, T *out_data, std::array<int,out_dim> &as, std::array<int,out_dim> &idx)
{
FASTOR_IF_CONSTEXPR(!_is_reduction) {
using Index_with_dims = typename put_dims_in_Index<resulting_tensor>::type;
constexpr std::array<size_t,resulting_tensor::Dimension> products_out = \
nprods<Index_with_dims,typename std_ext::make_index_sequence<resulting_tensor::Dimension>::type>::values;
V _vec_a;
for (size_t i=0; i<Last; i+=stride) {
idx[0] = i;
std::reverse_copy(idx.begin(),idx.end(),as.begin());
int index_a = as[idx_a[a_dim-1]];
for(int it = 0; it< a_dim; it++) {
index_a += products_a[it]*as[idx_a[it]];
}
int index_b = as[idx_b[b_dim-1]];
for(int it = 0; it< b_dim; it++) {
index_b += products_b[it]*as[idx_b[it]];
}
int index_out = as[idx_out[resulting_tensor::Dimension-1]];
for(int it = 0; it< static_cast<int>(resulting_tensor::Dimension); it++) {
index_out += products_out[it]*as[idx_out[it]];
}
// out_data[index_out] += a_data[index_a]*b_data[index_b];
// _vec_a.set(*(a_data+index_a));
_vec_a.set(a_data[index_a]);
// V _vec_out = _vec_a*V(b_data+index_b) + V(out_data+index_out);
V _vec_out = fmadd(_vec_a,V(&b_data[index_b]), V(&out_data[index_out]));
_vec_out.store(out_data+index_out,false);
// _vec_out.aligned_store(&out_data[index_out]);
}
}
else {
using Index_with_dims = typename put_dims_in_Index<resulting_tensor>::type;
constexpr std::array<size_t,resulting_tensor::Dimension> products_out = \
nprods<Index_with_dims,typename std_ext::make_index_sequence<resulting_tensor::Dimension>::type>::values;
V _vec_a;
for (size_t i=0; i<Last; i+=stride) {
idx[0] = i;
std::reverse_copy(idx.begin(),idx.end(),as.begin());
int index_a = as[idx_a[a_dim-1]];
for(int it = 0; it< a_dim; it++) {
index_a += products_a[it]*as[idx_a[it]];
}
int index_b = as[idx_b[b_dim-1]];
for(int it = 0; it< b_dim; it++) {
index_b += products_b[it]*as[idx_b[it]];
}
out_data[0] += a_data[index_a]*b_data[index_b];
}
}
return;
}
};
// template<typename T, size_t ...Idx0, size_t ...Idx1, size_t ...Rest0, size_t ...Rest1>
// struct RecursiveCartesian<Index<Idx0...>, Index<Idx1...>, Tensor<T,Rest0...>, Tensor<T,Rest1...>>
// {
// using OutTensor = typename contraction_impl<Index<Idx0...,Idx1...>, Tensor<T,Rest0...,Rest1...>,
// typename std_ext::make_index_sequence<sizeof...(Rest0)+sizeof...(Rest1)>::type>::type;
// using OutIndices = typename contraction_impl<Index<Idx0...,Idx1...>, Tensor<T,Rest0...,Rest1...>,
// typename std_ext::make_index_sequence<sizeof...(Rest0)+sizeof...(Rest1)>::type>::indices;
// static constexpr int a_dim = sizeof...(Rest0);
// static constexpr int b_dim = sizeof...(Rest1);
// static constexpr int out_dim = no_of_unique<Idx0...,Idx1...>::value;
// static constexpr auto& idx_a = IndexTensors<
// Index<Idx0..., Idx1...>,
// Tensor<T,Rest0...,Rest1...>,
// Index<Idx0...>,Tensor<T,Rest0...>,
// typename std_ext::make_index_sequence<sizeof...(Rest0)>::type>::indices;
// static constexpr auto& idx_b = IndexTensors<
// Index<Idx0..., Idx1...>,
// Tensor<T,Rest0...,Rest1...>,
// Index<Idx1...>,Tensor<T,Rest1...>,
// typename std_ext::make_index_sequence<sizeof...(Rest1)>::type>::indices;
// static constexpr auto& idx_out = IndexTensors<
// Index<Idx0..., Idx1...>,
// Tensor<T,Rest0...,Rest1...>,
// OutIndices,OutTensor,
// typename std_ext::make_index_sequence<OutTensor::Dimension>::type>::indices;
// using nloops = loop_setter<
// Index<Idx0...,Idx1...>,
// Tensor<T,Rest0...,Rest1...>,
// typename std_ext::make_index_sequence<out_dim>::type>;
// static constexpr auto& maxes_out = nloops::dims;
// static constexpr int total = nloops::value;
// static constexpr std::array<size_t,a_dim> products_a = nprods<Index<Rest0...>,typename std_ext::make_index_sequence<a_dim>::type>::values;
// static constexpr std::array<size_t,b_dim> products_b = nprods<Index<Rest1...>,typename std_ext::make_index_sequence<b_dim>::type>::values;
// using Index_with_dims = typename put_dims_in_Index<OutTensor>::type;
// static constexpr std::array<size_t,OutTensor::Dimension> products_out =
// nprods<Index_with_dims,typename std_ext::make_index_sequence<OutTensor::Dimension>::type>::values;
// #ifndef FASTOR_DONT_VECTORISE
// using vectorisability = is_vectorisable<Index<Idx0...>,Index<Idx1...>,Tensor<T,Rest1...>>;
// static constexpr int stride = vectorisability::stride;
// using V = typename vectorisability::type;
// #else
// static constexpr int stride = 1;
// using V = SIMDVector<T,sizeof(T)*8>;
// #endif
// static void Do(const T *a_data, const T *b_data, T *out_data, std::array<int,out_dim> &as, std::array<int,out_dim> &idx)
// {
// std::reverse_copy(idx.begin(),idx.end(),as.begin());
// int index_a = as[idx_a[a_dim-1]];
// for(int it = 0; it< a_dim; it++) {
// index_a += products_a[it]*as[idx_a[it]];
// }
// int index_b = as[idx_b[b_dim-1]];
// for(int it = 0; it< b_dim; it++) {
// index_b += products_b[it]*as[idx_b[it]];
// }
// int index_out = as[idx_out[OutTensor::Dimension-1]];
// for(int it = 0; it< static_cast<int>(OutTensor::Dimension); it++) {
// index_out += products_out[it]*as[idx_out[it]];
// }
// out_data[index_out] += a_data[index_a]*b_data[index_b];
// return;
// }
// };
template<typename T, size_t Last, size_t ...Idx0, size_t ...Idx1, size_t ...Rest0, size_t ...Rest1>
constexpr std::array<size_t,sizeof...(Rest0)> RecursiveCartesian<Index<Idx0...>, Index<Idx1...>,
Tensor<T,Rest0...>, Tensor<T,Rest1...>,Last>::products_a;
template<typename T, size_t Last, size_t ...Idx0, size_t ...Idx1, size_t ...Rest0, size_t ...Rest1>
constexpr std::array<size_t,sizeof...(Rest1)> RecursiveCartesian<Index<Idx0...>, Index<Idx1...>,
Tensor<T,Rest0...>, Tensor<T,Rest1...>,Last>::products_b;
// template<typename T, size_t Last, size_t ...Idx0, size_t ...Idx1, size_t ...Rest0, size_t ...Rest1>
// constexpr std::array<size_t,RecursiveCartesian<Index<Idx0...>, Index<Idx1...>,
// Tensor<T,Rest0...>, Tensor<T,Rest1...>,Last>::OutTensor::Dimension> RecursiveCartesian<Index<Idx0...>, Index<Idx1...>,
// Tensor<T,Rest0...>, Tensor<T,Rest1...>,Last>::products_out;
template<class Idx0, class Idx1, class Tens0, class Tens1, class Args>
struct RecursiveCartesianDispatcher;
template<typename T, size_t ...Idx0, size_t ...Idx1, size_t ...Rest0, size_t ...Rest1, size_t ... Args>
struct RecursiveCartesianDispatcher<Index<Idx0...>, Index<Idx1...>, Tensor<T,Rest0...>, Tensor<T,Rest1...>, Index<Args...> >
{
static constexpr int out_dim = no_of_unique<Idx0...,Idx1...>::value;
static FASTOR_INLINE void Do(const T *a_data, const T *b_data, T *out_data,
std::array<int,out_dim> &as, std::array<int,out_dim> &idx) {
return RecursiveCartesian<Index<Idx0...>, Index<Idx1...>,
Tensor<T,Rest0...>, Tensor<T,Rest1...>, Args...>::Do(a_data, b_data, out_data, as, idx);
}
};
template<class T, class U, class enable=void>
struct extractor_contract_2 {};
template<size_t ... Idx0, size_t ... Idx1>
struct extractor_contract_2<Index<Idx0...>, Index<Idx1...>,
typename std::enable_if<no_of_unique<Idx0...,Idx1...>::value!=sizeof...(Idx0)+sizeof...(Idx1)>::type> {
template<typename T, size_t ... Rest0, size_t ... Rest1>
static
FASTOR_INLINE
typename contraction_impl<Index<Idx0...,Idx1...>, Tensor<T,Rest0...,Rest1...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)+sizeof...(Rest1)>::type>::type
contract_impl(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b) {
using _contraction_impl = contraction_impl<Index<Idx0...,Idx1...>, Tensor<T,Rest0...,Rest1...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)+sizeof...(Rest1)>::type>;
using resulting_tensor = typename _contraction_impl::type;
// is reduction including permuted reduction
constexpr bool _is_reduction = resulting_tensor::dimension_t::value == 0;
#if CONTRACT_OPT==-3
static_assert(!_is_reduction,"THIS VARIANT OF EINSUM CANNOT DEAL WITH REDUCTION CASES");
constexpr int total = no_of_loops_to_set<Index<Idx0...>,Index<Idx1...>,Tensor<T,Rest0...>,Tensor<T,Rest1...>,
typename std_ext::make_index_sequence<no_of_unique<Idx0...,Idx1...>::value>::type>::value;
using index_generator = contract_meta_engine<Index<Idx0...>,Index<Idx1...>,Tensor<T,Rest0...>,Tensor<T,Rest1...>,
typename std_ext::make_index_sequence<total>::type>;
using OutTensor = typename index_generator::OutTensor;
constexpr auto& index_a = index_generator::index_a;
constexpr auto& index_b = index_generator::index_b;
constexpr auto& index_out = index_generator::index_out;
OutTensor out;
out.zeros();
const T *a_data = a.data();
const T *b_data = b.data();
T *out_data = out.data();
#ifndef FASTOR_DONT_VECTORISE
using vectorisability = is_vectorisable<Index<Idx0...>,Index<Idx1...>,Tensor<T,Rest1...>>;
constexpr int stride = vectorisability::stride;
using V = typename vectorisability::type;
#else
constexpr int stride = 1;
using V = SIMDVector<T,sizeof(T)*8>;
#endif
V _vec_a;
for (int i = 0; i < total; i+=stride) {
// out_data[index_out[i]] += a_data[index_a[i]]*b_data[index_b[i]];
_vec_a.set(*(a_data+index_a[i]));
V _vec_out = _vec_a*V(b_data+index_b[i]) + V(out_data+index_out[i]);
// _vec_out.store(out_data+index_out[i]);
_vec_out.aligned_store(out_data+index_out[i]);
}
return out;
}
#elif CONTRACT_OPT==-2
static_assert(!_is_reduction,"THIS VARIANT OF EINSUM CANNOT DEAL WITH REDUCTION CASES");
resulting_tensor out;
out.zeros();
const T *a_data = a.data();
const T *b_data = b.data();
T *out_data = out.data();
constexpr int a_dim = sizeof...(Rest0);
constexpr int b_dim = sizeof...(Rest1);
constexpr auto& idx_a = IndexFirstTensor<Index<Idx0...>,Index<Idx1...>, Tensor<T,Rest0...>,Tensor<T,Rest1...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)>::type>::indices;
constexpr auto& idx_b = IndexSecondTensor<Index<Idx0...>,Index<Idx1...>, Tensor<T,Rest0...>,Tensor<T,Rest1...>,
typename std_ext::make_index_sequence<sizeof...(Rest1)>::type>::indices;
constexpr auto& idx_out = IndexResultingTensor<Index<Idx0...>,Index<Idx1...>, Tensor<T,Rest0...>,Tensor<T,Rest1...>,
typename std_ext::make_index_sequence<resulting_tensor::Dimension>::type>::indices;
constexpr int total = no_of_loops_to_set<Index<Idx0...>,Index<Idx1...>,Tensor<T,Rest0...>,Tensor<T,Rest1...>,
typename std_ext::make_index_sequence<no_of_unique<Idx0...,Idx1...>::value>::type>::value;
using maxes_out_type = typename no_of_loops_to_set<Index<Idx0...>,Index<Idx1...>,Tensor<T,Rest0...>,Tensor<T,Rest1...>,
typename std_ext::make_index_sequence<no_of_unique<Idx0...,Idx1...>::value>::type>::type;
constexpr auto& as_all = cartesian_product<maxes_out_type,typename std_ext::make_index_sequence<total>::type>::values;
// constexpr auto as_all = cartesian_product_2<maxes_out.size(),total>(maxes_out);
constexpr std::array<size_t,a_dim> products_a = nprods<Index<Rest0...>,typename std_ext::make_index_sequence<a_dim>::type>::values;
constexpr std::array<size_t,b_dim> products_b = nprods<Index<Rest1...>,typename std_ext::make_index_sequence<b_dim>::type>::values;
using Index_with_dims = typename put_dims_in_Index<resulting_tensor>::type;
constexpr std::array<size_t,Index_with_dims::NoIndices> products_out = nprods<Index_with_dims,
typename std_ext::make_index_sequence<Index_with_dims::NoIndices>::type>::values;
#ifndef FASTOR_DONT_VECTORISE
using vectorisability = is_vectorisable<Index<Idx0...>,Index<Idx1...>,Tensor<T,Rest1...>>;
constexpr int stride = vectorisability::stride;
using V = typename vectorisability::type;
#else
constexpr int stride = 1;
using V = SIMDVector<T,simd_abi::scalar>;
#endif
int it;
V _vec_a;
for (int i = 0; i < total; i+=stride) {
int index_a = as_all[i][idx_a[a_dim-1]];
for(it = 0; it< a_dim; it++) {
index_a += products_a[it]*as_all[i][idx_a[it]];
}
int index_b = as_all[i][idx_b[b_dim-1]];
for(it = 0; it< b_dim; it++) {
index_b += products_b[it]*as_all[i][idx_b[it]];
}
int index_out = as_all[i][idx_out[idx_out.size()-1]];
for(it = 0; it< idx_out.size(); it++) {
index_out += products_out[it]*as_all[i][idx_out[it]];
}
_vec_a.set(*(a_data+index_a));
V _vec_out = _vec_a*V(b_data+index_b) + V(out_data+index_out);
// _vec_out.store(out_data+index_out);
_vec_out.aligned_store(out_data+index_out);
}
return out;
}
#elif CONTRACT_OPT==-1
using resulting_index = typename _contraction_impl::indices;
resulting_tensor out;
out.zeros();
const T *a_data = a.data();
const T *b_data = b.data();
T *out_data = out.data();
constexpr int a_dim = sizeof...(Rest0);
constexpr int b_dim = sizeof...(Rest1);
constexpr int out_dim = no_of_unique<Idx0...,Idx1...>::value;
constexpr auto& idx_a = IndexTensors<
Index<Idx0..., Idx1...>,
Tensor<T,Rest0...,Rest1...>,
Index<Idx0...>,Tensor<T,Rest0...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)>::type>::indices;
constexpr auto& idx_b = IndexTensors<
Index<Idx0..., Idx1...>,
Tensor<T,Rest0...,Rest1...>,
Index<Idx1...>,Tensor<T,Rest1...>,
typename std_ext::make_index_sequence<sizeof...(Rest1)>::type>::indices;
constexpr auto& idx_out = IndexTensors<
Index<Idx0..., Idx1...>,
Tensor<T,Rest0...,Rest1...>,
resulting_index,resulting_tensor,
typename std_ext::make_index_sequence<resulting_tensor::Dimension>::type>::indices;
using nloops = loop_setter<
Index<Idx0...,Idx1...>,
Tensor<T,Rest0...,Rest1...>,
typename std_ext::make_index_sequence<out_dim>::type>;
constexpr auto& maxes_out = nloops::dims;
constexpr int total = nloops::value;
constexpr std::array<size_t,a_dim> products_a = nprods<Index<Rest0...>,typename std_ext::make_index_sequence<a_dim>::type>::values;
constexpr std::array<size_t,b_dim> products_b = nprods<Index<Rest1...>,typename std_ext::make_index_sequence<b_dim>::type>::values;
#ifndef FASTOR_DONT_VECTORISE
using vectorisability = is_vectorisable<Index<Idx0...>,Index<Idx1...>,Tensor<T,Rest1...>>;
constexpr int stride = vectorisability::stride;
using V = typename vectorisability::type;
#else
constexpr int stride = 1;
using V = SIMDVector<T,sizeof(T)*8>;
#endif
std::array<int,out_dim> as = {};
int it;
V _vec_a;
#if FASTOR_CXX_VERSION >= 2017
constexpr std::array<int,out_dim> remainings = find_remaining(maxes_out, total);
#endif
FASTOR_IF_CONSTEXPR(!_is_reduction) {
using Index_with_dims = typename put_dims_in_Index<resulting_tensor>::type;
constexpr std::array<size_t,resulting_tensor::Dimension> products_out = \
nprods<Index_with_dims,typename std_ext::make_index_sequence<resulting_tensor::Dimension>::type>::values;
for (int i = 0; i < total; i+=stride) {
#if FASTOR_CXX_VERSION >= 2017
for (int n = 0; n < out_dim; ++n) {
as[n] = ( i / remainings[n] ) % (int)maxes_out[n];
}
#else
int remaining = total;
for (int n = 0; n < out_dim; ++n) {
remaining /= maxes_out[n];
as[n] = ( i / remaining ) % maxes_out[n];
}
#endif
int index_a = as[idx_a[a_dim-1]];
for(it = 0; it< a_dim; it++) {
index_a += products_a[it]*as[idx_a[it]];
}
int index_b = as[idx_b[b_dim-1]];
for(it = 0; it< b_dim; it++) {
index_b += products_b[it]*as[idx_b[it]];
}
int index_out = as[idx_out[resulting_tensor::Dimension-1]];
for(it = 0; it< static_cast<int>(resulting_tensor::Dimension); it++) {
index_out += products_out[it]*as[idx_out[it]];
}
// println(index_out,index_a,index_b,"\n");
_vec_a.set(*(a_data+index_a));
// _vec_a.broadcast(&a_data[index_a]);
V _vec_out = _vec_a*V(b_data+index_b) + V(out_data+index_out);
// V _vec_out = fmadd(_vec_a,V(b_data+index_b), V(out_data+index_out));
// _vec_out.store(out_data+index_out);
_vec_out.aligned_store(out_data+index_out);
}
// ACTUALLY MUCH SLOWER
// constexpr auto as_all = cartesian_product_2<out_dim,total>(maxes_out);
// for (int i = 0; i < total; i+=stride) {
// int index_a = as_all[i][idx_a[a_dim-1]];
// for(it = 0; it< a_dim; it++) {
// index_a += products_a[it]*as_all[i][idx_a[it]];
// }
// int index_b = as_all[i][idx_b[b_dim-1]];
// for(it = 0; it< b_dim; it++) {
// index_b += products_b[it]*as_all[i][idx_b[it]];
// }
// int index_out = as_all[i][idx_out[idx_out.size()-1]];
// for(it = 0; it< idx_out.size(); it++) {
// index_out += products_out[it]*as_all[i][idx_out[it]];
// }
// _vec_a.set(*(a_data+index_a));
// // V _vec_out = _vec_a*V(b_data+index_b) + V(out_data+index_out);
// V _vec_out = fmadd(_vec_a,V(b_data+index_b), V(out_data+index_out));
// _vec_out.aligned_store(out_data+index_out);
// }
}
else {
for (int i = 0; i < total; i+=stride) {
#if FASTOR_CXX_VERSION >= 2017
for (int n = 0; n < out_dim; ++n) {
as[n] = ( i / remainings[n] ) % (int)maxes_out[n];
}
#else
int remaining = total;
for (int n = 0; n < out_dim; ++n) {
remaining /= maxes_out[n];
as[n] = ( i / remaining ) % maxes_out[n];
}
#endif
int index_a = as[idx_a[a_dim-1]];
for(it = 0; it< a_dim; it++) {
index_a += products_a[it]*as[idx_a[it]];
}
int index_b = as[idx_b[b_dim-1]];
for(it = 0; it< b_dim; it++) {
index_b += products_b[it]*as[idx_b[it]];
}
out_data[0] += a_data[index_a]*b_data[index_b];
}
}
return out;
}
#elif CONTRACT_OPT==1
using resulting_index = typename _contraction_impl::indices;
resulting_tensor out;
out.zeros();
const T *a_data = a.data();
const T *b_data = b.data();
T *out_data = out.data();
constexpr int a_dim = sizeof...(Rest0);
constexpr int b_dim = sizeof...(Rest1);
constexpr int out_dim = no_of_unique<Idx0...,Idx1...>::value;
constexpr auto& idx_a = IndexTensors<
Index<Idx0..., Idx1...>,
Tensor<T,Rest0...,Rest1...>,
Index<Idx0...>,Tensor<T,Rest0...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)>::type>::indices;
constexpr auto& idx_b = IndexTensors<
Index<Idx0..., Idx1...>,
Tensor<T,Rest0...,Rest1...>,
Index<Idx1...>,Tensor<T,Rest1...>,
typename std_ext::make_index_sequence<sizeof...(Rest1)>::type>::indices;
constexpr auto& idx_out = IndexTensors<
Index<Idx0..., Idx1...>,
Tensor<T,Rest0...,Rest1...>,
resulting_index,resulting_tensor,
typename std_ext::make_index_sequence<resulting_tensor::Dimension>::type>::indices;
using nloops = loop_setter<
Index<Idx0...,Idx1...>,
Tensor<T,Rest0...,Rest1...>,
typename std_ext::make_index_sequence<out_dim>::type>;
constexpr auto& maxes_out = nloops::dims;
constexpr int total = nloops::value;
constexpr std::array<size_t,a_dim> products_a = nprods<Index<Rest0...>,typename std_ext::make_index_sequence<a_dim>::type>::values;
constexpr std::array<size_t,b_dim> products_b = nprods<Index<Rest1...>,typename std_ext::make_index_sequence<b_dim>::type>::values;
#ifndef FASTOR_DONT_VECTORISE
using vectorisability = is_vectorisable<Index<Idx0...>,Index<Idx1...>,Tensor<T,Rest1...>>;
constexpr int stride = vectorisability::stride;
using V = typename vectorisability::type;
#else
constexpr int stride = 1;
using V = SIMDVector<T,sizeof(T)*8>;
#endif
std::array<int,out_dim> as = {};
int it, jt, counter = 0;
V _vec_a;
FASTOR_IF_CONSTEXPR(!_is_reduction) {
using Index_with_dims = typename put_dims_in_Index<resulting_tensor>::type;
constexpr std::array<size_t,resulting_tensor::Dimension> products_out = \
nprods<Index_with_dims,typename std_ext::make_index_sequence<resulting_tensor::Dimension>::type>::values;
while(true)
{
int index_a = as[idx_a[a_dim-1]];
for(it = 0; it< a_dim; it++) {
index_a += products_a[it]*as[idx_a[it]];
}
int index_b = as[idx_b[b_dim-1]];
for(it = 0; it< b_dim; it++) {
index_b += products_b[it]*as[idx_b[it]];
}
int index_out = as[idx_out[resulting_tensor::Dimension-1]];
for(it = 0; it< static_cast<int>(resulting_tensor::Dimension); it++) {
index_out += products_out[it]*as[idx_out[it]];
}
_vec_a.set(*(a_data+index_a));
// V _vec_out = _vec_a*V(b_data+index_b) + V(out_data+index_out);
V _vec_out = fmadd(_vec_a,V(b_data+index_b), V(out_data+index_out));
_vec_out.store(out_data+index_out,false);
// _vec_out.aligned_store(out_data+index_out);
counter++;
for(jt = maxes_out.size()-1; jt>=0 ; jt--)
{
if (jt == maxes_out.size()-1) as[jt]+=stride;
else as[jt] +=1;
if(as[jt]<maxes_out[jt])
break;
else
as[jt]=0;
}
if(jt<0)
break;
}
}
else {
while(true)
{
int index_a = as[idx_a[a_dim-1]];
for(it = 0; it< a_dim; it++) {
index_a += products_a[it]*as[idx_a[it]];
}
int index_b = as[idx_b[b_dim-1]];
for(it = 0; it< b_dim; it++) {
index_b += products_b[it]*as[idx_b[it]];
}
out_data[0] += a_data[index_a]*b_data[index_b];
counter++;
for(jt = maxes_out.size()-1; jt>=0 ; jt--)
{
if (jt == maxes_out.size()-1) as[jt]+=1;
else as[jt] +=1;
if(as[jt]<maxes_out[jt])
break;
else
as[jt]=0;
}
if(jt<0)
break;
}
}
return out;
}
#else
resulting_tensor out;
out.zeros();
const T *a_data = a.data();
const T *b_data = b.data();
T *out_data = out.data();
constexpr int out_dim = no_of_unique<Idx0...,Idx1...>::value;
std::array<int,out_dim> as = {};
std::array<int,out_dim> idx = {};
using nloops = loop_setter<
Index<Idx0...,Idx1...>,
Tensor<T,Rest0...,Rest1...>,
typename std_ext::make_index_sequence<out_dim>::type>;
using dims_type = typename nloops::dims_type;
RecursiveCartesianDispatcher<Index<Idx0...>,Index<Idx1...>,
Tensor<T,Rest0...>,Tensor<T,Rest1...>,dims_type>::Do(a_data,b_data,out_data,as,idx);
return out;
}
#endif
};
// Specialisation for outer product case
template<size_t ... Idx0, size_t ... Idx1>
struct extractor_contract_2<Index<Idx0...>, Index<Idx1...>,
typename std::enable_if<no_of_unique<Idx0...,Idx1...>::value==sizeof...(Idx0)+sizeof...(Idx1)>::type> {
template<typename T, size_t ... Rest0, size_t ... Rest1>
static Tensor<T,Rest0...,Rest1...>
FASTOR_INLINE contract_impl(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b) {
Tensor<T,Rest0...,Rest1...> out;
_dyadic<T,pack_prod<Rest0...>::value, pack_prod<Rest1...>::value>(a.data(),b.data(),out.data());
return out;
}
};
template<class Index_I, class Index_J,
typename T, size_t ... Rest0, size_t ... Rest1>
FASTOR_INLINE
auto contraction(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b)
-> decltype(extractor_contract_2<Index_I,Index_J>::contract_impl(a,b)) {
return extractor_contract_2<Index_I,Index_J>::contract_impl(a,b);
}
}
#endif // CONTRACTION_H

View File

@@ -0,0 +1,131 @@
#ifndef CONTRACTION_SINGLE_H
#define CONTRACTION_SINGLE_H
#include "Fastor/tensor/Tensor.h"
#include "Fastor/tensor_algebra/indicial.h"
namespace Fastor {
template<class T>
struct extractor_contract_1 {};
template<size_t ... Idx0>
struct extractor_contract_1<Index<Idx0...>> {
template<typename T, size_t ... Rest0>
static
typename contraction_impl<Index<Idx0...>, Tensor<T,Rest0...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)>::type>::type
FASTOR_INLINE contract_impl(const Tensor<T,Rest0...> &a) {
constexpr bool _is_reduction = is_single_reduction_v<Index<Idx0...>,Tensor<T,Rest0...>>;
using OutTensor = typename contraction_impl<Index<Idx0...>, Tensor<T,Rest0...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)>::type>::type;
using OutIndices = typename contraction_impl<Index<Idx0...>, Tensor<T,Rest0...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)>::type>::indices;
OutTensor out;
out.zeros();
const T *a_data = a.data();
T *out_data = out.data();
constexpr int a_dim = sizeof...(Rest0);
constexpr int out_dim = no_of_unique<Idx0...>::value;
constexpr auto& idx_a = IndexTensors<
Index<Idx0...>,
Tensor<T,Rest0...>,
Index<Idx0...>,Tensor<T,Rest0...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)>::type>::indices;
constexpr auto& idx_out = IndexTensors<
Index<Idx0...>,
Tensor<T,Rest0...>,
OutIndices,OutTensor,
typename std_ext::make_index_sequence<OutTensor::Dimension>::type>::indices;
using nloops = loop_setter<
Index<Idx0...>,
Tensor<T,Rest0...>,
typename std_ext::make_index_sequence<out_dim>::type>;
constexpr auto& maxes_out = nloops::dims;
constexpr int total = nloops::value;
constexpr std::array<size_t,a_dim> products_a = nprods<Index<Rest0...>,typename std_ext::make_index_sequence<a_dim>::type>::values;
int as[out_dim] = {};
constexpr int stride = 1;
FASTOR_IF_CONSTEXPR (!_is_reduction) {
using Index_with_dims = typename put_dims_in_Index<OutTensor>::type;
constexpr std::array<size_t,OutTensor::Dimension> products_out = \
nprods<Index_with_dims,typename std_ext::make_index_sequence<OutTensor::Dimension>::type>::values;
int it, jt, counter = 0;
while(counter < total)
{
int index_a = as[idx_a[a_dim-1]];
for(it = 0; it< a_dim; it++) {
index_a += products_a[it]*as[idx_a[it]];
}
int index_out = as[idx_out[OutTensor::Dimension-1]];
for(it = 0; it< static_cast<int>(OutTensor::Dimension); it++) {
index_out += products_out[it]*as[idx_out[it]];
}
out_data[index_out] += a_data[index_a];
for(jt = out_dim-1 ; jt>=0 ; jt--)
{
if(++as[jt]<maxes_out[jt])
break;
else
as[jt]=0;
}
if(jt<0)
break;
counter++;
}
}
else {
int it, jt, counter = 0;
while(counter < total)
{
int index_a = as[idx_a[a_dim-1]];
for(it = 0; it< a_dim; it++) {
index_a += products_a[it]*as[idx_a[it]];
}
out_data[0] += a_data[index_a];
for(jt = out_dim-1 ; jt>=0 ; jt--)
{
if(++as[jt]<maxes_out[jt])
break;
else
as[jt]=0;
}
if(jt<0)
break;
counter++;
}
}
return out;
}
};
template<class Index_I,
typename T, size_t ... Rest0>
auto contraction(const Tensor<T,Rest0...> &a)
-> decltype(extractor_contract_1<Index_I>::contract_impl(a)) {
return extractor_contract_1<Index_I>::contract_impl(a);
}
} // end of namespace Fastor
#endif // CONTRACTION_SINGLE_H

View File

@@ -0,0 +1,292 @@
#ifndef EINSUM_H
#define EINSUM_H
#include "Fastor/backend/backend.h"
#include "Fastor/tensor/Tensor.h"
#include "Fastor/meta/einsum_meta.h"
#include "Fastor/tensor_algebra/indicial.h"
#include "Fastor/backend/voigt.h"
#include "Fastor/tensor_algebra/permutation.h"
#include "Fastor/tensor_algebra/permute.h"
#include "Fastor/tensor_algebra/innerproduct.h"
#include "Fastor/tensor_algebra/outerproduct.h"
#include "Fastor/tensor_algebra/contraction.h"
#include "Fastor/tensor_algebra/contraction_single.h"
#include "Fastor/tensor_algebra/strided_contraction.h"
namespace Fastor {
// Single tensor
//-----------------------------------------------------------------------------------------------------------------------//
template<class Index_I, typename T, size_t ... Rest0>
FASTOR_INLINE
auto einsum(const Tensor<T,Rest0...> &a)
-> decltype(extractor_contract_1<Index_I>::contract_impl(a)) {
static_assert(einsum_index_checker<Index_I>::value,
"INDICES FOR EINSUM FUNCTION CANNOT APPEAR MORE THAN TWICE. USE INNER INSTEAD");
return extractor_contract_1<Index_I>::contract_impl(a);
}
//-----------------------------------------------------------------------------------------------------------------------//
// Two tensor (by-pair)
//-----------------------------------------------------------------------------------------------------------------------//
// Inner product case
//-----------------------------------------------------------------------------------------------------------------------//
template<class Index_I, class Index_J,
typename T, size_t ... Rest0, size_t ... Rest1,
enable_if_t_<is_pair_reduction_v<Index_I,Index_J>,bool> = false>
FASTOR_INLINE
Tensor<T> einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b) {
return inner(a,b);
}
// General by-pair product cases
//-----------------------------------------------------------------------------------------------------------------------//
template<class Index_I, class Index_J,
typename T, size_t ... Rest0, size_t ... Rest1,
typename std::enable_if<!is_pair_reduction<Index_I,Index_J>::value &&
!internal::is_generalised_matrix_vector<Index_I,Index_J>::value &&
!internal::is_generalised_vector_matrix<Index_I,Index_J>::value &&
!internal::is_generalised_matrix_matrix<Index_I,Index_J>::value
,bool>::type=0>
FASTOR_INLINE
auto einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b)
-> decltype(extractor_contract_2<Index_I,Index_J>::contract_impl(a,b)) {
static_assert(einsum_index_checker<typename concat_<Index_I,Index_J>::type>::value,
"INDICES FOR EINSUM FUNCTION CANNOT APPEAR MORE THAN TWICE. USE INNER INSTEAD");
// // Dispatch to the right routine
// using vectorisability = is_vectorisable<Index_I,Index_J,Tensor<T,Rest1...>>;
// // constexpr bool is_reducible = vectorisability::last_index_contracted;
// constexpr bool is_reducible = vectorisability::is_reducible;
// FASTOR_IF_CONSTEXPR (is_reducible) {
// return extractor_reducible_contract<Index_I,Index_J>::contract_impl(a,b);
// }
// else {
// return extractor_contract_2<Index_I,Index_J>::contract_impl(a,b);
// }
return extractor_contract_2<Index_I,Index_J>::contract_impl(a,b);
}
template<class Index_I, class Index_J,
typename T, size_t ...Rest0, size_t ...Rest1,
typename std::enable_if<
internal::is_generalised_matrix_vector<Index_I,Index_J>::value,
bool>::type = 0>
FASTOR_INLINE
auto
einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b) //{
-> decltype(extractor_contract_2<Index_I,Index_J>::contract_impl(a,b)) {
constexpr size_t which_one_is_vector = internal::is_generalised_matrix_vector<Index_I,Index_J>::which_one_is_vector;
constexpr size_t matches_up_to = internal::is_generalised_matrix_vector<Index_I,Index_J>::matches_up_to;
constexpr size_t rest0[sizeof...(Rest0)] = {Rest0...};
constexpr size_t rest1[sizeof...(Rest1)] = {Rest1...};
constexpr size_t product = which_one_is_vector == 1 ? partial_prod(rest0, matches_up_to) : partial_prod(rest1, matches_up_to);
constexpr size_t vec_product = which_one_is_vector == 1 ? pack_prod<Rest1...>::value : pack_prod<Rest0...>::value;
decltype(extractor_contract_2<Index_I,Index_J>::contract_impl(a,b)) out;
which_one_is_vector == 1 ? _matmul<T,product,vec_product,1>(a.data(),b.data(),out.data()) :\
_matmul<T,product,vec_product,1>(b.data(),a.data(),out.data());
return out;
}
template<class Index_I, class Index_J,
typename T, size_t ...Rest0, size_t ...Rest1,
typename std::enable_if<
internal::is_generalised_vector_matrix<Index_I,Index_J>::value,
bool>::type = 0>
FASTOR_INLINE
auto
einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b) //{
-> decltype(extractor_contract_2<Index_I,Index_J>::contract_impl(a,b)) {
constexpr size_t which_one_is_vector = internal::is_generalised_vector_matrix<Index_I,Index_J>::which_one_is_vector;
constexpr size_t matches_up_to = internal::is_generalised_vector_matrix<Index_I,Index_J>::matches_up_to;
constexpr size_t rest0[sizeof...(Rest0)] = {Rest0...};
constexpr size_t rest1[sizeof...(Rest1)] = {Rest1...};
constexpr size_t product = which_one_is_vector == 1 ? partial_prod_reverse(rest0, matches_up_to) : partial_prod_reverse(rest1, matches_up_to);
constexpr size_t vec_product = which_one_is_vector == 1 ? pack_prod<Rest1...>::value : pack_prod<Rest0...>::value;
decltype(extractor_contract_2<Index_I,Index_J>::contract_impl(a,b)) out;
which_one_is_vector == 1 ? _matmul<T,1,vec_product,product>(b.data(),a.data(),out.data()) :\
_matmul<T,1,vec_product,product>(a.data(),b.data(),out.data());
return out;
}
template<class Index_I, class Index_J,
typename T, size_t ...Rest0, size_t ...Rest1,
typename std::enable_if<
internal::is_generalised_matrix_matrix<Index_I,Index_J>::value,
bool>::type = 0>
FASTOR_INLINE
auto
einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b) //{
-> decltype(extractor_contract_2<Index_I,Index_J>::contract_impl(a,b)) {
constexpr size_t matches_up_to = internal::is_generalised_matrix_matrix<Index_I,Index_J>::ncontracted;
constexpr size_t rest0[sizeof...(Rest0)] = {Rest0...};
constexpr size_t rest1[sizeof...(Rest1)] = {Rest1...};
constexpr size_t K_product = partial_prod(rest1, matches_up_to - 1);
constexpr size_t M = partial_prod(rest0, sizeof...(Rest0) - matches_up_to - 1);
constexpr size_t N = partial_prod(rest1, sizeof...(Rest1) - 1, matches_up_to);
decltype(extractor_contract_2<Index_I,Index_J>::contract_impl(a,b)) out;
_matmul<T,M,K_product,N>(a.data(),b.data(),out.data());
return out;
}
//-----------------------------------------------------------------------------------------------------------------------//
// matmul dispatcher for 2nd order tensors (matrix-matrix)
// also includes matrix-vector and vector-matrix when vector is of size
// nx1 or 1xn
template<class Ind0, class Ind1,
typename T, size_t I, size_t J, size_t K,
typename std::enable_if<Ind0::Size==2 && Ind1::Size==2 &&
Ind0::values[1] == Ind1::values[0] &&
Ind0::values[1] != Ind0::values[0] &&
Ind0::values[1] != Ind1::values[1] &&
Ind0::values[0] != Ind1::values[1]
,bool>::type = 0>
FASTOR_INLINE Tensor<T,I,K>
einsum(const Tensor<T,I,J> &a, const Tensor<T,J,K> &b) {
Tensor<T,I,K> out;
_matmul<T,I,J,K>(a.data(),b.data(),out.data());
return out;
}
// matmul dispatcher for matrix-vector
template<class Ind0, class Ind1,
typename T, size_t I, size_t J,
typename std::enable_if<Ind0::Size==2 && Ind1::Size==1 &&
Ind0::values[1] == Ind1::values[0] &&
Ind0::values[0] != Ind1::values[0]
,bool>::type = 0>
FASTOR_INLINE Tensor<T,I>
einsum(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;
}
// matmul dispatcher for matrix-vector
template<class Ind0, class Ind1,
typename T, size_t I, size_t J,
typename std::enable_if<Ind0::Size==2 && Ind1::Size==1 &&
Ind0::values[0] == Ind1::values[0] &&
Ind0::values[1] != Ind1::values[0],bool>::type = 0>
FASTOR_INLINE Tensor<T,J>
einsum(const Tensor<T,I,J> &a, const Tensor<T,I> &b) {
Tensor<T,J> out;
_matmul<T,1,I,J>(b.data(),a.data(),out.data());
return out;
}
// matmul dispatcher for vector-matrix
template<class Ind0, class Ind1,
typename T, size_t I, size_t J,
typename std::enable_if<Ind1::Size==2 && Ind0::Size==1 &&
Ind1::values[0] == Ind0::values[0] &&
Ind1::values[1] != Ind0::values[0],bool>::type = 0>
FASTOR_INLINE Tensor<T,J>
einsum(const Tensor<T,I> &a, const Tensor<T,I,J> &b) {
Tensor<T,J> out;
_matmul<T,1,I,J>(a.data(),b.data(),out.data());
return out;
}
// matmul dispatcher for vector-matrix
template<class Ind0, class Ind1,
typename T, size_t I, size_t J,
typename std::enable_if<Ind1::Size==2 && Ind0::Size==1 &&
Ind1::values[1] == Ind0::values[0] &&
Ind1::values[0] != Ind0::values[0],bool>::type = 0>
FASTOR_INLINE Tensor<T,I>
einsum(const Tensor<T,J> &a, const Tensor<T,I,J> &b) {
Tensor<T,I> out;
_matmul<T,I,J,1>(b.data(),a.data(),out.data());
return out;
}
#ifdef FASTOR_AVX_IMPL
// Specific overloads
// With Voigt conversion
template<class Ind0, class Ind1, int Convert,
typename T, size_t I, size_t J, size_t K, size_t L,
typename std::enable_if<(std::is_same<T,float>::value || std::is_same<T,double>::value) &&
I==J && J==K && K==L && (I==2 || I==3) &&
Ind0::NoIndices==2 && Ind1::NoIndices==2 && Convert==FASTOR_Voigt,bool>::type = 0>
FASTOR_INLINE typename VoigtType<T,I,J,K,L>::return_type
einsum(const Tensor<T,I,J> & a, const Tensor<T,K,L> &b) {
using OutTensor = typename VoigtType<T,I,J,K,L>::return_type;
OutTensor out;
constexpr int i = static_cast<int>(Ind0::values[0]);
constexpr int j = static_cast<int>(Ind0::values[1]);
constexpr int k = static_cast<int>(Ind1::values[0]);
constexpr int l = static_cast<int>(Ind1::values[1]);
constexpr bool is_dyadic = i<j && j<k && k<l;
constexpr bool is_cyclic = (i<j && i<k && i<l) && j>k && j<l;
static_assert(is_dyadic || is_cyclic, "INCORRECT INPUT FOR EINSUM FUNCTION");
if (is_dyadic) {
_outer<T,I,J,K,L>(a.data(),b.data(),out.data());
}
if (is_cyclic) {
_cyclic<T,I,J,K,L>(a.data(),b.data(),out.data());
}
return out;
}
template<class Ind0, class Ind1, int Convert,
typename T, size_t I, size_t J, size_t K, size_t L,
typename std::enable_if<(!std::is_same<T,float>::value && !std::is_same<T,double>::value) &&
I==J && J==K && K==L && (I==2 || I==3) &&
Ind0::Size==2 && Ind1::Size==2 && Convert==FASTOR_Voigt,bool>::type = 0>
FASTOR_INLINE typename VoigtType<T,I,J,K,L>::return_type
einsum(const Tensor<T,I,J> & a, const Tensor<T,K,L> &b) {
constexpr int i = static_cast<int>(Ind0::values[0]);
constexpr int j = static_cast<int>(Ind0::values[1]);
constexpr int k = static_cast<int>(Ind1::values[0]);
constexpr int l = static_cast<int>(Ind1::values[1]);
constexpr bool is_dyadic = i<j && j<k && k<l;
constexpr bool is_cyclic = (i<j && i<k && i<l) && j>k && j<l;
static_assert(is_dyadic || is_cyclic, "INCORRECT INPUT FOR EINSUM FUNCTION");
using Ind = typename concat_<Ind0,Ind1>::type;
if (is_dyadic) {
auto out = contraction<Ind0,Ind1>(a,b);
return voigt(out);
}
if (is_cyclic) {
auto out = permutation<Ind>(contraction<Ind0,Ind1>(a,b));
return voigt(out);
}
}
#endif
} // end of namespace
#endif // EINSUM_H

View File

@@ -0,0 +1,332 @@
#ifndef EXPLICIT_EINSUM_H
#define EXPLICIT_EINSUM_H
#include "Fastor/tensor_algebra/indicial.h"
#include "Fastor/meta/opmin_meta.h"
#include "Fastor/tensor_algebra/permutation.h"
#include "Fastor/tensor_algebra/permute.h"
#include "Fastor/tensor_algebra/einsum.h"
#include "Fastor/tensor_algebra/network_einsum.h"
#include "Fastor/tensor_algebra/abstract_contraction.h"
namespace Fastor {
#if FASTOR_CXX_VERSION >= 2017
// Single tensor
//-----------------------------------------------------------------------------------------------------------------------//
template<class Index_I, class Index_O,
typename T, size_t ... Rest0>
FASTOR_INLINE
typename permute_helper<internal::permute_mapped_index_t<
typename einsum_helper<Index_I,Tensor<T,Rest0...>>::resulting_index, typename Index_O::parent_type>,
typename einsum_helper<Index_I,Tensor<T,Rest0...>>::resulting_tensor>::resulting_tensor
einsum(const Tensor<T,Rest0...> &a) {
using _einsum_helper = einsum_helper<Index_I,Tensor<T,Rest0...>>;
using resulting_index_einsum = typename _einsum_helper::resulting_index;
using resulting_tensor_einsum = typename _einsum_helper::resulting_tensor;
auto res = einsum<Index_I>(a);
using mapped_index = internal::permute_mapped_index_t<resulting_index_einsum,typename Index_O::parent_type>;
constexpr bool requires_permutation = requires_permute_v<mapped_index, resulting_tensor_einsum>;
FASTOR_IF_CONSTEXPR(!requires_permutation) return res;
return permute<mapped_index>(res);
}
//-----------------------------------------------------------------------------------------------------------------------//
// Two tensor (by-pair)
//-----------------------------------------------------------------------------------------------------------------------//
template<class Index_I, class Index_J, class Index_O,
typename T, size_t ... Rest0, size_t ... Rest1>
FASTOR_INLINE
typename permute_helper<internal::permute_mapped_index_t<
typename einsum_helper<Index_I,Index_J,Tensor<T,Rest0...>,Tensor<T,Rest1...>>::resulting_index, typename Index_O::parent_type>,
typename einsum_helper<Index_I,Index_J,Tensor<T,Rest0...>,Tensor<T,Rest1...>>::resulting_tensor>::resulting_tensor
einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b) {
using _einsum_helper = einsum_helper<Index_I,Index_J,Tensor<T,Rest0...>,Tensor<T,Rest1...>>;
using resulting_index_einsum = typename _einsum_helper::resulting_index;
using resulting_tensor_einsum = typename _einsum_helper::resulting_tensor;
auto res = einsum<Index_I,Index_J>(a,b);
using mapped_index = internal::permute_mapped_index_t<resulting_index_einsum,typename Index_O::parent_type>;
constexpr bool requires_permutation = requires_permute_v<mapped_index, resulting_tensor_einsum>;
FASTOR_IF_CONSTEXPR(!requires_permutation) return res;
return permute<mapped_index>(res);
}
//-----------------------------------------------------------------------------------------------------------------------//
// 3 tensor network
//-----------------------------------------------------------------------------------------------------------------------//
template<class Index_I, class Index_J, class Index_K, class Index_O,
typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2>
FASTOR_INLINE
typename permute_helper<
internal::permute_mapped_index_t<
typename einsum_helper<Index_I,Index_J,Index_K,Tensor<T,Rest0...>,Tensor<T,Rest1...>,Tensor<T,Rest2...>>::resulting_index,
typename Index_O::parent_type
>,
typename einsum_helper<Index_I,Index_J,Index_K,Tensor<T,Rest0...>,Tensor<T,Rest1...>,Tensor<T,Rest2...>>::resulting_tensor>::resulting_tensor
einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b, const Tensor<T,Rest2...> &c) {
using _einsum_helper = einsum_helper<Index_I,Index_J,Index_K,Tensor<T,Rest0...>,Tensor<T,Rest1...>,Tensor<T,Rest2...>>;
using resulting_index_einsum = typename _einsum_helper::resulting_index;
using resulting_tensor_einsum = typename _einsum_helper::resulting_tensor;
auto res = einsum<Index_I,Index_J,Index_K>(a,b,c);
using mapped_index = internal::permute_mapped_index_t<resulting_index_einsum,typename Index_O::parent_type>;
constexpr bool requires_permutation = requires_permute_v<mapped_index, resulting_tensor_einsum>;
FASTOR_IF_CONSTEXPR(!requires_permutation) return res;
return permute<mapped_index>(res);
}
//-----------------------------------------------------------------------------------------------------------------------//
// 4 tensor network
//-----------------------------------------------------------------------------------------------------------------------//
template<class Index_I, class Index_J, class Index_K, class Index_L, class Index_O,
typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2, size_t ... Rest3>
FASTOR_INLINE
typename permute_helper<
internal::permute_mapped_index_t<
typename einsum_helper<Index_I,Index_J,Index_K,Index_L,
Tensor<T,Rest0...>,Tensor<T,Rest1...>,Tensor<T,Rest2...>,Tensor<T,Rest3...>>::resulting_index,
typename Index_O::parent_type
>,
typename einsum_helper<Index_I,Index_J,Index_K,Index_L,
Tensor<T,Rest0...>,Tensor<T,Rest1...>,Tensor<T,Rest2...>,Tensor<T,Rest3...>>::resulting_tensor>::resulting_tensor
einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b, const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d) {
using _einsum_helper = einsum_helper<Index_I,Index_J,Index_K,Index_L,
Tensor<T,Rest0...>,Tensor<T,Rest1...>,Tensor<T,Rest2...>,Tensor<T,Rest3...>>;
using resulting_index_einsum = typename _einsum_helper::resulting_index;
using resulting_tensor_einsum = typename _einsum_helper::resulting_tensor;
auto res = einsum<Index_I,Index_J,Index_K,Index_L>(a,b,c,d);
using mapped_index = internal::permute_mapped_index_t<resulting_index_einsum,typename Index_O::parent_type>;
constexpr bool requires_permutation = requires_permute_v<mapped_index, resulting_tensor_einsum>;
FASTOR_IF_CONSTEXPR(!requires_permutation) return res;
return permute<mapped_index>(res);
}
//-----------------------------------------------------------------------------------------------------------------------//
// 5 tensor network
//-----------------------------------------------------------------------------------------------------------------------//
template<class Index_I, class Index_J, class Index_K, class Index_L, class Index_M, class Index_O,
typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2, size_t ... Rest3, size_t ... Rest4>
FASTOR_INLINE
typename permute_helper<
internal::permute_mapped_index_t<
typename einsum_helper<Index_I,Index_J,Index_K,Index_L,Index_M,
Tensor<T,Rest0...>,Tensor<T,Rest1...>,Tensor<T,Rest2...>,Tensor<T,Rest3...>,Tensor<T,Rest4...>>::resulting_index,
typename Index_O::parent_type
>,
typename einsum_helper<Index_I,Index_J,Index_K,Index_L,Index_M,
Tensor<T,Rest0...>,Tensor<T,Rest1...>,Tensor<T,Rest2...>,Tensor<T,Rest3...>,Tensor<T,Rest4...>>::resulting_tensor
>::resulting_tensor
einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b, const Tensor<T,Rest2...> &c,
const Tensor<T,Rest3...> &d, const Tensor<T,Rest4...> &e) {
using _einsum_helper = einsum_helper<Index_I,Index_J,Index_K,Index_L,Index_M,
Tensor<T,Rest0...>,Tensor<T,Rest1...>,Tensor<T,Rest2...>,Tensor<T,Rest3...>,Tensor<T,Rest4...>>;
using resulting_index_einsum = typename _einsum_helper::resulting_index;
using resulting_tensor_einsum = typename _einsum_helper::resulting_tensor;
auto res = einsum<Index_I,Index_J,Index_K,Index_L,Index_M>(a,b,c,d,e);
using mapped_index = internal::permute_mapped_index_t<resulting_index_einsum,typename Index_O::parent_type>;
constexpr bool requires_permutation = requires_permute_v<mapped_index, resulting_tensor_einsum>;
FASTOR_IF_CONSTEXPR(!requires_permutation) return res;
return permute<mapped_index>(res);
}
//-----------------------------------------------------------------------------------------------------------------------//
// 6 tensor network
//-----------------------------------------------------------------------------------------------------------------------//
template<class Index_I, class Index_J, class Index_K, class Index_L, class Index_M, class Index_N, class Index_O,
typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2, size_t ... Rest3, size_t ... Rest4, size_t ... Rest5>
FASTOR_INLINE
typename permute_helper<
internal::permute_mapped_index_t<
typename einsum_helper<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N,
Tensor<T,Rest0...>,Tensor<T,Rest1...>,Tensor<T,Rest2...>,Tensor<T,Rest3...>,Tensor<T,Rest4...>,Tensor<T,Rest5...>>::resulting_index,
typename Index_O::parent_type
>,
typename einsum_helper<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N,
Tensor<T,Rest0...>,Tensor<T,Rest1...>,Tensor<T,Rest2...>,Tensor<T,Rest3...>,Tensor<T,Rest4...>,Tensor<T,Rest5...>>::resulting_tensor
>::resulting_tensor
einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b, const Tensor<T,Rest2...> &c,
const Tensor<T,Rest3...> &d, const Tensor<T,Rest4...> &e, const Tensor<T,Rest5...> &f) {
using _einsum_helper = einsum_helper<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N,
Tensor<T,Rest0...>,Tensor<T,Rest1...>,Tensor<T,Rest2...>,Tensor<T,Rest3...>,Tensor<T,Rest4...>,Tensor<T,Rest5...>>;
using resulting_index_einsum = typename _einsum_helper::resulting_index;
using resulting_tensor_einsum = typename _einsum_helper::resulting_tensor;
auto res = einsum<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N>(a,b,c,d,e,f);
using mapped_index = internal::permute_mapped_index_t<resulting_index_einsum,typename Index_O::parent_type>;
constexpr bool requires_permutation = requires_permute_v<mapped_index, resulting_tensor_einsum>;
FASTOR_IF_CONSTEXPR(!requires_permutation) return res;
return permute<mapped_index>(res);
}
//-----------------------------------------------------------------------------------------------------------------------//
// network einsum for expressions
//-----------------------------------------------------------------------------------------------------------------------//
// single expression explicit einsum
template<class Index_I, class Index_O,
typename AbstractTensorType0, enable_if_t_<!is_tensor_v<AbstractTensorType0>,bool> = false>
FASTOR_INLINE
decltype(auto)
einsum(const AbstractTensorType0& a)
{
decltype(auto) tmp = evaluate(a);
auto res = einsum<Index_I>(tmp);
using resulting_index_einsum = typename einsum_helper<Index_I,decltype(tmp)>::resulting_index;
using mapped_index = internal::permute_mapped_index_t<resulting_index_einsum,typename Index_O::parent_type>;
constexpr bool requires_permutation = requires_permute_v<mapped_index, decltype(res)>;
FASTOR_IF_CONSTEXPR(!requires_permutation) return res;
return permute<mapped_index>(res);
}
// pair expression explicit einsum
template<class Index_I, class Index_J, class Index_O,
typename Derived0, typename Derived1, size_t DIM0, size_t DIM1>
FASTOR_INLINE
decltype(auto)
einsum(const AbstractTensor<Derived0,DIM0>& a, const AbstractTensor<Derived1,DIM1>& b)
{
auto res = einsum<Index_I,Index_J>(a.self(),b.self());
using ttype0 = typename Derived0::resulting_type;
using ttype1 = typename Derived1::resulting_type;
using resulting_index_einsum = typename einsum_helper<Index_I,Index_J,ttype0,ttype1>::resulting_index;
using mapped_index = internal::permute_mapped_index_t<resulting_index_einsum,typename Index_O::parent_type>;
constexpr bool requires_permutation = requires_permute_v<mapped_index, decltype(res)>;
FASTOR_IF_CONSTEXPR(!requires_permutation) return res;
return permute<mapped_index>(res);
}
// 3 tensor network expression explicit einsum
template<class Index_I, class Index_J, class Index_K, class Index_O,
typename Derived0, typename Derived1, typename Derived2, size_t DIM0, size_t DIM1, size_t DIM2>
FASTOR_INLINE
decltype(auto)
einsum(const AbstractTensor<Derived0,DIM0>& a, const AbstractTensor<Derived1,DIM1>& b, const AbstractTensor<Derived2,DIM2>& c)
{
auto res = einsum<Index_I,Index_J,Index_K>(a.self(),b.self(),c.self());
using ttype0 = typename Derived0::resulting_type;
using ttype1 = typename Derived1::resulting_type;
using ttype2 = typename Derived2::resulting_type;
using resulting_index_einsum = typename einsum_helper<Index_I,Index_J,Index_K,ttype0,ttype1,ttype2>::resulting_index;
using mapped_index = internal::permute_mapped_index_t<resulting_index_einsum,typename Index_O::parent_type>;
constexpr bool requires_permutation = requires_permute_v<mapped_index, decltype(res)>;
FASTOR_IF_CONSTEXPR(!requires_permutation) return res;
return permute<mapped_index>(res);
}
// 4 tensor network expression explicit einsum
template<class Index_I, class Index_J, class Index_K, class Index_L, class Index_O,
typename Derived0, typename Derived1, typename Derived2, typename Derived3,
size_t DIM0, size_t DIM1, size_t DIM2, size_t DIM3>
FASTOR_INLINE
decltype(auto)
einsum(
const AbstractTensor<Derived0,DIM0>& a, const AbstractTensor<Derived1,DIM1>& b,
const AbstractTensor<Derived2,DIM2>& c, const AbstractTensor<Derived3,DIM3>& d)
{
auto res = einsum<Index_I,Index_J,Index_K,Index_L>(a.self(),b.self(),c.self(),d.self());
using ttype0 = typename Derived0::resulting_type;
using ttype1 = typename Derived1::resulting_type;
using ttype2 = typename Derived2::resulting_type;
using ttype3 = typename Derived3::resulting_type;
using resulting_index_einsum = typename einsum_helper<Index_I,Index_J,Index_K,Index_L,ttype0,ttype1,ttype2,ttype3>::resulting_index;
using mapped_index = internal::permute_mapped_index_t<resulting_index_einsum,typename Index_O::parent_type>;
constexpr bool requires_permutation = requires_permute_v<mapped_index, decltype(res)>;
FASTOR_IF_CONSTEXPR(!requires_permutation) return res;
return permute<mapped_index>(res);
}
// 5 tensor network expression explicit einsum
template<class Index_I, class Index_J, class Index_K, class Index_L, class Index_M, class Index_O,
typename Derived0, typename Derived1, typename Derived2, typename Derived3, typename Derived4,
size_t DIM0, size_t DIM1, size_t DIM2, size_t DIM3, size_t DIM4>
FASTOR_INLINE
decltype(auto)
einsum(
const AbstractTensor<Derived0,DIM0>& a, const AbstractTensor<Derived1,DIM1>& b,
const AbstractTensor<Derived2,DIM2>& c, const AbstractTensor<Derived3,DIM3>& d,
const AbstractTensor<Derived4,DIM4>& e)
{
auto res = einsum<Index_I,Index_J,Index_K,Index_L,Index_M>(a.self(),b.self(),c.self(),d.self(),e.self());
using ttype0 = typename Derived0::resulting_type;
using ttype1 = typename Derived1::resulting_type;
using ttype2 = typename Derived2::resulting_type;
using ttype3 = typename Derived3::resulting_type;
using ttype4 = typename Derived4::resulting_type;
using resulting_index_einsum = typename einsum_helper<Index_I,Index_J,Index_K,Index_L,Index_M,
ttype0,ttype1,ttype2,ttype3,ttype4>::resulting_index;
using mapped_index = internal::permute_mapped_index_t<resulting_index_einsum,typename Index_O::parent_type>;
constexpr bool requires_permutation = requires_permute_v<mapped_index, decltype(res)>;
FASTOR_IF_CONSTEXPR(!requires_permutation) return res;
return permute<mapped_index>(res);
}
// 6 tensor network expression explicit einsum
template<class Index_I, class Index_J, class Index_K, class Index_L, class Index_M, class Index_N, class Index_O,
typename Derived0, typename Derived1, typename Derived2, typename Derived3, typename Derived4, typename Derived5,
size_t DIM0, size_t DIM1, size_t DIM2, size_t DIM3, size_t DIM4, size_t DIM5>
FASTOR_INLINE
decltype(auto)
einsum(
const AbstractTensor<Derived0,DIM0>& a, const AbstractTensor<Derived1,DIM1>& b,
const AbstractTensor<Derived2,DIM2>& c, const AbstractTensor<Derived3,DIM3>& d,
const AbstractTensor<Derived4,DIM4>& e, const AbstractTensor<Derived5,DIM5>& f)
{
auto res = einsum<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N>(a.self(),b.self(),c.self(),d.self(),e.self(),f.self());
using ttype0 = typename Derived0::resulting_type;
using ttype1 = typename Derived1::resulting_type;
using ttype2 = typename Derived2::resulting_type;
using ttype3 = typename Derived3::resulting_type;
using ttype4 = typename Derived4::resulting_type;
using ttype5 = typename Derived5::resulting_type;
using resulting_index_einsum = typename einsum_helper<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N,
ttype0,ttype1,ttype2,ttype3,ttype4,ttype5>::resulting_index;
using mapped_index = internal::permute_mapped_index_t<resulting_index_einsum,typename Index_O::parent_type>;
constexpr bool requires_permutation = requires_permute_v<mapped_index, decltype(res)>;
FASTOR_IF_CONSTEXPR(!requires_permutation) return res;
return permute<mapped_index>(res);
}
#if !defined(FASTOR_MSVC)
template<class Index_I, class ... Index_Ks, class Index_O,
typename AbstractTensorType0, typename ... AbstractTensorTypes,
enable_if_t_<sizeof...(AbstractTensorTypes) >= 6, bool > = false>
FASTOR_INLINE
decltype(auto)
einsum(const AbstractTensorType0& a, const AbstractTensorTypes& ... rest)
{
auto res = internal::unpack_einsum_tuple<Index_I,Index_Ks...>::apply(internal::contraction_chain_evaluate(a,rest...));
auto res_idx = internal::unpack_einsum_helper_tuple<Index_I,Index_Ks...>::apply(internal::contraction_chain_evaluate(a,rest...));
using mapped_index = internal::permute_mapped_index_t<decltype(res_idx),typename Index_O::parent_type>;
constexpr bool requires_permutation = requires_permute_v<mapped_index, decltype(res)>;
FASTOR_IF_CONSTEXPR(!requires_permutation) return res;
return permute<mapped_index>(res);
}
#endif
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
#endif // CXX 2017
} // end of namespace
#endif // EXPLICIT_EINSUM_H

View File

@@ -0,0 +1,66 @@
#ifndef INDICIAL_H
#define INDICIAL_H
#include "Fastor/config/config.h"
#include "Fastor/meta/einsum_meta.h"
#include <array>
namespace Fastor {
//-----------------------------------------------------------------------------------------------------------//
template <FASTOR_INDEX ... All>
struct Index {
static constexpr FASTOR_INDEX Size = sizeof...(All);
static constexpr std::array<FASTOR_INDEX,sizeof...(All)> values = {All...};
};
template<FASTOR_INDEX ... All>
constexpr FASTOR_INDEX Index<All...>::Size;
template<FASTOR_INDEX ... All>
constexpr std::array<FASTOR_INDEX,sizeof...(All)> Index<All...>::values;
namespace internal {
template<FASTOR_INDEX N, class seq>
struct make_index_impl;
template<FASTOR_INDEX N, size_t ... ss>
struct make_index_impl<N,std_ext::index_sequence<ss...>> {
using type = Index<ss...>;
};
} // internal
template<FASTOR_INDEX N>
struct make_index {
using type = typename internal::make_index_impl<N,typename std_ext::make_index_sequence<N>::type>::type;
};
template<FASTOR_INDEX N>
using make_index_t = typename make_index<N>::type;
template<FASTOR_INDEX ... All>
struct OIndex : public Index<All...> {
static constexpr FASTOR_INDEX Size = sizeof...(All);
using parent_type = Index<All...>;
};
template<FASTOR_INDEX ... All>
constexpr FASTOR_INDEX OIndex<All...>::Size;
template<class Idx>
struct to_oindex;
template<FASTOR_INDEX ... All>
struct to_oindex<Index<All...>>
{
using type = OIndex<All...>;
};
template<class Idx>
using to_oindex_t = typename to_oindex<Idx>::type;
//-----------------------------------------------------------------------------------------------------------//
} // end of namespace Fastor
#endif // INDICIAL_H

View File

@@ -0,0 +1,149 @@
#ifndef INNERPRODUCT_H
#define INNERPRODUCT_H
#include "Fastor/backend/doublecontract.h"
#include "Fastor/tensor/Tensor.h"
#include "Fastor/tensor/TensorTraits.h"
namespace Fastor {
// Inner products - reduction to a scalar
//----------------------------------------------------------------------------------------------
template<typename T, size_t ... Rest>
T inner(const Tensor<T,Rest...> &a) {
//! Reduces a multi-dimensional tensor to a scalar
//!
//! If a is scalar/Tensor<T> returns the value itself
//! If a is a vector Tensor<T,N> returns the sum of values
//! If a is a second order tensor Tensor<T,N,N> returns the trace
//! If a is a third order tensor Tensor<T,N,N,N> returns a_iii
//! ...
//!
//! The size of the tensor in all dimensions should be equal (uniform)
static_assert(no_of_unique<Rest...>::value<=1, "REDUCTION IS ONLY POSSIBLE ON UNIFORM TENSORS");
constexpr int ndim = sizeof...(Rest);
T *a_data = a.data();
if (ndim==0) {
return a_data[0];
}
else if (ndim==1) {
return a.sum();
}
else {
constexpr std::array<size_t,ndim> products = nprods<Index<Rest...>,
typename std_ext::make_index_sequence<ndim>::type>::values;
T reductor = static_cast<T>(0);
for (size_t i=0; i<a.dimension(0); ++i) {
size_t index_a = i;
for(size_t it = 0; it< ndim; it++) {
index_a += products[it]*i;
}
reductor += a_data[index_a];
}
return reductor;
}
}
template<typename T, size_t ... Rest>
FASTOR_INLINE T inner(const Tensor<T,Rest...> &a, const Tensor<T,Rest...> &b) {
//! Reduction of a tensor pair to a scalar, for instance A_ijklm * B_ijklm
//! If a and b are scalars/vectors, returns dot product
//! If a and b are matrices, returns double contraction
//! For third order tensors returns a_ijk*b_ijk
//! ...
const T *a_data = a.data();
const T *b_data = b.data();
constexpr size_t ndim = sizeof...(Rest);
FASTOR_IF_CONSTEXPR (ndim>0) {
return _doublecontract<T,pack_prod<Rest...>::value,1>(a_data,b_data);
}
else {
return (*a_data)*(*b_data);
}
}
// Expressions
//---------------------------------------------------------------------------------------------------
template<typename Derived0, size_t DIM0>
FASTOR_INLINE
typename Derived0::scalar_type
inner(const AbstractTensor<Derived0,DIM0> &a) {
using result_type = typename Derived0::result_type;
return inner(result_type(a));
}
template<typename Derived0, size_t DIM0, typename Derived1, size_t DIM1,
enable_if_t_<!is_tensor_v<Derived0> && !is_tensor_v<Derived1>,bool> = false >
FASTOR_INLINE
typename Derived0::scalar_type
inner(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b) {
using lhs_type = typename Derived0::result_type;
using rhs_type = typename Derived1::result_type;
return inner(lhs_type(a),rhs_type(b));
}
template<typename Derived0, size_t DIM0, typename Derived1, size_t DIM1,
enable_if_t_<!is_tensor_v<Derived0> && is_tensor_v<Derived1>,bool> = false >
FASTOR_INLINE
typename Derived0::scalar_type
inner(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b) {
using lhs_type = typename Derived0::result_type;
return inner(lhs_type(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> = false >
FASTOR_INLINE
typename Derived0::scalar_type
inner(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b) {
using rhs_type = typename Derived1::result_type;
return inner(a.self(),rhs_type(b));
}
// multiple chained expressions - network innerproduct
//---------------------------------------------------------------------------------------------------
#if FASTOR_CXX_VERSION >= 2014
namespace internal {
template<typename AbstractTensorType0>
FASTOR_INLINE
auto
innerproduct_chain_expression(const AbstractTensorType0& a)
-> decltype(a.self())
{
return a.self();
}
template<typename AbstractTensorType0, typename AbstractTensorType1, typename ... AbstractTensorTypes>
FASTOR_INLINE
auto
innerproduct_chain_expression(const AbstractTensorType0& a, const AbstractTensorType1& b, const AbstractTensorTypes& ... rest)
// -> decltype(innerproduct_chain_expression(evaluate(a*b),rest...))
{
const auto src = evaluate(a*b);
return innerproduct_chain_expression(src,rest...);
}
} // internal
template<typename AbstractTensorType0, typename AbstractTensorType1, typename ... AbstractTensorTypes,
enable_if_t_<sizeof...(AbstractTensorTypes) >= 1,bool> = false>
FASTOR_INLINE
auto
inner(const AbstractTensorType0& a, const AbstractTensorType1& b, const AbstractTensorTypes& ... rest)
-> decltype(internal::innerproduct_chain_expression(a,b,rest...).sum())
{
return internal::innerproduct_chain_expression(a,b,rest...).sum();
}
#endif
//---------------------------------------------------------------------------------------------------
}
#endif // INNERPRODUCT_H

View File

@@ -0,0 +1,448 @@
#ifndef NETWORK_CONTRACTION_H
#define NETWORK_CONTRACTION_H
#ifndef FASTOR_DONT_PERFORM_OP_MIN
#include "Fastor/tensor_algebra/einsum.h"
#include "Fastor/meta/opmin_meta.h"
namespace Fastor {
// Three tensor network
//---------------------------------------------------------------------------------------------------------------------//
template<class T, class U, class V>
struct extractor_contract_3 {};
template<size_t ... Idx0, size_t ... Idx1, size_t ... Idx2>
struct extractor_contract_3<Index<Idx0...>, Index<Idx1...>, Index<Idx2...> > {
template<typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2>
static
typename contraction_impl<
Index<Idx0...,Idx1...,Idx2...>, Tensor<T,Rest0...,Rest1...,Rest2...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)+sizeof...(Rest1)+sizeof...(Rest2)>::type>::type
FASTOR_INLINE
contract_impl(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b, const Tensor<T,Rest2...> &c) {
using cost_model = triplet_flop_cost<Index<Idx0...>,Index<Idx1...>,Index<Idx2...>,
Tensor<T,Rest0...>,Tensor<T,Rest1...>,Tensor<T,Rest2...>>;
using resulting_index_0 = typename cost_model::resulting_index_0;
using resulting_index_1 = typename cost_model::resulting_index_1;
using resulting_index_2 = typename cost_model::resulting_index_2;
#ifndef FASTOR_KEEP_DP_FIXED
constexpr int which_variant = cost_model::which_variant;
FASTOR_IF_CONSTEXPR (which_variant == 0) {
auto tmp = einsum<Index<Idx0...>,Index<Idx1...>>(a,b);
return einsum<resulting_index_0,Index<Idx2...>>(tmp,c);
}
else FASTOR_IF_CONSTEXPR (which_variant == 1) {
auto tmp = einsum<Index<Idx0...>,Index<Idx2...>>(a,c);
return einsum<Index<Idx1...>,resulting_index_1>(b,tmp);
}
else {
auto tmp = einsum<Index<Idx1...>,Index<Idx2...>>(b,c);
return einsum<Index<Idx0...>,resulting_index_2>(a,tmp);
}
#else
// for benchmarks
auto tmp = einsum<Index<Idx1...>,Index<Idx2...>>(b,c);
return einsum<Index<Idx0...>,resulting_index_2>(a,tmp);
#endif
}
};
template<class Index_I, class Index_J, class Index_K,
typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2>
FASTOR_INLINE
auto contraction(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b, const Tensor<T,Rest2...> &c)
-> decltype(extractor_contract_3<Index_I,Index_J,Index_K>::contract_impl(a,b,c)) {
return extractor_contract_3<Index_I,Index_J,Index_K>::contract_impl(a,b,c);
}
//---------------------------------------------------------------------------------------------------------------------//
// Four tensor network
//---------------------------------------------------------------------------------------------------------------------//
template<class T, class U, class V, class W>
struct extractor_contract_4 {};
template<size_t ... Idx0, size_t ... Idx1, size_t ... Idx2, size_t ... Idx3>
struct extractor_contract_4<Index<Idx0...>, Index<Idx1...>, Index<Idx2...>, Index<Idx3...> > {
template<typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2, size_t ... Rest3>
static
typename contraction_impl<
Index<Idx0...,Idx1...,Idx2...,Idx3...>, Tensor<T,Rest0...,Rest1...,Rest2...,Rest3...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)+sizeof...(Rest1)+sizeof...(Rest2)+sizeof...(Rest3)>::type>::type
FASTOR_INLINE
contract_impl(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b, const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d) {
#ifndef FASTOR_KEEP_DP_FIXED
using cost_model = quartet_flop_cost<Index<Idx0...>,Index<Idx1...>,Index<Idx2...>,Index<Idx3...>,
Tensor<T,Rest0...>,Tensor<T,Rest1...>,Tensor<T,Rest2...>,Tensor<T,Rest3...>>;
using resulting_index_0 = typename cost_model::resulting_index_0;
using resulting_index_1 = typename cost_model::resulting_index_1;
using resulting_index_2 = typename cost_model::resulting_index_2;
using resulting_index_3 = typename cost_model::resulting_index_3;
constexpr int which_variant = cost_model::which_variant;
FASTOR_IF_CONSTEXPR (which_variant==0) {
auto tmp = einsum<Index<Idx0...>,Index<Idx1...>,Index<Idx2...>>(a,b,c);
return einsum<resulting_index_0,Index<Idx3...>>(tmp,d);
}
else FASTOR_IF_CONSTEXPR (which_variant==1) {
auto tmp = einsum<Index<Idx0...>,Index<Idx1...>,Index<Idx3...>>(a,b,d);
return einsum<Index<Idx2...>,resulting_index_1>(c,tmp);
}
else FASTOR_IF_CONSTEXPR (which_variant==2) {
auto tmp = einsum<Index<Idx0...>,Index<Idx2...>,Index<Idx3...>>(a,c,d);
return einsum<Index<Idx1...>,resulting_index_2>(b,tmp);
}
else {
auto tmp = einsum<Index<Idx1...>,Index<Idx2...>,Index<Idx3...>>(b,c,d);
return einsum<Index<Idx0...>,resulting_index_3>(a,tmp);
}
#else
// for benchmarks
using resulting_tensor_0 = typename get_resuling_tensor<Index<Idx0...>,Index<Idx1...>,
Tensor<T,Rest0...>,Tensor<T,Rest1...>>::type;
using resulting_index_0 = typename get_resuling_index<Index<Idx0...>,Index<Idx1...>,
Tensor<T,Rest0...>,Tensor<T,Rest1...>>::type;
using resulting_tensor_1 = typename get_resuling_tensor<Index<Idx2...>,Index<Idx3...>,
Tensor<T,Rest2...>,Tensor<T,Rest3...>>::type;
using resulting_index_1 = typename get_resuling_index<Index<Idx2...>,Index<Idx3...>,
Tensor<T,Rest2...>,Tensor<T,Rest3...>>::type;
resulting_tensor_0 tmp0 = einsum<Index<Idx0...>,Index<Idx1...>>(a,b);
resulting_tensor_1 tmp1 = einsum<Index<Idx2...>,Index<Idx3...>>(c,d);
auto res = einsum<resulting_index_0,resulting_index_1>(tmp0,tmp1);
return res;
#endif
}
};
template<class Index_I, class Index_J, class Index_K, class Index_L,
typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2, size_t ... Rest3>
FASTOR_INLINE
auto contraction(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b, const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d)
-> decltype(extractor_contract_4<Index_I,Index_J,Index_K,Index_L>::contract_impl(a,b,c,d)) {
return extractor_contract_4<Index_I,Index_J,Index_K,Index_L>::contract_impl(a,b,c,d);
}
//---------------------------------------------------------------------------------------------------------------------//
// Five tensor network
//---------------------------------------------------------------------------------------------------------------------//
template<class T, class U, class V, class W, class X>
struct extractor_contract_5 {};
template<size_t ... Idx0, size_t ... Idx1, size_t ... Idx2, size_t ... Idx3, size_t ... Idx4>
struct extractor_contract_5<Index<Idx0...>, Index<Idx1...>, Index<Idx2...>, Index<Idx3...>, Index<Idx4...> > {
template<typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2, size_t ... Rest3, size_t ... Rest4>
static
typename contraction_impl<Index<Idx0...,Idx1...,Idx2...,Idx3...,Idx4...>, Tensor<T,Rest0...,Rest1...,Rest2...,Rest3...,Rest4...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)+sizeof...(Rest1)+
sizeof...(Rest2)+sizeof...(Rest3)+sizeof...(Rest4)>::type>::type
FASTOR_INLINE
contract_impl(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b,
const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d,
const Tensor<T,Rest4...> &e) {
using cost_model = quintet_flop_cost<
Index<Idx0...>,Index<Idx1...>,Index<Idx2...>,Index<Idx3...>,Index<Idx4...>,
Tensor<T,Rest0...>,Tensor<T,Rest1...>,Tensor<T,Rest2...>,Tensor<T,Rest3...>,Tensor<T,Rest4...>>;
using resulting_index_0 = typename cost_model::resulting_index_0;
using resulting_index_1 = typename cost_model::resulting_index_1;
using resulting_index_2 = typename cost_model::resulting_index_2;
using resulting_index_3 = typename cost_model::resulting_index_3;
using resulting_index_4 = typename cost_model::resulting_index_4;
constexpr int which_variant = cost_model::which_variant;
FASTOR_IF_CONSTEXPR (which_variant==0) {
auto tmp = einsum<Index<Idx0...>,Index<Idx1...>,Index<Idx2...>,Index<Idx3...>>(a,b,c,d);
return einsum<resulting_index_0,Index<Idx4...>>(tmp,e);
}
else FASTOR_IF_CONSTEXPR (which_variant==1) {
auto tmp = einsum<Index<Idx0...>,Index<Idx1...>,Index<Idx2...>,Index<Idx4...>>(a,b,c,e);
return einsum<Index<Idx3...>,resulting_index_1>(d,tmp);
}
else FASTOR_IF_CONSTEXPR (which_variant==2) {
auto tmp = einsum<Index<Idx0...>,Index<Idx1...>,Index<Idx3...>,Index<Idx4...>>(a,b,d,e);
return einsum<Index<Idx2...>,resulting_index_2>(c,tmp);
}
else FASTOR_IF_CONSTEXPR (which_variant==3) {
auto tmp = einsum<Index<Idx0...>,Index<Idx2...>,Index<Idx3...>,Index<Idx4...>>(a,c,d,e);
return einsum<Index<Idx1...>,resulting_index_3>(b,tmp);
}
else {
auto tmp = einsum<Index<Idx1...>,Index<Idx2...>,Index<Idx3...>,Index<Idx4...>>(b,c,d,e);
return einsum<Index<Idx0...>,resulting_index_4>(a,tmp);
}
}
};
template<class Index_I, class Index_J, class Index_K, class Index_L, class Index_M,
typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2, size_t ... Rest3, size_t ... Rest4>
FASTOR_INLINE
auto contraction(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b,
const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d,
const Tensor<T,Rest4...> &e)
-> decltype(extractor_contract_5<Index_I,Index_J,Index_K,Index_L,Index_M>::contract_impl(a,b,c,d,e)) {
return extractor_contract_5<Index_I,Index_J,Index_K,Index_L,Index_M>::contract_impl(a,b,c,d,e);
}
//---------------------------------------------------------------------------------------------------------------------//
// Six tensor network
//---------------------------------------------------------------------------------------------------------------------//
template<class T, class U, class V, class W, class X, class Y>
struct extractor_contract_6 {};
template<size_t ... Idx0, size_t ... Idx1, size_t ... Idx2, size_t ... Idx3, size_t ... Idx4, size_t ... Idx5>
struct extractor_contract_6<Index<Idx0...>, Index<Idx1...>, Index<Idx2...>, Index<Idx3...>, Index<Idx4...>, Index<Idx5...> > {
template<typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2, size_t ... Rest3, size_t ... Rest4, size_t ... Rest5>
static
typename contraction_impl<Index<Idx0...,Idx1...,Idx2...,Idx3...,Idx4...,Idx5...>, Tensor<T,Rest0...,Rest1...,Rest2...,Rest3...,Rest4...,Rest5...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)+sizeof...(Rest1)+\
sizeof...(Rest2)+sizeof...(Rest3)+sizeof...(Rest4)+sizeof...(Rest5)>::type>::type
FASTOR_INLINE
contract_impl(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b,
const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d,
const Tensor<T,Rest4...> &e, const Tensor<T,Rest5...> &f) {
using cost_model = sixtet_flop_cost<Index<Idx0...>,
Index<Idx1...>,Index<Idx2...>,Index<Idx3...>,Index<Idx4...>,Index<Idx5...>,
Tensor<T,Rest0...>,Tensor<T,Rest1...>,Tensor<T,Rest2...>,Tensor<T,Rest3...>,Tensor<T,Rest4...>,Tensor<T,Rest5...>>;
using resulting_index_0 = typename cost_model::resulting_index_0;
using resulting_index_1 = typename cost_model::resulting_index_1;
using resulting_index_2 = typename cost_model::resulting_index_2;
using resulting_index_3 = typename cost_model::resulting_index_3;
using resulting_index_4 = typename cost_model::resulting_index_4;
using resulting_index_5 = typename cost_model::resulting_index_5;
constexpr int which_variant = cost_model::which_variant;
FASTOR_IF_CONSTEXPR (which_variant==0) {
auto tmp = einsum<Index<Idx0...>,Index<Idx1...>,Index<Idx2...>,Index<Idx3...>,Index<Idx4...>>(a,b,c,d,e);
return einsum<resulting_index_0,Index<Idx5...>>(tmp,f);
}
else FASTOR_IF_CONSTEXPR (which_variant==1) {
auto tmp = einsum<Index<Idx0...>,Index<Idx1...>,Index<Idx2...>,Index<Idx3...>,Index<Idx5...>>(a,b,c,d,f);
return einsum<Index<Idx4...>,resulting_index_1>(e,tmp);
}
else FASTOR_IF_CONSTEXPR (which_variant==2) {
auto tmp = einsum<Index<Idx0...>,Index<Idx1...>,Index<Idx2...>,Index<Idx4...>,Index<Idx5...>>(a,b,c,e,f);
return einsum<Index<Idx3...>,resulting_index_2>(d,tmp);
}
else FASTOR_IF_CONSTEXPR (which_variant==3) {
auto tmp = einsum<Index<Idx0...>,Index<Idx1...>,Index<Idx3...>,Index<Idx4...>,Index<Idx5...>>(a,b,d,e,f);
return einsum<Index<Idx2...>,resulting_index_3>(c,tmp);
}
else FASTOR_IF_CONSTEXPR (which_variant==4) {
auto tmp = einsum<Index<Idx0...>,Index<Idx2...>,Index<Idx3...>,Index<Idx4...>,Index<Idx5...>>(a,c,d,e,f);
return einsum<Index<Idx1...>,resulting_index_4>(b,tmp);
}
else {
auto tmp = einsum<Index<Idx1...>,Index<Idx2...>,Index<Idx3...>,Index<Idx4...>,Index<Idx5...>>(b,c,d,e,f);
return einsum<Index<Idx0...>,resulting_index_5>(a,tmp);
}
}
};
template<class Index_I, class Index_J, class Index_K, class Index_L, class Index_M, class Index_N,
typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2, size_t ... Rest3, size_t ... Rest4, size_t ... Rest5>
FASTOR_INLINE
auto contraction(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b,
const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d,
const Tensor<T,Rest4...> &e, const Tensor<T,Rest5...> &f)
-> decltype(extractor_contract_6<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N>::contract_impl(a,b,c,d,e,f)) {
return extractor_contract_6<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N>::contract_impl(a,b,c,d,e,f);
}
//---------------------------------------------------------------------------------------------------------------------//
// Seven tensor network
//---------------------------------------------------------------------------------------------------------------------//
template<class T, class U, class V, class W, class X, class Y, class Z>
struct extractor_contract_7 {};
template<size_t ... Idx0, size_t ... Idx1, size_t ... Idx2, size_t ... Idx3,
size_t ... Idx4, size_t ... Idx5, size_t ... Idx6>
struct extractor_contract_7<Index<Idx0...>, Index<Idx1...>, Index<Idx2...>,
Index<Idx3...>, Index<Idx4...>, Index<Idx5...>, Index<Idx6...> > {
template<typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2,
size_t ... Rest3, size_t ... Rest4, size_t ... Rest5, size_t ... Rest6>
static
typename contraction_impl<Index<Idx0...,Idx1...,Idx2...,Idx3...,Idx4...,Idx5...,Idx6...>,
Tensor<T,Rest0...,Rest1...,Rest2...,Rest3...,Rest4...,Rest5...,Rest6...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)+sizeof...(Rest1)+\
sizeof...(Rest2)+sizeof...(Rest3)+sizeof...(Rest4)+sizeof...(Rest5)+sizeof...(Rest6)>::type>::type
FASTOR_INLINE
contract_impl(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b,
const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d,
const Tensor<T,Rest4...> &e, const Tensor<T,Rest5...> &f,
const Tensor<T,Rest6...> &g) {
using resulting_tensor_0 = typename get_resuling_tensor<Index<Idx0...>,Index<Idx1...>,
Tensor<T,Rest0...>,Tensor<T,Rest1...>>::type;
using resulting_index_0 = typename get_resuling_index<Index<Idx0...>,Index<Idx1...>,
Tensor<T,Rest0...>,Tensor<T,Rest1...>>::type;
using resulting_tensor_1 = typename get_resuling_tensor<resulting_index_0,Index<Idx2...>,
resulting_tensor_0,Tensor<T,Rest2...>>::type;
using resulting_index_1 = typename get_resuling_index<resulting_index_0,Index<Idx2...>,
resulting_tensor_0,Tensor<T,Rest2...>>::type;
using resulting_tensor_2 = typename get_resuling_tensor<resulting_index_1,Index<Idx3...>,
resulting_tensor_1,Tensor<T,Rest3...>>::type;
using resulting_index_2 = typename get_resuling_index<resulting_index_1,Index<Idx3...>,
resulting_tensor_1,Tensor<T,Rest3...>>::type;
using resulting_tensor_3 = typename get_resuling_tensor<resulting_index_2,Index<Idx4...>,
resulting_tensor_2,Tensor<T,Rest4...>>::type;
using resulting_index_3 = typename get_resuling_index<resulting_index_2,Index<Idx4...>,
resulting_tensor_2,Tensor<T,Rest4...>>::type;
using resulting_tensor_4 = typename get_resuling_tensor<resulting_index_3,Index<Idx5...>,
resulting_tensor_3,Tensor<T,Rest5...>>::type;
using resulting_index_4 = typename get_resuling_index<resulting_index_3,Index<Idx5...>,
resulting_tensor_3,Tensor<T,Rest5...>>::type;
resulting_tensor_0 tmp0 = einsum<Index<Idx0...>,Index<Idx1...>>(a,b);
resulting_tensor_1 tmp1 = einsum<resulting_index_0,Index<Idx2...>>(tmp0,c);
resulting_tensor_2 tmp2 = einsum<resulting_index_1,Index<Idx3...>>(tmp1,d);
resulting_tensor_3 tmp3 = einsum<resulting_index_2,Index<Idx4...>>(tmp2,e);
resulting_tensor_4 tmp4 = einsum<resulting_index_3,Index<Idx5...>>(tmp3,f);
auto tmp5 = einsum<resulting_index_4,Index<Idx6...>>(tmp4,g);
return tmp5;
}
};
template<class Index_I, class Index_J, class Index_K, class Index_L,
class Index_M, class Index_N, class Index_O,
typename T, size_t ... Rest0, size_t ... Rest1,
size_t ... Rest2, size_t ... Rest3, size_t ... Rest4, size_t ... Rest5, size_t ... Rest6>
FASTOR_INLINE
auto contraction(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b,
const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d,
const Tensor<T,Rest4...> &e, const Tensor<T,Rest5...> &f,
const Tensor<T,Rest6...> &g)
-> decltype(extractor_contract_7<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N,Index_O>::contract_impl(a,b,c,d,e,f,g)) {
return extractor_contract_7<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N,Index_O>::contract_impl(a,b,c,d,e,f,g);
}
//---------------------------------------------------------------------------------------------------------------------//
// Eight tensor network
//---------------------------------------------------------------------------------------------------------------------//
template<class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
struct extractor_contract_8 {};
template<size_t ... Idx0, size_t ... Idx1, size_t ... Idx2, size_t ... Idx3,
size_t ... Idx4, size_t ... Idx5, size_t ... Idx6, size_t ... Idx7>
struct extractor_contract_8<Index<Idx0...>, Index<Idx1...>, Index<Idx2...>,
Index<Idx3...>, Index<Idx4...>, Index<Idx5...>, Index<Idx6...>, Index<Idx7...> > {
template<typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2,
size_t ... Rest3, size_t ... Rest4, size_t ... Rest5, size_t ... Rest6, size_t ... Rest7>
static
typename contraction_impl<Index<Idx0...,Idx1...,Idx2...,Idx3...,Idx4...,Idx5...,Idx6...>,
Tensor<T,Rest0...,Rest1...,Rest2...,Rest3...,Rest4...,Rest5...,Rest6...,Rest7...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)+sizeof...(Rest1)+\
sizeof...(Rest2)+sizeof...(Rest3)+sizeof...(Rest4)+sizeof...(Rest5)+\
sizeof...(Rest6)+sizeof...(Rest7)>::type>::type
contract_impl(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b,
const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d,
const Tensor<T,Rest4...> &e, const Tensor<T,Rest5...> &f,
const Tensor<T,Rest6...> &g, const Tensor<T,Rest7...> &h) {
using resulting_tensor_0 = typename get_resuling_tensor<Index<Idx0...>,Index<Idx1...>,
Tensor<T,Rest0...>,Tensor<T,Rest1...>>::type;
using resulting_index_0 = typename get_resuling_index<Index<Idx0...>,Index<Idx1...>,
Tensor<T,Rest0...>,Tensor<T,Rest1...>>::type;
using resulting_tensor_1 = typename get_resuling_tensor<resulting_index_0,Index<Idx2...>,
resulting_tensor_0,Tensor<T,Rest2...>>::type;
using resulting_index_1 = typename get_resuling_index<resulting_index_0,Index<Idx2...>,
resulting_tensor_0,Tensor<T,Rest2...>>::type;
using resulting_tensor_2 = typename get_resuling_tensor<resulting_index_1,Index<Idx3...>,
resulting_tensor_1,Tensor<T,Rest3...>>::type;
using resulting_index_2 = typename get_resuling_index<resulting_index_1,Index<Idx3...>,
resulting_tensor_1,Tensor<T,Rest3...>>::type;
using resulting_tensor_3 = typename get_resuling_tensor<resulting_index_2,Index<Idx4...>,
resulting_tensor_2,Tensor<T,Rest4...>>::type;
using resulting_index_3 = typename get_resuling_index<resulting_index_2,Index<Idx4...>,
resulting_tensor_2,Tensor<T,Rest4...>>::type;
using resulting_tensor_4 = typename get_resuling_tensor<resulting_index_3,Index<Idx5...>,
resulting_tensor_3,Tensor<T,Rest5...>>::type;
using resulting_index_4 = typename get_resuling_index<resulting_index_3,Index<Idx5...>,
resulting_tensor_3,Tensor<T,Rest5...>>::type;
using resulting_tensor_5 = typename get_resuling_tensor<resulting_index_4,Index<Idx6...>,
resulting_tensor_4,Tensor<T,Rest6...>>::type;
using resulting_index_5 = typename get_resuling_index<resulting_index_4,Index<Idx6...>,
resulting_tensor_4,Tensor<T,Rest6...>>::type;
resulting_tensor_0 tmp0 = einsum<Index<Idx0...>,Index<Idx1...>>(a,b);
resulting_tensor_1 tmp1 = einsum<resulting_index_0,Index<Idx2...>>(tmp0,c);
resulting_tensor_2 tmp2 = einsum<resulting_index_1,Index<Idx3...>>(tmp1,d);
resulting_tensor_3 tmp3 = einsum<resulting_index_2,Index<Idx4...>>(tmp2,e);
resulting_tensor_4 tmp4 = einsum<resulting_index_3,Index<Idx5...>>(tmp3,f);
resulting_tensor_5 tmp5 = einsum<resulting_index_4,Index<Idx6...>>(tmp4,g);
auto tmp6 = einsum<resulting_index_5,Index<Idx7...>>(tmp5,h);
return tmp6;
}
};
template<class Index_I, class Index_J, class Index_K, class Index_L,
class Index_M, class Index_N, class Index_O, class Index_P,
typename T, size_t ... Rest0, size_t ... Rest1,
size_t ... Rest2, size_t ... Rest3, size_t ... Rest4, size_t ... Rest5, size_t ... Rest6, size_t ... Rest7>
FASTOR_INLINE
auto contraction(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b,
const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d,
const Tensor<T,Rest4...> &e, const Tensor<T,Rest5...> &f,
const Tensor<T,Rest6...> &g, const Tensor<T,Rest7...> &h)
-> decltype(extractor_contract_8<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N,Index_O,Index_P>::contract_impl(a,b,c,d,e,f,g,h)) {
return extractor_contract_8<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N,Index_O,Index_P>::contract_impl(a,b,c,d,e,f,g,h);
}
//---------------------------------------------------------------------------------------------------------------------//
} // end of namespace Fastor
#endif // FASTOR_DONT_PERFORM_OP_MIN
#endif // NETWORK_CONTRACTION_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,254 @@
#ifndef EINSUM_NETWORK_H
#define EINSUM_NETWORK_H
#include "Fastor/tensor_algebra/network_contraction.h"
#include "Fastor/tensor_algebra/network_contraction_no_opmin.h"
namespace Fastor {
// Networks
//-----------------------------------------------------------------------------------------
#ifndef FASTOR_DONT_PERFORM_OP_MIN
// 3
template<class Index_I, class Index_J, class Index_K,
typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2>
FASTOR_INLINE
auto einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b, const Tensor<T,Rest2...> &c)
-> decltype(extractor_contract_3<Index_I,Index_J,Index_K>::contract_impl(a,b,c)) {
static_assert(einsum_index_checker<typename concat_<Index_I,Index_J,Index_K>::type>::value,
"INDICES FOR EINSUM FUNCTION CANNOT APPEAR MORE THAN TWICE. USE CONTRACTION INSTEAD");
return extractor_contract_3<Index_I,Index_J,Index_K>::contract_impl(a,b,c);
}
// 4
template<class Index_I, class Index_J, class Index_K, class Index_L,
typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2, size_t ... Rest3>
FASTOR_INLINE auto einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b, const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d)
-> decltype(extractor_contract_4<Index_I,Index_J,Index_K,Index_L>::contract_impl(a,b,c,d)) {
static_assert(einsum_index_checker<typename concat_<Index_I,Index_J,Index_K,Index_L>::type>::value,
"INDICES FOR EINSUM FUNCTION CANNOT APPEAR MORE THAN TWICE. USE CONTRACTION INSTEAD");
return extractor_contract_4<Index_I,Index_J,Index_K,Index_L>::contract_impl(a,b,c,d);
}
// 5
template<class Index_I, class Index_J, class Index_K, class Index_L, class Index_M,
typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2, size_t ... Rest3, size_t ... Rest4>
FASTOR_INLINE
auto einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b,
const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d,
const Tensor<T,Rest4...> &e)
-> decltype(extractor_contract_5<Index_I,Index_J,Index_K,Index_L,Index_M>::contract_impl(a,b,c,d,e)) {
static_assert(einsum_index_checker<typename concat_<Index_I,Index_J,Index_K,Index_L,Index_M>::type>::value,
"INDICES FOR EINSUM FUNCTION CANNOT APPEAR MORE THAN TWICE. USE CONTRACTION INSTEAD");
return extractor_contract_5<Index_I,Index_J,Index_K,Index_L,Index_M>::contract_impl(a,b,c,d,e);
}
// 6
template<class Index_I, class Index_J, class Index_K, class Index_L, class Index_M, class Index_N,
typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2, size_t ... Rest3, size_t ... Rest4, size_t ... Rest5>
FASTOR_INLINE
auto einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b,
const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d,
const Tensor<T,Rest4...> &e, const Tensor<T,Rest5...> &f)
-> decltype(extractor_contract_6<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N>::contract_impl(a,b,c,d,e,f)) {
static_assert(einsum_index_checker<typename concat_<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N>::type>::value,
"INDICES FOR EINSUM FUNCTION CANNOT APPEAR MORE THAN TWICE. USE CONTRACTION INSTEAD");
return extractor_contract_6<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N>::contract_impl(a,b,c,d,e,f);
}
// 7
template<class Index_I, class Index_J, class Index_K, class Index_L, class Index_M, class Index_N, class Index_O,
typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2,
size_t ... Rest3, size_t ... Rest4, size_t ... Rest5, size_t ... Rest6>
FASTOR_INLINE
auto einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b,
const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d,
const Tensor<T,Rest4...> &e, const Tensor<T,Rest5...> &f,
const Tensor<T,Rest6...> &g)
-> decltype(extractor_contract_7<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N,Index_O>::contract_impl(a,b,c,d,e,f,g)) {
static_assert(einsum_index_checker<typename concat_<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N,Index_O>::type>::value,
"INDICES FOR EINSUM FUNCTION CANNOT APPEAR MORE THAN TWICE. USE CONTRACTION INSTEAD");
return extractor_contract_7<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N,Index_O>::contract_impl(a,b,c,d,e,f,g);
}
// 8
template<class Index_I, class Index_J, class Index_K, class Index_L, class Index_M, class Index_N, class Index_O, class Index_P,
typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2,
size_t ... Rest3, size_t ... Rest4, size_t ... Rest5, size_t ... Rest6, size_t ... Rest7>
FASTOR_INLINE
auto einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b,
const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d,
const Tensor<T,Rest4...> &e, const Tensor<T,Rest5...> &f,
const Tensor<T,Rest6...> &g, const Tensor<T,Rest7...> &h)
-> decltype(extractor_contract_8<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N,Index_O,Index_P>::contract_impl(a,b,c,d,e,f,g,h)) {
static_assert(einsum_index_checker<typename concat_<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N,Index_O,Index_P>::type>::value,
"INDICES FOR EINSUM FUNCTION CANNOT APPEAR MORE THAN TWICE. USE CONTRACTION INSTEAD");
return extractor_contract_8<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N,Index_O,Index_P>::contract_impl(a,b,c,d,e,f,g,h);
}
#else
// No operation minimisation
//------------------------------------------------------------------------------------------------------------------//
// 3
template<class Index_I, class Index_J, class Index_K,
typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2>
FASTOR_INLINE
auto einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b, const Tensor<T,Rest2...> &c)
-> decltype(extractor_contract_3_no_opt<Index_I,Index_J,Index_K>::contract_impl(a,b,c)) {
static_assert(einsum_index_checker<typename concat_<Index_I,Index_J,Index_K>::type>::value,
"INDICES FOR EINSUM FUNCTION CANNOT APPEAR MORE THAN TWICE. USE CONTRACTION INSTEAD");
return extractor_contract_3_no_opt<Index_I,Index_J,Index_K>::contract_impl(a,b,c);
}
// 4
template<class Index_I, class Index_J, class Index_K, class Index_L,
typename T, size_t ... Rest0, size_t ... Rest1,
size_t ... Rest2, size_t ... Rest3>
FASTOR_INLINE
auto einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b,
const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d)
-> decltype(extractor_contract_4_no_opt<Index_I,Index_J,Index_K,Index_L>::contract_impl(a,b,c,d)) {
static_assert(einsum_index_checker<typename concat_<Index_I,Index_J,Index_K,Index_L>::type>::value,
"INDICES FOR EINSUM FUNCTION CANNOT APPEAR MORE THAN TWICE. USE CONTRACTION INSTEAD");
return extractor_contract_4_no_opt<Index_I,Index_J,Index_K,Index_L>::contract_impl(a,b,c,d);
}
// 5
template<class Index_I, class Index_J, class Index_K, class Index_L, class Index_M,
typename T, size_t ... Rest0, size_t ... Rest1,
size_t ... Rest2, size_t ... Rest3, size_t ... Rest4>
FASTOR_INLINE
auto einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b,
const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d,
const Tensor<T,Rest4...> &e)
-> decltype(extractor_contract_5_no_opt<Index_I,Index_J,Index_K,Index_L,Index_M>::contract_impl(a,b,c,d,e)) {
static_assert(einsum_index_checker<typename concat_<Index_I,Index_J,Index_K,Index_L,Index_M>::type>::value,
"INDICES FOR EINSUM FUNCTION CANNOT APPEAR MORE THAN TWICE. USE CONTRACTION INSTEAD");
return extractor_contract_5_no_opt<Index_I,Index_J,Index_K,Index_L,Index_M>::contract_impl(a,b,c,d,e);
}
// 6
template<class Index_I, class Index_J, class Index_K, class Index_L, class Index_M, class Index_N,
typename T, size_t ... Rest0, size_t ... Rest1,
size_t ... Rest2, size_t ... Rest3, size_t ... Rest4, size_t ... Rest5>
FASTOR_INLINE
auto einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b,
const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d,
const Tensor<T,Rest4...> &e, const Tensor<T,Rest5...> &f)
-> decltype(extractor_contract_6_no_opt<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N>::contract_impl(a,b,c,d,e,f)) {
static_assert(einsum_index_checker<typename concat_<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N>::type>::value,
"INDICES FOR EINSUM FUNCTION CANNOT APPEAR MORE THAN TWICE. USE CONTRACTION INSTEAD");
return extractor_contract_6_no_opt<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N>::contract_impl(a,b,c,d,e,f);
}
// 7
template<class Index_I, class Index_J, class Index_K, class Index_L, class Index_M, class Index_N, class Index_O,
typename T, size_t ... Rest0, size_t ... Rest1,
size_t ... Rest2, size_t ... Rest3, size_t ... Rest4, size_t ... Rest5, size_t ... Rest6>
FASTOR_INLINE
auto einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b,
const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d,
const Tensor<T,Rest4...> &e, const Tensor<T,Rest5...> &f,
const Tensor<T,Rest6...> &g)
-> decltype(extractor_contract_7_no_opt<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N,Index_O>::contract_impl(a,b,c,d,e,f,g)) {
static_assert(einsum_index_checker<typename concat_<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N,Index_O>::type>::value,
"INDICES FOR EINSUM FUNCTION CANNOT APPEAR MORE THAN TWICE. USE CONTRACTION INSTEAD");
return extractor_contract_7_no_opt<Index_I,Index_J,Index_K,Index_L,Index_M,Index_N,Index_O>::contract_impl(a,b,c,d,e,f,g);
}
// 8
template<class Ind0, class Ind1, class Ind2, class Ind3, class Ind4, class Ind5, class Ind6, class Ind7,
typename T, size_t ... Rest0, size_t ... Rest1, size_t ... Rest2, size_t ... Rest3,
size_t ... Rest4, size_t ... Rest5, size_t ... Rest6, size_t ... Rest7>
FASTOR_INLINE
auto einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b,
const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d,
const Tensor<T,Rest4...> &e, const Tensor<T,Rest5...> &f,
const Tensor<T,Rest6...> &g, const Tensor<T,Rest7...> &h)
-> decltype(extractor_contract_8_no_opt<Ind0,Ind1,Ind2,Ind3,Ind4,Ind5,Ind6,Ind7>::contract_impl(a,b,c,d,e,f,g,h)) {
static_assert(einsum_index_checker<typename concat_<Ind0,Ind1,Ind2,Ind3,Ind4,Ind5,Ind6,Ind7>::type>::value,
"INDICES FOR EINSUM FUNCTION CANNOT APPEAR MORE THAN TWICE. USE CONTRACTION INSTEAD");
return extractor_contract_8_no_opt<Ind0,Ind1,Ind2,Ind3,Ind4,Ind5,Ind6,Ind7>::contract_impl(a,b,c,d,e,f,g,h);
}
// 9
template<class Ind0, class Ind1, class Ind2, class Ind3, class Ind4, class Ind5, class Ind6,
class Ind7, class Ind8,
typename T, size_t ... Rest0, size_t ... Rest1,
size_t ... Rest2, size_t ... Rest3, size_t ... Rest4, size_t ... Rest5, size_t ... Rest6,
size_t ... Rest7, size_t ... Rest8>
FASTOR_INLINE
auto einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b,
const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d,
const Tensor<T,Rest4...> &e, const Tensor<T,Rest5...> &f,
const Tensor<T,Rest6...> &g, const Tensor<T,Rest7...> &h,
const Tensor<T,Rest8...> &h1)
-> decltype(extractor_contract_9_no_opt<Ind0,Ind1,Ind2,Ind3,Ind4,Ind5,Ind6,Ind7,Ind8>::contract_impl(a,b,c,d,e,f,g,h,h1)) {
static_assert(einsum_index_checker<typename concat_<Ind0,Ind1,Ind2,Ind3,Ind4,Ind5,Ind6,Ind7,Ind8>::type>::value,
"INDICES FOR EINSUM FUNCTION CANNOT APPEAR MORE THAN TWICE. USE CONTRACTION INSTEAD");
return extractor_contract_9_no_opt<Ind0,Ind1,Ind2,Ind3,Ind4,Ind5,Ind6,Ind7,Ind8>::contract_impl(a,b,c,d,e,f,g,h,h1);
}
// 10
template<class Ind0, class Ind1, class Ind2, class Ind3, class Ind4, class Ind5, class Ind6,
class Ind7, class Ind8, class Ind9,
typename T, size_t ... Rest0, size_t ... Rest1,
size_t ... Rest2, size_t ... Rest3, size_t ... Rest4, size_t ... Rest5, size_t ... Rest6,
size_t ... Rest7, size_t ... Rest8, size_t ... Rest9>
FASTOR_INLINE
auto einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b,
const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d,
const Tensor<T,Rest4...> &e, const Tensor<T,Rest5...> &f,
const Tensor<T,Rest6...> &g, const Tensor<T,Rest7...> &h,
const Tensor<T,Rest8...> &h1, const Tensor<T,Rest9...> &h2)
-> decltype(extractor_contract_10_no_opt<Ind0,Ind1,Ind2,Ind3,Ind4,Ind5,Ind6,Ind7,Ind8,Ind9>::contract_impl(a,b,c,d,e,f,g,h,h1,h2)) {
static_assert(einsum_index_checker<typename concat_<Ind0,Ind1,Ind2,Ind3,Ind4,Ind5,Ind6,Ind7,Ind8,Ind9>::type>::value,
"INDICES FOR EINSUM FUNCTION CANNOT APPEAR MORE THAN TWICE. USE CONTRACTION INSTEAD");
return extractor_contract_10_no_opt<Ind0,Ind1,Ind2,Ind3,Ind4,Ind5,Ind6,Ind7,Ind8,Ind9>::contract_impl(a,b,c,d,e,f,g,h,h1,h2);
}
// 11
template<class Ind0, class Ind1, class Ind2, class Ind3, class Ind4, class Ind5, class Ind6,
class Ind7, class Ind8, class Ind9, class Ind10,
typename T, size_t ... Rest0, size_t ... Rest1,
size_t ... Rest2, size_t ... Rest3, size_t ... Rest4, size_t ... Rest5, size_t ... Rest6,
size_t ... Rest7, size_t ... Rest8, size_t ... Rest9, size_t ... Rest10>
auto einsum(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b,
const Tensor<T,Rest2...> &c, const Tensor<T,Rest3...> &d,
const Tensor<T,Rest4...> &e, const Tensor<T,Rest5...> &f,
const Tensor<T,Rest6...> &g, const Tensor<T,Rest7...> &h,
const Tensor<T,Rest8...> &h1, const Tensor<T,Rest9...> &h2,
const Tensor<T,Rest10...> &h3)
-> decltype(extractor_contract_11_no_opt<Ind0,Ind1,Ind2,Ind3,Ind4,Ind5,Ind6,Ind7,Ind8,Ind9,Ind10>::contract_impl(a,b,c,d,e,f,g,h,h1,h2,h3)) {
static_assert(einsum_index_checker<typename concat_<Ind0,Ind1,Ind2,Ind3,Ind4,Ind5,Ind6,Ind7,Ind8,Ind9,Ind10>::type>::value,
"INDICES FOR EINSUM FUNCTION CANNOT APPEAR MORE THAN TWICE. USE CONTRACTION INSTEAD");
return extractor_contract_11_no_opt<Ind0,Ind1,Ind2,Ind3,Ind4,Ind5,Ind6,Ind7,Ind8,Ind9,Ind10>::contract_impl(a,b,c,d,e,f,g,h,h1,h2,h3);
}
#endif
} // end of namespace Fastor
#endif

View File

@@ -0,0 +1,124 @@
#ifndef OUTERPRODUCT_H
#define OUTERPRODUCT_H
#include "Fastor/backend/dyadic.h"
#include "Fastor/tensor/Tensor.h"
#include "Fastor/tensor/TensorTraits.h"
namespace Fastor {
// These set of functions implement the outer/dyadic products of two or multiple tensor expressions
//---------------------------------------------------------------------------------------------------
template<typename T, size_t ...Rest0, size_t ...Rest1>
FASTOR_INLINE Tensor<T,Rest0...,Rest1...>
outer(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b) {
Tensor<T,Rest0...,Rest1...> out;
_dyadic<T,pack_prod<Rest0...>::value,pack_prod<Rest1...>::value>(a.data(),b.data(),out.data());
return out;
}
template<typename T, size_t ...Rest0>
FASTOR_INLINE Tensor<T,Rest0...>
outer(const Tensor<T,Rest0...> &a, const Tensor<T,1> &b) {
Tensor<T,Rest0...,1> out = a*b.toscalar();
return out;
}
template<typename T, size_t ...Rest1>
FASTOR_INLINE Tensor<T,Rest1...>
outer(const Tensor<T,1> &a, const Tensor<T,Rest1...> &b) {
Tensor<T,1,Rest1...> out = a.toscalar()*b;
return out;
}
template<typename T>
FASTOR_INLINE Tensor<T>
outer(const Tensor<T> &a, const Tensor<T> &b) {
Tensor<T> out;
_dyadic<T,1,1>(a.data(),b.data(),out.data());
return out;
}
//---------------------------------------------------------------------------------------------------
// Expressions
//---------------------------------------------------------------------------------------------------
template<typename Derived0, size_t DIM0, typename Derived1, size_t DIM1,
enable_if_t_<!is_tensor_v<Derived0> && !is_tensor_v<Derived1>,bool> = false >
FASTOR_INLINE
concatenated_tensor_t<typename Derived0::result_type,typename Derived1::result_type>
outer(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b) {
using lhs_type = typename Derived0::result_type;
using rhs_type = typename Derived1::result_type;
return outer(lhs_type(a),rhs_type(b));
}
template<typename Derived0, size_t DIM0, typename Derived1, size_t DIM1,
enable_if_t_<!is_tensor_v<Derived0> && is_tensor_v<Derived1>,bool> = false >
FASTOR_INLINE
concatenated_tensor_t<typename Derived0::result_type,typename Derived1::result_type>
outer(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b) {
using lhs_type = typename Derived0::result_type;
return outer(lhs_type(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> = false >
FASTOR_INLINE
concatenated_tensor_t<typename Derived0::result_type,typename Derived1::result_type>
outer(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b) {
using rhs_type = typename Derived1::result_type;
return outer(a.self(),rhs_type(b));
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
template<typename T, size_t ... Rest0, size_t ... Rest1>
FASTOR_INLINE Tensor<T,Rest0...,Rest1...>
dyadic(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b) {
return outer(a,b);
}
template<typename Derived0, size_t DIM0, typename Derived1, size_t DIM1>
FASTOR_INLINE
concatenated_tensor_t<typename Derived0::result_type,typename Derived1::result_type>
dyadic(const AbstractTensor<Derived0,DIM0> &a, const AbstractTensor<Derived1,DIM1> &b) {
return outer(a,b);
}
//---------------------------------------------------------------------------------------------------
// multiple chained expressions - network outerproduct
//---------------------------------------------------------------------------------------------------
#if FASTOR_CXX_VERSION >= 2014
// template<typename AbstractTensorType0>
// FASTOR_INLINE
// auto
// outer(const AbstractTensorType0& a)
// {
// return a;
// }
template<typename AbstractTensorType0, typename AbstractTensorType1, typename ... AbstractTensorTypes,
enable_if_t_<is_greater_equal_v_<sizeof...(AbstractTensorTypes),1>,bool> = false >
FASTOR_INLINE
auto
outer(const AbstractTensorType0& a, const AbstractTensorType1& b, const AbstractTensorTypes& ... rest)
{
const auto res = outer(a,b);
return outer(res, rest...);
}
template<typename AbstractTensorType0, typename AbstractTensorType1, typename ... AbstractTensorTypes,
enable_if_t_<is_greater_equal_v_<sizeof...(AbstractTensorTypes),1>,bool> = false >
FASTOR_INLINE
auto
dyadic(const AbstractTensorType0& a, const AbstractTensorType1& b, const AbstractTensorTypes& ... rest)
{
return outer(a, b, rest...);
}
#endif
//---------------------------------------------------------------------------------------------------
} // end of namespace Fastor
#endif // OUTERPRODUCT_H

View File

@@ -0,0 +1,356 @@
#ifndef PERMUTATION_H
#define PERMUTATION_H
#include "Fastor/tensor/Tensor.h"
#include "Fastor/tensor/TensorTraits.h"
#include "Fastor/tensor_algebra/indicial.h"
#include "Fastor/meta/einsum_meta.h"
#include "Fastor/expressions/linalg_ops/linalg_traits.h"
namespace Fastor {
namespace internal {
template<class Idx, class Tens, size_t ... Args>
struct RecursiveCartesianPerm;
template<typename T, size_t ...Idx, size_t ...Rest, size_t First, size_t ... Lasts>
struct RecursiveCartesianPerm<Index<Idx...>, Tensor<T,Rest...>, First, Lasts...> {
static constexpr size_t out_dim = sizeof...(Rest);
static
FASTOR_INLINE
void Do(const T *a_data, T *out_data, std::array<size_t,out_dim> &as, std::array<size_t,out_dim> &idx) {
for (size_t i=0; i<First; ++i) {
idx[sizeof...(Lasts)] = i;
RecursiveCartesianPerm<Index<Idx...>, Tensor<T,Rest...>,Lasts...>::Do(a_data, out_data, as, idx);
}
}
};
template<typename T, size_t Last, size_t ...Idx, size_t ...Rest>
struct RecursiveCartesianPerm<Index<Idx...>, Tensor<T,Rest...>,Last>
{
using _permute_impl = permute_impl<Index<Idx...>, Tensor<T,Rest...>,
typename std_ext::make_index_sequence<sizeof...(Idx)>::type>;
using resulting_tensor = typename _permute_impl::resulting_tensor;
using resulting_index = typename _permute_impl::resulting_index;
using maxes_out_type = typename _permute_impl::maxes_out_type;
static constexpr std::array<size_t,sizeof...(Rest)> maxes_idx = resulting_index::values;
static constexpr std::array<size_t,sizeof...(Rest)> maxes_out = maxes_out_type::values;
static constexpr size_t a_dim = sizeof...(Rest);
static constexpr size_t out_dim = a_dim;
static constexpr std::array<size_t,a_dim> maxes_a = {Rest...};
static constexpr std::array<size_t,sizeof...(Rest)> products_a = nprods<Index<Rest...>,
typename std_ext::make_index_sequence<a_dim>::type>::values;
static constexpr std::array<size_t,sizeof...(Rest)> products_out = nprods<maxes_out_type,
typename std_ext::make_index_sequence<a_dim>::type>::values;
static
FASTOR_INLINE
void Do(const T *a_data, T *out_data, std::array<size_t,out_dim> &as, std::array<size_t,out_dim> &idx)
{
constexpr size_t stride = 1;
for (size_t i=0; i<Last; i+=stride) {
idx[0] = i;
std::reverse_copy(idx.begin(),idx.end(),as.begin());
size_t index_a = as[a_dim-1];
for(size_t it = 0; it< a_dim; it++) {
index_a += products_a[it]*as[it];
}
size_t index_out = as[maxes_idx[out_dim-1]];
for(size_t it = 0; it< out_dim-1; it++) {
index_out += products_out[it]*as[maxes_idx[it]];
}
out_data[index_out] = a_data[index_a];
}
}
};
template<typename T, size_t Last, size_t ...Idx, size_t ...Rest>
constexpr std::array<size_t,sizeof...(Rest)> RecursiveCartesianPerm<Index<Idx...>,
Tensor<T,Rest...>,Last>::maxes_idx;
template<typename T, size_t Last, size_t ...Idx, size_t ...Rest>
constexpr std::array<size_t,sizeof...(Rest)> RecursiveCartesianPerm<Index<Idx...>,
Tensor<T,Rest...>,Last>::maxes_out;
template<typename T, size_t Last, size_t ...Idx, size_t ...Rest>
constexpr std::array<size_t,sizeof...(Rest)> RecursiveCartesianPerm<Index<Idx...>,
Tensor<T,Rest...>,Last>::products_a;
template<typename T, size_t Last, size_t ...Idx, size_t ...Rest>
constexpr std::array<size_t,sizeof...(Rest)> RecursiveCartesianPerm<Index<Idx...>,
Tensor<T,Rest...>,Last>::products_out;
// template<typename T, size_t ...Idx, size_t ...Rest>
// struct RecursiveCartesianPerm<Index<Idx...>, Tensor<T,Rest...>>
// {
// using OutTensor = typename permute_impl<Index<Idx...>, Tensor<T,Rest...>,
// typename std_ext::make_index_sequence<sizeof...(Idx)>::type>::type;
// using maxes_out_type = typename permute_impl<Index<Idx...>, Tensor<T,Rest...>,
// typename std_ext::make_index_sequence<sizeof...(Idx)>::type>::maxes_out_type;
// using index_type = typename permute_impl<Index<Idx...>, Tensor<T,Rest...>,
// typename std_ext::make_index_sequence<sizeof...(Idx)>::type>::index_type;
// static constexpr auto maxes_idx = permute_impl<Index<Idx...>, Tensor<T,Rest...>,
// typename std_ext::make_index_sequence<sizeof...(Idx)>::type>::index_type::values;
// static constexpr auto maxes_out = permute_impl<Index<Idx...>, Tensor<T,Rest...>,
// typename std_ext::make_index_sequence<sizeof...(Idx)>::type>::maxes_out_type::values;
// static constexpr int a_dim = sizeof...(Rest);
// static constexpr int out_dim = a_dim;
// static constexpr std::array<int,a_dim> maxes_a = {Rest...};
// static constexpr std::array<size_t,sizeof...(Rest)> products_a = nprods<Index<Rest...>,
// typename std_ext::make_index_sequence<a_dim>::type>::values;
// static constexpr std::array<size_t,sizeof...(Rest)> products_out = nprods<maxes_out_type,
// typename std_ext::make_index_sequence<a_dim>::type>::values;
// static void Do(const T *a_data, T *out_data, std::array<int,out_dim> &as, std::array<int,out_dim> &idx)
// {
// std::reverse_copy(idx.begin(),idx.end(),as.begin());
// int index_a = as[a_dim-1];
// for(int it = 0; it< a_dim; it++) {
// index_a += products_a[it]*as[it];
// }
// int index_out = as[maxes_idx[out_dim-1]];
// for(int it = 0; it< out_dim-1; it++) {
// index_out += products_out[it]*as[maxes_idx[it]];
// }
// out_data[index_out] = a_data[index_a];
// }
// };
// template<typename T, size_t ...Idx, size_t ...Rest>
// constexpr std::array<size_t,sizeof...(Rest)> RecursiveCartesianPerm<Index<Idx...>,
// Tensor<T,Rest...>>::products_a;
// template<typename T, size_t ...Idx, size_t ...Rest>
// constexpr std::array<size_t,sizeof...(Rest)> RecursiveCartesianPerm<Index<Idx...>,
// Tensor<T,Rest...>>::products_out;
template<class Idx, class Tens, class Args>
struct RecursiveCartesianPermDispatcher;
template<typename T, size_t ...Idx, size_t ...Rest, size_t ... Args>
struct RecursiveCartesianPermDispatcher<Index<Idx...>, Tensor<T,Rest...>, Index<Args...> >
{
static constexpr size_t out_dim = sizeof...(Rest);
static FASTOR_INLINE void Do(const T *a_data, T *out_data,
std::array<size_t,out_dim> &as, std::array<size_t,out_dim> &idx) {
return RecursiveCartesianPerm<Index<Idx...>,Tensor<T,Rest...>, Args...>::Do(a_data, out_data, as, idx);
}
};
template<class T>
struct extractor_perm {};
template<size_t ... Idx>
struct extractor_perm<Index<Idx...> > {
template<typename T, size_t ... Rest>
static
FASTOR_INLINE
typename permute_impl<Index<Idx...>, Tensor<T,Rest...>,
typename std_ext::make_index_sequence<sizeof...(Idx)>::type>::resulting_tensor
permutation_impl(const Tensor<T,Rest...> &a) {
using _permute_impl = permute_impl<Index<Idx...>, Tensor<T,Rest...>,
typename std_ext::make_index_sequence<sizeof...(Idx)>::type>;
using resulting_tensor = typename _permute_impl::resulting_tensor;
constexpr bool requires_permutation = _permute_impl::requires_permutation;
FASTOR_IF_CONSTEXPR(!requires_permutation) return a;
#if CONTRACT_OPT==-1
using resulting_index = typename _permute_impl::resulting_index;
using maxes_out_type = typename _permute_impl::maxes_out_type;
constexpr auto& maxes_idx = resulting_index::values;
constexpr auto& maxes_out = maxes_out_type::values;
constexpr int a_dim = sizeof...(Rest);
constexpr int out_dim = a_dim;
constexpr std::array<int,a_dim> maxes_a = {Rest...};
constexpr auto& products_a = nprods<Index<Rest...>,
typename std_ext::make_index_sequence<a_dim>::type>::values;
constexpr auto& products_out = nprods<maxes_out_type,
typename std_ext::make_index_sequence<a_dim>::type>::values;
resulting_tensor out;
out.zeros();
T *a_data = a.data();
T *out_data = out.data();
int as[out_dim] = {};
int it,jt;
while(true)
{
int index_a = as[a_dim-1];
for(it = 0; it< a_dim; it++) {
index_a += products_a[it]*as[it];
}
int index_out = as[maxes_idx[out_dim-1]];
for(it = 0; it< out_dim-1; it++) {
index_out += products_out[it]*as[maxes_idx[it]];
}
out_data[index_out] = a_data[index_a];
for(jt = out_dim-1 ; jt>=0 ; jt--)
{
if(++as[jt]<maxes_a[jt])
break;
else
as[jt]=0;
}
if(jt<0)
break;
}
#else
resulting_tensor out;
const T *a_data = a.data();
T *out_data = out.data();
constexpr size_t out_dim = sizeof...(Rest);
std::array<size_t,out_dim> as = {};
std::array<size_t,out_dim> idx = {};
using nloops = loop_setter<
Index<Idx...>,
Tensor<T,Rest...>,
typename std_ext::make_index_sequence<out_dim>::type>;
using dims_type = typename nloops::dims_type;
RecursiveCartesianPermDispatcher<Index<Idx...>,Tensor<T,Rest...>,dims_type>::Do(a_data,out_data,as,idx);
#endif
return out;
}
// Abstract permutation
template<typename Derived, size_t DIMS,
enable_if_t_<!requires_evaluation_v<Derived>,bool> = false>
static
FASTOR_INLINE
typename permute_impl<
Index<Idx...>, typename Derived::result_type,
typename std_ext::make_index_sequence<sizeof...(Idx)>::type>::resulting_tensor
permutation_impl(const AbstractTensor<Derived,DIMS> &a) {
using T = typename Derived::scalar_type;
using tensor_type = typename Derived::result_type;
using _permute_impl = permute_impl<Index<Idx...>, tensor_type,
typename std_ext::make_index_sequence<sizeof...(Idx)>::type>;
using resulting_tensor = typename _permute_impl::resulting_tensor;
using resulting_index = typename _permute_impl::resulting_index;
using maxes_out_type = typename _permute_impl::maxes_out_type;
constexpr auto& maxes_idx = resulting_index::values;
constexpr int a_dim = DIMS;
constexpr int out_dim = a_dim;
constexpr std::array<int,a_dim> maxes_a = get_tensor_dimensions<tensor_type>::dims_int;
constexpr auto& products_a = nprods<typename get_tensor_dimensions<tensor_type>::tensor_to_index,
typename std_ext::make_index_sequence<a_dim>::type>::values;
constexpr auto& products_out = nprods<maxes_out_type,
typename std_ext::make_index_sequence<a_dim>::type>::values;
resulting_tensor out;
out.zeros();
T *out_data = out.data();
const Derived & a_src = a.self();
int as[out_dim] = {};
int it,jt;
while(true)
{
int index_a = as[a_dim-1];
for(it = 0; it< a_dim; it++) {
index_a += products_a[it]*as[it];
}
int index_out = as[maxes_idx[out_dim-1]];
for(it = 0; it< out_dim-1; it++) {
index_out += products_out[it]*as[maxes_idx[it]];
}
out_data[index_out] = a_src.template eval_s<T>(index_a);
for(jt = out_dim-1 ; jt>=0 ; jt--)
{
if(++as[jt]<maxes_a[jt])
break;
else
as[jt]=0;
}
if(jt<0)
break;
}
return out;
}
};
} // internal
template<class Index_I, typename T, size_t ... Rest>
FASTOR_INLINE
typename internal::permute_impl<Index_I, Tensor<T,Rest...>,
typename std_ext::make_index_sequence<sizeof...(Rest)>::type>::resulting_tensor
permutation(const Tensor<T, Rest...> &a) {
return internal::extractor_perm<Index_I>::permutation_impl(a);
}
template<class Index_I, typename Derived, size_t DIMS,
enable_if_t_<!requires_evaluation_v<Derived>,bool> = false>
FASTOR_INLINE
typename internal::permute_impl<Index_I,
typename Derived::result_type,
typename std_ext::make_index_sequence<DIMS>::type>::resulting_tensor
permutation(const AbstractTensor<Derived, DIMS> &a) {
return internal::extractor_perm<Index_I>::permutation_impl(a.self());
}
template<class Index_I, typename Derived, size_t DIMS,
enable_if_t_<requires_evaluation_v<Derived>,bool> = false>
FASTOR_INLINE
typename internal::permute_impl<Index_I,
typename Derived::result_type,
typename std_ext::make_index_sequence<DIMS>::type>::resulting_tensor
permutation(const AbstractTensor<Derived, DIMS> &a) {
using result_type = typename Derived::result_type;
const result_type tmp(a);
return internal::extractor_perm<Index_I>::permutation_impl(tmp);
}
} // end of namespace Fastor
#endif // PERMUTATION_H

View File

@@ -0,0 +1,397 @@
#ifndef PERMUTE_H
#define PERMUTE_H
#include "Fastor/tensor/Tensor.h"
#include "Fastor/tensor/TensorTraits.h"
#include "Fastor/meta/einsum_meta.h"
#include "Fastor/tensor_algebra/indicial.h"
#include "Fastor/expressions/linalg_ops/linalg_traits.h"
namespace Fastor {
namespace internal {
template<class Idx, class Tens, size_t ... Args>
struct NewRecursiveCartesianPerm;
template<typename T, size_t ...Idx, size_t ...Rest, size_t First, size_t ... Lasts>
struct NewRecursiveCartesianPerm<Index<Idx...>, Tensor<T,Rest...>, First, Lasts...> {
static constexpr size_t out_dim = sizeof...(Rest);
static
FASTOR_INLINE
void Do(const T *a_data, T *out_data, std::array<size_t,out_dim> &as, std::array<size_t,out_dim> &idx) {
for (size_t i=0; i<First; ++i) {
idx[sizeof...(Lasts)] = i;
NewRecursiveCartesianPerm<Index<Idx...>, Tensor<T,Rest...>,Lasts...>::Do(a_data, out_data, as, idx);
}
}
};
template<typename T, size_t Last, size_t ...Idx, size_t ...Rest>
struct NewRecursiveCartesianPerm<Index<Idx...>, Tensor<T,Rest...>,Last>
{
using _permute_impl = new_permute_impl<Index<Idx...>, Tensor<T,Rest...>,
typename std_ext::make_index_sequence<sizeof...(Idx)>::type>;
using resulting_tensor = typename _permute_impl::resulting_tensor;
using resulting_index = typename _permute_impl::resulting_index;
using maxes_out_type = typename put_dims_in_Index<resulting_tensor>::type;
static constexpr size_t a_dim = sizeof...(Rest);
static constexpr size_t out_dim = a_dim;
#if FASTOR_CXX_VERSION >= 2017
using reverse_map = internal::permute_mapped_index_t<Index<Idx...>,make_index_t<a_dim>>;
static constexpr std::array<size_t,sizeof...(Rest)> maxes_idx = reverse_map::values;
#else
static constexpr std::array<size_t,sizeof...(Rest)> maxes_idx = resulting_index::values;
#endif
static constexpr std::array<size_t,sizeof...(Rest)> products_a = nprods<Index<Rest...>,
typename std_ext::make_index_sequence<a_dim>::type>::values;
static constexpr std::array<size_t,sizeof...(Rest)> products_out = nprods<maxes_out_type,
typename std_ext::make_index_sequence<a_dim>::type>::values;
static
FASTOR_INLINE
void Do(const T *a_data, T *out_data, std::array<size_t,out_dim> &as, std::array<size_t,out_dim> &idx)
{
constexpr size_t stride = 1;
for (size_t i = 0; i < Last; i+=stride) {
idx[0] = i;
std::reverse_copy(idx.begin(),idx.end(),as.begin());
#if FASTOR_CXX_VERSION >= 2017
size_t index_a = as[maxes_idx[a_dim-1]];
for(size_t it = 0; it< a_dim; it++) {
index_a += products_a[it]*as[maxes_idx[it]];
}
size_t index_out = as[out_dim-1];
for(size_t it = 0; it< out_dim-1; it++) {
index_out += products_out[it]*as[it];
}
#else
size_t index_a = as[a_dim-1];
for(size_t it = 0; it< a_dim; it++) {
index_a += products_a[it]*as[it];
}
size_t index_out = as[maxes_idx[out_dim-1]];
for(size_t it = 0; it< out_dim-1; it++) {
index_out += products_out[it]*as[maxes_idx[it]];
}
#endif
out_data[index_out] = a_data[index_a];
}
}
};
template<typename T, size_t Last, size_t ...Idx, size_t ...Rest>
constexpr std::array<size_t,sizeof...(Rest)> NewRecursiveCartesianPerm<Index<Idx...>,
Tensor<T,Rest...>,Last>::maxes_idx;
template<typename T, size_t Last, size_t ...Idx, size_t ...Rest>
constexpr std::array<size_t,sizeof...(Rest)> NewRecursiveCartesianPerm<Index<Idx...>,
Tensor<T,Rest...>,Last>::products_a;
template<typename T, size_t Last, size_t ...Idx, size_t ...Rest>
constexpr std::array<size_t,sizeof...(Rest)> NewRecursiveCartesianPerm<Index<Idx...>,
Tensor<T,Rest...>,Last>::products_out;
template<class Idx, class Tens, class Args>
struct NewRecursiveCartesianPermDispatcher;
template<typename T, size_t ...Idx, size_t ...Rest, size_t ... Args>
struct NewRecursiveCartesianPermDispatcher<Index<Idx...>, Tensor<T,Rest...>, Index<Args...> >
{
static constexpr size_t out_dim = sizeof...(Rest);
static FASTOR_INLINE void Do(const T *a_data, T *out_data,
std::array<size_t,out_dim> &as, std::array<size_t,out_dim> &idx) {
return NewRecursiveCartesianPerm<Index<Idx...>,Tensor<T,Rest...>, Args...>::Do(a_data, out_data, as, idx);
}
};
template<class T>
struct new_extractor_perm {};
template<size_t ... Idx>
struct new_extractor_perm<Index<Idx...> > {
template<typename T, size_t ... Rest>
static
FASTOR_INLINE
typename new_permute_impl<Index<Idx...>, Tensor<T,Rest...>,
typename std_ext::make_index_sequence<sizeof...(Idx)>::type>::resulting_tensor
permutation_impl(const Tensor<T,Rest...> &a) {
using _permute_impl = new_permute_impl<Index<Idx...>, Tensor<T,Rest...>,
typename std_ext::make_index_sequence<sizeof...(Idx)>::type>;
using resulting_tensor = typename _permute_impl::resulting_tensor;
constexpr bool requires_permutation = _permute_impl::requires_permutation;
FASTOR_IF_CONSTEXPR(!requires_permutation) return a;
#if CONTRACT_OPT==-1
using maxes_out_type = typename put_dims_in_Index<resulting_tensor>::type;
constexpr size_t a_dim = sizeof...(Rest);
constexpr size_t out_dim = a_dim;
constexpr auto& products_a = nprods<Index<Rest...>,
typename std_ext::make_index_sequence<a_dim>::type>::values;
constexpr auto& products_out = nprods<maxes_out_type,
typename std_ext::make_index_sequence<a_dim>::type>::values;
resulting_tensor out;
T *a_data = a.data();
T *out_data = out.data();
size_t as[out_dim] = {};
int jt;
#if FASTOR_CXX_VERSION >= 2017
constexpr std::array<size_t,a_dim> maxes_out = maxes_out_type::values;
// Map to go from out to in
// Get the reverse map - this is to get contiguous memory writes
using reverse_map = internal::permute_mapped_index_t<Index<Idx...>,make_index_t<a_dim>>;
constexpr auto& maxes_idx = reverse_map::values;
// print(type_name<mapped_index>());
while(true)
{
size_t index_a = as[maxes_idx[a_dim-1]];
for(size_t it = 0; it< a_dim; it++) {
index_a += products_a[it]*as[maxes_idx[it]];
}
size_t index_out = as[out_dim-1];
for(size_t it = 0; it< out_dim-1; it++) {
index_out += products_out[it]*as[it];
}
// print(index_out);
// print(index_a);
out_data[index_out] = a_data[index_a];
for(jt = out_dim-1 ; jt>=0 ; jt--)
{
if(++as[jt]<maxes_out[jt])
break;
else
as[jt]=0;
}
if(jt<0)
break;
}
#else
using resulting_index = typename _permute_impl::resulting_index;
constexpr std::array<size_t,a_dim> maxes_a = {Rest...};
// Map to go from in to out
constexpr auto& maxes_idx = resulting_index::values;
while(true)
{
size_t index_a = as[a_dim-1];
for(size_t it = 0; it< a_dim; it++) {
index_a += products_a[it]*as[it];
}
size_t index_out = as[maxes_idx[out_dim-1]];
for(size_t it = 0; it< out_dim-1; it++) {
index_out += products_out[it]*as[maxes_idx[it]];
}
out_data[index_out] = a_data[index_a];
for(jt = out_dim-1 ; jt>=0 ; jt--)
{
if(++as[jt]<maxes_a[jt])
break;
else
as[jt]=0;
}
if(jt<0)
break;
}
#endif
#else
resulting_tensor out;
const T *a_data = a.data();
T *out_data = out.data();
constexpr size_t a_dim = sizeof...(Rest);
constexpr size_t out_dim = sizeof...(Rest);
std::array<size_t,out_dim> as = {};
std::array<size_t,out_dim> idx = {};
#if FASTOR_CXX_VERSION >= 2017
using reverse_map = internal::permute_mapped_index_t<Index<Idx...>,make_index_t<a_dim>>;
using nloops = loop_setter<
reverse_map,
resulting_tensor,
typename std_ext::make_index_sequence<out_dim>::type>;
using dims_type = typename nloops::dims_type;
NewRecursiveCartesianPermDispatcher<Index<Idx...>,Tensor<T,Rest...>,dims_type>::Do(a_data,out_data,as,idx);
#else
using nloops = loop_setter<
Index<Idx...>,
Tensor<T,Rest...>,
typename std_ext::make_index_sequence<out_dim>::type>;
using dims_type = typename nloops::dims_type;
NewRecursiveCartesianPermDispatcher<Index<Idx...>,Tensor<T,Rest...>,dims_type>::Do(a_data,out_data,as,idx);
#endif
#endif
return out;
}
// Abstract permutation
template<typename Derived, size_t DIMS,
enable_if_t_<!requires_evaluation_v<Derived>,bool> = false>
static
FASTOR_INLINE
typename new_permute_impl<
Index<Idx...>, typename Derived::result_type,
typename std_ext::make_index_sequence<sizeof...(Idx)>::type>::resulting_tensor
permutation_impl(const AbstractTensor<Derived,DIMS> &a) {
using T = typename Derived::scalar_type;
using tensor_type = typename Derived::result_type;
using _permute_impl = new_permute_impl<Index<Idx...>, tensor_type,
typename std_ext::make_index_sequence<sizeof...(Idx)>::type>;
using resulting_tensor = typename _permute_impl::resulting_tensor;
constexpr bool requires_permutation = _permute_impl::requires_permutation;
FASTOR_IF_CONSTEXPR(!requires_permutation) return a;
using maxes_out_type = typename put_dims_in_Index<resulting_tensor>::type;
constexpr size_t a_dim = DIMS;
constexpr size_t out_dim = a_dim;
constexpr auto& products_a = nprods<typename get_tensor_dimensions<tensor_type>::tensor_to_index,
typename std_ext::make_index_sequence<a_dim>::type>::values;
constexpr auto& products_out = nprods<maxes_out_type,
typename std_ext::make_index_sequence<a_dim>::type>::values;
resulting_tensor out;
T *out_data = out.data();
const Derived & a_src = a.self();
size_t as[out_dim] = {};
int jt;
#if FASTOR_CXX_VERSION >= 2017
constexpr std::array<size_t,a_dim> maxes_out = maxes_out_type::values;
// Map to go from in to out
// constexpr auto& maxes_idx = resulting_index::values;
// Map to go from out to in
// Get the reverse map - this is to get contiguous memory writes
using reverse_map = internal::permute_mapped_index_t<Index<Idx...>,make_index_t<a_dim>>;
constexpr auto& maxes_idx = reverse_map::values;
while(true)
{
size_t index_a = as[maxes_idx[a_dim-1]];
for(size_t it = 0; it< a_dim; it++) {
index_a += products_a[it]*as[maxes_idx[it]];
}
size_t index_out = as[out_dim-1];
for(size_t it = 0; it< out_dim-1; it++) {
index_out += products_out[it]*as[it];
}
out_data[index_out] = a_src.template eval_s<T>(index_a);
for(jt = out_dim-1 ; jt>=0 ; jt--)
{
if(++as[jt]<maxes_out[jt])
break;
else
as[jt]=0;
}
if(jt<0)
break;
}
#else
constexpr std::array<size_t,a_dim> maxes_a = get_tensor_dimensions<tensor_type>::dims;
// Map to go from in to out
using resulting_index = typename _permute_impl::resulting_index;
constexpr auto& maxes_idx = resulting_index::values;
while(true)
{
size_t index_a = as[a_dim-1];
for(size_t it = 0; it< a_dim; it++) {
index_a += products_a[it]*as[it];
}
size_t index_out = as[maxes_idx[out_dim-1]];
for(size_t it = 0; it< out_dim-1; it++) {
index_out += products_out[it]*as[maxes_idx[it]];
}
out_data[index_out] = a_src.template eval_s<T>(index_a);
for(jt = out_dim-1 ; jt>=0 ; jt--)
{
if(++as[jt]<maxes_a[jt])
break;
else
as[jt]=0;
}
if(jt<0)
break;
}
#endif
return out;
}
};
} // internal
template<class Index_I, typename T, size_t ... Rest>
FASTOR_INLINE
typename internal::new_permute_impl<Index_I, Tensor<T,Rest...>,
typename std_ext::make_index_sequence<sizeof...(Rest)>::type>::resulting_tensor
permute(const Tensor<T, Rest...> &a) {
return internal::new_extractor_perm<Index_I>::permutation_impl(a);
}
template<class Index_I, typename Derived, size_t DIMS,
enable_if_t_<!requires_evaluation_v<Derived>,bool> = false>
FASTOR_INLINE
typename internal::new_permute_impl<Index_I,
typename Derived::result_type,
typename std_ext::make_index_sequence<DIMS>::type>::resulting_tensor
permute(const AbstractTensor<Derived, DIMS> &a) {
return internal::new_extractor_perm<Index_I>::permutation_impl(a);
}
template<class Index_I, typename Derived, size_t DIMS,
enable_if_t_<requires_evaluation_v<Derived>,bool> = false>
FASTOR_INLINE
typename internal::new_permute_impl<Index_I,
typename Derived::result_type,
typename std_ext::make_index_sequence<DIMS>::type>::resulting_tensor
permute(const AbstractTensor<Derived, DIMS> &a) {
using result_type = typename Derived::result_type;
const result_type tmp(a);
return internal::new_extractor_perm<Index_I>::permutation_impl(tmp);
}
} // end of namespace Fastor
#endif // PERMUTE_H

View File

@@ -0,0 +1,293 @@
#ifndef REDUCIBLE_CONTRACTION_H
#define REDUCIBLE_CONTRACTION_H
#include "Fastor/tensor/Tensor.h"
#include "Fastor/tensor_algebra/indicial.h"
namespace Fastor {
// Broadcast-vectorisable contractions (if last dimension is contracted).
// Requres working on general strides
template<class T, class U>
struct extractor_reducible_contract {};
template<size_t ... Idx0, size_t ... Idx1>
struct extractor_reducible_contract<Index<Idx0...>, Index<Idx1...>> {
#if CONTRACT_OPT==2
template<typename T, size_t ... Rest0, size_t ... Rest1>
static
typename contraction_impl<Index<Idx0...,Idx1...>, Tensor<T,Rest0...,Rest1...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)+sizeof...(Rest1)>::type>::type
contract_impl(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b) {
static_assert(!is_pair_reduction_v<Index<Idx0...>,Index<Idx1...>>,"REDUCTION TO SCALAR REQUESTED. USE REDUCTION FUNCTION INSTEAD");
constexpr int total = no_of_loops_to_set<Index<Idx0...>,Index<Idx1...>,Tensor<T,Rest0...>,Tensor<T,Rest1...>,
typename std_ext::make_index_sequence<no_of_unique<Idx0...,Idx1...>::value>::type>::value;
using index_generator = contract_meta_engine<Index<Idx0...>,Index<Idx1...>,Tensor<T,Rest0...>,Tensor<T,Rest1...>,
typename std_ext::make_index_sequence<total>::type>;
using OutTensor = typename index_generator::OutTensor;
using OutIndices = typename index_generator::OutIndices;
constexpr auto& index_a = index_generator::index_a;
constexpr auto& index_b = index_generator::index_b;
constexpr auto& index_out = index_generator::index_out;
OutTensor out;
out.zeros();
const T *a_data = a.data();
const T *b_data = b.data();
T *out_data = out.data();
// Check for reducible vectorisability
constexpr int general_stride = general_stride_finder<Index<Idx0...>,Index<Idx1...>,
Tensor<T,Rest0...>,Tensor<T,Rest1...>, typename std_ext::make_index_sequence<sizeof...(Rest1)>::type>::value;
#ifndef FASTOR_DONT_VECTORISE
using vectorisability = is_reducibly_vectorisable<OutIndices,OutTensor>;
constexpr int stride = vectorisability::stride;
using V = typename vectorisability::type;
#else
constexpr int stride = 1;
using V = SIMDVector<T,sizeof(T)*8>;
#endif
V _vec_a, _vec_b;
for (int i = 0; i < total; i+=stride) {
_vec_a.set(*(a_data+index_a[i]));
const auto idx_b = index_b[i];
vector_setter(_vec_b, b_data, idx_b, general_stride);
V _vec_out = _vec_a*_vec_b + V(out_data+index_out[i]);
_vec_out.store(out_data+index_out[i]);
}
return out;
}
#elif CONTRACT_OPT==1
template<typename T, size_t ... Rest0, size_t ... Rest1>
static
typename contraction_impl<Index<Idx0...,Idx1...>, Tensor<T,Rest0...,Rest1...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)+sizeof...(Rest1)>::type>::type
contract_impl(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b) {
static_assert(!is_pair_reduction_v<Index<Idx0...>,Index<Idx1...>>,"REDUCTION TO SCALAR REQUESTED. USE REDUCTION FUNCTION INSTEAD");
using OutTensor = typename contraction_impl<Index<Idx0...,Idx1...>, Tensor<T,Rest0...,Rest1...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)+sizeof...(Rest1)>::type>::type;
using OutIndices = typename contraction_impl<Index<Idx0...,Idx1...>, Tensor<T,Rest0...,Rest1...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)+sizeof...(Rest1)>::type>::indices;
OutTensor out;
out.zeros();
const T *a_data = a.data();
const T *b_data = b.data();
T *out_data = out.data();
constexpr int a_dim = sizeof...(Rest0);
constexpr int b_dim = sizeof...(Rest1);
constexpr auto& idx_a = IndexFirstTensor<Index<Idx0...>,Index<Idx1...>, Tensor<T,Rest0...>,Tensor<T,Rest1...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)>::type>::indices;
constexpr auto& idx_b = IndexSecondTensor<Index<Idx0...>,Index<Idx1...>, Tensor<T,Rest0...>,Tensor<T,Rest1...>,
typename std_ext::make_index_sequence<sizeof...(Rest1)>::type>::indices;
constexpr auto& idx_out = IndexResultingTensor<Index<Idx0...>,Index<Idx1...>, Tensor<T,Rest0...>,Tensor<T,Rest1...>,
typename std_ext::make_index_sequence<OutTensor::Dimension>::type>::indices;
constexpr int total = no_of_loops_to_set<Index<Idx0...>,Index<Idx1...>,Tensor<T,Rest0...>,Tensor<T,Rest1...>,
typename std_ext::make_index_sequence<no_of_unique<Idx0...,Idx1...>::value>::type>::value;
using maxes_out_type = typename no_of_loops_to_set<Index<Idx0...>,Index<Idx1...>,Tensor<T,Rest0...>,Tensor<T,Rest1...>,
typename std_ext::make_index_sequence<no_of_unique<Idx0...,Idx1...>::value>::type>::type;
constexpr auto& as_all = cartesian_product<maxes_out_type,typename std_ext::make_index_sequence<total>::type>::values;
constexpr std::array<size_t,a_dim> products_a = nprods<Index<Rest0...>,typename std_ext::make_index_sequence<a_dim>::type>::values;
constexpr std::array<size_t,b_dim> products_b = nprods<Index<Rest1...>,typename std_ext::make_index_sequence<b_dim>::type>::values;
using Index_with_dims = typename put_dims_in_Index<OutTensor>::type;
constexpr std::array<size_t,Index_with_dims::NoIndices> products_out = nprods<Index_with_dims,
typename std_ext::make_index_sequence<Index_with_dims::NoIndices>::type>::values;
// Check for reducible vectorisability
constexpr int general_stride = general_stride_finder<Index<Idx0...>,Index<Idx1...>,
Tensor<T,Rest0...>,Tensor<T,Rest1...>, typename std_ext::make_index_sequence<b_dim>::type>::value;
#ifndef FASTOR_DONT_VECTORISE
using vectorisability = is_reducibly_vectorisable<OutIndices,OutTensor>;
constexpr int stride = vectorisability::stride;
using V = typename vectorisability::type;
#else
constexpr int stride = 1;
using V = SIMDVector<T,sizeof(T)*8>;
#endif
int it;
V _vec_a, _vec_b;
for (int i = 0; i < total; i+=stride) {
int index_a = as_all[i][idx_a[a_dim-1]];
for(it = 0; it< a_dim; it++) {
index_a += products_a[it]*as_all[i][idx_a[it]];
}
int index_b = as_all[i][idx_b[b_dim-1]];
for(it = 0; it< b_dim; it++) {
index_b += products_b[it]*as_all[i][idx_b[it]];
}
int index_out = as_all[i][idx_out[idx_out.size()-1]];
for(it = 0; it< idx_out.size(); it++) {
index_out += products_out[it]*as_all[i][idx_out[it]];
}
_vec_a.set(*(a_data+index_a));
// _vec_b.set(b_data[index_b+3*general_stride], b_data[index_b+2*general_stride],
// b_data[index_b+general_stride],b_data[index_b]);
vector_setter(_vec_b, b_data, index_b, general_stride);
V _vec_out = _vec_a*_vec_b + V(out_data+index_out);
_vec_out.store(out_data+index_out);
}
return out;
}
#else
template<typename T, size_t ... Rest0, size_t ... Rest1>
static
typename contraction_impl<Index<Idx0...,Idx1...>, Tensor<T,Rest0...,Rest1...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)+sizeof...(Rest1)>::type>::type
contract_impl(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b) {
static_assert(!is_pair_reduction_v<Index<Idx0...>,Index<Idx1...>>,"REDUCTION TO SCALAR REQUESTED. USE REDUCTION FUNCTION INSTEAD");
using OutTensor = typename contraction_impl<Index<Idx0...,Idx1...>, Tensor<T,Rest0...,Rest1...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)+sizeof...(Rest1)>::type>::type;
using OutIndice = typename contraction_impl<Index<Idx0...,Idx1...>, Tensor<T,Rest0...,Rest1...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)+sizeof...(Rest1)>::type>::indices;
OutTensor out;
out.zeros();
const T *a_data = a.data();
const T *b_data = b.data();
T *out_data = out.data();
constexpr int a_dim = sizeof...(Rest0);
constexpr int b_dim = sizeof...(Rest1);
constexpr int out_dim = no_of_unique<Idx0...,Idx1...>::value;
constexpr auto& idx_a = IndexTensors<
Index<Idx0..., Idx1...>,
Tensor<T,Rest0...,Rest1...>,
Index<Idx0...>,Tensor<T,Rest0...>,
typename std_ext::make_index_sequence<sizeof...(Rest0)>::type>::indices;
constexpr auto& idx_b = IndexTensors<
Index<Idx0..., Idx1...>,
Tensor<T,Rest0...,Rest1...>,
Index<Idx1...>,Tensor<T,Rest1...>,
typename std_ext::make_index_sequence<sizeof...(Rest1)>::type>::indices;
constexpr auto& idx_out = IndexTensors<
Index<Idx0..., Idx1...>,
Tensor<T,Rest0...,Rest1...>,
OutIndice,OutTensor,
typename std_ext::make_index_sequence<OutTensor::Dimension>::type>::indices;
using nloops = loop_setter<
Index<Idx0...,Idx1...>,
Tensor<T,Rest0...,Rest1...>,
typename std_ext::make_index_sequence<out_dim>::type>;
constexpr auto& maxes_out = nloops::dims;
constexpr int total = nloops::value;
constexpr std::array<size_t,a_dim> products_a = nprods<Index<Rest0...>,typename std_ext::make_index_sequence<a_dim>::type>::values;
constexpr std::array<size_t,b_dim> products_b = nprods<Index<Rest1...>,typename std_ext::make_index_sequence<b_dim>::type>::values;
using Index_with_dims = typename put_dims_in_Index<OutTensor>::type;
constexpr std::array<size_t,OutTensor::Dimension> products_out = \
nprods<Index_with_dims,typename std_ext::make_index_sequence<OutTensor::Dimension>::type>::values;
// Check for reducible vectorisability
constexpr int general_stride = general_stride_finder<Index<Idx0...>,Index<Idx1...>,
Tensor<T,Rest0...>,Tensor<T,Rest1...>, typename std_ext::make_index_sequence<b_dim>::type>::value;
#ifndef FASTOR_DONT_VECTORISE
using vectorisability = is_reducibly_vectorisable<OutIndice,OutTensor>;
constexpr int stride = vectorisability::stride;
using V = typename vectorisability::type;
#else
constexpr int stride = 1;
using V = SIMDVector<T,simd_abi::scalar>;
#endif
int as[out_dim];
std::fill(as,as+out_dim,0);
int it;
V _vec_a, _vec_b;
for (int i = 0; i < total; i+=stride) {
int remaining = total;
for (int n = 0; n < out_dim; ++n) {
remaining /= maxes_out[n];
as[n] = ( i / remaining ) % maxes_out[n];
}
int index_a = as[idx_a[a_dim-1]];
for(it = 0; it< a_dim; it++) {
index_a += products_a[it]*as[idx_a[it]];
}
int index_b = as[idx_b[b_dim-1]];
for(it = 0; it< b_dim; it++) {
index_b += products_b[it]*as[idx_b[it]];
}
int index_out = as[idx_out[OutTensor::Dimension-1]];
for(it = 0; it< static_cast<int>(OutTensor::Dimension); it++) {
index_out += products_out[it]*as[idx_out[it]];
}
// asm("#BEGIN");
_vec_a.set(*(a_data+index_a));
//_vec_a.broadcast(a_data+index_a);
vector_setter(_vec_b, b_data, index_b, general_stride);
V _vec_out = _vec_a*_vec_b + V(out_data+index_out);
_vec_out.store(out_data+index_out);
// asm("#END");
}
return out;
}
#endif
};
template<class Index_I, class Index_J,
typename T, size_t ... Rest0, size_t ... Rest1>
FASTOR_INLINE auto strided_contraction(const Tensor<T,Rest0...> &a, const Tensor<T,Rest1...> &b)
-> decltype(extractor_reducible_contract<Index_I,Index_J>::contract_impl(a,b)) {
return extractor_reducible_contract<Index_I,Index_J>::contract_impl(a,b);
}
}
#endif // REDUCIBLE_CONTRACTION_H