// // 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 #include #include 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 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::value & std::is_pointer::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 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 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::value & std::is_pointer::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 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 ValueType accumulate(SizeType const p, SizeType const*const n, PointerIn a, SizeType const*const w, ValueType k) { static_assert(std::is_pointer::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 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 ValueType accumulate(SizeType const p, SizeType const*const n, PointerIn a, SizeType const*const w, ValueType k, BinaryOp op) { static_assert(std::is_pointer::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 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 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::value & std::is_pointer::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 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 void trans( SizeType const p, SizeType const*const na, SizeType const*const pi, std::complex* c, SizeType const*const wc, std::complex* 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* c, std::complex* a)> lambda; lambda = [&lambda, na, wc, wa, pi](SizeType r, std::complex* c, std::complex* 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