Program Listing for File arrayContainer.hpp#
↰ Return to documentation for file (librapid/include/librapid/array/arrayContainer.hpp)
#ifndef LIBRAPID_ARRAY_ARRAY_CONTAINER_HPP
#define LIBRAPID_ARRAY_ARRAY_CONTAINER_HPP
namespace librapid {
namespace detail {
template<typename T>
struct SubscriptType {
using Scalar = T;
using Direct = const Scalar &;
using Ref = Scalar &;
};
template<typename T>
struct SubscriptType<Storage<T>> {
using Scalar = T;
using Direct = const Scalar &;
using Ref = Scalar &;
};
template<typename T, size_t... Dims>
struct SubscriptType<FixedStorage<T, Dims...>> {
using Scalar = T;
using Direct = const Scalar &;
using Ref = Scalar &;
};
#if defined(LIBRAPID_HAS_OPENCL)
template<typename T>
struct SubscriptType<OpenCLStorage<T>> {
using Scalar = T;
using Direct = const OpenCLRef<Scalar>;
using Ref = OpenCLRef<Scalar>;
};
#endif // LIBRAPID_HAS_OPENCL
#if defined(LIBRAPID_HAS_CUDA)
template<typename T>
struct SubscriptType<CudaStorage<T>> {
using Scalar = T;
using Direct = const detail::CudaRef<Scalar>;
using Ref = detail::CudaRef<Scalar>;
};
#endif // LIBRAPID_HAS_CUDA
} // namespace detail
namespace typetraits {
template<typename ShapeType_, typename StorageType_>
struct TypeInfo<array::ArrayContainer<ShapeType_, StorageType_>> {
static constexpr detail::LibRapidType type = detail::LibRapidType::ArrayContainer;
using Scalar = typename TypeInfo<StorageType_>::Scalar;
using Packet = std::false_type;
using Backend = typename TypeInfo<StorageType_>::Backend;
static constexpr int64_t packetWidth = 1;
static constexpr bool supportsArithmetic = TypeInfo<Scalar>::supportsArithmetic;
static constexpr bool supportsLogical = TypeInfo<Scalar>::supportsLogical;
static constexpr bool supportsBinary = TypeInfo<Scalar>::supportsBinary;
static constexpr bool allowVectorisation = TypeInfo<Scalar>::packetWidth > 1;
#if defined(LIBRAPID_HAS_CUDA)
static constexpr cudaDataType_t CudaType = TypeInfo<Scalar>::CudaType;
static constexpr int64_t cudaPacketWidth = 1;
#endif // LIBRAPID_HAS_CUDA
static constexpr bool canAlign = false;
static constexpr int64_t canMemcpy = false;
};
template<typename T>
struct IsArrayContainer : std::false_type {};
template<typename SizeType, size_t dims, typename StorageScalar>
struct IsArrayContainer<array::ArrayContainer<Shape<SizeType, dims>, StorageScalar>>
: std::true_type {};
LIBRAPID_DEFINE_AS_TYPE(
typename SizeType COMMA size_t dims COMMA typename StorageScalar,
array::ArrayContainer<Shape<SizeType COMMA dims> COMMA StorageScalar>);
} // namespace typetraits
namespace array {
template<typename ShapeType_, typename StorageType_>
class ArrayContainer {
public:
using StorageType = StorageType_;
using ShapeType = ShapeType_;
using StrideType = Stride<size_t, 32>;
using SizeType = typename ShapeType::SizeType;
using Scalar = typename StorageType::Scalar;
using Packet = typename typetraits::TypeInfo<Scalar>::Packet;
using Backend = typename typetraits::TypeInfo<ArrayContainer>::Backend;
using Iterator = detail::ArrayIterator<ArrayView<ArrayContainer>>;
using DirectSubscriptType = typename detail::SubscriptType<StorageType>::Direct;
using DirectRefSubscriptType = typename detail::SubscriptType<StorageType>::Ref;
ArrayContainer();
template<typename T>
LIBRAPID_ALWAYS_INLINE ArrayContainer(const std::initializer_list<T> &data);
template<typename T>
explicit LIBRAPID_ALWAYS_INLINE ArrayContainer(const std::vector<T> &data);
LIBRAPID_ALWAYS_INLINE explicit ArrayContainer(const ShapeType &shape);
LIBRAPID_ALWAYS_INLINE ArrayContainer(const ShapeType &shape, const Scalar &value);
LIBRAPID_ALWAYS_INLINE explicit ArrayContainer(const Scalar &value);
LIBRAPID_ALWAYS_INLINE explicit ArrayContainer(ShapeType &&shape);
LIBRAPID_ALWAYS_INLINE ArrayContainer(const ArrayContainer &other) = default;
LIBRAPID_ALWAYS_INLINE ArrayContainer(ArrayContainer &&other) noexcept = default;
template<typename TransposeType>
LIBRAPID_ALWAYS_INLINE ArrayContainer(const Transpose<TransposeType> &trans);
template<typename ShapeTypeA, typename StorageTypeA, typename ShapeTypeB,
typename StorageTypeB, typename Alpha, typename Beta>
LIBRAPID_ALWAYS_INLINE
ArrayContainer(const linalg::ArrayMultiply<ShapeTypeA, StorageTypeA, ShapeTypeB,
StorageTypeB, Alpha, Beta> &multiply);
template<typename desc, typename Functor_, typename... Args>
LIBRAPID_ALWAYS_INLINE ArrayContainer &
assign(const detail::Function<desc, Functor_, Args...> &function);
template<typename desc, typename Functor_, typename... Args>
LIBRAPID_ALWAYS_INLINE ArrayContainer(
const detail::Function<desc, Functor_, Args...> &function) LIBRAPID_RELEASE_NOEXCEPT;
LIBRAPID_ALWAYS_INLINE ArrayContainer &operator=(const ArrayContainer &other) = default;
LIBRAPID_ALWAYS_INLINE ArrayContainer &operator=(const Scalar &value);
LIBRAPID_ALWAYS_INLINE ArrayContainer &
operator=(ArrayContainer &&other) noexcept = default;
template<typename desc, typename Functor_, typename... Args>
LIBRAPID_ALWAYS_INLINE ArrayContainer &
operator=(const detail::Function<desc, Functor_, Args...> &function);
template<typename TransposeType>
LIBRAPID_ALWAYS_INLINE ArrayContainer &
operator=(const Transpose<TransposeType> &transpose);
template<typename ShapeTypeA, typename StorageTypeA, typename ShapeTypeB,
typename StorageTypeB, typename Alpha, typename Beta>
LIBRAPID_ALWAYS_INLINE ArrayContainer &
operator=(const linalg::ArrayMultiply<ShapeTypeA, StorageTypeA, ShapeTypeB,
StorageTypeB, Alpha, Beta> &multiply);
template<typename T>
detail::CommaInitializer<ArrayContainer> operator<<(const T &value);
// template<typename ScalarTo = Scalar, typename BackendTo = Backend>
// LIBRAPID_NODISCARD auto cast() const;
LIBRAPID_NODISCARD ArrayContainer copy() const;
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto operator[](int64_t index) const;
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto operator[](int64_t index);
template<typename... Indices>
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE DirectSubscriptType
operator()(Indices... indices) const;
template<typename... Indices>
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE DirectRefSubscriptType
operator()(Indices... indices);
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Scalar get() const;
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE typename ShapeType::SizeType
ndim() const noexcept;
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE const ShapeType &shape() const noexcept;
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE const StorageType &storage() const noexcept;
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE StorageType &storage() noexcept;
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Packet packet(size_t index) const;
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Scalar scalar(size_t index) const;
LIBRAPID_ALWAYS_INLINE void writePacket(size_t index, const Packet &value);
LIBRAPID_ALWAYS_INLINE void write(size_t index, const Scalar &value);
LIBRAPID_INLINE Iterator begin() const noexcept;
LIBRAPID_INLINE Iterator end() const noexcept;
LIBRAPID_INLINE Iterator begin();
LIBRAPID_INLINE Iterator end();
LIBRAPID_NODISCARD std::string str(const std::string &format = "{}") const;
private:
ShapeType m_shape; // The shape type of the array
StorageType m_storage; // The storage container of the array
};
template<typename ShapeType_, typename StorageType_>
ArrayContainer<ShapeType_, StorageType_>::ArrayContainer() :
m_shape(StorageType_::template defaultShape<ShapeType_>()) {}
template<typename ShapeType_, typename StorageType_>
template<typename T>
ArrayContainer<ShapeType_, StorageType_>::ArrayContainer(
const std::initializer_list<T> &data) :
m_shape({data.size()}),
m_storage(StorageType::fromData(data)) {}
template<typename ShapeType_, typename StorageType_>
template<typename T>
ArrayContainer<ShapeType_, StorageType_>::ArrayContainer(const std::vector<T> &data) :
m_shape({data.size()}), m_storage(StorageType::fromData(data)) {}
template<typename ShapeType_, typename StorageType_>
ArrayContainer<ShapeType_, StorageType_>::ArrayContainer(const ShapeType &shape) :
m_shape(shape), m_storage(shape.size()) {
static_assert(!typetraits::IsFixedStorage<StorageType_>::value,
"For a compile-time-defined shape, "
"the storage type must be "
"a FixedStorage object");
}
template<typename ShapeType_, typename StorageType_>
ArrayContainer<ShapeType_, StorageType_>::ArrayContainer(const ShapeType &shape,
const Scalar &value) :
m_shape(shape),
m_storage(shape.size(), value) {
static_assert(typetraits::IsStorage<StorageType_>::value ||
typetraits::IsOpenCLStorage<StorageType_>::value ||
typetraits::IsCudaStorage<StorageType_>::value,
"For a runtime-defined shape, "
"the storage type must be "
"either a Storage or a "
"CudaStorage object");
static_assert(!typetraits::IsFixedStorage<StorageType_>::value,
"For a compile-time-defined shape, "
"the storage type must be "
"a FixedStorage object");
}
template<typename ShapeType_, typename StorageType_>
ArrayContainer<ShapeType_, StorageType_>::ArrayContainer(const Scalar &value) :
m_shape(detail::shapeFromFixedStorage(m_storage)), m_storage(value) {
static_assert(typetraits::IsFixedStorage<StorageType_>::value,
"For a compile-time-defined shape, "
"the storage type must be "
"a FixedStorage object");
}
template<typename ShapeType_, typename StorageType_>
ArrayContainer<ShapeType_, StorageType_>::ArrayContainer(ShapeType_ &&shape) :
m_shape(std::forward<ShapeType_>(shape)), m_storage(m_shape.size()) {}
template<typename ShapeType_, typename StorageType_>
template<typename TransposeType>
ArrayContainer<ShapeType_, StorageType_>::ArrayContainer(
const array::Transpose<TransposeType> &trans) {
*this = trans;
}
template<typename ShapeType_, typename StorageType_>
template<typename ShapeTypeA, typename StorageTypeA, typename ShapeTypeB,
typename StorageTypeB, typename Alpha, typename Beta>
ArrayContainer<ShapeType_, StorageType_>::ArrayContainer(
const linalg::ArrayMultiply<ShapeTypeA, StorageTypeA, ShapeTypeB, StorageTypeB, Alpha,
Beta> &multiply) {
*this = multiply;
}
template<typename ShapeType_, typename StorageType_>
template<typename desc, typename Functor_, typename... Args>
auto ArrayContainer<ShapeType_, StorageType_>::assign(
const detail::Function<desc, Functor_, Args...> &function) -> ArrayContainer & {
using FunctionType = detail::Function<desc, Functor_, Args...>;
m_storage.resize(function.shape().size(), 0);
if constexpr (std::is_same_v<typename FunctionType::Backend, backend::OpenCL> ||
std::is_same_v<typename FunctionType::Backend, backend::CUDA>) {
detail::assign(*this, function);
} else {
#if !defined(LIBRAPID_OPTIMISE_SMALL_ARRAYS)
if (m_storage.size() > global::multithreadThreshold && global::numThreads > 1)
detail::assignParallel(*this, function);
else
#endif // LIBRAPID_OPTIMISE_SMALL_ARRAYS
detail::assign(*this, function);
}
return *this;
}
template<typename ShapeType_, typename StorageType_>
template<typename desc, typename Functor_, typename... Args>
ArrayContainer<ShapeType_, StorageType_>::ArrayContainer(
const detail::Function<desc, Functor_, Args...> &function) LIBRAPID_RELEASE_NOEXCEPT
: m_shape(function.shape()),
m_storage(m_shape.size()) {
assign(function);
}
template<typename ShapeType_, typename StorageType_>
template<typename desc, typename Functor_, typename... Args>
auto ArrayContainer<ShapeType_, StorageType_>::operator=(
const detail::Function<desc, Functor_, Args...> &function) -> ArrayContainer & {
return assign(function);
}
template<typename ShapeType_, typename StorageType_>
template<typename TransposeType>
auto ArrayContainer<ShapeType_, StorageType_>::operator=(
const Transpose<TransposeType> &transpose) -> ArrayContainer & {
m_shape = transpose.shape();
m_storage.resize(m_shape.size(), 0);
transpose.applyTo(*this);
return *this;
}
template<typename ShapeType_, typename StorageType_>
template<typename ShapeTypeA, typename StorageTypeA, typename ShapeTypeB,
typename StorageTypeB, typename Alpha, typename Beta>
auto ArrayContainer<ShapeType_, StorageType_>::operator=(
const linalg::ArrayMultiply<ShapeTypeA, StorageTypeA, ShapeTypeB, StorageTypeB, Alpha,
Beta> &arrayMultiply) -> ArrayContainer & {
m_shape = arrayMultiply.shape();
m_storage.resize(m_shape.size(), 0);
arrayMultiply.applyTo(*this);
return *this;
}
template<typename ShapeType_, typename StorageType_>
auto ArrayContainer<ShapeType_, StorageType_>::operator=(const Scalar &value)
-> ArrayContainer & {
LIBRAPID_ASSERT(m_shape.ndim() == 0, "Cannot assign a scalar to an array");
m_storage[0] = value;
return *this;
}
template<typename ShapeType_, typename StorageType_>
template<typename T>
auto ArrayContainer<ShapeType_, StorageType_>::operator<<(const T &value)
-> detail::CommaInitializer<ArrayContainer> {
return detail::CommaInitializer<ArrayContainer>(*this, static_cast<Scalar>(value));
}
template<typename ShapeType_, typename StorageType_>
auto ArrayContainer<ShapeType_, StorageType_>::copy() const -> ArrayContainer {
ArrayContainer res(m_shape);
res.m_storage = m_storage.copy();
return res;
}
template<typename ShapeType_, typename StorageType_>
auto ArrayContainer<ShapeType_, StorageType_>::operator[](int64_t index) const {
LIBRAPID_ASSERT(
index >= 0 && index < static_cast<int64_t>(m_shape[0]),
"Index {} out of bounds in ArrayContainer::operator[] with leading dimension={}",
index,
m_shape[0]);
if constexpr (typetraits::IsOpenCLStorage<StorageType_>::value) {
#if defined(LIBRAPID_HAS_OPENCL)
ArrayContainer res;
res.m_shape = m_shape.subshape(1, ndim());
auto subSize = res.shape().size();
int64_t storageSize = sizeof(typename StorageType_::Scalar);
cl_buffer_region region {index * subSize * storageSize, subSize * storageSize};
res.m_storage =
StorageType_(m_storage.data().createSubBuffer(
StorageType_::bufferFlags, CL_BUFFER_CREATE_TYPE_REGION, ®ion),
subSize,
false);
return res;
#else
LIBRAPID_ERROR("OpenCL support not enabled");
#endif // LIBRAPID_HAS_OPENCL
} else if constexpr (typetraits::IsCudaStorage<StorageType_>::value) {
#if defined(LIBRAPID_HAS_CUDA)
ArrayContainer res;
res.m_shape = m_shape.subshape(1, ndim());
auto subSize = res.shape().size();
Scalar *begin = m_storage.begin().get() + index * subSize;
res.m_storage = StorageType_(begin, subSize, false);
return res;
#else
LIBRAPID_ERROR("CUDA support not enabled");
#endif // LIBRAPID_HAS_CUDA
} else if constexpr (typetraits::IsFixedStorage<StorageType_>::value) {
return ArrayView(*this)[index];
} else {
ArrayContainer res;
res.m_shape = m_shape.subshape(1, ndim());
auto subSize = res.shape().size();
Scalar *begin = m_storage.begin() + index * subSize;
Scalar *end = begin + subSize;
res.m_storage = StorageType_(begin, end, false);
return res;
}
}
template<typename ShapeType_, typename StorageType_>
auto ArrayContainer<ShapeType_, StorageType_>::operator[](int64_t index) {
LIBRAPID_ASSERT(
index >= 0 && index < static_cast<int64_t>(m_shape[0]),
"Index {} out of bounds in ArrayContainer::operator[] with leading dimension={}",
index,
m_shape[0]);
if constexpr (typetraits::IsOpenCLStorage<StorageType_>::value) {
#if defined(LIBRAPID_HAS_OPENCL)
ArrayContainer res;
res.m_shape = m_shape.subshape(1, ndim());
auto subSize = res.shape().size();
int64_t storageSize = sizeof(typename StorageType_::Scalar);
cl_buffer_region region {index * subSize * storageSize, subSize * storageSize};
res.m_storage.set(
StorageType_(m_storage.data().createSubBuffer(
StorageType_::bufferFlags, CL_BUFFER_CREATE_TYPE_REGION, ®ion),
subSize,
false));
return res;
#else
LIBRAPID_ERROR("OpenCL support not enabled");
#endif // LIBRAPID_HAS_OPENCL
} else if constexpr (typetraits::IsCudaStorage<StorageType_>::value) {
#if defined(LIBRAPID_HAS_CUDA)
ArrayContainer res;
res.m_shape = m_shape.subshape(1, ndim());
auto subSize = res.shape().size();
Scalar *begin = m_storage.begin().get() + index * subSize;
res.m_storage.set(StorageType_(begin, subSize, false));
return res;
#else
LIBRAPID_ERROR("CUDA support not enabled");
#endif // LIBRAPID_HAS_CUDA
} else if constexpr (typetraits::IsFixedStorage<StorageType_>::value) {
return ArrayView(*this)[index];
} else {
ArrayContainer res;
res.m_shape = m_shape.subshape(1, ndim());
auto subSize = res.shape().size();
Scalar *begin = m_storage.begin() + index * subSize;
Scalar *end = begin + subSize;
res.m_storage.set(StorageType_(begin, end, false));
return res;
}
}
template<typename ShapeType_, typename StorageType_>
template<typename... Indices>
auto ArrayContainer<ShapeType_, StorageType_>::operator()(Indices... indices) const
-> DirectSubscriptType {
LIBRAPID_ASSERT(
m_shape.ndim() == sizeof...(Indices),
"ArrayContainer::operator() called with {} indices, but array has {} dimensions",
sizeof...(Indices),
m_shape.ndim());
int64_t index = 0;
for (int64_t i : {indices...}) {
LIBRAPID_ASSERT(
i >= 0 && i < static_cast<int64_t>(m_shape[index]),
"Index {} out of bounds in ArrayContainer::operator() with dimension={}",
i,
m_shape[index]);
index = index * m_shape[index] + i;
}
return m_storage[index];
}
template<typename ShapeType_, typename StorageType_>
template<typename... Indices>
auto ArrayContainer<ShapeType_, StorageType_>::operator()(Indices... indices)
-> DirectRefSubscriptType {
LIBRAPID_ASSERT(
m_shape.ndim() == sizeof...(Indices),
"ArrayContainer::operator() called with {} indices, but array has {} dimensions",
sizeof...(Indices),
m_shape.ndim());
int64_t index = 0;
int64_t count = 0;
for (int64_t i : {indices...}) {
LIBRAPID_ASSERT(
i >= 0 && i < static_cast<int64_t>(m_shape[count]),
"Index {} out of bounds in ArrayContainer::operator() with dimension={}",
i,
m_shape[index]);
index = index * m_shape[count++] + i;
}
return m_storage[index];
}
template<typename ShapeType_, typename StorageType_>
auto ArrayContainer<ShapeType_, StorageType_>::get() const -> Scalar {
LIBRAPID_ASSERT(m_shape.ndim() == 0,
"Can only cast a scalar ArrayView to a salar object");
return scalar(0);
}
template<typename ShapeType_, typename StorageType_>
auto ArrayContainer<ShapeType_, StorageType_>::ndim() const noexcept ->
typename ShapeType_::SizeType {
return m_shape.ndim();
}
template<typename ShapeType_, typename StorageType_>
auto ArrayContainer<ShapeType_, StorageType_>::shape() const noexcept -> const ShapeType & {
return m_shape;
}
template<typename ShapeType_, typename StorageType_>
auto ArrayContainer<ShapeType_, StorageType_>::storage() const noexcept
-> const StorageType & {
return m_storage;
}
template<typename ShapeType_, typename StorageType_>
auto ArrayContainer<ShapeType_, StorageType_>::storage() noexcept -> StorageType & {
return m_storage;
}
template<typename ShapeType_, typename StorageType_>
auto ArrayContainer<ShapeType_, StorageType_>::packet(size_t index) const -> Packet {
Packet res;
res.load(m_storage.begin() + index);
return res;
}
template<typename ShapeType_, typename StorageType_>
auto ArrayContainer<ShapeType_, StorageType_>::scalar(size_t index) const -> Scalar {
return m_storage[index];
}
template<typename ShapeType_, typename StorageType_>
void ArrayContainer<ShapeType_, StorageType_>::writePacket(size_t index,
const Packet &value) {
value.store(m_storage.begin() + index);
}
template<typename ShapeType_, typename StorageType_>
void ArrayContainer<ShapeType_, StorageType_>::write(size_t index, const Scalar &value) {
m_storage[index] = value;
}
template<typename ShapeType_, typename StorageType_>
auto ArrayContainer<ShapeType_, StorageType_>::begin() const noexcept -> Iterator {
return Iterator(ArrayView(*this), 0);
}
template<typename ShapeType_, typename StorageType_>
auto ArrayContainer<ShapeType_, StorageType_>::end() const noexcept -> Iterator {
return Iterator(ArrayView(*this), m_shape[0]);
}
template<typename ShapeType_, typename StorageType_>
auto ArrayContainer<ShapeType_, StorageType_>::begin() -> Iterator {
return Iterator(ArrayView(*this), 0);
}
template<typename ShapeType_, typename StorageType_>
auto ArrayContainer<ShapeType_, StorageType_>::end() -> Iterator {
return Iterator(ArrayView(*this), m_shape[0]);
}
template<typename ShapeType_, typename StorageType_>
std::string ArrayContainer<ShapeType_, StorageType_>::str(const std::string &format) const {
return ArrayView(*this).str(format);
}
} // namespace array
namespace detail {
template<typename T>
struct IsArrayType {
static constexpr bool val = false;
};
template<typename T>
struct IsArrayType<ArrayRef<T>> {
static constexpr bool val = true;
};
template<typename... T>
struct IsArrayType<FunctionRef<T...>> {
static constexpr bool val = true;
};
template<typename T>
struct IsArrayType<array::ArrayView<T>> {
static constexpr bool val = true;
};
template<typename First, typename... Types>
struct ContainsArrayType {
static constexpr auto evaluator() {
if constexpr (sizeof...(Types) == 0)
return IsArrayType<First>::val;
else
return IsArrayType<First>::val || ContainsArrayType<Types...>::val;
};
static constexpr bool val = evaluator();
};
}; // namespace detail
} // namespace librapid
// Support FMT printing
#ifdef FMT_API
LIBRAPID_SIMPLE_IO_IMPL(typename ShapeType_ COMMA typename StorageType_,
librapid::array::ArrayContainer<ShapeType_ COMMA StorageType_>)
LIBRAPID_SIMPLE_IO_NORANGE(typename ShapeType_ COMMA typename StorageType_,
librapid::array::ArrayContainer<ShapeType_ COMMA StorageType_>)
#endif // FMT_API
#endif // LIBRAPID_ARRAY_ARRAY_CONTAINER_HPP