Cypress  1.0
C++ Spiking Neural Network Simulation Framework
matrix.hpp
Go to the documentation of this file.
1 /*
2  * Cypress -- C++ Neural Associative Memory Simulator
3  * Copyright (C) 2016 Andreas Stöckel
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
28 #ifndef CYPRESS_UTIL_MATRIX_HPP
29 #define CYPRESS_UTIL_MATRIX_HPP
30 
31 #include <algorithm>
32 #include <cassert>
33 #include <memory>
34 #include <ostream>
35 #include <vector>
36 #ifndef NDEBUG
37 #include <sstream>
38 #include <stdexcept>
39 #endif
40 
41 namespace cypress {
42 
43 enum class MatrixFlags { NONE = 0, ZEROS = 1 };
44 
50 template <typename T>
51 class Matrix {
52 private:
57  T *m_buf;
58 
62  size_t m_rows, m_cols;
63 
67  bool m_destroy = true;
68 
69 #ifndef NDEBUG
70 
74  void check_range(size_t i) const
75  {
76  if (i > size()) {
77  std::stringstream ss;
78  ss << "[" << i << "] out of range for matrix of size " << size()
79  << std::endl;
80  throw std::out_of_range(ss.str());
81  }
82  }
83 
88  void check_range(size_t row, size_t col) const
89  {
90  if (row >= m_rows || col >= m_cols) {
91  std::stringstream ss;
92  ss << "[" << row << ", " << col
93  << "] out of range for matrix of size " << m_rows << " x "
94  << m_cols << std::endl;
95  throw std::out_of_range(ss.str());
96  }
97  }
98 #else
99 
103  void check_range(size_t) const {}
108  void check_range(size_t, size_t) const {}
109 #endif
110 
111 public:
115  Matrix() : m_buf(nullptr), m_rows(0), m_cols(0) {}
116 
120  Matrix(std::initializer_list<T> init) : Matrix(init.size(), 1)
121  {
122  size_t i = 0;
123  for (const auto &elem : init) {
124  (*this)[i++] = elem;
125  }
126  }
127 
131  template <size_t Rows, size_t Cols>
132  Matrix(const std::array<std::array<T, Cols>, Rows> &init)
133  : Matrix(Rows, Cols)
134  {
135  for (size_t i = 0; i < Rows; i++) {
136  for (size_t j = 0; j < Cols; j++) {
137  (*this)(i, j) = init[i][j];
138  }
139  }
140  }
141 
142  Matrix(const std::vector<std::vector<T>> &init)
143  : Matrix(init.size(), init[0].size())
144  {
145  for (size_t i = 1; i < init.size(); i++) {
146  assert(init[0].size() == init[i].size());
147  }
148  for (size_t i = 0; i < init.size(); i++) {
149  for (size_t j = 0; j < init[0].size(); j++) {
150  (*this)(i, j) = init[i][j];
151  }
152  }
153  }
154 
155  Matrix(const std::vector<T> &init) : Matrix(init.size(), 1)
156  {
157  for (size_t i = 0; i < init.size(); i++) {
158  (*this)(i, 0) = init[i];
159  }
160  }
161 
171  Matrix(size_t rows, size_t cols, MatrixFlags flags = MatrixFlags::NONE)
172  : m_buf(new T[rows * cols]), m_rows(rows), m_cols(cols)
173  {
174  if (flags == MatrixFlags::ZEROS) {
175  fill(T());
176  }
177  }
178 
188  Matrix(size_t rows, size_t cols, const T *data)
189  : m_buf(new T[rows * cols]), m_rows(rows), m_cols(cols)
190  {
191  std::copy(data, data + (rows * cols), begin());
192  }
193 
204  Matrix(size_t rows, size_t cols, T *data, bool destroy)
205  : m_buf(data), m_rows(rows), m_cols(cols), m_destroy(destroy){};
206 
207  Matrix(const Matrix &o)
208  : m_buf(new T[o.rows() * o.cols()]), m_rows(o.rows()), m_cols(o.cols())
209  {
210  std::copy(o.begin(), o.end(), begin());
211  }
212 
213  Matrix(Matrix &&o) noexcept
214  : m_buf(o.m_buf), m_rows(o.rows()), m_cols(o.cols())
215  {
216  o.m_buf = nullptr;
217  o.m_rows = 0;
218  o.m_cols = 0;
219  }
220 
222  {
223  delete[] m_buf;
224  m_rows = o.m_rows;
225  m_cols = o.m_cols;
226  m_buf = new T[o.rows() * o.cols()];
227  std::copy(o.begin(), o.end(), begin());
228  return *this;
229  }
230 
231  Matrix &operator=(Matrix &&o) noexcept
232  {
233  delete[] m_buf;
234  m_rows = o.m_rows;
235  m_cols = o.m_cols;
236  m_buf = o.m_buf;
237  o.m_buf = nullptr;
238  o.m_rows = 0;
239  o.m_cols = 0;
240  return *this;
241  }
242 
244  {
245  if (m_destroy) {
246  delete[] m_buf;
247  }
248  }
249 
253  operator std::vector<T>() const { return std::vector<T>(begin(), end()); }
254 
260  bool operator==(const Matrix<T> &o) const
261  {
262  return rows() == o.rows() && cols() == o.cols() &&
263  std::equal(begin(), end(), o.begin());
264  }
265 
269  Matrix<T> &fill(const T &val)
270  {
271  std::fill(begin(), end(), val);
272  return *this;
273  }
274 
275  T *begin(size_t row = 0) { return m_buf + row * cols(); }
276  T *end() { return m_buf + size(); }
277  const T *begin(size_t row = 0) const { return m_buf + row * cols(); }
278  const T *end() const { return m_buf + size(); }
279  const T *cbegin(size_t row = 0) const { return m_buf + row * cols(); }
280  const T *cend() const { return m_buf + size(); }
281 
285  T &operator()(size_t row, size_t col)
286  {
287  check_range(row, col);
288  return *(m_buf + row * m_cols + col);
289  }
290 
294  const T &operator()(size_t row, size_t col) const
295  {
296  check_range(row, col);
297  return *(m_buf + row * m_cols + col);
298  }
299 
303  T &operator()(size_t i)
304  {
305  check_range(i);
306  return *(m_buf + i);
307  }
308 
312  const T &operator()(size_t i) const
313  {
314  check_range(i);
315  return *(m_buf + i);
316  }
317 
321  T &operator[](size_t i)
322  {
323  check_range(i);
324  return *(m_buf + i);
325  }
326 
330  const T &operator[](size_t i) const
331  {
332  check_range(i);
333  return *(m_buf + i);
334  }
335 
339  size_t rows() const { return m_rows; }
340 
344  size_t cols() const { return m_cols; }
345 
349  size_t size() const { return m_rows * m_cols; }
350 
355  void resize(size_t rows, size_t cols)
356  {
357  if (rows != m_rows || cols != m_cols) {
358  delete[] m_buf;
359  m_buf = new T[rows * cols];
360  m_rows = rows;
361  m_cols = cols;
362  }
363  }
364 
369  void reshape(size_t rows, size_t cols)
370  {
371  if (rows * cols == m_rows * m_cols) {
372  m_rows = rows;
373  m_cols = cols;
374  }
375  else {
376  resize(rows, cols);
377  }
378  }
379 
383  T *data() { return m_buf; }
384 
388  const T *data() const { return m_buf; }
389 
393  friend std::ostream &operator<<(std::ostream &os, const Matrix<T> &m)
394  {
395  T const *d = m.data();
396  for (size_t row = 0; row < m.rows(); row++) {
397  for (size_t col = 0; col < m.cols(); col++) {
398  os << (col == 0 ? "" : ",") << *d;
399  d++;
400  }
401  os << std::endl;
402  }
403  return os;
404  }
405 
409  bool empty() const { return size() == 0; }
410 
418  Matrix submatrix(size_t row, size_t col) const
419  {
420  if (m_rows <= 1 || m_cols <= 1) {
421  throw std::invalid_argument(
422  "For calculation of a submatrix the dimension must be at least "
423  "2x2!");
424  }
425  if (m_rows <= row || m_cols <= col) {
426  throw std::invalid_argument(
427  "Matrix too small for calculating the submatrix with given row "
428  "and column!");
429  }
430  Matrix<T> subm(m_rows - 1, m_cols - 1);
431 
432  size_t subi = 0;
433  for (size_t i = 0; i < m_rows; i++) {
434  if (i == row) {
435  continue;
436  }
437  int subj = 0;
438  for (size_t j = 0; j < m_cols; j++) {
439  if (j == col) {
440  continue;
441  }
442  subm(subi, subj) = *(m_buf + i * m_cols + j);
443  subj++;
444  }
445  subi++;
446  }
447  return subm;
448  }
449 
457  T determinant() const
458  {
459  if (m_rows * m_cols == 0 || m_rows != m_cols) {
460  throw std::invalid_argument(
461  "Calculation of the determinant only valid for squared "
462  "matrices with dimension >=1!");
463  }
464  size_t dim = m_rows;
465  if (dim == 1) {
466  return *(m_buf);
467  }
468  else if (dim == 2) {
469  return (*(m_buf)) * (*(m_buf + 3)) -
470  (*(m_buf + 1)) * (*(m_buf + 2));
471  }
472  Matrix<T> subm(dim - 1, dim - 1);
473  T res = 0;
474  for (size_t x = 0; x < dim; x++) {
475  T tmp = (*(m_buf + x)); // mat(0.x)
476  if (tmp) {
477  T sign = x % 2 ? -1 : 1;
478  res += sign * tmp * submatrix(0, x).determinant();
479  }
480  }
481  return res;
482  }
483 
491  Matrix adjugate() const
492  {
493  if (m_rows * m_cols == 0 || m_rows != m_cols) {
494  throw std::invalid_argument(
495  "Calculation of the determinant only valid for squared "
496  "matrices with dimension >=1!");
497  }
498  size_t dim = m_rows;
499  if (dim == 1) {
500  return Matrix(*this);
501  }
502  Matrix<T> res(m_rows, m_cols);
503  for (size_t x = 0; x < dim; x++) {
504  for (size_t y = 0; y < dim; y++) {
505  T sign = (x + y) % 2 ? -1 : 1;
506  res(x, y) = sign * submatrix(y, x).determinant();
507  }
508  }
509  return res;
510  }
511 
521  {
522  auto det = determinant();
523  if (det == 0) {
524  throw std::invalid_argument(
525  "Could not calculate the inverse, as the determinant is zero!");
526  }
527  auto adj = adjugate();
528  Matrix<double> res(adj.rows(), adj.cols());
529  for (size_t i = 0; i < res.size(); i++) {
530  res[i] = double(adj[i]) / double(det);
531  }
532  return res;
533  }
534 };
535 
536 template <typename T, size_t Rows, size_t Cols>
537 Matrix<T> make_matrix(const std::array<std::array<T, Cols>, Rows> &init)
538 {
539  return Matrix<T>(init);
540 }
541 
542 template <typename T>
543 Matrix<T> make_matrix(std::initializer_list<T> list)
544 {
545  return Matrix<T>(list);
546 }
547 
548 template <typename T>
549 class Vector : public Matrix<T> {
550 public:
551  Vector() : Matrix<T>(0, 0) {}
553  : Matrix<T>(s, 1, flags)
554  {
555  }
556  Vector(std::initializer_list<T> init) : Vector(init.size())
557  {
558  size_t i = 0;
559  for (const auto &elem : init) {
560  (*this)[i++] = elem;
561  }
562  }
563  void resize(size_t s) { resize(s, 1); }
564 };
565 
566 } // namespace cypress
567 
568 #endif /* CYPRESS_UTIL_MATRIX_HPP */
Matrix(const std::vector< T > &init)
Definition: matrix.hpp:155
Matrix< double > inverse() const
Calculates the inverse of the matrix. As for the determinant, this runs into issues with unsigned typ...
Definition: matrix.hpp:520
Definition: matrix.hpp:549
Matrix< T > make_matrix(const std::array< std::array< T, Cols >, Rows > &init)
Definition: matrix.hpp:537
Matrix & operator=(const Matrix &o)
Definition: matrix.hpp:221
void resize(size_t s)
Definition: matrix.hpp:563
T & operator[](size_t i)
Definition: matrix.hpp:321
Matrix(const std::array< std::array< T, Cols >, Rows > &init)
Definition: matrix.hpp:132
T * data()
Definition: matrix.hpp:383
Matrix submatrix(size_t row, size_t col) const
Returns the submatrix not containing specified row and column.
Definition: matrix.hpp:418
Matrix(size_t rows, size_t cols, T *data, bool destroy)
Definition: matrix.hpp:204
void resize(size_t rows, size_t cols)
Definition: matrix.hpp:355
Vector()
Definition: matrix.hpp:551
const T & operator()(size_t i) const
Definition: matrix.hpp:312
size_t size() const
Definition: matrix.hpp:349
T determinant() const
Calculate the determinant of this matrix. Note that the type of the returned value is of the same typ...
Definition: matrix.hpp:457
T & operator()(size_t row, size_t col)
Definition: matrix.hpp:285
Matrix()
Definition: matrix.hpp:115
Definition: matrix.hpp:51
const T * data() const
Definition: matrix.hpp:388
size_t rows() const
Definition: matrix.hpp:339
T * begin(size_t row=0)
Definition: matrix.hpp:275
Matrix(Matrix &&o) noexcept
Definition: matrix.hpp:213
size_t cols() const
Definition: matrix.hpp:344
const T & operator[](size_t i) const
Definition: matrix.hpp:330
~Matrix()
Definition: matrix.hpp:243
bool operator==(const Matrix< T > &o) const
Definition: matrix.hpp:260
T & operator()(size_t i)
Definition: matrix.hpp:303
const T * end() const
Definition: matrix.hpp:278
void reshape(size_t rows, size_t cols)
Definition: matrix.hpp:369
Matrix(const Matrix &o)
Definition: matrix.hpp:207
const T & operator()(size_t row, size_t col) const
Definition: matrix.hpp:294
MatrixFlags
Definition: matrix.hpp:43
T * end()
Definition: matrix.hpp:276
Definition: brainscales_lib.hpp:39
Matrix & operator=(Matrix &&o) noexcept
Definition: matrix.hpp:231
const T * cend() const
Definition: matrix.hpp:280
const T * begin(size_t row=0) const
Definition: matrix.hpp:277
Vector(std::initializer_list< T > init)
Definition: matrix.hpp:556
Vector(size_t s, MatrixFlags flags=MatrixFlags::NONE)
Definition: matrix.hpp:552
Matrix< T > & fill(const T &val)
Definition: matrix.hpp:269
Matrix adjugate() const
Calculates the adjugate/adjunct matrix. As for the determinant, this runs into issues with unsigned t...
Definition: matrix.hpp:491
Matrix(const std::vector< std::vector< T >> &init)
Definition: matrix.hpp:142
const T * cbegin(size_t row=0) const
Definition: matrix.hpp:279
bool empty() const
Definition: matrix.hpp:409
Matrix(std::initializer_list< T > init)
Definition: matrix.hpp:120
Matrix(size_t rows, size_t cols, const T *data)
Definition: matrix.hpp:188
Matrix(size_t rows, size_t cols, MatrixFlags flags=MatrixFlags::NONE)
Definition: matrix.hpp:171