1#ifndef PROG3_NN_FINAL_PROJECT_V2025_01_TENSOR_H
2#define PROG3_NN_FINAL_PROJECT_V2025_01_TENSOR_H
23 template <std::
size_t Size>
24 constexpr void apply_with_counter(
const auto fn,
const std::array<std::size_t, Size>& size) {
25 std::array<std::size_t, Size> index{};
27 const std::size_t total_size = std::ranges::fold_left(size, 1, std::multiplies());
29 for (std::size_t i = 0; i < total_size; ++i) {
34 for (std::size_t j = 0; j < Size; ++j) {
35 if (index[j] < size[j]) {
62 template <
typename T,
size_t Rank>
64 std::array<size_t, Rank> m_shape;
65 std::array<size_t, Rank> m_steps;
66 std::vector<T> m_data;
69 std::size_t current_step = 1;
71 for (std::size_t i = Rank - 1; i !=
static_cast<std::size_t
>(-1); --i) {
72 m_steps[i] = current_step;
73 current_step *= m_shape[i];
77 template <
typename... Idxs>
78 requires(
sizeof...(Idxs) == Rank)
79 constexpr auto physical_index(
const Idxs... idxs)
const -> std::size_t {
80 const std::array<std::size_t, Rank> idxs_arr{
static_cast<std::size_t
>(idxs)...};
82 std::size_t physical_index = 0;
84 for (std::size_t i = 0; i < Rank; ++i) {
85 if (idxs_arr[i] >= m_shape[i]) {
86 throw std::out_of_range(
"Tensor index out of bounds");
88 physical_index += m_steps[i] * idxs_arr[i];
91 return physical_index;
104 static_cast<size_t>(1),
105 std::multiplies())) {
114 template <
typename... Dims>
115 requires(
sizeof...(Dims) == Rank)
117 : m_shape{
static_cast<size_t>(dims)...},
118 m_data(std::ranges::fold_left(m_shape, 1, std::multiplies())) {
123 return m_shape == other.m_shape && m_data == other.m_data;
127 if (list.size() != m_data.size()) {
128 throw std::invalid_argument(
"Data size does not match tensor size");
131 std::copy(list.begin(), list.end(), m_data.begin());
142 return m_data[physical_index(idxs...)];
145 [[nodiscard]]
constexpr auto operator()(
const auto... idxs)
const ->
const T& {
146 return m_data[physical_index(idxs...)];
149 auto operator()(
const std::array<size_t, Rank>& idxs) -> T& {
151 for (
size_t dim = 0; dim < Rank; dim++) {
152 idxs[dim] < m_shape[dim] ? idx += m_steps[dim] * idxs[dim]
153 :
throw std::out_of_range(
"Index out of bounds");
158 auto operator()(
const std::array<size_t, Rank>& idxs)
const ->
const T& {
160 for (
size_t dim = 0; dim < Rank; dim++) {
161 idxs[dim] < m_shape[dim] ? idx += m_steps[dim] * idxs[dim]
162 :
throw std::out_of_range(
"Index out of bounds");
168 return m_data.at(idx);
172 return m_data.at(idx);
175 [[nodiscard]]
auto size() const ->
size_t {
176 return m_data.size();
179 auto shape() const noexcept -> const std::array<
size_t, Rank>& {
188 void reshape(
const std::array<size_t, Rank>& new_shape) {
189 m_data.resize(std::ranges::fold_left(new_shape, 1, std::multiplies()));
199 template <
typename... Dims>
200 requires(
sizeof...(Dims) == Rank)
202 std::array<size_t, Rank> new_shape{
static_cast<size_t>(dims)...};
203 m_data.resize(std::ranges::fold_left(new_shape, 1, std::multiplies()));
213 void fill(
const T& value)
noexcept {
214 std::ranges::fill(m_data, value);
226 if (index >= m_shape[0]) {
227 throw std::out_of_range(
"Row index out of bounds");
231 for (
size_t j = 0; j < m_shape[1]; ++j) {
232 result(0, j) = (*this)(index, j);
246 if (row_tensor.shape()[0] != 1 || row_tensor.shape()[1] != m_shape[1]) {
247 throw std::invalid_argument(
"Row shape does not match");
250 for (
size_t j = 0; j < m_shape[1]; ++j) {
251 (*this)(index, j) = row_tensor(0, j);
264 if (index >= m_shape[0]) {
265 throw std::out_of_range(
"Index out of bounds");
269 for (
size_t j = 0; j < m_shape[1]; ++j) {
270 for (
size_t k = 0; k < m_shape[2]; ++k) {
271 result(j, k) = (*this)(index, j, k);
286 if (index >= m_shape[0]) {
287 throw std::out_of_range(
"Index out of bounds");
290 if (
slice.shape()[0] != m_shape[1] ||
slice.shape()[1] != m_shape[2]) {
291 throw std::invalid_argument(
"Slice shape does not match");
294 for (
size_t j = 0; j < m_shape[1]; ++j) {
295 for (
size_t k = 0; k < m_shape[2]; ++k) {
296 (*this)(index, j, k) =
slice(j, k);
309 if (m_shape == rhs.m_shape) {
312 for (std::size_t i = 0; i < m_data.size(); ++i) {
313 result[i] = fn(m_data[i], rhs[i]);
318 std::array<std::size_t, Rank> result_shape;
320 for (std::size_t i = 0; i < Rank; ++i) {
321 if (m_shape[i] == rhs.m_shape[i]) {
322 result_shape[i] = m_shape[i];
323 }
else if (m_shape[i] == 1 || rhs.m_shape[i] == 1) {
324 result_shape[i] = std::max(m_shape[i], rhs.m_shape[i]);
326 throw std::invalid_argument(
327 "Shapes do not match and they are not compatible for broadcasting");
334 [&](
const auto& result_index) {
335 std::array<std::size_t, Rank> lhs_index{result_index};
336 std::array<std::size_t, Rank> rhs_index{result_index};
338 for (std::size_t i = 0; i < Rank; ++i) {
339 lhs_index[i] %= m_shape[i];
342 for (std::size_t i = 0; i < Rank; ++i) {
343 rhs_index[i] %= rhs.m_shape[i];
346 std::apply(result, result_index) =
347 fn(std::apply(*
this, lhs_index), std::apply(rhs, rhs_index));
363 return broadcast(other, std::multiplies());
372 std::ranges::transform(m_data, result.m_data.begin(),
373 [&](
const T& value) { return value + scalar; });
379 std::ranges::transform(m_data, result.m_data.begin(),
380 [&](
const T& value) { return value - scalar; });
386 std::ranges::transform(m_data, result.m_data.begin(),
387 [&](
const T& value) { return value * scalar; });
393 std::ranges::transform(m_data, result.m_data.begin(),
394 [&](
const T& value) { return value / scalar; });
400 std::ranges::transform(tensor.m_data, result.m_data.begin(),
401 [&](
const T& value) { return scalar + value; });
407 std::ranges::transform(tensor.m_data, result.m_data.begin(),
408 [&](
const T& value) { return scalar - value; });
414 std::ranges::transform(tensor.m_data, result.m_data.begin(),
415 [&](
const T& value) { return scalar * value; });
421 std::ranges::transform(tensor.m_data, result.m_data.begin(),
422 [&](
const T& value) { return scalar / value; });
428 std::ranges::transform(m_data, result.m_data.begin(), std::negate());
433 -> std::ostream&
requires(Rank > 1) {
434 const auto&
shape = tensor.shape();
435 std::array<size_t, Rank> index{};
437 std::function<void(
size_t,
size_t)> print_recursive = [&](
size_t dim,
438 const size_t indent) {
439 out << std::string(indent,
' ') <<
"{\n";
441 for (
size_t i = 0; i <
shape[dim]; ++i) {
444 if (dim == Rank - 2) {
445 out << std::string(indent + 2,
' ');
446 for (
size_t j = 0; j <
shape[Rank - 1]; ++j) {
448 out << tensor(index) <<
" ";
452 print_recursive(dim + 1, indent + 2);
456 out << std::string(indent,
' ') <<
"}\n";
459 print_recursive(0, 0);
464 -> std::ostream&
requires(Rank == 1) {
465 std::ranges::copy(tensor, std::ostream_iterator<T>(out,
" "));
470 return m_data.begin();
477 [[nodiscard]]
auto begin() const noexcept {
478 return m_data.begin();
481 [[nodiscard]]
auto end() const noexcept {
493 for (std::size_t i = 0; i < m_shape[0]; ++i) {
494 for (std::size_t j = 0; j < m_shape[1]; ++j) {
495 result(j, i) = (*this)(i, j);
510 std::array<std::size_t, Rank> new_shape{m_shape};
511 std::swap(new_shape[Rank - 2], new_shape[Rank - 1]);
514 std::array<std::size_t, Rank - 2>
size{};
516 std::copy(m_shape.begin(), m_shape.end() - 2,
size.begin());
519 [&](
const auto& index) {
520 std::array<std::size_t, Rank> full_index;
521 std::copy(index.begin(), index.end(), full_index.begin());
523 for (std::size_t i = 0; i < m_shape[Rank - 2]; ++i) {
524 for (std::size_t j = 0; j < m_shape[Rank - 1]; ++j) {
525 full_index[Rank - 2] = i;
526 full_index[Rank - 1] = j;
527 const T src = std::apply(*
this, full_index);
529 full_index[Rank - 2] = j;
530 full_index[Rank - 1] = i;
531 T& dest = std::apply(result, full_index);
550 std::ranges::transform(m_data, result.m_data.begin(), fn);
562 template <
typename T>
565 if (lhs.shape()[1] != rhs.shape()[0]) {
566 throw std::invalid_argument(
"Incompatible matrix dimensions for multiplication");
572 for (std::size_t i = 0; i < result.
shape()[0]; ++i) {
573 for (std::size_t j = 0; j < result.
shape()[1]; ++j) {
574 for (std::size_t k = 0; k < lhs.shape()[1]; ++k) {
575 result(i, j) += lhs(i, k) * rhs(k, j);
590 template <
typename T, std::
size_t Rank>
594 for (std::size_t i = 0; i < Rank - 2; ++i) {
596 throw std::invalid_argument(
"Incompatible batch dimensions for multiplication");
600 std::array<std::size_t, Rank> new_shape{lhs.
shape()};
601 new_shape[Rank - 1] = rhs.
shape()[Rank - 1];
604 std::array<std::size_t, Rank - 2> size{};
605 std::copy(lhs.
shape().begin(), lhs.
shape().end() - 2, size.begin());
608 [&](
const auto& index) {
609 std::array<std::size_t, Rank> full_index;
610 std::ranges::copy(index, full_index.begin());
612 for (std::size_t i = 0; i < result.
shape()[Rank - 2]; ++i) {
613 for (std::size_t j = 0; j < result.
shape()[Rank - 1]; ++j) {
614 for (std::size_t k = 0; k < lhs.
shape()[Rank - 1]; ++k) {
615 full_index[Rank - 2] = i;
616 full_index[Rank - 1] = k;
617 const T& src1 = std::apply(lhs, full_index);
619 full_index[Rank - 2] = k;
620 full_index[Rank - 1] = j;
621 const T& src2 = std::apply(rhs, full_index);
623 full_index[Rank - 2] = i;
624 full_index[Rank - 1] = j;
625 T& dest = std::apply(result, full_index);
Tensor(const std::array< size_t, Rank > &shape)
Constructor que inicializa el tensor con una forma dada.
Definition tensor.h:100
Representa un tensor de tipo T y rango Rank.
Definition tensor.h:63
friend auto operator*(const T &scalar, const Tensor &tensor) -> Tensor< T, Rank >
Definition tensor.h:412
auto operator-() const -> Tensor< T, Rank >
Definition tensor.h:426
void reshape(const std::array< size_t, Rank > &new_shape)
Cambia la forma del tensor actual.
Definition tensor.h:188
auto operator-(const Tensor< T, Rank > &other) const -> Tensor< T, Rank >
Definition tensor.h:358
auto size() const -> size_t
Definition tensor.h:175
auto shape() const noexcept -> const std::array< size_t, Rank > &
Definition tensor.h:179
constexpr auto transpose_2d() const -> Tensor< T, 2 >
Trasponer un tensor de dimension 2. @complexity O(n).
Definition tensor.h:490
constexpr auto operator()(const auto... idxs) const -> const T &
Definition tensor.h:145
auto operator/(const T &scalar) const -> Tensor< T, Rank >
Definition tensor.h:391
auto operator[](const size_t idx) -> T &
Definition tensor.h:167
auto operator+(const T &scalar) const -> Tensor< T, Rank >
Definition tensor.h:370
constexpr auto apply(auto fn) const -> Tensor< T, Rank >
Aplica una funcion a todos los elementos del tensor.
Definition tensor.h:548
auto row(const size_t index) const -> Tensor< T, 2 > requires(Rank==2)
Genera tensor con fila particular.
Definition tensor.h:223
void reshape(const Dims... dims)
Cambia la forma del tensor actual. @taram Dims Nuevo "Rank" del tensor. @complexity O(Rank).
Definition tensor.h:201
auto operator()(const std::array< size_t, Rank > &idxs) -> T &
Definition tensor.h:149
void fill(const T &value) noexcept
Llena la data de un tesor con un valor.
Definition tensor.h:213
auto operator/(const Tensor< T, Rank > &other) const -> Tensor< T, Rank >
Definition tensor.h:366
friend auto operator+(const T &scalar, const Tensor &tensor) -> Tensor< T, Rank >
Definition tensor.h:398
auto end() const noexcept
Definition tensor.h:481
auto operator+(const Tensor< T, Rank > &other) const -> Tensor< T, Rank >
Definition tensor.h:354
auto end() noexcept
Definition tensor.h:473
auto slice(const size_t index) const -> Tensor< T, 2 > requires(Rank==3)
Genera tensor con fila particular para tensor de Rank 3.
Definition tensor.h:261
auto operator*(const T &scalar) const -> Tensor< T, Rank >
Definition tensor.h:384
Tensor(const Dims... dims)
Constructor variádico para inicializar la forma del tensor.
Definition tensor.h:116
constexpr auto transpose_2d() const -> Tensor< T, Rank > requires(Rank > 2)
Trasponer un tensor de dimension n mayor a 2. @complexity O(n).
Definition tensor.h:507
friend auto operator-(const T &scalar, const Tensor &tensor) -> Tensor< T, Rank >
Definition tensor.h:405
friend auto operator<<(std::ostream &out, const Tensor< T, Rank > &tensor) -> std::ostream &requires(Rank==1)
Definition tensor.h:463
friend auto operator/(const T &scalar, const Tensor &tensor) -> Tensor< T, Rank >
Definition tensor.h:419
constexpr auto operator==(const Tensor< T, Rank > &other) const -> bool
Definition tensor.h:122
auto operator[](const size_t idx) const -> const T &
Definition tensor.h:171
auto begin() noexcept
Definition tensor.h:469
auto operator*(const Tensor< T, Rank > &other) const -> Tensor< T, Rank >
Definition tensor.h:362
auto broadcast(const Tensor< T, Rank > &rhs, auto fn) const -> Tensor< T, Rank >
Realiza Broadcasting para un tensor.
Definition tensor.h:308
constexpr auto operator()(const auto... idxs) -> T &
Acceso a un elemento por índices.
Definition tensor.h:141
auto operator()(const std::array< size_t, Rank > &idxs) const -> const T &
Definition tensor.h:158
friend auto operator<<(std::ostream &out, const Tensor< T, Rank > &tensor) -> std::ostream &requires(Rank > 1)
Definition tensor.h:432
void set_slice(const size_t index, const Tensor< T, 2 > &slice)
Cambia el subtensor asignado.
Definition tensor.h:283
auto begin() const noexcept
Definition tensor.h:477
void set_row(const size_t index, const Tensor< T, 2 > &row_tensor)
Cambia fila especifica de un tensor.
Definition tensor.h:243
auto operator-(const T &scalar) const -> Tensor< T, Rank >
Definition tensor.h:377
Tensor(const std::array< size_t, Rank > &shape)
Constructor que inicializa el tensor con una forma dada.
Definition tensor.h:100
auto operator=(std::initializer_list< T > list) -> Tensor< T, Rank > &
Definition tensor.h:126
constexpr auto matrix_product(const Tensor< T, 2 > &lhs, const Tensor< T, 2 > &rhs) -> Tensor< T, 2 >
Realiza producto matricial entre 2 tensores de dimension 2.
Definition tensor.h:563