Skip to content

Commit

Permalink
Better matrix from data
Browse files Browse the repository at this point in the history
  • Loading branch information
Pencilcaseman committed Aug 24, 2023
1 parent 62c3cdb commit 4769335
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 91 deletions.
4 changes: 2 additions & 2 deletions librapid/include/librapid/array/arrayContainer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ namespace librapid {
template<typename T>
struct IsArrayContainer : std::false_type {};

template<typename StorageScalar>
struct IsArrayContainer<array::ArrayContainer<Shape, StorageScalar>> : std::true_type {};
template<typename ShapeType, typename StorageScalar>
struct IsArrayContainer<array::ArrayContainer<ShapeType, StorageScalar>> : std::true_type {};

LIBRAPID_DEFINE_AS_TYPE(typename StorageScalar,
array::ArrayContainer<Shape COMMA StorageScalar>);
Expand Down
129 changes: 63 additions & 66 deletions librapid/include/librapid/array/arrayFromData.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<typename Scalar, typename Backend>
LIBRAPID_ALWAYS_INLINE auto
array::ArrayContainer<Scalar, Backend>::fromData(const std::initializer_list<Scalar> &data)
-> ArrayContainer {
template<typename ShapeType, typename StorageType>
LIBRAPID_ALWAYS_INLINE auto array::ArrayContainer<ShapeType, StorageType>::fromData(
const std::initializer_list<Scalar> &data) -> ArrayContainer {
static_assert(!std::is_same_v<ShapeType, MatrixShape>,
"Cannot create a matrix from a 1D array");
LIBRAPID_ASSERT(data.size() > 0, "Array must have at least one element");
return ArrayContainer(data);
}

template<typename Scalar, typename Backend>
template<typename ShapeType, typename StorageType>
LIBRAPID_ALWAYS_INLINE auto
array::ArrayContainer<Scalar, Backend>::fromData(const std::vector<Scalar> &data)
array::ArrayContainer<ShapeType, StorageType>::fromData(const std::vector<Scalar> &data)
-> ArrayContainer {
static_assert(!std::is_same_v<ShapeType, MatrixShape>,
"Cannot create a matrix from a 1D array");
LIBRAPID_ASSERT(data.size() > 0, "Array must have at least one element");
return ArrayContainer(data);
}

template<typename Scalar, typename Backend>
LIBRAPID_ALWAYS_INLINE auto array::ArrayContainer<Scalar, Backend>::fromData(
template<typename ShapeType, typename StorageType>
LIBRAPID_ALWAYS_INLINE auto array::ArrayContainer<ShapeType, StorageType>::fromData(
const std::initializer_list<std::initializer_list<Scalar>> &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<ShapeType, MatrixShape>) {
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<typename Scalar, typename Backend>
LIBRAPID_ALWAYS_INLINE auto
array::ArrayContainer<Scalar, Backend>::fromData(const std::vector<std::vector<Scalar>> &data)
-> ArrayContainer {
template<typename ShapeType, typename StorageType>
LIBRAPID_ALWAYS_INLINE auto array::ArrayContainer<ShapeType, StorageType>::fromData(
const std::vector<std::vector<Scalar>> &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<ShapeType, MatrixShape>) {
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<typename Scalar, typename Backend> \
// auto array::ArrayContainer<Scalar, Backend>::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<Scalar, Backend>(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<typename Scalar, typename Backend> \
LIBRAPID_ALWAYS_INLINE auto array::ArrayContainer<Scalar, Backend>::fromData(const TYPE &data) \
-> ArrayContainer { \
template<typename ShapeType, typename StorageType> \
LIBRAPID_ALWAYS_INLINE auto array::ArrayContainer<ShapeType, StorageType>::fromData( \
const TYPE &data) -> ArrayContainer { \
LIBRAPID_ASSERT(data.size() > 0, "Cannot create a zero-sized array"); \
std::vector<ArrayContainer> tmp(data.size()); \
int64_t index = 0; \
Expand Down
26 changes: 18 additions & 8 deletions librapid/include/librapid/array/linalg/arrayMultiply.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -612,17 +612,27 @@ namespace librapid {
typename First, typename Second,
typename std::enable_if_t<IsArrayType<First>::value && IsArrayType<Second>::value, int> = 0>
auto dot(First &&a, Second &&b) {
using ScalarA = typename typetraits::TypeInfo<std::decay_t<First>>::Scalar;
using ScalarB = typename typetraits::TypeInfo<std::decay_t<Second>>::Scalar;
using BackendA = typename typetraits::TypeInfo<std::decay_t<First>>::Backend;
using BackendB = typename typetraits::TypeInfo<std::decay_t<Second>>::Backend;
using ArrayA = Array<ScalarA, BackendA>;
using ArrayB = Array<ScalarB, BackendB>;
using ScalarA = typename typetraits::TypeInfo<std::decay_t<First>>::Scalar;
using ScalarB = typename typetraits::TypeInfo<std::decay_t<Second>>::Scalar;
using BackendA = typename typetraits::TypeInfo<std::decay_t<First>>::Backend;
using BackendB = typename typetraits::TypeInfo<std::decay_t<Second>>::Backend;
using ArrayA = Array<ScalarA, BackendA>;
using ArrayB = Array<ScalarB, BackendB>;
using ShapeTypeA = typename typetraits::TypeInfo<std::decay_t<First>>::ShapeType;
using ShapeTypeB = typename typetraits::TypeInfo<std::decay_t<Second>>::ShapeType;
using StorageTypeA = typename typetraits::TypeInfo<std::decay_t<First>>::StorageType;
using StorageTypeB = typename typetraits::TypeInfo<std::decay_t<Second>>::StorageType;

auto [transA, alpha, arrA] = detail::dotHelper(std::forward<First>(a));
auto [transB, beta, arrB] = detail::dotHelper(std::forward<Second>(b));
return linalg::ArrayMultiply(
transA, transB, std::forward<ArrayA>(arrA), alpha * beta, std::forward<ArrayB>(arrB), 0);
return linalg::
ArrayMultiply<ShapeTypeA, StorageTypeA, ShapeTypeB, StorageTypeB, ScalarA, ScalarB>(
transA,
transB,
std::forward<ArrayA>(arrA),
alpha * beta,
std::forward<ArrayB>(arrB),
0);
}

namespace typetraits {
Expand Down
70 changes: 55 additions & 15 deletions librapid/include/librapid/array/shape.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -389,41 +389,81 @@ namespace librapid {

template<typename V>
LIBRAPID_ALWAYS_INLINE MatrixShape::MatrixShape(const std::initializer_list<V> &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<typename V>
LIBRAPID_ALWAYS_INLINE MatrixShape::MatrixShape(const std::vector<V> &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<typename V>
LIBRAPID_ALWAYS_INLINE auto MatrixShape::operator=(const std::initializer_list<V> &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<typename V>
LIBRAPID_ALWAYS_INLINE auto MatrixShape::operator=(const std::vector<V> &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;
}

Expand Down

0 comments on commit 4769335

Please sign in to comment.