You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4672 lines
157 KiB
C++
4672 lines
157 KiB
C++
/*------------------------------------------------------------------------------
|
|
* Copyright (c) 2023 by Bai Bing (seread@163.com)
|
|
* See COPYING file for copying and redistribution conditions.
|
|
*
|
|
* Alians IT Studio.
|
|
*----------------------------------------------------------------------------*/
|
|
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstring>
|
|
#include <execution>
|
|
#include <iterator>
|
|
#include <list>
|
|
#include <numeric>
|
|
#include <random>
|
|
#include <stdexcept>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "_Define.h"
|
|
#include "core/ComplexOperators.h"
|
|
#include "core/Endian.h"
|
|
#include "core/Enums.h"
|
|
#include "core/Error.h"
|
|
#include "core/FileSystem.h"
|
|
#include "core/StaticAsserts.h"
|
|
#include "core/String.h"
|
|
#include "core/TypeTraits.h"
|
|
#include "ASIterators.h"
|
|
#include "ASShape.h"
|
|
#include "ASSlice.h"
|
|
#include "core/FunCheck.h"
|
|
|
|
namespace ais
|
|
{
|
|
template <typename dtype = double, class Allocator = std::allocator<dtype>>
|
|
class AIS_EXPORT Matrix
|
|
{
|
|
private:
|
|
STATIC_ASSERT_VALID_DTYPE(dtype);
|
|
static_assert(is_same_v<dtype, typename Allocator::value_type>,
|
|
"value_type and Allocator::value_type must match");
|
|
|
|
using AllocType = typename std::allocator_traits<Allocator>::template rebind_alloc<dtype>;
|
|
using AllocTraits = std::allocator_traits<AllocType>;
|
|
using self_type = Matrix<dtype, AllocType>;
|
|
|
|
public:
|
|
using value_type = dtype;
|
|
using allocator_type = Allocator;
|
|
using pointer = typename AllocTraits::pointer;
|
|
using const_pointer = typename AllocTraits::const_pointer;
|
|
using reference = dtype &;
|
|
using const_reference = const dtype &;
|
|
using difference_type = typename AllocTraits::difference_type;
|
|
|
|
using iterator = Iterator<dtype, pointer, difference_type>;
|
|
using const_iterator = ConstIterator<dtype, const_pointer, difference_type>;
|
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
|
|
|
using column_iterator = ColumnIterator<dtype, size_t, pointer, difference_type>;
|
|
using const_column_iterator = ConstColumnIterator<dtype, size_t, const_pointer, difference_type>;
|
|
using reverse_column_iterator = std::reverse_iterator<column_iterator>;
|
|
using const_reverse_column_iterator = std::reverse_iterator<const_column_iterator>;
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Default Constructor, not very useful...
|
|
///
|
|
Matrix() = default;
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Constructor
|
|
///
|
|
/// @param squareSize: square number of rows and columns
|
|
///
|
|
explicit Matrix(size_t squareSize) : shape_(squareSize, squareSize),
|
|
size_(squareSize * squareSize)
|
|
{
|
|
new_matrix();
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Constructor
|
|
///
|
|
/// @param rows
|
|
/// @param columns
|
|
///
|
|
Matrix(size_t rows, size_t columns) : shape_(rows, columns),
|
|
size_(rows * columns)
|
|
{
|
|
new_matrix();
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Constructor
|
|
///
|
|
/// @param shape
|
|
///
|
|
explicit Matrix(const Shape &shape) : shape_(shape),
|
|
size_(shape_.size())
|
|
{
|
|
new_matrix();
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Constructor
|
|
///
|
|
/// @param list
|
|
///
|
|
Matrix(std::initializer_list<dtype> list) : shape_(1, (list.size())),
|
|
size_(shape_.size())
|
|
{
|
|
new_matrix();
|
|
// copy data from shape
|
|
if (size_ > 0)
|
|
{
|
|
std::copy(std::execution::par_unseq, list.begin(), list.end(), begin());
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Constructor
|
|
///
|
|
/// @param list: 2D initializer list
|
|
///
|
|
Matrix(const std::initializer_list<std::initializer_list<dtype>> &list) : shape_((list.size()), 0)
|
|
{
|
|
for (const auto &li : list)
|
|
{
|
|
if (shape_.cols == 0)
|
|
{
|
|
shape_.cols = (li.size());
|
|
}
|
|
else if (li.size() != shape_.cols)
|
|
{
|
|
THROW_INVALID_ARGUMENT("All rows of the initializer list needs to have the same number of elements");
|
|
}
|
|
}
|
|
|
|
size_ = shape_.size();
|
|
new_matrix();
|
|
size_t row = 0;
|
|
for (const auto &li : list)
|
|
{
|
|
const auto ptr = begin() += row * shape_.cols;
|
|
std::copy(std::execution::par_unseq, li.begin(), li.end(), ptr);
|
|
++row;
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Constructor
|
|
///
|
|
/// @param array
|
|
/// @param copy: (optional) boolean for whether to make a copy and own the data, or
|
|
/// act as a non-owning shell. Default true.
|
|
///
|
|
template <size_t arraySize, std::enable_if_t<is_valid_dtype_v<dtype>, int> = 0>
|
|
Matrix(std::array<dtype, arraySize> &array, bool copy = true) : shape_(1, arraySize),
|
|
size_(shape_.size())
|
|
{
|
|
if (copy)
|
|
{
|
|
new_matrix();
|
|
if (size_ > 0)
|
|
{
|
|
std::copy(std::execution::par_unseq, array.begin(), array.end(), begin());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
array_ = array.data();
|
|
ownsPtr_ = false;
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Constructor
|
|
///
|
|
/// @param array2D
|
|
/// @param copy: (optional) boolean for whether to make a copy and own the data, or
|
|
/// act as a non-owning shell. Default true.
|
|
///
|
|
template <size_t d0Size, size_t d1Size>
|
|
Matrix(std::array<std::array<dtype, d1Size>, d0Size> &array2D, bool copy = true) : shape_(d0Size, d1Size),
|
|
size_(shape_.size())
|
|
{
|
|
if (copy)
|
|
{
|
|
new_matrix();
|
|
if (size_ > 0)
|
|
{
|
|
const auto start = array2D.front().begin();
|
|
std::copy(std::execution::par_unseq, start, start + size_, begin());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
array_ = array2D.front().data();
|
|
ownsPtr_ = false;
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Constructor
|
|
///
|
|
/// @param vector
|
|
/// @param copy: (optional) boolean for whether to make a copy and own the data, or
|
|
/// act as a non-owning shell. Default true.
|
|
///
|
|
template <std::enable_if_t<is_valid_dtype_v<dtype>, int> = 0>
|
|
Matrix(std::vector<dtype> &vector, bool copy = true) : shape_(1, vector.size()),
|
|
size_(shape_.size())
|
|
{
|
|
if (copy)
|
|
{
|
|
new_matrix();
|
|
if (size_ > 0)
|
|
{
|
|
std::copy(std::execution::par_unseq, vector.begin(), vector.end(), begin());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
array_ = vector.data();
|
|
ownsPtr_ = false;
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Constructor
|
|
///
|
|
/// @param vector2D
|
|
///
|
|
explicit Matrix(const std::vector<std::vector<dtype>> &vector2D) : shape_(vector2D.size(), 0)
|
|
{
|
|
for (const auto &row : vector2D)
|
|
{
|
|
if (shape_.cols == 0)
|
|
{
|
|
shape_.cols = row.size();
|
|
}
|
|
else if (row.size() != shape_.cols)
|
|
{
|
|
THROW_INVALID_ARGUMENT("All rows of the 2d vector need to have the same number of elements");
|
|
}
|
|
}
|
|
|
|
size_ = shape_.size();
|
|
|
|
new_matrix();
|
|
auto currentPosition = begin();
|
|
for (const auto &row : vector2D)
|
|
{
|
|
std::copy(std::execution::par_unseq, row.begin(), row.end(), currentPosition);
|
|
currentPosition += shape_.cols;
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Constructor
|
|
///
|
|
/// @param list
|
|
///
|
|
explicit Matrix(const std::list<dtype> &list) : shape_(1, list.size()),
|
|
size_(shape_.size())
|
|
{
|
|
new_matrix();
|
|
if (size_ > 0)
|
|
{
|
|
std::copy(std::execution::par_unseq, list.begin(), list.end(), begin());
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Constructor
|
|
///
|
|
/// @param begin
|
|
/// @param end
|
|
///
|
|
template <typename Iterator,
|
|
std::enable_if_t<is_same_v<typename std::iterator_traits<Iterator>::value_type, dtype>, int> = 0>
|
|
Matrix(Iterator begin, Iterator end) : shape_(1, (std::distance(begin, end))),
|
|
size_(shape_.size())
|
|
{
|
|
new_matrix();
|
|
if (size_ > 0)
|
|
{
|
|
std::copy(std::execution::par_unseq, begin, end, begin());
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Constructor. Copies the contents of the buffer into
|
|
/// the array.
|
|
///
|
|
/// @param ptr: const_pointer to beginning of buffer
|
|
/// @param size: number of elements in buffer
|
|
///
|
|
Matrix(const_pointer ptr, size_t size) : shape_(1, size),
|
|
size_(size)
|
|
{
|
|
new_matrix();
|
|
if (ptr != nullptr && size_ > 0)
|
|
{
|
|
std::copy(std::execution::par_unseq, ptr, ptr + size_, begin());
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Constructor. Copies the contents of the buffer into
|
|
/// the array.
|
|
///
|
|
/// @param ptr: const_pointer to beginning of buffer
|
|
/// @param rows: number of rows of the buffer
|
|
/// @param columns: number of cols of the buffer
|
|
///
|
|
template <typename UIntType1,
|
|
typename UIntType2,
|
|
std::enable_if_t<!is_same_v<UIntType1, bool>, int> = 0,
|
|
std::enable_if_t<!is_same_v<UIntType2, bool>, int> = 0>
|
|
Matrix(const_pointer ptr, UIntType1 rows, UIntType2 columns) : shape_(rows, columns),
|
|
size_(shape_.size())
|
|
{
|
|
new_matrix();
|
|
if (ptr != nullptr && size_ > 0)
|
|
{
|
|
std::copy(std::execution::par_unseq, ptr, ptr + size_, begin());
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Constructor. Operates as a shell around an already existing
|
|
/// array of data.
|
|
///
|
|
/// @param ptr: pointer to beginning of the array
|
|
/// @param size: the number of elements in the array
|
|
/// @param takeOwnership: whether or not to take ownership of the data
|
|
/// and call delete[] in the destructor.
|
|
///
|
|
Matrix(pointer ptr, size_t size, bool takeOwnership) noexcept : shape_(1, size),
|
|
size_(size),
|
|
array_(ptr),
|
|
ownsPtr_(takeOwnership)
|
|
{
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Constructor. Operates as a shell around an already existing
|
|
/// array of data.
|
|
///
|
|
/// @param ptr: pointer to beginning of the array
|
|
/// @param rows: the number of rows in the array
|
|
/// @param columns: the number of column in the array
|
|
/// @param takeOwnership: whether or not to take ownership of the data
|
|
/// and call delete[] in the destructor.
|
|
///
|
|
Matrix(pointer ptr, size_t rows, size_t columns, bool takeOwnership) noexcept : shape_(rows, columns),
|
|
size_(rows * columns),
|
|
array_(ptr),
|
|
ownsPtr_(takeOwnership)
|
|
{
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Copy Constructor
|
|
///
|
|
/// @param otherArray
|
|
///
|
|
Matrix(const Matrix<dtype> &otherArray) : shape_(otherArray.shape_),
|
|
size_(otherArray.size_),
|
|
endianess_(otherArray.endianess_)
|
|
{
|
|
new_matrix();
|
|
if (size_ > 0)
|
|
{
|
|
std::copy(std::execution::par_unseq, otherArray.cbegin(), otherArray.cend(), begin());
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Move Constructor
|
|
///
|
|
/// @param otherArray
|
|
///
|
|
Matrix(Matrix<dtype> &&otherArray) noexcept : shape_(otherArray.shape_),
|
|
size_(otherArray.size_),
|
|
endianess_(otherArray.endianess_),
|
|
array_(otherArray.array_),
|
|
ownsPtr_(otherArray.ownsPtr_)
|
|
{
|
|
otherArray.shape_.rows = otherArray.shape_.cols = 0;
|
|
otherArray.size_ = 0;
|
|
otherArray.ownsPtr_ = false;
|
|
otherArray.array_ = nullptr;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Destructor
|
|
///
|
|
~Matrix() noexcept
|
|
{
|
|
clear();
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Assignment operator, performs a deep copy
|
|
///
|
|
/// @param rhs
|
|
/// @return Matrix<dtype>
|
|
///
|
|
Matrix<dtype> &operator=(const Matrix<dtype> &rhs)
|
|
{
|
|
if (&rhs != this)
|
|
{
|
|
if (rhs.size_ > 0)
|
|
{
|
|
new_matrix(rhs.shape_);
|
|
endianess_ = rhs.endianess_;
|
|
|
|
std::copy(std::execution::par_unseq, rhs.cbegin(), rhs.cend(), begin());
|
|
}
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Assignment operator, sets the entire array to a single
|
|
/// scaler value.
|
|
///
|
|
/// @param value
|
|
/// @return Matrix<dtype>
|
|
///
|
|
Matrix<dtype> &operator=(value_type value) noexcept
|
|
{
|
|
if (array_ != nullptr)
|
|
{
|
|
std::fill(std::execution::par_unseq, begin(), end(), value);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Move operator, performs a deep move
|
|
///
|
|
/// @param rhs
|
|
/// @return Matrix<dtype>
|
|
///
|
|
Matrix<dtype> &operator=(Matrix<dtype> &&rhs) noexcept
|
|
{
|
|
if (&rhs != this)
|
|
{
|
|
clear();
|
|
shape_ = rhs.shape_;
|
|
size_ = rhs.size_;
|
|
endianess_ = rhs.endianess_;
|
|
array_ = rhs.array_;
|
|
ownsPtr_ = rhs.ownsPtr_;
|
|
|
|
rhs.shape_.rows = rhs.shape_.cols = rhs.size_ = 0;
|
|
rhs.array_ = nullptr;
|
|
rhs.ownsPtr_ = false;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// 1D access operator with no bounds checking
|
|
///
|
|
/// @param pos
|
|
/// @return value
|
|
///
|
|
inline reference operator[](int64_t pos)
|
|
{
|
|
//if ((size_t)std::abs(pos) >= size_)
|
|
//{
|
|
// THROW_INVALID_ARGUMENT("pos out of data range");
|
|
//}
|
|
|
|
if (pos < 0)
|
|
{
|
|
pos += size_;
|
|
}
|
|
|
|
return array_[pos];
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const 1D access operator with no bounds checking
|
|
///
|
|
/// @param pos
|
|
/// @return value
|
|
///
|
|
inline const_reference operator[](int64_t pos) const
|
|
{
|
|
//if ((size_t)std::abs(pos) >= size_)
|
|
//{
|
|
// THROW_INVALID_ARGUMENT("pos out of data range");
|
|
//}
|
|
|
|
if (pos < 0)
|
|
{
|
|
// return value from backward, just like python [-1]
|
|
pos += size_;
|
|
}
|
|
|
|
return array_[pos];
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// 2D access operator with no bounds checking
|
|
///
|
|
/// @param rowPos
|
|
/// @param columnPos
|
|
/// @return value
|
|
///
|
|
inline reference operator()(int64_t rowPos, int64_t columnPos)
|
|
{
|
|
if (rowPos < 0)
|
|
{
|
|
rowPos += shape_.rows;
|
|
}
|
|
|
|
if (columnPos < 0)
|
|
{
|
|
columnPos += shape_.cols;
|
|
}
|
|
|
|
auto pos = rowPos * shape_.cols + columnPos;
|
|
//if (pos >= size_)
|
|
//{
|
|
// THROW_INVALID_ARGUMENT("pos out of data range");
|
|
//}
|
|
|
|
return array_[pos];
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const 2D access operator with no bounds checking
|
|
///
|
|
/// @param rowPos
|
|
/// @param columnPos
|
|
/// @return value
|
|
///
|
|
inline const_reference operator()(int64_t rowPos, int64_t columnPos) const
|
|
{
|
|
if (rowPos < 0)
|
|
{
|
|
rowPos += shape_.rows;
|
|
}
|
|
|
|
if (columnPos < 0)
|
|
{
|
|
columnPos += shape_.cols;
|
|
}
|
|
|
|
auto pos = rowPos * shape_.cols + columnPos;
|
|
//if (pos >= size_)
|
|
//{
|
|
// THROW_INVALID_ARGUMENT("pos out of data range");
|
|
//}
|
|
|
|
return array_[pos];
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// 1D Slicing access operator with bounds checking.
|
|
/// returned array is of the range [start, stop).
|
|
///
|
|
/// @param slice
|
|
/// @return Matrix
|
|
///
|
|
inline Matrix<dtype> operator[](const Slice &slice) const
|
|
{
|
|
Slice sliceCopy(slice);
|
|
|
|
size_t counter = 0;
|
|
Matrix<dtype> returnArray(1, sliceCopy.element_number(size_));
|
|
for (size_t i = sliceCopy.start; i < sliceCopy.stop; i += sliceCopy.step)
|
|
{
|
|
returnArray[counter++] = at(i);
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns the values from the input mask
|
|
///
|
|
/// @param mask
|
|
/// @return Matrix
|
|
///
|
|
inline Matrix<dtype> operator[](const Matrix<bool> &mask) const
|
|
{
|
|
if (mask.shape() != shape_)
|
|
{
|
|
THROW_INVALID_ARGUMENT(
|
|
"input mask must have the same shape as the matrix it will be masking.");
|
|
}
|
|
|
|
auto indices = mask.flatnonzero();
|
|
auto outArray = Matrix<dtype>(1, indices.size());
|
|
for (size_t i = 0; i < indices.size(); ++i)
|
|
{
|
|
outArray[i] = operator[](indices[i]);
|
|
}
|
|
|
|
return outArray;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns the values from the input indices
|
|
///
|
|
/// @param indices
|
|
/// @return Matrix
|
|
///
|
|
///
|
|
template <typename Indices, enable_if_t<is_same_v<Indices, Matrix<size_t>>, int> = 0>
|
|
inline Matrix<dtype> operator[](const Indices &indices) const
|
|
{
|
|
if (indices.max().item() > size_ - 1)
|
|
{
|
|
THROW_INVALID_ARGUMENT("input indices must be less than the array size.");
|
|
}
|
|
|
|
auto outArray = Matrix<dtype>(1, static_cast<size_t>(indices.size()));
|
|
size_t i = 0;
|
|
for (auto &index : indices)
|
|
{
|
|
outArray[i++] = operator[](index);
|
|
}
|
|
|
|
return outArray;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// 2D Slicing access operator with bounds checking.
|
|
/// returned array is of the range [start, stop).
|
|
///
|
|
/// @param rowSlice
|
|
/// @param columnSlice
|
|
/// @return Matrix
|
|
///
|
|
inline Matrix<dtype> operator()(Slice rowSlice, Slice columnSlice) const
|
|
{
|
|
Matrix<dtype> returnArray(rowSlice.element_number(shape_.rows), columnSlice.element_number(shape_.cols));
|
|
|
|
size_t rowCounter = 0;
|
|
size_t columnCounter = 0;
|
|
for (size_t row = rowSlice.start; row < rowSlice.stop; row += rowSlice.step)
|
|
{
|
|
for (size_t col = columnSlice.start; col < columnSlice.stop; col += columnSlice.step)
|
|
{
|
|
returnArray(rowCounter, columnCounter++) = at(row, col);
|
|
}
|
|
columnCounter = 0;
|
|
++rowCounter;
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// 2D Slicing access operator with bounds checking.
|
|
/// returned array is of the range [start, stop).
|
|
///
|
|
/// @param rowSlice
|
|
/// @param columnPos
|
|
/// @return Matrix
|
|
///
|
|
inline Matrix<dtype> operator()(Slice rowSlice, size_t columnPos) const
|
|
{
|
|
Matrix<dtype> returnArray(rowSlice.element_number(shape_.rows), 1);
|
|
|
|
size_t rowCounter = 0;
|
|
for (size_t row = rowSlice.start; row < rowSlice.stop; row += rowSlice.step)
|
|
{
|
|
returnArray(rowCounter++, 0) = at(row, columnPos);
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// 2D Slicing access operator with bounds checking.
|
|
/// returned array is of the range [start, stop).
|
|
///
|
|
/// @param rowPos
|
|
/// @param columnSlice
|
|
/// @return Matrix
|
|
///
|
|
inline Matrix<dtype> operator()(size_t rowPos, Slice columnSlice) const
|
|
{
|
|
Matrix<dtype> returnArray(1, columnSlice.element_number(shape_.cols));
|
|
|
|
size_t columnCounter = 0;
|
|
for (size_t col = columnSlice.start; col < columnSlice.stop; col += columnSlice.step)
|
|
{
|
|
returnArray(0, columnCounter++) = at(rowPos, col);
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// 2D index access operator with bounds checking.
|
|
/// returned array is of the range.
|
|
///
|
|
/// @param rowIndices
|
|
/// @param columnPos
|
|
/// @return Matrix
|
|
///
|
|
template <typename Indices,
|
|
enable_if_t<is_same_v<Indices, Matrix<size_t>> || is_same_v<Indices, Matrix<size_t>>, int> = 0>
|
|
inline Matrix<dtype> operator()(const Indices &rowIndices, size_t columnPos) const
|
|
{
|
|
const Matrix<size_t> columnIndices = {columnPos};
|
|
return operator()(rowIndices, columnIndices);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// 2D index access operator with bounds checking.
|
|
/// returned array is of the range.
|
|
///
|
|
/// @param rowIndices
|
|
/// @param columnSlice
|
|
/// @return Matrix
|
|
///
|
|
template <typename Indices,
|
|
enable_if_t<is_same_v<Indices, Matrix<size_t>> || is_same_v<Indices, Matrix<size_t>>, int> = 0>
|
|
inline Matrix<dtype> operator()(const Indices &rowIndices, Slice columnSlice) const
|
|
{
|
|
return operator()(rowIndices, to_indices(columnSlice, Axis::COLUMN));
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// 2D index access operator with bounds checking.
|
|
/// returned array is of the range.
|
|
///
|
|
/// @param rowIndex
|
|
/// @param columnIndices
|
|
/// @return Matrix
|
|
///
|
|
template <typename Indices,
|
|
enable_if_t<is_same_v<Indices, Matrix<size_t>> || is_same_v<Indices, Matrix<size_t>>, int> = 0>
|
|
inline Matrix<dtype> operator()(size_t rowIndex, const Indices &columnIndices) const
|
|
{
|
|
const Matrix<size_t> rowIndices = {rowIndex};
|
|
return operator()(rowIndices, columnIndices);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// 2D index access operator with bounds checking.
|
|
/// returned array is of the range.
|
|
///
|
|
/// @param rowSlice
|
|
/// @param columnIndices
|
|
/// @return Matrix
|
|
///
|
|
template <typename Indices,
|
|
enable_if_t<is_same_v<Indices, Matrix<size_t>> || is_same_v<Indices, Matrix<size_t>>, int> = 0>
|
|
inline Matrix<dtype> operator()(Slice rowSlice, const Indices &columnIndices) const
|
|
{
|
|
return operator()(to_indices(rowSlice, Axis::ROW), columnIndices);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// 2D index access operator with bounds checking.
|
|
/// returned array is of the range.
|
|
///
|
|
/// @param rowIndices
|
|
/// @param columnIndices
|
|
/// @return Matrix
|
|
///
|
|
template <typename RowIndices,
|
|
typename ColumnIndices,
|
|
enable_if_t<is_same_v<RowIndices, Matrix<size_t>> || is_same_v<RowIndices, Matrix<size_t>>, int> = 0,
|
|
enable_if_t<is_same_v<ColumnIndices, Matrix<size_t>> || is_same_v<ColumnIndices, Matrix<size_t>>, int> = 0>
|
|
inline Matrix<dtype> operator()(RowIndices rowIndices, ColumnIndices columnIndices) const
|
|
{
|
|
rowIndices.sort();
|
|
columnIndices.sort();
|
|
|
|
std::vector<size_t> rowIndicesUnique(rowIndices.size());
|
|
std::vector<size_t> columnIndicesUnique(columnIndices.size());
|
|
|
|
const auto lastRow =
|
|
std::unique_copy(std::execution::par_unseq, rowIndices.begin(), rowIndices.end(), rowIndicesUnique.begin());
|
|
const auto lastColumn =
|
|
std::unique_copy(std::execution::par_unseq, columnIndices.begin(), columnIndices.end(), columnIndicesUnique.begin());
|
|
|
|
Matrix<dtype> returnArray((lastRow - rowIndicesUnique.begin()),
|
|
(lastColumn - columnIndicesUnique.begin()));
|
|
|
|
size_t rowCounter = 0;
|
|
for (auto rowIter = rowIndicesUnique.begin(); rowIter != lastRow; ++rowIter)
|
|
{
|
|
size_t columnCounter = 0;
|
|
for (auto columnIter = columnIndicesUnique.begin(); columnIter != lastColumn; ++columnIter)
|
|
{
|
|
returnArray(rowCounter, columnCounter++) = at(*rowIter, *columnIter);
|
|
}
|
|
|
|
++rowCounter;
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns a Slice object for slicing a row to the end of
|
|
/// array.
|
|
///
|
|
/// @param startIndex (default 0)
|
|
/// @param step (default 1)
|
|
/// @return Slice
|
|
///
|
|
Slice column_slice(size_t startIndex = 0, size_t step = 1) const noexcept
|
|
{
|
|
return Slice(startIndex, shape_.cols, step);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns a Slice object for slicing a column to the end
|
|
/// of the array.
|
|
///
|
|
/// @param startIndex (default 0)
|
|
/// @param step (default 1)
|
|
/// @return Slice
|
|
///
|
|
Slice row_slice(size_t startIndex = 0, size_t step = 1) const noexcept
|
|
{
|
|
return Slice(startIndex, shape_.rows, step);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// 1D access method with bounds checking
|
|
///
|
|
/// @param pos
|
|
/// @return value
|
|
///
|
|
inline reference at(int64_t pos)
|
|
{
|
|
if ((size_t)std::abs(pos) >= size_)
|
|
{
|
|
std::string errStr = "Input index " + std::to_string(pos);
|
|
errStr += " is out of bounds for array of size " + std::to_string(size_) + ".";
|
|
THROW_INVALID_ARGUMENT(errStr);
|
|
}
|
|
|
|
return operator[](pos);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const 1D access method with bounds checking
|
|
///
|
|
/// @param pos
|
|
/// @return value
|
|
///
|
|
inline const_reference at(int64_t pos) const
|
|
{
|
|
if (std::abs(pos) >= size_)
|
|
{
|
|
std::string errStr = "Input index " + std::to_string(pos);
|
|
errStr += " is out of bounds for array of size " + std::to_string(size_) + ".";
|
|
THROW_INVALID_ARGUMENT(errStr);
|
|
}
|
|
|
|
return operator[](pos);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// 2D access method with bounds checking
|
|
///
|
|
/// @param rowPos
|
|
/// @param columnPos
|
|
/// @return value
|
|
///
|
|
inline reference at(int64_t rowPos, int64_t columnPos)
|
|
{
|
|
// this doesn't allow for calling the first element as -size_...
|
|
// but why would you really want to do that anyway?
|
|
if ((size_t)std::abs(rowPos) >= shape_.rows)
|
|
{
|
|
std::string errStr = "Row index " + std::to_string(rowPos);
|
|
errStr += " is out of bounds for array of size " + std::to_string(shape_.rows) + ".";
|
|
THROW_INVALID_ARGUMENT(errStr);
|
|
}
|
|
|
|
// this doesn't allow for calling the first element as -size_...
|
|
// but why would you really want to that anyway?
|
|
if ((size_t)std::abs(columnPos) >= shape_.cols)
|
|
{
|
|
std::string errStr = "Column index " + std::to_string(columnPos);
|
|
errStr += " is out of bounds for array of size " + std::to_string(shape_.cols) + ".";
|
|
THROW_INVALID_ARGUMENT(errStr);
|
|
}
|
|
|
|
return operator()(rowPos, columnPos);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const 2D access method with bounds checking
|
|
///
|
|
/// @param rowPos
|
|
/// @param columnPos
|
|
/// @return value
|
|
///
|
|
inline const_reference at(int64_t rowPos, int64_t columnPos) const
|
|
{
|
|
// this doesn't allow for calling the first element as -size_...
|
|
// but why would you really want to do that anyway?
|
|
if ((size_t)std::abs(rowPos) >= shape_.rows)
|
|
{
|
|
std::string errStr = "Row index " + std::to_string(rowPos);
|
|
errStr += " is out of bounds for array of size " + std::to_string(shape_.rows) + ".";
|
|
THROW_INVALID_ARGUMENT(errStr);
|
|
}
|
|
|
|
// this doesn't allow for calling the first element as -size_...
|
|
// but why would you really want to do that anyway?
|
|
if ((size_t)std::abs(columnPos) >= shape_.cols)
|
|
{
|
|
std::string errStr = "Column index " + std::to_string(columnPos);
|
|
errStr += " is out of bounds for array of size " + std::to_string(shape_.cols) + ".";
|
|
THROW_INVALID_ARGUMENT(errStr);
|
|
}
|
|
|
|
return operator()(rowPos, columnPos);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// 2D access method with bounds checking
|
|
///
|
|
/// @param rowPos
|
|
/// @param columnPos
|
|
/// @return value
|
|
///
|
|
std::pair<int32_t, int32_t> get_pos(iterator ci) const
|
|
{
|
|
if (size_ == 0)
|
|
THROW_INVALID_ARGUMENT("Matrix is empty.");
|
|
|
|
auto dis = std::distance(iterator(array_), ci);
|
|
auto r = int32_t(dis / shape_.cols);
|
|
auto c = int32_t(dis % shape_.cols);
|
|
|
|
return std::make_pair(r, c);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// get <row, col> from index
|
|
///
|
|
/// @return value
|
|
///
|
|
std::pair<int32_t, int32_t> get_pos(size_t i) const
|
|
{
|
|
if (size_ == 0)
|
|
THROW_INVALID_ARGUMENT("Matrix is empty.");
|
|
|
|
auto r = int32_t(i / shape_.cols);
|
|
auto c = int32_t(i % shape_.cols);
|
|
|
|
return std::make_pair(r, c);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return index of specials element
|
|
///
|
|
/// @param rowPos
|
|
/// @param columnPos
|
|
/// @return index
|
|
///
|
|
const size_t index(int64_t rowPos, int64_t columnPos) const
|
|
{
|
|
if (rowPos < 0)
|
|
{
|
|
rowPos += shape_.rows;
|
|
}
|
|
|
|
if (columnPos < 0)
|
|
{
|
|
columnPos += shape_.cols;
|
|
}
|
|
|
|
auto pos = rowPos * shape_.cols + columnPos;
|
|
if (pos >= size_)
|
|
{
|
|
THROW_INVALID_ARGUMENT("pos out of data range");
|
|
}
|
|
|
|
return pos;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:i
|
|
/// const 1D access method with bounds checking
|
|
///
|
|
/// @param slice
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> at(const Slice &slice) const
|
|
{
|
|
// the slice operator already provides bounds checking. just including
|
|
// the at method for completeness
|
|
return operator[](slice);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const 2D access method with bounds checking
|
|
///
|
|
/// @param rowSlice
|
|
/// @param columnSlice
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> at(const Slice &rowSlice, const Slice &columnSlice) const
|
|
{
|
|
// the slice operator already provides bounds checking. just including
|
|
// the at method for completeness
|
|
return operator()(rowSlice, columnSlice);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const 2D access method with bounds checking
|
|
///
|
|
/// @param rowSlice
|
|
/// @param columnPos
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> at(const Slice &rowSlice, size_t columnPos) const
|
|
{
|
|
// the slice operator already provides bounds checking. just including
|
|
// the at method for completeness
|
|
return operator()(rowSlice, columnPos);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const 2D access method with bounds checking
|
|
///
|
|
/// @param rowPos
|
|
/// @param columnSlice
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> at(size_t rowPos, const Slice &columnSlice) const
|
|
{
|
|
// the slice operator already provides bounds checking. just including
|
|
// the at method for completeness
|
|
return operator()(rowPos, columnSlice);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const 2D access method with bounds checking
|
|
///
|
|
/// @param rowIndices
|
|
/// @param columnIndices
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> at(const Matrix<size_t> &rowIndices, const Matrix<size_t> &columnIndices) const
|
|
{
|
|
// the slice operator already provides bounds checking. just including
|
|
// the at method for completeness
|
|
return operator()(rowIndices, columnIndices);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// iterator to the beginning of the flattened array
|
|
/// @return iterator
|
|
///
|
|
iterator begin() noexcept
|
|
{
|
|
return iterator(array_);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// iterator to the beginning of the input row
|
|
///
|
|
/// @param row
|
|
/// @return iterator
|
|
///
|
|
iterator begin(size_t row)
|
|
{
|
|
if (row >= shape_.rows)
|
|
{
|
|
THROW_INVALID_ARGUMENT("input row is greater than the number of rows in the array.");
|
|
}
|
|
|
|
return begin() += (row * shape_.cols);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const iterator to the beginning of the flattened array
|
|
/// @return const_iterator
|
|
///
|
|
const_iterator begin() const noexcept
|
|
{
|
|
return cbegin();
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const iterator to the beginning of the input row
|
|
///
|
|
/// @param row
|
|
/// @return const_iterator
|
|
///
|
|
const_iterator begin(size_t row) const
|
|
{
|
|
return cbegin(row);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const iterator to the beginning of the flattened array
|
|
///
|
|
/// @return const_iterator
|
|
///
|
|
const_iterator cbegin() const noexcept
|
|
{
|
|
return const_iterator(array_);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const iterator to the beginning of the input row
|
|
///
|
|
/// @param row
|
|
/// @return const_iterator
|
|
///
|
|
const_iterator cbegin(size_t row) const
|
|
{
|
|
if (row >= shape_.rows)
|
|
{
|
|
THROW_INVALID_ARGUMENT("input row is greater than the number of rows in the array.");
|
|
}
|
|
|
|
return cbegin() += (row * shape_.cols);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// column_iterator to the beginning of the flattened array
|
|
/// @return column_iterator
|
|
///
|
|
column_iterator colbegin() noexcept
|
|
{
|
|
return column_iterator(array_, shape_.rows, shape_.cols);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// column_iterator to the beginning of the input column
|
|
///
|
|
/// @param column
|
|
/// @return column_iterator
|
|
///
|
|
column_iterator colbegin(size_t column)
|
|
{
|
|
if (column >= shape_.cols)
|
|
{
|
|
THROW_INVALID_ARGUMENT("input col is greater than the number of cols in the array.");
|
|
}
|
|
|
|
return colbegin() += (column * shape_.rows);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const column_iterator to the beginning of the flattened array
|
|
/// @return const_column_iterator
|
|
///
|
|
const_column_iterator colbegin() const noexcept
|
|
{
|
|
return ccolbegin();
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const column_iterator to the beginning of the input column
|
|
///
|
|
/// @param column
|
|
/// @return const_column_iterator
|
|
///
|
|
const_column_iterator colbegin(size_t column) const
|
|
{
|
|
return ccolbegin(column);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const_column_iterator to the beginning of the flattened array
|
|
///
|
|
/// @return const_column_iterator
|
|
///
|
|
const_column_iterator ccolbegin() const noexcept
|
|
{
|
|
return const_column_iterator(array_, shape_.rows, shape_.cols);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const_column_iterator to the beginning of the input column
|
|
///
|
|
/// @param column
|
|
/// @return const_column_iterator
|
|
///
|
|
const_column_iterator ccolbegin(size_t column) const
|
|
{
|
|
if (column >= shape_.cols)
|
|
{
|
|
THROW_INVALID_ARGUMENT("input col is greater than the number of cols in the array.");
|
|
}
|
|
|
|
return ccolbegin() += (column * shape_.rows);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// reverse_iterator to the beginning of the flattened array
|
|
/// @return reverse_iterator
|
|
///
|
|
reverse_iterator rbegin() noexcept
|
|
{
|
|
return reverse_iterator(end());
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// reverse_iterator to the beginning of the input row
|
|
///
|
|
/// @param row
|
|
/// @return reverse_iterator
|
|
///
|
|
reverse_iterator rbegin(size_t row)
|
|
{
|
|
if (row >= shape_.rows)
|
|
{
|
|
THROW_INVALID_ARGUMENT("input row is greater than the number of rows in the array.");
|
|
}
|
|
|
|
return rbegin() += (shape_.rows - row - 1) * shape_.cols;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const iterator to the beginning of the flattened array
|
|
/// @return const_iterator
|
|
///
|
|
const_reverse_iterator rbegin() const noexcept
|
|
{
|
|
return crbegin();
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const iterator to the beginning of the input row
|
|
///
|
|
/// @param row
|
|
/// @return const_iterator
|
|
///
|
|
const_reverse_iterator rbegin(size_t row) const
|
|
{
|
|
return crbegin(row);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const_reverse_iterator to the beginning of the flattened array
|
|
///
|
|
/// @return const_reverse_iterator
|
|
///
|
|
const_reverse_iterator crbegin() const noexcept
|
|
{
|
|
return const_reverse_iterator(cend());
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const_reverse_iterator to the beginning of the input row
|
|
///
|
|
/// @param row
|
|
/// @return const_reverse_iterator
|
|
///
|
|
const_reverse_iterator crbegin(size_t row) const
|
|
{
|
|
if (row >= shape_.rows)
|
|
{
|
|
THROW_INVALID_ARGUMENT("input row is greater than the number of rows in the array.");
|
|
}
|
|
|
|
return crbegin() += (shape_.rows - row - 1) * shape_.cols;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// reverse_column_iterator to the beginning of the flattened array
|
|
/// @return reverse_column_iterator
|
|
///
|
|
reverse_column_iterator rcolbegin() noexcept
|
|
{
|
|
return reverse_column_iterator(colend());
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// reverse_column_iterator to the beginning of the input column
|
|
///
|
|
/// @param column
|
|
/// @return reverse_column_iterator
|
|
///
|
|
reverse_column_iterator rcolbegin(size_t column)
|
|
{
|
|
if (column >= shape_.cols)
|
|
{
|
|
THROW_INVALID_ARGUMENT("input col is greater than the number of cols in the array.");
|
|
}
|
|
|
|
return rcolbegin() += (shape_.cols - column - 1) * shape_.rows;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const iterator to the beginning of the flattened array
|
|
/// @return const_iterator
|
|
///
|
|
const_reverse_column_iterator rcolbegin() const noexcept
|
|
{
|
|
return crcolbegin();
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const iterator to the beginning of the input column
|
|
///
|
|
/// @param column
|
|
/// @return const_iterator
|
|
///
|
|
const_reverse_column_iterator rcolbegin(size_t column) const
|
|
{
|
|
return crcolbegin(column);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const_reverse_column_iterator to the beginning of the flattened array
|
|
///
|
|
/// @return const_reverse_column_iterator
|
|
///
|
|
const_reverse_column_iterator crcolbegin() const noexcept
|
|
{
|
|
return const_reverse_column_iterator(ccolend());
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const_reverse_column_iterator to the beginning of the input column
|
|
///
|
|
/// @param column
|
|
/// @return const_reverse_column_iterator
|
|
///
|
|
const_reverse_column_iterator crcolbegin(size_t column) const
|
|
{
|
|
if (column >= shape_.cols)
|
|
{
|
|
THROW_INVALID_ARGUMENT("input col is greater than the number of cols in the array.");
|
|
}
|
|
|
|
return crcolbegin() += (shape_.cols - column - 1) * shape_.rows;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// iterator to 1 past the end of the flattened array
|
|
/// @return iterator
|
|
///
|
|
iterator end() noexcept
|
|
{
|
|
return begin() += size_;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// iterator to the 1 past end of the row
|
|
///
|
|
/// @param row
|
|
/// @return iterator
|
|
///
|
|
iterator end(size_t row)
|
|
{
|
|
if (row >= shape_.rows)
|
|
{
|
|
THROW_INVALID_ARGUMENT("input row is greater than the number of rows in the array.");
|
|
}
|
|
|
|
return begin(row) += shape_.cols;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const iterator to 1 past the end of the flattened array
|
|
/// @return const_iterator
|
|
///
|
|
const_iterator end() const noexcept
|
|
{
|
|
return cend();
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const iterator to the 1 past end of the row
|
|
///
|
|
/// @param row
|
|
/// @return const_iterator
|
|
///
|
|
const_iterator end(size_t row) const
|
|
{
|
|
return cend(row);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const iterator to 1 past the end of the flattened array
|
|
///
|
|
/// @return const_iterator
|
|
///
|
|
const_iterator cend() const noexcept
|
|
{
|
|
return cbegin() += size_;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const iterator to 1 past the end of the input row
|
|
///
|
|
/// @param row
|
|
/// @return const_iterator
|
|
///
|
|
const_iterator cend(size_t row) const
|
|
{
|
|
if (row >= shape_.rows)
|
|
{
|
|
THROW_INVALID_ARGUMENT("input row is greater than the number of rows in the array.");
|
|
}
|
|
|
|
return cbegin(row) += shape_.cols;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// reverse_iterator to 1 past the end of the flattened array
|
|
/// @return reverse_iterator
|
|
///
|
|
reverse_iterator rend() noexcept
|
|
{
|
|
return rbegin() += size_;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// reverse_iterator to the 1 past end of the row
|
|
///
|
|
/// @param row
|
|
/// @return reverse_iterator
|
|
///
|
|
reverse_iterator rend(size_t row)
|
|
{
|
|
if (row >= shape_.rows)
|
|
{
|
|
THROW_INVALID_ARGUMENT("input row is greater than the number of rows in the array.");
|
|
}
|
|
|
|
return rbegin(row) += shape_.cols;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const_reverse_iterator to 1 past the end of the flattened array
|
|
/// @return const_reverse_iterator
|
|
///
|
|
const_reverse_iterator rend() const noexcept
|
|
{
|
|
return crend();
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const_reverse_iterator to the 1 past end of the row
|
|
///
|
|
/// @param row
|
|
/// @return const_reverse_iterator
|
|
///
|
|
const_reverse_iterator rend(size_t row) const
|
|
{
|
|
return crend(row);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const_reverse_iterator to 1 past the end of the flattened array
|
|
///
|
|
/// @return const_reverse_iterator
|
|
///
|
|
const_reverse_iterator crend() const noexcept
|
|
{
|
|
return crbegin() += size_;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const_reverse_iterator to 1 past the end of the input row
|
|
///
|
|
/// @param row
|
|
/// @return const_reverse_iterator
|
|
///
|
|
const_reverse_iterator crend(size_t row) const
|
|
{
|
|
if (row >= shape_.rows)
|
|
{
|
|
THROW_INVALID_ARGUMENT("input row is greater than the number of rows in the array.");
|
|
}
|
|
|
|
return crbegin(row) += shape_.cols;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// column_iterator to 1 past the end of the flattened array
|
|
/// @return column_iterator
|
|
///
|
|
column_iterator colend() noexcept
|
|
{
|
|
return colbegin() += size_;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// column_iterator to the 1 past end of the column
|
|
///
|
|
/// @param column
|
|
/// @return column_iterator
|
|
///
|
|
column_iterator colend(size_t column)
|
|
{
|
|
if (column >= shape_.cols)
|
|
{
|
|
THROW_INVALID_ARGUMENT("input col is greater than the number of cols in the array.");
|
|
}
|
|
|
|
return colbegin(column) += shape_.rows;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const column_iterator to 1 past the end of the flattened array
|
|
/// @return const_column_iterator
|
|
///
|
|
const_column_iterator colend() const noexcept
|
|
{
|
|
return ccolend();
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const column_iterator to the 1 past end of the column
|
|
///
|
|
/// @param column
|
|
/// @return const_column_iterator
|
|
///
|
|
const_column_iterator colend(size_t column) const
|
|
{
|
|
return ccolend(column);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const_column_iterator to 1 past the end of the flattened array
|
|
///
|
|
/// @return const_column_iterator
|
|
///
|
|
const_column_iterator ccolend() const noexcept
|
|
{
|
|
return ccolbegin() += size_;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const_column_iterator to 1 past the end of the input col
|
|
///
|
|
/// @param column
|
|
/// @return const_column_iterator
|
|
///
|
|
const_column_iterator ccolend(size_t column) const
|
|
{
|
|
if (column >= shape_.cols)
|
|
{
|
|
THROW_INVALID_ARGUMENT("input col is greater than the number of cols in the array.");
|
|
}
|
|
|
|
return ccolbegin(column) += shape_.rows;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// reverse_column_iterator to 1 past the end of the flattened array
|
|
/// @return reverse_column_iterator
|
|
///
|
|
reverse_column_iterator rcolend() noexcept
|
|
{
|
|
return rcolbegin() += size_;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// reverse_column_iterator to the 1 past end of the column
|
|
///
|
|
/// @param column
|
|
/// @return reverse_column_iterator
|
|
///
|
|
reverse_column_iterator rcolend(size_t column)
|
|
{
|
|
if (column >= shape_.cols)
|
|
{
|
|
THROW_INVALID_ARGUMENT("input col is greater than the number of cols in the array.");
|
|
}
|
|
|
|
return rcolbegin(column) += shape_.rows;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const_reverse_column_iterator to 1 past the end of the flattened array
|
|
/// @return const_reverse_column_iterator
|
|
///
|
|
const_reverse_column_iterator rcolend() const noexcept
|
|
{
|
|
return crcolend();
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const_reverse_column_iterator to the 1 past end of the column
|
|
///
|
|
/// @param column
|
|
/// @return const_reverse_column_iterator
|
|
///
|
|
const_reverse_column_iterator rcolend(size_t column) const
|
|
{
|
|
return crcolend(column);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const_reverse_column_iterator to 1 past the end of the flattened array
|
|
///
|
|
/// @return const_reverse_column_iterator
|
|
///
|
|
const_reverse_column_iterator crcolend() const noexcept
|
|
{
|
|
return crcolbegin() += size_;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// const_reverse_column_iterator to 1 past the end of the input col
|
|
///
|
|
/// @param column
|
|
/// @return const_reverse_column_iterator
|
|
///
|
|
const_reverse_column_iterator crcolend(size_t column) const
|
|
{
|
|
if (column >= shape_.cols)
|
|
{
|
|
THROW_INVALID_ARGUMENT("input col is greater than the number of cols in the array.");
|
|
}
|
|
|
|
return crcolbegin(column) += shape_.rows;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns True if all elements evaluate to True or non zero
|
|
///
|
|
/// @param axis (Optional, default NONE)
|
|
/// @return Matrix
|
|
///
|
|
Matrix<bool> all(Axis axis = Axis::NONE) const
|
|
{
|
|
STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype);
|
|
|
|
const auto function = [](dtype i) -> bool
|
|
{ return i != dtype{0}; };
|
|
|
|
switch (axis)
|
|
{
|
|
case Axis::NONE:
|
|
{
|
|
Matrix<bool> returnArray = {std::all_of(std::execution::par_unseq, cbegin(), cend(), function)};
|
|
return returnArray;
|
|
}
|
|
case Axis::COLUMN:
|
|
{
|
|
Matrix<bool> returnArray(1, shape_.rows);
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
returnArray(0, row) = std::all_of(std::execution::par_unseq, cbegin(row), cend(row), function);
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
case Axis::ROW:
|
|
{
|
|
return transpose().all(Axis::COLUMN);
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented axis type.");
|
|
return {}; // get rid of compiler warning
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns True if all elements evaluate to True or non zero
|
|
///
|
|
/// @param axis (Optional, default NONE)
|
|
/// @return Matrix
|
|
///
|
|
Matrix<bool> all(size_t begin_index, size_t end_index, Axis axis = Axis::NONE) const
|
|
{
|
|
STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype);
|
|
|
|
const auto function = [](dtype i) -> bool
|
|
{ return i != dtype{0}; };
|
|
|
|
if ((cbegin() + begin_index) > cend() || (cbegin() + end_index) > cend())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
switch (axis)
|
|
{
|
|
case Axis::NONE:
|
|
{
|
|
Matrix<bool> returnArray = {std::all_of(std::execution::par_unseq, cbegin() + begin_index, cend() + end_index, function)};
|
|
return returnArray;
|
|
}
|
|
case Axis::COLUMN:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented axis type.");
|
|
}
|
|
case Axis::ROW:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented axis type.");
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented axis type.");
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns True if any elements evaluate to True or non zero
|
|
///
|
|
/// @param axis (Optional, default NONE)
|
|
/// @return Matrix
|
|
///
|
|
Matrix<bool> any(Axis axis = Axis::NONE) const
|
|
{
|
|
STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype);
|
|
|
|
const auto function = [](dtype i) -> bool
|
|
{ return i != dtype{0}; };
|
|
|
|
switch (axis)
|
|
{
|
|
case Axis::NONE:
|
|
{
|
|
Matrix<bool> returnArray = {std::any_of(std::execution::par_unseq, cbegin(), cend(), function)};
|
|
return returnArray;
|
|
}
|
|
case Axis::COLUMN:
|
|
{
|
|
Matrix<bool> returnArray(1, shape_.rows);
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
returnArray(0, row) = std::any_of(std::execution::par_unseq, cbegin(row), cend(row), function);
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
case Axis::ROW:
|
|
{
|
|
return transpose().any(Axis::COLUMN);
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented axis type.");
|
|
return {}; // get rid of compiler warning
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return indices of the maximum values along the given axis.
|
|
/// Only the first index is returned.
|
|
///
|
|
/// @param axis (Optional, default NONE)
|
|
/// @return Matrix
|
|
///
|
|
Matrix<size_t> argmax(Axis axis = Axis::NONE) const
|
|
{
|
|
STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype);
|
|
|
|
const auto comparator = [](dtype lhs, dtype rhs) noexcept -> bool
|
|
{ return lhs < rhs; };
|
|
|
|
switch (axis)
|
|
{
|
|
case Axis::NONE:
|
|
{
|
|
Matrix<size_t> returnArray = {(
|
|
std::max_element(std::execution::par_unseq, cbegin(), cend(), comparator) - cbegin())};
|
|
return returnArray;
|
|
}
|
|
case Axis::COLUMN:
|
|
{
|
|
Matrix<size_t> returnArray(1, shape_.rows);
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
returnArray(0, row) = (std::max_element(std::execution::par_unseq, cbegin(row), cend(row), comparator) - cbegin(row));
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
case Axis::ROW:
|
|
{
|
|
return transpose().argmax(Axis::COLUMN);
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented axis type.");
|
|
return {}; // get rid of compiler warning
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return indices of the minimum values along the given axis.
|
|
/// Only the first index is returned.
|
|
///
|
|
/// @param axis (Optional, default NONE)
|
|
/// @return Matrix
|
|
///
|
|
Matrix<size_t> argmin(Axis axis = Axis::NONE) const
|
|
{
|
|
STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype);
|
|
|
|
const auto comparator = [](dtype lhs, dtype rhs) noexcept -> bool
|
|
{ return lhs < rhs; };
|
|
|
|
switch (axis)
|
|
{
|
|
case Axis::NONE:
|
|
{
|
|
Matrix<size_t> returnArray = {(
|
|
std::min_element(std::execution::par_unseq, cbegin(), cend(), comparator) - cbegin())};
|
|
return returnArray;
|
|
}
|
|
case Axis::COLUMN:
|
|
{
|
|
Matrix<size_t> returnArray(1, shape_.rows);
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
returnArray(0, row) = (std::min_element(std::execution::par_unseq, cbegin(row), cend(row), comparator) - cbegin(row));
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
case Axis::ROW:
|
|
{
|
|
return transpose().argmin(Axis::COLUMN);
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented axis type.");
|
|
return {}; // get rid of compiler warning
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns the indices that would sort this array.
|
|
///
|
|
/// @param axis (Optional, default NONE)
|
|
/// @return Matrix
|
|
///
|
|
Matrix<size_t> argsort(Axis axis = Axis::NONE) const
|
|
{
|
|
STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype);
|
|
|
|
switch (axis)
|
|
{
|
|
case Axis::NONE:
|
|
{
|
|
std::vector<size_t> idx(size_);
|
|
std::iota(idx.begin(), idx.end(), 0);
|
|
|
|
const auto function = [this](size_t i1, size_t i2) noexcept -> bool
|
|
{ return (*this)[i1] < (*this)[i2]; };
|
|
|
|
std::stable_sort(idx.begin(), idx.end(), function);
|
|
return Matrix<size_t>(idx);
|
|
}
|
|
case Axis::COLUMN:
|
|
{
|
|
Matrix<size_t> returnArray(shape_);
|
|
std::vector<size_t> idx(shape_.cols);
|
|
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
std::iota(idx.begin(), idx.end(), 0);
|
|
|
|
const auto function = [this, row](size_t i1, size_t i2) noexcept -> bool
|
|
{ return operator()(row, i1) < operator()(row, i2); };
|
|
|
|
std::stable_sort(idx.begin(), idx.end(), function);
|
|
|
|
for (size_t col = 0; col < shape_.cols; ++col)
|
|
{
|
|
returnArray(row, col) = idx[col];
|
|
}
|
|
}
|
|
return returnArray;
|
|
}
|
|
case Axis::ROW:
|
|
{
|
|
return transpose().argsort(Axis::COLUMN).transpose();
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented axis type.");
|
|
return {}; // get rid of compiler warning
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns a copy of the array, cast to a specified type.
|
|
/// Arithmetic to Arithmetic
|
|
///
|
|
/// @return Matrix
|
|
///
|
|
template <typename dtypeOut,
|
|
typename dtype_ = dtype,
|
|
enable_if_t<is_same_v<dtype_, dtype>, int> = 0,
|
|
enable_if_t<is_arithmetic_v<dtype_>, int> = 0,
|
|
enable_if_t<is_arithmetic_v<dtypeOut>, int> = 0>
|
|
Matrix<dtypeOut> astype() const
|
|
{
|
|
Matrix<dtypeOut> outArray(shape_);
|
|
std::transform(std::execution::par_unseq, cbegin(),
|
|
cend(),
|
|
outArray.begin(),
|
|
[](dtype value) -> dtypeOut
|
|
{ return static_cast<dtypeOut>(value); });
|
|
|
|
return outArray;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns a copy of the array, cast to a specified type.
|
|
/// Arithmetic to Complex
|
|
///
|
|
/// @return Matrix
|
|
///
|
|
template <typename dtypeOut,
|
|
typename dtype_ = dtype,
|
|
enable_if_t<is_same_v<dtype_, dtype>, int> = 0,
|
|
enable_if_t<is_arithmetic_v<dtype_>, int> = 0,
|
|
enable_if_t<is_complex_v<dtypeOut>, int> = 0>
|
|
Matrix<dtypeOut> astype() const
|
|
{
|
|
Matrix<dtypeOut> outArray(shape_);
|
|
|
|
const auto function = [](const_reference value) -> dtypeOut
|
|
{ return std::complex<typename dtypeOut::value_type>(value); };
|
|
|
|
std::transform(std::execution::par_unseq, cbegin(), cend(), outArray.begin(), function);
|
|
|
|
return outArray;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns a copy of the array, cast to a specified type.
|
|
/// Complex to Complex
|
|
///
|
|
/// @return Matrix
|
|
///
|
|
template <typename dtypeOut,
|
|
typename dtype_ = dtype,
|
|
enable_if_t<is_same_v<dtype_, dtype>, int> = 0,
|
|
enable_if_t<is_complex_v<dtype_>, int> = 0,
|
|
enable_if_t<is_complex_v<dtypeOut>, int> = 0>
|
|
Matrix<dtypeOut> astype() const
|
|
{
|
|
Matrix<dtypeOut> outArray(shape_);
|
|
|
|
if (is_same_v<dtypeOut, dtype>)
|
|
{
|
|
std::copy(std::execution::par_unseq, cbegin(), cend(), outArray.begin());
|
|
}
|
|
else
|
|
{
|
|
const auto function = [](const_reference value) noexcept -> dtypeOut
|
|
{ return complex_cast<typename dtypeOut::value_type>(value); };
|
|
|
|
std::transform(std::execution::par_unseq, cbegin(), cend(), outArray.begin(), function);
|
|
}
|
|
|
|
return outArray;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns a copy of the array, cast to a specified type.
|
|
/// Complex to Arithmetic
|
|
///
|
|
/// @return Matrix
|
|
///
|
|
template <typename dtypeOut,
|
|
typename dtype_ = dtype,
|
|
enable_if_t<is_same_v<dtype_, dtype>, int> = 0,
|
|
enable_if_t<is_complex_v<dtype_>, int> = 0,
|
|
enable_if_t<is_arithmetic_v<dtypeOut>, int> = 0>
|
|
Matrix<dtypeOut> astype() const
|
|
{
|
|
Matrix<dtypeOut> outArray(shape_);
|
|
|
|
const auto function = [](const_reference value) -> dtypeOut
|
|
{ return static_cast<dtypeOut>(value.real()); };
|
|
|
|
std::transform(std::execution::par_unseq, cbegin(), cend(), outArray.begin(), function);
|
|
|
|
return outArray;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns a copy of the last element of the flattened array.
|
|
///
|
|
/// @return dtype
|
|
///
|
|
const_reference back() const noexcept
|
|
{
|
|
return *(cend() - 1);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns a reference the last element of the flattened array.
|
|
///
|
|
/// @return dtype
|
|
///
|
|
reference back() noexcept
|
|
{
|
|
return *(end() - 1);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns a copy of the last element of the input row.
|
|
///
|
|
/// @return dtype
|
|
///
|
|
const_reference back(size_t row) const
|
|
{
|
|
return *(cend(row) - 1);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns a reference the last element of the input row.
|
|
///
|
|
/// @return dtype
|
|
///
|
|
reference back(size_t row)
|
|
{
|
|
return *(end(row) - 1);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Swap the bytes of the array elements in place
|
|
///
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> &byteswap() noexcept
|
|
{
|
|
STATIC_ASSERT_INTEGER(dtype);
|
|
|
|
std::for_each(std::execution::par_unseq, begin(),
|
|
end(),
|
|
[](dtype &value) noexcept -> void
|
|
{ value = endian::byteSwap(value); });
|
|
|
|
switch (endianess_)
|
|
{
|
|
case Endian::NATIVE:
|
|
{
|
|
endianess_ = endian::isLittleEndian() ? Endian::BIG : Endian::LITTLE;
|
|
break;
|
|
}
|
|
case Endian::LITTLE:
|
|
{
|
|
endianess_ = Endian::BIG;
|
|
break;
|
|
}
|
|
case Endian::BIG:
|
|
{
|
|
endianess_ = Endian::LITTLE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns an array whose values are limited to [min, max].
|
|
///
|
|
/// @param min: min value to clip to
|
|
/// @param max: max value to clip to
|
|
/// @return clipped value
|
|
///
|
|
Matrix<dtype> clip(value_type min, value_type max) const
|
|
{
|
|
STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype);
|
|
|
|
Matrix<dtype> outArray(shape_);
|
|
std::transform(std::execution::par_unseq, cbegin(),
|
|
cend(),
|
|
outArray.begin(),
|
|
[min, max](dtype value) noexcept -> dtype
|
|
{
|
|
#ifdef __cpp_lib_clamp
|
|
const auto comparator = [](dtype lhs, dtype rhs) noexcept -> bool
|
|
{ return lhs < rhs; };
|
|
|
|
return std::clamp(value, min, max, comparator);
|
|
#else
|
|
if (value < min)
|
|
{
|
|
return min;
|
|
}
|
|
else if (value > max)
|
|
{
|
|
return max;
|
|
}
|
|
|
|
return value;
|
|
#endif
|
|
});
|
|
|
|
return outArray;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns the full column of the array
|
|
///
|
|
///
|
|
/// @return Shape
|
|
///
|
|
Matrix<dtype> column(size_t column)
|
|
{
|
|
return operator()(row_slice(), column);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// returns whether or not a value is included the array
|
|
///
|
|
/// @param value
|
|
/// @param axis (Optional, default NONE)
|
|
/// @return bool
|
|
///
|
|
Matrix<bool> contains(value_type value, Axis axis = Axis::NONE) const
|
|
{
|
|
STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype);
|
|
|
|
switch (axis)
|
|
{
|
|
case Axis::NONE:
|
|
{
|
|
Matrix<bool> returnArray = {std::find(std::execution::par_unseq, cbegin(), cend(), value) != cend()};
|
|
return returnArray;
|
|
}
|
|
case Axis::COLUMN:
|
|
{
|
|
Matrix<bool> returnArray(1, shape_.rows);
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
returnArray(0, row) = std::find(std::execution::par_unseq, cbegin(row), cend(row), value) != cend(row);
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
case Axis::ROW:
|
|
{
|
|
return transpose().contains(value, Axis::COLUMN);
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented axis type.");
|
|
return {}; // get rid of compiler warning
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// returns whether or not a value is included the array
|
|
///
|
|
/// @param value
|
|
/// @param axis (Optional, default NONE)
|
|
/// @return bool
|
|
///
|
|
bool containsB(value_type value, Axis axis = Axis::NONE) const
|
|
{
|
|
Matrix<bool> returnArray = contains(value, axis);
|
|
|
|
return std::any_of(std::execution::par_unseq, returnArray.begin(), returnArray.end(), [](bool v)
|
|
{ return v; });
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return a copy of the array
|
|
///
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> copy() const
|
|
{
|
|
return Matrix<dtype>(*this);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return the cumulative product of the elements along the given axis.
|
|
///
|
|
/// [[1,2], [3,4]] =(Axis::NONE)=> [1,2,4,12]
|
|
/// [[1,2], [3,4]] =(Axis::0)=> [[1,2],[3,8]] cumprod by row
|
|
/// [[1,2], [3,4]] =(Axis::1)=> [[1,2],[3,12]] cumprod by column
|
|
///
|
|
/// @param axis (Optional, default NONE)
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> cumprod(Axis axis = Axis::NONE) const
|
|
{
|
|
STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype);
|
|
|
|
switch (axis)
|
|
{
|
|
case Axis::NONE:
|
|
{
|
|
Matrix<dtype> returnArray(1, size_);
|
|
returnArray[0] = front();
|
|
for (size_t i = 1; i < size_; ++i)
|
|
{
|
|
returnArray[i] = returnArray[i - 1] * array_[i];
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
case Axis::COLUMN:
|
|
{
|
|
Matrix<dtype> returnArray(shape_);
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
returnArray(row, 0) = operator()(row, 0);
|
|
for (size_t col = 1; col < shape_.cols; ++col)
|
|
{
|
|
returnArray(row, col) = returnArray(row, col - 1) * operator()(row, col);
|
|
}
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
case Axis::ROW:
|
|
{
|
|
return transpose().cumprod(Axis::COLUMN).transpose();
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented axis type.");
|
|
return {}; // get rid of compiler warning
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return the cumulative sum of the elements along the given axis.
|
|
///
|
|
/// [[1,2], [3,4]] =(Axis::NONE)=> [1,3,6,10]
|
|
/// [[1,2], [3,4]] =(Axis::0)=> [[1,2],[4,6]] cumsum by row
|
|
/// [[1,2], [3,4]] =(Axis::1)=> [[1,3],[3,7]] cumsum by column
|
|
///
|
|
/// @param axis (Optional, default NONE)
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> cumsum(Axis axis = Axis::NONE) const
|
|
{
|
|
STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype);
|
|
|
|
switch (axis)
|
|
{
|
|
case Axis::NONE:
|
|
{
|
|
Matrix<dtype> returnArray(1, size_);
|
|
returnArray[0] = front();
|
|
for (size_t i = 1; i < size_; ++i)
|
|
{
|
|
returnArray[i] = returnArray[i - 1] + array_[i];
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
case Axis::COLUMN:
|
|
{
|
|
Matrix<dtype> returnArray(shape_);
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
returnArray(row, 0) = operator()(row, 0);
|
|
for (size_t col = 1; col < shape_.cols; ++col)
|
|
{
|
|
returnArray(row, col) = returnArray(row, col - 1) + operator()(row, col);
|
|
}
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
case Axis::ROW:
|
|
{
|
|
return transpose().cumsum(Axis::COLUMN).transpose();
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented axis type.");
|
|
return {}; // get rid of compiler warning
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns the raw pointer to the underlying data
|
|
/// @return pointer
|
|
///
|
|
pointer data() noexcept
|
|
{
|
|
return array_;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns the raw pointer to the underlying data
|
|
/// @return const_pointer
|
|
///
|
|
const_pointer data() const noexcept
|
|
{
|
|
return array_;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Releases the internal data pointer so that the destructor
|
|
/// will not call delete on it, and returns the raw pointer
|
|
/// to the underlying data.
|
|
/// @return pointer
|
|
///
|
|
pointer release_data() noexcept
|
|
{
|
|
ownsPtr_ = false;
|
|
return data();
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return specified diagonals.
|
|
///
|
|
/// @param offset: Offset of the diagonal from the main diagonal. Can be both positive and negative. Defaults
|
|
/// to 0.
|
|
/// @param axis: (Optional, default ROW) axis the offset is applied to
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> diagonal(int64_t offset = 0, Axis axis = Axis::ROW) const
|
|
{
|
|
switch (axis)
|
|
{
|
|
case Axis::COLUMN:
|
|
{
|
|
std::vector<dtype> diagonalValues;
|
|
size_t col = 0;
|
|
for (size_t row = offset; row < shape_.rows; ++row)
|
|
{
|
|
if (row < 0)
|
|
{
|
|
++col;
|
|
continue;
|
|
}
|
|
if (col >= shape_.cols)
|
|
{
|
|
break;
|
|
}
|
|
|
|
diagonalValues.push_back(operator()(row, col));
|
|
++col;
|
|
}
|
|
|
|
return Matrix<dtype>(diagonalValues);
|
|
}
|
|
case Axis::ROW:
|
|
{
|
|
return transpose().diagonal(offset, Axis::COLUMN);
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented axis type.");
|
|
return {}; // get rid of compiler warning
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Dot product of two arrays.
|
|
///
|
|
/// For 2-D arrays it is equivalent to matrix multiplication,
|
|
/// and for 1-D arrays to inner product of vectors.
|
|
///
|
|
/// [1, 2], dot [1, 2], ==> [1*1 + 2*2, 1*1 + 2*2], = [5, 4],
|
|
/// [3, 4] [2, 1] [3*1 + 4*2, 3*2 + 4*1] [11, 10]
|
|
///
|
|
/// @param otherArray
|
|
/// @return dot product
|
|
///
|
|
Matrix<dtype> dot(const Matrix<dtype> &otherArray) const
|
|
{
|
|
STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype);
|
|
|
|
if (shape_ == otherArray.shape_ && (shape_.rows == 1 || shape_.cols == 1))
|
|
{
|
|
dtype dotProduct = std::inner_product(cbegin(), cend(), otherArray.cbegin(), dtype{0});
|
|
Matrix<dtype> returnArray = {dotProduct};
|
|
return returnArray;
|
|
}
|
|
if (shape_.cols == otherArray.shape_.rows)
|
|
{
|
|
// 2D array, use matrix multiplication
|
|
Matrix<dtype> returnArray(shape_.rows, otherArray.shape_.cols);
|
|
auto otherArrayT = otherArray.transpose();
|
|
|
|
for (size_t i = 0; i < shape_.rows; ++i)
|
|
{
|
|
for (size_t j = 0; j < otherArrayT.shape_.rows; ++j)
|
|
{
|
|
returnArray(i, j) =
|
|
std::inner_product(otherArrayT.cbegin(j), otherArrayT.cend(j), cbegin(i), dtype{0});
|
|
}
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
|
|
std::string errStr = "shapes of [" + std::to_string(shape_.rows) + ", " + std::to_string(shape_.cols) + "]";
|
|
errStr += " and [" + std::to_string(otherArray.shape_.rows) + ", " +
|
|
std::to_string(otherArray.shape_.cols) + "]";
|
|
errStr += " are not consistent.";
|
|
THROW_INVALID_ARGUMENT(errStr);
|
|
|
|
return Matrix<dtype>(); // get rid of compiler warning
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Dump a binary file of the array to the specified file.
|
|
/// The array can be read back with ais::load.
|
|
///
|
|
/// @param filename
|
|
///
|
|
void dump(const std::string &filename) const
|
|
{
|
|
filesystem::File f(filename);
|
|
if (!f.hasExt())
|
|
{
|
|
f.withExt(".bin");
|
|
}
|
|
|
|
std::ofstream ofile(f.fullname().c_str(), std::ios::binary);
|
|
if (!ofile.good())
|
|
{
|
|
THROW_RUNTIME_ERROR("Unable to open the input file:\n\t" + filename);
|
|
}
|
|
|
|
if (array_ != nullptr)
|
|
{
|
|
ofile.write(reinterpret_cast<const char *>(array_), size_ * sizeof(dtype));
|
|
}
|
|
ofile.close();
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return the matrix's endianess
|
|
///
|
|
/// @return Endian
|
|
///
|
|
Endian endianess() const noexcept
|
|
{
|
|
STATIC_ASSERT_ARITHMETIC(dtype);
|
|
|
|
return endianess_;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Fill the array with a scaler value.
|
|
///
|
|
/// @param fillValue
|
|
/// @return *this
|
|
///
|
|
Matrix<dtype> &fill(value_type fillValue) noexcept
|
|
{
|
|
std::fill(std::execution::par_unseq, begin(), end(), fillValue);
|
|
return std::forward<Matrix<dtype> &>(*this);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return the indices of the flattened array of the
|
|
/// elements that are non-zero.
|
|
///
|
|
/// @return Matrix
|
|
///
|
|
Matrix<size_t> flatnonzero() const
|
|
{
|
|
STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype);
|
|
|
|
std::vector<size_t> indices;
|
|
size_t idx = 0;
|
|
for (auto value : *this)
|
|
{
|
|
if (value != dtype{0})
|
|
{
|
|
indices.push_back(idx);
|
|
}
|
|
++idx;
|
|
}
|
|
|
|
return Matrix<size_t>(indices);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return a copy of the array collapsed into one dimension.
|
|
///
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> flatten() const
|
|
{
|
|
Matrix<dtype> outArray(1, size_);
|
|
std::copy(std::execution::par_unseq, cbegin(), cend(), outArray.begin());
|
|
return outArray;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns a copy of the first element of the flattened array.
|
|
///
|
|
/// @return dtype
|
|
///
|
|
const_reference front() const noexcept
|
|
{
|
|
return *cbegin();
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns a reference to the first element of the flattened array.
|
|
///
|
|
/// @return dtype
|
|
///
|
|
reference front() noexcept
|
|
{
|
|
return *begin();
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns a copy of the first element of the input row.
|
|
///
|
|
/// @return dtype
|
|
///
|
|
const_reference front(size_t row) const
|
|
{
|
|
return *cbegin(row);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns a reference to the first element of the input row.
|
|
///
|
|
/// @return dtype
|
|
///
|
|
reference front(size_t row)
|
|
{
|
|
return *begin(row);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns a new flat array with the givin flat input indices.
|
|
///
|
|
/// @param indices
|
|
/// @return values
|
|
///
|
|
Matrix<dtype> get_by_indices(const Matrix<size_t> &indices) const
|
|
{
|
|
return operator[](indices);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Takes in a boolean mask the same size as the array
|
|
/// and returns a flattened array with the values corresponding
|
|
/// to the input mask.
|
|
///
|
|
/// @param mask
|
|
/// @return values
|
|
///
|
|
Matrix<dtype> get_by_mask(const Matrix<bool> &mask) const
|
|
{
|
|
return operator[](mask);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return if the Matrix is empty. ie the default constructor
|
|
/// was used.
|
|
///
|
|
/// @return boolean
|
|
///
|
|
bool is_empty() const noexcept
|
|
{
|
|
return size_ == 0;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return if the Matrix is flat. ie the number of columns or
|
|
/// rows is equal to one.
|
|
///
|
|
/// @return boolean
|
|
///
|
|
bool is_flat() const noexcept
|
|
{
|
|
return shape_.rows == 1 || shape_.cols == 1;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return if the Matrix is sorted.
|
|
///
|
|
/// @param axis
|
|
/// @return boolean
|
|
///
|
|
Matrix<bool> is_sorted(Axis axis = Axis::NONE) const
|
|
{
|
|
STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype);
|
|
|
|
const auto comparator = [](dtype lhs, dtype rhs) noexcept -> bool
|
|
{ return lhs < rhs; };
|
|
|
|
switch (axis)
|
|
{
|
|
case Axis::NONE:
|
|
{
|
|
return {std::is_sorted(std::execution::par_unseq, cbegin(), cend(), comparator)};
|
|
}
|
|
case Axis::COLUMN:
|
|
{
|
|
Matrix<bool> returnArray(1, shape_.rows);
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
returnArray(0, row) = std::is_sorted(std::execution::par_unseq, cbegin(row), cend(row), comparator);
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
case Axis::ROW:
|
|
{
|
|
return transpose().is_sorted(Axis::COLUMN);
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented axis type.");
|
|
return {}; // get rid of compiler warning
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return if the Matrix is square.
|
|
///
|
|
/// @return boolean
|
|
///
|
|
bool is_square() const noexcept
|
|
{
|
|
return shape_.is_square();
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Copy an element of an array to a standard C++ scaler and return it.
|
|
///
|
|
/// @return array element
|
|
///
|
|
value_type item() const
|
|
{
|
|
if (size_ != 1)
|
|
{
|
|
THROW_INVALID_ARGUMENT("Can only convert an array of size 1 to a C++ scaler");
|
|
}
|
|
|
|
return front();
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return the maximum along a given axis.
|
|
///
|
|
/// @param axis (Optional, default NONE)
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> max(Axis axis = Axis::NONE) const
|
|
{
|
|
// STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype);
|
|
|
|
const auto comparator = [](dtype lhs, dtype rhs) noexcept -> bool
|
|
{ return lhs < rhs; };
|
|
|
|
switch (axis)
|
|
{
|
|
case Axis::NONE:
|
|
{
|
|
Matrix<dtype> returnArray = {*std::max_element(std::execution::par_unseq, cbegin(), cend(), comparator)};
|
|
return returnArray;
|
|
}
|
|
case Axis::COLUMN:
|
|
{
|
|
Matrix<dtype> returnArray(1, shape_.rows);
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
returnArray(0, row) = *std::max_element(std::execution::par_unseq, cbegin(row), cend(row), comparator);
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
case Axis::ROW:
|
|
{
|
|
return transpose().max(Axis::COLUMN);
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented axis type.");
|
|
return {}; // get rid of compiler warning
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return the minimum along a given axis.
|
|
///
|
|
/// @param axis (Optional, default NONE)
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> min(Axis axis = Axis::NONE) const
|
|
{
|
|
STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype);
|
|
|
|
const auto comparator = [](dtype lhs, dtype rhs) noexcept -> bool
|
|
{ return lhs < rhs; };
|
|
|
|
switch (axis)
|
|
{
|
|
case Axis::NONE:
|
|
{
|
|
Matrix<dtype> returnArray = {*std::min_element(std::execution::par_unseq, cbegin(), cend(), comparator)};
|
|
return returnArray;
|
|
}
|
|
case Axis::COLUMN:
|
|
{
|
|
Matrix<dtype> returnArray(1, shape_.rows);
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
returnArray(0, row) = *std::min_element(std::execution::par_unseq, cbegin(row), cend(row), comparator);
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
case Axis::ROW:
|
|
{
|
|
return transpose().min(Axis::COLUMN);
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented axis type.");
|
|
return {}; // get rid of compiler warning
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return the median along a given axis.
|
|
/// If the dtype is floating point then the middle elements will be
|
|
/// averaged for arrays of even number of elements.
|
|
/// If the dtype is integral then the middle elements will be integer
|
|
/// averaged (rounded down to integer) for arrays of even number of elements.
|
|
///
|
|
/// @param axis (Optional, default NONE)
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> median(Axis axis = Axis::NONE) const
|
|
{
|
|
STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype);
|
|
|
|
const auto comparator = [](dtype lhs, dtype rhs) noexcept -> bool
|
|
{ return lhs < rhs; };
|
|
|
|
if (size_ == 0)
|
|
{
|
|
THROW_RUNTIME_ERROR("Median is undefined for an array of size = 0.");
|
|
}
|
|
|
|
switch (axis)
|
|
{
|
|
case Axis::NONE:
|
|
{
|
|
Matrix<dtype> copyArray(*this);
|
|
|
|
const size_t middleIdx = size_ / 2; // integer division
|
|
std::nth_element(std::execution::par_unseq,
|
|
copyArray.begin(),
|
|
copyArray.begin() + middleIdx,
|
|
copyArray.end(),
|
|
comparator);
|
|
|
|
dtype medianValue = copyArray.array_[middleIdx];
|
|
if (size_ % 2 == 0)
|
|
{
|
|
const size_t lhsIndex = middleIdx - 1;
|
|
std::nth_element(std::execution::par_unseq,
|
|
copyArray.begin(),
|
|
copyArray.begin() + lhsIndex,
|
|
copyArray.end(),
|
|
comparator);
|
|
medianValue =
|
|
(medianValue + copyArray.array_[lhsIndex]) / dtype{2}; // potentially integer division, ok
|
|
}
|
|
|
|
return {medianValue};
|
|
}
|
|
case Axis::COLUMN:
|
|
{
|
|
Matrix<dtype> copyArray(*this);
|
|
Matrix<dtype> returnArray(1, shape_.rows);
|
|
|
|
const bool isEven = shape_.cols % 2 == 0;
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
const size_t middleIdx = shape_.cols / 2; // integer division
|
|
std::nth_element(std::execution::par_unseq,
|
|
copyArray.begin(row),
|
|
copyArray.begin(row) + middleIdx,
|
|
copyArray.end(row),
|
|
comparator);
|
|
|
|
dtype medianValue = copyArray(row, middleIdx);
|
|
if (isEven)
|
|
{
|
|
const size_t lhsIndex = middleIdx - 1;
|
|
std::nth_element(std::execution::par_unseq,
|
|
copyArray.begin(row),
|
|
copyArray.begin(row) + lhsIndex,
|
|
copyArray.end(row),
|
|
comparator);
|
|
medianValue = (medianValue + copyArray(row, lhsIndex)) / dtype{2}; // potentially integer division, ok
|
|
}
|
|
|
|
returnArray(0, row) = medianValue;
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
case Axis::ROW:
|
|
{
|
|
return transpose().median(Axis::COLUMN);
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented axis type.");
|
|
return {}; // get rid of compiler warning
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Fills the array with nans.
|
|
///
|
|
Matrix<dtype> &nans() noexcept
|
|
{
|
|
STATIC_ASSERT_FLOAT(dtype);
|
|
|
|
fill(std::nan("1"));
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Fills the array with empty.
|
|
///
|
|
Matrix<dtype> &emptys() noexcept
|
|
{
|
|
fill({});
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns the number of bytes held by the array
|
|
///
|
|
/// @return number of bytes
|
|
///
|
|
uint64_t nbytes() const noexcept
|
|
{
|
|
return static_cast<uint64_t>(sizeof(dtype) * size_);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return the array with the same data viewed with a
|
|
/// different byte order. only works for integer types.
|
|
///
|
|
/// @param endianess
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> new_byte_order(Endian endianess) const
|
|
{
|
|
STATIC_ASSERT_INTEGER(dtype);
|
|
|
|
const bool nativeIsLittle = endian::isLittleEndian();
|
|
|
|
switch (endianess_)
|
|
{
|
|
case Endian::NATIVE:
|
|
{
|
|
switch (endianess)
|
|
{
|
|
case Endian::NATIVE:
|
|
{
|
|
return Matrix(*this);
|
|
}
|
|
case Endian::BIG:
|
|
{
|
|
if (nativeIsLittle)
|
|
{
|
|
Matrix<dtype> outArray(shape_);
|
|
|
|
std::transform(std::execution::par_unseq, cbegin(), end(), outArray.begin(), endian::byteSwap<dtype>);
|
|
|
|
outArray.endianess_ = Endian::BIG;
|
|
return outArray;
|
|
}
|
|
else
|
|
{
|
|
auto outArray = Matrix(*this);
|
|
outArray.endianess_ = Endian::BIG;
|
|
return outArray;
|
|
}
|
|
}
|
|
case Endian::LITTLE:
|
|
{
|
|
if (nativeIsLittle)
|
|
{
|
|
auto outArray = Matrix(*this);
|
|
outArray.endianess_ = Endian::LITTLE;
|
|
return outArray;
|
|
}
|
|
else
|
|
{
|
|
Matrix<dtype> outArray(shape_);
|
|
|
|
std::transform(std::execution::par_unseq, cbegin(), end(), outArray.begin(), endian::byteSwap<dtype>);
|
|
|
|
outArray.endianess_ = Endian::LITTLE;
|
|
return outArray;
|
|
}
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented endian type.");
|
|
return {}; // get rid of compiler warning
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case Endian::BIG:
|
|
{
|
|
switch (endianess)
|
|
{
|
|
case Endian::NATIVE:
|
|
{
|
|
if (nativeIsLittle)
|
|
{
|
|
Matrix<dtype> outArray(shape_);
|
|
|
|
std::transform(std::execution::par_unseq, cbegin(), end(), outArray.begin(), endian::byteSwap<dtype>);
|
|
|
|
outArray.endianess_ = Endian::NATIVE;
|
|
return outArray;
|
|
}
|
|
else
|
|
{
|
|
auto outArray = Matrix(*this);
|
|
outArray.endianess_ = Endian::NATIVE;
|
|
return outArray;
|
|
}
|
|
}
|
|
case Endian::BIG:
|
|
{
|
|
return Matrix(*this);
|
|
}
|
|
case Endian::LITTLE:
|
|
{
|
|
Matrix<dtype> outArray(shape_);
|
|
|
|
std::transform(std::execution::par_unseq, cbegin(), end(), outArray.begin(), endian::byteSwap<dtype>);
|
|
|
|
outArray.endianess_ = Endian::LITTLE;
|
|
return outArray;
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented endian type.");
|
|
return {}; // get rid of compiler warning
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case Endian::LITTLE:
|
|
{
|
|
switch (endianess)
|
|
{
|
|
case Endian::NATIVE:
|
|
{
|
|
if (nativeIsLittle)
|
|
{
|
|
auto outArray = Matrix(*this);
|
|
outArray.endianess_ = Endian::NATIVE;
|
|
return outArray;
|
|
}
|
|
else
|
|
{
|
|
Matrix<dtype> outArray(shape_);
|
|
|
|
std::transform(std::execution::par_unseq, cbegin(), end(), outArray.begin(), endian::byteSwap<dtype>);
|
|
|
|
outArray.endianess_ = Endian::NATIVE;
|
|
return outArray;
|
|
}
|
|
}
|
|
case Endian::BIG:
|
|
{
|
|
Matrix<dtype> outArray(shape_);
|
|
|
|
std::transform(std::execution::par_unseq, cbegin(), end(), outArray.begin(), endian::byteSwap<dtype>);
|
|
|
|
outArray.endianess_ = Endian::BIG;
|
|
return outArray;
|
|
}
|
|
case Endian::LITTLE:
|
|
{
|
|
return Matrix(*this);
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented endian type.");
|
|
return {}; // get rid of compiler warning
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented endian type.");
|
|
return {}; // get rid of compiler warning
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns True if none elements evaluate to True or non zero
|
|
///
|
|
/// @param axis (Optional, default NONE)
|
|
/// @return Matrix
|
|
///
|
|
Matrix<bool> none(Axis axis = Axis::NONE) const
|
|
{
|
|
STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype);
|
|
|
|
const auto function = [](dtype i) -> bool
|
|
{ return i != dtype{0}; };
|
|
|
|
switch (axis)
|
|
{
|
|
case Axis::NONE:
|
|
{
|
|
Matrix<bool> returnArray = {std::none_of(std::execution::par_unseq, cbegin(), cend(), function)};
|
|
return returnArray;
|
|
}
|
|
case Axis::COLUMN:
|
|
{
|
|
Matrix<bool> returnArray(1, shape_.rows);
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
returnArray(0, row) = std::none_of(std::execution::par_unseq, cbegin(row), cend(row), function);
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
case Axis::ROW:
|
|
{
|
|
return transpose().none(Axis::COLUMN);
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented axis type.");
|
|
return {}; // get rid of compiler warning
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return the row/col indices of the array of the
|
|
/// elements that are non-zero.
|
|
///
|
|
/// @return std::pair<Matrix, Matrix> where first is the row indices and second is the
|
|
/// column indices
|
|
///
|
|
std::pair<Matrix<size_t>, Matrix<size_t>> nonzero() const;
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return the row/col indices of the array of the
|
|
/// elements that are non-zero.
|
|
///
|
|
/// @return std::pair<Matrix, Matrix> where first is the row indices and second is the
|
|
/// column indices
|
|
///
|
|
std::pair<Matrix<size_t>, Matrix<size_t>> nonempty() const;
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return the row/col indices of the array of the
|
|
/// elements that are not nan.
|
|
///
|
|
/// @return std::pair<Matrix, Matrix> where first is the row indices and second is the
|
|
/// column indices
|
|
///
|
|
std::pair<Matrix<size_t>, Matrix<size_t>> data_area() const;
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns the number of columns in the array
|
|
///
|
|
///
|
|
/// @return size_t
|
|
///
|
|
size_t columns() const noexcept
|
|
{
|
|
return shape_.cols;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns the number of rows in the array
|
|
///
|
|
///
|
|
/// @return size_t
|
|
///
|
|
size_t rows() const noexcept
|
|
{
|
|
return shape_.rows;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Fills the array with ones
|
|
///
|
|
///
|
|
Matrix<dtype> &ones() noexcept
|
|
{
|
|
STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype);
|
|
|
|
fill(dtype{1});
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns whether or not the array object owns the underlying data
|
|
///
|
|
/// @return bool
|
|
///
|
|
bool owns_internal_data() noexcept
|
|
{
|
|
return ownsPtr_;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Rearranges the elements in the array in such a way that
|
|
/// value of the element in kth position is in the position it
|
|
/// would be in a sorted array. All elements smaller than the kth
|
|
/// element are moved before this element and all equal or greater
|
|
/// are moved behind it. The ordering of the elements in the two
|
|
/// partitions is undefined.
|
|
///
|
|
/// @param kth: kth element
|
|
/// @param axis (Optional, default NONE)
|
|
/// @return None
|
|
///
|
|
Matrix<dtype> &partition(size_t kth, Axis axis = Axis::NONE)
|
|
{
|
|
STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype);
|
|
|
|
const auto comparator = [](dtype lhs, dtype rhs) noexcept -> bool
|
|
{ return lhs < rhs; };
|
|
|
|
switch (axis)
|
|
{
|
|
case Axis::NONE:
|
|
{
|
|
if (kth >= size_)
|
|
{
|
|
std::string errStr = "kth(=" + std::to_string(kth);
|
|
errStr += ") out of bounds (" + std::to_string(size_) + ")";
|
|
THROW_INVALID_ARGUMENT(errStr);
|
|
}
|
|
|
|
std::nth_element(std::execution::par_unseq, begin(), begin() + kth, end(), comparator);
|
|
break;
|
|
}
|
|
case Axis::COLUMN:
|
|
{
|
|
if (kth >= shape_.cols)
|
|
{
|
|
std::string errStr = "kth(=" + std::to_string(kth);
|
|
errStr += ") out of bounds (" + std::to_string(shape_.cols) + ")";
|
|
THROW_INVALID_ARGUMENT(errStr);
|
|
}
|
|
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
std::nth_element(std::execution::par_unseq, begin(row), begin(row) + kth, end(row), comparator);
|
|
}
|
|
break;
|
|
}
|
|
case Axis::ROW:
|
|
{
|
|
if (kth >= shape_.rows)
|
|
{
|
|
std::string errStr = "kth(=" + std::to_string(kth);
|
|
errStr += ") out of bounds (" + std::to_string(shape_.rows) + ")";
|
|
THROW_INVALID_ARGUMENT(errStr);
|
|
}
|
|
|
|
Matrix<dtype> transposedArray = transpose();
|
|
for (size_t row = 0; row < transposedArray.shape_.rows; ++row)
|
|
{
|
|
std::nth_element(std::execution::par_unseq, transposedArray.begin(row),
|
|
transposedArray.begin(row) + kth,
|
|
transposedArray.end(row),
|
|
comparator);
|
|
}
|
|
*this = transposedArray.transpose();
|
|
break;
|
|
}
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Prints the array to the console.
|
|
///
|
|
///
|
|
void print() const
|
|
{
|
|
std::cout << *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return the product of the array elements over the given axis
|
|
///
|
|
/// @param axis (Optional, default NONE)
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> prod(Axis axis = Axis::NONE) const
|
|
{
|
|
STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype);
|
|
|
|
switch (axis)
|
|
{
|
|
case Axis::NONE:
|
|
{
|
|
dtype product = std::accumulate(cbegin(), cend(), dtype{1}, std::multiplies<dtype>());
|
|
Matrix<dtype> returnArray = {product};
|
|
return returnArray;
|
|
}
|
|
case Axis::COLUMN:
|
|
{
|
|
Matrix<dtype> returnArray(1, shape_.rows);
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
returnArray(0, row) =
|
|
std::accumulate(cbegin(row), cend(row), dtype{1}, std::multiplies<dtype>());
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
case Axis::ROW:
|
|
{
|
|
return transpose().prod(Axis::COLUMN);
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented axis type.");
|
|
return {}; // get rid of compiler warning
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Peak to peak (maximum - minimum) value along a given axis.
|
|
///
|
|
/// @param axis (Optional, default NONE)
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> ptp(Axis axis = Axis::NONE) const
|
|
{
|
|
STATIC_ASSERT_ARITHMETIC_OR_COMPLEX(dtype);
|
|
|
|
const auto comparator = [](dtype lhs, dtype rhs) noexcept -> bool
|
|
{ return lhs < rhs; };
|
|
|
|
switch (axis)
|
|
{
|
|
case Axis::NONE:
|
|
{
|
|
const auto result = std::max_element(std::execution::par_unseq, cbegin(), cend(), comparator);
|
|
Matrix<dtype> returnArray = {*result.second - *result.first};
|
|
return returnArray;
|
|
}
|
|
case Axis::COLUMN:
|
|
{
|
|
Matrix<dtype> returnArray(1, shape_.rows);
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
const auto result = std::max_element(std::execution::par_unseq, cbegin(row), cend(row), comparator);
|
|
returnArray(0, row) = *result.second - *result.first;
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
case Axis::ROW:
|
|
{
|
|
return transpose().ptp(Axis::COLUMN);
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented axis type.");
|
|
return {}; // get rid of compiler warning
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// set the flat index element to the value
|
|
///
|
|
/// @param pos
|
|
/// @param value
|
|
///
|
|
Matrix<dtype> &put(size_t pos, value_type value)
|
|
{
|
|
at(pos) = value;
|
|
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// set the 2D row/col index element to the value
|
|
///
|
|
/// @param row
|
|
/// @param column
|
|
/// @param value
|
|
///
|
|
Matrix<dtype> &put(size_t row, size_t column, value_type value)
|
|
{
|
|
at(row, column) = value;
|
|
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// set the flat index elements to the values
|
|
///
|
|
/// @param pos
|
|
/// @param values
|
|
///
|
|
template <class ct>
|
|
Matrix<dtype> &putMany(size_t pos, const ct &list)
|
|
{
|
|
if ((pos + list.size()) > size_)
|
|
{
|
|
THROW_INVALID_ARGUMENT("pos out of data range");
|
|
}
|
|
|
|
const auto ptr = begin() + pos;
|
|
std::copy(std::execution::par_unseq, list.begin(), list.end(), ptr);
|
|
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// set the 2D row/col index element to the value
|
|
///
|
|
/// @param row
|
|
/// @param column
|
|
/// @param values
|
|
///
|
|
template <class ct>
|
|
Matrix<dtype> &putMany(size_t row, size_t column, const ct &list)
|
|
{
|
|
auto pos = row * shape_.cols + column;
|
|
return putMany(pos, list);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Set a.flat[n] = values for all n in indices.
|
|
///
|
|
/// @param indices
|
|
/// @param value
|
|
///
|
|
Matrix<dtype> &put(const Matrix<size_t> &indices, value_type value)
|
|
{
|
|
if (indices.size() > 0)
|
|
{
|
|
for (auto index : indices)
|
|
{
|
|
put(index, value);
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Set a.flat[n] = values[n] for all n in indices.
|
|
///
|
|
/// @param indices
|
|
/// @param values
|
|
///
|
|
Matrix<dtype> &put(const Matrix<size_t> &indices, const Matrix<dtype> &values)
|
|
{
|
|
if (indices.size() != values.size())
|
|
{
|
|
THROW_INVALID_ARGUMENT("Input indices do not match values dimensions.");
|
|
}
|
|
|
|
size_t counter = 0;
|
|
if (indices.size() > 0)
|
|
{
|
|
for (auto index : indices)
|
|
{
|
|
put(index, values[counter++]);
|
|
}
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Set the slice indices to the input value.
|
|
///
|
|
/// @param slice
|
|
/// @param value
|
|
///
|
|
Matrix<dtype> &put(const Slice &slice, value_type value)
|
|
{
|
|
Slice sliceCopy(slice);
|
|
sliceCopy.make_positive_and_validate(size_);
|
|
|
|
for (size_t i = sliceCopy.start; i < sliceCopy.stop; i += sliceCopy.step)
|
|
{
|
|
put(i, value);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Set the slice indices to the input values.
|
|
///
|
|
/// @param slice
|
|
/// @param values
|
|
///
|
|
Matrix<dtype> &put(const Slice &slice, const Matrix<dtype> &values)
|
|
{
|
|
Slice sliceCopy(slice);
|
|
sliceCopy.make_positive_and_validate(size_);
|
|
|
|
std::vector<size_t> indices;
|
|
for (size_t i = sliceCopy.start; i < sliceCopy.stop; i += sliceCopy.step)
|
|
{
|
|
indices.push_back(i);
|
|
}
|
|
|
|
return put(Matrix<size_t>(indices), values);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Set the slice indices to the input value.
|
|
///
|
|
/// @param rowSlice
|
|
/// @param columnSlice
|
|
/// @param value
|
|
///
|
|
Matrix<dtype> &put(const Slice &rowSlice, const Slice &columnSlice, value_type value)
|
|
{
|
|
Slice rowSliceCopy(rowSlice);
|
|
Slice columnSliceCopy(columnSlice);
|
|
|
|
rowSliceCopy.make_positive_and_validate(shape_.rows);
|
|
columnSliceCopy.make_positive_and_validate(shape_.cols);
|
|
|
|
std::vector<size_t> indices;
|
|
for (size_t row = rowSliceCopy.start; row < rowSliceCopy.stop; row += rowSliceCopy.step)
|
|
{
|
|
for (size_t col = columnSliceCopy.start; col < columnSliceCopy.stop; col += columnSliceCopy.step)
|
|
{
|
|
put(row, col, value);
|
|
}
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Set the slice indices to the input value.
|
|
///
|
|
/// @param rowSlice
|
|
/// @param columnPos
|
|
/// @param value
|
|
///
|
|
Matrix<dtype> &put(const Slice &rowSlice, size_t columnPos, value_type value)
|
|
{
|
|
Slice rowSliceCopy(rowSlice);
|
|
rowSliceCopy.make_positive_and_validate(shape_.rows);
|
|
|
|
std::vector<size_t> indices;
|
|
for (size_t row = rowSliceCopy.start; row < rowSliceCopy.stop; row += rowSliceCopy.step)
|
|
{
|
|
put(row, columnPos, value);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Set the slice indices to the input value.
|
|
///
|
|
/// @param rowPos
|
|
/// @param columnSlice
|
|
/// @param value
|
|
///
|
|
Matrix<dtype> &put(size_t rowPos, const Slice &columnSlice, value_type value)
|
|
{
|
|
Slice columnSliceCopy(columnSlice);
|
|
columnSliceCopy.make_positive_and_validate(shape_.cols);
|
|
|
|
std::vector<size_t> indices;
|
|
for (size_t col = columnSliceCopy.start; col < columnSliceCopy.stop; col += columnSliceCopy.step)
|
|
{
|
|
put(rowPos, col, value);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Set the slice indices to the input values.
|
|
///
|
|
/// @param rowSlice
|
|
/// @param columnSlice
|
|
/// @param values
|
|
///
|
|
Matrix<dtype> &put(const Slice &rowSlice, const Slice &columnSlice, const Matrix<dtype> &values)
|
|
{
|
|
Slice rowSliceCopy(rowSlice);
|
|
Slice columnSliceCopy(columnSlice);
|
|
|
|
rowSliceCopy.make_positive_and_validate(shape_.rows);
|
|
columnSliceCopy.make_positive_and_validate(shape_.cols);
|
|
|
|
std::vector<size_t> indices;
|
|
for (size_t row = rowSliceCopy.start; row < rowSliceCopy.stop; row += rowSliceCopy.step)
|
|
{
|
|
for (size_t col = columnSliceCopy.start; col < columnSliceCopy.stop; col += columnSliceCopy.step)
|
|
{
|
|
const size_t index = row * shape_.cols + col;
|
|
indices.push_back(index);
|
|
}
|
|
}
|
|
|
|
return put(Matrix<size_t>(indices), values);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Set the slice indices to the input values.
|
|
///
|
|
/// @param rowSlice
|
|
/// @param columnPos
|
|
/// @param values
|
|
///
|
|
Matrix<dtype> &put(const Slice &rowSlice, size_t columnPos, const Matrix<dtype> &values)
|
|
{
|
|
Slice rowSliceCopy(rowSlice);
|
|
rowSliceCopy.make_positive_and_validate(shape_.rows);
|
|
|
|
std::vector<size_t> indices;
|
|
for (size_t row = rowSliceCopy.start; row < rowSliceCopy.stop; row += rowSliceCopy.step)
|
|
{
|
|
const size_t index = row * shape_.cols + columnPos;
|
|
indices.push_back(index);
|
|
}
|
|
|
|
return put(Matrix<size_t>(indices), values);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Set the slice indices to the input values.
|
|
///
|
|
/// @param rowPos
|
|
/// @param columnSlice
|
|
/// @param values
|
|
///
|
|
Matrix<dtype> &put(size_t rowPos, const Slice &columnSlice, const Matrix<dtype> &values)
|
|
{
|
|
Slice columnSliceCopy(columnSlice);
|
|
columnSliceCopy.make_positive_and_validate(shape_.cols);
|
|
|
|
std::vector<size_t> indices;
|
|
for (size_t col = columnSliceCopy.start; col < columnSliceCopy.stop; col += columnSliceCopy.step)
|
|
{
|
|
const size_t index = rowPos * shape_.cols + col;
|
|
indices.push_back(index);
|
|
}
|
|
|
|
return put(Matrix<size_t>(indices), values);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Set the mask indices to the input value.
|
|
///
|
|
/// @param mask
|
|
/// @param value
|
|
///
|
|
Matrix<dtype> &put_mask(const Matrix<bool> &mask, value_type value)
|
|
{
|
|
if (mask.shape() != shape_)
|
|
{
|
|
THROW_INVALID_ARGUMENT("input mask must be the same shape as the array it is masking.");
|
|
}
|
|
|
|
return put(mask.flatnonzero(), value);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Set the mask indices to the input values.
|
|
///
|
|
/// @param mask
|
|
/// @param values
|
|
///
|
|
Matrix<dtype> &put_mask(const Matrix<bool> &mask, const Matrix<dtype> &values)
|
|
{
|
|
if (mask.shape() != shape_)
|
|
{
|
|
THROW_INVALID_ARGUMENT("input mask must be the same shape as the array it is masking.");
|
|
}
|
|
|
|
return put(mask.flatnonzero(), values);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Flattens the array but does not make a copy.
|
|
///
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> &ravel() noexcept
|
|
{
|
|
reshape(size_);
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Repeat elements of an array.
|
|
///
|
|
/// @param rows
|
|
/// @param columns
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> repeat(size_t rows, size_t columns) const
|
|
{
|
|
Matrix<dtype> returnArray(shape_.rows * rows, shape_.cols * columns);
|
|
|
|
for (size_t row = 0; row < rows; ++row)
|
|
{
|
|
for (size_t col = 0; col < columns; ++col)
|
|
{
|
|
std::vector<size_t> indices(shape_.size());
|
|
|
|
const size_t rowStart = row * shape_.rows;
|
|
const size_t colStart = col * shape_.cols;
|
|
|
|
const size_t rowEnd = (row + 1) * shape_.rows;
|
|
const size_t colEnd = (col + 1) * shape_.cols;
|
|
|
|
size_t counter = 0;
|
|
for (size_t rowIdx = rowStart; rowIdx < rowEnd; ++rowIdx)
|
|
{
|
|
for (size_t colIdx = colStart; colIdx < colEnd; ++colIdx)
|
|
{
|
|
indices[counter++] = rowIdx * returnArray.shape_.cols + colIdx;
|
|
}
|
|
}
|
|
|
|
returnArray.put(Matrix<size_t>(indices), *this);
|
|
}
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Repeat elements of an array.
|
|
///
|
|
/// @param repeatShape
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> repeat(const Shape &repeatShape) const
|
|
{
|
|
return repeat(repeatShape.rows, repeatShape.cols);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Replaces a value of the array with another value
|
|
///
|
|
/// @param oldValue: the value to replace
|
|
/// @param newValue: the value to replace with
|
|
///
|
|
inline void replace(value_type oldValue, value_type newValue)
|
|
{
|
|
if (std::isnan(oldValue))
|
|
std::replace_if(std::execution::par_unseq, begin(), end(), [](double v) { return std::isnan(v); }, newValue);
|
|
else
|
|
std::replace(std::execution::par_unseq, begin(), end(), oldValue, newValue);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// The new shape should be compatible with the original shape. If an single integer,
|
|
/// then the result will be a 1-D array of that length. One shape dimension
|
|
/// can be -1. In this case, the value is inferred from the length of the
|
|
/// array and remaining dimensions.
|
|
///
|
|
/// @param size
|
|
///
|
|
Matrix<dtype> &reshape(size_t size)
|
|
{
|
|
if (size != size_)
|
|
{
|
|
std::string errStr = "Cannot reshape array of size " + std::to_string(size_) + " into shape ";
|
|
errStr += "[" + std::to_string(1) + ", " + std::to_string(size) + "]";
|
|
THROW_RUNTIME_ERROR(errStr);
|
|
}
|
|
|
|
shape_.rows = 1;
|
|
shape_.cols = size;
|
|
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// The new shape should be compatible with the original shape. If an single integer,
|
|
/// then the result will be a 1-D array of that length. One shape dimension
|
|
/// can be -1. In this case, the value is inferred from the length of the
|
|
/// array and remaining dimensions.
|
|
///
|
|
/// @param rows
|
|
/// @param columns
|
|
///
|
|
Matrix<dtype> &reshape(int64_t rows, int64_t columns)
|
|
{
|
|
if (rows < 0)
|
|
{
|
|
if (size_ % columns == 0)
|
|
{
|
|
return reshape(size_ / columns, columns);
|
|
}
|
|
|
|
std::string errStr = "Cannot reshape array of size " + std::to_string(size_) + " into a shape ";
|
|
errStr += "with " + std::to_string(columns) + " columns";
|
|
THROW_INVALID_ARGUMENT(errStr);
|
|
}
|
|
|
|
if (columns < 0)
|
|
{
|
|
if (size_ % rows == 0)
|
|
{
|
|
return reshape(rows, size_ / rows);
|
|
}
|
|
|
|
std::string errStr = "Cannot reshape array of size " + std::to_string(size_) + " into a shape ";
|
|
errStr += "with " + std::to_string(rows) + " rows";
|
|
THROW_INVALID_ARGUMENT(errStr);
|
|
}
|
|
|
|
if ((rows * columns) != size_)
|
|
{
|
|
std::string errStr = "Cannot reshape array of size " + std::to_string(size_) + " into shape ";
|
|
errStr += "[" + std::to_string(rows) + ", " + std::to_string(columns) + "]";
|
|
THROW_INVALID_ARGUMENT(errStr);
|
|
}
|
|
|
|
shape_.rows = (rows);
|
|
shape_.cols = (columns);
|
|
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// The new shape should be compatible with the original shape. If an single integer,
|
|
/// then the result will be a 1-D array of that length. One shape dimension
|
|
/// can be -1. In this case, the value is inferred from the length of the
|
|
/// array and remaining dimensions.
|
|
///
|
|
/// @param shape
|
|
///
|
|
Matrix<dtype> &reshape(const Shape &shape)
|
|
{
|
|
return reshape(shape.rows, shape.cols);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Change shape and size of array in-place. All previous
|
|
/// data of the array is lost.
|
|
///
|
|
/// @param rows
|
|
/// @param columns
|
|
///
|
|
Matrix<dtype> &resize_fast(size_t rows, size_t columns)
|
|
{
|
|
new_matrix(Shape(rows, columns));
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Change shape and size of array in-place. All previous
|
|
/// data of the array is lost.
|
|
///
|
|
/// @param shape
|
|
///
|
|
Matrix<dtype> &resize_fast(const Shape &shape)
|
|
{
|
|
return resize_fast(shape.rows, shape.cols);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return a new array with the specified shape. If new shape
|
|
/// is larger than old shape then array will be padded with zeros.
|
|
/// If new shape is smaller than the old shape then the data will
|
|
/// be discarded.
|
|
///
|
|
/// @param rows
|
|
/// @param columns
|
|
///
|
|
Matrix<dtype> &resize_slow(size_t rows, size_t columns)
|
|
{
|
|
std::vector<dtype> oldData(size_);
|
|
std::copy(std::execution::par_unseq, begin(), end(), oldData.begin());
|
|
|
|
const Shape shape(rows, columns);
|
|
const Shape oldShape = shape_;
|
|
|
|
new_matrix(shape);
|
|
|
|
for (size_t row = 0; row < shape.rows; ++row)
|
|
{
|
|
for (size_t col = 0; col < shape.cols; ++col)
|
|
{
|
|
if (row >= oldShape.rows || col >= oldShape.cols)
|
|
{
|
|
operator()(row, col) = dtype{0}; // zero fill
|
|
}
|
|
else
|
|
{
|
|
operator()(row, col) = oldData[row * oldShape.cols + col];
|
|
}
|
|
}
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return a new array with the specified shape. If new shape
|
|
/// is larger than old shape then array will be padded with zeros.
|
|
/// If new shape is smaller than the old shape then the data will
|
|
/// be discarded.
|
|
///
|
|
/// @param shape
|
|
///
|
|
Matrix<dtype> &resize_slow(const Shape &shape)
|
|
{
|
|
return resize_slow(shape.rows, shape.cols);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return a with each element rounded to the given number
|
|
/// of decimals.
|
|
///
|
|
/// @param number_decimals (default 0)
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> round(uint8_t number_decimals = 0) const
|
|
{
|
|
STATIC_ASSERT_FLOAT(dtype);
|
|
|
|
Matrix<dtype> returnArray(shape_);
|
|
const double multFactor = std::pow(10., number_decimals);
|
|
const auto function = [multFactor](dtype value) noexcept -> dtype
|
|
{ return static_cast<dtype>(std::nearbyint(static_cast<double>(value) * multFactor) / multFactor); };
|
|
|
|
std::transform(std::execution::par_unseq, cbegin(), cend(), returnArray.begin(), function);
|
|
|
|
return returnArray;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Returns the full row of the array
|
|
///
|
|
///
|
|
/// @return Shape
|
|
///
|
|
Matrix<dtype> row(size_t row)
|
|
{
|
|
return Matrix<dtype>(cbegin(row), cend(row));
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return the shape of the array
|
|
///
|
|
/// @return Shape
|
|
///
|
|
Shape shape() const noexcept
|
|
{
|
|
return shape_;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return the size of the array
|
|
///
|
|
/// @return size
|
|
///
|
|
size_t size() const noexcept
|
|
{
|
|
return size_;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Sort an array, in-place.
|
|
///
|
|
/// @param axis (Optional, default NONE)
|
|
/// @return size
|
|
///
|
|
Matrix<dtype> &sort(Axis axis = Axis::NONE)
|
|
{
|
|
|
|
const auto comparator = [](dtype lhs, dtype rhs) noexcept -> bool
|
|
{ return lhs < rhs; };
|
|
|
|
switch (axis)
|
|
{
|
|
case Axis::NONE:
|
|
{
|
|
std::sort(std::execution::par_unseq, begin(), end(), comparator);
|
|
break;
|
|
}
|
|
case Axis::COLUMN:
|
|
{
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
std::sort(std::execution::par_unseq, begin(row), end(row), comparator);
|
|
}
|
|
break;
|
|
}
|
|
case Axis::ROW:
|
|
{
|
|
Matrix<dtype> transposedArray = transpose();
|
|
for (size_t row = 0; row < transposedArray.shape_.rows; ++row)
|
|
{
|
|
std::sort(std::execution::par_unseq, transposedArray.begin(row), transposedArray.end(row), comparator);
|
|
}
|
|
|
|
*this = transposedArray.transpose();
|
|
break;
|
|
}
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// returns the Matrix as a string representation
|
|
///
|
|
/// @return string
|
|
///
|
|
std::string str() const
|
|
{
|
|
|
|
std::string out;
|
|
out += "[\n";
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
out += "[";
|
|
// for (size_t col = 0; col < shape_.cols; ++col)
|
|
// {
|
|
// out += utils::value2str(operator()(row, col)) + ", ";
|
|
// }
|
|
|
|
auto ib = begin() + row * shape_.cols;
|
|
auto ie = ib + shape_.cols;
|
|
out += str_join(ib, ie, ", ");
|
|
|
|
if (row == shape_.rows - 1)
|
|
{
|
|
out += "]\n";
|
|
}
|
|
else
|
|
{
|
|
out += "],\n";
|
|
}
|
|
}
|
|
out += "]\n";
|
|
return out;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return the sum of the array elements over the given axis.
|
|
///
|
|
/// @param axis (Optional, default NONE)
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> sum(Axis axis = Axis::NONE) const
|
|
{
|
|
|
|
switch (axis)
|
|
{
|
|
case Axis::NONE:
|
|
{
|
|
Matrix<dtype> returnArray = {std::accumulate(cbegin(), cend(), dtype{0})};
|
|
return returnArray;
|
|
}
|
|
case Axis::COLUMN:
|
|
{
|
|
Matrix<dtype> returnArray(1, shape_.rows);
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
returnArray(0, row) = std::accumulate(cbegin(row), cend(row), dtype{0});
|
|
}
|
|
|
|
return returnArray;
|
|
}
|
|
case Axis::ROW:
|
|
{
|
|
return transpose().sum(Axis::COLUMN);
|
|
}
|
|
default:
|
|
{
|
|
THROW_INVALID_ARGUMENT("Unimplemented axis type.");
|
|
return {}; // get rid of compiler warning
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Interchange two axes of an array. Equivalent to transpose...
|
|
///
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> swap_axes() const
|
|
{
|
|
return transpose();
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Swaps rows of the array
|
|
///
|
|
/// @param colIdx1
|
|
/// @param colIdx2
|
|
///
|
|
void swap_columns(size_t colIdx1, size_t colIdx2)
|
|
{
|
|
for (size_t row = 0; row < static_cast<size_t>(shape_.rows); ++row)
|
|
{
|
|
std::swap(operator()(row, colIdx1), operator()(row, colIdx2));
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Swaps rows of the array
|
|
///
|
|
/// @param rowIdx1
|
|
/// @param rowIdx2
|
|
///
|
|
void swap_rows(size_t rowIdx1, size_t rowIdx2)
|
|
{
|
|
for (size_t col = 0; col < static_cast<size_t>(shape_.cols); ++col)
|
|
{
|
|
std::swap(operator()(rowIdx1, col), operator()(rowIdx2, col));
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Write array to a file as binary.
|
|
/// The data produced by this method can be recovered
|
|
/// using the function fromfile().
|
|
///
|
|
/// @param filename
|
|
/// @return None
|
|
///
|
|
void tofile(const std::string &filename) const
|
|
{
|
|
dump(filename);
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Write array to a file as text.
|
|
/// The data produced by this method can be recovered
|
|
/// using the function fromfile().
|
|
///
|
|
/// @param filename
|
|
/// @param separator: Separator between array items for text output.
|
|
/// @return None
|
|
///
|
|
void tofile(const std::string &filename, const char separator) const
|
|
{
|
|
|
|
filesystem::File f(filename);
|
|
if (!f.hasExt())
|
|
{
|
|
f.withExt("txt");
|
|
}
|
|
|
|
std::ofstream ofile(f.fullname().c_str());
|
|
if (!ofile.good())
|
|
{
|
|
THROW_RUNTIME_ERROR("Input file could not be opened:\n\t" + filename);
|
|
}
|
|
|
|
size_t counter = 0;
|
|
for (auto value : *this)
|
|
{
|
|
ofile << value;
|
|
if (counter++ != size_ - 1)
|
|
{
|
|
ofile << separator;
|
|
}
|
|
}
|
|
ofile.close();
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Converts the slice object to an Matrix of indices for this array
|
|
///
|
|
/// @param slice: the slice object
|
|
/// @param axis: the array axis
|
|
///
|
|
/// @return Matrix<size_t>
|
|
///
|
|
Matrix<size_t> to_indices(Slice slice, Axis axis = Axis::ROW) const
|
|
{
|
|
size_t element_number = 0;
|
|
switch (axis)
|
|
{
|
|
case Axis::NONE:
|
|
{
|
|
element_number = slice.element_number(size_);
|
|
break;
|
|
}
|
|
case Axis::ROW:
|
|
{
|
|
element_number = slice.element_number(shape_.rows);
|
|
break;
|
|
}
|
|
case Axis::COLUMN:
|
|
{
|
|
element_number = slice.element_number(shape_.cols);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// not actually possible, getting rid of compiler warning
|
|
THROW_INVALID_ARGUMENT("Invalid 'axis' option");
|
|
}
|
|
}
|
|
|
|
if (element_number == 0)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
Matrix<size_t> indices(1, element_number);
|
|
indices[0] = slice.start;
|
|
for (size_t i = 1; i < indices.size(); ++i)
|
|
{
|
|
indices[i] = (indices[i - 1] + slice.step);
|
|
}
|
|
|
|
return indices;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Write flattened array to an STL vector
|
|
///
|
|
/// @return std::vector
|
|
///
|
|
std::vector<dtype> to_vector() const
|
|
{
|
|
return std::vector<dtype>(cbegin(), cend());
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return the sum along diagonals of the array.
|
|
///
|
|
/// @param offset: Offset of the diagonal from the main diagonal. Can be both positive and negative. Defaults
|
|
/// to 0.
|
|
/// @param axis: (Optional, default ROW) Axis to offset from
|
|
///
|
|
/// @return value
|
|
///
|
|
value_type trace(size_t offset = 0, Axis axis = Axis::ROW) const
|
|
{
|
|
|
|
size_t rowStart = 0;
|
|
size_t colStart = 0;
|
|
switch (axis)
|
|
{
|
|
case Axis::ROW:
|
|
{
|
|
rowStart += offset;
|
|
break;
|
|
}
|
|
case Axis::COLUMN:
|
|
{
|
|
colStart += offset;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// if the user input NONE, override back to ROW
|
|
axis = Axis::ROW;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rowStart >= shape_.rows || colStart >= shape_.cols)
|
|
{
|
|
return dtype{0};
|
|
}
|
|
|
|
size_t col = colStart;
|
|
dtype sum = 0;
|
|
for (size_t row = rowStart; row < shape_.rows; ++row)
|
|
{
|
|
if (col >= shape_.cols)
|
|
{
|
|
break;
|
|
}
|
|
sum += operator()(row, col++);
|
|
}
|
|
|
|
return sum;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Transpose the rows and columns of an array
|
|
///
|
|
/// @return Matrix
|
|
///
|
|
Matrix<dtype> transpose() const
|
|
{
|
|
Matrix<dtype> transArray(shape_.cols, shape_.rows);
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
for (size_t col = 0; col < shape_.cols; ++col)
|
|
{
|
|
transArray(col, row) = operator()(row, col);
|
|
}
|
|
}
|
|
return transArray;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Fills the array with zeros
|
|
///
|
|
///
|
|
Matrix<dtype> &zeros() noexcept
|
|
{
|
|
|
|
fill(dtype{0});
|
|
return *this;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return random integer from low (inclusive) to high (exclusive),
|
|
/// with the given shape. If no high value is input then the range will
|
|
/// go from [0, 100).
|
|
///
|
|
/// @param begin position for filling area, default 0
|
|
/// @param end position for filling area, default size
|
|
/// @param low default 0
|
|
/// @param high default 100.
|
|
/// @return NdArray
|
|
///
|
|
Matrix<dtype> &fill_random(size_t begin, size_t end, dtype low = 0, dtype high = 100)
|
|
{
|
|
STATIC_ASSERT_INTEGER(dtype);
|
|
|
|
if (low == high)
|
|
{
|
|
THROW_INVALID_ARGUMENT("input low value must be less than the input high value.");
|
|
}
|
|
else if (low > high)
|
|
{
|
|
std::swap(low, high);
|
|
}
|
|
|
|
std::uniform_int_distribution<dtype> dist(low, high - 1);
|
|
std::mt19937_64 generator{};
|
|
auto b = this->begin() + begin;
|
|
auto e = this->begin() + end;
|
|
auto f = [&dist, &generator](dtype &value) noexcept -> void
|
|
{ value = int(dist(generator)); };
|
|
std::for_each(std::execution::par_unseq, b, e, f);
|
|
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
//====================================Attributes==============================
|
|
allocator_type allocator_{};
|
|
Shape shape_{0, 0};
|
|
size_t size_{0};
|
|
Endian endianess_{Endian::NATIVE};
|
|
pointer array_{nullptr};
|
|
bool ownsPtr_{false};
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Return iterator of specials index
|
|
///
|
|
/// @param rowPos
|
|
/// @param columnPos
|
|
/// @return iterator
|
|
///
|
|
iterator pos_iterator(int64_t rowPos, int64_t columnPos = 0)
|
|
{
|
|
// this doesn't allow for calling the first element as -size_...
|
|
// but why would you really want to do that anyway?
|
|
if (std::abs(rowPos) >= shape_.rows)
|
|
{
|
|
std::string errStr = "Row index " + std::to_string(rowPos);
|
|
errStr += " is out of bounds for array of size " + std::to_string(shape_.rows) + ".";
|
|
THROW_INVALID_ARGUMENT(errStr);
|
|
}
|
|
|
|
// this doesn't allow for calling the first element as -size_...
|
|
// but why would you really want to that anyway?
|
|
if (std::abs(columnPos) >= shape_.cols)
|
|
{
|
|
std::string errStr = "Column index " + std::to_string(columnPos);
|
|
errStr += " is out of bounds for array of size " + std::to_string(shape_.cols) + ".";
|
|
THROW_INVALID_ARGUMENT(errStr);
|
|
}
|
|
|
|
rowPos = rowPos >= 0 ? rowPos : rowPos + shape_.rows;
|
|
columnPos = columnPos >= 0 ? columnPos : shape_.cols + columns();
|
|
|
|
iterator out_iter = begin() + rowPos * columns() + columnPos;
|
|
|
|
return out_iter;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Deletes the internal array
|
|
///
|
|
void clear() noexcept
|
|
{
|
|
if (ownsPtr_ && array_ != nullptr)
|
|
{
|
|
allocator_.deallocate(array_, size_);
|
|
}
|
|
|
|
array_ = nullptr;
|
|
shape_.rows = shape_.cols = 0;
|
|
size_ = 0;
|
|
ownsPtr_ = false;
|
|
endianess_ = Endian::NATIVE;
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Creates a new internal array
|
|
///
|
|
void new_matrix()
|
|
{
|
|
if (size_ > 0)
|
|
{
|
|
array_ = allocator_.allocate(size_);
|
|
ownsPtr_ = true;
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
// Method Description:
|
|
/// Creates a new internal array
|
|
///
|
|
/// @param shape
|
|
///
|
|
void new_matrix(const Shape &shape)
|
|
{
|
|
clear();
|
|
|
|
shape_ = shape;
|
|
size_ = shape.size();
|
|
new_matrix();
|
|
}
|
|
|
|
friend std::ostream &operator<<(std::ostream &stream, const self_type &matrix)
|
|
{
|
|
stream << matrix.str();
|
|
return stream;
|
|
}
|
|
};
|
|
|
|
// NOTE: this needs to be defined outside of the class to get rid of a compiler
|
|
// error in Visual Studio
|
|
template <typename dtype, class _Alloc>
|
|
AIS_EXPORT std::pair<Matrix<size_t>, Matrix<size_t>> Matrix<dtype, _Alloc>::nonzero() const
|
|
{
|
|
|
|
std::vector<size_t> rowIndices;
|
|
std::vector<size_t> columnIndices;
|
|
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
for (size_t col = 0; col < shape_.cols; ++col)
|
|
{
|
|
auto d = operator()(row, col);
|
|
if (d != dtype{0})
|
|
{
|
|
rowIndices.push_back(row);
|
|
columnIndices.push_back(col);
|
|
}
|
|
}
|
|
}
|
|
|
|
return std::make_pair(Matrix<size_t>(rowIndices), Matrix<size_t>(columnIndices));
|
|
};
|
|
|
|
template <typename dtype, class _Alloc>
|
|
AIS_EXPORT std::pair<Matrix<size_t>, Matrix<size_t>> Matrix<dtype, _Alloc>::nonempty() const
|
|
{
|
|
|
|
std::vector<size_t> rowIndices;
|
|
std::vector<size_t> columnIndices;
|
|
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
for (size_t col = 0; col < shape_.cols; ++col)
|
|
{
|
|
auto d = operator()(row, col);
|
|
if (!ais::isnan(d))
|
|
{
|
|
rowIndices.push_back(row);
|
|
columnIndices.push_back(col);
|
|
}
|
|
}
|
|
}
|
|
|
|
return std::make_pair(Matrix<size_t>(rowIndices), Matrix<size_t>(columnIndices));
|
|
};
|
|
|
|
template <typename dtype, class _Alloc>
|
|
AIS_EXPORT std::pair<Matrix<size_t>, Matrix<size_t>> Matrix<dtype, _Alloc>::data_area() const
|
|
{
|
|
|
|
std::vector<size_t> rowIndices;
|
|
std::vector<size_t> columnIndices;
|
|
|
|
for (size_t row = 0; row < shape_.rows; ++row)
|
|
{
|
|
for (size_t col = 0; col < shape_.cols; ++col)
|
|
{
|
|
auto d = operator()(row, col);
|
|
if (!ais::isnan(d))
|
|
{
|
|
rowIndices.push_back(row);
|
|
columnIndices.push_back(col);
|
|
}
|
|
}
|
|
}
|
|
|
|
return std::make_pair(Matrix<size_t>(rowIndices), Matrix<size_t>(columnIndices));
|
|
}
|
|
|
|
using FMatrix = AIS_EXPORT Matrix<double>;
|
|
} // namespace ais
|