| // |
| // Copyright (c) 2018-2019, Cem Bassoy, cem.bassoy@gmail.com |
| // |
| // Distributed under the Boost Software License, Version 1.0. (See |
| // accompanying file LICENSE_1_0.txt or copy at |
| // http://www.boost.org/LICENSE_1_0.txt) |
| // |
| // The authors gratefully acknowledge the support of |
| // Fraunhofer IOSB, Ettlingen, Germany |
| // |
| |
| |
| #ifndef _BOOST_UBLAS_TENSOR_ALGORITHMS_HPP |
| #define _BOOST_UBLAS_TENSOR_ALGORITHMS_HPP |
| |
| |
| #include <stdexcept> |
| #include <complex> |
| #include <functional> |
| |
| namespace boost { |
| namespace numeric { |
| namespace ublas { |
| |
| |
| |
| /** @brief Copies a tensor to another tensor with different layouts |
| * |
| * Implements C[i1,i2,...,ip] = A[i1,i2,...,ip] |
| * |
| * @param[in] p rank of input and output tensor |
| * @param[in] n pointer to the extents of input or output tensor of length p |
| * @param[in] pi pointer to a one-based permutation tuple of length p |
| * @param[out] c pointer to the output tensor |
| * @param[in] wc pointer to the strides of output tensor c |
| * @param[in] a pointer to the input tensor |
| * @param[in] wa pointer to the strides of input tensor a |
| */ |
| template <class PointerOut, class PointerIn, class SizeType> |
| void copy(const SizeType p, SizeType const*const n, |
| PointerOut c, SizeType const*const wc, |
| PointerIn a, SizeType const*const wa) |
| { |
| static_assert( std::is_pointer<PointerOut>::value & std::is_pointer<PointerIn>::value, |
| "Static error in boost::numeric::ublas::copy: Argument types for pointers are not pointer types."); |
| if( p == 0 ) |
| return; |
| |
| if(c == nullptr || a == nullptr) |
| throw std::length_error("Error in boost::numeric::ublas::copy: Pointers shall not be null pointers."); |
| |
| if(wc == nullptr || wa == nullptr) |
| throw std::length_error("Error in boost::numeric::ublas::copy: Pointers shall not be null pointers."); |
| |
| if(n == nullptr) |
| throw std::length_error("Error in boost::numeric::ublas::copy: Pointers shall not be null pointers."); |
| |
| |
| std::function<void(SizeType r, PointerOut c, PointerIn a)> lambda; |
| |
| lambda = [&lambda, n, wc, wa](SizeType r, PointerOut c, PointerIn a) |
| { |
| if(r > 0) |
| for(auto d = 0u; d < n[r]; c += wc[r], a += wa[r], ++d) |
| lambda(r-1, c, a ); |
| else |
| for(auto d = 0u; d < n[0]; c += wc[0], a += wa[0], ++d) |
| *c = *a; |
| }; |
| |
| lambda( p-1, c, a ); |
| } |
| |
| |
| |
| /** @brief Copies a tensor to another tensor with different layouts applying a unary operation |
| * |
| * Implements C[i1,i2,...,ip] = op ( A[i1,i2,...,ip] ) |
| * |
| * @param[in] p rank of input and output tensor |
| * @param[in] n pointer to the extents of input or output tensor of length p |
| * @param[in] pi pointer to a one-based permutation tuple of length p |
| * @param[out] c pointer to the output tensor |
| * @param[in] wc pointer to the strides of output tensor c |
| * @param[in] a pointer to the input tensor |
| * @param[in] wa pointer to the strides of input tensor a |
| * @param[in] op unary operation |
| */ |
| template <class PointerOut, class PointerIn, class SizeType, class UnaryOp> |
| void transform(const SizeType p, |
| SizeType const*const n, |
| PointerOut c, SizeType const*const wc, |
| PointerIn a, SizeType const*const wa, |
| UnaryOp op) |
| { |
| static_assert( std::is_pointer<PointerOut>::value & std::is_pointer<PointerIn>::value, |
| "Static error in boost::numeric::ublas::transform: Argument types for pointers are not pointer types."); |
| if( p == 0 ) |
| return; |
| |
| if(c == nullptr || a == nullptr) |
| throw std::length_error("Error in boost::numeric::ublas::transform: Pointers shall not be null pointers."); |
| |
| if(wc == nullptr || wa == nullptr) |
| throw std::length_error("Error in boost::numeric::ublas::transform: Pointers shall not be null pointers."); |
| |
| if(n == nullptr) |
| throw std::length_error("Error in boost::numeric::ublas::transform: Pointers shall not be null pointers."); |
| |
| |
| std::function<void(SizeType r, PointerOut c, PointerIn a)> lambda; |
| |
| lambda = [&lambda, n, wc, wa, op](SizeType r, PointerOut c, PointerIn a) |
| { |
| if(r > 0) |
| for(auto d = 0u; d < n[r]; c += wc[r], a += wa[r], ++d) |
| lambda(r-1, c, a); |
| else |
| for(auto d = 0u; d < n[0]; c += wc[0], a += wa[0], ++d) |
| *c = op(*a); |
| }; |
| |
| lambda( p-1, c, a ); |
| |
| } |
| |
| |
| /** @brief Performs a reduce operation with all elements of the tensor and an initial value |
| * |
| * Implements k = sum_{i1,..,ip} A[i1,i2,...,ip] |
| * |
| * @param[in] r zero-based recursion level starting with r=p-1 |
| * @param[in] n pointer to the extents of input or output tensor |
| * @param[in] a pointer to the first input tensor |
| * @param[in] w pointer to the strides of input tensor a |
| * @param[in] k accumulated value |
| */ |
| template <class PointerIn, class ValueType, class SizeType> |
| ValueType accumulate(SizeType const p, SizeType const*const n, |
| PointerIn a, SizeType const*const w, |
| ValueType k) |
| { |
| static_assert(std::is_pointer<PointerIn>::value, |
| "Static error in boost::numeric::ublas::transform: Argument types for pointers are not pointer types."); |
| |
| if( p == 0 ) |
| return k; |
| |
| if(a == nullptr) |
| throw std::length_error("Error in boost::numeric::ublas::transform: Pointers shall not be null pointers."); |
| |
| if(w == nullptr) |
| throw std::length_error("Error in boost::numeric::ublas::transform: Pointers shall not be null pointers."); |
| |
| if(n == nullptr) |
| throw std::length_error("Error in boost::numeric::ublas::transform: Pointers shall not be null pointers."); |
| |
| |
| std::function<ValueType(SizeType r, PointerIn a, ValueType k)> lambda; |
| |
| lambda = [&lambda, n, w](SizeType r, PointerIn a, ValueType k) |
| { |
| if(r > 0u) |
| for(auto d = 0u; d < n[r]; a += w[r], ++d) |
| k = lambda(r-1, a, k); |
| else |
| for(auto d = 0u; d < n[0]; a += w[0], ++d) |
| k += *a; |
| return k; |
| }; |
| |
| return lambda( p-1, a, k ); |
| } |
| |
| /** @brief Performs a reduce operation with all elements of the tensor and an initial value |
| * |
| * Implements k = op ( k , A[i1,i2,...,ip] ), for all ir |
| * |
| * @param[in] r zero-based recursion level starting with r=p-1 |
| * @param[in] n pointer to the extents of input or output tensor |
| * @param[in] a pointer to the first input tensor |
| * @param[in] w pointer to the strides of input tensor a |
| * @param[in] k accumulated value |
| * @param[in] op binary operation |
| */ |
| |
| template <class PointerIn, class ValueType, class SizeType, class BinaryOp> |
| ValueType accumulate(SizeType const p, SizeType const*const n, |
| PointerIn a, SizeType const*const w, |
| ValueType k, BinaryOp op) |
| { |
| static_assert(std::is_pointer<PointerIn>::value, |
| "Static error in boost::numeric::ublas::transform: Argument types for pointers are not pointer types."); |
| |
| |
| if( p == 0 ) |
| return k; |
| |
| if(a == nullptr) |
| throw std::length_error("Error in boost::numeric::ublas::transform: Pointers shall not be null pointers."); |
| |
| if(w == nullptr) |
| throw std::length_error("Error in boost::numeric::ublas::transform: Pointers shall not be null pointers."); |
| |
| if(n == nullptr) |
| throw std::length_error("Error in boost::numeric::ublas::transform: Pointers shall not be null pointers."); |
| |
| |
| std::function<ValueType(SizeType r, PointerIn a, ValueType k)> lambda; |
| |
| lambda = [&lambda, n, w, op](SizeType r, PointerIn a, ValueType k) |
| { |
| if(r > 0u) |
| for(auto d = 0u; d < n[r]; a += w[r], ++d) |
| k = lambda(r-1, a, k); |
| else |
| for(auto d = 0u; d < n[0]; a += w[0], ++d) |
| k = op ( k, *a ); |
| return k; |
| }; |
| |
| return lambda( p-1, a, k ); |
| } |
| |
| /** @brief Transposes a tensor |
| * |
| * Implements C[tau[i1],tau[i2],...,tau[ip]] = A[i1,i2,...,ip] |
| * |
| * @note is used in function trans |
| * |
| * @param[in] p rank of input and output tensor |
| * @param[in] na pointer to the extents of the input tensor a of length p |
| * @param[in] pi pointer to a one-based permutation tuple of length p |
| * @param[out] c pointer to the output tensor |
| * @param[in] wc pointer to the strides of output tensor c |
| * @param[in] a pointer to the input tensor |
| * @param[in] wa pointer to the strides of input tensor a |
| */ |
| |
| template <class PointerOut, class PointerIn, class SizeType> |
| void trans( SizeType const p, SizeType const*const na, SizeType const*const pi, |
| PointerOut c, SizeType const*const wc, |
| PointerIn a, SizeType const*const wa) |
| { |
| |
| static_assert( std::is_pointer<PointerOut>::value & std::is_pointer<PointerIn>::value, |
| "Static error in boost::numeric::ublas::trans: Argument types for pointers are not pointer types."); |
| |
| if( p < 2) |
| return; |
| |
| if(c == nullptr || a == nullptr) |
| throw std::runtime_error("Error in boost::numeric::ublas::trans: Pointers shall not be null pointers."); |
| |
| if(na == nullptr) |
| throw std::runtime_error("Error in boost::numeric::ublas::trans: Pointers shall not be null."); |
| |
| if(wc == nullptr || wa == nullptr) |
| throw std::length_error("Error in boost::numeric::ublas::trans: Pointers shall not be null pointers."); |
| |
| if(na == nullptr) |
| throw std::length_error("Error in boost::numeric::ublas::trans: Pointers shall not be null pointers."); |
| |
| if(pi == nullptr) |
| throw std::length_error("Error in boost::numeric::ublas::trans: Pointers shall not be null pointers."); |
| |
| |
| std::function<void(SizeType r, PointerOut c, PointerIn a)> lambda; |
| |
| lambda = [&lambda, na, wc, wa, pi](SizeType r, PointerOut c, PointerIn a) |
| { |
| if(r > 0) |
| for(auto d = 0u; d < na[r]; c += wc[pi[r]-1], a += wa[r], ++d) |
| lambda(r-1, c, a); |
| else |
| for(auto d = 0u; d < na[0]; c += wc[pi[0]-1], a += wa[0], ++d) |
| *c = *a; |
| }; |
| |
| lambda( p-1, c, a ); |
| } |
| |
| |
| /** @brief Transposes a tensor |
| * |
| * Implements C[tau[i1],tau[i2],...,tau[ip]] = A[i1,i2,...,ip] |
| * |
| * @note is used in function trans |
| * |
| * @param[in] p rank of input and output tensor |
| * @param[in] na pointer to the extents of the input tensor a of length p |
| * @param[in] pi pointer to a one-based permutation tuple of length p |
| * @param[out] c pointer to the output tensor |
| * @param[in] wc pointer to the strides of output tensor c |
| * @param[in] a pointer to the input tensor |
| * @param[in] wa pointer to the strides of input tensor a |
| */ |
| |
| template <class ValueType, class SizeType> |
| void trans( SizeType const p, |
| SizeType const*const na, |
| SizeType const*const pi, |
| std::complex<ValueType>* c, SizeType const*const wc, |
| std::complex<ValueType>* a, SizeType const*const wa) |
| { |
| if( p < 2) |
| return; |
| |
| if(c == nullptr || a == nullptr) |
| throw std::length_error("Error in boost::numeric::ublas::trans: Pointers shall not be null pointers."); |
| |
| if(wc == nullptr || wa == nullptr) |
| throw std::length_error("Error in boost::numeric::ublas::trans: Pointers shall not be null pointers."); |
| |
| if(na == nullptr) |
| throw std::length_error("Error in boost::numeric::ublas::trans: Pointers shall not be null pointers."); |
| |
| if(pi == nullptr) |
| throw std::length_error("Error in boost::numeric::ublas::trans: Pointers shall not be null pointers."); |
| |
| |
| std::function<void(SizeType r, std::complex<ValueType>* c, std::complex<ValueType>* a)> lambda; |
| |
| lambda = [&lambda, na, wc, wa, pi](SizeType r, std::complex<ValueType>* c, std::complex<ValueType>* a) |
| { |
| if(r > 0) |
| for(auto d = 0u; d < na[r]; c += wc[pi[r]-1], a += wa[r], ++d) |
| lambda(r-1, c, a); |
| else |
| for(auto d = 0u; d < na[0]; c += wc[pi[0]-1], a += wa[0], ++d) |
| *c = std::conj(*a); |
| }; |
| |
| lambda( p-1, c, a ); |
| |
| } |
| |
| |
| |
| |
| } |
| } |
| } |
| |
| #endif |