// Copyright (C) 2006 Davis E. King (davis@dlib.net) // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_MATRIx_ #define DLIB_MATRIx_ #include "matrix_exp.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" #include "matrix_op.h" #include #ifdef DLIB_HAS_INITIALIZER_LISTS #include #endif #ifdef MATLAB_MEX_FILE #include #endif #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) // "warning C4723: potential divide by 0" - This warning is triggered in // matrix(const std::initializer_list& l) where the compiler can see that // matrix<> was templated in a way making NR ending up 0, but division by 0 at runtime // is not possible because the division operation is inside "if (NR!=0)" block. #pragma warning(disable : 4723) #endif namespace dlib { // ---------------------------------------------------------------------------------------- // 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, const long r, const long c ) { type temp = lhs(r,0)*rhs(0,c); for (long i = 1; 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, const long r, const long c ) { type temp = lhs(r,0)*rhs(0,c); for (long i = 1; 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::type const_ret_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; #ifdef DLIB_USE_BLAS // if there are BLAS functions to be called then we want to make sure we // always evaluate any complex expressions so that the BLAS bindings can happen. const static bool lhs_is_costly = (LHS::cost > 2)&&(RHS::NC != 1 || LHS::cost >= 10000); const static bool rhs_is_costly = (RHS::cost > 2)&&(LHS::NR != 1 || RHS::cost >= 10000); #else const static bool lhs_is_costly = (LHS::cost > 4)&&(RHS::NC != 1); const static bool rhs_is_costly = (RHS::cost > 4)&&(LHS::NR != 1); #endif // 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 = ((tmax::value!=0)? ((lhs_cost+rhs_cost)*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::const_ret_type const_ret_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; const static bool either_is_costly = lhs_is_costly || rhs_is_costly; const static bool both_are_costly = lhs_is_costly && rhs_is_costly; typedef typename conditional_matrix_temp::type LHS_ref_type; typedef typename conditional_matrix_temp::type RHS_ref_type; // 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_ ) : 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() && lhs.size() > 0 && rhs.size() > 0, "\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() ( const long r, const 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_exp& item ) const { return lhs.aliases(item) || rhs.aliases(item); } template bool destructively_aliases ( const matrix_exp& 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_mul_scal_exp; // ------------------------- // Now we declare some overloads that cause any scalar multiplications to percolate // up and outside of any matrix multiplies. Note that we are using the non-reference containing // mode of the matrix_mul_scal_exp object since we are passing in locally constructed matrix_multiply_exp // objects. So the matrix_mul_scal_exp object will contain copies of matrix_multiply_exp objects // rather than references to them. This could result in extra matrix copies if the matrix_multiply_exp // decided it should evaluate any of its arguments. So we also try to not apply this percolating operation // if the matrix_multiply_exp would contain a fully evaluated copy of the original matrix_mul_scal_exp // expression. // // Also, the reason we want to apply this transformation in the first place is because it (1) makes // the expressions going into matrix multiply expressions simpler and (2) it makes it a lot more // straightforward to bind BLAS calls to matrix expressions involving scalar multiplies. template < typename EXP1, typename EXP2 > inline const typename disable_if_c< matrix_multiply_exp, matrix_mul_scal_exp >::both_are_costly , matrix_mul_scal_exp,false> >::type operator* ( const matrix_mul_scal_exp& m1, const matrix_mul_scal_exp& m2 ) { typedef matrix_multiply_exp exp1; typedef matrix_mul_scal_exp exp2; return exp2(exp1(m1.m, m2.m), m1.s*m2.s); } template < typename EXP1, typename EXP2 > inline const typename disable_if_c< matrix_multiply_exp, EXP2 >::lhs_is_costly , matrix_mul_scal_exp,false> >::type operator* ( const matrix_mul_scal_exp& m1, const matrix_exp& m2 ) { typedef matrix_multiply_exp exp1; typedef matrix_mul_scal_exp exp2; return exp2(exp1(m1.m, m2.ref()), m1.s); } template < typename EXP1, typename EXP2 > inline const typename disable_if_c< matrix_multiply_exp >::rhs_is_costly , matrix_mul_scal_exp,false> >::type operator* ( const matrix_exp& m1, const matrix_mul_scal_exp& m2 ) { typedef matrix_multiply_exp exp1; typedef matrix_mul_scal_exp exp2; return exp2(exp1(m1.ref(), m2.m), m2.s); } // ---------------------------------------------------------------------------------------- template class matrix_add_exp; template struct matrix_traits > { typedef typename LHS::type type; typedef typename LHS::type const_ret_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+1; }; 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::const_ret_type const_ret_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; // 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_ ) : 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_exp& item ) const { return lhs.aliases(item) || rhs.aliases(item); } template bool destructively_aliases ( const matrix_exp& 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::type const_ret_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+1; }; 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::const_ret_type const_ret_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; // 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_ ) : 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 subtract two incompatible matrices" << "\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_exp& item ) const { return lhs.aliases(item) || rhs.aliases(item); } template bool destructively_aliases ( const matrix_exp& 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::type const_ret_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::const_ret_type const_ret_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; // 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_ ) : 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_exp& item ) const { return m.aliases(item); } template bool destructively_aliases ( const matrix_exp& 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 typename enable_if_c::is_integer, matrix_div_scal_exp >::type operator/ ( const matrix_exp& m, const S& s ) { return matrix_div_scal_exp(m.ref(),static_cast(s)); } // ---------------------------------------------------------------------------------------- template struct matrix_traits > { typedef typename M::type type; typedef typename M::type const_ret_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 struct conditional_reference { typedef T type; }; template struct conditional_reference { typedef T& type; }; template < typename M, bool use_reference > 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::const_ret_type const_ret_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; // You aren't allowed to multiply a matrix of matrices by a scalar. COMPILE_TIME_ASSERT(is_matrix::value == false); // 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_ ) : 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_exp& item ) const { return m.aliases(item); } template bool destructively_aliases ( const matrix_exp& item ) const { return m.destructively_aliases(item); } long nr ( ) const { return m.nr(); } long nc ( ) const { return m.nc(); } typedef typename conditional_reference::type M_ref_type; M_ref_type 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 ) { typedef typename EXP::type type; return matrix_mul_scal_exp(m.ref(),static_cast(s)); } template < typename EXP, typename S, bool B > inline typename disable_if, const matrix_mul_scal_exp >::type operator* ( const matrix_mul_scal_exp& m, const S& s ) { typedef typename EXP::type type; return matrix_mul_scal_exp(m.m,static_cast(s)*m.s); } template < typename EXP, typename S > inline typename disable_if, const matrix_mul_scal_exp >::type operator* ( const S& s, const matrix_exp& m ) { typedef typename EXP::type type; return matrix_mul_scal_exp(m.ref(),static_cast(s)); } template < typename EXP, typename S, bool B > inline typename disable_if, const matrix_mul_scal_exp >::type operator* ( const S& s, const matrix_mul_scal_exp& m ) { typedef typename EXP::type type; return matrix_mul_scal_exp(m.m,static_cast(s)*m.s); } template < typename EXP , typename S > inline const typename disable_if_c::is_integer, matrix_mul_scal_exp >::type operator/ ( const matrix_exp& m, const S& s ) { typedef typename EXP::type type; const type one = 1; return matrix_mul_scal_exp(m.ref(),one/static_cast(s)); } template < typename EXP, bool B, typename S > inline const typename disable_if_c::is_integer, matrix_mul_scal_exp >::type operator/ ( const matrix_mul_scal_exp& m, const S& s ) { typedef typename EXP::type type; return matrix_mul_scal_exp(m.m,m.s/static_cast(s)); } // ---------------------------------------------------------------------------------------- template struct op_s_div_m : basic_op_m { typedef typename M::type type; op_s_div_m( const M& m_, const type& s_) : basic_op_m(m_), s(s_){} const type s; const static long cost = M::cost+1; typedef const typename M::type const_ret_type; const_ret_type apply (long r, long c) const { return s/this->m(r,c); } }; template < typename EXP, typename S > const typename disable_if, matrix_op > >::type operator/ ( const S& val, const matrix_exp& m ) { typedef typename EXP::type type; typedef op_s_div_m op; return matrix_op(op(m.ref(), static_cast(val))); } // ---------------------------------------------------------------------------------------- template < typename EXP > inline const matrix_mul_scal_exp operator- ( const matrix_exp& m ) { return matrix_mul_scal_exp(m.ref(),-1); } template < typename EXP, bool B > inline const matrix_mul_scal_exp operator- ( const matrix_mul_scal_exp& m ) { return matrix_mul_scal_exp(m.m,-1*m.s); } // ---------------------------------------------------------------------------------------- template struct op_add_scalar : basic_op_m { typedef typename M::type type; op_add_scalar( const M& m_, const type& s_) : basic_op_m(m_), s(s_){} const type s; const static long cost = M::cost+1; typedef const typename M::type const_ret_type; const_ret_type apply (long r, long c) const { return this->m(r,c) + s; } }; template < typename EXP, typename T > const typename disable_if, matrix_op > >::type operator+ ( const matrix_exp& m, const T& val ) { typedef typename EXP::type type; typedef op_add_scalar op; return matrix_op(op(m.ref(), static_cast(val))); } template < typename EXP, typename T > const typename disable_if, matrix_op > >::type operator+ ( const T& val, const matrix_exp& m ) { typedef typename EXP::type type; typedef op_add_scalar op; return matrix_op(op(m.ref(), static_cast(val))); } // ---------------------------------------------------------------------------------------- template struct op_subl_scalar : basic_op_m { typedef typename M::type type; op_subl_scalar( const M& m_, const type& s_) : basic_op_m(m_), s(s_){} const type s; const static long cost = M::cost+1; typedef const typename M::type const_ret_type; const_ret_type apply (long r, long c) const { return s - this->m(r,c) ; } }; template < typename EXP, typename T > const typename disable_if, matrix_op > >::type operator- ( const T& val, const matrix_exp& m ) { typedef typename EXP::type type; typedef op_subl_scalar op; return matrix_op(op(m.ref(), static_cast(val))); } // ---------------------------------------------------------------------------------------- template struct op_subr_scalar : basic_op_m { typedef typename M::type type; op_subr_scalar( const M& m_, const type& s_) : basic_op_m(m_), s(s_){} const type s; const static long cost = M::cost+1; typedef const typename M::type const_ret_type; const_ret_type apply (long r, long c) const { return this->m(r,c) - s; } }; template < typename EXP, typename T > const typename disable_if, matrix_op > >::type operator- ( const matrix_exp& m, const T& val ) { typedef typename EXP::type type; typedef op_subr_scalar op; return matrix_op(op(m.ref(), static_cast(val))); } // ---------------------------------------------------------------------------------------- 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 struct op_pointer_to_mat; template struct op_pointer_to_col_vect; template < typename T, long num_rows, long num_cols, typename mem_manager, typename layout > struct matrix_traits > { typedef T type; typedef const T& const_ret_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::const_ret_type const_ret_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 T* iterator; typedef const T* const_iterator; matrix () { } explicit matrix ( long length ) { // 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 ) { 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 ) { // 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); } #ifdef DLIB_HAS_INITIALIZER_LISTS matrix(const std::initializer_list& l) { if (NR*NC != 0) { DLIB_ASSERT(l.size() == NR*NC, "\t matrix::matrix(const std::initializer_list& l)" << "\n\t You are trying to initialize a statically sized matrix with a list that doesn't have a matching size." << "\n\t l.size(): "<< l.size() << "\n\t NR*NC: "<< NR*NC); data.set_size(NR, NC); } else if (NR!=0) { DLIB_ASSERT(l.size()%NR == 0, "\t matrix::matrix(const std::initializer_list& l)" << "\n\t You are trying to initialize a statically sized matrix with a list that doesn't have a compatible size." << "\n\t l.size(): "<< l.size() << "\n\t NR: "<< NR); if (l.size() != 0) data.set_size(NR, l.size()/NR); } else if (NC!=0) { DLIB_ASSERT(l.size()%NC == 0, "\t matrix::matrix(const std::initializer_list& l)" << "\n\t You are trying to initialize a statically sized matrix with a list that doesn't have a compatible size." << "\n\t l.size(): "<< l.size() << "\n\t NC: "<< NC); if (l.size() != 0) data.set_size(l.size()/NC, NC); } else if (l.size() != 0) { data.set_size(l.size(),1); } if (l.size() != 0) { T* d = &data(0,0); for (auto&& v : l) *d++ = v; } } matrix& operator=(const std::initializer_list& l) { matrix temp(l); temp.swap(*this); return *this; } #endif // DLIB_HAS_INITIALIZER_LISTS #ifdef DLIB_HAS_RVALUE_REFERENCES matrix(matrix&& item) { #ifdef MATLAB_MEX_FILE // You can't move memory around when compiled in a matlab mex file and the // different locations have different ownership settings. if (data._private_is_owned_by_matlab() == item.data._private_is_owned_by_matlab()) { swap(item); } else { data.set_size(item.nr(),item.nc()); matrix_assign(*this, item); } #else swap(item); #endif } matrix& operator= ( matrix&& rhs ) { #ifdef MATLAB_MEX_FILE // You can't move memory around when compiled in a matlab mex file and the // different locations have different ownership settings. if (data._private_is_owned_by_matlab() == rhs.data._private_is_owned_by_matlab()) { swap(rhs); } else { data.set_size(rhs.nr(),rhs.nc()); matrix_assign(*this, rhs); } #else swap(rhs); #endif return *this; } #endif // DLIB_HAS_RVALUE_REFERENCES template explicit matrix ( 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; } } } 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( 0 <= i && i < size(), "\tconst type matrix::operator(i)" << "\n\tYou must give a valid row/column number" << "\n\ti: " << i << "\n\tsize(): " << size() << "\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( 0 <= i && i < size(), "\tconst type matrix::operator(i)" << "\n\tYou must give a valid row/column number" << "\n\ti: " << i << "\n\tsize(): " << size() << "\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); } #ifdef MATLAB_MEX_FILE void _private_set_mxArray( mxArray* mem ) { data._private_set_mxArray(mem); } mxArray* _private_release_mxArray( ) { return data._private_release_mxArray(); } void _private_mark_owned_by_matlab() { data._private_mark_owned_by_matlab(); } bool _private_is_owned_by_matlab() { return data._private_is_owned_by_matlab(); } #endif 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) { // This if statement is seemingly unnecessary since set_size() contains this // exact same if statement. However, structuring the code this way causes // gcc to handle the way it inlines this function in a much more favorable way. if (data.nr() == m.nr() && data.nc() == m.nc()) { matrix_assign(*this, m); } else { 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); COMPILE_TIME_ASSERT((is_same_type::value == true)); if (nr() == m.nr() && nc() == m.nc()) { 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); } } else { DLIB_ASSERT(size() == 0, "\t const matrix::operator+=(m)" << "\n\t You are trying to add two matrices that have incompatible dimensions."); *this = m; } 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); COMPILE_TIME_ASSERT((is_same_type::value == true)); if (nr() == m.nr() && nc() == m.nc()) { 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); } } else { DLIB_ASSERT(size() == 0, "\t const matrix::operator-=(m)" << "\n\t You are trying to subtract two matrices that have incompatible dimensions."); *this = -m; } return *this; } template matrix& operator *= ( const matrix_exp& m ) { *this = *this * m; return *this; } matrix& operator += ( const matrix& m ) { const long size = m.nr()*m.nc(); if (nr() == m.nr() && nc() == m.nc()) { for (long i = 0; i < size; ++i) data(i) += m.data(i); } else { DLIB_ASSERT(this->size() == 0, "\t const matrix::operator+=(m)" << "\n\t You are trying to add two matrices that have incompatible dimensions."); set_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(); if (nr() == m.nr() && nc() == m.nc()) { for (long i = 0; i < size; ++i) data(i) -= m.data(i); } else { DLIB_ASSERT(this->size() == 0, "\t const matrix::operator-=(m)" << "\n\t You are trying to subtract two matrices that have incompatible dimensions."); set_size(m.nr(), m.nc()); for (long i = 0; i < size; ++i) data(i) = -m.data(i); } return *this; } matrix& operator += ( const T val ) { const long size = nr()*nc(); for (long i = 0; i < size; ++i) data(i) += val; return *this; } matrix& operator -= ( const T val ) { const long size = nr()*nc(); for (long i = 0; i < size; ++i) data(i) -= val; return *this; } matrix& operator *= ( const T a ) { *this = *this * a; return *this; } matrix& operator /= ( const T a ) { *this = *this / 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_exp& ) const { return false; } bool aliases ( const matrix_exp >& item ) const { return (this == &item); } template bool destructively_aliases ( const matrix_exp& ) const { return false; } // These two aliases() routines are defined in matrix_mat.h bool aliases ( const matrix_exp > >& item ) const; bool aliases ( const matrix_exp > >& item ) const; iterator begin() { if (size() != 0) return &data(0,0); else return 0; } iterator end() { if (size() != 0) return &data(0,0)+size(); else return 0; } const_iterator begin() const { if (size() != 0) return &data(0,0); else return 0; } const_iterator end() const { if (size() != 0) return &data(0,0)+size(); else return 0; } 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) {} explicit literal_assign_helper(matrix* m_): m(m_), r(0), c(0),has_been_used(false) {next();} ~literal_assign_helper() noexcept(false) { 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\t r: " << r << "\n\t c: " << c << "\n\t m->nr(): " << m->nr() << "\n\t m->nc(): " << m->nc()); (*m)(r,c) = val; next(); has_been_used = true; return *this; } private: friend class matrix; 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: matrix& operator = ( const literal_assign_helper& val ) { *this = *val.m; return *this; } const literal_assign_helper operator = ( const T& val ) { // assign the given value to every spot in this matrix const long size = nr()*nc(); for (long i = 0; i < size; ++i) data(i) = 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 { // The reason the serialization is a little funny is because we are trying to // maintain backwards compatibility with an older serialization format used by // dlib while also encoding things in a way that lets the array2d and matrix // objects have compatible serialization formats. 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); // this is the newer serialization format if (nr < 0 || nc < 0) { nr *= -1; nc *= -1; } 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 T, long NR, long NC, typename mm, typename l > void serialize ( const ramdump_t>& item_, std::ostream& out ) { auto& item = item_.item; serialize(item.nr(), out); serialize(item.nc(), out); if (item.size() != 0) out.write((char*)&item(0,0), sizeof(item(0,0))*item.size()); } template < typename T, long NR, long NC, typename mm, typename l > void deserialize ( ramdump_t>&& item_, std::istream& in ) { auto& item = item_.item; long nr, nc; deserialize(nr, in); deserialize(nc, in); item.set_size(nr,nc); if (item.size() != 0) in.read((char*)&item(0,0), sizeof(item(0,0))*item.size()); } // ---------------------------------------------------------------------------------------- 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; } /* template < typename T, long NR, long NC, typename MM, typename L > std::istream& operator>> ( std::istream& in, matrix& m ); This function is defined inside the matrix_read_from_istream.h file. */ // ---------------------------------------------------------------------------------------- class print_matrix_as_csv_helper { /*! This object is used to define an io manipulator for matrix expressions. In particular, this code allows you to write statements like: cout << csv << yourmatrix; and have it print the matrix with commas separating each element. !*/ public: print_matrix_as_csv_helper (std::ostream& out_) : out(out_) {} template std::ostream& operator<< ( const matrix_exp& m ) { for (long r = 0; r < m.nr(); ++r) { for (long c = 0; c < m.nc(); ++c) { if (c+1 == m.nc()) out << m(r,c) << "\n"; else out << m(r,c) << ", "; } } return out; } private: std::ostream& out; }; class print_matrix_as_csv {}; const print_matrix_as_csv csv = print_matrix_as_csv(); inline print_matrix_as_csv_helper operator<< ( std::ostream& out, const print_matrix_as_csv& ) { return print_matrix_as_csv_helper(out); } // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- template class const_temp_matrix; template < typename EXP > struct matrix_traits > { typedef typename EXP::type type; typedef typename EXP::const_ret_type const_ret_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 = 1; }; template class const_temp_matrix : public matrix_exp >, noncopyable { public: typedef typename matrix_traits::type type; typedef typename matrix_traits::const_ret_type const_ret_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; const_temp_matrix ( const matrix_exp& item ) : ref_(item.ref()) {} const_temp_matrix ( const EXP& item ) : ref_(item) {} const_ret_type operator() ( long r, long c ) const { return ref_(r,c); } const_ret_type operator() ( long i ) const { return ref_(i); } template bool aliases ( const matrix_exp& item ) const { return ref_.aliases(item); } template bool destructively_aliases ( const matrix_exp& item ) const { return ref_.destructively_aliases(item); } long nr ( ) const { return ref_.nr(); } long nc ( ) const { return ref_.nc(); } private: typename conditional_matrix_temp::type ref_; }; // ---------------------------------------------------------------------------------------- typedef matrix matrix_colmajor; typedef matrix fmatrix_colmajor; } #ifdef _MSC_VER // put warnings back to their default settings #pragma warning(default : 4355) #pragma warning(default : 4723) #endif #endif // DLIB_MATRIx_