Advertisement
Nicolas_Janin

Stupid Matrix class

Dec 8th, 2013
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 8.20 KB | None | 0 0
  1. //  Copyright Nicolas Janin 2004 - 2013.
  2. //  Distributed under the Boost Software License, Version 1.0.
  3. //  (See accompanying file LICENSE_1_0.txt or copy
  4. //  at http://www.boost.org/LICENSE_1_0.txt)
  5.  
  6. #ifndef _SMatrix_HPP_
  7. #define _SMatrix_HPP_
  8.  
  9. #include<vector>
  10. #include<cassert>
  11. #include<iostream>
  12.  
  13.  
  14. /// Simple/Small/Stupid Matrix - A lightweight Matrix class
  15. ///
  16. /// This library is designed around a few simple ideas:
  17. /// 1. the API is straightforward and easily extensible.
  18. /// 2. although it performs okay in my tests, it's not designed for HPC
  19. ///    or matrices with more than say a million elements.
  20. ///    Use a dedicated library (Eigen, Armadillo...) for this kind
  21. ///    of work.
  22. ///    SMatrix is well suited for embedded devices or when you need
  23. ///    quick and dirty matrices in your code.
  24. /// 3. it tries to avoid allocations behind your back to preserve performance
  25. ///    and suit tight resource constraints.
  26. ///    This means that most operations are performed in place, including arithmetic
  27. ///    operations. You have to arrange your calculations in order to allocate
  28. ///    your matrices beforehand. There are exceptions that are documented in the API.
  29. ///    Moreover, matrices are *not* resized automatically.
  30. ///    This prevents spurious allocations while matrix calculations are performed
  31. ///    in your loops.
  32. /// 4. size verifications are performed using assertions. Please test your code
  33. ///    before releasing it to production !
  34. ///
  35. /// A word of caution:
  36. /// Indices are zero based.
  37. /// Also, because most operations are performed in place,
  38. /// SMatrix is *NOT* a direct replacement for Matlab.
  39. /// Speed and memory are of concern more than convenience.
  40. /// For instance, in Matlab
  41. /// A = B + C will allocate matrix A and A will store the result
  42. /// Here, the same effect can be achieved thus:
  43. /// B + C   // equivalent to B = B + C
  44. /// A = B   // copy B into A
  45. ///
  46.  
  47.  
  48. template <typename T> class SMatrix
  49. {
  50. public:
  51.     /// constructors/destructor
  52.  
  53.     SMatrix(const size_t l, const size_t c):
  54.         _iL(l), _iC(c), _v(new std::vector<T>(l * c)) {
  55.     }
  56.  
  57.     SMatrix(const size_t l, const size_t c, const T tab[]):
  58.         _iL(l), _iC(c), _v(new std::vector<T>(l * c)) {
  59.         for( size_t l = 0; l < _iL; l++ )
  60.             for( size_t c = 0; c < _iC; c++ )
  61.                 (*_v)[l*_iC + c] = tab[l*_iC + c];
  62.     }
  63.  
  64.     SMatrix(const SMatrix &rhs):
  65.         _iL(rhs.rows()), _iC(rhs.cols()), _v(new std::vector<T>(_iL * _iC)) {
  66.         (*_v) = *(rhs.getv());
  67.     }
  68.  
  69.     ~SMatrix(){ delete _v; }
  70.  
  71.     /// @brief accessors
  72.     inline T operator()(const size_t l, const size_t c) const {
  73.         assert(l < _iL && c < _iC);
  74.         return (*_v)[l * _iC + c];
  75.     }
  76.  
  77.     inline T& operator()(const size_t l, const size_t c) {
  78.         assert(l < _iL && c < _iC);
  79.         return  (*_v)[l * _iC + c];
  80.     }
  81.  
  82.     /// @brief dimensions
  83.     inline size_t rows() const { return _iL; }
  84.     inline size_t cols() const { return _iC; }
  85.  
  86.     ///  class operations
  87.    
  88.     /// @brief change the dimensions of the matrix
  89.     /// Warning: reallocates !
  90.     void resize(const size_t rows, const size_t cols) {
  91.         _iL = rows;
  92.         _iC = cols;
  93.         _v->resize(rows, cols);
  94.     }
  95.    
  96.     /// @brief  assignment operator. Copies the matrix.
  97.     T& operator=(const T& rhs) {
  98.         if (&rhs != this) {
  99.             assert(rhs.rows() == _iL && rhs.cols() == _iC);
  100.             //if(rhs.rows() != _iL || rhs.cols() != _iC)
  101.             //  resize(rhs.rows(), rhs.cols());
  102.             _v = rhs.getv();
  103.         }
  104.         return *this;
  105.     }
  106.    
  107.     void swap_rows(const size_t l1, const size_t l2){
  108.         // Todo
  109.     }
  110.    
  111.     /// @brief fill with the scalar s
  112.     inline size_t fill(const T s) {
  113.         size_t nbelem(_iL * _iC);
  114.         _v->assign(nbelem + 1, s);
  115.         return nbelem;
  116.     }
  117.    
  118.     /// @brief submatrix
  119.     size_t submatrix(SMatrix &sub, const size_t l1, const size_t c1,
  120.             const size_t l2, const size_t c2) const {
  121.         assert(l2 < _iL && l1 <= l2);
  122.         assert(c2 < _iC && c1 <= c2);
  123.         const size_t dim1 = l2 - l1 + 1;
  124.         const size_t dim2 = c2 - c1 + 1;
  125.         for(size_t l = l1, lsub = 0; l <= l2; ++l, ++lsub)
  126.             for(size_t c = c1, csub = 0; c <= c2; ++c, ++csub)
  127.                 sub(lsub, csub) = (*_v)[l * _iC + c];
  128.         return dim1 * dim2;
  129.     }
  130.    
  131.     /// @brief return a submatrix(1,0) with the line l
  132.     inline size_t row(SMatrix &row, const size_t l) const {
  133.         assert(l < _iL);
  134.         return submatrix(row, l, 0, l, _iC - 1);
  135.     }
  136.  
  137.     /// @brief return a submatrix(0,1) with the column c
  138.     inline size_t column(SMatrix &col, const size_t c) const {
  139.         assert(c < _iC);
  140.         return submatrix(col, 0, c, _iL - 1, c);
  141.     }
  142.  
  143.     /// @brief transpose the matrix in place
  144.     /// warning: temporary allocation for square matrices > 512
  145.     SMatrix& tr() {
  146.         /*if (_iL == _iC && _iL > 512){
  147.             std::vector<T> *v2 = new std::vector<T>(*_v);
  148.             this->blockTranspose(*v2, 0, _iL, 0, _iC);
  149.             delete v2;
  150.         }
  151.         else
  152.            */  
  153.             this->transpose();
  154.         return *this;
  155.     }
  156.    
  157.     ///  scalar operations
  158.     SMatrix& operator+(const T s){
  159.         for (size_t ij = 0; ij < _iC * _iL; ++ij) (*_v)[ij] += s;
  160.         return *this;
  161.     }
  162.    
  163.     SMatrix& operator-(const T s){
  164.         for (size_t ij = 0; ij < _iC * _iL; ++ij) (*_v)[ij] -= s;
  165.         return *this;
  166.     }
  167.    
  168.     SMatrix& operator*(const T s){
  169.         for (size_t ij = 0; ij < _iC * _iL; ++ij) (*_v)[ij] *= s;
  170.         return *this;
  171.     }
  172.    
  173.     SMatrix& operator/(const T s){
  174.         for (size_t ij = 0; ij < _iC * _iL; ++ij) (*_v)[ij] /= s;
  175.         return *this;
  176.     }
  177.  
  178.     // matrix to matrix operations
  179.     SMatrix& operator+(const SMatrix &rhs){
  180.         assert(_iL == rhs.rows() && _iC == rhs.cols());
  181.         std::vector<T>* rhs_v = rhs.getv();
  182.         for (size_t ij = 0; ij < _iC * _iL; ++ij) (*_v)[ij] += (*rhs_v)[ij];
  183.         return *this;
  184.     }
  185.    
  186.     SMatrix& operator-(const SMatrix &rhs){
  187.         assert(_iL == rhs.rows() && _iC == rhs.cols());
  188.         std::vector<T>* rhs_v = rhs.getv();
  189.         for (size_t ij = 0; ij < _iC * _iL; ++ij) (*_v)[ij] -= (*rhs_v)[ij];
  190.         return *this;
  191.     }
  192.  
  193.     /// @brief in place dot/scalar product, naive O(n^3) algorithm
  194.     /// warning: temporary stack allocation
  195.     // Could be accelerated with OpenMP
  196.     SMatrix& dot(const SMatrix &b){
  197.         assert(b.rows() == _iC);
  198.         size_t bcols = b.cols();
  199.         std::vector<T> prod(_iL * bcols);
  200.         for (size_t i = 0; i < _iL; ++i){
  201.             // Swap j and k loops for cache friendliness
  202.             for (size_t k = 0; k < _iC; ++k){
  203.                 const double aik = (*_v)[i * _iC + k];
  204.                 for (size_t j = 0; j < bcols; ++j)
  205.                     prod[i * bcols + j] += aik * b(k, j);
  206.             }
  207.         }
  208.         resize(_iL, b.cols());
  209.         *_v = prod;
  210.         return *this;
  211.     }
  212.    
  213.    
  214.     /// @brief apply the function f(arg) to each matrix element  
  215.     template<typename ret>
  216.     void apply(ret (*f)(T)) {
  217.         typename std::vector<T>::iterator it = _v->begin();
  218.         while(it != _v->end()) {
  219.             *it = (*f)(*it);
  220.             ++it;
  221.         }
  222.     }
  223.    
  224.     /// @brief apply the function f(arg1, arg2) to each matrix element  
  225.     /// arg2 must have the exact argument type, no implicit conversion is possible
  226.     template<typename ret, typename A>
  227.     void apply(ret (*f)(T, A), A arg2) {
  228.         typename std::vector<T>::iterator it = _v->begin();
  229.         while(it != _v->end()) {
  230.             *it = (*f)(*it, arg2);
  231.             ++it;
  232.         }
  233.     }
  234.  
  235.    
  236. protected:
  237.     size_t _iL, _iC;            // number of lines, columns
  238.     std::vector<T>  *_v;        // heap based matrix
  239.    
  240.     SMatrix() {}
  241.  
  242.     inline std::vector<T>* getv() const { return _v; }
  243.  
  244.     // transpose a MxN matrix
  245.     SMatrix& transpose() {
  246.         std::vector<T> v2((*_v));
  247.         (*_v).clear();
  248.         for (size_t l = 0; l < _iC; ++l) {
  249.             for (size_t c = 0; c < _iL; ++c) {
  250.                 (*_v).push_back(v2[c * _iC + l]);
  251.             }
  252.         }
  253.         std::swap(_iL, _iC);
  254.         return *this;
  255.     }
  256.  
  257.     // fast block algorithm for square matrices
  258.     SMatrix& blockTranspose(const std::vector<T> &v2,
  259.             const size_t i0, const size_t i1, const size_t j0, const size_t j1) {
  260.         const size_t di = i1 - i0;
  261.         const size_t dj = j1 - j0;
  262.         const size_t LEAFSIZE = 32;
  263.         // the idea here is to recursively subdivide the matrix into blocks
  264.         // that easily fit into cache
  265.         if (di >= dj && di > LEAFSIZE) {
  266.             const size_t im = (i0 + i1) / 2;
  267.             blockTranspose(v2, i0, im, j0, j1); // first half
  268.             blockTranspose(v2, im, i1, j0, j1); // second half
  269.         }
  270.         else if (dj > LEAFSIZE) {
  271.             const size_t jm = (j0 + j1) / 2;
  272.             blockTranspose(v2, i0, i1, j0, jm);
  273.             blockTranspose(v2, i0, i1, jm, j1);
  274.         }
  275.         else {
  276.             for (size_t i = i0; i < i1; i++)
  277.                 for (size_t j = j0; j < j1; j++){
  278.                     (*_v)[j * i1 + i] = v2[i * j1 + j];
  279.                 }
  280.         }
  281.        
  282.         return *this;
  283.     }
  284.  
  285.  
  286. };
  287.  
  288. #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement