Deep Learning Algorithm Implementations 1.0.0
C++ implementations of fundamental deep learning algorithms
Loading...
Searching...
No Matches
tensor.cpp
Go to the documentation of this file.
1#include <numeric>
2#include <xtensor-blas/xlinalg.hpp>
3#include <xtensor/containers/xadapt.hpp>
4#include <xtensor/generators/xrandom.hpp>
5#include <xtensor/views/xview.hpp>
6#include "utils/tensor.hpp"
7
8namespace utils {
9 // Default constructor is defined inline in header
10
11 // N-dimensional constructor
12 template<typename T>
13 Tensor<T>::Tensor(const std::vector<size_t>& shape) : rows_(shape.size() > 0 ? shape[0] : 0), cols_(shape.size() > 1 ? shape[1] : 1), data_(xt::zeros<T>(shape)) {}
14
15 // N-dimensional constructor with value
16 template<typename T>
17 Tensor<T>::Tensor(const std::vector<size_t>& shape, T value) : rows_(shape.size() > 0 ? shape[0] : 0), cols_(shape.size() > 1 ? shape[1] : 1), data_(xt::ones<T>(shape) * value) {}
18
19 // 2D constructor (backward compatibility)
20 template<typename T>
21 Tensor<T>::Tensor(size_t rows, size_t cols) : rows_(rows), cols_(cols), data_(xt::zeros<T>({rows, cols})) {}
22
23 // 2D constructor with value (backward compatibility)
24 template<typename T>
25 Tensor<T>::Tensor(size_t rows, size_t cols, T value) : rows_(rows), cols_(cols), data_(xt::ones<T>({rows, cols}) * value) {}
26
27 // Constructor from xt::xarray
28 template<typename T>
29 Tensor<T>::Tensor(const xt::xarray<T>& array) : data_(array) {
30 auto shape = array.shape();
31 rows_ = shape.size() > 0 ? shape[0] : 0;
32 cols_ = shape.size() > 1 ? shape[1] : 1;
33 }
34
35 // 2D initializer list constructor (backward compatibility)
36 template<typename T>
37 Tensor<T>::Tensor(std::initializer_list<std::initializer_list<T>> list) {
38 rows_ = list.size();
39 cols_ = list.begin()->size();
40
41 std::vector<T> flat_data;
42 flat_data.reserve(rows_ * cols_);
43
44 for (const auto &row: list) {
45 std::copy(row.begin(), row.end(), std::back_inserter(flat_data));
46 }
47
48 data_ = xt::adapt(flat_data, {rows_, cols_});
49 }
50
51 // operator() methods are defined as variadic templates in header
52
53 // Element access for 2D using at() method
54 template<typename T>
55 T &Tensor<T>::at(size_t row, size_t col) {
56 return operator()(row, col);
57 }
58
59 template<typename T>
60 const T &Tensor<T>::at(size_t row, size_t col) const {
61 return operator()(row, col);
62 }
63
64 // Getters
65 // size(), rows(), cols(), and shape() are defined inline in the header
66
67 // data() methods are defined inline in header
68
69 // Arithmetic operations
70 template<typename T>
72 if (rows_ != other.rows_ || cols_ != other.cols_) {
73 throw std::invalid_argument("Tensor shapes must match for addition");
74 }
75
76 Tensor<T> result(rows_, cols_);
77 result.data_ = data_ + other.data_;
78 return result;
79 }
80
81 template<typename T>
83 if (rows_ != other.rows_ || cols_ != other.cols_) {
84 throw std::invalid_argument("Tensor shapes must match for subtraction");
85 }
87 Tensor<T> result(rows_, cols_);
88 result.data_ = data_ - other.data_;
89 return result;
90 }
91
92 template<typename T>
94 if (rows_ != other.rows_ || cols_ != other.cols_) {
95 throw std::invalid_argument("Tensor shapes must match for element-wise multiplication");
96 }
97
98 Tensor<T> result(rows_, cols_);
99 result.data_ = data_ * other.data_;
100 return result;
101 }
102
103 template<typename T>
105 Tensor<T> result(rows_, cols_);
106 result.data_ = data_ * scalar;
107 return result;
108 }
109
110 template<typename T>
112 if (data_.dimension() != 2 || other.data_.dimension() != 2) {
113 throw std::invalid_argument("Matrix multiplication requires 2D tensors");
114 }
115 if (cols_ != other.rows_) {
116 throw std::invalid_argument("Tensor dimensions incompatible for matrix multiplication");
117 }
118
119 // TODO: Fix xtensor-blas compatibility issue with matrix multiplication
120 throw std::runtime_error("Matrix multiplication temporarily disabled due to xtensor-blas compatibility issue");
121 // Tensor<T> result(rows_, other.cols_);
122 // result.data_ = xt::linalg::dot(data_, other.data_);
123 // return result;
124 }
125
126 // Tensor operations
127 template<typename T>
129 if (data_.dimension() != 2) {
130 throw std::invalid_argument("Transpose requires 2D tensor");
131 }
132
133 Tensor<T> result(cols_, rows_);
134 result.data_ = xt::transpose(data_);
135 return result;
136 }
137
138 template<typename T>
139 Tensor<T> Tensor<T>::transpose(const std::vector<size_t>& axes) const {
140 if (axes.size() != data_.dimension()) {
141 throw std::invalid_argument("Number of axes must match tensor dimensions");
142 }
143
144 std::vector<size_t> current_shape = {rows_, cols_};
145 std::vector<size_t> new_shape(axes.size());
146 for (size_t i = 0; i < axes.size(); ++i) {
147 new_shape[i] = current_shape[axes[i]];
148 }
149
150 Tensor<T> result(new_shape);
151 result.data_ = xt::transpose(data_, axes);
152 return result;
153 }
154
155 template<typename T>
156 Tensor<T> Tensor<T>::reshape(const std::vector<size_t>& new_shape) const {
157 size_t old_size = rows_ * cols_;
158 size_t new_size = std::accumulate(new_shape.begin(), new_shape.end(), 1UL, std::multiplies<size_t>());
159
160 if (old_size != new_size) {
161 throw std::invalid_argument("Total size must remain the same for reshape");
162 }
163
164 Tensor<T> result(new_shape);
165 result.data_ = xt::reshape_view(data_, new_shape);
166 return result;
167 }
168
169 template<typename T>
170 Tensor<T> Tensor<T>::reshape(size_t new_rows, size_t new_cols) const {
171 return reshape({new_rows, new_cols});
172 }
174 template<typename T>
175 Tensor<T> Tensor<T>::view(const std::vector<size_t>& new_shape) const {
176 size_t old_size = rows_ * cols_;
177 size_t new_size = std::accumulate(new_shape.begin(), new_shape.end(), 1UL, std::multiplies<size_t>());
178
179 if (old_size != new_size) {
180 throw std::invalid_argument("Total size must remain the same for view");
182
183 Tensor<T> result(new_shape);
184 result.data_ = xt::reshape_view(data_, new_shape);
185 return result;
186 }
187
188 template<typename T>
190 std::vector<size_t> current_shape = {rows_, cols_};
191 std::vector<size_t> new_shape;
192
193 if (axis == -1) {
194 // Squeeze all dimensions of size 1
195 for (size_t dim : current_shape) {
196 if (dim != 1) {
197 new_shape.push_back(dim);
198 }
199 }
200 } else {
201 // Squeeze specific axis
202 if (axis >= static_cast<int>(current_shape.size()) || current_shape[axis] != 1) {
203 throw std::invalid_argument("Cannot squeeze dimension that is not 1");
204 }
205 for (size_t i = 0; i < current_shape.size(); ++i) {
206 if (i != static_cast<size_t>(axis)) {
207 new_shape.push_back(current_shape[i]);
208 }
209 }
211
212 if (new_shape.empty()) {
213 new_shape.push_back(1); // Ensure at least 1D
214 }
215
216 return view(new_shape);
218
219 template<typename T>
220 Tensor<T> Tensor<T>::unsqueeze(size_t axis) const {
221 std::vector<size_t> current_shape = {rows_, cols_};
222 if (axis > current_shape.size()) {
223 throw std::invalid_argument("Axis out of range for unsqueeze");
224 }
226 std::vector<size_t> new_shape = current_shape;
227 new_shape.insert(new_shape.begin() + axis, 1);
228
229 return view(new_shape);
230 }
231
232 template<typename T>
234 if (data_.dimension() != 2 || rows_ != cols_) {
235 throw std::invalid_argument("Determinant requires square 2D tensor");
236 }
237 // TODO: Fix xtensor-blas compatibility issue with determinant
238 throw std::runtime_error("Determinant computation temporarily disabled due to xtensor-blas compatibility issue");
239 // return xt::linalg::det(data_);
240 }
241
242 template<typename T>
244 if (data_.dimension() != 2 || rows_ != cols_) {
245 throw std::invalid_argument("Inverse requires square 2D tensor");
246 }
247
248 // TODO: Fix xtensor-blas compatibility issue with inverse
249 throw std::runtime_error("Matrix inverse computation temporarily disabled due to xtensor-blas compatibility issue");
250 // Tensor<T> result(rows_, cols_);
251 // result.data_ = xt::linalg::inv(data_);
252 // return result;
253 }
254
255 template<typename T>
257 if (data_.dimension() != 2 || rows_ != cols_) {
258 throw std::invalid_argument("Eigenvalues require square 2D tensor");
259 }
260 // TODO: Fix xtensor-blas compatibility issue with eigenvalues
261 throw std::runtime_error("Eigenvalues computation temporarily disabled due to xtensor-blas compatibility issue");
262 // return xt::linalg::eigvals(data_);
264
265 // Static factory methods
266 template<typename T>
267 Tensor<T> Tensor<T>::zeros(const std::vector<size_t>& shape) {
268 return Tensor<T>(shape);
269 }
271 template<typename T>
272 Tensor<T> Tensor<T>::zeros(size_t rows, size_t cols) {
273 return Tensor<T>(rows, cols);
274 }
275
276 template<typename T>
277 Tensor<T> Tensor<T>::ones(const std::vector<size_t>& shape) {
278 Tensor<T> result(shape);
279 result.data_ = xt::ones<T>(shape);
280 return result;
281 }
282
283 template<typename T>
284 Tensor<T> Tensor<T>::ones(size_t rows, size_t cols) {
285 return ones({rows, cols});
286 }
287
288 template<typename T>
289 Tensor<T> Tensor<T>::full(const std::vector<size_t>& shape, T value) {
290 return Tensor<T>(shape, value);
291 }
292
293 template<typename T>
295 std::vector<size_t> shape = {size, size};
296 Tensor<T> result(shape);
297 result.data_ = xt::eye<T>(size);
298 return result;
299 }
300
301 template<typename T>
302 Tensor<T> Tensor<T>::random(const std::vector<size_t>& shape) {
303 Tensor<T> result(shape);
304 if constexpr (std::is_integral_v<T>) {
305 result.data_ = xt::random::randint<T>(shape, T(0), T(10));
306 } else {
307 result.data_ = xt::random::rand<T>(shape, T(0), T(1));
308 }
309 return result;
310 }
311
312 template<typename T>
313 Tensor<T> Tensor<T>::random(const std::vector<size_t>& shape, T min, T max) {
314 Tensor<T> result(shape);
315 if constexpr (std::is_integral_v<T>) {
316 result.data_ = xt::random::randint<T>(shape, min, max);
317 } else {
318 result.data_ = xt::random::rand<T>(shape, min, max);
319 }
320 return result;
321 }
323 template<typename T>
324 Tensor<T> Tensor<T>::random(size_t rows, size_t cols) {
325 std::vector<size_t> shape = {rows, cols};
326 Tensor<T> result(shape);
327 if constexpr (std::is_integral_v<T>) {
328 result.data_ = xt::random::randint<T>(shape, T(0), T(10));
329 } else {
330 result.data_ = xt::random::rand<T>(shape, T(0), T(1));
331 }
332 return result;
333 }
334
335 template<typename T>
336 Tensor<T> Tensor<T>::random(size_t rows, size_t cols, T min, T max) {
337 std::vector<size_t> shape = {rows, cols};
338 Tensor<T> result(shape);
339 if constexpr (std::is_integral_v<T>) {
340 result.data_ = xt::random::randint<T>(shape, min, max);
341 } else {
342 result.data_ = xt::random::rand<T>(shape, min, max);
343 }
344 return result;
346
347 template<typename T>
348 Tensor<T> Tensor<T>::from_array(const xt::xarray<T>& array) {
349 return Tensor<T>(array);
350 }
351
352 // Non-member functions
353 template<typename T>
354 std::ostream &operator<<(std::ostream &os, const Tensor<T> &tensor) {
355 os << tensor.data();
356 return os;
357 }
358
359 template<typename T>
360 Tensor<T> dot(const Tensor<T> &a, const Tensor<T> &b) {
361 if (a.data().dimension() != 2 || b.data().dimension() != 2) {
362 throw std::invalid_argument("Dot product requires 2D tensors");
363 }
364 if (a.cols() != b.rows()) {
365 throw std::invalid_argument("Tensor dimensions incompatible for dot product");
366 }
368 // TODO: Fix xtensor-blas compatibility issue with dot product
369 throw std::runtime_error("Dot product computation temporarily disabled due to xtensor-blas compatibility issue");
370 // Tensor<T> result(a.rows(), b.cols());
371 // result.data() = xt::linalg::dot(a.data(), b.data());
372 // return result;
373 }
374
375 template<typename T>
376 T sum(const Tensor<T> &tensor) {
377 return xt::sum(tensor.data())();
378 }
379
380 template<typename T>
381 T mean(const Tensor<T> &tensor) {
382 return xt::mean(tensor.data())();
383 }
385 // Explicit template instantiations
386 template class Tensor<float>;
387 template class Tensor<double>;
388 template class Tensor<int>;
389
390 // Explicit instantiation of non-member functions
391 template std::ostream &operator<< <float>(std::ostream &, const Tensor<float> &);
392 template std::ostream &operator<< <double>(std::ostream &, const Tensor<double> &);
393
396
397 template float sum<float>(const Tensor<float> &);
398 template double sum<double>(const Tensor<double> &);
399
400 template float mean<float>(const Tensor<float> &);
401 template double mean<double>(const Tensor<double> &);
402} // namespace utils
Tensor()
Default constructor creating an empty tensor.
Definition tensor.hpp:58
size_t cols() const
Get the number of columns.
Definition tensor.hpp:296
static Tensor full(const std::vector< size_t > &shape, T value)
Create a tensor filled with a specific value.
Definition tensor.cpp:289
Tensor reshape(const std::vector< size_t > &new_shape) const
Reshape the tensor to new dimensions.
Definition tensor.cpp:156
static Tensor ones(const std::vector< size_t > &shape)
Create a tensor filled with ones.
Definition tensor.cpp:277
Tensor transpose() const
Compute the transpose of the tensor (for 2D tensors)
Definition tensor.cpp:128
static Tensor from_array(const xt::xarray< T > &array)
Create a tensor from an existing xt::xarray.
Definition tensor.cpp:348
static Tensor identity(size_t size)
Create an identity matrix (2D tensor)
Definition tensor.cpp:294
std::tuple< size_t, size_t > shape() const
Get the shape of the matric in one step.
Definition tensor.hpp:308
static Tensor random(const std::vector< size_t > &shape)
Create a random tensor with values between 0 and 1.
Definition tensor.cpp:302
Tensor squeeze(int axis=-1) const
Squeeze dimensions of size 1.
Definition tensor.cpp:189
auto eigenvalues() const
Calculate eigenvalues of the matrix (for 2D square tensors)
Definition tensor.cpp:256
Tensor view(const std::vector< size_t > &new_shape) const
Create a view of the tensor with new shape.
Definition tensor.cpp:175
Tensor unsqueeze(size_t axis) const
Add a dimension of size 1.
Definition tensor.cpp:220
xt::xarray< T > & data()
Get the underlying xtensor array.
Definition tensor.hpp:409
size_t rows() const
Get the number of rows.
Definition tensor.hpp:290
Tensor operator-(const Tensor &other) const
Tensor element-wise subtraction operator.
Definition tensor.cpp:82
Tensor inverse() const
Calculate the inverse of the matrix (for 2D square tensors)
Definition tensor.cpp:243
T determinant() const
Calculate the determinant of the matrix (for 2D square tensors)
Definition tensor.cpp:233
Tensor operator*(const Tensor &other) const
Tensor element-wise multiplication operator.
Definition tensor.cpp:93
Tensor matmul(const Tensor &other) const
Matrix multiplication operator (for 2D tensors)
Definition tensor.cpp:111
static Tensor zeros(const std::vector< size_t > &shape)
Create a zero tensor.
Definition tensor.cpp:267
T & at(size_t row, size_t col)
Access 2D tensor element at specified position (mutable) - backward compatibility.
Definition tensor.cpp:55
Tensor operator+(const Tensor &other) const
Tensor element-wise addition operator.
Definition tensor.cpp:71
template Tensor< double > dot< double >(const Tensor< double > &, const Tensor< double > &)
template std::ostream & operator<<< float >(std::ostream &, const Tensor< float > &)
template Tensor< float > dot< float >(const Tensor< float > &, const Tensor< float > &)
T mean(const Tensor< T > &tensor)
Calculate mean of all tensor elements.
Definition tensor.cpp:381
std::ostream & operator<<(std::ostream &os, const Tensor< T > &tensor)
Output stream operator for tensor visualization.
Definition tensor.cpp:354
template double sum< double >(const Tensor< double > &)
template double mean< double >(const Tensor< double > &)
Tensor< T > dot(const Tensor< T > &a, const Tensor< T > &b)
Compute dot product of two tensors.
Definition tensor.cpp:360
T sum(const Tensor< T > &tensor)
Calculate sum of all tensor elements.
Definition tensor.cpp:376
template float sum< float >(const Tensor< float > &)
template std::ostream & operator<<< double >(std::ostream &, const Tensor< double > &)
template float mean< float >(const Tensor< float > &)