diff --git a/librapid/include/librapid/array/arrayContainer.hpp b/librapid/include/librapid/array/arrayContainer.hpp index 96e41daf..b68dac35 100644 --- a/librapid/include/librapid/array/arrayContainer.hpp +++ b/librapid/include/librapid/array/arrayContainer.hpp @@ -72,8 +72,8 @@ namespace librapid { template struct IsArrayContainer : std::false_type {}; - template - struct IsArrayContainer> : std::true_type {}; + template + struct IsArrayContainer> : std::true_type {}; LIBRAPID_DEFINE_AS_TYPE(typename StorageScalar, array::ArrayContainer); diff --git a/librapid/include/librapid/array/arrayFromData.hpp b/librapid/include/librapid/array/arrayFromData.hpp index aba73326..4b69b628 100644 --- a/librapid/include/librapid/array/arrayFromData.hpp +++ b/librapid/include/librapid/array/arrayFromData.hpp @@ -2,94 +2,91 @@ #define LIBRAPID_ARRAY_FROM_DATA_HPP namespace librapid { - /// \brief Create an array from a list of values (possibly multi-dimensional) - /// - /// Create a new array from a potentially nested list of values. It is possible to specify the - /// data type of the Array with the \p Scalar template parameter. If no type is specified, the - /// type will be inferred from the data. The backend on which the Array is created can also be - /// specified with the \p Backend template parameter. If no backend is specified, the Array will - /// be created on the CPU. - /// - /// \tparam Scalar The type of the Array - /// \tparam Backend The backend on which the Array is created - /// \param data The data from which the Array is created - /// \return The created Array - template - LIBRAPID_ALWAYS_INLINE auto - array::ArrayContainer::fromData(const std::initializer_list &data) - -> ArrayContainer { + template + LIBRAPID_ALWAYS_INLINE auto array::ArrayContainer::fromData( + const std::initializer_list &data) -> ArrayContainer { + static_assert(!std::is_same_v, + "Cannot create a matrix from a 1D array"); LIBRAPID_ASSERT(data.size() > 0, "Array must have at least one element"); return ArrayContainer(data); } - template + template LIBRAPID_ALWAYS_INLINE auto - array::ArrayContainer::fromData(const std::vector &data) + array::ArrayContainer::fromData(const std::vector &data) -> ArrayContainer { + static_assert(!std::is_same_v, + "Cannot create a matrix from a 1D array"); LIBRAPID_ASSERT(data.size() > 0, "Array must have at least one element"); return ArrayContainer(data); } - template - LIBRAPID_ALWAYS_INLINE auto array::ArrayContainer::fromData( + template + LIBRAPID_ALWAYS_INLINE auto array::ArrayContainer::fromData( const std::initializer_list> &data) -> ArrayContainer { LIBRAPID_ASSERT(data.size() > 0, "Cannot create a zero-sized array"); - auto newShape = ShapeType({data.size(), data.begin()->size()}); + + if constexpr (std::is_same_v) { + auto newShape = ShapeType({data.size(), data.begin()->size()}); + auto res = ArrayContainer(newShape); + for (size_t i = 0; i < data.size(); ++i) { + LIBRAPID_ASSERT(data.begin()[i].size() == newShape[1], + "Arrays must have consistent shapes"); + for (size_t j = 0; j < data.begin()[i].size(); ++j) { + res(i, j) = data.begin()[i].begin()[j]; + } + } + return res; + } else { + auto newShape = ShapeType({data.size(), data.begin()->size()}); #if defined(LIBRAPID_ENABLE_ASSERT) - for (size_t i = 0; i < data.size(); ++i) { - LIBRAPID_ASSERT(data.begin()[i].size() == newShape[1], - "Arrays must have consistent shapes"); - } + for (size_t i = 0; i < data.size(); ++i) { + LIBRAPID_ASSERT(data.begin()[i].size() == newShape[1], + "Arrays must have consistent shapes"); + } #endif - auto res = ArrayContainer(newShape); - int64_t index = 0; - for (const auto &item : data) res[index++] = fromData(item); - return res; + auto res = ArrayContainer(newShape); + int64_t index = 0; + for (const auto &item : data) res[index++] = fromData(item); + return res; + } } - template - LIBRAPID_ALWAYS_INLINE auto - array::ArrayContainer::fromData(const std::vector> &data) - -> ArrayContainer { + template + LIBRAPID_ALWAYS_INLINE auto array::ArrayContainer::fromData( + const std::vector> &data) -> ArrayContainer { LIBRAPID_ASSERT(data.size() > 0, "Cannot create a zero-sized array"); - auto newShape = ShapeType({data.size(), data.begin()->size()}); + + if constexpr (std::is_same_v) { + auto newShape = ShapeType({data.size(), data[0].size()}); + auto res = ArrayContainer(newShape); + for (size_t i = 0; i < data.size(); ++i) { + LIBRAPID_ASSERT(data[i].size() == newShape[1], + "Arrays must have consistent shapes"); + for (size_t j = 0; j < data[i].size(); ++j) { + res(i, j) = data[i][j]; + } + } + return res; + } else { + auto newShape = ShapeType({data.size(), data.begin()->size()}); #if defined(LIBRAPID_ENABLE_ASSERT) - for (size_t i = 0; i < data.size(); ++i) { - LIBRAPID_ASSERT(data.begin()[i].size() == newShape[1], - "Arrays must have consistent shapes"); - } + for (size_t i = 0; i < data.size(); ++i) { + LIBRAPID_ASSERT(data.begin()[i].size() == newShape[1], + "Arrays must have consistent shapes"); + } #endif - auto res = ArrayContainer(newShape); - int64_t index = 0; - for (const auto &item : data) res[index++] = fromData(item); - return res; + auto res = ArrayContainer(newShape); + int64_t index = 0; + for (const auto &item : data) res[index++] = fromData(item); + return res; + } } - //#define HIGHER_DIMENSIONAL_FROM_DATA(TYPE) \ -// template \ -// auto array::ArrayContainer::fromData(const TYPE &data) -> ArrayContainer { \ -// LIBRAPID_ASSERT(data.size() > 0, "Cannot create a zero-sized array"); \ -// auto *tmp = new ArrayContainer[data.size()]; \ -// int64_t index = 0; \ -// for (const auto &item : data) tmp[index++] = fromData(item); \ -// auto zeroShape = tmp[0].shape(); \ -// for (int64_t i = 0; i < data.size(); ++i) \ -// LIBRAPID_ASSERT(tmp[i].shape().operator==(zeroShape), \ -// "Arrays must have consistent shapes"); \ -// auto newShape = ShapeType::zeros(zeroShape.ndim() + 1); \ -// newShape[0] = data.size(); \ -// for (size_t i = 0; i < zeroShape.ndim(); ++i) { newShape[i + 1] = zeroShape[i]; } \ -// auto res = Array(newShape); \ -// index = 0; \ -// for (int64_t i = 0; i < data.size(); ++i) res[i] = std::move(tmp[i]); \ -// delete[] tmp; \ -// return res; \ -// } - #define HIGHER_DIMENSIONAL_FROM_DATA(TYPE) \ - template \ - LIBRAPID_ALWAYS_INLINE auto array::ArrayContainer::fromData(const TYPE &data) \ - -> ArrayContainer { \ + template \ + LIBRAPID_ALWAYS_INLINE auto array::ArrayContainer::fromData( \ + const TYPE &data) -> ArrayContainer { \ LIBRAPID_ASSERT(data.size() > 0, "Cannot create a zero-sized array"); \ std::vector tmp(data.size()); \ int64_t index = 0; \ diff --git a/librapid/include/librapid/array/linalg/arrayMultiply.hpp b/librapid/include/librapid/array/linalg/arrayMultiply.hpp index 76af0531..3ba2be5c 100644 --- a/librapid/include/librapid/array/linalg/arrayMultiply.hpp +++ b/librapid/include/librapid/array/linalg/arrayMultiply.hpp @@ -612,17 +612,27 @@ namespace librapid { typename First, typename Second, typename std::enable_if_t::value && IsArrayType::value, int> = 0> auto dot(First &&a, Second &&b) { - using ScalarA = typename typetraits::TypeInfo>::Scalar; - using ScalarB = typename typetraits::TypeInfo>::Scalar; - using BackendA = typename typetraits::TypeInfo>::Backend; - using BackendB = typename typetraits::TypeInfo>::Backend; - using ArrayA = Array; - using ArrayB = Array; + using ScalarA = typename typetraits::TypeInfo>::Scalar; + using ScalarB = typename typetraits::TypeInfo>::Scalar; + using BackendA = typename typetraits::TypeInfo>::Backend; + using BackendB = typename typetraits::TypeInfo>::Backend; + using ArrayA = Array; + using ArrayB = Array; + using ShapeTypeA = typename typetraits::TypeInfo>::ShapeType; + using ShapeTypeB = typename typetraits::TypeInfo>::ShapeType; + using StorageTypeA = typename typetraits::TypeInfo>::StorageType; + using StorageTypeB = typename typetraits::TypeInfo>::StorageType; auto [transA, alpha, arrA] = detail::dotHelper(std::forward(a)); auto [transB, beta, arrB] = detail::dotHelper(std::forward(b)); - return linalg::ArrayMultiply( - transA, transB, std::forward(arrA), alpha * beta, std::forward(arrB), 0); + return linalg:: + ArrayMultiply( + transA, + transB, + std::forward(arrA), + alpha * beta, + std::forward(arrB), + 0); } namespace typetraits { diff --git a/librapid/include/librapid/array/shape.hpp b/librapid/include/librapid/array/shape.hpp index bd4a8ee9..cbd471ad 100644 --- a/librapid/include/librapid/array/shape.hpp +++ b/librapid/include/librapid/array/shape.hpp @@ -389,41 +389,81 @@ namespace librapid { template LIBRAPID_ALWAYS_INLINE MatrixShape::MatrixShape(const std::initializer_list &vals) { - LIBRAPID_ASSERT(vals.size() == 2, "MatrixShape must be initialized with 2 values"); - m_rows = *(vals.begin()); - m_cols = *(vals.begin() + 1); + LIBRAPID_ASSERT(vals.size() <= 2, "MatrixShape must be initialized with 2 values"); + if (vals.size() == 2) { + m_rows = *(vals.begin()); + m_cols = *(vals.begin() + 1); + } else if (vals.size() == 1) { + m_rows = *(vals.begin()); + m_cols = 1; + } else { + m_rows = 0; + m_cols = 0; + } } template LIBRAPID_ALWAYS_INLINE MatrixShape::MatrixShape(const std::vector &vals) { - LIBRAPID_ASSERT(vals.size() == 2, "MatrixShape must be initialized with 2 values"); - m_rows = vals[0]; - m_cols = vals[1]; + LIBRAPID_ASSERT(vals.size() <= 2, "MatrixShape must be initialized with 2 values"); + if (vals.size() == 2) { + m_rows = vals[0]; + m_cols = vals[1]; + } else if (vals.size() == 1) { + m_rows = vals[0]; + m_cols = 1; + } else { + m_rows = 0; + m_cols = 0; + } } LIBRAPID_ALWAYS_INLINE MatrixShape::MatrixShape(const Shape &other) { - LIBRAPID_ASSERT(other.ndim() == 2, + LIBRAPID_ASSERT(other.ndim() <= 2, "MatrixShape must be initialized with 2 dimension, but received {}", other.ndim()); - m_rows = other[0]; - m_cols = other[1]; + if (other.ndim() == 2) { + m_rows = other[0]; + m_cols = other[1]; + } else if (other.ndim() == 1) { + m_rows = other[0]; + m_cols = 1; + } else { + m_rows = 0; + m_cols = 0; + } } template LIBRAPID_ALWAYS_INLINE auto MatrixShape::operator=(const std::initializer_list &vals) -> MatrixShape & { - LIBRAPID_ASSERT(vals.size() == 2, "MatrixShape must be initialized with 2 values"); - m_rows = *(vals.begin()); - m_cols = *(vals.begin() + 1); + LIBRAPID_ASSERT(vals.size() <= 2, "MatrixShape must be initialized with 2 values"); + if (vals.size() == 2) { + m_rows = *(vals.begin()); + m_cols = *(vals.begin() + 1); + } else if (vals.size() == 1) { + m_rows = *(vals.begin()); + m_cols = 1; + } else { + m_rows = 0; + m_cols = 0; + } return *this; } template LIBRAPID_ALWAYS_INLINE auto MatrixShape::operator=(const std::vector &vals) -> MatrixShape & { - LIBRAPID_ASSERT(vals.size() == 2, "MatrixShape must be initialized with 2 values"); - m_rows = vals[0]; - m_cols = vals[1]; + LIBRAPID_ASSERT(vals.size() <= 2, "MatrixShape must be initialized with 2 values"); + if (vals.size() == 2) { + m_rows = vals[0]; + m_cols = vals[1]; + } else if (vals.size() == 1) { + m_rows = vals[0]; + m_cols = 1; + } else { + m_rows = 0; + m_cols = 0; + } return *this; }