diff options
Diffstat (limited to 'ml/dlib/dlib/matrix/matrix.h')
-rw-r--r-- | ml/dlib/dlib/matrix/matrix.h | 2162 |
1 files changed, 2162 insertions, 0 deletions
diff --git a/ml/dlib/dlib/matrix/matrix.h b/ml/dlib/dlib/matrix/matrix.h new file mode 100644 index 000000000..b16635879 --- /dev/null +++ b/ml/dlib/dlib/matrix/matrix.h @@ -0,0 +1,2162 @@ +// 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 <sstream> +#include <algorithm> +#include "../memory_manager.h" +#include "../is_kind.h" +#include "matrix_data_layout.h" +#include "matrix_assign_fwd.h" +#include "matrix_op.h" +#include <utility> +#ifdef DLIB_HAS_INITIALIZER_LISTS +#include <initializer_list> +#endif + +#ifdef MATLAB_MEX_FILE +#include <mex.h> +#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<T>& 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 <typename RHS_, typename LHS_> + 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 <LHS,RHS,lhs_nc,0> + { + typedef typename LHS::type type; + template <typename RHS_, typename LHS_> + 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 <typename LHS, typename RHS> + class matrix_multiply_exp; + + template <typename LHS, typename RHS> + struct matrix_traits<matrix_multiply_exp<LHS,RHS> > + { + 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<LHS::NC,RHS::NR>::value!=0)? ((lhs_cost+rhs_cost)*tmax<LHS::NC,RHS::NR>::value):(10000)); + }; + + template <typename T, bool is_ref> struct conditional_matrix_temp { typedef typename T::matrix_type type; }; + template <typename T> struct conditional_matrix_temp<T,true> { typedef T& type; }; + + template < + typename LHS, + typename RHS + > + class matrix_multiply_exp : public matrix_exp<matrix_multiply_exp<LHS,RHS> > + { + /*! + REQUIREMENTS ON LHS AND RHS + - must be matrix_exp objects. + !*/ + public: + + typedef typename matrix_traits<matrix_multiply_exp>::type type; + typedef typename matrix_traits<matrix_multiply_exp>::const_ret_type const_ret_type; + typedef typename matrix_traits<matrix_multiply_exp>::mem_manager_type mem_manager_type; + const static long NR = matrix_traits<matrix_multiply_exp>::NR; + const static long NC = matrix_traits<matrix_multiply_exp>::NC; + const static long cost = matrix_traits<matrix_multiply_exp>::cost; + typedef typename matrix_traits<matrix_multiply_exp>::layout_type layout_type; + + + const static bool lhs_is_costly = matrix_traits<matrix_multiply_exp>::lhs_is_costly; + const static bool rhs_is_costly = matrix_traits<matrix_multiply_exp>::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<const LHS,lhs_is_costly == false>::type LHS_ref_type; + typedef typename conditional_matrix_temp<const RHS,rhs_is_costly == false>::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 <typename T1, typename T2> + 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<typename LHS::type, typename RHS::type>::value == true)); + } + + inline const type operator() ( + const long r, + const long c + ) const + { + return matrix_multiply_helper<LHS,RHS>::eval(rhs,lhs,r,c); + } + + inline const type operator() ( long i ) const + { return matrix_exp<matrix_multiply_exp>::operator()(i); } + + long nr ( + ) const { return lhs.nr(); } + + long nc ( + ) const { return rhs.nc(); } + + template <typename U> + bool aliases ( + const matrix_exp<U>& item + ) const { return lhs.aliases(item) || rhs.aliases(item); } + + template <typename U> + bool destructively_aliases ( + const matrix_exp<U>& item + ) const { return aliases(item); } + + LHS_ref_type lhs; + RHS_ref_type rhs; + }; + + template < typename EXP1, typename EXP2 > + inline const matrix_multiply_exp<EXP1, EXP2> operator* ( + const matrix_exp<EXP1>& m1, + const matrix_exp<EXP2>& m2 + ) + { + return matrix_multiply_exp<EXP1, EXP2>(m1.ref(), m2.ref()); + } + + template <typename M, bool use_reference = true> + 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<EXP1>, matrix_mul_scal_exp<EXP2> >::both_are_costly , + matrix_mul_scal_exp<matrix_multiply_exp<EXP1, EXP2>,false> >::type operator* ( + const matrix_mul_scal_exp<EXP1>& m1, + const matrix_mul_scal_exp<EXP2>& m2 + ) + { + typedef matrix_multiply_exp<EXP1, EXP2> exp1; + typedef matrix_mul_scal_exp<exp1,false> 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<matrix_mul_scal_exp<EXP1>, EXP2 >::lhs_is_costly , + matrix_mul_scal_exp<matrix_multiply_exp<EXP1, EXP2>,false> >::type operator* ( + const matrix_mul_scal_exp<EXP1>& m1, + const matrix_exp<EXP2>& m2 + ) + { + typedef matrix_multiply_exp<EXP1, EXP2> exp1; + typedef matrix_mul_scal_exp<exp1,false> exp2; + return exp2(exp1(m1.m, m2.ref()), m1.s); + } + + template < typename EXP1, typename EXP2 > + inline const typename disable_if_c< matrix_multiply_exp<EXP1, matrix_mul_scal_exp<EXP2> >::rhs_is_costly , + matrix_mul_scal_exp<matrix_multiply_exp<EXP1, EXP2>,false> >::type operator* ( + const matrix_exp<EXP1>& m1, + const matrix_mul_scal_exp<EXP2>& m2 + ) + { + typedef matrix_multiply_exp<EXP1, EXP2> exp1; + typedef matrix_mul_scal_exp<exp1,false> exp2; + return exp2(exp1(m1.ref(), m2.m), m2.s); + } + +// ---------------------------------------------------------------------------------------- + + template <typename LHS, typename RHS> + class matrix_add_exp; + + template <typename LHS, typename RHS> + struct matrix_traits<matrix_add_exp<LHS,RHS> > + { + 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<matrix_add_exp<LHS,RHS> > + { + /*! + REQUIREMENTS ON LHS AND RHS + - must be matrix_exp objects. + !*/ + public: + typedef typename matrix_traits<matrix_add_exp>::type type; + typedef typename matrix_traits<matrix_add_exp>::const_ret_type const_ret_type; + typedef typename matrix_traits<matrix_add_exp>::mem_manager_type mem_manager_type; + const static long NR = matrix_traits<matrix_add_exp>::NR; + const static long NC = matrix_traits<matrix_add_exp>::NC; + const static long cost = matrix_traits<matrix_add_exp>::cost; + typedef typename matrix_traits<matrix_add_exp>::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 <typename T1, typename T2> + 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<typename LHS::type, typename RHS::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<matrix_add_exp>::operator()(i); } + + template <typename U> + bool aliases ( + const matrix_exp<U>& item + ) const { return lhs.aliases(item) || rhs.aliases(item); } + + template <typename U> + bool destructively_aliases ( + const matrix_exp<U>& 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<EXP1, EXP2> operator+ ( + const matrix_exp<EXP1>& m1, + const matrix_exp<EXP2>& m2 + ) + { + return matrix_add_exp<EXP1, EXP2>(m1.ref(),m2.ref()); + } + +// ---------------------------------------------------------------------------------------- + + template <typename LHS, typename RHS> + class matrix_subtract_exp; + + template <typename LHS, typename RHS> + struct matrix_traits<matrix_subtract_exp<LHS,RHS> > + { + 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<matrix_subtract_exp<LHS,RHS> > + { + /*! + REQUIREMENTS ON LHS AND RHS + - must be matrix_exp objects. + !*/ + public: + typedef typename matrix_traits<matrix_subtract_exp>::type type; + typedef typename matrix_traits<matrix_subtract_exp>::const_ret_type const_ret_type; + typedef typename matrix_traits<matrix_subtract_exp>::mem_manager_type mem_manager_type; + const static long NR = matrix_traits<matrix_subtract_exp>::NR; + const static long NC = matrix_traits<matrix_subtract_exp>::NC; + const static long cost = matrix_traits<matrix_subtract_exp>::cost; + typedef typename matrix_traits<matrix_subtract_exp>::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 <typename T1, typename T2> + 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<typename LHS::type, typename RHS::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<matrix_subtract_exp>::operator()(i); } + + template <typename U> + bool aliases ( + const matrix_exp<U>& item + ) const { return lhs.aliases(item) || rhs.aliases(item); } + + template <typename U> + bool destructively_aliases ( + const matrix_exp<U>& 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<EXP1, EXP2> operator- ( + const matrix_exp<EXP1>& m1, + const matrix_exp<EXP2>& m2 + ) + { + return matrix_subtract_exp<EXP1, EXP2>(m1.ref(),m2.ref()); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + class matrix_div_scal_exp; + + template <typename M> + struct matrix_traits<matrix_div_scal_exp<M> > + { + 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<matrix_div_scal_exp<M> > + { + /*! + REQUIREMENTS ON M + - must be a matrix_exp object. + !*/ + public: + typedef typename matrix_traits<matrix_div_scal_exp>::type type; + typedef typename matrix_traits<matrix_div_scal_exp>::const_ret_type const_ret_type; + typedef typename matrix_traits<matrix_div_scal_exp>::mem_manager_type mem_manager_type; + const static long NR = matrix_traits<matrix_div_scal_exp>::NR; + const static long NC = matrix_traits<matrix_div_scal_exp>::NC; + const static long cost = matrix_traits<matrix_div_scal_exp>::cost; + typedef typename matrix_traits<matrix_div_scal_exp>::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 <typename T1> + 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<matrix_div_scal_exp>::operator()(i); } + + template <typename U> + bool aliases ( + const matrix_exp<U>& item + ) const { return m.aliases(item); } + + template <typename U> + bool destructively_aliases ( + const matrix_exp<U>& 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<std::numeric_limits<typename EXP::type>::is_integer, matrix_div_scal_exp<EXP> >::type operator/ ( + const matrix_exp<EXP>& m, + const S& s + ) + { + return matrix_div_scal_exp<EXP>(m.ref(),static_cast<typename EXP::type>(s)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M, bool use_reference > + struct matrix_traits<matrix_mul_scal_exp<M,use_reference> > + { + 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 T, bool is_ref> struct conditional_reference { typedef T type; }; + template <typename T> struct conditional_reference<T,true> { typedef T& type; }; + + + template < + typename M, + bool use_reference + > + class matrix_mul_scal_exp : public matrix_exp<matrix_mul_scal_exp<M,use_reference> > + { + /*! + REQUIREMENTS ON M + - must be a matrix_exp object. + + !*/ + public: + typedef typename matrix_traits<matrix_mul_scal_exp>::type type; + typedef typename matrix_traits<matrix_mul_scal_exp>::const_ret_type const_ret_type; + typedef typename matrix_traits<matrix_mul_scal_exp>::mem_manager_type mem_manager_type; + const static long NR = matrix_traits<matrix_mul_scal_exp>::NR; + const static long NC = matrix_traits<matrix_mul_scal_exp>::NC; + const static long cost = matrix_traits<matrix_mul_scal_exp>::cost; + typedef typename matrix_traits<matrix_mul_scal_exp>::layout_type layout_type; + + // You aren't allowed to multiply a matrix of matrices by a scalar. + COMPILE_TIME_ASSERT(is_matrix<type>::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 <typename T1> + 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<matrix_mul_scal_exp>::operator()(i); } + + template <typename U> + bool aliases ( + const matrix_exp<U>& item + ) const { return m.aliases(item); } + + template <typename U> + bool destructively_aliases ( + const matrix_exp<U>& item + ) const { return m.destructively_aliases(item); } + + long nr ( + ) const { return m.nr(); } + + long nc ( + ) const { return m.nc(); } + + typedef typename conditional_reference<const M,use_reference>::type M_ref_type; + + M_ref_type m; + const type s; + }; + + template < + typename EXP, + typename S + > + inline typename disable_if<is_matrix<S>, const matrix_mul_scal_exp<EXP> >::type operator* ( + const matrix_exp<EXP>& m, + const S& s + ) + { + typedef typename EXP::type type; + return matrix_mul_scal_exp<EXP>(m.ref(),static_cast<type>(s)); + } + + template < + typename EXP, + typename S, + bool B + > + inline typename disable_if<is_matrix<S>, const matrix_mul_scal_exp<EXP> >::type operator* ( + const matrix_mul_scal_exp<EXP,B>& m, + const S& s + ) + { + typedef typename EXP::type type; + return matrix_mul_scal_exp<EXP>(m.m,static_cast<type>(s)*m.s); + } + + template < + typename EXP, + typename S + > + inline typename disable_if<is_matrix<S>, const matrix_mul_scal_exp<EXP> >::type operator* ( + const S& s, + const matrix_exp<EXP>& m + ) + { + typedef typename EXP::type type; + return matrix_mul_scal_exp<EXP>(m.ref(),static_cast<type>(s)); + } + + template < + typename EXP, + typename S, + bool B + > + inline typename disable_if<is_matrix<S>, const matrix_mul_scal_exp<EXP> >::type operator* ( + const S& s, + const matrix_mul_scal_exp<EXP,B>& m + ) + { + typedef typename EXP::type type; + return matrix_mul_scal_exp<EXP>(m.m,static_cast<type>(s)*m.s); + } + + template < + typename EXP , + typename S + > + inline const typename disable_if_c<std::numeric_limits<typename EXP::type>::is_integer, matrix_mul_scal_exp<EXP> >::type operator/ ( + const matrix_exp<EXP>& m, + const S& s + ) + { + typedef typename EXP::type type; + const type one = 1; + return matrix_mul_scal_exp<EXP>(m.ref(),one/static_cast<type>(s)); + } + + template < + typename EXP, + bool B, + typename S + > + inline const typename disable_if_c<std::numeric_limits<typename EXP::type>::is_integer, matrix_mul_scal_exp<EXP> >::type operator/ ( + const matrix_mul_scal_exp<EXP,B>& m, + const S& s + ) + { + typedef typename EXP::type type; + return matrix_mul_scal_exp<EXP>(m.m,m.s/static_cast<type>(s)); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_s_div_m : basic_op_m<M> + { + typedef typename M::type type; + + op_s_div_m( const M& m_, const type& s_) : basic_op_m<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<is_matrix<S>, matrix_op<op_s_div_m<EXP> > >::type operator/ ( + const S& val, + const matrix_exp<EXP>& m + ) + { + typedef typename EXP::type type; + + typedef op_s_div_m<EXP> op; + return matrix_op<op>(op(m.ref(), static_cast<type>(val))); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + inline const matrix_mul_scal_exp<EXP> operator- ( + const matrix_exp<EXP>& m + ) + { + return matrix_mul_scal_exp<EXP>(m.ref(),-1); + } + + template < + typename EXP, + bool B + > + inline const matrix_mul_scal_exp<EXP> operator- ( + const matrix_mul_scal_exp<EXP,B>& m + ) + { + return matrix_mul_scal_exp<EXP>(m.m,-1*m.s); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_add_scalar : basic_op_m<M> + { + typedef typename M::type type; + + op_add_scalar( const M& m_, const type& s_) : basic_op_m<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<is_matrix<T>, matrix_op<op_add_scalar<EXP> > >::type operator+ ( + const matrix_exp<EXP>& m, + const T& val + ) + { + typedef typename EXP::type type; + + typedef op_add_scalar<EXP> op; + return matrix_op<op>(op(m.ref(), static_cast<type>(val))); + } + + template < + typename EXP, + typename T + > + const typename disable_if<is_matrix<T>, matrix_op<op_add_scalar<EXP> > >::type operator+ ( + const T& val, + const matrix_exp<EXP>& m + ) + { + typedef typename EXP::type type; + + typedef op_add_scalar<EXP> op; + return matrix_op<op>(op(m.ref(), static_cast<type>(val))); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_subl_scalar : basic_op_m<M> + { + typedef typename M::type type; + + op_subl_scalar( const M& m_, const type& s_) : basic_op_m<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<is_matrix<T>, matrix_op<op_subl_scalar<EXP> > >::type operator- ( + const T& val, + const matrix_exp<EXP>& m + ) + { + typedef typename EXP::type type; + + typedef op_subl_scalar<EXP> op; + return matrix_op<op>(op(m.ref(), static_cast<type>(val))); + } + +// ---------------------------------------------------------------------------------------- + + template <typename M> + struct op_subr_scalar : basic_op_m<M> + { + typedef typename M::type type; + + op_subr_scalar( const M& m_, const type& s_) : basic_op_m<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<is_matrix<T>, matrix_op<op_subr_scalar<EXP> > >::type operator- ( + const matrix_exp<EXP>& m, + const T& val + ) + { + typedef typename EXP::type type; + + typedef op_subr_scalar<EXP> op; + return matrix_op<op>(op(m.ref(), static_cast<type>(val))); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP1, + typename EXP2 + > + bool operator== ( + const matrix_exp<EXP1>& m1, + const matrix_exp<EXP2>& 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<EXP1>& m1, + const matrix_exp<EXP2>& m2 + ) { return !(m1 == m2); } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + template <typename T> + struct op_pointer_to_mat; + template <typename T> + struct op_pointer_to_col_vect; + + template < + typename T, + long num_rows, + long num_cols, + typename mem_manager, + typename layout + > + struct matrix_traits<matrix<T,num_rows, num_cols, mem_manager, layout> > + { + 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<matrix<T,num_rows,num_cols, mem_manager,layout> > + { + + COMPILE_TIME_ASSERT(num_rows >= 0 && num_cols >= 0); + + public: + typedef typename matrix_traits<matrix>::type type; + typedef typename matrix_traits<matrix>::const_ret_type const_ret_type; + typedef typename matrix_traits<matrix>::mem_manager_type mem_manager_type; + typedef typename matrix_traits<matrix>::layout_type layout_type; + const static long NR = matrix_traits<matrix>::NR; + const static long NC = matrix_traits<matrix>::NC; + const static long cost = matrix_traits<matrix>::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 <typename EXP> + matrix ( + const matrix_exp<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<typename EXP::type,type>::value == true) || + (is_matrix<typename EXP::type>::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<matrix>(*this) + { + data.set_size(m.nr(),m.nc()); + matrix_assign(*this, m); + } + +#ifdef DLIB_HAS_INITIALIZER_LISTS + matrix(const std::initializer_list<T>& 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<T>& 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 <typename U, size_t len> + 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<T>(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 <typename U, size_t len> + 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<T>(array[idx]); + ++idx; + } + } + return *this; + } + + template <typename EXP> + matrix& operator= ( + const matrix_exp<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<typename EXP::type,type>::value == true) || + (is_matrix<typename EXP::type>::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 <typename EXP> + matrix& operator += ( + const matrix_exp<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<typename EXP::type,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 <typename EXP> + matrix& operator -= ( + const matrix_exp<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<typename EXP::type,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 <typename EXP> + matrix& operator *= ( + const matrix_exp<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 <typename U> + bool aliases ( + const matrix_exp<U>& + ) const { return false; } + + bool aliases ( + const matrix_exp<matrix<T,num_rows,num_cols, mem_manager,layout> >& item + ) const { return (this == &item); } + + template <typename U> + bool destructively_aliases ( + const matrix_exp<U>& + ) const { return false; } + + // These two aliases() routines are defined in matrix_mat.h + bool aliases ( + const matrix_exp<matrix_op<op_pointer_to_mat<T> > >& item + ) const; + bool aliases ( + const matrix_exp<matrix_op<op_pointer_to_col_vect<T> > >& 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<T,num_rows,num_cols,mem_manager,layout>; + + 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<T,NR,NC,mem_manager> data; + }; + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + template < + typename T, + long NR, + long NC, + typename mm, + typename l + > + void swap( + matrix<T,NR,NC,mm,l>& a, + matrix<T,NR,NC,mm,l>& b + ) { a.swap(b); } + + template < + typename T, + long NR, + long NC, + typename mm, + typename l + > + void serialize ( + const matrix<T,NR,NC,mm,l>& 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<T,NR,NC,mm,l>& 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<matrix<T,NR,NC,mm,l>>& 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<matrix<T,NR,NC,mm,l>>&& 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<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<streamsize>(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<T,NR,NC,MM,L>& 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 <typename EXP> + std::ostream& operator<< ( + const matrix_exp<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 <typename EXP> + class const_temp_matrix; + + template < + typename EXP + > + struct matrix_traits<const_temp_matrix<EXP> > + { + 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 <typename EXP> + class const_temp_matrix : public matrix_exp<const_temp_matrix<EXP> >, noncopyable + { + public: + typedef typename matrix_traits<const_temp_matrix>::type type; + typedef typename matrix_traits<const_temp_matrix>::const_ret_type const_ret_type; + typedef typename matrix_traits<const_temp_matrix>::mem_manager_type mem_manager_type; + typedef typename matrix_traits<const_temp_matrix>::layout_type layout_type; + const static long NR = matrix_traits<const_temp_matrix>::NR; + const static long NC = matrix_traits<const_temp_matrix>::NC; + const static long cost = matrix_traits<const_temp_matrix>::cost; + + const_temp_matrix ( + const matrix_exp<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 <typename U> + bool aliases ( + const matrix_exp<U>& item + ) const { return ref_.aliases(item); } + + template <typename U> + bool destructively_aliases ( + const matrix_exp<U>& item + ) const { return ref_.destructively_aliases(item); } + + long nr ( + ) const { return ref_.nr(); } + + long nc ( + ) const { return ref_.nc(); } + + private: + + typename conditional_matrix_temp<const EXP, (EXP::cost <= 1)>::type ref_; + }; + +// ---------------------------------------------------------------------------------------- + + typedef matrix<double,0,0,default_memory_manager,column_major_layout> matrix_colmajor; + typedef matrix<float,0,0,default_memory_manager,column_major_layout> fmatrix_colmajor; + +} + +#ifdef _MSC_VER +// put warnings back to their default settings +#pragma warning(default : 4355) +#pragma warning(default : 4723) +#endif + +#endif // DLIB_MATRIx_ + |