// Copyright (C) 2006 Davis E. King (davisking@users.sourceforge.net) // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_MATRIx_ #define DLIB_MATRIx_ #include "matrix_fwd.h" #include "matrix_abstract.h" #include "../algs.h" #include "../serialize.h" #include "../enable_if.h" #include #include #include "../memory_manager.h" #include "../is_kind.h" #include "matrix_data_layout.h" #include "matrix_assign_fwd.h" #ifdef _MSC_VER // Disable the following warnings for Visual Studio // This warning is: // "warning C4355: 'this' : used in base member initializer list" // Which we get from this code but it is not an error so I'm turning this // warning off and then turning it back on at the end of the file. #pragma warning(disable : 4355) #endif namespace dlib { // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // We want to return the compile time constant if our NR and NC dimensions // aren't zero but if they are then we want to call ref_.nx() and return // the correct values. template < typename exp_type, long NR > struct get_nr_helper { static inline long get(const exp_type&) { return NR; } }; template < typename exp_type > struct get_nr_helper { static inline long get(const exp_type& m) { return m.nr(); } }; template < typename exp_type, long NC > struct get_nc_helper { static inline long get(const exp_type&) { return NC; } }; template < typename exp_type > struct get_nc_helper { static inline long get(const exp_type& m) { return m.nc(); } }; template struct matrix_traits { typedef typename EXP::type type; typedef typename EXP::mem_manager_type mem_manager_type; typedef typename EXP::layout_type layout_type; const static long NR = EXP::NR; const static long NC = EXP::NC; const static long cost = EXP::cost; }; template < typename EXP > class matrix_exp { /*! REQUIREMENTS ON EXP EXP should be something convertible to a matrix_exp. That is, it should inherit from matrix_exp !*/ public: typedef typename matrix_traits::type type; typedef typename matrix_traits::mem_manager_type mem_manager_type; typedef typename matrix_traits::layout_type layout_type; const static long NR = matrix_traits::NR; const static long NC = matrix_traits::NC; const static long cost = matrix_traits::cost; typedef matrix matrix_type; typedef EXP exp_type; inline const type operator() ( long r, long c ) const { DLIB_ASSERT(r < nr() && c < nc() && r >= 0 && c >= c, "\tconst type matrix_exp::operator(r,c)" << "\n\tYou must give a valid row and column" << "\n\tr: " << r << "\n\tc: " << c << "\n\tnr(): " << nr() << "\n\tnc(): " << nc() << "\n\tthis: " << this ); return ref_(r,c); } const type operator() ( long i ) const { COMPILE_TIME_ASSERT(NC == 1 || NC == 0 || NR == 1 || NR == 0); DLIB_ASSERT(nc() == 1 || nr() == 1, "\tconst type matrix_exp::operator(i)" << "\n\tYou can only use this operator on column or row vectors" << "\n\ti: " << i << "\n\tnr(): " << nr() << "\n\tnc(): " << nc() << "\n\tthis: " << this ); DLIB_ASSERT( ((nc() == 1 && i < nr()) || (nr() == 1 && i < nc())) && i >= 0, "\tconst type matrix_exp::operator(i)" << "\n\tYou must give a valid row/column number" << "\n\ti: " << i << "\n\tnr(): " << nr() << "\n\tnc(): " << nc() << "\n\tthis: " << this ); if (nc() == 1) return ref_(i,0); else return ref_(0,i); } long size ( ) const { return nr()*nc(); } long nr ( ) const { return get_nr_helper::get(ref_); } long nc ( ) const { return get_nc_helper::get(ref_); } template bool aliases ( const matrix& item ) const { return ref_.aliases(item); } template bool destructively_aliases ( const matrix& item ) const { return ref_.destructively_aliases(item); } const exp_type& ref ( ) const { return ref_; } inline operator const type ( ) const { COMPILE_TIME_ASSERT(NC == 1 || NC == 0); COMPILE_TIME_ASSERT(NR == 1 || NR == 0); DLIB_ASSERT(nr() == 1 && nc() == 1, "\tmatrix_exp::operator const type&() const" << "\n\tYou can only use this operator on a 1x1 matrix" << "\n\tnr(): " << nr() << "\n\tnc(): " << nc() << "\n\tthis: " << this ); return ref_(0,0); } protected: explicit matrix_exp ( const EXP& exp ) : ref_(exp) {} private: // you can't copy a matrix_exp at all. Things that inherit from it must // define their own copy constructors that call the above protected // constructor so that the reference below is maintained correctly. matrix_exp(const matrix_exp& item); matrix_exp& operator= (const matrix_exp&); const exp_type& ref_; }; // ---------------------------------------------------------------------------------------- // something is a matrix if it is convertible to a matrix_exp object template struct is_matrix& > >::type > { static const bool value = true; }; /* is_matrix::value == 1 if T is a matrix type else 0 */ // ---------------------------------------------------------------------------------------- // This template will perform the needed loop for element multiplication using whichever // dimension is provided as a compile time constant (if one is at all). template < typename LHS, typename RHS, long lhs_nc = LHS::NC, long rhs_nr = RHS::NR > struct matrix_multiply_helper { typedef typename LHS::type type; template inline const static type eval ( const RHS_& rhs, const LHS_& lhs, long r, long c ) { type temp = type(); for (long i = 0; i < rhs.nr(); ++i) { temp += lhs(r,i)*rhs(i,c); } return temp; } }; template < typename LHS, typename RHS, long lhs_nc > struct matrix_multiply_helper { typedef typename LHS::type type; template inline const static type eval ( const RHS_& rhs, const LHS_& lhs, long r, long c ) { type temp = type(); for (long i = 0; i < lhs.nc(); ++i) { temp += lhs(r,i)*rhs(i,c); } return temp; } }; template class matrix_multiply_exp; template struct matrix_traits > { typedef typename LHS::type type; typedef typename LHS::mem_manager_type mem_manager_type; typedef typename LHS::layout_type layout_type; const static long NR = LHS::NR; const static long NC = RHS::NC; const static bool lhs_is_costly = (LHS::cost > 4)&&(RHS::NC != 1); const static bool rhs_is_costly = (RHS::cost > 4)&&(LHS::NR != 1); // Note that if we decide that one of the matrices is too costly we will evaluate it // into a temporary. Doing this resets its cost back to 1. const static long lhs_cost = ((lhs_is_costly==true)? 1 : (LHS::cost)); const static long rhs_cost = ((rhs_is_costly==true)? 1 : (RHS::cost)); // The cost of evaluating an element of a matrix multiply is the cost of evaluating elements from // RHS and LHS times the number of rows/columns in the RHS/LHS matrix. If we don't know the matrix // dimensions then just assume it is really large. const static long cost = (lhs_cost+rhs_cost)* ((tmax::value!=0)? (tmax::value):(10000)); }; template struct conditional_matrix_temp { typedef typename T::matrix_type type; }; template struct conditional_matrix_temp { typedef T& type; }; template < typename LHS, typename RHS > class matrix_multiply_exp : public matrix_exp > { /*! REQUIREMENTS ON LHS AND RHS - must be matrix_exp objects. !*/ public: typedef typename matrix_traits::type type; typedef typename matrix_traits::mem_manager_type mem_manager_type; const static long NR = matrix_traits::NR; const static long NC = matrix_traits::NC; const static long cost = matrix_traits::cost; typedef typename matrix_traits::layout_type layout_type; const static bool lhs_is_costly = matrix_traits::lhs_is_costly; const static bool rhs_is_costly = matrix_traits::rhs_is_costly; typedef typename conditional_matrix_temp::type LHS_ref_type; typedef typename conditional_matrix_temp::type RHS_ref_type; matrix_multiply_exp ( const matrix_multiply_exp& item ) : matrix_exp(*this), lhs(item.lhs), rhs(item.rhs) {} // This constructor exists simply for the purpose of causing a compile time error if // someone tries to create an instance of this object with the wrong kind of objects. template matrix_multiply_exp (T1,T2); inline matrix_multiply_exp ( const LHS& lhs_, const RHS& rhs_ ) : matrix_exp(*this), lhs(lhs_), rhs(rhs_) { // You are trying to multiply two incompatible matrices together. The number of columns // in the matrix on the left must match the number of rows in the matrix on the right. COMPILE_TIME_ASSERT(LHS::NC == RHS::NR || LHS::NC*RHS::NR == 0); DLIB_ASSERT(lhs.nc() == rhs.nr(), "\tconst matrix_exp operator*(const matrix_exp& lhs, const matrix_exp& rhs)" << "\n\tYou are trying to multiply two incompatible matrices together" << "\n\tlhs.nr(): " << lhs.nr() << "\n\tlhs.nc(): " << lhs.nc() << "\n\trhs.nr(): " << rhs.nr() << "\n\trhs.nc(): " << rhs.nc() << "\n\t&lhs: " << &lhs << "\n\t&rhs: " << &rhs ); // You can't multiply matrices together if they don't both contain the same type of elements. COMPILE_TIME_ASSERT((is_same_type::value == true)); } inline const type operator() ( long r, long c ) const { return matrix_multiply_helper::eval(rhs,lhs,r,c); } inline const type operator() ( long i ) const { return matrix_exp::operator()(i); } long nr ( ) const { return lhs.nr(); } long nc ( ) const { return rhs.nc(); } template bool aliases ( const matrix& item ) const { return lhs.aliases(item) || rhs.aliases(item); } template bool destructively_aliases ( const matrix& item ) const { return aliases(item); } LHS_ref_type lhs; RHS_ref_type rhs; }; template < typename EXP1, typename EXP2 > inline const matrix_multiply_exp operator* ( const matrix_exp& m1, const matrix_exp& m2 ) { return matrix_multiply_exp(m1.ref(), m2.ref()); } // ---------------------------------------------------------------------------------------- template class matrix_add_exp; template struct matrix_traits > { typedef typename LHS::type type; typedef typename LHS::mem_manager_type mem_manager_type; typedef typename LHS::layout_type layout_type; const static long NR = (RHS::NR > LHS::NR) ? RHS::NR : LHS::NR; const static long NC = (RHS::NC > LHS::NC) ? RHS::NC : LHS::NC; const static long cost = LHS::cost+RHS::cost; }; template < typename LHS, typename RHS > class matrix_add_exp : public matrix_exp > { /*! REQUIREMENTS ON LHS AND RHS - must be matrix_exp objects. !*/ public: typedef typename matrix_traits::type type; typedef typename matrix_traits::mem_manager_type mem_manager_type; const static long NR = matrix_traits::NR; const static long NC = matrix_traits::NC; const static long cost = matrix_traits::cost; typedef typename matrix_traits::layout_type layout_type; matrix_add_exp ( const matrix_add_exp& item ) : matrix_exp(*this), lhs(item.lhs), rhs(item.rhs) {} // This constructor exists simply for the purpose of causing a compile time error if // someone tries to create an instance of this object with the wrong kind of objects. template matrix_add_exp (T1,T2); matrix_add_exp ( const LHS& lhs_, const RHS& rhs_ ) : matrix_exp(*this), lhs(lhs_), rhs(rhs_) { // You can only add matrices together if they both have the same number of rows and columns. COMPILE_TIME_ASSERT(LHS::NR == RHS::NR || LHS::NR == 0 || RHS::NR == 0); COMPILE_TIME_ASSERT(LHS::NC == RHS::NC || LHS::NC == 0 || RHS::NC == 0); DLIB_ASSERT(lhs.nc() == rhs.nc() && lhs.nr() == rhs.nr(), "\tconst matrix_exp operator+(const matrix_exp& lhs, const matrix_exp& rhs)" << "\n\tYou are trying to add two incompatible matrices together" << "\n\tlhs.nr(): " << lhs.nr() << "\n\tlhs.nc(): " << lhs.nc() << "\n\trhs.nr(): " << rhs.nr() << "\n\trhs.nc(): " << rhs.nc() << "\n\t&lhs: " << &lhs << "\n\t&rhs: " << &rhs ); // You can only add matrices together if they both contain the same types of elements. COMPILE_TIME_ASSERT((is_same_type::value == true)); } const type operator() ( long r, long c ) const { return lhs(r,c) + rhs(r,c); } inline const type operator() ( long i ) const { return matrix_exp::operator()(i); } template bool aliases ( const matrix& item ) const { return lhs.aliases(item) || rhs.aliases(item); } template bool destructively_aliases ( const matrix& item ) const { return lhs.destructively_aliases(item) || rhs.destructively_aliases(item); } long nr ( ) const { return lhs.nr(); } long nc ( ) const { return lhs.nc(); } const LHS& lhs; const RHS& rhs; }; template < typename EXP1, typename EXP2 > inline const matrix_add_exp operator+ ( const matrix_exp& m1, const matrix_exp& m2 ) { return matrix_add_exp(m1.ref(),m2.ref()); } // ---------------------------------------------------------------------------------------- template class matrix_subtract_exp; template struct matrix_traits > { typedef typename LHS::type type; typedef typename LHS::mem_manager_type mem_manager_type; typedef typename LHS::layout_type layout_type; const static long NR = (RHS::NR > LHS::NR) ? RHS::NR : LHS::NR; const static long NC = (RHS::NC > LHS::NC) ? RHS::NC : LHS::NC; const static long cost = LHS::cost+RHS::cost; }; template < typename LHS, typename RHS > class matrix_subtract_exp : public matrix_exp > { /*! REQUIREMENTS ON LHS AND RHS - must be matrix_exp objects. !*/ public: typedef typename matrix_traits::type type; typedef typename matrix_traits::mem_manager_type mem_manager_type; const static long NR = matrix_traits::NR; const static long NC = matrix_traits::NC; const static long cost = matrix_traits::cost; typedef typename matrix_traits::layout_type layout_type; matrix_subtract_exp ( const matrix_subtract_exp& item ) : matrix_exp(*this), lhs(item.lhs), rhs(item.rhs) {} // This constructor exists simply for the purpose of causing a compile time error if // someone tries to create an instance of this object with the wrong kind of objects. template matrix_subtract_exp (T1,T2); matrix_subtract_exp ( const LHS& lhs_, const RHS& rhs_ ) : matrix_exp(*this), lhs(lhs_), rhs(rhs_) { // You can only subtract one matrix from another if they both have the same number of rows and columns. COMPILE_TIME_ASSERT(LHS::NR == RHS::NR || LHS::NR == 0 || RHS::NR == 0); COMPILE_TIME_ASSERT(LHS::NC == RHS::NC || LHS::NC == 0 || RHS::NC == 0); DLIB_ASSERT(lhs.nc() == rhs.nc() && lhs.nr() == rhs.nr(), "\tconst matrix_exp operator-(const matrix_exp& lhs, const matrix_exp& rhs)" << "\n\tYou are trying to add two incompatible matrices together" << "\n\tlhs.nr(): " << lhs.nr() << "\n\tlhs.nc(): " << lhs.nc() << "\n\trhs.nr(): " << rhs.nr() << "\n\trhs.nc(): " << rhs.nc() << "\n\t&lhs: " << &lhs << "\n\t&rhs: " << &rhs ); // You can only subtract one matrix from another if they both contain elements of the same type. COMPILE_TIME_ASSERT((is_same_type::value == true)); } const type operator() ( long r, long c ) const { return lhs(r,c) - rhs(r,c); } inline const type operator() ( long i ) const { return matrix_exp::operator()(i); } template bool aliases ( const matrix& item ) const { return lhs.aliases(item) || rhs.aliases(item); } template bool destructively_aliases ( const matrix& item ) const { return lhs.destructively_aliases(item) || rhs.destructively_aliases(item); } long nr ( ) const { return lhs.nr(); } long nc ( ) const { return lhs.nc(); } const LHS& lhs; const RHS& rhs; }; template < typename EXP1, typename EXP2 > inline const matrix_subtract_exp operator- ( const matrix_exp& m1, const matrix_exp& m2 ) { return matrix_subtract_exp(m1.ref(),m2.ref()); } // ---------------------------------------------------------------------------------------- template class matrix_div_scal_exp; template struct matrix_traits > { typedef typename M::type type; typedef typename M::mem_manager_type mem_manager_type; typedef typename M::layout_type layout_type; const static long NR = M::NR; const static long NC = M::NC; const static long cost = M::cost+1; }; template < typename M > class matrix_div_scal_exp : public matrix_exp > { /*! REQUIREMENTS ON M - must be a matrix_exp object. !*/ public: typedef typename matrix_traits::type type; typedef typename matrix_traits::mem_manager_type mem_manager_type; const static long NR = matrix_traits::NR; const static long NC = matrix_traits::NC; const static long cost = matrix_traits::cost; typedef typename matrix_traits::layout_type layout_type; matrix_div_scal_exp ( const matrix_div_scal_exp& item ) : matrix_exp(*this), m(item.m), s(item.s) {} // This constructor exists simply for the purpose of causing a compile time error if // someone tries to create an instance of this object with the wrong kind of objects. template matrix_div_scal_exp (T1, const type&); matrix_div_scal_exp ( const M& m_, const type& s_ ) : matrix_exp(*this), m(m_), s(s_) {} const type operator() ( long r, long c ) const { return m(r,c)/s; } inline const type operator() ( long i ) const { return matrix_exp::operator()(i); } template bool aliases ( const matrix& item ) const { return m.aliases(item); } template bool destructively_aliases ( const matrix& item ) const { return m.destructively_aliases(item); } long nr ( ) const { return m.nr(); } long nc ( ) const { return m.nc(); } const M& m; const type s; }; template < typename EXP, typename S > inline const matrix_div_scal_exp operator/ ( const matrix_exp& m, const S& s ) { return matrix_div_scal_exp(m.ref(),s); } // ---------------------------------------------------------------------------------------- template class matrix_mul_scal_exp; template struct matrix_traits > { typedef typename M::type type; typedef typename M::mem_manager_type mem_manager_type; typedef typename M::layout_type layout_type; const static long NR = M::NR; const static long NC = M::NC; const static long cost = M::cost+1; }; template < typename M > class matrix_mul_scal_exp : public matrix_exp > { /*! REQUIREMENTS ON M - must be a matrix_exp object. !*/ public: typedef typename matrix_traits::type type; typedef typename matrix_traits::mem_manager_type mem_manager_type; const static long NR = matrix_traits::NR; const static long NC = matrix_traits::NC; const static long cost = matrix_traits::cost; typedef typename matrix_traits::layout_type layout_type; matrix_mul_scal_exp ( const matrix_mul_scal_exp& item ) : matrix_exp(*this), m(item.m), s(item.s) {} // This constructor exists simply for the purpose of causing a compile time error if // someone tries to create an instance of this object with the wrong kind of objects. template matrix_mul_scal_exp (T1, const type&); matrix_mul_scal_exp ( const M& m_, const type& s_ ) : matrix_exp(*this), m(m_), s(s_) {} const type operator() ( long r, long c ) const { return m(r,c)*s; } inline const type operator() ( long i ) const { return matrix_exp::operator()(i); } template bool aliases ( const matrix& item ) const { return m.aliases(item); } template bool destructively_aliases ( const matrix& item ) const { return m.destructively_aliases(item); } long nr ( ) const { return m.nr(); } long nc ( ) const { return m.nc(); } const M& m; const type s; }; template < typename EXP, typename S > inline typename disable_if, const matrix_mul_scal_exp >::type operator* ( const matrix_exp& m, const S& s ) { return matrix_mul_scal_exp(m.ref(),s); } template < typename EXP, typename S > inline typename disable_if, const matrix_mul_scal_exp >::type operator* ( const S& s, const matrix_exp& m ) { return matrix_mul_scal_exp(m.ref(),s); } template < typename EXP > inline const matrix_mul_scal_exp operator/ ( const matrix_exp& m, const float& s ) { return matrix_mul_scal_exp(m.ref(),1.0f/s); } template < typename EXP > inline const matrix_mul_scal_exp operator/ ( const matrix_exp& m, const double& s ) { return matrix_mul_scal_exp(m.ref(),1.0/s); } template < typename EXP > inline const matrix_mul_scal_exp operator/ ( const matrix_exp& m, const long double& s ) { return matrix_mul_scal_exp(m.ref(),1.0/s); } template < typename EXP > inline const matrix_mul_scal_exp operator- ( const matrix_exp& m ) { return matrix_mul_scal_exp(m.ref(),-1); } // ---------------------------------------------------------------------------------------- template < typename EXP1, typename EXP2 > bool operator== ( const matrix_exp& m1, const matrix_exp& m2 ) { if (m1.nr() == m2.nr() && m1.nc() == m2.nc()) { for (long r = 0; r < m1.nr(); ++r) { for (long c = 0; c < m1.nc(); ++c) { if (m1(r,c) != m2(r,c)) return false; } } return true; } return false; } template < typename EXP1, typename EXP2 > inline bool operator!= ( const matrix_exp& m1, const matrix_exp& m2 ) { return !(m1 == m2); } // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- template < typename T, long num_rows, long num_cols, typename mem_manager, typename layout > struct matrix_traits > { typedef T type; typedef mem_manager mem_manager_type; typedef layout layout_type; const static long NR = num_rows; const static long NC = num_cols; const static long cost = 1; }; template < typename T, long num_rows, long num_cols, typename mem_manager, typename layout > class matrix : public matrix_exp > { COMPILE_TIME_ASSERT(num_rows >= 0 && num_cols >= 0); public: typedef typename matrix_traits::type type; typedef typename matrix_traits::mem_manager_type mem_manager_type; typedef typename matrix_traits::layout_type layout_type; const static long NR = matrix_traits::NR; const static long NC = matrix_traits::NC; const static long cost = matrix_traits::cost; matrix () : matrix_exp(*this) { } explicit matrix ( long length ) : matrix_exp(*this) { // This object you are trying to call matrix(length) on is not a column or // row vector. COMPILE_TIME_ASSERT(NR == 1 || NC == 1); DLIB_ASSERT( length >= 0, "\tmatrix::matrix(length)" << "\n\tlength must be at least 0" << "\n\tlength: " << length << "\n\tNR: " << NR << "\n\tNC: " << NC << "\n\tthis: " << this ); if (NR == 1) { DLIB_ASSERT(NC == 0 || NC == length, "\tmatrix::matrix(length)" << "\n\tSince this is a statically sized matrix length must equal NC" << "\n\tlength: " << length << "\n\tNR: " << NR << "\n\tNC: " << NC << "\n\tthis: " << this ); data.set_size(1,length); } else { DLIB_ASSERT(NR == 0 || NR == length, "\tvoid matrix::set_size(length)" << "\n\tSince this is a statically sized matrix length must equal NR" << "\n\tlength: " << length << "\n\tNR: " << NR << "\n\tNC: " << NC << "\n\tthis: " << this ); data.set_size(length,1); } } matrix ( long rows, long cols ) : matrix_exp(*this) { DLIB_ASSERT( (NR == 0 || NR == rows) && ( NC == 0 || NC == cols) && rows >= 0 && cols >= 0, "\tvoid matrix::matrix(rows, cols)" << "\n\tYou have supplied conflicting matrix dimensions" << "\n\trows: " << rows << "\n\tcols: " << cols << "\n\tNR: " << NR << "\n\tNC: " << NC ); data.set_size(rows,cols); } template matrix ( const matrix_exp& m ): matrix_exp(*this) { // You get an error on this line if the matrix m contains a type that isn't // the same as the type contained in the target matrix. COMPILE_TIME_ASSERT((is_same_type::value == true) || (is_matrix::value == true)); // The matrix you are trying to assign m to is a statically sized matrix and // m's dimensions don't match that of *this. COMPILE_TIME_ASSERT(EXP::NR == NR || NR == 0 || EXP::NR == 0); COMPILE_TIME_ASSERT(EXP::NC == NC || NC == 0 || EXP::NC == 0); DLIB_ASSERT((NR == 0 || NR == m.nr()) && (NC == 0 || NC == m.nc()), "\tmatrix& matrix::matrix(const matrix_exp& m)" << "\n\tYou are trying to assign a dynamically sized matrix to a statically sized matrix with the wrong size" << "\n\tNR: " << NR << "\n\tNC: " << NC << "\n\tm.nr(): " << m.nr() << "\n\tm.nc(): " << m.nc() << "\n\tthis: " << this ); data.set_size(m.nr(),m.nc()); matrix_assign(*this, m); } matrix ( const matrix& m ): matrix_exp(*this) { data.set_size(m.nr(),m.nc()); matrix_assign(*this, m); } template matrix ( U (&array)[len] ): matrix_exp(*this) { COMPILE_TIME_ASSERT(NR*NC == len && len > 0); size_t idx = 0; for (long r = 0; r < NR; ++r) { for (long c = 0; c < NC; ++c) { data(r,c) = static_cast(array[idx]); ++idx; } } } T& operator() ( long r, long c ) { DLIB_ASSERT(r < nr() && c < nc() && r >= 0 && c >= 0, "\tT& matrix::operator(r,c)" << "\n\tYou must give a valid row and column" << "\n\tr: " << r << "\n\tc: " << c << "\n\tnr(): " << nr() << "\n\tnc(): " << nc() << "\n\tthis: " << this ); return data(r,c); } const T& operator() ( long r, long c ) const { DLIB_ASSERT(r < nr() && c < nc() && r >= 0 && c >= 0, "\tconst T& matrix::operator(r,c)" << "\n\tYou must give a valid row and column" << "\n\tr: " << r << "\n\tc: " << c << "\n\tnr(): " << nr() << "\n\tnc(): " << nc() << "\n\tthis: " << this ); return data(r,c); } T& operator() ( long i ) { // You can only use this operator on column vectors. COMPILE_TIME_ASSERT(NC == 1 || NC == 0 || NR == 1 || NR == 0); DLIB_ASSERT(nc() == 1 || nr() == 1, "\tconst type matrix::operator(i)" << "\n\tYou can only use this operator on column or row vectors" << "\n\ti: " << i << "\n\tnr(): " << nr() << "\n\tnc(): " << nc() << "\n\tthis: " << this ); DLIB_ASSERT( ((nc() == 1 && i < nr()) || (nr() == 1 && i < nc())) && i >= 0, "\tconst type matrix::operator(i)" << "\n\tYou must give a valid row/column number" << "\n\ti: " << i << "\n\tnr(): " << nr() << "\n\tnc(): " << nc() << "\n\tthis: " << this ); return data(i); } const T& operator() ( long i ) const { // You can only use this operator on column vectors. COMPILE_TIME_ASSERT(NC == 1 || NC == 0 || NR == 1 || NR == 0); DLIB_ASSERT(nc() == 1 || nr() == 1, "\tconst type matrix::operator(i)" << "\n\tYou can only use this operator on column or row vectors" << "\n\ti: " << i << "\n\tnr(): " << nr() << "\n\tnc(): " << nc() << "\n\tthis: " << this ); DLIB_ASSERT( ((nc() == 1 && i < nr()) || (nr() == 1 && i < nc())) && i >= 0, "\tconst type matrix::operator(i)" << "\n\tYou must give a valid row/column number" << "\n\ti: " << i << "\n\tnr(): " << nr() << "\n\tnc(): " << nc() << "\n\tthis: " << this ); return data(i); } inline operator const type ( ) const { COMPILE_TIME_ASSERT(NC == 1 || NC == 0); COMPILE_TIME_ASSERT(NR == 1 || NR == 0); DLIB_ASSERT( nr() == 1 && nc() == 1 , "\tmatrix::operator const type" << "\n\tYou can only attempt to implicit convert a matrix to a scalar if" << "\n\tthe matrix is a 1x1 matrix" << "\n\tnr(): " << nr() << "\n\tnc(): " << nc() << "\n\tthis: " << this ); return data(0); } void set_size ( long rows, long cols ) { DLIB_ASSERT( (NR == 0 || NR == rows) && ( NC == 0 || NC == cols) && rows >= 0 && cols >= 0, "\tvoid matrix::set_size(rows, cols)" << "\n\tYou have supplied conflicting matrix dimensions" << "\n\trows: " << rows << "\n\tcols: " << cols << "\n\tNR: " << NR << "\n\tNC: " << NC << "\n\tthis: " << this ); if (nr() != rows || nc() != cols) data.set_size(rows,cols); } void set_size ( long length ) { // This object you are trying to call set_size(length) on is not a column or // row vector. COMPILE_TIME_ASSERT(NR == 1 || NC == 1); DLIB_ASSERT( length >= 0, "\tvoid matrix::set_size(length)" << "\n\tlength must be at least 0" << "\n\tlength: " << length << "\n\tNR: " << NR << "\n\tNC: " << NC << "\n\tthis: " << this ); if (NR == 1) { DLIB_ASSERT(NC == 0 || NC == length, "\tvoid matrix::set_size(length)" << "\n\tSince this is a statically sized matrix length must equal NC" << "\n\tlength: " << length << "\n\tNR: " << NR << "\n\tNC: " << NC << "\n\tthis: " << this ); if (nc() != length) data.set_size(1,length); } else { DLIB_ASSERT(NR == 0 || NR == length, "\tvoid matrix::set_size(length)" << "\n\tSince this is a statically sized matrix length must equal NR" << "\n\tlength: " << length << "\n\tNR: " << NR << "\n\tNC: " << NC << "\n\tthis: " << this ); if (nr() != length) data.set_size(length,1); } } long nr ( ) const { return data.nr(); } long nc ( ) const { return data.nc(); } long size ( ) const { return data.nr()*data.nc(); } template matrix& operator= ( U (&array)[len] ) { COMPILE_TIME_ASSERT(NR*NC == len && len > 0); size_t idx = 0; for (long r = 0; r < NR; ++r) { for (long c = 0; c < NC; ++c) { data(r,c) = static_cast(array[idx]); ++idx; } } return *this; } template matrix& operator= ( const matrix_exp& m ) { // You get an error on this line if the matrix you are trying to // assign m to is a statically sized matrix and m's dimensions don't // match that of *this. COMPILE_TIME_ASSERT(EXP::NR == NR || NR == 0 || EXP::NR == 0); COMPILE_TIME_ASSERT(EXP::NC == NC || NC == 0 || EXP::NC == 0); DLIB_ASSERT((NR == 0 || nr() == m.nr()) && (NC == 0 || nc() == m.nc()), "\tmatrix& matrix::operator=(const matrix_exp& m)" << "\n\tYou are trying to assign a dynamically sized matrix to a statically sized matrix with the wrong size" << "\n\tnr(): " << nr() << "\n\tnc(): " << nc() << "\n\tm.nr(): " << m.nr() << "\n\tm.nc(): " << m.nc() << "\n\tthis: " << this ); // You get an error on this line if the matrix m contains a type that isn't // the same as the type contained in the target matrix. COMPILE_TIME_ASSERT((is_same_type::value == true) || (is_matrix::value == true)); if (m.destructively_aliases(*this) == false) { set_size(m.nr(),m.nc()); matrix_assign(*this, m); } else { // we have to use a temporary matrix object here because // *this is aliased inside the matrix_exp m somewhere. matrix temp; temp.set_size(m.nr(),m.nc()); matrix_assign(temp, m); temp.swap(*this); } return *this; } template matrix& operator += ( const matrix_exp& m ) { // The matrix you are trying to assign m to is a statically sized matrix and // m's dimensions don't match that of *this. COMPILE_TIME_ASSERT(EXP::NR == NR || NR == 0 || EXP::NR == 0); COMPILE_TIME_ASSERT(EXP::NC == NC || NC == 0 || EXP::NC == 0); DLIB_ASSERT(this->nr() == m.nr() && this->nc() == m.nc(), "\tmatrix& matrix::operator+=(const matrix_exp& m)" << "\n\tYou are trying to add a dynamically sized matrix to a statically sized matrix with the wrong size" << "\n\tthis->nr(): " << nr() << "\n\tthis->nc(): " << nc() << "\n\tm.nr(): " << m.nr() << "\n\tm.nc(): " << m.nc() << "\n\tthis: " << this ); COMPILE_TIME_ASSERT((is_same_type::value == true)); if (m.destructively_aliases(*this) == false) { matrix_assign(*this, m + *this); } else { // we have to use a temporary matrix object here because // this->data is aliased inside the matrix_exp m somewhere. matrix temp; temp.set_size(m.nr(),m.nc()); matrix_assign(temp, m + *this); temp.swap(*this); } return *this; } template matrix& operator -= ( const matrix_exp& m ) { // The matrix you are trying to assign m to is a statically sized matrix and // m's dimensions don't match that of *this. COMPILE_TIME_ASSERT(EXP::NR == NR || NR == 0 || EXP::NR == 0); COMPILE_TIME_ASSERT(EXP::NC == NC || NC == 0 || EXP::NC == 0); DLIB_ASSERT(this->nr() == m.nr() && this->nc() == m.nc(), "\tmatrix& matrix::operator-=(const matrix_exp& m)" << "\n\tYou are trying to subtract a dynamically sized matrix from a statically sized matrix with the wrong size" << "\n\tthis->nr(): " << nr() << "\n\tthis->nc(): " << nc() << "\n\tm.nr(): " << m.nr() << "\n\tm.nc(): " << m.nc() << "\n\tthis: " << this ); COMPILE_TIME_ASSERT((is_same_type::value == true)); if (m.destructively_aliases(*this) == false) { matrix_assign(*this, *this - m); } else { // we have to use a temporary matrix object here because // this->data is aliased inside the matrix_exp m somewhere. matrix temp; temp.set_size(m.nr(),m.nc()); matrix_assign(temp, *this - m); temp.swap(*this); } return *this; } matrix& operator += ( const matrix& m ) { const long size = m.nr()*m.nc(); for (long i = 0; i < size; ++i) data(i) += m.data(i); return *this; } matrix& operator -= ( const matrix& m ) { const long size = m.nr()*m.nc(); for (long i = 0; i < size; ++i) data(i) -= m.data(i); return *this; } matrix& operator *= ( const T& a ) { const long size = data.nr()*data.nc(); for (long i = 0; i < size; ++i) data(i) *= a; return *this; } matrix& operator /= ( const T& a ) { const long size = data.nr()*data.nc(); for (long i = 0; i < size; ++i) data(i) /= a; return *this; } matrix& operator= ( const matrix& m ) { if (this != &m) { set_size(m.nr(),m.nc()); const long size = m.nr()*m.nc(); for (long i = 0; i < size; ++i) data(i) = m.data(i); } return *this; } void swap ( matrix& item ) { data.swap(item.data); } template bool aliases ( const matrix& item ) const { return false; } template bool destructively_aliases ( const matrix& item ) const { return false; } bool aliases ( const matrix& item ) const { return (this == &item); } private: struct literal_assign_helper { /* This struct is a helper struct returned by the operator<<() function below. It is used primarily to enable us to put DLIB_CASSERT statements on the usage of the operator<< form of matrix assignment. */ literal_assign_helper(const literal_assign_helper& item) : m(item.m), r(item.r), c(item.c), has_been_used(false) {} literal_assign_helper(matrix* m_): m(m_), r(0), c(0),has_been_used(false) {next();} ~literal_assign_helper() { DLIB_CASSERT(!has_been_used || r == m->nr(), "You have used the matrix comma based assignment incorrectly by failing to\n" "supply a full set of values for every element of a matrix object.\n"); } const literal_assign_helper& operator, ( const T& val ) const { DLIB_CASSERT(r < m->nr() && c < m->nc(), "You have used the matrix comma based assignment incorrectly by attempting to\n" << "supply more values than there are elements in the matrix object being assigned to.\n\n" << "Did you forget to call set_size()?\n"); (*m)(r,c) = val; next(); has_been_used = true; return *this; } private: void next ( ) const { ++c; if (c == m->nc()) { c = 0; ++r; } } matrix* m; mutable long r; mutable long c; mutable bool has_been_used; }; public: const literal_assign_helper operator = ( const T& val ) { // assign the given value to every spot in this matrix for (long r = 0; r < nr(); ++r) { for (long c = 0; c < nc(); ++c) { data(r,c) = val; } } // Now return the literal_assign_helper so that the user // can use the overloaded comma notation to initialize // the matrix if they want to. return literal_assign_helper(this); } private: typename layout::template layout data; }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- template < typename T, long NR, long NC, typename mm, typename l > void swap( matrix& a, matrix& b ) { a.swap(b); } template < typename T, long NR, long NC, typename mm, typename l > void serialize ( const matrix& item, std::ostream& out ) { try { serialize(item.nr(),out); serialize(item.nc(),out); for (long r = 0; r < item.nr(); ++r) { for (long c = 0; c < item.nc(); ++c) { serialize(item(r,c),out); } } } catch (serialization_error& e) { throw serialization_error(e.info + "\n while serializing dlib::matrix"); } } template < typename T, long NR, long NC, typename mm, typename l > void deserialize ( matrix& item, std::istream& in ) { try { long nr, nc; deserialize(nr,in); deserialize(nc,in); if (NR != 0 && nr != NR) throw serialization_error("Error while deserializing a dlib::matrix. Invalid rows"); if (NC != 0 && nc != NC) throw serialization_error("Error while deserializing a dlib::matrix. Invalid columns"); item.set_size(nr,nc); for (long r = 0; r < nr; ++r) { for (long c = 0; c < nc; ++c) { deserialize(item(r,c),in); } } } catch (serialization_error& e) { throw serialization_error(e.info + "\n while deserializing a dlib::matrix"); } } template < typename EXP > std::ostream& operator<< ( std::ostream& out, const matrix_exp& m ) { using namespace std; const streamsize old = out.width(); // first figure out how wide we should make each field string::size_type w = 0; ostringstream sout; for (long r = 0; r < m.nr(); ++r) { for (long c = 0; c < m.nc(); ++c) { sout << m(r,c); w = std::max(sout.str().size(),w); sout.str(""); } } // now actually print it for (long r = 0; r < m.nr(); ++r) { for (long c = 0; c < m.nc(); ++c) { out.width(static_cast(w)); out << m(r,c) << " "; } out << "\n"; } out.width(old); return out; } // ---------------------------------------------------------------------------------------- } #ifdef _MSC_VER // put that warning back to its default setting #pragma warning(default : 4355) #endif #endif // DLIB_MATRIx_